2007-07-19 12:40:28 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implements a basic security model
|
|
|
|
*/
|
|
|
|
class Security extends Controller {
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
|
|
|
* @var $username String Only used in dev-mode by setDefaultAdmin()
|
|
|
|
*/
|
|
|
|
protected static $username;
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
|
|
|
* @var $password String Only used in dev-mode by setDefaultAdmin()
|
|
|
|
*/
|
|
|
|
protected static $password;
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
protected static $strictPathChecking = false;
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2007-09-14 21:10:18 +02:00
|
|
|
* Register that we've had a permission failure trying to view the given page
|
|
|
|
*
|
2007-07-19 12:40:28 +02:00
|
|
|
* This will redirect to a login page.
|
|
|
|
* If you don't provide a messageSet, a default will be used.
|
2007-09-14 21:10:18 +02:00
|
|
|
*
|
|
|
|
* @param $page The controller that you were on to cause the permission
|
|
|
|
* failure.
|
|
|
|
* @param string|array $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
|
|
|
|
* - alreadyLoggedIn: The message to
|
|
|
|
* show if the user
|
|
|
|
* is already logged
|
|
|
|
* in and lacks the
|
|
|
|
* permission to
|
|
|
|
* access the item.
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2007-09-14 21:10:18 +02:00
|
|
|
static function permissionFailure($page, $messageSet = null) {
|
2007-07-19 12:40:28 +02:00
|
|
|
// Prepare the messageSet provided
|
|
|
|
if(!$messageSet) {
|
|
|
|
$messageSet = array(
|
|
|
|
'default' => "That page is secured. Enter your email address and password and we will send you right along.",
|
|
|
|
'alreadyLoggedIn' => "You don't have access to this page. If you have another password that can access that page, you can log in below.",
|
|
|
|
'logInAgain' => "You have been logged out. If you would like to log in again, enter a username and password below.",
|
|
|
|
);
|
|
|
|
} else if(!is_array($messageSet)) {
|
|
|
|
$messageSet = array('default' => $messageSet);
|
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
// Work out the right message to show
|
|
|
|
if(Member::currentUserID()) {
|
|
|
|
// user_error( 'PermFailure with member', E_USER_ERROR );
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-09-14 21:10:18 +02:00
|
|
|
$message = $messageSet['alreadyLoggedIn']
|
|
|
|
? $messageSet['alreadyLoggedIn']
|
|
|
|
: $messageSet['default'];
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-09-14 21:20:03 +02:00
|
|
|
if($member = Member::currentUser())
|
|
|
|
$member->logout();
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
} else if(substr(Director::history(),0,15) == 'Security/logout') {
|
2007-09-14 21:10:18 +02:00
|
|
|
$message = $messageSet['logInAgain']
|
|
|
|
? $messageSet['logInAgain']
|
|
|
|
: $messageSet['default'];
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
} else {
|
|
|
|
$message = $messageSet['default'];
|
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-09-14 21:10:18 +02:00
|
|
|
Session::set("Security.Message.message", $message);
|
|
|
|
Session::set("Security.Message.type", 'warning');
|
2007-07-19 12:40:28 +02:00
|
|
|
|
|
|
|
Session::set("BackURL", $_SERVER['REQUEST_URI']);
|
|
|
|
|
|
|
|
if(Director::is_ajax()) {
|
|
|
|
die('NOTLOGGEDIN:');
|
|
|
|
} else {
|
|
|
|
Director::redirect("Security/login");
|
|
|
|
}
|
2007-08-17 05:09:46 +02:00
|
|
|
return;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2007-09-14 19:04:11 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the login form to process according to the submitted data
|
|
|
|
*/
|
2007-07-19 12:40:28 +02:00
|
|
|
function LoginForm() {
|
2007-09-14 19:04:11 +02:00
|
|
|
if(is_array($_REQUEST) && isset($_REQUEST['AuthenticationMethod']))
|
|
|
|
{
|
2007-09-15 02:08:23 +02:00
|
|
|
$authenticator = trim($_REQUEST['AuthenticationMethod']);
|
|
|
|
|
|
|
|
$authenticators = Authenticator::getAuthenticators();
|
|
|
|
if(in_array($authenticator, $authenticators)) {
|
|
|
|
return call_user_func(array($authenticator, 'GetLoginForm'), $this);
|
2007-09-14 19:04:11 +02:00
|
|
|
}
|
|
|
|
}
|
2007-09-15 02:08:23 +02:00
|
|
|
|
|
|
|
user_error('Passed invalid authentication method', E_USER_ERROR);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2007-09-14 19:04:11 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the login forms for all available authentication methods
|
|
|
|
*
|
2007-09-14 21:10:18 +02:00
|
|
|
* @return array Returns an array of available login forms (array of Form
|
|
|
|
* objects).
|
|
|
|
*
|
2007-09-14 19:04:11 +02:00
|
|
|
* @todo Check how to activate/deactivate authentication methods
|
|
|
|
*/
|
|
|
|
function GetLoginForms()
|
|
|
|
{
|
|
|
|
$forms = array();
|
2007-09-15 02:08:23 +02:00
|
|
|
|
|
|
|
$authenticators = Authenticator::getAuthenticators();
|
|
|
|
foreach($authenticators as $authenticator) {
|
|
|
|
array_push($forms,
|
|
|
|
call_user_func(array($authenticator, 'GetLoginForm'),
|
|
|
|
$this));
|
|
|
|
}
|
2007-09-14 19:04:11 +02:00
|
|
|
|
|
|
|
return $forms;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a link to a security action
|
|
|
|
*
|
|
|
|
* @param string $action Name of the action
|
2007-09-14 21:10:18 +02:00
|
|
|
* @return string Returns the link to the given action
|
2007-09-14 19:04:11 +02:00
|
|
|
*/
|
2007-07-19 12:40:28 +02:00
|
|
|
function Link($action = null) {
|
|
|
|
return "Security/$action";
|
|
|
|
}
|
2007-09-14 19:04:11 +02:00
|
|
|
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2007-09-14 19:04:11 +02:00
|
|
|
* Log the currently logged in user out
|
|
|
|
*
|
2007-09-14 05:12:21 +02:00
|
|
|
* @param bool $redirect Redirect the user back to where they came.
|
2007-09-14 19:04:11 +02:00
|
|
|
* - If it's false, the code calling logout() is
|
|
|
|
* responsible for sending the user where-ever
|
|
|
|
* they should go.
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
|
|
|
function logout($redirect = true) {
|
2007-09-14 21:10:18 +02:00
|
|
|
if($member = Member::currentUser())
|
|
|
|
$member->logOut();
|
|
|
|
|
|
|
|
if($redirect)
|
|
|
|
Director::redirectBack();
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-09-14 19:04:11 +02:00
|
|
|
|
|
|
|
/**
|
2007-09-14 21:10:18 +02:00
|
|
|
* Show the "login" page
|
2007-09-14 19:04:11 +02:00
|
|
|
*
|
2007-09-14 21:10:18 +02:00
|
|
|
* @return string Returns the "login" page as HTML code.
|
2007-09-14 19:04:11 +02:00
|
|
|
*/
|
2007-07-19 12:40:28 +02:00
|
|
|
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");
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-09-14 21:13:12 +02:00
|
|
|
$customCSS = project() . '/css/tabs.css';
|
|
|
|
if(Director::fileExists($customCSS)) {
|
|
|
|
Requirements::css($customCSS);
|
|
|
|
}
|
|
|
|
|
|
|
|
$tmpPage = new Page();
|
2007-07-19 12:40:28 +02:00
|
|
|
$tmpPage->Title = "Log in";
|
|
|
|
$tmpPage->URLSegment = "Security";
|
|
|
|
|
|
|
|
$controller = new Page_Controller($tmpPage);
|
|
|
|
$controller->init();
|
2007-09-14 05:12:21 +02:00
|
|
|
Controller::$currentController = $controller;
|
2007-07-19 12:40:28 +02:00
|
|
|
|
|
|
|
if(SSViewer::hasTemplate("Security_login")) {
|
|
|
|
return $controller->renderWith(array("Security_login", "Page"));
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
} else {
|
2007-09-14 19:04:11 +02:00
|
|
|
$forms = $this->GetLoginForms();
|
|
|
|
$content = '';
|
|
|
|
foreach($forms as $form)
|
|
|
|
$content .= $form->forTemplate();
|
|
|
|
|
2007-09-14 21:10:18 +02:00
|
|
|
if(strlen($message = Session::get('Security.Message.message')) > 0) {
|
|
|
|
$message_type = Session::get('Security.Message.type');
|
|
|
|
if($message_type == 'bad') {
|
|
|
|
$message = "<p class=\"message $message_type\">$message</p>";
|
|
|
|
} else {
|
|
|
|
$message = "<p>$message</p>";
|
|
|
|
}
|
|
|
|
|
|
|
|
$customisedController = $controller->customise(array(
|
|
|
|
"Content" => $message,
|
|
|
|
"Form" => $content
|
|
|
|
));
|
|
|
|
} else {
|
|
|
|
$customisedController = $controller->customise(array(
|
|
|
|
"Content" => $content
|
|
|
|
));
|
|
|
|
}
|
2007-07-19 12:40:28 +02:00
|
|
|
|
|
|
|
return $customisedController->renderWith("Page");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-14 19:04:11 +02:00
|
|
|
|
|
|
|
/**
|
2007-09-14 21:10:18 +02:00
|
|
|
* Show the "lost password" page
|
2007-09-14 19:04:11 +02:00
|
|
|
*
|
2007-09-14 21:10:18 +02:00
|
|
|
* @return string Returns the "lost password " page as HTML code.
|
2007-09-14 19:04:11 +02:00
|
|
|
*/
|
2007-07-19 12:40:28 +02:00
|
|
|
function lostpassword() {
|
|
|
|
Requirements::javascript("jsparty/prototype.js");
|
|
|
|
Requirements::javascript("jsparty/behaviour.js");
|
|
|
|
Requirements::javascript("jsparty/loader.js");
|
|
|
|
Requirements::javascript("jsparty/prototype_improvements.js");
|
|
|
|
Requirements::javascript("jsparty/scriptaculous/effects.js");
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
$tmpPage = new Page();
|
|
|
|
$tmpPage->Title = "Lost Password";
|
|
|
|
$tmpPage->URLSegment = "Security";
|
|
|
|
$controller = new Page_Controller($tmpPage);
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
$customisedController = $controller->customise(array(
|
2007-09-14 21:10:18 +02:00
|
|
|
"Content" =>
|
|
|
|
"<p>Enter your e-mail address and we will send you a password</p>",
|
2007-07-19 12:40:28 +02:00
|
|
|
"Form" => $this->LostPasswordForm(),
|
|
|
|
));
|
|
|
|
|
2007-08-23 08:43:40 +02:00
|
|
|
//Controller::$currentController = $controller;
|
2007-07-19 12:40:28 +02:00
|
|
|
return $customisedController->renderWith("Page");
|
|
|
|
}
|
|
|
|
|
2007-09-14 19:04:11 +02:00
|
|
|
|
|
|
|
/**
|
2007-09-14 21:10:18 +02:00
|
|
|
* Show the "password sent" page
|
2007-09-14 19:04:11 +02:00
|
|
|
*
|
2007-09-14 21:10:18 +02:00
|
|
|
* @return string Returns the "password sent" page as HTML code.
|
2007-09-14 19:04:11 +02:00
|
|
|
*/
|
2007-07-19 12:40:28 +02:00
|
|
|
function passwordsent() {
|
|
|
|
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");
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
$tmpPage = new Page();
|
|
|
|
$tmpPage->Title = "Lost Password";
|
|
|
|
$tmpPage->URLSegment = "Security";
|
|
|
|
$controller = new Page_Controller($tmpPage);
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
$email = $this->urlParams['ID'];
|
|
|
|
$customisedController = $controller->customise(array(
|
|
|
|
"Title" => "Password sent to '$email'",
|
2007-09-14 21:10:18 +02:00
|
|
|
"Content" =>
|
|
|
|
"<p>Thank you, your password has been sent to '$email'.</p>",
|
2007-07-19 12:40:28 +02:00
|
|
|
));
|
|
|
|
|
2007-08-23 08:43:40 +02:00
|
|
|
//Controller::$currentController = $controller;
|
2007-07-19 12:40:28 +02:00
|
|
|
return $customisedController->renderWith("Page");
|
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
|
|
|
|
2007-09-14 19:04:11 +02:00
|
|
|
/**
|
2007-09-14 21:10:18 +02:00
|
|
|
* Factory method for the lost password form
|
2007-09-14 19:04:11 +02:00
|
|
|
*
|
2007-09-14 21:10:18 +02:00
|
|
|
* @return Form Returns the lost password form
|
2007-09-14 19:04:11 +02:00
|
|
|
*/
|
2007-07-19 12:40:28 +02:00
|
|
|
function LostPasswordForm() {
|
2007-09-14 05:12:21 +02:00
|
|
|
return new MemberLoginForm($this, "LostPasswordForm", new FieldSet(
|
2007-07-19 12:40:28 +02:00
|
|
|
new EmailField("Email", "Email address")
|
|
|
|
), new FieldSet(
|
|
|
|
new FormAction("forgotPassword", "Send me my password")
|
|
|
|
), false
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2007-09-14 21:10:18 +02:00
|
|
|
* Authenticate using the given email and password, returning the
|
|
|
|
* appropriate member object if
|
|
|
|
*
|
|
|
|
* @return bool|Member Returns FALSE if authentication fails, otherwise
|
|
|
|
* the member object
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
|
|
|
static function authenticate($RAW_email, $RAW_password) {
|
|
|
|
$SQL_email = Convert::raw2sql($RAW_email);
|
|
|
|
$SQL_password = Convert::raw2sql($RAW_password);
|
|
|
|
|
|
|
|
// Default login (see {@setDetaultAdmin()})
|
2007-09-14 21:10:18 +02:00
|
|
|
if(($RAW_email == self::$username) && ($RAW_password == self::$password)
|
|
|
|
&& !empty(self::$username) && !empty(self::$password)) {
|
2007-07-19 12:40:28 +02:00
|
|
|
$member = self::findAnAdministrator();
|
|
|
|
} else {
|
2007-09-14 21:10:18 +02:00
|
|
|
$member = DataObject::get_one("Member",
|
|
|
|
"Email = '$SQL_email' And Password = '$SQL_password'");
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
return $member;
|
|
|
|
}
|
|
|
|
|
2007-09-14 19:04:11 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
|
|
|
* Return a member with administrator privileges
|
2007-09-14 21:10:18 +02:00
|
|
|
*
|
|
|
|
* @return Member Returns a member object that has administrator
|
|
|
|
* privileges.
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2007-09-14 21:10:18 +02:00
|
|
|
static function findAnAdministrator($username = 'admin',
|
|
|
|
$password = 'password') {
|
2007-07-19 12:40:28 +02:00
|
|
|
$permission = DataObject::get_one("Permission", "`Code` = 'ADMIN'");
|
2007-08-16 08:36:24 +02:00
|
|
|
|
|
|
|
$adminGroup = null;
|
|
|
|
if($permission) $adminGroup = DataObject::get_one("Group", "`ID` = '{$permission->GroupID}'", true, "ID");
|
2007-07-19 12:40:28 +02:00
|
|
|
|
|
|
|
if($adminGroup) {
|
|
|
|
if($adminGroup->Members()->First()) {
|
|
|
|
$member = $adminGroup->Members()->First();
|
|
|
|
}
|
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
if(!$adminGroup) {
|
|
|
|
$adminGroup = Object::create('Group');
|
|
|
|
$adminGroup->Title = 'Administrators';
|
|
|
|
$adminGroup->Code = "administrators";
|
|
|
|
$adminGroup->write();
|
|
|
|
Permission::grant($adminGroup->ID, "ADMIN");
|
|
|
|
}
|
|
|
|
|
2007-07-20 01:15:05 +02:00
|
|
|
if(!isset($member)) {
|
2007-07-19 12:40:28 +02:00
|
|
|
$member = Object::create('Member');
|
|
|
|
$member->FirstName = $member->Surname = 'Admin';
|
|
|
|
$member->Email = $username;
|
|
|
|
$member->Password = $password;
|
|
|
|
$member->write();
|
|
|
|
$member->Groups()->add($adminGroup);
|
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
|
|
|
return $member;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-09-14 19:04:11 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2007-09-14 21:10:18 +02:00
|
|
|
* 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()}.
|
2007-09-14 05:12:21 +02:00
|
|
|
*
|
2007-07-19 12:40:28 +02:00
|
|
|
* @param $username String
|
|
|
|
* @param $password String (Cleartext)
|
|
|
|
*/
|
|
|
|
static function setDefaultAdmin( $username, $password ) {
|
|
|
|
if( self::$username || self::$password )
|
|
|
|
return;
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
self::$username = $username;
|
|
|
|
self::$password = $password;
|
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-09-14 19:04:11 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2007-09-14 21:10:18 +02:00
|
|
|
* Set strict path checking
|
|
|
|
*
|
|
|
|
* This prevents sharing of the session across several sites in the domain.
|
2007-09-14 05:12:21 +02:00
|
|
|
*
|
2007-09-14 21:10:18 +02:00
|
|
|
* @param boolean $strictPathChecking To enable or disable strict patch
|
|
|
|
* checking.
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
|
|
|
static function setStrictPathChecking($strictPathChecking) {
|
|
|
|
self::$strictPathChecking = $strictPathChecking;
|
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-09-14 19:04:11 +02:00
|
|
|
|
|
|
|
/**
|
2007-09-14 21:10:18 +02:00
|
|
|
* Get strict path checking
|
2007-09-14 19:04:11 +02:00
|
|
|
*
|
2007-09-14 21:10:18 +02:00
|
|
|
* @return boolean Status of strict path checking
|
2007-09-14 19:04:11 +02:00
|
|
|
*/
|
2007-07-19 12:40:28 +02:00
|
|
|
static function getStrictPathChecking() {
|
|
|
|
return self::$strictPathChecking;
|
|
|
|
}
|
|
|
|
}
|
2007-09-14 19:04:11 +02:00
|
|
|
|
|
|
|
?>
|