public function run($request) { $algo = Security::config()->password_encryption_algorithm; if ($algo == 'none') { $this->debugMessage('Password encryption disabled'); return; } // Are there members with a clear text password? $members = Member::get()->where(array('"Member"."PasswordEncryption"' => 'none', '"Member"."Password" IS NOT NULL')); if (!$members) { $this->debugMessage('No passwords to encrypt'); return; } // Encrypt the passwords... $this->debugMessage('Encrypting all passwords'); $this->debugMessage(sprintf('The passwords will be encrypted using the %s algorithm', $algo)); foreach ($members as $member) { // Force the update of the member record, as new passwords get // automatically encrypted according to the settings, this will do all // the work for us $member->PasswordEncryption = $algo; $member->forceChange(); $member->write(); $this->debugMessage(sprintf('Encrypted credentials for member #%d;', $member->ID)); } }
/** * Member object of the person who last published this record * * @return Member */ public function Publisher() { if (!$this->record['WasPublished']) { return null; } return Member::get()->byID($this->record['PublisherID']); }
public function getEditForm($id = null, $fields = null) { // TODO Duplicate record fetching (see parent implementation) if (!$id) { $id = $this->currentPageID(); } $form = parent::getEditForm($id); // TODO Duplicate record fetching (see parent implementation) $record = $this->getRecord($id); if ($record && !$record->canView()) { return Security::permissionFailure($this); } $memberList = GridField::create('Members', false, Member::get(), $memberListConfig = GridFieldConfig_RecordEditor::create()->addComponent(new GridFieldButtonRow('after'))->addComponent(new GridFieldExportButton('buttons-after-left')))->addExtraClass("members_grid"); if ($record && method_exists($record, 'getValidator')) { $validator = $record->getValidator(); } else { $validator = Member::singleton()->getValidator(); } $memberListConfig->getComponentByType('GridFieldDetailForm')->setValidator($validator); $groupList = GridField::create('Groups', false, Group::get(), GridFieldConfig_RecordEditor::create()); $columns = $groupList->getConfig()->getComponentByType('GridFieldDataColumns'); $columns->setDisplayFields(array('Breadcrumbs' => singleton('SilverStripe\\Security\\Group')->fieldLabel('Title'))); $columns->setFieldFormatting(array('Breadcrumbs' => function ($val, $item) { return Convert::raw2xml($item->getBreadcrumbs(' > ')); })); $fields = new FieldList($root = new TabSet('Root', $usersTab = new Tab('Users', _t('SecurityAdmin.Users', 'Users'), new LiteralField('MembersCautionText', sprintf('<div class="alert alert-warning" role="alert">%s</div>', _t('SecurityAdmin.MemberListCaution', 'Caution: Removing members from this list will remove them from all groups and the database'))), $memberList), $groupsTab = new Tab('Groups', singleton('SilverStripe\\Security\\Group')->i18n_plural_name(), $groupList)), new HiddenField('ID', false, 0)); // Add import capabilities. Limit to admin since the import logic can affect assigned permissions if (Permission::check('ADMIN')) { $fields->addFieldsToTab('Root.Users', array(new HeaderField(_t('SecurityAdmin.IMPORTUSERS', 'Import users'), 3), new LiteralField('MemberImportFormIframe', sprintf('<iframe src="%s" id="MemberImportFormIframe" width="100%%" height="250px" frameBorder="0">' . '</iframe>', $this->Link('memberimport'))))); $fields->addFieldsToTab('Root.Groups', array(new HeaderField(_t('SecurityAdmin.IMPORTGROUPS', 'Import groups'), 3), new LiteralField('GroupImportFormIframe', sprintf('<iframe src="%s" id="GroupImportFormIframe" width="100%%" height="250px" frameBorder="0">' . '</iframe>', $this->Link('groupimport'))))); } // Tab nav in CMS is rendered through separate template $root->setTemplate('CMSTabSet'); // Add roles editing interface if (Permission::check('APPLY_ROLES')) { $rolesField = GridField::create('Roles', false, PermissionRole::get(), GridFieldConfig_RecordEditor::create()); $rolesTab = $fields->findOrMakeTab('Root.Roles', _t('SecurityAdmin.TABROLES', 'Roles')); $rolesTab->push($rolesField); } $actionParam = $this->getRequest()->param('Action'); if ($actionParam == 'groups') { $groupsTab->addExtraClass('ui-state-active'); } elseif ($actionParam == 'users') { $usersTab->addExtraClass('ui-state-active'); } elseif ($actionParam == 'roles') { $rolesTab->addExtraClass('ui-state-active'); } $actions = new FieldList(); $form = Form::create($this, 'EditForm', $fields, $actions)->setHTMLID('Form_EditForm'); $form->addExtraClass('cms-edit-form'); $form->setTemplate($this->getTemplatesWithSuffix('_EditForm')); // Tab nav in CMS is rendered through separate template if ($form->Fields()->hasTabset()) { $form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet'); } $form->addExtraClass('center ss-tabset cms-tabset ' . $this->BaseCSSClasses()); $form->setAttribute('data-pjax-fragment', 'CurrentForm'); $this->extend('updateEditForm', $form); return $form; }
/** * 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; }
public function testCMSAccess() { $members = Member::get()->byIDs($this->allFixtureIDs('SilverStripe\\Security\\Member')); foreach ($members as $member) { $this->assertTrue(Permission::checkMember($member, 'CMS_ACCESS')); $this->assertTrue(Permission::checkMember($member, array('CMS_ACCESS', 'CMS_ACCESS_Security'))); $this->assertTrue(Permission::checkMember($member, array('CMS_ACCESS_Security', 'CMS_ACCESS'))); } $member = new Member(); $member->update(array('FirstName' => 'No CMS', 'Surname' => 'Access', 'Email' => '*****@*****.**')); $member->write(); $this->assertFalse(Permission::checkMember($member, 'CMS_ACCESS')); $this->assertFalse(Permission::checkMember($member, array('CMS_ACCESS', 'CMS_ACCESS_Security'))); $this->assertFalse(Permission::checkMember($member, array('CMS_ACCESS_Security', 'CMS_ACCESS'))); }
public function save($data, $form) { $member = Member::get()->byID($data['ID']); if (!$member) { return $this->httpError(404); } $origLocale = $member->Locale; if (!$member->canEdit()) { $form->sessionMessage(_t('Member.CANTEDIT', 'You don\'t have permission to do that'), 'bad'); return $this->redirectBack(); } $response = parent::save($data, $form); if ($origLocale != $data['Locale']) { $response->addHeader('X-Reload', true); $response->addHeader('X-ControllerURL', $this->Link()); } return $response; }
/** * Returns all members for a specific permission. * * @param $code String|array Either a single permission code, or a list of permission codes * @return SS_List Returns a set of member that have the specified * permission. */ public static function get_members_by_permission($code) { $toplevelGroups = self::get_groups_by_permission($code); if (!$toplevelGroups) { return new ArrayList(); } $groupIDs = array(); foreach ($toplevelGroups as $group) { $familyIDs = $group->collateFamilyIDs(); if (is_array($familyIDs)) { $groupIDs = array_merge($groupIDs, array_values($familyIDs)); } } if (empty($groupIDs)) { return new ArrayList(); } $groupClause = DB::placeholders($groupIDs); /** @skipUpgrade */ $members = Member::get()->where(array("\"Group\".\"ID\" IN ({$groupClause})" => $groupIDs))->leftJoin("Group_Members", '"Member"."ID" = "Group_Members"."MemberID"')->leftJoin("Group", '"Group_Members"."GroupID" = "Group"."ID"'); return $members; }
/** * Show the "change password" page. * This page can either be called directly by logged-in users * (in which case they need to provide their old password), * or through a link emailed through {@link lostpassword()}. * In this case no old password is required, authentication is ensured * through the Member.AutoLoginHash property. * * @see ChangePasswordForm * * @return string Returns the "change password" page as HTML code. */ public function changepassword() { $controller = $this->getResponseController(_t('Security.CHANGEPASSWORDHEADER', 'Change your password')); // if the controller calls Director::redirect(), this will break early if (($response = $controller->getResponse()) && $response->isFinished()) { return $response; } // Extract the member from the URL. $member = null; if (isset($_REQUEST['m'])) { $member = Member::get()->filter('ID', (int) $_REQUEST['m'])->first(); } // Check whether we are merely changin password, or resetting. if (isset($_REQUEST['t']) && $member && $member->validateAutoLoginToken($_REQUEST['t'])) { // On first valid password reset request redirect to the same URL without hash to avoid referrer leakage. // if there is a current member, they should be logged out if ($curMember = Member::currentUser()) { $curMember->logOut(); } // Store the hash for the change password form. Will be unset after reload within the ChangePasswordForm. Session::set('AutoLoginHash', $member->encryptWithUserSettings($_REQUEST['t'])); return $this->redirect($this->Link('changepassword')); } elseif (Session::get('AutoLoginHash')) { // Subsequent request after the "first load with hash" (see previous if clause). $customisedController = $controller->customise(array('Content' => '<p>' . _t('Security.ENTERNEWPASSWORD', 'Please enter a new password.') . '</p>', 'Form' => $this->ChangePasswordForm())); } elseif (Member::currentUser()) { // Logged in user requested a password change form. $customisedController = $controller->customise(array('Content' => '<p>' . _t('Security.CHANGEPASSWORDBELOW', 'You can change your password below.') . '</p>', 'Form' => $this->ChangePasswordForm())); } else { // Show friendly message if it seems like the user arrived here via password reset feature. if (isset($_REQUEST['m']) || isset($_REQUEST['t'])) { $customisedController = $controller->customise(array('Content' => _t('Security.NOTERESETLINKINVALID', '<p>The password reset link is invalid or expired.</p>' . '<p>You can request a new one <a href="{link1}">here</a> or change your password after' . ' you <a href="{link2}">logged in</a>.</p>', array('link1' => $this->Link('lostpassword'), 'link2' => $this->link('login'))))); } else { self::permissionFailure($this, _t('Security.ERRORPASSWORDPERMISSION', 'You must be logged in in order to change your password!')); return; } } return $customisedController->renderWith($this->getTemplatesFor('changepassword')); }
/** * 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 = (string) Member::config()->unique_identifier_field; // Only validate identifier field if it's actually set. This could be the case if // somebody removes `Email` from the list of required fields. if (isset($data[$identifierField])) { $id = isset($data['ID']) ? (int) $data['ID'] : 0; if (!$id && ($ctrl = $this->form->getController())) { // get the record when within GridField (Member editing page in CMS) if ($ctrl instanceof GridFieldDetailForm_ItemRequest && ($record = $ctrl->getRecord())) { $id = $record->ID; } } // If there's no ID passed via controller or form-data, use the assigned member (if available) if (!$id && ($member = $this->getForMember())) { $id = $member->exists() ? $member->ID : 0; } // set the found ID to the data array, so that extensions can also use it $data['ID'] = $id; $members = Member::get()->filter($identifierField, $data[$identifierField]); if ($id) { $members = $members->exclude('ID', $id); } if ($members->count() > 0) { $this->validationError($identifierField, _t('Member.VALIDATIONMEMBEREXISTS', 'A member already exists with the same {identifier}', array('identifier' => Member::singleton()->fieldLabel($identifierField))), 'required'); $valid = false; } } // Execute the validators on the extensions $results = $this->extend('updatePHP', $data, $this->form); $results[] = $valid; return min($results); }
/** * @covers GridField::getColumnCount */ public function testGetColumnCount() { $obj = new GridField('testfield', 'testfield', Member::get()); $this->assertEquals(3, $obj->getColumnCount()); }
/** * Forgot password form handler method. * Called when the user clicks on "I've lost my password". * Extensions can use the 'forgotPassword' method to veto executing * the logic, by returning FALSE. In this case, the user will be redirected back * to the form without further action. It is recommended to set a message * in the form detailing why the action was denied. * * @param array $data Submitted data * @return SS_HTTPResponse */ public function forgotPassword($data) { // Ensure password is given if (empty($data['Email'])) { $this->sessionMessage(_t('Member.ENTEREMAIL', 'Please enter an email address to get a password reset link.'), 'bad'); $this->controller->redirect('Security/lostpassword'); return; } // Find existing member $member = Member::get()->filter("Email", $data['Email'])->first(); // Allow vetoing forgot password requests $results = $this->extend('forgotPassword', $member); if ($results && is_array($results) && in_array(false, $results, true)) { return $this->controller->redirect('Security/lostpassword'); } if ($member) { $token = $member->generateAutologinTokenAndStoreHash(); /** @var Email $e */ $e = Email::create(); $e->setSubject(_t('Member.SUBJECTPASSWORDRESET', "Your password reset link", 'Email subject')); $e->setTemplate('ForgotPasswordEmail'); $e->populateTemplate($member); $e->populateTemplate(array('PasswordResetLink' => Security::getPasswordResetLink($member, $token))); $e->setTo($member->Email); $e->send(); $this->controller->redirect('Security/passwordsent/' . urlencode($data['Email'])); } elseif ($data['Email']) { // Avoid information disclosure by displaying the same status, // regardless wether the email address actually exists $this->controller->redirect('Security/passwordsent/' . rawurlencode($data['Email'])); } else { $this->sessionMessage(_t('Member.ENTEREMAIL', 'Please enter an email address to get a password reset link.'), 'bad'); $this->controller->redirect('Security/lostpassword'); } }
/** * Get a map of all members in the groups given that have CMS permissions * * If no groups are passed, all groups with CMS permissions will be used. * * @param array $groups Groups to consider or NULL to use all groups with * CMS permissions. * @return Map Returns a map of all members in the groups given that * have CMS permissions. */ public static function mapInCMSGroups($groups = null) { if (!$groups || $groups->Count() == 0) { $perms = array('ADMIN', 'CMS_ACCESS_AssetAdmin'); if (class_exists('SilverStripe\\CMS\\Controllers\\CMSMain')) { $cmsPerms = CMSMain::singleton()->providePermissions(); } else { $cmsPerms = LeftAndMain::singleton()->providePermissions(); } if (!empty($cmsPerms)) { $perms = array_unique(array_merge($perms, array_keys($cmsPerms))); } $permsClause = DB::placeholders($perms); /** @skipUpgrade */ $groups = Group::get()->innerJoin("Permission", '"Permission"."GroupID" = "Group"."ID"')->where(array("\"Permission\".\"Code\" IN ({$permsClause})" => $perms)); } $groupIDList = array(); if ($groups instanceof SS_List) { foreach ($groups as $group) { $groupIDList[] = $group->ID; } } elseif (is_array($groups)) { $groupIDList = $groups; } /** @skipUpgrade */ $members = Member::get()->innerJoin("Group_Members", '"Group_Members"."MemberID" = "Member"."ID"')->innerJoin("Group", '"Group"."ID" = "Group_Members"."GroupID"'); if ($groupIDList) { $groupClause = DB::placeholders($groupIDList); $members = $members->where(array("\"Group\".\"ID\" IN ({$groupClause})" => $groupIDList)); } return $members->sort('"Member"."Surname", "Member"."FirstName"')->map(); }
public function testBasicAuthFailureIncreasesFailedLoginCount() { // Prior to login $check = Member::get()->filter('Email', '*****@*****.**')->first(); $this->assertEquals(0, $check->FailedLoginCount); // First failed attempt $_SERVER['PHP_AUTH_USER'] = '******'; $_SERVER['PHP_AUTH_PW'] = 'test'; $response = Director::test('BasicAuthTest_ControllerSecuredWithoutPermission'); $check = Member::get()->filter('Email', '*****@*****.**')->first(); $this->assertEquals(1, $check->FailedLoginCount); // Second failed attempt $_SERVER['PHP_AUTH_PW'] = 'testwrong'; $response = Director::test('BasicAuthTest_ControllerSecuredWithoutPermission'); $check = Member::get()->filter('Email', '*****@*****.**')->first(); $this->assertEquals(2, $check->FailedLoginCount); // successful basic auth should reset failed login count $_SERVER['PHP_AUTH_PW'] = 'Password'; $response = Director::test('BasicAuthTest_ControllerSecuredWithoutPermission'); $check = Member::get()->filter('Email', '*****@*****.**')->first(); $this->assertEquals(0, $check->FailedLoginCount); }
/** * @covers GridFieldDataColumns::setDisplayFields * @covers GridFieldDataColumns::getDisplayFields */ public function testGridFieldDisplayFieldsWithBadArguments() { $this->setExpectedException('InvalidArgumentException'); $obj = new GridField('testfield', 'testfield', Member::get()); $columns = $obj->getConfig()->getComponentByType('GridFieldDataColumns'); $columns->setDisplayFields(new stdClass()); }