Users
, (*1)
Manages users of the CMS Icybee., (*2)
Permission and Ownership
The actions a user can perform are determined by the permission which are granted to him, or the
fact that he is the owner or not of a record., (*3)
Two methods are used to determine if a user has a specific permission, or if he is the owner of a
record. In order to best suit the many different situations, plugins are used to help., (*4)
Determine a user's permission
The method has_permission()
determines if a user has a specific permission. This permission can
be relative to a target, a module for instance., (*5)
The following example demonstrates how to verify is a user has the permission to update his own
profile, or if he has the permission to administer the module Users., (*6)
<?php
use ICanBoogie\Module;
if ($user->has_permission('update own profile')
|| $user->has_permission(Module::PERMISSION_ADMINISTER, $app->modules['users']))
{
// …
}
The assert_permission()
method throws a UserLacksPermission
exception if the user lacks a required permission., (*7)
<?php
use Icybee\Modules\Users\UserLacksPermission;
/* @var \Icybee\Modules\Users\User $user */
/* @var \ICanBoogie\Application $app */
/* @var string|int $permission */
$node = $app->models['nodes']->one;
try
{
$user->assert_permission($permission, $node);
// …
}
catch (UserLacksPermission $e)
{
var_dump($e->permission, $e->resource, $e->user);
}
Determine a user ownership
The method has_ownership()
determines if a user has ownership of a resource. Beware, ownership
does not mean permission! A user may own a resource and lack the permission to delete it., (*8)
The following example demonstrates how to verify if a user is the owner of a node., (*9)
<?php
$node = $app->models['nodes']->one;
if ($user->has_ownership($node))
{
// …
}
The assert_ownership()
method throws a UserLacksOwnership
exception if the user lacks ownership
of a resource., (*10)
<?php
use Icybee\Modules\Users\UserLacksOwnership;
/* @var \Icybee\Modules\Users\User $user */
/* @var \ICanBoogie\Application $app */
$node = $app->models['nodes']->one;
try
{
$user->assert_ownership($node);
// …
}
catch (UserLacksOwnership $e)
{
var_dump($e->resource, $e->user);
}
An open system
To better respond to the many different situation, an open system—based on the concept of
plugins—is used. With this system, a third party could provide support for shared ownership, which
is not supported by the module Users. Using this system, the module Roles provides support for
permissions associated with roles., (*11)
Resolvers are defined using the users
configuration., (*12)
Permission resolvers
Permission resolvers determine if a user has the required permission to perform an action.
For instance, the resolver defined by the module Roles determine the permission of a user
according to its roles and their associated permissions., (*13)
Permission resolvers are defined in the permission_resolver_list
array of the users
configuration. It can be a function or the name of a class implementing the
PermissionResolverInterface interface., (*14)
<?php
// …/config/users.php
return [
'permission_resolver_list' => [
'a_resolver' => 'Hooks::my_permission_resolver',
'another_resolver' => 'MyPermissionResolverClass'
]
];
Ownership resolvers
Ownership resolvers determine if a user is the owner of a record. The resolver defined by the
module regard a user as an owner of a record uid
property is not empty and matches the user's
identifier, or if the user is the admin., (*15)
<?php
namespace Icybee\Modules\Users;
use ICanBoogie\ActiveRecord;
function(User $user, ActiveRecord $record)
{
$uid = $user->uid;
if ($uid == 1 || (!empty($record->uid) && $record->uid == $uid))
{
return true;
}
}
Ownership resolvers are defined in the ownership_resolver_list
array of the users
configuration. It can be a function or the name of a class implementing the
OwnershipResolverInterface interface., (*16)
<?php
// …/config/users.php
return [
'ownership_resolver_list' => [
'a_resolver' => 'my_ownership_resolver',
'another_resolver' => 'MyOwnershipResolverClass'
]
];
Resolver weight
A weight can be specified for each resolver using the key weight
. This weight can be expressed
as a numeric value such as -10
and 10
, the special values top
and bottom
, or a position
relative to another resolver such as before:<resolver_id>
and after:<resolver_id>
,
where <resolver_id>
is the identifier of another resolver., (*17)
<?php
// users.roles/config/users.php
namespace Icybee\Modules\Users\Roles;
return [
'permission_resolver_list' => [
'roles' => [ PermissionResolver::class, 'weight' => 0 ]
],
'ownership_resolver_list' => [
'roles' => [ OwnershipResolver::class, 'weight' => 10 ]
]
];
In the following configuration, the weight of -10
allows the permission resolver defined
by "mymodule" to be positioned before the one defined by "roles". With the relative weight
before:roles
the ownership resolver of "mymodule" is positioned just before the one defined by
"roles"., (*18)
<?php
// mymodule/config/users.php
namespace App\Modules\MyModule;
return [
'permission_resolver_list' => [
'mymodule' => [ PermissionResolver::class, 'weight' => -10 ]
],
'ownership_resolver_list' => [
'mymodule' => [ OwnershipResolver::class, 'weight' => 'before:roles' ]
]
];
Calling resolvers
To determine if a user has a specific permission, the resolvers are invoked one by one, in order
to modify the permission value, which is initially set to false
. The value is modified if the
resolver returns something else than null
. Resolver generally only return true
if they
grant the permission, and null
otherwise. Indeed, a resolver returns false
only if it wants
to overwrite the response of a previous resolver., (*19)
The same process is used to determine if a user is the owner of a record., (*20)
Security
Preventing brute force login
In order to prevent brute force login, each failed attempt is counted in the metas of the target
user account. When the number of failed attempts reaches a limit (e.g. 10) the user account is
locked and a message with a key to unlock it is sent to the user's email address., (*21)
Once the message has been sent all subsequent connection requests will fail during an hour. After
this delay, the counter is reseted., (*22)
The following metas properties are used for the process:, (*23)
- (int)
failed_login_count
: The number of successive failed attempts. Reseted when the
user successfully login.
- (int)
failed_login_time
: Time of the last failed login.
- (string)
login_unlock_token
: Derivative salted token of the key sent by email for the user
to unlock its account.
- (int)
login_unlock_time
: Time at which login is unlocked.
Requirement
The package requires PHP 5.5 or later., (*24)
Installation
The recommended way to install this package is through Composer:, (*25)
$ composer require icybee/module-users
This module is part of the modules required by Icybee., (*26)
Cloning the repository
The package is available on GitHub, its repository can be
cloned with the following command line:, (*27)
$ git clone https://github.com/Icybee/module-users.git users
Documentation
The package is documented as part of the Icybee CMS
documentation. The documentation for the package and its
dependencies can be generated with the make doc
command. The documentation is generated in
the docs
directory using ApiGen. The package directory can later by
cleaned with the make clean
command., (*28)
Testing
The test suite is ran with the make test
command. PHPUnit and Composer need to be globally available to run the suite. The command installs dependencies as required. The make test-coverage
command runs test suite and also creates an HTML coverage report in "build/coverage". The directory can later be cleaned with the make clean
command., (*29)
The package is continuously tested by Travis CI., (*30)
, (*31)
License
icybee/module-users is licensed under the New BSD License - See the LICENSE file for details., (*32)