FORGEBOX Enterprise 🚀 - Take your ColdFusion (CFML) Development to Modern Times! Learn More...

cbguard

v4.0.0 Public

cbguard

Master Branch Build Status

Annotation driven guards for authentication and authorization in ColdBox

Usage

cbguard lets us lock down methods to logged in users and users with specific permissions using one annotation — secured. Just sticking the secured annotation on a handler or action is enough to require a user to log in before executing those events.

Here's an example of how to lock down an entire handler:

component secured {

    function index( event, rc, prc ) {
        // ...
    }

    function show( event, rc, prc ) {
        // ...
    }

}

You can be more specific and lock down only specific actions using the same annotation:

component {

    function create( event, rc, prc ) secured {
        // ...
    }

}

You can further lock down handlers and actions to a list of specific permissions. If specified, the logged in user must have one of the permissions in the list specified.

component secured="admin" {

    function index( event, rc, prc ) {
        // ...
    }

    function show( event, rc, prc ) {
        // ...
    }

}

In the above component, the user must have the admin permission to access the actions in this handler.

component {

    function show( event, rc, prc ) secured="admin,reviews_posts" {
        // ...
    }

}

Individual actions can be secured in the same way. Above, the show action requires the logged in user to have either the admin or the reviews_posts permission.

These two approaches can be combined and both handler and actions can be secured together:

component secured {

    function index( event, rc, prc ) {
        // ...
    }

    function new( event, rc, prc ) secured="create_posts" {
        // ...
    }

}

While the user needs to be logged in to interact at all with this handler, they also need the create_posts permission to interact with the new action.

Redirects

When a user is denied access to a action, an event of your choosing is executed instead. There are four keys that can be set in the moduleSettings struct that all come with good defaults.

  1. authenticationOverrideEvent (Default: Main.onAuthenticationFailure)

This is the event that is executed when the user is not logged in and is attempting to execute a secured action, whether or not that handler or action has permissions.

  1. authorizationOverrideEvent (Default: same as authenticationOverrideEvent)

This is the event that is executed when the user is logged in and is attempting to execute a secured action but does not have the requisite permissions.

  1. authenticationAjaxOverrideEvent (Default: Main.onAuthenticationFailure)

This is the event that is executed when the user is not logged in and is attempting to execute a secured action via ajax (event.isAjax()), whether or not that handler or action has permissions. By default, this will execute the same action that is configured for authenticationOverrideEvent.

  1. authorizationAjaxOverrideEvent (Default: same as authorizationOverrideEvent)

This is the event that is executed when the user is logged in and is attempting to execute a secured action via ajax (event.isAjax()) but does not have the requisite permissions. By default, this will execute the same action that is configured for authorizationOverrideEvent.

Setup

cbguard requires a bit of setup to function properly.

First, there are two interfaces that must be followed:

  1. AuthenticationServiceInterface
interface {

    /**
    * Must return an object that conforms to `HasPermissionsInterface`.
    * (This may be an implicit implements.)
    */
    public HasPermissionInterface function getUser();

    /**
    * Returns true if the user is logged in.
    */
    public boolean function isLoggedIn();

}

  1. HasPermissionInterface
interface {

    /**
    * Returns true if the user has the specified permission.
    */
    public boolean function hasPermission( required string permission );

}

Note: These interfaces are not enforced at compile time to give you maximum flexibility.

To configure the AuthenticationService, set the value of authenticationService in your moduleSettings to a WireBox mapping:

moduleSettings = {
    cbguard = {
        authenticationService = "[email protected]"
    }
};

The default authenticationService for cbguard is [email protected]. cbauth follows the AuthenticationServiceInterface out of the box.

config/ColdBox.cfc Settings

You can change the method names called on the AuthenticationService and the returned User if you need to. We highly discourage this use case, as it makes it harder to utilize the cbguard conventions across projects. However, should the need arise, you can modify the method names as follows:

moduleSettings = {
    cbguard = {
        methodNames = {
            isLoggedIn    = "getIsLoggedIn",
            getUser       = "retrieveUser",
            hasPermission = "checkPermission"
        }
    }
};

Additionally, you can modify the override action for each of the event types:

moduleSettings = {
    cbguard = {
        overrideActions = {
            authenticationOverrideEvent = "relocate",
            authenticationAjaxOverrideEvent = "override",
            authorizationOverrideEvent = "relocate",
            authorizationAjaxOverrideEvent = "override"
        }
    }
};

relocate refers to calling relocate on the controller. The user will be redirected to the new page. override refers to event.overrideEvent. This will not redirect but simply change the running event.

Module Overrides

All of the cbguard settings can be overriden inside a module. This allows modules, such as an API module, to provide their own authentication services as well as redirect events.

To specify some overrides, create a cbguard struct in your desired module's settings in that module's ModuleConfig.cfc.

component {

    this.name = "myModule";

    function configure() {
        settings = {
            "cbguard" = {
                "authenticationOverrideEvent" = "myModule:Main.onAuthenticationFailure",
                "authorizationOverrideEvent" = "myModule:Main.onAuthorizationFailure"
            }
        };
    }

}

Local Handler Overrides

If an onAuthenticationFailure or onAuthorizationFailure method exists on the handler being secured, it will be used in the case of an authentication or authorization failure event, respectively.

// handlers/Admin.cfc
component secured {

    function index( event, rc, prc ) {
        event.setView( "admin/index" );
    }

    function secret( event, rc, prc ) secured="superadmin" {
        event.setView( "admin/secret" );
    }

    function onAuthenticationFailure( event, rc, prc ) {
        relocate( "/login" );
    }

    function onAuthenticationFailure( event, rc, prc ) {
        flash.put( "authorizationError", "You don't have the correct permissions to access that resource." );
        redirectBack(); // from the redirectBack module
    }

}

Override Order

cbguard will process your authorization and authentication failures in the following order:

  1. Inline handler methods (onAuthenticationFailure & onAuthorizationFailure within your handlers).
  2. cbguard settings in the ModuleConfig of the handler's module. (Overrides in modules_app/api/ModuleConfig.cfc when the handler is in the module, i.e. modules_app/api/handlers/Main.cfc.)
  3. Overrides in config/ColdBox.cfc using moduleSettings.
  4. Default settings for the module.

autoRegisterInterceptor

If you need more control over the order of your interceptors you can disable the automatic loading of the SecuredEventInterceptor interceptor. If you do this you will need to register it yourself (most likely in config/ColdBox.cfc) as cbguard.interceptors.SecuredEventInterceptor.

v4.0.0

29 Oct 2019 — 16:20: 50 UTC

BREAKING

  • *: feat: Ability to use local override handlers if they exist (f4ab223)

v3.1.0

10 Sep 2019 — 21:47: 20 UTC

feat

  • ModuleConfig: Add a flag to prevent automatic registration (7d376f3)

fix

  • SecuredEventInterceptor: Ignore OPTIONS requests (c29810f)

v3.0.0

05 Sep 2019 — 19:09: 13 UTC

BREAKING

  • cbguard: Use ColdBox 5.6's handler metadata cache (72ba3d0)

feat

  • cbguard: Allow for per-module overrides of cbguard settings (2dbdc53)

fix

  • cbguard: Ensure implicit view events are still allowed (01d065e)

v2.0.0

16 Aug 2019 — 04:47: 13 UTC

BREAKING

  • SecuredEventInterceptor: Relocate by default for non-ajax events (8075b45)
  • build: Remove ACF 10 and ColdBox 4 support (b199f66)

build

  • box.json: Remove publish actions in favor of commandbox-semantic-release (ed2c53b)
  • travis: Use openjdk instead of oracle (81f9b45)
  • csr: Set up commandbox-semantic-release (c4f6077)

other

  • *: chore: Remove jmimemagic.log (0a7f7e0)

Here are all the versions for this package. Please note that you can leverage CommandBox package versioning to install any package you like. Please refer to our managing package version guide for more information.

Version Created Last Update Published By Stable Actions
Current
4.0.0 Oct 29 2019 11:20 AM Oct 29 2019 11:20 AM
Version History
3.1.0 Sep 10 2019 04:47 PM Sep 10 2019 04:47 PM
3.0.0 Sep 05 2019 02:09 PM Sep 05 2019 02:09 PM
2.0.0 Aug 15 2019 11:47 PM Aug 15 2019 11:47 PM
1.0.1 Sep 26 2018 09:49 AM Jun 04 2019 03:31 PM
1.0.0 Nov 22 2017 11:33 AM Nov 22 2017 11:33 AM

 

     
  • Nov 22 2017 11:33 AM
  • Oct 29 2019 11:20 AM
  • 897
  • 0
  • 2,888