Author: dkuleshov
Date: 2011-04-20 09:28:47 -0400 (Wed, 20 Apr 2011)
New Revision: 4262
Added:
core/trunk/exo.core.component.organization.api/src/main/java/org/exoplatform/services/organization/DigestAuthenticator.java
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/DigestAuthenticationHelper.java
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/jaas/DigestCallbackHandler.java
Modified:
core/trunk/exo.core.component.organization.api/src/main/java/org/exoplatform/services/organization/auth/OrganizationAuthenticatorImpl.java
core/trunk/exo.core.component.organization.api/src/main/java/org/exoplatform/services/organization/impl/mock/DummyOrganizationService.java
core/trunk/exo.core.component.organization.jdbc/src/main/java/org/exoplatform/services/organization/hibernate/UserDAOImpl.java
core/trunk/exo.core.component.organization.jdbc/src/main/java/org/exoplatform/services/organization/jdbc/UserDAOImpl.java
core/trunk/exo.core.component.security.core/pom.xml
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/PasswordCredential.java
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/j2ee/JbossLoginModule.java
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/j2ee/JettyLoginModule.java
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/j2ee/TomcatLoginModule.java
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/jaas/BasicCallbackHandler.java
core/trunk/exo.core.component.security.core/src/test/java/org/exoplatform/services/security/TestLoginModule.java
core/trunk/pom.xml
Log:
EXOJCR-1019: added digest auth
Added:
core/trunk/exo.core.component.organization.api/src/main/java/org/exoplatform/services/organization/DigestAuthenticator.java
===================================================================
---
core/trunk/exo.core.component.organization.api/src/main/java/org/exoplatform/services/organization/DigestAuthenticator.java
(rev 0)
+++
core/trunk/exo.core.component.organization.api/src/main/java/org/exoplatform/services/organization/DigestAuthenticator.java 2011-04-20
13:28:47 UTC (rev 4262)
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2009 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+
+package org.exoplatform.services.organization;
+
+import org.exoplatform.services.security.Credential;
+
+/**
+ * Created by The eXo Platform SAS .
+ *
+ * @author Dmitry Kuleshov
+ * @version $Id: $
+ */
+
+public interface DigestAuthenticator
+{
+ /**
+ * Checks if user's credentials are valid.
+ * It is more flexible because Credential may contain password context
+ * or some other useful data.
+ * @param credentials
+ * @return return true if the username and the password matches
+ * the database record, else return false.
+ * @throws Exception throw an exception if cannot access the database
+ */
+ public boolean authenticate(Credential[] credentials) throws Exception;
+}
Modified:
core/trunk/exo.core.component.organization.api/src/main/java/org/exoplatform/services/organization/auth/OrganizationAuthenticatorImpl.java
===================================================================
---
core/trunk/exo.core.component.organization.api/src/main/java/org/exoplatform/services/organization/auth/OrganizationAuthenticatorImpl.java 2011-04-20
10:44:24 UTC (rev 4261)
+++
core/trunk/exo.core.component.organization.api/src/main/java/org/exoplatform/services/organization/auth/OrganizationAuthenticatorImpl.java 2011-04-20
13:28:47 UTC (rev 4262)
@@ -22,8 +22,10 @@
import org.exoplatform.container.component.RequestLifeCycle;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
+import org.exoplatform.services.organization.DigestAuthenticator;
import org.exoplatform.services.organization.Membership;
import org.exoplatform.services.organization.OrganizationService;
+import org.exoplatform.services.organization.UserHandler;
import org.exoplatform.services.security.Authenticator;
import org.exoplatform.services.security.Credential;
import org.exoplatform.services.security.Identity;
@@ -128,7 +130,16 @@
password = new String(encrypter.encrypt(password.getBytes()));
begin(orgService);
- boolean success = orgService.getUserHandler().authenticate(user, password);
+ boolean success;
+ Object userHandler = orgService.getUserHandler();
+ if (userHandler instanceof DigestAuthenticator)
+ {
+ success = ((DigestAuthenticator)userHandler).authenticate(credentials);
+ }
+ else
+ {
+ success = ((UserHandler)userHandler).authenticate(user, password);
+ }
end(orgService);
if (!success)
Modified:
core/trunk/exo.core.component.organization.api/src/main/java/org/exoplatform/services/organization/impl/mock/DummyOrganizationService.java
===================================================================
---
core/trunk/exo.core.component.organization.api/src/main/java/org/exoplatform/services/organization/impl/mock/DummyOrganizationService.java 2011-04-20
10:44:24 UTC (rev 4261)
+++
core/trunk/exo.core.component.organization.api/src/main/java/org/exoplatform/services/organization/impl/mock/DummyOrganizationService.java 2011-04-20
13:28:47 UTC (rev 4262)
@@ -23,9 +23,9 @@
import org.exoplatform.commons.utils.LazyPageList;
import org.exoplatform.commons.utils.ListAccess;
-import org.exoplatform.commons.utils.ObjectPageList;
import org.exoplatform.commons.utils.PageList;
import org.exoplatform.services.organization.BaseOrganizationService;
+import org.exoplatform.services.organization.DigestAuthenticator;
import org.exoplatform.services.organization.Group;
import org.exoplatform.services.organization.GroupEventListener;
import org.exoplatform.services.organization.GroupHandler;
@@ -43,11 +43,16 @@
import org.exoplatform.services.organization.impl.MembershipImpl;
import org.exoplatform.services.organization.impl.UserImpl;
import org.exoplatform.services.organization.impl.UserProfileImpl;
+import org.exoplatform.services.security.Credential;
+import org.exoplatform.services.security.DigestAuthenticationHelper;
+import org.exoplatform.services.security.PasswordCredential;
+import org.exoplatform.services.security.UsernameCredential;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
/**
* @author benjaminmestrallet
@@ -149,7 +154,7 @@
}
}
- static public class UserHandlerImpl implements UserHandler
+ static public class UserHandlerImpl implements UserHandler, DigestAuthenticator
{
private static final int DEFAULT_LIST_SIZE = 6;
@@ -300,6 +305,27 @@
public boolean authenticate(String username, String password) throws Exception
{
+ return authenticate(new Credential[]{new UsernameCredential(username), new
PasswordCredential(password)});
+ }
+
+ public boolean authenticate(Credential[] credentials) throws Exception
+ {
+ String username = null;
+ String password = null;
+ Map<String, String> passwordContext = null;
+ for (Credential cred : credentials)
+ {
+ if (cred instanceof UsernameCredential)
+ {
+ username = ((UsernameCredential)cred).getUsername();
+ }
+ if (cred instanceof PasswordCredential)
+ {
+ password = ((PasswordCredential)cred).getPassword();
+ passwordContext = ((PasswordCredential)cred).getPasswordContext();
+ }
+ }
+
Iterator<User> it = users.iterator();
User usr = null;
@@ -317,14 +343,33 @@
if (usr != null)
{
if (usr.getUserName().equals("__anonim"))
+ {
return true;
-
- if (usr.getPassword().equals(password))
- return true;
+ }
+ if (passwordContext == null)
+ {
+ if (usr.getPassword().equals(password))
+ {
+ return true;
+ }
+ }
+ // passwordContext != null means that digest authentication is used
+ else
+ {
+ // so we need calculate MD5 cast
+ String dp = DigestAuthenticationHelper.calculatePassword(username,
usr.getPassword(), passwordContext);
+ // to compare it to sent by client
+ if (dp.equals(password))
+ {
+ return true;
+ }
+ }
}
return false;
}
+
+
}
public static class GroupHandlerImpl implements GroupHandler
Modified:
core/trunk/exo.core.component.organization.jdbc/src/main/java/org/exoplatform/services/organization/hibernate/UserDAOImpl.java
===================================================================
---
core/trunk/exo.core.component.organization.jdbc/src/main/java/org/exoplatform/services/organization/hibernate/UserDAOImpl.java 2011-04-20
10:44:24 UTC (rev 4261)
+++
core/trunk/exo.core.component.organization.jdbc/src/main/java/org/exoplatform/services/organization/hibernate/UserDAOImpl.java 2011-04-20
13:28:47 UTC (rev 4262)
@@ -25,12 +25,17 @@
import org.exoplatform.services.cache.ExoCache;
import org.exoplatform.services.database.HibernateService;
import org.exoplatform.services.database.ObjectQuery;
+import org.exoplatform.services.organization.DigestAuthenticator;
import org.exoplatform.services.organization.Query;
import org.exoplatform.services.organization.User;
import org.exoplatform.services.organization.UserEventListener;
import org.exoplatform.services.organization.UserEventListenerHandler;
import org.exoplatform.services.organization.UserHandler;
import org.exoplatform.services.organization.impl.UserImpl;
+import org.exoplatform.services.security.Credential;
+import org.exoplatform.services.security.DigestAuthenticationHelper;
+import org.exoplatform.services.security.PasswordCredential;
+import org.exoplatform.services.security.UsernameCredential;
import org.hibernate.Session;
import org.hibernate.Transaction;
@@ -40,12 +45,13 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
/**
* Created by The eXo Platform SAS Author : Mestrallet Benjamin
benjmestrallet(a)users.sourceforge.net
* Author : Tuan Nguyen tuan08(a)users.sourceforge.net Date: Aug 22, 2003 Time: 4:51:21 PM
*/
-public class UserDAOImpl implements UserHandler, UserEventListenerHandler
+public class UserDAOImpl implements UserHandler, UserEventListenerHandler,
DigestAuthenticator
{
public static final String queryFindUserByName =
"from u in class org.exoplatform.services.organization.impl.UserImpl " +
"where u.userName = ?";
@@ -173,10 +179,41 @@
public boolean authenticate(String username, String password) throws Exception
{
+ return authenticate(new Credential[]{new UsernameCredential(username), new
PasswordCredential(password)});
+ }
+
+ public boolean authenticate(Credential[] credentials) throws Exception
+ {
+ String username = null;
+ String password = null;
+ Map<String, String> passwordContext= null;
+ for (Credential cred : credentials)
+ {
+ if (cred instanceof UsernameCredential)
+ {
+ username = ((UsernameCredential)cred).getUsername();
+ }
+ if (cred instanceof PasswordCredential)
+ {
+ password = ((PasswordCredential)cred).getPassword();
+ passwordContext = ((PasswordCredential)cred).getPasswordContext();
+ }
+ }
+
User user = findUserByName(username);
if (user == null)
return false;
- boolean authenticated = user.getPassword().equals(password);
+
+ boolean authenticated;
+ if (passwordContext == null)
+ {
+ authenticated = user.getPassword().equals(password);
+ }
+ else
+ {
+ authenticated =
+ DigestAuthenticationHelper.calculatePassword(username, user.getPassword(),
passwordContext).equals(password);
+ }
if (authenticated)
{
UserImpl userImpl = (UserImpl)user;
Modified:
core/trunk/exo.core.component.organization.jdbc/src/main/java/org/exoplatform/services/organization/jdbc/UserDAOImpl.java
===================================================================
---
core/trunk/exo.core.component.organization.jdbc/src/main/java/org/exoplatform/services/organization/jdbc/UserDAOImpl.java 2011-04-20
10:44:24 UTC (rev 4261)
+++
core/trunk/exo.core.component.organization.jdbc/src/main/java/org/exoplatform/services/organization/jdbc/UserDAOImpl.java 2011-04-20
13:28:47 UTC (rev 4262)
@@ -28,15 +28,29 @@
import org.exoplatform.services.listener.ListenerService;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
-import org.exoplatform.services.organization.*;
+import org.exoplatform.services.organization.DigestAuthenticator;
+import org.exoplatform.services.organization.Group;
+import org.exoplatform.services.organization.GroupHandler;
+import org.exoplatform.services.organization.Membership;
+import org.exoplatform.services.organization.MembershipHandler;
+import org.exoplatform.services.organization.OrganizationService;
+import org.exoplatform.services.organization.Query;
+import org.exoplatform.services.organization.User;
+import org.exoplatform.services.organization.UserEventListener;
+import org.exoplatform.services.organization.UserHandler;
+import org.exoplatform.services.security.Credential;
+import org.exoplatform.services.security.DigestAuthenticationHelper;
+import org.exoplatform.services.security.PasswordCredential;
+import org.exoplatform.services.security.UsernameCredential;
import java.util.Calendar;
import java.util.List;
+import java.util.Map;
/**
* Created by The eXo Platform SAS Apr 7, 2007
*/
-public class UserDAOImpl extends StandardSQLDAO<UserImpl> implements UserHandler
+public class UserDAOImpl extends StandardSQLDAO<UserImpl> implements UserHandler,
DigestAuthenticator
{
protected static Log log =
ExoLogger.getLogger("exo.core.component.organization.jdbc.UserDAOImpl");
@@ -73,11 +87,42 @@
public boolean authenticate(String username, String password) throws Exception
{
+ return authenticate(new Credential[]{new UsernameCredential(username), new
PasswordCredential(password)});
+ }
+
+ public boolean authenticate(Credential[] credentials) throws Exception
+ {
+ String username = null;
+ String password = null;
+ Map<String, String> passwordContext = null;
+ for (Credential cred : credentials)
+ {
+ if (cred instanceof UsernameCredential)
+ {
+ username = ((UsernameCredential)cred).getUsername();
+ }
+ if (cred instanceof PasswordCredential)
+ {
+ password = ((PasswordCredential)cred).getPassword();
+ passwordContext = ((PasswordCredential)cred).getPasswordContext();
+ }
+ }
+
User user = findUserByName(username);
if (user == null)
return false;
- boolean authenticated = user.getPassword().equals(password);
+ boolean authenticated;
+ if (passwordContext == null)
+ {
+ authenticated = user.getPassword().equals(password);
+ }
+ else
+ {
+ authenticated =
+ DigestAuthenticationHelper.calculatePassword(username, user.getPassword(),
passwordContext).equals(password);
+ }
+
if (log.isDebugEnabled())
log.debug("+++++++++++AUTHENTICATE USERNAME " + username + " AND
PASS " + password + " - " + authenticated);
if (authenticated)
Modified: core/trunk/exo.core.component.security.core/pom.xml
===================================================================
--- core/trunk/exo.core.component.security.core/pom.xml 2011-04-20 10:44:24 UTC (rev
4261)
+++ core/trunk/exo.core.component.security.core/pom.xml 2011-04-20 13:28:47 UTC (rev
4262)
@@ -53,6 +53,14 @@
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.jboss.security</groupId>
+ <artifactId>jbosssx</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.aggregate</groupId>
+ <artifactId>jetty-plus</artifactId>
+ </dependency>
</dependencies>
<build>
Added:
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/DigestAuthenticationHelper.java
===================================================================
---
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/DigestAuthenticationHelper.java
(rev 0)
+++
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/DigestAuthenticationHelper.java 2011-04-20
13:28:47 UTC (rev 4262)
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2009 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.exoplatform.services.security;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+
+/**
+ * @author Dmitry Kuleshov
+ * @version $Id:$
+ */
+
+public class DigestAuthenticationHelper
+{
+
+ /**
+ * Number of HEX digits used for A1, A2 strings and password encoding.
+ * More information is settled in<a
href=http://www.apps.ietf.org/rfc/rfc2617.html#sec-3.2.2>RFC-2617</...;.
+ */
+ private static int HASH_HEX_LENGTH = 32;
+
+ public static String calculatePassword(String username, String originalPassword,
Map<String, String> passwordContext)
+ throws NoSuchAlgorithmException
+ {
+ // fetch needed data
+ String nc = passwordContext.get("nc");
+ String a2 = passwordContext.get("md5a2");
+ String uri = passwordContext.get("uri");
+ String qop = passwordContext.get("qop");
+ String nonce = passwordContext.get("nonce");
+ String realm = passwordContext.get("realmName");
+ String cnonce = passwordContext.get("cnonce");
+ String entity = passwordContext.get("entity");
+ String method = passwordContext.get("method");
+ if (realm == null)
+ {
+ // in case we have a jboss server, it uses 'realm' name
+ realm = passwordContext.get("realm");
+ }
+ if (a2 == null)
+ {
+ // in case we have a jboss server, it uses 'a2hash' name
+ a2 = passwordContext.get("a2hash");
+ }
+
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ // calculate MD5 hash of A1 string
+ String a1 = username + ":" + realm + ":" + originalPassword;
+ md.update(a1.getBytes());
+ // encode A1 in HEX digits
+ a1 = convertToHex(md.digest());
+
+ // if encoded A2 MD5 hash is not supplied by server
+ // we need to calculate it manually
+ if (a2 == null)
+ {
+ if (qop.equals("auth"))
+ {
+ md.update((method + ":" + uri).getBytes());
+ a2 = convertToHex(md.digest());
+ }
+ else if (qop.equals("auth-int"))
+ {
+ md.update((method + ":" + uri + ":" +
convertToHex(entity.getBytes())).getBytes());
+ a2 = convertToHex(md.digest());
+ }
+ }
+
+ // create a digest using provided data
+ String digest = a1 + ":" + nonce + ":" + nc + ":" +
cnonce + ":" + qop + ":" + a2;
+ md.update(digest.getBytes());
+ // return encoded hash using HEX digits digest
+ return convertToHex(md.digest());
+ }
+
+ public static String convertToHex(byte[] bin)
+ {
+ StringBuffer tmpStr = new StringBuffer(HASH_HEX_LENGTH);
+ int digit;
+
+ for (int i = 0; i < HASH_HEX_LENGTH / 2; i++)
+ {
+ // get integer presentation of left 4 bits of byte
+ digit = (bin[i] >> 4) & 0xf;
+ // append HEX digit
+ tmpStr.append(Integer.toHexString(digit));
+ // get integer presentation of right 4 bits of byte
+ digit = bin[i] & 0xf;
+ tmpStr.append(Integer.toHexString(digit));
+
+ };
+ return tmpStr.toString();
+ }
+}
Modified:
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/PasswordCredential.java
===================================================================
---
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/PasswordCredential.java 2011-04-20
10:44:24 UTC (rev 4261)
+++
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/PasswordCredential.java 2011-04-20
13:28:47 UTC (rev 4262)
@@ -18,6 +18,8 @@
*/
package org.exoplatform.services.security;
+import java.util.Map;
+
/**
* Created by The eXo Platform SAS .
*
@@ -39,6 +41,14 @@
private String password;
/**
+ * Digest Authorization Request Context.
+ * Here we're going to keep some information passed through request:
+ * qop, nonce, cnonce, algorithm, ns, etc. All context is defined in
+ * <a
href=http://www.apps.ietf.org/rfc/rfc2617.html#sec-3.2.2>RFC-2617</...;.
+ */
+ private Map<String, String> passwordContext = null;
+
+ /**
* Create new PasswordCredential.
* @param password password
*/
@@ -48,6 +58,25 @@
}
/**
+ * Create new PasswordCredential.
+ * @param password password
+ * @param passwordContext password context passed through Digest Authorization
request
+ */
+ public PasswordCredential(String password, Map<String, String> passwordContext)
+ {
+ this.password = password;
+ this.passwordContext = passwordContext;
+ }
+
+ /**
+ * @return password context
+ */
+ public Map<String, String> getPasswordContext()
+ {
+ return this.passwordContext;
+ }
+
+ /**
* @return password
*/
public String getPassword()
Modified:
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/j2ee/JbossLoginModule.java
===================================================================
---
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/j2ee/JbossLoginModule.java 2011-04-20
10:44:24 UTC (rev 4261)
+++
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/j2ee/JbossLoginModule.java 2011-04-20
13:28:47 UTC (rev 4262)
@@ -21,20 +21,33 @@
import org.exoplatform.container.monitor.jvm.J2EEServerInfo;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
+import org.exoplatform.services.security.Authenticator;
+import org.exoplatform.services.security.Credential;
+import org.exoplatform.services.security.Identity;
+import org.exoplatform.services.security.PasswordCredential;
+import org.exoplatform.services.security.UsernameCredential;
import org.exoplatform.services.security.jaas.DefaultLoginModule;
import org.exoplatform.services.security.jaas.JAASGroup;
import org.exoplatform.services.security.jaas.RolePrincipal;
import org.exoplatform.services.security.jaas.UserPrincipal;
+import org.jboss.security.auth.callback.MapCallback;
+import java.io.IOException;
import java.security.Principal;
import java.security.acl.Group;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import javax.management.MBeanServer;
import javax.management.ObjectName;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
/**
@@ -50,6 +63,11 @@
private static Log log =
ExoLogger.getLogger("exo.core.component.security.core.JbossLoginModule.class");
/**
+ * To retrieve password context during Digest Authentication.
+ */
+ private MapCallback[] mapCallback = {new MapCallback()};
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -81,6 +99,190 @@
}
/**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean login() throws LoginException
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("In login of JbossLoginModule.");
+ }
+ try
+ {
+ if (sharedState.containsKey("exo.security.identity"))
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("Use Identity from previous LoginModule");
+ }
+ identity = (Identity)sharedState.get("exo.security.identity");
+ }
+ else
+ {
+ if (!digestAuthenticationIsUsed())
+ {
+ return super.login();
+ }
+
+ if (log.isDebugEnabled())
+ {
+ log.debug("Try create identity");
+ }
+
+ Authenticator authenticator =
(Authenticator)getContainer().getComponentInstanceOfType(Authenticator.class);
+
+ if (authenticator == null)
+ {
+ throw new LoginException("No Authenticator component found, check
your configuration");
+ }
+
+ String userId = authenticator.validateUser(getCredentials());
+
+ identity = authenticator.createIdentity(userId);
+ sharedState.put("javax.security.auth.login.name", userId);
+ subject.getPrivateCredentials().add(getPassword());
+ subject.getPublicCredentials().add(getUsername());
+ }
+ return true;
+
+ }
+ catch (final Throwable e)
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug(e.getMessage(), e);
+ }
+
+ throw new LoginException(e.getMessage());
+ }
+ }
+
+ /**
+ * An utility method handles mapCallback and also checks if digest authentication is
used.
+ * @return true if digest authentication is used, otherwise - false
+ * @throws IOException
+ */
+ private boolean digestAuthenticationIsUsed() throws IOException
+ {
+ try
+ {
+ // here we're trying to handle mapCallback
+ // if it is handled successfully than digest
+ // authentication is used
+ callbackHandler.handle(mapCallback);
+ return true;
+ }
+ catch (UnsupportedCallbackException uce)
+ {
+ // otherwise UnsupportedCallbackException is thrown
+ return false;
+ }
+ }
+
+ /**
+ * An utility method to retrieve credentials. All needed for password hashing
information
+ * is retrieved from MapCallback. NameCallback and PasswordCallback are used to
correspondingly
+ * retrieve username and password.
+ * @return Credential
+ * @throws IOException
+ * @throws Exception
+ */
+ private Credential[] getCredentials() throws IOException
+ {
+ String username = null;
+ String password = null;
+ Map<String, String> passwordContext = new HashMap<String, String>();
+
+ passwordContext.put("qop",
(String)mapCallback[0].getInfo("qop"));
+ passwordContext.put("nonce",
(String)mapCallback[0].getInfo("nonce"));
+ passwordContext.put("cnonce",
(String)mapCallback[0].getInfo("cnonce"));
+ passwordContext.put("a2hash",
(String)mapCallback[0].getInfo("a2hash"));
+ passwordContext.put("nc",
(String)mapCallback[0].getInfo("nc"));
+ passwordContext.put("realm",
(String)mapCallback[0].getInfo("realm"));
+
+ try
+ {
+ Callback[] nameCallback = {new NameCallback("Username")};
+ callbackHandler.handle(nameCallback);
+ username = ((NameCallback)nameCallback[0]).getName();
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ if (log.isErrorEnabled())
+ {
+ log.error("Error on retrieving username from callback handler! ",
e);
+ }
+ }
+
+ try
+ {
+ Callback[] passwordCallback = {new PasswordCallback("Password",
false)};
+ callbackHandler.handle(passwordCallback);
+ password = new String(((PasswordCallback)passwordCallback[0]).getPassword());
+ ((PasswordCallback)passwordCallback[0]).clearPassword();
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ if (log.isErrorEnabled())
+ {
+ log.error("Error on retrieving password from callback handler! ",
e);
+ }
+ }
+
+ if (username == null || password == null)
+ {
+ return null;
+ }
+
+ return new Credential[]{new UsernameCredential(username), new
PasswordCredential(password, passwordContext)};
+ }
+
+ private UsernameCredential getUsername() throws IOException
+ {
+ String username = null;
+
+ try
+ {
+ Callback[] nameCallback = {new NameCallback("Username")};
+ callbackHandler.handle(nameCallback);
+ username = ((NameCallback)nameCallback[0]).getName();
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ if (log.isErrorEnabled())
+ {
+ log.error("Error on retrieving username from callback handler! ",
e);
+ }
+ }
+
+ return new UsernameCredential(username);
+ }
+
+ private String getPassword() throws IOException
+ {
+ String password = null;
+
+ try
+ {
+ Callback[] passwordCallback = {new PasswordCallback("Password",
false)};
+ callbackHandler.handle(passwordCallback);
+ password = new String(((PasswordCallback)passwordCallback[0]).getPassword());
+ ((PasswordCallback)passwordCallback[0]).clearPassword();
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ if (log.isErrorEnabled())
+ {
+ log.error("Error on retrieving password from callback handler! ",
e);
+ }
+ }
+
+ return password;
+ }
+
+ /**
* Attempts eviction of the subject in the JBoss security manager cache.
*
* @return a boolean
Modified:
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/j2ee/JettyLoginModule.java
===================================================================
---
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/j2ee/JettyLoginModule.java 2011-04-20
10:44:24 UTC (rev 4261)
+++
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/j2ee/JettyLoginModule.java 2011-04-20
13:28:47 UTC (rev 4262)
@@ -1,16 +1,35 @@
package org.exoplatform.services.security.j2ee;
+import org.eclipse.jetty.plus.jaas.callback.ObjectCallback;
+import org.eclipse.jetty.security.authentication.DigestAuthenticator;
+import org.exoplatform.services.security.Authenticator;
+import org.exoplatform.services.security.Credential;
+import org.exoplatform.services.security.Identity;
+import org.exoplatform.services.security.PasswordCredential;
+import org.exoplatform.services.security.UsernameCredential;
import org.exoplatform.services.security.jaas.DefaultLoginModule;
import org.exoplatform.services.security.jaas.RolePrincipal;
import org.exoplatform.services.security.jaas.UserPrincipal;
+import java.io.IOException;
+import java.lang.reflect.Field;
import java.security.Principal;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
import java.util.Set;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
public class JettyLoginModule extends DefaultLoginModule
{
+ /**
+ * To retrieve an object instance containing needed password context.
+ */
+ private Callback[] objectCallback = {new ObjectCallback()};
@Override
public boolean commit() throws LoginException
@@ -35,4 +54,199 @@
}
}
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean login() throws LoginException
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("In login of JettyLoginModule.");
+ }
+ try
+ {
+ if (sharedState.containsKey("exo.security.identity"))
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("Use Identity from previous LoginModule");
+ }
+ identity = (Identity)sharedState.get("exo.security.identity");
+ }
+ else
+ {
+ if (!digestAuthenticationIsUsed())
+ {
+ return super.login();
+ }
+
+ if (log.isDebugEnabled())
+ {
+ log.debug("Try create identity");
+ }
+
+ Authenticator authenticator =
(Authenticator)getContainer().getComponentInstanceOfType(Authenticator.class);
+
+ if (authenticator == null)
+ {
+ throw new LoginException("No Authenticator component found, check
your configuration");
+ }
+
+ String userId = authenticator.validateUser(getCredentials());
+
+ identity = authenticator.createIdentity(userId);
+ sharedState.put("javax.security.auth.login.name", userId);
+ // TODO use PasswordCredential wrapper
+ subject.getPrivateCredentials().add(getPassword());
+ subject.getPublicCredentials().add(getUsername());
+ }
+ return true;
+
+ }
+ catch (final Throwable e)
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug(e.getMessage(), e);
+ }
+
+ throw new LoginException(e.getMessage());
+ }
+ }
+
+ /**
+ * An utility method to handle object callback and also to checks if
+ * digest authentication is used during login operation.
+ * @return true if digest authentication is used, otherwise - false
+ * @throws IOException
+ * @throws UnsupportedCallbackException
+ */
+ private boolean digestAuthenticationIsUsed() throws IOException,
UnsupportedCallbackException
+ {
+ callbackHandler.handle(objectCallback);
+ // in case we have a digest authentication
+ // objectCallback should contain a structured instance
+ // in case we have a basic authentication
+ // objectCallback should contain only a string with a password
+ return !(((ObjectCallback)objectCallback[0]).getObject() instanceof String);
+ }
+
+ /**
+ * An utility method to get Credentials from object callback instance.
+ * It uses reflection mechanism to get access to Digest inner class of
+ * DigestAuthenticator, which is provided by object callback as it
+ * contains all needed information for password hashing.
+ * @return Credential
+ * @throws NoSuchFieldException
+ * @throws SecurityException
+ * @throws IllegalAccessException
+ * @throws IllegalArgumentException
+ * @throws Exception
+ */
+ private Credential[] getCredentials()
+ {
+ Map<String, String> passwordContext = new HashMap<String, String>();
+ Set<String> contextElements = new HashSet<String>();
+ // object to contain DigestAuthenticator$Digest instance to get
+ // needed data from instance's fields
+ Object objectFromCallback = ((ObjectCallback)objectCallback[0]).getObject();
+ String username = null;
+ String password = null;
+ // to keep DigestAuthenticator$Digest representation
+ Class<?> digestAuthenticatorClazz =
DigestAuthenticator.class.getDeclaredClasses()[0];
+
+ contextElements.add("cnonce");
+ contextElements.add("method");
+ contextElements.add("nc");
+ contextElements.add("nonce");
+ contextElements.add("qop");
+ contextElements.add("realm");
+ contextElements.add("uri");
+
+ try
+ {
+ // here we're going to retrieve needed information from Digest class fields
+ Iterator<String> elementIterator = contextElements.iterator();
+ String element;
+ Field field;
+ while (elementIterator.hasNext())
+ {
+ element = elementIterator.next();
+ field = digestAuthenticatorClazz.getDeclaredField(element);
+ // need to set true as all needed fields are in private class, thus are
private
+ field.setAccessible(true);
+ passwordContext.put(element, (String)field.get(objectFromCallback));
+ }
+
+ // get username
+ field = digestAuthenticatorClazz.getDeclaredField("username");
+ field.setAccessible(true);
+ username = (String)field.get(objectFromCallback);
+
+ // get password
+ field = digestAuthenticatorClazz.getDeclaredField("response");
+ field.setAccessible(true);
+ password = (String)field.get(objectFromCallback);
+ }
+ catch (Exception e)
+ {
+ if (log.isErrorEnabled())
+ {
+ log.error("Could not get credentials.", e);
+ }
+ }
+
+ if (username == null || password == null)
+ {
+ return null;
+ }
+
+ return new Credential[]{new UsernameCredential(username), new
PasswordCredential(password, passwordContext)};
+ }
+
+ private UsernameCredential getUsername()
+ {
+ String username = null;
+ Class<?> digestAuthenticatorClazz =
DigestAuthenticator.class.getDeclaredClasses()[0];
+ try
+ {
+ Field field = digestAuthenticatorClazz.getDeclaredField("username");
+ field.setAccessible(true);
+ username =
(String)field.get((((ObjectCallback)objectCallback[0]).getObject()));
+ }
+ catch (Exception e)
+ {
+ if (log.isErrorEnabled())
+ {
+ log.error("Could not get username.", e);
+ }
+ }
+
+ return new UsernameCredential(username);
+ }
+
+ private String getPassword() throws SecurityException, NoSuchFieldException,
IllegalArgumentException,
+ IllegalAccessException
+ {
+ String password = null;
+ Class<?> digestAuthenticatorClazz =
DigestAuthenticator.class.getDeclaredClasses()[0];
+ try
+ {
+ Field field = digestAuthenticatorClazz.getDeclaredField("response");
+ field.setAccessible(true);
+ password = (String)field.get((((ObjectCallback)objectCallback[0]).getObject()));
+ }
+ catch (Exception e)
+ {
+ if (log.isErrorEnabled())
+ {
+ log.error("Could not get password.", e);
+ }
+ }
+
+ return password;
+ }
+
}
Modified:
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/j2ee/TomcatLoginModule.java
===================================================================
---
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/j2ee/TomcatLoginModule.java 2011-04-20
10:44:24 UTC (rev 4261)
+++
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/j2ee/TomcatLoginModule.java 2011-04-20
13:28:47 UTC (rev 4262)
@@ -18,13 +18,28 @@
*/
package org.exoplatform.services.security.j2ee;
+import org.exoplatform.services.security.Authenticator;
+import org.exoplatform.services.security.Credential;
+import org.exoplatform.services.security.Identity;
+import org.exoplatform.services.security.PasswordCredential;
+import org.exoplatform.services.security.UsernameCredential;
import org.exoplatform.services.security.jaas.DefaultLoginModule;
import org.exoplatform.services.security.jaas.RolePrincipal;
import org.exoplatform.services.security.jaas.UserPrincipal;
+import java.io.IOException;
import java.security.Principal;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
import java.util.Set;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.TextInputCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
/**
@@ -63,4 +78,206 @@
}
}
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean login() throws LoginException
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("In login of TomcatLoginModule.");
+ }
+ try
+ {
+ if (sharedState.containsKey("exo.security.identity"))
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("Use Identity from previous LoginModule");
+ }
+ identity = (Identity)sharedState.get("exo.security.identity");
+ }
+ else
+ {
+ if (!digestAuthenticationIsUsed())
+ {
+ return super.login();
+ }
+
+ if (log.isDebugEnabled())
+ {
+ log.debug("Try create identity");
+ }
+
+ Authenticator authenticator =
(Authenticator)getContainer().getComponentInstanceOfType(Authenticator.class);
+
+ if (authenticator == null)
+ {
+ throw new LoginException("No Authenticator component found, check
your configuration");
+ }
+
+ String userId = authenticator.validateUser(getCredentials());
+
+ identity = authenticator.createIdentity(userId);
+ sharedState.put("javax.security.auth.login.name", userId);
+ subject.getPrivateCredentials().add(getPassword());
+ subject.getPublicCredentials().add(getUsername());
+ }
+ return true;
+
+ }
+ catch (final Throwable e)
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug(e.getMessage(), e);
+ }
+
+ throw new LoginException(e.getMessage());
+ }
+ }
+
+ /**
+ * An utility class to get credentials. To retrieve password context we use
TextInputCallback.
+ * NameCallback and PasswordCallback are used to correspondingly retrieve username and
password.
+ * @return Credential
+ * @throws IOException
+ * @throws Exception
+ */
+ private Credential[] getCredentials() throws IOException
+ {
+
+ Map<String, String> passwordContext = new HashMap<String, String>();
+ Set<String> contextElements = new HashSet<String>();
+ Callback[] digestCallback = new Callback[1];
+ String username = null;
+ String password = null;
+
+ contextElements.add("cnonce");
+ contextElements.add("nc");
+ contextElements.add("nonce");
+ contextElements.add("qop");
+ contextElements.add("realmName");
+ contextElements.add("md5a2");
+
+ Iterator<String> elementIterator = contextElements.iterator();
+
+ String element;
+ while (elementIterator.hasNext())
+ {
+ element = elementIterator.next();
+ try
+ {
+ digestCallback[0] = new TextInputCallback(element);
+ callbackHandler.handle(digestCallback);
+ passwordContext.put(element,
((TextInputCallback)digestCallback[0]).getText());
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("Unsupported callback type.", e);
+ }
+ }
+
+ }
+
+ try
+ {
+ Callback[] nameCallback = {new NameCallback("Username")};
+ callbackHandler.handle(nameCallback);
+ username = ((NameCallback)nameCallback[0]).getName();
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ if (log.isErrorEnabled())
+ {
+ log.error("Error on retrieving username from callback handler! ",
e);
+ }
+ }
+
+ try
+ {
+ Callback[] passwordCallback = {new PasswordCallback("Password",
false)};
+ callbackHandler.handle(passwordCallback);
+ password = new String(((PasswordCallback)passwordCallback[0]).getPassword());
+ ((PasswordCallback)passwordCallback[0]).clearPassword();
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ if (log.isErrorEnabled())
+ {
+ log.error("Error on retrieving password from callback handler! ",
e);
+ }
+ }
+
+ if (username == null || password == null)
+ {
+ return null;
+ }
+
+ return new Credential[]{new UsernameCredential(username), new
PasswordCredential(password, passwordContext)};
+ }
+
+ /**
+ * An utility method to check if digest authentication is used.
+ * @return true if digest authentication is used, otherwise - false
+ * @throws IOException
+ * @throws UnsupportedCallbackException
+ */
+ private boolean digestAuthenticationIsUsed() throws IOException,
UnsupportedCallbackException
+ {
+ Callback[] authCallback = {new TextInputCallback("authMethod")};
+ callbackHandler.handle(authCallback);
+ String authMethod = (String)(((TextInputCallback)authCallback[0]).getText());
+
+ return "DIGEST".equalsIgnoreCase(authMethod);
+ }
+
+ private UsernameCredential getUsername() throws IOException
+ {
+ String username = null;
+
+ try
+ {
+ Callback[] nameCallback = {new NameCallback("Username")};
+ callbackHandler.handle(nameCallback);
+ username = ((NameCallback)nameCallback[0]).getName();
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ if (log.isErrorEnabled())
+ {
+ log.error("Error on retrieving username from callback handler! ",
e);
+ }
+ }
+
+ return new UsernameCredential(username);
+ }
+
+ private String getPassword() throws IOException
+ {
+ String password = null;
+
+ try
+ {
+ Callback[] passwordCallback = {new PasswordCallback("Password",
false)};
+ callbackHandler.handle(passwordCallback);
+ password = new String(((PasswordCallback)passwordCallback[0]).getPassword());
+ ((PasswordCallback)passwordCallback[0]).clearPassword();
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ if (log.isErrorEnabled())
+ {
+ log.error("Error on retrieving password from callback handler! ",
e);
+ }
+ }
+
+ return password;
+ }
+
+
}
Modified:
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/jaas/BasicCallbackHandler.java
===================================================================
---
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/jaas/BasicCallbackHandler.java 2011-04-20
10:44:24 UTC (rev 4261)
+++
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/jaas/BasicCallbackHandler.java 2011-04-20
13:28:47 UTC (rev 4262)
@@ -24,6 +24,7 @@
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.TextInputCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
public class BasicCallbackHandler implements CallbackHandler
@@ -43,6 +44,12 @@
* @param login name
* @param password password
*/
+
+ /**
+ * Authentication method.
+ */
+ private final String authMethod = "BASIC";
+
public BasicCallbackHandler(String login, char[] password)
{
this.login = login;
@@ -64,6 +71,13 @@
{
((PasswordCallback)callbacks[i]).setPassword(password);
}
+ else if (callbacks[i] instanceof TextInputCallback)
+ {
+ if
("authMethod".equals(((TextInputCallback)callbacks[i]).getPrompt()))
+ {
+ ((TextInputCallback)callbacks[i]).setText(authMethod);
+ }
+ }
else
{
throw new UnsupportedCallbackException(callbacks[i], "Callback class not
supported");
Added:
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/jaas/DigestCallbackHandler.java
===================================================================
---
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/jaas/DigestCallbackHandler.java
(rev 0)
+++
core/trunk/exo.core.component.security.core/src/main/java/org/exoplatform/services/security/jaas/DigestCallbackHandler.java 2011-04-20
13:28:47 UTC (rev 4262)
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2009 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.exoplatform.services.security.jaas;
+
+import java.io.IOException;
+import java.util.Map;
+
+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.TextInputCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+public class DigestCallbackHandler implements CallbackHandler
+{
+ /**
+ * Name.
+ */
+ private String login;
+
+ /**
+ * Password.
+ */
+ private char[] password;
+
+ /**
+ * @param login name
+ * @param password password
+ */
+
+ /**
+ * Authentication method.
+ */
+ private final String authMethod = "DIGEST";
+
+ /**
+ * Here we pass all needed password context to be retrieved later on Callback
handling.
+ */
+ private final Map<String, String> passwordContext;
+
+ public DigestCallbackHandler(String login, char[] password, Map<String, String>
passwordContext)
+ {
+ this.login = login;
+ this.password = password;
+ this.passwordContext = passwordContext;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException
+ {
+ for (int i = 0; i < callbacks.length; i++)
+ {
+ if (callbacks[i] instanceof NameCallback)
+ {
+ ((NameCallback)callbacks[i]).setName(login);
+ }
+ else if (callbacks[i] instanceof PasswordCallback)
+ {
+ ((PasswordCallback)callbacks[i]).setPassword(password);
+ }
+ else if (callbacks[i] instanceof TextInputCallback)
+ {
+ if
("authMethod".equals(((TextInputCallback)callbacks[i]).getPrompt()))
+ {
+ ((TextInputCallback)callbacks[i]).setText(authMethod);
+ }
+ else if
("cnonce".equals(((TextInputCallback)callbacks[i]).getPrompt()))
+ {
+
((TextInputCallback)callbacks[i]).setText(passwordContext.get("cnonce"));
+ }
+ else if
("md5a2".equals(((TextInputCallback)callbacks[i]).getPrompt()))
+ {
+
((TextInputCallback)callbacks[i]).setText(passwordContext.get("md5a2"));
+ }
+ else if
("nc".equals(((TextInputCallback)callbacks[i]).getPrompt()))
+ {
+
((TextInputCallback)callbacks[i]).setText(passwordContext.get("nc"));
+ }
+ else if
("nonce".equals(((TextInputCallback)callbacks[i]).getPrompt()))
+ {
+
((TextInputCallback)callbacks[i]).setText(passwordContext.get("nonce"));
+ }
+ else if
("qop".equals(((TextInputCallback)callbacks[i]).getPrompt()))
+ {
+
((TextInputCallback)callbacks[i]).setText(passwordContext.get("qop"));
+ }
+ else if
("realmName".equals(((TextInputCallback)callbacks[i]).getPrompt()))
+ {
+
((TextInputCallback)callbacks[i]).setText(passwordContext.get("realmName"));
+ }
+ }
+ else
+ {
+ throw new UnsupportedCallbackException(callbacks[i], "Callback class not
supported");
+ }
+ }
+ }
+}
+
Modified:
core/trunk/exo.core.component.security.core/src/test/java/org/exoplatform/services/security/TestLoginModule.java
===================================================================
---
core/trunk/exo.core.component.security.core/src/test/java/org/exoplatform/services/security/TestLoginModule.java 2011-04-20
10:44:24 UTC (rev 4261)
+++
core/trunk/exo.core.component.security.core/src/test/java/org/exoplatform/services/security/TestLoginModule.java 2011-04-20
13:28:47 UTC (rev 4262)
@@ -22,8 +22,12 @@
import org.exoplatform.container.StandaloneContainer;
import org.exoplatform.services.security.jaas.BasicCallbackHandler;
+import org.exoplatform.services.security.jaas.DigestCallbackHandler;
import java.net.URL;
+import java.security.MessageDigest;
+import java.util.HashMap;
+import java.util.Map;
import javax.security.auth.login.LoginContext;
@@ -73,7 +77,7 @@
conversationRegistry.clear();
}
- public void testLogin() throws Exception
+ public void testBasicLogin() throws Exception
{
BasicCallbackHandler handler = new BasicCallbackHandler("exo",
"exo".toCharArray());
LoginContext loginContext = new LoginContext("exo", handler);
@@ -90,4 +94,66 @@
}
+ /**
+ * Here we test Digest Authorization. We artificially create a password context, to
emulate
+ * Authorize request environment. Than we login and expect to have "exo"
identity registered
+ * and corresponding group created. More information about Digest Authorization is
settled
+ * <a
href=http://www.apps.ietf.org/rfc/rfc2617.html>here</a>.
+ * @throws Exception
+ */
+ public void testDigestLogin() throws Exception
+ {
+ /**
+ * Number of hex digits.
+ * Hex digits are needed to encode A2, A1 elements as defined in RFC-2617.
+ */
+ int HASH_HEX_LENGTH = 32;
+
+ /**
+ * Here we are going to keep all password context information
+ */
+ Map<String, String> passwordContext = new HashMap<String, String>();
+
+ passwordContext.put("realmName", "eXo REST services");
+ passwordContext.put("nonce",
"2c613333aa4cc017d358c09f61977718");
+ passwordContext.put("cnonce",
"bFaGgjcb+QP47nzPpxtonQ28Kgbz22WsBqmKjHU49q9=");
+ passwordContext.put("qop", "auth");
+ passwordContext.put("nc", "00000001");
+ passwordContext.put("response",
"303c5080ac28ed876ea138d207fdf2cd");
+
+ // encrypt A2 string using MD5
+ String md5a2 = "Method:" + "/rest/jcr/repository/production";
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ md.update(md5a2.getBytes());
+
+ // encode encrypted A2 string using HEX digits
+ byte[] bin = md.digest();
+ StringBuffer tmpStr = new StringBuffer(HASH_HEX_LENGTH);
+ int digit;
+ for (int i = 0; i < HASH_HEX_LENGTH / 2; i++)
+ {
+ digit = (bin[i] >> 4) & 0xf;
+ tmpStr.append(Integer.toHexString(digit));
+ digit = bin[i] & 0xf;
+ tmpStr.append(Integer.toHexString(digit));
+
+ };
+
+ passwordContext.put("md5a2", tmpStr.toString());
+
+ DigestCallbackHandler handler = new DigestCallbackHandler("exo",
"exo".toCharArray(), passwordContext);
+ LoginContext loginContext = new LoginContext("exo", handler);
+ loginContext.login();
+
+ assertNotNull(identityRegistry.getIdentity("exo"));
+ assertEquals("exo",
identityRegistry.getIdentity("exo").getUserId());
+
+ assertEquals(1, identityRegistry.getIdentity("exo").getGroups().size());
+
+ StateKey key = new SimpleStateKey("exo");
+ conversationRegistry.register(key, new
ConversationState(identityRegistry.getIdentity("exo")));
+ assertNotNull(conversationRegistry.getState(key));
+
+ }
+
}
Modified: core/trunk/pom.xml
===================================================================
--- core/trunk/pom.xml 2011-04-20 10:44:24 UTC (rev 4261)
+++ core/trunk/pom.xml 2011-04-20 13:28:47 UTC (rev 4262)
@@ -131,6 +131,20 @@
<artifactId>exo.core.component.security.core</artifactId>
<version>${project.version}</version>
</dependency>
+
+ <dependency>
+ <groupId>org.jboss.security</groupId>
+ <artifactId>jbosssx</artifactId>
+ <scope>provided</scope>
+ <version>2.0.4</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty.aggregate</groupId>
+ <artifactId>jetty-plus</artifactId>
+ <version>7.1.5.v20100705</version>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>javax.resource</groupId>