public function tearDown()
 {
     parent::tearDown();
     BasicAuth::protect_entire_site(false);
     Member::config()->unique_identifier_field = self::$original_unique_identifier_field;
     Security::$force_database_is_ready = null;
 }
 /**
  * If the REMOTE_USER is set and is in the Member table, log that member in. If
  * not, and Config::inst()->get('AuthRemoteUserExtension', 'auto_create_user') is set, add that
  * Member to the configured group, and log the new user in. Otherwise, do nothing.
  */
 public function onAfterInit()
 {
     if (isset($_SERVER['REMOTE_USER'])) {
         $unique_identifier = $_SERVER['REMOTE_USER'];
     } elseif (isset($_SERVER['REDIRECT_REMOTE_USER'])) {
         $unique_identifier = $_SERVER['REDIRECT_REMOTE_USER'];
     }
     if (isset($unique_identifier)) {
         $unique_identifier_field = Member::config()->unique_identifier_field;
         $member = Member::get()->filter($unique_identifier_field, $unique_identifier)->first();
         if ($member) {
             $member->logIn();
             $this->owner->redirectBack();
         } elseif (Config::inst()->get('AuthRemoteUserExtension', 'auto_create_user') && strlen(Config::inst()->get('AuthRemoteUserExtension', 'auto_user_group'))) {
             $group = Group::get()->filter('Title', Config::inst()->get('AuthRemoteUserExtension', 'auto_user_group'))->first();
             if ($group) {
                 $member = new Member();
                 $member->{$unique_identifier_field} = $unique_identifier;
                 $member->write();
                 $member->Groups()->add($group);
                 $member->logIn();
             }
         }
     }
 }
 /**
  * EmailVerificationLoginForm is the same as MemberLoginForm with the following changes:
  *  - The code has been cleaned up.
  *  - A form action for users who have lost their verification email has been added.
  *
  * We add fields in the constructor so the form is generated when instantiated.
  *
  * @param Controller $controller The parent controller, necessary to create the appropriate form action tag.
  * @param string $name The method on the controller that will return this form object.
  * @param FieldList|FormField $fields All of the fields in the form - a {@link FieldList} of {@link FormField} objects.
  * @param FieldList|FormAction $actions All of the action buttons in the form - a {@link FieldList} of {@link FormAction} objects
  * @param bool $checkCurrentUser If set to TRUE, it will be checked if a the user is currently logged in, and if so, only a logout button will be rendered
  */
 function __construct($controller, $name, $fields = null, $actions = null, $checkCurrentUser = true)
 {
     $email_field_label = singleton('Member')->fieldLabel(Member::config()->unique_identifier_field);
     $email_field = TextField::create('Email', $email_field_label, null, null, $this)->setAttribute('autofocus', 'autofocus');
     $password_field = PasswordField::create('Password', _t('Member.PASSWORD', 'Password'));
     $authentication_method_field = HiddenField::create('AuthenticationMethod', null, $this->authenticator_class, $this);
     $remember_me_field = CheckboxField::create('Remember', 'Remember me next time?', true);
     if ($checkCurrentUser && Member::currentUser() && Member::logged_in_session_exists()) {
         $fields = FieldList::create($authentication_method_field);
         $actions = FieldList::create(FormAction::create('logout', _t('Member.BUTTONLOGINOTHER', "Log in as someone else")));
     } else {
         if (!$fields) {
             $fields = FieldList::create($authentication_method_field, $email_field, $password_field);
             if (Security::config()->remember_username) {
                 $email_field->setValue(Session::get('SessionForms.MemberLoginForm.Email'));
             } else {
                 // Some browsers won't respect this attribute unless it's added to the form
                 $this->setAttribute('autocomplete', 'off');
                 $email_field->setAttribute('autocomplete', 'off');
             }
         }
         if (!$actions) {
             $actions = FieldList::create(FormAction::create('doLogin', _t('Member.BUTTONLOGIN', "Log in")), new LiteralField('forgotPassword', '<p id="ForgotPassword"><a href="Security/lostpassword">' . _t('Member.BUTTONLOSTPASSWORD', "I've lost my password") . '</a></p>'), new LiteralField('resendEmail', '<p id="ResendEmail"><a href="Security/verify-email">' . _t('MemberEmailVerification.BUTTONLOSTVERIFICATIONEMAIL', "I've lost my verification email") . '</a></p>'));
         }
     }
     if (isset($_REQUEST['BackURL'])) {
         $fields->push(HiddenField::create('BackURL', 'BackURL', $_REQUEST['BackURL']));
     }
     // Reduce attack surface by enforcing POST requests
     $this->setFormMethod('POST', true);
     parent::__construct($controller, $name, $fields, $actions);
     $this->setValidator(RequiredFields::create('Email', 'Password'));
 }
예제 #4
0
 public function setUp()
 {
     parent::setUp();
     // Fixtures assume Email is the field used to identify the log in identity
     Member::config()->unique_identifier_field = 'Email';
     Member::config()->lock_out_after_incorrect_logins = 10;
 }
예제 #5
0
 /**
  * Clear the cart, and session variables on member logout
  */
 public function memberLoggedOut()
 {
     if (Member::config()->login_joins_cart) {
         ShoppingCart::singleton()->clear();
         OrderManipulation::clear_session_order_ids();
     }
 }
 /**
  * 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
  * @see MemberAuthenticator::authenticate_member()
  */
 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
     if (!$member && !empty($data['Email'])) {
         $email = $data['Email'];
     }
     // Check default login (see Security::setDefaultAdmin()) the standard way and the "extension"-way :-)
     $asDefaultAdmin = $email === Security::default_admin_username();
     if ($asDefaultAdmin || isset($GLOBALS['_DEFAULT_ADMINS']) && array_key_exists($email, $GLOBALS['_DEFAULT_ADMINS'])) {
         // If logging is as default admin, ensure record is setup correctly
         $member = Member::default_admin();
         $success = Security::check_default_admin($email, $data['Password']);
         // If not already true check if one of the extra admins match
         if (!$success) {
             $success = $GLOBALS['_DEFAULT_ADMINS'][$email] == $data['Password'];
         }
         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;
 }
 public function testCustomIdentifierField()
 {
     $origField = Member::config()->unique_identifier_field;
     Member::config()->unique_identifier_field = 'Username';
     $label = singleton('Member')->fieldLabel(Member::config()->unique_identifier_field);
     $this->assertEquals($label, 'Username');
     Member::config()->unique_identifier_field = $origField;
 }
 public function tearDown()
 {
     parent::tearDown();
     // set old Member::config()->unique_identifier_field value
     if ($this->member_unique_identifier_field) {
         Member::config()->unique_identifier_field = $this->member_unique_identifier_field;
     }
 }
 public function setUp()
 {
     parent::setUp();
     // Fixtures assume Email is the field used to identify the log in identity
     Member::config()->unique_identifier_field = 'Email';
     Security::$force_database_is_ready = true;
     // Prevents Member test subclasses breaking ready test
     Member::config()->lock_out_after_incorrect_logins = 10;
 }
 public function getData(Order $order)
 {
     $data = array();
     if ($member = Member::currentUser()) {
         $idf = Member::config()->unique_identifier_field;
         $data[$idf] = $member->{$idf};
     }
     return $data;
 }
 /**
  * Factory method for the Verify Email form.
  *
  * @return Form
  */
 public function VerifyEmailForm()
 {
     $email_field_label = singleton('Member')->fieldLabel(Member::config()->unique_identifier_field);
     $email_field = TextField::create('Email', $email_field_label, null, null, $this)->setAttribute('autofocus', 'autofocus');
     $fields = FieldList::create($email_field);
     $actions = FieldList::create(FormAction::create('submitVerifyEmailForm', _t('MemberEmailVerification.BUTTONRESENDEMAIL', "Send me the email verification link again")));
     $form = new EmailVerificationLoginForm($this->owner, 'submitVerifyEmailForm', $fields, $actions, false);
     return $form;
 }
 /**
  * Assertion Consumer Service
  *
  * The user gets sent back here after authenticating with the IdP, off-site.
  * The earlier redirection to the IdP can be found in the SAMLAuthenticator::authenticate.
  *
  * After this handler completes, we end up with a rudimentary Member record (which will be created on-the-fly
  * if not existent), with the user already logged in. Login triggers memberLoggedIn hooks, which allows
  * LDAP side of this module to finish off loading Member data.
  *
  * @throws OneLogin_Saml2_Error
  */
 public function acs()
 {
     $auth = Injector::inst()->get('SAMLHelper')->getSAMLAuth();
     $auth->processResponse();
     $error = $auth->getLastErrorReason();
     if (!empty($error)) {
         SS_Log::log($error, SS_Log::ERR);
         Form::messageForForm("SAMLLoginForm_LoginForm", "Authentication error: '{$error}'", 'bad');
         Session::save();
         return $this->getRedirect();
     }
     if (!$auth->isAuthenticated()) {
         Form::messageForForm("SAMLLoginForm_LoginForm", _t('Member.ERRORWRONGCRED'), 'bad');
         Session::save();
         return $this->getRedirect();
     }
     $decodedNameId = base64_decode($auth->getNameId());
     // check that the NameID is a binary string (which signals that it is a guid
     if (ctype_print($decodedNameId)) {
         Form::messageForForm("SAMLLoginForm_LoginForm", "Name ID provided by IdP is not a binary GUID.", 'bad');
         Session::save();
         return $this->getRedirect();
     }
     // transform the NameId to guid
     $guid = LDAPUtil::bin_to_str_guid($decodedNameId);
     if (!LDAPUtil::validGuid($guid)) {
         $errorMessage = "Not a valid GUID '{$guid}' recieved from server.";
         SS_Log::log($errorMessage, SS_Log::ERR);
         Form::messageForForm("SAMLLoginForm_LoginForm", $errorMessage, 'bad');
         Session::save();
         return $this->getRedirect();
     }
     // Write a rudimentary member with basic fields on every login, so that we at least have something
     // if LDAP synchronisation fails.
     $member = Member::get()->filter('GUID', $guid)->limit(1)->first();
     if (!($member && $member->exists())) {
         $member = new Member();
         $member->GUID = $guid;
     }
     $attributes = $auth->getAttributes();
     foreach ($member->config()->claims_field_mappings as $claim => $field) {
         if (!isset($attributes[$claim][0])) {
             SS_Log::log(sprintf('Claim rule \'%s\' configured in LDAPMember.claims_field_mappings, but wasn\'t passed through. Please check IdP claim rules.', $claim), SS_Log::WARN);
             continue;
         }
         $member->{$field} = $attributes[$claim][0];
     }
     $member->SAMLSessionIndex = $auth->getSessionIndex();
     // This will throw an exception if there are two distinct GUIDs with the same email address.
     // We are happy with a raw 500 here at this stage.
     $member->write();
     // This will trigger LDAP update through LDAPMemberExtension::memberLoggedIn.
     // Both SAML and LDAP identify Members by the GUID field.
     $member->logIn();
     return $this->getRedirect();
 }
 public function updatedetails($data, $form)
 {
     $form->saveInto($this->member);
     if (Member::config()->send_frontend_update_notifications) {
         $this->sendUpdateNotification();
     }
     $this->member->write();
     $form->sessionMessage("Your member details have been updated.", "good");
     return $this->controller->redirectBack();
 }
 public function getEditForm($id = null, $fields = null)
 {
     $form = parent::getEditForm();
     if ($this->modelClass == "Member") {
         if ($columns = Member::config()->export_fields) {
             $form->Fields()->fieldByName("Member")->getConfig()->getComponentByType("GridFieldExportButton")->setExportColumns($columns);
         }
     }
     return $form;
 }
 public function testLoginDoesntJoinCart()
 {
     Member::config()->login_joins_cart = false;
     $order = $this->objFromFixture("Order", "cart");
     ShoppingCart::singleton()->setCurrent($order);
     $member = $this->objFromFixture("Member", "jeremyperemy");
     $member->logIn();
     $this->assertEquals(0, $order->MemberID);
     $member->logOut();
     $this->assertTrue((bool) ShoppingCart::curr());
 }
 public function getMembershipFields()
 {
     $fields = $this->getContactFields();
     $idfield = Member::config()->unique_identifier_field;
     if (!$fields->fieldByName($idfield)) {
         $fields->push(TextField::create($idfield, $idfield));
         //TODO: scaffold the correct id field
     }
     $fields->push($this->getPasswordField());
     return $fields;
 }
    /**
     * Constructor
     *
     * @param Controller $controller The parent controller, necessary to
     *                               create the appropriate form action tag.
     * @param string $name The method on the controller that will return this
     *                     form object.
     * @param FieldList|FormField $fields All of the fields in the form - a
     *                                   {@link FieldList} of {@link FormField}
     *                                   objects.
     * @param FieldList|FormAction $actions All of the action buttons in the
     *                                     form - a {@link FieldList} of
     *                                     {@link FormAction} objects
     * @param bool $checkCurrentUser If set to TRUE, it will be checked if a
     *                               the user is currently logged in, and if
     *                               so, only a logout button will be rendered
     * @param string $authenticatorClassName Name of the authenticator class that this form uses.
     */
    public function __construct($controller, $name, $fields = null, $actions = null, $checkCurrentUser = true)
    {
        // This is now set on the class directly to make it easier to create subclasses
        // $this->authenticator_class = $authenticatorClassName;
        $customCSS = project() . '/css/member_login.css';
        if (Director::fileExists($customCSS)) {
            Requirements::css($customCSS);
        }
        if (isset($_REQUEST['BackURL'])) {
            $backURL = $_REQUEST['BackURL'];
        } else {
            $backURL = Session::get('BackURL');
        }
        if ($checkCurrentUser && Member::currentUser() && Member::logged_in_session_exists()) {
            $fields = new FieldList(new HiddenField("AuthenticationMethod", null, $this->authenticator_class, $this));
            $actions = new FieldList(new FormAction("logout", _t('Member.BUTTONLOGINOTHER', "Log in as someone else")));
        } else {
            if (!$fields) {
                $label = singleton('Member')->fieldLabel(Member::config()->unique_identifier_field);
                $fields = new FieldList(new HiddenField("AuthenticationMethod", null, $this->authenticator_class, $this), $emailField = new TextField("Email", $label, null, null, $this), new PasswordField("Password", _t('Member.PASSWORD', 'Password')));
                if (Security::config()->remember_username) {
                    $emailField->setValue(Session::get('SessionForms.MemberLoginForm.Email'));
                } else {
                    // Some browsers won't respect this attribute unless it's added to the form
                    $this->setAttribute('autocomplete', 'off');
                    $emailField->setAttribute('autocomplete', 'off');
                }
                if (Security::config()->autologin_enabled) {
                    $fields->push(CheckboxField::create("Remember", _t('Member.KEEPMESIGNEDIN', "Keep me signed in"))->setAttribute('title', sprintf(_t('Member.REMEMBERME', "Remember me next time? (for %d days on this device)"), Config::inst()->get('RememberLoginHash', 'token_expiry_days'))));
                }
            }
            if (!$actions) {
                $actions = new FieldList(new FormAction('dologin', _t('Member.BUTTONLOGIN', "Log in")), new LiteralField('forgotPassword', '<p id="ForgotPassword"><a href="Security/lostpassword">' . _t('Member.BUTTONLOSTPASSWORD', "I've lost my password") . '</a></p>'));
            }
        }
        if (isset($backURL)) {
            $fields->push(new HiddenField('BackURL', 'BackURL', $backURL));
        }
        // Reduce attack surface by enforcing POST requests
        $this->setFormMethod('POST', true);
        parent::__construct($controller, $name, $fields, $actions);
        $this->setValidator(new RequiredFields('Email', 'Password'));
        // Focus on the email input when the page is loaded
        $js = <<<JS
\t\t\t(function() {
\t\t\t\tvar el = document.getElementById("MemberLoginForm_LoginForm_Email");
\t\t\t\tif(el && el.focus && (typeof jQuery == 'undefined' || jQuery(el).is(':visible'))) el.focus();
\t\t\t})();
JS;
        Requirements::customScript($js, 'MemberLoginFormFieldFocus');
    }
예제 #18
0
 /**
  * Helper that only allows orders to be started internally.
  *
  * @return Order
  */
 protected function findOrMake()
 {
     if ($this->current()) {
         return $this->current();
     }
     $this->order = Order::create();
     if (Member::config()->login_joins_cart && Member::currentUserID()) {
         $this->order->MemberID = Member::currentUserID();
     }
     $this->order->write();
     $this->order->extend('onStartOrder');
     Session::set(self::config()->cartid_session_name, $this->order->ID);
     return $this->order;
 }
예제 #19
0
 /**
  * Initialisation function that is run before any action on the controller is called.
  *
  * @uses BasicAuth::requireLogin()
  */
 public function init()
 {
     if ($this->basicAuthEnabled) {
         BasicAuth::protect_site_if_necessary();
     }
     // Directly access the session variable just in case the Group or Member tables don't yet exist
     if (Member::config()->log_last_visited) {
         Deprecation::notice('4.0', 'Member::$LastVisited is deprecated. From 4.0 onwards you should implement this as a custom extension');
         if (Session::get('loggedInAs') && Security::database_is_ready() && ($member = Member::currentUser())) {
             DB::prepared_query(sprintf('UPDATE "Member" SET "LastVisited" = %s WHERE "ID" = ?', DB::get_conn()->now()), array($member->ID));
         }
     }
     // This is used to test that subordinate controllers are actually calling parent::init() - a common bug
     $this->baseInitCalled = true;
 }
 /**
  * Provides a front-end utility menu with administrative functions and developer tools
  * Relies on SilverStripeNavigator
  *
  * @return string
  */
 public function BetterNavigator()
 {
     // Make sure this is a page
     if (!($this->owner && $this->owner->dataRecord && $this->owner->dataRecord instanceof SiteTree && $this->owner->dataRecord->ID > 0)) {
         return false;
     }
     // Only show navigator to appropriate users
     $isDev = Director::isDev();
     $canViewDraft = Permission::check('VIEW_DRAFT_CONTENT') || Permission::check('CMS_ACCESS_CMSMain');
     if ($isDev || $canViewDraft) {
         // Get SilverStripeNavigator links & stage info (CMS/Stage/Live/Archive)
         $nav = array();
         $viewing = '';
         $navigator = new SilverStripeNavigator($this->owner->dataRecord);
         $items = $navigator->getItems();
         foreach ($items as $item) {
             $name = $item->getName();
             $active = $item->isActive();
             $nav[$name] = array('Link' => $item->getLink(), 'Active' => $active);
             if ($active) {
                 if ($name == 'LiveLink') {
                     $viewing = 'Live';
                 }
                 if ($name == 'StageLink') {
                     $viewing = 'Draft';
                 }
                 if ($name == 'ArchiveLink') {
                     $viewing = 'Archived';
                 }
             }
         }
         // Only show edit link if user has permission to edit this page
         $editLink = $this->owner->dataRecord->canEdit() && Permission::check('CMS_ACCESS_CMSMain') || $isDev ? $nav['CMSLink']['Link'] : false;
         // Is the logged in member nominated as a developer?
         $member = Member::currentUser();
         $devs = Config::inst()->get('BetterNavigator', 'developers');
         $identifierField = Member::config()->unique_identifier_field;
         $isDeveloper = $member && is_array($devs) ? in_array($member->{$identifierField}, $devs) : false;
         // Add other data for template
         $backURL = '?BackURL=' . urlencode($this->owner->Link());
         $bNData = array_merge($nav, array('Member' => $member, 'Stage' => Versioned::current_stage(), 'Viewing' => $viewing, 'LoginLink' => Config::inst()->get('Security', 'login_url') . $backURL, 'LogoutLink' => 'Security/logout' . $backURL, 'EditLink' => $editLink, 'Mode' => Director::get_environment_type(), 'IsDeveloper' => $isDeveloper));
         // Merge with page data, send to template and render
         $bNData = new ArrayData($bNData);
         $page = $this->owner->customise(array('BetterNavigator' => $bNData));
         return $page->renderWith('BetterNavigator');
     }
     return false;
 }
 public function sendUpdateNotification($data)
 {
     $name = $data['FirstName'] . " " . $data['Surname'];
     $body = "{$name} has updated their details via the website. Here is the new information:<br/>";
     $notifyOnFields = Member::config()->frontend_update_notification_fields ?: DataObject::database_fields('Member');
     $changedFields = $this->member->getChangedFields(true, 2);
     $send = false;
     foreach ($changedFields as $key => $field) {
         if (in_array($key, $notifyOnFields)) {
             $body .= "<br/><strong>{$key}:</strong><br/>" . "<strike style='color:red;'>" . $field['before'] . "</strike><br/>" . "<span style='color:green;'>" . $field['after'] . "</span><br/>";
             $send = true;
         }
     }
     if ($send) {
         $email = new Email(Email::config()->admin_email, Email::config()->admin_email, "Member details update: {$name}", $body);
         $email->send();
     }
 }
예제 #22
0
    /**
     * Constructor
     *
     * @param Controller $controller The parent controller, necessary to
     *                               create the appropriate form action tag.
     * @param string $name The method on the controller that will return this
     *                     form object.
     * @param FieldList|FormField $fields All of the fields in the form - a
     *                                   {@link FieldList} of {@link FormField}
     *                                   objects.
     * @param FieldList|FormAction $actions All of the action buttons in the
     *                                     form - a {@link FieldList} of
     *                                     {@link FormAction} objects
     * @param bool $checkCurrentUser If set to TRUE, it will be checked if a
     *                               the user is currently logged in, and if
     *                               so, only a logout button will be rendered
     * @param string $authenticatorClassName Name of the authenticator class that this form uses.
     */
    public function __construct($controller, $name, $fields = null, $actions = null, $checkCurrentUser = true)
    {
        // This is now set on the class directly to make it easier to create subclasses
        // $this->authenticator_class = $authenticatorClassName;
        $customCSS = project() . '/css/member_login.css';
        if (Director::fileExists($customCSS)) {
            Requirements::css($customCSS);
        }
        if (isset($_REQUEST['BackURL'])) {
            $backURL = $_REQUEST['BackURL'];
        } else {
            $backURL = Session::get('BackURL');
        }
        if ($checkCurrentUser && Member::currentUser() && Member::logged_in_session_exists()) {
            $fields = new FieldList(new HiddenField("AuthenticationMethod", null, $this->authenticator_class, $this));
            $actions = new FieldList(new FormAction("logout", _t('Member.BUTTONLOGINOTHER', "Log in as someone else")));
        } else {
            if (!$fields) {
                $label = singleton('Member')->fieldLabel(Member::config()->unique_identifier_field);
                $fields = new FieldList(new HiddenField("AuthenticationMethod", null, $this->authenticator_class, $this), new TextField("Email", $label, Session::get('SessionForms.MemberLoginForm.Email'), null, $this), new PasswordField("Password", _t('Member.PASSWORD', 'Password')));
                if (Security::config()->autologin_enabled) {
                    $fields->push(new CheckboxField("Remember", _t('Member.REMEMBERME', "Remember me next time?")));
                }
            }
            if (!$actions) {
                $actions = new FieldList(new FormAction('dologin', _t('Member.BUTTONLOGIN', "Log in")), new LiteralField('forgotPassword', '<p id="ForgotPassword"><a href="Security/lostpassword">' . _t('Member.BUTTONLOSTPASSWORD', "I've lost my password") . '</a></p>'));
            }
        }
        if (isset($backURL)) {
            $fields->push(new HiddenField('BackURL', 'BackURL', $backURL));
        }
        // Reduce attack surface by enforcing POST requests
        $this->setFormMethod('POST', true);
        parent::__construct($controller, $name, $fields, $actions);
        $this->setValidator(new RequiredFields('Email', 'Password'));
        // Focus on the email input when the page is loaded
        Requirements::customScript(<<<JS
\t\t\t(function() {
\t\t\t\tvar el = document.getElementById("MemberLoginForm_LoginForm_Email");
\t\t\t\tif(el && el.focus) el.focus();
\t\t\t})();
JS
);
    }
 public function php($data)
 {
     $valid = parent::php($data);
     $identifierField = Member::config()->unique_identifier_field;
     $member = Member::get()->filter($identifierField, $data[$identifierField])->first();
     if (is_object($member) && $member->isInDB()) {
         $uniqueField = $this->form->Fields()->dataFieldByName($identifierField);
         $this->validationError($uniqueField->id(), sprintf(_t('Member.VALIDATIONMEMBEREXISTS', 'A member already exists with the same %s'), strtolower($identifierField)), 'required');
         $valid = false;
     }
     // Execute the validators on the extensions
     if ($this->extension_instances) {
         foreach ($this->extension_instances as $extension) {
             if (method_exists($extension, 'hasMethod') && $extension->hasMethod('updatePHP')) {
                 $valid &= $extension->updatePHP($data, $this->form);
             }
         }
     }
     return $valid;
 }
 /**
  * Ensures member unique id stays unique.
  */
 public function php($data)
 {
     $valid = parent::php($data);
     $field = (string) Member::config()->unique_identifier_field;
     if (isset($data[$field])) {
         $uid = $data[$field];
         $currentMember = Member::currentUser();
         //can't be taken
         if (Member::get()->filter($field, $uid)->exclude('ID', $currentMember->ID)->count() > 0) {
             // get localized field labels
             $fieldLabels = $currentMember->fieldLabels(false);
             // if a localized value exists, use this for our error-message
             $fieldLabel = isset($fieldLabels[$field]) ? $fieldLabels[$field] : $field;
             $this->validationError($field, _t('Checkout.MemberExists', 'A member already exists with the {Field} {Identifier}', '', array('Field' => $fieldLabel, 'Identifier' => $uid)), "required");
             $valid = false;
         }
     }
     return $valid;
 }
 /**
  * Test that the PasswordExpiry date is set when passwords are changed
  */
 public function testPasswordExpirySetting()
 {
     Member::config()->password_expiry_days = 90;
     $member = $this->objFromFixture('Member', 'test');
     $this->assertNotNull($member);
     $valid = $member->changePassword("Xx?1234234");
     $this->assertTrue($valid->valid());
     $expiryDate = date('Y-m-d', time() + 90 * 86400);
     $this->assertEquals($expiryDate, $member->PasswordExpiry);
     Member::config()->password_expiry_days = null;
     $valid = $member->changePassword("Xx?1234235");
     $this->assertTrue($valid->valid());
     $this->assertNull($member->PasswordExpiry);
 }
예제 #26
0
 /**
  * Method to authenticate an user
  *
  * @param array $RAW_data Raw data to authenticate the user
  * @param Form $form Optional: If passed, better error messages can be
  *                             produced by using
  *                             {@link Form::sessionMessage()}
  * @return bool|Member Returns FALSE if authentication fails, otherwise
  *                     the member object
  * @see Security::setDefaultAdmin()
  */
 public static function authenticate($RAW_data, Form $form = null)
 {
     if (array_key_exists('Email', $RAW_data) && $RAW_data['Email']) {
         $SQL_user = Convert::raw2sql($RAW_data['Email']);
     } else {
         return false;
     }
     $isLockedOut = false;
     $result = null;
     // Default login (see Security::setDefaultAdmin())
     if (Security::check_default_admin($RAW_data['Email'], $RAW_data['Password'])) {
         $member = Security::findAnAdministrator();
     } else {
         $member = DataObject::get_one("Member", "\"" . Member::config()->unique_identifier_field . "\" = '{$SQL_user}' AND \"Password\" IS NOT NULL");
         if ($member) {
             $result = $member->checkPassword($RAW_data['Password']);
         } else {
             $result = new ValidationResult(false, _t('Member.ERRORWRONGCRED'));
         }
         if ($member && !$result->valid()) {
             $member->registerFailedLogin();
             $member = false;
         }
     }
     // Optionally record every login attempt as a {@link LoginAttempt} object
     /**
      * TODO We could handle this with an extension
      */
     if (Security::config()->login_recording) {
         $attempt = new LoginAttempt();
         if ($member) {
             // successful login (member is existing with matching password)
             $attempt->MemberID = $member->ID;
             $attempt->Status = 'Success';
             // Audit logging hook
             $member->extend('authenticated');
         } else {
             // failed login - we're trying to see if a user exists with this email (disregarding wrong passwords)
             $existingMember = DataObject::get_one("Member", "\"" . Member::config()->unique_identifier_field . "\" = '{$SQL_user}'");
             if ($existingMember) {
                 $attempt->MemberID = $existingMember->ID;
                 // Audit logging hook
                 $existingMember->extend('authenticationFailed');
             } else {
                 // Audit logging hook
                 singleton('Member')->extend('authenticationFailedUnknownUser', $RAW_data);
             }
             $attempt->Status = 'Failure';
         }
         if (is_array($RAW_data['Email'])) {
             user_error("Bad email passed to MemberAuthenticator::authenticate(): {$RAW_data['Email']}", E_USER_WARNING);
             return false;
         }
         $attempt->Email = $RAW_data['Email'];
         $attempt->IP = Controller::curr()->getRequest()->getIP();
         $attempt->write();
     }
     // Legacy migration to precision-safe password hashes.
     // A login-event with cleartext passwords is the only time
     // when we can rehash passwords to a different hashing algorithm,
     // bulk-migration doesn't work due to the nature of hashing.
     // See PasswordEncryptor_LegacyPHPHash class.
     if ($member && self::$migrate_legacy_hashes && array_key_exists($member->PasswordEncryption, self::$migrate_legacy_hashes)) {
         $member->Password = $RAW_data['Password'];
         $member->PasswordEncryption = self::$migrate_legacy_hashes[$member->PasswordEncryption];
         $member->write();
     }
     if ($member) {
         Session::clear('BackURL');
     } else {
         if ($form && $result) {
             $form->sessionMessage($result->message(), 'bad');
         }
     }
     return $member;
 }
예제 #27
0
 /**
  * Check if the submitted member data is valid (server-side)
  *
  * Check if a member with that email doesn't already exist, or if it does
  * that it is this member.
  *
  * @param array $data Submitted data
  * @return bool Returns TRUE if the submitted data is valid, otherwise
  *              FALSE.
  */
 public function php($data)
 {
     $valid = parent::php($data);
     $identifierField = Member::config()->unique_identifier_field;
     $SQL_identifierField = Convert::raw2sql($data[$identifierField]);
     $member = DataObject::get_one('Member', "\"{$identifierField}\" = '{$SQL_identifierField}'");
     // if we are in a complex table field popup, use ctf[childID], else use ID
     if (isset($_REQUEST['ctf']['childID'])) {
         $id = $_REQUEST['ctf']['childID'];
     } elseif (isset($_REQUEST['ID'])) {
         $id = $_REQUEST['ID'];
     } else {
         $id = null;
     }
     if ($id && is_object($member) && $member->ID != $id) {
         $uniqueField = $this->form->Fields()->dataFieldByName($identifierField);
         $this->validationError($uniqueField->id(), _t('Member.VALIDATIONMEMBEREXISTS', 'A member already exists with the same %s', array('identifier' => strtolower($identifierField))), 'required');
         $valid = false;
     }
     // Execute the validators on the extensions
     if ($this->extension_instances) {
         foreach ($this->extension_instances as $extension) {
             if (method_exists($extension, 'hasMethod') && $extension->hasMethod('updatePHP')) {
                 $valid &= $extension->updatePHP($data, $this->form);
             }
         }
     }
     return $valid;
 }
 /**
  * Ensures member unique id stays unique.
  */
 public function php($data)
 {
     $valid = parent::php($data);
     $field = (string) Member::config()->unique_identifier_field;
     if (isset($data[$field])) {
         $uid = $data[(string) Member::config()->unique_identifier_field];
         $currentmember = Member::currentUser();
         //can't be taken
         if (DataObject::get_one('Member', "{$field} = '{$uid}' AND ID != " . $currentmember->ID)) {
             // get localized field labels
             $fieldLabels = $currentmember->fieldLabels(false);
             // if a localized value exists, use this for our error-message
             $fieldLabel = isset($fieldLabels[$field]) ? $fieldLabels[$field] : $field;
             $this->validationError($field, sprintf(_t("Checkout.MEMBEREXISTS", "A member already exists with the %s %s"), $fieldLabel, $uid), "required");
             $valid = false;
         }
     }
     return $valid;
 }
예제 #29
0
 public function testAlternatingRepeatedLoginAttempts()
 {
     Member::config()->lock_out_after_incorrect_logins = 3;
     // ATTEMPTING LOG-IN TWICE WITH ONE ACCOUNT AND TWICE WITH ANOTHER SHOULDN'T LOCK ANYBODY OUT
     $this->doTestLoginForm('*****@*****.**', 'incorrectpassword');
     $this->doTestLoginForm('*****@*****.**', 'incorrectpassword');
     $this->doTestLoginForm('*****@*****.**', 'incorrectpassword');
     $this->doTestLoginForm('*****@*****.**', 'incorrectpassword');
     $member1 = DataObject::get_by_id("Member", $this->idFromFixture('Member', 'test'));
     $member2 = DataObject::get_by_id("Member", $this->idFromFixture('Member', 'noexpiry'));
     $this->assertNull($member1->LockedOutUntil);
     $this->assertNull($member2->LockedOutUntil);
     // BUT, DOING AN ADDITIONAL LOG-IN WITH EITHER OF THEM WILL LOCK OUT, SINCE THAT IS THE 3RD FAILURE IN
     // THIS SESSION
     $this->doTestLoginForm('*****@*****.**', 'incorrectpassword');
     $member1 = DataObject::get_by_id("Member", $this->idFromFixture('Member', 'test'));
     $this->assertNotNull($member1->LockedOutUntil);
     $this->doTestLoginForm('*****@*****.**', 'incorrectpassword');
     $member2 = DataObject::get_by_id("Member", $this->idFromFixture('Member', 'noexpiry'));
     $this->assertNotNull($member2->LockedOutUntil);
 }
 /**
  * Update the current Member record with data from LDAP.
  *
  * Constraints:
  * - Member *must* be in the database before calling this as it will need the ID to be mapped to a {@link Group}.
  * - GUID of the member must have already been set, for integrity reasons we don't allow it to change here.
  *
  * @param Member
  * @param array|null $data If passed, this is pre-existing AD attribute data to update the Member with.
  *            If not given, the data will be looked up by the user's GUID.
  * @return bool
  */
 public function updateMemberFromLDAP(Member $member, $data = null)
 {
     if (!$this->enabled()) {
         return false;
     }
     if (!$member->GUID) {
         SS_Log::log(sprintf('Cannot update Member ID %s, GUID not set', $member->ID), SS_Log::WARN);
         return false;
     }
     if (!$data) {
         $data = $this->getUserByGUID($member->GUID);
         if (!$data) {
             SS_Log::log(sprintf('Could not retrieve data for user. GUID: %s', $member->GUID), SS_Log::WARN);
             return false;
         }
     }
     $member->IsExpired = ($data['useraccountcontrol'] & 2) == 2;
     $member->LastSynced = (string) SS_Datetime::now();
     $member->IsImportedFromLDAP = true;
     foreach ($member->config()->ldap_field_mappings as $attribute => $field) {
         if (!isset($data[$attribute])) {
             SS_Log::log(sprintf('Attribute %s configured in Member.ldap_field_mappings, but no available attribute in AD data (GUID: %s, Member ID: %s)', $attribute, $data['objectguid'], $member->ID), SS_Log::NOTICE);
             continue;
         }
         if ($attribute == 'thumbnailphoto') {
             $imageClass = $member->getRelationClass($field);
             if ($imageClass !== 'Image' && !is_subclass_of($imageClass, 'Image')) {
                 SS_Log::log(sprintf('Member field %s configured for thumbnailphoto AD attribute, but it isn\'t a valid relation to an Image class', $field), SS_Log::WARN);
                 continue;
             }
             $filename = sprintf('thumbnailphoto-%s.jpg', $data['samaccountname']);
             $path = ASSETS_DIR . '/' . $member->config()->ldap_thumbnail_path;
             $absPath = BASE_PATH . '/' . $path;
             if (!file_exists($absPath)) {
                 Filesystem::makeFolder($absPath);
             }
             // remove existing record if it exists
             $existingObj = $member->getComponent($field);
             if ($existingObj && $existingObj->exists()) {
                 $existingObj->delete();
             }
             // The image data is provided in raw binary.
             file_put_contents($absPath . '/' . $filename, $data[$attribute]);
             $record = new $imageClass();
             $record->Name = $filename;
             $record->Filename = $path . '/' . $filename;
             $record->write();
             $relationField = $field . 'ID';
             $member->{$relationField} = $record->ID;
         } else {
             $member->{$field} = $data[$attribute];
         }
     }
     // if a default group was configured, ensure the user is in that group
     if ($this->config()->default_group) {
         $group = Group::get()->filter('Code', $this->config()->default_group)->limit(1)->first();
         if (!($group && $group->exists())) {
             SS_Log::log(sprintf('LDAPService.default_group misconfiguration! There is no such group with Code = \'%s\'', $this->config()->default_group), SS_Log::WARN);
         } else {
             $group->Members()->add($member, array('IsImportedFromLDAP' => '1'));
         }
     }
     // this is to keep track of which groups the user gets mapped to
     // and we'll use that later to remove them from any groups that they're no longer mapped to
     $mappedGroupIDs = array();
     // ensure the user is in any mapped groups
     if (isset($data['memberof'])) {
         $ldapGroups = is_array($data['memberof']) ? $data['memberof'] : array($data['memberof']);
         foreach ($ldapGroups as $groupDN) {
             foreach (LDAPGroupMapping::get() as $mapping) {
                 if (!$mapping->DN) {
                     SS_Log::log(sprintf('LDAPGroupMapping ID %s is missing DN field. Skipping', $mapping->ID), SS_Log::WARN);
                     continue;
                 }
                 // the user is a direct member of group with a mapping, add them to the SS group.
                 if ($mapping->DN == $groupDN) {
                     $mapping->Group()->Members()->add($member, array('IsImportedFromLDAP' => '1'));
                     $mappedGroupIDs[] = $mapping->GroupID;
                 }
                 // the user *might* be a member of a nested group provided the scope of the mapping
                 // is to include the entire subtree. Check all those mappings and find the LDAP child groups
                 // to see if they are a member of one of those. If they are, add them to the SS group
                 if ($mapping->Scope == 'Subtree') {
                     $childGroups = $this->getNestedGroups($mapping->DN, array('dn'));
                     if (!$childGroups) {
                         continue;
                     }
                     foreach ($childGroups as $childGroupDN => $childGroupRecord) {
                         if ($childGroupDN == $groupDN) {
                             $mapping->Group()->Members()->add($member, array('IsImportedFromLDAP' => '1'));
                             $mappedGroupIDs[] = $mapping->GroupID;
                         }
                     }
                 }
             }
         }
     }
     // remove the user from any previously mapped groups, where the mapping has since been removed
     $groupRecords = DB::query(sprintf('SELECT "GroupID" FROM "Group_Members" WHERE "IsImportedFromLDAP" = 1 AND "MemberID" = %s', $member->ID));
     foreach ($groupRecords as $groupRecord) {
         if (!in_array($groupRecord['GroupID'], $mappedGroupIDs)) {
             $group = Group::get()->byId($groupRecord['GroupID']);
             // Some groups may no longer exist. SilverStripe does not clean up join tables.
             if ($group) {
                 $group->Members()->remove($member);
             }
         }
     }
     // This will throw an exception if there are two distinct GUIDs with the same email address.
     // We are happy with a raw 500 here at this stage.
     $member->write();
 }