/** * */ package com.digitalsanctuary.jforum; import java.rmi.RemoteException; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.http.Cookie; import net.jforum.ControllerUtils; import net.jforum.JForumExecutionContext; import net.jforum.context.RequestContext; import net.jforum.context.SessionContext; import net.jforum.dao.DataAccessDriver; import net.jforum.dao.GroupDAO; import net.jforum.dao.UserDAO; import net.jforum.entities.Group; import net.jforum.entities.User; import net.jforum.entities.UserSession; import net.jforum.sso.SSO; import net.jforum.sso.SSOUtils; import net.jforum.util.preferences.ConfigKeys; import net.jforum.util.preferences.SystemGlobals; import com.atlassian.crowd.integration.exception.InvalidAuthorizationTokenException; import com.atlassian.crowd.integration.exception.InvalidTokenException; import com.atlassian.crowd.integration.exception.ObjectNotFoundException; import com.atlassian.crowd.integration.service.soap.client.SecurityServerClientFactory; import com.atlassian.crowd.integration.soap.SOAPAttribute; import com.atlassian.crowd.integration.soap.SOAPPrincipal; /** * The CrowdSSO class implements the JForum SSO interface and hooks into Crowd to enable single sign-on and * auto-registration. * * @author Devon Hillard */ public class CrowdSSO implements SSO { /** * */ private static final String SSO_CROWD_SYNC_GROUPS_FLAG = "sso.crowd.syncGroups"; /** The Constant CROWD_TOKEN_ATTR. */ private static final String CROWD_TOKEN_ATTR = "CrowdToken"; /** The Constant CROWD_TOKEN_KEY_COOKIE_NAME. */ private static final String CROWD_TOKEN_KEY_COOKIE_NAME = "crowd.token_key"; /** The Constant CROWD_SOAP_ATTR_EMAIL. */ private static final String CROWD_SOAP_ATTR_EMAIL = "mail"; /** The logger. */ static final Logger log = Logger.getLogger(CrowdSSO.class.getName()); /** * Authenticate user. * * @param pRequestContext * the request context * * @return the string * * @see net.jforum.sso.SSO#authenticateUser(net.jforum.context.RequestContext) */ public String authenticateUser(final RequestContext pRequestContext) { String jforumUsername = null; log.log(Level.INFO, "Entering authenticateUser method"); User user = null; try { // Lookup the user from Crowd based on the token key in the cookie. SOAPPrincipal principal = SecurityServerClientFactory.getSecurityServerClient().findPrincipalByToken( getCrowdTokenKey(pRequestContext)); final SOAPAttribute[] attributes = principal.getAttributes(); String crowdUsername = principal.getName(); log.log(Level.INFO, "principal username: " + crowdUsername); SSOUtils ssoUtils = new SSOUtils(); if (!ssoUtils.userExists(crowdUsername)) { log.log(Level.INFO, "user wasn't found in jforum. Creating."); String email = getAttribute(CROWD_SOAP_ATTR_EMAIL, attributes); ssoUtils.register(crowdUsername, email); } user = ssoUtils.getUser(); // If we managed to load up a user, cache the crowd token in a cookie to check the session later. if (user != null) { jforumUsername = user.getUsername(); SessionContext session = JForumExecutionContext.getRequest().getSessionContext(); session.setAttribute(CROWD_TOKEN_ATTR, getCrowdTokenKey(pRequestContext)); if (SystemGlobals.getBoolValue(SSO_CROWD_SYNC_GROUPS_FLAG)) { syncGroups(user.getId()); } } } catch (RemoteException e) { log.log(Level.WARNING, e.getMessage()); } catch (InvalidAuthorizationTokenException e) { log.log(Level.WARNING, e.getMessage()); } catch (InvalidTokenException e) { log.log(Level.WARNING, e.getMessage()); } log.log(Level.INFO, "Returning username: " + jforumUsername); return jforumUsername; } /** * Checks if is session valid. * * @param pUserSession * the user session * @param pRequestContext * the request context * * @return true, if checks if is session valid * * @see net.jforum.sso.SSO#isSessionValid(net.jforum.entities.UserSession, net.jforum.context.RequestContext) */ public boolean isSessionValid(final UserSession pUserSession, final RequestContext pRequestContext) { log.log(Level.INFO, "isSessionValid entered."); boolean validSession = false; SessionContext session = JForumExecutionContext.getRequest().getSessionContext(); // if the current user is anonymous, blow out a potential old session token if (pUserSession.getUserId() == SystemGlobals.getIntValue(ConfigKeys.ANONYMOUS_USER_ID)) { session.setAttribute(CROWD_TOKEN_ATTR, null); } String crowdSessionToken = (String) session.getAttribute(CROWD_TOKEN_ATTR); String crowdToken = getCrowdTokenKey(pRequestContext); log.log(Level.INFO, "isSessionValid: crowdSessionToken: " + crowdSessionToken); log.log(Level.INFO, "isSessionValid: crowdToken: " + crowdToken); // If we've already loaded in a crowd token, check to see if it's the same as the current one if (crowdSessionToken != null && crowdSessionToken.length() > 0) { if (crowdSessionToken.equals(crowdToken)) { validSession = true; } } log.log(Level.INFO, "isSessionValid: returning: " + validSession); return validSession; } private void syncGroups(final int pUserId) { log.log(Level.INFO, "Entering syncGroups method"); UserDAO userDAO = DataAccessDriver.getInstance().newUserDAO(); User user = userDAO.selectById(pUserId); // Get the current groups from Crowd String[] crowdGroups = getCrowdGroups(user.getUsername()); // Get the current groups from JForum List forumGroups = user.getGroupsList(); // Loop through all the crowd groups for (String crowdGroup : crowdGroups) { boolean crowdGroupInForumGroups = false; // Check if the crowd group is already in the JForum list for (Group group : forumGroups) { if (group.getName().equals(crowdGroup)) { crowdGroupInForumGroups = true; } } // if the crowd group isn't in JForum, add it if (!crowdGroupInForumGroups) { GroupDAO groupDAO = DataAccessDriver.getInstance().newGroupDAO(); // See if the group exists in JForum Group newGroup = getGroupByName(crowdGroup); // If not, create it if (newGroup == null) { newGroup = new Group(); newGroup.setName(crowdGroup); newGroup.setDescription("Crowd Group " + crowdGroup); newGroup.setParentId(0); groupDAO.addNew(newGroup); } Group loadedGroup = getGroupByName(newGroup.getName()); userDAO.addToGroup(user.getId(), new int[] { loadedGroup.getId() }); forumGroups.add(newGroup); } } // Loop through all the JForum groups for (Group jforumGroup : forumGroups) { boolean jForumGroupInCrowdGroups = false; // Check if the JForum group is still live in the Crowd list for (String crowdGroup : crowdGroups) { if (crowdGroup.equals(jforumGroup.getName())) { jForumGroupInCrowdGroups = true; } } // If the JForum group isn't in Crowd, remove it from the user's list if (!jForumGroupInCrowdGroups) { userDAO.removeFromGroup(user.getId(), new int[] { jforumGroup.getId() }); } } userDAO.update(user); } private String[] getCrowdGroups(final String pCrowdUsername) { String[] groups = null; try { groups = SecurityServerClientFactory.getSecurityServerClient().findGroupMemberships(pCrowdUsername); } catch (RemoteException e) { log.log(Level.WARNING, e.getMessage()); } catch (InvalidAuthorizationTokenException e) { log.log(Level.WARNING, e.getMessage()); } catch (ObjectNotFoundException e) { log.log(Level.WARNING, e.getMessage()); } return groups; } private Group getGroupByName(final String pGroupName) { List groups = DataAccessDriver.getInstance().newGroupDAO().selectAll(); for (Group group : groups) { if (group.getName() != null && group.getName().equals(pGroupName)) { return group; } } return null; } /** * Gets the crowd token key from the cookie. * * @param pRequestContext * the request context. * * @return the crowd token key. */ private String getCrowdTokenKey(final RequestContext pRequestContext) { Cookie crowdTokenCookie = ControllerUtils.getCookie(CROWD_TOKEN_KEY_COOKIE_NAME); if (crowdTokenCookie == null) { log.log(Level.WARNING, "crowdTokenCookie is null."); } String crowdTokenKey = null; if (crowdTokenCookie != null) { crowdTokenKey = crowdTokenCookie.getValue(); log.log(Level.WARNING, "crowdTokenKey: " + crowdTokenKey); } return crowdTokenKey; } /** * Look up the value of the given attribute name within the SOAP attributes provided from Crowd. * * @param pAttributeName * The attribute to lookup * @param pAttributesArray * SOAPAttributes provided by crowd for a user. * * @return String value of the attribute */ private String getAttribute(final String pAttributeName, final SOAPAttribute[] pAttributesArray) { for (int i = 0; i < pAttributesArray.length; i++) { final SOAPAttribute attribute = pAttributesArray[i]; final String name = attribute.getName(); if (name != null && pAttributeName.equals(name)) { final String[] values = attribute.getValues(); if (values != null) { return values[0]; } } } return null; } }