/** * Get the locale of the Member, or if we're not logged in or don't have a locale, use the default one * @return string */ protected function locale() { if (($member = Member::currentUser()) && $member->Locale) { return $member->Locale; } return i18n::get_locale(); }
/** * Change the password * * @param array $data The user submitted data * @return SS_HTTPResponse */ public function doChangePassword(array $data) { if ($member = Member::currentUser()) { // The user was logged in, check the current password if (empty($data['OldPassword']) || !$member->checkPassword($data['OldPassword'])->valid()) { $this->clearMessage(); $this->sessionMessage(_t('Member.ERRORPASSWORDNOTMATCH', "Your current password does not match, please try again"), "bad"); // redirect back to the form, instead of using redirectBack() which could send the user elsewhere. return $this->controller->redirect($this->controller->Link('changepassword')); } } if (!$member) { if (Session::get('AutoLoginHash')) { $member = Member::member_from_autologinhash(Session::get('AutoLoginHash')); } // The user is not logged in and no valid auto login hash is available if (!$member) { Session::clear('AutoLoginHash'); return $this->controller->redirect($this->controller->Link('login')); } } // Check the new password if (empty($data['NewPassword1'])) { $this->clearMessage(); $this->sessionMessage(_t('Member.EMPTYNEWPASSWORD', "The new password can't be empty, please try again"), "bad"); // redirect back to the form, instead of using redirectBack() which could send the user elsewhere. return $this->controller->redirect($this->controller->Link('changepassword')); } else { if ($data['NewPassword1'] == $data['NewPassword2']) { $isValid = $member->changePassword($data['NewPassword1']); if ($isValid->valid()) { $member->logIn(); // TODO Add confirmation message to login redirect Session::clear('AutoLoginHash'); // Clear locked out status $member->LockedOutUntil = null; $member->FailedLoginCount = null; $member->write(); if (!empty($_REQUEST['BackURL']) && Director::is_site_url($_REQUEST['BackURL'])) { $url = Director::absoluteURL($_REQUEST['BackURL']); return $this->controller->redirect($url); } else { // Redirect to default location - the login form saying "You are logged in as..." $redirectURL = HTTP::setGetVar('BackURL', Director::absoluteBaseURL(), $this->controller->Link('login')); return $this->controller->redirect($redirectURL); } } else { $this->clearMessage(); $this->sessionMessage(_t('Member.INVALIDNEWPASSWORD', "We couldn't accept that password: {password}", array('password' => nl2br("\n" . Convert::raw2xml($isValid->starredList())))), "bad", false); // redirect back to the form, instead of using redirectBack() which could send the user elsewhere. return $this->controller->redirect($this->controller->Link('changepassword')); } } else { $this->clearMessage(); $this->sessionMessage(_t('Member.ERRORNEWPASSWORD', "You have entered your new password differently, try again"), "bad"); // redirect back to the form, instead of using redirectBack() which could send the user elsewhere. return $this->controller->redirect($this->controller->Link('changepassword')); } } }
/** * Require basic authentication. Will request a username and password if none is given. * * Used by {@link Controller::init()}. * * @throws HTTPResponse_Exception * * @param string $realm * @param string|array $permissionCode Optional * @param boolean $tryUsingSessionLogin If true, then the method with authenticate against the * session log-in if those credentials are disabled. * @return Member|bool $member */ public static function requireLogin($realm, $permissionCode = null, $tryUsingSessionLogin = true) { $isRunningTests = class_exists('SilverStripe\\Dev\\SapphireTest', false) && SapphireTest::is_running_test(); if (!Security::database_is_ready() || Director::is_cli() && !$isRunningTests) { return true; } /* * Enable HTTP Basic authentication workaround for PHP running in CGI mode with Apache * Depending on server configuration the auth header may be in HTTP_AUTHORIZATION or * REDIRECT_HTTP_AUTHORIZATION * * The follow rewrite rule must be in the sites .htaccess file to enable this workaround * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] */ $authHeader = isset($_SERVER['HTTP_AUTHORIZATION']) ? $_SERVER['HTTP_AUTHORIZATION'] : (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) ? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] : null); $matches = array(); if ($authHeader && preg_match('/Basic\\s+(.*)$/i', $authHeader, $matches)) { list($name, $password) = explode(':', base64_decode($matches[1])); $_SERVER['PHP_AUTH_USER'] = strip_tags($name); $_SERVER['PHP_AUTH_PW'] = strip_tags($password); } $member = null; if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) { $member = MemberAuthenticator::authenticate(array('Email' => $_SERVER['PHP_AUTH_USER'], 'Password' => $_SERVER['PHP_AUTH_PW']), null); } if (!$member && $tryUsingSessionLogin) { $member = Member::currentUser(); } // If we've failed the authentication mechanism, then show the login form if (!$member) { $response = new HTTPResponse(null, 401); $response->addHeader('WWW-Authenticate', "Basic realm=\"{$realm}\""); if (isset($_SERVER['PHP_AUTH_USER'])) { $response->setBody(_t('BasicAuth.ERRORNOTREC', "That username / password isn't recognised")); } else { $response->setBody(_t('BasicAuth.ENTERINFO', "Please enter a username and password.")); } // Exception is caught by RequestHandler->handleRequest() and will halt further execution $e = new HTTPResponse_Exception(null, 401); $e->setResponse($response); throw $e; } if ($permissionCode && !Permission::checkMember($member->ID, $permissionCode)) { $response = new HTTPResponse(null, 401); $response->addHeader('WWW-Authenticate', "Basic realm=\"{$realm}\""); if (isset($_SERVER['PHP_AUTH_USER'])) { $response->setBody(_t('BasicAuth.ERRORNOTADMIN', "That user is not an administrator.")); } // Exception is caught by RequestHandler->handleRequest() and will halt further execution $e = new HTTPResponse_Exception(null, 401); $e->setResponse($response); throw $e; } return $member; }
public function testShowEditLinks() { if (Member::currentUser()) { Member::currentUser()->logOut(); } $content = new CSSContentParser($this->gridField->FieldHolder()); // Check that there are content $this->assertEquals(3, count($content->getBySelector('.ss-gridfield-item'))); // Make sure that there are edit links, even though the user doesn't have "edit" permissions // (they can still view the records) $this->assertEquals(2, count($content->getBySelector('.edit-link')), 'Edit links should show when not logged in.'); }
public function testDeleteActionWithoutCorrectPermission() { if (Member::currentUser()) { Member::currentUser()->logOut(); } $this->setExpectedException('SilverStripe\\ORM\\ValidationException'); $stateID = 'testGridStateActionField'; Session::set($stateID, array('grid' => '', 'actionName' => 'deleterecord', 'args' => array('RecordID' => $this->idFromFixture('GridFieldAction_Delete_Team', 'team1')))); $token = SecurityToken::inst(); $request = new SS_HTTPRequest('POST', 'url', array(), array('action_gridFieldAlterAction?StateID=' . $stateID => true, $token->getName() => $token->getValue())); $this->gridField->gridFieldAlterAction(array('StateID' => $stateID), $this->form, $request); $this->assertEquals(3, $this->list->count(), 'User should\'t be able to delete records without correct permissions.'); }
public function canView($member = null) { if (!$member && $member !== false) { $member = Member::currentUser(); } // cms menus only for logged-in members if (!$member) { return false; } // Check they can access the CMS and that they are trying to edit themselves if (Permission::checkMember($member, "CMS_ACCESS") && $member->ID === Member::currentUserID()) { return true; } return false; }
/** * Returns the main menu of the CMS. This is also used by init() * to work out which sections the user has access to. * * @param bool $cached * @return SS_List */ public function MainMenu($cached = true) { if (!isset($this->_cache_MainMenu) || !$cached) { // Don't accidentally return a menu if you're not logged in - it's used to determine access. if (!Member::currentUser()) { return new ArrayList(); } // Encode into DO set $menu = new ArrayList(); $menuItems = CMSMenu::get_viewable_menu_items(); // extra styling for custom menu-icons $menuIconStyling = ''; if ($menuItems) { /** @var CMSMenuItem $menuItem */ foreach ($menuItems as $code => $menuItem) { // alternate permission checks (in addition to LeftAndMain->canView()) if (isset($menuItem->controller) && $this->hasMethod('alternateMenuDisplayCheck') && !$this->alternateMenuDisplayCheck($menuItem->controller)) { continue; } $linkingmode = "link"; if ($menuItem->controller && get_class($this) == $menuItem->controller) { $linkingmode = "current"; } else { if (strpos($this->Link(), $menuItem->url) !== false) { if ($this->Link() == $menuItem->url) { $linkingmode = "current"; // default menu is the one with a blank {@link url_segment} } else { if (singleton($menuItem->controller)->stat('url_segment') == '') { if ($this->Link() == AdminRootController::admin_url()) { $linkingmode = "current"; } } else { $linkingmode = "current"; } } } } // already set in CMSMenu::populate_menu(), but from a static pre-controller // context, so doesn't respect the current user locale in _t() calls - as a workaround, // we simply call LeftAndMain::menu_title() again // if we're dealing with a controller if ($menuItem->controller) { $title = LeftAndMain::menu_title($menuItem->controller); } else { $title = $menuItem->title; } // Provide styling for custom $menu-icon. Done here instead of in // CMSMenu::populate_menu(), because the icon is part of // the CMS right pane for the specified class as well... if ($menuItem->controller) { $menuIcon = LeftAndMain::menu_icon_for_class($menuItem->controller); if (!empty($menuIcon)) { $menuIconStyling .= $menuIcon; } } $menu->push(new ArrayData(array("MenuItem" => $menuItem, "AttributesHTML" => $menuItem->getAttributesHTML(), "Title" => Convert::raw2xml($title), "Code" => $code, "Icon" => strtolower($code), "Link" => $menuItem->url, "LinkingMode" => $linkingmode))); } } if ($menuIconStyling) { Requirements::customCSS($menuIconStyling); } $this->_cache_MainMenu = $menu; } return $this->_cache_MainMenu; }
/** * Return a date formatted as per a CMS user's settings. * * @param Member $member * @return boolean | string A date formatted as per user-defined settings. */ public function FormatFromSettings($member = null) { require_once 'Zend/Date.php'; if (!$member) { if (!Member::currentUserID()) { return false; } $member = Member::currentUser(); } $formatD = $member->getDateFormat(); $zendDate = new Zend_Date($this->getValue(), 'y-MM-dd'); return $zendDate->toString($formatD); }
/** * Send user to the right location after login * * @param array $data * @return HTTPResponse */ protected function logInUserAndRedirect($data) { // Check password expiry if (Member::currentUser()->isPasswordExpired()) { // Redirect the user to the external password change form if necessary return $this->redirectToChangePassword(); } else { // Link to success template $url = $this->controller->Link('success'); return $this->controller->redirect($url); } }
/** * Generate a CSV import form for a single {@link DataObject} subclass. * * @return Form */ public function ImportForm() { $modelSNG = singleton($this->modelClass); $modelName = $modelSNG->i18n_singular_name(); // check if a import form should be generated if (!$this->showImportForm || is_array($this->showImportForm) && !in_array($this->modelClass, $this->showImportForm)) { return false; } $importers = $this->getModelImporters(); if (!$importers || !isset($importers[$this->modelClass])) { return false; } if (!$modelSNG->canCreate(Member::currentUser())) { return false; } $fields = new FieldList(new HiddenField('ClassName', _t('ModelAdmin.CLASSTYPE'), $this->modelClass), new FileField('_CsvFile', false)); // get HTML specification for each import (column names etc.) $importerClass = $importers[$this->modelClass]; $importer = new $importerClass($this->modelClass); $spec = $importer->getImportSpec(); $specFields = new ArrayList(); foreach ($spec['fields'] as $name => $desc) { $specFields->push(new ArrayData(array('Name' => $name, 'Description' => $desc))); } $specRelations = new ArrayList(); foreach ($spec['relations'] as $name => $desc) { $specRelations->push(new ArrayData(array('Name' => $name, 'Description' => $desc))); } $specHTML = $this->customise(array('ClassName' => $this->sanitiseClassName($this->modelClass), 'ModelName' => Convert::raw2att($modelName), 'Fields' => $specFields, 'Relations' => $specRelations))->renderWith('Includes/ModelAdmin_ImportSpec'); $fields->push(new LiteralField("SpecFor{$modelName}", $specHTML)); $fields->push(new CheckboxField('EmptyBeforeImport', _t('ModelAdmin.EMPTYBEFOREIMPORT', 'Replace data'), false)); $actions = new FieldList(new FormAction('import', _t('ModelAdmin.IMPORT', 'Import from CSV'))); $form = new Form($this, "ImportForm", $fields, $actions); $form->setFormAction(Controller::join_links($this->Link($this->sanitiseClassName($this->modelClass)), 'ImportForm')); $this->extend('updateImportForm', $form); return $form; }
/** * Returns true if the member is allowed to do the given action. Defaults to the currently logged * in user. * * @param string $perm * @param null|member $member * * @return bool */ public function can($perm, $member = null) { if (!$member) { $member = Member::currentUser(); } if (is_array($perm)) { $perm = array_map(array($this, 'can'), $perm, array_fill(0, count($perm), $member)); return min($perm); } if ($this->hasMethod($methodName = 'can' . $perm)) { return $this->{$methodName}($member); } else { return true; } }
/** * Validate this field * * @param Validator $validator * @return bool */ public function validate($validator) { $name = $this->name; // if field isn't visible, don't validate if (!$this->isSaveable()) { return true; } $this->passwordField->setValue($this->value); $this->confirmPasswordfield->setValue($this->confirmValue); $value = $this->passwordField->Value(); // both password-fields should be the same if ($value != $this->confirmPasswordfield->Value()) { $validator->validationError($name, _t('Form.VALIDATIONPASSWORDSDONTMATCH', "Passwords don't match"), "validation"); return false; } if (!$this->canBeEmpty) { // both password-fields shouldn't be empty if (!$value || !$this->confirmPasswordfield->Value()) { $validator->validationError($name, _t('Form.VALIDATIONPASSWORDSNOTEMPTY', "Passwords can't be empty"), "validation"); return false; } } // lengths if ($this->minLength || $this->maxLength) { $errorMsg = null; $limit = null; if ($this->minLength && $this->maxLength) { $limit = "{{$this->minLength},{$this->maxLength}}"; $errorMsg = _t('ConfirmedPasswordField.BETWEEN', 'Passwords must be {min} to {max} characters long.', array('min' => $this->minLength, 'max' => $this->maxLength)); } elseif ($this->minLength) { $limit = "{{$this->minLength}}.*"; $errorMsg = _t('ConfirmedPasswordField.ATLEAST', 'Passwords must be at least {min} characters long.', array('min' => $this->minLength)); } elseif ($this->maxLength) { $limit = "{0,{$this->maxLength}}"; $errorMsg = _t('ConfirmedPasswordField.MAXIMUM', 'Passwords must be at most {max} characters long.', array('max' => $this->maxLength)); } $limitRegex = '/^.' . $limit . '$/'; if (!empty($value) && !preg_match($limitRegex, $value)) { $validator->validationError($name, $errorMsg, "validation"); } } if ($this->requireStrongPassword) { if (!preg_match('/^(([a-zA-Z]+\\d+)|(\\d+[a-zA-Z]+))[a-zA-Z0-9]*$/', $value)) { $validator->validationError($name, _t('Form.VALIDATIONSTRONGPASSWORD', "Passwords must have at least one digit and one alphanumeric character"), "validation"); return false; } } // Check if current password is valid if (!empty($value) && $this->getRequireExistingPassword()) { if (!$this->currentPasswordValue) { $validator->validationError($name, _t('ConfirmedPasswordField.CURRENT_PASSWORD_MISSING', "You must enter your current password."), "validation"); return false; } // Check this password is valid for the current user $member = Member::currentUser(); if (!$member) { $validator->validationError($name, _t('ConfirmedPasswordField.LOGGED_IN_ERROR', "You must be logged in to change your password."), "validation"); return false; } // With a valid user and password, check the password is correct $checkResult = $member->checkPassword($this->currentPasswordValue); if (!$checkResult->valid()) { $validator->validationError($name, _t('ConfirmedPasswordField.CURRENT_PASSWORD_ERROR', "The current password you have entered is not correct."), "validation"); return false; } } return true; }
/** * Login in the user and figure out where to redirect the browser. * * The $data has this format * array( * 'AuthenticationMethod' => 'MemberAuthenticator', * 'Email' => '*****@*****.**', * 'Password' => '1nitialPassword', * 'BackURL' => 'test/link', * [Optional: 'Remember' => 1 ] * ) * * @param array $data * @return SS_HTTPResponse */ protected function logInUserAndRedirect($data) { Session::clear('SessionForms.MemberLoginForm.Email'); Session::clear('SessionForms.MemberLoginForm.Remember'); if (Member::currentUser()->isPasswordExpired()) { if (isset($_REQUEST['BackURL']) && ($backURL = $_REQUEST['BackURL'])) { Session::set('BackURL', $backURL); } $cp = ChangePasswordForm::create($this->controller, 'SilverStripe\\Security\\ChangePasswordForm'); $cp->sessionMessage(_t('Member.PASSWORDEXPIRED', 'Your password has expired. Please choose a new one.'), 'good'); return $this->controller->redirect('Security/changepassword'); } // Absolute redirection URLs may cause spoofing if (!empty($_REQUEST['BackURL'])) { $url = $_REQUEST['BackURL']; if (Director::is_site_url($url)) { $url = Director::absoluteURL($url); } else { // Spoofing attack, redirect to homepage instead of spoofing url $url = Director::absoluteBaseURL(); } return $this->controller->redirect($url); } // If a default login dest has been set, redirect to that. if ($url = Security::config()->default_login_dest) { $url = Controller::join_links(Director::absoluteBaseURL(), $url); return $this->controller->redirect($url); } // Redirect the user to the page where they came from $member = Member::currentUser(); if ($member) { $firstname = Convert::raw2xml($member->FirstName); if (!empty($data['Remember'])) { Session::set('SessionForms.MemberLoginForm.Remember', '1'); $member->logIn(true); } else { $member->logIn(); } Session::set('Security.Message.message', _t('Member.WELCOMEBACK', "Welcome Back, {firstname}", array('firstname' => $firstname))); Session::set("Security.Message.type", "good"); } Controller::curr()->redirectBack(); }
public function canDelete($member = null) { if (!$member || !is_a($member, 'SilverStripe\\Security\\Member') || is_numeric($member)) { $member = Member::currentUser(); } // extended access checks $results = $this->extend('canDelete', $member); if ($results && is_array($results)) { if (!min($results)) { return false; } } return $this->canEdit($member); }
/** * Check if this file can be deleted * * @param Member $member * @return boolean */ public function canDelete($member = null) { if (!$member) { $member = Member::currentUser(); } $result = $this->extendedCan('canDelete', $member); if ($result !== null) { return $result; } return $this->canEdit($member); }
/** * @param Member $member * @return bool */ public function canView($member = null) { if (!$member) { $member = Member::currentUser(); } // Expectation that versioned::canView will hide this object in draft $result = $this->extendedCan('canView', $member); if ($result !== null) { return $result; } // Open to public return true; }
/** * Export core. * * @param GridField $gridField * @return ArrayData */ public function generatePrintData(GridField $gridField) { $printColumns = $this->getPrintColumnsForGridField($gridField); $header = null; if ($this->printHasHeader) { $header = new ArrayList(); foreach ($printColumns as $field => $label) { $header->push(new ArrayData(array("CellString" => $label))); } } $items = $gridField->getManipulatedList(); $itemRows = new ArrayList(); /** @var DataObject $item */ foreach ($items->limit(null) as $item) { $itemRow = new ArrayList(); foreach ($printColumns as $field => $label) { $value = $gridField->getDataFieldValue($item, $field); if ($item->escapeTypeForField($field) != 'xml') { $value = Convert::raw2xml($value); } $itemRow->push(new ArrayData(array("CellString" => $value))); } $itemRows->push(new ArrayData(array("ItemRow" => $itemRow))); if ($item->hasMethod('destroy')) { $item->destroy(); } } $ret = new ArrayData(array("Title" => $this->getTitle($gridField), "Header" => $header, "ItemRows" => $itemRows, "Datetime" => DBDatetime::now(), "Member" => Member::currentUser())); return $ret; }
public function testAutomaticRedirectionOnLogin() { // BackURL with permission error (not authenticated) should not redirect if ($member = Member::currentUser()) { $member->logOut(); } $response = $this->getRecursive('SecurityTest_SecuredController'); $this->assertContains(Convert::raw2xml("That page is secured."), $response->getBody()); $this->assertContains('<input type="submit" name="action_dologin"', $response->getBody()); // Non-logged in user should not be redirected, but instead shown the login form // No message/context is available as the user has not attempted to view the secured controller $response = $this->getRecursive('Security/login?BackURL=SecurityTest_SecuredController/'); $this->assertNotContains(Convert::raw2xml("That page is secured."), $response->getBody()); $this->assertNotContains(Convert::raw2xml("You don't have access to this page"), $response->getBody()); $this->assertContains('<input type="submit" name="action_dologin"', $response->getBody()); // BackURL with permission error (wrong permissions) should not redirect $this->logInAs('grouplessmember'); $response = $this->getRecursive('SecurityTest_SecuredController'); $this->assertContains(Convert::raw2xml("You don't have access to this page"), $response->getBody()); $this->assertContains('<input type="submit" name="action_logout" value="Log in as someone else"', $response->getBody()); // Directly accessing this page should attempt to follow the BackURL, but stop when it encounters the error $response = $this->getRecursive('Security/login?BackURL=SecurityTest_SecuredController/'); $this->assertContains(Convert::raw2xml("You don't have access to this page"), $response->getBody()); $this->assertContains('<input type="submit" name="action_logout" value="Log in as someone else"', $response->getBody()); // Check correctly logged in admin doesn't generate the same errors $this->logInAs('admin'); $response = $this->getRecursive('SecurityTest_SecuredController'); $this->assertContains(Convert::raw2xml("Success"), $response->getBody()); // Directly accessing this page should attempt to follow the BackURL and succeed $response = $this->getRecursive('Security/login?BackURL=SecurityTest_SecuredController/'); $this->assertContains(Convert::raw2xml("Success"), $response->getBody()); }
/** * Users can edit their own record. * Otherwise they'll need ADMIN or CMS_ACCESS_SecurityAdmin permissions * * @param Member $member * @return bool */ public function canDelete($member = null) { if (!$member instanceof Member) { $member = Member::currentUser(); } //check for extensions, we do this first as they can overrule everything $extended = $this->extendedCan(__FUNCTION__, $member); if ($extended !== null) { return $extended; } //need to be logged in and/or most checks below rely on $member being a Member if (!$member) { return false; } // Members are not allowed to remove themselves, // since it would create inconsistencies in the admin UIs. if ($this->ID && $member->ID == $this->ID) { return false; } // HACK: if you want to delete a member, you have to be a member yourself. // this is a hack because what this should do is to stop a user // deleting a member who has more privileges (e.g. a non-Admin deleting an Admin) if (Permission::checkMember($this, 'ADMIN')) { if (!Permission::checkMember($member, 'ADMIN')) { return false; } } //standard check return Permission::checkMember($member, 'CMS_ACCESS_SecurityAdmin'); }
/** * Get all menu items that the passed member can view. * Defaults to {@link Member::currentUser()}. * * @param Member $member * @return array */ public static function get_viewable_menu_items($member = null) { if (!$member && $member !== FALSE) { $member = Member::currentUser(); } $viewableMenuItems = array(); $allMenuItems = self::get_menu_items(); if ($allMenuItems) { foreach ($allMenuItems as $code => $menuItem) { // exclude all items which have a controller to perform permission // checks on if ($menuItem->controller) { $controllerObj = singleton($menuItem->controller); if (Controller::has_curr()) { // Necessary for canView() to have request data available, // e.g. to check permissions against LeftAndMain->currentPage() $controllerObj->setRequest(Controller::curr()->getRequest()); if (!$controllerObj->canView($member)) { continue; } } } $viewableMenuItems[$code] = $menuItem; } } return $viewableMenuItems; }
/** * Determine if the current user is able to set the given site stage / archive * * @param SS_HTTPRequest $request * @return bool */ public static function can_choose_site_stage($request) { // Request is allowed if stage isn't being modified if ((!$request->getVar('stage') || $request->getVar('stage') === static::LIVE) && !$request->getVar('archiveDate')) { return true; } // Check permissions with member ID in session. $member = Member::currentUser(); $permissions = Config::inst()->get(get_called_class(), 'non_live_permissions'); return $member && Permission::checkMember($member, $permissions); }
/** * 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')); }
/** * Default permissions for this ChangeSetItem * * @param string $perm * @param Member $member * @param array $context * @return bool */ public function can($perm, $member = null, $context = array()) { if (!$member) { $member = Member::currentUser(); } // Allow extensions to bypass default permissions, but only if // each change can be individually published. $extended = $this->extendedCan($perm, $member, $context); if ($extended !== null) { return $extended; } // Default permissions return (bool) Permission::checkMember($member, ChangeSet::config()->required_permission); }
/** * Get the list of groups that the given member belongs to. * * Call without an argument to get the groups that the current member * belongs to. In this case, the results will be session-cached. * * @param int $memberID The ID of the member. Leave blank for the current * member. * @return array Returns a list of group IDs to which the member belongs * to or NULL. */ public static function groupList($memberID = null) { // Default to current member, with session-caching if (!$memberID) { $member = Member::currentUser(); if ($member && isset($_SESSION['Permission_groupList'][$member->ID])) { return $_SESSION['Permission_groupList'][$member->ID]; } } else { $member = DataObject::get_by_id("SilverStripe\\Security\\Member", $memberID); } if ($member) { // Build a list of the IDs of the groups. Most of the heavy lifting // is done by Member::Groups // NOTE: This isn't effecient; but it's called once per session so // it's a low priority to fix. $groups = $member->Groups(); $groupList = array(); if ($groups) { foreach ($groups as $group) { $groupList[] = $group->ID; } } // Session caching if (!$memberID) { $_SESSION['Permission_groupList'][$member->ID] = $groupList; } return isset($groupList) ? $groupList : null; } }
/** * Returns true if the member is allowed to do the given action. * See {@link extendedCan()} for a more versatile tri-state permission control. * * @param string $perm The permission to be checked, such as 'View'. * @param Member $member The member whose permissions need checking. Defaults to the currently logged * in user. * @param array $context Additional $context to pass to extendedCan() * * @return boolean True if the the member is allowed to do the given action */ public function can($perm, $member = null, $context = array()) { if (!isset($member)) { $member = Member::currentUser(); } if (Permission::checkMember($member, "ADMIN")) { return true; } if ($this->getSchema()->manyManyComponent(static::class, 'Can' . $perm)) { if ($this->ParentID && $this->SecurityType == 'Inherit') { if (!($p = $this->Parent)) { return false; } return $this->Parent->can($perm, $member); } else { $permissionCache = $this->uninherited('permissionCache'); $memberID = $member ? $member->ID : 'none'; if (!isset($permissionCache[$memberID][$perm])) { if ($member->ID) { $groups = $member->Groups(); } $groupList = implode(', ', $groups->column("ID")); // TODO Fix relation table hardcoding $query = new SQLSelect("\"Page_Can{$perm}\".PageID", array("\"Page_Can{$perm}\""), "GroupID IN ({$groupList})"); $permissionCache[$memberID][$perm] = $query->execute()->column(); if ($perm == "View") { // TODO Fix relation table hardcoding $query = new SQLSelect("\"SiteTree\".\"ID\"", array("\"SiteTree\"", "LEFT JOIN \"Page_CanView\" ON \"Page_CanView\".\"PageID\" = \"SiteTree\".\"ID\""), "\"Page_CanView\".\"PageID\" IS NULL"); $unsecuredPages = $query->execute()->column(); if ($permissionCache[$memberID][$perm]) { $permissionCache[$memberID][$perm] = array_merge($permissionCache[$memberID][$perm], $unsecuredPages); } else { $permissionCache[$memberID][$perm] = $unsecuredPages; } } Config::inst()->update($this->class, 'permissionCache', $permissionCache); } if ($permissionCache[$memberID][$perm]) { return in_array($this->ID, $permissionCache[$memberID][$perm]); } } } else { return parent::can($perm, $member); } }
public function testCoreGlobalVariableCalls() { $this->assertEquals(Director::absoluteBaseURL(), $this->render('{$absoluteBaseURL}'), 'Director::absoluteBaseURL can be called from within template'); $this->assertEquals(Director::absoluteBaseURL(), $this->render('{$AbsoluteBaseURL}'), 'Upper-case %AbsoluteBaseURL can be called from within template'); $this->assertEquals(Director::is_ajax(), $this->render('{$isAjax}'), 'All variations of is_ajax result in the correct call'); $this->assertEquals(Director::is_ajax(), $this->render('{$IsAjax}'), 'All variations of is_ajax result in the correct call'); $this->assertEquals(Director::is_ajax(), $this->render('{$is_ajax}'), 'All variations of is_ajax result in the correct call'); $this->assertEquals(Director::is_ajax(), $this->render('{$Is_ajax}'), 'All variations of is_ajax result in the correct call'); $this->assertEquals(i18n::get_locale(), $this->render('{$i18nLocale}'), 'i18n template functions result correct result'); $this->assertEquals(i18n::get_locale(), $this->render('{$get_locale}'), 'i18n template functions result correct result'); $this->assertEquals((string) Member::currentUser(), $this->render('{$CurrentMember}'), 'Member template functions result correct result'); $this->assertEquals((string) Member::currentUser(), $this->render('{$CurrentUser}'), 'Member template functions result correct result'); $this->assertEquals((string) Member::currentUser(), $this->render('{$currentMember}'), 'Member template functions result correct result'); $this->assertEquals((string) Member::currentUser(), $this->render('{$currentUser}'), 'Member template functions result correct result'); $this->assertEquals(SecurityToken::getSecurityID(), $this->render('{$getSecurityID}'), 'SecurityToken template functions result correct result'); $this->assertEquals(SecurityToken::getSecurityID(), $this->render('{$SecurityID}'), 'SecurityToken template functions result correct result'); $this->assertEquals(Permission::check("ADMIN"), (bool) $this->render('{$HasPerm(\'ADMIN\')}'), 'Permissions template functions result correct result'); $this->assertEquals(Permission::check("ADMIN"), (bool) $this->render('{$hasPerm(\'ADMIN\')}'), 'Permissions template functions result correct result'); }