[coldbox-4.3.0] cbsecurity & REST API

Does anyone have an example of using the published cbsecurity module/interceptor with REST APIs? The recipe on security in the documentation isn’t bad but it doesn’t cover cbsecurity specifically.

I’ve got it generally working, however when a user does not have access to a resource I would like to respond to that request with a 403 Forbidden and a JSON response with specific message to the user describing the error. However, cbsecurity by default will redirect them to make another request, via setNextEvent() where I could throw the 403 but because of the redirect to a generic page with no parameters I don’t see a way to easily pass the JSON data seamlessly…maybe FlashRAM?

Is there a way to prevent the redirect and just respond with a 403 to the first request within my custom userValidator() method? If it makes sense I could extend cbsecurity interceptor and override the processRules() function. Just try to get some opinions as to the least hacky approach here. How are other folks tackling this and other related security + REST API problems?

Thanks!
Wes

Wes cbsecurity mostly deals as an incoming event firewall. No access then redirect occurs.

What you need is a deeper layer st the handler level. Please look at approach of using a base handler with an around advice. This is in our rest template

Box coldbox create app skeleton=rest

Luis Majano
CEO
Ortus Solutions, Corp
www.ortussolutions.com
P/F: 1-888-557-8057

I’m actually rocking the BaseHandler from the REST template, so maybe I’m slightly on track. Are there any examples of usage of the REST template BaseHandler and an around advice interacting with the cbsecurity rules?

I guess I’m just a little confused because I’ve set up my cbsecurity rules and told it which roles should have access to which events, and that is working…except by default it will only respond with a 302 redirect to the rules[x].redirect when the user’s role doesn’t match or for any other reason the userValidator returns false. Really I’d prefer to just respond with the correct HTTP status code and message the first time.

In my case should I only use cbsecurity to validate if the user is simply authenticated, ignoring roles altogether and then rely on the BaseHander around advice to observe some other custom created permission system?

Thanks!
Wes

Wes, I whitelist API routes from CBSecurity, as the API aroundHandler method can handle authentication failures and deliver a JSON response. Since CBSecurity is a relocation provider, it’s not really suited for API security.

Ugh. Thank you, but that’s frustrating to learn. I mean, when I read the documentation on cbox-security/cbsecurity it makes it sound near like a silver bullet. “This module will provide your application with a security rule engine.” I guess when I read “security rule engine” I think this is THE THING in my application that will provide the security, right? It’s even got description in there about how you can extend it to to utilize more fine grain permissions beyond simple roles. So, if it’s not that…

Do you just roll you own security system then? Jon, do you create your own permission/roles tables that you compare against from within the API BaseHandler around advice handler before executing the event? In the case of an API, let’s say some users have been delegated to be able to update certain types of records by making PUT requests, but some users cannot, and should have a 403 returned to the front-end UI.

Thanks – learning a lot today.

Wes

Wes,

It depends on the authentication mechanism for your API. CBSecurity was designed to protect and MVC application, specifically the “V” and “C”. Since API’s are really only “MC”, relocation isn’t desirable.

A couple of things to remember, though: Even if your API is whitelisted, your interceptors still fire at preProcess and preEvent and preHandler. This means that your methods of authentication can be universal and the API doesn’t need to re-do the work.

Let’s say you are using token authentication, but you want to bypass that if an existing session meets the permissions required. The latter isn’t really “stateless” as REST is supposed to be, by definition, but it’s a workable authentication pattern if your API is being consumed by the internal interface.

What I typically do is put an interceptor in front of the Security Interceptor that checks for the expected authentication payloads - headers, request context collection form submissions, etc. Then security interceptor already has the information it needs by the time it runs its own preProcess interception, but my API also has the ability to verify permissions and authentication status, once it reaches the handler:
[
{

class=“interceptors.UserAuthentication”,

name=“UserAuthentication”

},

{

class = “cbsecurity.interceptors.Security”,

name = “CBSecurity”,

properties = {

rulesFile = “/config/security.json.cfm”,

rulesSource = “json”,

validatorModel = “SecurityService”

}

}
]

Note that the validatorModel in the CBSecurity settings. That SecurityService implements the interface which provides cbsecurity the validation check. Have a look at the ContentBox SecurityService object, a close approximation of how that’s implemented: ContentBox/SecurityService.cfc at development · Ortus-Solutions/ContentBox · GitHub

There is also a value of prc.currentUser that is set by the UserAuthentication interceptor, if the user is logged in. All my API has to do is to use the available methods within the prc.currentUser object to verify login, roles, permissions, etc. So, I can add a preHandler to my BaseAPIHhandler that contains something like this.

if( isNull( prc.currentUser ) || !isNull( prc.currentUser.getPermissions() ) {

event.overrideEvent( “API.BaseHandler.onAuthoriziationFailure” );

}

That covers every single request and will override the requested event, if the user is not logged in. Depending on the application, there may be additional permissions checks and the like at the individual handler level, but the methodology is pretty much the same.

If your app only has an API, then you can simply add that to your authorized user interceptor and the event will automatically be overridden.

HTH,

Jon

Jon, this is killer – thank you for taking so much of your personal time to help a stranger on the internet! I’m going to play with the ideas you’ve given me here and I’ll write back soon. I do have a few follow-up questions, if you please:

  1. For managing permissions you’ve just created your own custom system for that, right? There is no module out there I’m missing where I can add a table or two and get a nearly-baked permission system?

  2. If you’ve rolled your own, could you share or point me to the basic architecture you’ve found helpful? I’m imagining a table that lists the protected content and a second table that maps users to the content, maybe with some C,R,U,D, flags or something as part of defining that relationship. I just don’t need to recreate this wheel that I know has been done ten thousand times over.
    Thanks again so much, this is so good!

Wes

Wes,

Permissions are pretty specific to an application and its data model (e.g. – ORM, CFML Queries, NoSQL, etc). First, you have to decide whether you want to use Role-based restrictions or Permission-based restrictions. If permission-based, then you need to define the lookup pattern for those permissions and the methods for recursion. Permissions are usually functionality-based, so some sort of delimiter convention within your app will allow you to do what you need to do to check granular permissions. I usually start with the basic CRUD permissions and then expand on them as necessary:

User:Create
User:Read

User:Update

User:Delete

Alternately, you could use just two, which simplify the above:

User:Manage
User:Read

Then I would implement methods in my user object (e.g. prc.currentUser ) to validate roles and permissions:

user.isInRole( required string roleName )

user.isInAnyRole( required string roleList )
user.hasPermission( required string permission )

Usually I marshall those permissions in to the user object when it is loaded ( e.g. ORM postLoad interception ), that allows me to quickly look them up, at any point. This is usually a “private” property within the variables scope of the user object.

Finally there many ways to define the level of permission required for a method. One relatively simple way is to define those permissions within the handler, itself:

this.requiredPermissions = {

“get” : “User:Read”,

“list” : “User:Read”,

“add” : “User:Create”,

“update”: “User:Update”,

“delete”: “User:Delete”

};

Then I can add an additional check for the permission required for the action to the preHandler method referenced before:

if(

isNull( prc.currentUser )

!structKeyExists( this.requiredPermissions, event.getCurrentAction() )

!prc.currentUser.hasPermission( this.requiredPermissions[ event.getCurrentAction() ] )

) {

event.overrideEvent( “API.BaseHandler.onAuthorizationFailure” );

}

Note that I’ve removed the check on the permissions relationships, since hasPermission will handle that.

There are so many ways to deal with Roles and Permissions validation so my caveat would be that there may very well be a better way, which would work with your app.

HTH,

Jon