mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #7102 from Firesphere/pulls/auth-docs
DOCS Authentication
This commit is contained in:
commit
b270eee1b0
@ -8,17 +8,18 @@ The [Member](api:SilverStripe\Security\Member) class is used to represent user a
|
||||
|
||||
## Testing For Logged In Users
|
||||
|
||||
The [api:Security] class comes with a static method for getting information about the current logged in user.
|
||||
|
||||
**Security::getCurrentUser()**
|
||||
|
||||
Returns the full *Member* Object for the current user, returns *null* if user is not logged in.
|
||||
|
||||
Retrieves the current logged in member. Returns *null* if user is not logged in, otherwise, the Member object is returned.
|
||||
|
||||
```php
|
||||
if( $member = Security::getCurrentUser() ) {
|
||||
// Work with $member
|
||||
} else {
|
||||
// Do non-member stuff
|
||||
}
|
||||
if( $member = Security::getCurrentUser() ) {
|
||||
// Work with $member
|
||||
} else {
|
||||
// Do non-member stuff
|
||||
}
|
||||
```
|
||||
|
||||
## Subclassing
|
||||
@ -30,54 +31,48 @@ This is the least desirable way of extending the [Member](api:SilverStripe\Secur
|
||||
|
||||
You can define subclasses of [Member](api:SilverStripe\Security\Member) to add extra fields or functionality to the built-in membership system.
|
||||
|
||||
|
||||
```php
|
||||
use SilverStripe\Security\Member;
|
||||
|
||||
class MyMember extends Member
|
||||
{
|
||||
private static $db = [
|
||||
"Age" => "Int",
|
||||
"Address" => "Text",
|
||||
];
|
||||
}
|
||||
|
||||
class MyMember extends Member {
|
||||
private static $db = array(
|
||||
"Age" => "Int",
|
||||
"Address" => "Text",
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
To ensure that all new members are created using this class, put it in `Injector`.
|
||||
To ensure that all new members are created using this class, put a call to [api:Injector] in
|
||||
`(project)/_config/_config.yml`:
|
||||
|
||||
|
||||
```yaml
|
||||
```yml
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
SilverStripe\Security\Member:
|
||||
class: My\Project\MyMember
|
||||
SilverStripe\Security\Member:
|
||||
class: MyVendor\MyNamespace\MyMemberClass
|
||||
```
|
||||
|
||||
Note that if you want to look this class-name up, you can call `Injector::inst()->get('Member')->ClassName`
|
||||
|
||||
## Overloading getCMSFields()
|
||||
|
||||
If you overload the built-in public function getCMSFields(), then you can change the form that is used to view & edit member
|
||||
details in the newsletter system. This function returns a [FieldList](api:SilverStripe\Forms\FieldList) object. You should generally start by calling
|
||||
parent::getCMSFields() and manipulate the [FieldList](api:SilverStripe\Forms\FieldList) from there.
|
||||
|
||||
|
||||
```php
|
||||
public function getCMSFields()
|
||||
{
|
||||
$fields = parent::getCMSFields();
|
||||
$fields->insertBefore("HTMLEmail", new TextField("Age"));
|
||||
$fields->removeByName("JobTitle");
|
||||
$fields->removeByName("Organisation");
|
||||
return $fields;
|
||||
}
|
||||
public function getCMSFields() {
|
||||
$fields = parent::getCMSFields();
|
||||
$fields->insertBefore("HTMLEmail", new TextField("Age"));
|
||||
$fields->removeByName("JobTitle");
|
||||
$fields->removeByName("Organisation");
|
||||
return $fields;
|
||||
}
|
||||
```
|
||||
|
||||
## Extending Member or DataObject?
|
||||
|
||||
Basic rule: Class [Member](api:SilverStripe\Security\Member) should just be extended for entities who have some kind of login.
|
||||
If you have different types of [Member](api:SilverStripe\Security\Member)s in the system, you have to make sure that those with login-capabilities have
|
||||
unique email-addresses (as this is used for login-credentials).
|
||||
For persons without login-capabilities (e.g. for an address-database), you shouldn't extend [Member](api:SilverStripe\Security\Member) to avoid conflicts
|
||||
with the Member-database. This enables us to have a different subclass of [Member](api:SilverStripe\Security\Member) for an email-address with login-data,
|
||||
Basic rule: Class [api:Member] should just be extended for entities who have some kind of login.
|
||||
If you have different types of [api:Member]s in the system, you have to make sure that those with login-capabilities a unique field to be used for the login.
|
||||
For persons without login-capabilities (e.g. for an address-database), you shouldn't extend [api:Member] to avoid conflicts
|
||||
with the Member-database. This enables us to have a different subclass of [api:Member] for an email-address with login-data,
|
||||
and another subclass for the same email-address in the address-database.
|
||||
|
||||
## Member Role Extension
|
||||
@ -86,12 +81,10 @@ Using inheritance to add extra behaviour or data fields to a member is limiting,
|
||||
class. A better way is to use role extensions to add this behaviour. Add the following to your
|
||||
`[config.yml](/developer_guides/configuration/configuration/#configuration-yaml-syntax-and-rules)`.
|
||||
|
||||
|
||||
```yml
|
||||
|
||||
Member:
|
||||
extensions:
|
||||
- MyMemberExtension
|
||||
SilverStripe\Security\Member:
|
||||
extensions:
|
||||
- MyMemberExtension
|
||||
```
|
||||
|
||||
A role extension is simply a subclass of [DataExtension](api:SilverStripe\ORM\DataExtension) that is designed to be used to add behaviour to [Member](api:SilverStripe\Security\Member).
|
||||
@ -100,36 +93,36 @@ things, you should add appropriate [Permission::checkMember()](api:SilverStripe\
|
||||
|
||||
|
||||
```php
|
||||
use SilverStripe\Security\Permission;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
use SilverStripe\Security\Permission;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
|
||||
class MyMemberExtension extends DataExtension
|
||||
class MyMemberExtension extends DataExtension
|
||||
{
|
||||
/**
|
||||
|
||||
* Modify the field set to be displayed in the CMS detail pop-up
|
||||
*/
|
||||
public function updateCMSFields(FieldList $currentFields)
|
||||
{
|
||||
/**
|
||||
|
||||
* Modify the field set to be displayed in the CMS detail pop-up
|
||||
*/
|
||||
public function updateCMSFields(FieldList $currentFields)
|
||||
{
|
||||
// Only show the additional fields on an appropriate kind of use
|
||||
if(Permission::checkMember($this->owner->ID, "VIEW_FORUM")) {
|
||||
// Edit the FieldList passed, adding or removing fields as necessary
|
||||
// Edit the FieldList passed, adding or removing fields as necessary
|
||||
}
|
||||
}
|
||||
|
||||
// define additional properties
|
||||
private static $db = [];
|
||||
private static $has_one = [];
|
||||
private static $has_many = [];
|
||||
private static $many_many = [];
|
||||
private static $belongs_many_many = [];
|
||||
|
||||
public function somethingElse()
|
||||
{
|
||||
// You can add any other methods you like, which you can call directly on the member object.
|
||||
}
|
||||
}
|
||||
|
||||
// define additional properties
|
||||
private static $db = [];
|
||||
private static $has_one = [];
|
||||
private static $has_many = [];
|
||||
private static $many_many = [];
|
||||
private static $belongs_many_many = [];
|
||||
|
||||
public function somethingElse()
|
||||
{
|
||||
// You can add any other methods you like, which you can call directly on the member object.
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Saved User Logins ##
|
||||
@ -165,18 +158,19 @@ E.g.
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Dev\BuildTask;
|
||||
|
||||
class CleanRecordsTask extends BuildTask
|
||||
class CleanRecordsTask extends BuildTask
|
||||
{
|
||||
public function run($request)
|
||||
{
|
||||
public function run($request)
|
||||
{
|
||||
if (!Director::is_cli()) {
|
||||
throw new BadMethodCallException('This task only runs on CLI');
|
||||
}
|
||||
$admin = Security::findAnAdministrator();
|
||||
Member::actAs($admin, function() {
|
||||
DataRecord::get()->filter('Dirty', true)->removeAll();
|
||||
});
|
||||
if (!Director::is_cli()) {
|
||||
throw new BadMethodCallException('This task only runs on CLI');
|
||||
}
|
||||
$admin = Security::findAnAdministrator();
|
||||
Member::actAs($admin, function() {
|
||||
DataRecord::get()->filter('Dirty', true)->removeAll();
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Documentation
|
||||
|
@ -47,8 +47,8 @@ class PageController implements PermissionProvider
|
||||
```
|
||||
|
||||
|
||||
This can then be used to add a dropdown for permission codes to the security panel. Permission::get_all_codes() will be
|
||||
a helper method that will call providePermissions() on every applicable class, and collate the resuls into a single
|
||||
This can then be used to add a dropdown for permission codes to the security panel. `Permission::get_all_codes()` will be
|
||||
a helper method that will call `providePermissions()` on every applicable class, and collate the resuls into a single
|
||||
dropdown.
|
||||
|
||||
## Default use
|
||||
@ -60,8 +60,6 @@ By default, permissions are used in the following way:
|
||||
* If not logged in, the 'View' permissions must be 'anyone logged in' for a page to be displayed in a menu
|
||||
* If logged in, you must be allowed to view a page for it to be displayed in a menu
|
||||
|
||||
**NOTE:** Should the canView() method on SiteTree be updated to call Permission::check("SITETREE_VIEW", $this->ID)?
|
||||
Making this work well is a subtle business and should be discussed with a few developers.
|
||||
|
||||
## Setting up permissions
|
||||
|
||||
|
@ -46,3 +46,86 @@ It is advisable to configure this user in your `.env` file inside of the web roo
|
||||
When a user logs in with these credentials, then a [Member](api:SilverStripe\Security\Member) with the Email 'admin' will be generated in
|
||||
the database, but without any password information. This means that the password can be reset or changed by simply
|
||||
updating the `.env` file.
|
||||
|
||||
## Registering a new Authenticator
|
||||
|
||||
```yaml
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
SilverStripe\Security\Security:
|
||||
properties:
|
||||
Authenticators:
|
||||
myauthenticator: %$MyVendor\MyProject\Authenticator\MyAuthenticator
|
||||
```
|
||||
If there is no authenticator registered, `Authenticator` will try to fall back on the default provided authenticator (`default`), which can be changed using the following config, replacing the MemberAuthenticator with your authenticator:
|
||||
```yaml
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
SilverStripe\Security\Security:
|
||||
properties:
|
||||
Authenticators:
|
||||
default: %$MyVendor\MyProject\Authenticator\MyAuthenticator
|
||||
```
|
||||
|
||||
By default, the `SilverStripe\Security\MemberAuthenticator\MemberAuthenticator` is seen as the default authenticator until it's explicitly set in the config.
|
||||
|
||||
Every Authenticator is expected to handle services. The `Authenticator` Interface provides the available services:
|
||||
```php
|
||||
|
||||
const LOGIN = 1;
|
||||
const LOGOUT = 2;
|
||||
const CHANGE_PASSWORD = 4;
|
||||
const RESET_PASSWORD = 8;
|
||||
const CMS_LOGIN = 16;
|
||||
|
||||
/**
|
||||
* Returns the services supported by this authenticator
|
||||
*
|
||||
* The number should be a bitwise-OR of 1 or more of the following constants:
|
||||
* Authenticator::LOGIN, Authenticator::LOGOUT, Authenticator::CHANGE_PASSWORD,
|
||||
* Authenticator::RESET_PASSWORD, or Authenticator::CMS_LOGIN
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function supportedServices();
|
||||
```
|
||||
|
||||
If there is no available authenticator for the required action (either one of the constants above), an error will be thrown.
|
||||
|
||||
Custom Authenticators are expected to have the following methods implemented:
|
||||
* `getLoginHandler()`
|
||||
* `getLogoutHandler()`
|
||||
* `getChangePasswordHandler()`
|
||||
* `getLostPasswordHandler()`
|
||||
|
||||
All expect a `$link` variable, to handle the request.
|
||||
Further, there is
|
||||
* `authenticate()`
|
||||
Which expects the data to be used for authentication as an array and a nullable variable `$result` by reference, which returns a `ValidationResult`.
|
||||
|
||||
If only a subset of the supportedServices() will be provided by the custom Authenticator, it is advised to extend `SilverStripe\Security\MemberAuthenticator\MemberAuthenticator`, as that default contains all required methods already and only an override or follow up needs to be written.
|
||||
|
||||
An example of how to write a multi-factor authentication [can be found here](https://gist.github.com/sminnee/bc646147f3941a764d0410f2044433c7).
|
||||
|
||||
## IdentityStore
|
||||
|
||||
A new IdentityStore, e.g. an LDAP IdentityStore can be registered as follows in a `security.yml` file (Not an actual valid LDAP configuration):
|
||||
```yaml
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
MyProject\LDAP\Authenticator\LDAPAuthenticator:
|
||||
properties:
|
||||
LDAPSettings:
|
||||
- URL: https://my-ldap-location.com
|
||||
CascadeInTo: %$SilverStripe\Security\MemberAuthenticator\SessionAuthenticationHandler
|
||||
SilverStripe\Security\AuthenticationHandler:
|
||||
class: SilverStripe\Security\RequestAuthenticationHandler
|
||||
properties:
|
||||
Handlers:
|
||||
ldap: %$MyProject\LDAP\Authenticator\LDAPAuthenticator
|
||||
```
|
||||
|
||||
CascadeInTo is used to defer login or logout actions to other authenticators, after the first one has been logged in. In the example of LDAP authenticator, this is useful to check e.g. the validity of the Session (is the user still logged in?) and if not, or it's LDAP login period has expired, only then validate against the external service again, limiting the amount of requests to the external service.
|
||||
|
||||
Upon request, the Member is authenticated against the given AuthenticatorHandlers. To override an Authenticator, override it's name in the `YML` to your own Handler.
|
||||
|
||||
To get applicable Authenticators for a certain request, refer to [API:Security:getApplicableAuthenticators()].
|
||||
|
||||
To register `CMS` authenticators, use the same procedure as above, only replace `SilverStripe\Security\Security` with `SilverStripe\Security\CMSSecurity`.
|
||||
|
@ -63,6 +63,7 @@ guide developers in preparing existing 3.x code for compatibility with 4.0
|
||||
* The `GDBackend` and `ImagickBackend` classes have been replaced by a unified `InterventionBackend` which uses the
|
||||
[intervention/image](https://github.com/intervention/image) library to power manipualations.
|
||||
* Dependencies can managed via [recipe-plugin](https://github.com/silverstripe/recipe-plugin). See [recipe-core](https://github.com/silverstripe/recipe-core) and [recipe-cms](https://github.com/silverstripe/recipe-cms) as examples.
|
||||
* Authentication has been upgraded to a modular approach using re-usable interfaces and easier to hook in to LoginHandlers.
|
||||
|
||||
## <a name="upgrading"></a>Upgrading
|
||||
|
||||
@ -1419,7 +1420,7 @@ specific functions.
|
||||
|
||||
#### Upgrading custom Authenticators
|
||||
|
||||
The methods `register` and `unregister` on `Authenticator` are deprecated in favor of the `Config` system. This means that any custom Authenticator needs to be registered through the yml config:
|
||||
The methods `register` and `unregister` on `Authenticator` are deprecated in favor of the `Config` system. This means that any custom Authenticator needs to be registered through the yml config. For further information on how to create a custom authentication method, [see the Authentication documentation](/developer_guides/Security/Authentication).
|
||||
|
||||
```yml
|
||||
SilverStripe\Security\Authenticator;
|
||||
@ -1438,6 +1439,11 @@ As soon as a custom authenticator is registered, the default authenticator will
|
||||
|
||||
By default, the `SilverStripe\Security\MemberAuthenticator` is seen as the default authenticator until it's explicitly set in the config.
|
||||
|
||||
##### IdentityStore and RequestFilters
|
||||
As of SilverStripe 4, every request is authenticated against an `IdentityStore`, by default a CookieAuthenticationHandler and a SessionAuthenticationHandler, which are called from the `AuthenticationHandler`. If there is a valid `Member`, it is set on `Security::setCurrentUser()`, which defaults no `null`.
|
||||
|
||||
IdentityStores are responsible for logging members in and out (e.g. destroy cookies and sessions, or instantiate them).
|
||||
|
||||
#### Upgrading Config API usages
|
||||
|
||||
Performance optimisations have been made to Config which, under certain circumstances, require developer
|
||||
|
Loading…
Reference in New Issue
Block a user