Browse Source

Restrict creating organisations by user (#193)

* restrict creating organizations based on right on user

* revert bindata.go

* reverse vendor lib

* revert goimports change

* set AllowCreateOrganization default value to true

* revert locale

* added default value for AllowCreateOrganization

* fix typo in migration-comment

* fix comment

* add coments in migration
Schwobaland 2 years ago
parent
commit
c0904f1942

+ 14 - 0
models/error.go

@@ -123,6 +123,20 @@ func (err ErrUserHasOrgs) Error() string {
123 123
 	return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID)
124 124
 }
125 125
 
126
+// ErrUserNotAllowedCreateOrg represents a "UserNotAllowedCreateOrg" kind of error.
127
+type ErrUserNotAllowedCreateOrg struct {
128
+}
129
+
130
+// IsErrUserNotAllowedCreateOrg checks if an error is an ErrUserNotAllowedCreateOrg.
131
+func IsErrUserNotAllowedCreateOrg(err error) bool {
132
+	_, ok := err.(ErrUserNotAllowedCreateOrg)
133
+	return ok
134
+}
135
+
136
+func (err ErrUserNotAllowedCreateOrg) Error() string {
137
+	return fmt.Sprintf("user is not allowed to create organizations")
138
+}
139
+
126 140
 // ErrReachLimitOfRepo represents a "ReachLimitOfRepo" kind of error.
127 141
 type ErrReachLimitOfRepo struct {
128 142
 	Limit int

+ 3 - 1
models/migrations/migrations.go

@@ -76,8 +76,10 @@ var migrations = []Migration{
76 76
 
77 77
 	// v13 -> v14:v0.9.87
78 78
 	NewMigration("set comment updated with created", setCommentUpdatedWithCreated),
79
-
79
+	// v14
80 80
 	NewMigration("create user column diff view style", createUserColumnDiffViewStyle),
81
+	// v15
82
+	NewMigration("create user column allow create organization", createAllowCreateOrganizationColumn),
81 83
 }
82 84
 
83 85
 // Migrate database to current version

+ 30 - 0
models/migrations/v15.go

@@ -0,0 +1,30 @@
1
+// Copyright 2016 Gitea. All rights reserved.
2
+// Use of this source code is governed by a MIT-style
3
+// license that can be found in the LICENSE file.
4
+
5
+package migrations
6
+
7
+import (
8
+	"fmt"
9
+
10
+	"github.com/go-xorm/xorm"
11
+)
12
+
13
+// UserV15 describes the added field for User
14
+type UserV15 struct {
15
+	AllowCreateOrganization bool
16
+}
17
+
18
+// TableName will be invoked by XORM to customrize the table name
19
+func (*UserV15) TableName() string {
20
+	return "user"
21
+}
22
+
23
+func createAllowCreateOrganizationColumn(x *xorm.Engine) error {
24
+	if err := x.Sync2(new(UserV15)); err != nil {
25
+		return fmt.Errorf("Sync2: %v", err)
26
+	} else if _, err = x.Where("type=0").Cols("allow_create_organization").Update(&UserV15{AllowCreateOrganization: true}); err != nil {
27
+		return fmt.Errorf("set allow_create_organization: %v", err)
28
+	}
29
+	return nil
30
+}

+ 4 - 0
models/org.go

@@ -97,6 +97,10 @@ func (org *User) RemoveOrgRepo(repoID int64) error {
97 97
 
98 98
 // CreateOrganization creates record of a new organization.
99 99
 func CreateOrganization(org, owner *User) (err error) {
100
+	if !owner.CanCreateOrganization() {
101
+		return ErrUserNotAllowedCreateOrg{}
102
+	}
103
+
100 104
 	if err = IsUsableUsername(org.Name); err != nil {
101 105
 		return err
102 106
 	}

+ 12 - 5
models/user.go

@@ -102,11 +102,12 @@ type User struct {
102 102
 	MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1"`
103 103
 
104 104
 	// Permissions
105
-	IsActive         bool // Activate primary email
106
-	IsAdmin          bool
107
-	AllowGitHook     bool
108
-	AllowImportLocal bool // Allow migrate repository by local path
109
-	ProhibitLogin    bool
105
+	IsActive                bool // Activate primary email
106
+	IsAdmin                 bool
107
+	AllowGitHook            bool
108
+	AllowImportLocal        bool // Allow migrate repository by local path
109
+	AllowCreateOrganization bool `xorm:"DEFAULT true"`
110
+	ProhibitLogin           bool
110 111
 
111 112
 	// Avatar
112 113
 	Avatar          string `xorm:"VARCHAR(2048) NOT NULL"`
@@ -210,6 +211,11 @@ func (u *User) CanCreateRepo() bool {
210 211
 	return u.NumRepos < u.MaxRepoCreation
211 212
 }
212 213
 
214
+// CanCreateOrganization returns true if user can create organisation.
215
+func (u *User) CanCreateOrganization() bool {
216
+	return u.IsAdmin || u.AllowCreateOrganization
217
+}
218
+
213 219
 // CanEditGitHook returns true if user can edit Git hooks.
214 220
 func (u *User) CanEditGitHook() bool {
215 221
 	return u.IsAdmin || u.AllowGitHook
@@ -611,6 +617,7 @@ func CreateUser(u *User) (err error) {
611 617
 		return err
612 618
 	}
613 619
 	u.EncodePasswd()
620
+	u.AllowCreateOrganization = true
614 621
 	u.MaxRepoCreation = -1
615 622
 
616 623
 	sess := x.NewSession()

+ 14 - 13
modules/auth/admin.go

@@ -27,19 +27,20 @@ func (f *AdminCreateUserForm) Validate(ctx *macaron.Context, errs binding.Errors
27 27
 
28 28
 // AdminEditUserForm form for admin to create user
29 29
 type AdminEditUserForm struct {
30
-	LoginType        string `binding:"Required"`
31
-	LoginName        string
32
-	FullName         string `binding:"MaxSize(100)"`
33
-	Email            string `binding:"Required;Email;MaxSize(254)"`
34
-	Password         string `binding:"MaxSize(255)"`
35
-	Website          string `binding:"MaxSize(50)"`
36
-	Location         string `binding:"MaxSize(50)"`
37
-	MaxRepoCreation  int
38
-	Active           bool
39
-	Admin            bool
40
-	AllowGitHook     bool
41
-	AllowImportLocal bool
42
-	ProhibitLogin    bool
30
+	LoginType               string `binding:"Required"`
31
+	LoginName               string
32
+	FullName                string `binding:"MaxSize(100)"`
33
+	Email                   string `binding:"Required;Email;MaxSize(254)"`
34
+	Password                string `binding:"MaxSize(255)"`
35
+	Website                 string `binding:"MaxSize(50)"`
36
+	Location                string `binding:"MaxSize(50)"`
37
+	MaxRepoCreation         int
38
+	Active                  bool
39
+	Admin                   bool
40
+	AllowGitHook            bool
41
+	AllowImportLocal        bool
42
+	AllowCreateOrganization bool
43
+	ProhibitLogin           bool
43 44
 }
44 45
 
45 46
 // Validate validates form fields

+ 2 - 0
options/locale/locale_en-US.ini

@@ -844,6 +844,7 @@ team_permission_desc = What permission level should this team have?
844 844
 
845 845
 form.name_reserved = Organization name '%s' is reserved.
846 846
 form.name_pattern_not_allowed = Organization name pattern '%s' is not allowed.
847
+form.create_org_not_allowed = This user is not allowed to create an organization.
847 848
 
848 849
 settings = Settings
849 850
 settings.options = Options
@@ -994,6 +995,7 @@ users.prohibit_login = This account is prohibited to login
994 995
 users.is_admin = This account has administrator permissions
995 996
 users.allow_git_hook = This account has permissions to create Git hooks
996 997
 users.allow_import_local = This account has permissions to import local repositories
998
+users.allow_create_organization = This account has permissions to create Organizations
997 999
 users.update_profile = Update Account Profile
998 1000
 users.delete_account = Delete This Account
999 1001
 users.still_own_repo = This account still has ownership over at least one repository, you have to delete or transfer them first.

+ 1 - 0
routers/admin/users.go

@@ -214,6 +214,7 @@ func EditUserPost(ctx *context.Context, form auth.AdminEditUserForm) {
214 214
 	u.IsAdmin = form.Admin
215 215
 	u.AllowGitHook = form.AllowGitHook
216 216
 	u.AllowImportLocal = form.AllowImportLocal
217
+	u.AllowCreateOrganization = form.AllowCreateOrganization
217 218
 	u.ProhibitLogin = form.ProhibitLogin
218 219
 
219 220
 	if err := models.UpdateUser(u); err != nil {

+ 8 - 0
routers/org/org.go

@@ -5,6 +5,8 @@
5 5
 package org
6 6
 
7 7
 import (
8
+	"errors"
9
+
8 10
 	"code.gitea.io/gitea/models"
9 11
 	"code.gitea.io/gitea/modules/auth"
10 12
 	"code.gitea.io/gitea/modules/base"
@@ -21,6 +23,10 @@ const (
21 23
 // Create render the page for create organization
22 24
 func Create(ctx *context.Context) {
23 25
 	ctx.Data["Title"] = ctx.Tr("new_org")
26
+	if !ctx.User.CanCreateOrganization() {
27
+		ctx.Handle(500, "Not allowed", errors.New(ctx.Tr("org.form.create_org_not_allowed")))
28
+		return
29
+	}
24 30
 	ctx.HTML(200, tplCreateOrg)
25 31
 }
26 32
 
@@ -48,6 +54,8 @@ func CreatePost(ctx *context.Context, form auth.CreateOrgForm) {
48 54
 			ctx.RenderWithErr(ctx.Tr("org.form.name_reserved", err.(models.ErrNameReserved).Name), tplCreateOrg, &form)
49 55
 		case models.IsErrNamePatternNotAllowed(err):
50 56
 			ctx.RenderWithErr(ctx.Tr("org.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplCreateOrg, &form)
57
+		case models.IsErrUserNotAllowedCreateOrg(err):
58
+			ctx.RenderWithErr(ctx.Tr("org.form.create_org_not_allowed"), tplCreateOrg, &form)
51 59
 		default:
52 60
 			ctx.Handle(500, "CreateOrganization", err)
53 61
 		}

+ 6 - 0
templates/admin/user/edit.tmpl

@@ -97,6 +97,12 @@
97 97
 								<input name="allow_import_local" type="checkbox" {{if .User.CanImportLocal}}checked{{end}}>
98 98
 							</div>
99 99
 						</div>
100
+						<div class="inline field">
101
+							<div class="ui checkbox">
102
+								<label><strong>{{.i18n.Tr "admin.users.allow_create_organization"}}</strong></label>
103
+								<input name="allow_create_organization" type="checkbox" {{if .User.CanCreateOrganization}}checked{{end}}>
104
+							</div>
105
+						</div>
100 106
 
101 107
 						<div class="ui divider"></div>
102 108
 

+ 2 - 0
templates/base/head.tmpl

@@ -94,9 +94,11 @@
94 94
 												<a class="item" href="{{AppSubUrl}}/repo/migrate">
95 95
 													<i class="octicon octicon-repo-clone"></i> {{.i18n.Tr "new_migrate"}}
96 96
 												</a>
97
+												{{if .SignedUser.CanCreateOrganization}}
97 98
 												<a class="item" href="{{AppSubUrl}}/org/create">
98 99
 													<i class="octicon octicon-organization"></i> {{.i18n.Tr "new_org"}}
99 100
 												</a>
101
+												{{end}}
100 102
 											</div><!-- end content create new menu -->
101 103
 										</div><!-- end dropdown menu create new -->
102 104