From f577d615c54df24c8df7b0db2eb665c08684e271 Mon Sep 17 00:00:00 2001 From: Victor Boctor Date: Sat, 13 Jan 2018 13:14:36 -0800 Subject: [PATCH] Implement `UserCreateCommand` and use from Web UI Fixes #23837 --- core/access_api.php | 38 ++++++- core/commands/UserCreateCommand.php | 158 ++++++++++++++++++++++++++++ manage_user_create.php | 86 +++++---------- 3 files changed, 222 insertions(+), 60 deletions(-) create mode 100644 core/commands/UserCreateCommand.php diff --git a/core/access_api.php b/core/access_api.php index 1a920d2765..ebb7c0910c 100644 --- a/core/access_api.php +++ b/core/access_api.php @@ -54,6 +54,8 @@ require_api( 'string_api.php' ); require_api( 'user_api.php' ); +use Mantis\Exceptions\ClientException; + # @global array $g_cache_access_matrix $g_cache_access_matrix = array(); @@ -858,4 +860,38 @@ function access_can_see_handler_for_bug( BugData $p_bug, $p_user_id = null ) { $p_bug->id ); return $t_can_view_handler; -} \ No newline at end of file +} + +/** + * Parse access level reference array parsed from json. + * + * @param array $p_access The access level + * @return integer The access level + * @throws ClientException Access level is invalid or not specified. + */ +function access_parse_array( array $p_access ) { + $t_access_levels_enum = config_get( 'access_levels_enum_string' ); + $t_access_level = false; + + if( isset( $p_access['id'] ) ) { + $t_access_level = (int)$p_access['id']; + + # Make sure the provided id is valid + if( !MantisEnum::hasValue( $t_access_levels_enum, $t_access_level ) ) { + $t_access_level = false; + } + } + + if( isset( $p_access['name'] ) ) { + $t_access_level = MantisEnum::getValue( $t_access_levels_enum, $p_access['name'] ); + } + + if( $t_access_level === false ) { + throw new ClientException( + 'Invalid access level', + ERROR_INVALID_FIELD_VALUE, + array( 'access_level' ) ); + } + + return $t_access_level; +} diff --git a/core/commands/UserCreateCommand.php b/core/commands/UserCreateCommand.php new file mode 100644 index 0000000000..4636eadb93 --- /dev/null +++ b/core/commands/UserCreateCommand.php @@ -0,0 +1,158 @@ +. + +require_api( 'authentication_api.php' ); +require_api( 'constant_inc.php' ); +require_api( 'config_api.php' ); +require_api( 'helper_api.php' ); +require_api( 'user_api.php' ); + +use Mantis\Exceptions\ClientException; + +/** + * A command that creates a user account. + * + * Sample: + * { + * "data": { + * "username": "vboctor", + * "password": "p@ssw0rd", + * "real_name": "Victor Boctor", + * "email": "vboctor@example.com", + * "access_level": { "name" => "developer" }, + * "enabled": true, + * "protected": false + * } + * } + */ +class UserCreateCommand extends Command { + /** + * @var string user name + */ + private $username; + + /** + * @var string user real name + */ + private $realname; + + /** + * @var string user email address + */ + private $email; + + /** + * @var string user password + */ + private $password; + + /** + * @var boolean user can edit their information. Usually, true for shared accounts. + */ + private $protected; + + /** + * @var boolean user account enabled for login. + */ + private $enabled; + + /** + * Constructor + * + * @param array $p_data The command data. + */ + function __construct( array $p_data ) { + parent::__construct( $p_data ); + } + + /** + * Validate the data. + */ + function validate() { + # Ensure user has access level to create users + access_ensure_global_level( config_get_global( 'manage_user_threshold' ) ); + + # Access Level + $this->access_level = access_parse_array( + $this->payload( + 'access_level', + array( 'id' => config_get_global( 'default_new_account_access_level' ) ) ) ); + + # Don't allow the creation of accounts with access levels higher than that of + # the user creating the account. + access_ensure_global_level( $this->access_level ); + + # Username and Real Name + $this->username = trim( $this->payload( 'username', '' ) ); + if( is_blank( $this->username ) ) { + throw new ClientException( 'username must be specified', ERROR_EMPTY_FIELD ); + } + + $this->realname = string_normalize( $this->payload( 'real_name', '' ) ); + user_ensure_name_valid( $this->username ); + user_ensure_realname_unique( $this->username, $this->realname ); + + # Protected and Enabled Flags + $this->protected = $this->payload( 'protected', false ); + $this->enabled = $this->payload( 'enabled', true ); + + # Email + $this->email = trim( $this->payload( 'email', '' ) ); + email_ensure_not_disposable( $this->email ); + + # Password + $this->password = $this->payload( 'password', '' ); + if( ( ON == config_get( 'send_reset_password' ) ) && + ( ON == config_get( 'enable_email_notification' ) ) ) { + # Check code will be sent to the user directly via email. Dummy password set to random + # Create random password + $this->password = auth_generate_random_password(); + } else { + $this->password = $this->payload( 'password', auth_generate_random_password() ); + } + } + + /** + * Process the command. + * + * @returns array Command response + */ + protected function process() { + # Need to send the user creation mail in the tracker language, not in the creating admin's language + # Park the current language name until the user has been created + lang_push( config_get_global( 'default_language' ) ); + + # create the user + $t_admin_name = user_get_name( auth_get_current_user_id() ); + user_create( + $this->username, + $this->password, + $this->email, + $this->access_level, + $this->protected, + $this->enabled, + $this->realname, + $t_admin_name ); + + # set language back to user language + lang_pop(); + + return array( + 'id' => user_get_id_by_name( $this->username ) + ); + } +} + diff --git a/manage_user_create.php b/manage_user_create.php index 46bdd06d6a..06157f4cc3 100644 --- a/manage_user_create.php +++ b/manage_user_create.php @@ -25,9 +25,7 @@ * @uses core.php * @uses access_api.php * @uses authentication_api.php - * @uses config_api.php * @uses constant_inc.php - * @uses email_api.php * @uses form_api.php * @uses gpc_api.php * @uses helper_api.php @@ -35,16 +33,13 @@ * @uses lang_api.php * @uses print_api.php * @uses string_api.php - * @uses user_api.php * @uses utility_api.php */ require_once( 'core.php' ); require_api( 'access_api.php' ); require_api( 'authentication_api.php' ); -require_api( 'config_api.php' ); require_api( 'constant_inc.php' ); -require_api( 'email_api.php' ); require_api( 'form_api.php' ); require_api( 'gpc_api.php' ); require_api( 'helper_api.php' ); @@ -52,13 +47,11 @@ require_api( 'lang_api.php' ); require_api( 'print_api.php' ); require_api( 'string_api.php' ); -require_api( 'user_api.php' ); require_api( 'utility_api.php' ); form_security_validate( 'manage_user_create' ); auth_reauthenticate(); -access_ensure_global_level( config_get( 'manage_user_threshold' ) ); $f_username = gpc_get_string( 'username' ); $f_realname = gpc_get_string( 'realname', '' ); @@ -69,64 +62,40 @@ $f_protected = gpc_get_bool( 'protected' ); $f_enabled = gpc_get_bool( 'enabled' ); -# check for empty username -$f_username = trim( $f_username ); -if( is_blank( $f_username ) ) { - trigger_error( ERROR_EMPTY_FIELD, ERROR ); -} - -# Check the name for validity here so we do it before promting to use a -# blank password (don't want to prompt the user if the process will fail -# anyway) -# strip extra space from real name -$t_realname = string_normalize( $f_realname ); -user_ensure_name_valid( $f_username ); -user_ensure_realname_unique( $f_username, $f_realname ); - if( $f_password != $f_password_verify ) { trigger_error( ERROR_USER_CREATE_PASSWORD_MISMATCH, ERROR ); } -$f_email = trim( $f_email ); -email_ensure_not_disposable( $f_email ); - -if( ( ON == config_get( 'send_reset_password' ) ) && ( ON == config_get( 'enable_email_notification' ) ) ) { - # Check code will be sent to the user directly via email. Dummy password set to random - # Create random password - $f_password = auth_generate_random_password(); -} else { - # Password won't to be sent by email. It entered by the admin - # Now, if the password is empty, confirm that that is what we wanted - if( is_blank( $f_password ) ) { - helper_ensure_confirmed( lang_get( 'empty_password_sure_msg' ), - lang_get( 'empty_password_button' ) ); - } +# Password won't to be sent by email. It entered by the admin +# Now, if the password is empty, confirm that that is what we wanted +if( is_blank( $f_password ) && + ( ON != config_get( 'send_reset_password' ) ) || + ( ON != config_get( 'enable_email_notification' ) ) ) { + helper_ensure_confirmed( + lang_get( 'empty_password_sure_msg' ), + lang_get( 'empty_password_button' ) ); } -# Don't allow the creation of accounts with access levels higher than that of -# the user creating the account. -access_ensure_global_level( $f_access_level ); - -# Need to send the user creation mail in the tracker language, not in the creating admin's language -# Park the current language name until the user has been created -lang_push( config_get_global( 'default_language' ) ); - -# create the user -$t_admin_name = user_get_name( auth_get_current_user_id() ); -$t_cookie = user_create( $f_username, $f_password, $f_email, $f_access_level, $f_protected, $f_enabled, $t_realname, $t_admin_name ); - -# set language back to user language -lang_pop(); +$t_data = array( + 'query' => array(), + 'payload' => array( + 'username' => $f_username, + 'email' => $f_email, + 'access_level' => array( 'id' => $f_access_level ), + 'real_name' => $f_realname, + 'password' => $f_password, + 'protected' => $f_protected, + 'enabled' => $f_enabled + ) +); + +$t_command = new UserCreateCommand( $t_data ); +$t_result = $t_command->execute(); form_security_purge( 'manage_user_create' ); -if( $t_cookie === false ) { - $t_redirect_url = 'manage_user_page.php'; -} else { - # ok, we created the user, get the row again - $t_user_id = user_get_id_by_name( $f_username ); - $t_redirect_url = 'manage_user_edit_page.php?user_id=' . $t_user_id; -} +$t_user_id = $t_result['id']; +$t_redirect_url = 'manage_user_edit_page.php?user_id=' . $t_user_id; layout_page_header( null, $t_redirect_url ); @@ -137,8 +106,7 @@ . lang_get( 'created_user_part2' ) . ' ' . $t_access_level . '
'; html_operation_successful( $t_redirect_url, $t_message ); -?> - -'; + layout_page_end();