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
This commit is contained in:
Schwobaland 2016-12-31 03:33:30 +01:00 committed by Lunny Xiao
parent b75450ad36
commit c0904f1942
11 changed files with 96 additions and 19 deletions

View file

@ -123,6 +123,20 @@ func (err ErrUserHasOrgs) Error() string {
return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID) return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID)
} }
// ErrUserNotAllowedCreateOrg represents a "UserNotAllowedCreateOrg" kind of error.
type ErrUserNotAllowedCreateOrg struct {
}
// IsErrUserNotAllowedCreateOrg checks if an error is an ErrUserNotAllowedCreateOrg.
func IsErrUserNotAllowedCreateOrg(err error) bool {
_, ok := err.(ErrUserNotAllowedCreateOrg)
return ok
}
func (err ErrUserNotAllowedCreateOrg) Error() string {
return fmt.Sprintf("user is not allowed to create organizations")
}
// ErrReachLimitOfRepo represents a "ReachLimitOfRepo" kind of error. // ErrReachLimitOfRepo represents a "ReachLimitOfRepo" kind of error.
type ErrReachLimitOfRepo struct { type ErrReachLimitOfRepo struct {
Limit int Limit int

View file

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

30
models/migrations/v15.go Normal file
View file

@ -0,0 +1,30 @@
// Copyright 2016 Gitea. 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 (
"fmt"
"github.com/go-xorm/xorm"
)
// UserV15 describes the added field for User
type UserV15 struct {
AllowCreateOrganization bool
}
// TableName will be invoked by XORM to customrize the table name
func (*UserV15) TableName() string {
return "user"
}
func createAllowCreateOrganizationColumn(x *xorm.Engine) error {
if err := x.Sync2(new(UserV15)); err != nil {
return fmt.Errorf("Sync2: %v", err)
} else if _, err = x.Where("type=0").Cols("allow_create_organization").Update(&UserV15{AllowCreateOrganization: true}); err != nil {
return fmt.Errorf("set allow_create_organization: %v", err)
}
return nil
}

View file

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

View file

@ -102,11 +102,12 @@ type User struct {
MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1"` MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1"`
// Permissions // Permissions
IsActive bool // Activate primary email IsActive bool // Activate primary email
IsAdmin bool IsAdmin bool
AllowGitHook bool AllowGitHook bool
AllowImportLocal bool // Allow migrate repository by local path AllowImportLocal bool // Allow migrate repository by local path
ProhibitLogin bool AllowCreateOrganization bool `xorm:"DEFAULT true"`
ProhibitLogin bool
// Avatar // Avatar
Avatar string `xorm:"VARCHAR(2048) NOT NULL"` Avatar string `xorm:"VARCHAR(2048) NOT NULL"`
@ -210,6 +211,11 @@ func (u *User) CanCreateRepo() bool {
return u.NumRepos < u.MaxRepoCreation return u.NumRepos < u.MaxRepoCreation
} }
// CanCreateOrganization returns true if user can create organisation.
func (u *User) CanCreateOrganization() bool {
return u.IsAdmin || u.AllowCreateOrganization
}
// CanEditGitHook returns true if user can edit Git hooks. // CanEditGitHook returns true if user can edit Git hooks.
func (u *User) CanEditGitHook() bool { func (u *User) CanEditGitHook() bool {
return u.IsAdmin || u.AllowGitHook return u.IsAdmin || u.AllowGitHook
@ -611,6 +617,7 @@ func CreateUser(u *User) (err error) {
return err return err
} }
u.EncodePasswd() u.EncodePasswd()
u.AllowCreateOrganization = true
u.MaxRepoCreation = -1 u.MaxRepoCreation = -1
sess := x.NewSession() sess := x.NewSession()

View file

@ -27,19 +27,20 @@ func (f *AdminCreateUserForm) Validate(ctx *macaron.Context, errs binding.Errors
// AdminEditUserForm form for admin to create user // AdminEditUserForm form for admin to create user
type AdminEditUserForm struct { type AdminEditUserForm struct {
LoginType string `binding:"Required"` LoginType string `binding:"Required"`
LoginName string LoginName string
FullName string `binding:"MaxSize(100)"` FullName string `binding:"MaxSize(100)"`
Email string `binding:"Required;Email;MaxSize(254)"` Email string `binding:"Required;Email;MaxSize(254)"`
Password string `binding:"MaxSize(255)"` Password string `binding:"MaxSize(255)"`
Website string `binding:"MaxSize(50)"` Website string `binding:"MaxSize(50)"`
Location string `binding:"MaxSize(50)"` Location string `binding:"MaxSize(50)"`
MaxRepoCreation int MaxRepoCreation int
Active bool Active bool
Admin bool Admin bool
AllowGitHook bool AllowGitHook bool
AllowImportLocal bool AllowImportLocal bool
ProhibitLogin bool AllowCreateOrganization bool
ProhibitLogin bool
} }
// Validate validates form fields // Validate validates form fields

View file

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

View file

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

View file

@ -5,6 +5,8 @@
package org package org
import ( import (
"errors"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
@ -21,6 +23,10 @@ const (
// Create render the page for create organization // Create render the page for create organization
func Create(ctx *context.Context) { func Create(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("new_org") ctx.Data["Title"] = ctx.Tr("new_org")
if !ctx.User.CanCreateOrganization() {
ctx.Handle(500, "Not allowed", errors.New(ctx.Tr("org.form.create_org_not_allowed")))
return
}
ctx.HTML(200, tplCreateOrg) ctx.HTML(200, tplCreateOrg)
} }
@ -48,6 +54,8 @@ func CreatePost(ctx *context.Context, form auth.CreateOrgForm) {
ctx.RenderWithErr(ctx.Tr("org.form.name_reserved", err.(models.ErrNameReserved).Name), tplCreateOrg, &form) ctx.RenderWithErr(ctx.Tr("org.form.name_reserved", err.(models.ErrNameReserved).Name), tplCreateOrg, &form)
case models.IsErrNamePatternNotAllowed(err): case models.IsErrNamePatternNotAllowed(err):
ctx.RenderWithErr(ctx.Tr("org.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplCreateOrg, &form) ctx.RenderWithErr(ctx.Tr("org.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplCreateOrg, &form)
case models.IsErrUserNotAllowedCreateOrg(err):
ctx.RenderWithErr(ctx.Tr("org.form.create_org_not_allowed"), tplCreateOrg, &form)
default: default:
ctx.Handle(500, "CreateOrganization", err) ctx.Handle(500, "CreateOrganization", err)
} }

View file

@ -97,6 +97,12 @@
<input name="allow_import_local" type="checkbox" {{if .User.CanImportLocal}}checked{{end}}> <input name="allow_import_local" type="checkbox" {{if .User.CanImportLocal}}checked{{end}}>
</div> </div>
</div> </div>
<div class="inline field">
<div class="ui checkbox">
<label><strong>{{.i18n.Tr "admin.users.allow_create_organization"}}</strong></label>
<input name="allow_create_organization" type="checkbox" {{if .User.CanCreateOrganization}}checked{{end}}>
</div>
</div>
<div class="ui divider"></div> <div class="ui divider"></div>

View file

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