Skip to content

Commit eeebfc8

Browse files
authored
Merge pull request #535 from lonnieezell/add-filter-permission-and-group
Add filter permission and group
2 parents 58a9779 + b995529 commit eeebfc8

22 files changed

+372
-21
lines changed

docs/authorization.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
- [can()](#can)
1010
- [inGroup()](#ingroup)
1111
- [hasPermission()](#haspermission)
12+
- [Authorizing via Filters](#authorizing-via-filters)
13+
- [Authorizing via Routes](#authorizing-via-routes)
1214
- [Managing User Permissions](#managing-user-permissions)
1315
- [addPermission()](#addpermission)
1416
- [removePermission()](#removepermission)
@@ -128,6 +130,28 @@ if (! $user->hasPermission('users.create')) {
128130
}
129131
```
130132

133+
#### Authorizing via Filters
134+
135+
You can restrict access to multiple routes through a [Controller Filter](https://codeigniter.com/user_guide/incoming/filters.html). One is provided for both restricting via groups the user belongs to, as well as which permission they need. The filters are automatically registered with the system under the `group` and `permission` aliases, respectively. You can define the protections within `app/Config/Filters.php`:
136+
137+
```php
138+
public $filters = [
139+
'group:admin,superadmin' => ['before' => ['admin/*']],
140+
'permission:users.manage' => ['before' => ['admin/users/*']],
141+
];
142+
```
143+
144+
#### Authorizing via Routes
145+
146+
The filters can also be used on a route or route group level:
147+
148+
```php
149+
$routes->group('admin', ['filter' => 'group:admin,superadmin'], static function ($routes) {
150+
$routes->resource('users');
151+
});
152+
153+
```
154+
131155
## Managing User Permissions
132156

133157
Permissions can be granted on a user level as well as on a group level. Any user-level permissions granted will
@@ -199,7 +223,7 @@ $user->syncGroups('admin', 'beta');
199223

200224
#### getGroups()
201225

202-
Returns all groups this user is a part of.
226+
Returns all groups this user is a part of.
203227

204228
```php
205229
$user->getGroups();

docs/install.md

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -135,15 +135,32 @@ your project.
135135
1. Use InnoDB, not MyISAM.
136136

137137
## Controller Filters
138+
The [Controller Filters](https://codeigniter.com/user_guide/incoming/filters.html) you can use to protect your routes the shield provides are:
139+
140+
```php
141+
public $aliases = [
142+
// ...
143+
'session' => \CodeIgniter\Shield\Filters\SessionAuth::class,
144+
'tokens' => \CodeIgniter\Shield\Filters\TokenAuth::class,
145+
'chain' => \CodeIgniter\Shield\Filters\ChainAuth::class,
146+
'auth-rates' => \CodeIgniter\Shield\Filters\AuthRates::class,
147+
'group' => \CodeIgniter\Shield\Filters\GroupFilter::class,
148+
'permission' => \CodeIgniter\Shield\Filters\PermissionFilter::class,
149+
];
150+
```
151+
152+
Filters | Description
153+
--- | ---
154+
session and tokens | The `Session` and `AccessTokens` authenticators, respectively.
155+
chained | The filter will check both authenticators in sequence to see if the user is logged in through either of authenticators, allowing a single API endpoint to work for both an SPA using session auth, and a mobile app using access tokens.
156+
auth-rates | Provides a good basis for rate limiting of auth-related routes.
157+
group | Checks if the user is in one of the groups passed in.
158+
permission | Checks if the user has the passed permissions.
138159

139-
Shield provides 4 [Controller Filters](https://codeigniter.com/user_guide/incoming/filters.html) you can
140-
use to protect your routes, `session`, `tokens`, and `chained`. The first two cover the `Session` and
141-
`AccessTokens` authenticators, respectively. The `chained` filter will check both authenticators in sequence
142-
to see if the user is logged in through either of authenticators, allowing a single API endpoint to
143-
work for both an SPA using session auth, and a mobile app using access tokens. The fourth, `auth-rates`,
144-
provides a good basis for rate limiting of auth-related routes.
145160
These can be used in any of the [normal filter config settings](https://codeigniter.com/user_guide/incoming/filters.html?highlight=filter#globals), or [within the routes file](https://codeigniter.com/user_guide/incoming/routing.html?highlight=routs#applying-filters).
146161

162+
> **Note** These filters are already loaded for you by the registrar class located at `src/Config/Registrar.php`.
163+
147164
### Protect All Pages
148165

149166
If you want to limit all routes (e.g. `localhost:8080/admin`, `localhost:8080/panel` and ...), you need to add the following code in the `app/Config/Filters.php` file.
@@ -158,18 +175,6 @@ public $globals = [
158175
];
159176
```
160177

161-
> **Note** These filters are already loaded for you by the registrar class located at `src/Config/Registrar.php`.
162-
163-
```php
164-
public $aliases = [
165-
// ...
166-
'session' => \CodeIgniter\Shield\Filters\SessionAuth::class,
167-
'tokens' => \CodeIgniter\Shield\Filters\TokenAuth::class,
168-
'chain' => \CodeIgniter\Shield\Filters\ChainAuth::class,
169-
'auth-rates' => \CodeIgniter\Shield\Filters\AuthRates::class,
170-
];
171-
```
172-
173178
### Rate Limiting
174179

175180
To help protect your authentication forms from being spammed by bots, it is recommended that you use

src/Authorization/AuthorizationException.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,9 @@ public static function forUnknownPermission(string $permission): self
1919
{
2020
return new self(lang('Auth.unknownPermission', [$permission]));
2121
}
22+
23+
public static function forUnauthorized(): self
24+
{
25+
return new self(lang('Auth.notEnoughPrivilege'));
26+
}
2227
}

src/Config/Registrar.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
use CodeIgniter\Shield\Collectors\Auth;
99
use CodeIgniter\Shield\Filters\AuthRates;
1010
use CodeIgniter\Shield\Filters\ChainAuth;
11+
use CodeIgniter\Shield\Filters\GroupFilter;
12+
use CodeIgniter\Shield\Filters\PermissionFilter;
1113
use CodeIgniter\Shield\Filters\SessionAuth;
1214
use CodeIgniter\Shield\Filters\TokenAuth;
1315

@@ -24,6 +26,8 @@ public static function Filters(): array
2426
'tokens' => TokenAuth::class,
2527
'chain' => ChainAuth::class,
2628
'auth-rates' => AuthRates::class,
29+
'group' => GroupFilter::class,
30+
'permission' => PermissionFilter::class,
2731
],
2832
];
2933
}

src/Exceptions/GroupException.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CodeIgniter\Shield\Exceptions;
6+
7+
use CodeIgniter\Shield\Authorization\AuthorizationException;
8+
9+
class GroupException extends AuthorizationException
10+
{
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CodeIgniter\Shield\Exceptions;
6+
7+
use CodeIgniter\Shield\Authorization\AuthorizationException;
8+
9+
class PermissionException extends AuthorizationException
10+
{
11+
}

src/Filters/AbstractAuthFilter.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CodeIgniter\Shield\Filters;
6+
7+
use CodeIgniter\Filters\FilterInterface;
8+
use CodeIgniter\HTTP\RedirectResponse;
9+
use CodeIgniter\HTTP\RequestInterface;
10+
use CodeIgniter\HTTP\Response;
11+
use CodeIgniter\HTTP\ResponseInterface;
12+
13+
/**
14+
* Group Authorization Filter.
15+
*/
16+
abstract class AbstractAuthFilter implements FilterInterface
17+
{
18+
/**
19+
* Ensures the user is logged in and a member of one or
20+
* more groups as specified in the filter.
21+
*
22+
* @param array|null $arguments
23+
*
24+
* @return RedirectResponse|void
25+
*/
26+
public function before(RequestInterface $request, $arguments = null)
27+
{
28+
if (empty($arguments)) {
29+
return;
30+
}
31+
32+
if (! auth()->loggedIn()) {
33+
return redirect()->route('login');
34+
}
35+
36+
if ($this->isAuthorized($arguments)) {
37+
return;
38+
}
39+
40+
// If the previous_url is from this site, then
41+
// we can redirect back to it.
42+
if (strpos(previous_url(), site_url()) === 0) {
43+
return redirect()->back()->with('error', lang('Auth.notEnoughPrivilege'));
44+
}
45+
46+
// Otherwise, we'll just send them to the home page.
47+
return redirect()->to('/')->with('error', lang('Auth.notEnoughPrivilege'));
48+
}
49+
50+
/**
51+
* We don't have anything to do here.
52+
*
53+
* @param Response|ResponseInterface $response
54+
* @param array|null $arguments
55+
*/
56+
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null): void
57+
{
58+
// Nothing required
59+
}
60+
61+
abstract protected function isAuthorized(array $arguments): bool;
62+
}

src/Filters/GroupFilter.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CodeIgniter\Shield\Filters;
6+
7+
/**
8+
* Group Authorization Filter.
9+
*/
10+
class GroupFilter extends AbstractAuthFilter
11+
{
12+
/**
13+
* Ensures the user is logged in and a member of one or
14+
* more groups as specified in the filter.
15+
*/
16+
protected function isAuthorized(array $arguments): bool
17+
{
18+
return auth()->user()->inGroup(...$arguments);
19+
}
20+
}

src/Filters/PermissionFilter.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CodeIgniter\Shield\Filters;
6+
7+
/**
8+
* Permission Authorization Filter.
9+
*/
10+
class PermissionFilter extends AbstractAuthFilter
11+
{
12+
/**
13+
* Ensures the user is logged in and has one or more
14+
* of the permissions as specified in the filter.
15+
*/
16+
protected function isAuthorized(array $arguments): bool
17+
{
18+
foreach ($arguments as $permission) {
19+
if (auth()->user()->can($permission)) {
20+
return true;
21+
}
22+
}
23+
24+
return false;
25+
}
26+
}

src/Language/de/Auth.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
'invalidEmail' => 'Es konnte nicht überprüft werden, ob die E-Mail-Adresse mit der gespeicherten übereinstimmt.',
2020
'unableSendEmailToUser' => 'Leider gab es ein Problem beim Senden der E-Mail. Wir konnten keine E-Mail an "{0}" senden.',
2121
'throttled' => 'Es wurden zu viele Anfragen von dieser IP-Adresse gestellt. Sie können es in {0} Sekunden erneut versuchen.',
22+
'notEnoughPrivilege' => 'Sie haben nicht die erforderliche Berechtigung, um den gewünschten Vorgang auszuführen.',
2223

2324
'email' => 'E-Mail-Adresse',
2425
'username' => 'Benutzername',

0 commit comments

Comments
 (0)