/**
  * Attempt to find and authenticate member if possible from the given data
  *
  * @param array $data
  * @param Form $form
  * @param bool &$success Success flag
  * @return Member Found member, regardless of successful login
  */
 protected static function authenticate_member($data, $form, &$success)
 {
     // Default success to false
     $success = false;
     // Attempt to identify by temporary ID
     $member = null;
     $email = null;
     if (!empty($data['tempid'])) {
         // Find user by tempid, in case they are re-validating an existing session
         $member = Member::member_from_tempid($data['tempid']);
         if ($member) {
             $email = $member->Email;
         }
     }
     // Otherwise, get email from posted value instead
     /** @skipUpgrade */
     if (!$member && !empty($data['Email'])) {
         $email = $data['Email'];
     }
     // Check default login (see Security::setDefaultAdmin())
     $asDefaultAdmin = $email === Security::default_admin_username();
     if ($asDefaultAdmin) {
         // If logging is as default admin, ensure record is setup correctly
         $member = Member::default_admin();
         $success = !$member->isLockedOut() && Security::check_default_admin($email, $data['Password']);
         //protect against failed login
         if ($success) {
             return $member;
         }
     }
     // Attempt to identify user by email
     if (!$member && $email) {
         // Find user by email
         $member = Member::get()->filter(Member::config()->unique_identifier_field, $email)->first();
     }
     // Validate against member if possible
     if ($member && !$asDefaultAdmin) {
         $result = $member->checkPassword($data['Password']);
         $success = $result->valid();
     } else {
         $result = new ValidationResult(false, _t('Member.ERRORWRONGCRED'));
     }
     // Emit failure to member and form (if available)
     if (!$success) {
         if ($member) {
             $member->registerFailedLogin();
         }
         if ($form) {
             $form->sessionMessage($result->message(), 'bad');
         }
     } else {
         if ($member) {
             $member->registerSuccessfulLogin();
         }
     }
     return $member;
 }
 /**
  * Test combining validation results together
  */
 public function testCombineResults()
 {
     $result = new ValidationResult();
     $anotherresult = new ValidationResult();
     $yetanotherresult = new ValidationResult();
     $anotherresult->error("Eat with your mouth closed", "EATING101");
     $yetanotherresult->error("You didn't wash your hands", "BECLEAN");
     $this->assertTrue($result->valid());
     $this->assertFalse($anotherresult->valid());
     $this->assertFalse($yetanotherresult->valid());
     $result->combineAnd($anotherresult)->combineAnd($yetanotherresult);
     $this->assertFalse($result->valid());
     $this->assertEquals(array("EATING101" => "Eat with your mouth closed", "BECLEAN" => "You didn't wash your hands"), $result->messageList());
 }
 /**
  * @param String $password
  * @param Member $member
  * @return ValidationResult
  */
 public function validate($password, $member)
 {
     $valid = ValidationResult::create();
     if ($this->minLength) {
         if (strlen($password) < $this->minLength) {
             $valid->error(sprintf(_t('PasswordValidator.TOOSHORT', 'Password is too short, it must be %s or more characters long'), $this->minLength), 'TOO_SHORT');
         }
     }
     if ($this->minScore) {
         $score = 0;
         $missedTests = array();
         foreach ($this->testNames as $name) {
             if (preg_match(self::config()->character_strength_tests[$name], $password)) {
                 $score++;
             } else {
                 $missedTests[] = _t('PasswordValidator.STRENGTHTEST' . strtoupper($name), $name, 'The user needs to add this to their password for more complexity');
             }
         }
         if ($score < $this->minScore) {
             $valid->error(sprintf(_t('PasswordValidator.LOWCHARSTRENGTH', 'Please increase password strength by adding some of the following characters: %s'), implode(', ', $missedTests)), 'LOW_CHARACTER_STRENGTH');
         }
     }
     if ($this->historicalPasswordCount) {
         $previousPasswords = MemberPassword::get()->where(array('"MemberPassword"."MemberID"' => $member->ID))->sort('"Created" DESC, "ID" DESC')->limit($this->historicalPasswordCount);
         /** @var MemberPassword $previousPassword */
         foreach ($previousPasswords as $previousPassword) {
             if ($previousPassword->checkPassword($password)) {
                 $valid->error(_t('PasswordValidator.PREVPASSWORD', 'You\'ve already used that password in the past, please choose a new password'), 'PREVIOUS_PASSWORD');
                 break;
             }
         }
     }
     return $valid;
 }
 /**
  * Validate the owner object - check for existence of infinite loops.
  *
  * @param ValidationResult $validationResult
  */
 public function validate(ValidationResult $validationResult)
 {
     // The object is new, won't be looping.
     if (!$this->owner->ID) {
         return;
     }
     // The object has no parent, won't be looping.
     if (!$this->owner->ParentID) {
         return;
     }
     // The parent has not changed, skip the check for performance reasons.
     if (!$this->owner->isChanged('ParentID')) {
         return;
     }
     // Walk the hierarchy upwards until we reach the top, or until we reach the originating node again.
     $node = $this->owner;
     while ($node) {
         if ($node->ParentID == $this->owner->ID) {
             // Hierarchy is looping.
             $validationResult->error(_t('Hierarchy.InfiniteLoopNotAllowed', 'Infinite loop found within the "{type}" hierarchy. Please change the parent to resolve this', 'First argument is the class that makes up the hierarchy.', array('type' => $this->owner->class)), 'INFINITE_LOOP');
             break;
         }
         $node = $node->ParentID ? $node->Parent() : null;
     }
     // At this point the $validationResult contains the response.
 }
 /**
  * Construct a new ValidationException with an optional ValidationResult object
  *
  * @param ValidationResult|string $result The ValidationResult containing the
  * failed result. Can be substituted with an error message instead if no
  * ValidationResult exists.
  * @param string|integer $message The error message. If $result was given the
  * message string rather than a ValidationResult object then this will have
  * the error code number.
  * @param integer $code The error code number, if not given in the second parameter
  */
 public function __construct($result = null, $message = null, $code = 0)
 {
     // Check arguments
     if (!$result instanceof ValidationResult) {
         // Shift parameters if no ValidationResult is given
         $code = $message;
         $message = $result;
         // Infer ValidationResult from parameters
         $result = new ValidationResult(false, $message);
     } elseif (empty($message)) {
         // Infer message if not given
         $message = $result->message();
     }
     // Construct
     $this->result = $result;
     parent::__construct($message, $code);
 }
 /**
  * Validate the current object.
  *
  * By default, there is no validation - objects are always valid!  However, you can overload this method in your
  * DataObject sub-classes to specify custom validation, or use the hook through DataExtension.
  *
  * Invalid objects won't be able to be written - a warning will be thrown and no write will occur.  onBeforeWrite()
  * and onAfterWrite() won't get called either.
  *
  * It is expected that you call validate() in your own application to test that an object is valid before
  * attempting a write, and respond appropriately if it isn't.
  *
  * @see {@link ValidationResult}
  * @return ValidationResult
  */
 public function validate()
 {
     $result = ValidationResult::create();
     $this->extend('validate', $result);
     return $result;
 }
 /**
  * Hook to validate this record against a validation result
  *
  * @param ValidationResult $result
  * @param string $filename Optional filename to validate. If omitted, the current value is validated.
  * @return bool Valid flag
  */
 public function validate(ValidationResult $result, $filename = null)
 {
     if (empty($filename)) {
         $filename = $this->getFilename();
     }
     if (empty($filename) || $this->isValidFilename($filename)) {
         return true;
     }
     // Check allowed extensions
     $extensions = $this->getAllowedExtensions();
     if (empty($extensions)) {
         $extensions = File::config()->allowed_extensions;
     }
     sort($extensions);
     $message = _t('File.INVALIDEXTENSION', 'Extension is not allowed (valid: {extensions})', 'Argument 1: Comma-separated list of valid extensions', array('extensions' => wordwrap(implode(', ', $extensions))));
     $result->error($message);
     return false;
 }
 /**
  * Event handler called before writing to the database.
  */
 public function onBeforeWrite()
 {
     if ($this->SetPassword) {
         $this->Password = $this->SetPassword;
     }
     // If a member with the same "unique identifier" already exists with a different ID, don't allow merging.
     // Note: This does not a full replacement for safeguards in the controller layer (e.g. in a registration form),
     // but rather a last line of defense against data inconsistencies.
     $identifierField = Member::config()->unique_identifier_field;
     if ($this->{$identifierField}) {
         // Note: Same logic as Member_Validator class
         $filter = array("\"{$identifierField}\"" => $this->{$identifierField});
         if ($this->ID) {
             $filter[] = array('"Member"."ID" <> ?' => $this->ID);
         }
         $existingRecord = DataObject::get_one('SilverStripe\\Security\\Member', $filter);
         if ($existingRecord) {
             throw new ValidationException(ValidationResult::create(false, _t('Member.ValidationIdentifierFailed', 'Can\'t overwrite existing member #{id} with identical identifier ({name} = {value}))', 'Values in brackets show "fieldname = value", usually denoting an existing email address', array('id' => $existingRecord->ID, 'name' => $identifierField, 'value' => $this->{$identifierField}))));
         }
     }
     // We don't send emails out on dev/tests sites to prevent accidentally spamming users.
     // However, if TestMailer is in use this isn't a risk.
     if ((Director::isLive() || Email::mailer() instanceof TestMailer) && $this->isChanged('Password') && $this->record['Password'] && $this->config()->notify_password_change) {
         /** @var Email $e */
         $e = Email::create();
         $e->setSubject(_t('Member.SUBJECTPASSWORDCHANGED', "Your password has been changed", 'Email subject'));
         $e->setTemplate('ChangePasswordEmail');
         $e->populateTemplate($this);
         $e->setTo($this->Email);
         $e->send();
     }
     // The test on $this->ID is used for when records are initially created.
     // Note that this only works with cleartext passwords, as we can't rehash
     // existing passwords.
     if (!$this->ID && $this->Password || $this->isChanged('Password')) {
         // Password was changed: encrypt the password according the settings
         $encryption_details = Security::encrypt_password($this->Password, $this->Salt, $this->PasswordEncryption ? $this->PasswordEncryption : Security::config()->password_encryption_algorithm, $this);
         // Overwrite the Password property with the hashed value
         $this->Password = $encryption_details['password'];
         $this->Salt = $encryption_details['salt'];
         $this->PasswordEncryption = $encryption_details['algorithm'];
         // If we haven't manually set a password expiry
         if (!$this->isChanged('PasswordExpiry')) {
             // then set it for us
             if (self::config()->password_expiry_days) {
                 $this->PasswordExpiry = date('Y-m-d', time() + 86400 * self::config()->password_expiry_days);
             } else {
                 $this->PasswordExpiry = null;
             }
         }
     }
     // save locale
     if (!$this->Locale) {
         $this->Locale = i18n::get_locale();
     }
     parent::onBeforeWrite();
 }
 /**
  * Combine this Validation Result with the ValidationResult given in other.
  * It will be valid if both this and the other result are valid.
  * This object will be modified to contain the new validation information.
  *
  * @param ValidationResult the validation result object to combine
  * @return ValidationResult this
  */
 public function combineAnd(ValidationResult $other)
 {
     $this->isValid = $this->isValid && $other->valid();
     $this->errorList = array_merge($this->errorList, $other->messageList());
     return $this;
 }