You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Sep 2, 2020. It is now read-only.
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.
Copy file name to clipboardExpand all lines: README.md
+79-2Lines changed: 79 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -84,6 +84,80 @@ component secured {
84
84
85
85
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.
86
86
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
+
```
87
161
88
162
### Redirects
89
163
@@ -118,7 +192,7 @@ First, there are two interfaces that must be followed:
118
192
interface {
119
193
120
194
/**
121
-
* Must return an object that conforms to `HasPermissionsInterface`.
195
+
* Must return an object that conforms to `HasPermissionInterface`.
122
196
* (This may be an implicit implements.)
123
197
*/
124
198
public HasPermissionInterface function getUser();
@@ -139,8 +213,11 @@ interface {
139
213
140
214
/**
141
215
* 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.
142
219
*/
143
-
public boolean function hasPermission( required string permission );
220
+
public boolean function hasPermission( required string permission, struct additionalArgs );
0 commit comments