From 8d98450d7005fc050bffce08244a2d59b88c6d30 Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Tue, 24 Sep 2019 19:03:08 +0200 Subject: [PATCH 01/18] Add team permission setting to allow creating repo in organization. Signed-off-by: David Svantesson --- models/org.go | 44 +++++++++++++++++++++++++---- models/org_team.go | 23 ++++++++-------- models/repo.go | 13 ++++++++- models/repo_collaboration.go | 46 ++++++++++++++----------------- modules/auth/org.go | 9 +++--- modules/context/org.go | 21 ++++++++++---- modules/structs/org_team.go | 9 ++++-- options/locale/locale_en-US.ini | 2 ++ routers/api/v1/convert/convert.go | 11 ++++---- routers/api/v1/org/team.go | 10 ++++--- routers/api/v1/repo/repo.go | 8 +++--- routers/org/teams.go | 10 ++++--- routers/repo/repo.go | 10 +++---- templates/org/home.tmpl | 2 +- templates/org/team/new.tmpl | 8 ++++++ templates/swagger/v1_json.tmpl | 12 ++++++++ 16 files changed, 160 insertions(+), 78 deletions(-) diff --git a/models/org.go b/models/org.go index ca3bce81a1c19..1da3a84fb7964 100644 --- a/models/org.go +++ b/models/org.go @@ -29,6 +29,10 @@ func (org *User) IsOrgMember(uid int64) (bool, error) { return IsOrganizationMember(org.ID, uid) } +func (org *User) CanCreateOrgRepo(uid int64) (bool, error) { + return CanCreateOrgRepo(org.ID, uid) +} + func (org *User) getTeam(e Engine, name string) (*Team, error) { return getTeam(e, org.ID, name) } @@ -149,11 +153,12 @@ func CreateOrganization(org, owner *User) (err error) { // Create default owner team. t := &Team{ - OrgID: org.ID, - LowerName: strings.ToLower(ownerTeamName), - Name: ownerTeamName, - Authorize: AccessModeOwner, - NumMembers: 1, + OrgID: org.ID, + LowerName: strings.ToLower(ownerTeamName), + Name: ownerTeamName, + Authorize: AccessModeOwner, + NumMembers: 1, + CanCreateOrgRepo: true, } if _, err = sess.Insert(t); err != nil { return fmt.Errorf("insert owner team: %v", err) @@ -335,6 +340,18 @@ func IsPublicMembership(orgID, uid int64) (bool, error) { Exist() } +func CanCreateOrgRepo(orgID, uid int64) (bool, error) { + if owner, err := IsOrganizationOwner(orgID, uid); owner == true || err != nil { + return owner, err + } + return x. + Where("team.can_create_org_repo = true"). + Join("INNER", "team_user", "team_user.team_id = team.id"). + And("team_user.uid = ?", uid). + And("team_user.org_id = ?", orgID). + Exist(new(Team)) +} + func getOrgsByUserID(sess *xorm.Session, userID int64, showAll bool) ([]*User, error) { orgs := make([]*User, 0, 10) if !showAll { @@ -366,6 +383,17 @@ func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) { Find(&orgs) } +func getOrgsCanCreateRepoByUserIDDesc(sess *xorm.Session, userID int64) ([]*User, error) { + orgs := make([]*User, 0, 10) + return orgs, sess. + Join("INNER", "`team_user`", "`team_user`.org_id=`user`.id"). + Join("INNER", "`team`", "`team`.id=`team_user`.team_id"). + Where("`team_user`.uid=?", userID). + And(builder.Eq{"`team`.authorize": AccessModeOwner}.Or(builder.Eq{"`team`.can_create_org_repo": true})). + Asc("`user`.name"). + Find(&orgs) +} + // HasOrgVisible tells if the given user can see the given org func HasOrgVisible(org *User, user *User) bool { return hasOrgVisible(x, org, user) @@ -414,6 +442,12 @@ func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) { return getOwnedOrgsByUserID(x.Desc(desc), userID) } +// GetOrgsCanCreateRepoByUserIDDesc returns a list of organizations where given user ID +// are allowed to create repos, ordered descending by the given condition. +func GetOrgsCanCreateRepoByUserIDDesc(userID int64, desc string) ([]*User, error) { + return getOrgsCanCreateRepoByUserIDDesc(x.Desc(desc), userID) +} + // GetOrgUsersByUserID returns all organization-user relations by user ID. func GetOrgUsersByUserID(uid int64, all bool) ([]*OrgUser, error) { ous := make([]*OrgUser, 0, 10) diff --git a/models/org_team.go b/models/org_team.go index 90a089417da1b..c315a514f0819 100644 --- a/models/org_team.go +++ b/models/org_team.go @@ -21,17 +21,18 @@ const ownerTeamName = "Owners" // Team represents a organization team. type Team struct { - ID int64 `xorm:"pk autoincr"` - OrgID int64 `xorm:"INDEX"` - LowerName string - Name string - Description string - Authorize AccessMode - Repos []*Repository `xorm:"-"` - Members []*User `xorm:"-"` - NumRepos int - NumMembers int - Units []*TeamUnit `xorm:"-"` + ID int64 `xorm:"pk autoincr"` + OrgID int64 `xorm:"INDEX"` + LowerName string + Name string + Description string + Authorize AccessMode + Repos []*Repository `xorm:"-"` + Members []*User `xorm:"-"` + NumRepos int + NumMembers int + Units []*TeamUnit `xorm:"-"` + CanCreateOrgRepo bool `xorm:"NOT NULL DEFAULT false"` } // ColorFormat provides a basic color format for a Team diff --git a/models/repo.go b/models/repo.go index ffd1c30606b14..6bb2780cc4946 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1326,7 +1326,18 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err return fmt.Errorf("getOwnerTeam: %v", err) } else if err = t.addRepository(e, repo); err != nil { return fmt.Errorf("addRepository: %v", err) - } else if err = prepareWebhooks(e, repo, HookEventRepository, &api.RepositoryPayload{ + } + + if !t.IsMember(doer.ID) { + // If creator not in owner team, make repo admin + if err = repo.addCollaborator(e, doer); err != nil { + return fmt.Errorf("AddCollaborator: %v", err) + } else if err = repo.changeCollaborationAccessMode(e, doer.ID, AccessModeAdmin); err != nil { + return fmt.Errorf("ChangeCollaborationAccessMode: %v", err) + } + } + + if err = prepareWebhooks(e, repo, HookEventRepository, &api.RepositoryPayload{ Action: api.HookRepoCreated, Repository: repo.innerAPIFormat(e, AccessModeOwner, false), Organization: u.APIFormat(), diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go index 40ddf6a28cfec..1c49eed3c8cc8 100644 --- a/models/repo_collaboration.go +++ b/models/repo_collaboration.go @@ -16,14 +16,13 @@ type Collaboration struct { Mode AccessMode `xorm:"DEFAULT 2 NOT NULL"` } -// AddCollaborator adds new collaboration to a repository with default access mode. -func (repo *Repository) AddCollaborator(u *User) error { +func (repo *Repository) addCollaborator(e Engine, u *User) error { collaboration := &Collaboration{ RepoID: repo.ID, UserID: u.ID, } - has, err := x.Get(collaboration) + has, err := e.Get(collaboration) if err != nil { return err } else if has { @@ -31,26 +30,25 @@ func (repo *Repository) AddCollaborator(u *User) error { } collaboration.Mode = AccessModeWrite - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return err - } - - if _, err = sess.InsertOne(collaboration); err != nil { + if _, err = e.InsertOne(collaboration); err != nil { return err } if repo.Owner.IsOrganization() { - err = repo.recalculateTeamAccesses(sess, 0) + err = repo.recalculateTeamAccesses(e, 0) } else { - err = repo.recalculateAccesses(sess) + err = repo.recalculateAccesses(e) } if err != nil { return fmt.Errorf("recalculateAccesses 'team=%v': %v", repo.Owner.IsOrganization(), err) } - return sess.Commit() + return nil +} + +// AddCollaborator adds new collaboration to a repository with default access mode. +func (repo *Repository) AddCollaborator(u *User) error { + return repo.addCollaborator(x, u) } func (repo *Repository) getCollaborations(e Engine) ([]*Collaboration, error) { @@ -98,8 +96,7 @@ func (repo *Repository) IsCollaborator(userID int64) (bool, error) { return repo.isCollaborator(x, userID) } -// ChangeCollaborationAccessMode sets new access mode for the collaboration. -func (repo *Repository) ChangeCollaborationAccessMode(uid int64, mode AccessMode) error { +func (repo *Repository) changeCollaborationAccessMode(e Engine, uid int64, mode AccessMode) error { // Discard invalid input if mode <= AccessModeNone || mode > AccessModeOwner { return nil @@ -109,7 +106,7 @@ func (repo *Repository) ChangeCollaborationAccessMode(uid int64, mode AccessMode RepoID: repo.ID, UserID: uid, } - has, err := x.Get(collaboration) + has, err := e.Get(collaboration) if err != nil { return fmt.Errorf("get collaboration: %v", err) } else if !has { @@ -121,22 +118,21 @@ func (repo *Repository) ChangeCollaborationAccessMode(uid int64, mode AccessMode } collaboration.Mode = mode - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return err - } - - if _, err = sess. + if _, err = e. ID(collaboration.ID). Cols("mode"). Update(collaboration); err != nil { return fmt.Errorf("update collaboration: %v", err) - } else if _, err = sess.Exec("UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?", mode, uid, repo.ID); err != nil { + } else if _, err = e.Exec("UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?", mode, uid, repo.ID); err != nil { return fmt.Errorf("update access table: %v", err) } - return sess.Commit() + return nil +} + +// ChangeCollaborationAccessMode sets new access mode for the collaboration. +func (repo *Repository) ChangeCollaborationAccessMode(uid int64, mode AccessMode) error { + return repo.changeCollaborationAccessMode(x, uid, mode) } // DeleteCollaboration removes collaboration relation between the user and repository. diff --git a/modules/auth/org.go b/modules/auth/org.go index 2abffdf74eca7..235d98c4047c2 100644 --- a/modules/auth/org.go +++ b/modules/auth/org.go @@ -57,10 +57,11 @@ func (f *UpdateOrgSettingForm) Validate(ctx *macaron.Context, errs binding.Error // CreateTeamForm form for creating team type CreateTeamForm struct { - TeamName string `binding:"Required;AlphaDashDot;MaxSize(30)"` - Description string `binding:"MaxSize(255)"` - Permission string - Units []models.UnitType + TeamName string `binding:"Required;AlphaDashDot;MaxSize(30)"` + Description string `binding:"MaxSize(255)"` + Permission string + Units []models.UnitType + CanCreateOrgRepo bool } // Validate validates the fields diff --git a/modules/context/org.go b/modules/context/org.go index 48674743340c5..b315ef6a08949 100644 --- a/modules/context/org.go +++ b/modules/context/org.go @@ -15,12 +15,13 @@ import ( // Organization contains organization context type Organization struct { - IsOwner bool - IsMember bool - IsTeamMember bool // Is member of team. - IsTeamAdmin bool // In owner team or team that has admin permission level. - Organization *models.User - OrgLink string + IsOwner bool + IsMember bool + IsTeamMember bool // Is member of team. + IsTeamAdmin bool // In owner team or team that has admin permission level. + Organization *models.User + OrgLink string + CanCreateOrgRepo bool Team *models.Team } @@ -73,6 +74,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { ctx.Org.IsMember = true ctx.Org.IsTeamMember = true ctx.Org.IsTeamAdmin = true + ctx.Org.CanCreateOrgRepo = true } else if ctx.IsSigned { ctx.Org.IsOwner, err = org.IsOwnedBy(ctx.User.ID) if err != nil { @@ -84,12 +86,18 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { ctx.Org.IsMember = true ctx.Org.IsTeamMember = true ctx.Org.IsTeamAdmin = true + ctx.Org.CanCreateOrgRepo = true } else { ctx.Org.IsMember, err = org.IsOrgMember(ctx.User.ID) if err != nil { ctx.ServerError("IsOrgMember", err) return } + ctx.Org.CanCreateOrgRepo, err = org.CanCreateOrgRepo(ctx.User.ID) + if err != nil { + ctx.ServerError("CanCreateOrgRepo", err) + return + } } } else { // Fake data. @@ -102,6 +110,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { } ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember + ctx.Data["CanCreateOrgRepo"] = ctx.Org.CanCreateOrgRepo ctx.Org.OrgLink = setting.AppSubURL + "/org/" + org.Name ctx.Data["OrgLink"] = ctx.Org.OrgLink diff --git a/modules/structs/org_team.go b/modules/structs/org_team.go index bea4a10ad494d..8180f626b4869 100644 --- a/modules/structs/org_team.go +++ b/modules/structs/org_team.go @@ -14,7 +14,8 @@ type Team struct { // enum: none,read,write,admin,owner Permission string `json:"permission"` // example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"] - Units []string `json:"units"` + Units []string `json:"units"` + CanCreateOrgRepo bool `json:"can_create_org_repo"` } // CreateTeamOption options for creating a team @@ -25,7 +26,8 @@ type CreateTeamOption struct { // enum: read,write,admin Permission string `json:"permission"` // example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"] - Units []string `json:"units"` + Units []string `json:"units"` + CanCreateOrgRepo bool `json:"can_create_org_repo"` } // EditTeamOption options for editing a team @@ -36,5 +38,6 @@ type EditTeamOption struct { // enum: read,write,admin Permission string `json:"permission"` // example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"] - Units []string `json:"units"` + Units []string `json:"units"` + CanCreateOrgRepo bool `json:"can_create_org_repo"` } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 7bb45396846e1..9b3207703a91f 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1516,6 +1516,8 @@ members.invite_now = Invite Now teams.join = Join teams.leave = Leave +teams.can_create_org_repo = Create repository permission +teams.can_create_org_repo_helper = Team members are allowed to create repositories in organization. teams.read_access = Read Access teams.read_access_helper = Members can view and clone team repositories. teams.write_access = Write Access diff --git a/routers/api/v1/convert/convert.go b/routers/api/v1/convert/convert.go index e0e7f609c7417..48e8d039ac135 100644 --- a/routers/api/v1/convert/convert.go +++ b/routers/api/v1/convert/convert.go @@ -221,11 +221,12 @@ func ToOrganization(org *models.User) *api.Organization { // ToTeam convert models.Team to api.Team func ToTeam(team *models.Team) *api.Team { return &api.Team{ - ID: team.ID, - Name: team.Name, - Description: team.Description, - Permission: team.Authorize.String(), - Units: team.GetUnitNames(), + ID: team.ID, + Name: team.Name, + Description: team.Description, + Permission: team.Authorize.String(), + Units: team.GetUnitNames(), + CanCreateOrgRepo: team.CanCreateOrgRepo, } } diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index 7b8fd12fba9a5..21b56f54130d8 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -125,10 +125,11 @@ func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) { // "201": // "$ref": "#/responses/Team" team := &models.Team{ - OrgID: ctx.Org.Organization.ID, - Name: form.Name, - Description: form.Description, - Authorize: models.ParseAccessMode(form.Permission), + OrgID: ctx.Org.Organization.ID, + Name: form.Name, + Description: form.Description, + Authorize: models.ParseAccessMode(form.Permission), + CanCreateOrgRepo: form.CanCreateOrgRepo, } unitTypes := models.FindUnitTypes(form.Units...) @@ -183,6 +184,7 @@ func EditTeam(ctx *context.APIContext, form api.EditTeamOption) { team.Description = form.Description team.Authorize = models.ParseAccessMode(form.Permission) unitTypes := models.FindUnitTypes(form.Units...) + team.CanCreateOrgRepo = form.CanCreateOrgRepo if team.Authorize < models.AccessModeOwner { var units = make([]*models.TeamUnit, 0, len(form.Units)) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index c838ba7271c21..63510625bce63 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -306,12 +306,12 @@ func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) { } if !ctx.User.IsAdmin { - isOwner, err := org.IsOwnedBy(ctx.User.ID) + canCreate, err := org.CanCreateOrgRepo(ctx.User.ID) if err != nil { - ctx.ServerError("IsOwnedBy", err) + ctx.ServerError("CanCreateOrgRepo", err) return - } else if !isOwner { - ctx.Error(403, "", "Given user is not owner of organization.") + } else if !canCreate { + ctx.Error(403, "", "Given user is not allowed to create repository in organization.") return } } diff --git a/routers/org/teams.go b/routers/org/teams.go index 7ead6ea5ff8ca..c76890db368d8 100644 --- a/routers/org/teams.go +++ b/routers/org/teams.go @@ -182,10 +182,11 @@ func NewTeamPost(ctx *context.Context, form auth.CreateTeamForm) { ctx.Data["Units"] = models.Units t := &models.Team{ - OrgID: ctx.Org.Organization.ID, - Name: form.TeamName, - Description: form.Description, - Authorize: models.ParseAccessMode(form.Permission), + OrgID: ctx.Org.Organization.ID, + Name: form.TeamName, + Description: form.Description, + Authorize: models.ParseAccessMode(form.Permission), + CanCreateOrgRepo: form.CanCreateOrgRepo, } if t.Authorize < models.AccessModeOwner { @@ -294,6 +295,7 @@ func EditTeamPost(ctx *context.Context, form auth.CreateTeamForm) { return } } + t.CanCreateOrgRepo = form.CanCreateOrgRepo if ctx.HasError() { ctx.HTML(200, tplTeamNew) diff --git a/routers/repo/repo.go b/routers/repo/repo.go index b67384d72193f..615917c06d6f7 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -52,9 +52,9 @@ func MustBeAbleToUpload(ctx *context.Context) { } func checkContextUser(ctx *context.Context, uid int64) *models.User { - orgs, err := models.GetOwnedOrgsByUserIDDesc(ctx.User.ID, "updated_unix") + orgs, err := models.GetOrgsCanCreateRepoByUserIDDesc(ctx.User.ID, "updated_unix") if err != nil { - ctx.ServerError("GetOwnedOrgsByUserIDDesc", err) + ctx.ServerError("GetOrgsCanCreateRepoByUserIDDesc", err) return nil } ctx.Data["Orgs"] = orgs @@ -80,11 +80,11 @@ func checkContextUser(ctx *context.Context, uid int64) *models.User { return nil } if !ctx.User.IsAdmin { - isOwner, err := org.IsOwnedBy(ctx.User.ID) + canCreate, err := org.CanCreateOrgRepo(ctx.User.ID) if err != nil { - ctx.ServerError("IsOwnedBy", err) + ctx.ServerError("CanCreateOrgRepo", err) return nil - } else if !isOwner { + } else if !canCreate { ctx.Error(403) return nil } diff --git a/templates/org/home.tmpl b/templates/org/home.tmpl index 03bb5252767f7..0aa575707ab3c 100644 --- a/templates/org/home.tmpl +++ b/templates/org/home.tmpl @@ -22,7 +22,7 @@
- {{if .IsOrganizationOwner}} + {{if .CanCreateOrgRepo}} diff --git a/templates/org/team/new.tmpl b/templates/org/team/new.tmpl index fb79c9b7fbac5..870de4708163f 100644 --- a/templates/org/team/new.tmpl +++ b/templates/org/team/new.tmpl @@ -48,6 +48,14 @@ {{.i18n.Tr "org.teams.admin_access_helper"}}
+ +
+
+ + + {{.i18n.Tr "org.teams.can_create_org_repo_helper"}} +
+
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index a5fef2f5e6c39..25445349ba30a 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -7909,6 +7909,10 @@ "name" ], "properties": { + "can_create_org_repo": { + "type": "boolean", + "x-go-name": "CanCreateOrgRepo" + }, "description": { "type": "string", "x-go-name": "Description" @@ -8451,6 +8455,10 @@ "name" ], "properties": { + "can_create_org_repo": { + "type": "boolean", + "x-go-name": "CanCreateOrgRepo" + }, "description": { "type": "string", "x-go-name": "Description" @@ -10011,6 +10019,10 @@ "description": "Team represents a team in an organization", "type": "object", "properties": { + "can_create_org_repo": { + "type": "boolean", + "x-go-name": "CanCreateOrgRepo" + }, "description": { "type": "string", "x-go-name": "Description" From 81a61357a8c1c888950bf57817d5fad36eea9e74 Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Tue, 24 Sep 2019 21:31:25 +0200 Subject: [PATCH 02/18] Add test case for creating repo when have team creation access. Signed-off-by: David Svantesson --- integrations/api_repo_test.go | 2 ++ models/fixtures/team.yml | 20 ++++++++++++++++++++ models/fixtures/team_user.yml | 12 ++++++++++++ models/fixtures/user.yml | 22 +++++++++++++++++++++- 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go index 60fe4a3649576..cdc03d90856de 100644 --- a/integrations/api_repo_test.go +++ b/integrations/api_repo_test.go @@ -347,6 +347,8 @@ func TestAPIOrgRepoCreate(t *testing.T) { {ctxUserID: 1, orgName: "user3", repoName: "repo-admin", expectedStatus: http.StatusCreated}, {ctxUserID: 2, orgName: "user3", repoName: "repo-own", expectedStatus: http.StatusCreated}, {ctxUserID: 2, orgName: "user6", repoName: "repo-bad-org", expectedStatus: http.StatusForbidden}, + {ctxUserID: 27, orgName: "user3", repoName: "repo-creator", expectedStatus: http.StatusCreated}, + {ctxUserID: 27, orgName: "user6", repoName: "repo-not-creator", expectedStatus: http.StatusForbidden}, } prepareTestEnv(t) diff --git a/models/fixtures/team.yml b/models/fixtures/team.yml index 4da87b731fff0..b7e38561720e2 100644 --- a/models/fixtures/team.yml +++ b/models/fixtures/team.yml @@ -96,3 +96,23 @@ authorize: 1 # read num_repos: 0 num_members: 0 + +- + id: 12 + org_id: 3 + lower_name: team12creators + name: team12Creators + authorize: 3 # admin + num_repos: 0 + num_members: 1 + can_create_org_repo: true + +- + id: 13 + org_id: 6 + lower_name: team13notcreators + name: team13NotCreators + authorize: 3 # admin + num_repos: 0 + num_members: 1 + can_create_org_repo: false diff --git a/models/fixtures/team_user.yml b/models/fixtures/team_user.yml index 4fc609791d396..b50b145eb6a6b 100644 --- a/models/fixtures/team_user.yml +++ b/models/fixtures/team_user.yml @@ -69,3 +69,15 @@ org_id: 25 team_id: 10 uid: 24 + +- + id: 13 + org_id: 3 + team_id: 12 + uid: 27 + +- + id: 14 + org_id: 6 + team_id: 13 + uid: 27 diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index a204241f9c790..d5d7b281e7721 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -427,4 +427,24 @@ num_repos: 1 num_members: 0 num_teams: 1 - repo_admin_change_team_access: true \ No newline at end of file + repo_admin_change_team_access: true + +- + id: 27 + lower_name: user27 + name: user27 + full_name: "user27" + email: user27@example.com + keep_email_private: true + passwd: 7d93daa0d1e6f2305cc8fa496847d61dc7320bb16262f9c55dd753480207234cdd96a93194e408341971742f4701772a025a # password + type: 0 # individual + salt: ZogKvWdyEx + is_admin: false + avatar: avatar27 + avatar_email: user27@example.com + num_repos: 0 + num_stars: 0 + num_followers: 0 + num_following: 0 + is_active: true + From f20cfd39fb2325cc7592b61de45cae8359ef4fc6 Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Sun, 29 Sep 2019 13:16:18 +0200 Subject: [PATCH 03/18] build error: should omit comparison to bool constant Signed-off-by: David Svantesson --- models/org.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/org.go b/models/org.go index 1da3a84fb7964..e74215098e4bb 100644 --- a/models/org.go +++ b/models/org.go @@ -341,7 +341,7 @@ func IsPublicMembership(orgID, uid int64) (bool, error) { } func CanCreateOrgRepo(orgID, uid int64) (bool, error) { - if owner, err := IsOrganizationOwner(orgID, uid); owner == true || err != nil { + if owner, err := IsOrganizationOwner(orgID, uid); owner || err != nil { return owner, err } return x. From 64a33f6b02106f8d1a40f5bade8d3b2981d4bbad Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Sun, 29 Sep 2019 13:23:07 +0200 Subject: [PATCH 04/18] Add comment on exported functions --- models/org.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/models/org.go b/models/org.go index e74215098e4bb..b298fc777a64f 100644 --- a/models/org.go +++ b/models/org.go @@ -29,6 +29,7 @@ func (org *User) IsOrgMember(uid int64) (bool, error) { return IsOrganizationMember(org.ID, uid) } +// CanCreateOrgRepo returns true if given user can create repo in organization func (org *User) CanCreateOrgRepo(uid int64) (bool, error) { return CanCreateOrgRepo(org.ID, uid) } @@ -340,6 +341,7 @@ func IsPublicMembership(orgID, uid int64) (bool, error) { Exist() } +// CanCreateOrgRepo returns true if user can create repo in organization func CanCreateOrgRepo(orgID, uid int64) (bool, error) { if owner, err := IsOrganizationOwner(orgID, uid); owner || err != nil { return owner, err From f59f511ec037802ac2b93e497f122d5282106596 Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Sun, 29 Sep 2019 14:10:29 +0000 Subject: [PATCH 05/18] Fix fixture consistency, fix existing unit tests --- models/fixtures/org_user.yml | 13 +++++++++++++ models/fixtures/user.yml | 8 ++++---- models/org_test.go | 12 +++++++----- models/user_test.go | 4 ++-- models/userlist_test.go | 12 ++++++------ 5 files changed, 32 insertions(+), 17 deletions(-) diff --git a/models/fixtures/org_user.yml b/models/fixtures/org_user.yml index 385492dd68d11..cc0deccb0c7c2 100644 --- a/models/fixtures/org_user.yml +++ b/models/fixtures/org_user.yml @@ -45,3 +45,16 @@ uid: 24 org_id: 25 is_public: true + +- + id: 9 + uid: 27 + org_id: 3 + is_public: true + +- + id: 10 + uid: 27 + org_id: 6 + is_public: true + diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index d5d7b281e7721..a3bd22577a518 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -50,8 +50,8 @@ avatar: avatar3 avatar_email: user3@example.com num_repos: 3 - num_members: 2 - num_teams: 3 + num_members: 3 + num_teams: 4 - id: 4 @@ -102,8 +102,8 @@ avatar: avatar6 avatar_email: user6@example.com num_repos: 0 - num_members: 1 - num_teams: 1 + num_members: 2 + num_teams: 2 - id: 7 diff --git a/models/org_test.go b/models/org_test.go index 2f2c5a2d5eb19..31a0da6f52991 100644 --- a/models/org_test.go +++ b/models/org_test.go @@ -87,10 +87,11 @@ func TestUser_GetTeams(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) org := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) assert.NoError(t, org.GetTeams()) - if assert.Len(t, org.Teams, 3) { + if assert.Len(t, org.Teams, 4) { assert.Equal(t, int64(1), org.Teams[0].ID) assert.Equal(t, int64(2), org.Teams[1].ID) - assert.Equal(t, int64(7), org.Teams[2].ID) + assert.Equal(t, int64(12), org.Teams[2].ID) + assert.Equal(t, int64(7), org.Teams[3].ID) } } @@ -98,9 +99,10 @@ func TestUser_GetMembers(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) org := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) assert.NoError(t, org.GetMembers()) - if assert.Len(t, org.Members, 2) { + if assert.Len(t, org.Members, 3) { assert.Equal(t, int64(2), org.Members[0].ID) - assert.Equal(t, int64(4), org.Members[1].ID) + assert.Equal(t, int64(27), org.Members[1].ID) + assert.Equal(t, int64(4), org.Members[2].ID) } } @@ -395,7 +397,7 @@ func TestGetOrgUsersByOrgID(t *testing.T) { orgUsers, err := GetOrgUsersByOrgID(3) assert.NoError(t, err) - if assert.Len(t, orgUsers, 2) { + if assert.Len(t, orgUsers, 3) { assert.Equal(t, OrgUser{ ID: orgUsers[0].ID, OrgID: 3, diff --git a/models/user_test.go b/models/user_test.go index bcb955817c330..2525d8379dfc1 100644 --- a/models/user_test.go +++ b/models/user_test.go @@ -153,13 +153,13 @@ func TestSearchUsers(t *testing.T) { } testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1}, - []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24}) + []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27}) testUserSuccess(&SearchUserOptions{Page: 1, IsActive: util.OptionalBoolFalse}, []int64{9}) testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue}, - []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24}) + []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27}) testUserSuccess(&SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue}, []int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) diff --git a/models/userlist_test.go b/models/userlist_test.go index ca08cc90ce89c..c795960e60ffa 100644 --- a/models/userlist_test.go +++ b/models/userlist_test.go @@ -17,8 +17,8 @@ func TestUserListIsPublicMember(t *testing.T) { orgid int64 expected map[int64]bool }{ - {3, map[int64]bool{2: true, 4: false}}, - {6, map[int64]bool{5: true}}, + {3, map[int64]bool{2: true, 4: false, 27: true}}, + {6, map[int64]bool{5: true, 27: true}}, {7, map[int64]bool{5: false}}, {25, map[int64]bool{24: true}}, {22, map[int64]bool{}}, @@ -43,8 +43,8 @@ func TestUserListIsUserOrgOwner(t *testing.T) { orgid int64 expected map[int64]bool }{ - {3, map[int64]bool{2: true, 4: false}}, - {6, map[int64]bool{5: true}}, + {3, map[int64]bool{2: true, 4: false, 27: false}}, + {6, map[int64]bool{5: true, 27: false}}, {7, map[int64]bool{5: true}}, {25, map[int64]bool{24: false}}, // ErrTeamNotExist {22, map[int64]bool{}}, // No member @@ -69,8 +69,8 @@ func TestUserListIsTwoFaEnrolled(t *testing.T) { orgid int64 expected map[int64]bool }{ - {3, map[int64]bool{2: false, 4: false}}, - {6, map[int64]bool{5: false}}, + {3, map[int64]bool{2: false, 4: false, 27: false}}, + {6, map[int64]bool{5: false, 27: false}}, {7, map[int64]bool{5: false}}, {25, map[int64]bool{24: true}}, {22, map[int64]bool{}}, From 71f5c0049a8706c7801bc41d4672978d4320f82c Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Sun, 29 Sep 2019 17:24:01 +0200 Subject: [PATCH 06/18] Fix boolean comparison in xorm query. --- models/org.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/org.go b/models/org.go index b298fc777a64f..41663fab18349 100644 --- a/models/org.go +++ b/models/org.go @@ -347,7 +347,7 @@ func CanCreateOrgRepo(orgID, uid int64) (bool, error) { return owner, err } return x. - Where("team.can_create_org_repo = true"). + Where(builder.Eq{"team.can_create_org_repo": true}). Join("INNER", "team_user", "team_user.team_id = team.id"). And("team_user.uid = ?", uid). And("team_user.org_id = ?", orgID). From d3eb84f0ab1f87c581cf27bc46f2abbcd6dbfac4 Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Mon, 30 Sep 2019 08:38:19 +0200 Subject: [PATCH 07/18] addCollaborator and changeCollaborationAccessMode separate steps More clear to use different if-cases. --- models/repo.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models/repo.go b/models/repo.go index 6bb2780cc4946..21260c40fb514 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1332,7 +1332,8 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err // If creator not in owner team, make repo admin if err = repo.addCollaborator(e, doer); err != nil { return fmt.Errorf("AddCollaborator: %v", err) - } else if err = repo.changeCollaborationAccessMode(e, doer.ID, AccessModeAdmin); err != nil { + } + if err = repo.changeCollaborationAccessMode(e, doer.ID, AccessModeAdmin); err != nil { return fmt.Errorf("ChangeCollaborationAccessMode: %v", err) } } From e2080e8483ac9e96c9bfff40a55bfe927ee1fd74 Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Mon, 30 Sep 2019 08:39:56 +0200 Subject: [PATCH 08/18] Create and commit xorm session --- models/repo_collaboration.go | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go index 1c49eed3c8cc8..f9f789799dcba 100644 --- a/models/repo_collaboration.go +++ b/models/repo_collaboration.go @@ -48,7 +48,21 @@ func (repo *Repository) addCollaborator(e Engine, u *User) error { // AddCollaborator adds new collaboration to a repository with default access mode. func (repo *Repository) AddCollaborator(u *User) error { - return repo.addCollaborator(x, u) + sess := x.NewSession() + defer sess.Close() + if err = sess.Begin(); err != nil { + return err + } + + if err = repo.addCollaborator(sess, u); err != nil { + return err + } + + if err = sess.Commit(); err != nil { + return err + } + + return nil } func (repo *Repository) getCollaborations(e Engine) ([]*Collaboration, error) { @@ -132,7 +146,21 @@ func (repo *Repository) changeCollaborationAccessMode(e Engine, uid int64, mode // ChangeCollaborationAccessMode sets new access mode for the collaboration. func (repo *Repository) ChangeCollaborationAccessMode(uid int64, mode AccessMode) error { - return repo.changeCollaborationAccessMode(x, uid, mode) + sess := x.NewSession() + defer sess.Close() + if err = sess.Begin(); err != nil { + return err + } + + if err = repo.changeCollaborationAccessMode(sess, uid, mode); err != nil { + return err + } + + if err = sess.Commit(); err != nil { + return err + } + + return nil } // DeleteCollaboration removes collaboration relation between the user and repository. From 9da17c121229b4cb6b8606726ddc587d342143f1 Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Mon, 30 Sep 2019 14:16:59 +0000 Subject: [PATCH 09/18] fix --- models/repo_collaboration.go | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go index f9f789799dcba..38c03b492dd2b 100644 --- a/models/repo_collaboration.go +++ b/models/repo_collaboration.go @@ -50,19 +50,15 @@ func (repo *Repository) addCollaborator(e Engine, u *User) error { func (repo *Repository) AddCollaborator(u *User) error { sess := x.NewSession() defer sess.Close() - if err = sess.Begin(); err != nil { - return err - } - - if err = repo.addCollaborator(sess, u); err != nil { + if err := sess.Begin(); err != nil { return err } - if err = sess.Commit(); err != nil { + if err := repo.addCollaborator(sess, u); err != nil { return err } - return nil + return sess.Commit() } func (repo *Repository) getCollaborations(e Engine) ([]*Collaboration, error) { @@ -148,19 +144,15 @@ func (repo *Repository) changeCollaborationAccessMode(e Engine, uid int64, mode func (repo *Repository) ChangeCollaborationAccessMode(uid int64, mode AccessMode) error { sess := x.NewSession() defer sess.Close() - if err = sess.Begin(); err != nil { - return err - } - - if err = repo.changeCollaborationAccessMode(sess, uid, mode); err != nil { + if err := sess.Begin(); err != nil { return err } - if err = sess.Commit(); err != nil { + if err := repo.changeCollaborationAccessMode(sess, uid, mode); err != nil { return err } - return nil + return sess.Commit() } // DeleteCollaboration removes collaboration relation between the user and repository. From af050d99999cb8fb7a8e3f679276c8beed6d7c78 Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Mon, 30 Sep 2019 14:40:56 +0000 Subject: [PATCH 10/18] Add information of create repo permission in team sidebar --- options/locale/locale_en-US.ini | 1 + templates/org/team/sidebar.tmpl | 3 +++ 2 files changed, 4 insertions(+) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 9b3207703a91f..06618737fb0df 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1537,6 +1537,7 @@ teams.delete_team_success = The team has been deleted. teams.read_permission_desc = This team grants Read access: members can view and clone team repositories. teams.write_permission_desc = This team grants Write access: members can read from and push to team repositories. teams.admin_permission_desc = This team grants Admin access: members can read from, push to and add collaborators to team repositories. +teams.create_repo_permission_desc = Additionally, this team grants Create repository permission: members can create new repositories in organization. teams.repositories = Team Repositories teams.search_repo_placeholder = Search repository… teams.add_team_repository = Add Team Repository diff --git a/templates/org/team/sidebar.tmpl b/templates/org/team/sidebar.tmpl index 846613e32e77f..68b2e4659a4e7 100644 --- a/templates/org/team/sidebar.tmpl +++ b/templates/org/team/sidebar.tmpl @@ -28,6 +28,9 @@ {{else if (eq .Team.Authorize 3)}} {{.i18n.Tr "org.teams.admin_permission_desc" | Str2html}} {{end}} + {{if .Team.CanCreateOrgRepo}} +

{{.i18n.Tr "org.teams.create_repo_permission_desc" | Str2html}} + {{end}} {{if .IsOrganizationOwner}} From c2882c0660d3090188b72b5596e5b4b8aa98c5d2 Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Mon, 30 Sep 2019 17:43:12 +0200 Subject: [PATCH 11/18] Add migration step --- models/migrations/migrations.go | 2 ++ models/migrations/v98.go | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 models/migrations/v98.go diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 7680e7747c306..baa3dab6321db 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -250,6 +250,8 @@ var migrations = []Migration{ NewMigration("delete orphaned attachments", deleteOrphanedAttachments), // v97 -> v98 NewMigration("add repo_admin_change_team_access to user", addRepoAdminChangeTeamAccessColumnForUser), + // v98 -> v99 + NewMigration("add can_create_org_repo to team", addCanCreateOrgRepoColumnForTeam), } // Migrate database to current version diff --git a/models/migrations/v98.go b/models/migrations/v98.go new file mode 100644 index 0000000000000..07b540550e912 --- /dev/null +++ b/models/migrations/v98.go @@ -0,0 +1,15 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import "github.com/go-xorm/xorm" + +func addCanCreateOrgRepoColumnForTeam(x *xorm.Engine) error { + type Team struct { + CanCreateOrgRepo bool `xorm:"NOT NULL DEFAULT false"` + } + + return x.Sync2(new(Team)) +} From 3bf22f82f3e191c5413b06c387ed7acc25df7080 Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Thu, 3 Oct 2019 20:28:56 +0200 Subject: [PATCH 12/18] Clarify that repository creator will be administrator. --- options/locale/locale_en-US.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 06618737fb0df..1931a74f0d855 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1517,7 +1517,7 @@ members.invite_now = Invite Now teams.join = Join teams.leave = Leave teams.can_create_org_repo = Create repository permission -teams.can_create_org_repo_helper = Team members are allowed to create repositories in organization. +teams.can_create_org_repo_helper = Members can create new repositories in organization. Creator will get administrator access to the new repository. teams.read_access = Read Access teams.read_access_helper = Members can view and clone team repositories. teams.write_access = Write Access From dbe39359673241fe9ea8e32d34cbad931aaa4076 Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Thu, 7 Nov 2019 22:35:57 +0000 Subject: [PATCH 13/18] Fix some things after merge --- models/migrations/v106.go..go | 15 --------------- models/repo.go | 2 +- models/repo_collaboration.go | 2 +- routers/api/v1/convert/convert.go | 2 +- routers/api/v1/org/team.go | 2 +- routers/org/teams.go | 2 +- 6 files changed, 5 insertions(+), 20 deletions(-) delete mode 100644 models/migrations/v106.go..go diff --git a/models/migrations/v106.go..go b/models/migrations/v106.go..go deleted file mode 100644 index 07b540550e912..0000000000000 --- a/models/migrations/v106.go..go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2019 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package migrations - -import "github.com/go-xorm/xorm" - -func addCanCreateOrgRepoColumnForTeam(x *xorm.Engine) error { - type Team struct { - CanCreateOrgRepo bool `xorm:"NOT NULL DEFAULT false"` - } - - return x.Sync2(new(Team)) -} diff --git a/models/repo.go b/models/repo.go index c8c7ead0fac0e..59467775a80b1 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1471,7 +1471,7 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err } if err = repo.changeCollaborationAccessMode(e, doer.ID, AccessModeAdmin); err != nil { return fmt.Errorf("ChangeCollaborationAccessMode: %v", err) - } + } } } else if err = repo.recalculateAccesses(e); err != nil { // Organization automatically called this in addRepository method. diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go index fd073acb7e6c3..575d62092a5f8 100644 --- a/models/repo_collaboration.go +++ b/models/repo_collaboration.go @@ -34,7 +34,7 @@ func (repo *Repository) addCollaborator(e Engine, u *User) error { return err } - if err = repo.recalculateUserAccess(sess, u.ID); err != nil { + if err = repo.recalculateUserAccess(e, u.ID); err != nil { return fmt.Errorf("recalculateAccesses 'team=%v': %v", repo.Owner.IsOrganization(), err) } diff --git a/routers/api/v1/convert/convert.go b/routers/api/v1/convert/convert.go index b13f34608857c..6a243622db3b3 100644 --- a/routers/api/v1/convert/convert.go +++ b/routers/api/v1/convert/convert.go @@ -231,7 +231,7 @@ func ToTeam(team *models.Team) *api.Team { Name: team.Name, Description: team.Description, IncludesAllRepositories: team.IncludesAllRepositories, - CanCreateOrgRepo: team.CanCreateOrgRepo, + CanCreateOrgRepo: team.CanCreateOrgRepo, Permission: team.Authorize.String(), Units: team.GetUnitNames(), } diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index 23a1a53b0a477..8a2b98e042845 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -132,7 +132,7 @@ func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) { Name: form.Name, Description: form.Description, IncludesAllRepositories: form.IncludesAllRepositories, - CanCreateOrgRepo: form.CanCreateOrgRepo, + CanCreateOrgRepo: form.CanCreateOrgRepo, Authorize: models.ParseAccessMode(form.Permission), } diff --git a/routers/org/teams.go b/routers/org/teams.go index 0a5fc15e80d3c..8b525a8b386bf 100644 --- a/routers/org/teams.go +++ b/routers/org/teams.go @@ -189,7 +189,7 @@ func NewTeamPost(ctx *context.Context, form auth.CreateTeamForm) { Description: form.Description, Authorize: models.ParseAccessMode(form.Permission), IncludesAllRepositories: includesAllRepositories, - CanCreateOrgRepo: form.CanCreateOrgRepo, + CanCreateOrgRepo: form.CanCreateOrgRepo, } if t.Authorize < models.AccessModeOwner { From f5e5d1f838394f775dec6a46a79f65660c3edb05 Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Thu, 7 Nov 2019 22:48:30 +0000 Subject: [PATCH 14/18] Fix language text that use html --- templates/org/team/new.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/org/team/new.tmpl b/templates/org/team/new.tmpl index 8625d9aab23c8..c38fa4d94019a 100644 --- a/templates/org/team/new.tmpl +++ b/templates/org/team/new.tmpl @@ -31,14 +31,14 @@
- {{.i18n.Tr "org.teams.specific_repositories_helper"}} + {{.i18n.Tr "org.teams.specific_repositories_helper" | Str2html}}
- {{.i18n.Tr "org.teams.all_repositories_helper"}} + {{.i18n.Tr "org.teams.all_repositories_helper" | Str2html}}
From e0830aa45536055bedd9ced3e3954460e3f106a6 Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Thu, 7 Nov 2019 22:53:13 +0000 Subject: [PATCH 15/18] migrations file --- models/migrations/v106.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 models/migrations/v106.go diff --git a/models/migrations/v106.go b/models/migrations/v106.go new file mode 100644 index 0000000000000..abe731768116b --- /dev/null +++ b/models/migrations/v106.go @@ -0,0 +1,17 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import ( + "xorm.io/xorm" +) + +func addCanCreateOrgRepoColumnForTeam(x *xorm.Engine) error { + type Team struct { + CanCreateOrgRepo bool `xorm:"NOT NULL DEFAULT false"` + } + + return x.Sync2(new(Team)) +} From 0d00ba17fef6a02c949655f1d3d71c392b1e5f62 Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Thu, 7 Nov 2019 23:22:36 +0000 Subject: [PATCH 16/18] Create repository permission -> Create repositories --- options/locale/locale_en-US.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 86d5cc2f26528..6a08a1ab4ad59 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1562,7 +1562,7 @@ members.invite_now = Invite Now teams.join = Join teams.leave = Leave -teams.can_create_org_repo = Create repository permission +teams.can_create_org_repo = Create repositories teams.can_create_org_repo_helper = Members can create new repositories in organization. Creator will get administrator access to the new repository. teams.read_access = Read Access teams.read_access_helper = Members can view and clone team repositories. From 55f7cb30c4232c5d1a493793e61a74a5b504cdfc Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Tue, 12 Nov 2019 08:05:29 +0100 Subject: [PATCH 17/18] fix merge --- integrations/api_repo_test.go | 4 ++-- models/fixtures/user.yml | 8 ++++---- models/migrations/{v108..go => v108.go} | 0 models/org_test.go | 2 +- models/user_test.go | 4 ++-- models/userlist_test.go | 12 ++++++------ 6 files changed, 15 insertions(+), 15 deletions(-) rename models/migrations/{v108..go => v108.go} (100%) diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go index 85f47435c1739..e021911afdb2d 100644 --- a/integrations/api_repo_test.go +++ b/integrations/api_repo_test.go @@ -347,8 +347,8 @@ func TestAPIOrgRepoCreate(t *testing.T) { {ctxUserID: 1, orgName: "user3", repoName: "repo-admin", expectedStatus: http.StatusCreated}, {ctxUserID: 2, orgName: "user3", repoName: "repo-own", expectedStatus: http.StatusCreated}, {ctxUserID: 2, orgName: "user6", repoName: "repo-bad-org", expectedStatus: http.StatusForbidden}, - {ctxUserID: 27, orgName: "user3", repoName: "repo-creator", expectedStatus: http.StatusCreated}, - {ctxUserID: 27, orgName: "user6", repoName: "repo-not-creator", expectedStatus: http.StatusForbidden}, + {ctxUserID: 28, orgName: "user3", repoName: "repo-creator", expectedStatus: http.StatusCreated}, + {ctxUserID: 28, orgName: "user6", repoName: "repo-not-creator", expectedStatus: http.StatusForbidden}, } prepareTestEnv(t) diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index 3891f1d9b5b45..17294b881f08d 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -50,8 +50,8 @@ avatar: avatar3 avatar_email: user3@example.com num_repos: 3 - num_members: 2 - num_teams: 3 + num_members: 3 + num_teams: 4 - id: 4 @@ -102,8 +102,8 @@ avatar: avatar6 avatar_email: user6@example.com num_repos: 0 - num_members: 1 - num_teams: 1 + num_members: 2 + num_teams: 2 - id: 7 diff --git a/models/migrations/v108..go b/models/migrations/v108.go similarity index 100% rename from models/migrations/v108..go rename to models/migrations/v108.go diff --git a/models/org_test.go b/models/org_test.go index 31a0da6f52991..1a6b288dc75dd 100644 --- a/models/org_test.go +++ b/models/org_test.go @@ -101,7 +101,7 @@ func TestUser_GetMembers(t *testing.T) { assert.NoError(t, org.GetMembers()) if assert.Len(t, org.Members, 3) { assert.Equal(t, int64(2), org.Members[0].ID) - assert.Equal(t, int64(27), org.Members[1].ID) + assert.Equal(t, int64(28), org.Members[1].ID) assert.Equal(t, int64(4), org.Members[2].ID) } } diff --git a/models/user_test.go b/models/user_test.go index e9f84d0af6693..95f4d5d3633bb 100644 --- a/models/user_test.go +++ b/models/user_test.go @@ -153,13 +153,13 @@ func TestSearchUsers(t *testing.T) { } testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1}, - []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27}) + []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28}) testUserSuccess(&SearchUserOptions{Page: 1, IsActive: util.OptionalBoolFalse}, []int64{9}) testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue}, - []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27}) + []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 28}) testUserSuccess(&SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue}, []int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) diff --git a/models/userlist_test.go b/models/userlist_test.go index c795960e60ffa..c48cfb61c168b 100644 --- a/models/userlist_test.go +++ b/models/userlist_test.go @@ -17,8 +17,8 @@ func TestUserListIsPublicMember(t *testing.T) { orgid int64 expected map[int64]bool }{ - {3, map[int64]bool{2: true, 4: false, 27: true}}, - {6, map[int64]bool{5: true, 27: true}}, + {3, map[int64]bool{2: true, 4: false, 28: true}}, + {6, map[int64]bool{5: true, 28: true}}, {7, map[int64]bool{5: false}}, {25, map[int64]bool{24: true}}, {22, map[int64]bool{}}, @@ -43,8 +43,8 @@ func TestUserListIsUserOrgOwner(t *testing.T) { orgid int64 expected map[int64]bool }{ - {3, map[int64]bool{2: true, 4: false, 27: false}}, - {6, map[int64]bool{5: true, 27: false}}, + {3, map[int64]bool{2: true, 4: false, 28: false}}, + {6, map[int64]bool{5: true, 28: false}}, {7, map[int64]bool{5: true}}, {25, map[int64]bool{24: false}}, // ErrTeamNotExist {22, map[int64]bool{}}, // No member @@ -69,8 +69,8 @@ func TestUserListIsTwoFaEnrolled(t *testing.T) { orgid int64 expected map[int64]bool }{ - {3, map[int64]bool{2: false, 4: false, 27: false}}, - {6, map[int64]bool{5: false, 27: false}}, + {3, map[int64]bool{2: false, 4: false, 28: false}}, + {6, map[int64]bool{5: false, 28: false}}, {7, map[int64]bool{5: false}}, {25, map[int64]bool{24: true}}, {22, map[int64]bool{}}, From 8e14c34e540571b9b54a2cbd794f811db64a28c1 Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Tue, 12 Nov 2019 19:14:21 +0100 Subject: [PATCH 18/18] fix review comments --- models/org.go | 26 +++++++++++--------------- models/repo.go | 6 ++---- models/repo_collaboration.go | 6 +----- routers/repo/repo.go | 4 ++-- 4 files changed, 16 insertions(+), 26 deletions(-) diff --git a/models/org.go b/models/org.go index dcfc1fa947d3b..f14dad1dbb625 100644 --- a/models/org.go +++ b/models/org.go @@ -389,17 +389,6 @@ func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) { Find(&orgs) } -func getOrgsCanCreateRepoByUserIDDesc(sess *xorm.Session, userID int64) ([]*User, error) { - orgs := make([]*User, 0, 10) - return orgs, sess. - Join("INNER", "`team_user`", "`team_user`.org_id=`user`.id"). - Join("INNER", "`team`", "`team`.id=`team_user`.team_id"). - Where("`team_user`.uid=?", userID). - And(builder.Eq{"`team`.authorize": AccessModeOwner}.Or(builder.Eq{"`team`.can_create_org_repo": true})). - Asc("`user`.name"). - Find(&orgs) -} - // HasOrgVisible tells if the given user can see the given org func HasOrgVisible(org *User, user *User) bool { return hasOrgVisible(x, org, user) @@ -448,10 +437,17 @@ func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) { return getOwnedOrgsByUserID(x.Desc(desc), userID) } -// GetOrgsCanCreateRepoByUserIDDesc returns a list of organizations where given user ID -// are allowed to create repos, ordered descending by the given condition. -func GetOrgsCanCreateRepoByUserIDDesc(userID int64, desc string) ([]*User, error) { - return getOrgsCanCreateRepoByUserIDDesc(x.Desc(desc), userID) +// GetOrgsCanCreateRepoByUserID returns a list of organizations where given user ID +// are allowed to create repos. +func GetOrgsCanCreateRepoByUserID(userID int64) ([]*User, error) { + orgs := make([]*User, 0, 10) + + return orgs, x.Join("INNER", "`team_user`", "`team_user`.org_id=`user`.id"). + Join("INNER", "`team`", "`team`.id=`team_user`.team_id"). + Where("`team_user`.uid=?", userID). + And(builder.Eq{"`team`.authorize": AccessModeOwner}.Or(builder.Eq{"`team`.can_create_org_repo": true})). + Desc("`user`.updated_unix"). + Find(&orgs) } // GetOrgUsersByUserID returns all organization-user relations by user ID. diff --git a/models/repo.go b/models/repo.go index 2d373af1dbdba..e29496afb225b 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1586,11 +1586,9 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err } } - isAdmin, err := isUserRepoAdmin(e, repo, doer) - if err != nil { + if isAdmin, err := isUserRepoAdmin(e, repo, doer); err != nil { return fmt.Errorf("isUserRepoAdmin: %v", err) - } - if !isAdmin { + } else if !isAdmin { // Make creator repo admin if it wan't assigned automatically if err = repo.addCollaborator(e, doer); err != nil { return fmt.Errorf("AddCollaborator: %v", err) diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go index 575d62092a5f8..3c19a65ab9d01 100644 --- a/models/repo_collaboration.go +++ b/models/repo_collaboration.go @@ -34,11 +34,7 @@ func (repo *Repository) addCollaborator(e Engine, u *User) error { return err } - if err = repo.recalculateUserAccess(e, u.ID); err != nil { - return fmt.Errorf("recalculateAccesses 'team=%v': %v", repo.Owner.IsOrganization(), err) - } - - return nil + return repo.recalculateUserAccess(e, u.ID) } // AddCollaborator adds new collaboration to a repository with default access mode. diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 2fe187bd77df7..b78dd5376ee07 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -53,9 +53,9 @@ func MustBeAbleToUpload(ctx *context.Context) { } func checkContextUser(ctx *context.Context, uid int64) *models.User { - orgs, err := models.GetOrgsCanCreateRepoByUserIDDesc(ctx.User.ID, "updated_unix") + orgs, err := models.GetOrgsCanCreateRepoByUserID(ctx.User.ID) if err != nil { - ctx.ServerError("GetOrgsCanCreateRepoByUserIDDesc", err) + ctx.ServerError("GetOrgsCanCreateRepoByUserID", err) return nil } ctx.Data["Orgs"] = orgs