You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by am...@apache.org on 2004/11/16 23:34:08 UTC

svn commit: rev 76044 - in geronimo/trunk/modules: assembly/src/plan jetty/src/java/org/apache/geronimo/jetty jetty/src/test-resources/data jetty/src/test/org/apache/geronimo/jetty security/src/java/org/apache/geronimo/security security/src/java/org/apache/geronimo/security/jaas security/src/java/org/apache/geronimo/security/remoting security/src/java/org/apache/geronimo/security/remoting/jmx security/src/test-data/data security/src/test/org/apache/geronimo/security security/src/test/org/apache/geronimo/security/bridge security/src/test/org/apache/geronimo/security/jaas security/src/test/org/apache/geronimo/security/remoting/jmx

Author: ammulder
Date: Tue Nov 16 14:34:08 2004
New Revision: 76044

Added:
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/DecouplingCallbackHandler.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasClientId.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasLoginCoordinator.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasLoginModuleConfiguration.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasLoginService.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasLoginServiceMBean.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasSecurityContext.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/remoting/jmx/JaasLoginServiceRemotingClient.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/remoting/jmx/JaasLoginServiceRemotingServer.java
   geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/TimeoutTest.java
Removed:
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/LocalLoginModule.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/LoginModuleCacheObject.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/LoginModuleConstants.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/LoginModuleId.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/LoginService.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/LoginServiceMBean.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/RemoteLoginModule.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/RemoteLoginModuleLocalWrapper.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/RemoteLoginModuleRemoteWrapper.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/SerializableACE.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/remoting/RemoteLoginServiceFactory.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/remoting/jmx/LoginServiceStub.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/remoting/jmx/RemoteLoginServiceFactory.java
Modified:
   geronimo/trunk/modules/assembly/src/plan/j2ee-server-plan.xml
   geronimo/trunk/modules/jetty/src/java/org/apache/geronimo/jetty/JAASJettyRealm.java
   geronimo/trunk/modules/jetty/src/test-resources/data/login.config
   geronimo/trunk/modules/jetty/src/test/org/apache/geronimo/jetty/BaseSecurityTest.java
   geronimo/trunk/modules/jetty/src/test/org/apache/geronimo/jetty/SecurityTest.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/ContextManager.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/ConfigurationEntryRealmLocal.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/ConfigurationEntryRealmRemote.java
   geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/GeronimoLoginConfiguration.java
   geronimo/trunk/modules/security/src/test-data/data/login.config
   geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/AbstractTest.java
   geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/bridge/CallerIdentityUserPasswordBridgeTest.java
   geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/bridge/ConfiguredIdentityUserPasswordBridgeTest.java
   geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/bridge/MappingUserPasswordBridgeTest.java
   geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/ConfigurationEntryTest.java
   geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/LoginPropertiesFileTest.java
   geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/LoginSQLTest.java
   geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/LoginSimpleRealmTest.java
   geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/remoting/jmx/RemoteLoginTest.java
Log:
Initial work on security.  More to come some other time.


Modified: geronimo/trunk/modules/assembly/src/plan/j2ee-server-plan.xml
==============================================================================
--- geronimo/trunk/modules/assembly/src/plan/j2ee-server-plan.xml	(original)
+++ geronimo/trunk/modules/assembly/src/plan/j2ee-server-plan.xml	Tue Nov 16 14:34:08 2004
@@ -122,9 +122,9 @@
         <reference name="Realms">geronimo.security:type=SecurityRealm,*</reference>
     </gbean>
 
-    <gbean name="geronimo.security:type=LoginService" class="org.apache.geronimo.security.jaas.LoginService">
+    <gbean name="geronimo.security:type=JaasLoginService" class="org.apache.geronimo.security.jaas.JaasLoginService">
         <reference name="Realms">geronimo.security:type=SecurityRealm,*</reference>
-        <attribute name="reclaimPeriod" type="long">100000</attribute>
+<!--        <attribute name="reclaimPeriod" type="long">100000</attribute>-->
         <attribute name="algorithm" type="java.lang.String">HmacSHA1</attribute>
         <attribute name="password" type="java.lang.String">secret</attribute>
     </gbean>

Modified: geronimo/trunk/modules/jetty/src/java/org/apache/geronimo/jetty/JAASJettyRealm.java
==============================================================================
--- geronimo/trunk/modules/jetty/src/java/org/apache/geronimo/jetty/JAASJettyRealm.java	(original)
+++ geronimo/trunk/modules/jetty/src/java/org/apache/geronimo/jetty/JAASJettyRealm.java	Tue Nov 16 14:34:08 2004
@@ -22,6 +22,7 @@
 import java.util.HashMap;
 import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
+import javax.security.auth.Subject;
 import javax.security.jacc.WebRoleRefPermission;
 
 import org.apache.commons.logging.Log;
@@ -92,12 +93,12 @@
             loginContext.login();
             callbackHandler.clear();
 
-            ContextManager.registerSubject(loginContext.getSubject());
-            ContextManager.setCurrentCaller(loginContext.getSubject());
+            Subject subject = ContextManager.getServerSideSubject(loginContext.getSubject());
+            ContextManager.setCurrentCaller(subject);
 
             //login success
             userPrincipal = new JAASJettyPrincipal(username);
-            userPrincipal.setSubject(loginContext.getSubject());
+            userPrincipal.setSubject(subject);
 
             userMap.put(username, userPrincipal);
 

Modified: geronimo/trunk/modules/jetty/src/test-resources/data/login.config
==============================================================================
--- geronimo/trunk/modules/jetty/src/test-resources/data/login.config	(original)
+++ geronimo/trunk/modules/jetty/src/test-resources/data/login.config	Tue Nov 16 14:34:08 2004
@@ -1,9 +1,5 @@
-
 jaasTest {
-    org.apache.geronimo.security.jaas.LocalLoginModule required
-    debug=true
+    org.apache.geronimo.security.jaas.JaasLoginCoordinator required
     realm="demo-properties-realm"
     kernel="geronimo.kernel";
 };
-
-

Modified: geronimo/trunk/modules/jetty/src/test/org/apache/geronimo/jetty/BaseSecurityTest.java
==============================================================================
--- geronimo/trunk/modules/jetty/src/test/org/apache/geronimo/jetty/BaseSecurityTest.java	(original)
+++ geronimo/trunk/modules/jetty/src/test/org/apache/geronimo/jetty/BaseSecurityTest.java	Tue Nov 16 14:34:08 2004
@@ -110,10 +110,10 @@
         securityServiceGBean.setReferencePatterns("Realms", Collections.singleton(new ObjectName("geronimo.security:type=SecurityRealm,*")));
         securityServiceGBean.setAttribute("policyConfigurationFactory", "org.apache.geronimo.security.jacc.GeronimoPolicyConfigurationFactory");
 
-        loginServiceGBean = new GBeanMBean("org.apache.geronimo.security.jaas.LoginService");
-        loginServiceName = new ObjectName("geronimo.security:type=LoginService");
+        loginServiceGBean = new GBeanMBean("org.apache.geronimo.security.jaas.JaasLoginService");
+        loginServiceName = new ObjectName("geronimo.security:type=JaasLoginService");
         loginServiceGBean.setReferencePatterns("Realms", Collections.singleton(new ObjectName("geronimo.security:type=SecurityRealm,*")));
-        loginServiceGBean.setAttribute("reclaimPeriod", new Long(1000 * 1000));
+//        loginServiceGBean.setAttribute("reclaimPeriod", new Long(1000 * 1000));
         loginServiceGBean.setAttribute("algorithm", "HmacSHA1");
         loginServiceGBean.setAttribute("password", "secret");
 

Modified: geronimo/trunk/modules/jetty/src/test/org/apache/geronimo/jetty/SecurityTest.java
==============================================================================
--- geronimo/trunk/modules/jetty/src/test/org/apache/geronimo/jetty/SecurityTest.java	(original)
+++ geronimo/trunk/modules/jetty/src/test/org/apache/geronimo/jetty/SecurityTest.java	Tue Nov 16 14:34:08 2004
@@ -91,6 +91,7 @@
         connection.setRequestProperty("Cookie", cookie);
         connection.setInstanceFollowRedirects(false);
         BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+        System.out.println("Headers: "+connection.getHeaderFields());
 
         assertEquals(HttpURLConnection.HTTP_OK, connection.getResponseCode());
         assertEquals("Hello World", reader.readLine());

Modified: geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/ContextManager.java
==============================================================================
--- geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/ContextManager.java	(original)
+++ geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/ContextManager.java	Tue Nov 16 14:34:08 2004
@@ -63,6 +63,20 @@
     public static final GeronimoSecurityPermission GET_CONTEXT = new GeronimoSecurityPermission("getContext");
     public static final GeronimoSecurityPermission SET_CONTEXT = new GeronimoSecurityPermission("setContext");
 
+    /**
+     * After a login, the client is left with a relatively empty Subject, while
+     * the Subject used by the server has more important contents.  This method
+     * lets a server-side component acting as an authentication client (such
+     * as Tocmat/Jetty) access the fully populated server-side Subject.
+     */ 
+    public static Subject getServerSideSubject(Subject clientSideSubject) {
+        Set set = clientSideSubject.getPrincipals(IdentificationPrincipal.class);
+        if(set == null || set.size() == 0) {
+            return null;
+        }
+        IdentificationPrincipal idp = (IdentificationPrincipal)set.iterator().next();
+        return getRegisteredSubject(idp.getId());
+    }
 
     public static void setCurrentCallerId(Serializable id) {
         SecurityManager sm = System.getSecurityManager();

Modified: geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/ConfigurationEntryRealmLocal.java
==============================================================================
--- geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/ConfigurationEntryRealmLocal.java	(original)
+++ geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/ConfigurationEntryRealmLocal.java	Tue Nov 16 14:34:08 2004
@@ -58,7 +58,7 @@
     public AppConfigurationEntry[] getAppConfigurationEntry() {
         try {
             return new AppConfigurationEntry[]{
-                new AppConfigurationEntry("org.apache.geronimo.security.jaas.LocalLoginModule",
+                new AppConfigurationEntry("org.apache.geronimo.security.jaas.JaasLoginCoordinator",
                         getControlFlag().getFlag(),
                         getOptions())};
         } catch (Exception e) {

Modified: geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/ConfigurationEntryRealmRemote.java
==============================================================================
--- geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/ConfigurationEntryRealmRemote.java	(original)
+++ geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/ConfigurationEntryRealmRemote.java	Tue Nov 16 14:34:08 2004
@@ -41,24 +41,42 @@
  * @see javax.security.auth.login.Configuration
  */
 public class ConfigurationEntryRealmRemote extends ConfigurationEntryRealmLocal {
-    private String URI;
+    private String realmName;
+    private String host;
+    private int port;
 
     public ConfigurationEntryRealmRemote(Kernel kernel) {
         super(kernel);
     }
 
-    public String getURI() {
-        return URI;
+    public String getHost() {
+        return host;
     }
 
-    public void setURI(String URI) {
-        this.URI = URI;
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public String getRealmName() {
+        return realmName;
+    }
+
+    public void setRealmName(String realmName) {
+        this.realmName = realmName;
     }
 
     public AppConfigurationEntry[] getAppConfigurationEntry() {
         try {
             return new AppConfigurationEntry[]{
-                new AppConfigurationEntry("org.apache.geronimo.security.jaas.LocalLoginModule",
+                new AppConfigurationEntry("org.apache.geronimo.security.jaas.JaasLoginCoordinator",
                         getControlFlag().getFlag(),
                         getOptions())};
         } catch (Exception e) {
@@ -69,7 +87,9 @@
     public void doStart() throws WaitingException, Exception {
         super.doStart();
 
-        options.put("uri", URI);
+        options.put("host", host);
+        options.put("realm", realmName);
+        options.put("port", Integer.toString(port));
     }
 
     public void doStop() throws WaitingException, Exception {
@@ -80,7 +100,9 @@
 
     static {
         GBeanInfoBuilder infoFactory = new GBeanInfoBuilder(ConfigurationEntryRealmRemote.class, ConfigurationEntryRealmLocal.GBEAN_INFO);
-        infoFactory.addAttribute("URI", String.class, true);
+        infoFactory.addAttribute("host", String.class, true);
+        infoFactory.addAttribute("port", int.class, true);
+        infoFactory.addAttribute("realmName", String.class, true);
         GBEAN_INFO = infoFactory.getBeanInfo();
     }
 

Added: geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/DecouplingCallbackHandler.java
==============================================================================
--- (empty file)
+++ geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/DecouplingCallbackHandler.java	Tue Nov 16 14:34:08 2004
@@ -0,0 +1,47 @@
+package org.apache.geronimo.security.jaas;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import java.io.IOException;
+
+/**
+ * This callback handler separates the process of obtaining callbacks from
+ * the user from the process of providing the user's values to the login
+ * module.  This means the JaasLoginService can figure out what callbacks
+ * the module wants and prompt the user in advance, and then turn around
+ * and pass those values to the login module, instead of actually prompting
+ * the user at the mercy of the login module.
+ */
+public class DecouplingCallbackHandler implements CallbackHandler {
+    private Callback[] source;
+    private boolean exploring = true;
+
+    public DecouplingCallbackHandler() {
+    }
+
+    public void handle(Callback[] callbacks)
+            throws IOException, UnsupportedCallbackException {
+        if (exploring) {
+            source = callbacks;
+            throw new UnsupportedCallbackException(callbacks.length > 0 ? callbacks[0] : null, "DO NOT PROCEED WITH THIS LOGIN");
+        } else {
+            if(callbacks.length != source.length) {
+                throw new IOException("Mismatched callbacks");
+            }
+            for (int i = 0; i < callbacks.length; i++) {
+                callbacks[i] = source[i];
+            }
+        }
+    }
+
+    public void setExploring() {
+        exploring = true;
+        source = null;
+    }
+
+    public Callback[] finalizeCallbackList() {
+        exploring = false;
+        return source;
+    }
+}

Modified: geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/GeronimoLoginConfiguration.java
==============================================================================
--- geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/GeronimoLoginConfiguration.java	(original)
+++ geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/GeronimoLoginConfiguration.java	Tue Nov 16 14:34:08 2004
@@ -30,6 +30,12 @@
 
 
 /**
+ * A JAAS configuration mechanism (associating JAAS configuration names with
+ * specific LoginModule configurations).  This is a drop-in replacement for the
+ * normal file-reading JAAS configuration mechanism.  Instead of getting
+ * its configuration from its file, it gets its configuration from other
+ * GBeans running in Geronimo.
+ *
  * @version $Rev$ $Date$
  */
 public class GeronimoLoginConfiguration extends Configuration implements GBeanLifecycle {

Added: geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasClientId.java
==============================================================================
--- (empty file)
+++ geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasClientId.java	Tue Nov 16 14:34:08 2004
@@ -0,0 +1,91 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache 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.apache.org/licenses/LICENSE-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.
+ */
+
+package org.apache.geronimo.security.jaas;
+
+import java.io.Serializable;
+
+
+/**
+ * @version $Rev: 46019 $ $Date: 2004-09-14 05:56:06 -0400 (Tue, 14 Sep 2004) $
+ */
+public class JaasClientId implements Serializable {
+    private final long clientId;
+    private final byte[] hash;
+    private transient int hashCode;
+    private transient String name;
+
+    public JaasClientId(long clientId, byte[] hash) {
+        this.clientId = clientId;
+        this.hash = hash;
+    }
+
+    public long getClientId() {
+        return clientId;
+    }
+
+    public byte[] getHash() {
+        return hash;
+    }
+
+    public boolean equals(Object obj) {
+        if (!(obj instanceof JaasClientId)) return false;
+
+        JaasClientId another = (JaasClientId) obj;
+        if (another.clientId != clientId) return false;
+        for (int i = 0; i < hash.length; i++) {
+            if (another.hash[i] != hash[i]) return false;
+        }
+        return true;
+    }
+
+    public String toString() {
+        if (name == null) {
+            StringBuffer buffer = new StringBuffer();
+            buffer.append('[');
+            buffer.append(clientId);
+            buffer.append(":0x");
+            for (int i = 0; i < hash.length; i++) {
+                buffer.append(HEXCHAR[(hash[i]>>>4)&0x0F]);
+                buffer.append(HEXCHAR[(hash[i]    )&0x0F]);
+            }
+            buffer.append(']');
+            name = buffer.toString();
+        }
+        return name;
+    }
+
+    /**
+     * Returns a hashcode for this LoginModuleId.
+     *
+     * @return a hashcode for this LoginModuleId.
+     */
+    public int hashCode() {
+        if (hashCode == 0) {
+            for (int i = 0; i < hash.length; i++) {
+                hashCode ^= hash[i];
+            }
+            hashCode ^= (int)(clientId ^ (clientId >>> 32));
+        }
+        return hashCode;
+    }
+
+    private static final char[] HEXCHAR = {
+        '0', '1', '2', '3', '4', '5', '6', '7',
+        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+    };
+}

Added: geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasLoginCoordinator.java
==============================================================================
--- (empty file)
+++ geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasLoginCoordinator.java	Tue Nov 16 14:34:08 2004
@@ -0,0 +1,199 @@
+package org.apache.geronimo.security.jaas;
+
+import org.apache.geronimo.security.remoting.jmx.JaasLoginServiceRemotingClient;
+import org.apache.geronimo.kernel.Kernel;
+import org.apache.geronimo.kernel.jmx.MBeanProxyFactory;
+
+import javax.security.auth.spi.LoginModule;
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.Callback;
+import java.util.*;
+import java.security.Principal;
+
+/**
+ * A LoginModule implementation which connects to a Geronimo server under
+ * the covers, and uses Geronimo realms to resolve the login.  It handles a
+ * mix of client-side and server-side login modules.  It treats any client
+ * side module as something it should manage and execute, while a server side
+ * login module would be managed and executed by the Geronimo server.
+ *
+ * Note that this can actually be run from within a Geronimo server, in which
+ * case the client/server distinction is somewhat less important, and the
+ * communication is optimized by avoiding network traffic.
+ *
+ * @version $Revision: 1.0$
+ */
+public class JaasLoginCoordinator implements LoginModule {
+    public final static String OPTION_HOST = "host";
+    public final static String OPTION_PORT = "port";
+    public final static String OPTION_KERNEL = "kernel";
+    public final static String OPTION_REALM = "realm";
+    private String serverHost;
+    private int serverPort;
+    private String realmName;
+    private String kernelName;
+    private JaasLoginServiceMBean service;
+    private CallbackHandler handler;
+    private Subject subject;
+    private Set processedPrincipals = new HashSet();
+    private JaasLoginModuleConfiguration[] config;
+    private JaasClientId client;
+    LoginModuleConfiguration[] workers;
+
+    public void initialize(Subject subject, CallbackHandler callbackHandler,
+                           Map sharedState, Map options) {
+        serverHost = (String) options.get(OPTION_HOST);
+        Object port = options.get(OPTION_PORT);
+        if(port != null) {
+            serverPort = Integer.parseInt((String)port);
+        }
+        realmName = (String) options.get(OPTION_REALM);
+        kernelName = (String) options.get(OPTION_KERNEL);
+        service = connect();
+        handler = callbackHandler;
+        this.subject = subject;
+    }
+
+    public boolean login() throws LoginException {
+        client = service.connectToRealm(realmName);
+        config = service.getLoginConfiguration(client);
+        workers = new LoginModuleConfiguration[config.length];
+        for (int i = 0; i < workers.length; i++) {
+            LoginModule wrapper;
+            if(config[i].isServerSide()) {
+                wrapper = new ServerLoginModule(i);
+            } else {
+                LoginModule source = config[i].getLoginModule(JaasLoginCoordinator.class.getClassLoader());
+                wrapper = new ClientLoginModule(source, i);
+            }
+            workers[i] = new LoginModuleConfiguration(wrapper, config[i].getFlag());
+            workers[i].getModule().initialize(subject, handler, new HashMap(), config[i].getOptions());
+        }
+        return LoginUtils.computeLogin(workers);
+    }
+
+    public boolean commit() throws LoginException {
+        for (int i = 0; i < workers.length; i++) {
+            workers[i].getModule().commit();
+        }
+        subject.getPrincipals().add(service.loginSucceeded(client));
+        return true;
+    }
+
+    public boolean abort() throws LoginException {
+        try {
+            for (int i = 0; i < workers.length; i++) {
+                workers[i].getModule().abort();
+            }
+        } finally {
+            service.loginFailed(client);
+        }
+        return true;
+    }
+
+    public boolean logout() throws LoginException {
+        try {
+            for (int i = 0; i < workers.length; i++) {
+                workers[i].getModule().logout();
+            }
+        } finally {
+            service.logout(client);
+        }
+        return true;
+    }
+
+    private JaasLoginServiceMBean connect() {
+        if(serverHost != null && serverPort > 0) {
+            return JaasLoginServiceRemotingClient.create(serverHost, serverPort);
+        } else {
+            return (JaasLoginServiceMBean) MBeanProxyFactory.getProxy(JaasLoginServiceMBean.class, Kernel.getKernel(kernelName).getMBeanServer(), JaasLoginService.OBJECT_NAME);
+        }
+    }
+
+    private class ClientLoginModule implements LoginModule {
+        private LoginModule source;
+        int index;
+
+        public ClientLoginModule(LoginModule source, int index) {
+            this.source = source;
+            this.index = index;
+        }
+
+        public void initialize(Subject subject, CallbackHandler callbackHandler,
+                               Map sharedState, Map options) {
+            source.initialize(subject, callbackHandler, sharedState, options);
+        }
+
+        public boolean login() throws LoginException {
+           return source.login();
+        }
+
+        public boolean commit() throws LoginException {
+            boolean result = source.commit();
+            List list = new ArrayList();
+            for (Iterator it = subject.getPrincipals().iterator(); it.hasNext();) {
+                Principal p = (Principal) it.next();
+                if(!processedPrincipals.contains(p)) {
+                    list.add(p);
+                    processedPrincipals.add(p);
+                }
+            }
+            service.clientLoginModuleCommit(client, index, (Principal[]) list.toArray(new Principal[list.size()]));
+            return result;
+        }
+
+        public boolean abort() throws LoginException {
+            return source.abort();
+        }
+
+        public boolean logout() throws LoginException {
+            return source.logout();
+        }
+    }
+
+    private class ServerLoginModule implements LoginModule {
+        int index;
+        CallbackHandler handler;
+        Callback[] callbacks;
+
+        public ServerLoginModule(int index) {
+            this.index = index;
+        }
+
+        public void initialize(Subject subject, CallbackHandler handler,
+                               Map sharedState, Map options) {
+            this.handler = handler;
+            try {
+                callbacks = service.getServerLoginCallbacks(client, index);
+            } catch (LoginException e) {
+                throw new RuntimeException("Server unable to initialize login module", e);
+            }
+        }
+
+        public boolean login() throws LoginException {
+            try {
+                handler.handle(callbacks);
+                return service.performServerLogin(client, index, callbacks);
+            } catch (LoginException e) {
+                throw e;
+            } catch (Exception e) {
+                e.printStackTrace();
+                throw new LoginException("Unable to log in: "+e.getMessage());
+            }
+        }
+
+        public boolean commit() throws LoginException {
+            return service.serverLoginModuleCommit(client, index);
+        }
+
+        public boolean abort() throws LoginException {
+            return false; // taken care of with a single call to the server
+        }
+
+        public boolean logout() throws LoginException {
+            return false; // taken care of with a single call to the server
+        }
+    }
+}

Added: geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasLoginModuleConfiguration.java
==============================================================================
--- (empty file)
+++ geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasLoginModuleConfiguration.java	Tue Nov 16 14:34:08 2004
@@ -0,0 +1,52 @@
+package org.apache.geronimo.security.jaas;
+
+import org.apache.geronimo.common.GeronimoSecurityException;
+
+import javax.security.auth.spi.LoginModule;
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * Describes the configuration of a LoginModule -- its name, class, control
+ * flag, options, and the Geronimo extension for whether it should run on
+ * the client side or server side.
+ *
+ * @version $Revision: 1.0$
+ */
+public class JaasLoginModuleConfiguration implements Serializable {
+    private boolean serverSide;
+    private LoginModuleControlFlag flag;
+    private String loginModuleName;
+    private Map options;
+    private transient LoginModule loginModule;
+
+    public JaasLoginModuleConfiguration(String loginModuleName, LoginModuleControlFlag flag, Map options, boolean serverSide) {
+        this.serverSide = serverSide;
+        this.flag = flag;
+        this.loginModuleName = loginModuleName;
+        this.options = options;
+    }
+
+    public LoginModule getLoginModule(ClassLoader loader) throws GeronimoSecurityException {
+        if(loginModule == null) {
+            try {
+                loginModule = (LoginModule) loader.loadClass(loginModuleName).newInstance();
+            } catch (Exception e) {
+                throw new GeronimoSecurityException("Unable to instantiate login module", e);
+            }
+        }
+        return loginModule;
+    }
+
+    public boolean isServerSide() {
+        return serverSide;
+    }
+
+    public LoginModuleControlFlag getFlag() {
+        return flag;
+    }
+
+    public Map getOptions() {
+        return options;
+    }
+}

Added: geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasLoginService.java
==============================================================================
--- (empty file)
+++ geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasLoginService.java	Tue Nov 16 14:34:08 2004
@@ -0,0 +1,394 @@
+package org.apache.geronimo.security.jaas;
+
+import org.apache.geronimo.gbean.*;
+import org.apache.geronimo.security.realm.SecurityRealm;
+import org.apache.geronimo.security.SubjectId;
+import org.apache.geronimo.security.ContextManager;
+import org.apache.geronimo.security.IdentificationPrincipal;
+import org.apache.geronimo.common.GeronimoSecurityException;
+import org.apache.geronimo.kernel.jmx.JMXUtil;
+
+import javax.security.auth.callback.*;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.spi.LoginModule;
+import javax.security.auth.Subject;
+import javax.crypto.SecretKey;
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import javax.management.ObjectName;
+import java.security.Principal;
+import java.security.NoSuchAlgorithmException;
+import java.security.InvalidKeyException;
+import java.util.*;
+
+import EDU.oswego.cs.dl.util.concurrent.ClockDaemon;
+import EDU.oswego.cs.dl.util.concurrent.ThreadFactory;
+
+/**
+ * The single point of contact for Geronimo JAAS realms.  Instead of attempting
+ * to interact with JAAS realms directly, a client should either interact with
+ * this service, or use a LoginModule implementation that interacts with this
+ * service.
+ *
+ * @version $Revision: 1.0$
+ */
+public class JaasLoginService implements GBeanLifecycle, JaasLoginServiceMBean {
+    public static final ObjectName OBJECT_NAME = JMXUtil.getObjectName("geronimo.security:type=JaasLoginService");
+    private final static int DEFAULT_EXPIRED_LOGIN_SCAN_INTERVAL = 300000; // 5 mins
+    private final static int DEFAULT_MAX_LOGIN_DURATION =  1000 * 3600 * 24; // 1 day
+    private final static ClockDaemon clockDaemon;
+    private static long nextLoginModuleId = System.currentTimeMillis();
+    private ReferenceCollection realms;
+    private Object expiredLoginScanIdentifier;
+    private final SecretKey key;
+    private final String algorithm;
+    private final ClassLoader classLoader;
+    private final Map activeLogins = new Hashtable();
+    private int expiredLoginScanIntervalMillis = DEFAULT_EXPIRED_LOGIN_SCAN_INTERVAL;
+    private int maxLoginDurationMillis = DEFAULT_MAX_LOGIN_DURATION;
+
+    public JaasLoginService(String algorithm, String password, ClassLoader classLoader) {
+        this.classLoader = classLoader;
+        this.algorithm = algorithm;
+        //todo: password could just be randomly generated??
+        key = new SecretKeySpec(password.getBytes(), algorithm);
+    }
+
+    /**
+     * GBean property
+     */
+    public Collection getRealms() throws GeronimoSecurityException {
+        return realms;
+    }
+
+    /**
+     * GBean property
+     */
+    public void setRealms(Collection realms) {
+        this.realms = (ReferenceCollection) realms;
+        //todo: add listener to drop logins when realm is removed
+    }
+
+    /**
+     * GBean property
+     */
+    public int getMaxLoginDurationMillis() {
+        return maxLoginDurationMillis;
+    }
+
+    /**
+     * GBean property
+     */
+    public void setMaxLoginDurationMillis(int maxLoginDurationMillis) {
+        if(maxLoginDurationMillis == 0) {
+            maxLoginDurationMillis = DEFAULT_MAX_LOGIN_DURATION;
+        }
+        this.maxLoginDurationMillis = maxLoginDurationMillis;
+    }
+
+    /**
+     * GBean property
+     */
+    public int getExpiredLoginScanIntervalMillis() {
+        return expiredLoginScanIntervalMillis;
+    }
+
+    /**
+     * GBean property
+     */
+    public void setExpiredLoginScanIntervalMillis(int expiredLoginScanIntervalMillis) {
+        if(expiredLoginScanIntervalMillis == 0) {
+            expiredLoginScanIntervalMillis = DEFAULT_EXPIRED_LOGIN_SCAN_INTERVAL;
+        }
+        this.expiredLoginScanIntervalMillis = expiredLoginScanIntervalMillis;
+    }
+
+    public void doStart() throws WaitingException, Exception {
+        expiredLoginScanIdentifier = clockDaemon.executePeriodically(expiredLoginScanIntervalMillis, new ExpirationMonitor(), true);
+    }
+
+    public void doStop() throws WaitingException, Exception {
+        ClockDaemon.cancel(expiredLoginScanIdentifier);
+        //todo: shut down all logins
+    }
+
+    public void doFail() {
+        //todo: shut down all logins
+    }
+
+    /**
+     * Starts a new authentication process on behalf of an end user.  The
+     * returned ID will identify that user throughout the user's interaction
+     * with the server.  On the server side, that means maintaining the
+     * Subject and Principals for the user.
+     *
+     * @return The UserIdentifier used as an argument for the rest of the
+     *         methods in this class.
+     */
+    public JaasClientId connectToRealm(String realmName) {
+        for (Iterator it = realms.iterator(); it.hasNext();) {
+            SecurityRealm realm = (SecurityRealm) it.next();
+            if(realm.getRealmName().equals(realmName)) {
+                return initializeClient(realm);
+            }
+        }
+        throw new GeronimoSecurityException("No such realm ("+realmName+")");
+    }
+
+    /**
+     * Gets the login module configuration for the specified realm.  The
+     * caller needs that in order to perform the authentication process.
+     */
+    public JaasLoginModuleConfiguration[] getLoginConfiguration(JaasClientId userIdentifier) throws LoginException {
+        JaasSecurityContext context = (JaasSecurityContext) activeLogins.get(userIdentifier);
+        if(context == null) {
+            throw new ExpiredLoginModuleException();
+        }
+        return context.getModules();
+    }
+
+    /**
+     * Retrieves callbacks for a server side login module.  When the client
+     * is going through the configured login modules, if a specific login
+     * module is client-side, it will be handled directly.  If it is
+     * server-side, the client gets the callbacks (using this method),
+     * populates them, and sends them back to the server.
+     */
+    public Callback[] getServerLoginCallbacks(JaasClientId userIdentifier, int loginModuleIndex) throws LoginException {
+        JaasSecurityContext context = (JaasSecurityContext) activeLogins.get(userIdentifier);
+        if(context == null) {
+            throw new ExpiredLoginModuleException();
+        }
+        if(loginModuleIndex < 0 || loginModuleIndex >= context.getModules().length || !context.getModules()[loginModuleIndex].isServerSide()) {
+            throw new LoginException("Invalid login module specified");
+        }
+        JaasLoginModuleConfiguration config = context.getModules()[loginModuleIndex];
+        LoginModule module = config.getLoginModule(classLoader);
+        //todo: properly handle shared state
+        module.initialize(context.getSubject(), context.getHandler(), new HashMap(), config.getOptions());
+        try {
+            module.login();
+        } catch (LoginException e) {}
+        try {
+            module.abort();
+        } catch(LoginException e) {}
+        return context.getHandler().finalizeCallbackList();
+    }
+
+    /**
+     * Returns populated callbacks for a server side login module.  When the
+     * client is going through the configured login modules, if a specific
+     * login module is client-side, it will be handled directly.  If it is
+     * server-side, the client gets the callbacks, populates them, and sends
+     * them back to the server (using this method).
+     */
+    public boolean performServerLogin(JaasClientId userIdentifier, int loginModuleIndex, Callback[] results) throws LoginException {
+        JaasSecurityContext context = (JaasSecurityContext) activeLogins.get(userIdentifier);
+        if(context == null) {
+            throw new ExpiredLoginModuleException();
+        }
+        if(loginModuleIndex < 0 || loginModuleIndex >= context.getModules().length || !context.getModules()[loginModuleIndex].isServerSide()) {
+            throw new LoginException("Invalid login module specified");
+        }
+        JaasLoginModuleConfiguration module = context.getModules()[loginModuleIndex];
+        return module.getLoginModule(classLoader).login();
+    }
+
+    /**
+     * Indicates that the overall login succeeded, and some principals were
+     * generated by a client-side login module.  This method needs to be called
+     * once for each client-side login module, to specify Principals for each
+     * module.
+     */
+    public void clientLoginModuleCommit(JaasClientId userIdentifier, int loginModuleIndex, Principal[] clientLoginModulePrincipals) throws LoginException {
+        JaasSecurityContext context = (JaasSecurityContext) activeLogins.get(userIdentifier);
+        if(context == null) {
+            throw new ExpiredLoginModuleException();
+        }
+        if(loginModuleIndex < 0 || loginModuleIndex >= context.getModules().length || context.getModules()[loginModuleIndex].isServerSide()) {
+            throw new LoginException("Invalid login module specified");
+        }
+        context.processPrincipals(clientLoginModulePrincipals);
+    }
+
+    /**
+     * Indicates that the overall login succeeded, and a particular server-side
+     * login module should be committed.  This method needs to be called
+     * once for each server-side login module that was processed before the
+     * overall authentication succeeded.
+     */
+    public boolean serverLoginModuleCommit(JaasClientId userIdentifier, int loginModuleIndex) throws LoginException {
+        JaasSecurityContext context = (JaasSecurityContext) activeLogins.get(userIdentifier);
+        if(context == null) {
+            throw new ExpiredLoginModuleException();
+        }
+        if(loginModuleIndex < 0 || loginModuleIndex >= context.getModules().length || !context.getModules()[loginModuleIndex].isServerSide()) {
+            throw new LoginException("Invalid login module specified");
+        }
+        JaasLoginModuleConfiguration module = context.getModules()[loginModuleIndex];
+        boolean result = module.getLoginModule(classLoader).commit();
+        context.processPrincipals();
+        return result;
+    }
+
+    /**
+     * Indicates that the overall login succeeded.  All login modules that were
+     * touched should have been logged in and committed before calling this.
+     */
+    public IdentificationPrincipal loginSucceeded(JaasClientId userIdentifier) throws LoginException {
+        JaasSecurityContext context = (JaasSecurityContext) activeLogins.get(userIdentifier);
+        if(context == null) {
+            throw new ExpiredLoginModuleException();
+        }
+
+        Subject subject = context.getSubject();
+        ContextManager.registerSubject(subject);
+        SubjectId id = ContextManager.getSubjectId(subject);
+        IdentificationPrincipal principal = new IdentificationPrincipal(id);
+        subject.getPrincipals().add(principal);
+        return principal;
+    }
+
+    /**
+     * Indicates that the overall login failed, and the server should release
+     * any resources associated with the user ID.
+     */
+    public void loginFailed(JaasClientId userIdentifier) {
+        activeLogins.remove(userIdentifier);
+    }
+
+    /**
+     * Indicates that the client has logged out, and the server should release
+     * any resources associated with the user ID.
+     */
+    public void logout(JaasClientId userIdentifier) throws LoginException {
+        JaasSecurityContext context = (JaasSecurityContext) activeLogins.get(userIdentifier);
+        if(context == null) {
+            throw new ExpiredLoginModuleException();
+        }
+        ContextManager.unregisterSubject(context.getSubject());
+        activeLogins.remove(userIdentifier);
+    }
+
+    /**
+     * Prepares a new security context for a new client.  Each client uses a
+     * unique security context to sture their authentication progress,
+     * principals, etc.
+     *
+     * @param realm The realm the client is authenticating to
+     */
+    private JaasClientId initializeClient(SecurityRealm realm) {
+        long id;
+        synchronized(JaasLoginService.class) {
+            id = ++nextLoginModuleId;
+        }
+        JaasClientId clientId = new JaasClientId(id, hash(id));
+        AppConfigurationEntry[] entries = realm.getAppConfigurationEntries();
+        JaasLoginModuleConfiguration[] modules = new JaasLoginModuleConfiguration[entries.length];
+        for (int i = 0; i < modules.length; i++) {
+            modules[i] = new JaasLoginModuleConfiguration(entries[i].getLoginModuleName(),
+                    LoginModuleControlFlag.getInstance(entries[i].getControlFlag()), entries[i].getOptions(),
+                    realm.isLoginModuleLocal()); //todo: calculate local-ness per module
+        }
+        JaasSecurityContext context = new JaasSecurityContext(realm.getRealmName(), modules);
+        activeLogins.put(clientId, context);
+        return clientId;
+    }
+
+    /**
+     * Hashes a unique ID.  The client keeps an object around with the ID and
+     * the hash of the ID.  That way it's not so easy to forge an ID and steal
+     * someone else's account.
+     */
+    private byte[] hash(long id) {
+        byte[] bytes = new byte[8];
+        for (int i = 7; i >= 0; i--) {
+            bytes[i] = (byte) (id);
+            id >>>= 8;
+        }
+
+        try {
+            Mac mac = Mac.getInstance(algorithm);
+            mac.init(key);
+            mac.update(bytes);
+
+            return mac.doFinal();
+        } catch (NoSuchAlgorithmException e) {
+        } catch (InvalidKeyException e) {
+        }
+        assert false : "Should never have reached here";
+        return null;
+    }
+
+
+
+    // This stuff takes care of whacking old logins
+    static {
+        clockDaemon = new ClockDaemon();
+        clockDaemon.setThreadFactory(new ThreadFactory() {
+            public Thread newThread(Runnable r) {
+                Thread t = new Thread(r, "LoginService login modules monitor");
+                t.setDaemon(true);
+                return t;
+            }
+        });
+    }
+    private class ExpirationMonitor implements Runnable { //todo: different timeouts per realm?
+        public void run() {
+            long now = System.currentTimeMillis();
+            List list = new LinkedList();
+            synchronized(activeLogins) {
+                for (Iterator it = activeLogins.keySet().iterator(); it.hasNext();) {
+                    JaasClientId id = (JaasClientId) it.next();
+                    JaasSecurityContext context = (JaasSecurityContext) activeLogins.get(id);
+                    int age = (int)(now-context.getCreated());
+                    if(context.isDone() || age > maxLoginDurationMillis) {
+                        list.add(context);
+                        context.setDone(true);
+                        it.remove();
+                    }
+                }
+            }
+            for (Iterator it = list.iterator(); it.hasNext();) {
+                JaasSecurityContext context = (JaasSecurityContext) it.next();
+                ContextManager.unregisterSubject(context.getSubject());
+            }
+        }
+    }
+
+
+
+    // This stuff takes care of making this object into a GBean
+    public static final GBeanInfo GBEAN_INFO;
+
+    static {
+        GBeanInfoBuilder infoFactory = new GBeanInfoBuilder(JaasLoginService.class);
+
+        infoFactory.addAttribute("algorithm", String.class, true);
+        infoFactory.addAttribute("password", String.class, true);
+        infoFactory.addAttribute("classLoader", ClassLoader.class, false);
+        infoFactory.addAttribute("maxLoginDurationMillis", int.class, true);
+        infoFactory.addAttribute("expiredLoginScanIntervalMillis", int.class, true);
+
+        infoFactory.addOperation("connectToRealm", new Class[]{String.class});
+        infoFactory.addOperation("getLoginConfiguration", new Class[]{JaasClientId.class});
+        infoFactory.addOperation("getServerLoginCallbacks", new Class[]{JaasClientId.class, int.class});
+        infoFactory.addOperation("performServerLogin", new Class[]{JaasClientId.class, int.class, Callback[].class});
+        infoFactory.addOperation("clientLoginModuleCommit", new Class[]{JaasClientId.class, int.class, Principal[].class});
+        infoFactory.addOperation("serverLoginModuleCommit", new Class[]{JaasClientId.class, int.class});
+        infoFactory.addOperation("loginSucceeded", new Class[]{JaasClientId.class});
+        infoFactory.addOperation("loginFailed", new Class[]{JaasClientId.class});
+        infoFactory.addOperation("logout", new Class[]{JaasClientId.class});
+
+        infoFactory.addReference("Realms", SecurityRealm.class);
+
+        infoFactory.setConstructor(new String[] {"algorithm", "password", "classLoader"});
+
+        GBEAN_INFO = infoFactory.getBeanInfo();
+    }
+
+    public static GBeanInfo getGBeanInfo() {
+        return GBEAN_INFO;
+    }
+}

Added: geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasLoginServiceMBean.java
==============================================================================
--- (empty file)
+++ geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasLoginServiceMBean.java	Tue Nov 16 14:34:08 2004
@@ -0,0 +1,115 @@
+package org.apache.geronimo.security.jaas;
+
+import org.apache.geronimo.common.GeronimoSecurityException;
+import org.apache.geronimo.security.IdentificationPrincipal;
+
+import javax.security.auth.login.LoginException;
+import javax.security.auth.callback.Callback;
+import java.security.Principal;
+import java.util.Collection;
+
+/**
+ * 
+ *
+ * @version $Revision: 1.0$
+ */
+public interface JaasLoginServiceMBean {
+    /**
+     * GBean property
+     */
+    public Collection getRealms() throws GeronimoSecurityException;
+
+    /**
+     * GBean property
+     */
+    public void setRealms(Collection realms);
+
+    /**
+     * GBean property
+     */
+    public int getMaxLoginDurationMillis();
+
+    /**
+     * GBean property
+     */
+    public void setMaxLoginDurationMillis(int maxLoginDurationMillis);
+
+    /**
+     * GBean property
+     */
+    public int getExpiredLoginScanIntervalMillis();
+
+    /**
+     * GBean property
+     */
+    public void setExpiredLoginScanIntervalMillis(int expiredLoginScanIntervalMillis);
+
+    /**
+     * Starts a new authentication process on behalf of an end user.  The
+     * returned ID will identify that user throughout the user's interaction
+     * with the server.  On the server side, that means maintaining the
+     * Subject and Principals for the user.
+     *
+     * @return The UserIdentifier used as an argument for the rest of the
+     *         methods in this class.
+     */
+    public JaasClientId connectToRealm(String realmName);
+
+    /**
+     * Gets the login module configuration for the specified realm.  The
+     * caller needs that in order to perform the authentication process.
+     */
+    public JaasLoginModuleConfiguration[] getLoginConfiguration(JaasClientId userIdentifier) throws LoginException ;
+
+    /**
+     * Retrieves callbacks for a server side login module.  When the client
+     * is going through the configured login modules, if a specific login
+     * module is client-side, it will be handled directly.  If it is
+     * server-side, the client gets the callbacks (using this method),
+     * populates them, and sends them back to the server.
+     */
+    public Callback[] getServerLoginCallbacks(JaasClientId userIdentifier, int loginModuleIndex) throws LoginException;
+
+    /**
+     * Returns populated callbacks for a server side login module.  When the
+     * client is going through the configured login modules, if a specific
+     * login module is client-side, it will be handled directly.  If it is
+     * server-side, the client gets the callbacks, populates them, and sends
+     * them back to the server (using this method).
+     */
+    public boolean performServerLogin(JaasClientId userIdentifier, int loginModuleIndex, Callback[] results) throws LoginException;
+
+    /**
+     * Indicates that the overall login succeeded, and some principals were
+     * generated by a client-side login module.  This method needs to be called
+     * once for each client-side login module, to specify Principals for each
+     * module.
+     */
+    public void clientLoginModuleCommit(JaasClientId userIdentifier, int loginModuleIndex, Principal[] clientLoginModulePrincipals) throws LoginException;
+
+    /**
+     * Indicates that the overall login succeeded, and a particular server-side
+     * login module should be committed.  This method needs to be called
+     * once for each server-side login module that was processed before the
+     * overall authentication succeeded.
+     */
+    public boolean serverLoginModuleCommit(JaasClientId userIdentifier, int loginModuleIndex) throws LoginException;
+
+    /**
+     * Indicates that the overall login succeeded.  All login modules that were
+     * touched should have been logged in and committed before calling this.
+     */
+    public IdentificationPrincipal loginSucceeded(JaasClientId userIdentifier) throws LoginException;
+
+    /**
+     * Indicates that the overall login failed, and the server should release
+     * any resources associated with the user ID.
+     */
+    public void loginFailed(JaasClientId userIdentifier);
+
+    /**
+     * Indicates that the client has logged out, and the server should release
+     * any resources associated with the user ID.
+     */
+    public void logout(JaasClientId userIdentifier) throws LoginException;
+}

Added: geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasSecurityContext.java
==============================================================================
--- (empty file)
+++ geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/jaas/JaasSecurityContext.java	Tue Nov 16 14:34:08 2004
@@ -0,0 +1,81 @@
+package org.apache.geronimo.security.jaas;
+
+import org.apache.geronimo.security.RealmPrincipal;
+import org.apache.geronimo.security.ContextManager;
+
+import javax.security.auth.Subject;
+import java.util.*;
+import java.security.Principal;
+
+/**
+ * Tracks security information about a single user.  This is used before,
+ * during, and after the login.
+ *
+ * @version $Revision: 1.0$
+ */
+public class JaasSecurityContext {
+    private String realmName;
+    private Subject subject;
+    private long created;
+    private boolean done;
+    private JaasLoginModuleConfiguration[] modules;
+    private DecouplingCallbackHandler handler;
+    private Set processedPrincipals = new HashSet();
+
+    public JaasSecurityContext(String realmName, JaasLoginModuleConfiguration[] modules) {
+        this.realmName = realmName;
+        this.created = System.currentTimeMillis();
+        this.done = false;
+        this.modules = modules;
+        subject = new Subject();
+    }
+
+    public Subject getSubject() {
+        return subject;
+    }
+
+    public long getCreated() {
+        return created;
+    }
+
+    public boolean isDone() {
+        return done;
+    }
+
+    public void setDone(boolean done) {
+        this.done = done;
+    }
+
+    public JaasLoginModuleConfiguration[] getModules() {
+        return modules;
+    }
+
+    public DecouplingCallbackHandler getHandler() {
+        if(handler == null) { //lazy create
+            handler = new DecouplingCallbackHandler();
+        }
+        return handler;
+    }
+
+    public void processPrincipals() {
+        List list = new LinkedList();
+        for (Iterator it = subject.getPrincipals().iterator(); it.hasNext();) {
+            Principal p = (Principal) it.next();
+            if(!processedPrincipals.contains(p)) {
+                list.add(ContextManager.registerPrincipal(new RealmPrincipal(realmName, p)));
+                processedPrincipals.add(p);
+            }
+        }
+        subject.getPrincipals().addAll(list);
+    }
+
+    public void processPrincipals(Principal[] principals) {
+        List list = new LinkedList();
+        for (int i = 0; i < principals.length; i++) {
+            Principal p = principals[i];
+            list.add(p);
+            list.add(ContextManager.registerPrincipal(new RealmPrincipal(realmName, p)));
+        }
+        subject.getPrincipals().addAll(list);
+    }
+}

Added: geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/remoting/jmx/JaasLoginServiceRemotingClient.java
==============================================================================
--- (empty file)
+++ geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/remoting/jmx/JaasLoginServiceRemotingClient.java	Tue Nov 16 14:34:08 2004
@@ -0,0 +1,56 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache 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.apache.org/licenses/LICENSE-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.
+ */
+
+package org.apache.geronimo.security.remoting.jmx;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.geronimo.core.service.Interceptor;
+import org.apache.geronimo.proxy.ProxyContainer;
+import org.apache.geronimo.remoting.MarshalingInterceptor;
+import org.apache.geronimo.remoting.jmx.NotificationRemoterInterceptor;
+import org.apache.geronimo.remoting.transport.RemoteTransportInterceptor;
+import org.apache.geronimo.security.jaas.JaasLoginServiceMBean;
+
+
+/**
+ * @version $Rev: 46019 $ $Date: 2004-09-14 05:56:06 -0400 (Tue, 14 Sep 2004) $
+ */
+public class JaasLoginServiceRemotingClient {
+    static public JaasLoginServiceMBean create(String host, int port) throws IllegalArgumentException {
+        URI target;
+        try {
+            target = new URI("async", null, host, port, "/JMX", null, "geronimo.remoting:target=JaasLoginServiceRemotingServer");
+        } catch (URISyntaxException e) {
+            throw new IllegalArgumentException("Bad host or port.");
+        }
+        return create(target);
+    }
+
+    static public JaasLoginServiceMBean create(URI target) {
+        // Setup the client side container..
+        RemoteTransportInterceptor remoteInterceptor = new RemoteTransportInterceptor(target);
+        remoteInterceptor.setRemoteURI(target);
+
+        Interceptor firstInterceptor = new MarshalingInterceptor(remoteInterceptor);
+        firstInterceptor = new NotificationRemoterInterceptor(firstInterceptor);
+
+        ProxyContainer clientContainer = new ProxyContainer(firstInterceptor);
+        return (JaasLoginServiceMBean) clientContainer.createProxy(JaasLoginServiceMBean.class.getClassLoader(), new Class[]{JaasLoginServiceMBean.class});
+    }
+}

Added: geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/remoting/jmx/JaasLoginServiceRemotingServer.java
==============================================================================
--- (empty file)
+++ geronimo/trunk/modules/security/src/java/org/apache/geronimo/security/remoting/jmx/JaasLoginServiceRemotingServer.java	Tue Nov 16 14:34:08 2004
@@ -0,0 +1,111 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache 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.apache.org/licenses/LICENSE-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.
+ */
+
+package org.apache.geronimo.security.remoting.jmx;
+
+import javax.management.ObjectName;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.geronimo.core.service.Interceptor;
+import org.apache.geronimo.gbean.GBeanInfo;
+import org.apache.geronimo.gbean.GBeanInfoBuilder;
+import org.apache.geronimo.gbean.GBeanLifecycle;
+import org.apache.geronimo.kernel.Kernel;
+import org.apache.geronimo.kernel.jmx.JMXUtil;
+import org.apache.geronimo.kernel.jmx.MBeanProxyFactory;
+import org.apache.geronimo.proxy.ProxyContainer;
+import org.apache.geronimo.proxy.ReflexiveInterceptor;
+import org.apache.geronimo.remoting.DeMarshalingInterceptor;
+import org.apache.geronimo.remoting.router.JMXRouter;
+import org.apache.geronimo.remoting.router.JMXTarget;
+import org.apache.geronimo.security.jaas.JaasLoginServiceMBean;
+
+
+/**
+ * @version $Rev: 56022 $ $Date: 2004-10-30 01:16:18 -0400 (Sat, 30 Oct 2004) $
+ */
+public class JaasLoginServiceRemotingServer implements GBeanLifecycle, JMXTarget {
+    private static final Log log = LogFactory.getLog(JaasLoginServiceRemotingServer.class);
+    private final Kernel kernel;
+    private final ObjectName objectName;
+    private ProxyContainer serverContainer;
+    private DeMarshalingInterceptor demarshaller;
+    private JMXRouter router;
+
+    public JaasLoginServiceRemotingServer(Kernel kernel, String objectName) {
+        this.kernel = kernel;
+        this.objectName = JMXUtil.getObjectName(objectName);
+    }
+
+    public Interceptor getRemotingEndpointInterceptor() {
+        return demarshaller;
+    }
+
+    public JMXRouter getRouter() {
+        return router;
+    }
+
+    public void setRouter(JMXRouter router) {
+        this.router = router;
+    }
+
+    public void doStart() throws Exception {
+        router.register(objectName, this);
+
+        // Setup the server side contianer..
+        JaasLoginServiceMBean loginService = (JaasLoginServiceMBean) MBeanProxyFactory.getProxy(JaasLoginServiceMBean.class,
+                kernel.getMBeanServer(),
+                JMXUtil.getObjectName("geronimo.security:type=JaasLoginService"));
+        Interceptor firstInterceptor = new ReflexiveInterceptor(loginService);
+        demarshaller = new DeMarshalingInterceptor(firstInterceptor, getClass().getClassLoader());
+        serverContainer = new ProxyContainer(firstInterceptor);
+
+        log.info("Started login service stub");
+    }
+
+    public void doStop() {
+        router.unregister(objectName);
+
+        serverContainer = null;
+        demarshaller = null;
+        log.info("Stopped login service stub");
+    }
+
+    public void doFail() {
+        serverContainer = null;
+        demarshaller = null;
+        log.info("Failed login service stub");
+    }
+
+    public static final GBeanInfo GBEAN_INFO;
+
+    static {
+        GBeanInfoBuilder infoFactory = new GBeanInfoBuilder(JaasLoginServiceRemotingServer.class);
+        infoFactory.addAttribute("kernel", Kernel.class, false);
+        infoFactory.addAttribute("objectName", String.class, false);
+        infoFactory.addReference("Router", JMXRouter.class);
+        infoFactory.addOperation("getRemotingEndpointInterceptor");
+        infoFactory.setConstructor(new String[]{"kernel", "objectName"});
+        GBEAN_INFO = infoFactory.getBeanInfo();
+    }
+
+    public static GBeanInfo getGBeanInfo() {
+        return GBEAN_INFO;
+    }
+}

Modified: geronimo/trunk/modules/security/src/test-data/data/login.config
==============================================================================
--- geronimo/trunk/modules/security/src/test-data/data/login.config	(original)
+++ geronimo/trunk/modules/security/src/test-data/data/login.config	Tue Nov 16 14:34:08 2004
@@ -3,48 +3,41 @@
  */
 
 FOO {
-    org.apache.geronimo.security.jaas.RemoteLoginModule required
-    debug=true
-    uri="async://localhost:4242"
-    realm="properties-realm"
-    kernel="test.kernel";
+    org.apache.geronimo.security.jaas.JaasLoginCoordinator required
+    host="localhost"
+    port="4242"
+    realm="properties-realm";
 };
 
 kerberos {
-    org.apache.geronimo.security.jaas.RemoteLoginModule required
-    debug=true
-    uri="async://localhost:4242"
-    realm="TOOLAZYDOGS.COM"
-    kernel="test.kernel";
+    org.apache.geronimo.security.jaas.JaasLoginCoordinator required
+    host="localhost"
+    port="4242"
+    realm="TOOLAZYDOGS.COM";
 };
 
 sql {
-    org.apache.geronimo.security.jaas.RemoteLoginModule required
-    debug=true
-    uri="async://localhost:4242"
-    realm="sql-realm"
-    kernel="test.kernel";
+    org.apache.geronimo.security.jaas.JaasLoginCoordinator required
+    host="localhost"
+    port="4242"
+    realm="sql-realm";
 };
 
 properties {
-    org.apache.geronimo.security.jaas.RemoteLoginModule required
-    debug=true
-    uri="async://localhost:4242"
-    realm="properties-realm"
-    kernel="test.kernel";
+    org.apache.geronimo.security.jaas.JaasLoginCoordinator required
+    host="localhost"
+    port="4242"
+    realm="properties-realm";
 };
 
 kerberos-local {
-    org.apache.geronimo.security.jaas.LocalLoginModule required
-    debug=true
+    org.apache.geronimo.security.jaas.JaasLoginCoordinator required
     realm="TOOLAZYDOGS.COM"
     kernel="test.kernel";
 };
 
 bridge {
-    org.apache.geronimo.security.jaas.LocalLoginModule required
-    debug=true
+    org.apache.geronimo.security.jaas.JaasLoginCoordinator required
     realm="bridge-realm"
     kernel="test.kernel";
 };
-

Modified: geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/AbstractTest.java
==============================================================================
--- geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/AbstractTest.java	(original)
+++ geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/AbstractTest.java	Tue Nov 16 14:34:08 2004
@@ -55,10 +55,10 @@
 
         // Create all the parts
 
-        gbean = new GBeanMBean("org.apache.geronimo.security.jaas.LoginService");
-        loginService = new ObjectName("geronimo.security:type=LoginService");
+        gbean = new GBeanMBean("org.apache.geronimo.security.jaas.JaasLoginService");
+        loginService = new ObjectName("geronimo.security:type=JaasLoginService");
         gbean.setReferencePatterns("Realms", Collections.singleton(new ObjectName("geronimo.security:type=SecurityRealm,*")));
-        gbean.setAttribute("reclaimPeriod", new Long(10 * 1000));  // todo check other tests to see if ok
+//        gbean.setAttribute("reclaimPeriod", new Long(10 * 1000));  // todo check other tests to see if ok
         gbean.setAttribute("algorithm", "HmacSHA1");
         gbean.setAttribute("password", "secret");
         kernel.loadGBean(loginService, gbean);
@@ -85,9 +85,9 @@
         jmxRouter = new ObjectName("geronimo.remoting:router=JMXRouter");
         kernel.loadGBean(jmxRouter, gbean);
 
-        gbean = new GBeanMBean("org.apache.geronimo.security.remoting.jmx.LoginServiceStub");
+        gbean = new GBeanMBean("org.apache.geronimo.security.remoting.jmx.JaasLoginServiceRemotingServer");
         gbean.setReferencePatterns("Router", Collections.singleton(jmxRouter));
-        serverStub = new ObjectName("geronimo.remoting:target=LoginServiceStub");
+        serverStub = new ObjectName("geronimo.remoting:target=JaasLoginServiceRemotingServer");
         kernel.loadGBean(serverStub, gbean);
 
         kernel.startGBean(loginService);

Modified: geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/bridge/CallerIdentityUserPasswordBridgeTest.java
==============================================================================
--- geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/bridge/CallerIdentityUserPasswordBridgeTest.java	(original)
+++ geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/bridge/CallerIdentityUserPasswordBridgeTest.java	Tue Nov 16 14:34:08 2004
@@ -21,6 +21,10 @@
 import javax.security.auth.login.LoginException;
 
 import org.apache.geronimo.security.realm.providers.GeronimoPasswordCredential;
+import org.apache.geronimo.security.IdentificationPrincipal;
+import org.apache.geronimo.security.ContextManager;
+
+import java.util.Set;
 
 
 /**
@@ -40,6 +44,11 @@
         Subject sourceSubject = new Subject();
         sourceSubject.getPrivateCredentials().add(new GeronimoPasswordCredential(AbstractBridgeTest.USER, AbstractBridgeTest.PASSWORD.toCharArray()));
         Subject targetSubject = bridge.mapSubject(sourceSubject);
+        assertTrue("expected non-null client subject", targetSubject != null);
+        Set set = targetSubject.getPrincipals(IdentificationPrincipal.class);
+        assertEquals("client subject should have one ID principal", set.size(), 1);
+        IdentificationPrincipal idp = (IdentificationPrincipal)set.iterator().next();
+        targetSubject = ContextManager.getRegisteredSubject(idp.getId());
         checkValidSubject(targetSubject);
     }
 

Modified: geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/bridge/ConfiguredIdentityUserPasswordBridgeTest.java
==============================================================================
--- geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/bridge/ConfiguredIdentityUserPasswordBridgeTest.java	(original)
+++ geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/bridge/ConfiguredIdentityUserPasswordBridgeTest.java	Tue Nov 16 14:34:08 2004
@@ -17,7 +17,11 @@
 
 package org.apache.geronimo.security.bridge;
 
+import org.apache.geronimo.security.IdentificationPrincipal;
+import org.apache.geronimo.security.ContextManager;
+
 import javax.security.auth.Subject;
+import java.util.Set;
 
 
 /**
@@ -35,6 +39,13 @@
     public void testConfiguredIdentityBridge() throws Exception {
         Subject sourceSubject = new Subject();
         Subject targetSubject = bridge.mapSubject(sourceSubject);
+
+        assertTrue("expected non-null client subject", targetSubject != null);
+        Set set = targetSubject.getPrincipals(IdentificationPrincipal.class);
+        assertEquals("client subject should have one ID principal", set.size(), 1);
+        IdentificationPrincipal idp = (IdentificationPrincipal)set.iterator().next();
+        targetSubject = ContextManager.getRegisteredSubject(idp.getId());
+
         checkValidSubject(targetSubject);
     }
 

Modified: geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/bridge/MappingUserPasswordBridgeTest.java
==============================================================================
--- geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/bridge/MappingUserPasswordBridgeTest.java	(original)
+++ geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/bridge/MappingUserPasswordBridgeTest.java	Tue Nov 16 14:34:08 2004
@@ -17,9 +17,13 @@
 
 package org.apache.geronimo.security.bridge;
 
+import org.apache.geronimo.security.IdentificationPrincipal;
+import org.apache.geronimo.security.ContextManager;
+
 import java.security.Principal;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 import javax.security.auth.Subject;
 
 
@@ -65,6 +69,11 @@
         subject.getPrincipals().add(new TestUserNamePrincipal(SOURCE_USER_1));
         subject.getPrincipals().add(new TestPasswordPrincipal(SOURCE_PASSWORD_1));
         Subject targetSubject = bridge.mapSubject(subject);
+        assertTrue("expected non-null client subject", targetSubject != null);
+        Set set = targetSubject.getPrincipals(IdentificationPrincipal.class);
+        assertEquals("client subject should have one ID principal", set.size(), 1);
+        IdentificationPrincipal idp = (IdentificationPrincipal)set.iterator().next();
+        targetSubject = ContextManager.getRegisteredSubject(idp.getId());
         checkValidSubject(targetSubject);
     }
 
@@ -75,7 +84,7 @@
         try {
             bridge.mapSubject(subject);
             fail();
-        } catch (Exception e) {
+        } catch (Throwable e) {
         }
     }
 
@@ -87,7 +96,7 @@
         try {
             bridge.mapSubject(subject);
             fail();
-        } catch (Exception e) {
+        } catch (Throwable e) {
         }
     }
 

Modified: geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/ConfigurationEntryTest.java
==============================================================================
--- geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/ConfigurationEntryTest.java	(original)
+++ geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/ConfigurationEntryTest.java	Tue Nov 16 14:34:08 2004
@@ -24,6 +24,7 @@
 import java.net.URI;
 import java.util.Collections;
 import java.util.Properties;
+import java.util.Set;
 
 import junit.framework.TestCase;
 
@@ -57,13 +58,18 @@
 
         context.login();
         Subject subject = context.getSubject();
+        assertTrue("expected non-null client subject", subject != null);
+        Set set = subject.getPrincipals(IdentificationPrincipal.class);
+        assertEquals("client subject should have one ID principal", set.size(), 1);
+        IdentificationPrincipal idp = (IdentificationPrincipal)set.iterator().next();
+        subject = ContextManager.getRegisteredSubject(idp.getId());
 
-        assertTrue("expected non-null subject", subject != null);
-        assertTrue("subject should have one remote principal", subject.getPrincipals(IdentificationPrincipal.class).size() == 1);
+        assertTrue("expected non-null server subject", subject != null);
+        assertTrue("server subject should have one remote principal", subject.getPrincipals(IdentificationPrincipal.class).size() == 1);
         IdentificationPrincipal remote = (IdentificationPrincipal) subject.getPrincipals(IdentificationPrincipal.class).iterator().next();
-        assertTrue("subject should be associated with remote id", ContextManager.getRegisteredSubject(remote.getId()) != null);
-        assertTrue("subject should have five principals", subject.getPrincipals().size() == 5);
-        assertTrue("subject should have two realm principal", subject.getPrincipals(RealmPrincipal.class).size() == 2);
+        assertTrue("server subject should be associated with remote id", ContextManager.getRegisteredSubject(remote.getId()) != null);
+        assertTrue("server subject should have two realm principals ("+subject.getPrincipals(RealmPrincipal.class).size()+")", subject.getPrincipals(RealmPrincipal.class).size() == 2);
+        assertTrue("server subject should have five principals ("+subject.getPrincipals().size()+")", subject.getPrincipals().size() == 5);
         RealmPrincipal principal = (RealmPrincipal) subject.getPrincipals(RealmPrincipal.class).iterator().next();
         assertTrue("id of principal should be non-zero", principal.getId() != 0);
 
@@ -90,10 +96,10 @@
         loginConfiguration = new ObjectName("geronimo.security:type=LoginConfiguration");
         kernel.loadGBean(loginConfiguration, gbean);
 
-        gbean = new GBeanMBean("org.apache.geronimo.security.jaas.LoginService");
-        loginService = new ObjectName("geronimo.security:type=LoginService");
+        gbean = new GBeanMBean("org.apache.geronimo.security.jaas.JaasLoginService");
+        loginService = new ObjectName("geronimo.security:type=JaasLoginService");
         gbean.setReferencePatterns("Realms", Collections.singleton(new ObjectName("geronimo.security:type=SecurityRealm,*")));
-        gbean.setAttribute("reclaimPeriod", new Long(100));
+//        gbean.setAttribute("reclaimPeriod", new Long(100));
         gbean.setAttribute("algorithm", "HmacSHA1");
         gbean.setAttribute("password", "secret");
         kernel.loadGBean(loginService, gbean);
@@ -130,9 +136,9 @@
         jmxRouter = new ObjectName("geronimo.remoting:router=JMXRouter");
         kernel.loadGBean(jmxRouter, gbean);
 
-        gbean = new GBeanMBean("org.apache.geronimo.security.remoting.jmx.LoginServiceStub");
+        gbean = new GBeanMBean("org.apache.geronimo.security.remoting.jmx.JaasLoginServiceRemotingServer");
         gbean.setReferencePatterns("Router", Collections.singleton(jmxRouter));
-        serverStub = new ObjectName("geronimo.remoting:target=LoginServiceStub");
+        serverStub = new ObjectName("geronimo.remoting:target=JaasLoginServiceRemotingServer");
         kernel.loadGBean(serverStub, gbean);
 
         kernel.startGBean(loginConfiguration);

Modified: geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/LoginPropertiesFileTest.java
==============================================================================
--- geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/LoginPropertiesFileTest.java	(original)
+++ geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/LoginPropertiesFileTest.java	Tue Nov 16 14:34:08 2004
@@ -23,6 +23,7 @@
 import java.io.File;
 import java.util.Collections;
 import java.util.Properties;
+import java.util.Set;
 
 import org.apache.geronimo.gbean.jmx.GBeanMBean;
 import org.apache.geronimo.security.AbstractTest;
@@ -99,18 +100,23 @@
 
         context.login();
         Subject subject = context.getSubject();
+        assertTrue("expected non-null client subject", subject != null);
+        Set set = subject.getPrincipals(IdentificationPrincipal.class);
+        assertEquals("client subject should have one ID principal", set.size(), 1);
+        IdentificationPrincipal idp = (IdentificationPrincipal)set.iterator().next();
+        subject = ContextManager.getRegisteredSubject(idp.getId());
 
-        assertTrue("expected non-null subject", subject != null);
-        assertTrue("subject should have one remote principal", subject.getPrincipals(IdentificationPrincipal.class).size() == 1);
+        assertTrue("expected non-null server subject", subject != null);
+        assertTrue("server subject should have one remote principal", subject.getPrincipals(IdentificationPrincipal.class).size() == 1);
         IdentificationPrincipal remote = (IdentificationPrincipal) subject.getPrincipals(IdentificationPrincipal.class).iterator().next();
-        assertTrue("subject should be associated with remote id", ContextManager.getRegisteredSubject(remote.getId()) != null);
-        assertTrue("subject should have five principals", subject.getPrincipals().size() == 5);
-        assertTrue("subject should have two realm principal", subject.getPrincipals(RealmPrincipal.class).size() == 2);
+        assertTrue("server subject should be associated with remote id", ContextManager.getRegisteredSubject(remote.getId()) != null);
+        assertTrue("server subject should have five principals", subject.getPrincipals().size() == 5);
+        assertTrue("server subject should have two realm principal", subject.getPrincipals(RealmPrincipal.class).size() == 2);
         RealmPrincipal principal = (RealmPrincipal) subject.getPrincipals(RealmPrincipal.class).iterator().next();
         assertTrue("id of principal should be non-zero", principal.getId() != 0);
 
         context.logout();
 
-        assertTrue("id of subject should be null", ContextManager.getSubjectId(subject) == null);
+        assertTrue("id of server subject should be null", ContextManager.getSubjectId(subject) == null);
     }
 }

Modified: geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/LoginSQLTest.java
==============================================================================
--- geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/LoginSQLTest.java	(original)
+++ geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/LoginSQLTest.java	Tue Nov 16 14:34:08 2004
@@ -112,7 +112,7 @@
     public void testNothing() {
     }
 
-    public void XtestLogin() throws Exception {
+    public void testLogin() throws Exception {
         LoginContext context = new LoginContext("sql", new UsernamePasswordCallback("alan", "starcraft"));
 
         context.login();
@@ -128,7 +128,7 @@
         context.logout();
     }
 
-    public void XtestLogoutTimeout() throws Exception {
+    public void testLogoutTimeout() throws Exception {
 
         assertEquals(new Integer(State.RUNNING_INDEX), kernel.getAttribute(sqlRealm, "state"));
 
@@ -171,7 +171,7 @@
         }
     }
 
-    public void XtestReloginTimeout() throws Exception {
+    public void testReloginTimeout() throws Exception {
         LoginContext context = new LoginContext("sql", new UsernamePasswordCallback("alan", "starcraft"));
 
         context.login();

Modified: geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/LoginSimpleRealmTest.java
==============================================================================
--- geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/LoginSimpleRealmTest.java	(original)
+++ geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/LoginSimpleRealmTest.java	Tue Nov 16 14:34:08 2004
@@ -31,6 +31,7 @@
 import java.security.Principal;
 import java.util.Map;
 import java.util.Properties;
+import java.util.Set;
 
 import org.apache.geronimo.gbean.jmx.GBeanMBean;
 import org.apache.geronimo.security.AbstractTest;
@@ -109,13 +110,18 @@
 
         context.login();
         Subject subject = context.getSubject();
+        assertTrue("expected non-null client subject", subject != null);
+        Set set = subject.getPrincipals(IdentificationPrincipal.class);
+        assertEquals("client subject should have one ID principal", set.size(), 1);
+        IdentificationPrincipal idp = (IdentificationPrincipal)set.iterator().next();
+        subject = ContextManager.getRegisteredSubject(idp.getId());
 
-        assertTrue("expected non-null subject", subject != null);
-        assertTrue("subject should have one remote principal", subject.getPrincipals(IdentificationPrincipal.class).size() == 1);
+        assertTrue("expected non-null server subject", subject != null);
+        assertTrue("server subject should have one remote principal", subject.getPrincipals(IdentificationPrincipal.class).size() == 1);
         IdentificationPrincipal remote = (IdentificationPrincipal) subject.getPrincipals(IdentificationPrincipal.class).iterator().next();
-        assertTrue("subject should be associated with remote id", ContextManager.getRegisteredSubject(remote.getId()) != null);
-        assertTrue("subject should have five principals", subject.getPrincipals().size() == 5);
-        assertTrue("subject should have two realm principal", subject.getPrincipals(RealmPrincipal.class).size() == 2);
+        assertTrue("server subject should be associated with remote id", ContextManager.getRegisteredSubject(remote.getId()) != null);
+        assertTrue("server subject should have five principals", subject.getPrincipals().size() == 5);
+        assertTrue("server subject should have two realm principal", subject.getPrincipals(RealmPrincipal.class).size() == 2);
         RealmPrincipal principal = (RealmPrincipal) subject.getPrincipals(RealmPrincipal.class).iterator().next();
         assertTrue("id of principal should be non-zero", principal.getId() != 0);
 

Added: geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/TimeoutTest.java
==============================================================================
--- (empty file)
+++ geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/jaas/TimeoutTest.java	Tue Nov 16 14:34:08 2004
@@ -0,0 +1,191 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache 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.apache.org/licenses/LICENSE-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.
+ */
+
+package org.apache.geronimo.security.jaas;
+
+import javax.management.ObjectName;
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import java.io.File;
+import java.util.Collections;
+import java.util.Properties;
+import java.util.Set;
+import java.net.URI;
+
+import org.apache.geronimo.gbean.jmx.GBeanMBean;
+import org.apache.geronimo.security.AbstractTest;
+import org.apache.geronimo.security.ContextManager;
+import org.apache.geronimo.security.IdentificationPrincipal;
+import org.apache.geronimo.security.RealmPrincipal;
+import org.apache.geronimo.security.bridge.TestRealm;
+import org.apache.geronimo.system.serverinfo.ServerInfo;
+import org.apache.geronimo.kernel.Kernel;
+
+
+/**
+ * @version $Rev: 46019 $ $Date: 2004-09-14 05:56:06 -0400 (Tue, 14 Sep 2004) $
+ */
+public class TimeoutTest extends AbstractTest {
+
+    protected ObjectName serverInfo;
+    protected ObjectName loginConfiguration;
+    protected ObjectName propertiesRealm;
+    protected ObjectName propertiesCE;
+
+    public void setUp() throws Exception {
+        kernel = new Kernel("test.kernel", "simple.geronimo.test");
+        kernel.boot();
+
+        GBeanMBean gbean;
+
+        // Create all the parts
+
+        gbean = new GBeanMBean("org.apache.geronimo.security.jaas.JaasLoginService");
+        loginService = new ObjectName("geronimo.security:type=JaasLoginService");
+        gbean.setReferencePatterns("Realms", Collections.singleton(new ObjectName("geronimo.security:type=SecurityRealm,*")));
+        gbean.setAttribute("expiredLoginScanIntervalMillis", new Integer(50));
+        gbean.setAttribute("maxLoginDurationMillis", new Integer(1000));
+        gbean.setAttribute("algorithm", "HmacSHA1");
+        gbean.setAttribute("password", "secret");
+        kernel.loadGBean(loginService, gbean);
+
+        gbean = new GBeanMBean("org.apache.geronimo.security.bridge.TestRealm");
+        testRealm = new ObjectName("geronimo.security:type=SecurityRealm,realm=testrealm");
+        gbean.setAttribute("realmName", TestRealm.REALM_NAME);
+        gbean.setAttribute("maxLoginModuleAge", new Long(1 * 1000));
+        gbean.setAttribute("debug", new Boolean(true));
+        kernel.loadGBean(testRealm, gbean);
+
+        gbean = new GBeanMBean("org.apache.geronimo.remoting.router.SubsystemRouter");
+        subsystemRouter = new ObjectName("geronimo.remoting:router=SubsystemRouter");
+        kernel.loadGBean(subsystemRouter, gbean);
+
+        gbean = new GBeanMBean("org.apache.geronimo.remoting.transport.TransportLoader");
+        gbean.setAttribute("bindURI", new URI("async://0.0.0.0:4242"));
+        gbean.setReferencePatterns("Router", Collections.singleton(subsystemRouter));
+        asyncTransport = new ObjectName("geronimo.remoting:transport=async");
+        kernel.loadGBean(asyncTransport, gbean);
+
+        gbean = new GBeanMBean("org.apache.geronimo.remoting.router.JMXRouter");
+        gbean.setReferencePatterns("SubsystemRouter", Collections.singleton(subsystemRouter));
+        jmxRouter = new ObjectName("geronimo.remoting:router=JMXRouter");
+        kernel.loadGBean(jmxRouter, gbean);
+
+        gbean = new GBeanMBean("org.apache.geronimo.security.remoting.jmx.JaasLoginServiceRemotingServer");
+        gbean.setReferencePatterns("Router", Collections.singleton(jmxRouter));
+        serverStub = new ObjectName("geronimo.remoting:target=JaasLoginServiceRemotingServer");
+        kernel.loadGBean(serverStub, gbean);
+
+        kernel.startGBean(loginService);
+        kernel.startGBean(testRealm);
+        kernel.startGBean(subsystemRouter);
+        kernel.startGBean(asyncTransport);
+        kernel.startGBean(jmxRouter);
+        kernel.startGBean(serverStub);
+
+        gbean = new GBeanMBean(ServerInfo.GBEAN_INFO);
+        serverInfo = new ObjectName("geronimo.system:role=ServerInfo");
+        gbean.setAttribute("baseDirectory", ".");
+        kernel.loadGBean(serverInfo, gbean);
+        kernel.startGBean(serverInfo);
+
+        gbean = new GBeanMBean("org.apache.geronimo.security.jaas.GeronimoLoginConfiguration");
+        loginConfiguration = new ObjectName("geronimo.security:type=LoginConfiguration");
+        kernel.loadGBean(loginConfiguration, gbean);
+
+        gbean = new GBeanMBean("org.apache.geronimo.security.realm.providers.PropertiesFileSecurityRealm");
+        propertiesRealm = new ObjectName("geronimo.security:type=SecurityRealm,realm=properties-realm");
+        gbean.setAttribute("realmName", "properties-realm");
+        gbean.setAttribute("maxLoginModuleAge", new Long(1 * 1000));
+        gbean.setAttribute("usersURI", (new File(new File("."), "src/test-data/data/users.properties")).toURI());
+        gbean.setAttribute("groupsURI", (new File(new File("."), "src/test-data/data/groups.properties")).toURI());
+        gbean.setReferencePatterns("ServerInfo", Collections.singleton(serverInfo));
+        kernel.loadGBean(propertiesRealm, gbean);
+
+        gbean = new GBeanMBean("org.apache.geronimo.security.jaas.ConfigurationEntryRealmLocal");
+        propertiesCE = new ObjectName("geronimo.security:type=ConfigurationEntry,jaasId=properties");
+        gbean.setAttribute("applicationConfigName", "properties");
+        gbean.setAttribute("realmName", "properties-realm");
+        gbean.setAttribute("controlFlag", LoginModuleControlFlag.REQUIRED);
+        gbean.setAttribute("options", new Properties());
+        kernel.loadGBean(propertiesCE, gbean);
+
+        kernel.startGBean(loginConfiguration);
+        kernel.startGBean(propertiesRealm);
+        kernel.startGBean(propertiesCE);
+    }
+
+    public void tearDown() throws Exception {
+        kernel.stopGBean(propertiesCE);
+        kernel.stopGBean(propertiesRealm);
+        kernel.stopGBean(loginConfiguration);
+        kernel.stopGBean(serverInfo);
+
+        kernel.unloadGBean(propertiesRealm);
+        kernel.unloadGBean(propertiesCE);
+        kernel.unloadGBean(loginConfiguration);
+        kernel.unloadGBean(serverInfo);
+
+        kernel.stopGBean(serverStub);
+        kernel.stopGBean(jmxRouter);
+        kernel.stopGBean(asyncTransport);
+        kernel.stopGBean(subsystemRouter);
+        kernel.stopGBean(testRealm);
+        kernel.stopGBean(loginService);
+
+        kernel.unloadGBean(loginService);
+        kernel.unloadGBean(testRealm);
+        kernel.unloadGBean(subsystemRouter);
+        kernel.unloadGBean(asyncTransport);
+        kernel.unloadGBean(jmxRouter);
+        kernel.unloadGBean(serverStub);
+
+        kernel.shutdown();
+    }
+
+    public void testTimeout() throws Exception {
+
+        LoginContext context = new LoginContext("properties", new AbstractTest.UsernamePasswordCallback("alan", "starcraft"));
+
+        context.login();
+        Subject subject = context.getSubject();
+        assertTrue("expected non-null client subject", subject != null);
+        Set set = subject.getPrincipals(IdentificationPrincipal.class);
+        assertEquals("client subject should have one ID principal", set.size(), 1);
+        IdentificationPrincipal idp = (IdentificationPrincipal) set.iterator().next();
+        subject = ContextManager.getRegisteredSubject(idp.getId());
+
+        assertTrue("expected non-null server subject", subject != null);
+        assertTrue("server subject should have one remote principal", subject.getPrincipals(IdentificationPrincipal.class).size() == 1);
+        IdentificationPrincipal remote = (IdentificationPrincipal) subject.getPrincipals(IdentificationPrincipal.class).iterator().next();
+        assertTrue("server subject should be associated with remote id", ContextManager.getRegisteredSubject(remote.getId()) != null);
+        assertTrue("server subject should have five principals", subject.getPrincipals().size() == 5);
+        assertTrue("server subject should have two realm principal", subject.getPrincipals(RealmPrincipal.class).size() == 2);
+        RealmPrincipal principal = (RealmPrincipal) subject.getPrincipals(RealmPrincipal.class).iterator().next();
+        assertTrue("id of principal should be non-zero", principal.getId() != 0);
+
+        assertTrue("id of server subject should be non-null", ContextManager.getSubjectId(subject) != null);
+
+        Thread.sleep(300); // wait for timeout to kick in
+
+        assertTrue("id of server subject should be non-null", ContextManager.getSubjectId(subject) != null);
+
+        Thread.sleep(1700); // wait for timeout to kick in
+
+        assertTrue("id of server subject should be null", ContextManager.getSubjectId(subject) == null);
+    }
+}

Modified: geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/remoting/jmx/RemoteLoginTest.java
==============================================================================
--- geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/remoting/jmx/RemoteLoginTest.java	(original)
+++ geronimo/trunk/modules/security/src/test/org/apache/geronimo/security/remoting/jmx/RemoteLoginTest.java	Tue Nov 16 14:34:08 2004
@@ -37,7 +37,7 @@
 import org.apache.geronimo.remoting.transport.TransportLoader;
 import org.apache.geronimo.security.IdentificationPrincipal;
 import org.apache.geronimo.security.RealmPrincipal;
-import org.apache.geronimo.security.jaas.LoginServiceMBean;
+import org.apache.geronimo.security.jaas.JaasLoginServiceMBean;
 import org.apache.geronimo.system.serverinfo.ServerInfo;
 
 
@@ -57,9 +57,9 @@
     ObjectName jmxRouter;
     ObjectName secureJmxRouter;
     ObjectName serverStub;
-    LoginServiceMBean asyncRemoteProxy;
-    LoginServiceMBean saslRemoteProxy;
-    LoginServiceMBean gssapiRemoteProxy;
+    JaasLoginServiceMBean asyncRemoteProxy;
+    JaasLoginServiceMBean saslRemoteProxy;
+    JaasLoginServiceMBean gssapiRemoteProxy;
 
     public void testNothing() {
     }
@@ -100,10 +100,10 @@
         kernel.loadGBean(serverInfo, gbean);
         kernel.startGBean(serverInfo);
 
-        gbean = new GBeanMBean("org.apache.geronimo.security.jaas.LoginService");
-        loginService = new ObjectName("geronimo.security:type=LoginService");
+        gbean = new GBeanMBean("org.apache.geronimo.security.jaas.JaasLoginService");
+        loginService = new ObjectName("geronimo.security:type=JaasLoginService");
         gbean.setReferencePatterns("Realms", Collections.singleton(new ObjectName("geronimo.security:type=SecurityRealm,*")));
-        gbean.setAttribute("reclaimPeriod", new Long(100));
+//        gbean.setAttribute("reclaimPeriod", new Long(100));
         gbean.setAttribute("algorithm", "HmacSHA1");
         gbean.setAttribute("password", "secret");
         kernel.loadGBean(loginService, gbean);
@@ -153,9 +153,9 @@
         secureJmxRouter = new ObjectName("geronimo.remoting:router=JMXRouter,type=secure");
         kernel.loadGBean(secureJmxRouter, gbean);
 
-        gbean = new GBeanMBean("org.apache.geronimo.security.remoting.jmx.LoginServiceStub");
+        gbean = new GBeanMBean("org.apache.geronimo.security.remoting.jmx.JaasLoginServiceRemotingServer");
         gbean.setReferencePatterns("Router", Collections.singleton(secureJmxRouter));
-        serverStub = new ObjectName("geronimo.remoting:target=LoginServiceStub");
+        serverStub = new ObjectName("geronimo.remoting:target=JaasLoginServiceRemotingServer");
         kernel.loadGBean(serverStub, gbean);
 
         kernel.startGBean(loginService);
@@ -171,15 +171,15 @@
 
         TransportLoader bean = (TransportLoader) MBeanProxyFactory.getProxy(TransportLoader.class, kernel.getMBeanServer(), asyncTransport);
         URI connectURI = bean.getClientConnectURI();
-        asyncRemoteProxy = RemoteLoginServiceFactory.create(connectURI.getHost(), connectURI.getPort());
+        asyncRemoteProxy = JaasLoginServiceRemotingClient.create(connectURI.getHost(), connectURI.getPort());
 
         bean = (TransportLoader) MBeanProxyFactory.getProxy(TransportLoader.class, kernel.getMBeanServer(), saslTransport);
         connectURI = bean.getClientConnectURI();
-        saslRemoteProxy = RemoteLoginServiceFactory.create(connectURI.getHost(), connectURI.getPort());
+        saslRemoteProxy = JaasLoginServiceRemotingClient.create(connectURI.getHost(), connectURI.getPort());
 
         bean = (TransportLoader) MBeanProxyFactory.getProxy(TransportLoader.class, kernel.getMBeanServer(), gssapiTransport);
         connectURI = bean.getClientConnectURI();
-        gssapiRemoteProxy = RemoteLoginServiceFactory.create(connectURI.getHost(), connectURI.getPort());
+        gssapiRemoteProxy = JaasLoginServiceRemotingClient.create(connectURI.getHost(), connectURI.getPort());
     }
 
     protected void tearDown() throws Exception {