Fix showing issues in your repositories (#18916) (#19191)

- Make a restriction on which issues can be shown based on if you the user or team has write permission to the repository.
- Fixes a issue whereby you wouldn't see any associated issues with a specific team on a organization if you wasn't a member(fixed by zeroing the User{ID} in the options).
- Resolves #18913

Co-authored-by: Gusted <williamzijl7@hotmail.com>
This commit is contained in:
6543 2022-03-24 00:36:38 +01:00 committed by GitHub
parent e3d8e92bdc
commit 42229dc0b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 15 deletions

View File

@ -1551,6 +1551,7 @@ const (
FilterModeCreate FilterModeCreate
FilterModeMention FilterModeMention
FilterModeReviewRequested FilterModeReviewRequested
FilterModeYourRepositories
) )
func parseCountResult(results []map[string][]byte) int64 { func parseCountResult(results []map[string][]byte) int64 {
@ -1695,6 +1696,7 @@ type UserIssueStatsOptions struct {
IssueIDs []int64 IssueIDs []int64
IsArchived util.OptionalBool IsArchived util.OptionalBool
LabelIDs []int64 LabelIDs []int64
RepoCond builder.Cond
Org *Organization Org *Organization
Team *Team Team *Team
} }
@ -1712,6 +1714,9 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
if len(opts.IssueIDs) > 0 { if len(opts.IssueIDs) > 0 {
cond = cond.And(builder.In("issue.id", opts.IssueIDs)) cond = cond.And(builder.In("issue.id", opts.IssueIDs))
} }
if opts.RepoCond != nil {
cond = cond.And(opts.RepoCond)
}
if opts.UserID > 0 { if opts.UserID > 0 {
cond = cond.And(issuePullAccessibleRepoCond("issue.repo_id", opts.UserID, opts.Org, opts.Team, opts.IsPull)) cond = cond.And(issuePullAccessibleRepoCond("issue.repo_id", opts.UserID, opts.Org, opts.Team, opts.IsPull))
@ -1733,7 +1738,7 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
} }
switch opts.FilterMode { switch opts.FilterMode {
case FilterModeAll: case FilterModeAll, FilterModeYourRepositories:
stats.OpenCount, err = sess(cond). stats.OpenCount, err = sess(cond).
And("issue.is_closed = ?", false). And("issue.is_closed = ?", false).
Count(new(Issue)) Count(new(Issue))

View File

@ -197,7 +197,7 @@ func Milestones(ctx *context.Context) {
if issueReposQueryPattern.MatchString(reposQuery) { if issueReposQueryPattern.MatchString(reposQuery) {
// remove "[" and "]" from string // remove "[" and "]" from string
reposQuery = reposQuery[1 : len(reposQuery)-1] reposQuery = reposQuery[1 : len(reposQuery)-1]
//for each ID (delimiter ",") add to int to repoIDs // for each ID (delimiter ",") add to int to repoIDs
for _, rID := range strings.Split(reposQuery, ",") { for _, rID := range strings.Split(reposQuery, ",") {
// Ensure nonempty string entries // Ensure nonempty string entries
@ -350,7 +350,6 @@ func Issues(ctx *context.Context) {
var issueReposQueryPattern = regexp.MustCompile(`^\[\d+(,\d+)*,?\]$`) var issueReposQueryPattern = regexp.MustCompile(`^\[\d+(,\d+)*,?\]$`)
func buildIssueOverview(ctx *context.Context, unitType unit.Type) { func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
// ---------------------------------------------------- // ----------------------------------------------------
// Determine user; can be either user or organization. // Determine user; can be either user or organization.
// Return with NotFound or ServerError if unsuccessful. // Return with NotFound or ServerError if unsuccessful.
@ -364,7 +363,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
var ( var (
viewType string viewType string
sortType = ctx.FormString("sort") sortType = ctx.FormString("sort")
filterMode = models.FilterModeAll filterMode int
) )
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
@ -390,8 +389,10 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
filterMode = models.FilterModeMention filterMode = models.FilterModeMention
case "review_requested": case "review_requested":
filterMode = models.FilterModeReviewRequested filterMode = models.FilterModeReviewRequested
case "your_repositories": // filterMode already set to All case "your_repositories":
fallthrough
default: default:
filterMode = models.FilterModeYourRepositories
viewType = "your_repositories" viewType = "your_repositories"
} }
@ -421,6 +422,30 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
User: ctx.User, User: ctx.User,
} }
// Search all repositories which
//
// As user:
// - Owns the repository.
// - Have collaborator permissions in repository.
//
// As org:
// - Owns the repository.
//
// As team:
// - Team org's owns the repository.
// - Team has read permission to repository.
repoOpts := &models.SearchRepoOptions{
Actor: ctx.User,
OwnerID: ctx.User.ID,
Private: true,
AllPublic: false,
AllLimited: false,
}
if ctxUser.IsOrganization() && ctx.Org.Team != nil {
repoOpts.TeamID = ctx.Org.Team.ID
}
switch filterMode { switch filterMode {
case models.FilterModeAll: case models.FilterModeAll:
case models.FilterModeAssign: case models.FilterModeAssign:
@ -431,6 +456,19 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
opts.MentionedID = ctx.User.ID opts.MentionedID = ctx.User.ID
case models.FilterModeReviewRequested: case models.FilterModeReviewRequested:
opts.ReviewRequestedID = ctx.User.ID opts.ReviewRequestedID = ctx.User.ID
case models.FilterModeYourRepositories:
if ctxUser.IsOrganization() && ctx.Org.Team != nil {
// Fixes a issue whereby the user's ID would be used
// to check if it's in the team(which possible isn't the case).
opts.User = nil
}
userRepoIDs, _, err := models.SearchRepositoryIDs(repoOpts)
if err != nil {
ctx.ServerError("models.SearchRepositoryIDs: %v", err)
return
}
opts.RepoIDs = userRepoIDs
} }
// keyword holds the search term entered into the search field. // keyword holds the search term entered into the search field.
@ -562,8 +600,12 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
Org: org, Org: org,
Team: team, Team: team,
} }
if len(repoIDs) > 0 { if filterMode == models.FilterModeYourRepositories {
statsOpts.RepoIDs = repoIDs statsOpts.RepoCond = models.SearchRepositoryCondition(repoOpts)
}
// Detect when we only should search by team.
if opts.User == nil {
statsOpts.UserID = 0
} }
issueStats, err = models.GetUserIssueStats(statsOpts) issueStats, err = models.GetUserIssueStats(statsOpts)
if err != nil { if err != nil {
@ -586,8 +628,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
ctx.Data["IsShowClosed"] = isShowClosed ctx.Data["IsShowClosed"] = isShowClosed
ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] = ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] = issue_service.GetRefEndNamesAndURLs(issues, ctx.FormString("RepoLink"))
issue_service.GetRefEndNamesAndURLs(issues, ctx.FormString("RepoLink"))
ctx.Data["Issues"] = issues ctx.Data["Issues"] = issues
@ -661,7 +702,7 @@ func getRepoIDs(reposQuery string) []int64 {
var repoIDs []int64 var repoIDs []int64
// remove "[" and "]" from string // remove "[" and "]" from string
reposQuery = reposQuery[1 : len(reposQuery)-1] reposQuery = reposQuery[1 : len(reposQuery)-1]
//for each ID (delimiter ",") add to int to repoIDs // for each ID (delimiter ",") add to int to repoIDs
for _, rID := range strings.Split(reposQuery, ",") { for _, rID := range strings.Split(reposQuery, ",") {
// Ensure nonempty string entries // Ensure nonempty string entries
if rID != "" && rID != "0" { if rID != "" && rID != "0" {
@ -693,8 +734,8 @@ func issueIDsFromSearch(ctxUser *user_model.User, keyword string, opts *models.I
} }
func loadRepoByIDs(ctxUser *user_model.User, issueCountByRepo map[int64]int64, unitType unit.Type) (map[int64]*repo_model.Repository, error) { func loadRepoByIDs(ctxUser *user_model.User, issueCountByRepo map[int64]int64, unitType unit.Type) (map[int64]*repo_model.Repository, error) {
var totalRes = make(map[int64]*repo_model.Repository, len(issueCountByRepo)) totalRes := make(map[int64]*repo_model.Repository, len(issueCountByRepo))
var repoIDs = make([]int64, 0, 500) repoIDs := make([]int64, 0, 500)
for id := range issueCountByRepo { for id := range issueCountByRepo {
if id <= 0 { if id <= 0 {
continue continue
@ -745,7 +786,7 @@ func ShowGPGKeys(ctx *context.Context, uid int64) {
if err != nil { if err != nil {
if asymkey_model.IsErrGPGKeyImportNotExist(err) { if asymkey_model.IsErrGPGKeyImportNotExist(err) {
failedEntitiesID = append(failedEntitiesID, k.KeyID) failedEntitiesID = append(failedEntitiesID, k.KeyID)
continue //Skip previous import without backup of imported armored key continue // Skip previous import without backup of imported armored key
} }
ctx.ServerError("ShowGPGKeys", err) ctx.ServerError("ShowGPGKeys", err)
return return
@ -755,12 +796,12 @@ func ShowGPGKeys(ctx *context.Context, uid int64) {
var buf bytes.Buffer var buf bytes.Buffer
headers := make(map[string]string) headers := make(map[string]string)
if len(failedEntitiesID) > 0 { //If some key need re-import to be exported if len(failedEntitiesID) > 0 { // If some key need re-import to be exported
headers["Note"] = fmt.Sprintf("The keys with the following IDs couldn't be exported and need to be reuploaded %s", strings.Join(failedEntitiesID, ", ")) headers["Note"] = fmt.Sprintf("The keys with the following IDs couldn't be exported and need to be reuploaded %s", strings.Join(failedEntitiesID, ", "))
} }
writer, _ := armor.Encode(&buf, "PGP PUBLIC KEY BLOCK", headers) writer, _ := armor.Encode(&buf, "PGP PUBLIC KEY BLOCK", headers)
for _, e := range entities { for _, e := range entities {
err = e.Serialize(writer) //TODO find why key are exported with a different cipherTypeByte as original (should not be blocking but strange) err = e.Serialize(writer) // TODO find why key are exported with a different cipherTypeByte as original (should not be blocking but strange)
if err != nil { if err != nil {
ctx.ServerError("ShowGPGKeys", err) ctx.ServerError("ShowGPGKeys", err)
return return