You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@harmony.apache.org by le...@apache.org on 2007/10/10 07:32:04 UTC

svn commit: r583360 - in /harmony/enhanced/classlib/trunk/modules/auth: META-INF/ make/ src/main/java/common/org/apache/harmony/auth/module/ src/test/java/common/org/apache/harmony/auth/tests/module/

Author: leoli
Date: Tue Oct  9 22:32:03 2007
New Revision: 583360

URL: http://svn.apache.org/viewvc?rev=583360&view=rev
Log:
Apply patch for HARMONY-4719([classlib][auth]Harmony lacks support for JndiLoginModule).

Added:
    harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/DebugUtil.java   (with props)
    harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/JndiLoginModule.java   (with props)
    harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/SharedStateManager.java   (with props)
    harmony/enhanced/classlib/trunk/modules/auth/src/test/java/common/org/apache/harmony/auth/tests/module/JndiLoginModuleTest.java   (with props)
Modified:
    harmony/enhanced/classlib/trunk/modules/auth/META-INF/MANIFEST.MF
    harmony/enhanced/classlib/trunk/modules/auth/make/exclude.common

Modified: harmony/enhanced/classlib/trunk/modules/auth/META-INF/MANIFEST.MF
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/auth/META-INF/MANIFEST.MF?rev=583360&r1=583359&r2=583360&view=diff
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/auth/META-INF/MANIFEST.MF (original)
+++ harmony/enhanced/classlib/trunk/modules/auth/META-INF/MANIFEST.MF Tue Oct  9 22:32:03 2007
@@ -24,6 +24,8 @@
  java.util.zip,
  javax.crypto,
  javax.crypto.spec,
+ javax.naming,
+ javax.naming.directory,
  org.apache.harmony.kernel.vm,
  org.apache.harmony.security,
  org.apache.harmony.security.asn1,
@@ -31,8 +33,8 @@
  org.apache.harmony.security.utils,
  org.apache.harmony.security.x501,
  org.apache.harmony.testframework.serialization;hy_usage=test;resolution:=optional,
- tests.support.resource;hy_usage=test;resolution:=optional,
- tests.support;hy_usage=test;resolution:=optional
+ tests.support;hy_usage=test;resolution:=optional,
+ tests.support.resource;hy_usage=test;resolution:=optional
 Export-Package: javax.security.auth,
  javax.security.auth.callback,
  javax.security.auth.kerberos,

Modified: harmony/enhanced/classlib/trunk/modules/auth/make/exclude.common
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/auth/make/exclude.common?rev=583360&r1=583359&r2=583360&view=diff
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/auth/make/exclude.common (original)
+++ harmony/enhanced/classlib/trunk/modules/auth/make/exclude.common Tue Oct  9 22:32:03 2007
@@ -1,2 +1,4 @@
 # excluded to avoid random failures 
 org/apache/harmony/auth/tests/module/Krb5LoginModuleTest.java
+# jndi provider does not exist now
+org/apache/harmony/auth/tests/module/JndiLoginModuleTest.java
\ No newline at end of file

Added: harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/DebugUtil.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/DebugUtil.java?rev=583360&view=auto
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/DebugUtil.java (added)
+++ harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/DebugUtil.java Tue Oct  9 22:32:03 2007
@@ -0,0 +1,52 @@
+/*
+ *  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.harmony.auth.module;
+
+import java.util.Map;
+
+public class DebugUtil {
+	
+	private boolean debugFlag = false;
+	
+	private StringBuilder loginDebugInfo = null;
+	
+	public DebugUtil(final Map<String,?> options){
+		processDebugSwitch(options);
+	}
+	
+	protected void recordDebugInfo(String debugInfo){
+        if(debugFlag)
+            loginDebugInfo.append(debugInfo);
+    }
+    
+    protected void printAndClearDebugInfo(){
+        if(debugFlag)
+        {
+            System.out.print(loginDebugInfo.toString());
+            loginDebugInfo = new StringBuilder();
+        }
+    }
+    
+    private void processDebugSwitch(final Map<String,?> options){
+    	Object optionValue = options.get("debug");
+    	if (optionValue != null && optionValue.equals("true")) {
+            debugFlag = true;
+            loginDebugInfo = new StringBuilder();
+        }
+    }
+}

Propchange: harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/DebugUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/JndiLoginModule.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/JndiLoginModule.java?rev=583360&view=auto
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/JndiLoginModule.java (added)
+++ harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/JndiLoginModule.java Tue Oct  9 22:32:03 2007
@@ -0,0 +1,322 @@
+/*
+ *  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.harmony.auth.module;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
+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.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+import org.apache.harmony.auth.UnixNumericGroupPrincipal;
+import org.apache.harmony.auth.UnixNumericUserPrincipal;
+import org.apache.harmony.auth.UnixPrincipal;
+
+public class JndiLoginModule extends SharedStateManager implements LoginModule {
+
+    public final String USER_PROVIDER = "group.provider.url";
+
+    public final String GROUP_PROVIDER = "user.provider.url";
+
+    //harmony lacks jndi provider
+    private final String JNDI_FACTORY = "";
+
+    private LoginModuleUtils.LoginModuleStatus status = new LoginModuleUtils.LoginModuleStatus();
+
+    private Subject subject;
+
+    private CallbackHandler callbackHandler;
+
+    private Map<String, ?> options;
+
+    private String jndiUserProvider;
+
+    private String jndiGroupProvider;
+
+    private String userID;
+
+    private char[] userPassword;
+
+    private Long uidNumber;
+
+    private Long gidNumber;
+
+    private UnixPrincipal unixPrincipal;
+
+    private UnixNumericUserPrincipal unixNumericUserPrincipal;
+
+    private Set<UnixNumericGroupPrincipal> unixNumericGroupPrincipals;
+
+    public boolean abort() throws LoginException {
+        LoginModuleUtils.ACTION action = status.checkAbout();
+        if (action.equals(LoginModuleUtils.ACTION.no_action)) {
+            if (status.isLoggined()) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+        clear();
+        debugUtil.recordDebugInfo("[JndiLoginModule] aborted authentication failed\n");
+        if(status.isCommitted()){
+        	debugUtil.recordDebugInfo("[JndiLoginModule]: logged out Subject\n");
+        }
+        debugUtil.printAndClearDebugInfo();
+        status.logouted();
+        return true;
+    }
+
+    public boolean commit() throws LoginException {
+        LoginModuleUtils.ACTION action = status.checkCommit();
+        switch (action) {
+        case no_action:
+            return true;
+        case logout:
+            clear();
+            throw new LoginException("Fail to login");
+        default:
+            if (subject.isReadOnly()) {
+                clear();
+                throw new LoginException("Subject is readonly.");
+            }
+            subject.getPrincipals().add(unixPrincipal);
+            debugUtil.recordDebugInfo("[JndiLoginModule] added UnixPrincipal to Subject\n");
+            subject.getPrincipals().add(unixNumericUserPrincipal);
+            debugUtil.recordDebugInfo("[JndiLoginModule] added UnixNumericUserPrincipal to Subject\n");
+            for (Principal principal : unixNumericGroupPrincipals) {
+                subject.getPrincipals().add(principal);
+            }
+            debugUtil.recordDebugInfo("[JndiLoginModule] added UnixNumericGroupPrincipal(s) to Subject\n");
+            debugUtil.printAndClearDebugInfo();
+            status.committed();
+            clearPass();
+            return true;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void initialize(Subject subject, CallbackHandler callbackHandler,
+            Map<String, ?> sharedState, Map<String, ?> options) {
+        this.subject = subject;
+        this.callbackHandler = callbackHandler;
+        if (null == options) {
+            throw new NullPointerException();
+        }
+        this.options = options;
+        debugUtil = new DebugUtil(options);
+        prepareSharedState(sharedState, options);
+        status.initialized();
+    }
+
+    public boolean login() throws LoginException {
+        LoginModuleUtils.ACTION action = status.checkLogin();
+        if (action.equals(LoginModuleUtils.ACTION.no_action)) {
+            return true;
+        }
+        getJndiParameters();
+        loginWithSharedState();
+        debugUtil.recordDebugInfo("[JndiLoginModule] user: '"+ userID + "' has UID: " + uidNumber + "\n");
+        debugUtil.recordDebugInfo("[JndiLoginModule] user: '"+ userID + "' has GID: " + gidNumber + "\n");
+        getPrinclpalsFromJndi();
+        debugUtil.printAndClearDebugInfo();
+        status.logined();
+        return true;
+    }
+
+    public boolean logout() throws LoginException {
+        LoginModuleUtils.ACTION action = status.checkLogout();
+        if (action.equals(LoginModuleUtils.ACTION.no_action)) {
+            return true;
+        }
+        clear();
+        debugUtil.recordDebugInfo("[JndiLoginModule] logged out Subject\n");
+        debugUtil.printAndClearDebugInfo();
+        status.logouted();
+        return true;
+    }
+
+    private void getJndiParameters() throws LoginException {
+        jndiUserProvider = (String) options.get("user.provider.url");
+        jndiGroupProvider = (String) options.get("group.provider.url");
+        if (jndiUserProvider == null) {
+            throw new LoginException("Unable to locate JNDI user provider");
+        }
+        if (jndiGroupProvider == null) {
+            throw new LoginException("Unable to locate JNDI group provider");
+        }
+        debugUtil.recordDebugInfo("[JndiLoginModule] user provider: " + jndiUserProvider + "\n"
+                +"[JndiLoginModule] group provider: " + jndiGroupProvider
+                + "\n");
+    }
+
+    //not accomplished yet
+    protected boolean mainAuthenticationProcess() throws LoginException {
+
+        //check group provider
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
+        env.put(Context.PROVIDER_URL, jndiGroupProvider);
+        try {
+            DirContext context = new InitialDirContext(env);
+            context.close();
+        } catch (NamingException e) {
+            throw new LoginException(e.toString());
+        }
+        //check user
+        env = new Hashtable<String, String>();
+        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
+        env.put(Context.PROVIDER_URL, jndiUserProvider);
+        Attribute passwordAttr;
+        Attribute uidNumberAttr;
+        Attribute gidNumberAttr;
+        String jndiUserPassword = "";
+        try {
+            DirContext context = new InitialDirContext(env);
+            SearchControls constraints = new SearchControls();
+            constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
+            Attributes attrs = new BasicAttributes("uid", userID);
+            NamingEnumeration ne = context.search("", attrs);
+            String[] attrIds = new String[] { "userPassword", "uidNumber",
+                    "gidNumber" };
+            if (ne.hasMore()) {
+                NameClassPair item = (NameClassPair) ne.next();
+                Attributes userAttrs = context.getAttributes(item.getName(),
+                        attrIds);
+                passwordAttr = userAttrs.get("userPassword");
+                if (passwordAttr == null) {
+                    throw new LoginException("Cannot get user password");
+                }
+                jndiUserPassword = new String((byte[])passwordAttr.get());
+                if (!jndiUserPassword.equals(crypto(new String(userPassword)))) {
+                    return false;
+                }
+
+                uidNumberAttr = userAttrs.get("uidNumber");
+                if (uidNumberAttr == null) {
+                    throw new LoginException("Cannot get uidNumber information");
+                }
+                uidNumber = Long.valueOf((String) uidNumberAttr.get());
+
+                gidNumberAttr = userAttrs.get("gidNumber");
+                if (gidNumberAttr == null) {
+                    throw new LoginException("Cannot get gidNumber information");
+                }
+                gidNumber = Long.valueOf((String) gidNumberAttr.get());
+            }
+        } catch (NamingException e) {
+            throw new LoginException(e.toString());
+        }
+
+        return true;
+    }
+
+    private void getPrinclpalsFromJndi() {
+        unixPrincipal = new UnixPrincipal(userID);
+        unixNumericUserPrincipal = new UnixNumericUserPrincipal(uidNumber);
+        unixNumericGroupPrincipals = new HashSet<UnixNumericGroupPrincipal>();
+        unixNumericGroupPrincipals.add(new UnixNumericGroupPrincipal(gidNumber,
+                true));
+    }
+
+    protected void getUserIdentityFromCallbackHandler() throws LoginException {
+        
+        if (callbackHandler == null) {
+            throw new LoginException("no CallbackHandler available");
+        }
+        ArrayList<Callback> callbacks = new ArrayList<Callback>();
+        NameCallback jndiNameCallback = new NameCallback("User ID");
+        callbacks.add(jndiNameCallback);
+        PasswordCallback jndiPasswordCallback = new PasswordCallback(
+                "User Password", false);
+        callbacks.add(jndiPasswordCallback);
+        try {
+            callbackHandler.handle(callbacks.toArray(new Callback[callbacks
+                    .size()]));
+        } catch (Exception e) {
+            throw new LoginException(e.toString());
+        }
+        userID = jndiNameCallback.getName();
+        userPassword = jndiPasswordCallback.getPassword();
+    }
+    
+    private void clear() throws LoginException {
+        LoginModuleUtils.clearPassword(userPassword);
+        userPassword = null;
+        if (unixPrincipal != null) {
+            subject.getPrincipals().remove(unixPrincipal);
+            unixPrincipal = null;
+        }
+
+        if (unixNumericUserPrincipal != null) {
+            subject.getPrincipals().remove(unixNumericUserPrincipal);
+            unixNumericUserPrincipal = null;
+        }
+
+        if (unixNumericGroupPrincipals != null) {
+            for (UnixNumericGroupPrincipal ungp : unixNumericGroupPrincipals)
+                subject.getPrincipals().remove(ungp);
+            unixNumericGroupPrincipals.clear();
+            unixNumericGroupPrincipals = null;
+        }
+        status.logouted();
+    }
+
+    private String crypto(String userPassword) {
+        //need to implement a crypto algorithm
+        return userPassword;
+    }
+    
+    protected void setUserName(String userName){
+    	this.userID = userName;
+    }
+    
+    protected void setUserPassword(char[] userPassword){
+    	this.userPassword = userPassword;
+    }
+    
+    protected String getUserName(){
+    	return userID;
+    }
+    
+    protected char[] getUserPassword(){
+    	return userPassword;
+    }
+    
+    protected String getModuleName(){
+    	return "JndiLoginModule"; 
+    }
+}

Propchange: harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/JndiLoginModule.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/SharedStateManager.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/SharedStateManager.java?rev=583360&view=auto
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/SharedStateManager.java (added)
+++ harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/SharedStateManager.java Tue Oct  9 22:32:03 2007
@@ -0,0 +1,185 @@
+/*
+ *  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.harmony.auth.module;
+
+import java.util.Map;
+
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginException;
+
+abstract public class SharedStateManager {
+
+    private boolean useFirstPass = false;
+
+    private boolean tryFirstPass = false;
+
+    private boolean storePass = false;
+
+    private boolean clearPass = false;
+
+    protected DebugUtil debugUtil;
+
+    protected Map<String, Object> sharedState;
+
+    @SuppressWarnings("unchecked")
+    protected void prepareSharedState(Map<String, ?> sharedState,
+            final Map<String, ?> options) {
+        this.sharedState = (Map<String, Object>) sharedState;
+
+        useFirstPass = false;
+        tryFirstPass = false;
+        storePass = false;
+        clearPass = false;
+        Object optionValue = null;
+
+        optionValue = options.get("useFirstPass");
+        if (optionValue != null && optionValue.equals("true")) {
+            useFirstPass = true;
+        }
+
+        optionValue = options.get("tryFirstPass");
+        if (optionValue != null && optionValue.equals("true")) {
+            tryFirstPass = true;
+            useFirstPass = false;
+        }
+
+        optionValue = options.get("storePass");
+        if (optionValue != null && optionValue.equals("true")) {
+            storePass = true;
+        }
+
+        optionValue = options.get("clearPass");
+        if (optionValue != null && optionValue.equals("true")) {
+            clearPass = true;
+            storePass = false;
+        }
+    }
+
+    protected void loginWithSharedState() throws LoginException {
+        if (useFirstPass || tryFirstPass) {
+            getUserIdentityFromSharedStatus();
+        } else {
+            getUserIdentityFromCallbackHandler();
+        }
+        boolean passAuth = false;
+        passAuth = mainAuthenticationProcess();
+        if (!passAuth) {
+            if (tryFirstPass) {
+                debugUtil.recordDebugInfo("["
+                        + getModuleName()
+                        + "] tryFirstPass failed with:"
+                        + new FailedLoginException("Login incorrect")
+                                .toString() + "\n");
+                getUserIdentityFromCallbackHandler();
+                passAuth = mainAuthenticationProcess();
+                if (!passAuth) {
+                    debugUtil.recordDebugInfo("[" + getModuleName()
+                            + "] regular authentication failed\n");
+                    debugUtil.printAndClearDebugInfo();
+                    throw new FailedLoginException("Login incorrect");
+                } else {
+                    debugUtil.recordDebugInfo("[" + getModuleName()
+                            + "] regular authentication succeeded\n");
+                }
+            } else {
+                if (useFirstPass) {
+                    debugUtil.recordDebugInfo("["
+                            + getModuleName()
+                            + "] useFirstPass failed with:"
+                            + new FailedLoginException("Login incorrect")
+                                    .toString() + "\n");
+                } else {
+                    debugUtil.recordDebugInfo("[" + getModuleName()
+                            + "] regular authentication failed\n");
+                }
+                debugUtil.printAndClearDebugInfo();
+                throw new FailedLoginException("Login incorrect");
+            }
+        } else {
+            if (tryFirstPass) {
+                debugUtil.recordDebugInfo("[" + getModuleName()
+                        + "] tryFirstPass ");
+            } else if (useFirstPass) {
+                debugUtil.recordDebugInfo("[" + getModuleName()
+                        + "] useFirstPass ");
+            } else {
+                debugUtil.recordDebugInfo("[" + getModuleName()
+                        + "] regular authentication ");
+            }
+            debugUtil.recordDebugInfo("succeeded\n");
+        }
+        storePass();
+    }
+
+    private void getUserIdentityFromSharedStatus() throws LoginException {
+        if (sharedState == null)
+            throw new LoginException("No shared status");
+        String userName = (String) sharedState
+                .get("javax.security.auth.login.name");
+        char[] userPassword = (char[]) sharedState
+                .get("javax.security.auth.login.password");
+        if (userName == null || userPassword == null) {
+            throw new LoginException(
+                    "Cannot get user ID or user password from shared state");
+        }
+        setUserName(userName);
+        setUserPassword(userPassword);
+    }
+
+    protected void storePass() throws LoginException {
+        if (storePass) {
+            if (sharedState == null) {
+                throw new LoginException("No Shared State");
+            }
+            if (sharedState.get("javax.security.auth.login.name") == null) {
+                sharedState
+                        .put("javax.security.auth.login.name", getUserName());
+            }
+            if (sharedState.get("javax.security.auth.login.password") == null) {
+                sharedState.put("javax.security.auth.login.password",
+                        getUserPassword());
+            }
+        }
+    }
+
+    protected void clearPass() throws LoginException {
+        if (clearPass) {
+            if (sharedState == null) {
+                throw new LoginException("No Shared State");
+            }
+            sharedState.remove("javax.security.auth.login.name");
+            sharedState.remove("javax.security.auth.login.password");
+        }
+    }
+
+    abstract protected boolean mainAuthenticationProcess()
+            throws LoginException;
+
+    abstract protected void getUserIdentityFromCallbackHandler()
+            throws LoginException;
+
+    abstract protected void setUserName(String userName);
+
+    abstract protected String getUserName();
+
+    abstract protected void setUserPassword(char[] userPassword);
+
+    abstract protected char[] getUserPassword();
+
+    abstract protected String getModuleName();
+}

Propchange: harmony/enhanced/classlib/trunk/modules/auth/src/main/java/common/org/apache/harmony/auth/module/SharedStateManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: harmony/enhanced/classlib/trunk/modules/auth/src/test/java/common/org/apache/harmony/auth/tests/module/JndiLoginModuleTest.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/auth/src/test/java/common/org/apache/harmony/auth/tests/module/JndiLoginModuleTest.java?rev=583360&view=auto
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/auth/src/test/java/common/org/apache/harmony/auth/tests/module/JndiLoginModuleTest.java (added)
+++ harmony/enhanced/classlib/trunk/modules/auth/src/test/java/common/org/apache/harmony/auth/tests/module/JndiLoginModuleTest.java Tue Oct  9 22:32:03 2007
@@ -0,0 +1,329 @@
+/*
+ *  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.harmony.auth.tests.module;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.Set;
+
+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 junit.framework.TestCase;
+
+import org.apache.harmony.auth.module.JndiLoginModule;
+
+public class JndiLoginModuleTest extends TestCase {
+
+    // module options
+    private HashMap<String, String> options = new HashMap<String, String>();
+
+    private final String LDAP_SERVER_LOCATION = "";
+
+    private final String USER_PROVIDER_URL = "ldap://" + LDAP_SERVER_LOCATION
+            + ":389/ou=People,o=JNDITutorial,dc=my-domain,dc=com";
+
+    private final String GROUP_PROVIDER_URL = "ldap://" + LDAP_SERVER_LOCATION
+            + ":389/ou=Groups,o=JNDITutorial,dc=my-domain,dc=com";
+
+    protected void setUp() throws Exception {
+        options.put("user.provider.url", USER_PROVIDER_URL);
+        options.put("group.provider.url", GROUP_PROVIDER_URL);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        options.clear();
+    }
+
+    /**
+     * Test method for
+     * {@link org.apache.harmony.auth.module.JndiLoginModule#abort()}.
+     */
+    public void test_abort() throws LoginException {
+        JndiLoginModule jlm = new JndiLoginModule();
+        try {
+            assertFalse("Should return false if login failed or no login", jlm
+                    .abort());
+        } catch (LoginException e) {
+            fail("Abort failed");
+        }
+        Subject subject = new Subject();
+        subject.setReadOnly();
+        jlm.initialize(subject, null, null, options);
+        try {
+            assertFalse("Should return false if login failed or no login", jlm
+                    .abort());
+        } catch (Exception e) {
+            fail("Not any exception here");
+        }
+        subject = new Subject();
+        jlm.initialize(subject, new FaultCallbackHandler(), null, options);
+        try {
+            jlm.login();
+            fail("login should fail");
+        } catch (LoginException e) {
+            assertFalse("Should return false because of login failure", jlm
+                    .abort());
+        }
+        subject = new Subject();
+        jlm.initialize(subject, new MockCallbackHandler(), null, options);
+        jlm.login();
+        assertTrue("Should return true if login was successful", jlm.abort());
+    }
+
+    /**
+     * Test method for
+     * {@link org.apache.harmony.auth.module.JndiLoginModule#commit()}.
+     */
+    public void test_commit() {
+        JndiLoginModule module = new JndiLoginModule();
+        Subject subject = new Subject();
+        module.initialize(subject, new MockCallbackHandler(), null, options);
+        try {
+            assertTrue("Login should be successful", module.login());
+            module.commit();
+        } catch (LoginException e) {
+            fail("Login shouldn't fail");
+        }
+        Set<Principal> principals = subject.getPrincipals();
+        assertFalse("Should get at least one principal", principals.isEmpty());
+        subject = new Subject();
+        subject.setReadOnly();
+        module.initialize(subject, new MockCallbackHandler(), null, options);
+        try {
+            assertFalse("Commit shouldn't be successful", module.commit());
+            fail("Should throw LoginException here because of trying to clear read-only subject");
+        } catch (LoginException e) {
+            // expected LoginException here
+        }
+    }
+
+    /**
+     * Test method for
+     * {@link org.apache.harmony.auth.module.JndiLoginModule#initialize(javax.security.auth.Subject, javax.security.auth.callback.CallbackHandler, java.util.Map, java.util.Map)}.
+     */
+    public void test_initialize() {
+        JndiLoginModule module = new JndiLoginModule();
+        try {
+            module.initialize(null, null, null, null);
+            fail("Should throw NullPointerException here.");
+        } catch (NullPointerException e) {
+            // expected NullPointerException
+        }
+    }
+
+    /**
+     * Test method for
+     * {@link org.apache.harmony.auth.module.JndiLoginModule#login()}.
+     */
+    public void test_login() {
+        JndiLoginModule module = new JndiLoginModule();
+        HashMap<String, String> emptyOptions = new HashMap<String, String>();
+        module.initialize(null, new MockCallbackHandler(), null, emptyOptions);
+        try {
+            module.login();
+            fail("Should throw LoginException here.");
+        } catch (LoginException e) {
+            // expected LoginException
+        }
+
+        Subject subject = new Subject();
+        module.initialize(subject, new MockCallbackHandler(), null, options);
+        try {
+            assertTrue("Login should be successful", module.login());
+        } catch (LoginException e) {
+            fail("Login shouldn't fail");
+        }
+        module.initialize(subject, new FaultCallbackHandler(), null, options);
+        try {
+            assertFalse("Login shouldn't be successful", module.login());
+            fail("Login should fail");
+        } catch (LoginException e) {
+            // expected Loginexception here
+        }
+    }
+
+    /**
+     * Test method for
+     * {@link org.apache.harmony.auth.module.JndiLoginModule#logout()}.
+     */
+    public void test_logout() {
+        JndiLoginModule module = new JndiLoginModule();
+        Subject subject = new Subject();
+        module.initialize(subject, new MockCallbackHandler(), null, options);
+        try {
+            assertTrue("Login should be successful", module.login());
+            module.commit();
+        } catch (LoginException e) {
+            fail("Login shouldn't fail");
+        }
+        Set<Principal> principals = subject.getPrincipals();
+        assertFalse("Should get at least one principal", principals.isEmpty());
+        try {
+            assertTrue("Should be true", module.logout());
+        } catch (LoginException e) {
+            fail("Logout failed");
+        }
+        principals = subject.getPrincipals();
+        assertTrue("Principals should be cleared", principals.isEmpty());
+    }
+
+    public void test_optionsAndSharedStatus() throws LoginException {
+        options.put("debug", "true");
+        options.put("useFirstPass", "true");
+        HashMap<String, Object> status = new HashMap<String, Object>();
+        status.put("javax.security.auth.login.name", "leo");
+        status.put("javax.security.auth.login.password", "faultPass"
+                .toCharArray());
+        JndiLoginModule module = new JndiLoginModule();
+        Subject subject = new Subject();
+        module.initialize(subject, new MockCallbackHandler(), status, options);
+        try {
+            module.login();
+            fail("Should be failed for using password from shared state");
+        } catch (LoginException e) {
+            // expected LoginException here
+        }
+
+        options.remove("useFirstPass");
+        options.put("tryFirstPass", "true");
+        module.initialize(subject, new MockCallbackHandler(), status, options);
+        try {
+            module.login();
+            module.commit();
+        } catch (LoginException e) {
+            fail("Login should be failed");
+        } finally {
+            module.logout();
+        }
+
+        options.remove("tryFirstPass");
+        options.put("clearPass", "true");
+        status.put("javax.security.auth.login.name", "leo");
+        status.put("javax.security.auth.login.password", "passw0rd"
+                .toCharArray());
+        module.initialize(subject, new MockCallbackHandler(), status, options);
+        try {
+            module.login();
+            module.commit();
+            assertNull(
+                    "javax.security.auth.login.name in shared state should be null when clearPass switch on",
+                    status.get("javax.security.auth.login.name"));
+            assertNull(
+                    "javax.security.auth.login.password in shared state should be null when clearPass switch on",
+                    status.get("javax.security.auth.login.password"));
+        } catch (LoginException e) {
+            fail("Login shouldn't fail");
+        } finally {
+            module.logout();
+        }
+
+        status = new HashMap<String, Object>();
+        options.remove("clearPass");
+        options.put("storePass", "true");
+        module.initialize(subject, new FaultCallbackHandler(), status, options);
+        try {
+            module.login();
+            module.commit();
+        } catch (LoginException e) {
+            assertNull(
+                    "javax.security.auth.login.name in shared state should be null when login failed",
+                    status.get("javax.security.auth.login.name"));
+            assertNull(
+                    "javax.security.auth.login.password in shared state should be null when login failed",
+                    status.get("javax.security.auth.login.password"));
+        } finally {
+            module.logout();
+        }
+
+        module.initialize(subject, new MockCallbackHandler(), status, options);
+        try {
+            module.login();
+            module.commit();
+        } catch (LoginException e) {
+            fail("Login failed");
+        } finally {
+            module.logout();
+        }
+        assertNotNull(
+                "javax.security.auth.login.name should be stored in shared state when storePass switch on",
+                status.get("javax.security.auth.login.name"));
+        assertNotNull(
+                "javax.security.auth.login.password should be stored in shared state when storePass switch on",
+                status.get("javax.security.auth.login.password"));
+
+        status.put("javax.security.auth.login.name", "tester");
+        status.put("javax.security.auth.login.password", "testerPass");
+        module.initialize(subject, new MockCallbackHandler(), status, options);
+        try {
+            module.login();
+            module.commit();
+        } catch (LoginException e) {
+            fail("Login failed");
+        } finally {
+            module.logout();
+        }
+        assertEquals("Should't override the username value in sharedState",
+                status.get("javax.security.auth.login.name"), "tester");
+        assertEquals("Should't override the password value in sharedState",
+                status.get("javax.security.auth.login.password"), "testerPass");
+    }
+
+    static private class MockCallbackHandler implements CallbackHandler {
+
+        public void handle(Callback[] callbacks) throws IOException,
+                UnsupportedCallbackException {
+            for (int i = 0; i < callbacks.length; i++) {
+                if (callbacks[i] instanceof NameCallback) {
+                    NameCallback nc = (NameCallback) callbacks[i];
+                    nc.setName("leo");
+                } else if (callbacks[i] instanceof PasswordCallback) {
+                    PasswordCallback pc = (PasswordCallback) callbacks[i];
+                    pc.setPassword("passw0rd".toCharArray());
+                } else {
+                    throw new Error(callbacks[i].getClass().toString());
+                }
+            }
+        }
+    }
+
+    static private class FaultCallbackHandler implements CallbackHandler {
+
+        public void handle(Callback[] callbacks) throws IOException,
+                UnsupportedCallbackException {
+            for (int i = 0; i < callbacks.length; i++) {
+                if (callbacks[i] instanceof NameCallback) {
+                    NameCallback nc = (NameCallback) callbacks[i];
+                    nc.setName("leo");
+                } else if (callbacks[i] instanceof PasswordCallback) {
+                    PasswordCallback pc = (PasswordCallback) callbacks[i];
+                    pc.setPassword("password".toCharArray());
+                } else {
+                    throw new Error(callbacks[i].getClass().toString());
+                }
+            }
+        }
+    }
+}

Propchange: harmony/enhanced/classlib/trunk/modules/auth/src/test/java/common/org/apache/harmony/auth/tests/module/JndiLoginModuleTest.java
------------------------------------------------------------------------------
    svn:eol-style = native