Authorization Plugins

A Session Controller Authorization plugin, is responsible for providing information which will be used for authorization of users.

Plugins Supplied with Ceptor PortalProtect Standard Distribution

By default, the following authorization plugins are supplied:

  • dk.itp.security.passticket.server.ConfigBasedAuthorizationPlugin
    Reads ACLs and protected URLs from the session controllers configuration properties. 
  • dk.itp.security.passticket.server.FileAuthorizationPlugin
    Reads list of protected URLs, ACLs and Groups from the file system.
  • dk.itp.security.ldap.LdapAuthorizationPlugin
    Reads ACLs, protected URLs and Groups from an LDAP server. 
  • dk.itp.portalprotect.useradmin.server.UAAuthorizationPlugin
    Reads ACLs, and Groups from the UserAdmin Database - supports searching for users and looking up a users group for use in administration - e.g. used by the WebLogic SSPI Plugins when Integrating with Oracle WebLogic.

Developing an Authorization Plugin

There are a couple of interfaces which an Authorization Plugin can or must implement;

  • dk.itp.security.authorization.IAuthorizationPlugin
    This interface must be implemented by an authorization plugin, it provides mandatory methods which must be implemented. 
  • dk.itp.security.authorization.IAuthorizationPlugin2
    This interface should be implemented by the authorization plugin - it contains methods called when installing/uninstalling it.
  • dk.itp.security.authorization.IAuthorizationUserGetter
    This interface can be implemented by a plugin, if it supports looking up users based on wildcard searches, and if it can retrieve the list of groups a user is member of.

Multiple Authorization Plugins can be installed in the Session Controller simultaneously, so e.g. protected URLs, ACLs or User Groups can be obtained from multiple different places at once.

When an agent asks for e.g. all available ACLs, then all installed plugins will be queried and the sum of their output will be returned. This differs from Authentication Plugins where only a single plugin is called at a time.


IAuthorizationPlugin
package dk.itp.security.authorization;
import java.util.Vector;

import dk.itp.security.authorization.client.OperationNotSupportedException;

/**
 * This interface must be implemented by all authorization plugins, it combines the common characteristics for
 * all (well, most anyway) types of authorization repositories.
 *
 * @author Kim Rasmussen
 * 
 * <pre>
 * PortalProtect - Security infrastructure
 * Copyright(c) 2001, IT Practice A/S, All rights reserved.
 * 
 * This source code is confidential.
 * </pre>
 */
public interface IAuthorizationPlugin {
	/**
	 * Returns the list of ACLs for a given principal
	 */
	public Vector getACLsForUser(String principal) throws OperationNotSupportedException;

	/**
	 * Returns the type of Authorization, all plugins have assigned a particular id identifying that specify type
	 * of Authorization.
	 */
	int getAuthorizationType();

	/**
	 * Returns the list of all available ACLs, or null if no complete list is available
	 *
	 * @param identifier The identifier can be used to select a subset of ACLs, if not null, it can identify a specific server to retrieve ACLs from.
	 */
	public Vector getAvailableACLs(String identifier) throws OperationNotSupportedException;

	/**
	 * Returns the list of all known groups.
	 *
	 * @return List og groups
	 * @param identifier Server specific identifier or null to retrieve all groups
	 */
	Vector getAvailableGroups(String identifier) throws OperationNotSupportedException;

	/**
	 * Returns the name of this particular Authorization method
	 */
	String getName();

	/**
	 * Returns a specif ACL, this can be used by agents when they know they need a specific ACL rather than the whole list.
	 *
	 * @param name The name of the ACL entry to retrieve
	 */
	Object getSpecificACL(String identifier, String name) throws OperationNotSupportedException;

	/**
	 * Returns a specific group.
	 *
	 * @return List of groups
	 * @param identifier Server specific identifier or null to retrieve all groups
	 * @param name Name of group to return
	 */
	Object getSpecificGroup(String identifier, String name) throws OperationNotSupportedException;

	/**
	 * Return the list of protected URLs - the tunnel agent will then protect these URLs, and only allow
	 * access to them, if the user has the proper permissions.
	 *
	 * @param identifier Server specific identifier or null to retrieve all URL entries
	 */
	Vector getProtectedURLs(String identifier) throws OperationNotSupportedException;

	/**
	 * Updates the configuration for this plugin
	 */
	void setConfiguration(java.util.Properties props);
	
	/**
	 * Return html formatted status information about detailed information and/or current status
	 * @return String null if no information available
	 */
	String getStatusText();
}
IAuthorizationPlugin2
package dk.itp.security.authorization;

import dk.itp.security.passticket.server.PTSServer;
import dk.itp.statistics.Statistics;

/**
 * Extended implementation of an authorization plugin - 
 * @author Kim Rasmussen
 *
 */
public interface IAuthorizationPlugin2 extends IAuthorizationPlugin {
    /**
     * Called when the plugin is started, to give it the session controller instance - it can use it for doing advanced
     * stuff, such as sending commands to other session controllers if needed.
     * 
     * @param sessionController
     */
    void initialize(PTSServer sessionController);
    
    /**
     * Called when the plugin is about to be uninstalled/unloaded - it will give it a chance to unregister any listeners.
     */
    void uninstall();

	/**
	 * Gives the plugin a place to put its statistics, if any
	 */
	void setStatistics(Statistics stat);
    
}

IAuthorizationPluginUserGetter
package dk.itp.security.authorization;
import java.util.Vector;

import dk.itp.security.authorization.client.OperationNotSupportedException;
import dk.itp.security.passticket.PTException;

/**
 * This interface can be implemented by an authorization plugin - if implemented, the plugin can deliver a list of its available users.
 *
 * @author Kim Rasmussen
 * @version $revision$
 * 
 * <pre>
 * PortalProtect - Security infrastructure
 * Copyright(c) 2001, IT Practice A/S, All rights reserved.
 * 
 * This source code is confidential.
 * </pre>
 */
public interface IAuthorizationPluginUserGetter {
	/**
	 * Returns known users for the given criterias.<br>
	 * Note that this method is most suited for exposing known users to an administration interface, like weblogics portal administration.
	 * @param wildcard The wildcard to match, could be * or part of the username/id
	 * @param groupName If not null, searches for users within the specified group
	 * @param searchSpec Additional search parameters, can be used for limiting searches if not null
	 * @param maxReturns Maximum number of user entries to return
	 * @param identifier Either identifies the server name, or null for non-server specific
	 * @return Vector of UserEntry records
	 * @throws PTException. This is thrown if some error occurs.
	 */
	public Vector getAvailableUsers(String wildcard, String groupName, String searchSpec, int maxReturns, String identifier) throws PTException, OperationNotSupportedException;
	
	/**
	 * Returns a list groups which the specified user belongs to
	 * @param userid Userid to query
	 */
	public String[] getUserGroups(String userid) throws PTException, OperationNotSupportedException;
}

Sample Plugin

The Ceptor PortalProtect distribution contains a set of sample plugins, including their source code

This plugin implements fetching users and groups from the User Administration Database using the UserAdmin API. It demonstrates the functionality of an authorization plugin.

This sample plugin reads the list of protected URLs from a file called protectedurls.txt and groups and ACLs from the database.

Sample UAAuthorizationPlugin
package dk.portalprotect.sample.plugins;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Vector;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dk.itp.portalprotect.useradmin.data.Acl;
import dk.itp.portalprotect.useradmin.data.Challenge;
import dk.itp.portalprotect.useradmin.data.Group;
import dk.itp.portalprotect.useradmin.data.QuickUser;
import dk.itp.portalprotect.useradmin.data.User;
import dk.itp.pp.useradmin.client.IUserAdmin;
import dk.itp.pp.useradmin.client.UserAdminAgent;
import dk.itp.pp.useradmin.client.UserAdminException;
import dk.itp.security.authorization.IAuthorizationPlugin;
import dk.itp.security.authorization.IAuthorizationPluginUserGetter;
import dk.itp.security.authorization.client.ACLEntry;
import dk.itp.security.authorization.client.GroupEntry;
import dk.itp.security.authorization.client.OperationNotSupportedException;
import dk.itp.security.authorization.client.PermissionEntry;
import dk.itp.security.authorization.client.URLEntry;
import dk.itp.security.authorization.client.UserEntry;
import dk.itp.security.passticket.AuthTypes;
import dk.itp.security.passticket.PTException;

/**
 * This Authorization plugin reads usergroups from the useradmin database, 
 * and, acls and protected URLs from files.
 * 
 * @author Kim Rasmussen
 * @version $Revision$
 * 
 * <pre>
 * PortalProtect - Security infrastructure
 * Copyright(c) 2001, IT Practice A/S, All rights reserved.
 * 
 * This source code is confidential.
 * </pre>
 */
public class UAAuthorizationPlugin implements IAuthorizationPlugin, IAuthorizationPluginUserGetter {
	private Vector protectedURLs;
	private static Logger log = LoggerFactory.getLogger(UAAuthorizationPlugin.class);

	/**
	 * @see IAuthorizationPlugin#getACLsForUser(String)
	 */
	public Vector getACLsForUser(String principal) throws OperationNotSupportedException {
		throw new OperationNotSupportedException("Not supported");
	}

	/**
	 * @see IAuthorizationPlugin#getAuthorizationType()
	 */
	public int getAuthorizationType() {
		return AuthTypes.AUTHORIZATIONTYPE_USERADMINISTRATION;
	}

	/**
	 * @see IAuthorizationPlugin#getAvailableACLs(String)
	 */
	public Vector getAvailableACLs(String identifier) throws OperationNotSupportedException {
		Vector ret = new Vector();
		Hashtable ht = new Hashtable();
		
		IUserAdmin ua = UserAdminAgent.getInstance();
		
		try {
			Acl[] acls = ua.listAcls();
		
			for(int i = 0; i < acls.length; i++) {
				String name = acls[i].getName();
				String permission = "*";
				int ofs = name.lastIndexOf('.');
				
				if (ofs >= 0) {
					permission = name.substring(ofs+1);
					name = name.substring(0, ofs);
				}
				
				ACLEntry entry = (ACLEntry)ht.get(name);
				if (entry == null) {
					entry = new ACLEntry(name);
					ht.put(name, entry);
					ret.addElement(entry);
				}
				PermissionEntry pe = new PermissionEntry(permission);
				entry.addPermission(pe);
				
				Group[] groups = acls[i].getGroups();
				for( int n = 0; groups != null && n < groups.length; n++) {
					Group grp = groups[n];
					pe.addMember(grp.getName());
				}
				
				QuickUser[] users = acls[i].getUsers();
				for( int n = 0; users != null && n < users.length; n++) {
					QuickUser usr = users[n];
					pe.addMember("uid:"+usr.getID());
				}
			}
		} catch (Throwable e) {
			log.error("Unable to read acls from UserAdmin server", e);
		}
		return ret;
	}

	/**
	 * @see IAuthorizationPlugin#getAvailableGroups(String)
	 */
	public Vector getAvailableGroups(String identifier) throws OperationNotSupportedException {
		Vector usergroups = new Vector();

		IUserAdmin ua = UserAdminAgent.getInstance();
		
		try {
			Group[] groups = ua.getAllGroups();
			for(int i = 0; i < groups.length; i++) {
				GroupEntry entry = new GroupEntry(groups[i].getName(), groups[i].getName());
				
				// Do not fill in the list of group members, since group membership is stored
				// on the individual user record, since it scales a lot better than storing all users that are
				// member of a group in the group itself.
				
				usergroups.addElement(entry);
			}
		} catch (Throwable e) {
			log.error("Unable to read user groups from UserAdmin server", e);
		}
		
		return usergroups;
	}

	/**
	 * @see IAuthorizationPlugin#getName()
	 */
	public String getName() {
		return "Useradmin DB authorization plugin";
	}
	
	/**
	 * Returns known users for the given criterias.<br>
	 * Note that this method is most suited for exposing known users to an administration interface, like weblogics portal administration.
	 * @param wildcard The wildcard to match, could be * or part of the username/id
	 * @param groupName If not null, searches for users within the specified group
	 * @param searchSpec Additional search parameters, can be used for limiting searches if not null
	 * @param maxReturns Maximum number of user entries to return
	 * @param identifier Either identifies the server name, or null for non-server specific
	 * @return Vector of UserEntry records
	 * @throws PTException. This is thrown if some error occurs.
	 */
	public Vector getAvailableUsers(String wildcard, String groupName, String searchSpec, int maxReturns, String identifier) throws PTException {
		Vector users = new Vector();

		IUserAdmin ua = UserAdminAgent.getInstance();
		
		try {
			Challenge[] challenges = ua.searchChallenges(wildcard.replace('*', '%'), groupName, maxReturns);
			for(int i = 0; i < challenges.length; i++) {
				UserEntry entry = new UserEntry(challenges[i].getLogonID(), null);
				users.addElement(entry);
			}
		} catch (Throwable e) {
			log.error("Unable to read user groups from UserAdmin server", e);
		}
		
		return users;
	}
	

	/**
	 * @see IAuthorizationPlugin#getSpecificACL(String, String)
	 */
	public Object getSpecificACL(String identifier, String name) throws OperationNotSupportedException {
		Vector v = getAvailableACLs(identifier);
		if (v == null)
			return null;
			
		Enumeration enumeration = v.elements();
		while(enumeration.hasMoreElements()) {
			ACLEntry entry = (ACLEntry)enumeration.nextElement();
			
			if (entry.getName().equals(name))
				return entry;
		}
		
		return null;
	}

	/**
	 * @see IAuthorizationPlugin#getSpecificGroup(String, String)
	 */
	public Object getSpecificGroup(String identifier, String name) throws OperationNotSupportedException {
		IUserAdmin ua = UserAdminAgent.getInstance();
		
		try {
			Group[] groups = ua.getAllGroups();
			for(int i = 0; i < groups.length; i++) {
				if (name.equals(groups[i].getName()))
					return new GroupEntry(groups[i].getName(), groups[i].getName());
			}
		} catch (Throwable e) {
			log.error("Unable to read user groups from UserAdmin server", e);
		}
		
		return null;
	}

	/**
	 * @see IAuthorizationPlugin#getProtectedURLs(String)
	 */
	public Vector getProtectedURLs(String identifier) throws OperationNotSupportedException {
		return protectedURLs;
	}

	/**
	 * @see IAuthorizationPlugin#setConfiguration(Properties)
	 */
	public void setConfiguration(Properties props) {
		System.getProperties().setProperty("useradmin.server", props.getProperty("useradminservers", "localhost:15000"));

		String userid = props.getProperty("ua_userid", "userid");
		String password = props.getProperty("ua_credentials", "password");

		try {
			UserAdminAgent.getInstance().login(userid, password);
		} catch (UserAdminException e) {
			log.error("Unable to login to user administration", e);
		}
		
		protectedURLs = new Vector();

		String filename = props.getProperty("protectedUrlsFile", "protectedurls.txt");
		log.info("Reading protected URLs from " + filename);
		FileReader fr = null;
		try {
			fr = new FileReader(filename);
			BufferedReader br = new BufferedReader(fr);
			String line = br.readLine();
			while (line != null) {
				while (line != null && line.trim().length() == 0)
					line = br.readLine();
				if (line == null)
					break;

				String url = line.trim();
				URLEntry entry = new URLEntry(url);
				// Now, read the permissions required to access this URL - they are all indented at least one whitespace
				// when the next line does not start with a whitespace, it is probably another URL

				do {
					line = br.readLine();
					while (line != null && line.trim().length() == 0)
						line = br.readLine();
					if (line == null)
						break;
					if (!Character.isWhitespace(line.charAt(0))) {
						break;
					}
					entry.addPrincipal(line.trim());
				}
				while (line != null && Character.isWhitespace(line.charAt(0)));
				protectedURLs.addElement(entry);
				log.info("Added protected URL: " + entry);
			}

			log.info("Finished reading URLs");
		} catch (Exception e) {
			log.error("Unable to read protected URLs", e);
		} finally {
			if (fr != null)
				try {
					fr.close();
				} catch (IOException e) {}
		}
	}

	/**
	 * @see IAuthorizationPlugin#getStatusText()
	 */
	public String getStatusText() {
		return null;
	}

	/**
	 * @see dk.itp.security.authorization.IAuthorizationPluginUserGetter#getUserGroups(java.lang.String)
	 */
	public String[] getUserGroups(String userid) throws PTException, OperationNotSupportedException {
		try {
			User user = UserAdminAgent.getInstance().getUser(userid, -1);
			Group[] groups = UserAdminAgent.getInstance().getGroups(user.getID());
			String[] result = new String[groups.length];
			for(int i = 0; i < groups.length; i++) {
				result[i] = groups[i].getName();
			}
			return result;
		} catch(UserAdminException e) {
			if (e.getErrorCode() == UserAdminException.ERROR_NOROWSFOUND)
				return new String[0];
			log.warn("Problem reading users groups, user: " + userid, e);
			
			// Don't throw a UserAdminException, since the client (which is probably a weblogic realm) does not have this exception class)
			throw new PTException(e.toString(), e.getErrorCode(), e.getErrorText());
		}
	}
}