1
1
mirror of https://github.com/go-gitea/gitea synced 2025-07-23 02:38:35 +00:00

Add user blocking (#29028)

Fixes #17453

This PR adds the abbility to block a user from a personal account or
organization to restrict how the blocked user can interact with the
blocker. The docs explain what's the consequence of blocking a user.

Screenshots:


![grafik](https://github.com/go-gitea/gitea/assets/1666336/4ed884f3-e06a-4862-afd3-3b8aa2488dc6)


![grafik](https://github.com/go-gitea/gitea/assets/1666336/ae6d4981-f252-4f50-a429-04f0f9f1cdf1)


![grafik](https://github.com/go-gitea/gitea/assets/1666336/ca153599-5b0f-4b4a-90fe-18bdfd6f0b6b)

---------

Co-authored-by: Lauris BH <lauris@nix.lv>
This commit is contained in:
KN4CK3R
2024-03-04 09:16:03 +01:00
committed by GitHub
parent 8e12ba34ba
commit c337ff0ec7
109 changed files with 2878 additions and 548 deletions

View File

@@ -8,7 +8,6 @@ import (
"errors"
"net/http"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
@@ -54,15 +53,10 @@ func ListCollaborators(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"
count, err := db.Count[repo_model.Collaboration](ctx, repo_model.FindCollaborationOptions{
RepoID: ctx.Repo.Repository.ID,
collaborators, total, err := repo_model.GetCollaborators(ctx, &repo_model.FindCollaborationOptions{
ListOptions: utils.GetListOptions(ctx),
RepoID: ctx.Repo.Repository.ID,
})
if err != nil {
ctx.InternalServerError(err)
return
}
collaborators, err := repo_model.GetCollaborators(ctx, ctx.Repo.Repository.ID, utils.GetListOptions(ctx))
if err != nil {
ctx.Error(http.StatusInternalServerError, "ListCollaborators", err)
return
@@ -73,7 +67,7 @@ func ListCollaborators(ctx *context.APIContext) {
users[i] = convert.ToUser(ctx, collaborator.User, ctx.Doer)
}
ctx.SetTotalCountHeader(count)
ctx.SetTotalCountHeader(total)
ctx.JSON(http.StatusOK, users)
}
@@ -159,6 +153,8 @@ func AddCollaborator(ctx *context.APIContext) {
// responses:
// "204":
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
// "422":
@@ -182,7 +178,11 @@ func AddCollaborator(ctx *context.APIContext) {
}
if err := repo_module.AddCollaborator(ctx, ctx.Repo.Repository, collaborator); err != nil {
ctx.Error(http.StatusInternalServerError, "AddCollaborator", err)
if errors.Is(err, user_model.ErrBlockedUser) {
ctx.Error(http.StatusForbidden, "AddCollaborator", err)
} else {
ctx.Error(http.StatusInternalServerError, "AddCollaborator", err)
}
return
}
@@ -237,7 +237,7 @@ func DeleteCollaborator(ctx *context.APIContext) {
return
}
if err := repo_service.DeleteCollaboration(ctx, ctx.Repo.Repository, collaborator.ID); err != nil {
if err := repo_service.DeleteCollaboration(ctx, ctx.Repo.Repository, collaborator); err != nil {
ctx.Error(http.StatusInternalServerError, "DeleteCollaboration", err)
return
}

View File

@@ -149,6 +149,8 @@ func CreateFork(ctx *context.APIContext) {
if err != nil {
if errors.Is(err, util.ErrAlreadyExist) || repo_model.IsErrReachLimitOfRepo(err) {
ctx.Error(http.StatusConflict, "ForkRepository", err)
} else if errors.Is(err, user_model.ErrBlockedUser) {
ctx.Error(http.StatusForbidden, "ForkRepository", err)
} else {
ctx.Error(http.StatusInternalServerError, "ForkRepository", err)
}

View File

@@ -5,6 +5,7 @@
package repo
import (
"errors"
"fmt"
"net/http"
"strconv"
@@ -653,6 +654,7 @@ func CreateIssue(ctx *context.APIContext) {
// "$ref": "#/responses/validationError"
// "423":
// "$ref": "#/responses/repoArchivedError"
form := web.GetForm(ctx).(*api.CreateIssueOption)
var deadlineUnix timeutil.TimeStamp
if form.Deadline != nil && ctx.Repo.CanWrite(unit.TypeIssues) {
@@ -710,9 +712,11 @@ func CreateIssue(ctx *context.APIContext) {
if err := issue_service.NewIssue(ctx, ctx.Repo.Repository, issue, form.Labels, nil, assigneeIDs); err != nil {
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) {
ctx.Error(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err)
return
} else if errors.Is(err, user_model.ErrBlockedUser) {
ctx.Error(http.StatusForbidden, "NewIssue", err)
} else {
ctx.Error(http.StatusInternalServerError, "NewIssue", err)
}
ctx.Error(http.StatusInternalServerError, "NewIssue", err)
return
}
@@ -848,7 +852,11 @@ func EditIssue(ctx *context.APIContext) {
err = issue_service.UpdateAssignees(ctx, issue, oneAssignee, form.Assignees, ctx.Doer)
if err != nil {
ctx.Error(http.StatusInternalServerError, "UpdateAssignees", err)
if errors.Is(err, user_model.ErrBlockedUser) {
ctx.Error(http.StatusForbidden, "UpdateAssignees", err)
} else {
ctx.Error(http.StatusInternalServerError, "UpdateAssignees", err)
}
return
}
}

View File

@@ -382,6 +382,7 @@ func CreateIssueComment(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
// "423":
// "$ref": "#/responses/repoArchivedError"
form := web.GetForm(ctx).(*api.CreateIssueCommentOption)
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
if err != nil {
@@ -401,7 +402,11 @@ func CreateIssueComment(ctx *context.APIContext) {
comment, err := issue_service.CreateIssueComment(ctx, ctx.Doer, ctx.Repo.Repository, issue, form.Body, nil)
if err != nil {
ctx.Error(http.StatusInternalServerError, "CreateIssueComment", err)
if errors.Is(err, user_model.ErrBlockedUser) {
ctx.Error(http.StatusForbidden, "CreateIssueComment", err)
} else {
ctx.Error(http.StatusInternalServerError, "CreateIssueComment", err)
}
return
}
@@ -522,6 +527,7 @@ func EditIssueComment(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
// "423":
// "$ref": "#/responses/repoArchivedError"
form := web.GetForm(ctx).(*api.EditIssueCommentOption)
editIssueComment(ctx, *form)
}
@@ -610,7 +616,11 @@ func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption)
oldContent := comment.Content
comment.Content = form.Body
if err := issue_service.UpdateComment(ctx, comment, ctx.Doer, oldContent); err != nil {
ctx.Error(http.StatusInternalServerError, "UpdateComment", err)
if errors.Is(err, user_model.ErrBlockedUser) {
ctx.Error(http.StatusForbidden, "UpdateComment", err)
} else {
ctx.Error(http.StatusInternalServerError, "UpdateComment", err)
}
return
}

View File

@@ -4,10 +4,12 @@
package repo
import (
"errors"
"net/http"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
@@ -154,6 +156,8 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) {
// "$ref": "#/responses/Attachment"
// "400":
// "$ref": "#/responses/error"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/error"
// "423":
@@ -199,7 +203,11 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) {
}
if err = issue_service.UpdateComment(ctx, comment, ctx.Doer, comment.Content); err != nil {
ctx.ServerError("UpdateComment", err)
if errors.Is(err, user_model.ErrBlockedUser) {
ctx.Error(http.StatusForbidden, "UpdateComment", err)
} else {
ctx.ServerError("UpdateComment", err)
}
return
}

View File

@@ -8,11 +8,13 @@ import (
"net/http"
issues_model "code.gitea.io/gitea/models/issues"
user_model "code.gitea.io/gitea/models/user"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
issue_service "code.gitea.io/gitea/services/issue"
)
// GetIssueCommentReactions list reactions of a comment from an issue
@@ -218,9 +220,9 @@ func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOp
if isCreateType {
// PostIssueCommentReaction part
reaction, err := issues_model.CreateCommentReaction(ctx, ctx.Doer.ID, comment.Issue.ID, comment.ID, form.Reaction)
reaction, err := issue_service.CreateCommentReaction(ctx, ctx.Doer, comment, form.Reaction)
if err != nil {
if issues_model.IsErrForbiddenIssueReaction(err) {
if issues_model.IsErrForbiddenIssueReaction(err) || errors.Is(err, user_model.ErrBlockedUser) {
ctx.Error(http.StatusForbidden, err.Error(), err)
} else if issues_model.IsErrReactionAlreadyExist(err) {
ctx.JSON(http.StatusOK, api.Reaction{
@@ -434,9 +436,9 @@ func changeIssueReaction(ctx *context.APIContext, form api.EditReactionOption, i
if isCreateType {
// PostIssueReaction part
reaction, err := issues_model.CreateIssueReaction(ctx, ctx.Doer.ID, issue.ID, form.Reaction)
reaction, err := issue_service.CreateIssueReaction(ctx, ctx.Doer, issue, form.Reaction)
if err != nil {
if issues_model.IsErrForbiddenIssueReaction(err) {
if issues_model.IsErrForbiddenIssueReaction(err) || errors.Is(err, user_model.ErrBlockedUser) {
ctx.Error(http.StatusForbidden, err.Error(), err)
} else if issues_model.IsErrReactionAlreadyExist(err) {
ctx.JSON(http.StatusOK, api.Reaction{
@@ -445,7 +447,7 @@ func changeIssueReaction(ctx *context.APIContext, form api.EditReactionOption, i
Created: reaction.CreatedUnix.AsTime(),
})
} else {
ctx.Error(http.StatusInternalServerError, "CreateCommentReaction", err)
ctx.Error(http.StatusInternalServerError, "CreateIssueReaction", err)
}
return
}

View File

@@ -362,6 +362,8 @@ func CreatePullRequest(ctx *context.APIContext) {
// responses:
// "201":
// "$ref": "#/responses/PullRequest"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
// "409":
@@ -510,9 +512,11 @@ func CreatePullRequest(ctx *context.APIContext) {
if err := pull_service.NewPullRequest(ctx, repo, prIssue, labelIDs, []string{}, pr, assigneeIDs); err != nil {
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) {
ctx.Error(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err)
return
} else if errors.Is(err, user_model.ErrBlockedUser) {
ctx.Error(http.StatusForbidden, "BlockedUser", err)
} else {
ctx.Error(http.StatusInternalServerError, "NewPullRequest", err)
}
ctx.Error(http.StatusInternalServerError, "NewPullRequest", err)
return
}
@@ -630,6 +634,8 @@ func EditPullRequest(ctx *context.APIContext) {
if err != nil {
if user_model.IsErrUserNotExist(err) {
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
} else if errors.Is(err, user_model.ErrBlockedUser) {
ctx.Error(http.StatusForbidden, "UpdateAssignees", err)
} else {
ctx.Error(http.StatusInternalServerError, "UpdateAssignees", err)
}

View File

@@ -4,6 +4,7 @@
package repo
import (
"errors"
"fmt"
"net/http"
@@ -117,7 +118,11 @@ func Transfer(ctx *context.APIContext) {
return
}
ctx.InternalServerError(err)
if errors.Is(err, user_model.ErrBlockedUser) {
ctx.Error(http.StatusForbidden, "BlockedUser", err)
} else {
ctx.InternalServerError(err)
}
return
}