/** * Saves a new or existing CB+CMS user * WARNINGS: * - You must verify authorization of user to perform this (user checkCBpermissions() ) * - You must $this->load() existing user first * * @param array $array Raw unfiltered input, typically $_POST * @param int $ui 1 = Front-end (limitted rights), 2 = Backend (almost unlimitted), 0 = automated (full) * @param string $reason 'edit' or 'register' * @return boolean */ function saveSafely(&$array, $ui, $reason) { global $_CB_framework, $_CB_database, $ueConfig, $_PLUGINS; // Get current user state and store it into $oldUserComplete: $oldUserComplete = new moscomprofilerUser($this->_db); foreach (array_keys(get_object_vars($this)) as $k) { if (substr($k, 0, 1) != '_') { // ignore internal vars $oldUserComplete->{$k} = $this->{$k}; } } if ($oldUserComplete->gids === null) { $oldUserComplete->gids = array(); } // 1) Process and validate the fields in form by CB field plugins: // 2) Bind the fields to CMS User: $bindResults = $this->bindSafely($array, $ui, $reason, $oldUserComplete); if ($bindResults) { // During bindSafely, in saveTabContents, the validations have already taken place, for mandatory fields. if ($this->name == '' && $this->username == '' && $this->email != '') { $this->username = $this->email; $this->_cmsUser->username = $this->username; } // Checks that name is set. If not, uses the username as name, as Mambo/Joola mosUser::store() uses name for ACL // and ACL bugs with no name. if ($this->name == '') { $this->name = $this->username; $this->_cmsUser->name = $this->name; } elseif ($this->username == '') { $this->username = $this->name; $this->_cmsUser->username = $this->username; } if (!$this->checkSafely()) { $bindResults = false; } } // For new registrations or backend user creations, set registration date and password if neeeded: $isNew = !$this->id; $newCBuser = $oldUserComplete->user_id == null; if ($isNew) { if (checkJversion() != 1) { // J1.5 works better with null here... has bug that it offsets the time by server date, others need this: $this->registerDate = $_CB_framework->dateDbOfNow(); } } if ($bindResults) { if ($isNew) { if ($this->password == null) { $this->setRandomPassword(); $ueConfig['emailpass'] = 1; // set this global to 1 to force password to be sent to new users. } } // In backend only: if group has been changed and where original group was a Super Admin: check if there is at least a super-admin left: if ($ui == 2) { $myGids = $_CB_framework->acl->get_groups_below_me(null, true); $cms_admin = $_CB_framework->acl->mapGroupNamesToValues('Administrator'); $cms_super_admin = $_CB_framework->acl->mapGroupNamesToValues('Superadministrator'); $i_am_super_admin = $_CB_framework->acl->amIaSuperAdmin(); $i_am_admin = in_array($cms_admin, $myGids); if (!$isNew) { if (checkJversion() == 2) { if ($i_am_super_admin && $_CB_framework->myId() == $this->id) { // Check that a fool Super User does not block himself: if ($this->block && !$oldUserComplete->block) { $this->_error = 'Super Users can not block themselves'; return false; } // Check that a fool Super User does not demote himself from Super-User rights: if ($this->gids != $oldUserComplete->gids) { $staysSuperUser = $_CB_framework->acl->authorizeGroupsForAction($this->gids, 'core.admin', null); if (!$staysSuperUser) { $this->_error = 'You cannot demote yourself from your Super User permission'; return false; } } } // Check that a non-Super User/non-admin does not demote an admin or a Super user: if ($this->gids != $oldUserComplete->gids) { if (!$i_am_super_admin && !(CBuser::getMyInstance()->authoriseAction('core.admin') || CBuser::getMyInstance()->authoriseAction('core.manage', 'com_users') && CBuser::getMyInstance()->authoriseAction('core.edit', 'com_users') && CBuser::getMyInstance()->authoriseAction('core.edit.state', 'com_users'))) { // I am not a Super User and not an Users administrator: $userIsSuperUser = JUser::getInstance($this->id)->authorise('core.admin'); // User is super-user: Check if he stays so: if ($userIsSuperUser) { $staysSuperUser = $_CB_framework->acl->authorizeGroupsForAction($this->gids, 'core.admin', null); if (!$staysSuperUser) { $this->_error = 'You cannot remove a Super User permission. Only Super Users can do that.'; return false; } } $userCanAdminUsers = (CBuser::getInstance($this->id)->authoriseAction('core.manage', 'com_users') || CBuser::getInstance($this->id)->authoriseAction('core.manage')) && CBuser::getInstance($this->id)->authoriseAction('core.edit', 'com_users') && CBuser::getInstance($this->id)->authoriseAction('core.edit.state', 'com_users'); // User is users-administrator: check if he can stay so: if ($userCanAdminUsers) { $staysUserAdmin = ($_CB_framework->acl->authorizeGroupsForAction($this->gids, 'core.manage', 'com_users') || $_CB_framework->acl->authorizeGroupsForAction($this->gids, 'core.manage')) && $_CB_framework->acl->authorizeGroupsForAction($this->gids, 'core.edit', 'com_users') && $_CB_framework->acl->authorizeGroupsForAction($this->gids, 'core.edit.state', 'com_users'); if (!$staysUserAdmin) { $this->_error = 'An users manager cannot be demoted by a non-administrator'; return false; } } } } } else { if ($this->gid != $oldUserComplete->gid) { if ($oldUserComplete->gid == $cms_super_admin) { // count number of active super admins $query = 'SELECT COUNT( id )' . "\n FROM #__users" . "\n WHERE gid = " . (int) $cms_super_admin . "\n AND block = 0"; $_CB_database->setQuery($query); $count = $_CB_database->loadResult(); if ($count <= 1) { // disallow change if only one Super Admin exists $this->_error = 'You cannot change this users Group as it is the only active Super Administrator for your site'; return false; } } $user_group = strtolower($_CB_framework->acl->get_group_name($oldUserComplete->gid, 'ARO')); if ($user_group == 'super administrator' && !$i_am_super_admin) { // disallow change of super-Admin by non-super admin $this->_error = 'You cannot change this users Group as you are not a Super Administrator for your site'; return false; } elseif ($this->id == $_CB_framework->myId() && $i_am_super_admin) { // CB-specific: disallow change of own Super Admin group: $this->_error = 'You cannot change your own Super Administrator status for your site'; return false; } else { if (!$i_am_super_admin && $i_am_admin && $oldUserComplete->gid == $cms_admin) { // disallow change of super-Admin by non-super admin $this->_error = 'You cannot change the Group of another Administrator as you are not a Super Administrator for your site'; return false; } elseif (in_array($oldUserComplete->gid, $myGids) && !in_array($this->gid, $myGids)) { // disallow change of group of user into a group that is not child of admin/superadmin: $this->_error = 'You cannot change the Group of this user to a group that is not child of Registered or Manager as otherwise that user cannot login. If you really need to do that, you can do it in Joomla User Manager.'; return false; } } } // ensure user can't add group higher than themselves done below } } // Security check to avoid creating/editing user to higher level than himself: CB response to artf4529. if (!$i_am_super_admin && $this->gids != $oldUserComplete->gids) { // Does user try to edit a user that has higher groups ? if (count(array_diff($this->gids, $myGids)) != 0) { $this->_error = 'Unauthorized attempt to change an user at higher level than allowed !'; return false; } // Does the user try to demote higher levels ? if (array_diff($this->gids, $myGids) != array_diff($oldUserComplete->gids, $myGids)) { $this->_error = 'Unauthorized attempt to change higher groups of an user than allowed !'; return false; } } } } if ($reason == 'edit') { if ($ui == 1) { $_PLUGINS->trigger('onBeforeUserUpdate', array(&$this, &$this, &$oldUserComplete, &$oldUserComplete)); } elseif ($ui == 2) { if ($isNew || $newCBuser) { $_PLUGINS->trigger('onBeforeNewUser', array(&$this, &$this, false)); } else { $_PLUGINS->trigger('onBeforeUpdateUser', array(&$this, &$this, &$oldUserComplete)); } } } elseif ($reason == 'register') { $_PLUGINS->trigger('onBeforeUserRegistration', array(&$this, &$this)); } $beforeResult = !$_PLUGINS->is_errors(); if (!$beforeResult) { $this->_error = $_PLUGINS->getErrorMSG(false); // $_PLUGIN collects all error messages, incl. previous ones. } // Saves tab plugins: // on edits, user params and block/email/approved/confirmed are done in cb.core predefined fields. // So now calls this and more (CBtabs are already created in $this->bindSafely() ). $pluginTabsResult = true; if ($reason == 'edit') { $this->_cbTabs->savePluginTabs($this, $array); $pluginTabsResult = !$_PLUGINS->is_errors(); if (!$pluginTabsResult) { $this->_error = $_PLUGINS->getErrorMSG(false); // $_PLUGIN collects all error messages, incl. previous ones. } } if ($bindResults && $beforeResult && $pluginTabsResult) { // Hashes password for CMS storage: $clearTextPassword = $this->password; if ($clearTextPassword) { $hashedPassword = $this->hashAndSaltPassword($clearTextPassword); $this->password = $hashedPassword; } // Stores user if it's a new user: if ($isNew) { if (!$this->store()) { return false; } } // Restores cleartext password for the saveRegistrationPluginTabs: $this->password = $clearTextPassword; if ($isNew) { // Sets the instance of user, to avoid reload from database, and loss of the cleartext password. CBuser::setUserGetCBUserInstance($this); } } if ($reason == 'register') { // call here since we got to have a user id: $registerResults = array(); $registerResults['tabs'] = $this->_cbTabs->saveRegistrationPluginTabs($this, $array); if ($_PLUGINS->is_errors()) { if ($bindResults && $beforeResult && $pluginTabsResult) { $plugins_error = $_PLUGINS->getErrorMSG(false); // $_PLUGIN collects all error messages, incl. previous ones. if ($isNew) { // if it was a new user, and plugin gave error, revert the creation: $this->delete(); } $this->_error = $plugins_error; } else { $this->_error = $_PLUGINS->getErrorMSG(false); // $_PLUGIN collects all error messages, incl. previous ones. } $pluginTabsResult = false; } } if ($bindResults && $beforeResult && $pluginTabsResult) { $this->_cbTabs->commitTabsContents($this, $array, $reason); $commit_errors = $_PLUGINS->getErrorMSG(false); if (count($commit_errors) > 0) { $this->_error = $commit_errors; $bindResults = false; } } if (!($bindResults && $beforeResult && $pluginTabsResult)) { $this->_cbTabs->rollbackTabsContents($this, $array, $reason); // Normal error exit point: $_PLUGINS->trigger('onSaveUserError', array(&$this, $this->_error, $reason)); if (is_array($this->_error)) { $this->_error = implode('<br />', $this->_error); } return false; } // Stores the user (again if it's a new as the plugins might have changed the user record): if ($clearTextPassword) { $this->password = $hashedPassword; } if (!$this->store()) { return false; } // Restores cleartext password for the onAfter and activation events: $this->password = $clearTextPassword; // Triggers onAfter and activateUser events: if ($reason == 'edit') { if ($ui == 1) { $_PLUGINS->trigger('onAfterUserUpdate', array(&$this, &$this, $oldUserComplete)); } elseif ($ui == 2) { if ($isNew || $newCBuser) { if ($isNew) { $ueConfig['emailpass'] = 1; // set this global to 1 to force password to be sent to new users. } $_PLUGINS->trigger('onAfterNewUser', array(&$this, &$this, false, true)); if ($this->block == 0 && $this->approved == 1 && $this->confirmed) { activateUser($this, 2, 'NewUser', false, $isNew); } } else { if (!(($oldUserComplete->approved == 1 || $oldUserComplete->approved == 2) && $oldUserComplete->confirmed) && ($this->approved == 1 && $this->confirmed)) { // first time a just registered and confirmed user got approved in backend through save user: if (isset($ueConfig['emailpass']) && $ueConfig['emailpass'] == "1" && $this->password == '') { // generate the password is auto-generated and not set by the admin at this occasion: $this->setRandomPassword(); $pwd = $this->hashAndSaltPassword($this->password); $_CB_database->setQuery("UPDATE #__users SET password="******" WHERE id = " . (int) $this->id); $_CB_database->query(); } } $_PLUGINS->trigger('onAfterUpdateUser', array(&$this, &$this, $oldUserComplete)); if (!(($oldUserComplete->approved == 1 || $oldUserComplete->approved == 2) && $oldUserComplete->confirmed) && ($this->approved == 1 && $this->confirmed)) { // first time a just registered and confirmed user got approved in backend through save user: activateUser($this, 2, 'UpdateUser', false); } } } } elseif ($reason == 'register') { $registerResults['after'] = $_PLUGINS->trigger('onAfterUserRegistration', array(&$this, &$this, true)); $registerResults['ok'] = true; return $registerResults; } return true; }
/** * Saves a new or existing CB+CMS user * WARNINGS: * - You must verify authorization of user to perform this (user checkCBpermissions() ) * - You must $this->load() existing user first * * @param array $array Raw unfiltered input, typically $_POST * @param int $ui 1 = Front-end (limitted rights), 2 = Backend (almost unlimitted), 0 = automated (full) * @param string $reason 'edit' or 'register' * @return boolean */ function saveSafely( &$array, $ui, $reason ) { global $_CB_framework, $_CB_database, $ueConfig, $_PLUGINS; // Get current user state and store it into $oldUserComplete: $oldUserComplete = new moscomprofilerUser( $this->_db ); foreach ( array_keys( get_object_vars( $this ) ) as $k ) { if( substr( $k, 0, 1 ) != '_' ) { // ignore internal vars $oldUserComplete->$k = $this->$k; } } // 1) Process and validate the fields in form by CB field plugins: // 2) Bind the fields to CMS User: $bindResults = $this->bindSafely( $array, $ui, $reason, $oldUserComplete ); if ( $bindResults ) { // During bindSafely, in saveTabContents, the validations have already taken place, for mandatory fields. if ( ( $this->name == '' ) && ( $this->username == '' ) && ( $this->email != '' ) ) { $this->username = $this->email; $this->_cmsUser->username = $this->username; } // Checks that name is set. If not, uses the username as name, as Mambo/Joola mosUser::store() uses name for ACL // and ACL bugs with no name. if ( $this->name == '' ) { $this->name = $this->username; $this->_cmsUser->name = $this->name; } elseif ( $this->username == '' ) { $this->username = $this->name; $this->_cmsUser->username = $this->username; } if ( ! $this->checkSafely() ) { $bindResults = false; } } // For new registrations or backend user creations, set registration date and password if neeeded: $isNew = ( ! $this->id ); $newCBuser = ( $oldUserComplete->user_id == null ); if ( $isNew ) { if ( checkJversion() != 1 ) { // J1.5 works better with null here... has bug that it offsets the time by server date, others need this: $this->registerDate = date('Y-m-d H:i:s', $_CB_framework->now() ); } } if ( $bindResults ) { if ( $isNew ) { if ( $this->password == null ) { $this->setRandomPassword(); $ueConfig['emailpass'] = 1; // set this global to 1 to force password to be sent to new users. } } // In backend only: if group has been changed and where original group was a Super Admin: check if there is at least a super-admin left: if ( $ui == 2 ) { $myGid = userGID( $_CB_framework->myId() ); $cms_admin = $_CB_framework->acl->mapGroupNamesToValues( 'Administrator' ); $cms_super_admin = $_CB_framework->acl->mapGroupNamesToValues( 'Superadministrator' ); if ( ! $isNew ) { if ( $this->gid != $oldUserComplete->gid ) { if ( $oldUserComplete->gid == $cms_super_admin ) { // count number of active super admins if ( checkJversion() == 2 ) { $query = 'SELECT COUNT( a.id )' . "\n FROM #__users AS a" . "\n INNER JOIN #__user_usergroup_map AS b" . ' ON b.user_id = a.id' . "\n WHERE b.group_id = " . (int) $cms_super_admin . "\n AND a.block = 0" ; } else { $query = 'SELECT COUNT( id )' . "\n FROM #__users" . "\n WHERE gid = " . (int) $cms_super_admin . "\n AND block = 0" ; } $_CB_database->setQuery( $query ); $count = $_CB_database->loadResult(); if ( $count <= 1 ) { // disallow change if only one Super Admin exists $this->_error = 'You cannot change this users Group as it is the only active Super Administrator for your site'; return false; } } $user_group = strtolower( $_CB_framework->acl->get_group_name( $oldUserComplete->gid, 'ARO' ) ); if ( ( $user_group == 'super administrator' && $myGid != $cms_super_admin ) ) { // disallow change of super-Admin by non-super admin $this->_error = 'You cannot change this users Group as you are not a Super Administrator for your site'; return false; } elseif ( $this->id == $_CB_framework->myId() && $myGid == $cms_super_admin ) { // CB-specific: disallow change of own Super Admin group: $this->_error = 'You cannot change your own Super Administrator status for your site'; return false; } else if ( $myGid == $cms_admin && $oldUserComplete->gid == $cms_admin ) { // disallow change of super-Admin by non-super admin $this->_error = 'You cannot change the Group of another Administrator as you are not a Super Administrator for your site'; return false; } // ensure user can't add group higher than themselves done below } } // Security check to avoid creating/editing user to higher level than himself: CB response to artf4529. if ( ! in_array( $this->gid, getChildGIDS( $myGid ) ) ) { $this->_error = 'illegal attempt to set user at higher level than allowed !'; return false; } } } if ( $reason == 'edit' ) { if ( $ui == 1 ) { $_PLUGINS->trigger( 'onBeforeUserUpdate', array( &$this, &$this, &$oldUserComplete, &$oldUserComplete ) ); } elseif ( $ui == 2 ) { if ( $isNew || $newCBuser ) { $_PLUGINS->trigger( 'onBeforeNewUser', array( &$this, &$this, false ) ); } else { $_PLUGINS->trigger( 'onBeforeUpdateUser', array( &$this, &$this, &$oldUserComplete ) ); } } } elseif ( $reason == 'register' ) { $_PLUGINS->trigger( 'onBeforeUserRegistration', array( &$this, &$this ) ); } $beforeResult = ! $_PLUGINS->is_errors(); if ( ! $beforeResult ) { $this->_error = $_PLUGINS->getErrorMSG( false ); // $_PLUGIN collects all error messages, incl. previous ones. } // Saves tab plugins: // on edits, user params and block/email/approved/confirmed are done in cb.core predefined fields. // So now calls this and more (CBtabs are already created in $this->bindSafely() ). $pluginTabsResult = true; if ( $reason == 'edit' ) { $this->_cbTabs->savePluginTabs( $this, $array ); $pluginTabsResult = ! $_PLUGINS->is_errors(); if ( ! $pluginTabsResult ) { $this->_error = $_PLUGINS->getErrorMSG( false ); // $_PLUGIN collects all error messages, incl. previous ones. } } if ( $bindResults && $beforeResult && $pluginTabsResult ) { // Hashes password for CMS storage: $clearTextPassword = $this->password; if ( $clearTextPassword ) { $hashedPassword = $this->hashAndSaltPassword( $clearTextPassword ); $this->password = $hashedPassword; } // Stores user if it's a new user: if ( $isNew ) { if ( ! $this->store() ) { return false; } } // Restores cleartext password for the saveRegistrationPluginTabs: $this->password = $clearTextPassword; } if ( $reason == 'register' ) { if ( $bindResults && $beforeResult && $pluginTabsResult ) { // Sets the instance of user, to avoid reload from database, and loss of the cleartext password. CBuser::setUserGetCBUserInstance( $this ); } // call here since we got to have a user id: $registerResults = array(); $registerResults['tabs'] = $this->_cbTabs->saveRegistrationPluginTabs( $this, $array ); if ( $_PLUGINS->is_errors() ) { if ( $bindResults && $beforeResult && $pluginTabsResult ) { $plugins_error = $_PLUGINS->getErrorMSG( false ); // $_PLUGIN collects all error messages, incl. previous ones. if ( $isNew ) { // if it was a new user, and plugin gave error, revert the creation: $this->delete(); } $this->_error = $plugins_error; } else { $this->_error = $_PLUGINS->getErrorMSG( false ); // $_PLUGIN collects all error messages, incl. previous ones. } $pluginTabsResult = false; } } if ( ! ( $bindResults && $beforeResult && $pluginTabsResult ) ) { // Normal error exit point: $_PLUGINS->trigger( 'onSaveUserError', array( &$this, $this->_error, $reason ) ); if ( is_array( $this->_error ) ) { $this->_error = implode( '<br />', $this->_error ); } return false; } // Stores the user (again if it's a new as the plugins might have changed the user record): if ( $clearTextPassword ) { $this->password = $hashedPassword; } if ( ! $this->store() ) { return false; } // Restores cleartext password for the onAfter and activation events: $this->password = $clearTextPassword; // Triggers onAfter and activateUser events: if ( $reason == 'edit' ) { if ( $ui == 1 ) { $_PLUGINS->trigger( 'onAfterUserUpdate', array( &$this, &$this, $oldUserComplete ) ); } elseif ( $ui == 2 ) { if ( $isNew || $newCBuser ) { if ( $isNew ) { $ueConfig['emailpass'] = 1; // set this global to 1 to force password to be sent to new users. } $_PLUGINS->trigger( 'onAfterNewUser', array( &$this, &$this, false, true ) ); if ( $this->block == 0 && $this->approved == 1 && $this->confirmed ) { activateUser( $this, 2, 'NewUser', false, $isNew ); } } else { if ( ( ! ( ( $oldUserComplete->approved == 1 || $oldUserComplete->approved == 2 ) && $oldUserComplete->confirmed ) ) && ($this->approved == 1 && $this->confirmed ) ) { // first time a just registered and confirmed user got approved in backend through save user: if( isset( $ueConfig['emailpass'] ) && ( $ueConfig['emailpass'] == "1" ) && ( $this->password == '' ) ) { // generate the password is auto-generated and not set by the admin at this occasion: $this->setRandomPassword(); $pwd = $this->hashAndSaltPassword( $this->password ); $_CB_database->setQuery( "UPDATE #__users SET password="******" WHERE id = " . (int) $this->id ); $_CB_database->query(); } } $_PLUGINS->trigger( 'onAfterUpdateUser', array( &$this, &$this, $oldUserComplete ) ); if ( ( ! ( ( $oldUserComplete->approved == 1 || $oldUserComplete->approved == 2 ) && $oldUserComplete->confirmed ) ) && ($this->approved == 1 && $this->confirmed ) ) { // first time a just registered and confirmed user got approved in backend through save user: activateUser( $this, 2, 'UpdateUser', false ); } } } } elseif ( $reason == 'register' ) { $registerResults['after'] = $_PLUGINS->trigger( 'onAfterUserRegistration', array( &$this, &$this, true ) ); $registerResults['ok'] = true; return $registerResults; } return true; }