Blog

cbSecurity 2.3 Released

Luis Majano March 30, 2020

Spread the word

Luis Majano

March 30, 2020

Spread the word


Share your thoughts

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"
All security methods will call the application's configured Authentication Service to retrieve the currently logged in user. If the user is not logged in an immediate `NoUserLoggedIn` exception will be thrown by all methods.

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 the message 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.

Please note that you could potentially do these type of methods by leveraging the currently logged in user and it's `hasPermission()` method. However, these methods provide abstraction and can easily be mocked!
// 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
}
Please note that we do user equality by calling the `getId()` method of the authenticated user and the incoming user. This is part of our `IAuthUser` interface requirements.

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.

cbSecurity injects the `secureView()` method into the request context via the `preProcess` interception point.
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

Recent Entries

Adobe CFSummit 2024 Recap | Ortus Solutions

Adobe CFSummit 2024 Recap | Ortus Solutions

A huge thank you to everyone who visited our booth! We had the pleasure of engaging in meaningful conversations about Modern CFML and ColdFusion development, sharing insights, and exploring exciting new ideas. Visitors also got a chance to participate in some fun activities and win fabulous prizes, including our customized t-shirts, which were a huge hit!

Maria Jose Herrera
Maria Jose Herrera
October 02, 2024
BoxLang September Newsletter

BoxLang September Newsletter

Welcome to the September edition of the BoxLang Newsletter! This month has been packed with exciting updates, new features, and enhancements that continue to shape BoxLang into a robust and versatile programming language. In this edition, we highlight the latest beta releases, new integrations, and key features that have been introduced.

Maria Jose Herrera
Maria Jose Herrera
October 01, 2024
Fall Savings: Unlock Exclusive Discounts Until October 31st!

Fall Savings: Unlock Exclusive Discounts Until October 31st!

Get ready for the Ortus October Fall Savings! This is your opportunity to elevate your development experience with fantastic offers designed for everyone—from seasoned developers to tech enthusiasts. Our special promotions are here to help you achieve your goals while saving money!

Don’t Miss Out on These Amazing Offers!

Use the code FALLSAVINGS24 at checkout to enjoy these incredible discounts before they expire on October 31st

Cristobal Escobar
Cristobal Escobar
October 01, 2024