From d6ddbf2cff60b1446795b33ba8c41b0924559d5c Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Sat, 15 Sep 2007 20:00:00 +0000 Subject: [PATCH] mlanthaler: Implemented OpenIDAuthenticatedRole so that the OpenID credentials are now stored in a specific column (gsoc ticket #4). In that way member system is will remain extensible (no restriction due to not support multiple-inheritance). The role is applied automatically when the OpenIDAuthenticator is registered. (merged from branches/gsoc) git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@41911 467b73ca-7a2a-4603-9d3b-597d59a354a9 --- security/Authenticator.php | 26 ++++- security/MemberAuthenticator.php | 4 +- security/MemberLoginForm.php | 8 +- security/OpenIDAuthenticatedRole.php | 143 +++++++++++++++++++++++++++ security/OpenIDAuthenticator.php | 81 ++++++++++----- security/OpenIDLoginForm.php | 4 +- 6 files changed, 234 insertions(+), 32 deletions(-) create mode 100644 security/OpenIDAuthenticatedRole.php diff --git a/security/Authenticator.php b/security/Authenticator.php index 1627d47ba..46c38f566 100644 --- a/security/Authenticator.php +++ b/security/Authenticator.php @@ -76,8 +76,13 @@ abstract class Authenticator extends Object if(is_subclass_of($authenticator, 'Authenticator') == false) return false; - if(in_array($authenticator, self::$authenticators) == false) - array_push(self::$authenticators, $authenticator); + if(in_array($authenticator, self::$authenticators) == false) { + if(call_user_func(array($authenticator, 'onRegister')) === true) { + array_push(self::$authenticators, $authenticator); + } else { + return false; + } + } return true; } @@ -92,6 +97,23 @@ abstract class Authenticator extends Object public static function getAuthenticators() { return self::$authenticators; } + + + /** + * Callback function that is called when the authenticator is registered + * + * Use this method for initialization of a newly registered authenticator. + * Just overload this method and it will be called when the authenticator + * is registered. + * If the method returns FALSE, the authenticator won't be + * registered! + * + * @return bool Returns TRUE on success, FALSE otherwise. + */ + protected static function onRegister() { + return true; + } } + ?> \ No newline at end of file diff --git a/security/MemberAuthenticator.php b/security/MemberAuthenticator.php index 6d3f26197..ec0f5f338 100644 --- a/security/MemberAuthenticator.php +++ b/security/MemberAuthenticator.php @@ -36,7 +36,7 @@ class MemberAuthenticator extends Authenticator { Session::clear("BackURL"); } else if(!is_null($form)) { $form->sessionMessage( - "That doesn't seem to be the right email address or password. Please try again.", + "That doesn't seem to be the right e-mail address or password. Please try again.", "bad"); } @@ -63,7 +63,7 @@ class MemberAuthenticator extends Authenticator { * @return string Returns the name of the authentication method. */ public static function getName() { - return "Email & Password"; + return "E-mail & Password"; } } diff --git a/security/MemberLoginForm.php b/security/MemberLoginForm.php index 5c2212e53..c0851881f 100644 --- a/security/MemberLoginForm.php +++ b/security/MemberLoginForm.php @@ -53,7 +53,7 @@ class MemberLoginForm extends LoginForm { $fields = new FieldSet( new HiddenField("AuthenticationMethod", null, $this->authenticator_class, $this), - new TextField("Email", "Email address", + new TextField("Email", "E-mail address", Session::get('SessionForms.MemberLoginForm.Email'), null, $this), new EncryptField("Password", "Password", null, $this), new CheckboxField("Remember", "Remember me next time?", @@ -114,7 +114,9 @@ class MemberLoginForm extends LoginForm { if($badLoginURL = Session::get("BadLoginURL")) { Director::redirect($badLoginURL); } else { - Director::redirectBack(); + // Show the right tab on failed login + Director::redirect(Director::absoluteURL(Security::Link("login")) . + '#' . $this->FormName() .'_tab'); } } } @@ -178,7 +180,7 @@ class MemberLoginForm extends LoginForm { } 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?", + "Sorry, but I don't recognise the e-mail address. Maybe you need to sign up, or perhaps you used another e-mail address?", "bad"); Director::redirectBack(); diff --git a/security/OpenIDAuthenticatedRole.php b/security/OpenIDAuthenticatedRole.php new file mode 100644 index 000000000..01d7f14e0 --- /dev/null +++ b/security/OpenIDAuthenticatedRole.php @@ -0,0 +1,143 @@ + + */ + + + +/** + * Decorator for the member class to support OpenID authentication + * + * This class adds the needed fields to the default member class to support + * authentication via OpenID. + * + * @author Markus Lanthaler numRecords(); + + if($exist > 0) { + DB::query( "UPDATE `Member`, `ForumMember` " . + "SET `Member`.`ClassName` = 'Member'," . "`Member`.`ForumRank` = + `ForumMember`.`ForumRank`," . "`Member`.`Occupation` = + `ForumMember`.`Occupation`," . "`Member`.`Country` = + `ForumMember`.`Country`," . "`Member`.`Nickname` = + `ForumMember`.`Nickname`," . "`Member`.`FirstNamePublic` = + `ForumMember`.`FirstNamePublic`," . "`Member`.`SurnamePublic` = + `ForumMember`.`SurnamePublic`," . "`Member`.`OccupationPublic` = + `ForumMember`.`OccupationPublic`," . "`Member`.`CountryPublic` = + `ForumMember`.`CountryPublic`," . "`Member`.`EmailPublic` = + `ForumMember`.`EmailPublic`," . "`Member`.`AvatarID` = + `ForumMember`.`AvatarID`," . "`Member`.`LastViewed` = + `ForumMember`.`LastViewed`" . "WHERE `Member`.`ID` = + `ForumMember`.`ID`" + ); + echo( "
The data transfer has succeeded. However, + to complete it, you must delete the ForumMember table. To do this, + execute the query \"DROP TABLE 'ForumMember'\".
" ); + } + }*/ + } + + + /** + * Define extra database fields + * + * Returns a map where the keys are db, has_one, etc, and the values are + * additional fields/relations to be defined + * + * @return array Returns a map where the keys are db, has_one, etc, and + * the values are additional fields/relations to be defined + */ + function extraDBFields() { + return array( + 'db' => array('IdentityURL' => 'Varchar(255)'), + 'has_one' => array(), + 'defaults' => array('IdentityURL' => null), + 'indexes' => array('IdentityURL', 'unique (IdentityURL)') + ); + } + + + /** + * Change the member dialog in the CMS + * + * This method updates the form in the member dialog to make it possible + * to edit the new database fields. + */ + function updateCMSFields(FieldSet &$fields) { + //if(Permission::checkMember($this->owner->ID, "ACCESS_FORUM")) { + $fields->push(new HeaderField("OpenID/i-name credentials"), "OpenIDHeader"); + $fields->push(new LiteralField("OpenIDDescription", + "

Make sure you enter your normalized OpenID/i-name credentials here, i.e. with protocol and trailing slash for OpenID (e.g. http://openid.silverstripe.com/).

")); + $fields->push(new TextField("IdentityURL", "OpenID URL/i-name"), "IdentityURL"); + +/* + $fields->push(new PasswordField("ConfirmPassword", "Confirm Password")); + $fields->push(new ImageField("Avatar", "Upload avatar")); + $fields->push(new DropdownField("ForumRank", "User rating", + array("Community Member" => "Community Member", + "Administrator" => "Administrator", + "Moderator" => "Moderator", + "SilverStripe User" => "SilverStripe User", + "SilverStripe Developer" => "SilverStripe Developer", + "Core Development Team" => "Core Development Team", + "Google Summer of Code Hacker" => "Google Summer of Code Hacker", + "Lead Developer" => "Lead Developer") + ) + ); + }*/ + } + + /** + * Can the current user edit the given member? + * + * Only the user itself or an administrator can edit an user account. + * + * @return bool Returns TRUE if this member can be edited, FALSE otherwise + */ + function canEdit() { + if($this->owner->ID == Member::currentUserID()) + return true; + + $member = Member::currentUser(); + if($member) + return $member->isAdmin(); + + return false; + } + + + + + /** + * Factory method for the member validator + * + * @return Member_Validator Returns an instance of a + * {@link Member_Validator} object. + */ + function getValidator() { + die('

Called getValidator()

'); + return new Member_Validator(); + } +} + + +?> \ No newline at end of file diff --git a/security/OpenIDAuthenticator.php b/security/OpenIDAuthenticator.php index 1b1d78095..4d5f6b5dc 100644 --- a/security/OpenIDAuthenticator.php +++ b/security/OpenIDAuthenticator.php @@ -39,13 +39,30 @@ require_once "Auth/OpenID/SReg.php"; */ class OpenIDAuthenticator extends Authenticator { + /** + * Callback function that is called when the authenticator is registered + * + * Use this method for initialization of a newly registered authenticator. + * Just overload this method and it will be called when the authenticator + * is registered. + * If the method returns FALSE, the authenticator won't be + * registered! + * + * @return bool Returns TRUE on success, FALSE otherwise. + */ + protected static function onRegister() { + Member::addRole('OpenIDAuthenticatedRole'); + return true; + } + + /** * 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()} + * @param Form $form Optional: If passed, better error messages can be + * produced by using + * {@link Form::sessionMessage()} * @return bool Returns FALSE if authentication fails, otherwise the * method will not return at all because the browser will be * redirected to some other server. @@ -54,7 +71,16 @@ class OpenIDAuthenticator extends Authenticator { * (without rendering a form and using javascript) */ public function authenticate(array $RAW_data, Form $form = null) { - $openid = $RAW_data['OpenIDURL']; + $openid = trim($RAW_data['OpenIDURL']); + + if(strlen($openid) == 0) { + if(!is_null($form)) { + $form->sessionMessage("Please enter your OpenID URL or your i-name.", + "bad"); + } + return false; + } + $trust_root = Director::absoluteBaseURL(); $return_to_url = $trust_root . 'OpenIDAuthenticator_Controller'; @@ -74,8 +100,9 @@ class OpenIDAuthenticator extends Authenticator { return false; } - $SQL_user = Convert::raw2sql($auth_request->endpoint->claimed_id); - if(!($member = DataObject::get_one("Member", "Email = '$SQL_user'"))) { + $SQL_identity = Convert::raw2sql($auth_request->endpoint->claimed_id); + if(!($member = DataObject::get_one("Member", + "Member.IdentityURL = '$SQL_identity'"))) { if(!is_null($form)) { $form->sessionMessage("Either your account is not enabled for " . "OpenID/i-name authentication " . @@ -89,10 +116,12 @@ class OpenIDAuthenticator extends Authenticator { if($auth_request->shouldSendRedirect()) { // For OpenID 1, send a redirect. - $redirect_url = $auth_request->redirectURL($trust_root, $return_to_url); + $redirect_url = $auth_request->redirectURL($trust_root, + $return_to_url); if(Auth_OpenID::isFailure($redirect_url)) { - displayError("Could not redirect to server: " . $redirect_url->message); + displayError("Could not redirect to server: " . + $redirect_url->message); } else { Director::redirect($redirect_url); } @@ -102,16 +131,19 @@ class OpenIDAuthenticator extends Authenticator { // server. $form_id = 'openid_message'; $form_html = $auth_request->formMarkup($trust_root, $return_to_url, - false, array('id' => $form_id)); + false, + array('id' => $form_id)); if(Auth_OpenID::isFailure($form_html)) { - displayError("Could not redirect to server: " . $form_html->message); + displayError("Could not redirect to server: " . + $form_html->message); } else { $page_contents = array( "", "OpenID transaction in progress", "", - "", + "", $form_html, "

Click "Continue" to login. You are only seeing " . "this because you appear to have JavaScript disabled.

", @@ -129,8 +161,8 @@ class OpenIDAuthenticator extends Authenticator { /** * Method that creates the login form for this authentication method * - * @param Controller The parent controller, necessary to create the - * appropriate form action tag + * @param Controller The parent controller, necessary to create the + * appropriate form action tag * @return Form Returns the login form to use with this authentication * method */ @@ -139,12 +171,12 @@ class OpenIDAuthenticator extends Authenticator { } - /** - * Get the name of the authentication method - * - * @return string Returns the name of the authentication method. - */ - public static function getName() { + /** + * Get the name of the authentication method + * + * @return string Returns the name of the authentication method. + */ + public static function getName() { return "OpenID/i-name"; } } @@ -202,10 +234,9 @@ class OpenIDAuthenticator_Controller extends Controller { } else if($response->status == Auth_OpenID_SUCCESS) { $openid = $response->identity_url; - $user = $openid; if($response->endpoint->canonicalID) { - $user = $response->endpoint->canonicalID; + $openid = $response->endpoint->canonicalID; } @@ -213,10 +244,12 @@ class OpenIDAuthenticator_Controller extends Controller { Profiler::unmark("OpenIDAuthenticator_Controller"); - $SQL_user = Convert::raw2sql($user); - if($member = DataObject::get_one("Member", "Email = '$SQL_user'")) { + $SQL_identity = Convert::raw2sql($openid); + if($member = DataObject::get_one("Member", + "Member.IdentityURL = '$SQL_identity'")) { $firstname = Convert::raw2xml($member->FirstName); - Session::set("Security.Message.message", "Welcome Back, {$firstname}"); + Session::set("Security.Message.message", + "Welcome Back, {$firstname}"); Session::set("Security.Message.type", "good"); $member->LogIn( diff --git a/security/OpenIDLoginForm.php b/security/OpenIDLoginForm.php index 836e4460c..df083a250 100644 --- a/security/OpenIDLoginForm.php +++ b/security/OpenIDLoginForm.php @@ -111,7 +111,9 @@ class OpenIDLoginForm extends LoginForm { if($badLoginURL = Session::get("BadLoginURL")){ Director::redirect($badLoginURL); } else { - Director::redirectBack(); + // Show the right tab on failed login + Director::redirect(Director::absoluteURL(Security::Link("login")) . + '#' . $this->FormName() .'_tab'); } }