From 978dc00305f1af705ae8900977529bac6fa4c72b Mon Sep 17 00:00:00 2001 From: Unknwon Date: Sat, 5 Dec 2015 17:13:13 -0500 Subject: [PATCH] APIs: admin users --- models/error.go | 20 +++++ models/login.go | 4 +- models/publickey.go | 22 ++--- modules/auth/auth_form.go | 4 +- routers/admin/users.go | 6 +- routers/api/v1/admin/users.go | 148 ++++++++++++++++++++++++++++++++++ routers/api/v1/api.go | 16 +++- routers/api/v1/user/keys.go | 19 ++--- 8 files changed, 207 insertions(+), 32 deletions(-) create mode 100644 routers/api/v1/admin/users.go diff --git a/models/error.go b/models/error.go index 561252e84..54d32d154 100644 --- a/models/error.go +++ b/models/error.go @@ -528,3 +528,23 @@ func IsErrAttachmentNotExist(err error) bool { func (err ErrAttachmentNotExist) Error() string { return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID) } + +// _____ __ .__ __ .__ __ .__ +// / _ \ __ ___/ |_| |__ ____ _____/ |_|__| ____ _____ _/ |_|__| ____ ____ +// / /_\ \| | \ __\ | \_/ __ \ / \ __\ |/ ___\\__ \\ __\ |/ _ \ / \ +// / | \ | /| | | Y \ ___/| | \ | | \ \___ / __ \| | | ( <_> ) | \ +// \____|__ /____/ |__| |___| /\___ >___| /__| |__|\___ >____ /__| |__|\____/|___| / +// \/ \/ \/ \/ \/ \/ \/ + +type ErrAuthenticationNotExist struct { + ID int64 +} + +func IsErrAuthenticationNotExist(err error) bool { + _, ok := err.(ErrAuthenticationNotExist) + return ok +} + +func (err ErrAuthenticationNotExist) Error() string { + return fmt.Sprintf("Authentication does not exist [id: %d]", err.ID) +} diff --git a/models/login.go b/models/login.go index 1ec5309db..5e2601064 100644 --- a/models/login.go +++ b/models/login.go @@ -36,7 +36,6 @@ const ( var ( ErrAuthenticationAlreadyExist = errors.New("Authentication already exist") - ErrAuthenticationNotExist = errors.New("Authentication does not exist") ErrAuthenticationUserUsed = errors.New("Authentication has been used by some users") ) @@ -191,13 +190,14 @@ func LoginSources() ([]*LoginSource, error) { return auths, x.Find(&auths) } +// GetLoginSourceByID returns login source by given ID. func GetLoginSourceByID(id int64) (*LoginSource, error) { source := new(LoginSource) has, err := x.Id(id).Get(source) if err != nil { return nil, err } else if !has { - return nil, ErrAuthenticationNotExist + return nil, ErrAuthenticationNotExist{id} } return source, nil } diff --git a/models/publickey.go b/models/publickey.go index ac0ec71f4..683894781 100644 --- a/models/publickey.go +++ b/models/publickey.go @@ -460,7 +460,7 @@ func DeletePublicKey(doer *User, id int64) (err error) { } // Check if user has access to delete this key. - if doer.Id != key.OwnerID { + if !doer.IsAdmin && doer.Id != key.OwnerID { return ErrKeyAccessDenied{doer.Id, key.ID, "public"} } @@ -672,15 +672,17 @@ func DeleteDeployKey(doer *User, id int64) error { } // Check if user has access to delete this key. - repo, err := GetRepositoryByID(key.RepoID) - if err != nil { - return fmt.Errorf("GetRepositoryByID: %v", err) - } - yes, err := HasAccess(doer, repo, ACCESS_MODE_ADMIN) - if err != nil { - return fmt.Errorf("HasAccess: %v", err) - } else if !yes { - return ErrKeyAccessDenied{doer.Id, key.ID, "deploy"} + if !doer.IsAdmin { + repo, err := GetRepositoryByID(key.RepoID) + if err != nil { + return fmt.Errorf("GetRepositoryByID: %v", err) + } + yes, err := HasAccess(doer, repo, ACCESS_MODE_ADMIN) + if err != nil { + return fmt.Errorf("HasAccess: %v", err) + } else if !yes { + return ErrKeyAccessDenied{doer.Id, key.ID, "deploy"} + } } sess := x.NewSession() diff --git a/modules/auth/auth_form.go b/modules/auth/auth_form.go index 1604792a6..68a968830 100644 --- a/modules/auth/auth_form.go +++ b/modules/auth/auth_form.go @@ -18,7 +18,7 @@ type AuthenticationForm struct { BindDN string BindPassword string UserBase string - UserDN string `form:"user_dn"` + UserDN string AttributeUsername string AttributeName string AttributeSurname string @@ -32,7 +32,7 @@ type AuthenticationForm struct { AllowedDomains string TLS bool SkipVerify bool - PAMServiceName string `form:"pam_service_name"` + PAMServiceName string } func (f *AuthenticationForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { diff --git a/routers/admin/users.go b/routers/admin/users.go index d5358553c..ae17a99cd 100644 --- a/routers/admin/users.go +++ b/routers/admin/users.go @@ -121,7 +121,7 @@ func NewUserPost(ctx *middleware.Context, form auth.AdminCrateUserForm) { } return } - log.Trace("Account created by admin(%s): %s", ctx.User.Name, u.Name) + log.Trace("Account created by admin (%s): %s", ctx.User.Name, u.Name) // Send e-mail notification. if form.SendNotify && setting.MailService != nil { @@ -224,7 +224,7 @@ func EditUserPost(ctx *middleware.Context, form auth.AdminEditUserForm) { } return } - log.Trace("Account profile updated by admin(%s): %s", ctx.User.Name, u.Name) + log.Trace("Account profile updated by admin (%s): %s", ctx.User.Name, u.Name) ctx.Flash.Success(ctx.Tr("admin.users.update_profile_success")) ctx.Redirect(setting.AppSubUrl + "/admin/users/" + ctx.Params(":userid")) @@ -254,7 +254,7 @@ func DeleteUser(ctx *middleware.Context) { } return } - log.Trace("Account deleted by admin(%s): %s", ctx.User.Name, u.Name) + log.Trace("Account deleted by admin (%s): %s", ctx.User.Name, u.Name) ctx.Flash.Success(ctx.Tr("admin.users.deletion_success")) ctx.JSON(200, map[string]interface{}{ diff --git a/routers/api/v1/admin/users.go b/routers/api/v1/admin/users.go new file mode 100644 index 000000000..c203a09a1 --- /dev/null +++ b/routers/api/v1/admin/users.go @@ -0,0 +1,148 @@ +// Copyright 2015 The Gogs 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 admin + +import ( + api "github.com/gogits/go-gogs-client" + + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/mailer" + "github.com/gogits/gogs/modules/middleware" + "github.com/gogits/gogs/modules/setting" + "github.com/gogits/gogs/routers/api/v1/user" + to "github.com/gogits/gogs/routers/api/v1/utils" +) + +func parseLoginSource(ctx *middleware.Context, u *models.User, sourceID int64, loginName string) { + if sourceID == 0 { + return + } + + source, err := models.GetLoginSourceByID(sourceID) + if err != nil { + if models.IsErrAuthenticationNotExist(err) { + ctx.APIError(422, "", err) + } else { + ctx.APIError(500, "GetLoginSourceByID", err) + } + return + } + + u.LoginType = source.Type + u.LoginSource = source.ID + u.LoginName = loginName +} + +func CreateUser(ctx *middleware.Context, form api.CreateUserOption) { + u := &models.User{ + Name: form.Username, + Email: form.Email, + Passwd: form.Password, + IsActive: true, + LoginType: models.PLAIN, + } + + parseLoginSource(ctx, u, form.SourceID, form.LoginName) + if ctx.Written() { + return + } + + if err := models.CreateUser(u); err != nil { + if models.IsErrUserAlreadyExist(err) || + models.IsErrEmailAlreadyUsed(err) || + models.IsErrNameReserved(err) || + models.IsErrNamePatternNotAllowed(err) { + ctx.APIError(422, "", err) + } else { + ctx.APIError(500, "CreateUser", err) + } + return + } + log.Trace("Account created by admin (%s): %s", ctx.User.Name, u.Name) + + // Send e-mail notification. + if form.SendNotify && setting.MailService != nil { + mailer.SendRegisterNotifyMail(ctx.Context, u) + } + + ctx.JSON(201, to.ApiUser(u)) +} + +func EditUser(ctx *middleware.Context, form api.EditUserOption) { + u := user.GetUserByParams(ctx) + if ctx.Written() { + return + } + + parseLoginSource(ctx, u, form.SourceID, form.LoginName) + if ctx.Written() { + return + } + + if len(form.Password) > 0 { + u.Passwd = form.Password + u.Salt = models.GetUserSalt() + u.EncodePasswd() + } + + u.LoginName = form.LoginName + u.FullName = form.FullName + u.Email = form.Email + u.Website = form.Website + u.Location = form.Location + if form.Active != nil { + u.IsActive = *form.Active + } + if form.Admin != nil { + u.IsAdmin = *form.Admin + } + if form.AllowGitHook != nil { + u.AllowGitHook = *form.AllowGitHook + } + if form.AllowImportLocal != nil { + u.AllowImportLocal = *form.AllowImportLocal + } + + if err := models.UpdateUser(u); err != nil { + if models.IsErrEmailAlreadyUsed(err) { + ctx.APIError(422, "", err) + } else { + ctx.APIError(500, "UpdateUser", err) + } + return + } + log.Trace("Account profile updated by admin (%s): %s", ctx.User.Name, u.Name) + + ctx.JSON(200, to.ApiUser(u)) +} + +func DeleteUser(ctx *middleware.Context) { + u := user.GetUserByParams(ctx) + if ctx.Written() { + return + } + + if err := models.DeleteUser(u); err != nil { + if models.IsErrUserOwnRepos(err) || + models.IsErrUserHasOrgs(err) { + ctx.APIError(422, "", err) + } else { + ctx.APIError(500, "DeleteUser", err) + } + return + } + log.Trace("Account deleted by admin(%s): %s", ctx.User.Name, u.Name) + + ctx.Status(204) +} + +func CreatePublicKey(ctx *middleware.Context, form api.CreateKeyOption) { + u := user.GetUserByParams(ctx) + if ctx.Written() { + return + } + user.CreateUserPublicKey(ctx, form, u.Id) +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 01577bba7..1ac60c26b 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -15,6 +15,7 @@ import ( "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/auth" "github.com/gogits/gogs/modules/middleware" + "github.com/gogits/gogs/routers/api/v1/admin" "github.com/gogits/gogs/routers/api/v1/misc" "github.com/gogits/gogs/routers/api/v1/repo" "github.com/gogits/gogs/routers/api/v1/user" @@ -132,8 +133,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/users", func() { m.Group("/:username", func() { - m.Combo("/keys").Get(user.ListPublicKeys). - Post(ReqAdmin(), user.CreateUserPublicKey) + m.Get("/keys", user.ListPublicKeys) }) }, ReqToken()) @@ -179,5 +179,17 @@ func RegisterRoutes(m *macaron.Macaron) { m.Any("/*", func(ctx *middleware.Context) { ctx.Error(404) }) + + m.Group("/admin", func() { + m.Group("/users", func() { + m.Post("", bind(api.CreateUserOption{}), admin.CreateUser) + + m.Group("/:username", func() { + m.Combo("").Patch(bind(api.EditUserOption{}), admin.EditUser). + Delete(admin.DeleteUser) + m.Post("/keys", admin.CreatePublicKey) + }) + }) + }, ReqAdmin()) }) } diff --git a/routers/api/v1/user/keys.go b/routers/api/v1/user/keys.go index 597ee9f1e..213631e14 100644 --- a/routers/api/v1/user/keys.go +++ b/routers/api/v1/user/keys.go @@ -14,7 +14,8 @@ import ( to "github.com/gogits/gogs/routers/api/v1/utils" ) -func getUserByParams(ctx *middleware.Context) *models.User { +// GetUserByParams returns user whose name is presented in URL paramenter. +func GetUserByParams(ctx *middleware.Context) *models.User { user, err := models.GetUserByName(ctx.Params(":username")) if err != nil { if models.IsErrUserNotExist(err) { @@ -54,7 +55,7 @@ func ListMyPublicKeys(ctx *middleware.Context) { // https://github.com/gogits/go-gogs-client/wiki/Users-Public-Keys#list-public-keys-for-a-user func ListPublicKeys(ctx *middleware.Context) { - user := getUserByParams(ctx) + user := GetUserByParams(ctx) if ctx.Written() { return } @@ -77,7 +78,8 @@ func GetPublicKey(ctx *middleware.Context) { ctx.JSON(200, to.ApiPublicKey(apiLink, key)) } -func createUserPublicKey(ctx *middleware.Context, form api.CreateKeyOption, uid int64) { +// CreateUserPublicKey creates new public key to given user by ID. +func CreateUserPublicKey(ctx *middleware.Context, form api.CreateKeyOption, uid int64) { content, err := models.CheckPublicKeyString(form.Key) if err != nil { repo.HandleCheckKeyStringError(ctx, err) @@ -93,18 +95,9 @@ func createUserPublicKey(ctx *middleware.Context, form api.CreateKeyOption, uid ctx.JSON(201, to.ApiPublicKey(apiLink, key)) } -// https://github.com/gogits/go-gogs-client/wiki/Users-Public-Keys#create-a-public-key-for-user -func CreateUserPublicKey(ctx *middleware.Context, form api.CreateKeyOption) { - user := getUserByParams(ctx) - if ctx.Written() { - return - } - createUserPublicKey(ctx, form, user.Id) -} - // https://github.com/gogits/go-gogs-client/wiki/Users-Public-Keys#create-a-public-key func CreatePublicKey(ctx *middleware.Context, form api.CreateKeyOption) { - createUserPublicKey(ctx, form, ctx.User.Id) + CreateUserPublicKey(ctx, form, ctx.User.Id) } // https://github.com/gogits/go-gogs-client/wiki/Users-Public-Keys#delete-a-public-key