Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
2020-APR-03
<major>.<minor>.<patch>2019-OCT-02
cbsecurity = {
validator = "CBAuthValidator@cbsecurity"
}event.secureView( permissions, successView, failView )ES#html.startForm(action=prc.xehDoLogin,name="loginForm")#
#html.hiddenField(name="_securedURL",value=event.getValue('_securedURL',''))#
#html.textfield(name="username",label="Username: ",size="40",required="required",class="textfield",value=prc.rememberMe)#
#html.passwordField(name="password",label="Password: ",size="40",required="required",class="textfield")#
<div id="loginButtonbar">
#html.checkBox(name="rememberMe",value=true,checked=(len(prc.rememberMe)))#
#html.label(field="rememberMe",content="Remember Me ",class="inline")#
#html.submitButton(value=" Log In ",class="buttonred")#
</div>
<br/>
<img src="#prc.cbRoot#/includes/images/lock.png" alt="lostPassword" />
<a href="#event.buildLink(prc.xehLostPassword)#">Lost your password?</a>
#html.endForm()#moduleSettings = {
// CB Security
cbSecurity : {
"rules" : "config/security.json.cfm"
};[
{
"whitelist": "user\\.login,user\\.logout,^main.*",
"securelist": "^user\\.*, ^admin",
"match": "event",
"roles": "admin",
"permissions": "",
"redirect": "user.login",
"useSSL": false
},
{
"whitelist": "",
"securelist": "^shopping",
"match": "url",
"roles": "",
"permissions": "shop,checkout",
"redirect": "user.login",
"useSSL": true
}
]secureWhen( context, [errorMessage] )// 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() );( user ) => {}
function( user );moduleSettings = {
// CB Security
cbSecurity : {
"rules" : "config/security.xml.cfm"
};<?xml version="1.0" encoding="ISO-8859-1"?>
<-- <
Declare as many rule elements as you want, order is important
Remember that the securelist can contain a list of regular
expressions if you want
ex: All events in the user handler
user\..*
ex: All events
.*
ex: All events that start with admin
^admin
If you are not using regular expressions, just write the text
that can be found in an event.
-->
<rules>
<rule>
<match>event</match>
<whitelist>user\.login,user\.logout,^main.*</whitelist>
<securelist>^user\..*, ^admin</securelist>
<roles>admin</roles>
<permissions>read,write</permissions>
<redirect>user.login</redirect>
</rule>
<rule>
<match>event</match>
<whitelist></whitelist>
<securelist>^moderator</securelist>
<roles>admin,moderator</roles>
<permissions>read</permissions>
<redirect>user.login</redirect>
</rule>
<rule>
<match>url</match>
<whitelist></whitelist>
<securelist>/secured.*</securelist>
<roles>admin,paid_subscriber</roles>
<permissions></permissions>
<redirect>user.pay</redirect>
</rule>
</rules>moduleSettings = {
// CB Security
cbSecurity : {
"rules" : "model",
"rulesModel" : "SecurityService"
}
};// 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 );<cfif cbsecure().has( "USER_ADMIN" )>
This is only visible to user admins!
</cfif>
<cfif cbsecure().has( "SYSTEM_ADMIN" )>
<a href="/user/impersonate/#prc.user.getId()#">Impersonate User</a>
</cfif>
<cfif cbsecure().sameUser( prc.user )>
<i class="fa fa-star">This is You!</i>
</cfif>if( cbSecurity.has( "PERM" ) ){
auditUser();
}
if( cbSecurity.sameUser( prc.incomingUser ) ){
// you can change your gravatar
}// Mixin: Handlers/Layouts/Views
cbsecure()
// Injection
property name="cbSecurity" inject="@cbSecurity"// Verify the currently logged in user has at least one of those permissions,
// else throw a NotAuthorized exception
cbSecurity.secure( permissions, [message] );
cbsecure().secure( permissions, [message] );// 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] );when( permissions, success, fail )// Lambda approach
( user, permissions ) => {
// your code here
};
// UDF/Closure
function( user, permissions ){
// your code here
}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
);// 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 )moduleSettings approach instead of root approach (See compat section)interceptData struct in each interception call:cbsecurity.validator. So if the global validator is something other than jwt but your module REQUIRES JWT validation, then just add it in your ModuleConfig.cfccomponent extends="coldbox.system.Interceptor"{
function cbSecurity_onInvalidAuthentication( event, interceptData ){
// do what you like here
}
function cbSecurity_onInvalidAuthorization( event, interceptData ){
// do what you like here
}
}cbsecurity = {
validator = "JWTService@cbsecurity"
}settings = {
cbsecurity = {
validator = "JWTService@cbsecurity"
}
}settings = {
// CB Security Rules to prepend to global rules
cbsecurity = {
// Module Relocation when an invalid access is detected, instead of each rule declaring one.
"invalidAuthenticationEvent" : "mod1:secure.index",
// Default Authentication Action: override or redirect when a user has not logged in
"defaultAuthenticationAction" : "redirect",
// Module override event when an invalid access is detected, instead of each rule declaring one.
"invalidAuthorizationEvent" : "mod1:secure.auth",
// Default Authorization Action: override or redirect when a user does not have enough permissions to access something
"defaultAuthorizationAction" : "redirect",
// You can define your security rules here
"rules" : [
{
"secureList" : "mod1:home"
},
{
"secureList" : "mod1/modOverride",
"match" : "url",
"action" : "override"
}
]
}
};filepath has json or xml in it, we will use that as the source style

# Latest version
install cbsecurity
# Bleeding Edge
install cbsecurity@bemoduleSettings = {
cbSecurity = {
validator = "SecurityService"
}
}settings = {
cbsecurity = {
"rules" : "#modulePath#/config/firewallRules.json"
// other config here... <---
}
};// Module Settings
moduleSettings = {
// CB Security
cbSecurity : {
// The global invalid authentication event or URI or URL to go if an invalid authentication occurs
"invalidAuthenticationEvent" : "",
// Default Authentication Action: override or redirect when a user has not logged in
"defaultAuthenticationAction" : "redirect",
// The global invalid authorization event or URI or URL to go if an invalid authorization occurs
"invalidAuthorizationEvent" : "",
// Default Authorization Action: override or redirect when a user does not have enough permissions to access something
"defaultAuthorizationAction" : "redirect",
// You can define your security rules here or externally via a source
"rules" : [],
// The validator is an object that will validate rules and annotations and provide feedback on either authentication or authorization issues.
"validator" : "CBAuthValidator@cbsecurity",
// The WireBox ID of the authentication service to use in cbSecurity which must adhere to the cbsecurity.interfaces.IAuthService interface.
"authenticationService" : "authenticationService@cbauth",
// WireBox ID of the user service to use
"userService" : "",
// The name of the variable to use to store an authenticated user in prc scope if using a validator that supports it.
"prcUserVariable" : "oCurrentUser",
// If source is model, the wirebox Id to use for retrieving the rules
"rulesModel" : "",
// If source is model, then the name of the method to get the rules, we default to `getSecurityRules`
"rulesModelMethod" : "getSecurityRules",
// If source is db then the datasource name to use
"rulesDSN" : "",
// If source is db then the table to get the rules from
"rulesTable" : "",
// If source is db then the ordering of the select
"rulesOrderBy" : "",
// If source is db then you can have your custom select SQL
"rulesSql" : "",
// Use regular expression matching on the rule match types
"useRegex" : true,
// Force SSL for all relocations
"useSSL" : false,
// Auto load the global security firewall
"autoLoadFirewall" : true,
// Activate handler/action based annotation security
"handlerAnnotationSecurity" : true,
// Activate security rule visualizer, defaults to false by default
"enableSecurityVisualizer" : false,
// JWT Settings
"jwt" : {
// The issuer authority for the tokens, placed in the `iss` claim
"issuer" : "",
// The jwt secret encoding key to use. This key is only effective within the `config/Coldbox.cfc`. Specifying within a module does nothing.
"secretKey" : getSystemSetting( "JWT_SECRET", "" ),
// by default it uses the authorization bearer header, but you can also pass a custom one as well or as an rc variable.
"customAuthHeader" : "x-auth-token",
// The expiration in minutes for the jwt tokens
"expiration" : 60,
// If true, enables refresh tokens, token creation methods will return a struct instead
// of just the access token. e.g. { access_token: "", refresh_token : "" }
"enableRefreshTokens" : false,
// The default expiration for refresh tokens, defaults to 30 days
"refreshExpiration" : 10080,
// The Custom header to inspect for refresh tokens
"customRefreshHeader" : "x-refresh-token",
// If enabled, the JWT validator will inspect the request for refresh tokens and expired access tokens
// It will then automatically refresh them for you and return them back as
// response headers in the same request according to the customRefreshHeader and customAuthHeader
"enableAutoRefreshValidator" : false,
// Enable the POST > /cbsecurity/refreshtoken API endpoint
"enableRefreshEndpoint" : true,
// encryption algorithm to use, valid algorithms are: HS256, HS384, and HS512
"algorithm" : "HS512",
// Which claims neds to be present on the jwt token or `TokenInvalidException` upon verification and decoding
"requiredClaims" : [],
// The token storage settings
"tokenStorage" : {
// enable or not, default is true
"enabled" : true,
// A cache key prefix to use when storing the tokens
"keyPrefix" : "cbjwt_",
// The driver to use: db, cachebox or a WireBox ID
"driver" : "cachebox",
// Driver specific properties
"properties" : { "cacheName" : "default" }
}
}
}
};/**
* Copyright since 2016 by Ortus Solutions, Corp
* www.ortussolutions.com
* ---
* All security validators must implement the following methods
*/
interface{
/**
* This function is called once an incoming event matches a security rule.
* You will receive the security rule that matched and an instance of the ColdBox controller.
*
* You must return a struct with two keys:
* - allow:boolean True, user can continue access, false, invalid access actions will ensue
* - type:string(authentication|authorization) The type of block that ocurred. Either an authentication or an authorization issue.
*
* @return { allow:boolean, type:string(authentication|authorization) }
*/
struct function ruleValidator( required rule, required controller );
/**
* This function is called once access to a handler/action is detected.
* You will receive the secured annotation value and an instance of the ColdBox Controller
*
* You must return a struct with two keys:
* - allow:boolean True, user can continue access, false, invalid access actions will ensue
* - type:string(authentication|authorization) The type of block that ocurred. Either an authentication or an authorization issue.
*
* @return { allow:boolean, type:string(authentication|authorization) }
*/
struct function annotationValidator( required securedValue, required controller );
}struct function ruleValidator( required rule, required controller ){
return permissionValidator( rule.permissions, controller, rule );
}
struct function annotationValidator( required securedValue, required controller ){
return permissionValidator( securedValue, controller );
}
private function permissionValidator( permissions, controller, rule ){
var results = { "allow" : false, "type" : "authentication", "messages" : "" };
var user = security.getCurrentUser();
// First check if user has been authenticated.
if( user.isLoaded() AND user.isLoggedIn() ){
// Do we have the right permissions
if( len( arguments.permissions ) ){
results.allow = user.checkPermission( arguments.permission );
results.type = "authorization";
} else {
results.allow = true;
}
}
return results;
}property name="tokenStorage" inject="DBTokenStorage@cbsecurity";
property name="tokenStorage" inject="CacheTokenStorage@cbsecurity";
jwtAuth().getTokenStorage()interface{
/**
* Configure the storage by passing in the properties
*
* @return JWTStorage
*/
any function configure( required properties );
/**
* Set a token in the storage
*
* @key The cache key
* @token The token to store
* @expiration The token expiration
*
* @return JWTStorage
*/
any function set( required key, required token, required expiration );
/**
* Verify if the passed in token key exists
*
* @key The cache key
*/
boolean function exists( required key );
/**
* Retrieve the token via the cache key, if the key doesn't exist a TokenNotFoundException will be thrown
*
* @key The cache key
* @defaultValue If not found, return a default value
*
* @throws TokenNotFoundException
*/
any function get( required key, defaultValue );
/**
* Invalidate/delete one or more keys from the storage
*
* @key A cache key or an array of keys to clear
*
* @return JWTStorage
*/
any function clear( required any key );
/**
* Clear all the keys in the storage
*
* @async Run in a separate thread
*
* @return JWTStorage
*/
any function clearAll( boolean async=false );
/**
* Retrieve all the jwt keys stored in the storage
*/
array function keys();
/**
* The size of the storage
*/
numeric function size();
}// Secure the entire handler
component secured{
function index(event,rc,prc){}
function list(event,rc,prc){}
}
// Same as this
component secured=true{
}
// Do NOT secure the handler
component secured=false{
}
// Same as this, no annotation!
component{
function index(event,rc,prc) secured{
}
function list(event,rc,prc) secured="list"{
}
}// Secure this handler
component secured="admin,users"{
function index(event,rc,prc) secured="list"{
}
function save(event,rc,prc) secured="write"{
}
}cbsecurity = {
validator = "CFValidator@cbsecurity"
}
component extends="coldbox.system.Interceptor"{
function cbSecurity_onJWTCreation( event, interceptData ){
// do what you like here
}
}component{
function login( event, rc, prc ){
event.setView( "security/login" );
}
function doLogin( event, rc, prc ){
cflogin(
idletimeout=getSetting( "LoginTimeout" ),
applicationtoken=getSetting( "AppName" ),
cookiedomain='myapp.com'
){
cfoauth(
type = "Google",
clientid = "YOUR_CLIENT_ID",
secretkey = "YOUR_GOOGLE_CLIENTSECRET",
redirecturi = "YOUR_CALLBACK_URI",
result = "res",
scope = "YOUR_SCOPES",
state = "cftoken=#cftoken#"
);
cfloginuser(
name = "#res.other.email#",
password = "#res.access_token#",
roles = "user"
);
}
}
function doLogout( event, rc, prc ){
cflogout();
relocate( "security.login" );
}
}{
"whitelist" : "",
"securelist" : "",
"match" : "event", // or url
"roles" : "",
"permissions" : "",
"redirect" : "",
"overrideEvent" : "",
"useSSL" : false,
"action" : "redirect", // or override
"module" : ""
};{
"secureList" : "*",
"redirect" : "mysecret.event"
}{
"secureList" : "*",
"overrideEvent" : "main.onInvalidEvent"
}{
"secureList" : "^api.*",
"action" : "override"
}{
"secureList" : ".*",
"whitelist : "^login"
}{
"secureList" : ".*",
"whitelist" : "login",
"permissions" : "nonExistingPermission"
}// Module Settings
moduleSettings = {
// CB Security
cbSecurity : {
// The global invalid authentication event or URI or URL to go if an invalid authentication occurs
"invalidAuthenticationEvent" : "",
// Default Authentication Action: override or redirect when a user has not logged in
"defaultAuthenticationAction" : "redirect",
// The global invalid authorization event or URI or URL to go if an invalid authorization occurs
"invalidAuthorizationEvent" : "",
// Default Authorization Action: override or redirect when a user does not have enough permissions to access something
"defaultAuthorizationAction" : "redirect",
// You can define your security rules here or externally via a source
// specify an array for inline, or a string (db|json|xml|model) for externally
"rules" : [],
// The validator is an object that will validate rules and annotations and provide feedback on either authentication or authorization issues.
"validator" : "CBAuthValidator@cbsecurity",
// The WireBox ID of the authentication service to use in cbSecurity which must adhere to the cbsecurity.interfaces.IAuthService interface.
"authenticationService" : "authenticationService@cbauth",
// WireBox ID of the user service to use
"userService" : "",
// The name of the variable to use to store an authenticated user in prc scope if using a validator that supports it.
"prcUserVariable" : "oCurrentUser",
// If source is model, the wirebox Id to use for retrieving the rules
"rulesModel" : "",
// If source is model, then the name of the method to get the rules, we default to `getSecurityRules`
"rulesModelMethod" : "getSecurityRules",
// If source is db then the datasource name to use
"rulesDSN" : "",
// If source is db then the table to get the rules from
"rulesTable" : "",
// If source is db then the ordering of the select
"rulesOrderBy" : "",
// If source is db then you can have your custom select SQL
"rulesSql" : "",
// Use regular expression matching on the rule match types
"useRegex" : true,
// Force SSL for all relocations
"useSSL" : false,
// Auto load the global security firewall
"autoLoadFirewall" : true,
// Activate handler/action based annotation security
"handlerAnnotationSecurity" : true,
// Activate security rule visualizer, defaults to false by default
"enableSecurityVisualizer" : false,
// JWT Settings
"jwt" : {
// The issuer authority for the tokens, placed in the `iss` claim
"issuer" : "",
// The jwt secret encoding key to use
"secretKey" : getSystemSetting( "JWT_SECRET", "" ),
// by default it uses the authorization bearer header, but you can also pass a custom one as well or as an rc variable.
"customAuthHeader" : "x-auth-token",
// The expiration in minutes for the jwt tokens
"expiration" : 60,
// If true, enables refresh tokens, longer lived tokens (not implemented yet)
"enableRefreshTokens" : false,
// The default expiration for refresh tokens, defaults to 30 days
"refreshExpiration" : 43200,
// encryption algorithm to use, valid algorithms are: HS256, HS384, and HS512
"algorithm" : "HS512",
// Which claims neds to be present on the jwt token or `TokenInvalidException` upon verification and decoding
"requiredClaims" : [] ,
// The token storage settings
"tokenStorage" : {
// enable or not, default is true
"enabled" : true,
// A cache key prefix to use when storing the tokens
"keyPrefix" : "cbjwt_",
// The driver to use: db, cachebox or a WireBox ID
"driver" : "cachebox",
// Driver specific properties
"properties" : {
"cacheName" : "default"
}
}
}
}
};
jwt : {
...
// If true, enables refresh tokens, token creation methods will return a struct instead of just an access token string
// e.g. { access_token: "", refresh_token : "" }
"enableRefreshTokens" : false,
// The default expiration for refresh tokens in minutes, defaults to 7 days
"refreshExpiration" : 10080,
// The custom header to inspect for refresh tokens
"customRefreshHeader" : "x-refresh-token",
// If enabled, the JWT validator will inspect the request for refresh tokens and expired access tokens
// It will then automatically refresh them for you and return them back as
// response headers in the same request according to the `customRefreshHeader` and `customAuthHeader`
"enableAutoRefreshValidator" : false,
// Enable the POST > /cbsecurity/refreshtoken API endpoint
"enableRefreshEndpoint" : false
}{
"access_token" : "AYjcyMzY3ZDhiNmJk",
"refresh_token" : "RjY2NjM5NzA2OWJj"
}var newTokens = jwtService.refreshToken();
var newTokens = jwtService.refreshToken( storedRefreshToken );/**
* Manually refresh tokens by passing a valid refresh token and returning two new tokens:
* <code>{ access_token : "", refresh_token : "" }</code>
*
* @refreshToken A refresh token
* @customClaims A struct of custom claims to apply to the new tokens
*
* @throws RefreshTokensNotActive If the setting enableRefreshTokens is false
* @throws TokenExpiredException If the token has expired or no longer in the storage (invalidated)
* @throws TokenInvalidException If the token doesn't verify decoding
* @throws TokenNotFoundException If the token cannot be found in the headers
*
* @return A struct of { access_token : "", refresh_token : "" }
*/
struct function refreshToken( token = discoverRefreshToken(), struct customClaims = {} ){
"access_token" : "AYjcyMzY3ZDhiNmJk",
"refresh_token" : "RjY2NjM5NzA2OWJj"
}// Mixin: Handlers/Interceptors/Layouts/Views
cbsecure()
// Injection
property name="cbSecurity" inject="@cbSecurity"// Verify the currently logged in user has those permission,
// else throw a NotAuthorized exception
cbSecurity.secure( permissions, [message] );
cbsecure().secure( permissions, [message] );// 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] );when( permissions, success, fail )// Lambda approach
( user, permissions ) => {
// your code here
};
// UDF/Closure
function( user, permissions ){
// your code here
}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
);// When all permissions must exist in the user
whenAll( permissions, success, fail)
// When none of the permissions exist in the user
whenNone( permissions, success, fail )// 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 );<cfif cbsecure().has( "USER_ADMIN" )>
This is only visible to user admins!
</cfif>
<cfif cbsecure().has( "SYSTEM_ADMIN" )>
<a href="/user/impersonate/#prc.user.getId()#">Impersonate User</a>
</cfif>
<cfif cbsecure().sameUser( prc.user )>
<i class="fa fa-star">This is You!</i>
</cfif>if( cbSecurity.has( "PERM" ) ){
auditUser();
}
if( cbSecurity.sameUser( prc.incomingUser ) ){
// you can change your gravatar
}secureWhen( context, [errorMessage] )// 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() );( user ) => {}
function( user );event.secureView( permissions, successView, failView )autoLoadFirewall : falsehandlerAnnotationSecurity : falseenableSecurityVisualizer : truesettings = {
// CB Security Module Settings
cbsecurity : {
// Module Relocation when an invalid access is detected, instead of each rule declaring one.
"invalidAuthenticationEvent" : "api:Home.onInvalidAuth",
// Default Auhtentication Action: override or redirect when a user has not logged in
"defaultAuthenticationAction" : "override",
// Module override event when an invalid access is detected, instead of each rule declaring one.
"invalidAuthorizationEvent" : "api:Home.onInvalidAuthorization",
// Default invalid action: override or redirect when an invalid access is detected, default is to redirect
"defaultAuthorizationAction" : "override",
// The validator to use for this module
"validator" : "JWTService@cbsecurity",
// You can define your security rules here or externally via a source
"rules" : [ { "secureList" : "api:Secure\.*" } ]
}
}interceptors = [
{
class="cbsecurity.interceptors.Security",
name="FirewallName",
properties={
// All Settings from above
}
]

csrf() : To generate a hidden field (csrf) with the tokenmoduleSettings = {
// CB Security
cbSecurity : {
// The global security rules
"rules" : [
// should use direct action and do a global redirect
{
"whitelist": "",
"securelist": "admin",
"match": "event",
"roles": "admin",
"permissions": "",
"action" : "redirect"
},
// no action, use global default action
{
"whitelist": "",
"securelist": "noAction",
"match": "url",
"roles": "admin",
"permissions": ""
},
// Using overrideEvent only, so use an explicit override
{
"securelist": "ruleActionOverride",
"match": "url",
"overrideEvent": "main.login"
},
// direct action, use global override
{
"whitelist": "",
"securelist": "override",
"match": "url",
"roles": "",
"permissions": "",
"action" : "override"
},
// Using redirect only, so use an explicit redirect
{
"securelist": "ruleActionRedirect",
"match": "url",
"redirect": "main.login"
}
]
}
};moduleSettings = {
cbcsrf : {
// By default we load up an interceptor that verifies all non-GET incoming requests against the token validations
enableAutoVerifier : false,
// A list of events to exclude from csrf verification, regex allowed: e.g. stripe\..*
verifyExcludes : [],
// By default, all csrf tokens have a life-span of 30 minutes. After 30 minutes, they expire and we aut-generate new ones.
// If you do not want expiring tokens, then set this value to 0
rotationTimeout : 30,
// Enable the /cbcsrf/generate endpoint to generate cbcsrf tokens for secured users.
enableEndpoint : false,
// The WireBox mapping to use for the CacheStorage
cacheStorage : "CacheStorage@cbstorages",
// Enable/Disable the cbAuth login/logout listener in order to rotate keys
enableAuthTokenRotator : false
}
};/**
* Provides a random token and stores it in the coldbox cache storages. You can also provide a specific key to store.
*
* @key A random token is generated for the key provided.
* @forceNew If set to true, a new token is generated every time the function is called. If false, in case a token exists for the key, the same key is returned.
*
* @return csrf token
*/
string function csrfToken( string key='', boolean forceNew=false )
/**
* Validates the given token against the same stored in the session for a specific key.
*
* @token Token that to be validated against the token stored in the session.
* @key The key against which the token be searched.
*
* @return Valid or Invalid Token
*/
boolean function csrfVerify( required string token='', string key='' )
/**
* Generate a random token and build a hidden form element so you can submit it with your form
*
* @key A random token is generated for the key provided.
* @forceNew If set to true, a new token is generated every time the function is called. If false, in case a token exists for the key, the same key is returned.
*
* @return HTML of the hidden field (csrf)
*/
string function csrf( string key='', boolean forceNew=false )
/**
* Generate a random token in a hidden form element and javascript that will refresh the page automatically when the token expires
*
* @key A random token is generated for the key provided. CFID is the default
* @forceNew If set to true, a new token is generated every time the function is called. If false, in case a token exists for the key, the same key is returned.
*
* @return HTML of the hidden field (csrf)
*/
string function csrfField( string key='', boolean forceNew=false )
/**
* Clears out all csrf token stored
*/
function csrfRotate()component{
property name="cbcsrf" inject="@cbcsrf";
}moduleSettings = {
cbcsrf : {
// Enable/Disable the cbAuth login/logout listener in order to rotate keys
enableAuthTokenRotator : true
}
};component{
function doLogin( event, rc, prc ){
if( valid login ){
// login user
csrfRotate();
}
}
function logout( event, rc, prc ){
csrfRotate();
}
}component extends="coldbox.system.EventHandler"{
function signUp( event, rc, prc ){
// Store this in a hidden field in the form
prc.token = csrfGenerate();
event.setView( "registration/signup" );
}
function signUpProcess( event, rc, prc ){
// Verify CSFR token from form
if( csrfVerify( rc.token ?: '' ) {
// save form
} else {
// Something isn't right
relocate( 'handler.signup' );
}
}
}cbcsrf : {
// Enable the verifier
enableAutoVerifier : true,
// A list of events to exclude from csrf verification, regex allowed: e.g. stripe\..*
verifyExcludes : [
]
}component{
function doTestSave( event, rc, prc ) skipCsrf{
}
}moduleSettings = {
// CB Security
cbSecurity : {
rules : "db", // Rules are in the database
rulesDSN : "myDatasource", // The datasource
rulesTable : "securityRules", // The table that has the rules
rulesOrderBy : "order asc" // An optional ordering
}
};getToken():string - Get the stored token from prc.jwt_token, if it doesn't exist, it tries to parse it via parseToken(), if not token is set this will be an empty string.In this page you will find a thorough overview of the capabilities of the ColdBox Security module.
{
"iat": 1569340662,
"scope": "",
"iss": "http://127.0.0.1:56596/",
"sub": 123,
"exp": 1569344262,
"jti": "12954F907C0535ABE97F761829C6BD11"
}{
"iat": 1569340662,
"scope": "",
"iss": "http://127.0.0.1:56596/",
"sub": 2222,
"exp": 1569344262,
"jti": "234234CDDEEDD",
"cbsecurity_refresh" : true
}// Injection
property name="jwtService" inject="JwtService@cbsecurity";
// Helper Method in any handler/layout/interceptors/views
jwtAuth()cbsecurity : {
// The WireBox ID of the authentication service to use in cbSecurity which must adhere to the cbsecurity.interfaces.IAuthService interface.
authenticationService : "authenticationService@cbauth",
// WireBox ID of the user service to use
userService : "",
// The name of the variable to use to store an authenticated user in prc scope if using a validator that supports it.
prcUserVariable : "oCurrentUser",
// JWT Settings
jwt : {
// The issuer authority for the tokens, placed in the `iss` claim
issuer : "",
// The jwt secret encoding key, defaults to getSystemEnv( "JWT_SECRET", "" )
// This key is only effective within the `config/Coldbox.cfc`. Specifying within a module does nothing.
secretKey : getSystemSetting( "JWT_SECRET", "" ),
// by default it uses the authorization bearer header, but you can also pass a custom one as well.
customAuthHeader : "x-auth-token",
// The expiration in minutes for the jwt tokens
expiration : 60,
// If true, enables refresh tokens, token creation methods will return a struct instead
// of just the access token. e.g. { access_token: "", refresh_token : "" }
enableRefreshTokens : false,
// The default expiration for refresh tokens, defaults to 30 days
refreshExpiration : 10080,
// The Custom header to inspect for refresh tokens
customRefreshHeader : "x-refresh-token",
// If enabled, the JWT validator will inspect the request for refresh tokens and expired access tokens
// It will then automatically refresh them for you and return them back as
// response headers in the same request according to the customRefreshHeader and customAuthHeader
enableAutoRefreshValidator : false,
// Enable the POST > /cbsecurity/refreshtoken API endpoint
enableRefreshEndpoint : true,
// encryption algorithm to use, valid algorithms are: HS256, HS384, and HS512
algorithm : "HS512",
// Which claims neds to be present on the jwt token or `TokenInvalidException` upon verification and decoding
requiredClaims : [] ,
// The token storage settings
tokenStorage : {
// enable or not, default is true
"enabled" : true
// A cache key prefix to use when storing the tokens
"keyPrefix" : "cbjwt_",
// The driver to use: db, cachebox or a WireBox ID
"driver" : "cachebox",
// Driver specific properties
"properties" : {
cacheName : "default"
}
}
}
}$ openssl pkcs8 -topk8 -nocrypt -in privatekey.pem -out privatekey.pk8/**
* Copyright since 2016 by Ortus Solutions, Corp
* www.ortussolutions.com
* ---
* If you use the jwt services, then your jwt subject user must implement this interface
*/
interface{
/**
* A struct of custom claims to add to the JWT token when creating it
*
* @payload The actual payload structure that was used in the request
*
* @return A structure of custom claims
*/
struct function getJwtCustomClaims( required struct payload );
/**
* This function returns an array of all the scopes that should be attached to the JWT token that will be used for authorization.
*/
array function getJwtScopes();
}
component accessors="true" {
property name="auth" inject="authenticationService@cbauth";
property name="id";
property name="firstName";
property name="lastName";
property name="username";
property name="password";
function init(){
variables.id = "";
variables.firstName = "";
variables.lastName = "";
variables.username = "";
variables.password = "";
variables.permissions = [ "write", "read" ];
return this;
}
boolean function isLoaded(){
return ( !isNull( variables.id ) && len( variables.id ) );
}
/**
* A struct of custom claims to add to the JWT token
*/
struct function getJWTCustomClaims(){
return { "role" : "admin" };
}
/**
* This function returns an array of all the scopes that should be attached to the JWT token that will be used for authorization.
*/
array function getJWTScopes(){
return variables.permissions;
}
/**
* Verify if the user has one or more of the passed in permissions
*
* @permission One or a list of permissions to check for access
*
*/
boolean function hasPermission( required permission ){
if ( isSimpleValue( arguments.permission ) ) {
arguments.permission = listToArray( arguments.permission );
}
return arguments.permission
.filter( function(item){
return ( variables.permissions.ListFindNoCase( item ) );
} )
.len();
}
}{
"access_token" : "AYjcyMzY3ZDhiNmJk",
"refresh_token" : "RjY2NjM5NzA2OWJj"
}post( "/api/login" , "api.auth.login" );
post( "/api/logout" , "api.auth.logout" );
post( "/api/register" , "api.auth.register" );component{
function login( event, rc, prc ){
param rc.username = "";
param rc.password = "";
try {
var token = jwtAuth().attempt( rc.username, rc.password );
return {
"error" : false,
"data" : token,
"message" : "Bearer token created and it expires in #jwtAuth().getSettings().jwt.expiration# minutes"
};
} catch ( "InvalidCredentials" e ) {
event.setHTTPHeader( statusCode = 401, statusText = "Unauthorized" );
return { "error" : true, "data" : "", "message" : "Invalid Credentials" };
}
}
function register( event, rc, prc ){
param rc.firstName = "";
param rc.lastName = "";
param rc.username = "";
param rc.password = "";
prc.oUser = populateModel( "User" );
userService.create( prc.oUser );
var token = jwtAuth().fromuser( prc.oUser );
return {
"error" : false,
"data" : token,
"message" : "User registered correctly and Bearer token created and it expires in #jwtAuth().getSettings().jwt.expiration# minutes"
};
}
function logout( event, rc, prc ){
jwtAuth().logout();
return { "error" : false, "data" : "", "message" : "Successfully logged out" };
}
}http {
# These settings affect outbound headers via proxy server
proxy_buffer_size 64k;
proxy_buffers 4 128k;
proxy_busy_buffers_size 128k;
# These settings affect the http client request headers
client_body_buffer_size 128k;
client_header_buffer_size 64k;
large_client_header_buffers 8 128k;
# These settings affect HTTP/2 headers, however some versions of NGINX will throw an error on HTTP/1 requests if these are not present
http2_max_header_size 128k;
http2_max_field_size 1000m;
}<VirtualHost 10.10.50.50:80>
ServerName www.mysite.com
LimitRequestFieldSize 128000
RewriteEngine On
...
...
</VirtualHost>cbsecurity_matchedRulegetSecurityRules() method from it

{
"whitelist" : "",
"securelist" : "",
"match" : "event", // or url
"roles" : "",
"permissions" : "",
"redirect" : "",
"overrideEvent" : "",
"useSSL" : false,
"action" : "redirect", // or override
"module" : ""
};// Secure the entire handler
component secured{
function index(event,rc,prc){}
function list(event,rc,prc){}
}
// Same as this
component secured=true{
}
// Do NOT secure the handler
component secured=false{
}
// Same as this, no annotation!
component{
function index(event,rc,prc) secured{
}
function list(event,rc,prc) secured="list"{
}
rules = [
{
"whitelist" : "", // A list of white list events or Uri's
"securelist" : "", // A list of secured list events or Uri's
"match" : "event", // Match the event or a url
"roles" : "", // Attach a list of roles to the rule
"permissions" : "", // Attach a list of permissions to the rule
"redirect" : "", // If rule breaks, and you have a redirect it will redirect here
"overrideEvent" : "", // If rule breaks, and you have an event, it will override it
"useSSL" : false, // Force SSL,
"action" : "", // The action to use (redirect|override) when no redirect or overrideEvent is defined in the rule.
"module" : "" // metadata we can add so mark rules that come from modules
};
]// CB Security
cbSecurity : {
// Global Relocation when an invalid access is detected, instead of each rule declaring one.
"invalidAuthenticationEvent" : "main.index",
// Global override event when an invalid access is detected, instead of each rule declaring one.
"invalidAuthorizationEvent" : "main.index",
// Default invalid action: override or redirect when an invalid access is detected, default is to redirect
"defaultAuthorizationAction" : "redirect",
// The global security rules
"rules" : [
// should use direct action and do a global redirect
{
"whitelist": "",
"securelist": "admin",
"match": "event",
"roles": "admin",
"permissions": "",
"action" : "redirect"
},
// no action, use global default action
{
"whitelist": "",
"securelist": "noAction",
"match": "url",
"roles": "admin",
"permissions": ""
},
// Using overrideEvent only, so use an explicit override
{
"securelist": "ruleActionOverride",
"match": "url",
"overrideEvent": "main.login"
},
// direct action, use global override
{
"whitelist": "",
"securelist": "override",
"match": "url",
"roles": "",
"permissions": "",
"action" : "override"
},
// Using redirect only, so use an explicit redirect
{
"securelist": "ruleActionRedirect",
"match": "url",
"redirect": "main.login"
}
]
}
};// Secure this handler
component secured{
function index(event,rc,prc){}
function list(event,rc,prc){}
}
// Same as this
component secured=true{
}
// Not the same as this
component secured=false{
}
// Or this
component{
function index(event,rc,prc) secured{
}
function list(event,rc,prc) secured="list"{
}
}// Secure this handler
component secured="admin,users"{
function index(event,rc,prc) secured="list"{
}
function save(event,rc,prc) secured="write"{
}
}/**
* This function is called once an incoming event matches a security rule.
* You will receive the security rule that matched and an instance of the ColdBox controller.
*
* You must return a struct with two keys:
* - allow:boolean True, user can continue access, false, invalid access actions will ensue
* - type:string(authentication|authorization) The type of block that ocurred. Either an authentication or an authorization issue.
*
* @return { allow:boolean, type:string(authentication|authorization) }
*/
struct function ruleValidator( required rule, required controller );
/**
* This function is called once access to a handler/action is detected.
* You will receive the secured annotation value and an instance of the ColdBox Controller
*
* You must return a struct with two keys:
* - allow:boolean True, user can continue access, false, invalid access actions will ensue
* - type:string(authentication|authorization) The type of block that ocurred. Either an authentication or an authorization issue.
*
* @return { allow:boolean, type:string(authentication|authorization) }
*/
struct function annotationValidator( required securedValue, required controller );cbsecurity = {
validator = "CBAuthValidator@cbsecurity"
} cbAuth: {
userServiceClass: "UserService"
}struct function ruleValidator( required rule, required controller ){
return validateSecurity( arguments.rule.roles );
}
struct function annotationValidator( required securedValue, required controller ){
return validateSecurity( arguments.securedValue );
}
private function validateSecurity( required roles ){
var results = { "allow" : false, "type" : "authentication" };
// Are we logged in?
if( isUserLoggedIn() ){
// Do we have any roles?
if( listLen( arguments.roles ) ){
results.allow = isUserInAnyRole( arguments.roles );
results.type = "authorization";
} else {
// We are satisfied!
results.allow.true;
}
}
return results;
}component singleton{
struct function ruleValidator( required rule, required controller ){
return permissionValidator( rule.permissions, controller, rule );
}
struct function annotationValidator( required securedValue, required controller ){
return permissionValidator( securedValue, controller );
}
private function permissionValidator( permissions, controller, rule ){
var results = { "allow" : false, "type" : "authentication" };
var user = getCurrentUser();
// First check if user has been authenticated.
if( user.isLoaded() AND user.isLoggedIn() ){
// Do we have the right permissions
if( len( arguments.permissions ) ){
results.allow = user.checkPermission( arguments.permission );
results.type = "authorization";
} else {
results.allow = true;
}
}
return results;
}
}// Mixin approach
cbSecure()
// Injection
property name="cbSecurity" inject="@CBSecurity";// Only allow access to user_admin
cbSecure().secure( "USER_ADMIN" );
// Only allow access if you have all of these permissions
cbSecure().secureAll( "EDITOR, POST_PUBLISH" )
// YOu must not have this permission, if you do, kick you out
cbSecure().secureNone( "FORGEBOX_USER" )
// Secure using security evaluations
// Kick out if you do not have the AUTHOR_ADMIN or you are not the same incoming author
cbSecurity.secureWhen(
cbSecurity.none( "AUTHOR_ADMIN" ) &&
!cbSecurity.sameUser( oAuthor )
)
// Secure using a closure
cbSecurity.secureWhen( ( user ) => !user.isConfirmed() );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
);function edit( event, rc, prc ){
var oUser = userService.getOrFail( rc.id ?: "" );
if( !sameUser( oUser ) ){
relocate( "/users" );
}
}
<cfif cbsecure().all( "USER_ADMIN,USER_EDITOR" )>
This is only visible to user admins!
</cfif>
<cfif cbsecure().has( "SYSTEM_ADMIN" )>
<a href="/user/impersonate/#prc.user.getId()#">Impersonate User</a>
</cfif>
<cfif cbsecure().sameUser( prc.user )>
<i class="fa fa-star">This is You!</i>
</cfif>component{
function index( event, rc, prc ){
event.secureView( "USER_ADMIN", "users/admin/index", "users/index" );
}
}




// CB Security
cbSecurity : {
// The WireBox ID of the authentication service to use in cbSecurity which must adhere to the cbsecurity.interfaces.IAuthService interface.
"authenticationService" : "authenticationService@cbauth"
}/**
* Copyright since 2016 by Ortus Solutions, Corp
* www.ortussolutions.com
* ---
* If you register an authentication service with cbsecurity it must adhere to this interface
*/
interface{
/**
* Get the authenticated user
*
* @throws NoUserLoggedIn : If the user is not logged in
*
* @return User that implements IAuthUser
*/
any function getUser();
/**
* Verifies if a user is logged in
*/
boolean function isLoggedIn();
/**
* Try to authenticate a user into the system. If the authentication fails an exception is thrown, else the logged in user object is returned
*
* @username The username to log in with
* @password The password to log in with
*
* @throws InvalidCredentials
*
* @return User : The logged in user object
*/
any function authenticate( required username, required password );
/**
* Login a user into our persistent scopes
*
* @user The user object to log in
*
* @return The same user object so you can do functional goodness
*/
function login( required user );
/**
* Logs out the currently logged in user from the system
*/
function logout();
}interface{
/**
* Return the unique identifier for the user
*/
function getId();
/**
* Verify if the user has one or more of the passed in permissions
*
* @permission One or a list of permissions to check for access
*
*/
boolean function hasPermission( required permission );
}interface{
/**
* Verify if the incoming username/password are valid credentials.
*
* @username The username
* @password The password
*/
boolean function isValidCredentials( required username, required password );
/**
* Retrieve a user by username
*
* @return User that implements JWTSubject and/or IAuthUser
*/
function retrieveUserByUsername( required username );
/**
* Retrieve a user by unique identifier
*
* @id The unique identifier
*
* @return User that implements JWTSubject and/or IAuthUser
*/
function retrieveUserById( required id );
}