Java tutorial
/* * JOSSO: Java Open Single Sign-On * * Copyright 2004-2009, Atricore, Inc. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * */ package org.josso.gateway.identity.service.store; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.josso.auth.BaseCredential; import org.josso.auth.Credential; import org.josso.auth.CredentialKey; import org.josso.auth.CredentialProvider; import org.josso.auth.scheme.AuthenticationScheme; import org.josso.gateway.SSONameValuePair; import org.josso.gateway.SSOException; import org.josso.gateway.identity.exceptions.NoSuchRoleException; import org.josso.gateway.identity.exceptions.NoSuchUserException; import org.josso.gateway.identity.exceptions.SSOIdentityException; import org.josso.gateway.identity.service.BaseRole; import org.josso.gateway.identity.service.BaseRoleImpl; import org.josso.gateway.identity.service.BaseUser; import org.josso.gateway.identity.service.BaseUserImpl; import org.josso.selfservices.ChallengeResponseCredential; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.w3c.dom.Node; import org.w3c.dom.Element; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilder; import java.security.cert.X509Certificate; import java.util.*; import java.util.Map.Entry; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; /** * @org.apache.xbean.XBean element="memory-store" * * @author <a href="mailto:sgonzalez@josso.org">Sebastian Gonzalez Oyuela</a> * @version $Id: MemoryIdentityStore.java 568 2008-07-31 18:39:20Z sgonzalez $ * @org.apache.xbean.XBean element="memory-store" * <p/> * Memory based implementation of an IdentityStore and CredentialStore that reads * data from XML files. */ public class MemoryIdentityStore extends AbstractStore implements ExtendedIdentityStore { private static final Log logger = LogFactory.getLog(MemoryIdentityStore.class); // A map with BaseRole instances, the map key is the role name. private Map<String, Element> _roles; // A map with BaseUser instances, the key is the username. private Map<String, Element> _users; // Stores BaseRoles associated to each user. // The map key is the username. // The map value is a Set of rolenames. private Map<String, Set<String>> _userRoles; // Stores credential values (Object) associated to each user. // The map key is the username. // THe map value is another Map with credetinals (name=key/value) private Map<String, Element> _principalCredentials; // Stores user keys associated with principal lookup key. // It is used for strong authentication (finding certificates). // e.g. principalLookupKey value can be CN from user certificate. // The map key is a principal lookup key. // The map values is a list of user keys. private Map<String, List<String>> _principalLookupKeys; private boolean _initialized; private String _credentialsFileName; private String _usersFileName; public MemoryIdentityStore() { super(); logger.debug("Creating new MemoryIdentityStore"); _users = new HashMap<String, Element>(7); _userRoles = new HashMap<String, Set<String>>(11); _roles = new HashMap<String, Element>(11); _principalCredentials = new HashMap<String, Element>(11); _principalLookupKeys = new HashMap<String, List<String>>(11); _initialized = false; } /** * Initializes the store, reads data from XML files. */ public synchronized void initialize() { try { // This store can work as an identityStore and as a credentialStore, so // configuration parameters are optional. if (_usersFileName != null) loadUsersData(_usersFileName); if (_credentialsFileName != null) loadCredentialsData(_credentialsFileName); _initialized = true; } catch (Exception e) { logger.error(e, e); throw new RuntimeException("Can't initialize memory store : " + e.getMessage(), e); } } /** * Loads users from file. * * @param fName the file containing user definitions. */ protected void loadUsersData(String fName) throws Exception { // First, users logger.info("Reading users from : " + fName); DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); Document doc = docBuilder.parse(getClass().getResourceAsStream("/" + fName)); // normalize text representation (what for ?!) doc.getDocumentElement().normalize(); logger.debug("Root element of the doc is " + doc.getDocumentElement().getNodeName()); this.loadRoles(doc); logger.info("Loaded " + _roles.size() + " roles from : " + fName); this.loadUsers(doc); logger.info("Loaded " + _users.size() + " users from : " + fName); } protected void loadRoles(Document doc) throws SSOException { NodeList listOfRoles = doc.getElementsByTagName("role"); int totalRoles = listOfRoles.getLength(); logger.debug("Total roles: " + totalRoles); for (int i = 0; i < listOfRoles.getLength(); i++) { Node roleNode = listOfRoles.item(i); if (roleNode.getNodeType() == Node.ELEMENT_NODE) { Element domRole = (Element) roleNode; Element domName = (Element) domRole.getElementsByTagName("name").item(0); // Store DOM Element as a role. logger.debug("Storing role for name : [" + getTextContent(domName) + "]"); _roles.put(getTextContent(domName), domRole); } } } protected void loadUsers(Document doc) throws Exception { NodeList listOfUsers = doc.getElementsByTagName("user"); int totalUsers = listOfUsers.getLength(); logger.debug("Total users: " + totalUsers); for (int i = 0; i < listOfUsers.getLength(); i++) { Node userNode = listOfUsers.item(i); if (userNode.getNodeType() == Node.ELEMENT_NODE) { Element userElement = (Element) userNode; Node domName = userElement.getElementsByTagName("name").item(0); logger.debug("Storing user for name : " + getTextContent(domName)); // Store DOM Element as a user. _users.put(getTextContent(domName), userElement); } } } /** * Loads credentials from file. * * @param fName the file containing user definitions. */ protected void loadCredentialsData(String fName) throws Exception { logger.info("Reading credentials from : " + fName); DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); Document doc = docBuilder.parse(getClass().getResourceAsStream("/" + fName)); // normalize text representation (what for ?!) doc.getDocumentElement().normalize(); loadCredentials(doc); logger.info("Loaded " + _principalCredentials.size() + " credential sets from : " + fName); } protected void loadCredentials(Document doc) throws Exception { NodeList credentialSetLst = doc.getElementsByTagName("credential-set"); int totalCredentials = credentialSetLst.getLength(); logger.debug("Total credential sets: " + totalCredentials); for (int i = 0; i < credentialSetLst.getLength(); i++) { // Each credential set has a key and a list of credentials. Element domCredentialSet = (Element) credentialSetLst.item(i); Node domKey = domCredentialSet.getElementsByTagName("key").item(0); if (domKey.getNodeType() != Node.ELEMENT_NODE || !domKey.getNodeName().equals("key")) throw new SSOIdentityException( "Credential set definitions need a 'key' element [" + domKey.getNodeName() + "]"); String key = getTextContent(domKey); logger.info("Storing credentials for key : " + key); _principalCredentials.put(key, domCredentialSet); // Each credential set can have a principalLookupKey. Node principalLookupDomKey = domCredentialSet.getElementsByTagName("principalLookupKey").item(0); if (principalLookupDomKey != null && principalLookupDomKey.getNodeType() == Node.ELEMENT_NODE && principalLookupDomKey.getNodeName().equals("principalLookupKey")) { String principalLookupKey = getTextContent(principalLookupDomKey); logger.info("Storing principal lookup key for " + key + " : " + principalLookupKey); List<String> principalKeys = _principalLookupKeys.get(principalLookupKey); if (principalKeys == null) { principalKeys = new ArrayList<String>(); } principalKeys.add(key); _principalLookupKeys.put(principalLookupKey, principalKeys); } } } protected Collection<BaseUser> listUsers() throws SSOIdentityException { if (!_initialized) initialize(); Collection<Element> domUsers = _users.values(); List<BaseUser> ssoUsers = new ArrayList<BaseUser>(domUsers.size()); for (Element domUser : domUsers) { ssoUsers.add(toBaseUser(domUser)); } return ssoUsers; } // ------------------------------------------------------------------------------ // IdentityStore // ------------------------------------------------------------------------------ // BaseUser related methods. public synchronized BaseUser loadUser(UserKey key) throws NoSuchUserException, SSOIdentityException { if (!_initialized) initialize(); if (!(key instanceof SimpleUserKey)) { throw new SSOIdentityException("Unsupported key type : " + key.getClass().getName()); } Element domUser = _users.get(((SimpleUserKey) key).getId()); if (domUser == null) { for (Entry<String, Element> entry : _users.entrySet()) { if (entry.getKey().equalsIgnoreCase(((SimpleUserKey) key).getId())) { domUser = entry.getValue(); break; } } } if (domUser == null) throw new NoSuchUserException(key); BaseUser user = toBaseUser(domUser); if (logger.isDebugEnabled()) logger.debug("[load(" + key + ")] : ok"); return user; } /** * @param key * @throws SSOIdentityException */ public synchronized BaseRole[] findRolesByUserKey(UserKey key) throws SSOIdentityException { // TODO : This should be added to the store lifecycle if (!_initialized) initialize(); List<BaseRole> roles = new ArrayList<BaseRole>(); SimpleUserKey simpleKey = (SimpleUserKey) key; Set<String> roleNames = _userRoles.get(simpleKey.getId()); if (roleNames != null) { Iterator it = roleNames.iterator(); while (it.hasNext()) { String roleName = (String) it.next(); BaseRole role = findRoleByName(roleName); if (role == null) throw new SSOIdentityException( "Role '" + roleName + "' declared for user '" + key + "' not defined"); roles.add(role); } } return roles.toArray(new BaseRole[roles.size()]); } public String loadUsernameByRelayCredential(ChallengeResponseCredential cred) throws SSOIdentityException { logger.debug("Looking for user with " + cred.getId() + "=[" + cred.getResponse() + "]"); Collection<BaseUser> users = listUsers(); for (BaseUser user : users) { SSONameValuePair[] props = user.getProperties(); if (logger.isDebugEnabled()) logger.debug("Checking user : " + user.getName() + " with " + props.length + " properties."); if (props == null) continue; for (SSONameValuePair prop : props) { if (logger.isDebugEnabled()) logger.debug("Checking property : " + prop.getName() + "=[" + prop.getValue() + "]"); if (prop.getName().equals(cred.getId()) && prop.getValue().equals(cred.getResponse())) return user.getName(); } } // return null; } public void updateAccountPassword(UserKey key, Credential newPassword) { // TODO !!! } // ------------------------------------------------------------------------------ // CredentialStore // ------------------------------------------------------------------------------ /** * Gets configured credentials for this principal. * * @param key used to retrieve this credentials. * @throws SSOIdentityException */ public Credential[] loadCredentials(CredentialKey key, CredentialProvider cp) throws SSOIdentityException { // TODO : This should be added to the store lifecycle if (!_initialized) initialize(); if (!(key instanceof SimpleUserKey)) { throw new SSOIdentityException("Unsupported key type : " + key.getClass().getName()); } SimpleUserKey simpleKey = (SimpleUserKey) key; List<Element> credentialElements = getCredentialElements(simpleKey, cp); Credential[] creds = toCredentials(credentialElements, cp); logger.debug("Found " + creds.length + " credentials!"); if (logger.isDebugEnabled()) { for (int i = 0; i < creds.length; i++) { logger.debug("Credential[" + i + "]=" + creds[i]); } } return creds; } /** * Load user UID (username) from store. * * @param key the key used to load UID from store. * @param cp credential provider * @throws SSOIdentityException */ public String loadUID(CredentialKey key, CredentialProvider cp) throws SSOIdentityException { if (!(key instanceof SimpleUserKey)) { throw new SSOIdentityException("Unsupported key type : " + key.getClass().getName()); } SimpleUserKey simpleKey = (SimpleUserKey) key; if (key instanceof CertificateUserKey) { X509Certificate certificate = ((CertificateUserKey) key).getCertificate(); if (certificate != null) { List<Element> credentialElements = getCredentialElements(simpleKey, cp); for (Element credentialElement : credentialElements) { List<Credential> creds = toCredentials(credentialElement, cp); for (Credential cred : creds) { if (((BaseCredential) cred).getValue() instanceof X509Certificate && certificate.equals((X509Certificate) ((BaseCredential) cred).getValue())) { return getTextContent(credentialElement.getElementsByTagName("key").item(0)); } } } } } else { return simpleKey.getId(); } return null; } // ------------------------------------------------------------------------------ // utils .... // ------------------------------------------------------------------------------ /** * Gets credential element for the given key. * * @param key user id (username) * @return credential element */ protected Element getCredentialElement(String key) { Element credentialElement = _principalCredentials.get(key); if (credentialElement == null) { for (Entry<String, Element> entry : _principalCredentials.entrySet()) { if (entry.getKey().equalsIgnoreCase(key)) { credentialElement = entry.getValue(); break; } } } return credentialElement; } /** * Gets a list of credential elements for the given key. * * @param key user id (username), or principal lookup value (for certificates) * @param cp credential provider * @return list of credential elements */ protected List<Element> getCredentialElements(SimpleUserKey key, CredentialProvider cp) { List<Element> credentialElements = new ArrayList<Element>(); String schemeName = null; if (cp instanceof AuthenticationScheme) { schemeName = ((AuthenticationScheme) cp).getName(); } List<String> principalKeys = null; if ("strong-authentication".equals(schemeName)) { principalKeys = _principalLookupKeys.get(key.getId()); if (principalKeys != null && principalKeys.size() > 0) { for (String principalKey : principalKeys) { Element credentialElement = getCredentialElement(principalKey); if (credentialElement != null) { credentialElements.add(credentialElement); } } } } if (credentialElements.size() == 0) { Element credentialElement = getCredentialElement(key.getId()); if (credentialElement != null) { credentialElements.add(credentialElement); } } return credentialElements; } protected Credential[] toCredentials(List<Element> domCredentialSets, CredentialProvider cp) throws SSOIdentityException { List<Credential> creds = new ArrayList<Credential>(); if (domCredentialSets == null || domCredentialSets.size() == 0) { return creds.toArray(new Credential[creds.size()]); } for (Element domCredentialSet : domCredentialSets) { creds.addAll(toCredentials(domCredentialSet, cp)); } return creds.toArray(new Credential[creds.size()]); } /** * Transforms a DOM Node to a Credential instance * * @return */ protected List<Credential> toCredentials(Element domCredentialSet, CredentialProvider cp) throws SSOIdentityException { List<Credential> creds = new ArrayList<Credential>(); if (domCredentialSet == null) { return creds; } NodeList domCredentials = domCredentialSet.getElementsByTagName("credential"); // Each child must be a credential element for (int i = 0; i < domCredentials.getLength(); i++) { Element domCredential = (Element) domCredentials.item(i); if (domCredential.getNodeType() != Node.ELEMENT_NODE || !domCredential.getNodeName().equals("credential")) continue; Node domName = domCredential.getElementsByTagName("name").item(0); if (domName.getNodeType() != Node.ELEMENT_NODE || !domName.getNodeName().equals("name")) throw new SSOIdentityException("Credential definitions need a 'name' and 'value' element"); Node domValue = domCredential.getElementsByTagName("value").item(0); if (domValue.getNodeType() != Node.ELEMENT_NODE || !domValue.getNodeName().equals("value")) throw new SSOIdentityException("Credential definitions need a 'name' and 'value' element"); String name = getTextContent(domName); String value = getTextContent(domValue); if (logger.isDebugEnabled()) logger.debug("Creating credential [" + name + "/" + value + "] "); Credential c = cp.newCredential(name, value); if (c != null) creds.add(c); } return creds; } protected BaseRole toBaseRole(Element domRole) throws SSOIdentityException { Node domName = ((Element) domRole).getElementsByTagName("name").item(0); if (domName.getNodeType() != Node.ELEMENT_NODE || !domName.getNodeName().equals("name")) throw new SSOIdentityException("Role definitions need a 'name' element"); return new BaseRoleImpl(getTextContent(domName)); } protected BaseUser toBaseUser(Element domUser) throws SSOIdentityException { // Build the user instance Node domUsername = ((Element) domUser).getElementsByTagName("name").item(0); if (domUsername.getNodeType() != Node.ELEMENT_NODE || !domUsername.getNodeName().equals("name")) { throw new SSOIdentityException("User definitions need a 'name'"); } String username = getTextContent(domUsername); BaseUser user = new BaseUserImpl(); UserKey key = new SimpleUserKey(username); user.setName(username); // Add user properties NodeList propertiesLst = ((Element) domUser).getElementsByTagName("property"); for (int i = 0; i < propertiesLst.getLength(); i++) { Element domProperty = (Element) propertiesLst.item(i); Node domName = ((Element) domProperty).getElementsByTagName("name").item(0); if (domName.getNodeType() != Node.ELEMENT_NODE || !domName.getNodeName().equals("name")) throw new SSOIdentityException("Property definitions need a 'name' and 'value' element"); Node domValue = ((Element) domProperty).getElementsByTagName("value").item(0); if (domValue.getNodeType() != Node.ELEMENT_NODE || !domValue.getNodeName().equals("value")) throw new SSOIdentityException("Property definitions need a 'name' and 'value' element"); String name = getTextContent(domName); String value = getTextContent(domValue); user.addProperty(new SSONameValuePair(name, value)); } // Add user roles !? NodeList rolesLst = ((Element) domUser).getElementsByTagName("roles"); if (rolesLst.getLength() > 1) throw new SSOIdentityException("Only one 'roles' element can be defined for a user"); if (rolesLst.getLength() > 0) { Set<String> roles = new HashSet<String>(); Node domRoles = rolesLst.item(0); String stRoles = getTextContent(domRoles); StringTokenizer st = new StringTokenizer(stRoles != null ? stRoles : "", ","); while (st.hasMoreTokens()) { String roleName = st.nextToken().trim(); BaseRole role = findRoleByName(roleName); roles.add(roleName); logger.debug("User is in role : " + role); } _userRoles.put(username, roles); } return user; } protected CredentialKey createCredentialKey(String name) { // TODO : Use proper key adapter. return new SimpleUserKey(name); } public synchronized Set<String> getRoleKeys() throws SSOIdentityException { return _roles.keySet(); } public synchronized BaseRole loadRole(RoleKey roleKey) throws NoSuchRoleException, SSOIdentityException { BaseRole role = (BaseRole) _roles.get(roleKey); if (role == null) throw new NoSuchRoleException(roleKey); return role; } public synchronized BaseRole findRoleByName(String name) throws SSOIdentityException { Element domRole = _roles.get(name); if (domRole == null) throw new SSOIdentityException("No such role : " + name); return toBaseRole(domRole); } // ----------------------------------------------------------------------------------------- // Private Utils. // ----------------------------------------------------------------------------------------- protected UserKey createUserKey(BaseUser user) { // TODO : Use proper key adapter ... return new SimpleUserKey(user.getName()); } protected BaseRole createRole(String name) { BaseRole role = new BaseRoleImpl(); role.setName(name); return role; } protected RoleKey createRoleKey(BaseRole role) { // TODO : Use proper key adapter ... return new SimpleRoleKey(role.getName()); } // --------------------------------------------------------------------- // Configuration properties // --------------------------------------------------------------------- public void setCredentialsFileName(String credentialsFileName) { logger.debug("Setting crednetials file name to : " + credentialsFileName); _credentialsFileName = credentialsFileName; } public String getCredentialsFileName() { return _credentialsFileName; } public void setUsersFileName(String usersFileName) { logger.debug("Setting users file name to : " + usersFileName); _usersFileName = usersFileName; } // Some utils ... protected String getTextContent(Node node) { try { // Only supported in earlier versions of DOM Method getTextContent = node.getClass().getMethod("getTextContent"); return (String) getTextContent.invoke(node); } catch (NoSuchMethodException e) { logger.debug("Using old DOM Java Api to get Node text content"); } catch (InvocationTargetException e) { logger.warn(e.getMessage(), e); } catch (IllegalAccessException e) { logger.warn(e.getMessage(), e); } // Old DOM API usage to get node's text content NodeList children = node.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node child = children.item(i); if (child.getNodeType() == Node.TEXT_NODE) { return child.getNodeValue(); } } return null; } }