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:
parent
b75450ad36
commit
c0904f1942
11 changed files with 96 additions and 19 deletions
|
@ -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
|
||||||
|
|
|
@ -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
30
models/migrations/v15.go
Normal 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
|
||||||
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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 -->
|
||||||
|
|
||||||
|
|
Reference in a new issue