Version 6 pre-stable
This version of Silverstripe CMS has not yet been given a stable release. See the release roadmap for more information. Go to documentation for the most recent stable version.

Member

Introduction

The Member class is used to represent user accounts on a Silverstripe CMS site (including newsletter recipients).

Testing for logged in users

The Security class comes with a static method for getting information about the current logged in user.

Security::getCurrentUser() retrieves the current logged in member. Returns null if user is not logged in, otherwise, the Member object is returned.

use SilverStripe\Security\Security;

$member = Security::getCurrentUser()
if ($member) {
    // Work with $member
} else {
    // Do non-member stuff
}

Subclassing

This is the least desirable way of extending the Member class. It's better to use Extension (see below).

You can define subclasses of Member to add extra fields or functionality to the built-in membership system.

namespace App\Security;

use SilverStripe\Security\Member;

class MyMember extends Member
{
    private static $db = [
        'Age' => 'Int',
        'Address' => 'Text',
    ];
}

To ensure that all new members are created using this class, put a call to Injector in (project)/_config/_config.yml:

SilverStripe\Core\Injector\Injector:
  SilverStripe\Security\Member:
    class: App\Security\MyMemberClass

Note that if you want to look this class-name up, you can call Injector::inst()->get('Member')->ClassName

Overriding getCMSFields()

If you override 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 object. You should generally start by calling $this->beforeUpdateCMSFields() and manipulate the FieldList from there.

namespace App\Security;

use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\TextField;
use SilverStripe\Security\Member;

class MyMember extends Member
{
    // ...

    public function getCMSFields()
    {
        $this->beforeUpdateCMSFields(function (FieldList $fields) {
            $fields->insertBefore('HTMLEmail', TextField::create('Age'));
            $fields->removeByName('JobTitle');
            $fields->removeByName('Organisation');
        });
        return parent::getCMSFields();
    }
}

Extending Member or DataObject

Basic rule: Class Member should just be extended for entities who have some kind of login. If you have different types of Members 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 Member to avoid conflicts with the Member database table. This enables us to have a different subclass of Member for an email-address with login-data, and another subclass for the same email-address in the address-database.

Member role extension

Using inheritance to add extra behaviour or data fields to a member is limiting, because you can only inherit from 1 class. A better way is to use role extensions to add this behaviour. Add the following to your config.yml.

SilverStripe\Security\Member:
  extensions:
    - App\Extension\MyMemberExtension

A role extension is simply a subclass of Extension that is designed to be used to add behaviour to Member. The roles affect the entire class - all members will get the additional behaviour. However, if you want to restrict things, you should add appropriate Permission::checkMember() calls to the role's methods.

namespace App\Extension;

use SilverStripe\Core\Extension;
use SilverStripe\Form\FieldList;
use SilverStripe\Security\Permission;

class MyMemberExtension extends Extension
{
    // define additional properties
    private static $db = [
        'MyNewField' => 'Text',
    ];

    /**
    * Modify the field set to be displayed in the CMS detail pop-up
    */
    protected 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
        }
    }

    public function somethingElse()
    {
        // You can add any other methods you like, which you can call directly on the member object.
    }
}

Saved user logins

Logins can be "remembered" across multiple devices when user checks the "Remember Me" box. By default, a new login token will be created and associated with the device used during authentication. When user logs out, all previously saved tokens for all devices will be revoked, unless RememberLoginHash::$logout_across_devices is set to false. For extra security, single tokens can be enforced by setting RememberLoginHash::$force_single_token to true. Tokens will be valid for 30 days by default and this can be modified via RememberLoginHash::$token_expiry_days.

Acting as another user

Occasionally, it may be necessary not only to check permissions of a particular member, but also to temporarily assume the identity of another user for certain tasks. For example when running a CLI task, it may be necessary to log in as an administrator to perform write operations.

You can use Member::actAs() method, which takes a member or member id to act as, and a callback within which the current user will be assigned the given member. After this method returns the current state will be restored to whichever current user (if any) was logged in.

If you pass in null as a first argument, you can also mock being logged out, without modifying the current user.

Note: Take care not to invoke this method to perform any operation the current user should not reasonably be expected to be allowed to do.

For example:

namespace App\Task;

use App\Model\DataRecord;
use SilverStripe\Dev\BuildTask;
use SilverStripe\PolyExecution\PolyOutput;
use SilverStripe\Security\Member;
use SilverStripe\Security\Security;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;

class CleanRecordsTask extends BuildTask
{
    private static bool $can_run_in_browser = false;

    protected function execute(InputInterface $input, PolyOutput $output): int
    {
        $admin = Security::findAnAdministrator();
        Member::actAs($admin, function () {
            DataRecord::get()->filter('Dirty', true)->removeAll();
        });
        return Command::SUCCESS;
    }
}

API documentation

Member