From a377a67e540a33941e4a5436d169e4cfa93af58a Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Fri, 14 Sep 2007 03:12:21 +0000 Subject: [PATCH] mlanthaler: Switched to an authenticator and a form class to be able to add other authentication methods. (merged from branches/gsoc) mlanthaler: The missing authenticator base class... (merged from branches/gsocmlanthaler: Switched to an authenticator and a form class to be able to add other authentication methods. (merged from branches/gsoc) mlanthaler: The missing authenticator base class... (merged from branches/gsoc)) git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@41729 467b73ca-7a2a-4603-9d3b-597d59a354a9 --- forms/ComplexTableField.php | 29 ++ security/Authenticator.php | 33 +++ security/MemberAuthenticator.php | 43 +++ .../{LoginForm.php => MemberLoginForm.php} | 275 ++++++++++-------- security/Security.php | 76 +++-- templates/ComplexTableField_popup.ss | 47 +-- 6 files changed, 329 insertions(+), 174 deletions(-) create mode 100644 security/Authenticator.php create mode 100644 security/MemberAuthenticator.php rename security/{LoginForm.php => MemberLoginForm.php} (64%) diff --git a/forms/ComplexTableField.php b/forms/ComplexTableField.php index fec8a9b6c..d9b05662a 100755 --- a/forms/ComplexTableField.php +++ b/forms/ComplexTableField.php @@ -457,6 +457,35 @@ JS; $start = $_REQUEST['ctf']['start'] - 1; return $this->PopupBaseLink() . "&methodName={$_REQUEST['methodName']}&ctf[childID]={$item->ID}&ctf[start]={$start}"; } + + /** + * Method handles pagination in asset popup. + * + * @return Object DataObjectSet + */ + + function pagination() { + $this->pageSize = 10; + $currentItem = $this->PopupCurrentItem(); + $result = new DataObjectSet(); + if($currentItem < 6) { + $offset = 1; + } elseif($this->totalCount - $currentItem <= 4) { + $offset = $currentItem - (10 - ($this->totalCount - $currentItem)); + $offset = $offset <= 0 ? 1 : $offset; + } else { + $offset = $currentItem - 5; + } + for($i = $offset;$i <= $offset + $this->pageSize && $i <= $this->totalCount;$i++) { + $start = $i - 1; + $item = $this->unpagedSourceItems->getOffset($i-1); + $links['link'] = $this->PopupBaseLink() . "&methodName={$_REQUEST['methodName']}&ctf[childID]={$item->ID}&ctf[start]={$start}"; + $links['number'] = $i; + $links['active'] = $i == $currentItem ? false : true; + $result->push(new ArrayData($links)); + } + return $result; + } diff --git a/security/Authenticator.php b/security/Authenticator.php new file mode 100644 index 000000000..b0527cf85 --- /dev/null +++ b/security/Authenticator.php @@ -0,0 +1,33 @@ + + */ +abstract class Authenticator extends Object +{ + /** + * Method to authenticate an user + * + * @param array $RAW_data Raw data to authenticate the user + * @return bool|Member Returns FALSE if authentication fails, otherwise + * the member object + */ + public abstract function authenticate(array $RAW_data); + + + /** + * Method that creates the login form for this authentication method + * + * @return Form Returns the login form to use with this authentication + * method + */ + public abstract function getLoginForm(); +} + +?> \ No newline at end of file diff --git a/security/MemberAuthenticator.php b/security/MemberAuthenticator.php new file mode 100644 index 000000000..5368529cc --- /dev/null +++ b/security/MemberAuthenticator.php @@ -0,0 +1,43 @@ + + */ +class MemberAuthenticator extends Authenticator { + + /** + * Method to authenticate an user + * + * @param array $RAW_data Raw data to authenticate the user + * @return bool|Member Returns FALSE if authentication fails, otherwise + * the member object + */ + public function authenticate(array $RAW_data) { + $SQL_user = Convert::raw2sql($RAW_data['Email']); + $SQL_password = Convert::raw2sql($RAW_data['Password']); + + $member = DataObject::get_one( + "Member", "Email = '$SQL_user' And Password = '$SQL_password'"); + + if($member) { + Session::clear("BackURL"); + } + + return $member; + } + + + /** + * Method that creates the login form for this authentication method + * + * @return Form Returns the login form to use with this authentication + * method + */ + public function getLoginForm() { + return Object::create("MemberLoginForm", $this, "LoginForm"); + } +} + +?> \ No newline at end of file diff --git a/security/LoginForm.php b/security/MemberLoginForm.php similarity index 64% rename from security/LoginForm.php rename to security/MemberLoginForm.php index b9d6f4bfb..9b0f24768 100644 --- a/security/LoginForm.php +++ b/security/MemberLoginForm.php @@ -1,116 +1,159 @@ -push(new HiddenField('BackURL', 'BackURL', $backURL)); - } - - parent::__construct($controller, $name, $fields, $actions); - } - - protected function getMessageFromSession() { - parent::getMessageFromSession(); - if(($member = Member::currentUser()) && !Session::get('LoginForm.force_message')) { - $this->message = "You're logged in as $member->FirstName."; - } - Session::set('LoginForm.force_message', false); - } - - public function dologin($data) { - if($this->performLogin($data)){ - if(isset($_REQUEST['BackURL']) && $backURL = $_REQUEST['BackURL']) { - Session::clear("BackURL"); - Director::redirect($backURL); - }else - Director::redirectBack(); - }else{ - - if($badLoginURL = Session::get("BadLoginURL")){ - Director::redirect($badLoginURL); - }else{ - Director::redirectBack(); - } - } - } - - public function logout(){ - $s = new Security(); - return $s->logout(); - } - - /* check the membership - - * if one of them or both don't match, set the fields which are unmatched with red star * - */ - public function performLogin($data){ - if($member = Security::authenticate($data['Email'], $data['Password'])) { - $firstname = Convert::raw2xml($member->FirstName); - $this->sessionMessage("Welcome Back, {$firstname}", "good"); - $member->LogIn(); - if(isset($data['Remember'])) { - // Deliberately obscure... - Cookie::set('alc_enc',base64_encode("$data[Email]:$data[Password]")); - } - return $member; - - } else { - $this->sessionMessage("That doesn't seem to be the right email address or password. Please try again.", "bad"); - return null; - } - } - - - - function forgotPassword($data) { - $SQL_data = Convert::raw2sql($data); - if($data['Email'] && $member = DataObject::get_one("Member", "Member.Email = '$SQL_data[Email]'")) { - if(!$member->Password) { - $member->createNewPassword(); - $member->write(); - } - - $member->sendInfo('forgotPassword'); - Director::redirect('Security/passwordsent/' . urlencode($data['Email'])); - - } else if($data['Email']) { - $this->sessionMessage("Sorry, but I don't recognise the email address. Maybe you need to sign up, or perhaps you used another email address?", "bad"); - Director::redirectBack(); - - } else { - Director::redirect("Security/lostpassword"); - - } - } -} - - -?> +push(new HiddenField('BackURL', 'BackURL', $backURL)); + } + + parent::__construct($controller, $name, $fields, $actions); + } + + + /** + * Get message from session + */ + protected function getMessageFromSession() { + parent::getMessageFromSession(); + if(($member = Member::currentUser()) && !Session::get('MemberLoginForm.force_message')) { + $this->message = "You're logged in as $member->FirstName."; + } + Session::set('MemberLoginForm.force_message', false); + } + + + /** + * Login form handler method + * + * This method is called when the user clicks on "Log in" + * + * @param array $data Submitted data + */ + public function dologin($data) { + if($this->performLogin($data)){ + + if($backURL = $_REQUEST['BackURL']) { + Session::clear("BackURL"); + Director::redirect($backURL); + }else + Director::redirectBack(); + }else{ + + if($badLoginURL = Session::get("BadLoginURL")){ + Director::redirect($badLoginURL); + }else{ + Director::redirectBack(); + } + } + } + + + /** + * Log out + * + * @todo Figure out for what this method is used! + */ + public function logout(){ + $s = new Security(); + return $s->logout(); + } + + + /** + * Check the membership + * + * If one of them or both don't match, set the fields which are unmatched with red star * + */ + public function performLogin($data){ + if($member = MemberAuthenticator::authenticate($data)) { + $firstname = Convert::raw2xml($member->FirstName); + $this->sessionMessage("Welcome Back, {$firstname}", "good"); + $member->LogIn(); + if(isset($data['Remember'])) { + // Deliberately obscure... + Cookie::set('alc_enc',base64_encode("$data[Email]:$data[Password]")); + } + return $member; + + } else { + $this->sessionMessage("That doesn't seem to be the right email address or password. Please try again.", "bad"); + return null; + } + } + + + /** + * Forgot password form handler method + * + * This method is called when the user clicks on "Log in" + * + * @param array $data Submitted data + */ + function forgotPassword($data) { + $SQL_data = Convert::raw2sql($data); + if($data['Email'] && $member = DataObject::get_one("Member", "Member.Email = '$SQL_data[Email]'")) { + if(!$member->Password) { + $member->createNewPassword(); + $member->write(); + } + + $member->sendInfo('forgotPassword'); + Director::redirect('Security/passwordsent/' . urlencode($data['Email'])); + + } else if($data['Email']) { + $this->sessionMessage("Sorry, but I don't recognise the email address. Maybe you need to sign up, or perhaps you used another email address?", "bad"); + Director::redirectBack(); + + } else { + Director::redirect("Security/lostpassword"); + + } + } +} + + +?> \ No newline at end of file diff --git a/security/Security.php b/security/Security.php index 0e28c395a..a264a429a 100644 --- a/security/Security.php +++ b/security/Security.php @@ -4,19 +4,19 @@ * Implements a basic security model */ class Security extends Controller { - + /** * @var $username String Only used in dev-mode by setDefaultAdmin() */ protected static $username; - + /** * @var $password String Only used in dev-mode by setDefaultAdmin() */ protected static $password; - + protected static $strictPathChecking = false; - + /** * Register that we've had a permission failure trying to view the given page. * This will redirect to a login page. @@ -24,13 +24,13 @@ class Security extends Controller { * @param messageSet The message to show to the user. This can be a string, or a map of different * messages for different contexts. If you pass an array, you can use the following keys: * - default: The default message - * - logInAgain: The message to show if the user has just logged out and the + * - logInAgain: The message to show if the user has just logged out and the * - alreadyLoggedIn: The message to show if the user is already logged in and lacks the permission to access the item. * If you don't provide a messageSet, a default will be used. */ static function permissionFailure($page = null, $messageSet = null) { $loginForm = singleton('Security')->LoginForm(); - + //user_error('debug', E_USER_ERROR); // Prepare the messageSet provided if(!$messageSet) { @@ -42,24 +42,24 @@ class Security extends Controller { } else if(!is_array($messageSet)) { $messageSet = array('default' => $messageSet); } - + // Work out the right message to show if(Member::currentUserID()) { // user_error( 'PermFailure with member', E_USER_ERROR ); - + $message = $messageSet['alreadyLoggedIn'] ? $messageSet['alreadyLoggedIn'] : $messageSet['default']; //$_SESSION['LoginForm']['force_form'] = true; //$_SESSION['LoginForm']['type'] = 'warning'; - + Member::logout(); - + } else if(substr(Director::history(),0,15) == 'Security/logout') { $message = $messageSet['logInAgain'] ? $messageSet['logInAgain'] : $messageSet['default']; - + } else { $message = $messageSet['default']; } - + $loginForm->sessionMessage($message, 'warning'); // $_SESSION['LoginForm']['message'] = $message; @@ -74,15 +74,13 @@ class Security extends Controller { } function LoginForm() { - $customCSS = project() . '/css/login.css'; - if(Director::fileExists($customCSS)) Requirements::css($customCSS); - return Object::create("LoginForm", $this, "LoginForm"); + return MemberAuthenticator::GetLoginForm(); } function Link($action = null) { return "Security/$action"; } /** - * @param bool $redirect Redirect the user back to where they came. + * @param bool $redirect Redirect the user back to where they came. * - If it's false, the code calling logout() is responsible for sending the user where-ever they should go. */ function logout($redirect = true) { @@ -91,25 +89,25 @@ class Security extends Controller { Session::clear("loggedInAs"); if($redirect) Director::redirectBack(); } - + function login() { Requirements::javascript("jsparty/behaviour.js"); Requirements::javascript("jsparty/loader.js"); Requirements::javascript("jsparty/prototype.js"); Requirements::javascript("jsparty/prototype_improvements.js"); Requirements::javascript("jsparty/scriptaculous/effects.js"); - - $tmpPage = DataObject::get_one('Page', "URLSegment = 'home'"); + + $tmpPage = DataObject::get_one('Page', "URLSegment = 'home'"); $tmpPage->Title = "Log in"; $tmpPage->URLSegment = "Security"; $controller = new Page_Controller($tmpPage); $controller->init(); - //Controller::$currentController = $controller; + Controller::$currentController = $controller; if(SSViewer::hasTemplate("Security_login")) { return $controller->renderWith(array("Security_login", "Page")); - + } else { $customisedController = $controller->customise(array( "Content" => $this->LoginForm()->forTemplate() @@ -125,12 +123,12 @@ class Security extends Controller { Requirements::javascript("jsparty/loader.js"); Requirements::javascript("jsparty/prototype_improvements.js"); Requirements::javascript("jsparty/scriptaculous/effects.js"); - + $tmpPage = new Page(); $tmpPage->Title = "Lost Password"; $tmpPage->URLSegment = "Security"; $controller = new Page_Controller($tmpPage); - + $customisedController = $controller->customise(array( "Content" => "

Enter your e-mail address and we will send you a password

", "Form" => $this->LostPasswordForm(), @@ -146,12 +144,12 @@ class Security extends Controller { Requirements::javascript("jsparty/prototype.js"); Requirements::javascript("jsparty/prototype_improvements.js"); Requirements::javascript("jsparty/scriptaculous/effects.js"); - + $tmpPage = new Page(); $tmpPage->Title = "Lost Password"; $tmpPage->URLSegment = "Security"; $controller = new Page_Controller($tmpPage); - + $email = $this->urlParams['ID']; $customisedController = $controller->customise(array( "Title" => "Password sent to '$email'", @@ -161,10 +159,10 @@ class Security extends Controller { //Controller::$currentController = $controller; return $customisedController->renderWith("Page"); } - - + + function LostPasswordForm() { - return new LoginForm($this, "LostPasswordForm", new FieldSet( + return new MemberLoginForm($this, "LostPasswordForm", new FieldSet( new EmailField("Email", "Email address") ), new FieldSet( new FormAction("forgotPassword", "Send me my password") @@ -186,7 +184,7 @@ class Security extends Controller { } else { $member = DataObject::get_one("Member", "Email = '$SQL_email' And Password = '$SQL_password'"); } - + return $member; } @@ -204,7 +202,7 @@ class Security extends Controller { $member = $adminGroup->Members()->First(); } } - + if(!$adminGroup) { $adminGroup = Object::create('Group'); $adminGroup->Title = 'Administrators'; @@ -221,37 +219,37 @@ class Security extends Controller { $member->write(); $member->Groups()->add($adminGroup); } - - return $member; + + return $member; } - + /** * This will set a static default-admin (e.g. "td") which is not existing as * a database-record. By this workaround we can test pages in dev-mode with * a unified login. Submitted login-credentials are first checked against * this static information in {@authenticate()}. - * + * * @param $username String * @param $password String (Cleartext) */ static function setDefaultAdmin( $username, $password ) { if( self::$username || self::$password ) return; - + self::$username = $username; self::$password = $password; } - + /** * Set strict path checking. This prevents sharing of the session * across several sites in the domain. - * - * @param strictPathChecking boolean to enable or disable strict patch checking. + * + * @param strictPathChecking boolean to enable or disable strict patch checking. */ static function setStrictPathChecking($strictPathChecking) { self::$strictPathChecking = $strictPathChecking; } - + static function getStrictPathChecking() { return self::$strictPathChecking; } diff --git a/templates/ComplexTableField_popup.ss b/templates/ComplexTableField_popup.ss index 4d42f81b6..20810968a 100755 --- a/templates/ComplexTableField_popup.ss +++ b/templates/ComplexTableField_popup.ss @@ -11,26 +11,35 @@ <% if IsAddMode %> <% else %> <% if ShowPagination %> - - - - - - -
- <% if PopupFirstLink %>View first $NameSingular - <% else %>View first $NameSingular<% end_if %> - <% if PopupPrevLink %>View previous $NameSingular - <% else %>View previous $NameSingular<% end_if %> - - Displaying $PopupCurrentItem of $TotalCount - - <% if PopupNextLink %>View next $NameSingular - <% else %>View next $NameSingular<% end_if %> - <% if PopupLastLink %>View last $NameSingular - <% else %>View last $NameSingular<% end_if %> -
+