We are excited to bring you another release for cbSecurity packed with security goodness: version 2.3.0. This version focuses on security contexts from ANY layer of a ColdBox application. It will also enhance your functional skills as well, as it introduces some nice semantics for securing your code.
# Install install cbsecurity # Update update cbsecurity
What's New With 2.3.0
This release focuses on bringing a strong focus on protecting every development layer of a ColdBox application. It introduces the cbSecurity
model that can be used by any layer and provides you with a great functional API to secure your code.
Explicit Authorizations
There will be times where you will need authorization checks outside of the incoming request rules or the handler annotations. This can be from within interceptors, models, layouts or even views. For this, we have provided the cbSecurity
model so you can do explicit authorization checks anywhere you like.
cbSecurity
Model
You can inject our model or you can use our handy cbsecure()
mixin (interceptors/handlers/layouts/views) and then call the appropriate security functions:
// Mixin: Handlers/Interceptors/Layouts/Views cbsecure() // Injection property name="cbSecurity" inject="@cbSecurity"
The secure()
Methods
Now that you have access to the model, you can use the following method to verify explicit permissions and authorize access. This method will throw an exception if the user does not validate the incoming permissions context (NotAuthorized
).
// Verify the currently logged in user has those permission, // else throw a NotAuthorized exception cbSecurity.secure( permissions, [message] ); cbsecure().secure( permissions, [message] );
- The
permission
can be an array, string or list of the permissions to validate. - The
message
is a custom error message to be used in themessage
string of the exception thrown.
You also have two more authorization methods that will verify certain permission conditions for you:
// Authorize that the user has ALL of the incoming permissions cbSecurity.secureAll( permissions, [message] ); // Authorize that the user has NONE of the incoming permissions cbSecurity.secureNone( permissions, [message] );
Conditional Authorizations Using when()
There are also cases where you want to execute a piece of code by determining if the user has access to do so. For example, only a USER_ADMIN
can change people's roles or you want to filter some data for certain users. For this, we have created the when()
method with the following signature:
when( permissions, success, fail )
- The
permissions
is a permission array or list that will be Or'ed - The
success
is a closure/lambda or UDF that will execute if the permissions validate. - The
fail
is a closure/lambda or UDF that will execute if the permissions DID not validate, much like an else statement
Both closures/functions takes in a user
which is the currently authenticated user, the called in permissions
and can return anything.
// Lambda approach ( user, permissions ) => { // your code here }; // UDF/Closure function( user, permissions ){ // your code here }
You can also chain the when()
calls if needed, to create beautiful security contexts. So if we go back to our admin examples, we can do something like this:
var oAuthor = authorService.getOrFail( rc.authorId ); prc.data = userService.getData(); // Run Security Contexts cbSecure() // Only user admin can change to the incoming role .when( "USER_ADMIN", ( user ) => oAuthor.setRole( roleService.get( rc.roleID ) ) ) // The system admin can set a super admin .when( "SYSTEM_ADMIN", ( user ) => oAuthor.setRole( roleService.getSystemAdmin() ) ) // Filter the data to be shown to the user .when( "USER_READ_ONLY", ( user ) => prc.data.filter( ( i ) => !i.isClassified ) ) // Calling with a fail closure cbSecurity.when( "USER_ADMIN", ( user ) => user.setRole( "admin" ), //success ( user ) => relocate( "Invaliduser" ) //fail );
We have also added the following whenX()
methods to serve your needs when evaluating the permissions:
// When all permissions must exist in the user whenAll( permissoins, success, fail) // When none of the permissions exist in the user whenNone( permissions, success, fail )
Verification Methods
If you just want to validate if a user has certain permissions or maybe no permissions at all or if a passed user is the same as the logged in user, then you can use the following boolean methods that only do verification.
// Checks the user has one or at least one permission if the // permission is a list or array boolean cbSecurity.has( permission ); // The user must have ALL the permissions boolean cbSecurity.all( permission ); // The user must NOT have any of the permissions boolean cbSecurity.none( permission ); // Verify if the passed in user is the same as the logged in user boolean cbSecurity.sameUser( user );
These are great to have a unified and abstracted way to verifying permissions or if the passed user is the same as the logged in user. Here are some examples:
View Layer
This is only visible to user admins! Impersonate User This is You!
Other Layers:
if( cbSecurity.has( "PERM" ) ){ auditUser(); } if( cbSecurity.sameUser( prc.incomingUser ) ){ // you can change your gravatar }
Authorization Contexts
There are also times where you need to validate custom conditions and block access to certain areas. This way, you can implement your own custom security logic and leverage cbSecurity for blockage. You will accomplish this via the secureWhen()
method:
secureWhen( context, [errorMessage] )
The context
can be a closure/lambda/udf or a boolean evaluation:
// Using as a closure/lambda cbSecurity.secureWhen( ( user ) => !user.isConfirmed() ) cbSecurity.secureWhen( ( user ) => !oEntry.canPublish( user ) ) // Using a boolean evaluation cbSecurity.secureWhen( cbSecurity.none( "AUTHOR_ADMIN" ) && !cbSecurity.sameUser( oAuthor ) ) cbSecurity.whenNone( "AUTHOR_ADMIN", ( user ) => relocate() );
The closure/udf will receive the currently authenticated user as the first argument.
( user ) => {} function( user );
Securing Views
You can also use our handy event.secureView()
method in the request context to pivot between views according to user permissions.
event.secureView( permissions, successView, failView )
This will allow you to set the successView
if the user has the permissions or the failView
if they don't.
cbSecurity
Method Summary
Blocking Methods
When certain permission context is met, if not throws NotAuthorized
secure( permissions, [message] )
secureAll( permissions, [message] )
secureNone( permissions, [message] )
secureWhen( context, [message] )
guard() alias to secure()
Action Context Methods
When certain permission context is met, execute the success function/closure, else if a fail
closure is defined, execute that instead.
when( permissions, success, fail )
whenAll( permissions, success, fail )
whenNone( permissions, success, fail )
Verification Methods
Verify permissions or user equality
has( permissions ):boolean
all( permissions ):boolean
none( permissions ):boolean
sameUser( user ):boolean
Request Context Methods
secureView( permissions, successView, failView )
Add Your Comment