[MODERATION] add user blocking API
- Follow up for: #540, #802 - Add API routes for user blocking from user and organization perspective. - The new routes have integration testing. - The new model functions have unit tests. - Actually quite boring to write and to read this pull request. (cherry picked from commitf3afaf15c7) (cherry picked from commit6d754db3e5) (cherry picked from commit2a89ddc0ac)
This commit is contained in:
		
							parent
							
								
									4743eaa6a0
								
							
						
					
					
						commit
						4a147bff7e
					
				
					 12 changed files with 626 additions and 9 deletions
				
			
		| 
						 | 
				
			
			@ -52,18 +52,29 @@ func UnblockUser(ctx context.Context, userID, blockID int64) error {
 | 
			
		|||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CountBlockedUsers returns the number of users the user has blocked.
 | 
			
		||||
func CountBlockedUsers(ctx context.Context, userID int64) (int64, error) {
 | 
			
		||||
	return db.GetEngine(ctx).Where("user_id=?", userID).Count(&BlockedUser{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListBlockedUsers returns the users that the user has blocked.
 | 
			
		||||
// The created_unix field of the user struct is overridden by the creation_unix
 | 
			
		||||
// field of blockeduser.
 | 
			
		||||
func ListBlockedUsers(ctx context.Context, userID int64) ([]*User, error) {
 | 
			
		||||
	users := make([]*User, 0, 8)
 | 
			
		||||
	err := db.GetEngine(ctx).
 | 
			
		||||
func ListBlockedUsers(ctx context.Context, userID int64, opts db.ListOptions) ([]*User, error) {
 | 
			
		||||
	sess := db.GetEngine(ctx).
 | 
			
		||||
		Select("`forgejo_blocked_user`.created_unix, `user`.*").
 | 
			
		||||
		Join("INNER", "forgejo_blocked_user", "`user`.id=`forgejo_blocked_user`.block_id").
 | 
			
		||||
		Where("`forgejo_blocked_user`.user_id=?", userID).
 | 
			
		||||
		Find(&users)
 | 
			
		||||
		Where("`forgejo_blocked_user`.user_id=?", userID)
 | 
			
		||||
 | 
			
		||||
	return users, err
 | 
			
		||||
	if opts.Page > 0 {
 | 
			
		||||
		sess = db.SetSessionPagination(sess, &opts)
 | 
			
		||||
		users := make([]*User, 0, opts.PageSize)
 | 
			
		||||
 | 
			
		||||
		return users, sess.Find(&users)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	users := make([]*User, 0, 8)
 | 
			
		||||
	return users, sess.Find(&users)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListBlockedByUsersID returns the ids of the users that blocked the user.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,7 +45,7 @@ func TestUnblockUser(t *testing.T) {
 | 
			
		|||
func TestListBlockedUsers(t *testing.T) {
 | 
			
		||||
	assert.NoError(t, unittest.PrepareTestDatabase())
 | 
			
		||||
 | 
			
		||||
	blockedUsers, err := user_model.ListBlockedUsers(db.DefaultContext, 4)
 | 
			
		||||
	blockedUsers, err := user_model.ListBlockedUsers(db.DefaultContext, 4, db.ListOptions{})
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	if assert.Len(t, blockedUsers, 1) {
 | 
			
		||||
		assert.EqualValues(t, 1, blockedUsers[0].ID)
 | 
			
		||||
| 
						 | 
				
			
			@ -61,3 +61,15 @@ func TestListBlockedByUsersID(t *testing.T) {
 | 
			
		|||
		assert.EqualValues(t, 4, blockedByUserIDs[0])
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCountBlockedUsers(t *testing.T) {
 | 
			
		||||
	assert.NoError(t, unittest.PrepareTestDatabase())
 | 
			
		||||
 | 
			
		||||
	count, err := user_model.CountBlockedUsers(db.DefaultContext, 4)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.EqualValues(t, 1, count)
 | 
			
		||||
 | 
			
		||||
	count, err = user_model.CountBlockedUsers(db.DefaultContext, 1)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.EqualValues(t, 0, count)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										13
									
								
								modules/structs/moderation.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								modules/structs/moderation.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
// Copyright 2023 The Forgejo Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package structs
 | 
			
		||||
 | 
			
		||||
import "time"
 | 
			
		||||
 | 
			
		||||
// BlockedUser represents a blocked user.
 | 
			
		||||
type BlockedUser struct {
 | 
			
		||||
	BlockID int64 `json:"block_id"`
 | 
			
		||||
	// swagger:strfmt date-time
 | 
			
		||||
	Created time.Time `json:"created_at"`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -899,6 +899,12 @@ func Routes() *web.Route {
 | 
			
		|||
					Patch(bind(api.EditHookOption{}), user.EditHook).
 | 
			
		||||
					Delete(user.DeleteHook)
 | 
			
		||||
			}, reqWebhooksEnabled())
 | 
			
		||||
 | 
			
		||||
			m.Group("", func() {
 | 
			
		||||
				m.Get("/list_blocked", user.ListBlockedUsers)
 | 
			
		||||
				m.Put("/block/{username}", user.BlockUser)
 | 
			
		||||
				m.Put("/unblock/{username}", user.UnblockUser)
 | 
			
		||||
			})
 | 
			
		||||
		}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken())
 | 
			
		||||
 | 
			
		||||
		// Repositories (requires repo scope, org scope)
 | 
			
		||||
| 
						 | 
				
			
			@ -1315,6 +1321,12 @@ func Routes() *web.Route {
 | 
			
		|||
					Delete(org.DeleteHook)
 | 
			
		||||
			}, reqToken(), reqOrgOwnership(), reqWebhooksEnabled())
 | 
			
		||||
			m.Get("/activities/feeds", org.ListOrgActivityFeeds)
 | 
			
		||||
 | 
			
		||||
			m.Group("", func() {
 | 
			
		||||
				m.Get("/list_blocked", reqToken(), reqOrgOwnership(), org.ListBlockedUsers)
 | 
			
		||||
				m.Put("/block/{username}", reqToken(), reqOrgOwnership(), org.BlockUser)
 | 
			
		||||
				m.Put("/unblock/{username}", reqToken(), reqOrgOwnership(), org.UnblockUser)
 | 
			
		||||
			}, reqToken(), reqOrgOwnership())
 | 
			
		||||
		}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true))
 | 
			
		||||
		m.Group("/teams/{teamid}", func() {
 | 
			
		||||
			m.Combo("").Get(reqToken(), org.GetTeam).
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -437,3 +437,95 @@ func ListOrgActivityFeeds(ctx *context.APIContext) {
 | 
			
		|||
 | 
			
		||||
	ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListBlockedUsers list the organization's blocked users.
 | 
			
		||||
func ListBlockedUsers(ctx *context.APIContext) {
 | 
			
		||||
	// swagger:operation GET /orgs/{org}/list_blocked organization orgListBlockedUsers
 | 
			
		||||
	// ---
 | 
			
		||||
	// summary: List the organization's blocked users
 | 
			
		||||
	// produces:
 | 
			
		||||
	// - application/json
 | 
			
		||||
	// parameters:
 | 
			
		||||
	// - name: org
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: name of the org
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// - name: page
 | 
			
		||||
	//   in: query
 | 
			
		||||
	//   description: page number of results to return (1-based)
 | 
			
		||||
	//   type: integer
 | 
			
		||||
	// - name: limit
 | 
			
		||||
	//   in: query
 | 
			
		||||
	//   description: page size of results
 | 
			
		||||
	//   type: integer
 | 
			
		||||
	// responses:
 | 
			
		||||
	//   "200":
 | 
			
		||||
	//     "$ref": "#/responses/BlockedUserList"
 | 
			
		||||
 | 
			
		||||
	utils.ListUserBlockedUsers(ctx, ctx.ContextUser)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BlockUser blocks a user from the organization.
 | 
			
		||||
func BlockUser(ctx *context.APIContext) {
 | 
			
		||||
	// swagger:operation PUT /orgs/{org}/block/{username} organization orgBlockUser
 | 
			
		||||
	// ---
 | 
			
		||||
	// summary: Blocks a user from the organization
 | 
			
		||||
	// produces:
 | 
			
		||||
	// - application/json
 | 
			
		||||
	// parameters:
 | 
			
		||||
	// - name: org
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: name of the org
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// - name: username
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: username of the user
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// responses:
 | 
			
		||||
	//   "204":
 | 
			
		||||
	//     "$ref": "#/responses/empty"
 | 
			
		||||
	//   "404":
 | 
			
		||||
	//     "$ref": "#/responses/notFound"
 | 
			
		||||
 | 
			
		||||
	user := user.GetUserByParams(ctx)
 | 
			
		||||
	if ctx.Written() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	utils.BlockUser(ctx, ctx.Org.Organization.AsUser(), user)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnblockUser unblocks a user from the organization.
 | 
			
		||||
func UnblockUser(ctx *context.APIContext) {
 | 
			
		||||
	// swagger:operation PUT /orgs/{org}/unblock/{username} organization orgUnblockUser
 | 
			
		||||
	// ---
 | 
			
		||||
	// summary: Unblock a user from the organization
 | 
			
		||||
	// produces:
 | 
			
		||||
	// - application/json
 | 
			
		||||
	// parameters:
 | 
			
		||||
	// - name: org
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: name of the org
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// - name: username
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: username of the user
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// responses:
 | 
			
		||||
	//   "204":
 | 
			
		||||
	//     "$ref": "#/responses/empty"
 | 
			
		||||
	//   "404":
 | 
			
		||||
	//     "$ref": "#/responses/notFound"
 | 
			
		||||
 | 
			
		||||
	user := user.GetUserByParams(ctx)
 | 
			
		||||
	if ctx.Written() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	utils.UnblockUser(ctx, ctx.Org.Organization.AsUser(), user)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -414,3 +414,10 @@ type swaggerRepoNewIssuePinsAllowed struct {
 | 
			
		|||
	// in:body
 | 
			
		||||
	Body api.NewIssuePinsAllowed `json:"body"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BlockedUserList
 | 
			
		||||
// swagger:response BlockedUserList
 | 
			
		||||
type swaggerBlockedUserList struct {
 | 
			
		||||
	// in:body
 | 
			
		||||
	Body []api.BlockedUser `json:"body"`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -202,3 +202,80 @@ func ListUserActivityFeeds(ctx *context.APIContext) {
 | 
			
		|||
 | 
			
		||||
	ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListBlockedUsers list the authenticated user's blocked users.
 | 
			
		||||
func ListBlockedUsers(ctx *context.APIContext) {
 | 
			
		||||
	// swagger:operation GET /user/list_blocked user userListBlockedUsers
 | 
			
		||||
	// ---
 | 
			
		||||
	// summary: List the authenticated user's blocked users
 | 
			
		||||
	// produces:
 | 
			
		||||
	// - application/json
 | 
			
		||||
	// parameters:
 | 
			
		||||
	// - name: page
 | 
			
		||||
	//   in: query
 | 
			
		||||
	//   description: page number of results to return (1-based)
 | 
			
		||||
	//   type: integer
 | 
			
		||||
	// - name: limit
 | 
			
		||||
	//   in: query
 | 
			
		||||
	//   description: page size of results
 | 
			
		||||
	//   type: integer
 | 
			
		||||
	// responses:
 | 
			
		||||
	//   "200":
 | 
			
		||||
	//     "$ref": "#/responses/BlockedUserList"
 | 
			
		||||
 | 
			
		||||
	utils.ListUserBlockedUsers(ctx, ctx.Doer)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BlockUser blocks a user from the doer.
 | 
			
		||||
func BlockUser(ctx *context.APIContext) {
 | 
			
		||||
	// swagger:operation PUT /user/block/{username} user userBlockUser
 | 
			
		||||
	// ---
 | 
			
		||||
	// summary: Blocks a user from the doer.
 | 
			
		||||
	// produces:
 | 
			
		||||
	// - application/json
 | 
			
		||||
	// parameters:
 | 
			
		||||
	// - name: username
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: username of the user
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// responses:
 | 
			
		||||
	//   "204":
 | 
			
		||||
	//     "$ref": "#/responses/empty"
 | 
			
		||||
	//   "404":
 | 
			
		||||
	//     "$ref": "#/responses/notFound"
 | 
			
		||||
 | 
			
		||||
	user := GetUserByParams(ctx)
 | 
			
		||||
	if ctx.Written() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	utils.BlockUser(ctx, ctx.Doer, user)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnblockUser unblocks a user from the doer.
 | 
			
		||||
func UnblockUser(ctx *context.APIContext) {
 | 
			
		||||
	// swagger:operation PUT /user/unblock/{username} user userUnblockUser
 | 
			
		||||
	// ---
 | 
			
		||||
	// summary: Unblocks a user from the doer.
 | 
			
		||||
	// produces:
 | 
			
		||||
	// - application/json
 | 
			
		||||
	// parameters:
 | 
			
		||||
	// - name: username
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: username of the user
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// responses:
 | 
			
		||||
	//   "204":
 | 
			
		||||
	//     "$ref": "#/responses/empty"
 | 
			
		||||
	//   "404":
 | 
			
		||||
	//     "$ref": "#/responses/notFound"
 | 
			
		||||
 | 
			
		||||
	user := GetUserByParams(ctx)
 | 
			
		||||
	if ctx.Written() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	utils.UnblockUser(ctx, ctx.Doer, user)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										65
									
								
								routers/api/v1/utils/block.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								routers/api/v1/utils/block.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,65 @@
 | 
			
		|||
// Copyright 2023 The Forgejo Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package utils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	user_model "code.gitea.io/gitea/models/user"
 | 
			
		||||
	"code.gitea.io/gitea/modules/context"
 | 
			
		||||
	api "code.gitea.io/gitea/modules/structs"
 | 
			
		||||
	user_service "code.gitea.io/gitea/services/user"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ListUserBlockedUsers lists the blocked users of the provided doer.
 | 
			
		||||
func ListUserBlockedUsers(ctx *context.APIContext, doer *user_model.User) {
 | 
			
		||||
	count, err := user_model.CountBlockedUsers(ctx, doer.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.InternalServerError(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	blockedUsers, err := user_model.ListBlockedUsers(ctx, doer.ID, GetListOptions(ctx))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.InternalServerError(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	apiBlockedUsers := make([]*api.BlockedUser, len(blockedUsers))
 | 
			
		||||
	for i, blockedUser := range blockedUsers {
 | 
			
		||||
		apiBlockedUsers[i] = &api.BlockedUser{
 | 
			
		||||
			BlockID: blockedUser.ID,
 | 
			
		||||
			Created: blockedUser.CreatedUnix.AsTime(),
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ctx.InternalServerError(err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.SetTotalCountHeader(count)
 | 
			
		||||
	ctx.JSON(http.StatusOK, apiBlockedUsers)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BlockUser blocks the blockUser from the doer.
 | 
			
		||||
func BlockUser(ctx *context.APIContext, doer, blockUser *user_model.User) {
 | 
			
		||||
	err := user_service.BlockUser(ctx, doer.ID, blockUser.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.InternalServerError(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.Status(http.StatusNoContent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnblockUser unblocks the blockUser from the doer.
 | 
			
		||||
func UnblockUser(ctx *context.APIContext, doer, blockUser *user_model.User) {
 | 
			
		||||
	err := user_model.UnblockUser(ctx, doer.ID, blockUser.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.InternalServerError(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.Status(http.StatusNoContent)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -7,6 +7,7 @@ import (
 | 
			
		|||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models/db"
 | 
			
		||||
	user_model "code.gitea.io/gitea/models/user"
 | 
			
		||||
	"code.gitea.io/gitea/modules/context"
 | 
			
		||||
	"code.gitea.io/gitea/routers/utils"
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +21,7 @@ func BlockedUsers(ctx *context.Context) {
 | 
			
		|||
	ctx.Data["Title"] = ctx.Tr("settings.blocked_users")
 | 
			
		||||
	ctx.Data["PageIsSettingsBlockedUsers"] = true
 | 
			
		||||
 | 
			
		||||
	blockedUsers, err := user_model.ListBlockedUsers(ctx, ctx.Org.Organization.ID)
 | 
			
		||||
	blockedUsers, err := user_model.ListBlockedUsers(ctx, ctx.Org.Organization.ID, db.ListOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.ServerError("ListBlockedUsers", err)
 | 
			
		||||
		return
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,7 @@ package setting
 | 
			
		|||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models/db"
 | 
			
		||||
	user_model "code.gitea.io/gitea/models/user"
 | 
			
		||||
	"code.gitea.io/gitea/modules/base"
 | 
			
		||||
	"code.gitea.io/gitea/modules/context"
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +24,7 @@ func BlockedUsers(ctx *context.Context) {
 | 
			
		|||
	ctx.Data["BaseLink"] = setting.AppSubURL + "/user/settings/blocked_users"
 | 
			
		||||
	ctx.Data["BaseLinkNew"] = setting.AppSubURL + "/user/settings/blocked_users"
 | 
			
		||||
 | 
			
		||||
	blockedUsers, err := user_model.ListBlockedUsers(ctx, ctx.Doer.ID)
 | 
			
		||||
	blockedUsers, err := user_model.ListBlockedUsers(ctx, ctx.Doer.ID, db.ListOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.ServerError("ListBlockedUsers", err)
 | 
			
		||||
		return
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										225
									
								
								templates/swagger/v1_json.tmpl
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										225
									
								
								templates/swagger/v1_json.tmpl
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -1595,6 +1595,42 @@
 | 
			
		|||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/orgs/{org}/block/{username}": {
 | 
			
		||||
      "put": {
 | 
			
		||||
        "produces": [
 | 
			
		||||
          "application/json"
 | 
			
		||||
        ],
 | 
			
		||||
        "tags": [
 | 
			
		||||
          "organization"
 | 
			
		||||
        ],
 | 
			
		||||
        "summary": "Blocks a user from the organization",
 | 
			
		||||
        "operationId": "orgBlockUser",
 | 
			
		||||
        "parameters": [
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "name of the org",
 | 
			
		||||
            "name": "org",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "username of the user",
 | 
			
		||||
            "name": "username",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": {
 | 
			
		||||
          "204": {
 | 
			
		||||
            "$ref": "#/responses/empty"
 | 
			
		||||
          },
 | 
			
		||||
          "404": {
 | 
			
		||||
            "$ref": "#/responses/notFound"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/orgs/{org}/hooks": {
 | 
			
		||||
      "get": {
 | 
			
		||||
        "produces": [
 | 
			
		||||
| 
						 | 
				
			
			@ -1959,6 +1995,44 @@
 | 
			
		|||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/orgs/{org}/list_blocked": {
 | 
			
		||||
      "get": {
 | 
			
		||||
        "produces": [
 | 
			
		||||
          "application/json"
 | 
			
		||||
        ],
 | 
			
		||||
        "tags": [
 | 
			
		||||
          "organization"
 | 
			
		||||
        ],
 | 
			
		||||
        "summary": "List the organization's blocked users",
 | 
			
		||||
        "operationId": "orgListBlockedUsers",
 | 
			
		||||
        "parameters": [
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "name of the org",
 | 
			
		||||
            "name": "org",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "type": "integer",
 | 
			
		||||
            "description": "page number of results to return (1-based)",
 | 
			
		||||
            "name": "page",
 | 
			
		||||
            "in": "query"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "type": "integer",
 | 
			
		||||
            "description": "page size of results",
 | 
			
		||||
            "name": "limit",
 | 
			
		||||
            "in": "query"
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": {
 | 
			
		||||
          "200": {
 | 
			
		||||
            "$ref": "#/responses/BlockedUserList"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/orgs/{org}/members": {
 | 
			
		||||
      "get": {
 | 
			
		||||
        "produces": [
 | 
			
		||||
| 
						 | 
				
			
			@ -2423,6 +2497,42 @@
 | 
			
		|||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/orgs/{org}/unblock/{username}": {
 | 
			
		||||
      "put": {
 | 
			
		||||
        "produces": [
 | 
			
		||||
          "application/json"
 | 
			
		||||
        ],
 | 
			
		||||
        "tags": [
 | 
			
		||||
          "organization"
 | 
			
		||||
        ],
 | 
			
		||||
        "summary": "Unblock a user from the organization",
 | 
			
		||||
        "operationId": "orgUnblockUser",
 | 
			
		||||
        "parameters": [
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "name of the org",
 | 
			
		||||
            "name": "org",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "username of the user",
 | 
			
		||||
            "name": "username",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": {
 | 
			
		||||
          "204": {
 | 
			
		||||
            "$ref": "#/responses/empty"
 | 
			
		||||
          },
 | 
			
		||||
          "404": {
 | 
			
		||||
            "$ref": "#/responses/notFound"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/packages/{owner}": {
 | 
			
		||||
      "get": {
 | 
			
		||||
        "produces": [
 | 
			
		||||
| 
						 | 
				
			
			@ -13787,6 +13897,35 @@
 | 
			
		|||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/user/block/{username}": {
 | 
			
		||||
      "put": {
 | 
			
		||||
        "produces": [
 | 
			
		||||
          "application/json"
 | 
			
		||||
        ],
 | 
			
		||||
        "tags": [
 | 
			
		||||
          "user"
 | 
			
		||||
        ],
 | 
			
		||||
        "summary": "Blocks a user from the doer.",
 | 
			
		||||
        "operationId": "userBlockUser",
 | 
			
		||||
        "parameters": [
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "username of the user",
 | 
			
		||||
            "name": "username",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": {
 | 
			
		||||
          "204": {
 | 
			
		||||
            "$ref": "#/responses/empty"
 | 
			
		||||
          },
 | 
			
		||||
          "404": {
 | 
			
		||||
            "$ref": "#/responses/notFound"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/user/emails": {
 | 
			
		||||
      "get": {
 | 
			
		||||
        "produces": [
 | 
			
		||||
| 
						 | 
				
			
			@ -14436,6 +14575,37 @@
 | 
			
		|||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/user/list_blocked": {
 | 
			
		||||
      "get": {
 | 
			
		||||
        "produces": [
 | 
			
		||||
          "application/json"
 | 
			
		||||
        ],
 | 
			
		||||
        "tags": [
 | 
			
		||||
          "user"
 | 
			
		||||
        ],
 | 
			
		||||
        "summary": "List the authenticated user's blocked users",
 | 
			
		||||
        "operationId": "userListBlockedUsers",
 | 
			
		||||
        "parameters": [
 | 
			
		||||
          {
 | 
			
		||||
            "type": "integer",
 | 
			
		||||
            "description": "page number of results to return (1-based)",
 | 
			
		||||
            "name": "page",
 | 
			
		||||
            "in": "query"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "type": "integer",
 | 
			
		||||
            "description": "page size of results",
 | 
			
		||||
            "name": "limit",
 | 
			
		||||
            "in": "query"
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": {
 | 
			
		||||
          "200": {
 | 
			
		||||
            "$ref": "#/responses/BlockedUserList"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/user/orgs": {
 | 
			
		||||
      "get": {
 | 
			
		||||
        "produces": [
 | 
			
		||||
| 
						 | 
				
			
			@ -14837,6 +15007,35 @@
 | 
			
		|||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/user/unblock/{username}": {
 | 
			
		||||
      "put": {
 | 
			
		||||
        "produces": [
 | 
			
		||||
          "application/json"
 | 
			
		||||
        ],
 | 
			
		||||
        "tags": [
 | 
			
		||||
          "user"
 | 
			
		||||
        ],
 | 
			
		||||
        "summary": "Unblocks a user from the doer.",
 | 
			
		||||
        "operationId": "userUnblockUser",
 | 
			
		||||
        "parameters": [
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "username of the user",
 | 
			
		||||
            "name": "username",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": {
 | 
			
		||||
          "204": {
 | 
			
		||||
            "$ref": "#/responses/empty"
 | 
			
		||||
          },
 | 
			
		||||
          "404": {
 | 
			
		||||
            "$ref": "#/responses/notFound"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/users/search": {
 | 
			
		||||
      "get": {
 | 
			
		||||
        "produces": [
 | 
			
		||||
| 
						 | 
				
			
			@ -15767,6 +15966,23 @@
 | 
			
		|||
      },
 | 
			
		||||
      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
			
		||||
    },
 | 
			
		||||
    "BlockedUser": {
 | 
			
		||||
      "type": "object",
 | 
			
		||||
      "title": "BlockedUser represents a blocked user.",
 | 
			
		||||
      "properties": {
 | 
			
		||||
        "block_id": {
 | 
			
		||||
          "type": "integer",
 | 
			
		||||
          "format": "int64",
 | 
			
		||||
          "x-go-name": "BlockID"
 | 
			
		||||
        },
 | 
			
		||||
        "created_at": {
 | 
			
		||||
          "type": "string",
 | 
			
		||||
          "format": "date-time",
 | 
			
		||||
          "x-go-name": "Created"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
			
		||||
    },
 | 
			
		||||
    "Branch": {
 | 
			
		||||
      "description": "Branch represents a repository branch",
 | 
			
		||||
      "type": "object",
 | 
			
		||||
| 
						 | 
				
			
			@ -21950,6 +22166,15 @@
 | 
			
		|||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "BlockedUserList": {
 | 
			
		||||
      "description": "BlockedUserList",
 | 
			
		||||
      "schema": {
 | 
			
		||||
        "type": "array",
 | 
			
		||||
        "items": {
 | 
			
		||||
          "$ref": "#/definitions/BlockedUser"
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Branch": {
 | 
			
		||||
      "description": "Branch",
 | 
			
		||||
      "schema": {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										101
									
								
								tests/integration/api_block_test.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								tests/integration/api_block_test.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,101 @@
 | 
			
		|||
// Copyright 2023 The Forgejo Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package integration
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	auth_model "code.gitea.io/gitea/models/auth"
 | 
			
		||||
	"code.gitea.io/gitea/models/unittest"
 | 
			
		||||
	user_model "code.gitea.io/gitea/models/user"
 | 
			
		||||
	api "code.gitea.io/gitea/modules/structs"
 | 
			
		||||
	"code.gitea.io/gitea/tests"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestAPIUserBlock(t *testing.T) {
 | 
			
		||||
	defer tests.PrepareTestEnv(t)()
 | 
			
		||||
 | 
			
		||||
	user := "user4"
 | 
			
		||||
	session := loginUser(t, user)
 | 
			
		||||
	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser)
 | 
			
		||||
 | 
			
		||||
	t.Run("BlockUser", func(t *testing.T) {
 | 
			
		||||
		defer tests.PrintCurrentTest(t)()
 | 
			
		||||
 | 
			
		||||
		req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/block/user2?token=%s", token))
 | 
			
		||||
		MakeRequest(t, req, http.StatusNoContent)
 | 
			
		||||
 | 
			
		||||
		unittest.AssertExistsAndLoadBean(t, &user_model.BlockedUser{UserID: 4, BlockID: 2})
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t.Run("ListBlocked", func(t *testing.T) {
 | 
			
		||||
		defer tests.PrintCurrentTest(t)()
 | 
			
		||||
 | 
			
		||||
		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/list_blocked?token=%s", token))
 | 
			
		||||
		resp := MakeRequest(t, req, http.StatusOK)
 | 
			
		||||
 | 
			
		||||
		// One user just got blocked and the other one is defined in the fixtures.
 | 
			
		||||
		assert.Equal(t, "2", resp.Header().Get("X-Total-Count"))
 | 
			
		||||
 | 
			
		||||
		var blockedUsers []api.BlockedUser
 | 
			
		||||
		DecodeJSON(t, resp, &blockedUsers)
 | 
			
		||||
		assert.Len(t, blockedUsers, 2)
 | 
			
		||||
		assert.EqualValues(t, 1, blockedUsers[0].BlockID)
 | 
			
		||||
		assert.EqualValues(t, 2, blockedUsers[1].BlockID)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t.Run("UnblockUser", func(t *testing.T) {
 | 
			
		||||
		defer tests.PrintCurrentTest(t)()
 | 
			
		||||
 | 
			
		||||
		req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/unblock/user2?token=%s", token))
 | 
			
		||||
		MakeRequest(t, req, http.StatusNoContent)
 | 
			
		||||
 | 
			
		||||
		unittest.AssertNotExistsBean(t, &user_model.BlockedUser{UserID: 4, BlockID: 2})
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestAPIOrgBlock(t *testing.T) {
 | 
			
		||||
	defer tests.PrepareTestEnv(t)()
 | 
			
		||||
 | 
			
		||||
	user := "user5"
 | 
			
		||||
	org := "user6"
 | 
			
		||||
	session := loginUser(t, user)
 | 
			
		||||
	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrganization)
 | 
			
		||||
 | 
			
		||||
	t.Run("BlockUser", func(t *testing.T) {
 | 
			
		||||
		defer tests.PrintCurrentTest(t)()
 | 
			
		||||
 | 
			
		||||
		req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/orgs/%s/block/user2?token=%s", org, token))
 | 
			
		||||
		MakeRequest(t, req, http.StatusNoContent)
 | 
			
		||||
 | 
			
		||||
		unittest.AssertExistsAndLoadBean(t, &user_model.BlockedUser{UserID: 6, BlockID: 2})
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t.Run("ListBlocked", func(t *testing.T) {
 | 
			
		||||
		defer tests.PrintCurrentTest(t)()
 | 
			
		||||
 | 
			
		||||
		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/orgs/%s/list_blocked?token=%s", org, token))
 | 
			
		||||
		resp := MakeRequest(t, req, http.StatusOK)
 | 
			
		||||
 | 
			
		||||
		assert.Equal(t, "1", resp.Header().Get("X-Total-Count"))
 | 
			
		||||
 | 
			
		||||
		var blockedUsers []api.BlockedUser
 | 
			
		||||
		DecodeJSON(t, resp, &blockedUsers)
 | 
			
		||||
		assert.Len(t, blockedUsers, 1)
 | 
			
		||||
		assert.EqualValues(t, 2, blockedUsers[0].BlockID)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t.Run("UnblockUser", func(t *testing.T) {
 | 
			
		||||
		defer tests.PrintCurrentTest(t)()
 | 
			
		||||
 | 
			
		||||
		req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/orgs/%s/unblock/user2?token=%s", org, token))
 | 
			
		||||
		MakeRequest(t, req, http.StatusNoContent)
 | 
			
		||||
 | 
			
		||||
		unittest.AssertNotExistsBean(t, &user_model.BlockedUser{UserID: 6, BlockID: 2})
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue