| 8 | | var $habtm = array('roles' => array('unique'=>true)); |
| 9 | | |
| 10 | | /** |
| 11 | | * @access private |
| 12 | | */ |
| 13 | | var $__initial_attributes = array(); |
| 14 | | var $__requires_password_confirmation = true; |
| 15 | | |
| 16 | | /** |
| 17 | | * We need to get initial values when instantiating to know if attributes like password have been changed |
| 18 | | */ |
| 19 | | function __construct() |
| 20 | | { |
| 21 | | $attributes = (array)func_get_args(); |
| 22 | | $this->__initial_attributes = isset($attributes[1]) && is_array($attributes[1]) ? $attributes[1] : array(); |
| 23 | | return $this->init($attributes); |
| 24 | | } |
| 25 | | |
| 26 | | /** |
| 27 | | * Main authentication method |
| 28 | | * |
| 29 | | * @param string $login user name or password |
| 30 | | * @param string $password |
| 31 | | * @return False if not found or not enabled, User instance if succedes |
| 32 | | */ |
| 33 | | function authenticate($login, $password) |
| 34 | | { |
| 35 | | $UserInstance =& new User(); |
| 36 | | |
| 37 | | $login_or_email = preg_match(AK_EMAIL_REGULAR_EXPRESSION, $login) ? 'email' : 'login'; |
| 38 | | |
| 39 | | if($User =& $UserInstance->find('first', array('conditions'=>array($login_or_email.' = ? AND __owner.is_enabled = ? AND _roles.is_enabled = ?', $login, true, true), 'include'=>'role')) && $User->isValidPassword($password)){ |
| 40 | | $User->set('last_login_at', Ak::getDate()); |
| 41 | | $User->save(); |
| 42 | | return $User; |
| 43 | | } |
| 44 | | return false; |
| 45 | | } |
| 46 | | |
| 47 | | function signUp($user_details, $options = array()) |
| 48 | | { |
| 49 | | $user_details['is_enabled'] = true; |
| 50 | | $this->setAttributes($user_details); |
| 51 | | if($this->save()){ |
| 52 | | $this->setDefaultRole(); |
| 53 | | $this->sendSignupMessage(array( |
| 54 | | 'login' => $user_details['login'], |
| 55 | | 'password' => $user_details['password'], |
| 56 | | )); |
| 57 | | return true; |
| 58 | | } |
| 59 | | return false; |
| 60 | | } |
| 61 | | |
| 62 | | function setDefaultRole() |
| 63 | | { |
| 64 | | $settings = Ak::getSettings('admin'); |
| 65 | | if(!empty($settings['account_settings']['default_role'])){ |
| 66 | | $this->role->load(); |
| 67 | | $Role = new Role(); |
| 68 | | if($DefaultRole = $Role->findFirstBy('name', $settings['account_settings']['default_role'])){ |
| 69 | | $this->role->set($DefaultRole); |
| 70 | | } |
| 71 | | } |
| 72 | | } |
| 73 | | |
| 74 | | function sendSignupMessage($options = array()) |
| 75 | | { |
| 76 | | $default_options = array( |
| 77 | | 'signup_message' => 'registration_details' |
| 78 | | ); |
| 79 | | $options = array_merge($default_options, $options); |
| 80 | | if(!empty($options['signup_message'])){ |
| 81 | | Ak::import_mailer('account_mailer'); |
| 82 | | $Mailer =& new AccountMailer(); |
| 83 | | $Mailer->_login = $options['login']; |
| 84 | | $Mailer->_password = $options['password']; |
| 85 | | $Mailer->deliver($options['signup_message'], $this->get('email')); |
| 86 | | } |
| 87 | | } |
| 88 | | |
| 89 | | |
| 90 | | // Validation |
| 91 | | // --------------- |
| 92 | | |
| 93 | | function validate() |
| 94 | | { |
| 95 | | $this->validatesUniquenessOf('email', array('message'=>$this->t('email %email already in use', array('%email'=>$this->get('email'))))); |
| 96 | | $this->validatesUniquenessOf('login', array('message'=>$this->t('login %login already in use', array('%login'=>$this->get('login'))))); |
| 97 | | $this->validatesPresenceOf(array('login','email')); |
| 98 | | $this->validatesFormatOf('email', AK_EMAIL_REGULAR_EXPRESSION, $this->t('Invalid email address')); |
| 99 | | $this->validatesLengthOf('login', array('in'=>array(3, 40), 'too_long' => $this->t('pick a shorter login'), 'too_short' => $this->t('pick a longer name'))); |
| 100 | | $this->validatesLengthOf('password', array('in'=>array(4, 40), 'too_long' => $this->t('pick a shorter password'), 'too_short' => $this->t('pick a longer password'))); |
| 101 | | } |
| 102 | | |
| 103 | | function validatesPassword() |
| 104 | | { |
| 105 | | $requires_password_confirmation = $this->hasAttributeBeenModified('password') ? $this->__requires_password_confirmation : false; |
| 106 | | $this->validatesPresenceOf($requires_password_confirmation ? array('password','password_confirmation') : array('password')); |
| 107 | | $requires_password_confirmation ? $this->validatesConfirmationOf('password', $this->t('Must match confirmation')) : null; |
| 108 | | return strlen($this->getErrorsOn('password').$this->getErrorsOn('password_confirmation')) == 0; |
| 109 | | } |
| 110 | | |
| 111 | | function needsPasswordLengthValidation() |
| 112 | | { |
| 113 | | return $this->isNewRecord() || !empty($this->password); |
| 114 | | } |
| 115 | | |
| 116 | | function needsEmailValidation() |
| 117 | | { |
| 118 | | return empty($this->_byspass_email_validation); |
| 119 | | } |
| 120 | | |
| 121 | | function validatesExistanceOfOriginalPasswordWhenUpdatingLogin() |
| 122 | | { |
| 123 | | if($this->hasAttributeBeenModified('login')){ |
| 124 | | if(!$this->isValidPassword($this->get('password'), true, true)){ |
| 125 | | $this->addError('login', $this->t('can\' be modified unless you provide a valid password.')); |
| 126 | | }else{ |
| 127 | | $this->set('password_confirmation', $this->get('password')); |
| 128 | | } |
| 129 | | } |
| 130 | | } |
| 131 | | |
| 132 | | function isValidPassword($password, $hash_password = true, $hash_using_original_name = false) |
| 133 | | { |
| 134 | | return $this->getPreviousValueForAttribute('password') == ($hash_password ? $this->sha1($password, $hash_using_original_name) : $password); |
| 135 | | } |
| 136 | | |
| 137 | | |
| 138 | | // Triggers |
| 139 | | // --------------- |
| 140 | | |
| 141 | | function beforeCreate() |
| 142 | | { |
| 143 | | $this->validatesPassword(); |
| 144 | | $this->encryptPassword(); |
| 145 | | return !$this->hasErrors(); |
| 146 | | } |
| 147 | | |
| 148 | | function beforeDestroy() |
| 149 | | { |
| 150 | | return !$this->hasRootPrivileges(); |
| 151 | | } |
| 152 | | |
| 153 | | function beforeUpdate() |
| 154 | | { |
| 155 | | $this->validatesExistanceOfOriginalPasswordWhenUpdatingLogin(); |
| 156 | | $this->validatesPassword(); |
| 157 | | $this->_encryptPasswordUnlessEmptyOrUnchanged(); |
| 158 | | return !$this->hasErrors(); |
| 159 | | } |
| 160 | | |
| 161 | | function afterSave() |
| 162 | | { |
| 163 | | $this->__initial_attributes = $this->getAttributes(); |
| 164 | | return true; |
| 165 | | } |
| 166 | | |
| 167 | | function afterCreate() |
| 168 | | { |
| 169 | | if(empty($this->roles)){ |
| 170 | | $this->role->load(); |
| 171 | | $Role =& new Role(); |
| 172 | | if($Role =& $Role->findFirstBy('name', AK_DEFAULT_USER_ROLE)){ |
| 173 | | $this->role->set($Role); |
| 174 | | } |
| 175 | | } |
| 176 | | return true; |
| 177 | | } |
| 178 | | |
| 179 | | |
| 180 | | |
| 181 | | // Enabling disabling accounts |
| 182 | | // -------------------------- |
| 183 | | |
| 184 | | |
| 185 | | function enable() |
| 186 | | { |
| 187 | | $this->updateAttribute('is_enabled', true); |
| 188 | | } |
| 189 | | |
| 190 | | function disable() |
| 191 | | { |
| 192 | | $this->updateAttribute('is_enabled', false); |
| 193 | | } |
| 194 | | |
| 195 | | |
| 196 | | |
| 197 | | |
| 198 | | // Inspecting original values |
| 199 | | // -------------------------- |
| 200 | | |
| 201 | | |
| 202 | | function hasAttributeBeenModified($attribute) |
| 203 | | { |
| 204 | | return $this->getPreviousValueForAttribute($attribute) != $this->get($attribute); |
| 205 | | } |
| 206 | | |
| 207 | | function getPreviousValueForAttribute($attribute) |
| 208 | | { |
| 209 | | return $this->hasColumn($attribute) && isset($this->__initial_attributes[$attribute]) ? $this->__initial_attributes[$attribute] : null; |
| 210 | | } |
| 211 | | |
| 212 | | |
| 213 | | // Hashing |
| 214 | | // ----------------------- |
| 215 | | |
| 216 | | function encryptPassword() |
| 217 | | { |
| 218 | | $this->set('password', $this->sha1($this->get('password'))); |
| 219 | | } |
| 220 | | |
| 221 | | function sha1($phrase, $use_original_login = false) |
| 222 | | { |
| 223 | | $login = $use_original_login ? $this->getPreviousValueForAttribute('login') : $this->get('login'); |
| 224 | | empty($this->password_salt) ? $this->set('password_salt', Ak::randomString(16)) : null; |
| 225 | | return sha1($this->get('password_salt').$phrase.$login); |
| 226 | | } |
| 227 | | |
| 228 | | function isTokenValid($token) |
| 229 | | { |
| 230 | | return $this->getToken() == $token; |
| 231 | | } |
| 232 | | |
| 233 | | function _encryptPasswordUnlessEmptyOrUnchanged() |
| 234 | | { |
| 235 | | if($this->hasAttributeBeenModified('password') || $this->get('password') == ''){ |
| 236 | | $this->encryptPassword(); |
| 237 | | }else{ |
| 238 | | $this->set('password', $this->getPreviousValueForAttribute('password')); |
| 239 | | } |
| 240 | | } |
| 241 | | |
| 242 | | |
| 243 | | |
| 244 | | |
| 245 | | // User::getToken(), User::loadFromToken() |
| 246 | | /** |
| 247 | | * Returns a one time use token for accesing an account. |
| 248 | | * |
| 249 | | * This might be used for retrieving lost passwords. |
| 250 | | * |
| 251 | | * Tokens can be validated using the Sentinel::isValidLoginTokenForUser method |
| 252 | | */ |
| 253 | | function getToken($options = array()) |
| 254 | | { |
| 255 | | $default_options = array( |
| 256 | | 'id' => (int)$this->get('id'), |
| 257 | | 'single_use' => !empty($options['single_use']) |
| 258 | | ); |
| 259 | | $options = array_merge($default_options, $options); |
| 260 | | |
| 261 | | $options['expires'] = empty($options['expires']) ? 0 : Ak::getTimestamp()+((empty($options['expires']) ? '0' : ($options['expires'] == true ? 86400 : $options['expires']))); |
| 262 | | $options['single_use'] = $options['single_use'] ? 1 : 0; |
| 263 | | |
| 264 | | $options['hash'] = $this->_getTokenHash($options); |
| 265 | | |
| 266 | | return $this->_encodeToken($options); |
| 267 | | } |
| 268 | | |
| 269 | | function _getTokenHash($options) |
| 270 | | { |
| 271 | | return md5($this->get('id'). |
| 272 | | $this->get('email'). |
| 273 | | $this->get('login'). |
| 274 | | $this->get('password'). |
| 275 | | $this->get('password_salt'). |
| 276 | | (!empty($options['single_use'])?$this->get('last_login_at'):''). |
| 277 | | $this->get('is_enabled'). |
| 278 | | (isset($options['expires'])?$options['expires']:'')); |
| 279 | | } |
| 280 | | |
| 281 | | /** |
| 282 | | * Given an array of options it will return an encrypted url string |
| 283 | | * |
| 284 | | * @param array $options token options |
| 285 | | * @return string Url ready authentication Token |
| 286 | | */ |
| 287 | | function _encodeToken($options) |
| 288 | | { |
| 289 | | return base64_encode(Ak::blowfishEncrypt(Ak::toJson($options), Ak::getSetting('admin', 'token_key'))); |
| 290 | | } |
| 291 | | |
| 292 | | /** |
| 293 | | * Decodes a token generated with encodeToken and returns an array of options |
| 294 | | * |
| 295 | | * @param string $token token options |
| 296 | | * @param bool $url_decode should it URL decode the token true by default |
| 297 | | * @return array Array of options for the authentication token |
| 298 | | */ |
| 299 | | function _decodeToken($token) |
| 300 | | { |
| 301 | | return (array)Ak::fromJson(Ak::blowfishDecrypt(base64_decode($token), Ak::getSetting('admin', 'token_key'))); |
| 302 | | } |
| 303 | | |
| 304 | | |
| 305 | | // Permissions |
| 306 | | // ---------------------- |
| 307 | | function &getPermissions() |
| 308 | | { |
| 309 | | $this->role->load(); |
| 310 | | $Permissions = array(); |
| 311 | | if(!empty($this->roles)){ |
| 312 | | foreach (array_keys($this->roles) as $k){ |
| 313 | | $Permissions = array_merge($Permissions, $this->roles[$k]->getPermissions()); |
| 314 | | } |
| 315 | | } |
| 316 | | return $Permissions; |
| 317 | | } |
| 318 | | |
| 319 | | function can($task, $extension = null, $force_reload = false) |
| 320 | | { |
| 321 | | if(!isset($this->_activeRecordHasBeenInstantiated) || |
| 322 | | $this->getModelName() != 'User'){ |
| 323 | | if (User::isLoaded()) { |
| 324 | | $User =& User::getCurrentUser(); |
| 325 | | return $User->can($task, $extension, $force_reload); |
| 326 | | } else { |
| 327 | | return false; |
| 328 | | } |
| 329 | | } |
| 330 | | |
| 331 | | static $Permissions; |
| 332 | | if(!isset($Permissions) || $force_reload){ |
| 333 | | $Permissions = array(); |
| 334 | | $UserPermissions =& $this->getPermissions(); |
| 335 | | foreach (array_keys($UserPermissions) as $k){ |
| 336 | | $extension_id = $UserPermissions[$k]->get('extension_id'); |
| 337 | | $Permissions[(empty($extension_id)?'core':$extension_id)][] = $UserPermissions[$k]->get('name'); |
| 338 | | } |
| 339 | | } |
| 340 | | $extension_id = $this->_getExtensionId($extension); |
| 341 | | return (!empty($Permissions[$extension_id]) && in_array($task, $Permissions[$extension_id])) ? true : $this->_addRootPermission($task, $extension_id); |
| 342 | | } |
| 343 | | |
| 344 | | function hasRole($role_name, $force_reload = false) |
| 345 | | { |
| 346 | | if(!isset($this->_activeRecordHasBeenInstantiated)){ |
| 347 | | $User =& User::getCurrentUser(); |
| 348 | | return $User->hasRole($role_name, $force_reload); |
| 349 | | } |
| 350 | | $role_name = strtolower($role_name); |
| 351 | | $Roles =& $this->getRoles($force_reload); |
| 352 | | if(!empty($Roles)){ |
| 353 | | foreach(array_keys($Roles) as $k){ |
| 354 | | if(strtolower($Roles[$k]->get('name')) == $role_name){ |
| 355 | | return true; |
| 356 | | } |
| 357 | | } |
| 358 | | } |
| 359 | | return false; |
| 360 | | } |
| 361 | | |
| 362 | | function &getRoles($force_reload = false) |
| 363 | | { |
| 364 | | if((!isset($this->LoadedRoles) || $force_reload) && $this->role->load()){ |
| 365 | | $this->LoadedRoles = array(); |
| 366 | | foreach (array_keys($this->roles) as $k){ |
| 367 | | $this->LoadedRoles[$this->roles[$k]->getId()] =& $this->roles[$k]; |
| 368 | | foreach ($this->roles[$k]->nested_set->getFullSet() as $Role){ |
| 369 | | $this->LoadedRoles[$Role->getId()] = $Role; |
| 370 | | } |
| 371 | | } |
| 372 | | return $this->LoadedRoles; |
| 373 | | } |
| 374 | | $result = array(); |
| 375 | | return $result; |
| 376 | | } |
| 377 | | |
| 378 | | function hasRootPrivileges() |
| 379 | | { |
| 380 | | $this->role->load(); |
| 381 | | return isset($this->roles[0]) ? $this->roles[0]->nested_set->isRoot() : false; |
| 382 | | } |
| 383 | | |
| 384 | | function _addRootPermission($task, $extension_id) |
| 385 | | { |
| 386 | | if($this->hasRootPrivileges()){ |
| 387 | | $Permission =& new Permission(); |
| 388 | | $Permission =& $Permission->findOrCreateBy('name AND extension_id', $task, $extension_id); |
| 389 | | $this->roles[0]->addPermission($Permission); |
| 390 | | return true; |
| 391 | | } |
| 392 | | return false; |
| 393 | | } |
| 394 | | |
| 395 | | function _getExtensionId($extension, $force_reload = false) |
| 396 | | { |
| 397 | | static $extenssion_ids = array(); |
| 398 | | if(is_string($extension) && !is_numeric($extension)){ |
| 399 | | if(isset($extenssion_ids[$extension]) && $force_reload == false){ |
| 400 | | return $extenssion_ids[$extension]; |
| 401 | | } |
| 402 | | $extension_key = $extension; |
| 403 | | Ak::import('Extension'); |
| 404 | | $ExtensionInstance =& new Extension(); |
| 405 | | $extension =& $ExtensionInstance->findOrCreateBy('name', $extension); |
| 406 | | } |
| 407 | | $extension = is_object($extension) ? $extension->getId() : (empty($extension)?'core':$extension); |
| 408 | | isset($extension_key) ? $extenssion_ids[$extension_key] = $extension : null; |
| 409 | | return $extension; |
| 410 | | } |
| 411 | | |
| 412 | | |
| 413 | | /** |
| 414 | | * Returns the current user if it is set, otherwise throws an error |
| 415 | | * |
| 416 | | * @see isLoaded() to check before and not throw an error |
| 417 | | * @return User |
| 418 | | */ |
| 419 | | function getCurrentUser() |
| 420 | | { |
| 421 | | $User =& Ak::getStaticVar('CurrentUser'); |
| 422 | | if (empty($User)) { |
| 423 | | trigger_error(Ak::t('Current user has not been set yet.'), E_USER_ERROR); |
| 424 | | } |
| 425 | | return $User; |
| 426 | | } |
| 427 | | /** |
| 428 | | * Checks if the user is set |
| 429 | | * |
| 430 | | * @return boolean |
| 431 | | */ |
| 432 | | function isLoaded() |
| 433 | | { |
| 434 | | return Ak::getStaticVar('CurrentUser') != null; |
| 435 | | } |
| 436 | | |
| 437 | | /** |
| 438 | | * Sets the current user |
| 439 | | * |
| 440 | | * @param User $CurrentUser |
| 441 | | */ |
| 442 | | function setCurrentUser($CurrentUser) |
| 443 | | { |
| 444 | | Ak::_staticVar('CurrentUser', $CurrentUser); |
| 445 | | } |
| 446 | | |
| 447 | | |
| 448 | | function unsetCurrentUser() |
| 449 | | { |
| 450 | | User::setCurrentUser(null); |
| 451 | | } |