Dashboard > Documentation > ... > 2. Developer Guide > 2.11. Security

View Info

2.11. Security

 Documentation Summary
 Page Summary

Introduction

Security in Flex applications cannot rely on standard web-app security-constraint's configured in web.xml: generally, you have only one channel-definition (equivalent to a url-pattern in web.xml) and multiple destinations. So, the security must be destination based rather than url pattern based, and J2EE standard configuration in web.xml does not provide anything like that.

With a configured SecurityService, you will be able to use RemoteObject's setCredentials, setRemoteCredentials and logout methods.

Another important feature in security is to be able to create and expose a java.security.Principal to, for example, an EJB3 Session Bean backend (so role based security can be used).

At this time, Granite DS only provides Tomcat5+ and Jetty6+ security services (JBoss/[Tomcat|Jetty] is working and, potentially, all application servers with a Tomcat or Jetty servlet container).

For a complete sample of a security service, please download and install graniteds-ejb3-1.0.0.zip (read the README.txt file!).

For specific Seam/Identity or Spring/Acegi security implementations, please refer to Seam Services or Spring Services.

Configuration

To enable security, you simply put this kind of declaration in your granite-config.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE granite-config PUBLIC
    "-//Granite Data Services//DTD granite-config internal//EN"
    "http://www.graniteds.org/public/dtd/1.0.0/granite-config.dtd">
<granite-config>
    ...
    <security type="org.granite.messaging.service.security.TomcatSecurityService"/>
    <!--
    Alternatively:
    <security type="org.granite.messaging.service.security.Jetty6SecurityService"/>
    -->
</granite-config>

Normally, there is no need to pass additional parameters, but TomcatSecurityService accepts one optional parameter for its internal server.findService advanced utility (otherwise, the first available service is used by default):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE granite-config PUBLIC
    "-//Granite Data Services//DTD granite-config internal//EN"
    "http://www.graniteds.org/public/dtd/1.0.0/granite-config.dtd">
<granite-config>
    ...
    <security type="org.granite.messaging.service.security.TomcatSecurityService">
        <param name="service" value="your-tomcat-service-name-here"/>
    </security>
</granite-config>

You may now use role based security on destination in your services-config.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
    <services>
        <service id="granite-service"
            class="flex.messaging.services.RemotingService"
            messageTypes="flex.messaging.messages.RemotingMessage">
            <destination id="person">
                <channels>
                    <channel ref="my-graniteamf"/>
                </channels>
                <properties>
                    <scope>session</scope>
                    <source>test.pojo.PersonService</source>
                </properties>
                <security>
                    <security-constraint>
                        <auth-method>Custom</auth-method>
                        <roles>
                            <role>user</role>
                            <role>admin</role>
                        </roles>
                    </security-constraint>
                </security>
            </destination>
        </service>
        <service id="granite-service"
            class="flex.messaging.services.RemotingService"
            messageTypes="flex.messaging.messages.RemotingMessage">
            <destination id="personRestricted">
                <channels>
                    <channel ref="my-graniteamf"/>
                </channels>
                <properties>
                    <scope>session</scope>
                    <source>test.pojo.PersonRestrictedService</source>
                </properties>
                <security>
                    <security-constraint>
                        <auth-method>Custom</auth-method>
                        <roles>
                            <role>admin</role>
                        </roles>
                    </security-constraint>
                </security>
            </destination>
        </service>
    </services>
    ...
</services-config>

Here, the "person" destination can be used by authentified users with "user" or "admin" roles, while the "personRestricted" destination can only be used by authentified users with the "admin" role.

Please refer to Tomcat and JBoss documentation for setting up your users/roles configuration.

SecuredRemteObject

The simplest way to use security in your Flex application is to used the org.granite.rpc.remoting.mxml.SecureRemoteObject action script class. This class brings advanced event based security support as show here:

...
import org.granite.rpc.remoting.mxml.SecureRemoteObject;
import org.granite.events.SecurityEvent;
...
private var srv:SecureRemoteObject = null;
...
public function init():void {
    srv = new SecureRemoteObject("mydestination");
    srv.addEventListener(SecurityEvent.ALL, onSecurityEvent);
    ...
}

public function onSecurityEvent(event:SecurityEvent):void {
    switch (event.type) {
    case SecurityEvent.INVALID_CREDENTIALS:
        // show message "wrong username or pasword"
        break;
    case SecurityEvent.NOT_LOGGED_IN:
        srv.logout(); // reset remote object
        // show login panel...
        break;
    case SecurityEvent.SESSION_EXPIRED:
        srv.logout(); // reset remote object
        // show login panel...
        break;
    case SecurityEvent.ACCESS_DENIED:
        // show message "you don't have rights..."
        break;
    }
}

public function onCredentialsSet(username:String, password:String):void {
    srv.setCredentials(username, password);
    ...
}

public function doLogout():void {
    srv.logout();
    ...
}
...

Note that you must compile your mxml/as classes with the granite.swc library in order to use SecureRemoteObject.

Role Based Security with EJB3

If you use EJB3 session beans, you can use role based security checking with special annotations:

@Stateless
@Local(PersonService.class)
@SecurityDomain("other")
public class PersonServiceBean implements PersonService {
    
    @PersistenceContext
    protected EntityManager manager;

    public List<Person> findAllPersons() {
        return manager.createQuery("select distinct p from Person p").getResultList();
    }

    @RolesAllowed({"admin"})
    public Person createPerson(Person person) {
        return manager.merge(person);
    }

    @RolesAllowed({"admin"})
    public Person modifyPerson(Person person) {
        return manager.merge(person);
    }

    @RolesAllowed({"admin"})
    public void deletePerson(Person person) {
        person = manager.find(Person.class, person.getId());
        manager.remove(person);
    }
}

Here, within the security domain "other" (@SecurityDomain("other"), see login-config.xml in <JBOSS_HOME>/server/default/conf), calling the 3 last methods will fail if the current user does not have the "admin" role (@RolesAllowed({"admin"})), while the findAllPersons method is available for everybody.

To know more about EJB3 security, please refer to EJB3 specification and JBoss documentation.

Granite DS Security Implementation

Granite DS implements security based on the following SecurityService interface (note that "Service" in "SecurityService" has nothing to do with a true Flex destination, since security services are not exposed to outside calls):

package org.granite.messaging.service.security;

import java.util.Map;
import org.granite.messaging.service.ServiceInvocationContext;

public interface SecurityService {

    public void configure(Map<String, String> params);

    public void login(Object credentials) throws SecurityServiceException;

    public Object authorize(AbstractSecurityContext context) throws Exception;

    public void logout() throws SecurityServiceException;
}

An implementation of this interface must be thread safe (only one instance of this service is used in the entire web-app and will be called by concurrent threads).

  • configure: this method is called at startup time and gives a chance to pass parameters to the security service.
  • login: this method is called when you call one of the setCredentials or setRemoteCredentials RemoteObject's method (note that those method calls do not fire any request by themselves but only pass credentials on the next destination service method call). The login method is responsible of creating and exposing a java.security.Principal or throwing an appropriate org.granite.messaging.service.security.SecurityServiceException if credentials are invalid. Note that credentials are a Base64 string with the common "username:password" format.
  • authorize: this method is called upon each and every service method call invocations (RemoteObject) or subscribe/publish actions (Consumer/Producer). When used with RemoteObjects, the autorize method is responsible of checking security, calling the service method and returning the corresponding result. When used with Consumers/Producers, it is simply responsible of checking security (no service method invocation, no result). If authorization fails (either because the user is not logged in or because it doesn't have required rights), it must throw an appropriate org.granite.messaging.service.security.SecurityServiceException.
  • logout: this method is called when you call the logout RemoteObject's method (note that the RemoteObject.logout method fires a remote request by itself).

Fine Grained Per Destination Security

You may write and configure a specific DestinationSecurizer in order to get fine grained security checking for specific actions. This especially useful for Gravity (Data Push) when you want to control subscribe/publish calls.

For Gravity, you must implement the org.granite.gravity.security.GravityDestinationSecurizer interface:

public interface GravityDestinationSecurizer extends DestinationSecurizer {

    public void canSubscribe(GravityInvocationContext context)
        throws SecurityServiceException;
    public void canPublish(GravityInvocationContext context)
        throws SecurityServiceException;
}

For standard remote calls, you must implement the org.granite.messaging.service.security.RemotingDestinationSecurizer interface:

public interface RemotingDestinationSecurizer extends DestinationSecurizer {

    public void canExecute(ServiceInvocationContext context)
        throws SecurityServiceException;
}

In both cases, you should also tell Granite where to use your securizer:

services-config.xml
<services-config>
    <services>
        <service ...>
            <destination id="securized">
                ...
                <properties>
                    <securizer>path.to.MyDestinationSecurizer</securizer>
                </properties>
            </destination>
        </service>
    </services>
    ...
</services-config>

Note that securizers (if any) are always called before the standard SecurityService.authorize method.


Browse Space
- Pages
- Labels
- Attachments
- Mail
- Bookmarks
- News
- Activity
- Advanced

Explore Confluence
- Popular Labels
- Notation Guide

Your Account
Log In

Other Features

View a printable version of the current page.

Add Content
- Add Comment


Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.6.0 Build:#913 Sep 27, 2007)
Bug/feature request - Contact Administrators

SourceForge.net Logo
Copyright © 2007-2008 Adequate Systems. All Rights Reserved.