Skip to content

Commit 8d98450

Browse files
Add team permission setting to allow creating repo in organization.
Signed-off-by: David Svantesson <[email protected]>
1 parent 43c0249 commit 8d98450

File tree

16 files changed

+160
-78
lines changed

16 files changed

+160
-78
lines changed

models/org.go

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ func (org *User) IsOrgMember(uid int64) (bool, error) {
2929
return IsOrganizationMember(org.ID, uid)
3030
}
3131

32+
func (org *User) CanCreateOrgRepo(uid int64) (bool, error) {
33+
return CanCreateOrgRepo(org.ID, uid)
34+
}
35+
3236
func (org *User) getTeam(e Engine, name string) (*Team, error) {
3337
return getTeam(e, org.ID, name)
3438
}
@@ -149,11 +153,12 @@ func CreateOrganization(org, owner *User) (err error) {
149153

150154
// Create default owner team.
151155
t := &Team{
152-
OrgID: org.ID,
153-
LowerName: strings.ToLower(ownerTeamName),
154-
Name: ownerTeamName,
155-
Authorize: AccessModeOwner,
156-
NumMembers: 1,
156+
OrgID: org.ID,
157+
LowerName: strings.ToLower(ownerTeamName),
158+
Name: ownerTeamName,
159+
Authorize: AccessModeOwner,
160+
NumMembers: 1,
161+
CanCreateOrgRepo: true,
157162
}
158163
if _, err = sess.Insert(t); err != nil {
159164
return fmt.Errorf("insert owner team: %v", err)
@@ -335,6 +340,18 @@ func IsPublicMembership(orgID, uid int64) (bool, error) {
335340
Exist()
336341
}
337342

343+
func CanCreateOrgRepo(orgID, uid int64) (bool, error) {
344+
if owner, err := IsOrganizationOwner(orgID, uid); owner == true || err != nil {
345+
return owner, err
346+
}
347+
return x.
348+
Where("team.can_create_org_repo = true").
349+
Join("INNER", "team_user", "team_user.team_id = team.id").
350+
And("team_user.uid = ?", uid).
351+
And("team_user.org_id = ?", orgID).
352+
Exist(new(Team))
353+
}
354+
338355
func getOrgsByUserID(sess *xorm.Session, userID int64, showAll bool) ([]*User, error) {
339356
orgs := make([]*User, 0, 10)
340357
if !showAll {
@@ -366,6 +383,17 @@ func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) {
366383
Find(&orgs)
367384
}
368385

386+
func getOrgsCanCreateRepoByUserIDDesc(sess *xorm.Session, userID int64) ([]*User, error) {
387+
orgs := make([]*User, 0, 10)
388+
return orgs, sess.
389+
Join("INNER", "`team_user`", "`team_user`.org_id=`user`.id").
390+
Join("INNER", "`team`", "`team`.id=`team_user`.team_id").
391+
Where("`team_user`.uid=?", userID).
392+
And(builder.Eq{"`team`.authorize": AccessModeOwner}.Or(builder.Eq{"`team`.can_create_org_repo": true})).
393+
Asc("`user`.name").
394+
Find(&orgs)
395+
}
396+
369397
// HasOrgVisible tells if the given user can see the given org
370398
func HasOrgVisible(org *User, user *User) bool {
371399
return hasOrgVisible(x, org, user)
@@ -414,6 +442,12 @@ func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) {
414442
return getOwnedOrgsByUserID(x.Desc(desc), userID)
415443
}
416444

445+
// GetOrgsCanCreateRepoByUserIDDesc returns a list of organizations where given user ID
446+
// are allowed to create repos, ordered descending by the given condition.
447+
func GetOrgsCanCreateRepoByUserIDDesc(userID int64, desc string) ([]*User, error) {
448+
return getOrgsCanCreateRepoByUserIDDesc(x.Desc(desc), userID)
449+
}
450+
417451
// GetOrgUsersByUserID returns all organization-user relations by user ID.
418452
func GetOrgUsersByUserID(uid int64, all bool) ([]*OrgUser, error) {
419453
ous := make([]*OrgUser, 0, 10)

models/org_team.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,18 @@ const ownerTeamName = "Owners"
2121

2222
// Team represents a organization team.
2323
type Team struct {
24-
ID int64 `xorm:"pk autoincr"`
25-
OrgID int64 `xorm:"INDEX"`
26-
LowerName string
27-
Name string
28-
Description string
29-
Authorize AccessMode
30-
Repos []*Repository `xorm:"-"`
31-
Members []*User `xorm:"-"`
32-
NumRepos int
33-
NumMembers int
34-
Units []*TeamUnit `xorm:"-"`
24+
ID int64 `xorm:"pk autoincr"`
25+
OrgID int64 `xorm:"INDEX"`
26+
LowerName string
27+
Name string
28+
Description string
29+
Authorize AccessMode
30+
Repos []*Repository `xorm:"-"`
31+
Members []*User `xorm:"-"`
32+
NumRepos int
33+
NumMembers int
34+
Units []*TeamUnit `xorm:"-"`
35+
CanCreateOrgRepo bool `xorm:"NOT NULL DEFAULT false"`
3536
}
3637

3738
// ColorFormat provides a basic color format for a Team

models/repo.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1326,7 +1326,18 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err
13261326
return fmt.Errorf("getOwnerTeam: %v", err)
13271327
} else if err = t.addRepository(e, repo); err != nil {
13281328
return fmt.Errorf("addRepository: %v", err)
1329-
} else if err = prepareWebhooks(e, repo, HookEventRepository, &api.RepositoryPayload{
1329+
}
1330+
1331+
if !t.IsMember(doer.ID) {
1332+
// If creator not in owner team, make repo admin
1333+
if err = repo.addCollaborator(e, doer); err != nil {
1334+
return fmt.Errorf("AddCollaborator: %v", err)
1335+
} else if err = repo.changeCollaborationAccessMode(e, doer.ID, AccessModeAdmin); err != nil {
1336+
return fmt.Errorf("ChangeCollaborationAccessMode: %v", err)
1337+
}
1338+
}
1339+
1340+
if err = prepareWebhooks(e, repo, HookEventRepository, &api.RepositoryPayload{
13301341
Action: api.HookRepoCreated,
13311342
Repository: repo.innerAPIFormat(e, AccessModeOwner, false),
13321343
Organization: u.APIFormat(),

models/repo_collaboration.go

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,41 +16,39 @@ type Collaboration struct {
1616
Mode AccessMode `xorm:"DEFAULT 2 NOT NULL"`
1717
}
1818

19-
// AddCollaborator adds new collaboration to a repository with default access mode.
20-
func (repo *Repository) AddCollaborator(u *User) error {
19+
func (repo *Repository) addCollaborator(e Engine, u *User) error {
2120
collaboration := &Collaboration{
2221
RepoID: repo.ID,
2322
UserID: u.ID,
2423
}
2524

26-
has, err := x.Get(collaboration)
25+
has, err := e.Get(collaboration)
2726
if err != nil {
2827
return err
2928
} else if has {
3029
return nil
3130
}
3231
collaboration.Mode = AccessModeWrite
3332

34-
sess := x.NewSession()
35-
defer sess.Close()
36-
if err = sess.Begin(); err != nil {
37-
return err
38-
}
39-
40-
if _, err = sess.InsertOne(collaboration); err != nil {
33+
if _, err = e.InsertOne(collaboration); err != nil {
4134
return err
4235
}
4336

4437
if repo.Owner.IsOrganization() {
45-
err = repo.recalculateTeamAccesses(sess, 0)
38+
err = repo.recalculateTeamAccesses(e, 0)
4639
} else {
47-
err = repo.recalculateAccesses(sess)
40+
err = repo.recalculateAccesses(e)
4841
}
4942
if err != nil {
5043
return fmt.Errorf("recalculateAccesses 'team=%v': %v", repo.Owner.IsOrganization(), err)
5144
}
5245

53-
return sess.Commit()
46+
return nil
47+
}
48+
49+
// AddCollaborator adds new collaboration to a repository with default access mode.
50+
func (repo *Repository) AddCollaborator(u *User) error {
51+
return repo.addCollaborator(x, u)
5452
}
5553

5654
func (repo *Repository) getCollaborations(e Engine) ([]*Collaboration, error) {
@@ -98,8 +96,7 @@ func (repo *Repository) IsCollaborator(userID int64) (bool, error) {
9896
return repo.isCollaborator(x, userID)
9997
}
10098

101-
// ChangeCollaborationAccessMode sets new access mode for the collaboration.
102-
func (repo *Repository) ChangeCollaborationAccessMode(uid int64, mode AccessMode) error {
99+
func (repo *Repository) changeCollaborationAccessMode(e Engine, uid int64, mode AccessMode) error {
103100
// Discard invalid input
104101
if mode <= AccessModeNone || mode > AccessModeOwner {
105102
return nil
@@ -109,7 +106,7 @@ func (repo *Repository) ChangeCollaborationAccessMode(uid int64, mode AccessMode
109106
RepoID: repo.ID,
110107
UserID: uid,
111108
}
112-
has, err := x.Get(collaboration)
109+
has, err := e.Get(collaboration)
113110
if err != nil {
114111
return fmt.Errorf("get collaboration: %v", err)
115112
} else if !has {
@@ -121,22 +118,21 @@ func (repo *Repository) ChangeCollaborationAccessMode(uid int64, mode AccessMode
121118
}
122119
collaboration.Mode = mode
123120

124-
sess := x.NewSession()
125-
defer sess.Close()
126-
if err = sess.Begin(); err != nil {
127-
return err
128-
}
129-
130-
if _, err = sess.
121+
if _, err = e.
131122
ID(collaboration.ID).
132123
Cols("mode").
133124
Update(collaboration); err != nil {
134125
return fmt.Errorf("update collaboration: %v", err)
135-
} else if _, err = sess.Exec("UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?", mode, uid, repo.ID); err != nil {
126+
} else if _, err = e.Exec("UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?", mode, uid, repo.ID); err != nil {
136127
return fmt.Errorf("update access table: %v", err)
137128
}
138129

139-
return sess.Commit()
130+
return nil
131+
}
132+
133+
// ChangeCollaborationAccessMode sets new access mode for the collaboration.
134+
func (repo *Repository) ChangeCollaborationAccessMode(uid int64, mode AccessMode) error {
135+
return repo.changeCollaborationAccessMode(x, uid, mode)
140136
}
141137

142138
// DeleteCollaboration removes collaboration relation between the user and repository.

modules/auth/org.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,11 @@ func (f *UpdateOrgSettingForm) Validate(ctx *macaron.Context, errs binding.Error
5757

5858
// CreateTeamForm form for creating team
5959
type CreateTeamForm struct {
60-
TeamName string `binding:"Required;AlphaDashDot;MaxSize(30)"`
61-
Description string `binding:"MaxSize(255)"`
62-
Permission string
63-
Units []models.UnitType
60+
TeamName string `binding:"Required;AlphaDashDot;MaxSize(30)"`
61+
Description string `binding:"MaxSize(255)"`
62+
Permission string
63+
Units []models.UnitType
64+
CanCreateOrgRepo bool
6465
}
6566

6667
// Validate validates the fields

modules/context/org.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ import (
1515

1616
// Organization contains organization context
1717
type Organization struct {
18-
IsOwner bool
19-
IsMember bool
20-
IsTeamMember bool // Is member of team.
21-
IsTeamAdmin bool // In owner team or team that has admin permission level.
22-
Organization *models.User
23-
OrgLink string
18+
IsOwner bool
19+
IsMember bool
20+
IsTeamMember bool // Is member of team.
21+
IsTeamAdmin bool // In owner team or team that has admin permission level.
22+
Organization *models.User
23+
OrgLink string
24+
CanCreateOrgRepo bool
2425

2526
Team *models.Team
2627
}
@@ -73,6 +74,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
7374
ctx.Org.IsMember = true
7475
ctx.Org.IsTeamMember = true
7576
ctx.Org.IsTeamAdmin = true
77+
ctx.Org.CanCreateOrgRepo = true
7678
} else if ctx.IsSigned {
7779
ctx.Org.IsOwner, err = org.IsOwnedBy(ctx.User.ID)
7880
if err != nil {
@@ -84,12 +86,18 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
8486
ctx.Org.IsMember = true
8587
ctx.Org.IsTeamMember = true
8688
ctx.Org.IsTeamAdmin = true
89+
ctx.Org.CanCreateOrgRepo = true
8790
} else {
8891
ctx.Org.IsMember, err = org.IsOrgMember(ctx.User.ID)
8992
if err != nil {
9093
ctx.ServerError("IsOrgMember", err)
9194
return
9295
}
96+
ctx.Org.CanCreateOrgRepo, err = org.CanCreateOrgRepo(ctx.User.ID)
97+
if err != nil {
98+
ctx.ServerError("CanCreateOrgRepo", err)
99+
return
100+
}
93101
}
94102
} else {
95103
// Fake data.
@@ -102,6 +110,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
102110
}
103111
ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
104112
ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember
113+
ctx.Data["CanCreateOrgRepo"] = ctx.Org.CanCreateOrgRepo
105114

106115
ctx.Org.OrgLink = setting.AppSubURL + "/org/" + org.Name
107116
ctx.Data["OrgLink"] = ctx.Org.OrgLink

modules/structs/org_team.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ type Team struct {
1414
// enum: none,read,write,admin,owner
1515
Permission string `json:"permission"`
1616
// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"]
17-
Units []string `json:"units"`
17+
Units []string `json:"units"`
18+
CanCreateOrgRepo bool `json:"can_create_org_repo"`
1819
}
1920

2021
// CreateTeamOption options for creating a team
@@ -25,7 +26,8 @@ type CreateTeamOption struct {
2526
// enum: read,write,admin
2627
Permission string `json:"permission"`
2728
// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"]
28-
Units []string `json:"units"`
29+
Units []string `json:"units"`
30+
CanCreateOrgRepo bool `json:"can_create_org_repo"`
2931
}
3032

3133
// EditTeamOption options for editing a team
@@ -36,5 +38,6 @@ type EditTeamOption struct {
3638
// enum: read,write,admin
3739
Permission string `json:"permission"`
3840
// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"]
39-
Units []string `json:"units"`
41+
Units []string `json:"units"`
42+
CanCreateOrgRepo bool `json:"can_create_org_repo"`
4043
}

options/locale/locale_en-US.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,8 @@ members.invite_now = Invite Now
15161516

15171517
teams.join = Join
15181518
teams.leave = Leave
1519+
teams.can_create_org_repo = Create repository permission
1520+
teams.can_create_org_repo_helper = Team members are allowed to create repositories in organization.
15191521
teams.read_access = Read Access
15201522
teams.read_access_helper = Members can view and clone team repositories.
15211523
teams.write_access = Write Access

routers/api/v1/convert/convert.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -221,11 +221,12 @@ func ToOrganization(org *models.User) *api.Organization {
221221
// ToTeam convert models.Team to api.Team
222222
func ToTeam(team *models.Team) *api.Team {
223223
return &api.Team{
224-
ID: team.ID,
225-
Name: team.Name,
226-
Description: team.Description,
227-
Permission: team.Authorize.String(),
228-
Units: team.GetUnitNames(),
224+
ID: team.ID,
225+
Name: team.Name,
226+
Description: team.Description,
227+
Permission: team.Authorize.String(),
228+
Units: team.GetUnitNames(),
229+
CanCreateOrgRepo: team.CanCreateOrgRepo,
229230
}
230231
}
231232

routers/api/v1/org/team.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,11 @@ func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) {
125125
// "201":
126126
// "$ref": "#/responses/Team"
127127
team := &models.Team{
128-
OrgID: ctx.Org.Organization.ID,
129-
Name: form.Name,
130-
Description: form.Description,
131-
Authorize: models.ParseAccessMode(form.Permission),
128+
OrgID: ctx.Org.Organization.ID,
129+
Name: form.Name,
130+
Description: form.Description,
131+
Authorize: models.ParseAccessMode(form.Permission),
132+
CanCreateOrgRepo: form.CanCreateOrgRepo,
132133
}
133134

134135
unitTypes := models.FindUnitTypes(form.Units...)
@@ -183,6 +184,7 @@ func EditTeam(ctx *context.APIContext, form api.EditTeamOption) {
183184
team.Description = form.Description
184185
team.Authorize = models.ParseAccessMode(form.Permission)
185186
unitTypes := models.FindUnitTypes(form.Units...)
187+
team.CanCreateOrgRepo = form.CanCreateOrgRepo
186188

187189
if team.Authorize < models.AccessModeOwner {
188190
var units = make([]*models.TeamUnit, 0, len(form.Units))

routers/api/v1/repo/repo.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -306,12 +306,12 @@ func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) {
306306
}
307307

308308
if !ctx.User.IsAdmin {
309-
isOwner, err := org.IsOwnedBy(ctx.User.ID)
309+
canCreate, err := org.CanCreateOrgRepo(ctx.User.ID)
310310
if err != nil {
311-
ctx.ServerError("IsOwnedBy", err)
311+
ctx.ServerError("CanCreateOrgRepo", err)
312312
return
313-
} else if !isOwner {
314-
ctx.Error(403, "", "Given user is not owner of organization.")
313+
} else if !canCreate {
314+
ctx.Error(403, "", "Given user is not allowed to create repository in organization.")
315315
return
316316
}
317317
}

0 commit comments

Comments
 (0)