/* * Copyright 2003-2010 Tufts University Licensed under the * Educational Community License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may * obtain a copy of the License at * * http://www.osedu.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing * permissions and limitations under the License. */ /* * AuthenticationManager.java * * Created on October 23, 2003, 3:22 PM */ package tufts.oki.authentication; import java.util.*; import tufts.oki.OsidManager; import tufts.oki.shared.*; import osid.authentication.*; import javax.naming.*; import javax.naming.directory.*; /** * The AuthenticationManager is responsible for verifying the identity of a person or * entity desiring access to OKI OSID based services. The interface is intended to * isolate applications from the underlying details of verify a user. Multiple * authenication methods may be supported by a single interface, identified by * an AuthenticationType. In this implementation, the following types are defined: * <ul> * <li>AuthenticationLDAP * </ul> * LDAP-based authentication uses JNDI to access enterprise * directory information. Other methods, such as Kerberos are not supported at * this time. * <p> * The AuthenticationManager * maintains a list of known authentication types, which can be accessed using * getAuthenticationTypes(). This list is created and initialized here. * * @author Mark Norton */ public class AuthenticationManager extends OsidManager implements osid.authentication.AuthenticationManager { private Vector am_types = null; private osid.shared.Agent am_agent = null; // Used for LDAP verification. private String am_username = null; private String am_password = null; static public osid.shared.SharedManager sharedMgr = null; // SharedManager is cached. /** * Creates a new instance of AuthenticationManager with owner passed. * * @author Mark Norton */ public AuthenticationManager() { super(); // Create a vector of AuthN types and initialize it with all known types. am_types = new Vector(2); AuthenticationLDAP l_type = new AuthenticationLDAP(); am_types.addElement(l_type); } /** * Creates a new instance of AuthenticationManager with owner passed. * * @author Mark Norton */ public AuthenticationManager(osid.OsidOwner owner) { super(owner); // Create a vector of AuthN types and initialize it with all known types. am_types = new Vector(2); AuthenticationLDAP l_type = new AuthenticationLDAP(); am_types.addElement(l_type); } /** * Initialize the authorization manager. This should be called if the Authorization * Manager is loaded by OsidLoader. It causes the default user to be initialized and * the Shared Manager to be cached locally. * * @author Mark Norton */ public void initManager (osid.shared.SharedManager sharedMgr) { this.sharedMgr = sharedMgr; } /** * Get the Agent of an authenticted user. * * @author Mark Norton * * @return A valid Agent object intialized with any known propertites. * * @deprecated in rc6.1. Replaced by authenticateUser(type). */ public osid.shared.Agent authenticate(osid.shared.Type authType) throws osid.authentication.AuthenticationException { throw new osid.authentication.AuthenticationException("Deprecated"); } /** * AuthenticateUser is the main method used to check the identity of a user. * An authenticationType is passed to indicate which information will be * required to validate this user and which enterprise authentication service * will be used. * <p> * We assume that the AuthenticationManager is initially called * with an empty owner and that it will be updated to the correct owner once authenticated. * This makes sense, because subsequent calls to isAuthenticated() will fail unless the * agent-owner is correctly set. * <p> * In order to speparate out implementations which used different underlying authentication * schemems, authenticateUser() calls aauthenticationViaLDAP(). * <p> * Note that changes introduced in rc6.1 of the osid.authentication.AuthenticationManager cause * an agent object to be created as a side effect. The Id of this object can be gotten by * getUserId(). I have also added getUserAgent(), which is not part of the formal interface. * * @author Mark Norton */ public void authenticateUser(osid.shared.Type authType) throws osid.authentication.AuthenticationException { Agent temp = null; // Check authNType against all known authorization types. if (authType.isEqual (new AuthenticationLDAP())) am_agent = this.authenticateViaLDAP(); else // Type is not known. throw new osid.authentication.AuthenticationException(osid.authentication.AuthenticationException.UNKNOWN_TYPE); } /** * Destroy all authentications of the agent-owner associated with this manager. * * @author Mark Norton */ public void destroyAuthentication() throws osid.authentication.AuthenticationException { // The the owner, which has context. osid.OsidOwner owner = super.getOwner(); // Interate over all known autentication types and remove them from context. osid.shared.TypeIterator it = getAuthenticationTypes(); try { while (it.hasNext() == true) { osid.shared.Type type = it.next(); String key = getAuthNTypeKey(type); try { owner.removeContext(key); } catch (osid.OsidException ex1) { // We could report an error here, but if it doesn't exist, we don't need to destroy. } } } catch (osid.shared.SharedException ex2) { // As far as I know, there are no real exceptions thrown. } // Set the agent to null. am_agent = null; } /** * Destroy the authentication of given type of the agent-owner associated with this manager. * * @author Mark Norton * * @deprecated in rc6.1. Replaced by destroyAuthenticationForType(type); */ public void destroyAuthentication(osid.shared.Type authenticationType) throws osid.authentication.AuthenticationException { throw new osid.authentication.AuthenticationException("Deprecated"); } /** * Destroy the authentication of given type of the agent-owner associated with this manager. * * @author Mark Norton */ public void destroyAuthenticationForType(osid.shared.Type authenticationType) throws osid.authentication.AuthenticationException { // Check to see final this is a valid type and if not, throw exception. if (isValidAuthNType (authenticationType) == false) { throw new osid.authentication.AuthenticationException(osid.authentication.AuthenticationException.UNKNOWN_TYPE); } // Remove the context associated with this type. osid.OsidOwner owner = super.getOwner(); String key = getAuthNTypeKey(authenticationType); try { owner.removeContext(key); } catch (osid.OsidException ex) { // We could report an error here, but if it doesn't exist, we don't need to destroy. } // Set the agent to null. am_agent = null; } /** * Returns the known authentication types as an iterated list. * * @author Mark Norton * * @return TypeIterator which lists each known authentication type. */ public osid.shared.TypeIterator getAuthenticationTypes() { tufts.oki.shared.TypeIterator it = new tufts.oki.shared.TypeIterator (am_types); return it; } /** * Common pratice (such as it is at this stage) indicates that the agent to * be tested for authentication is kept in the agent-owner of the OsidManager. * This method is called to determine if this agent object is still authenticated. * The main reason that this would fail is if there is a time limit associated * with the authentication type given. Once the limit expires, the agent is * no longer valid and must be re-authenticated. * * @author Mark Norton * * @deprecated in rc6.1. Replaced by isUserAuthenticated(type); */ public boolean isAuthenticated(osid.shared.Type authenticationType) throws osid.authentication.AuthenticationException { throw new osid.authentication.AuthenticationException("Deprecated"); } /** * As of rc6.1, authenticateUser() causes an agent to be located as a side * effect. Once found, it can be used as the basis for further authentication. * Some form of session authorizaion might be needed to determine sustained * authentication against some time limit. In this implementation, if the * user has been authenticated once, he or she remains authenticated forever. * <p> * Common pratice (such as it is at this stage) indicates that the agent to * be tested for authentication is kept in the agent-owner of the OsidManager. * This method is called to determine if this agent object is still authenticated. * The main reason that this would fail is if there is a time limit associated * with the authentication type given. Once the limit expires, the agent is * no longer valid and must be re-authenticated. * * @author Mark Norton */ public boolean isUserAuthenticated(osid.shared.Type authenticationType) throws osid.authentication.AuthenticationException { return (am_agent != null); } /** * Get the user Id associated with the authentication type passed. * * @author Mark Norton */ public osid.shared.Id getUserId(osid.shared.Type authenticationType) throws osid.authentication.AuthenticationException { try { return am_agent.getId(); } catch (osid.shared.SharedException ex) { throw new osid.authentication.AuthenticationException("Unknown Id"); } } /** * Get the user Agent associated with the authentication type passed. * <p> * This is an extension to the osid.authentication.AuthenticationManager definition. * * @author Mark Norton */ public osid.shared.Agent getAgent(osid.shared.Type authenticationType) { return am_agent; } /* Private methods for this implementation follow. */ /* ----------------------------------------------- */ /** * Checks to see if is a valid authentication type was passed. * This is a private method in AuthenticationManager and is not part of the * osid.authentication interface definitions. * * @author Mark Norton * * @return True if the type passed is a valid authentication type. */ private boolean isValidAuthNType (osid.shared.Type authNType) { boolean val = false; // Check authNType against all known authorization types. if (authNType.isEqual (new AuthenticationLDAP())) val = true; return val; } /** * Get the authentication keyword associated with this authentication type. * This is a private method in AuthenticationManager and is not part of the * osid.authentication interface definitions. * * @author Mark Norton * * @return The authentication keyword for this authentication type. */ private String getAuthNTypeKey (osid.shared.Type authNType) throws osid.authentication.AuthenticationException { String key; // Check authNType against all known authorization types. if (authNType.isEqual (new AuthenticationLDAP())) key = AuthenticationLDAP.AUTHN_LDAP_KEY; else throw new osid.authentication.AuthenticationException(osid.authentication.AuthenticationException.UNKNOWN_TYPE); return key; } /** * Authenticate using the LDAP method. The current plan is to accept a user name * and look it up in the tufts.edu LDAP registry. If the user has a valid tufts.edu * email address, then he or she is considered a valid user. * <p> * While this will allow students at Tufts to use VUE, a different validation scheme will * be needed for anyone else. Also note that it will be pretty easy to guess at a * user name if you have the email address of anyone at Tufts. * <p> * Authenticate using LDAP. Username and password must be set before calling this * method. Checks for null and blank username and password, then verifies against * ldap.tufts.edu server using JDNI. If the user validates against the LDAP server, * he or she is considered to be a valid Tufts student, faculty, or administrator. * This method replaces the previous version which only validated usernames. * <p> * This is a private method in AuthenticationManager and is not part of the * osid.authentication interface definitions. * * @author Mark Norton * * @return A valid Agent object intialized with any known propertites. */ private osid.shared.Agent authenticateViaLDAP() throws osid.authentication.AuthenticationException { java.util.Properties props = new java.util.Properties(); // Check username and password. Username and password are not allowed to be null. Password may not be blank. // if ((am_username == null) || (am_password == null) || (am_password.trim().length() == 0)) { if ((am_username == null) ) { throw new osid.authentication.AuthenticationException (osid.authentication.AuthenticationException.OPERATION_FAILED); } // Set up the properties needed to establish an LDAP context. props.put (Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); props.put (Context.PROVIDER_URL, "ldaps://ldap.tufts.edu/"); try { /* Create the initial directory context. */ DirContext ctx = new InitialDirContext (props); //System.out.println ("Initial context gained."); /* Specify search constraints to search subtree */ SearchControls constraints = new SearchControls(); constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); /* Search for an entry with a user name given by am_username. List of SearchResult. */ NamingEnumeration results = ctx.search("dc=tufts,dc=edu", "uid="+am_username, constraints); // If the results are null, this is not a valid user. if (!results.hasMore()) { throw new osid.authentication.AuthenticationException (osid.authentication.AuthenticationException.OPERATION_FAILED); } else { //System.out.println ("LDAP search success"); } } catch (NamingException e) { throw new osid.authentication.AuthenticationException (osid.authentication.AuthenticationException.OPERATION_FAILED); } // Create an agent and return it. try { if (sharedMgr != null) am_agent = sharedMgr.createAgent(am_username, new tufts.oki.shared.AgentPersonType()); else am_agent = new tufts.oki.shared.Agent(am_username, new tufts.oki.shared.AgentPersonType()); //System.out.println ("Agent created."); } catch (osid.shared.SharedException e) {} return am_agent; } // This approach is based on an example from java.sun.com. private osid.shared.Agent authenticateViaLDAP3() throws osid.authentication.AuthenticationException { java.util.Properties props = new java.util.Properties(); // Check username and password. Username and password are not allowed to be null. Password may not be blank. if ((am_username == null) || (am_password == null) || (am_password.trim().length() == 0)) { throw new osid.authentication.AuthenticationException (osid.authentication.AuthenticationException.OPERATION_FAILED); } // Set up the properties needed to establish an LDAP context. props.put (Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); props.put (Context.PROVIDER_URL, "ldaps://ldap.tufts.edu/o=Central Administration (Affiliate)"); // Add the username and password. props.put (Context.SECURITY_AUTHENTICATION,"simple"); props.put (Context.SECURITY_PRINCIPAL, "uid="+am_username+", ou=Computer Services, o=Central Administration (Affiliate)"); props.put (Context.SECURITY_CREDENTIALS, am_password); try { /* Create the initial directory context. */ DirContext ctx = new InitialDirContext (props); //System.out.println ("Initial context gained."); /* Specify search constraints to search subtree */ SearchControls constraints = new SearchControls(); constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); /* Search for an entry with a user name given by am_username. List of SearchResult. */ NamingEnumeration results = ctx.search("dc=tufts,dc=edu", "uid="+am_username, constraints); // If the results are null, this is not a valid user. if (!results.hasMore()) { throw new osid.authentication.AuthenticationException (osid.authentication.AuthenticationException.OPERATION_FAILED); } else { //System.out.println ("LDAP search success"); SearchResult sr = (SearchResult) results.next(); //System.out.println ("Search result: "+sr.toString()); // More than one result is an error. if (results.hasMore()) { throw new osid.authentication.AuthenticationException (osid.authentication.AuthenticationException.OPERATION_FAILED); } } } catch (NamingException e) { throw new osid.authentication.AuthenticationException (osid.authentication.AuthenticationException.OPERATION_FAILED); } // Create an agent and return it. try { if (sharedMgr != null) am_agent = sharedMgr.createAgent(am_username, new tufts.oki.shared.AgentPersonType()); else am_agent = new tufts.oki.shared.Agent(am_username, new tufts.oki.shared.AgentPersonType()); //System.out.println ("Agent created."); } catch (osid.shared.SharedException e) {} return am_agent; } /** * Get the username and password of this user. This is likely a call-out to some * GUI component, perhaps a dialog, perhaps a Struts form. In the simplest implementation * this can be gotten from stdin. * <p> * This is a private method in AuthenticationManager and is not part of the * osid.authentication interface definitions. * * @author Mark Norton */ private String[] getUsernamePassword() { String[] nameAndPw = new String[2]; nameAndPw[0] = "mnorton"; // Stubbed entry of user name. nameAndPw[1] = "xyzzy"; // Stubbed entry of password. return nameAndPw; } /** * Set the user name to be used in LDAP authenticaiton. This must be set before * calling authenticateUser(). * <p> * Note that this method is an extension to the original osid.authentication definitions. * * @author Mark Norton */ public void setUsername (String username) { am_username = username; } /** * Set the password to be used in an LDAP authentication. This must be set before * calling authenticate user(). * <p> * Note that this method is an extension to teh original osid.authentication definitions. * * @author Mark Norton */ public void setPassword (String password) { am_password = password; } }