2015-12-06 01:13:13 +03:00
// Copyright 2015 The Gogs Authors. All rights reserved.
2019-01-24 01:30:19 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2015-12-06 01:13:13 +03:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package admin
import (
2019-10-14 18:24:26 +03:00
"errors"
2020-02-03 19:46:33 +03:00
"fmt"
2019-12-20 20:07:12 +03:00
"net/http"
2021-11-26 04:56:16 +03:00
"strings"
2019-10-14 18:24:26 +03:00
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/models"
2021-12-10 11:14:24 +03:00
asymkey_model "code.gitea.io/gitea/models/asymkey"
2022-01-02 16:12:35 +03:00
"code.gitea.io/gitea/models/auth"
2021-11-24 12:49:20 +03:00
"code.gitea.io/gitea/models/db"
2021-11-11 10:03:30 +03:00
user_model "code.gitea.io/gitea/models/user"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/context"
2019-11-10 07:41:51 +03:00
"code.gitea.io/gitea/modules/convert"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/log"
2019-10-14 18:24:26 +03:00
"code.gitea.io/gitea/modules/password"
2021-12-17 05:03:39 +03:00
"code.gitea.io/gitea/modules/setting"
2019-05-11 13:21:34 +03:00
api "code.gitea.io/gitea/modules/structs"
2022-04-29 22:38:11 +03:00
"code.gitea.io/gitea/modules/util"
2021-01-26 18:36:53 +03:00
"code.gitea.io/gitea/modules/web"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/routers/api/v1/user"
2020-01-24 22:00:29 +03:00
"code.gitea.io/gitea/routers/api/v1/utils"
2021-12-10 11:14:24 +03:00
asymkey_service "code.gitea.io/gitea/services/asymkey"
2019-09-24 08:02:49 +03:00
"code.gitea.io/gitea/services/mailer"
2021-11-18 20:42:27 +03:00
user_service "code.gitea.io/gitea/services/user"
2015-12-06 01:13:13 +03:00
)
2022-01-02 16:12:35 +03:00
func parseAuthSource ( ctx * context . APIContext , u * user_model . User , sourceID int64 , loginName string ) {
2015-12-06 01:13:13 +03:00
if sourceID == 0 {
return
}
2022-01-02 16:12:35 +03:00
source , err := auth . GetSourceByID ( sourceID )
2015-12-06 01:13:13 +03:00
if err != nil {
2022-01-02 16:12:35 +03:00
if auth . IsErrSourceNotExist ( err ) {
2019-12-20 20:07:12 +03:00
ctx . Error ( http . StatusUnprocessableEntity , "" , err )
2015-12-06 01:13:13 +03:00
} else {
2022-01-02 16:12:35 +03:00
ctx . Error ( http . StatusInternalServerError , "auth.GetSourceByID" , err )
2015-12-06 01:13:13 +03:00
}
return
}
u . LoginType = source . Type
u . LoginSource = source . ID
u . LoginName = loginName
}
2017-11-13 10:02:25 +03:00
// CreateUser create a user
2021-01-26 18:36:53 +03:00
func CreateUser ( ctx * context . APIContext ) {
2017-11-13 10:02:25 +03:00
// swagger:operation POST /admin/users admin adminCreateUser
// ---
// summary: Create a user
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/CreateUserOption"
// responses:
// "201":
// "$ref": "#/responses/User"
2019-12-20 20:07:12 +03:00
// "400":
// "$ref": "#/responses/error"
2020-01-09 14:56:32 +03:00
// "403":
// "$ref": "#/responses/forbidden"
2017-11-13 10:02:25 +03:00
// "422":
// "$ref": "#/responses/validationError"
2022-03-26 12:04:22 +03:00
2021-01-26 18:36:53 +03:00
form := web . GetForm ( ctx ) . ( * api . CreateUserOption )
2021-06-26 22:53:14 +03:00
2021-11-24 12:49:20 +03:00
u := & user_model . User {
2019-02-27 22:37:57 +03:00
Name : form . Username ,
FullName : form . FullName ,
Email : form . Email ,
Passwd : form . Password ,
MustChangePassword : true ,
2022-01-02 16:12:35 +03:00
LoginType : auth . Plain ,
2019-02-27 22:37:57 +03:00
}
if form . MustChangePassword != nil {
u . MustChangePassword = * form . MustChangePassword
2015-12-06 01:13:13 +03:00
}
2022-01-02 16:12:35 +03:00
parseAuthSource ( ctx , u , form . SourceID , form . LoginName )
2015-12-06 01:13:13 +03:00
if ctx . Written ( ) {
return
}
2019-10-14 18:24:26 +03:00
if ! password . IsComplexEnough ( form . Password ) {
err := errors . New ( "PasswordComplexity" )
2019-12-20 20:07:12 +03:00
ctx . Error ( http . StatusBadRequest , "PasswordComplexity" , err )
2019-10-14 18:24:26 +03:00
return
}
2021-05-31 09:18:11 +03:00
pwned , err := password . IsPwned ( ctx , form . Password )
2020-09-09 01:06:39 +03:00
if pwned {
if err != nil {
log . Error ( err . Error ( ) )
}
ctx . Data [ "Err_Password" ] = true
ctx . Error ( http . StatusBadRequest , "PasswordPwned" , errors . New ( "PasswordPwned" ) )
return
}
2021-06-26 22:53:14 +03:00
2022-04-29 22:38:11 +03:00
overwriteDefault := & user_model . CreateUserOverwriteOptions {
IsActive : util . OptionalBoolTrue ,
}
if form . Restricted != nil {
overwriteDefault . IsRestricted = util . OptionalBoolOf ( * form . Restricted )
}
2021-06-26 22:53:14 +03:00
if form . Visibility != "" {
2022-04-29 22:38:11 +03:00
visibility := api . VisibilityModes [ form . Visibility ]
overwriteDefault . Visibility = & visibility
2021-06-26 22:53:14 +03:00
}
2021-11-24 12:49:20 +03:00
if err := user_model . CreateUser ( u , overwriteDefault ) ; err != nil {
if user_model . IsErrUserAlreadyExist ( err ) ||
2021-11-11 10:03:30 +03:00
user_model . IsErrEmailAlreadyUsed ( err ) ||
2021-11-24 12:49:20 +03:00
db . IsErrNameReserved ( err ) ||
db . IsErrNameCharsNotAllowed ( err ) ||
2022-03-14 20:39:54 +03:00
user_model . IsErrEmailCharIsNotSupported ( err ) ||
2021-11-11 10:03:30 +03:00
user_model . IsErrEmailInvalid ( err ) ||
2021-11-24 12:49:20 +03:00
db . IsErrNamePatternNotAllowed ( err ) {
2019-12-20 20:07:12 +03:00
ctx . Error ( http . StatusUnprocessableEntity , "" , err )
2015-12-06 01:13:13 +03:00
} else {
2019-12-20 20:07:12 +03:00
ctx . Error ( http . StatusInternalServerError , "CreateUser" , err )
2015-12-06 01:13:13 +03:00
}
return
}
2022-03-22 10:03:22 +03:00
log . Trace ( "Account created by admin (%s): %s" , ctx . Doer . Name , u . Name )
2015-12-06 01:13:13 +03:00
2016-07-15 19:36:39 +03:00
// Send email notification.
2019-09-24 08:02:49 +03:00
if form . SendNotify {
2021-04-02 13:25:13 +03:00
mailer . SendRegisterNotifyMail ( u )
2015-12-06 01:13:13 +03:00
}
2022-03-22 10:03:22 +03:00
ctx . JSON ( http . StatusCreated , convert . ToUser ( u , ctx . Doer ) )
2015-12-06 01:13:13 +03:00
}
2016-11-24 10:04:31 +03:00
// EditUser api for modifying a user's information
2021-01-26 18:36:53 +03:00
func EditUser ( ctx * context . APIContext ) {
2017-11-13 10:02:25 +03:00
// swagger:operation PATCH /admin/users/{username} admin adminEditUser
// ---
// summary: Edit an existing user
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: username
// in: path
// description: username of user to edit
// type: string
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/EditUserOption"
// responses:
// "200":
// "$ref": "#/responses/User"
// "403":
// "$ref": "#/responses/forbidden"
// "422":
// "$ref": "#/responses/validationError"
2022-03-26 12:04:22 +03:00
2021-01-26 18:36:53 +03:00
form := web . GetForm ( ctx ) . ( * api . EditUserOption )
2015-12-06 01:13:13 +03:00
2022-03-26 12:04:22 +03:00
parseAuthSource ( ctx , ctx . ContextUser , form . SourceID , form . LoginName )
2015-12-06 01:13:13 +03:00
if ctx . Written ( ) {
return
}
2020-11-20 04:56:42 +03:00
if len ( form . Password ) != 0 {
2021-12-17 05:03:39 +03:00
if len ( form . Password ) < setting . MinPasswordLength {
ctx . Error ( http . StatusBadRequest , "PasswordTooShort" , fmt . Errorf ( "password must be at least %d characters" , setting . MinPasswordLength ) )
return
}
2019-10-14 18:24:26 +03:00
if ! password . IsComplexEnough ( form . Password ) {
err := errors . New ( "PasswordComplexity" )
2019-12-20 20:07:12 +03:00
ctx . Error ( http . StatusBadRequest , "PasswordComplexity" , err )
2019-10-14 18:24:26 +03:00
return
}
2021-05-31 09:18:11 +03:00
pwned , err := password . IsPwned ( ctx , form . Password )
2020-09-09 01:06:39 +03:00
if pwned {
if err != nil {
log . Error ( err . Error ( ) )
}
ctx . Data [ "Err_Password" ] = true
ctx . Error ( http . StatusBadRequest , "PasswordPwned" , errors . New ( "PasswordPwned" ) )
return
}
2022-03-26 12:04:22 +03:00
if ctx . ContextUser . Salt , err = user_model . GetUserSalt ( ) ; err != nil {
2019-12-20 20:07:12 +03:00
ctx . Error ( http . StatusInternalServerError , "UpdateUser" , err )
2016-12-20 15:32:02 +03:00
return
}
2022-03-26 12:04:22 +03:00
if err = ctx . ContextUser . SetPassword ( form . Password ) ; err != nil {
2021-01-10 21:05:18 +03:00
ctx . InternalServerError ( err )
return
}
2015-12-06 01:13:13 +03:00
}
2019-02-27 22:37:57 +03:00
if form . MustChangePassword != nil {
2022-03-26 12:04:22 +03:00
ctx . ContextUser . MustChangePassword = * form . MustChangePassword
2019-02-27 22:37:57 +03:00
}
2022-03-26 12:04:22 +03:00
ctx . ContextUser . LoginName = form . LoginName
2020-11-20 04:56:42 +03:00
if form . FullName != nil {
2022-03-26 12:04:22 +03:00
ctx . ContextUser . FullName = * form . FullName
2020-11-20 04:56:42 +03:00
}
2021-11-26 04:56:16 +03:00
var emailChanged bool
2020-11-20 04:56:42 +03:00
if form . Email != nil {
2021-11-26 04:56:16 +03:00
email := strings . TrimSpace ( * form . Email )
if len ( email ) == 0 {
2020-11-20 04:56:42 +03:00
ctx . Error ( http . StatusUnprocessableEntity , "" , fmt . Errorf ( "email is not allowed to be empty string" ) )
return
}
2021-11-26 04:56:16 +03:00
if err := user_model . ValidateEmail ( email ) ; err != nil {
ctx . InternalServerError ( err )
return
}
2022-03-26 12:04:22 +03:00
emailChanged = ! strings . EqualFold ( ctx . ContextUser . Email , email )
ctx . ContextUser . Email = email
2020-11-20 04:56:42 +03:00
}
if form . Website != nil {
2022-03-26 12:04:22 +03:00
ctx . ContextUser . Website = * form . Website
2020-11-20 04:56:42 +03:00
}
if form . Location != nil {
2022-03-26 12:04:22 +03:00
ctx . ContextUser . Location = * form . Location
2020-11-20 04:56:42 +03:00
}
2021-05-02 22:03:15 +03:00
if form . Description != nil {
2022-03-26 12:04:22 +03:00
ctx . ContextUser . Description = * form . Description
2021-05-02 22:03:15 +03:00
}
2015-12-06 01:13:13 +03:00
if form . Active != nil {
2022-03-26 12:04:22 +03:00
ctx . ContextUser . IsActive = * form . Active
2015-12-06 01:13:13 +03:00
}
2021-06-26 22:53:14 +03:00
if len ( form . Visibility ) != 0 {
2022-03-26 12:04:22 +03:00
ctx . ContextUser . Visibility = api . VisibilityModes [ form . Visibility ]
2021-06-26 22:53:14 +03:00
}
2015-12-06 01:13:13 +03:00
if form . Admin != nil {
2022-03-26 12:04:22 +03:00
ctx . ContextUser . IsAdmin = * form . Admin
2015-12-06 01:13:13 +03:00
}
if form . AllowGitHook != nil {
2022-03-26 12:04:22 +03:00
ctx . ContextUser . AllowGitHook = * form . AllowGitHook
2015-12-06 01:13:13 +03:00
}
if form . AllowImportLocal != nil {
2022-03-26 12:04:22 +03:00
ctx . ContextUser . AllowImportLocal = * form . AllowImportLocal
2015-12-06 01:13:13 +03:00
}
2016-08-11 21:49:31 +03:00
if form . MaxRepoCreation != nil {
2022-03-26 12:04:22 +03:00
ctx . ContextUser . MaxRepoCreation = * form . MaxRepoCreation
2016-08-11 21:49:31 +03:00
}
2018-08-24 02:59:47 +03:00
if form . AllowCreateOrganization != nil {
2022-03-26 12:04:22 +03:00
ctx . ContextUser . AllowCreateOrganization = * form . AllowCreateOrganization
2018-08-24 02:59:47 +03:00
}
if form . ProhibitLogin != nil {
2022-03-26 12:04:22 +03:00
ctx . ContextUser . ProhibitLogin = * form . ProhibitLogin
2018-08-24 02:59:47 +03:00
}
2021-02-18 11:25:35 +03:00
if form . Restricted != nil {
2022-03-26 12:04:22 +03:00
ctx . ContextUser . IsRestricted = * form . Restricted
2021-02-18 11:25:35 +03:00
}
2015-12-06 01:13:13 +03:00
2022-05-20 17:08:52 +03:00
if err := user_model . UpdateUser ( ctx , ctx . ContextUser , emailChanged ) ; err != nil {
2022-03-14 20:39:54 +03:00
if user_model . IsErrEmailAlreadyUsed ( err ) ||
user_model . IsErrEmailCharIsNotSupported ( err ) ||
user_model . IsErrEmailInvalid ( err ) {
2019-12-20 20:07:12 +03:00
ctx . Error ( http . StatusUnprocessableEntity , "" , err )
2015-12-06 01:13:13 +03:00
} else {
2019-12-20 20:07:12 +03:00
ctx . Error ( http . StatusInternalServerError , "UpdateUser" , err )
2015-12-06 01:13:13 +03:00
}
return
}
2022-03-26 12:04:22 +03:00
log . Trace ( "Account profile updated by admin (%s): %s" , ctx . Doer . Name , ctx . ContextUser . Name )
2015-12-06 01:13:13 +03:00
2022-03-26 12:04:22 +03:00
ctx . JSON ( http . StatusOK , convert . ToUser ( ctx . ContextUser , ctx . Doer ) )
2015-12-06 01:13:13 +03:00
}
2016-11-24 10:04:31 +03:00
// DeleteUser api for deleting a user
2016-03-14 01:49:16 +03:00
func DeleteUser ( ctx * context . APIContext ) {
2017-11-13 10:02:25 +03:00
// swagger:operation DELETE /admin/users/{username} admin adminDeleteUser
// ---
// summary: Delete a user
// produces:
// - application/json
// parameters:
// - name: username
// in: path
// description: username of user to delete
// type: string
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
// "422":
// "$ref": "#/responses/validationError"
2019-12-20 20:07:12 +03:00
2022-03-26 12:04:22 +03:00
if ctx . ContextUser . IsOrganization ( ) {
ctx . Error ( http . StatusUnprocessableEntity , "" , fmt . Errorf ( "%s is an organization not a user" , ctx . ContextUser . Name ) )
2020-02-03 19:46:33 +03:00
return
}
2022-05-08 23:22:55 +03:00
// admin should not delete themself
if ctx . ContextUser . ID == ctx . Doer . ID {
ctx . Error ( http . StatusUnprocessableEntity , "" , fmt . Errorf ( "you cannot delete yourself" ) )
return
}
2022-07-14 10:22:09 +03:00
if err := user_service . DeleteUser ( ctx , ctx . ContextUser , ctx . FormBool ( "purge" ) ) ; err != nil {
2015-12-06 01:13:13 +03:00
if models . IsErrUserOwnRepos ( err ) ||
2022-03-30 11:42:47 +03:00
models . IsErrUserHasOrgs ( err ) ||
models . IsErrUserOwnPackages ( err ) {
2019-12-20 20:07:12 +03:00
ctx . Error ( http . StatusUnprocessableEntity , "" , err )
2015-12-06 01:13:13 +03:00
} else {
2019-12-20 20:07:12 +03:00
ctx . Error ( http . StatusInternalServerError , "DeleteUser" , err )
2015-12-06 01:13:13 +03:00
}
return
}
2022-03-26 12:04:22 +03:00
log . Trace ( "Account deleted by admin(%s): %s" , ctx . Doer . Name , ctx . ContextUser . Name )
2015-12-06 01:13:13 +03:00
2019-12-20 20:07:12 +03:00
ctx . Status ( http . StatusNoContent )
2015-12-06 01:13:13 +03:00
}
2016-11-24 10:04:31 +03:00
// CreatePublicKey api for creating a public key to a user
2021-01-26 18:36:53 +03:00
func CreatePublicKey ( ctx * context . APIContext ) {
2017-11-13 10:02:25 +03:00
// swagger:operation POST /admin/users/{username}/keys admin adminCreatePublicKey
// ---
// summary: Add a public key on behalf of a user
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: username
// in: path
// description: username of the user
// type: string
// required: true
2018-10-21 06:40:42 +03:00
// - name: key
// in: body
// schema:
// "$ref": "#/definitions/CreateKeyOption"
2017-11-13 10:02:25 +03:00
// responses:
// "201":
// "$ref": "#/responses/PublicKey"
// "403":
// "$ref": "#/responses/forbidden"
// "422":
// "$ref": "#/responses/validationError"
2022-03-26 12:04:22 +03:00
2021-01-26 18:36:53 +03:00
form := web . GetForm ( ctx ) . ( * api . CreateKeyOption )
2022-03-26 12:04:22 +03:00
user . CreateUserPublicKey ( ctx , * form , ctx . ContextUser . ID )
2015-12-06 01:13:13 +03:00
}
2017-12-06 13:27:10 +03:00
// DeleteUserPublicKey api for deleting a user's public key
func DeleteUserPublicKey ( ctx * context . APIContext ) {
// swagger:operation DELETE /admin/users/{username}/keys/{id} admin adminDeleteUserPublicKey
// ---
// summary: Delete a user's public key
// produces:
// - application/json
// parameters:
// - name: username
// in: path
// description: username of user
// type: string
// required: true
// - name: id
// in: path
// description: id of the key to delete
// type: integer
2018-10-21 06:40:42 +03:00
// format: int64
2017-12-06 13:27:10 +03:00
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
2019-12-20 20:07:12 +03:00
2022-03-26 12:04:22 +03:00
if err := asymkey_service . DeletePublicKey ( ctx . ContextUser , ctx . ParamsInt64 ( ":id" ) ) ; err != nil {
2021-12-10 11:14:24 +03:00
if asymkey_model . IsErrKeyNotExist ( err ) {
2019-03-19 05:29:43 +03:00
ctx . NotFound ( )
2021-12-10 11:14:24 +03:00
} else if asymkey_model . IsErrKeyAccessDenied ( err ) {
2019-12-20 20:07:12 +03:00
ctx . Error ( http . StatusForbidden , "" , "You do not have access to this key" )
2017-12-06 13:27:10 +03:00
} else {
2019-12-20 20:07:12 +03:00
ctx . Error ( http . StatusInternalServerError , "DeleteUserPublicKey" , err )
2017-12-06 13:27:10 +03:00
}
return
}
2022-03-26 12:04:22 +03:00
log . Trace ( "Key deleted by admin(%s): %s" , ctx . Doer . Name , ctx . ContextUser . Name )
2017-12-06 13:27:10 +03:00
2019-12-20 20:07:12 +03:00
ctx . Status ( http . StatusNoContent )
2017-12-06 13:27:10 +03:00
}
2019-01-24 01:30:19 +03:00
2022-01-20 20:46:10 +03:00
// GetAllUsers API for getting information of all the users
2019-01-24 01:30:19 +03:00
func GetAllUsers ( ctx * context . APIContext ) {
// swagger:operation GET /admin/users admin adminGetAllUsers
// ---
// summary: List all users
// produces:
// - application/json
2020-01-24 22:00:29 +03:00
// parameters:
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
2020-06-09 07:57:38 +03:00
// description: page size of results
2020-01-24 22:00:29 +03:00
// type: integer
2019-01-24 01:30:19 +03:00
// responses:
// "200":
// "$ref": "#/responses/UserList"
// "403":
// "$ref": "#/responses/forbidden"
2019-12-20 20:07:12 +03:00
2020-06-21 11:22:06 +03:00
listOptions := utils . GetListOptions ( ctx )
2021-11-24 12:49:20 +03:00
users , maxResults , err := user_model . SearchUsers ( & user_model . SearchUserOptions {
2022-03-22 10:03:22 +03:00
Actor : ctx . Doer ,
2021-11-24 12:49:20 +03:00
Type : user_model . UserTypeIndividual ,
OrderBy : db . SearchOrderByAlphabetically ,
2020-06-21 11:22:06 +03:00
ListOptions : listOptions ,
2019-01-24 01:30:19 +03:00
} )
if err != nil {
2019-12-20 20:07:12 +03:00
ctx . Error ( http . StatusInternalServerError , "GetAllUsers" , err )
2019-01-24 01:30:19 +03:00
return
}
2019-04-15 19:36:59 +03:00
results := make ( [ ] * api . User , len ( users ) )
for i := range users {
2022-03-22 10:03:22 +03:00
results [ i ] = convert . ToUser ( users [ i ] , ctx . Doer )
2019-04-15 19:36:59 +03:00
}
2020-06-21 11:22:06 +03:00
ctx . SetLinkHeader ( int ( maxResults ) , listOptions . PageSize )
2021-08-12 15:43:08 +03:00
ctx . SetTotalCountHeader ( maxResults )
2019-12-20 20:07:12 +03:00
ctx . JSON ( http . StatusOK , & results )
2019-01-24 01:30:19 +03:00
}