Cartalyst LLC.
Sentinel by Cartalyst
30
352
22
137
973

Introduction

A modern and framework agnostic authorization and authentication package featuring roles, permissions, custom hashing algorithms and additional security features.

The package follows the FIG standard PSR-4 to ensure a high level of interoperability between shared PHP code.

The package requires PHP 5.4+ and comes bundled with a Laravel 4 Facade and a Service Provider to simplify the optional framework integration.

Have a read through the Installation Guide and on how to Integrate it with Laravel 4.

Create a user
Sentinel::register(array(
    'email'    => 'john.doe@example.com',
    'password' => 'foobar',
));
Authenticate a user
Sentinel::authenticate(array(
    'email'    => 'john.doe@example.com',
    'password' => 'foobar',
));

Features

Sentinel is a complete refactor of our popular Sentry authentication & authorization library. Everything you admired plus a whole lot more.

  • Authentication.
  • Authorization.
  • Registration.
  • Users & Roles Management.
  • Driver based permission system.
  • Flexible activation scenarios.
  • Reminders (password reset).
  • Inter-account throttling with DDoS protection.
  • Custom hashing strategies.
  • Multiple sessions.
  • Multiple login columns.
  • Integration with Laravel.
  • Allow use of multiple ORM implementations.
  • Native facade for easy usage outside Laravel.
  • Interface driven (your own implementations at will).

Sentry vs Sentinel

Feature Sentry Sentinel
Persistences Single Single/Multiple
Store additional data on persistences No Yes
Login attributes Single Multiple (ex. email, username)
Custom checkpoints No Yes
Custom hashing strategies No Yes
Driver-based permissions No Yes
Inter-account throttling with DDoS protection Basic Advanced

Installation

The best and easiest way to install Sentinel is with Composer.

If you have installed Composer globally run the following:

composer require cartalyst/sentinel "1.0.*"

Otherwise you'll have to manually download the composer.phar file:

curl -sS https://getcomposer.org/installer | php
php composer.phar require cartalyst/sentinel "1.0.*"

Now you are able to require the vendor/autoload.php file to autoload the package.

Integration

Cartalyst packages are framework agnostic and as such can be integrated easily natively or with your favorite framework.

Laravel 4

The Sentinel package has optional support for Laravel 4 and it comes bundled with a Service Provider and a Facade for easy integration.

After installing the package, open your Laravel config file located at app/config/app.php and add the following lines.

In the $providers array add the following service provider for this package.

'Cartalyst\Sentinel\Laravel\SentinelServiceProvider',

In the $aliases array add the following facades for this package.

'Activation' => 'Cartalyst\Sentinel\Laravel\Facades\Activation',
'Reminder'   => 'Cartalyst\Sentinel\Laravel\Facades\Reminder',
'Sentinel'   => 'Cartalyst\Sentinel\Laravel\Facades\Sentinel',

Migrations

Run the following command to migrate Sentinel.

php artisan migrate --package=cartalyst/sentinel

Configuration

After installing, you can publish the package configuration file into your application by running the following command on your terminal:

php artisan config:publish cartalyst/sentinel

This will publish the config file to app/config/packages/cartalyst/sentinel/config.php where you can modify the package configuration.

Native

Sentinel ships with default implementations for illuminate/database, in order to use it, make sure you require it on your composer.json file.

// Import the necessary classes
use Cartalyst\Sentinel\Native\Facades\Sentinel;
use Illuminate\Database\Capsule\Manager as Capsule;

// Include the composer autoload file
require 'vendor/autoload.php';

// Setup a new Eloquent Capsule instance
$capsule = new Capsule;

$capsule->addConnection([
    'driver'    => 'mysql',
    'host'      => 'localhost',
    'database'  => 'sentinel',
    'username'  => 'user',
    'password'  => 'secret',
    'charset'   => 'utf8',
    'collation' => 'utf8_unicode_ci',
]);

$capsule->bootEloquent();

The integration is done and you can now use all the available methods, here's an example:

// Register a new user
Sentinel::register([
    'email'    => 'test@example.com',
    'password' => 'foobar',
]);

Usage

Sentinel provides you all the tools you need to manage a role based authentication and authorization system.

Authentication

In this section, we will cover the Sentinel authentication methods.

Sentinel::authenticate()

This method authenticates a user against the given $credentials, additionally a second bool argument of true can be passed to set the remember state on the user.

Returns: Cartalyst\Sentinel\Users\UserInterface or false.

Arguments
Key Required Type Default Description
$credentials true array null The user credentials.
$remember false bool false Flag to set the remember cookie.
Example
$credentials = [
    'email'    => 'john.doe@example.com',
    'password' => 'password',
];

Sentinel::authenticate($credentials);
Example Response
{
    id: "1",
    email: "john.doe@example.com",
    permissions: {
        admin: true
    },
    last_login: {
        date: "2014-02-17 03:44:31",
        timezone_type: 3,
        timezone: "UTC"
    },
    first_name: "John",
    last_name: "Doe",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Sentinel::authenticateAndRemember()

This method authenticates and remembers the user, it's an alias fore the authenticate() method but it sets the $remember flag to true.

Returns: Cartalyst\Sentinel\Users\UserInterface or false.

Arguments
Key Required Type Default Description
$credentials true array null The user credentials.
Example
$credentials = [
    'email'    => 'john.doe@example.com',
    'password' => 'password',
];

Sentinel::authenticateAndRemember($credentials);
Example Response
{
    id: "1",
    email: "john.doe@example.com",
    permissions: {
        admin: true
    },
    last_login: {
        date: "2014-02-17 03:44:31",
        timezone_type: 3,
        timezone: "UTC"
    },
    first_name: "John",
    last_name: "Doe",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Sentinel::forceAuthenticate()

Authenticates a user bypassing all checkpoints.

Returns: Cartalyst\Sentinel\Users\UserInterface or false.

Arguments
Key Required Type Default Description
$credentials true array null The user credentials.
$remember false bool false Flag to set the remember cookie.
Example
$credentials = [
    'email'    => 'john.doe@example.com',
    'password' => 'password',
];

Sentinel::forceAuthenticate($credentials);
Example Response
{
    id: "1",
    email: "john.doe@example.com",
    permissions: {
        admin: true
    },
    last_login: {
        date: "2014-02-17 03:44:31",
        timezone_type: 3,
        timezone: "UTC"
    },
    first_name: "John",
    last_name: "Doe",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Sentinel::forceAuthenticateAndRemember()

Authenticates and remembers a user bypassing all checkpoints.

Returns: Cartalyst\Sentinel\Users\UserInterface or false.

Arguments
Key Required Type Default Description
$credentials true array null The user credentials.
Example
$credentials = [
    'email'    => 'john.doe@example.com',
    'password' => 'password',
];

Sentinel::forceAuthenticateAndRemember($credentials);
Example Response
{
    id: "1",
    email: "john.doe@example.com",
    permissions: {
        admin: true
    },
    last_login: {
        date: "2014-02-17 03:44:31",
        timezone_type: 3,
        timezone: "UTC"
    },
    first_name: "John",
    last_name: "Doe",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Sentinel::stateless()

Performs stateless authentication.

Returns: Cartalyst\Sentinel\Users\UserInterface or false.

Arguments
Key Required Type Default Description
$credentials true array null The user credentials.
Example
$credentials = [
    'email'    => 'john.doe@example.com',
    'password' => 'password',
];

if ($user = Sentinel::stateless($credentials))
{
    // Authentication successful and the user is assigned to the `$user` variable.
}
else
{
    // Authentication failed.
}
Example Response
{
    id: "1",
    email: "john.doe@example.com",
    permissions: {
        admin: true
    },
    last_login: {
        date: "2014-02-17 03:44:31",
        timezone_type: 3,
        timezone: "UTC"
    },
    first_name: "John",
    last_name: "Doe",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Sentinel::basic()

Authenticates using the HTTP basic auth.

Returns the auth response.

Example
return Sentinel::basic();

Authorization

In this section, we will cover authorization methods.

Sentinel::check()

Check if a user is logged in.

Returns: Cartalyst\Sentinel\Users\UserInterface or false.

Example
if ($user = Sentinel::check())
{
    // User is logged in and assigned to the `$user` variable.
}
else
{
    // User is not logged in
}
Example Response
{
    id: "1",
    email: "john.doe@example.com",
    permissions: {
        admin: true
    },
    last_login: {
        date: "2014-02-17 03:44:31",
        timezone_type: 3,
        timezone: "UTC"
    },
    first_name: "John",
    last_name: "Doe",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Sentinel::forceCheck()

Check if a user is logged in, bypassing all checkpoints.

Returns: Cartalyst\Sentinel\Users\UserInterface or false.

Example
if ($user = Sentinel::forceCheck())
{
    // User is logged in and assigned to the `$user` variable.
}
else
{
    // User is not logged in
}
Example Response
{
    id: "1",
    email: "john.doe@example.com",
    permissions: {
        admin: true
    },
    last_login: {
        date: "2014-02-17 03:44:31",
        timezone_type: 3,
        timezone: "UTC"
    },
    first_name: "John",
    last_name: "Doe",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Sentinel::guest()

Check if no user is currently logged in.

Returns true if the user is not logged in and false otherwise.

Example
if (Sentinel::guest())
{
    // User is not logged in
}
Example Response
true

Sentinel::getUser()

Retrieves the currently logged in user.

Returns: Cartalyst\Sentinel\Users\UserInterface or null.

Arguments
Key Required Type Default Description
$check false bool true A flag to instruct sentinel whether it should perform a check for a logged in user if it hasn't been checked yet on the given request.
Example
if ($user = Sentinel::getUser())
{
    // User is logged in and assigned to the `$user` variable.
}

Registration

In this section, we will cover registration methods.

Sentinel::register()

With this method you'll be able to register new users onto your application.

The first argument is a key/value pair which should contain the user login column name, the password and other attributes you see fit.

The second argument is a boolean, that when set to true will automatically activate the user account.

Arguments
Key Required Type Default Description
$credentials true array null The user credentials.
$callback false bool ; Closure null This argument is used for two things, either pass in true to activate the user or a Closure that would be executed before the user is created and can prevent user creation if it returns false.
Example
$credentials = [
    'email'    => 'john.doe@example.com',
    'password' => 'password',
];

$user = Sentinel::register($credentials);
Example Response
{
    email: "john.doe@example.com",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:01"
    id: 2
}

Sentinel::registerAndActivate()

This method registers and activates the user, it's an alias for the register() method but it sets the $callback flag to true.

Arguments
Key Required Type Default Description
$credentials true array null The user credentials.
Example
$credentials = [
    'email'    => 'john.doe@example.com',
    'password' => 'password',
];

$user = Sentinel::registerAndActivate($credentials);
Example Response
{
    email: "john.doe@example.com",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:01"
    id: 2
}

Login

In this section, we will cover login methods.

Sentinel::login()

This method logs the given a user in, additionally a second bool argument of true can be passed to set the remember state on the user.

Returns: Cartalyst\Sentinel\Users\UserInterface or false.

Arguments
Key Required Type Default Description
$user true Cartalyst\Sentinel\Users\UserInterface null The Sentinel user object.
$remember false bool false Flag to set the remember cookie.
Example
$user = Sentinel::findById(1);

Sentinel::login($user);
Example Response
{
    id: "1",
    email: "john.doe@example.com",
    permissions: {
        admin: true
    },
    last_login: {
        date: "2014-02-17 03:44:31",
        timezone_type: 3,
        timezone: "UTC"
    },
    first_name: "John",
    last_name: "Doe",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Sentinel::loginAndRemember()

This method logs and remembers the given user, it's an alias fore the login() method but it sets the $remember flag to true.

Returns: Cartalyst\Sentinel\Users\UserInterface or false.

Arguments
Key Required Type Default Description
$user true Cartalyst\Sentinel\Users\UserInterface null The Sentinel user object.
Example
$user = Sentinel::findById(1);

Sentinel::loginAndRemember($user);
Example Response
{
    id: "1",
    email: "john.doe@example.com",
    permissions: {
        admin: true
    },
    last_login: {
        date: "2014-02-17 03:44:31",
        timezone_type: 3,
        timezone: "UTC"
    },
    first_name: "John",
    last_name: "Doe",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Sentinel::logout()

Logs a user out, optionally can be passed a bool parameter true that will flush all active sessions for the user.

Arguments
Key Required Type Default Description
$user false Cartalyst\Sentinel\Users\UserInterface null The Sentinel user object.
$everywhere false bool false Flag for whether it should terminate all sessions.
Examples

Please refer to the examples below for different ways on terminate your users sessions.

Destroy the current logged in user session
Sentinel::logout();
Destroy all sessions for the current logged in user
Sentinel::logout(null, true);
Destroy the given user session
$user = Sentinel::findUserById(1);

Sentinel::logout($user);
Destroy all sessions for the given user
$user = Sentinel::findUserById(1);

Sentinel::logout($user, true);
Example Response
{
    id: "1",
    email: "john.doe@example.com",
    permissions: {
        admin: true
    },
    last_login: {
        date: "2014-02-17 03:44:31",
        timezone_type: 3,
        timezone: "UTC"
    },
    first_name: "John",
    last_name: "Doe",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Users

The user repository can be accessed using Sentinel::getUserRepository() and allows you to manage users using Sentinel.

Note 1 You can use the methods below directly on the Sentinel facade without the getUserRepository part. Example Sentinel::findById(1) instead of Sentinel::getUserRepository()->findById(1).

Note 2 You can add the word User between find and the method name and drop the getUserRepository call. Example Sentinel::findUserByCredentials($credentials) instead of Sentinel::getUserRepository()->findByCredentials($credentials).

Sentinel::findById()

Finds a user using it's id.

Returns: Cartalyst\Sentinel\Users\UserInterface or null.

Arguments
Key Required Type Default Description
$id true int null The user unique identifier.
Example
$user = Sentinel::findById(1);
Example Response
{
    id: "1",
    email: "john.doe@example.com",
    permissions: {
        admin: true
    },
    last_login: {
        date: "2014-02-17 03:44:31",
        timezone_type: 3,
        timezone: "UTC"
    },
    first_name: "John",
    last_name: "Doe",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Sentinel::findByCredentials()

Finds a user by it's credentials.

Returns: Cartalyst\Sentinel\Users\UserInterface or null.

Arguments
Key Required Type Default Description
$credentials true array null The user credentials.
Example
$credentials = [
    'login' => 'john.doe@example.com',
];

$user = Sentinel::findByCredentials($credentials);
Example Response
{
    id: "1",
    email: "john.doe@example.com",
    permissions: {
        admin: true
    },
    last_login: {
        date: "2014-02-17 03:44:31",
        timezone_type: 3,
        timezone: "UTC"
    },
    first_name: "John",
    last_name: "Doe",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Sentinel::findByPersistenceCode()

Finds a user by persistence code.

Returns: Cartalyst\Sentinel\Users\UserInterface or null.

Arguments
Key Required Type Default Description
$code true string null The persistence code.
Example
$user = Sentinel::findByPersistenceCode('persistence_code_here');
Example Response
{
    id: "1",
    email: "john.doe@example.com",
    permissions: {
        admin: true
    },
    last_login: {
        date: "2014-02-17 03:44:31",
        timezone_type: 3,
        timezone: "UTC"
    },
    first_name: "John",
    last_name: "Doe",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Sentinel::validateCredentials()

Validates the user credentials.

This is useful when you want to verify if the current user password matches the given password.

Arguments
Key Required Type Default Description
$credentials true array null The user credentials.
Example
$credentials = [
    'email'    => 'john.doe@example.com',
    'password' => 'password',
];

$user = Sentinel::findUserById(1);

$user = Sentinel::validateCredentials($user, $credentials);
Example Response
true

Sentinel::validForCreation()

Validates a user for creation.

Arguments
Key Required Type Default Description
$credentials true array null The user credentials.
Example
$credentials = [
    'email'    => 'john.doe@example.com',
    'password' => 'password',
];

$user = Sentinel::validForCreation($credentials);
Example Response
true

Sentinel::validForUpdate()

Validates a user for update.

Arguments
Key Required Type Default Description
$user true Cartalyst\Sentinel\Users\UserInterface null The Sentinel user object.
$credentials true array null The user credentials.
Example
$user = Sentinel::findById(1);

$credentials = [
    'email' => 'johnathan.doe@example.com',
];

$user = Sentinel::validForUpdate($user, $credentials);
Example Response
true

Sentinel::create()

Creates a new user.

Arguments
Key Required Type Default Description
$credentials true array null The user credentials.
$callback false Closure null A Closure that would be executed before the user is created and can prevent user creation if it returns false.
Example
$credentials = [
    'email'    => 'john.doe@example.com',
    'password' => 'password',
];

$user = Sentinel::create($credentials);
Example Response
{
    id: "1",
    email: "john.doe@example.com",
    permissions: {
        admin: true
    },
    last_login: {
        date: "2014-02-17 03:44:31",
        timezone_type: 3,
        timezone: "UTC"
    },
    first_name: "John",
    last_name: "Doe",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Sentinel::update()

Updates an existing user.

Arguments
Key Required Type Default Description
$user true Cartalyst\Sentinel\Users\UserInterface null The Sentinel user object.
$credentials true array null The user credentials.
Example
$user = Sentinel::findById(1);

$credentials = [
    'email' => 'new.john.doe@example.com',
];

$user = Sentinel::update($user, $credentials);
Example Response
{
    id: "1",
    email: "john.doe@example.com",
    permissions: {
        admin: true
    },
    last_login: {
        date: "2014-02-17 03:44:31",
        timezone_type: 3,
        timezone: "UTC"
    },
    first_name: "John",
    last_name: "Doe",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

$user->delete()

A user object can be deleted by calling eloquent's delete method on the user object. All related records for that specific user will be deleted as well.

Example
$user = Sentinel::findById(1);

$user->delete();

Sentinel::getHasher()

Returns the current hasher.

Example
$hasher = Sentinel::getHasher();

Sentinel::setHasher()

Sets the hasher.

Arguments
Key Required Type Default Description
$hasher true Cartalyst\Sentinel\Hashing\HasherInterface null The hasher object.
Example
Sentinel::setHasher(new Cartalyst\Sentinel\Hashing\WhirlpoolHasher);

Sentinel::inRole($role)

Check if the current user belongs to the given role.

Arguments
Key Required Type Default Description
$role true string null The role to check against.
Example
$admin = Sentinel::inRole('admin');

Sentinel::createModel()

Creates a new user model instance.

$user = Sentinel::createModel();

Sentinel::setModel()

Sets the user model.

Your new model needs to extend the Cartalyst\Sentinel\Users\EloquentUser class.

Arguments
Key Required Type Default Description
$model true string null The users model class name.
Example
Sentinel::setModel('Acme\Models\User');

Roles

The role repository can be accessed using Sentinel::getRoleRepository() and allows you to manage roles using Sentinel.

Note You can add the word Role between find and the method name and drop the getRoleRepository call. Example Sentinel::findRoleBySlug($slug) instead of Sentinel::getRoleRepository()->findBySlug($slug).

Sentinel::findRoleById()

Finds a role by its ID.

Returns: Cartalyst\Sentinel\Roles\RoleInterface or null.

Arguments
Key Required Type Default Description
$id true int null The role unique identifier.
Example
$role = Sentinel::findRoleById(1);
Example Response
{
    id: "1",
    slug: "admin",
    name: "Admin",
    permissions: {
        admin: true
    },
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Sentinel::findRoleBySlug()

Finds a role by its slug.

Returns: Cartalyst\Sentinel\Roles\RoleInterface or null.

Arguments
Key Required Type Default Description
$slug true string null The role slug.
Example
$role = Sentinel::findRoleBySlug('admin');
Example Response
{
    id: "1",
    slug: "admin",
    name: "Admin",
    permissions: {
        admin: true
    },
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Sentinel::findRoleByName()

Finds a role by its name.

Returns: Cartalyst\Sentinel\Roles\RoleInterface or null.

Arguments
Key Required Type Default Description
$name true string null The role name.
Example
$role = Sentinel::findRoleByName('Admin');
Example Response
{
    id: "1",
    slug: "admin",
    name: "Admin",
    permissions: {
        admin: true
    },
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Sentinel::getRoleRepository()->createModel()

Creates a new role model instance.

$role = Sentinel::getRoleRepository()->createModel();

Sentinel::getRoleRepository()->setModel()

Sets the role model.

Your new model needs to extend the Cartalyst\Sentinel\Roles\EloquentRole class.

Arguments
Key Required Type Default Description
$model true string null The roles model class name.
Example
Sentinel::getRoleRepository()->setModel('Acme\Models\Role');

Create a new role.

Create a new role.

Arguments
Key Required Type Default Description
$attributes true array null The role attributes.
Example
$role = Sentinel::getRoleRepository()->createModel()->create([
    'name' => 'Subscribers',
    'slug' => 'subscribers',
]);
Example Response
{
    name: "Subscribers",
    slug: "subscribers",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37",
    id: 2
}

Assign a user to a role.

$user = Sentinel::findById(1);

$role = Sentinel::findRoleByName('Subscribers');

$role->users()->attach($user);

Remove a user from a role.

$user = Sentinel::findById(1);

$role = Sentinel::findRoleByName('Subscribers');

$role->users()->detach($user);

Permissions

Permissions can be broken down into two types and two implementations. Depending on the used implementation, these permission types will behave differently.

  • Role Permissions
  • User Permissions

Standard - This implementation will give the user-based permissions a higher priority and will override role-based permissions. Any permissions granted/rejected on the user will always take precendece over any role-based permissions assigned.

Strict - This implementation will reject a permission as soon as one rejected permission is found on either the user or any of the assigned roles. Granting a user a permission that is rejected on a role he is assigned to will not grant that user this permission.

Role-based permissions that define the same permission with different access rights will be rejected in case of any rejections on any role.

If a user is not assigned a permission, the user will inherit permissions from the role. If a user is assigned a permission of false or true, then the user's permission will override the role permission.

Note The permission type is set to StandardPermissions by default; it can be changed on the config file.

Administrator Role
{
    "name" : "Administrator",
    "permissions" : {
        "user.create" : true,
        "user.delete" : true,
        "user.view"   : true,
        "user.update" : true
    }
}
Moderator Role
{
    "name" : "Moderator",
    "permissions" : {
        "user.create" : false,
        "user.delete" : false,
        "user.view"   : true,
        "user.update" : true
    }
}

And you have these three users, one as an Administrator, one as a Moderator and the last one has both the Administrator and Moderator roles assigned.

User - John Doe
{
    "id" : 1,
    "first_name" : "John",
    "last_name" : "Doe",
    "roles" : ["administrator"],
    "permissions" : null
}

This user has access to everything and can execute every action on your application.

User - Jane Smith
{
    "id" : 2,
    "first_name" : "Jane",
    "last_name" : "Smith",
    "roles" : ["moderator"],
    "permissions" : {
        "user.update" : false
    }
}
  • Can view users.
  • Cannot create, update or delete users.

Note: The use of user.update : false demonstrates Permission Inheritance, which applies only when using Standard Mode (inheritance is disabled, by design, when using Strict Mode). When a permission is defined at the user-level, it overrides the same permission that is defined on the role. Given the above example, the user will be denied the user.update permission, even though the permission is allowed on the role.

User - Bruce Wayne
{
    "id" : 3,
    "first_name" : "Bruce",
    "last_name" : "Wayne",
    "roles" : ["administrator", "moderator"],
    "permissions" : {
        "user.create" : true
    }
}
  • Can create, update and view users.
  • Cannot execute delete users.

This is a special user, mainly because this user has two roles assigned. There are some things that you should know when assigning multiple roles to a user.

When a user has two or more roles assigned, if those roles define the same permissions but they have different values (e.g., one role grants the creation of users and the other role denies it), once any of those role permissions are denied, the user will be denied access to that permission, no matter what the other roles have as a permission value and no matter which permission type (standard or strict) is being used.

This means that for you to allow a permission for this specific user, you have to be using standard permissions and you have to change the user permission to grant access.

Usage

Permissions live on permissible models, users and roles.

You can add, modify, update or delete permissions directly on the objects.

Storing Permissions

Permissions can either be stored as associative arrays on the Eloquent user or role by assigning it to the permissions attribute or using designated permission methods which make the process easier.

Array

Grant the user user.create and reject user.delete.

$user = Sentinel::findById(1);

$user->permissions = [
    'user.create' => true,
    'user.delete' => false,
];

$user->save();

Grant the role user.update and user.view permissions.

$role = Sentinel::findRoleById(1);

$role->permissions = [
    'user.update' => true,
    'user.view' => true,
];

$role->save();

Designated methods

Note addPermission and updatePermission will default to true, calling addPermission('x') will grant the user or role that permission, passing false as a second parameter will deny that permission.

Grant the user user.create and reject user.update.

$user = Sentinel::findById(1);

$user->addPermission('user.create');
$user->addPermission('user.update', false);

$user->save();

Remove user.delete from the user.

Note Removing a permission does not explicitly mean rejection, it will fallback to permission inheritance.

$user = Sentinel::findById(1);

$user->removePermission('user.delete')->save();

Update existing user.create and reject user.update

$role = Sentinel::findRoleById(1);

$role->updatePermission('user.create');
$role->updatePermission('user.update', false, true)->save();

Note 1: addPermission, updatePermission and removePermission are chainable. Note 2: On updatePermission, passing true as a third argument will create the permission if it does not already exist.

Checking for Permissions

Permissions checks can be conducted using one of two methods.

Both methods can receive an argument of either a single permission passed as a string or an array of permissions.

hasAccess

This method will strictly require all passed permissions to be true in order to grant access.

This test will require both user.create and user.update to be true in order for permissions to be granted.

$user = Sentinel::findById(1);

if ($user->hasAccess(['user.create', 'user.update']))
{
    // Execute this code if the user has permission
}
else
{
    // Execute this code if the permission check failed
}
hasAnyAccess

This method will grant access if any permission passes the check.

This test will require only one permission of user.admin and user.create to be true in order for permissions to be granted.

if (Sentinel::hasAnyAccess(['user.admin', 'user.update']))
{
    // Execute this code if the user has permission
}
else
{
    // Execute this code if the permission check failed
}

Note You can use Sentinel::hasAccess() or Sentinel::hasAnyAccess() directly which will call the methods on the currently logged in user, incase there's no user logged in, a BadMethodCallException will be thrown.

Wildcard Checks

Permissions can be checked based on wildcards using the * character to match any of a set of permissions.

$user = Sentinel::findById(1);

if ($user->hasAccess('user.*'))
{
    // Execute this code if the user has permission
}
else
{
    // Execute this code if the permission check failed
}

Controller Based Permissions

You can easily implement permission checks based on controller methods, consider the following example implemented as a Laravel filter.

Permissions can be stored as action names on users and roles, then simply perform checks on the action before executing it and redirect on failure with an error message.

Route::filter('permissions', function($route, $request)
{
    $action = $route->getActionName();

    if (Sentinel::hasAccess($action))
    {
        return;
    }

    return Redirect::to('/')->withErrors('Permission denied.');
});

Activation

Activation allows you to manage activations through Sentinel.

Activation::create()

Creates a new activation record for the user.

Returns: Cartalyst\Sentinel\Activations\EloquentActivation.

Arguments
Key Required Type Default Description
$user true Cartalyst\Sentinel\Users\UserInterface null The Sentinel user object.
Example
$user = Sentinel::findById(1);

$activation = Activation::create($user);
Example Response
{
    code: "HNjOSGWoVHCNx70UAnbphnAJVIttFvot",
    user_id: "1",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37",
    id: 1
}

Activation::exists()

Check if an activation record exists for the user.

Returns: Cartalyst\Sentinel\Activations\EloquentActivation or false.

Arguments
Key Required Type Default Description
$user true Cartalyst\Sentinel\Users\UserInterface null The Sentinel user object.
Example
$user = Sentinel::findById(1);

$activation = Activation::exists($user);
Example Response
{
    id: "1",
    user_id: "1",
    code: "HNjOSGWoVHCNx70UAnbphnAJVIttFvot",
    completed: false,
    completed_at: null,
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Activation::complete()

Attempt to complete activation for the user using the code passed.

Returns: bool

Arguments
Key Required Type Default Description
$user true Cartalyst\Sentinel\Users\UserInterface null The Sentinel user object.
$code true string null The activation code.
Example
$user = Sentinel::findById(1);

if (Activation::complete($user, 'activation_code_here'))
{
    // Activation was successfull
}
else
{
    // Activation not found or not completed.
}
Example Response
true

Activation::completed()

Check if activation has been completed for the user.

Returns: Cartalyst\Sentinel\Activations\EloquentActivation or false.

Arguments
Key Required Type Default Description
$user true Cartalyst\Sentinel\Users\UserInterface null The Sentinel user object.
Example
$user = Sentinel::findById(1);

if ($activation = Activation::completed($user))
{
    // User has completed the activation process
}
else
{
    // Activation not found or not completed
}
Example Response
{
    id: "1",
    user_id: "1",
    code: "HiaVCzyLb6XFeZcVFpfUlCoLGZfhddHs",
    completed: true,
    completed_at: "2014-02-17 02:44:13",
    created_at: "2014-02-17 02:43:01",
    updated_at: "2014-02-17 02:43:37"
}

Activation::remove()

Remove the activation for the user.

Returns: true or null.

Arguments
Key Required Type Default Description
$user true Cartalyst\Sentinel\Users\UserInterface null The Sentinel user object.
Example
$user = Sentinel::findById(1);

Activation::remove($user);
Example Response
true

Activation::removeExpired()

Removes all the expired activations.

Activation::removeExpired();

Activation::createModel()

Creates a new activation model instance.

$activation = Activate::createModel();

Activation::setModel()

Sets the activation model.

Arguments
Key Required Type Default Description
$model true string null The new activation model.
Example
Activation::setModel('Your\Activation\Model');

Reminder

Reminder allows you to manage reminders through Sentinel.

Reminder::create($user)

Creates a new reminder record for the user.

Returns the reminder object.

$user = Sentinel::findById(1);

Reminder::create($user);

Reminder::exists($user)

Check if a reminder record exists for the user.

Returns the reminder object or bool.

$user = Sentinel::findById(1);

Reminder::exists($user);

Reminder::complete($user, $code, $password)

Attempt to complete the password reset for the user using the code passed and the new password.

Returns bool.

$user = Sentinel::findById(1);

if ($reminder = Reminder::complete($user, 'reminder_code_here', 'new_password_here'))
{
    // Reminder was successfull
}
else
{
    // Reminder not found or not completed.
}

Reminder::removeExpired()

Remove all expired reminders.

Reminder::removeExpired();

Reminder::createModel()

Creates a new reminder model instance.

$reminder = Reminder::createModel();

Reminder::setModel($model)

Sets the reminder model.

Reminder::setModel('Your\Reminder\Model');

Throttle

There are three types of throttling.

  • global throttling will monitor the overall failed login attempts across your site and can limit the affects of an attempted DDoS attack.
  • ip throttling allows you to throttle the failed login attempts (across any account) of a given IP address.
  • user throttling allows you to throttle the login attempts on an individual user account.

Each type of throttling has the same options. The first is the interval, this is the time (in seconds) for which we check for failed logins. Any logins outside this time are no longer assessed when throttling.

The second option is thresholds, this may be approached using one of two ways.

  • The first way, is by providing a key/value array, the key is the number of failed login attempts, and the value is the delay in seconds before the next attempt can occur.
  • The second way is by providing an integer, if the number of failed login attempts outweigh the thresholds integer, that throttle is locked until there are no more failed login attempts within the specified interval.

On this premise, we encourage you to use array thresholds for global throttling (and perhaps IP throttling as well), so as to not lock your whole site out for minutes on end because it's being DDoS'd. However, for user throttling, locking a single account out because somebody is attempting to breach it could be an appropriate response.

You may use any type of throttling for any scenario, and the specific configurations are designed to be customized as your site grows.

Exceptions

  • Cartalyst\Sentinel\Checkpoints\ThrottlingException
Methods Parameters Description
setDelay Cartalyst\Sentinel\Users\UserInterface $user Sets a user object on the exception.
getDelay .. Retrieves the user object that caused the exception.
setType string $type Sets a user object on the exception.
getType .. Retrieves the user object that caused the exception.
getFree .. Retrieves time the throttle is lifted.

Checkpoints

Checkpoints can be referred to as security gates, the authentication process has to successfully pass through every single gate defined in order to be granted access.

By default, when logging in, checks for existing sessions and failed logins occur, you may configure an indefinite number of "checkpoints".

These are classes which may respond to each event and handle accordingly. We ship with two, an activation checkpoint and a throttle checkpoint.

Note Checkpoints must implement Cartalyst\Sentinel\Checkpoints\CheckpointInterface.

Feel free to add, remove or re-order these.

Activation

The activation checkpoint is responsible for validating the login attempt against the activation checkpoint to make sure the user is activated prior to granting access to a specific area.

Throttle

The throttle checkpoint is responsible for validating the login attempts against the defined throttling rules.

Usage

Functions

Sentinel::addCheckpoint($key, $checkpoint)

Add a new checkpoint.

$checkpoint = new Your\Custom\Checkpoint;

Sentinel::addCheckpoint('your_checkpoint', $checkpoint);
Sentinel::removeCheckpoint($key);
Sentinel::removeCheckpoint('activation');
Sentinel::enableCheckpoints()

Enable checkpoints.

Sentinel::enableCheckpoints();
Sentinel::disableCheckpoints()

Disable checkpoints.

Sentinel::disableCheckpoints();
Sentinel::checkpointsStatus()

Check whether checkpoints are enabled or disabled.

$checkpoints = Sentinel::checkpointsStatus();
Sentinel::bypassCheckpoints($callback, $checkpoints)

Execute a closure that bypasses all checkpoints.

Bypass all checkpoints.

$callback = function($sentinel)
{
    return $sentinel->check();
};

return Sentinel::bypassCheckpoints($callback);

Bypass specific checkpoints.

$callback = function($sentinel)
{
    return $sentinel->check();
};

return Sentinel::bypassCheckpoints($callback, ['activation']);

Hashing

By default, Sentinel encourages the sole use of the native PHP 5.5 hashing standard, password_hash(). Sentinel requires no configuration to use this method.

While it is not encouraged for security reasons, we provide functionality to override the hashing strategy used by Sentinel so as to accomodate for legacy applications moving forward.

There are 5 built in hashers:

Native Hasher

The encouraged hasher to use in Sentinel is the native hasher. It will use PHP 5.5's password_hash() function and is setup to use the most secure hashing strategy of the day (which is current bcrypt). There is no setup required for this hasher.

The native hasher can be used with PHP 5.4 by adding the ircmaxell/password-compat package to your composer.json file.

Bcrypt Hasher

The Bcrypt hasher uses the Bcrypt hashing algorithm. It is a safe algorithm to use, however this hasher has been deprecated in favor of the native hasher as it provides a uniform API to whatever the chosen hashing strategy of the day is.

To use the Bcrypt hasher:

// Native PHP
$sentinel->setHasher(new Cartalyst\Sentinel\Hashing\BcryptHasher);

// In Laravel
Sentinel::setHasher(new Cartalyst\Sentinel\Hashing\BcryptHasher);

Callback Hasher

The callback hasher is a strategy which allows you to define the methods used to hash a value and in-turn check the hashed value. This is particularly useful when upgrading from legacy systems, which may use one or more hashing strategies. It will allow you to write logic that accounts for old strategies and new strategies, as seen in the example below.

Be extremely careful that you don't expose vulnerabilities in your system by designing a hashing strategy that is unsafe to use.

To use the callback hasher:

$hasher = function($value)
{
    return password_hash($value, PASSWORD_DEFAULT);
};

$checker = function($value, $hashedValue)
{
    // Try use the safe password_hash() function first, as all newly hashed passwords will use this
    if (password_verify($value, $hashedValue))
    {
        return true;
    }

    // Because we're upgrading from a legacy system, we'll check if the hash is an old one and therefore allow us to log the person in anyway
    return some_method_to_check_a_hash($value, $hashedValue);
}

// Native PHP
$sentinel->setHasher(new Cartalyst\Sentinel\Hashing\CallbackHasher($hasher, $checker));

// In Laravel
Sentinel::setHasher(new Cartalyst\Sentinel\Hashing\CallbackHasher($hasher, $checker));

Other Hashers

Other hashers, such as the whirlpool hasher and the SHA256 hasher are supported by Sentinel, however we do not encourage their use as these algorithms are open to vulnerabilities. We would encourage people to use the callback hasher and implement their own logic for moving away from such systems.

We understand that not every system needs to move away from these strategies however. Telling Sentinel to use these strategies is straight forward:

// Native PHP
$sentinel->setHasher(new Cartalyst\Sentinel\Hashing\WhirlpoolHasher);
$sentinel->setHasher(new Cartalyst\Sentinel\Hashing\Sha256Hasher);

// In Laravel
Sentinel::setHasher(new Cartalyst\Sentinel\Hashing\WhirlpoolHasher);
Sentinel::setHasher(new Cartalyst\Sentinel\Hashing\Sha256Hasher);

Addons

Addons extends the functionality and features of Sentinel.

Sentinel Expired-Permissions

Sentinel Expired-Permissions is a Sentinel addon that allows you to set expiration periods on permissions.

Sentinel Expired-Permissions is under construction and will be announced at a later date.

Sentinel Multi-Tenancy

Sentinel Multi-Tenancy is a Sentinel addon that provides a standard Multi-tenancy implementation.

Sentinel Multi-Tenancy is under construction and will be announced at a later date.

Sentinel Oauth-Server

Sentinel Oauth-Server is a Sentinel addon that provides an OAuth server implementation.

Sentinel Oauth-Server is under construction and will be announced at a later date.

Sentinel Social

Sentinel Social is a Sentinel addon that provides an OAuth 1/2 Provider implementation.

Learn more about Sentinel Social.

Sentinel Unique-Passwords

Sentinel Unique-Passwords is a Sentinel addon that prevents users from setting the same password more than once.

Learn more about Sentinel Unique-Passwords.

You wont find fancy lifestyle graphics and marketing bravado here. Just cold... hard... code...

Code Well, Rock On!
Processing Payment...