Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions models/actions/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log"
Expand All @@ -38,9 +39,10 @@ type ActionTask struct {
Started timeutil.TimeStamp `xorm:"index"`
Stopped timeutil.TimeStamp `xorm:"index(stopped_log_expired)"`

RepoID int64 `xorm:"index"`
OwnerID int64 `xorm:"index"`
CommitSHA string `xorm:"index"`
RepoID int64 `xorm:"index"`
Repo *repo_model.Repository `xorm:"-"`
OwnerID int64 `xorm:"index"`
CommitSHA string `xorm:"index"`
IsForkPullRequest bool

Token string `xorm:"-"`
Expand Down Expand Up @@ -144,14 +146,22 @@ func (task *ActionTask) LoadAttributes(ctx context.Context) error {
task.Steps = steps
}

return nil
return task.LoadRepository(ctx)
}

func (task *ActionTask) GenerateToken() (err error) {
task.Token, task.TokenSalt, task.TokenHash, task.TokenLastEight, err = generateSaltedToken()
return err
}

func (task *ActionTask) LoadRepository(ctx context.Context) (err error) {
if task.Repo != nil {
return nil
}
task.Repo, err = repo_model.GetRepositoryByID(ctx, task.RepoID)
return err
}

func GetTaskByID(ctx context.Context, id int64) (*ActionTask, error) {
var task ActionTask
has, err := db.GetEngine(ctx).Where("id=?", id).Get(&task)
Expand Down
20 changes: 20 additions & 0 deletions models/fixtures/action_run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,23 @@
updated: 1683636626
need_approval: 0
approved_by: 0
-
id: 804
title: "use a private action"
repo_id: 60
owner_id: 40
workflow_id: "run.yaml"
index: 189
trigger_user_id: 40
ref: "refs/heads/master"
commit_sha: "6e64b26de7ba966d01d90ecfaf5c7f14ef203e86"
event: "push"
trigger_event: "push"
is_fork_pull_request: 0
status: 1
started: 1683636528
stopped: 1683636626
created: 1683636108
updated: 1683636626
need_approval: 0
approved_by: 0
14 changes: 14 additions & 0 deletions models/fixtures/action_run_job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,17 @@
status: 5
started: 1683636528
stopped: 1683636626
-
id: 205
run_id: 804
repo_id: 6
owner_id: 10
commit_sha: 6e64b26de7ba966d01d90ecfaf5c7f14ef203e86
is_fork_pull_request: 0
name: job_2
attempt: 1
job_id: job_2
task_id: 48
status: 1
started: 1683636528
stopped: 1683636626
20 changes: 20 additions & 0 deletions models/fixtures/action_task.yml
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,23 @@
log_length: 0
log_size: 0
log_expired: 0
-
id: 55
job_id: 205
attempt: 1
runner_id: 1
status: 6 # 6 is the status code for "running"
started: 1683636528
stopped: 1683636626
repo_id: 6
owner_id: 10
commit_sha: 6e64b26de7ba966d01d90ecfaf5c7f14ef203e86
is_fork_pull_request: 0
token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc478422b
token_salt: ERxJGHvg3I
token_last_eight: 182199eb
log_filename: collaborative-owner-test/1a/49.log
log_in_storage: 1
log_length: 707
log_size: 90179
log_expired: 0
7 changes: 7 additions & 0 deletions models/fixtures/repo_unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -733,3 +733,10 @@
type: 3
config: "{\"IgnoreWhitespaceConflicts\":false,\"AllowMerge\":true,\"AllowRebase\":true,\"AllowRebaseMerge\":true,\"AllowSquash\":true}"
created_unix: 946684810

-
id: 111
repo_id: 3
type: 10
config: "{}"
created_unix: 946684810
17 changes: 17 additions & 0 deletions models/repo/repo_unit.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ func (cfg *PullRequestsConfig) GetDefaultMergeStyle() MergeStyle {

type ActionsConfig struct {
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) {
Expand All @@ -192,6 +195,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)
Expand Down
11 changes: 6 additions & 5 deletions models/user/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package user
import (
"context"
"fmt"
"slices"
"strings"

"code.gitea.io/gitea/models/db"
Expand All @@ -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
Expand All @@ -43,16 +44,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 {
switch opts.Type {
case UserTypeIndividual:
switch {
case slices.Contains(opts.Types, UserTypeIndividual):
cond = cond.Or(builder.Eq{"type": UserTypeUserReserved}).Or(
builder.Eq{"type": UserTypeBot},
).Or(
builder.Eq{"type": UserTypeRemoteUser},
)
case UserTypeOrganization:
case slices.Contains(opts.Types, UserTypeOrganization):
cond = cond.Or(builder.Eq{"type": UserTypeOrganizationReserved})
}
}
Expand Down
12 changes: 12 additions & 0 deletions models/user/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -1444,3 +1444,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
}
4 changes: 2 additions & 2 deletions models/user/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,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)
}

Expand All @@ -150,7 +150,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)
}

Expand Down
9 changes: 9 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3907,6 +3907,15 @@ variables.update.success = The variable has been edited.
logs.always_auto_scroll = Always auto scroll logs
logs.always_expand_running = Always expand running logs

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
type-1.display_name = Individual Project
Expand Down
2 changes: 1 addition & 1 deletion routers/api/v1/admin/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down
2 changes: 1 addition & 1 deletion routers/api/v1/admin/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,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,
Expand Down
2 changes: 1 addition & 1 deletion routers/api/v1/org/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,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,
})
Expand Down
2 changes: 1 addition & 1 deletion routers/api/v1/user/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,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,
Expand Down
2 changes: 1 addition & 1 deletion routers/web/admin/orgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion routers/web/admin/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,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,
},
Expand Down
2 changes: 1 addition & 1 deletion routers/web/explore/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down
2 changes: 1 addition & 1 deletion routers/web/explore/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,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},
Expand Down
2 changes: 1 addition & 1 deletion routers/web/home.go
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down
13 changes: 11 additions & 2 deletions routers/web/repo/githttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,17 @@ func httpBase(ctx *context.Context) *serviceHandler {
return nil
}
if task.RepoID != repo.ID {
ctx.PlainText(http.StatusForbidden, "User permission denied")
return nil
if err := task.LoadRepository(ctx); err != nil {
ctx.ServerError("LoadRepository", err)
return nil
}
actionsCfg := repo.MustGetUnit(ctx, unit.TypeActions).ActionsConfig()
if !actionsCfg.IsCollaborativeOwner(task.Repo.OwnerID) || !task.Repo.IsPrivate {
// The task repo can access the current repo only if the task repo is private and
// the owner of the task repo is a collaborative owner of the current repo.
ctx.PlainText(http.StatusForbidden, "User permission denied")
return nil
}
}

if task.IsForkPullRequest {
Expand Down
Loading