You are viewing a plain text version of this content. The canonical link for it is here.
Posted to xindice-dev@xml.apache.org by gi...@apache.org on 2003/01/03 17:58:31 UTC

cvs commit: xml-xindice/java/scratchpad/src/org/apache/xindice/core/security PassiveCallbackHandler.java UserManager.java UserManagerException.java XindiceLoginModule.java XindiceUserManager.java

gianugo     2003/01/03 08:58:31

  Added:       java/scratchpad/tests/src/org/apache/xindice/core/security
                        PassiveCallbackHandlerTest.java
                        XindiceLoginModuleTest.java
                        XindiceUserManagerTest.java
               java/scratchpad/src/org/apache/xindice/core/security
                        PassiveCallbackHandler.java UserManager.java
                        UserManagerException.java XindiceLoginModule.java
                        XindiceUserManager.java
  Log:
  Committed a first scratch implementation of security in Xindice, using a JAAS oriented approach. WARNING: This code ATM doesn't work yet, it's being committed for public review and personal comfort :-)
  
  Revision  Changes    Path
  1.1                  xml-xindice/java/scratchpad/tests/src/org/apache/xindice/core/security/PassiveCallbackHandlerTest.java
  
  Index: PassiveCallbackHandlerTest.java
  ===================================================================
  package org.apache.xindice.core.security;
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xindice" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999-2001, The dbXML
   * Group, L.L.C., http://www.dbxmlgroup.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  import java.io.IOException;
  import javax.security.auth.callback.Callback;
  import javax.security.auth.callback.LanguageCallback;
  import javax.security.auth.callback.NameCallback;
  import javax.security.auth.callback.PasswordCallback;
  import javax.security.auth.callback.UnsupportedCallbackException;
  
  import junit.framework.TestCase;
  
  
  /**
   * Test case for PassiveAuthenticatorCallback
   *
   * @author <a href="mailto:gianugo@apache.org">Gianugo Rabellino</a>
   */
  public class PassiveCallbackHandlerTest extends TestCase {
      
      PassiveCallbackHandler pch;    
  	
      /**
  	 * @see junit.framework.TestCase#setUp()
  	 */
  	protected void setUp() throws Exception {
  		pch = new PassiveCallbackHandler("testuser", "testpassword");        
  	}
      
      public void testUserAndPassword() throws Exception {
          NameCallback nc = new NameCallback("test");
          PasswordCallback pc = new PasswordCallback("test", true);
          
          pch.handle(new Callback[] { nc, pc});
          
          assertEquals(nc.getName(), "testuser");
          assertEquals(new String(pc.getPassword()), "testpassword");   
      } 
      
      public void testFailWithIncorrectCallbacks() {
          
          NameCallback nc = new NameCallback("test");
          LanguageCallback lc = new LanguageCallback();
                  
          try {
  			pch.handle(new Callback[] { nc, lc});
              fail("An expected UnsupportedCallbackException was not raised");
  		} catch (IOException e) {
              fail("Unexpected IOEXception");
  		} catch (UnsupportedCallbackException e) {
  		}
          
      } 
      
  
  }
  
  
  
  1.1                  xml-xindice/java/scratchpad/tests/src/org/apache/xindice/core/security/XindiceLoginModuleTest.java
  
  Index: XindiceLoginModuleTest.java
  ===================================================================
  package org.apache.xindice.core.security;
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xindice" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999-2001, The dbXML
   * Group, L.L.C., http://www.dbxmlgroup.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  
  import java.util.HashMap;
  import java.util.Map;
  import javax.security.auth.Subject;
  import javax.security.auth.callback.CallbackHandler;
  import javax.security.auth.login.LoginContext;
  import javax.security.auth.login.LoginException;
  import javax.security.auth.spi.LoginModule;
  
  import junit.framework.TestCase;
  
  
  /**
   * Test for the XindiceLoginModule.
   *
   * @author <a href="mailto:gianugo@apache.org">Gianugo Rabellino</a>
   */
  public class XindiceLoginModuleTest extends TestCase {
      
      XindiceLoginModule xlm;
      Subject subject;
      Map sharedState;
      Map options;
      CallbackHandler ch;
      
      
  	/**
       * Setup the LoginModule instance.
       * 
  	 * @see junit.framework.TestCase#setUp()
  	 */
  	protected void setUp() throws Exception {
         xlm = new XindiceLoginModule();
         subject = new Subject();
         ch = new PassiveCallbackHandler("testuser", "testpassword");
         sharedState = new HashMap();
         options = new HashMap();                
  	}
      
  	/**
  	 * @see junit.framework.TestCase#tearDown()
  	 */
  	protected void tearDown() throws Exception {
  		xlm.logout();
  	}
      
      public void testValidLogin() throws LoginException {
          xlm.initialize(subject, ch, sharedState, options);
          assertTrue(xlm.login());                
      }
  
      public void testInvalidLogin() throws LoginException {
          ch = new PassiveCallbackHandler("invaliduser", "testpassword");
          xlm.initialize(subject, ch, sharedState, options);
          assertFalse(xlm.login());                
      }
  
  }
  
  
  
  1.1                  xml-xindice/java/scratchpad/tests/src/org/apache/xindice/core/security/XindiceUserManagerTest.java
  
  Index: XindiceUserManagerTest.java
  ===================================================================
  package org.apache.xindice.core.security;
  
  import javax.xml.parsers.DocumentBuilderFactory;
  import javax.xml.parsers.ParserConfigurationException;
  import junit.framework.TestCase;
  
  import org.apache.xindice.xml.dom.DOMParser;
  import org.w3c.dom.Document;
  import org.w3c.dom.Element;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  
  /**
   * Unit test for XindiceAuthenticator.
   * 
   * @author <a href="mailto:gianugo@apache.org">Gianugo Rabellino</a>
   */
  public class XindiceUserManagerTest extends TestCase {
      
      final String XML = 
           "<?xml version=\"1.0\"?>"
           + "<users>"
           + "<user name=\"test\" password=\"test\">"
           + "<role name=\"root\"/>"
           + "</user>"
           + "<user name=\"readonly\" password=\"readonly\">"
           + "<role name=\"ro\"/>"
           + "</user>"
           + "<user name=\"readwrite\" password=\"readwrite\">"
           + "<role name=\"rw\"/>"
           + "</user>"          
           + "<user name=\"manyroles\" password=\"manyroles\">"
           + "<role name=\"root\"/>"
           + "<role name=\"ro\"/>"
           + "<role name=\"rw\"/>"
           + "<role name=\"1\"/>"
           + "<role name=\"2\"/>"
           + "<role name=\"3\"/>"
           + "<role name=\"4\"/>"
           + "</user>"                 
           + "</users>";
           
      XindiceUserManager usermanager = new XindiceUserManager();     
      
      /**
       * @see junit.framework.TestCase#setUp()
       */
      protected void setUp() throws Exception {
          Document doc = DOMParser.toDocument(XML);
          usermanager.streamFromXML(doc.getDocumentElement());        
      }
      
      public void testAuthentication() {
          try {
              assertTrue(usermanager.checkPassword("test", "test"));
              assertFalse(usermanager.checkPassword("test", "wrong"));
          } catch (Exception e) {
              // Shouldn't happen
              fail("Got an exception:" + e.getMessage());
          }       
      }
      
      public void testAddUser() {
          try {
              usermanager.addUser("added", "added");     
              assertTrue(usermanager.checkPassword("added", "added"));
          } catch (Exception e) {
              // Shouldn't happen
              fail("Got an exception:" + e.getMessage());
          };  
      }
      
      public void testDeleteExistingUser() {
          try {
              usermanager.deleteUser("readwrite");
              assertFalse(usermanager.checkPassword("added","added"));        
          } catch (Exception e) {
              fail("Got an exception while deleting a user: " + e);
          }                
      }
      
      public void testDeleteNonExistingUser() {
          try {
              usermanager.deleteUser("nonexistant");
              fail("Expecting an exception in deleting a non existent user, got none");
          } catch (Exception e) {
              assertEquals(e.getMessage(), UserManagerException.NO_SUCH_USER);
          }
      }
      
              
      public void testDuplicateUser() {
          try {
              usermanager.addUser("test", "test");
              fail("A UserManagerException was expected");
          } catch (UserManagerException e) {
              assertEquals(e.getMessage(), UserManagerException.USER_ALREADY_EXISTS);
          }
      }
      
      public void testChangePassword() {
          try {
              usermanager.changePassword("readonly", "newpassword");
              assertTrue(usermanager.checkPassword("readonly", "newpassword")); 
          } catch (Exception e) {
                  fail("Unexpected exception on password change:" + e.getMessage());
          }      
      }    
  
      public void testChangePasswordNonExistingUser() {
          try {
              usermanager.changePassword("nonexistent", "test");
              fail("A UserManagerException was expected");
          } catch (UserManagerException e) {
              assertEquals(e.getMessage(), UserManagerException.NO_SUCH_USER);
          }
      }
  
      
      public void testStreamToXML() {
          DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
          Document doc = null;
          try {
              doc = factory.newDocumentBuilder().newDocument();
          } catch (ParserConfigurationException e) {
              fail("Unable to create a DOM object" + e.getMessage());
          }
          
          Element element = usermanager.streamToXML(doc);
          
          assertEquals(element.getNodeName(), XindiceUserManager.CONFIGURATION);
          
          NodeList nl = element.getChildNodes();
          
          assertTrue(nl.getLength() > 0);
          
          for (int i = 0; i < nl.getLength(); i++) {
              assertTrue(nl.item(i).getNodeType() == Node.ELEMENT_NODE);
              assertEquals(((Element)nl.item(i)).getNodeName(), 
                  XindiceUserManager.USERTAG);
              assertTrue(((Element)nl.item(i)).hasAttribute(XindiceUserManager.USERNAME));
              assertTrue(((Element)nl.item(i)).hasAttribute(XindiceUserManager.PASSWORD));
              
              NodeList rolesNl = nl.item(i).getChildNodes();
              assertTrue(rolesNl.getLength() > 0);
              
              for (int ii = 0; ii < nl.getLength(); ii++) {
                   assertTrue(rolesNl.item(ii).getNodeType() == Node.ELEMENT_NODE);
                   assertEquals(((Element)rolesNl.item(ii)).getNodeName(), XindiceUserManager.ROLE);
                   assertTrue(((Element)rolesNl.item(ii)).hasAttribute(XindiceUserManager.USERNAME));  
              } 
                  
          }
          
          XindiceUserManager newUserManager = new XindiceUserManager();
          
          newUserManager.streamFromXML(element);
          try {
              assertTrue(newUserManager.checkPassword("test", "test"));
          } catch (Exception e) {
              fail("The serialized UserManager is not the same as the current one ");
          }
                  
      }    
  
      public void testRoles() {
          
          String[] result = (String[])usermanager.getRoles("test");
          assertEquals(1, result.length);
          assertEquals("root", result[0]);
          
      }   
      
      public void testManyRoles() {
          
          String[] result = (String[])usermanager.getRoles("manyroles");
          assertEquals(7, result.length);
          assertEquals("root", result[0]);
          
      }  
  }
  
  
  
  1.1                  xml-xindice/java/scratchpad/src/org/apache/xindice/core/security/PassiveCallbackHandler.java
  
  Index: PassiveCallbackHandler.java
  ===================================================================
  package org.apache.xindice.core.security;
  
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xindice" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999-2001, The dbXML
   * Group, L.L.C., http://www.dbxmlgroup.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  
  import java.io.IOException;
  
  import javax.security.auth.callback.Callback;
  import javax.security.auth.callback.CallbackHandler;
  import javax.security.auth.callback.NameCallback;
  import javax.security.auth.callback.PasswordCallback;
  import javax.security.auth.callback.UnsupportedCallbackException;
  
  
  /**
   * A CallbackHandler that does nothing but store and provide to
   * applications the username/password pairs.
   *
   * @author <a href="mailto:gianugo@apache.org">Gianugo Rabellino</a>
   */
  
  public class PassiveCallbackHandler implements CallbackHandler {
      
      /** The user name */
      protected String user;
      
      /** The user password */
      protected String password;           
      
  	/**
  	 * Constructor for PassiveCallbackHandler, given a user/password pair.
       * 
       * @param user the user name.
       * @param password the password.
  	 */
      
  	public PassiveCallbackHandler(String user, String password) {
  		this.user = user;
          this.password = password;
  	}
  
      /**
       * Process the Callback array, and set the username and password. 
       * 
       * @param callbacks an array of Callbacks: must be exactly one NameCallback and one PasswordCallback
       * @throws UnsupportedCallbackException if one of the Callbacks is not a Name or PasswordCallback
       */
      public void handle(Callback[] callbacks)
                  throws IOException, UnsupportedCallbackException {
          for (int i = 0; i < callbacks.length; i++) {
  		    if (callbacks[i] instanceof NameCallback) {
                  ((NameCallback)callbacks[i]).setName(user);				
              } else if (callbacks[i] instanceof PasswordCallback) {
                  ((PasswordCallback)callbacks[i]).setPassword(password.toCharArray());             
              } else {
                  throw new UnsupportedCallbackException(callbacks[i]);
              }       
  		}           
      }
  }
  
  
  
  1.1                  xml-xindice/java/scratchpad/src/org/apache/xindice/core/security/UserManager.java
  
  Index: UserManager.java
  ===================================================================
  package org.apache.xindice.core.security;
  
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xindice" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999-2001, The dbXML
   * Group, L.L.C., http://www.dbxmlgroup.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   * 
   */
  
  /**
   * A simple interface for managing users, roles and passwords.
   * 
   * @version $Id: UserManager.java,v 1.1 2003/01/03 16:58:31 gianugo Exp $
   * @author <a href="mailto:gianugo@apache.org">Gianugo Rabellino</a>
   */
  
  public interface UserManager {
      
      /**
       * Add a user.
       *
       * @param name The user name.
       * @param password The user password
       * @throws UserManagerException if the user already exists.
       */
      void addUser(String name, String password) throws UserManagerException;
      
      /** 
       * Delete a user.
       * 
       * @param The user name
       * @throws UserManagerException if the user does not exist.
       */    
      void deleteUser(String name) throws UserManagerException;
      
      /**
       * Set a password for a given user.
       * 
       * @param name
       * @param password
       * @throws UserManagerException if the user does not exist.
       */    
      void changePassword(String name, String newPassword) throws UserManagerException;
      
      /**
       * Check if a given password is valid.
       * 
       * @param name the user name
       * @param password the given password
       * 
       */    
      boolean checkPassword(String name, String password);
  
      /**
       * Returns an array of Object representing the roles
       * for a given user or null if the user does not exist.
       * 
       * @return the role array
       */ 
      String[] getRoles(String user);
  }
  
  
  
  1.1                  xml-xindice/java/scratchpad/src/org/apache/xindice/core/security/UserManagerException.java
  
  Index: UserManagerException.java
  ===================================================================
  package org.apache.xindice.core.security;
  
  
  /**
   * A simple exception for user management.
   * 
   * @author <a href="mailto:gianugo@apache.org">Gianugo Rabellino</a>
   */
  public class UserManagerException extends Exception {
  
      public static final String USER_ALREADY_EXISTS = "User already exists";
      public static final String NO_SUCH_USER = "No such user";
  
      String message = "Unknown error";
      
      /**
       * Constructor UserManagerException.
       * @param message the specific error message.
       */
      public UserManagerException(String message) {
          this.message = message;
      }
  
          
      /**
       * 
       * @see java.lang.Throwable#getMessage()
       */
      public String getMessage() {
          return message;
      }
  
  }
  
  
  
  1.1                  xml-xindice/java/scratchpad/src/org/apache/xindice/core/security/XindiceLoginModule.java
  
  Index: XindiceLoginModule.java
  ===================================================================
  package org.apache.xindice.core.security;
  
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xindice" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999-2001, The dbXML
   * Group, L.L.C., http://www.dbxmlgroup.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
   
  
   
  import java.io.IOException;
  import java.util.Map;
  
  import javax.security.auth.Subject;
  import javax.security.auth.callback.Callback;
  import javax.security.auth.callback.CallbackHandler;
  import javax.security.auth.callback.NameCallback;
  import javax.security.auth.callback.PasswordCallback;
  import javax.security.auth.callback.UnsupportedCallbackException;
  import javax.security.auth.login.LoginException;
  import javax.security.auth.spi.LoginModule;
  
  
  /**
   * This class implements a simple JAAS compatible LoginModule that
   * authenticates against the Xindice user base.  Authentication in Xindice is
   * configured using the  system.xml file as follows: 
   * 
   * &lt;security&gt;
   *     &lt;authentication type="JAAS" configuration="jaas.properties"/&gt;
   * &lt;/security&gt; 
   * 
   * the jaas.properties file must reside in the same
   * directory of the system.xml file,  and must follow the standard JAAS
   * conventions. The default configuration uses this LoginModule to perform
   * authentication.
   *
   * @author <a href="mailto:gianugo@apache.org">Gianugo Rabellino</a>
   * @version $Id: XindiceLoginModule.java,v 1.1 2003/01/03 16:58:31 gianugo Exp $
   */
  
  public class XindiceLoginModule implements LoginModule {
      
      protected Subject m_subject;
      protected CallbackHandler m_handler;
      protected Map m_sharedState;
      protected Map m_options;
      
      protected UserManager m_manager;
      
      protected String user;
  
      private boolean success = false;
  
      /**
       * Initialize the module. This is performed by LoginContext.
       *
       * @see javax.security.auth.spi.LoginModule#initialize(Subject,
       *      CallbackHandler, Map, Map)
       */
      public void initialize(Subject subject, CallbackHandler handler,
                             Map sharedState, Map options) {
          this.m_subject = subject;
          this.m_handler = handler;
          this.m_sharedState = sharedState;
          this.m_options = options;
      }
  
      /**
       * Perform the login. This is the "Phase 1", which only verifies
       * the user credential.
       * 
       * @throws LoginException if the authentication process can't be executed.
       */
      public boolean login() throws LoginException {
          // Prepare the environment.
          NameCallback nc = new NameCallback("user");
          PasswordCallback pc = new PasswordCallback("password", true);
          Callback[] callbacks = new Callback[] {nc, pc};        
          
          // Handle the data.        
          try {
  			m_handler.handle(callbacks);
  		} catch (IOException e) {
              // NOTREACHED, hopefully...
              throw new LoginException("A problem occurred while logging in");
  		} catch (UnsupportedCallbackException e) {
              throw new LoginException("A problem occurred while logging in");
  		}
          
          // Get the results.
          user = nc.getName();
          
          String password = new String(pc.getPassword());
          
          if (user.equals("testuser") && password.equals("testpassword")) 
              success = true;
                  
          return success;
      }
  
      /**
       * Commit the authentication (Phase 2). Here will go the authorization
       * specific code
       * 
       * @throws LoginException if an error occurs (not likely ATM)
       */
      public boolean commit() throws LoginException {
          if (success)
              m_subject.getPrincipals().add(user);
          return true;
      }
  
      /**
       * TBD
       * @see javax.security.auth.spi.LoginModule#abort()
       */
      public boolean abort() throws LoginException {
          logout();
          return true;
      }
  
      /**
       * Clear the instance variables.
       * 
       * @see javax.security.auth.spi.LoginModule#logout()
       */
      public boolean logout() throws LoginException {
          m_subject = null;
          m_handler = null;
          m_sharedState = null;
          user = null;
          return true;
      }
  }
  
  
  
  1.1                  xml-xindice/java/scratchpad/src/org/apache/xindice/core/security/XindiceUserManager.java
  
  Index: XindiceUserManager.java
  ===================================================================
  package org.apache.xindice.core.security;
  
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xindice" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999-2001, The dbXML
   * Group, L.L.C., http://www.dbxmlgroup.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   * 
   */
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.Map;
  import java.util.Vector;
  
  import org.apache.xindice.xml.XMLSerializable;
  import org.w3c.dom.DOMException;
  import org.w3c.dom.Document;
  import org.w3c.dom.Element;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  
  
  /**
   * An Xindice based User Manager, using the SysUsers collection. 
   *
   * 
   * @author <a href="mailto:gianugo@apache.org">Gianugo Rabellino</a>
   */
  
  public class XindiceUserManager implements UserManager, XMLSerializable {
  
      static final String CONFIGURATION = "users";
      static final String USERTAG = "user";
      static final String USERNAME = "name";
      static final String PASSWORD = "password";
      static final String ROLE = "role";
      
      protected Map users;
      protected Map roles;
      
      public XindiceUserManager() {
          users = Collections.synchronizedMap(new HashMap());
          roles = Collections.synchronizedMap(new HashMap());
      }    
      
      /**
       * Check if the password is correct. Will return false (for security purposes)
       * even if the user does not exist.
       * 
       * @see org.apache.xindice.core.security.Authenticator#authenticate(java.lang.String, java.lang.String)
       */
      public boolean checkPassword(String user, String password) {
              
              String currentPassword = (String) users.get(user);
              if (currentPassword != null && currentPassword.equals(password))
                  return true;
              
          return false;        
      }
      
      /**
       * Add a user.
       * 
       * @param name The user name.
       * @param password The user password
       * @throws UserManagerException if the user already exists.
       */
      
      public void addUser(String name, String password) throws UserManagerException {
          if (!users.containsKey(name))
              users.put(name,password);
          else 
              throw new UserManagerException(UserManagerException.USER_ALREADY_EXISTS);            
      }
      
      /**
       * Delete a user.
       * 
       * @see org.apache.xindice.core.security.UserManager#deleteUser(java.lang.String)
       */
      public void deleteUser(String name) throws UserManagerException {
          if (users.containsKey(name)) 
              users.remove(name);
          else
              throw new UserManagerException(UserManagerException.NO_SUCH_USER);   
      }
      
      /**
       * Change the password of a given user.
       * 
       * @see org.apache.xindice.core.security.UserManager#changePassword(java.lang.String, java.lang.String)
       */
      public void changePassword(String name, String password) throws UserManagerException {
          if (users.containsKey(name)) {
              users.remove(name);
              users.put(name, password);
          } else {
              throw new UserManagerException(UserManagerException.NO_SUCH_USER);
          }
      }
      
      /**
       * Serialize the instance in XML format.
       * 
       * @see org.apache.xindice.xml.XMLSerializable#streamToXML(org.w3c.dom.Document)
       */
      public Element streamToXML(Document doc) throws DOMException {
          Element root = doc.createElement(CONFIGURATION);
          
          Iterator i = users.keySet().iterator();
          
          while (i.hasNext()) {
              String currentUser = (String) i.next();
              String currentPassword = (String) users.get(currentUser);
              
              Element userElement = doc.createElement(USERTAG);
              
              userElement.setAttribute(USERNAME, currentUser);
              userElement.setAttribute(PASSWORD, currentPassword);
              
              String[] roles = getRoles(currentUser);
              for (int j = 0; j < roles.length; j++) {
                   Element roleElement = doc.createElement(ROLE);
                   roleElement.setAttribute(USERNAME, roles[j]);
                   System.err.println("Added " + roles[j] + " to user " + currentUser);
                   userElement.appendChild(roleElement);    				
  			}
              
              root.appendChild(userElement);
              
              
          }
                          
          
          return root;
          
      }
      
      /**
       * Populate the instance from an XML.
       * TODO: make it more robust, this implementation is *really* rough.
       * 
       * @see org.apache.xindice.xml.XMLSerializable#streamFromXML(org.w3c.dom.Element)
       */
      public void streamFromXML(Element element) throws DOMException {
          NodeList nl = element.getChildNodes();
          
          for (int i = 0; i< nl.getLength(); i++) {
              
              Node node = nl.item(i);
              
              if (node.getNodeType() != Node.ELEMENT_NODE)
                  continue;
              
              Element current = (Element)node;
              
              if (current.getNodeName().equals(USERTAG)) {
                  String name = current.getAttribute(USERNAME);
                  String password = current.getAttribute(PASSWORD);
                  if (!users.containsKey(name)) {
                      users.put(name, password);
                      //Populate the roles
                      NodeList rolesNl = current.getChildNodes();
                      ArrayList roleNames = new ArrayList(5);
                      for (int ii = 0; ii < rolesNl.getLength(); ii++) {
                          Node currentRoleNode = rolesNl.item(ii);
                                                                          
                          if (node.getNodeType() != Node.ELEMENT_NODE)
                              continue;
                          
                          Element currentRole = (Element)currentRoleNode;
                          
                          if (currentRole.getNodeName().equals(ROLE) && currentRole.hasAttribute(USERNAME)) 
                              roleNames.add(currentRole.getAttribute(USERNAME));    
                          else
                              throw new DOMException(DOMException.SYNTAX_ERR, "Unexpected Element: " +
                                  currentRole.getNodeName());                                                    
                      
                      }
                      roles.put(name, roleNames);
                  }                       
              }                    
          }        
      }
      
      /**
       * Returns an array of String representing the roles
       * for a given user or null if the user does not exist.
       * 
       * @return the role array
       */ 
      public String[] getRoles(String user) {
          ArrayList roleList = (ArrayList) roles.get(user);
          
          if (roleList != null)
              return (String[]) roleList.toArray(new String[1]);        
          
          return null;
      }    
  
  }