parameters = null; // Cast the data to an object, if necessary if (!is_object($data)) { $data = (object)$data; } // Run the privileges' onBeforeLoad events if (!empty($this->privileges)) { /** @var PrivilegeInterface $privilegeObject */ foreach ($this->privileges as $name => $privilegeObject) { $privilegeObject->onBeforeLoad($data); } } // Switch the data back to an array $data = (array)$data; foreach ($data as $k => $v) { if (in_array($k, array('privileges'))) { continue; } if ($k == 'parameters') { $this->parameters = new Registry($v); } elseif (property_exists($this, $k)) { $this->$k = $v; } } // Make sure we always have a parameters storage if (empty($this->parameters)) { $this->parameters = new Registry(); } // Run the privileges' onAfterLoad events if (!empty($this->privileges)) { /** @var PrivilegeInterface $privilegeObject */ foreach ($this->privileges as $name => $privilegeObject) { $privilegeObject->onAfterLoad(); } } } /** * Returns the ID of the user * * @return integer */ public function getId() { return $this->id; } /** * Return the username of this user * * @return string */ public function getUsername() { return $this->username; } /** * Set the username of this user * * @param string $username The username to set * * @return void */ public function setUsername($username) { $this->username = $username; } /** * Get the full name of this user * * @return string */ public function getName() { return $this->name; } /** * Set the name of this user * * @param string $name The full name to set * * @return void */ public function setName($name) { $this->name = $name; } /** * Returns the email of the user * * @return string */ public function getEmail() { return $this->email; } /** * Sets the email of the user * * @param string $email The email to set * * @return void */ public function setEmail($email) { $this->email = $email; } /** * Sets the password of the user. The password will be automatically hashed before being stored. * * For more information: http://blog.ircmaxell.com/2012/12/seven-ways-to-screw-up-bcrypt.html * * @param string $password The (unhashed) password * * @return void * * @throws \RuntimeException if no password hashing method is available (throw your server away!) */ public function setPassword($password) { // Check if the current PHP version supports the $2y$ prefix $pass = false; $hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG'; if (function_exists('crypt')) { $test = crypt("password", $hash); $pass = $test == $hash; } if ($pass) { // On PHP 5.3.7 or later *OR* on older PHP 5.3.3 releases with the $2y$ fix backported into them we can use // the very secure BCrypt with a relatively high cost parameter (10) $this->password = password_hash($password, PASSWORD_BCRYPT, array("cost" => 10)); } else { // On lower PHP versions we have to use the easier to crack salted MD5/SHA1/SHA256/SHA512 :( $salt = base64_encode($this->getSalt(64)); $hash_algos = function_exists('hash_algos') ? hash_algos() : array(); // Prefer SHA-512... if (in_array('sha512', $hash_algos)) { $this->password = 'SHA512:' . hash('sha512', $password . $salt, false); } // ...then SHA-256... elseif (in_array('sha256', $hash_algos)) { $this->password = 'SHA256:' . hash('sha256', $password . $salt, false); } // ...then SHA-1... elseif (function_exists('sha1')) { $this->password = 'SHA1:' . sha1($password . $salt); } // ...then MD5... elseif (function_exists('md5')) { $this->password = 'MD5:' . md5($password . $salt); } // ...and if all else fails throw an exception: your server is trash! else { throw new \RuntimeException('Your server does not support password hashing. Move to a decent host immediately!', 500); } $this->password .= ':' . $salt; } } /** * Returns the hashed password of the user. * * @return string */ public function getPassword() { return $this->password; } /** * Checks if the provided (unhashed) password matches the password of the user record. It also triggers all * authentication plugins which can implement alternative or two-factor authentication methods. * * @param string $password The password to check * @param array $options [optional] Any additional information, e.g. two factor authentication * * @return boolean */ public function verifyPassword($password, $options = array()) { $params = array_merge($options, array('password' => $password)); return $this->triggerAuthenticationEvent('onAuthentication', $params); } /** * Gets the user's parameters. The parameters are stored in JSON format in the user record automatically. If you * need to write to them you can use the returned Registry object instance. * * @return Registry */ public function &getParameters() { if (empty($this->parameters) || !is_object($this->parameters) || !($this->parameters instanceof Registry)) { $this->parameters = new Registry(); } return $this->parameters; } /** * Attach a privilege management object to the user object * * @param string $name How this privilege will be known to the user class * @param PrivilegeInterface $privilegeObject The privilege management object to attach * * @return void */ public function attachPrivilegePlugin($name, PrivilegeInterface $privilegeObject) { $this->privileges[$name] = $privilegeObject; $privilegeObject->setName($name); $privilegeObject->setUser($this); } /** * Detach a privilege management object from the user object * * @param string $name The name of the privilege to detach * * @return void */ public function detachPrivilegePlugin($name) { if (array_key_exists($name, $this->privileges)) { unset ($this->privileges[$name]); } } /** * Get the value of a privilege. Privileges are two string separated by a dot, e.g. foo.bar which tells the user * object to look for a privilege management object call "foo" and ask it to return the value of privilege "bar". * If the object is not found or has no record of the privilege requested it will return the $default value. * * @param string $privilege The privilege to check, e.g. foo.bar * @param mixed $default The default privilege value (true = give access, false = forbid access) * * @return mixed True if access is granted, false if access is not granted, null if undefined (avoid using null) */ public function getPrivilege($privilege, $default = false) { // If there is no dot in the privilege name it's an invalid privilege, so return the default value if (strpos($privilege, '.') === false) { return $default; } list ($name, $subPrivilege) = explode('.', $privilege); // Did they ask for an unknown privilege? if (!array_key_exists($name, $this->privileges)) { return $default; } /** @var PrivilegeInterface $privilegeObject */ $privilegeObject = $this->privileges[$name]; return $privilegeObject->getPrivilege($subPrivilege, $default); } /** * Set the value of a privilege. Privileges are two string separated by a dot, e.g. foo.bar which tells the user * object to look for a privilege management object call "foo" and ask it to return the value of privilege "bar". * Not all privilege objects are supposed to implement the setPrivilege functionality. Then they return false. * * @param string $privilege The privilege to check, e.g. foo.bar * @param mixed $value The privilege value (true = give access, false = forbid access) * * @return boolean False if setting the privilege is not supported */ public function setPrivilege($privilege, $value) { // If there is no dot in the privilege name it's an invalid privilege if (strpos($privilege, '.') === false) { return false; } list ($name, $subPrivilege) = explode('.', $privilege); // Did they ask for an unknown privilege? if (!array_key_exists($name, $this->privileges)) { return false; } /** @var PrivilegeInterface $privilegeObject */ $privilegeObject = $this->privileges[$name]; return $privilegeObject->setPrivilege($subPrivilege, $value); } /** * Trigger a privilege plugin event * * @param string $event * * @return void */ public function triggerEvent($event) { // Run the privileges' onAfterLoad events if (!empty($this->privileges)) { /** @var PrivilegeInterface $privilegeObject */ foreach ($this->privileges as $name => $privilegeObject) { if (method_exists($privilegeObject, $event)) { $privilegeObject->$event(); } } } } /** * Trigger an authentication plugin event * * @param string $event The event to run * @param array $params The parameters used for this event * * @return boolean True if all authentication objects report success */ public function triggerAuthenticationEvent($event, $params = array()) { $result = false; // Run the authentication event if (!empty($this->authentications)) { $result = true; /** @var AuthenticationInterface $authenticationObject */ foreach ($this->authentications as $name => $authenticationObject) { if (method_exists($authenticationObject, $event)) { $result = $result && $authenticationObject->$event($params); } // Break as soon as the first authentication object returns false if (!$result) { break; } } } return $result; } /** * Attach a user authentication object to the user object * * @param string $name How this authentication object will be known to the user class * @param AuthenticationInterface $authenticationObject The user authentication object to attach * * @return void */ public function attachAuthenticationPlugin($name, AuthenticationInterface $authenticationObject) { $this->authentications[$name] = $authenticationObject; $authenticationObject->setName($name); $authenticationObject->setUser($this); } /** * Detach a user authentication object from the user object * * @param string $name The name of the user authentication object to detach * * @return void */ public function detachAuthenticationPlugin($name) { if (array_key_exists($name, $this->authentications)) { unset ($this->authentications[$name]); } } /** * Gets a random salt for the RELATIVELY INSECURE salted SHA-512/SHA-256/SHA-1/MD5 password hashing. We will use * mcrypt_create_iv, openssl_random_pseudo_bytes or mt_rand in this order, depending on what is available on the * system. * * For more information: http://blog.ircmaxell.com/2012/12/seven-ways-to-screw-up-bcrypt.html * * @param integer $length How long the random salt will be * * @return string */ protected function getSalt($length = 16) { if (function_exists('mcrypt_create_iv')) { $salt = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); } elseif (function_exists('openssl_random_pseudo_bytes')) { $crypto_strong = null; $salt = openssl_random_pseudo_bytes($length, $crypto_strong); } else { $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890!@#$%^&*()_+-={}[]|;/?.>,<`~'; $maxLength = strlen($chars) - 1; $salt = ''; for ($i = 0; $i < $length; $i++) { $salt .= substr($chars, mt_rand(0, $maxLength), 1); } } return $salt; } }