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 7.3+ and comes bundled with a Laravel 8 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 8.
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 "^5.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 "^5.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 8
The Sentinel package has optional support for Laravel 8 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 config/app.php
and add the following lines.
In the $providers
array add the following service provider for this package.
Cartalyst\Sentinel\Laravel\SentinelServiceProvider::class,
In the $aliases
array add the following facades for this package.
'Activation' => Cartalyst\Sentinel\Laravel\Facades\Activation::class,
'Reminder' => Cartalyst\Sentinel\Laravel\Facades\Reminder::class,
'Sentinel' => Cartalyst\Sentinel\Laravel\Facades\Sentinel::class,
Assets
Run the following command to publish the migrations and config file.
php artisan vendor:publish --provider="Cartalyst\Sentinel\Laravel\SentinelServiceProvider"
Migrations
Run the following command to migrate Sentinel after publishing the assets.
Note: Before running the following command, please remove the default Laravel migrations to avoid table collision.
php artisan migrate
Configuration
After publishing, the sentinel config file can be found under config/cartalyst.sentinel.php
where you can modify the package configuration.
Native
Sentinel being framework agnostic, by default, ships with the implementation for the Illuminate Database component.
In order to use it, make sure you require the necessary dependencies by running:
composer require illuminate/database illuminate/events symfony/http-foundation
// 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 and a third bool
argument of false
can be passed to disable automatic login.
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. |
$login | false | bool | true | Flag to disable automatic login. |
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
}
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 bool
or a Closure
, that when set to true
will automatically activate the user account or when a Closure
is passed the UserRepositoryInterface
for any aditional checks before creating the user, if the Closure
returns true
the user will be saved to the DB otherwise it will not be saved.
Returns: Cartalyst\Sentinel\Users\UserInterface
or false
.
Arguments
Key | Required | Type | Default | Description |
---|---|---|---|---|
$credentials | true | array | null | The user credentials. |
$callback | false | bool ; Closure | false | 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
.
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',
];
$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 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.
Returns: bool
.
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);
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. ExampleSentinel::findById(1)
instead ofSentinel::getUserRepository()->findById(1)
.Note 2 You can add the word
User
betweenfind
and the method name and drop thegetUserRepository
call. ExampleSentinel::findUserByCredentials($credentials)
instead ofSentinel::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.
Returns: bool
.
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
$credentials = [
'email' => 'john.doe@example.com',
'password' => 'password',
];
$user = Sentinel::findUserById(1);
$user = Sentinel::validateCredentials($user, $credentials);
Sentinel::validForCreation()
Validates a user for creation.
Returns: bool
.
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);
Sentinel::validForUpdate()
Validates a user for update.
Returns: bool
.
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);
Sentinel::create()
Creates a new user.
Returns: Cartalyst\Sentinel\Users\UserInterface
or null
.
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.
Returns: Cartalyst\Sentinel\Users\UserInterface
.
Arguments
Key | Required | Type | Default | Description |
---|---|---|---|---|
$user | true | Cartalyst\Sentinel\Users\UserInterface ; int | 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.
Returns: Cartalyst\Sentinel\Hashing\HasherInterface
.
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()
Check if the current user belongs to the given role.
Returns: bool
.
Arguments
Key | Required | Type | Default | Description |
---|---|---|---|---|
$role | true | Cartalyst\Sentinel\Roles\RoleInterface ; string | null | The role to check against. |
Example
$admin = Sentinel::inRole('admin');
Sentinel::createModel()
Creates a new user model instance.
Returns: Cartalyst\Sentinel\Users\EloquentUser
.
$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
betweenfind
and the method name and drop thegetRoleRepository
call. ExampleSentinel::findRoleBySlug($slug)
instead ofSentinel::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.
Returns: Cartalyst\Sentinel\Roles\EloquentRole
.
$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 theconfig
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
demonstratesPermission Inheritance
, which applies only when usingStandard Mode
(inheritance is disabled, by design, when usingStrict 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 theuser.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
andupdatePermission
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
andremovePermission
are chainable. Note 2: OnupdatePermission
, passingtrue
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()
orSentinel::hasAnyAccess()
directly which will call the methods on the currently logged in user, incase there's no user logged in, aBadMethodCallException
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: bool
.
Arguments
Key | Required | Type | Default | Description |
---|---|---|---|---|
$user | true | Cartalyst\Sentinel\Users\UserInterface | null | The Sentinel user object. |
$code | false | string | null | The activation code. |
Example
$user = Sentinel::findById(1);
$activation = Activation::exists($user);
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.
}
Activation::completed()
Check if activation has been completed for the user.
Returns: bool
.
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
}
Activation::remove()
Remove the activation for the user.
Returns: bool
.
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);
Activation::removeExpired()
Removes all the expired activations.
Returns: bool
.
Activation::removeExpired();
Activation::createModel()
Creates a new activation model instance.
Returns: Cartalyst\Sentinel\Activations\EloquentActivation
.
$activation = Activation::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()
Creates a new reminder record for the user.
Returns: Cartalyst\Sentinel\Reminders\EloquentReminder
.
Arguments
Key | Required | Type | Default | Description |
---|---|---|---|---|
$user | true | Cartalyst\Sentinel\Users\UserInterface | null | The Sentinel user object. |
$user = Sentinel::findById(1);
Reminder::create($user);
Reminder::exists()
Check if a reminder record exists for the user.
Returns: bool
.
Arguments
Key | Required | Type | Default | Description |
---|---|---|---|---|
$user | true | Cartalyst\Sentinel\Users\UserInterface | null | The Sentinel user object. |
$code | false | string | null | The code used for identifying the reminder. |
$user = Sentinel::findById(1);
Reminder::exists($user);
Reminder::complete()
Attempt to complete the password reset for the user using the code passed and the new password.
Returns: bool
.
Arguments
Key | Required | Type | Default | Description |
---|---|---|---|---|
$user | true | Cartalyst\Sentinel\Users\UserInterface | null | The Sentinel user object. |
$code | false | string | null | The code used for identifying the reminder. |
$password | true | string | null | The new password to use for the user. |
$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.
Returns: bool
.
Reminder::removeExpired();
Reminder::createModel()
Creates a new reminder model instance.
Returns: Cartalyst\Sentinel\Reminders\EloquentReminder
.
$reminder = Reminder::createModel();
Reminder::setModel()
Sets the reminder model.
Arguments
Key | Required | Type | Default | Description |
---|---|---|---|---|
$model | true | string | null | The new reminders 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()
Add a new checkpoint.
Arguments
Key | Required | Type | Default | Description |
---|---|---|---|---|
$key | true | string | null | The array key to use to identify the checkpoint. |
$checkpoint | true | Cartalyst\Sentinel\Checkpoints\CheckpointInterface | null | The checkpoint to add. |
Example
$checkpoint = new Your\Custom\Checkpoint;
Sentinel::addCheckpoint('your_checkpoint', $checkpoint);
Sentinel::removeCheckpoint();
Arguments
Key | Required | Type | Default | Description |
---|---|---|---|---|
$key | true | string | null | The array key to use to identify the checkpoint. |
Example
Sentinel::removeCheckpoint('activation');
Sentinel::enableCheckpoints()
Enable checkpoints.
Example
Sentinel::enableCheckpoints();
Sentinel::disableCheckpoints()
Disable checkpoints.
Example
Sentinel::disableCheckpoints();
Sentinel::checkpointsStatus()
Check whether checkpoints are enabled or disabled.
Example
$checkpoints = Sentinel::checkpointsStatus();
Sentinel::bypassCheckpoints()
Execute a closure that bypasses all checkpoints.
Bypass all checkpoints.
Returns: result of $callback
.
Arguments
Key | Required | Type | Default | Description |
---|---|---|---|---|
$callback | true | closure | null | Closure to use when bypassing checkpoints. |
$checkpoints | false | array | [] | Array of checkpoints to bypass. |
Example
$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 or newer'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.
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.