link
Avalon
Avalon Central
Home PlanetProductsCentral
Advanced Features
Granting Permissions to classes in container

Starting from Merlin 3.3, it is possible to grant permissions to classes within a container. This is done by the declaration of a <grant> element in the classloader definition section in the block descriptor (e.g. block.xml).

In the example below you should be able to see the mechanism. The multiple <action> elements, instead of a single String value as is more common, was chosen for easier tool support while still allowing you to place multiple comma separated actions into a single <action> element, passed to the Permission constructor as-is.

    <container>
      <classloader>
        <classpath>
          <repository>
            <resource id="avalon-framework:avalon-framework-impl" version="4.1.5"/>
            <resource id="mystuff:myfilestorage"/>
          </repository>
        </classpath>
        <grant>
          <permission class="java.io.FilePermission" name="/mystore" >
            <action>read</action>
            <action>write</action>
          </permission>
          <permission class="java.util.PropertyPermission" name="*" >
            <action>read</action>
          </permission>
          <permission class="java.util.PropertyPermission" name="com.mycompany.*" >
            <action>read</action>
            <action>write</action>
          </permission>
        </grant> 
      </classloader>
    </container>

There can only be a single <grant> for each <classloader> and any number of <permission> elements within.

Implementing Security in Components.

If you are only looking for basic security, similar to any typical stand-alone application, that would depend on a Java Security Policy file, you don't need to do anything special. All classes in Java will perform the security checks behind the scenes, protecting files, network connection and many other system resources. Please refer to your Java Security documentation for full details.

If you want to guard some resource or a section of code, you will need to;

  • Create a subclass of java.security.Permission, or one of its subclasses such as java.lang.BasicPermission, and override the implies(), equals() and hashCode() methods.
  • Insert a AccessController.checkPermission() at the relevant points in your code. (See examples below.)
Examples of Security
Using an existing Permission class

This first example uses a simple named RuntimePermission.

    public SuperGlue getSuperGlue()
    {
        Permission p = new RuntimePermission( "useSuperGlue" );
        AccessController.checkPermission( p );
        return m_SuperGlue;
    }

In this example, we utilizes the existing java.lang.RuntimePermission to do a very simple check, i.e is the current protection domain allowed to use the SuperGlue.

And to make this work in your Merlin application, you would need to insert the appropriate permission in the <grant> element.

    <container>
      <classloader>
        <!-- other stuff -->
        <grant>
          <permission class="java.lang.RuntimePermission" name="useSuperGlue" />
        </grant>
      </classloader>
    </container>
Creating a new Permission class

If you need something more complicated that can not be fulfilled with the existing Permission classes, you will need to create your own. This can be rather tricky, depending on what you are actually trying to do.

In the example below, we have a Permission class that ensures that an amount is within its boundaries, for instance for a banking application. The semantics are;

  • The name argument for a granted permission (declared) contains a minimum value, followed by a dash and then followed by a maximum value.
  • The name argument for a required permission (programmatically) only contains a single value, which is the requested amount. The amount is expressed in cent, and no fractional numbers needed.
  • If any of the two values are missing, the default is used. The default is 1000000 for each.
  • The action argument contains either "deposit" or "withdrawal".
  • The granted permission must contain the action of the required permission, and the required permission's amount must be within the limits of the granted permission.

As we can see it is a fairly straight forward algorithm, but a bit hard to put in words, and I hope I haven't missed something. To do this with Java Security permissions is fairly easy.

public class AccountPermission extends Permission
{
    private long m_Minimum;
    private long m_Maximum;
    private int  m_Actions;
    
    public AccountPermission( String amount, String actions )
    {
        super( amount );
        parseAmount( amount );
        parseActions( actions );
    }
    
    public int hashCode()
    {
        return (int) m_Actions * 876238684 + m_Minimum * 23457241393 + m_Maximum;
    }
    
    public boolean equals( Object obj )
    {
        if( ! ( obj.getClass().equals( AccountPermission.class ) ) )
            return false;
        AmountPermission other = (AmountPermission) obj;
        return m_Actions == other.m_Actions  &&
               m_Minimum == other.m_Minimum &&
               m_Maximum == other.m_Maximum;
    }
    
    public boolean implies( Permission permission )
    {
        if( ! (permission.getClass().equals( AmountPermission.class ) ) )
            return false;
        AmountPermission requesting = (AmountPermission) permission;
        if( ( m_Actions & requesting.m_Actions ) > 0 )
            return false;
        if( requesting.m_Minimum < m_Minimum )
            return false;
        if( requesting.m_Minimum > m_Maximum )
            return false;
        return true;
    }
    
    private void parseAmount( String amount )
    {
        m_Minimum = 1000000;
        m_Maximum = 1000000;
        if( amount == null || "".equals( amount ) )
            return;
            
        int dash = amount.indexOf( '-' );
        if( dash < 0 )
        {
            try
            {
                m_Minimum = Long.parseLong( amount );
            } catch( NumberFormatException e )
            {} // ignore, use default
        }
        else
        {
            String am1 = amount.substring( 0, dash );
            String am2 = amount.substring( dash + 1 );
            try
            {
                m_Minimum = Long.parseLong( am1 );
            } catch( NumberFormatException e )
            {} // ignore, use default
            try
            {
                m_Maximum = Long.parseLong( am2 );
            } catch( NumberFormatException e )
            {} // ignore, use default
        }
    }
    
    private void parseActions( String actions )
    {
        // This should probably be done differently.
        m_Actions = 0;
        if( actions.indexOf( "withdrawal" ) )
            m_Actions = 1;
        if( actions.indexOf( "deposit" ) )
            m_Actions += 2;
    }
}
    

Please note that the code has not yet been tested. If you do please post any mistake to dev@avalon.apache.org. Thank you.

Then in the actual code, we would do something like this;

    public void deposit( long amount )
    {
        AmountPermission p = new AmountPermission( (String) amount, "deposit" );
        AccessController.checkPermission( p );
    }
    
    public void withdraw( long amount )
    {
        AmountPermission p = new AmountPermission( (String) amount, "withdrawal" );
        AccessController.checkPermission( p );
    }

Wasn't that easy? Well, it would have been if we could tie the principal customer/client/user to the protection domain that is checked. This is currently on the drawing board for Avalon Merlin, and will probably not be ready until version 4.0, somewhere mid or late 2004. While awaiting this Subject-based, generic, pluggable security system, you can hack the above example a little bit, for some basic subject driven security.

In the implies() method, you reach out and detect who is executing the current thread, for instance through a ThreadLocal variable, ask some authoritive object instance for the amounts allowed and perform the check. This is NOT the recommended method for larger and more complex system (such as banks), but can work as a temporary solution for the time being.