Skip to content

Commit

Permalink
Merge pull request #1 from lonnieezell/develop
Browse files Browse the repository at this point in the history
Update
  • Loading branch information
nControl88 authored Apr 14, 2020
2 parents 9e7d80d + 4c3f359 commit 80058c9
Show file tree
Hide file tree
Showing 13 changed files with 378 additions and 53 deletions.
28 changes: 15 additions & 13 deletions docs/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,26 +122,16 @@ and will describe the process here so that you can understand the flow.
If you do NOT want your users to be able to use persistent logins, you can turn this off in `app/Config/Auth.php`,
along with a number of other settings. See the section on [Configuration](#configuration), below.

If enabled, the remember-me tokens are checked automatically during the LocalAuthenticator's `check()` method.
No further action is need on your part.

### Security Flow

- When a user is set to be remembered, a Token is created that consists of a modified version of the user's email and a random 128-character, alpha-numeric string.
- The Token is saved to a cookie on the user's machine. This will later be used to identify the user when logging in automatically.
- The Token is then salted, hashed and stored in the database. The original token is then discarded and the system doesn't know anything about it anymore.
- When logging in automatically, the Token is retrieved from the cookie, salted and hashed and we attempt to find a match in the database.
- After automatic logins, the old tokens are discarded, both from the cookie and the database, and a new Token is generated and the process continues as described here.

### Automatic Logins
You can attempt to log a user in automatically, if they've been remembered, with the `viaRemember()` method. There are
no parameters. The method will return either true or false on success/fail.

if (! $auth->check() )
{
if (! $auth->viaRemember() )
{
$this->session->set('redirect_url', current_url() );
return redirect()->route('login');
}
}

## Removing All User's Persistent Logins
To allow a user to remove all login attempts associated with their email address, across all devices they might be
Expand Down Expand Up @@ -174,6 +164,18 @@ used during registration. We use the `Activator` service for this.
By default, we provide one type of activator and this is `EmailActivator`. You can also prepare your own activator,
which will e.g. use an SMS to confirm activation. There are many possibilities.

## Force a password reset

If you need to force a user to reset their password, you can use the `forcePasswordReset()` method on the User
entity to generate the required information on the model. This will then be checked during the LocalAuthenticator's
`check()` method, which is used by the AuthTrait and all Filters. At this point, the user will not be able to
proceed to any protected pages. You must save the changes through the UserModel before the changes will persist.

```
$user->forcePasswordReset();
$userModel->save($user);
```

## Configuration
Many aspects of the system can be configured in the `Config/Auth.php` config file. These options are described here.

Expand Down
50 changes: 50 additions & 0 deletions docs/events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Events Guide

The following events are thrown by the default configuration of Myth:Auth. These can be used to extend the
flow without having to modify the core files.

## Authentication

**login ($user)**

This is fired after a user successfully logs into the system. The User entity is passed in as the only argument.

**logout ($user)**

This is fired after a user has been logged out of the system. The user entity is the only argument.


## Authorization

**beforeAddUserToGroup ($userId, $groupId)**

Called before adding a user to the group. Returning false from your event action will stop the user from
being added to the group.

**didAddUserToGroup ($userId, $groupId)**

Called immediately after adding a user to a group.

**beforeRemoveUserFromGroup ($userId, $groupId)**

Called before removing a user from the group. Returning false from your event action will stop the user from
being removed.

**didRemoveUserFromGroup ($userId, $groupId)**

Called immediately after removing a user from a group.

**beforeAddPermissionToUser ($userId, $permissionId)**

Called before adding a permission to the user. Returning false from your event action will stop the permission from
being added.

**didAddPermissionToUser ($userId, $permissionId)**

Called immediately after adding a permission to a user.

**beforeRemovePermissionToUser ($userId, $permissionId)**

Called before removing a permission from the user. Returning false from your event action will stop execution.


9 changes: 8 additions & 1 deletion src/Authentication/LocalAuthenticator.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php namespace Myth\Auth\Authentication;

use CodeIgniter\Router\Exceptions\RedirectException;
use \Config\Services;
use Myth\Auth\Entities\User;
use Myth\Auth\Exceptions\AuthException;
Expand Down Expand Up @@ -66,6 +67,12 @@ public function check(): bool
{
if ($this->isLoggedIn())
{
// Do we need to force the user to reset their password?
if ($this->user && $this->user->force_pass_reset)
{
throw new RedirectException(route_to('reset-password') .'?token='.$this->user->reset_hash);
}

return true;
}

Expand All @@ -78,7 +85,7 @@ public function check(): bool
return false;
}

list($selector, $validator) = explode(':', $remember);
[$selector, $validator] = explode(':', $remember);
$validator = hash('sha256', $validator);

$token = $this->loginModel->getRememberToken($selector);
Expand Down
43 changes: 33 additions & 10 deletions src/Authorization/FlatAuthorization.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,13 @@ public function addUserToGroup(int $userid, $group)
return null;
}

if (! Events::trigger('beforeAddUserToGroup', $userid, $group))
$groupId = $this->getGroupID($group);

if (! Events::trigger('beforeAddUserToGroup', $userid, $groupId))
{
return false;
}

$groupId = $this->getGroupID($group);

// Group ID
if (! is_numeric($groupId))
{
Expand All @@ -183,7 +183,7 @@ public function addUserToGroup(int $userid, $group)
return false;
}

Events::trigger('didAddUserToGroup', $userid, $group);
Events::trigger('didAddUserToGroup', $userid, $groupId);

return true;
}
Expand All @@ -208,13 +208,13 @@ public function removeUserFromGroup(int $userId, $group)
return null;
}

if (! Events::trigger('beforeRemoveUserFromGroup', $userId, $group))
$groupId = $this->getGroupID($group);

if (! Events::trigger('beforeRemoveUserFromGroup', $userId, $groupId))
{
return false;
}

$groupId = $this->getGroupID($group);

// Group ID
if (! is_numeric($groupId))
{
Expand All @@ -228,7 +228,7 @@ public function removeUserFromGroup(int $userId, $group)
return false;
}

Events::trigger('didRemoveUserFromGroup', $userId, $group);
Events::trigger('didRemoveUserFromGroup', $userId, $groupId);

return true;
}
Expand Down Expand Up @@ -323,7 +323,7 @@ public function addPermissionToUser($permission, int $userId)
return null;
}

if (! Events::trigger('beforeAddPermissionToUser', $userId, $permission))
if (! Events::trigger('beforeAddPermissionToUser', $userId, $permissionId))
{
return false;
}
Expand All @@ -343,6 +343,8 @@ public function addPermissionToUser($permission, int $userId)
$this->permissionModel->addPermissionToUser($permissionId, $user->id);
}

Events::trigger('didAddPermissionToUser', $userId, $permissionId);

return true;
}

Expand Down Expand Up @@ -372,7 +374,7 @@ public function removePermissionFromUser($permission, int $userId)

$userId = (int)$userId;

if (! Events::trigger('beforeRemovePermissionFromUser', $userId, $permission))
if (! Events::trigger('beforeRemovePermissionFromUser', $userId, $permissionId))
{
return false;
}
Expand Down Expand Up @@ -703,4 +705,25 @@ protected function getPermissionID($permission)
return (int)$p->id;
}

/**
* Returns an array of all permissions in the system for a group
* The group can be either the ID or the name of the group.
*
* @param int|string $group
*
* @return mixed
*/
public function groupPermissions($group)
{
if (is_numeric($group))
{
return $this->groupModel->getPermissionsForGroup($group);
}
else
{
$g = $this->groupModel->where('name', $group)->first();
return $this->groupModel->getPermissionsForGroup($g->id);
}
}

}
31 changes: 31 additions & 0 deletions src/Authorization/GroupModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,37 @@ public function getGroupsForUser(int $userId)
// Permissions
//--------------------------------------------------------------------

/**
* Gets all permissions for a group in a way that can be
* easily used to check against:
*
* [
* id => name,
* id => name
* ]
*
* @param int $groupId
*
* @return array
*/
public function getPermissionsForGroup(int $groupId): array
{
$permissionModel = model(PermissionModel::class);
$fromGroup = $permissionModel
->select('auth_permissions.*')
->join('auth_groups_permissions', 'auth_groups_permissions.permission_id = auth_permissions.id', 'inner')
->where('group_id', $groupId)
->findAll();

$found = [];
foreach ($fromGroup as $permission)
{
$found[$permission['id']] = $permission;
}

return $found;
}

/**
* Add a single permission to a single group, by IDs.
*
Expand Down
Loading

0 comments on commit 80058c9

Please sign in to comment.