From 833def96cf742baedb2014a3d622a0540a9f222e Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Tue, 19 Nov 2024 17:34:06 +0800 Subject: [PATCH 01/15] actions from private repos --- models/repo/repo_unit.go | 3 ++- options/locale/locale_en-US.ini | 5 +++++ routers/web/repo/githttp.go | 12 ++++++++-- routers/web/repo/setting/actions.go | 23 ++++++++++++++++++++ routers/web/web.go | 1 + templates/repo/settings/actions.tmpl | 2 ++ templates/repo/settings/actions_general.tmpl | 16 ++++++++++++++ templates/repo/settings/navbar.tmpl | 5 ++++- 8 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 routers/web/repo/setting/actions.go create mode 100644 templates/repo/settings/actions_general.tmpl diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index cb52c2c9e2058..73884f5fb0b21 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -169,7 +169,8 @@ func (cfg *PullRequestsConfig) GetDefaultMergeStyle() MergeStyle { } type ActionsConfig struct { - DisabledWorkflows []string + DisabledWorkflows []string + AccessbleFromOtherRepos bool } func (cfg *ActionsConfig) EnableWorkflow(file string) { diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index c3639fb72e2f3..7e0d280ec102f 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3751,6 +3751,11 @@ variables.creation.success = The variable "%s" has been added. variables.update.failed = Failed to edit variable. variables.update.success = The variable has been edited. +general = General +general.settings = Actions General Settings +general.actions_accessible_from_other_repositories = Accessible from repositories owned by '%s' +general.actions_accessible_from_other_repositories_desc = Workflows in other repositories that are owned by the user '%s' can access the actions and reusable workflows in this repository. Access is allowed only from private repositories. + [projects] deleted.display_name = Deleted Project type-1.display_name = Individual Project diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index 58a2bdbab1c34..2af559817a326 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -195,8 +195,16 @@ func httpBase(ctx *context.Context) *serviceHandler { return nil } if task.RepoID != repo.ID { - ctx.PlainText(http.StatusForbidden, "User permission denied") - return nil + taskRepo, err := repo_model.GetRepositoryByID(ctx, task.RepoID) + if err != nil { + ctx.ServerError("GetRepositoryByID", err) + return nil + } + actionsCfg := repo.MustGetUnit(ctx, unit.TypeActions).ActionsConfig() + if !taskRepo.IsPrivate || taskRepo.OwnerID != repo.OwnerID || !actionsCfg.AccessbleFromOtherRepos { + ctx.PlainText(http.StatusForbidden, "User permission denied") + return nil + } } if task.IsForkPullRequest { diff --git a/routers/web/repo/setting/actions.go b/routers/web/repo/setting/actions.go new file mode 100644 index 0000000000000..2689a0825ba88 --- /dev/null +++ b/routers/web/repo/setting/actions.go @@ -0,0 +1,23 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package setting + +import ( + "net/http" + + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/services/context" +) + +const ( + tplRepoActionsGeneral base.TplName = "repo/settings/actions" +) + +func ActionsGeneral(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("actions.general") + ctx.Data["PageType"] = "general" + ctx.Data["PageIsActionsSettingsGeneral"] = true + + ctx.HTML(http.StatusOK, tplRepoActionsGeneral) +} diff --git a/routers/web/web.go b/routers/web/web.go index 137c67730652d..e266bdd884018 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1133,6 +1133,7 @@ func registerRoutes(m *web.Router) { addSettingsRunnersRoutes() addSettingsSecretsRoutes() addSettingsVariablesRoutes() + m.Get("/general", repo_setting.ActionsGeneral) }, actions.MustEnableActions) // the follow handler must be under "settings", otherwise this incomplete repo can't be accessed m.Group("/migrate", func() { diff --git a/templates/repo/settings/actions.tmpl b/templates/repo/settings/actions.tmpl index f38ab5b658412..5388de35af35e 100644 --- a/templates/repo/settings/actions.tmpl +++ b/templates/repo/settings/actions.tmpl @@ -6,6 +6,8 @@ {{template "shared/secrets/add_list" .}} {{else if eq .PageType "variables"}} {{template "shared/variables/variable_list" .}} + {{else if eq .PageType "general"}} + {{template "repo/settings/actions_general" .}} {{end}} {{template "repo/settings/layout_footer" .}} diff --git a/templates/repo/settings/actions_general.tmpl b/templates/repo/settings/actions_general.tmpl new file mode 100644 index 0000000000000..65d3917e8cd05 --- /dev/null +++ b/templates/repo/settings/actions_general.tmpl @@ -0,0 +1,16 @@ +
+

+ {{ctx.Locale.Tr "actions.general.settings"}} +

+
+
+
+
+ + +

{{ctx.Locale.Tr "actions.general.actions_accessible_from_other_repositories_desc" .Owner.Name}}

+
+
+
+
+
diff --git a/templates/repo/settings/navbar.tmpl b/templates/repo/settings/navbar.tmpl index 3e127ccbb3517..e33893bee7959 100644 --- a/templates/repo/settings/navbar.tmpl +++ b/templates/repo/settings/navbar.tmpl @@ -34,7 +34,7 @@ {{end}} {{end}} {{if and .EnableActions (.Permission.CanRead ctx.Consts.RepoUnitTypeActions)}} -
+
{{ctx.Locale.Tr "actions.actions"}}
{{end}} From f73073df4142b4235167652b8e1f433f2f14ebcd Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Wed, 20 Nov 2024 13:03:23 +0800 Subject: [PATCH 02/15] add ActionsGeneralSettingsPost --- routers/web/repo/setting/actions.go | 34 ++++++++++++++++++-- routers/web/web.go | 4 ++- templates/repo/settings/actions_general.tmpl | 9 ++++-- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/routers/web/repo/setting/actions.go b/routers/web/repo/setting/actions.go index 2689a0825ba88..778078115c9a3 100644 --- a/routers/web/repo/setting/actions.go +++ b/routers/web/repo/setting/actions.go @@ -6,18 +6,46 @@ package setting import ( "net/http" + repo_model "code.gitea.io/gitea/models/repo" + unit_model "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/services/context" ) const ( - tplRepoActionsGeneral base.TplName = "repo/settings/actions" + tplRepoActionsGeneralSettings base.TplName = "repo/settings/actions" ) -func ActionsGeneral(ctx *context.Context) { +func ActionsGeneralSettings(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("actions.general") ctx.Data["PageType"] = "general" ctx.Data["PageIsActionsSettingsGeneral"] = true - ctx.HTML(http.StatusOK, tplRepoActionsGeneral) + actionsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeActions) + if err != nil { + ctx.ServerError("GetUnit", err) + return + } + actionsCfg := actionsUnit.ActionsConfig() + + ctx.Data["AccessibleFromOtherRepos"] = actionsCfg.AccessbleFromOtherRepos + + ctx.HTML(http.StatusOK, tplRepoActionsGeneralSettings) +} + +func ActionsGeneralSettingsPost(ctx *context.Context) { + actionsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeActions) + if err != nil { + ctx.ServerError("GetUnit", err) + return + } + actionsCfg := actionsUnit.ActionsConfig() + actionsCfg.AccessbleFromOtherRepos = ctx.FormBool("actions_accessible_from_other_repositories") + + if err := repo_model.UpdateRepoUnit(ctx, actionsUnit); err != nil { + ctx.ServerError("UpdateRepoUnit", err) + return + } + + ctx.Redirect(ctx.Repo.RepoLink + "/settings/actions/general") } diff --git a/routers/web/web.go b/routers/web/web.go index e266bdd884018..8cd4455899d7a 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1133,7 +1133,9 @@ func registerRoutes(m *web.Router) { addSettingsRunnersRoutes() addSettingsSecretsRoutes() addSettingsVariablesRoutes() - m.Get("/general", repo_setting.ActionsGeneral) + m.Combo("/general"). + Get(repo_setting.ActionsGeneralSettings). + Post(repo_setting.ActionsGeneralSettingsPost) }, actions.MustEnableActions) // the follow handler must be under "settings", otherwise this incomplete repo can't be accessed m.Group("/migrate", func() { diff --git a/templates/repo/settings/actions_general.tmpl b/templates/repo/settings/actions_general.tmpl index 65d3917e8cd05..9ec9dccc36cdd 100644 --- a/templates/repo/settings/actions_general.tmpl +++ b/templates/repo/settings/actions_general.tmpl @@ -3,14 +3,19 @@ {{ctx.Locale.Tr "actions.general.settings"}}
-
+ + {{.CsrfTokenHtml}}
- +

{{ctx.Locale.Tr "actions.general.actions_accessible_from_other_repositories_desc" .Owner.Name}}

+
+
+ +
From 73b00ea10e03ce9b41971cb4460bba490e0416fc Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Wed, 20 Nov 2024 14:15:08 +0800 Subject: [PATCH 03/15] misc --- routers/web/repo/githttp.go | 8 ++++++-- routers/web/repo/setting/actions.go | 4 +--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index 2af559817a326..d7e036067527c 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -195,13 +195,17 @@ func httpBase(ctx *context.Context) *serviceHandler { return nil } if task.RepoID != repo.ID { + actionsCfg := repo.MustGetUnit(ctx, unit.TypeActions).ActionsConfig() taskRepo, err := repo_model.GetRepositoryByID(ctx, task.RepoID) if err != nil { ctx.ServerError("GetRepositoryByID", err) return nil } - actionsCfg := repo.MustGetUnit(ctx, unit.TypeActions).ActionsConfig() - if !taskRepo.IsPrivate || taskRepo.OwnerID != repo.OwnerID || !actionsCfg.AccessbleFromOtherRepos { + if !actionsCfg.AccessbleFromOtherRepos || taskRepo.OwnerID != repo.OwnerID || !taskRepo.IsPrivate { + // See https://docs.github.com/en/actions/sharing-automations/sharing-actions-and-workflows-from-your-private-repository + // Any actions or reusable workflows stored in the private repository can be used in + // workflows defined in other private repositories owned by the same organization or user. + // Actions and reusable workflows stored in private repositories cannot be used in public repositories. ctx.PlainText(http.StatusForbidden, "User permission denied") return nil } diff --git a/routers/web/repo/setting/actions.go b/routers/web/repo/setting/actions.go index 778078115c9a3..2dea6c0f7c9e7 100644 --- a/routers/web/repo/setting/actions.go +++ b/routers/web/repo/setting/actions.go @@ -12,9 +12,7 @@ import ( "code.gitea.io/gitea/services/context" ) -const ( - tplRepoActionsGeneralSettings base.TplName = "repo/settings/actions" -) +const tplRepoActionsGeneralSettings base.TplName = "repo/settings/actions" func ActionsGeneralSettings(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("actions.general") From 8fcf1b68be1479a18c464c68d587b476bca57ea6 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Wed, 20 Nov 2024 16:01:54 +0800 Subject: [PATCH 04/15] lint --- templates/repo/settings/actions_general.tmpl | 38 ++++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/templates/repo/settings/actions_general.tmpl b/templates/repo/settings/actions_general.tmpl index 9ec9dccc36cdd..3ff0887132236 100644 --- a/templates/repo/settings/actions_general.tmpl +++ b/templates/repo/settings/actions_general.tmpl @@ -1,21 +1,21 @@
-

- {{ctx.Locale.Tr "actions.general.settings"}} -

-
-
- {{.CsrfTokenHtml}} -
-
- - -

{{ctx.Locale.Tr "actions.general.actions_accessible_from_other_repositories_desc" .Owner.Name}}

-
-
-
-
- -
-
-
+

+ {{ctx.Locale.Tr "actions.general.settings"}} +

+
+
+ {{.CsrfTokenHtml}} +
+
+ + +

{{ctx.Locale.Tr "actions.general.actions_accessible_from_other_repositories_desc" .Owner.Name}}

+
+
+
+
+ +
+
+
From bc71cc4fc0fa7137d17518ff09d40ef9ca89e95d Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Fri, 22 Nov 2024 16:03:54 +0800 Subject: [PATCH 05/15] improvements for public repos --- options/locale/locale_en-US.ini | 1 + routers/web/repo/setting/actions.go | 18 +++++++++++------- templates/repo/settings/actions_general.tmpl | 10 ++++++++-- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 5fb9a8d53a0e4..82ac38ea00d95 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3758,6 +3758,7 @@ general = General general.settings = Actions General Settings general.actions_accessible_from_other_repositories = Accessible from repositories owned by '%s' general.actions_accessible_from_other_repositories_desc = Workflows in other repositories that are owned by the user '%s' can access the actions and reusable workflows in this repository. Access is allowed only from private repositories. +general.actions_always_accessible_desc = The actions and workflows of a public repository are always accessible to other repositories. [projects] deleted.display_name = Deleted Project diff --git a/routers/web/repo/setting/actions.go b/routers/web/repo/setting/actions.go index 2dea6c0f7c9e7..b8afbbc314f04 100644 --- a/routers/web/repo/setting/actions.go +++ b/routers/web/repo/setting/actions.go @@ -19,14 +19,18 @@ func ActionsGeneralSettings(ctx *context.Context) { ctx.Data["PageType"] = "general" ctx.Data["PageIsActionsSettingsGeneral"] = true - actionsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeActions) - if err != nil { - ctx.ServerError("GetUnit", err) - return + accessbleFromOtherRepos := false + if !ctx.Repo.Repository.IsPrivate { + accessbleFromOtherRepos = true + } else { + actionsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeActions) + if err != nil { + ctx.ServerError("GetUnit", err) + return + } + accessbleFromOtherRepos = actionsUnit.ActionsConfig().AccessbleFromOtherRepos } - actionsCfg := actionsUnit.ActionsConfig() - - ctx.Data["AccessibleFromOtherRepos"] = actionsCfg.AccessbleFromOtherRepos + ctx.Data["AccessibleFromOtherRepos"] = accessbleFromOtherRepos ctx.HTML(http.StatusOK, tplRepoActionsGeneralSettings) } diff --git a/templates/repo/settings/actions_general.tmpl b/templates/repo/settings/actions_general.tmpl index 3ff0887132236..071d386b31985 100644 --- a/templates/repo/settings/actions_general.tmpl +++ b/templates/repo/settings/actions_general.tmpl @@ -7,9 +7,15 @@ {{.CsrfTokenHtml}}
- + -

{{ctx.Locale.Tr "actions.general.actions_accessible_from_other_repositories_desc" .Owner.Name}}

+

+ {{if .Repository.IsPrivate}} + {{ctx.Locale.Tr "actions.general.actions_accessible_from_other_repositories_desc" .Owner.Name}} + {{else}} + {{ctx.Locale.Tr "actions.general.actions_always_accessible_desc"}} + {{end}} +

From 9ae509373f17e40701e85b23731d20aec373524f Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Fri, 22 Nov 2024 16:32:25 +0800 Subject: [PATCH 06/15] lint --- routers/web/repo/setting/actions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/web/repo/setting/actions.go b/routers/web/repo/setting/actions.go index b8afbbc314f04..a1bfe46b99a2b 100644 --- a/routers/web/repo/setting/actions.go +++ b/routers/web/repo/setting/actions.go @@ -19,7 +19,7 @@ func ActionsGeneralSettings(ctx *context.Context) { ctx.Data["PageType"] = "general" ctx.Data["PageIsActionsSettingsGeneral"] = true - accessbleFromOtherRepos := false + var accessbleFromOtherRepos bool if !ctx.Repo.Repository.IsPrivate { accessbleFromOtherRepos = true } else { From 6142f66358c74fd2ebb0fb838a2414f4495531ee Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Tue, 26 Nov 2024 17:12:27 +0800 Subject: [PATCH 07/15] use CollaborativeOwnerIDs --- models/repo/repo_unit.go | 20 ++++++- models/user/search.go | 10 ++-- models/user/user.go | 12 ++++ models/user/user_test.go | 4 +- options/locale/locale_en-US.ini | 9 +-- routers/api/v1/admin/org.go | 2 +- routers/api/v1/admin/user.go | 2 +- routers/api/v1/org/org.go | 2 +- routers/api/v1/user/user.go | 2 +- routers/web/admin/orgs.go | 2 +- routers/web/admin/users.go | 2 +- routers/web/explore/org.go | 2 +- routers/web/explore/user.go | 2 +- routers/web/home.go | 2 +- routers/web/repo/githttp.go | 2 +- routers/web/repo/setting/actions.go | 62 +++++++++++++++++--- routers/web/user/search.go | 6 +- routers/web/web.go | 10 +++- templates/repo/settings/actions_general.tmpl | 57 ++++++++++++------ web_src/js/features/comp/SearchUserBox.ts | 3 +- 20 files changed, 159 insertions(+), 54 deletions(-) diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index 73884f5fb0b21..6835257eee1a0 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -169,8 +169,10 @@ func (cfg *PullRequestsConfig) GetDefaultMergeStyle() MergeStyle { } type ActionsConfig struct { - DisabledWorkflows []string - AccessbleFromOtherRepos bool + DisabledWorkflows []string + // CollaborativeOwnerIDs is a list of owner IDs used to share actions from private repos. + // Only workflows from the private repos whose owners are in CollaborativeOwnerIDs can access the current repo's actions. + CollaborativeOwnerIDs []int64 } func (cfg *ActionsConfig) EnableWorkflow(file string) { @@ -195,6 +197,20 @@ func (cfg *ActionsConfig) DisableWorkflow(file string) { cfg.DisabledWorkflows = append(cfg.DisabledWorkflows, file) } +func (cfg *ActionsConfig) AddCollaborativeOwner(ownerID int64) { + if !slices.Contains(cfg.CollaborativeOwnerIDs, ownerID) { + cfg.CollaborativeOwnerIDs = append(cfg.CollaborativeOwnerIDs, ownerID) + } +} + +func (cfg *ActionsConfig) RemoveCollaborativeOwner(ownerID int64) { + cfg.CollaborativeOwnerIDs = util.SliceRemoveAll(cfg.CollaborativeOwnerIDs, ownerID) +} + +func (cfg *ActionsConfig) IsCollaborativeOwner(ownerID int64) bool { + return slices.Contains(cfg.CollaborativeOwnerIDs, ownerID) +} + // FromDB fills up a ActionsConfig from serialized format. func (cfg *ActionsConfig) FromDB(bs []byte) error { return json.UnmarshalHandleDoubleEncode(bs, &cfg) diff --git a/models/user/search.go b/models/user/search.go index 6af33892373ad..f55ac07775dbd 100644 --- a/models/user/search.go +++ b/models/user/search.go @@ -6,6 +6,7 @@ package user import ( "context" "fmt" + "slices" "strings" "code.gitea.io/gitea/models/db" @@ -22,7 +23,7 @@ type SearchUserOptions struct { db.ListOptions Keyword string - Type UserType + Types []UserType UID int64 LoginName string // this option should be used only for admin user SourceID int64 // this option should be used only for admin user @@ -45,15 +46,16 @@ type SearchUserOptions struct { func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Session { var cond builder.Cond - cond = builder.Eq{"type": opts.Type} + cond = builder.In("type", opts.Types) if opts.IncludeReserved { - if opts.Type == UserTypeIndividual { + if slices.Contains(opts.Types, UserTypeIndividual) { cond = cond.Or(builder.Eq{"type": UserTypeUserReserved}).Or( builder.Eq{"type": UserTypeBot}, ).Or( builder.Eq{"type": UserTypeRemoteUser}, ) - } else if opts.Type == UserTypeOrganization { + } + if slices.Contains(opts.Types, UserTypeOrganization) { cond = cond.Or(builder.Eq{"type": UserTypeOrganizationReserved}) } } diff --git a/models/user/user.go b/models/user/user.go index bd92693b6e0ff..37b0ea0cb1260 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -1317,3 +1317,15 @@ func DisabledFeaturesWithLoginType(user *User) *container.Set[string] { } return &setting.Admin.UserDisabledFeatures } + +// GetUserOrOrgIDByName returns the id for a user or an org by name +func GetUserOrOrgIDByName(ctx context.Context, name string) (int64, error) { + var id int64 + has, err := db.GetEngine(ctx).Table("user").Where("name = ?", name).Cols("id").Get(&id) + if err != nil { + return 0, err + } else if !has { + return 0, fmt.Errorf("user or org with name %s: %w", name, util.ErrNotExist) + } + return id, nil +} diff --git a/models/user/user_test.go b/models/user/user_test.go index 6701be39a5536..1fcd8aad1f564 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -76,7 +76,7 @@ func TestSearchUsers(t *testing.T) { // test orgs testOrgSuccess := func(opts *user_model.SearchUserOptions, expectedOrgIDs []int64) { - opts.Type = user_model.UserTypeOrganization + opts.Types = []user_model.UserType{user_model.UserTypeOrganization} testSuccess(opts, expectedOrgIDs) } @@ -100,7 +100,7 @@ func TestSearchUsers(t *testing.T) { // test users testUserSuccess := func(opts *user_model.SearchUserOptions, expectedUserIDs []int64) { - opts.Type = user_model.UserTypeIndividual + opts.Types = []user_model.UserType{user_model.UserTypeIndividual} testSuccess(opts, expectedUserIDs) } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 82ac38ea00d95..19055e4bd54e6 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3755,10 +3755,11 @@ variables.update.failed = Failed to edit variable. variables.update.success = The variable has been edited. general = General -general.settings = Actions General Settings -general.actions_accessible_from_other_repositories = Accessible from repositories owned by '%s' -general.actions_accessible_from_other_repositories_desc = Workflows in other repositories that are owned by the user '%s' can access the actions and reusable workflows in this repository. Access is allowed only from private repositories. -general.actions_always_accessible_desc = The actions and workflows of a public repository are always accessible to other repositories. +general.collaborative_owners_management = Collaborative Owners Management +general.add_collaborative_owner = Add Collaborative Owner +general.collaborative_owner_not_exist = The collaborative owner does not exist. +general.remove_collaborative_owner = Remove Collaborative Owner +general.remove_collaborative_owner_desc = Removing a collaborative owner will prevent the repositories of the owner from accessing the actions in this repository. Continue? [projects] deleted.display_name = Deleted Project diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go index a5c299bbf01c8..30577700670cd 100644 --- a/routers/api/v1/admin/org.go +++ b/routers/api/v1/admin/org.go @@ -103,7 +103,7 @@ func GetAllOrgs(ctx *context.APIContext) { users, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ Actor: ctx.Doer, - Type: user_model.UserTypeOrganization, + Types: []user_model.UserType{user_model.UserTypeOrganization}, OrderBy: db.SearchOrderByAlphabetically, ListOptions: listOptions, Visible: []api.VisibleType{api.VisibleTypePublic, api.VisibleTypeLimited, api.VisibleTypePrivate}, diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index b0f40084da38b..a476aad562793 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -423,7 +423,7 @@ func SearchUsers(ctx *context.APIContext) { users, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ Actor: ctx.Doer, - Type: user_model.UserTypeIndividual, + Types: []user_model.UserType{user_model.UserTypeIndividual}, LoginName: ctx.FormTrim("login_name"), SourceID: ctx.FormInt64("source_id"), OrderBy: db.SearchOrderByAlphabetically, diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 9e5874627298d..1b9b7e41d66b8 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -203,7 +203,7 @@ func GetAll(ctx *context.APIContext) { publicOrgs, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ Actor: ctx.Doer, ListOptions: listOptions, - Type: user_model.UserTypeOrganization, + Types: []user_model.UserType{user_model.UserTypeOrganization}, OrderBy: db.SearchOrderByAlphabetically, Visible: vMode, }) diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go index a9011427fb577..45ef1c83e5431 100644 --- a/routers/api/v1/user/user.go +++ b/routers/api/v1/user/user.go @@ -76,7 +76,7 @@ func Search(ctx *context.APIContext) { Actor: ctx.Doer, Keyword: ctx.FormTrim("q"), UID: uid, - Type: user_model.UserTypeIndividual, + Types: []user_model.UserType{user_model.UserTypeIndividual}, SearchByEmail: true, Visible: visible, ListOptions: listOptions, diff --git a/routers/web/admin/orgs.go b/routers/web/admin/orgs.go index cea28f82203b2..31b1e08e9f66b 100644 --- a/routers/web/admin/orgs.go +++ b/routers/web/admin/orgs.go @@ -29,7 +29,7 @@ func Organizations(ctx *context.Context) { explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{ Actor: ctx.Doer, - Type: user_model.UserTypeOrganization, + Types: []user_model.UserType{user_model.UserTypeOrganization}, IncludeReserved: true, // administrator needs to list all accounts include reserved ListOptions: db.ListOptions{ PageSize: setting.UI.Admin.OrgPagingNum, diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index a6b0b5c78bb13..ac5945d10e82f 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -71,7 +71,7 @@ func Users(ctx *context.Context) { explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{ Actor: ctx.Doer, - Type: user_model.UserTypeIndividual, + Types: []user_model.UserType{user_model.UserTypeIndividual}, ListOptions: db.ListOptions{ PageSize: setting.UI.Admin.UserPagingNum, }, diff --git a/routers/web/explore/org.go b/routers/web/explore/org.go index 7bb71acfd78e0..8748f4a019d06 100644 --- a/routers/web/explore/org.go +++ b/routers/web/explore/org.go @@ -46,7 +46,7 @@ func Organizations(ctx *context.Context) { RenderUserSearch(ctx, &user_model.SearchUserOptions{ Actor: ctx.Doer, - Type: user_model.UserTypeOrganization, + Types: []user_model.UserType{user_model.UserTypeOrganization}, ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum}, Visible: visibleTypes, diff --git a/routers/web/explore/user.go b/routers/web/explore/user.go index c009982d420ce..0c1e5b2fcfee9 100644 --- a/routers/web/explore/user.go +++ b/routers/web/explore/user.go @@ -156,7 +156,7 @@ func Users(ctx *context.Context) { RenderUserSearch(ctx, &user_model.SearchUserOptions{ Actor: ctx.Doer, - Type: user_model.UserTypeIndividual, + Types: []user_model.UserType{user_model.UserTypeIndividual}, ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum}, IsActive: optional.Some(true), Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate}, diff --git a/routers/web/home.go b/routers/web/home.go index d4be0931e850d..358749a3bf8f8 100644 --- a/routers/web/home.go +++ b/routers/web/home.go @@ -69,7 +69,7 @@ func HomeSitemap(ctx *context.Context) { m := sitemap.NewSitemapIndex() if !setting.Service.Explore.DisableUsersPage { _, cnt, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ - Type: user_model.UserTypeIndividual, + Types: []user_model.UserType{user_model.UserTypeIndividual}, ListOptions: db.ListOptions{PageSize: 1}, IsActive: optional.Some(true), Visible: []structs.VisibleType{structs.VisibleTypePublic}, diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index d7e036067527c..023e3c650e313 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -201,7 +201,7 @@ func httpBase(ctx *context.Context) *serviceHandler { ctx.ServerError("GetRepositoryByID", err) return nil } - if !actionsCfg.AccessbleFromOtherRepos || taskRepo.OwnerID != repo.OwnerID || !taskRepo.IsPrivate { + if !actionsCfg.IsCollaborativeOwner(taskRepo.OwnerID) || taskRepo.OwnerID != repo.OwnerID || !taskRepo.IsPrivate { // See https://docs.github.com/en/actions/sharing-automations/sharing-actions-and-workflows-from-your-private-repository // Any actions or reusable workflows stored in the private repository can be used in // workflows defined in other private repositories owned by the same organization or user. diff --git a/routers/web/repo/setting/actions.go b/routers/web/repo/setting/actions.go index a1bfe46b99a2b..d17fbd140953f 100644 --- a/routers/web/repo/setting/actions.go +++ b/routers/web/repo/setting/actions.go @@ -4,11 +4,16 @@ package setting import ( + "errors" + "fmt" "net/http" + "strings" repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/context" ) @@ -19,35 +24,74 @@ func ActionsGeneralSettings(ctx *context.Context) { ctx.Data["PageType"] = "general" ctx.Data["PageIsActionsSettingsGeneral"] = true - var accessbleFromOtherRepos bool - if !ctx.Repo.Repository.IsPrivate { - accessbleFromOtherRepos = true - } else { + if ctx.Repo.Repository.IsPrivate { actionsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeActions) if err != nil { ctx.ServerError("GetUnit", err) return } - accessbleFromOtherRepos = actionsUnit.ActionsConfig().AccessbleFromOtherRepos + collaborativeOwnerIDs := actionsUnit.ActionsConfig().CollaborativeOwnerIDs + collaborativeOwners, err := user_model.GetUsersByIDs(ctx, collaborativeOwnerIDs) + if err != nil { + ctx.ServerError("GetUsersByIDs", err) + return + } + ctx.Data["CollaborativeOwners"] = collaborativeOwners } - ctx.Data["AccessibleFromOtherRepos"] = accessbleFromOtherRepos ctx.HTML(http.StatusOK, tplRepoActionsGeneralSettings) } -func ActionsGeneralSettingsPost(ctx *context.Context) { +func AddCollaborativeOwner(ctx *context.Context) { + redirectURL := fmt.Sprintf("%s/settings/actions/general", ctx.Repo.RepoLink) + name := strings.ToLower(ctx.FormString("collaborative_owner")) + + ownerID, err := user_model.GetUserOrOrgIDByName(ctx, name) + if err != nil { + if errors.Is(err, util.ErrNotExist) { + ctx.Flash.Error(ctx.Tr("form.user_not_exist")) + ctx.Redirect(redirectURL) + } else { + ctx.ServerError("GetUserOrOrgIDByName", err) + } + return + } + actionsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeActions) if err != nil { ctx.ServerError("GetUnit", err) return } actionsCfg := actionsUnit.ActionsConfig() - actionsCfg.AccessbleFromOtherRepos = ctx.FormBool("actions_accessible_from_other_repositories") + actionsCfg.AddCollaborativeOwner(ownerID) + if err := repo_model.UpdateRepoUnit(ctx, actionsUnit); err != nil { + ctx.ServerError("UpdateRepoUnit", err) + return + } + + ctx.Redirect(redirectURL) +} +func DeleteCollaborativeOwner(ctx *context.Context) { + redirectURL := fmt.Sprintf("%s/settings/actions/general", ctx.Repo.RepoLink) + ownerID := ctx.FormInt64("id") + + actionsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeActions) + if err != nil { + ctx.ServerError("GetUnit", err) + return + } + actionsCfg := actionsUnit.ActionsConfig() + if !actionsCfg.IsCollaborativeOwner(ownerID) { + ctx.Flash.Error(ctx.Tr("actions.general.collaborative_owner_not_exist")) + ctx.Redirect(redirectURL) + return + } + actionsCfg.RemoveCollaborativeOwner(ownerID) if err := repo_model.UpdateRepoUnit(ctx, actionsUnit); err != nil { ctx.ServerError("UpdateRepoUnit", err) return } - ctx.Redirect(ctx.Repo.RepoLink + "/settings/actions/general") + ctx.JSONRedirect(redirectURL) } diff --git a/routers/web/user/search.go b/routers/web/user/search.go index be5eee90a971a..6f0526c161005 100644 --- a/routers/web/user/search.go +++ b/routers/web/user/search.go @@ -16,10 +16,14 @@ import ( // SearchCandidates searches candidate users for dropdown list func SearchCandidates(ctx *context.Context) { + searchUserTypes := []user_model.UserType{user_model.UserTypeIndividual} + if ctx.FormBool("orgs") { + searchUserTypes = append(searchUserTypes, user_model.UserTypeOrganization) + } users, _, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ Actor: ctx.Doer, Keyword: ctx.FormTrim("q"), - Type: user_model.UserTypeIndividual, + Types: searchUserTypes, IsActive: optional.Some(true), ListOptions: db.ListOptions{PageSize: setting.UI.MembersPagingNum}, }) diff --git a/routers/web/web.go b/routers/web/web.go index 2995de8d95f9f..cf6eb2ef7dee8 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1133,9 +1133,13 @@ func registerRoutes(m *web.Router) { addSettingsRunnersRoutes() addSettingsSecretsRoutes() addSettingsVariablesRoutes() - m.Combo("/general"). - Get(repo_setting.ActionsGeneralSettings). - Post(repo_setting.ActionsGeneralSettingsPost) + m.Group("/general", func() { + m.Get("", repo_setting.ActionsGeneralSettings) + m.Group("/collaborative_owner", func() { + m.Post("/add", repo_setting.AddCollaborativeOwner) + m.Post("/delete", repo_setting.DeleteCollaborativeOwner) + }) + }) }, actions.MustEnableActions) // the follow handler must be under "settings", otherwise this incomplete repo can't be accessed m.Group("/migrate", func() { diff --git a/templates/repo/settings/actions_general.tmpl b/templates/repo/settings/actions_general.tmpl index 071d386b31985..d94ce27fb9660 100644 --- a/templates/repo/settings/actions_general.tmpl +++ b/templates/repo/settings/actions_general.tmpl @@ -1,27 +1,48 @@

- {{ctx.Locale.Tr "actions.general.settings"}} + {{ctx.Locale.Tr "actions.general.collaborative_owners_management"}}

+ {{if .CollaborativeOwners}}
-
- {{.CsrfTokenHtml}} -
-
- - -

- {{if .Repository.IsPrivate}} - {{ctx.Locale.Tr "actions.general.actions_accessible_from_other_repositories_desc" .Owner.Name}} - {{else}} - {{ctx.Locale.Tr "actions.general.actions_always_accessible_desc"}} - {{end}} -

+
+ {{range .CollaborativeOwners}} +
+ +
+
+ {{template "shared/user/name" .}} +
+
+
+ +
+ {{end}} +
+
+ {{end}} +
+ + {{.CsrfTokenHtml}} + -
-
- -
+
+ + diff --git a/web_src/js/features/comp/SearchUserBox.ts b/web_src/js/features/comp/SearchUserBox.ts index 2e3b3f83beea8..db2d5a97fa932 100644 --- a/web_src/js/features/comp/SearchUserBox.ts +++ b/web_src/js/features/comp/SearchUserBox.ts @@ -10,10 +10,11 @@ export function initCompSearchUserBox() { const allowEmailInput = searchUserBox.getAttribute('data-allow-email') === 'true'; const allowEmailDescription = searchUserBox.getAttribute('data-allow-email-description') ?? undefined; + const includeOrgs = searchUserBox.getAttribute('data-include-orgs') === 'true'; fomanticQuery(searchUserBox).search({ minCharacters: 2, apiSettings: { - url: `${appSubUrl}/user/search_candidates?q={query}`, + url: `${appSubUrl}/user/search_candidates?q={query}&orgs=${includeOrgs}`, onResponse(response) { const resultItems = []; const searchQuery = searchUserBox.querySelector('input').value; From baf3839ac7448516c85a51cbea6b06071f0d69e6 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Wed, 27 Nov 2024 11:22:21 +0800 Subject: [PATCH 08/15] improve ui --- options/locale/locale_en-US.ini | 2 ++ templates/repo/settings/actions_general.tmpl | 36 ++++++++++++-------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 19055e4bd54e6..40adb4f286773 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3756,10 +3756,12 @@ variables.update.success = The variable has been edited. general = General general.collaborative_owners_management = Collaborative Owners Management +general.collaborative_owners_management_help = A collaborative owner is a user or an organization whose private repository has access to the actions and workflows of this repository. general.add_collaborative_owner = Add Collaborative Owner general.collaborative_owner_not_exist = The collaborative owner does not exist. general.remove_collaborative_owner = Remove Collaborative Owner general.remove_collaborative_owner_desc = Removing a collaborative owner will prevent the repositories of the owner from accessing the actions in this repository. Continue? +general.collaborative_owner_not_required = The actions and workflows of a public repository are always accessible to other repositories. You do not need to specify collaborative owners. [projects] deleted.display_name = Deleted Project diff --git a/templates/repo/settings/actions_general.tmpl b/templates/repo/settings/actions_general.tmpl index d94ce27fb9660..0019236d3a493 100644 --- a/templates/repo/settings/actions_general.tmpl +++ b/templates/repo/settings/actions_general.tmpl @@ -2,25 +2,30 @@

{{ctx.Locale.Tr "actions.general.collaborative_owners_management"}}

- {{if .CollaborativeOwners}} + {{if not .Repository.IsPrivate}} +
+ {{ctx.Locale.Tr "actions.general.collaborative_owner_not_required"}} +
+ {{else}} + {{if len .CollaborativeOwners}}
{{range .CollaborativeOwners}} -
- -
-
- {{template "shared/user/name" .}} -
-
-
- +
+ +
+
+ {{template "shared/user/name" .}}
+
+ +
+
{{end}}
@@ -33,7 +38,10 @@
+
+ {{ctx.Locale.Tr "actions.general.collaborative_owners_management_help"}}
+ {{end}}