Skip to content
This repository was archived by the owner on Sep 2, 2020. It is now read-only.

Commit 0602b9b

Browse files
authored
feat(Guard): Add a guard service that can authorize anywhere during the request
The `Guard@cbguard` service can be used anywhere during the request (but usually in a handler) to authorize a user against a `permission` and an optional struct of `additionalArgs`. This does not change any existing aspects of cbguard. In the future, a DSL may be introduced that allows passing `additionalArgs` via the `secured` annotation. Additionally, you can define custom guards using the `define` method. Custom guards, when defined, are used in place of the `hasPermission` method on the user. It can be defined using a closure/UDF, component, or WireBox mapping and allows you to better handle custom authorization rules.
2 parents bce4472 + 36c6f63 commit 0602b9b

File tree

10 files changed

+590
-4
lines changed

10 files changed

+590
-4
lines changed

README.md

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,80 @@ component secured {
8484

8585
While the user needs to be logged in to interact at all with this handler, they also need the `create_posts` permission to interact with the `new` action.
8686

87+
### Service approach
88+
89+
`cbguard` also allows you to check for authorization at any point in the request lifecycle using the `Guard@cbguard` component.
90+
91+
```cfc
92+
component secured {
93+
94+
property name="guard" inject="@cbguard";
95+
96+
function update( event, rc, prc ) {
97+
var post = getInstance( "Post" ).findOrFail( rc.post );
98+
99+
// this will throw a `NotAuthorized` exception if the user cannot update the post
100+
guard.authorize( "update-post", { "post": post } );
101+
102+
// update the post as normal...
103+
}
104+
105+
}
106+
```
107+
108+
The methods available to you on the `Guard` component are as follows:
109+
110+
```cfc
111+
public boolean function allows( required any permissions, struct additionalArgs = {} );
112+
public boolean function denies( required any permissions, struct additionalArgs = {} );
113+
public boolean function all( required any permissions, struct additionalArgs = {} );
114+
public boolean function none( required any permissions, struct additionalArgs = {} );
115+
public void function authorize( required any permissions, struct additionalArgs = {}, string errorMessage );
116+
```
117+
118+
In all cases `permissions` can be either a string, a list of strings, or an array of strings.
119+
120+
In the case of `authorize` the `errorMessage` replaces the thrown error message
121+
in the `NotAuthorized`. exception. It can also be a closure that takes the following shape:
122+
123+
```cfc
124+
string function errorMessage( array permissions, any user, struct additionalArgs );
125+
```
126+
127+
#### Defining Custom Guards
128+
129+
While handling all of your guard clauses inside the `hasPermission` method on your user
130+
works fine, you may want to define a different way to handle permissions. You
131+
can do this by declaring custom guards using the `guard.define` method. Here's the signature:
132+
133+
```cfc
134+
public Guard function define( required string name, required any callback );
135+
```
136+
137+
The `name` will match against a permission name. If it matches, the guard is
138+
called instead of calling `hasPermission` on the `User` model. (You can always
139+
call `hasPermission` on the `User` inside your guard callback if you need.)
140+
141+
The callback can be: a closure or UDF, a component with an `authorize` function,
142+
or a WireBox mapping to a component with an `authorize` function. Please note
143+
that the authorize function must be explicitly defined and public (No `onMissingMethod`).
144+
This `authorize` function is called with two parameters: the `user` being authorized
145+
and a struct of `additionalArgs` and must return a `boolean`, like so:
146+
147+
```cfc
148+
public boolean function authorize( required any user, struct additionalArgs = {} );
149+
```
150+
151+
Using this approach, you can define custom guards anywhere in your application:
152+
`config/ColdBox.cfc`, `ModuleConfig.cfc` of your custom modules, etc. The
153+
`Guard` component is registered as a singleton, so it will keep track of all the
154+
guards registered, even from different sources.
155+
156+
If you have a need to remove a guard definition you can do so with the `removeDefinition` method:
157+
158+
```cfc
159+
public Guard function removeDefinition( required string name );
160+
```
87161

88162
### Redirects
89163

@@ -118,7 +192,7 @@ First, there are two interfaces that must be followed:
118192
interface {
119193
120194
/**
121-
* Must return an object that conforms to `HasPermissionsInterface`.
195+
* Must return an object that conforms to `HasPermissionInterface`.
122196
* (This may be an implicit implements.)
123197
*/
124198
public HasPermissionInterface function getUser();
@@ -139,8 +213,11 @@ interface {
139213
140214
/**
141215
* Returns true if the user has the specified permission.
216+
* Any additional arguments may be passed in as the second argument.
217+
* This allows you to check if a user can access a specific resource,
218+
* rather than just a generic check.
142219
*/
143-
public boolean function hasPermission( required string permission );
220+
public boolean function hasPermission( required string permission, struct additionalArgs );
144221
145222
}
146223
```

interfaces/HasPermissionInterface.cfc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ interface {
22

33
/**
44
* Returns true if the user has the specified permission.
5+
* Any additional arguments may be passed in as the second argument.
6+
* This allows you to check if a user can access a specific resource,
7+
* rather than just a generic check.
58
*/
6-
public boolean function hasPermission( required string permission );
9+
public boolean function hasPermission( required string permission, struct additionalArgs );
710

811
}

0 commit comments

Comments
 (0)