Authentication and Authorization

From JRapid

Jump to: navigation, search

Contents

Introduction

Authentication and authorization are two of the most common needs for applications. JRapid offers a simple, straightforward way to handle everything from logins, to specialized views based on particular users. All this is accomplished quickly and easily with an importable template offering users, logins and roles from which to customize your project's access. This template is an example of what JRapid calls a stereotypes, a way to imbue particular entities with specialized code and certain extra functionality. It is also easy to pass the particular user's ID to subsets so they can restrict the results based on viewer.

User Management Template

Templates are available through the JRapid Community. They are a piece of JRapid source code that contains a set of entity definitions that together model a certain functionality or pattern. To import the template go to the Add-ons menu and select the Import template option.

File:auth1.png


This will show you a list of the available templates. Click on the Import link of the User management template.


File:auth2.png


This will automatically create the entities and other resources needed to implement this functionality.


File:auth3.png


The User entity implements the User stereotype. This requires the entity to define the username, password, privileges and roles properties. More properties can be added to the User entity, but the ones mentioned above must be present. Passwords are encrypted due to the use of the encrypt attribute in the property holding this value.

<property label="Password" name="password" type="password" encrypt="encrypt"/>

In the Login entity an encrypt attribute is included at entity tag level.

<entity encrypt="encrypt" label="Log In" name="Login" stereotype="Login" transient="transient">


Privileges is a special enumset and defines fixed options for the application that can only be changed during development. This enumeration of options will allow you to define the access privileges present in the application to the finest detail. This enumset is created with two values: ADMIN and ANONYMOUS.

    <enumset name="Privileges" privileges="privileges">
        <enum value="ADMIN">ADMIN</enum>
        <enum value="ANONYMOUS">ANONYMOUS</enum>
    </enumset>

Privileges are bound to application functionality, this means, they are part of the application’s source code and thus are fixed during execution.

The Role entity uses the Role stereotype. A Role groups privileges and makes it easier to grant them to a User. Roles, for being represented by an entity, can be defined or modified during execution of the application. This gives flexibility to this permissions approach. Roles are dynamic and represent real world user profiles.

The Login entity is transient and implements the Login stereotype. This provides the entity with the methods required for authentication of Users. By defining the entity in this way the form will automatically validate the entered credentials against the User records upon submit. If the authentication is successful, the application sets the user and granted privileges as attributes of the established session. In the Advanced tab of the Login entity details form you may see the Login stereotype selected, as expected, and a Next function attribute. This defines the JavaScript code to be executed after a successful login which is, by default, the page reload. This is useful because when a user with no session tries to access a protected page, the system intercepts the request and shows the login form. Once the user is authenticated and authorization is checked, the page reload shows the originally requested resource.


The ChangePassword entity defines the properties required for a change password form but does not implement this functionality. You must overwrite the store method in the ChangePasswordServices class to perform the action.

Application

Once all the entities, privileges and roles have been defined the developer must make use of them to limit the access for users depending on their role.

There are several ways in which this model can be used and these may be used in conjunction.

Limiting of data

Depending on the role granted to a User the records belonging to a certain entity may be listed in a partial way. Take the following example. A sales application has a Company entity that stores all the prospects and clients. Only one user is the responsible seller for a company. Each user may only list and edit their companies.

If a login process is implemented then we may know who the user that is listing the companies is and show only the corresponding records. This may be accomplished by defining a subset that filters the records by the property holding the seller value for each company. For getting the current user’s id, JRapid offers the variable userId.

With the subset created we can then create a listing that uses it to filter the results to be shown. The entity looks like this:

<entity label="Company" menu="Menu" name="Company">
        <listing name="userListing" subset="forUser"/>

        <subset name="forUser">
            <condition field="salesRepresentative" value="User:find(userId)"/>
        </subset>

        <property display="primary" label="Name" name="name"/>
        <property display="secondary" label="Load Date" name="loadDate" type="date" widget="jdatepicker"/>
        <property label="Close Date" name="closeDate" type="date"/>
        <property display="secondary" entity="Country" label="Country" name="country"/>
        <property collection="set" entity="Industry" label="Industries" name="industries"/>
        <property display="secondary" entity="User" label="Sales Representative" name="salesRepresentative"/>
</entity>


Before taking a look at the listing we’ve just created we must use the Login form to establish a session. After authenticating successfully we navigate to the listing using the same browser window where the session was established.

The list shows only the companies that belong to the user Tom.


File:auth4.png

Authorization

During development you may define different restrictions for the basic operations based on privileges defined for each entity. In the Advanced tab of the Edit entity form you enable this feature by checking the Restrict option. The underlying code added to the entity uses the restrict element.

<entity label="Company" name="Company">
        <restrict>
            <allow action="SAVE" privilege="ADMIN"/>
            <allow action="READ" privilege="ANONYMOUS"/>
            <allow action="LIST" privilege="ANONYMOUS"/>
            <allow action="REMOVE" privilege="ADMIN"/>
        </restrict>
        <property display="primary" label="Name" name="name"/>
        <property display="secondary" label="Load Date" name="loadDate" type="date" widget="jdatepicker" />
</entity>


You may define the privilege required to save, read, list or remove instances of an entity. These restrictions generate entries in the authorization.properties file of your project. This file is separated in two levels:

/JavaSource/authorization.properties
This file includes the authorization.properties described next. It is never overwritten by the generation process so it is the place where you should add any customizations.
/JavaSource/%PACKAGE%/controller/authorization.properties
This file is automatically generated based on the restrict elements defined for your entities.


The restrictions defined here take effect in two ways: They restrict access to the files that implement the UI and they restrict the access to the web services that are involved in each operation.

Example

In the above company example, the service for reading a particular company is anonymous. This service can be accessed via an HTTP GET request as detailed in Marshalling and Unmarshalling.

  <company id='1'  style='' >
     <name>JRapid</name>
     <address>Cordoba 900</address>
     <expiration></expiration>
     <active>false</active>
  </company>		

Changing the above restrict to the following will force the user to have logged in to access even the HTTP GET service.

<entity label="Company" name="Company">
        <restrict>
            <allow action="SAVE" privilege="ADMIN"/>
            <allow action="READ" privilege="ADMIN"/>
            <allow action="LIST" privilege="ADMIN"/>
            <allow action="REMOVE" privilege="ADMIN"/>
        </restrict>
        <property display="primary" label="Name" name="name"/>
        <property display="secondary" label="Load Date" name="loadDate" type="date" widget="jdatepicker" />
</entity>

The result of the same request changes after the restrict is altered:

  <exception><![CDATA[Not allowed to execute service: 'com.libraryproject.services.CompanyServices.find'.]]></exception>		

The restrictions on the outgoing services can be viewed under JavaSource/com/<project>/controller/authorization.properties. The front-end authorizations determine what is prevented at the HTML level, and the service authorization determines which services require login credentials saved to the session to access.

-privileges.class=com.libraryproject.entities.enums.Privileges
-login.form=/Main/Login_form.html?w=w
/xml/Login/0=ANONYMOUS

#### GENERAL AUTHORIZATION ####


#### FRONT END AUTHORIZATION ####


# Company
/Main/Company_form.html=ADMIN
/Main/Company_list.html=ADMIN
# Login
/Main/Login_form.html=ANONYMOUS
/Main/Login_list.html=
		
#### SERVICES AUTHORIZATION ####		


# 
com.libraryproject.services.CompanyServices.store=ADMIN
com.libraryproject.services.CompanyServices.find=ADMIN
com.libraryproject.services.CompanyServices.findPage=ADMIN
com.libraryproject.services.CompanyServices.remove=ADMIN
com.libraryproject.services.CompanyServices.removeMany=ADMIN

Service Restriction Example

It's also easy to restrict access to custom services by editing authorization.properties. Please make sure to edit the root one, and not the nested one as that one is overwritten by project changes.

/JavaSource/authorization.properties

This file by default contains a reference to the project generated authorization properties, but you can easily add your own file. The second line is the inclusion of the custom properties file for the example company services.

  @include=com/libraryproject/controller/authorization.properties
  @include=companyAuth.properties

Next I create the second file in the JavaSource directory alongside the authorization.properties file containing the following text.

  com.libraryproject.services.CompanyServices.findDefaultCompanies=ADMIN	

Finally, access to the findDefaultCompanies service is now restricted:

  <exception>
    <![CDATA[Not allowed to execute service: 'com.libraryproject.services.CompanyServices.findDefaultCompanies'.]]>
  </exception>


Custom Java Code for User Information Usage

When customizing functionality through Java coding you can access the session to identify the current user and check privileges to restrict certain operations.

For example, you may find the current user with the following sentence:

User user = User.DAO().findById(Session.getMySession().getUserId());

And you may check if a given privilege is granted to the user by:

if(user.getPrivileges().contains("R")) {
   ...
}

Adding a Log Out Link

Currently, the log out feature is not included in the User Management template as it requires some custom tweaks. In this section, we'll go through the steps required to add this functionality.

First, we need to add a Logout transient entity that will act as the confirmation dialog shown to the user.

<entity label="Log Out" name="Logout" transient="transient">
        <html>Are you sure you want to log out?</html>
        <next function="window.location.reload();" type="function"/>
</entity>

When the user submits this form, the corresponding store method in the services class is executed. This is where we need to add some lines to terminate the session. Edit the LogoutServices class and override the store method as follows.


import com.jrapid.controller.Session;
import BASEPACKAGE.entities.Logout;

public class LogoutServices extends LogoutServicesAbstract {
	
    @Override
    public Object store(String id, Logout voobj) {
        Session.getMySession().setUserId(0L);
        Session.getMySession().clearPrivileges();
        return super.store(id, voobj);
    }
}

After this method is executed, the JavaScript code specified in the next element of the 'Logout' entity is executed. This code tries to reload the current page, but as the user no longer has a valid session, he is redirected to the log in form.

The final step is to provide a link where the user may click to start the log out process. All this link needs to do is open the Logout form. As the Logout entity is transient, there is currently no way to specify a value for the menu attribute and have it shown in the menubar.

A simple approach is to include a link or button on the panel header. Here is the AML code snippet for a button.

    <panel name="Index" title="Welcome to JRapid!">
        <restrict>
            <allow action="VIEW" privilege="LOGGEDIN"/>
        </restrict>
        <menu/>
        <header><![CDATA[Welcome to JRapid!<br/>
                <button onclick="jrapid.form({entity: 'Logout'});">
                     Log Out
                </button>]]>
        </header>
        <column width="100%">
            <accordeon>
                <accordeonitem title="Companies" visible="true">
                    <listing entity="Company" name="companiesListing" panelheight="100%" title="Companies"/>
                </accordeonitem>
            </accordeon>
        </column>
        <footer><a href="http://www.jrapid.com">www.jrapid.com</a></footer>
    </panel>

Implementing the Change Password Logic

The User Management template creates a ChangePassword transient entity that provides a form with the necessary properties for the user to change and confirm the new password. The only thing we need to do is add the logic to the ChangePasswordServices class. This is, override the store method with the following.

        @Override
	public Object store(String id, ChangePassword voobj) {
		User u = User.DAO().findById(Session.getMySession().getUserId());
		if (!voobj.getNewPassword().equals(voobj.getConfirmPassword())) {
			throw new ServiceException("Passwords do not match");
		}
		
		/* Optional: force new password different to current one*/
		if (Encrypter.encrypt(voobj.getNewPassword()).equals(u.getPassword())) {
			throw new ServiceException("You password must be different to the current one");
		}
		
		u.setPassword(Encrypter.encrypt(voobj.getNewPassword()));
		
		return 1L;
	}

Last, we need to provide a link to the change password form. We can use the same technique we used for the log out form. In your panel header, include a button that opens the corresponding form.

    <panel name="Index" title="Welcome to JRapid!">
        <restrict>
            <allow action="VIEW" privilege="LOGGEDIN"/>
        </restrict>
        <menu/>
        <header><![CDATA[Welcome to JRapid!<br/>
                <button onclick="jrapid.form({entity: 'ChangePassword'});">
                     Change Password
                </button>]]>
        </header>
        <column width="100%">
            <accordeon>
                <accordeonitem title="Companies" visible="true">
                    <listing entity="Company" name="companiesListing" panelheight="100%" title="Companies"/>
                </accordeonitem>
            </accordeon>
        </column>
        <footer><a href="http://www.jrapid.com">www.jrapid.com</a></footer>
    </panel>

See also

Personal tools