All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.theit.hudson.crowd.CrowdConfigurationService Maven / Gradle / Ivy

Go to download

SecurityRealm that enables the use of Atlassian's Crowd identity management server.

The newest version!
/*
 * @(#)CrowdConfigurationService.java
 * 
 * The MIT License
 * 
 * Copyright (C)2011 Thorsten Heit.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package de.theit.hudson.crowd;

import static de.theit.hudson.crowd.ErrorMessages.applicationPermission;
import static de.theit.hudson.crowd.ErrorMessages.groupNotFound;
import static de.theit.hudson.crowd.ErrorMessages.invalidAuthentication;
import static de.theit.hudson.crowd.ErrorMessages.operationFailed;
import static de.theit.hudson.crowd.ErrorMessages.specifyGroup;
import static de.theit.hudson.crowd.ErrorMessages.userNotFound;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;

import com.atlassian.crowd.exception.ApplicationPermissionException;
import com.atlassian.crowd.exception.GroupNotFoundException;
import com.atlassian.crowd.exception.InvalidAuthenticationException;
import com.atlassian.crowd.exception.OperationFailedException;
import com.atlassian.crowd.exception.UserNotFoundException;
import com.atlassian.crowd.integration.http.CrowdHttpAuthenticator;
import com.atlassian.crowd.integration.http.util.CrowdHttpTokenHelper;
import com.atlassian.crowd.model.group.Group;
import com.atlassian.crowd.service.client.ClientProperties;
import com.atlassian.crowd.service.client.CrowdClient;

/**
 * This class contains all objects that are necessary to access the REST
 * services on the remote Crowd server. Additionally it contains some helper
 * methods to check for group membership and availability.
 * 
 * @author Thorsten Heit ([email protected])
 * @since 08.09.2011
 * @version $Id$
 */
public class CrowdConfigurationService {
	/** Used for logging purposes. */
	private static final Logger LOG = Logger
			.getLogger(CrowdConfigurationService.class.getName());

	/**
	 * The maximum number of groups that can be fetched from the Crowd server
	 * for a user in one request.
	 */
	private static final int MAX_GROUPS = 500;

	/** Holds the Crowd client properties. */
	ClientProperties clientProperties;

	/** The Crowd client to access the REST services on the remote Crowd server. */
	CrowdClient crowdClient;

	/** The helper class for Crowd SSO token operations. */
	CrowdHttpTokenHelper tokenHelper;

	/**
	 * The interface used to manage HTTP authentication and web/SSO
	 * authentication integration.
	 */
	CrowdHttpAuthenticator crowdHttpAuthenticator;

	/** The names of all user groups that are allowed to login. */
	Collection allowedGroupNames;

	/** Specifies whether nested groups may be used. */
	private boolean nestedGroups;

	/**
	 * Creates a new Crowd configuration object.
	 * 
	 * @param pGroupNames
	 *            The group names to use when authenticating Crowd users. May
	 *            not be null.
	 * @param pNestedGroups
	 *            Specifies whether nested groups should be used when validating
	 *            users against a group name.
	 */
	public CrowdConfigurationService(String pGroupNames, boolean pNestedGroups) {
		if (0 == pGroupNames.length()) {
			throw new IllegalArgumentException(specifyGroup());
		}

		if (LOG.isLoggable(Level.INFO)) {
			LOG.info("Groups given for Crowd configuration service: "
					+ pGroupNames);
		}
		this.allowedGroupNames = new ArrayList();
		for (String group : pGroupNames.split("[,\\s]")) {
			if (null != group && group.trim().length() > 0) {
				if (LOG.isLoggable(Level.FINE)) {
					LOG.fine("-> adding allowed group name: " + group);
				}
				this.allowedGroupNames.add(group);
			}
		}

		if (this.allowedGroupNames.isEmpty()) {
			throw new IllegalArgumentException(specifyGroup());
		}

		this.nestedGroups = pNestedGroups;
	}

	/**
	 * Checks whether the user is a member of one of the Crowd groups whose
	 * members are allowed to login.
	 * 
	 * @param username
	 *            The name of the user to check. May not be null or
	 *            empty.
	 * @return true if and only if the group exists, is active and
	 *         the user is either a direct group member or, if nested groups may
	 *         be used, a nested group member. false else.
	 */
	public boolean isGroupMember(String username) {
		boolean retval = false;

		try {
			for (String group : this.allowedGroupNames) {
				retval = isGroupMember(username, group);
				if (retval) {
					break;
				}
			}
		} catch (ApplicationPermissionException ex) {
			LOG.warning(applicationPermission());
		} catch (InvalidAuthenticationException ex) {
			LOG.warning(invalidAuthentication());
		} catch (OperationFailedException ex) {
			LOG.log(Level.SEVERE, operationFailed(), ex);
		}

		return retval;
	}

	/**
	 * Checks whether the user is a member of the given Crowd group.
	 * 
	 * @param username
	 *            The name of the user to check. May not be null or
	 *            empty.
	 * @param group
	 *            The name of the group to check the user against. May not be
	 *            null.
	 * @return true if and only if the group exists, is active and
	 *         the user is either a direct group member or, if nested groups may
	 *         be used, a nested group member. false else.
	 * 
	 * @throws ApplicationPermissionException
	 *             If the application is not permitted to perform the requested
	 *             operation on the server.
	 * @throws InvalidAuthenticationException
	 *             If the application and password are not valid.
	 * @throws OperationFailedException
	 *             If the operation has failed for any other reason, including
	 *             invalid arguments and the operation not being supported on
	 *             the server.
	 */
	private boolean isGroupMember(String username, String group)
			throws ApplicationPermissionException,
			InvalidAuthenticationException, OperationFailedException {
		boolean retval = false;

		if (isGroupActive(group)) {
			if (LOG.isLoggable(Level.FINE)) {
				LOG.fine("Checking group membership for user '" + username
						+ "' and group '" + group + "'...");
			}
			if (this.crowdClient.isUserDirectGroupMember(username, group)) {
				retval = true;
				if (LOG.isLoggable(Level.FINER)) {
					LOG.finer("=> user is a direct group member");
				}
			} else if (this.nestedGroups
					&& this.crowdClient
							.isUserNestedGroupMember(username, group)) {
				retval = true;
				if (LOG.isLoggable(Level.FINER)) {
					LOG.finer("=> user is a nested group member");
				}
			}
		}

		return retval;
	}

	/**
	 * Checks if the specified group name exists on the remote Crowd server and
	 * is active.
	 * 
	 * @param groupName
	 *            The name of the group to check. May not be null
	 *            or empty.
	 * @return true if and only if the group name is not empty,
	 *         does exist on the remote Crowd server and is active.
	 *         false else.
	 * @throws InvalidAuthenticationException
	 *             If the application and password are not valid.
	 * @throws ApplicationPermissionException
	 *             If the application is not permitted to perform the requested
	 *             operation on the server
	 * @throws OperationFailedException
	 *             If the operation has failed for any other reason, including
	 *             invalid arguments and the operation not being supported on
	 *             the server.
	 */
	public boolean isGroupActive(String groupName)
			throws InvalidAuthenticationException,
			ApplicationPermissionException, OperationFailedException {
		boolean retval = false;

		try {
			if (LOG.isLoggable(Level.FINE)) {
				LOG.fine("Checking whether group is active: " + groupName);
			}
			Group group = this.crowdClient.getGroup(groupName);
			if (null != group) {
				retval = group.isActive();
			}
		} catch (GroupNotFoundException ex) {
			if (LOG.isLoggable(Level.FINE)) {
				LOG.fine(groupNotFound(groupName));
			}
		}

		return retval;
	}

	/**
	 * Retrieves the list of all (nested) groups from the Crowd server that the
	 * user is a member of.
	 * 
	 * @param username
	 *            The name of the user. May not be null.
	 * @return The list of all groups that the user is a member of. Always
	 *         non-null.
	 */
	public Collection getAuthoritiesForUser(String username) {
		Collection authorities = new TreeSet(
				new Comparator() {
					@Override
					public int compare(GrantedAuthority ga1,
							GrantedAuthority ga2) {
						return ga1.getAuthority().compareTo(ga2.getAuthority());
					}
				});

		HashSet groupNames = new HashSet();

		// retrieve the names of all groups the user is a direct member of
		try {
			int index = 0;
			if (LOG.isLoggable(Level.FINE)) {
				LOG.fine("Retrieve list of groups with direct membership for user '"
						+ username + "'...");
			}
			while (true) {
				if (LOG.isLoggable(Level.FINEST)) {
					LOG.finest("Fetching groups [" + index + "..."
							+ (index + MAX_GROUPS - 1) + "]...");
				}
				List groups = this.crowdClient.getGroupsForUser(
						username, index, MAX_GROUPS);
				if (null == groups || groups.isEmpty()) {
					break;
				}
				for (Group group : groups) {
					if (group.isActive()) {
						groupNames.add(group.getName());
					}
				}
				index += MAX_GROUPS;
			}
		} catch (UserNotFoundException ex) {
			if (LOG.isLoggable(Level.INFO)) {
				LOG.info(userNotFound(username));
			}
		} catch (InvalidAuthenticationException ex) {
			LOG.warning(invalidAuthentication());
		} catch (ApplicationPermissionException ex) {
			LOG.warning(applicationPermission());
		} catch (OperationFailedException ex) {
			LOG.log(Level.SEVERE, operationFailed(), ex);
		}

		// now the same but for nested group membership if this configuration
		// setting is active/enabled
		if (this.nestedGroups) {
			try {
				int index = 0;
				if (LOG.isLoggable(Level.FINE)) {
					LOG.fine("Retrieve list of groups with direct membership for user '"
							+ username + "'...");
				}
				while (true) {
					if (LOG.isLoggable(Level.FINEST)) {
						LOG.finest("Fetching groups [" + index + "..."
								+ (index + MAX_GROUPS - 1) + "]...");
					}
					List groups = this.crowdClient
							.getGroupsForNestedUser(username, index, MAX_GROUPS);
					if (null == groups || groups.isEmpty()) {
						break;
					}
					for (Group group : groups) {
						if (group.isActive()) {
							groupNames.add(group.getName());
						}
					}
					index += MAX_GROUPS;
				}
			} catch (UserNotFoundException ex) {
				if (LOG.isLoggable(Level.INFO)) {
					LOG.info(userNotFound(username));
				}
			} catch (InvalidAuthenticationException ex) {
				LOG.warning(invalidAuthentication());
			} catch (ApplicationPermissionException ex) {
				LOG.warning(applicationPermission());
			} catch (OperationFailedException ex) {
				LOG.log(Level.SEVERE, operationFailed(), ex);
			}
		}

		// now create the list of authorities
		for (String str : groupNames) {
			authorities.add(new GrantedAuthorityImpl(str));
		}

		return authorities;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy