package org.jboss.seam.security.external.openid; import java.io.Serializable; import java.util.Collection; import java.util.LinkedList; import java.util.List; import javax.enterprise.context.SessionScoped; import javax.enterprise.inject.Instance; import javax.inject.Inject; import javax.inject.Named; import javax.servlet.http.HttpServletResponse; import org.jboss.solder.logging.Logger; import org.jboss.seam.security.AuthenticationException; import org.jboss.seam.security.Authenticator; import org.jboss.seam.security.BaseAuthenticator; import org.jboss.seam.security.Identity; import org.jboss.seam.security.external.openid.api.OpenIdPrincipal; import org.jboss.seam.security.external.openid.api.OpenIdRelyingPartyApi; import org.jboss.seam.security.external.openid.api.OpenIdRequestedAttribute; import org.jboss.seam.security.external.openid.providers.OpenIdProvider; import org.jboss.seam.security.management.picketlink.IdentitySessionProducer; import org.picketlink.idm.api.Group; import org.picketlink.idm.api.IdentitySession; import org.picketlink.idm.api.Role; import org.picketlink.idm.api.RoleType; import org.picketlink.idm.api.User; import org.picketlink.idm.common.exception.FeatureNotSupportedException; import org.picketlink.idm.common.exception.IdentityException; /** * An Authenticator implementation that uses OpenID to authenticate the user. * * @author Shane Bryzak */ public @Named("openIdAuthenticator") @SessionScoped class OpenIdAuthenticator extends BaseAuthenticator implements Authenticator, Serializable { private static final long serialVersionUID = 4669651866032932651L; @Inject Instance<OpenIdRelyingPartyApi> openIdApiInstance; @Inject List<OpenIdProvider> providers; @Inject Logger log; @Inject HttpServletResponse response; @Inject Instance<IdentitySession> identitySession; @Inject IdentitySessionProducer identitySessionProducer; @Inject Identity identity; /** * If this property is set to true (the default) then user roles and attributes will be managed using the Identity * Management API. */ private boolean identityManaged = true; /** * This code indicates which OpenID provider should be used to authenticate against. See the classes in the * org.jboss.seam.security.external.openid.providers package. */ private String providerCode; public boolean isIdentityManaged() { return identityManaged; } public void setIdentityManaged(boolean identityManaged) { this.identityManaged = identityManaged; } public String getProviderCode() { return providerCode; } public void setProviderCode(String providerCode) { this.providerCode = providerCode; } protected OpenIdProvider getSelectedProvider() { if (providerCode != null) { for (OpenIdProvider provider : providers) { if (providerCode.equals(provider.getCode())) return provider; } } return null; } public void authenticate() { OpenIdProvider selectedProvider = getSelectedProvider(); if (selectedProvider == null) { throw new IllegalStateException("No OpenID provider has been selected"); } OpenIdRelyingPartyApi openIdApi = openIdApiInstance.get(); List<OpenIdRequestedAttribute> attributes = new LinkedList<OpenIdRequestedAttribute>(); selectedProvider.requestAttributes(openIdApi, attributes); openIdApi.login(selectedProvider.getUrl(), attributes, getResponse()); setStatus(AuthenticationStatus.DEFERRED); } protected HttpServletResponse getResponse() { return response; } public List<OpenIdProvider> getProviders() { return providers; } public void success(OpenIdPrincipal principal) { User user = new OpenIdUser(principal); if (isIdentityManaged()) { // By default we set the status to FAILURE, if we manage to get to the end // of this method we get rewarded with a SUCCESS setStatus(AuthenticationStatus.FAILURE); if (identitySessionProducer.isConfigured()) { validateManagedUser(principal); } } setUser(user); setStatus(AuthenticationStatus.SUCCESS); } /** * Validates the OpenID user against the local Identity Management store. Important!! This method * must be invoked within an active transaction if you are using JpaIdentityStore (i.e., add the * @Transactional annotation to your authentication method). * * * @param principal */ protected void validateManagedUser(OpenIdPrincipal principal) { IdentitySession session = identitySession.get(); try { // Check that the user's identity exists if (session.getPersistenceManager().findUser(principal.getIdentifier()) == null) { // The user wasn't found, let's create them User user = session.getPersistenceManager().createUser(principal.getIdentifier()); // TODO allow the OpenID -> IDM attribute mapping to be configured // Map fetched attributes to identity-managed attributes for new users for (String alias : principal.getAttributeValues().keySet()) { session.getAttributesManager().addAttribute(user, alias, principal.getAttribute(alias)); } // Load the user's roles and groups try { Collection<RoleType> roleTypes = session.getRoleManager().findUserRoleTypes(user); for (RoleType roleType : roleTypes) { for (Role role : session.getRoleManager().findRoles(user, roleType)) { identity.addRole(role.getRoleType().getName(), role.getGroup().getName(), role.getGroup().getGroupType()); } } for (Group g : session.getRelationshipManager().findAssociatedGroups(user)) { identity.addGroup(g.getName(), g.getGroupType()); } } catch (FeatureNotSupportedException ex) { throw new AuthenticationException("Error loading user's roles and groups", ex); } catch (IdentityException ex) { throw new AuthenticationException("Error loading user's roles and groups", ex); } } } catch (IdentityException ex) { throw new AuthenticationException("Error locating User record for OpenID user", ex); } } }