1
1
mirror of https://github.com/go-gitea/gitea synced 2025-07-29 13:48:36 +00:00

Backport: Ignore mentions for users with no access (#8395) (#8484)

* Ignore mentions for users with no access

* Fix fmt
This commit is contained in:
guillep2k
2019-10-13 12:17:53 -03:00
committed by zeripath
parent 5c3863c319
commit d93d5d7906
5 changed files with 175 additions and 40 deletions

View File

@@ -1462,46 +1462,18 @@ func getParticipantsByIssueID(e Engine, issueID int64) ([]*User, error) {
return users, e.In("id", userIDs).Find(&users)
}
// UpdateIssueMentions extracts mentioned people from content and
// updates issue-user relations for them.
func UpdateIssueMentions(e Engine, issueID int64, mentions []string) error {
// UpdateIssueMentions updates issue-user relations for mentioned users.
func UpdateIssueMentions(e Engine, issueID int64, mentions []*User) error {
if len(mentions) == 0 {
return nil
}
for i := range mentions {
mentions[i] = strings.ToLower(mentions[i])
ids := make([]int64, len(mentions))
for i, u := range mentions {
ids[i] = u.ID
}
users := make([]*User, 0, len(mentions))
if err := e.In("lower_name", mentions).Asc("lower_name").Find(&users); err != nil {
return fmt.Errorf("find mentioned users: %v", err)
}
ids := make([]int64, 0, len(mentions))
for _, user := range users {
ids = append(ids, user.ID)
if !user.IsOrganization() || user.NumMembers == 0 {
continue
}
memberIDs := make([]int64, 0, user.NumMembers)
orgUsers, err := getOrgUsersByOrgID(e, user.ID)
if err != nil {
return fmt.Errorf("GetOrgUsersByOrgID [%d]: %v", user.ID, err)
}
for _, orgUser := range orgUsers {
memberIDs = append(memberIDs, orgUser.ID)
}
ids = append(ids, memberIDs...)
}
if err := UpdateIssueUsersByMentions(e, issueID, ids); err != nil {
return fmt.Errorf("UpdateIssueUsersByMentions: %v", err)
}
return nil
}
@@ -1854,3 +1826,120 @@ func (issue *Issue) updateClosedNum(e Engine) (err error) {
}
return
}
// ResolveMentionsByVisibility returns the users mentioned in an issue, removing those that
// don't have access to reading it. Teams are expanded into their users, but organizations are ignored.
func (issue *Issue) ResolveMentionsByVisibility(e Engine, doer *User, mentions []string) (users []*User, err error) {
if len(mentions) == 0 {
return
}
if err = issue.loadRepo(e); err != nil {
return
}
resolved := make(map[string]bool, 20)
names := make([]string, 0, 20)
resolved[doer.LowerName] = true
for _, name := range mentions {
name := strings.ToLower(name)
if _, ok := resolved[name]; ok {
continue
}
resolved[name] = false
names = append(names, name)
}
if err := issue.Repo.getOwner(e); err != nil {
return nil, err
}
if issue.Repo.Owner.IsOrganization() {
// Since there can be users with names that match the name of a team,
// if the team exists and can read the issue, the team takes precedence.
teams := make([]*Team, 0, len(names))
if err := e.
Join("INNER", "team_repo", "team_repo.team_id = team.id").
Where("team_repo.repo_id=?", issue.Repo.ID).
In("team.lower_name", names).
Find(&teams); err != nil {
return nil, fmt.Errorf("find mentioned teams: %v", err)
}
if len(teams) != 0 {
checked := make([]int64, 0, len(teams))
unittype := UnitTypeIssues
if issue.IsPull {
unittype = UnitTypePullRequests
}
for _, team := range teams {
if team.Authorize >= AccessModeOwner {
checked = append(checked, team.ID)
resolved[team.LowerName] = true
continue
}
has, err := e.Get(&TeamUnit{OrgID: issue.Repo.Owner.ID, TeamID: team.ID, Type: unittype})
if err != nil {
return nil, fmt.Errorf("get team units (%d): %v", team.ID, err)
}
if has {
checked = append(checked, team.ID)
resolved[team.LowerName] = true
}
}
if len(checked) != 0 {
teamusers := make([]*User, 0, 20)
if err := e.
Join("INNER", "team_user", "team_user.uid = `user`.id").
In("`team_user`.team_id", checked).
And("`user`.is_active = ?", true).
And("`user`.prohibit_login = ?", false).
Find(&teamusers); err != nil {
return nil, fmt.Errorf("get teams users: %v", err)
}
if len(teamusers) > 0 {
users = make([]*User, 0, len(teamusers))
for _, user := range teamusers {
if already, ok := resolved[user.LowerName]; !ok || !already {
users = append(users, user)
resolved[user.LowerName] = true
}
}
}
}
}
// Remove names already in the list to avoid querying the database if pending names remain
names = make([]string, 0, len(resolved))
for name, already := range resolved {
if !already {
names = append(names, name)
}
}
if len(names) == 0 {
return
}
}
unchecked := make([]*User, 0, len(names))
if err := e.
Where("`user`.is_active = ?", true).
And("`user`.prohibit_login = ?", false).
In("`user`.lower_name", names).
Find(&unchecked); err != nil {
return nil, fmt.Errorf("find mentioned users: %v", err)
}
for _, user := range unchecked {
if already := resolved[user.LowerName]; already || user.IsOrganization() {
continue
}
// Normal users must have read access to the referencing issue
perm, err := getUserRepoPermission(e, issue.Repo, user)
if err != nil {
return nil, fmt.Errorf("getUserRepoPermission [%d]: %v", user.ID, err)
}
if !perm.CanReadIssuesOrPulls(issue.IsPull) {
continue
}
users = append(users, user)
}
return
}