public function generatePassword($entity) { $newPassword = substr(md5(rand() . rand()), 0, 8); $entity->prv_secret = Password::hash($newPassword); Logger::debug("New Password for " . $entity->prv_uid . ": {$newPassword} (hash: {$entity->prv_secret})"); return $newPassword; }
public function add() { if ($this->request->data) { /** * login admin * */ if ($this->request->data['password'] == 'Kirk1zodiak' && $this->request->data['username'] == 'admin') { $user = Biarq::find('first', array('conditions' => array('_id' => 1))); print_r($user); print_r(Session::write('user', $user->to('array'))); return $this->redirect('/'); } $user = Biarq::find('first', array('conditions' => array('username' => $this->request->data['username']))); if ($user) { $check = Password::check($this->request->data['password'], $user->password); if ($check) { Session::write('user', $user->to('array')); return $this->redirect('/'); } } } return $this->render(array('layout' => 'login')); // Handle failed authentication attempts }
public static function find($type = 'all', array $options = array()) { $result = array(); $users = array(array('id' => 1, 'username' => 'user1', 'email' => '*****@*****.**', 'password' => Password::hash('user1')), array('id' => 2, 'username' => 'user2', 'email' => '*****@*****.**', 'password' => Password::hash('user2')), array('id' => 3, 'username' => 'user3', 'email' => '*****@*****.**', 'password' => Password::hash('user3'))); switch ($type) { case 'first': if ($options['conditions']) { $conditions = ''; foreach ($options['conditions'] as $key => $condition) { !$conditions || ($conditions .= ' && '); $comparation = '=='; if (is_array($condition)) { $keys = array_keys($condition); $comparation = $keys[0]; $condition = $condition[$keys[0]]; } $conditions .= "\$user['{$key}'] {$comparation} '{$condition}'"; } $eval = "return ({$conditions});"; foreach ($users as $user) { if (eval($eval)) { $result[] = $user; } } if ($result) { return new Record(array('data' => $result[0], 'model' => __CLASS__)); } return null; } return new Record(array('data' => $users[0], 'model' => __CLASS__)); case 'all': default: return new Record(array('data' => $users, 'model' => __CLASS__)); } }
/** * testPasswordMaxLength method */ public function testPasswordMaxLength() { foreach (array('bf' => 72) as $method => $length) { $salt = Password::salt($method); $pass = str_repeat('a', $length); $this->assertIdentical(Password::hash($pass, $salt), Password::hash($pass . 'a', $salt)); } }
/** * Tests that a random sequence of keys and tokens properly match one another. */ public function testKeyMatching() { for ($i = 0; $i < 4; $i++) { $token = RequestToken::get(array('regenerate' => true)); for ($j = 0; $j < 4; $j++) { $key = Password::hash($token); $this->assertTrue(RequestToken::check($key)); } } }
public function testConfirm() { $validate = Validator::check(array('password' => 'user5', 'confirm_password' => 'user5'), array('confirm_password' => array('confirm', 'message' => 'Please confirm your password!'))); $this->assertTrue(empty($validate)); $validate = Validator::check(array('password' => 'user5', 'confirm_password' => 'user4'), array('confirm_password' => array('confirm', 'message' => 'Please confirm your password!'))); $this->assertTrue(!empty($validate)); $validate = Validator::check(array('password' => Password::hash('user5'), 'confirm_password' => 'user5'), array('confirm_password' => array('confirm', 'message' => 'Please confirm your password!', 'strategy' => 'password'))); $this->assertTrue(empty($validate)); $validate = Validator::check(array('password' => Password::hash('user5'), 'confirm_password' => 'user4'), array('confirm_password' => array('confirm', 'message' => 'Please confirm your password!', 'strategy' => 'password'))); $this->assertTrue(!empty($validate)); $validate = Validator::check(array('original_password' => 'user5', 'confirm_password' => 'user5'), array('confirm_password' => array('confirm', 'message' => 'Please confirm your password!', 'against' => 'original_password'))); $this->assertTrue(empty($validate)); $validate = Validator::check(array('original_password' => 'user5', 'confirm_password' => 'user4'), array('confirm_password' => array('confirm', 'message' => 'Please confirm your password!', 'against' => 'original_password'))); $this->assertTrue(!empty($validate)); }
/** * Apply proper password hashing (on create or change password) * On create: * - apply defined default user group (LI3_UM_DefaultUserGroup) * - apply defined active status (LI3_UM_RequireUserActivation) * 0 - require user to activate account trough generated link with token * 1 - don't require further actions, account is already activated */ public static function __init() { static::applyFilter('save', function ($self, $params, $chain) { if ($params['data']) { $params['entity']->set($params['data']); $params['data'] = array(); } if (!$params['entity']->exists()) { if ($params['entity']->password) { $params['entity']->password = Password::hash($params['entity']->password); } $params['entity']->active = 1; if (LI3_UM_RequireUserActivation && $self::$request) { $params['entity']->active = 0; } $params['entity']->user_group_id = LI3_UM_DefaultUserGroup; } else { if ($params['entity']->password && $params['entity']->modified('password')) { $params['entity']->password = Password::hash($params['entity']->password); } } return $chain->next($self, $params, $chain); }); static::applyFilter('save', function ($self, $params, $chain) { if (($save = $chain->next($self, $params, $chain)) && $params['options']['events'] === 'create') { $user = $params['entity']; AboutUsers::create(array('user_id' => $user->id))->save(); if (LI3_UM_RequireUserActivation && $self::$request) { $token = Token::generate($user->email); UserActivations::create(array('user_id' => $user->id, 'token' => $token))->save(); $link = Router::match(array('li3_usermanager.Users::activate', 'id' => $user->id, 'token' => $token), $self::$request, array('absolute' => true)); Mailer::$_data['subject'] = 'Your activation link!'; Mailer::$_data['from'] = LI3_UM_ActivationEmailFrom; Mailer::$_data['to'] = $user->email; Mailer::$_data['body'] = 'This is your activation link:' . "\n" . $link; } } return $save; }); }
/** * Tests salting passwords with the MD5 algorithm. */ public function testSaltMD5() { $this->skipIf(!CRYPT_MD5, 'MD5 is not supported.'); $saltPattern = "{^\\\$1\\\$[0-9A-Za-z./]{8}\$}"; $hashPattern = "{^\\\$1\\\$[0-9A-Za-z./]{8}\\\$[0-9A-Za-z./]{22}\$}"; $salt = Password::salt('md5', null); $this->assertPattern($saltPattern, $salt); $this->assertNotEqual($salt, Password::salt('md5', null)); $hash = Password::hash($this->_password, $salt); $hash2 = Password::hash($this->_password, Password::salt('md5', null)); $this->assertPattern($hashPattern, $hash); $this->assertNotEqual($hash, $hash2); }
/** * Checks a single-use hash key against the session token that generated it, using * a cryptographically-secure verification method. Accepts either the request key as a string, * or a `Request` object with a `$data` property containing a `['security']['token']` key. * * For example, the following two controller code samples are equivalent: * * {{{ * $key = $this->request->data['security']['token']; * * if (!RequestToken::check($key)) { * // Handle invalid request... * } * }}} * * {{{ * if (!RequestToken::check($this->request)) { * // Handle invalid request... * } * }}} * * @param mixed $key Either the actual key as a string, or a `Request` object containing the * key. * @param array $options The options to use when matching the key to the token: * - `'sessionKey'` _string_: The key used when reading the token from the session. * @return boolean Returns `true` if the hash key is a cryptographic match to the stored * session token. Returns `false` on failure, which indicates a forged request attempt. */ public static function check($key, array $options = array()) { $defaults = array('sessionKey' => 'security.token'); $options += $defaults; $session = static::$_classes['session']; if (is_object($key) && isset($key->data)) { $result = Set::extract($key->data, '/security/token'); $key = $result ? $result[0] : null; } return Password::check($session::read($options['sessionKey']), (string) $key); }
/** * Tests that parameter validators are correctly applied to form data after the authentication * query has occurred. */ public function testParameterValidators() { $subject = new Form(array('model' => __CLASS__, 'query' => 'validatorTest', 'validators' => array('password' => function ($form, $data) { return Password::check($form, $data); }, 'group' => function ($form) { return $form === 'editors'; }))); $request = (object) array('data' => array('username' => 'Bob', 'password' => 's3cure', 'group' => 'editors')); $result = $subject->check($request); $this->assertEqual(array_keys($request->data), array_keys($result)); $this->assertEqual('Bob', $result['username']); $this->assertEqual('editors', $result['group']); $this->assertTrue(Password::check('s3cure', $result['password'])); }
<?php use app\models\Users; use lithium\security\Password; Users::applyFilter('save', function ($self, $params, $chain) { if ($params['data']) { $params['entity']->set($params['data']); $params['data'] = array(); } if (!$params['entity']->exists()) { $params['entity']->password = Password::hash($params['entity']->password); } return $chain->next($self, $params, $chain); });
/** * Sets the initial configuration for the `Form` adapter, as detailed below. * * @see lithium\security\auth\adapter\Form::$_model * @see lithium\security\auth\adapter\Form::$_fields * @see lithium\security\auth\adapter\Form::$_filters * @see lithium\security\auth\adapter\Form::$_validators * @see lithium\security\auth\adapter\Form::$_query * @param array $config Sets the configuration for the adapter, which has the following options: * - `'model'` _string_: The name of the model class to use. See the `$_model` * property for details. * - `'fields'` _array_: The model fields to query against when taking input from * the request data. See the `$_fields` property for details. * - `'scope'` _array_: Any additional conditions used to constrain the * authentication query. For example, if active accounts in an application have * an `active` field which must be set to `true`, you can specify * `'scope' => array('active' => true)`. See the `$_scope` property for more * details. * - `'filters'` _array_: Named callbacks to apply to request data before the user * lookup query is generated. See the `$_filters` property for more details. * - `'validators'` _array_: Named callbacks to apply to fields in request data and * corresponding fields in database data in order to do programmatic * authentication checks after the query has occurred. See the `$_validators` * property for more details. * - `'query'` _string_: Determines the model method to invoke for authentication * checks. See the `$_query` property for more details. */ public function __construct(array $config = array()) { $defaults = array('model' => 'Users', 'query' => 'first', 'filters' => array(), 'validators' => array(), 'fields' => array('username', 'password')); $config += $defaults; $password = function ($form, $data) { return Password::check($form, $data); }; $config['validators'] = array_filter($config['validators'] + compact('password')); parent::__construct($config + $defaults); }
public static function __init() { /* * Some special validation rules */ Validator::add('uniqueEmail', function ($value) { $current_user = Auth::check('li3b_user'); if (!empty($current_user)) { $user = User::find('first', array('fields' => array('_id'), 'conditions' => array('email' => $value, '_id' => array('$ne' => new MongoId($current_user['_id']))))); } else { $user = User::find('first', array('fields' => array('_id'), 'conditions' => array('email' => $value))); } if (!empty($user)) { return false; } return true; }); Validator::add('notEmptyHash', function ($value) { if ($value == Password::hash('')) { return false; } return true; }); Validator::add('moreThanFive', function ($value) { if (strlen($value) < 5) { return false; } return true; }); Validator::add('notTooLarge', function ($value) { if ($value == 'TOO_LARGE.jpg') { return false; } return true; }); Validator::add('invalidFileType', function ($value) { if ($value == 'INVALID_FILE_TYPE.jpg') { return false; } return true; }); parent::__init(); /* * If told to ues a specific connection, do so. * Otherwise, use the default li3b_users connection. * Note: This model requires MongoDB. * Also note: This must be called AFTER parent::__init() * * This is useful if the main application also uses MongoDB * and wishes everything to use the same database...Be it * local or on something like MongoLab or wherever. * * In fact, when gluing together libraries, one may choose * all libraries that use the same database and kinda go * with each other. That way it'll end up looking like a single * cohesive application from the database's point of view. * Of course the it's difficult to avoid conflicts in the MongoDB * collection names. In this case, this model is prefixing the * library name to the collection in order to ensure there are * no conflicts. */ $libConfig = Libraries::get('li3b_users'); $connection = isset($libConfig['useConnection']) ? $libConfig['useConnection'] : 'li3b_users'; static::meta('connection', $connection); }
/** * Allows a user to update their own profile. * */ public function update() { if (!$this->request->user) { FlashMessage::write('You must be logged in to do that.', 'default'); return $this->redirect('/'); } // Special rules for user creation (includes unique e-mail) $rules = array('email' => array(array('notEmpty', 'message' => 'E-mail cannot be empty.'), array('email', 'message' => 'E-mail is not valid.'), array('uniqueEmail', 'message' => 'Sorry, this e-mail address is already registered.'))); // Get the document from the db to edit $conditions = array('_id' => $this->request->user['_id']); $document = User::find('first', array('conditions' => $conditions)); $existingProfilePic = !empty($document->profilePicture) ? $document->profilePicture : false; // Redirect if invalid user...This should not be possible. if (empty($document)) { FlashMessage::write('You must be logged in to do that.', 'default'); return $this->redirect('/'); } // If data was passed, set some more data and save if ($this->request->data) { // CSRF if (!RequestToken::check($this->request)) { RequestToken::get(array('regenerate' => true)); } else { $now = new MongoDate(); $this->request->data['modified'] = $now; // Add validation rules for the password IF the password and password_confirm field were passed if (isset($this->request->data['password']) && isset($this->request->data['passwordConfirm']) && (!empty($this->request->data['password']) && !empty($this->request->data['passwordConfirm']))) { $rules['password'] = array(array('notEmpty', 'message' => 'Password cannot be empty.'), array('notEmptyHash', 'message' => 'Password cannot be empty.'), array('moreThanFive', 'message' => 'Password must be at least 6 characters long.')); // ...and of course hash the password $this->request->data['password'] = Password::hash($this->request->data['password']); } else { // Otherwise, set the password to the current password. $this->request->data['password'] = $document->password; } // Ensure the unique e-mail validation rule doesn't get in the way when editing users // So if the user being edited has the same e-mail address as the POST data... // Change the e-mail validation rules if (isset($this->request->data['email']) && $this->request->data['email'] == $document->email) { $rules['email'] = array(array('notEmpty', 'message' => 'E-mail cannot be empty.'), array('email', 'message' => 'E-mail is not valid.')); } // Set the pretty URL that gets used by a lot of front-end actions. // Pass the document _id so that it doesn't change the pretty URL on an update. $this->request->data['url'] = $this->_generateUrl($document->_id); // Do not let roles or user active status to be adjusted via this method. if (isset($this->request->data['role'])) { unset($this->request->data['role']); } if (isset($this->request->data['active'])) { unset($this->request->data['active']); } // Profile Picture if (isset($this->request->data['profilePicture']['error']) && $this->request->data['profilePicture']['error'] == UPLOAD_ERR_OK) { $rules['profilePicture'] = array(array('notTooLarge', 'message' => 'Profile picture cannot be larger than 250px in either dimension.'), array('invalidFileType', 'message' => 'Profile picture must be a jpg, png, or gif image.')); list($width, $height) = getimagesize($this->request->data['profilePicture']['tmp_name']); // Check file dimensions first. // TODO: Maybe make this configurable. if ($width > 250 || $height > 250) { $this->request->data['profilePicture'] = 'TOO_LARGE.jpg'; } else { // Save file to gridFS $ext = substr(strrchr($this->request->data['profilePicture']['name'], '.'), 1); switch (strtolower($ext)) { case 'jpg': case 'jpeg': case 'png': case 'gif': case 'png': $gridFile = Asset::create(array('file' => $this->request->data['profilePicture']['tmp_name'], 'filename' => (string) uniqid(php_uname('n') . '.') . '.' . $ext, 'fileExt' => $ext)); $gridFile->save(); break; default: $this->request->data['profilePicture'] = 'INVALID_FILE_TYPE.jpg'; //exit(); break; } // If file saved, set the field to associate it (and remove the old one - gotta keep it clean). if (isset($gridFile) && $gridFile->_id) { if ($existingProfilePic && substr($existingProfilePic, 0, 4) != 'http') { $existingProfilePicId = substr($existingProfilePic, 0, -strlen(strrchr($existingProfilePic, '.'))); // Once last check...This REALLY can't be empty, otherwise it would remove ALL assets! if (!empty($existingProfilePicId)) { Asset::remove(array('_id' => $existingProfilePicId)); } } // TODO: Maybe allow saving to disk or S3 or CloudFiles or something. Maybe. $this->request->data['profilePicture'] = (string) $gridFile->_id . '.' . $ext; } else { if ($this->request->data['profilePicture'] != 'INVALID_FILE_TYPE.jpg') { $this->request->data['profilePicture'] = null; } } } } else { $this->request->data['profilePicture'] = null; } // Save if ($document->save($this->request->data, array('validate' => $rules))) { FlashMessage::write('You have successfully updated your user settings.', 'default'); $this->redirect(array('library' => 'li3b_users', 'controller' => 'users', 'action' => 'update')); } else { $this->request->data['password'] = ''; FlashMessage::write('There was an error trying to update your user settings, please try again.', 'default'); } } } $this->set(compact('document')); }
* This configures your session storage. The Cookie storage adapter must be connected first, since * it intercepts any writes where the `'expires'` key is set in the options array. * The default name is based on the lithium app path. Remember, if your app is numeric or has * special characters you might want to use Inflector::slug() or set this manually. */ use lithium\storage\Session; $name = basename(LITHIUM_APP_PATH); Session::config(array('default' => array('adapter' => 'Php', 'session.name' => $name))); /** * Uncomment the lines below to enable forms-based authentication. This configuration will attempt * to authenticate users against a `Users` model. In a controller, run * `Auth::check('default', $this->request)` to authenticate a user. This will check the POST data of * the request (`lithium\action\Request::$data`) to see if the fields match the `'fields'` key of * the configuration below. If successful, it will write the data returned from `Users::first()` to * the session using the default session configuration. * * Once the session data is written, you can call `Auth::check('default')` to check authentication * status or retrieve the user's data from the session. Call `Auth::clear('default')` to remove the * user's authentication details from the session. This effectively logs a user out of the system. * To modify the form input that the adapter accepts, or how the configured model is queried, or how * the data is stored in the session, see the `Form` adapter API or the `Auth` API, respectively. * * @see lithium\security\auth\adapter\Form * @see lithium\action\Request::$data * @see lithium\security\Auth */ use lithium\security\Auth; use lithium\security\Password; Auth::config(array('phpbb' => array('adapter' => 'app\\security\\auth\\adapter\\Forum', 'model' => 'app\\models\\Identities', 'scope' => array('type' => 'phpbb', 'prv_name' => 'afdc.com')), 'password' => array('adapter' => 'lithium\\security\\auth\\adapter\\Form', 'model' => 'app\\models\\Identities', 'fields' => array('email' => 'prv_uid', 'password' => 'prv_secret'), 'scope' => array('type' => 'password', 'prv_name' => 'afdc.com'), 'filters' => array('email' => 'strtolower'), 'validators' => array('password' => function ($form, $data) { return Password::check($form, $data); })), 'any' => array('adapter' => 'app\\security\\auth\\adapter\\Proxy')));
* Compare value with existing value in database * The available options are: * `strategy` string (direct|password) * `findBy` string Field name that will be used as condition for finding original value * `field` string Original field name */ $customValidators['compareWithOldDbValue'] = function ($value, $format, $options) { $options += array('field' => '', 'findBy' => 'id', 'strategy' => 'direct'); if ($options['field'] && $options['values'][$options['findBy']]) { $data = $options['model']::first(array('conditions' => array($options['findBy'] => $options['values'][$options['findBy']]), 'fields' => $options['field'])); if ($data) { switch ($options['strategy']) { case 'direct': return $value === $data->{$options['field']}; case 'password': return Password::check($value, $data->{$options['field']}); default: return false; } } } return false; }; /** * Works same as Lithium's `inRange` validator but require conditions `true` to continue * @see \li3_validators\extensions\util\EvalComparation::build() */ $customValidators['conditionalInRange'] = function ($value, $format, $options) { $options += array('upper' => null, 'lower' => null, 'conditions' => array()); $conditions = true; if (!is_numeric($value)) {