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.
To view this discussion on the web visit https://groups.google.com/d/msgid/coldbox/2ef973cc-605e-482e-805c-102c734325f4%40googlegroups.com.
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: https://github.com/Ortus-Solutions/ContentBox/blob/development/modules/contentbox/models/security/SecurityService.cfc
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
To view this discussion on the web visit https://groups.google.com/d/msgid/coldbox/59286fb0-67eb-4359-924e-ec1f7329a9b5%40googlegroups.com.
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.
To view this discussion on the web visit https://groups.google.com/d/msgid/coldbox/c36b0aa8-c97d-44e3-9e17-745cf65e6a91%40googlegroups.com.