Skip to content

Commit f1d4ede

Browse files
committed
tons of features in progress for v2.3
- cbSecurity Model - cbsecure() mixin - secure( permissions, [message] ) - secureAll( permissions, [message] ) - secureNone( permissions, [message] ) - secureWhen( context, [message] ) - guard() => secure() - has( permissions ):boolean - all( permissions ):boolean - none( permissions ):boolean - sameUser( user ):boolean
1 parent b056772 commit f1d4ede

File tree

8 files changed

+607
-5
lines changed

8 files changed

+607
-5
lines changed

helpers/mixins.cfm

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
<cfscript>
2+
/**
3+
* Retrieve the JwtService
4+
*/
25
function jwtAuth() {
36
return wirebox.getInstance( "JwtService@cbSecurity" );
4-
}
7+
}
8+
9+
/**
10+
* Retrieve the CBSecurity Service Object
11+
*/
12+
function cbSecure() {
13+
return wirebox.getInstance( "CBSecurity@cbSecurity" );
14+
}
15+
516
</cfscript>

models/CBSecurity.cfc

Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
/**
2+
* Copyright since 2016 by Ortus Solutions, Corp
3+
* www.ortussolutions.com
4+
* ---
5+
* This service is in charge of offering security capabilties to your ColdBox applications
6+
*
7+
* It can be injected by using the `@cbSecurity` annotation
8+
*
9+
* <pre>
10+
* property name="cbSecurity" inject="@cbsecurity";
11+
* </pre>
12+
*
13+
* Or you can use the `cbSecure()` mixin
14+
*
15+
* <pre>
16+
* cbsecure().secure();
17+
* </pre>
18+
*/
19+
component singleton accessors="true" {
20+
21+
/*********************************************************************************************/
22+
/** DI **/
23+
/*********************************************************************************************/
24+
25+
property name="settings" inject="coldbox:moduleSettings:cbsecurity";
26+
property name="log" inject="logbox:logger:{this}";
27+
property name="wirebox" inject="wirebox";
28+
29+
30+
/*********************************************************************************************/
31+
/** PROPERTIES **/
32+
/*********************************************************************************************/
33+
34+
/**
35+
* The auth service in use according to the configuration file
36+
*/
37+
property name="authService";
38+
39+
/**
40+
* The user service in use according to the configuration file
41+
*/
42+
property name="userService";
43+
44+
/*********************************************************************************************/
45+
/** Static Vars **/
46+
/*********************************************************************************************/
47+
48+
variables.DEFAULT_ERROR_MESSAGE = "Not authorized!";
49+
50+
/**
51+
* Constructor
52+
*/
53+
function init(){
54+
return this;
55+
}
56+
57+
/**
58+
* Get the user service object defined accordingly in the settings
59+
*
60+
* @throws IncompleteConfiguration
61+
*
62+
* @return cbsecurity.interfaces.IUserService
63+
*/
64+
any function getUserService(){
65+
// If loaded, use it!
66+
if ( !isNull( variables.userService ) ) {
67+
return variables.userService;
68+
}
69+
70+
// Check and Load Baby!
71+
if ( !len( variables.settings.userService ) ) {
72+
throw(
73+
message = "No [userService] provided in the settings. Please set it in the `config/ColdBox.cfc` under `moduleSettings.cbsecurity.userService`.",
74+
type = "IncompleteConfiguration"
75+
);
76+
}
77+
78+
return variables.userService = variables.wirebox.getInstance( variables.settings.userService );
79+
}
80+
81+
/**
82+
* Get the authentication service defined accordingly in the settings
83+
*
84+
* @throws IncompleteConfiguration
85+
*
86+
* @return cbsecurity.interfaces.IAuthService
87+
*/
88+
any function getAuthService(){
89+
// If loaded, use it!
90+
if ( !isNull( variables.authService ) ) {
91+
return variables.authService;
92+
}
93+
94+
// Check and Load Baby!
95+
if ( !len( variables.settings.authenticationService ) ) {
96+
throw(
97+
message = "No [authService] provided in the settings. Please set in `config/ColdBox.cfc` under `moduleSettings.cbsecurity.authenticationService`.",
98+
type = "IncompleteConfiguration"
99+
);
100+
}
101+
102+
return variables.authService = variables.wirebox.getInstance( variables.settings.authenticationService );
103+
}
104+
105+
/***************************************************************/
106+
/* Verification Methods
107+
/***************************************************************/
108+
109+
/**
110+
* Verify if the incoming permissions exist in the currently authenticated user.
111+
* All permissions are Or'ed together
112+
*
113+
* @throws NoUserLoggedIn
114+
*
115+
* @permissions One, a list or an array of permissions
116+
*/
117+
boolean function has( required permissions ){
118+
var oUser = getAuthService().getUser();
119+
120+
return arrayWrap( arguments.permissions )
121+
.filter( function( item ){
122+
return oUser.hasPermission( arguments.item );
123+
} ).len() > 0;
124+
}
125+
126+
/**
127+
* Verify that ALL the permissions passed must exist within the authenticated user
128+
*
129+
* @throws NoUserLoggedIn
130+
*
131+
* @permissions One, a list or an array of permissions
132+
*/
133+
boolean function all( required permissions ){
134+
var oUser = getAuthService().getUser();
135+
var aPerms = arrayWrap( arguments.permissions );
136+
137+
return aPerms
138+
.filter( function( item ){
139+
return oUser.hasPermission( arguments.item );
140+
} ).len() == aPerms.len();
141+
}
142+
143+
/**
144+
* Verify that NONE of the permissions passed must exist within the authenticated user
145+
*
146+
* @throws NoUserLoggedIn
147+
*
148+
* @permissions One, a list or an array of permissions
149+
*/
150+
boolean function none( required permissions ){
151+
var oUser = getAuthService().getUser();
152+
153+
return arrayWrap( arguments.permissions )
154+
.filter( function( item ){
155+
return oUser.hasPermission( arguments.item );
156+
} ).len() == 0;
157+
}
158+
159+
/**
160+
* Verify that the passed in user object must be the same as the authenticated user
161+
* Equality is done by evaluating the `getid()` method on both objects.
162+
*
163+
* @throws NoUserLoggedIn
164+
*
165+
* @user The user to test for equality
166+
*/
167+
boolean function sameUser( required user ){
168+
return ( arguments.user.getId() == getAuthService().getUser().getId() );
169+
}
170+
171+
/***************************************************************/
172+
/* Blocking Methods
173+
/***************************************************************/
174+
175+
/**
176+
* Verifies if the currently logged in user has any of the passed permissions.
177+
*
178+
* @throws NotAuthorized
179+
*
180+
* @permissions One, a list or an array of permissions
181+
* @message The error message to throw in the exception
182+
*
183+
* @returns CBSecurity
184+
*/
185+
CBSecurity function secure( required permissions, message=variables.DEFAULT_ERROR_MESSAGE ){
186+
if( !has( arguments.permissions ) ){
187+
throw( type = "NotAuthorized", message=arguments.message );
188+
}
189+
return this;
190+
}
191+
192+
/**
193+
* Verifies if the currently logged in user has ALL of the passed permissions.
194+
*
195+
* @throws NotAuthorized
196+
*
197+
* @permissions One, a list or an array of permissions
198+
* @message The error message to throw in the exception
199+
*
200+
* @returns CBSecurity
201+
*/
202+
CBSecurity function secureAll( required permissions, message=variables.DEFAULT_ERROR_MESSAGE ){
203+
if( !all( arguments.permissions ) ){
204+
throw( type = "NotAuthorized", message=arguments.message );
205+
}
206+
return this;
207+
}
208+
209+
/**
210+
* Verifies if the currently logged in user has NONE of the passed permissions.
211+
*
212+
* @throws NotAuthorized
213+
*
214+
* @permissions One, a list or an array of permissions
215+
* @message The error message to throw in the exception
216+
*
217+
* @returns CBSecurity
218+
*/
219+
CBSecurity function secureNone( required permissions, message=variables.DEFAULT_ERROR_MESSAGE ){
220+
if( !none( arguments.permissions ) ){
221+
throw( type = "NotAuthorized", message=arguments.message );
222+
}
223+
return this;
224+
}
225+
226+
/**
227+
* Verifies the passed in context closure/lambda/udf to a boolean expression.
228+
* If the context is true, then the exception is thrown. The context must be false in order to pass.
229+
*
230+
* The context udf/closure/lambda must adhere to the following signature
231+
*
232+
* <pre>
233+
* function( user ){}
234+
* ( user ) => {}
235+
* </pre>
236+
*
237+
* It receives the currently logged in user
238+
*
239+
* @throws NotAuthorized
240+
*
241+
* @context A closure/lambda/udf that returns boolean, or a boolean expression
242+
* @message The error message to throw in the exception
243+
*
244+
* @returns CBSecurity
245+
*/
246+
CBSecurity function secureWhen( required context, message=variables.DEFAULT_ERROR_MESSAGE ){
247+
var results = arguments.context;
248+
// Check if udf/lambda
249+
if( isCustomFunction( arguments.context ) || isClosure( arguments.context ) ){
250+
results = arguments.context( getAuthService().getUser() );
251+
}
252+
if( results ){
253+
throw( type = "NotAuthorized", message = arguments.message );
254+
}
255+
return this;
256+
}
257+
258+
/**
259+
* Alias proxy if somebody is coming from cbguard, proxies to the secure() method
260+
*/
261+
function guard(){
262+
return secure( argumentCollection=arguments );
263+
}
264+
265+
/***************************************************************/
266+
/* Action Context Methods
267+
/***************************************************************/
268+
269+
/**
270+
* This method will verify that any permissions must exist in the currently logged in user.
271+
*
272+
* - If the result is true, then it will execute the success closure/lambda or udf.
273+
* - If the restul is false, then it will execute the fail closure/lambda or udf
274+
*
275+
* The success or fail closures/lambdas/udfs must match the following signature
276+
*
277+
* <pre>
278+
* function( user, permissions ){}
279+
* ( user, permissions ) => {}
280+
* </pre>
281+
*
282+
* They receive the currently logged in user and the permissions that where evaluated
283+
*
284+
* @permissions One, a list or an array of permissions
285+
* @success The closure/lambda/udf that executes if the context passes
286+
* @fail The closure/lambda/udf that executes if the context fails
287+
*/
288+
function when( required permissions, required success, fail ){
289+
290+
}
291+
292+
/**
293+
* This method will verify that ALL permissions must exist in the currently logged in user.
294+
*
295+
* - If the result is true, then it will execute the success closure/lambda or udf.
296+
* - If the restul is false, then it will execute the fail closure/lambda or udf
297+
*
298+
* The success or fail closures/lambdas/udfs must match the following signature
299+
*
300+
* <pre>
301+
* function( user, permissions ){}
302+
* ( user, permissions ) => {}
303+
* </pre>
304+
*
305+
* They receive the currently logged in user and the permissions that where evaluated
306+
*
307+
* @permissions One, a list or an array of permissions
308+
* @success The closure/lambda/udf that executes if the context passes
309+
* @fail The closure/lambda/udf that executes if the context fails
310+
*/
311+
function whenAll( required permissions, required success, fail ){
312+
313+
}
314+
315+
/**
316+
* This method will verify that NONE of the permissions must exist in the currently logged in user.
317+
*
318+
* - If the result is true, then it will execute the success closure/lambda or udf.
319+
* - If the restul is false, then it will execute the fail closure/lambda or udf
320+
*
321+
* The success or fail closures/lambdas/udfs must match the following signature
322+
*
323+
* <pre>
324+
* function( user, permissions ){}
325+
* ( user, permissions ) => {}
326+
* </pre>
327+
*
328+
* They receive the currently logged in user and the permissions that where evaluated
329+
*
330+
* @permissions One, a list or an array of permissions
331+
* @success The closure/lambda/udf that executes if the context passes
332+
* @fail The closure/lambda/udf that executes if the context fails
333+
*/
334+
function whenNone( required permissions, required success, fail ){
335+
336+
}
337+
338+
/***************************************************************/
339+
/* Private Methods
340+
/***************************************************************/
341+
342+
/**
343+
* convert one or a list of permissions to an array, if it's an array we don't touch it
344+
*
345+
* @items One, a list or an array
346+
*/
347+
private function arrayWrap( required items ){
348+
return isArray( arguments.items ) ? items : items.listToArray();
349+
}
350+
351+
}

models/jwt/JwtService.cfc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ component accessors="true" singleton {
5151
"scopes"
5252
];
5353

54-
// Default Settings
54+
// Default JWT Settings
5555
variables.DEFAULT_SETTINGS = {
5656
// The jwt token issuer claim -> iss
5757
"issuer" : "",
File renamed without changes.
File renamed without changes.

test-harness/handlers/Main.cfc

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,13 @@ component {
2828
}
2929

3030

31-
// Run on first init
32-
any function onAppInit( event, rc, prc ){
31+
/**
32+
* cbSecureMixin
33+
*/
34+
function cbSecureMixin( event, rc, prc ){
35+
cbsecure().getAuthService();
36+
cbSecure().getUserService();
37+
return cbsecure().getSettings();
3338
}
3439

35-
}
40+
}

0 commit comments

Comments
 (0)