Question

In my application, a user may create an account freely, and it needs to own a queue (or topic) to communicate 2 backend processes between them. I don't want to have to modify activemq's configuration every time that someone creates an account. I have already created a jaasAuthenticationPlugin and it works fine. Here is the relevant part of my activemq.xml file:

<plugins>
  <!-- 'activemq-domain' defined in conf/login.conf -->
  <jaasAuthenticationPlugin configuration="activemq-domain" />

  <authorizationPlugin>
    <map>
      <authorizationMap>
        <authorizationEntries>
          <authorizationEntry queue="foobarQueue"
              write="foobarGroup"
              read="foobarGroup"
              admin="foobarGroup"
          />
        </authorizationEntries>
      </authorizationMap>
    </map>
  </authorizationPlugin>
</plugins>

As you may deduct, the authentication plugin is authenticating a user (foobar in this example) and putting the user in the foobarGroup group. The AuthorizationEntry is granting read, write and admin privileges to the foobarQueue to this foobarGroup. This is working well, but now if I create a new user, I must come to this file and add a new AuthorizationEntry. Is it possible with a simple configuration line in the activemq.xml to do something like:

<authorizationEntry
    queue="<% Username %>"
    write="<% Username %>"
    read="<% Username %>"
    admin="<% Username %>"
/>

or should I write some JAAS authorization class to do that?

Was it helpful?

Solution

Finally I have written a class to handle the Authorization part. It was a bit difficult because documentation is difficult to find and I couldn't find any good example. Digging in the source code of the default LDAPAuthorizationMap was key. Anyway, the source for anyone interested:

package com.example.activemq;


import org.apache.activemq.advisory.AdvisorySupport;
import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.jaas.GroupPrincipal;
import org.apache.activemq.security.AuthorizationMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.HashSet;
import java.util.Set;

public class OwnedUserQueueAuthorizator implements AuthorizationMap {
    private static final Log log =
            LogFactory.getLog(OwnedUserQueueAuthorizator.class);
    private boolean debug = false;

    // the Destination will be the name of the user, and we should return that
    // the group with user name has read,write and admin privileges to the
    // topic/queue named like the username


    // for temporary destinations, if null is returned, then everybody has
    // permission.

    public Set<GroupPrincipal> getTempDestinationAdminACLs() {
        return null;
    }

    public Set<GroupPrincipal> getTempDestinationReadACLs() {
        return null;
    }

    public Set<GroupPrincipal> getTempDestinationWriteACLs() {
        return null;
    }

    // for persistent destinations

    public Set<GroupPrincipal> getAdminACLs(ActiveMQDestination destination) {
        if (debug) {
            log.debug("getAdminACLs: " + destination.getPhysicalName());
        }
        return getACLs(destination);
    }

    public Set<GroupPrincipal> getReadACLs(ActiveMQDestination destination) {
        if (debug) {
            log.debug("getReadACLs: " + destination.getPhysicalName());
        }
        return getACLs(destination);
    }

    public Set<GroupPrincipal> getWriteACLs(ActiveMQDestination destination) {
        if (debug) {
            log.debug("getwriteACLs: " + destination.getPhysicalName());
        }
        return getACLs(destination);
    }

    private Set<GroupPrincipal> getACLs(ActiveMQDestination destination) {
        Set<GroupPrincipal> result;

        if (AdvisorySupport.isAdvisoryTopic(destination)) {
            result = getACLsForAdvisory();
        } else {
            result = new HashSet<GroupPrincipal>();
            // Destination should be something like UUID or UUID.whatever...,
            // so we must add only the first component as the group principal
            result.add(new GroupPrincipal(
                    destination.getDestinationPaths()[0])
            );
        }

        if (debug) {
            String s = "";
            for (GroupPrincipal gp : result) {
                s += ", " + gp.getName();
            }
            log.debug("groupPrincipals: " + "[" + s.substring(2) + "]");
        }
        return result;
    }

    private Set<GroupPrincipal> getACLsForAdvisory() {
        Set<GroupPrincipal> result = new HashSet<GroupPrincipal>();
        GroupPrincipal g = new GroupPrincipal("advisories");
        result.add(g);
        return result;
    }

    // Properties
    // -------------------------------------------------------------------------

    // if the <bean> definition in the activemq.xml has some
    // <property name="foo" value="..." />
    // defined, they will call this.setFoo($value), so, even if these get/set
    // methods aren't called from here, they are really needed.

    public void setDebug(String debug) {
        this.debug = "true".equalsIgnoreCase(debug);
    }

    public String getDebug() {
        return String.valueOf(debug);
    }
}

The conf/activemq.xml file:

<beans ...>
    ...
    <broker ...>
    ...
        <plugins>                                                               
            <!-- 'activemq-domain' defined in conf/login.conf -->               
            <jaasAuthenticationPlugin configuration="activemq-domain" />        

            <authorizationPlugin>                                               
                <map>                                                           
                    <bean id="OwnedUserQueueAuthorizationMap"                            
                        class="com.example.activemq.OwnedUserQueueAuthorizator"   
                        xmlns="http://www.springframework.org/schema/beans">    
                        <property name="debug" value="false"/>                   
                    </bean>                                                     
                </map>                                                          
            </authorizationPlugin>                                              
        </plugins>   
        ...
    </broker>
    ...
</beans>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top