ColdBox HMVC Documentation
DocsSourceSupportTraining
5.x
5.x
  • Introduction
  • Intro
    • Introduction
      • What's New With 5.6.0
      • What's New With 5.5.0
      • What's New With 5.4.0
      • What's New With 5.3.0
      • What's New With 5.2.0
      • What's New With 5.1.4
      • What's New With 5.1.3
      • What's New With 5.1.2
      • What's New With 5.1.1
      • What's New With 5.1.0
      • What's New With 5.0.0
      • Upgrading to ColdBox 5
      • About This Book
      • Author
  • For Newbies
    • 60 Minute Quick Start
      • Installing ColdBox
      • My First ColdBox Application
      • My First Handler & View
      • Linking Events Together
      • Working With Event Handlers
      • Adding A Layout
      • Adding A Model
      • RESTFul Data
      • Next Steps
  • Getting Started
    • Getting Started Guide
    • Installation
    • Conventions
    • Configuration
      • ColdBox.cfc
        • Configuration Directives
          • CacheBox
          • ColdBox
          • Conventions
          • Environments
          • Flash
          • InterceptorSettings
          • Interceptors
          • Layouts
          • LayoutSettings
          • LogBox
          • Modules
          • ModuleSettings
          • Settings
          • WireBox
        • System Settings (Java Properties and Environment Variables)
      • Using Settings
      • Bootstrapper - Application.cfc
  • The Basics
    • Request Context
    • Routing
      • Requirements
        • Rewrite Rules
      • Application Router
      • Routing DSL
        • Routing By Convention
        • Pattern Placeholders
        • Routing Methods
        • Resourceful Routes
        • Named Routes
        • Routing Groups
        • Routing Namespaces
      • Building Routable Links
      • RESTFul Extension Detection
      • HTTP Method Spoofing
      • HTML Base Tag
      • Pathinfo Providers
    • Event Handlers
      • How are events called?
      • Getting & Setting Values
      • Setting Views
      • Relocating
      • Rendering Data
      • Sending Files
      • Interception Methods
        • Pre Advices
        • Post Advices
        • Around Advices
      • Model Integration
        • Model Data Binding
      • HTTP Method Security
      • Implicit Methods
      • Executing Events
      • Executing Routes
      • Viewlets - Reusable Events
      • Event Caching
      • Validation
    • Layouts & Views
      • Views
        • Rendering Views
        • Rendering External Views
        • Rendering With Local Variables
        • Rendering Collections
        • View Caching
        • View Helpers
        • View Events
      • Layouts
        • Basic Layouts
        • Default Layout
        • Nested Layouts
        • Overriding Layouts
        • Layouts From A Module
        • Layout Helpers
        • Layout Events
      • Implicit Layout-View Declarations
      • Helpers UDF's
      • ColdBox Elixir
    • Models
      • Domain Modeling
        • Service Layer
        • Data Layers
        • Book
      • Conventions Location
      • WireBox Binder
      • Super Type Usage Methods
      • Injection DSL
        • Model Object Namespace
        • ColdBox Namespace
        • CacheBox Namespace
        • LogBox Namespace
        • WireBox Namespace
        • EntityService Namespace
      • Object Scopes
      • Coding: Solo Style
        • Datasource
        • Contact.cfc
        • ContactDAO.cfc
        • ContactService.cfc
        • Contacts Handler
      • Coding: ActiveEntity Style
        • ORM
        • Contact.cfc
        • Contacts Handler
        • Views
      • Coding: Virtual Service Layer
        • ORM
        • Contacts.cfc
        • Contacts Handler
        • Views
      • Coding: ORM Scaffolding
        • ORM
        • Contacts.cfc
        • Scaffold
  • HMVC
    • Modules
      • Core Modules
      • Locations
      • Parent Configuration
      • Module Layout
        • Changing The Module Layout
      • Module Service
        • Module Lifecycle
        • Module Registration
        • Module Activation
        • Module Unloading
        • Common Methods
        • Loading New Modules
        • Loading A-la-carte Modules
        • Module Events
      • ModuleConfig
        • Public Module Properties\/Directives
        • The Decorated Variables
        • The configure() Method
        • Module Settings
        • Environment Control
        • Interceptor Events
      • Module Event Executions
      • URL Routing
        • Default Route Execution
        • Module Routes Files
      • Request Context Module Methods
      • Layout and View Renderings
        • Layout\/View Discovery
        • Overriding Views
        • Overriding Layouts
        • Default Module Layout
        • Explicit Module Renderings
      • Models
      • Module CF Mappings
      • Module Dependencies
      • Module Helpers
      • Module Bundles
      • Module Inception
  • Testing
    • Testing Quick Start
    • Testing ColdBox Applications
      • Test Harness
      • ColdBox Testing Classes
      • Integration Testing
        • Test Annotations
        • Common Methods
        • Life-Cycle Events
        • Test Setup
        • The execute() Method
        • The Handler To Test
        • The Integration Test
        • Handler Returning Results
        • Testing Without Virtual Application
        • Rendering Results
        • HTTP Method Mocking
      • Interceptor Testing
      • Model Object Testing
      • Tips & Tricks
  • Digging Deeper
    • Interceptors
      • How do they work?
        • Conventions
      • Interceptor Declaration
      • Interceptor Registration
      • Core Interception Points
        • Application Life Cycle Events
        • Object Creating Events
        • Layout-View Events
        • Module Events
        • CacheBox Events
      • Restricting Execution
      • Interceptor Output Buffer
      • Custom Events
        • Configuration Registration
        • Programmatic Registration
        • Listening
        • Announcing Interceptions
      • Unregistering Interceptors
      • Reporting Methods
      • Interceptor Asynchronicity
        • Async Announcements
        • Async Listeners With Join
        • Async Listeners No Join
        • Asynchronous Annotations
    • Flash RAM
      • Flash Storage
      • Using Flash RAM
      • Creating Your Own Flash Scope
    • HTML Helper
    • ColdBox Proxy
      • Getting Started
      • The Base Proxy Object
      • The Event Handlers
        • Distinguishing Request Types
        • RenderData()
      • Proxy Events
      • Standard Return Types
      • Caveats & Gotchas
    • Request Context Decorator
    • Controller Decorator
    • Recipes
      • Building REST APIs
      • Application Templates
      • ColdBox Exception Handling
      • Debugging ColdBox Apps
      • Clearing the View Cache
      • Building a simple Basic HTTP Authentication Interceptor
  • Architecture Concepts
    • What is MVC
    • What is ColdBox
    • How ColdBox Works
    • Testing Concepts
      • Functional Testing
      • Non-Functional Testing
      • Bugs Cost Money
      • Static Testing
      • Dynamic Testing
      • Developer Focus
      • Testing Vocabulary
Powered by GitBook
On this page
  • Introduction
  • The Interceptor
  • Interceptor Test
  • Security Service
  • Security Service Test
  • Interceptor Declaration

Was this helpful?

Edit on Git
Export as PDF
  1. Digging Deeper
  2. Recipes

Building a simple Basic HTTP Authentication Interceptor

Introduction

In this recipe we will create a simple interceptor that will be in charge of challenging users with HTTP Basic Authentication. It features the usage of all the new RESTful methods in our Request Context that will make this interceptor really straightforward. We will start by knowing that this interceptor will need a security service to verify security, so we will also touch on this.

The Interceptor

Our simple security interceptor will be intercepting at preProcess so all incoming requests are inspected for security. Remember that I will also be putting best practices in place and will be creating some unit tests and mocking for all classes. So check out our interceptor:

/**
* Intercepts with HTTP Basic Authentication
*/
component {

    // Security Service
    property name="securityService" inject="id:SecurityService";


    void function configure(){
    }

    void function preProcess(event,struct interceptData){

        // Verify Incoming Headers to see if we are authorizing already or we are already Authorized
        if( !securityService.isLoggedIn() OR len( event.getHTTPHeader("Authorization","") ) ){

            // Verify incoming authorization
            var credentials = event.getHTTPBasicCredentials();
            if( securityService.authorize(credentials.username, credentials.password) ){
                // we are secured woot woot!
                return;
            };

            // Not secure!
            event.setHTTPHeader(name="WWW-Authenticate",value="basic realm=""Please enter your username and password for our Cool App!""");

            // secured content data and skip event execution
            event.renderData(data="<h1>Unathorized Access<p>Content Requires Authentication</p>",statusCode="401",statusText="Unauthorized")
                .noExecution();
        }

    }

}

As you can see it relies on a SecurityService model object that is being wired via:

// Security Service
property name="securityService" inject="id:SecurityService";

Then we check if a user is logged in or not and if not we either verify their incoming HTTP basic credentials or if none, we challenge them by setting up some cool headers and bypass event execution:

// Verify Incoming Headers to see if we are authorizing already or we are already Authorized
if( !securityService.isLoggedIn() OR len( event.getHTTPHeader("Authorization","") ) ){

    // Verify incoming authorization
    var credentials = event.getHTTPBasicCredentials();
    if( securityService.authorize(credentials.username, credentials.password) ){
        // we are secured woot woot!
        return;
    };

    // Not secure!
    event.setHTTPHeader(name="WWW-Authenticate",value="basic realm=""Please enter your username and password for our Cool App!""");

    // secured content data and skip event execution
    event.renderData(data="<h1>Unathorized Access<p>Content Requires Authentication</p>",statusCode="401",statusText="Unauthorized")
        .noExecution();
}

The renderData() is essential in not only setting the 401 status codes but also concatenating to a noExecution() method so it bypasses any event as we want to secure them.

Interceptor Test

So to make sure this works, here is our Interceptor Test Case with all possibilities for our Security Interceptor.

component extends="coldbox.system.testing.BaseInterceptorTest" interceptor="simpleMVC.interceptors.SimpleSecurity"{

    function setup(){
        super.setup();
        // mock security service
        mockSecurityService = getMockBox().createEmptyMock("simpleMVC.model.SecurityService");
        // inject mock into interceptor
        interceptor.$property("securityService","variables",mockSecurityService);
    }

    function testPreProcess(){
        var mockEvent = getMockRequestContext();

        // TEST A
        // test already logged in and mock authorize so we can see if it was called
        mockSecurityService.$("isLoggedIn",true).$("authorize",false);
        // call interceptor
        interceptor.preProcess(mockEvent,{});
        // verify nothing called
        assertTrue( mockSecurityService.$never("authorize") );

        // TEST B
        // test NOT logged in and NO credentials, so just challenge
        mockSecurityService.$("isLoggedIn",false).$("authorize",false);
        // mock incoming headers and no auth credentials
        mockEvent.$("getHTTPHeader").$args("Authorization").$results("")
            .$("getHTTPBasicCredentials",{username="",password=""})
            .$("setHTTPHeader");
        // call interceptor
        interceptor.preProcess(mockEvent,{});
        // verify authorize called once
        assertTrue( mockSecurityService.$once("authorize") );
        // Assert Set Header
        AssertTrue( mockEvent.$once("setHTTPHeader") );
        // assert renderdata
        assertEquals( "401", mockEvent.getRenderData().statusCode );

        // TEST C
        // Test NOT logged in With basic credentials that are valid
        mockSecurityService.$("isLoggedIn",false).$("authorize",true);
        // reset mocks for testing
        mockEvent.$("getHTTPBasicCredentials",{username="luis",password="luis"})
            .$("setHTTPHeader");
        // call interceptor
        interceptor.preProcess(mockEvent,{});
        // Assert header never called.
        AssertTrue( mockEvent.$never("setHTTPHeader") );
    }


}

As you can see from our A,B, anc C tests that we use MockBox to mock the security service, the request context and methods so we can build our interceptor without knowledge of other parts.

Security Service

Now that we have our interceptor built and fully tested, let's build the security service.

component accessors="true" singleton{

    // Dependencies
    property name="sessionStorage"     inject="coldbox:plugin:SessionStorage";

    /**
    * Constructor
    */
    public SecurityService function init(){

        variables.username = "luis";
        variables.password = "coldbox";

        return this;
    }

    /**
    * Authorize with basic auth
    */
    function authorize(username,password){

        // Validate Credentials, we can do better here
        if( variables.username eq username AND variables.password eq password ){
            // Set simple validation
            sessionStorage.setVar("userAuthorized",  true );
            return true;
        }

        return false;
    }

    /**
    * Checks if user already logged in or not.
    */
    function isLoggedIn(){
        if( sessionStorage.getVar("userAuthorized","false") ){
            return true;
        }
        return false;
    }

}

We use a simple auth with luis and coldbox as the password. Of course, you would get fancy and store these in a database and have a nice object model around it. For this purposes was having a simple 1 time username and password. We also use the SessionStorage plugin in order to interact with the session scope with extra pizass and the most important thing: We can mock it!

Security Service Test

component extends="coldbox.system.testing.BaseModelTest" model="simpleMVC.model.SecurityService"{

    function setup(){
        super.setup();
        // init model
        model.init();
        // mock dependency
        mockSession = getMockBox().createEmptyMock("coldbox.system.plugins.SessionStorage");
        model.$property("sessionStorage","variables",mockSession);
    }

    function testauthorize(){

        // A: Invalid
        mockSession.$("setVar");
        r = model.authorize("invalid","invalid");
        assertFalse( r );
        assertTrue( mockSession.$never("setVar") );

        // B: Valid
        mockSession.$("setVar");
        r = model.authorize("luis","coldbox");
        assertTrue( r );
        assertTrue( mockSession.$once("setVar") );


    }

    function testIsLoggedIn(){

        // A: Invalid
        mockSession.$("getVar",false);
        assertFalse( model.isLoggedIn() );

        // B: Valid
        mockSession.$("getVar",true);
        assertTrue( model.isLoggedIn() );
    }

}

Again, you can see that now we use our BaseModelTest case and continue to use mocks for our dependencies.

Interceptor Declaration

Open your Coldbox.cfc configuration file and add it.

//Register interceptors as an array, we need order
interceptors = [
    //SES
    {class="coldbox.system.interceptors.SES"},
    // Security
    { class="interceptors.SimpleSecurity" }
];

We just add our Simple Security interceptor and reinit your application and you should be now using simple security.

Last updated 7 years ago

Was this helpful?