Blog

Gavin Pickin

December 23, 2017

Spread the word


Share your thoughts

At Ortus Solutions, we love the holidays, and we wanted to gift you a gift of developer productivity, we will share a few tips and tricks that will keep giving all year around. In this series we'll be giving you 12 ContentBox tips. Keep your eye out for other 12 tips of Christmas series on our blog, including a new one this year, 12 modules of Christmas on ForgeBox.

Day 10 - Security and Permissions in ContentBox Modules - One of the reasons I love working with ContentBox is all of the built in User management, permissions, roles, and creating ColdBox modules to extend ContentBox is easy. Today we'll look at how to use Security and Permissions in your ContentBox modules.

If your Module is using the ContentBox Module lifecycle, ContentBox preps your request, and handles tasks like themes, settings, checking for a logged in user etc. The current logged in user, is always available to you in the prc. This is handled by an interceptor, to ensure it is done on every request. Whether you are using a front end or admin module, you can access that user with `prc.oCurrentAuthor`

A great addition to your ContentBox Module ( also works with straight ColdBox apps ) is a module designed by Eric Peterson, called CB Guard. https://github.com/coldbox-modules/cbguard

This gives us a simple but powerful convention based security approach. There is a lot of great information on using this module in the readme on github, but we'll give you a run down here, and some secrets to using it with ContentBox.

To ensure a user is logged in to access a handler, you can add the metadata `secured` to your handler. This locks down the entire handler

component secured {

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

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

}

To ensure a user is logged in to access an action, you can add the metadata `secured` to your action.

component {

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

}

To ensure a user is logged in and has the correct permission to access a handler, you can add the metadata `secured=”list,of,permissions”` to your handler. The user must have one of the listed permissions.

component secured="admin" {
	
    function index( event, rc, prc ) {
        // ...
    }

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

}

To ensure a user is logged in and has the correct permission to access an action, you can add the metadata `secured=”list,of,permissions”` to your action. The user must have one of the listed permissions.

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

}

These two approaches can be combined and both handler and actions can be secured together:
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.

component secured {

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

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

}

Configuration

CBGuard can be used with any ColdBox app, all you have to do is configure it. You can set settings for redirects:

  • authenticationOverrideEvent 
  • authorizationOverrideEvent 
  • authenticationAjaxOverrideEvent 
  • authorizationAjaxOverrideEvent 

As well as the redirects, you have to define how CBGuard checks your user is authenticated, and if the authenticated user is authorized ( has permission ), by defining the CFC that conforms to AuthenticationServiceInterface and HasPermissionInterface.

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

moduleSettings = {
    cbguard = {
        authenticationService = "SecurityService@myapp"
    }
};

The default authenticationService for cbguard is AuthenticationService@cbauth. cbauth follows the AuthenticationServiceInterface out of the box.

Advanced Setup - Overriding the Interface to work with ContentBox

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"
        }
    }
};

Using CBGuard in a ContentBox App

Here is an example config for a real ContentBox app using CBGaurd, just add this into you /config/ColdBox.cfc

moduleSettings = {
	cbguard = {
		authenticationService = "UserService@rccore",
		methodNames = {
			isLoggedIn    = "isLoggedIn",
			getUser       = "getAuthorSession",
			hasPermission = "checkPermission"
		},
		authenticationOverrideEvent			= "security:login.new",
		authorizationOverrideEvent			= "ui:auth.onAuthorizationFailure",
		authenticationAjaxOverrideEvent		= "security:login.new",
		authorizationAjaxOverrideEvent		= "ui:auth.onAuthorizationFailure"
	}
};

What are we doing in this config?

  • We are using a custom authenticationService CFC, we'll share below
  • We are changing method names, so they match ContentBox's normal methods for checking Permission, and checking if a user is logged in for example.
  • We set authentication ( not logged in error ) redirects to the security module, with the action login.new.
  • We set authorization ( logged in but not the right permission ) redirects to the UI module, with the action auth.onAuthorizationFailure.

UserService.cfc required for CBGuard with ContentBox

Other than this configuration, the only code changes we need to implement, are in the UserService, lets look at that UserService.cfc.
We are looking at merging these requirements of CBGuard to make this even easier, but for now, you need to create a CFC like so.

/**
* User Service
* This User Service extends the ContentBox Author Service, allowing us to override and extend ContentBox's author service for the needs of this application
*/
component singleton extends="contentbox.models.security.AuthorService" {
	

	/**
	* Constructor
	*/
	public UserService function init(){
		super.init();
		return this;
	}

	/**
	* Proxy the getAuthorSession to the ContentBox security service getAuthorSession()
	*
	* @return author entity of the logged in user, or an empty author entity
	*/
	function getAuthorSession(){
		return securityService.getAuthorSession();
	}

	/**
	* Determinies if there is currently a user logged in
	*
	* @return boolean value - if there is a user logged in, or not.
	*/
	function isLoggedIn(){
		var author = getAuthorSession();
		return ( author.isLoaded() && author.isLoggedIn() );
	}
}

With some simple meta data, a little config, and this simple CFC, you can now use authentication and authorization all over your ContentBox modules.
Try it out, and let us know what you think.

Happy Holidays

Add Your Comment

Recent Entries

Ortus June 2024 Newsletter!

Ortus June 2024 Newsletter!

Welcome to the latest edition of the Ortus Newsletter! This month, we're excited to bring you highlights from our sessions at CFCamp and Open South Code, as well as a sneak peek into our upcoming events. Discover the latest developments in BoxLang, our dynamic new JVM language, and catch up on all the insightful presentations by our expert team. Let's dive in!

Maria Jose Herrera
Maria Jose Herrera
June 28, 2024
BoxLang June 2024 Newsletter!

BoxLang June 2024 Newsletter!

We're thrilled to bring you the latest updates and exciting developments from the world of BoxLang. This month, we're diving into the newest beta release, introducing a new podcast series, showcasing innovative integrations, and sharing insights from recent events. Whether you're a seasoned developer or just getting started, there's something here for everyone to explore and enjoy.

Maria Jose Herrera
Maria Jose Herrera
June 28, 2024
BoxLang 1.0.0 Beta 3 Launched

BoxLang 1.0.0 Beta 3 Launched

We are thrilled to announce the release of BoxLang 1.0.0-Beta 3! This latest beta version is packed with exciting new features and essential bug fixes, including robust encryption functionality, enhanced Java interoperability, and more efficient event handling. Key highlights include the introduction of query caching capabilities, seamless coercion of Java Single Abstract Method (SAM) interfaces from BoxLang functions, and support for virtual thread executors. So, let’s dive into the details of what’s new in BoxLang 1.0.0-Beta 3 and how you can start leveraging these updates today!

Luis Majano
Luis Majano
June 28, 2024