You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by da...@apache.org on 2007/03/25 05:26:56 UTC

svn commit: r522150 - in /incubator/openejb/trunk/openejb3: container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ container/openejb-core/src/main/java/org/apache/openejb/config/ server/openejb-client/src/main/java/org/apache/openej...

Author: dain
Date: Sat Mar 24 20:26:50 2007
New Revision: 522150

URL: http://svn.apache.org/viewvc?view=rev&rev=522150
Log:
Added client side login module

Added:
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientIdentityPrincipal.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientLoginModule.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/StaticUsernamePasswordCallbackHandler.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/UsernamePasswordCallbackHandler.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/client.login.conf
    incubator/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/ClientLoginTest.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/LoginTestUtil.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/MainTest.java
Removed:
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/App.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/AppTest.java
Modified:
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ClientInfo.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Main.java

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java?view=diff&rev=522150&r1=522149&r2=522150
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java Sat Mar 24 20:26:50 2007
@@ -434,6 +434,9 @@
                 if (clientInfo.mainClass != null) {
                     containerSystem.getJNDIContext().bind("java:openejb/client/" + clientInfo.moduleId + "/comp/mainClass", clientInfo.mainClass);
                 }
+                if (clientInfo.callbackHandler != null) {
+                    containerSystem.getJNDIContext().bind("java:openejb/client/" + clientInfo.moduleId + "/comp/callbackHandler", clientInfo.callbackHandler);
+                }
                 ArrayList<Injection> injections = new ArrayList<Injection>();
                 JndiEncInfo jndiEnc = clientInfo.jndiEnc;
                 for (EjbReferenceInfo info : jndiEnc.ejbReferences) {

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ClientInfo.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ClientInfo.java?view=diff&rev=522150&r1=522149&r2=522150
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ClientInfo.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ClientInfo.java Sat Mar 24 20:26:50 2007
@@ -28,7 +28,8 @@
     public String largeIcon;
     public String moduleId;
     public String mainClass;
-    
+    public String callbackHandler;
+
     public JndiEncInfo jndiEnc;
 
     public final List<CallbackInfo> postConstruct = new ArrayList<CallbackInfo>();

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java?view=diff&rev=522150&r1=522149&r2=522150
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java Sat Mar 24 20:26:50 2007
@@ -354,6 +354,7 @@
             clientInfo.displayName = applicationClient.getDisplayName();
             clientInfo.codebase = clientModule.getJarLocation();
             clientInfo.mainClass = clientModule.getMainClass();
+            clientInfo.callbackHandler = applicationClient.getCallbackHandler();
             clientInfo.moduleId = getClientModuleId(clientModule);
 
             JndiEncInfoBuilder jndiEncInfoBuilder = new JndiEncInfoBuilder(appInfo.ejbJars);

Modified: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java?view=diff&rev=522150&r1=522149&r2=522150
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java (original)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java Sat Mar 24 20:26:50 2007
@@ -18,7 +18,6 @@
 
 import java.io.IOException;
 import java.io.ObjectInput;
-import java.io.ObjectInputStream;
 import java.io.ObjectOutput;
 import java.io.ObjectOutputStream;
 import java.io.OutputStream;
@@ -28,10 +27,20 @@
 import java.util.logging.Logger;
 
 public class Client {
+    private static final Logger logger = Logger.getLogger("OpenEJB.client");
 
-    private static Logger logger = Logger.getLogger(Client.class.getName());
+    private static Client client = new Client();
+
+    // This lame hook point if only of testing
+    public static void setClient(Client client) {
+        Client.client = client;
+    }
 
     public static Response request(Request req, Response res, ServerMetaData server) throws RemoteException {
+        return client.processRequest(req, res, server);
+    }
+
+    protected Response processRequest(Request req, Response res, ServerMetaData server) throws RemoteException {
         if (server == null)
             throw new IllegalArgumentException("Server instance cannot be null");
 
@@ -56,7 +65,7 @@
                 }
             }
             
-            //If no servers responded, thow an error
+            // If no servers responded, throw an error
             if (conn == null) {
                 StringBuffer buffer = new StringBuffer();
                 for (int i = 0; i < uris.length; i++) {

Added: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientIdentityPrincipal.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientIdentityPrincipal.java?view=auto&rev=522150
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientIdentityPrincipal.java (added)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientIdentityPrincipal.java Sat Mar 24 20:26:50 2007
@@ -0,0 +1,40 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.openejb.client;
+
+import java.security.Principal;
+import java.io.Serializable;
+
+public class ClientIdentityPrincipal implements Principal, Serializable {
+    private static final long serialVersionUID = -4620883427203231384L;
+    private final String name;
+    private final Object clientIdentity;
+
+    public ClientIdentityPrincipal(String name, Object clientIdentity) {
+        this.name = name;
+        this.clientIdentity = clientIdentity;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Object getClientIdentity() {
+        return clientIdentity;
+    }
+}

Added: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientLoginModule.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientLoginModule.java?view=auto&rev=522150
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientLoginModule.java (added)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientLoginModule.java Sat Mar 24 20:26:50 2007
@@ -0,0 +1,151 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.openejb.client;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.spi.LoginModule;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.rmi.RemoteException;
+import java.util.Map;
+import java.util.logging.Logger;
+
+public class ClientLoginModule implements LoginModule {
+    private static final Logger log = Logger.getLogger("OpenEJB.client");
+    private Subject subject;
+    private CallbackHandler callbackHandler;
+    private String serverUri;
+    private boolean debug;
+
+    private String user;
+    private Object clientIdentity;
+    private ClientIdentityPrincipal principal;
+
+    public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
+        this.subject = subject;
+        this.callbackHandler = callbackHandler;
+
+        // determine the server uri
+        serverUri = System.getProperty("openejb.server.uri");
+        if (serverUri == null) {
+            serverUri = (String) options.get("openejb.server.uri");
+        }
+
+        this.debug = "true".equalsIgnoreCase((String) options.get("debug"));
+        if (debug) {
+            log.config("Initialized ClientLoginModule: debug=" + debug);
+        }
+    }
+
+    public boolean login() throws LoginException {
+        // determine the server location
+        URI location = null;
+        try {
+            location = new URI(serverUri);
+        } catch (Exception e) {
+            if (serverUri.indexOf("://") == -1) {
+                try {
+                    location = new URI("foo://" + serverUri);
+                } catch (URISyntaxException giveUp) {
+                    throw new LoginException("Invalid openejb.server.uri " + serverUri);
+                }
+            }
+        }
+        ServerMetaData server = new ServerMetaData(location);
+
+        // create the callbacks
+        Callback[] callbacks = new Callback[2];
+        callbacks[0] = new NameCallback("Username: ");
+        callbacks[1] = new PasswordCallback("Password: ", false);
+
+        // get the call back values (username and password)
+        try {
+            callbackHandler.handle(callbacks);
+        } catch (IOException ioe) {
+            throw new LoginException(ioe.getMessage());
+        } catch (UnsupportedCallbackException uce) {
+            throw new LoginException(uce.getMessage() + " not available to obtain information from user");
+        }
+        user = ((NameCallback) callbacks[0]).getName();
+        char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword();
+        if (tmpPassword == null) tmpPassword = new char[0];
+
+        // authenticate
+        AuthenticationRequest authReq = new AuthenticationRequest(user, new String(tmpPassword));
+        AuthenticationResponse authRes;
+        try {
+            authRes = (AuthenticationResponse) Client.request(authReq, new AuthenticationResponse(), server);
+        } catch (RemoteException e) {
+            throw (LoginException) new LoginException("Unable to authenticate with server " + serverUri).initCause(e);
+        }
+
+        // check the response
+        if (authRes.getResponseCode() == ResponseCodes.AUTH_GRANTED) {
+            clientIdentity = authRes.getIdentity().getClientIdentity();
+        } else {
+            throw new FailedLoginException("This principle is not authorized.");
+        }
+
+        if (debug) {
+            log.config("login " + user);
+        }
+        return true;
+    }
+
+    public boolean commit() throws LoginException {
+        principal = new ClientIdentityPrincipal(user, clientIdentity);
+        subject.getPrincipals().add(principal);
+
+        if (debug) {
+            log.config("commit");
+        }
+        return true;
+    }
+
+    public boolean abort() throws LoginException {
+        clear();
+
+        if (debug) {
+            log.config("abort");
+        }
+        return true;
+    }
+
+    public boolean logout() throws LoginException {
+        subject.getPrincipals().remove(principal);
+
+        if (debug) {
+            log.config("logout");
+        }
+        return true;
+    }
+
+    private void clear() {
+        user = null;
+        clientIdentity = null;
+        principal = null;
+    }
+}

Modified: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Main.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Main.java?view=diff&rev=522150&r1=522149&r2=522150
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Main.java (original)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Main.java Sat Mar 24 20:26:50 2007
@@ -18,15 +18,21 @@
 
 import javax.naming.InitialContext;
 import javax.naming.NamingException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
+import javax.naming.NameNotFoundException;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginContext;
 import java.io.File;
-import java.net.URLClassLoader;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
 import java.net.URL;
+import java.net.URLClassLoader;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * @version $Rev$ $Date$
@@ -37,19 +43,38 @@
 
         System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, "org.apache.openejb.client");
 
+        // the new initial context is automatically hooked up to the server side
+        // java:openejb/client/${clientModuleId} tree
         InitialContext initialContext = new InitialContext();
 
-
+        // path to the client jar file
         String path = (String) initialContext.lookup("java:comp/path");
         // TODO: Download the file
         File file = new File(path);
 
+        // Create a child class loader containing the application jar
         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
-        classLoader = new URLClassLoader(new URL[]{file.toURL()}, classLoader);
+        if (classLoader == null) {
+            classLoader = new URLClassLoader(new URL[]{file.toURL()});
+        } else {
+            classLoader = new URLClassLoader(new URL[]{file.toURL()}, classLoader);
+        }
+        Thread.currentThread().setContextClassLoader(classLoader);
 
+        // load the main class and get the main method
+        // do this first so we fail fast on a bad class path
         String mainClassName = (String) initialContext.lookup("java:comp/mainClass");
-
         Class mainClass = classLoader.loadClass(mainClassName);
+        final Method mainMethod = mainClass.getMethod("main", String[].class);
+
+        // load the callback handler class
+        // again do this before any major work so we can fail fase
+        Class callbackHandlerClass = null;
+        try {
+            String callbackHandlerName = (String) initialContext.lookup("java:comp/callbackHandler");
+            callbackHandlerClass = classLoader.loadClass(callbackHandlerName);
+        } catch (NameNotFoundException ignored) {
+        }
 
         InjectionMetaData injectionMetaData = (InjectionMetaData) initialContext.lookup("java:comp/injections");
         for (Injection injection : injectionMetaData.getInjections()) {
@@ -65,13 +90,66 @@
             }
         }
 
-        Method mainMethod = mainClass.getMethod("main", args.getClass());
-        mainMethod.invoke(null, new Object[]{args});
+        // if there is no security then just call the main method
+        final Object[] mainArgs = new Object[] {args};
+        if (callbackHandlerClass == null) {
+            invoke(mainMethod, mainArgs);
+        } else {
+            // create the callback handler
+            CallbackHandler callbackHandler = (CallbackHandler) callbackHandlerClass.newInstance();
+
+            // initialize the jaas system
+            loadJassLoginConfig(classLoader);
+
+            // login
+            LoginContext loginContext = new LoginContext("ClientLogin", callbackHandler);
+            loginContext.login();
 
+            // success - get the subject
+            Subject subject = loginContext.getSubject();
+
+            // call the main method in a doAs so the subject is associated with the thread
+            try {
+                Subject.doAs(subject, new PrivilegedExceptionAction() {
+                    public Object run() throws Exception {
+                        invoke(mainMethod, mainArgs);
+                        return null;
+                    }
+                });
+            } finally {
+                // And finally, logout
+                loginContext.logout();
+            }
+        }
+    }
+
+    private static void invoke(Method mainMethod, Object[] mainArgs) throws Exception {
+        try {
+            mainMethod.invoke(null, mainArgs);
+        } catch (InvocationTargetException e) {
+            Throwable cause = e.getCause();
+            if (cause instanceof Exception) {
+                throw (Exception) cause;
+            } else if (cause instanceof Error) {
+                throw (Error) cause;
+            }
+            throw new Error(e);
+        }
+    }
+
+    private static void loadJassLoginConfig(ClassLoader classLoader) {
+        String path = System.getProperty("java.security.auth.login.config");
+        if (path == null) {
+            URL resource = classLoader.getResource("client.login.config");
+            if (resource != null) {
+                path = resource.getFile();
+                System.setProperty("java.security.auth.login.config", path);
+            }
+        }
     }
 
     private static String[] siftArgs(String[] args) {
-        List<String> argsList = new ArrayList();
+        List<String> argsList = new ArrayList<String>();
         for (int i = 0; i < args.length; i++) {
             String arg = args[i];
             if (arg.indexOf("-D") == -1) {
@@ -82,18 +160,16 @@
                 System.setProperty(prop, val);
             }
         }
-        return (String[]) argsList.toArray(new String[argsList.size()]);
+        return argsList.toArray(new String[argsList.size()]);
     }
 
 
     private static void setAccessible(final Field field) {
-         AccessController.doPrivileged(new PrivilegedAction() {
+         AccessController.doPrivileged(new PrivilegedAction<Object>() {
              public Object run() {
                  field.setAccessible(true);
                  return null;
              }
          });
      }
-
-
 }

Added: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/StaticUsernamePasswordCallbackHandler.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/StaticUsernamePasswordCallbackHandler.java?view=auto&rev=522150
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/StaticUsernamePasswordCallbackHandler.java (added)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/StaticUsernamePasswordCallbackHandler.java Sat Mar 24 20:26:50 2007
@@ -0,0 +1,63 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.openejb.client;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.NameCallback;
+import java.io.IOException;
+
+/**
+ * A JASS username password CallbackHandler.
+ */
+public class StaticUsernamePasswordCallbackHandler implements CallbackHandler {
+    private static String username;
+    private static String password;
+
+
+    public static void setUsername(String username) {
+        StaticUsernamePasswordCallbackHandler.username = username;
+    }
+
+    public static void setPassword(String password) {
+        StaticUsernamePasswordCallbackHandler.password = password;
+    }
+
+    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+        for (int i = 0; i < callbacks.length; i++) {
+            Callback callback = callbacks[i];
+            if (callback instanceof PasswordCallback) {
+                PasswordCallback passwordCallback = (PasswordCallback) callback;
+                if (password == null) {
+                    passwordCallback.setPassword(null);
+                } else {
+                    passwordCallback.setPassword(password.toCharArray());
+                }
+            } else if (callback instanceof NameCallback) {
+                NameCallback nameCallback = (NameCallback) callback;
+                if (username == null) {
+                    nameCallback.setName(null);
+                } else {
+                    nameCallback.setName(username);
+                }
+            }
+        }
+    }
+}

Added: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/UsernamePasswordCallbackHandler.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/UsernamePasswordCallbackHandler.java?view=auto&rev=522150
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/UsernamePasswordCallbackHandler.java (added)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/UsernamePasswordCallbackHandler.java Sat Mar 24 20:26:50 2007
@@ -0,0 +1,60 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.openejb.client;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.NameCallback;
+import java.io.IOException;
+
+/**
+ * A JASS username password CallbackHandler.
+ */
+public class UsernamePasswordCallbackHandler implements CallbackHandler {
+
+    private final String username;
+    private final String password;
+
+    public UsernamePasswordCallbackHandler(String username, String password) {
+        this.username = username;
+        this.password = password;
+    }
+
+    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+        for (int i = 0; i < callbacks.length; i++) {
+            Callback callback = callbacks[i];
+            if (callback instanceof PasswordCallback) {
+                PasswordCallback passwordCallback = (PasswordCallback) callback;
+                if (password == null) {
+                    passwordCallback.setPassword(null);
+                } else {
+                    passwordCallback.setPassword(password.toCharArray());
+                }
+            } else if (callback instanceof NameCallback) {
+                NameCallback nameCallback = (NameCallback) callback;
+                if (username == null) {
+                    nameCallback.setName(null);
+                } else {
+                    nameCallback.setName(username);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file

Added: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/client.login.conf
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/client.login.conf?view=auto&rev=522150
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/client.login.conf (added)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/client.login.conf Sat Mar 24 20:26:50 2007
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+ClientLogin {
+    org.apache.openejb.client.ClientLoginModule required
+        debug=true
+        openejb.server.uri="oejb://localhost:4201";
+};

Added: incubator/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/ClientLoginTest.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/ClientLoginTest.java?view=auto&rev=522150
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/ClientLoginTest.java (added)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/ClientLoginTest.java Sat Mar 24 20:26:50 2007
@@ -0,0 +1,73 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.openejb.client;
+
+import junit.framework.TestCase;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+public class ClientLoginTest extends TestCase {
+    protected void setUp() throws Exception {
+        super.setUp();
+        LoginTestUtil.initialize();
+    }
+
+    public void testAuthGranted() throws LoginException {
+        // setup the server response
+        LoginTestUtil.setAuthGranted();
+
+        // attempt a login
+        LoginContext context = new LoginContext("ClientLogin", new UsernamePasswordCallbackHandler("jonathan", "secret"));
+        context.login();
+
+        // Verify stored server request
+        assertTrue("serverRequest should be an instance of AuthenticationRequest", LoginTestUtil.serverRequest instanceof AuthenticationRequest);
+        AuthenticationRequest authenticationRequest = (AuthenticationRequest) LoginTestUtil.serverRequest;
+        assertEquals("jonathan", authenticationRequest.getPrincipal());
+        assertEquals("secret", authenticationRequest.getCredentials());
+
+        // get the subject
+        Subject subject = context.getSubject();
+
+        // verify subject
+        assertEquals("Should have one principal", 1, subject.getPrincipals().size());
+        assertEquals("Should have one user principal", 1, subject.getPrincipals(ClientIdentityPrincipal.class).size());
+        ClientIdentityPrincipal principal = subject.getPrincipals(ClientIdentityPrincipal.class).iterator().next();
+        assertEquals("jonathan", principal.getName());
+        assertEquals("SecretIdentity", principal.getClientIdentity());
+
+        context.logout();
+
+        assertEquals("Should have zero principals", 0, subject.getPrincipals().size());
+    }
+
+    public void testAuthDenied() throws Exception {
+        LoginTestUtil.setAuthDenied();
+
+        LoginContext context = new LoginContext("ClientLogin", new UsernamePasswordCallbackHandler("nobody", "secret"));
+        try {
+            context.login();
+            fail("Should have thrown a FailedLoginException");
+        } catch (FailedLoginException doNothing) {
+        }
+
+    }
+}

Added: incubator/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/LoginTestUtil.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/LoginTestUtil.java?view=auto&rev=522150
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/LoginTestUtil.java (added)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/LoginTestUtil.java Sat Mar 24 20:26:50 2007
@@ -0,0 +1,57 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.openejb.client;
+
+import java.rmi.RemoteException;
+import java.net.URL;
+
+public class LoginTestUtil {
+    public static Request serverRequest;
+    public static AuthenticationResponse serverResponse;
+
+    public static void initialize() throws Exception {
+        String path = System.getProperty("java.security.auth.login.config");
+        if (path == null) {
+            URL resource = ClientLoginTest.class.getClassLoader().getResource("client.login.conf");
+            if (resource != null) {
+                path = resource.getFile();
+                System.setProperty("java.security.auth.login.config", path);
+            }
+        }
+        System.out.println("Path to login config: " + path);
+
+        Client.setClient(new Client() {
+            protected Response processRequest(Request req, Response res, ServerMetaData server) throws RemoteException {
+                serverRequest = req;
+                return serverResponse;
+            }
+        });
+    }
+
+    static void setAuthGranted() {
+        serverResponse = new AuthenticationResponse();
+        serverResponse.setIdentity(new ClientMetaData("SecretIdentity"));
+        serverResponse.setResponseCode(ResponseCodes.AUTH_GRANTED);
+    }
+
+    static void setAuthDenied() {
+        // setup the server response
+        serverResponse = new AuthenticationResponse();
+        serverResponse.setResponseCode(ResponseCodes.AUTH_DENIED);
+    }
+}

Added: incubator/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/MainTest.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/MainTest.java?view=auto&rev=522150
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/MainTest.java (added)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/MainTest.java Sat Mar 24 20:26:50 2007
@@ -0,0 +1,248 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.openejb.client;
+
+import junit.framework.TestCase;
+
+import javax.naming.spi.InitialContextFactory;
+import javax.naming.spi.NamingManager;
+import javax.naming.spi.InitialContextFactoryBuilder;
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.Binding;
+import javax.naming.NameParser;
+import javax.naming.NameNotFoundException;
+import javax.security.auth.Subject;
+import javax.security.auth.login.FailedLoginException;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.TreeMap;
+import java.security.AccessController;
+
+public class MainTest extends TestCase {
+    static {
+        try {
+            NamingManager.setInitialContextFactoryBuilder(new MockContextFactoryBuilder());
+        } catch (NamingException e) {
+        }
+    }
+
+    public static Map<String,Object> jndi = new TreeMap<String,Object>();
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        LoginTestUtil.initialize();
+
+        jndi.clear();
+        jndi.put("java:comp/path", "fake.jar");
+        jndi.put("java:comp/injections", new InjectionMetaData());
+    }
+
+    public void testSecureMain() throws Exception {
+        jndi.put("java:comp/callbackHandler", StaticUsernamePasswordCallbackHandler.class.getName());
+
+        StaticUsernamePasswordCallbackHandler.setUsername("victoria");
+        StaticUsernamePasswordCallbackHandler.setPassword("secret");
+        LoginTestUtil.setAuthGranted();
+        
+        jndi.put("java:comp/mainClass", SecureMain.class.getName());
+        Main.main(new String[0]);
+    }
+
+    public void testSecureMainFailed() throws Exception {
+        jndi.put("java:comp/callbackHandler", StaticUsernamePasswordCallbackHandler.class.getName());
+
+        StaticUsernamePasswordCallbackHandler.setUsername("victoria");
+        StaticUsernamePasswordCallbackHandler.setPassword("secret");
+        LoginTestUtil.setAuthDenied();
+
+        jndi.put("java:comp/mainClass", SecureMain.class.getName());
+        try {
+            Main.main(new String[0]);
+            fail("Expected main method to throw FailedLoginException");
+        } catch (FailedLoginException expected) {
+        }
+    }
+
+    public static class SecureMain {
+        public static void main(String[] args) {
+            Subject subject = Subject.getSubject(AccessController.getContext());
+
+            // verify subject
+            assertEquals("Should have one principal", 1, subject.getPrincipals().size());
+            assertEquals("Should have one user principal", 1, subject.getPrincipals(ClientIdentityPrincipal.class).size());
+            ClientIdentityPrincipal principal = subject.getPrincipals(ClientIdentityPrincipal.class).iterator().next();
+            assertEquals("victoria", principal.getName());
+            assertEquals("SecretIdentity", principal.getClientIdentity());
+        }
+    }
+
+    public void testNormalMain() throws Exception {
+        jndi.put("java:comp/mainClass", NormalMain.class.getName());
+        Main.main(new String[0]);
+    }
+
+    public static class NormalMain {
+        public static void main(String[] args) {
+            Subject subject = Subject.getSubject(AccessController.getContext());
+
+            assertNull("subject is not null", subject);
+        }
+    }
+
+
+    //
+    // Ignore these
+    //
+    public static class MockContextFactoryBuilder implements InitialContextFactoryBuilder {
+        public InitialContextFactory createInitialContextFactory(Hashtable<?, ?> environment) throws NamingException {
+            return new MockContextFactory();
+        }
+    }
+
+    public static class MockContextFactory implements InitialContextFactory {
+        public Context getInitialContext(Hashtable<?, ?> environment) throws NamingException {
+            return new MockContext();
+        }
+    }
+
+    public static class MockContext implements Context {
+        public Object lookup(String name) throws NamingException {
+            Object value = jndi.get(name);
+            if (value == null) {
+                throw new NameNotFoundException(name);
+            }
+            return value;
+        }
+
+        public Object lookup(Name name) throws NamingException {
+            return null;
+        }
+
+        public void bind(Name name, Object obj) throws NamingException {
+
+        }
+
+        public void bind(String name, Object obj) throws NamingException {
+
+        }
+
+        public void rebind(Name name, Object obj) throws NamingException {
+
+        }
+
+        public void rebind(String name, Object obj) throws NamingException {
+
+        }
+
+        public void unbind(Name name) throws NamingException {
+
+        }
+
+        public void unbind(String name) throws NamingException {
+
+        }
+
+        public void rename(Name oldName, Name newName) throws NamingException {
+
+        }
+
+        public void rename(String oldName, String newName) throws NamingException {
+
+        }
+
+        public NamingEnumeration<NameClassPair> list(Name name) throws NamingException {
+            return null;
+        }
+
+        public NamingEnumeration<NameClassPair> list(String name) throws NamingException {
+            return null;
+        }
+
+        public NamingEnumeration<Binding> listBindings(Name name) throws NamingException {
+            return null;
+        }
+
+        public NamingEnumeration<Binding> listBindings(String name) throws NamingException {
+            return null;
+        }
+
+        public void destroySubcontext(Name name) throws NamingException {
+
+        }
+
+        public void destroySubcontext(String name) throws NamingException {
+
+        }
+
+        public Context createSubcontext(Name name) throws NamingException {
+            return null;
+        }
+
+        public Context createSubcontext(String name) throws NamingException {
+            return null;
+        }
+
+        public Object lookupLink(Name name) throws NamingException {
+            return null;
+        }
+
+        public Object lookupLink(String name) throws NamingException {
+            return null;
+        }
+
+        public NameParser getNameParser(Name name) throws NamingException {
+            return null;
+        }
+
+        public NameParser getNameParser(String name) throws NamingException {
+            return null;
+        }
+
+        public Name composeName(Name name, Name prefix) throws NamingException {
+            return null;
+        }
+
+        public String composeName(String name, String prefix) throws NamingException {
+            return null;
+        }
+
+        public Object addToEnvironment(String propName, Object propVal) throws NamingException {
+            return null;
+        }
+
+        public Object removeFromEnvironment(String propName) throws NamingException {
+            return null;
+        }
+
+        public Hashtable<?, ?> getEnvironment() throws NamingException {
+            return null;
+        }
+
+        public void close() throws NamingException {
+
+        }
+
+        public String getNameInNamespace() throws NamingException {
+            return null;
+        }
+    }
+}