[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
) (cherry picked from commit4a147bff7e
) Conflicts: routers/api/v1/api.go templates/swagger/v1_json.tmpl (cherry picked from commitbb8c339185
) (cherry picked from commit5a11569a01
)
This commit is contained in:
parent
b1f3069a22
commit
2373c801ee
12 changed files with 626 additions and 9 deletions
|
@ -52,18 +52,29 @@ func UnblockUser(ctx context.Context, userID, blockID int64) error {
|
||||||
return err
|
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.
|
// ListBlockedUsers returns the users that the user has blocked.
|
||||||
// The created_unix field of the user struct is overridden by the creation_unix
|
// The created_unix field of the user struct is overridden by the creation_unix
|
||||||
// field of blockeduser.
|
// field of blockeduser.
|
||||||
func ListBlockedUsers(ctx context.Context, userID int64) ([]*User, error) {
|
func ListBlockedUsers(ctx context.Context, userID int64, opts db.ListOptions) ([]*User, error) {
|
||||||
users := make([]*User, 0, 8)
|
sess := db.GetEngine(ctx).
|
||||||
err := db.GetEngine(ctx).
|
|
||||||
Select("`forgejo_blocked_user`.created_unix, `user`.*").
|
Select("`forgejo_blocked_user`.created_unix, `user`.*").
|
||||||
Join("INNER", "forgejo_blocked_user", "`user`.id=`forgejo_blocked_user`.block_id").
|
Join("INNER", "forgejo_blocked_user", "`user`.id=`forgejo_blocked_user`.block_id").
|
||||||
Where("`forgejo_blocked_user`.user_id=?", userID).
|
Where("`forgejo_blocked_user`.user_id=?", userID)
|
||||||
Find(&users)
|
|
||||||
|
|
||||||
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.
|
// 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) {
|
func TestListBlockedUsers(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
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)
|
assert.NoError(t, err)
|
||||||
if assert.Len(t, blockedUsers, 1) {
|
if assert.Len(t, blockedUsers, 1) {
|
||||||
assert.EqualValues(t, 1, blockedUsers[0].ID)
|
assert.EqualValues(t, 1, blockedUsers[0].ID)
|
||||||
|
@ -61,3 +61,15 @@ func TestListBlockedByUsersID(t *testing.T) {
|
||||||
assert.EqualValues(t, 4, blockedByUserIDs[0])
|
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"`
|
||||||
|
}
|
|
@ -900,6 +900,12 @@ func Routes() *web.Route {
|
||||||
Delete(user.DeleteHook)
|
Delete(user.DeleteHook)
|
||||||
}, reqWebhooksEnabled())
|
}, reqWebhooksEnabled())
|
||||||
|
|
||||||
|
m.Group("", func() {
|
||||||
|
m.Get("/list_blocked", user.ListBlockedUsers)
|
||||||
|
m.Put("/block/{username}", user.BlockUser)
|
||||||
|
m.Put("/unblock/{username}", user.UnblockUser)
|
||||||
|
})
|
||||||
|
|
||||||
m.Group("/avatar", func() {
|
m.Group("/avatar", func() {
|
||||||
m.Post("", bind(api.UpdateUserAvatarOption{}), user.UpdateAvatar)
|
m.Post("", bind(api.UpdateUserAvatarOption{}), user.UpdateAvatar)
|
||||||
m.Delete("", user.DeleteAvatar)
|
m.Delete("", user.DeleteAvatar)
|
||||||
|
@ -1328,6 +1334,12 @@ func Routes() *web.Route {
|
||||||
m.Delete("", org.DeleteAvatar)
|
m.Delete("", org.DeleteAvatar)
|
||||||
}, reqToken(), reqOrgOwnership())
|
}, reqToken(), reqOrgOwnership())
|
||||||
m.Get("/activities/feeds", org.ListOrgActivityFeeds)
|
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))
|
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true))
|
||||||
m.Group("/teams/{teamid}", func() {
|
m.Group("/teams/{teamid}", func() {
|
||||||
m.Combo("").Get(reqToken(), org.GetTeam).
|
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))
|
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
|
// in:body
|
||||||
Body api.NewIssuePinsAllowed `json:"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))
|
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"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/routers/utils"
|
"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["Title"] = ctx.Tr("settings.blocked_users")
|
||||||
ctx.Data["PageIsSettingsBlockedUsers"] = true
|
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 {
|
if err != nil {
|
||||||
ctx.ServerError("ListBlockedUsers", err)
|
ctx.ServerError("ListBlockedUsers", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -6,6 +6,7 @@ package setting
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"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["BaseLink"] = setting.AppSubURL + "/user/settings/blocked_users"
|
||||||
ctx.Data["BaseLinkNew"] = 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 {
|
if err != nil {
|
||||||
ctx.ServerError("ListBlockedUsers", err)
|
ctx.ServerError("ListBlockedUsers", err)
|
||||||
return
|
return
|
||||||
|
|
225
templates/swagger/v1_json.tmpl
generated
225
templates/swagger/v1_json.tmpl
generated
|
@ -1652,6 +1652,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": {
|
"/orgs/{org}/hooks": {
|
||||||
"get": {
|
"get": {
|
||||||
"produces": [
|
"produces": [
|
||||||
|
@ -2016,6 +2052,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": {
|
"/orgs/{org}/members": {
|
||||||
"get": {
|
"get": {
|
||||||
"produces": [
|
"produces": [
|
||||||
|
@ -2480,6 +2554,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}": {
|
"/packages/{owner}": {
|
||||||
"get": {
|
"get": {
|
||||||
"produces": [
|
"produces": [
|
||||||
|
@ -13959,6 +14069,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": {
|
"/user/emails": {
|
||||||
"get": {
|
"get": {
|
||||||
"produces": [
|
"produces": [
|
||||||
|
@ -14608,6 +14747,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": {
|
"/user/orgs": {
|
||||||
"get": {
|
"get": {
|
||||||
"produces": [
|
"produces": [
|
||||||
|
@ -15009,6 +15179,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": {
|
"/users/search": {
|
||||||
"get": {
|
"get": {
|
||||||
"produces": [
|
"produces": [
|
||||||
|
@ -15939,6 +16138,23 @@
|
||||||
},
|
},
|
||||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
"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": {
|
"Branch": {
|
||||||
"description": "Branch represents a repository branch",
|
"description": "Branch represents a repository branch",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
@ -22146,6 +22362,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"BlockedUserList": {
|
||||||
|
"description": "BlockedUserList",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/BlockedUser"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Branch": {
|
"Branch": {
|
||||||
"description": "Branch",
|
"description": "Branch",
|
||||||
"schema": {
|
"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…
Reference in a new issue