mirror of
https://github.com/go-gitea/gitea.git
synced 2025-01-31 09:41:28 +02:00
Support public code/issue access for private repositories (#33127)
Close #8649, close #639 (will add "anonymous access" in following PRs)
This commit is contained in:
parent
ecd463c2f1
commit
a98a836e76
@ -175,10 +175,14 @@ func (p *Permission) LogString() string {
|
||||
return fmt.Sprintf(format, args...)
|
||||
}
|
||||
|
||||
func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
|
||||
func finalProcessRepoUnitPermission(user *user_model.User, perm *Permission) {
|
||||
if user == nil || user.ID <= 0 {
|
||||
// for anonymous access, it could be:
|
||||
// AccessMode is None or Read, units has repo units, unitModes is nil
|
||||
return
|
||||
}
|
||||
|
||||
// apply everyone access permissions
|
||||
for _, u := range perm.units {
|
||||
if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.everyoneAccessMode[u.Type] {
|
||||
if perm.everyoneAccessMode == nil {
|
||||
@ -187,17 +191,40 @@ func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
|
||||
perm.everyoneAccessMode[u.Type] = u.EveryoneAccessMode
|
||||
}
|
||||
}
|
||||
|
||||
if perm.unitsMode == nil {
|
||||
// if unitsMode is not set, then it means that the default p.AccessMode applies to all units
|
||||
return
|
||||
}
|
||||
|
||||
// remove no permission units
|
||||
origPermUnits := perm.units
|
||||
perm.units = make([]*repo_model.RepoUnit, 0, len(perm.units))
|
||||
for _, u := range origPermUnits {
|
||||
shouldKeep := false
|
||||
for t := range perm.unitsMode {
|
||||
if shouldKeep = u.Type == t; shouldKeep {
|
||||
break
|
||||
}
|
||||
}
|
||||
for t := range perm.everyoneAccessMode {
|
||||
if shouldKeep = shouldKeep || u.Type == t; shouldKeep {
|
||||
break
|
||||
}
|
||||
}
|
||||
if shouldKeep {
|
||||
perm.units = append(perm.units, u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserRepoPermission returns the user permissions to the repository
|
||||
func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (perm Permission, err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
applyEveryoneRepoPermission(user, &perm)
|
||||
}
|
||||
if log.IsTrace() {
|
||||
log.Trace("Permission Loaded for user %-v in repo %-v, permissions: %-+v", user, repo, perm)
|
||||
finalProcessRepoUnitPermission(user, &perm)
|
||||
}
|
||||
log.Trace("Permission Loaded for user %-v in repo %-v, permissions: %-+v", user, repo, perm)
|
||||
}()
|
||||
|
||||
if err = repo.LoadUnits(ctx); err != nil {
|
||||
@ -294,16 +321,6 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
|
||||
}
|
||||
}
|
||||
|
||||
// remove no permission units
|
||||
perm.units = make([]*repo_model.RepoUnit, 0, len(repo.Units))
|
||||
for t := range perm.unitsMode {
|
||||
for _, u := range repo.Units {
|
||||
if u.Type == t {
|
||||
perm.units = append(perm.units, u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return perm, err
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
|
||||
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
|
||||
},
|
||||
}
|
||||
applyEveryoneRepoPermission(nil, &perm)
|
||||
finalProcessRepoUnitPermission(nil, &perm)
|
||||
assert.False(t, perm.CanRead(unit.TypeWiki))
|
||||
|
||||
perm = Permission{
|
||||
@ -59,7 +59,7 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
|
||||
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
|
||||
},
|
||||
}
|
||||
applyEveryoneRepoPermission(&user_model.User{ID: 0}, &perm)
|
||||
finalProcessRepoUnitPermission(&user_model.User{ID: 0}, &perm)
|
||||
assert.False(t, perm.CanRead(unit.TypeWiki))
|
||||
|
||||
perm = Permission{
|
||||
@ -68,7 +68,7 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
|
||||
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
|
||||
},
|
||||
}
|
||||
applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
|
||||
finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
|
||||
assert.True(t, perm.CanRead(unit.TypeWiki))
|
||||
|
||||
perm = Permission{
|
||||
@ -77,20 +77,22 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
|
||||
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
|
||||
},
|
||||
}
|
||||
applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
|
||||
finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
|
||||
// it should work the same as "EveryoneAccessMode: none" because the default AccessMode should be applied to units
|
||||
assert.True(t, perm.CanWrite(unit.TypeWiki))
|
||||
|
||||
perm = Permission{
|
||||
units: []*repo_model.RepoUnit{
|
||||
{Type: unit.TypeCode}, // will be removed
|
||||
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
|
||||
},
|
||||
unitsMode: map[unit.Type]perm_model.AccessMode{
|
||||
unit.TypeWiki: perm_model.AccessModeWrite,
|
||||
},
|
||||
}
|
||||
applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
|
||||
finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
|
||||
assert.True(t, perm.CanWrite(unit.TypeWiki))
|
||||
assert.Len(t, perm.units, 1)
|
||||
}
|
||||
|
||||
func TestUnitAccessMode(t *testing.T) {
|
||||
|
@ -2158,7 +2158,7 @@ settings.advanced_settings = Advanced Settings
|
||||
settings.wiki_desc = Enable Repository Wiki
|
||||
settings.use_internal_wiki = Use Built-In Wiki
|
||||
settings.default_wiki_branch_name = Default Wiki Branch Name
|
||||
settings.default_wiki_everyone_access = Default Access Permission for signed-in users:
|
||||
settings.default_permission_everyone_access = Default access permission for all signed-in users:
|
||||
settings.failed_to_change_default_wiki_branch = Failed to change the default wiki branch.
|
||||
settings.use_external_wiki = Use External Wiki
|
||||
settings.external_wiki_url = External Wiki URL
|
||||
|
@ -26,13 +26,9 @@ type userSearchResponse struct {
|
||||
Results []*userSearchInfo `json:"results"`
|
||||
}
|
||||
|
||||
// IssuePosters get posters for current repo's issues/pull requests
|
||||
func IssuePosters(ctx *context.Context) {
|
||||
issuePosters(ctx, false)
|
||||
}
|
||||
|
||||
func PullPosters(ctx *context.Context) {
|
||||
issuePosters(ctx, true)
|
||||
func IssuePullPosters(ctx *context.Context) {
|
||||
isPullList := ctx.PathParam("type") == "pulls"
|
||||
issuePosters(ctx, isPullList)
|
||||
}
|
||||
|
||||
func issuePosters(ctx *context.Context, isPullList bool) {
|
||||
|
@ -49,6 +49,15 @@ const (
|
||||
tplDeployKeys templates.TplName = "repo/settings/deploy_keys"
|
||||
)
|
||||
|
||||
func parseEveryoneAccessMode(permission string, allowed ...perm.AccessMode) perm.AccessMode {
|
||||
// if site admin forces repositories to be private, then do not allow any other access mode,
|
||||
// otherwise the "force private" setting would be bypassed
|
||||
if setting.Repository.ForcePrivate {
|
||||
return perm.AccessModeNone
|
||||
}
|
||||
return perm.ParseAccessMode(permission, allowed...)
|
||||
}
|
||||
|
||||
// SettingsCtxData is a middleware that sets all the general context data for the
|
||||
// settings template.
|
||||
func SettingsCtxData(ctx *context.Context) {
|
||||
@ -447,8 +456,9 @@ func SettingsPost(ctx *context.Context) {
|
||||
|
||||
if form.EnableCode && !unit_model.TypeCode.UnitGlobalDisabled() {
|
||||
units = append(units, repo_model.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: unit_model.TypeCode,
|
||||
RepoID: repo.ID,
|
||||
Type: unit_model.TypeCode,
|
||||
EveryoneAccessMode: parseEveryoneAccessMode(form.DefaultCodeEveryoneAccess, perm.AccessModeNone, perm.AccessModeRead),
|
||||
})
|
||||
} else if !unit_model.TypeCode.UnitGlobalDisabled() {
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeCode)
|
||||
@ -474,7 +484,7 @@ func SettingsPost(ctx *context.Context) {
|
||||
RepoID: repo.ID,
|
||||
Type: unit_model.TypeWiki,
|
||||
Config: new(repo_model.UnitConfig),
|
||||
EveryoneAccessMode: perm.ParseAccessMode(form.DefaultWikiEveryoneAccess, perm.AccessModeNone, perm.AccessModeRead, perm.AccessModeWrite),
|
||||
EveryoneAccessMode: parseEveryoneAccessMode(form.DefaultWikiEveryoneAccess, perm.AccessModeNone, perm.AccessModeRead, perm.AccessModeWrite),
|
||||
})
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
|
||||
} else {
|
||||
@ -524,6 +534,7 @@ func SettingsPost(ctx *context.Context) {
|
||||
AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime,
|
||||
EnableDependencies: form.EnableIssueDependencies,
|
||||
},
|
||||
EveryoneAccessMode: parseEveryoneAccessMode(form.DefaultIssuesEveryoneAccess, perm.AccessModeNone, perm.AccessModeRead),
|
||||
})
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
|
||||
} else {
|
||||
|
@ -101,7 +101,7 @@ func buildAuthGroup() *auth_service.Group {
|
||||
group.Add(&auth_service.Basic{}) // FIXME: this should be removed and only applied in download and git/lfs routers
|
||||
|
||||
if setting.Service.EnableReverseProxyAuth {
|
||||
group.Add(&auth_service.ReverseProxy{}) // reverseproxy should before Session, otherwise the header will be ignored if user has login
|
||||
group.Add(&auth_service.ReverseProxy{}) // reverse-proxy should before Session, otherwise the header will be ignored if user has login
|
||||
}
|
||||
group.Add(&auth_service.Session{})
|
||||
|
||||
@ -816,21 +816,24 @@ func registerRoutes(m *web.Router) {
|
||||
m.Post("/{username}", reqSignIn, context.UserAssignmentWeb(), user.Action)
|
||||
|
||||
reqRepoAdmin := context.RequireRepoAdmin()
|
||||
reqRepoCodeWriter := context.RequireRepoWriter(unit.TypeCode)
|
||||
reqRepoCodeWriter := context.RequireUnitWriter(unit.TypeCode)
|
||||
canEnableEditor := context.CanEnableEditor()
|
||||
reqRepoCodeReader := context.RequireRepoReader(unit.TypeCode)
|
||||
reqRepoReleaseWriter := context.RequireRepoWriter(unit.TypeReleases)
|
||||
reqRepoReleaseReader := context.RequireRepoReader(unit.TypeReleases)
|
||||
reqRepoWikiReader := context.RequireRepoReader(unit.TypeWiki)
|
||||
reqRepoWikiWriter := context.RequireRepoWriter(unit.TypeWiki)
|
||||
reqRepoIssueReader := context.RequireRepoReader(unit.TypeIssues)
|
||||
reqRepoPullsReader := context.RequireRepoReader(unit.TypePullRequests)
|
||||
reqRepoIssuesOrPullsWriter := context.RequireRepoWriterOr(unit.TypeIssues, unit.TypePullRequests)
|
||||
reqRepoIssuesOrPullsReader := context.RequireRepoReaderOr(unit.TypeIssues, unit.TypePullRequests)
|
||||
reqRepoProjectsReader := context.RequireRepoReader(unit.TypeProjects)
|
||||
reqRepoProjectsWriter := context.RequireRepoWriter(unit.TypeProjects)
|
||||
reqRepoActionsReader := context.RequireRepoReader(unit.TypeActions)
|
||||
reqRepoActionsWriter := context.RequireRepoWriter(unit.TypeActions)
|
||||
reqRepoReleaseWriter := context.RequireUnitWriter(unit.TypeReleases)
|
||||
reqRepoReleaseReader := context.RequireUnitReader(unit.TypeReleases)
|
||||
reqRepoIssuesOrPullsWriter := context.RequireUnitWriter(unit.TypeIssues, unit.TypePullRequests)
|
||||
reqRepoIssuesOrPullsReader := context.RequireUnitReader(unit.TypeIssues, unit.TypePullRequests)
|
||||
reqRepoProjectsReader := context.RequireUnitReader(unit.TypeProjects)
|
||||
reqRepoProjectsWriter := context.RequireUnitWriter(unit.TypeProjects)
|
||||
reqRepoActionsReader := context.RequireUnitReader(unit.TypeActions)
|
||||
reqRepoActionsWriter := context.RequireUnitWriter(unit.TypeActions)
|
||||
|
||||
// the legacy names "reqRepoXxx" should be renamed to the correct name "reqUnitXxx", these permissions are for units, not repos
|
||||
reqUnitsWithMarkdown := context.RequireUnitReader(unit.TypeCode, unit.TypeIssues, unit.TypePullRequests, unit.TypeReleases, unit.TypeWiki)
|
||||
reqUnitCodeReader := context.RequireUnitReader(unit.TypeCode)
|
||||
reqUnitIssuesReader := context.RequireUnitReader(unit.TypeIssues)
|
||||
reqUnitPullsReader := context.RequireUnitReader(unit.TypePullRequests)
|
||||
reqUnitWikiReader := context.RequireUnitReader(unit.TypeWiki)
|
||||
reqUnitWikiWriter := context.RequireUnitWriter(unit.TypeWiki)
|
||||
|
||||
reqPackageAccess := func(accessMode perm.AccessMode) func(ctx *context.Context) {
|
||||
return func(ctx *context.Context) {
|
||||
@ -1053,7 +1056,7 @@ func registerRoutes(m *web.Router) {
|
||||
m.Group("/migrate", func() {
|
||||
m.Get("/status", repo.MigrateStatus)
|
||||
})
|
||||
}, optSignIn, context.RepoAssignment, reqRepoCodeReader)
|
||||
}, optSignIn, context.RepoAssignment, reqUnitCodeReader)
|
||||
// end "/{username}/{reponame}/-": migrate
|
||||
|
||||
m.Group("/{username}/{reponame}/settings", func() {
|
||||
@ -1151,8 +1154,7 @@ func registerRoutes(m *web.Router) {
|
||||
// user/org home, including rss feeds
|
||||
m.Get("/{username}/{reponame}", optSignIn, context.RepoAssignment, context.RepoRef(), repo.SetEditorconfigIfExists, repo.Home)
|
||||
|
||||
// TODO: maybe it should relax the permission to allow "any access"
|
||||
m.Post("/{username}/{reponame}/markup", optSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypeCode, unit.TypeIssues, unit.TypePullRequests, unit.TypeReleases, unit.TypeWiki), web.Bind(structs.MarkupOption{}), misc.Markup)
|
||||
m.Post("/{username}/{reponame}/markup", optSignIn, context.RepoAssignment, reqUnitsWithMarkdown, web.Bind(structs.MarkupOption{}), misc.Markup)
|
||||
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
m.Get("/find/*", repo.FindFiles)
|
||||
@ -1164,41 +1166,40 @@ func registerRoutes(m *web.Router) {
|
||||
m.Get("/compare", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff)
|
||||
m.Combo("/compare/*", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists).
|
||||
Get(repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff).
|
||||
Post(reqSignIn, context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, web.Bind(forms.CreateIssueForm{}), repo.SetWhitespaceBehavior, repo.CompareAndPullRequestPost)
|
||||
}, optSignIn, context.RepoAssignment, reqRepoCodeReader)
|
||||
Post(reqSignIn, context.RepoMustNotBeArchived(), reqUnitPullsReader, repo.MustAllowPulls, web.Bind(forms.CreateIssueForm{}), repo.SetWhitespaceBehavior, repo.CompareAndPullRequestPost)
|
||||
}, optSignIn, context.RepoAssignment, reqUnitCodeReader)
|
||||
// end "/{username}/{reponame}": repo code: find, compare, list
|
||||
|
||||
addIssuesPullsViewRoutes := func() {
|
||||
// for /{username}/{reponame}/issues" or "/{username}/{reponame}/pulls"
|
||||
m.Get("/posters", repo.IssuePullPosters)
|
||||
m.Group("/{index}", func() {
|
||||
m.Get("/info", repo.GetIssueInfo)
|
||||
m.Get("/attachments", repo.GetIssueAttachments)
|
||||
m.Get("/attachments/{uuid}", repo.GetAttachment)
|
||||
m.Group("/content-history", func() {
|
||||
m.Get("/overview", repo.GetContentHistoryOverview)
|
||||
m.Get("/list", repo.GetContentHistoryList)
|
||||
m.Get("/detail", repo.GetContentHistoryDetail)
|
||||
})
|
||||
})
|
||||
}
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
m.Get("/issues/posters", repo.IssuePosters) // it can't use {type:issues|pulls} because it would conflict with other routes like "/pulls/{index}"
|
||||
m.Get("/pulls/posters", repo.PullPosters)
|
||||
m.Get("/comments/{id}/attachments", repo.GetCommentAttachments)
|
||||
m.Get("/labels", repo.RetrieveLabelsForList, repo.Labels)
|
||||
m.Get("/milestones", repo.Milestones)
|
||||
m.Get("/milestone/{id}", context.RepoRef(), repo.MilestoneIssuesAndPulls)
|
||||
m.Group("/{type:issues|pulls}", func() {
|
||||
m.Group("/{index}", func() {
|
||||
m.Get("/info", repo.GetIssueInfo)
|
||||
m.Get("/attachments", repo.GetIssueAttachments)
|
||||
m.Get("/attachments/{uuid}", repo.GetAttachment)
|
||||
m.Group("/content-history", func() {
|
||||
m.Get("/overview", repo.GetContentHistoryOverview)
|
||||
m.Get("/list", repo.GetContentHistoryList)
|
||||
m.Get("/detail", repo.GetContentHistoryDetail)
|
||||
})
|
||||
})
|
||||
}, context.RepoRef())
|
||||
m.Get("/issues/suggestions", repo.IssueSuggestions)
|
||||
}, optSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader)
|
||||
}, optSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader) // issue/pull attachments, labels, milestones
|
||||
|
||||
m.Group("/{username}/{reponame}/{type:issues}", addIssuesPullsViewRoutes, optSignIn, context.RepoAssignment, reqUnitIssuesReader)
|
||||
m.Group("/{username}/{reponame}/{type:pulls}", addIssuesPullsViewRoutes, optSignIn, context.RepoAssignment, reqUnitPullsReader)
|
||||
// end "/{username}/{reponame}": view milestone, label, issue, pull, etc
|
||||
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
m.Group("/{type:issues|pulls}", func() {
|
||||
m.Get("", repo.Issues)
|
||||
m.Group("/{index}", func() {
|
||||
m.Get("", repo.ViewIssue)
|
||||
})
|
||||
})
|
||||
}, optSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypeIssues, unit.TypePullRequests, unit.TypeExternalTracker))
|
||||
m.Group("/{username}/{reponame}/{type:issues}", func() {
|
||||
m.Get("", repo.Issues)
|
||||
m.Get("/{index}", repo.ViewIssue)
|
||||
}, optSignIn, context.RepoAssignment, context.RequireUnitReader(unit.TypeIssues, unit.TypeExternalTracker))
|
||||
// end "/{username}/{reponame}": issue/pull list, issue/pull view, external tracker
|
||||
|
||||
m.Group("/{username}/{reponame}", func() { // edit issues, pulls, labels, milestones, etc
|
||||
@ -1209,11 +1210,10 @@ func registerRoutes(m *web.Router) {
|
||||
m.Get("/choose", context.RepoRef(), repo.NewIssueChooseTemplate)
|
||||
})
|
||||
m.Get("/search", repo.SearchRepoIssuesJSON)
|
||||
}, context.RepoMustNotBeArchived(), reqRepoIssueReader)
|
||||
}, reqUnitIssuesReader)
|
||||
|
||||
// FIXME: should use different URLs but mostly same logic for comments of issue and pull request.
|
||||
// So they can apply their own enable/disable logic on routers.
|
||||
m.Group("/{type:issues|pulls}", func() {
|
||||
addIssuesPullsRoutes := func() {
|
||||
// for "/{username}/{reponame}/issues" or "/{username}/{reponame}/pulls"
|
||||
m.Group("/{index}", func() {
|
||||
m.Post("/title", repo.UpdateIssueTitle)
|
||||
m.Post("/content", repo.UpdateIssueContent)
|
||||
@ -1240,39 +1240,37 @@ func registerRoutes(m *web.Router) {
|
||||
m.Post("/lock", reqRepoIssuesOrPullsWriter, web.Bind(forms.IssueLockForm{}), repo.LockIssue)
|
||||
m.Post("/unlock", reqRepoIssuesOrPullsWriter, repo.UnlockIssue)
|
||||
m.Post("/delete", reqRepoAdmin, repo.DeleteIssue)
|
||||
}, context.RepoMustNotBeArchived())
|
||||
|
||||
m.Group("/{index}", func() {
|
||||
m.Post("/content-history/soft-delete", repo.SoftDeleteContentHistory)
|
||||
})
|
||||
|
||||
m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel)
|
||||
m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone)
|
||||
m.Post("/projects", reqRepoIssuesOrPullsWriter, reqRepoProjectsReader, repo.UpdateIssueProject)
|
||||
m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee)
|
||||
m.Post("/request_review", repo.UpdatePullReviewRequest)
|
||||
m.Post("/dismiss_review", reqRepoAdmin, web.Bind(forms.DismissReviewForm{}), repo.DismissReview)
|
||||
m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus)
|
||||
m.Post("/delete", reqRepoAdmin, repo.BatchDeleteIssues)
|
||||
m.Post("/resolve_conversation", repo.SetShowOutdatedComments, repo.UpdateResolveConversation)
|
||||
m.Post("/attachments", repo.UploadIssueAttachment)
|
||||
m.Post("/attachments/remove", repo.DeleteAttachment)
|
||||
|
||||
m.Post("/projects", reqRepoIssuesOrPullsWriter, reqRepoProjectsReader, repo.UpdateIssueProject)
|
||||
m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee)
|
||||
m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus)
|
||||
m.Post("/delete", reqRepoAdmin, repo.BatchDeleteIssues)
|
||||
m.Delete("/unpin/{index}", reqRepoAdmin, repo.IssueUnpin)
|
||||
m.Post("/move_pin", reqRepoAdmin, repo.IssuePinMove)
|
||||
}, context.RepoMustNotBeArchived())
|
||||
}
|
||||
m.Group("/{type:issues}", addIssuesPullsRoutes, reqUnitIssuesReader, context.RepoMustNotBeArchived())
|
||||
m.Group("/{type:pulls}", addIssuesPullsRoutes, reqUnitPullsReader, context.RepoMustNotBeArchived())
|
||||
|
||||
m.Group("/comments/{id}", func() {
|
||||
m.Post("", repo.UpdateCommentContent)
|
||||
m.Post("/delete", repo.DeleteComment)
|
||||
m.Post("/reactions/{action}", web.Bind(forms.ReactionForm{}), repo.ChangeCommentReaction)
|
||||
}, context.RepoMustNotBeArchived())
|
||||
}, reqRepoIssuesOrPullsReader) // edit issue/pull comment
|
||||
|
||||
m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel)
|
||||
m.Group("/labels", func() {
|
||||
m.Post("/new", web.Bind(forms.CreateLabelForm{}), repo.NewLabel)
|
||||
m.Post("/edit", web.Bind(forms.CreateLabelForm{}), repo.UpdateLabel)
|
||||
m.Post("/delete", repo.DeleteLabel)
|
||||
m.Post("/initialize", web.Bind(forms.InitializeLabelsForm{}), repo.InitializeLabels)
|
||||
}, context.RepoMustNotBeArchived(), reqRepoIssuesOrPullsWriter, context.RepoRef())
|
||||
}, reqRepoIssuesOrPullsWriter, context.RepoRef())
|
||||
|
||||
m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone)
|
||||
m.Group("/milestones", func() {
|
||||
m.Combo("/new").Get(repo.NewMilestone).
|
||||
Post(web.Bind(forms.CreateMilestoneForm{}), repo.NewMilestonePost)
|
||||
@ -1280,11 +1278,15 @@ func registerRoutes(m *web.Router) {
|
||||
m.Post("/{id}/edit", web.Bind(forms.CreateMilestoneForm{}), repo.EditMilestonePost)
|
||||
m.Post("/{id}/{action}", repo.ChangeMilestoneStatus)
|
||||
m.Post("/delete", repo.DeleteMilestone)
|
||||
}, context.RepoMustNotBeArchived(), reqRepoIssuesOrPullsWriter, context.RepoRef())
|
||||
m.Group("/pull", func() {
|
||||
m.Post("/{index}/target_branch", repo.UpdatePullRequestTarget)
|
||||
}, context.RepoMustNotBeArchived())
|
||||
}, reqSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader)
|
||||
}, reqRepoIssuesOrPullsWriter, context.RepoRef())
|
||||
|
||||
m.Group("", func() {
|
||||
m.Post("/request_review", repo.UpdatePullReviewRequest)
|
||||
m.Post("/dismiss_review", reqRepoAdmin, web.Bind(forms.DismissReviewForm{}), repo.DismissReview)
|
||||
m.Post("/resolve_conversation", repo.SetShowOutdatedComments, repo.UpdateResolveConversation)
|
||||
m.Post("/pull/{index}/target_branch", repo.UpdatePullRequestTarget)
|
||||
}, reqUnitPullsReader)
|
||||
}, reqSignIn, context.RepoAssignment, context.RepoMustNotBeArchived())
|
||||
// end "/{username}/{reponame}": create or edit issues, pulls, labels, milestones
|
||||
|
||||
m.Group("/{username}/{reponame}", func() { // repo code
|
||||
@ -1324,7 +1326,7 @@ func registerRoutes(m *web.Router) {
|
||||
}, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty)
|
||||
|
||||
m.Combo("/fork").Get(repo.Fork).Post(web.Bind(forms.CreateRepoForm{}), repo.ForkPost)
|
||||
}, reqSignIn, context.RepoAssignment, reqRepoCodeReader)
|
||||
}, reqSignIn, context.RepoAssignment, reqUnitCodeReader)
|
||||
// end "/{username}/{reponame}": repo code
|
||||
|
||||
m.Group("/{username}/{reponame}", func() { // repo tags
|
||||
@ -1337,7 +1339,7 @@ func registerRoutes(m *web.Router) {
|
||||
repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag, context.RepoRefByTypeOptions{IgnoreNotExistErr: true}))
|
||||
m.Post("/tags/delete", repo.DeleteTag, reqSignIn,
|
||||
repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef())
|
||||
}, optSignIn, context.RepoAssignment, reqRepoCodeReader)
|
||||
}, optSignIn, context.RepoAssignment, reqUnitCodeReader)
|
||||
// end "/{username}/{reponame}": repo tags
|
||||
|
||||
m.Group("/{username}/{reponame}", func() { // repo releases
|
||||
@ -1440,43 +1442,48 @@ func registerRoutes(m *web.Router) {
|
||||
m.Group("/{username}/{reponame}/wiki", func() {
|
||||
m.Combo("").
|
||||
Get(repo.Wiki).
|
||||
Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
|
||||
Post(context.RepoMustNotBeArchived(), reqSignIn, reqUnitWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
|
||||
m.Combo("/*").
|
||||
Get(repo.Wiki).
|
||||
Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
|
||||
Post(context.RepoMustNotBeArchived(), reqSignIn, reqUnitWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
|
||||
m.Get("/blob_excerpt/{sha}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ExcerptBlob)
|
||||
m.Get("/commit/{sha:[a-f0-9]{7,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
|
||||
m.Get("/commit/{sha:[a-f0-9]{7,64}}.{ext:patch|diff}", repo.RawDiff)
|
||||
m.Get("/raw/*", repo.WikiRaw)
|
||||
}, optSignIn, context.RepoAssignment, repo.MustEnableWiki, reqRepoWikiReader, func(ctx *context.Context) {
|
||||
}, optSignIn, context.RepoAssignment, repo.MustEnableWiki, reqUnitWikiReader, func(ctx *context.Context) {
|
||||
ctx.Data["PageIsWiki"] = true
|
||||
ctx.Data["CloneButtonOriginLink"] = ctx.Repo.Repository.WikiCloneLink(ctx, ctx.Doer)
|
||||
})
|
||||
// end "/{username}/{reponame}/wiki"
|
||||
|
||||
m.Group("/{username}/{reponame}/activity", func() {
|
||||
// activity has its own permission checks
|
||||
m.Get("", repo.Activity)
|
||||
m.Get("/{period}", repo.Activity)
|
||||
m.Group("/contributors", func() {
|
||||
m.Get("", repo.Contributors)
|
||||
m.Get("/data", repo.ContributorsData)
|
||||
})
|
||||
m.Group("/code-frequency", func() {
|
||||
m.Get("", repo.CodeFrequency)
|
||||
m.Get("/data", repo.CodeFrequencyData)
|
||||
})
|
||||
m.Group("/recent-commits", func() {
|
||||
m.Get("", repo.RecentCommits)
|
||||
m.Get("/data", repo.RecentCommitsData)
|
||||
})
|
||||
|
||||
m.Group("", func() {
|
||||
m.Group("/contributors", func() {
|
||||
m.Get("", repo.Contributors)
|
||||
m.Get("/data", repo.ContributorsData)
|
||||
})
|
||||
m.Group("/code-frequency", func() {
|
||||
m.Get("", repo.CodeFrequency)
|
||||
m.Get("/data", repo.CodeFrequencyData)
|
||||
})
|
||||
m.Group("/recent-commits", func() {
|
||||
m.Get("", repo.RecentCommits)
|
||||
m.Get("/data", repo.RecentCommitsData)
|
||||
})
|
||||
}, reqUnitCodeReader)
|
||||
},
|
||||
optSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases),
|
||||
context.RepoRef(), repo.MustBeNotEmpty,
|
||||
optSignIn, context.RepoAssignment, repo.MustBeNotEmpty,
|
||||
context.RequireUnitReader(unit.TypeCode, unit.TypeIssues, unit.TypePullRequests, unit.TypeReleases),
|
||||
)
|
||||
// end "/{username}/{reponame}/activity"
|
||||
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
m.Group("/pulls/{index}", func() {
|
||||
m.Get("/{type:pulls}", repo.Issues)
|
||||
m.Group("/{type:pulls}/{index}", func() {
|
||||
m.Get("", repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewIssue)
|
||||
m.Get(".diff", repo.DownloadPullDiff)
|
||||
m.Get(".patch", repo.DownloadPullPatch)
|
||||
@ -1501,7 +1508,7 @@ func registerRoutes(m *web.Router) {
|
||||
}, context.RepoMustNotBeArchived())
|
||||
})
|
||||
})
|
||||
}, optSignIn, context.RepoAssignment, repo.MustAllowPulls, reqRepoPullsReader)
|
||||
}, optSignIn, context.RepoAssignment, repo.MustAllowPulls, reqUnitPullsReader)
|
||||
// end "/{username}/{reponame}/pulls/{index}": repo pull request
|
||||
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
@ -1582,13 +1589,13 @@ func registerRoutes(m *web.Router) {
|
||||
m.Get("/forks", context.RepoRef(), repo.Forks)
|
||||
m.Get("/commit/{sha:([a-f0-9]{7,64})}.{ext:patch|diff}", repo.MustBeNotEmpty, repo.RawDiff)
|
||||
m.Post("/lastcommit/*", context.RepoRefByType(context.RepoRefCommit), repo.LastCommit)
|
||||
}, optSignIn, context.RepoAssignment, reqRepoCodeReader)
|
||||
}, optSignIn, context.RepoAssignment, reqUnitCodeReader)
|
||||
// end "/{username}/{reponame}": repo code
|
||||
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
m.Get("/stars", repo.Stars)
|
||||
m.Get("/watchers", repo.Watchers)
|
||||
m.Get("/search", reqRepoCodeReader, repo.Search)
|
||||
m.Get("/search", reqUnitCodeReader, repo.Search)
|
||||
m.Post("/action/{action}", reqSignIn, repo.Action)
|
||||
}, optSignIn, context.RepoAssignment, context.RepoRef())
|
||||
|
||||
|
@ -9,24 +9,13 @@ import (
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
// RequireRepoAdmin returns a middleware for requiring repository admin permission
|
||||
func RequireRepoAdmin() func(ctx *Context) {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.IsSigned || !ctx.Repo.IsAdmin() {
|
||||
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RequireRepoWriter returns a middleware for requiring repository write to the specify unitType
|
||||
func RequireRepoWriter(unitType unit.Type) func(ctx *Context) {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.Repo.CanWrite(unitType) {
|
||||
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
|
||||
ctx.NotFound("RequireRepoAdmin denies the request", nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -42,75 +31,30 @@ func CanEnableEditor() func(ctx *Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// RequireRepoWriterOr returns a middleware for requiring repository write to one of the unit permission
|
||||
func RequireRepoWriterOr(unitTypes ...unit.Type) func(ctx *Context) {
|
||||
// RequireUnitWriter returns a middleware for requiring repository write to one of the unit permission
|
||||
func RequireUnitWriter(unitTypes ...unit.Type) func(ctx *Context) {
|
||||
return func(ctx *Context) {
|
||||
for _, unitType := range unitTypes {
|
||||
if ctx.Repo.CanWrite(unitType) {
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
|
||||
ctx.NotFound("RequireUnitWriter denies the request", nil)
|
||||
}
|
||||
}
|
||||
|
||||
// RequireRepoReader returns a middleware for requiring repository read to the specify unitType
|
||||
func RequireRepoReader(unitType unit.Type) func(ctx *Context) {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.Repo.CanRead(unitType) {
|
||||
if unitType == unit.TypeCode && canWriteAsMaintainer(ctx) {
|
||||
return
|
||||
}
|
||||
if log.IsTrace() {
|
||||
if ctx.IsSigned {
|
||||
log.Trace("Permission Denied: User %-v cannot read %-v in Repo %-v\n"+
|
||||
"User in Repo has Permissions: %-+v",
|
||||
ctx.Doer,
|
||||
unitType,
|
||||
ctx.Repo.Repository,
|
||||
ctx.Repo.Permission)
|
||||
} else {
|
||||
log.Trace("Permission Denied: Anonymous user cannot read %-v in Repo %-v\n"+
|
||||
"Anonymous user in Repo has Permissions: %-+v",
|
||||
unitType,
|
||||
ctx.Repo.Repository,
|
||||
ctx.Repo.Permission)
|
||||
}
|
||||
}
|
||||
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RequireRepoReaderOr returns a middleware for requiring repository write to one of the unit permission
|
||||
func RequireRepoReaderOr(unitTypes ...unit.Type) func(ctx *Context) {
|
||||
// RequireUnitReader returns a middleware for requiring repository write to one of the unit permission
|
||||
func RequireUnitReader(unitTypes ...unit.Type) func(ctx *Context) {
|
||||
return func(ctx *Context) {
|
||||
for _, unitType := range unitTypes {
|
||||
if ctx.Repo.CanRead(unitType) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if log.IsTrace() {
|
||||
var format string
|
||||
var args []any
|
||||
if ctx.IsSigned {
|
||||
format = "Permission Denied: User %-v cannot read ["
|
||||
args = append(args, ctx.Doer)
|
||||
} else {
|
||||
format = "Permission Denied: Anonymous user cannot read ["
|
||||
if unitType == unit.TypeCode && canWriteAsMaintainer(ctx) {
|
||||
return
|
||||
}
|
||||
for _, unit := range unitTypes {
|
||||
format += "%-v, "
|
||||
args = append(args, unit)
|
||||
}
|
||||
|
||||
format = format[:len(format)-2] + "] in Repo %-v\n" +
|
||||
"User in Repo has Permissions: %-+v"
|
||||
args = append(args, ctx.Repo.Repository, ctx.Repo.Permission)
|
||||
log.Trace(format, args...)
|
||||
}
|
||||
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
|
||||
ctx.NotFound("RequireUnitReader denies the request", nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,41 +110,51 @@ type RepoSettingForm struct {
|
||||
EnablePrune bool
|
||||
|
||||
// Advanced settings
|
||||
EnableCode bool
|
||||
EnableWiki bool
|
||||
EnableExternalWiki bool
|
||||
DefaultWikiBranch string
|
||||
DefaultWikiEveryoneAccess string
|
||||
ExternalWikiURL string
|
||||
EnableCode bool
|
||||
DefaultCodeEveryoneAccess string
|
||||
|
||||
EnableWiki bool
|
||||
EnableExternalWiki bool
|
||||
DefaultWikiBranch string
|
||||
DefaultWikiEveryoneAccess string
|
||||
ExternalWikiURL string
|
||||
|
||||
EnableIssues bool
|
||||
DefaultIssuesEveryoneAccess string
|
||||
EnableExternalTracker bool
|
||||
ExternalTrackerURL string
|
||||
TrackerURLFormat string
|
||||
TrackerIssueStyle string
|
||||
ExternalTrackerRegexpPattern string
|
||||
EnableCloseIssuesViaCommitInAnyBranch bool
|
||||
EnableProjects bool
|
||||
ProjectsMode string
|
||||
EnableReleases bool
|
||||
EnablePackages bool
|
||||
EnablePulls bool
|
||||
EnableActions bool
|
||||
PullsIgnoreWhitespace bool
|
||||
PullsAllowMerge bool
|
||||
PullsAllowRebase bool
|
||||
PullsAllowRebaseMerge bool
|
||||
PullsAllowSquash bool
|
||||
PullsAllowFastForwardOnly bool
|
||||
PullsAllowManualMerge bool
|
||||
PullsDefaultMergeStyle string
|
||||
EnableAutodetectManualMerge bool
|
||||
PullsAllowRebaseUpdate bool
|
||||
DefaultDeleteBranchAfterMerge bool
|
||||
DefaultAllowMaintainerEdit bool
|
||||
EnableTimetracker bool
|
||||
AllowOnlyContributorsToTrackTime bool
|
||||
EnableIssueDependencies bool
|
||||
IsArchived bool
|
||||
|
||||
EnableProjects bool
|
||||
ProjectsMode string
|
||||
|
||||
EnableReleases bool
|
||||
|
||||
EnablePackages bool
|
||||
|
||||
EnablePulls bool
|
||||
PullsIgnoreWhitespace bool
|
||||
PullsAllowMerge bool
|
||||
PullsAllowRebase bool
|
||||
PullsAllowRebaseMerge bool
|
||||
PullsAllowSquash bool
|
||||
PullsAllowFastForwardOnly bool
|
||||
PullsAllowManualMerge bool
|
||||
PullsDefaultMergeStyle string
|
||||
EnableAutodetectManualMerge bool
|
||||
PullsAllowRebaseUpdate bool
|
||||
DefaultDeleteBranchAfterMerge bool
|
||||
DefaultAllowMaintainerEdit bool
|
||||
EnableTimetracker bool
|
||||
AllowOnlyContributorsToTrackTime bool
|
||||
EnableIssueDependencies bool
|
||||
|
||||
EnableActions bool
|
||||
|
||||
IsArchived bool
|
||||
|
||||
// Signing Settings
|
||||
TrustModel string
|
||||
|
@ -302,6 +302,15 @@
|
||||
<input class="enable-system" name="enable_code" type="checkbox"{{if $isCodeEnabled}} checked{{end}}>
|
||||
<label>{{ctx.Locale.Tr "repo.code.desc"}}</label>
|
||||
</div>
|
||||
<div class="inline field tw-pl-4">
|
||||
{{$unitCode := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypeCode}}
|
||||
<label>{{ctx.Locale.Tr "repo.settings.default_permission_everyone_access"}}</label>
|
||||
<select name="default_code_everyone_access" class="ui selection dropdown">
|
||||
{{/* everyone access mode is different from others, none means it is unset and won't be applied */}}
|
||||
<option value="none" {{Iif (eq $unitCode.EveryoneAccessMode 0) "selected"}}>{{ctx.Locale.Tr "settings.permission_not_set"}}</option>
|
||||
<option value="read" {{Iif (eq $unitCode.EveryoneAccessMode 1) "selected"}}>{{ctx.Locale.Tr "settings.permission_read"}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{$isInternalWikiEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeWiki}}
|
||||
@ -331,7 +340,7 @@
|
||||
</div>
|
||||
<div class="inline field">
|
||||
{{$unitInternalWiki := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypeWiki}}
|
||||
<label>{{ctx.Locale.Tr "repo.settings.default_wiki_everyone_access"}}</label>
|
||||
<label>{{ctx.Locale.Tr "repo.settings.default_permission_everyone_access"}}</label>
|
||||
<select name="default_wiki_everyone_access" class="ui selection dropdown">
|
||||
{{/* everyone access mode is different from others, none means it is unset and won't be applied */}}
|
||||
<option value="none" {{Iif (eq $unitInternalWiki.EveryoneAccessMode 0) "selected"}}>{{ctx.Locale.Tr "settings.permission_not_set"}}</option>
|
||||
@ -374,6 +383,15 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="field tw-pl-4 {{if (.Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeExternalTracker)}}disabled{{end}}" id="internal_issue_box">
|
||||
<div class="inline field">
|
||||
{{$unitIssue := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypeIssues}}
|
||||
<label>{{ctx.Locale.Tr "repo.settings.default_permission_everyone_access"}}</label>
|
||||
<select name="default_issues_everyone_access" class="ui selection dropdown">
|
||||
{{/* everyone access mode is different from others, none means it is unset and won't be applied */}}
|
||||
<option value="none" {{Iif (eq $unitIssue.EveryoneAccessMode 0) "selected"}}>{{ctx.Locale.Tr "settings.permission_not_set"}}</option>
|
||||
<option value="read" {{Iif (eq $unitIssue.EveryoneAccessMode 1) "selected"}}>{{ctx.Locale.Tr "settings.permission_read"}}</option>
|
||||
</select>
|
||||
</div>
|
||||
{{if .Repository.CanEnableTimetracker}}
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
|
Loading…
Reference in New Issue
Block a user