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 {