Securing travel request flow

This part shows how to leverage IBM® WebSphere® sMash security capabilities in Assemble flow. Including user service, user authentication and security rules.

Tutorial steps

The following steps outline the necessary actions to add user authentication to the travel request application:

  1. Enable form-based authentication for the flow.We need to protect the flow to the authenticated users by adding the following security rule definition to the config/zero.config file:

    /config/security/secretKey = "6BQwDHSqzivGb5ki/O8yYQ=="
    		
    @include "security/enableSecurity.config"
    		
    # secure travel request flow
    @include "security/formAuthentication.config"{
    	"conditions": "/request/path =~ /travelrequest(/.*)?"
    }
    		
    @include "security/formLoginURL.config" {
    	"formLoginPage": "/auth/login.html"
    }
    		
    # all authenticated user can submit travel request
    @include "security/authorization.config"{
    	"conditions": "/request/path =~ /travelrequest/index.flow/.+/SubmitRequest",
    	"groups" : ["ALL_AUTHENTICATED_USERS"]
    }
    	

    It says that all request URIs that start with /travelrequest will be protected by an HTML login form. The flow's access URL is start with /travelrequest, so it will be pretecteed. The last stanza is the authorization rule which protects resource with the authorization rule being applied to the SubmitRequest under travelrequest.It means the SubmitRequest could be used by any registered user without dividing users into specialized groups, so any registered user can submit the travel request.

  2. Provide the login form. You should create a file directly under the application's virtual root: public/auth/login.html, when using the IDE use the source tab hand code the form, or you may use the widgets to create the form yourself.This file should contain the default login form used for WebSphere sMash applications; note the names of the ID and password input fields and do not change them:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <title>Log in</title>
    <style type="text/css">
    @import "../dijit/themes/soria/soria.css";
    @import "../dojo/resources/dojo.css";
    </style>
    <script type="text/javascript" src="../dojo/dojo.js"
        djConfig="parseOnLoad: true"></script> 
    <script type="text/javascript">
    dojo.require("dojo.parser");
    dojo.require("dijit.form.Form");
    dojo.require("dijit.form.TextBox");
    dojo.require("dijit.form.Button");
    dojo.require("dojox.wire.ml.Action");
    dojo.require("dojox.wire.ml.Invocation");
    </script>
    <style>
    
        h2{
            color:#666666;
            font-family:"Lucida Grande",Verdana,Tahoma,Arial,sans-serif;
            font-size:24px;
            font-weight:bold;
            margin: 40 0 2 60;
            padding-bottom:2px;
            border-bottom:1px solid #DADADA;
        }
    
    </style>
    </head>
    <body class="soria">
    <h2>Log in</h2>
    <form dojoType="dijit.form.Form" id="login"    name="login" action=""     method="POST"
        style="border: 1px dashed #E0E0E0; width: 313px; height: 144px; position: absolute; left: 65px; top: 90px;">
        <input dojoType="dijit.form.TextBox" id="zeroUserName"
            name="zeroUserName"    type="text"    style="position: absolute; left: 95px; top: 30px;"></input>
        <input dojoType="dijit.form.TextBox" id="zeroPassword"
            name="zeroPassword"    type="password" style="position: absolute; left: 95px; top: 70px;"></input>
        <input dojoType="dijit.form.TextBox" id="postLoginTargetURI" readonly="true" name="postLoginTargetURI"
            value="/travelrequest/index.flow" type="text" style="position: absolute; left: 33px; top: 176px; display: none;"></input>
        <label for="zeroUserName" style="position: absolute; left: 12px; top: 30px;">Username:</label>
        <label for="zeroPassword" style="position: absolute; left: 12px; top: 70px;">Password:</label>
    </form>
    <div dojoType="dijit.form.Button" id="Button_1"
            label="Login"
            style="position: absolute; left: 180px; top: 190px;"></div>
    <div dojoType="dojox.wire.ml.Action" trigger="Button_1" triggerEvent="onClick">
        <div dojoType="dojox.wire.ml.Invocation"    object="login"        method="submit"></div>
    </div>
    
    </body>
    </html>
    

    The zeroUserName and zeroPassword names are standard names used by WebSphere sMash Core to process login forms without any additional programming or configuration by the user. Also notice the hidden field named postLoginTargetURI, which tells WebSphere sMash Core to which page the user should be sent after he is authenticated. In our case, we are going to the flow, public/travelrequest/index.flow.

  3. Add sample users. By default, authentication is handled with a file-based service that reads and writes user account data to a file named config/zero.users. Below is a sample zero.users file that contains the three submitter names we saw in our sample data, each with the password set to mypassword:

    bob@example.com:34819d7beeabb9260a5c854bc85b3e44:
    dan@example.com:34819d7beeabb9260a5c854bc85b3e44:
    sue@example.com:34819d7beeabb9260a5c854bc85b3e44:
    

    You need to use the real email address as the user account, so the flow can send email to you. You can later add or remove user accounts using the WebSphere sMash command line interface (try command zero help users for more information).

  4. Enable instance based security for travel approval step of the flow. The flow provides the instance level authorization which allows authorzing specific authorization in any webUI activity. It enables authorizing endpoint URI that webUI activity provided with user name in assignTo attribute of the activity in a flow instance.

    /config/handlers += [{
    	"events" : "authorize",
    	"handler" : "zero.assemble.flow.webui.WebUIAuthorizationHandler.class",
    	"conditions": "/request/path =~ /travelrequest/index.flow/.+/ApproveRequest"
    }]
    	

    It means only the employee's manager is authorized to access the ApproveRequest under the very flow instance.

  5. Cutomize 403 error page.We provides this error page if the user is not authenrized to access protected resource. For example, if a registered user who is not the manager of the travel requester will get this error page if he wants to access the /ApproveRequest of the flow. The error page should under /app/errors/.By the convention of error handling, we should name it as error403.gt.

    <% 
        request.zero.zerosite = "http://www.projectzero.org"
        request.headers.out."Content-Type" = "text/html; charset=UTF-8" 
        def coreBundle = new zero.core.utils.ClientMessageFormatter("zero.core.resource.messages") 
    %>
    <html>
    <head>
       <title><%=coreBundle.getMessage("error.title")%></title>
       <base href="<%= getAbsoluteUri("/") %>" />
       <link rel="StyleSheet" href="zero/theme/ZeroDocStyle.css" type="text/css" />
    <!--[if lt IE 7]>
       <script LANGUAGE="JavaScript" SRC="zero/js/IEPngFix.js"></script>
    <![endif]-->
    
       <style type="text/css">
           table {width: 100%; border-spacing: 0px; border: 0px; border-collapse: collapse; font-family: inherit; font-style: monospace; font-size: 12pt}
           td.errorMessage {background-color: #DAE4F2; }   
           td.rootCause {background-color: #FAFAFA; font-size: 10pt}   
           td.label {color: white; background-color: #00387D; text-align: right; vertical-align: top; white-space: nowrap; width: 125px}   
           .errorMessage{
                 font-size: 14pt;
                margin-top:1pt;
                padding-top:1pt;
                font-weight:normal;
                line-height:1.5em;
           }
       </style>
    </head>
    
       <body id='home'>
         <div id='container'>
           <div id='navigation'>
            <ul>
              <li id='logo'>
                <a href='<%= request.zero.zerosite[] %>' title='WebSphere sMash'>WebSphere sMash</a>
              </li>
              <li>
                <div class='searchArea'>
                  <form name='SearchForm' action='http://www.google.com/search' method='get'>
                    <input type='text' name='q' value='' />
                    <input id='search-button' type='submit' name='Search' value='<%=coreBundle.getMessage("search")%>' />
                    <input type='hidden' name='q' value='+site:www.projectzero.org' />
                  </form>
                </div>
              </li>
            </ul>
          </div>
        </div>
        <div id='contentContainer'>
          <div id='content'>
            <div id='mainContent' style="margin-top:5px">
            <h1>Access not allowed</h1>
            <span class="errorMessage"> You(<%=request.subject.remoteUser[]%>) are not allowed to access this page because your account does not have the proper privileges.</span>
              <a href="/auth/logout.gt?redirectURL=<%=request.path[] %>" style="font-size:1.3em">Please login as another user</a>.
            </div>
          </div>
        </div>
        <div div style="background-color: black; padding: 5px">
          <a href="http://www.ibm.com" title="IBM Logo" id="footerlogo"><img src="zero/images/ibm_logo_sm.gif" alt="IBM" title="IBM"/></a>
        </div>
       </body>
    </html>
    
    	
  6. Provide logout and re-login handler. We provides it if user get a error page and want to re-login as another user account.You should create a file directly under the application's virtual root: public/auth/logout.gt

    <%
    zero.core.security.LoginService.logout();
    zput("/request/status", 302);
    zput("/request/headers/out"+ "/Location", request.params.redirectURL[]!=null?request.params.redirectURL[]:"/auth/login.html");
    %>	
    	
  7. Now you have built all parts of this application, start the application by clicking Start button in AppBuilder.
    After following the previous steps, you can interact with the application in the following ways: http://localhost:8080/travelrequest/

Summary

During the course of the tutorials, you have built a secure collaboration application that can sumbit and approve the travel request by difference users. You used the App Builder to continue to develop the application to create the flow, create view for the webUI activity by using zero forms.Then you added security to prevent unauthorized users from accessing the flow.

Troubleshooting

A working sample of the Travel Request application can be obtained through the App Builder from the My Applications view and clicking on Create from repository. A dialog is shown prompting for the module to copy or link to. Type in zero.travelrequest.demo, change the Application name to travelrequest and leave defaults for the other fields. Click the Create button and the application will be opened.

Version 1.1.30089