SPNEGO support
authorMathieu Baudier <mbaudier@argeo.org>
Tue, 21 Feb 2017 21:00:22 +0000 (22:00 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Tue, 21 Feb 2017 21:00:22 +0000 (22:00 +0100)
20 files changed:
demo/argeo_node_rap.properties
org.argeo.cms.ui.workbench/build.properties
org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/jcr/commands/AddRemoteRepository.java
org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/jcr/model/RemoteRepositoryElem.java
org.argeo.cms/bnd.bnd
org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java
org.argeo.cms/src/org/argeo/cms/auth/SingleUserLoginModule.java
org.argeo.cms/src/org/argeo/cms/auth/SpnegoLoginModule.java
org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java
org.argeo.cms/src/org/argeo/cms/internal/http/CmsSessionProvider.java
org.argeo.cms/src/org/argeo/cms/internal/http/DataHttpContext.java
org.argeo.cms/src/org/argeo/cms/internal/http/NodeHttp.java
org.argeo.cms/src/org/argeo/cms/internal/http/PrivateHttpContext.java
org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoCredentialProvider.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/http/client/jaas.cfg [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsSecurity.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas-ipa.cfg
org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg
org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdapUserAdmin.java

index 54b4735e52ddfd8a705e879a7dcf565d4b0a567e..832c536b073756bc5e67f9902c2e35c5b0268f15 100644 (file)
@@ -34,7 +34,7 @@ argeo.node.repo.type=localfs
 
 #argeo.node.useradmin.uris="dc=example,dc=com.ldif dc=example,dc=org.ldif"
 
-sun.security.krb5.debug=true
+#sun.security.krb5.debug=true
 
 # HTTP
 org.osgi.service.http.port=7070
index 38c57510471a01d79d663c15a173efa01dd420a8..1b9b7bd51fc0cb48dd8a947616bcb01d77322515 100644 (file)
@@ -3,4 +3,5 @@ output.. =  bin/
 bin.includes = META-INF/,\
                .,\
                icons/,\
-               plugin.xml
\ No newline at end of file
+               plugin.xml
+additional.bundles = org.apache.commons.httpclient
index 8b19b51a95045675f91fa040849153d456b65460..6fdb274d0f5bac4a9bb23dfc951bb5af344dab77 100644 (file)
@@ -59,16 +59,14 @@ import org.eclipse.swt.widgets.Text;
  * Connect to a remote repository and, if successful publish it as an OSGi
  * service.
  */
-public class AddRemoteRepository extends AbstractHandler implements
-               WorkbenchConstants, ArgeoNames {
+public class AddRemoteRepository extends AbstractHandler implements WorkbenchConstants, ArgeoNames {
 
        private RepositoryFactory repositoryFactory;
        private Repository nodeRepository;
        private Keyring keyring;
 
        public Object execute(ExecutionEvent event) throws ExecutionException {
-               RemoteRepositoryLoginDialog dlg = new RemoteRepositoryLoginDialog(
-                               Display.getDefault().getActiveShell());
+               RemoteRepositoryLoginDialog dlg = new RemoteRepositoryLoginDialog(Display.getDefault().getActiveShell());
                if (dlg.open() == Dialog.OK) {
                        CommandUtils.callCommand(Refresh.ID);
                }
@@ -105,16 +103,13 @@ public class AddRemoteRepository extends AbstractHandler implements
 
                protected Control createDialogArea(Composite parent) {
                        Composite dialogarea = (Composite) super.createDialogArea(parent);
-                       dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
-                                       true));
+                       dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
                        Composite composite = new Composite(dialogarea, SWT.NONE);
                        composite.setLayout(new GridLayout(2, false));
-                       composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
-                                       false));
+                       composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
                        setMessage("Login to remote repository", IMessageProvider.NONE);
                        name = createLT(composite, "Name", "remoteRepository");
-                       uri = createLT(composite, "URI",
-                                       "http://localhost:7070/jcr/node");
+                       uri = createLT(composite, "URI", "http://localhost:7070/jcr/node");
                        username = createLT(composite, "User", "");
                        password = createLP(composite, "Password");
 
@@ -146,20 +141,19 @@ public class AddRemoteRepository extends AbstractHandler implements
                                params.put(NodeConstants.LABELED_URI, checkedUriStr);
                                Repository repository = repositoryFactory.getRepository(params);
                                if (username.getText().trim().equals("")) {// anonymous
-                                       session = repository.login();
+                                       // FIXME make it more generic
+                                       session = repository.login("main");
                                } else {
                                        // FIXME use getTextChars() when upgrading to 3.7
                                        // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=297412
                                        char[] pwd = password.getText().toCharArray();
-                                       SimpleCredentials sc = new SimpleCredentials(
-                                                       username.getText(), pwd);
+                                       SimpleCredentials sc = new SimpleCredentials(username.getText(), pwd);
                                        session = repository.login(sc, "main");
                                        MessageDialog.openInformation(getParentShell(), "Success",
                                                        "Connection to '" + uri.getText() + "' successful");
                                }
                        } catch (Exception e) {
-                               ErrorFeedback.show(
-                                               "Connection test failed for " + uri.getText(), e);
+                               ErrorFeedback.show("Connection test failed for " + uri.getText(), e);
                        } finally {
                                JcrUtils.logoutQuietly(session);
                        }
@@ -172,28 +166,20 @@ public class AddRemoteRepository extends AbstractHandler implements
                                nodeSession = nodeRepository.login();
                                Node home = NodeUtils.getUserHome(nodeSession);
 
-                               Node remote = home.hasNode(ARGEO_REMOTE) ? home
-                                               .getNode(ARGEO_REMOTE) : home.addNode(ARGEO_REMOTE);
+                               Node remote = home.hasNode(ARGEO_REMOTE) ? home.getNode(ARGEO_REMOTE) : home.addNode(ARGEO_REMOTE);
                                if (remote.hasNode(name.getText()))
-                                       throw new EclipseUiException(
-                                                       "There is already a remote repository named "
-                                                                       + name.getText());
-                               Node remoteRepository = remote.addNode(name.getText(),
-                                               ArgeoTypes.ARGEO_REMOTE_REPOSITORY);
+                                       throw new EclipseUiException("There is already a remote repository named " + name.getText());
+                               Node remoteRepository = remote.addNode(name.getText(), ArgeoTypes.ARGEO_REMOTE_REPOSITORY);
                                remoteRepository.setProperty(ARGEO_URI, uri.getText());
                                remoteRepository.setProperty(ARGEO_USER_ID, username.getText());
                                nodeSession.save();
                                if (saveInKeyring.getSelection()) {
-                                       String pwdPath = remoteRepository.getPath() + '/'
-                                                       + ARGEO_PASSWORD;
+                                       String pwdPath = remoteRepository.getPath() + '/' + ARGEO_PASSWORD;
                                        keyring.set(pwdPath, password.getText().toCharArray());
                                }
                                nodeSession.save();
-                               MessageDialog.openInformation(
-                                               getParentShell(),
-                                               "Repository Added",
-                                               "Remote repository '" + username.getText() + "@"
-                                                               + uri.getText() + "' added");
+                               MessageDialog.openInformation(getParentShell(), "Repository Added",
+                                               "Remote repository '" + username.getText() + "@" + uri.getText() + "' added");
 
                                super.okPressed();
                        } catch (Exception e) {
@@ -213,8 +199,7 @@ public class AddRemoteRepository extends AbstractHandler implements
                }
 
                /** Creates label and check. */
-               protected Button createLC(Composite parent, String label,
-                               Boolean initial) {
+               protected Button createLC(Composite parent, String label, Boolean initial) {
                        new Label(parent, SWT.NONE).setText(label);
                        Button check = new Button(parent, SWT.CHECK);
                        check.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
@@ -224,8 +209,7 @@ public class AddRemoteRepository extends AbstractHandler implements
 
                protected Text createLP(Composite parent, String label) {
                        new Label(parent, SWT.NONE).setText(label);
-                       Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER
-                                       | SWT.PASSWORD);
+                       Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER | SWT.PASSWORD);
                        text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
                        return text;
                }
index f34f4de244c63411e6fef5a024a99f4486ebccee..bd3cd32d6fb02959b303fe83fc26ab698d62145e 100644 (file)
@@ -43,8 +43,7 @@ public class RemoteRepositoryElem extends RepositoryElem {
        private final RepositoryFactory repositoryFactory;
        private final String uri;
 
-       public RemoteRepositoryElem(String alias,
-                       RepositoryFactory repositoryFactory, String uri, TreeParent parent,
+       public RemoteRepositoryElem(String alias, RepositoryFactory repositoryFactory, String uri, TreeParent parent,
                        Session userSession, Keyring keyring, String remoteNodePath) {
                super(alias, null, parent);
                this.repositoryFactory = repositoryFactory;
@@ -55,29 +54,27 @@ public class RemoteRepositoryElem extends RepositoryElem {
        }
 
        @Override
-       protected Session repositoryLogin(String workspaceName)
-                       throws RepositoryException {
+       protected Session repositoryLogin(String workspaceName) throws RepositoryException {
                Node remoteRepository = userSession.getNode(remoteNodePath);
-               String userID = remoteRepository.getProperty(ArgeoNames.ARGEO_USER_ID)
-                               .getString();
-               String pwdPath = remoteRepository.getPath() + '/'
-                               + ArgeoNames.ARGEO_PASSWORD;
-               char[] password = keyring.getAsChars(pwdPath);
-
-               try {
-                       SimpleCredentials credentials = new SimpleCredentials(userID,
-                                       password);
-                       return getRepository().login(credentials, workspaceName);
-               } finally {
-                       Arrays.fill(password, 0, password.length, ' ');
+               String userID = remoteRepository.getProperty(ArgeoNames.ARGEO_USER_ID).getString();
+               if (userID.trim().equals("")) {
+                       return getRepository().login(workspaceName);
+               } else {
+                       String pwdPath = remoteRepository.getPath() + '/' + ArgeoNames.ARGEO_PASSWORD;
+                       char[] password = keyring.getAsChars(pwdPath);
+                       try {
+                               SimpleCredentials credentials = new SimpleCredentials(userID, password);
+                               return getRepository().login(credentials, workspaceName);
+                       } finally {
+                               Arrays.fill(password, 0, password.length, ' ');
+                       }
                }
        }
 
        @Override
        public Repository getRepository() {
                if (repository == null)
-                       repository = NodeUtils.getRepositoryByUri(repositoryFactory,
-                                       uri);
+                       repository = NodeUtils.getRepositoryByUri(repositoryFactory, uri);
                return super.getRepository();
        }
 
index b638bb21be67abd0c7572ed5b5cb0681d6716265..f25797109df64ba8314f4344e6adecf27d2717d1 100644 (file)
@@ -8,5 +8,6 @@ org.apache.jackrabbit.webdav.jcr,\
 org.eclipse.equinox.http.jetty,\
 org.springframework.context,\
 org.springframework.core.io,\
+org.apache.commons.httpclient.cookie;resolution:=optional,\
 *
 Provide-Capability: cms.datamodel;name=cms;cnd=/org/argeo/cms/cms.cnd;abstract=true
\ No newline at end of file
index 6dae68d1d9a4095d8a5ff161823c8084eec27b53..fb7a6123acba9bc94677c8caab12f37ae78ab9a1 100644 (file)
@@ -214,6 +214,15 @@ class CmsAuthUtils {
                return true;
        }
 
+       public static <T extends Principal> T getSinglePrincipal(Subject subject, Class<T> clss) {
+               Set<T> principals = subject.getPrincipals(clss);
+               if (principals.isEmpty())
+                       return null;
+               if (principals.size() > 1)
+                       throw new IllegalStateException("Only one " + clss + " principal expected in " + subject);
+               return principals.iterator().next();
+       }
+
        private CmsAuthUtils() {
 
        }
index 801e4af7c24d4c4dad48e8c74a755463d1cad9cf..4d2cc33390183f406a8fd1a71b2552ae479936d5 100644 (file)
@@ -1,37 +1,70 @@
 package org.argeo.cms.auth;
 
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.security.Principal;
 import java.util.Map;
 import java.util.Set;
 
+import javax.naming.ldap.LdapName;
 import javax.security.auth.Subject;
 import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.kerberos.KerberosPrincipal;
 import javax.security.auth.login.LoginException;
 import javax.security.auth.spi.LoginModule;
 import javax.security.auth.x500.X500Principal;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.argeo.cms.internal.auth.ImpliedByPrincipal;
+import org.argeo.naming.LdapAttrs;
 import org.argeo.node.NodeConstants;
 import org.argeo.node.security.DataAdminPrincipal;
+import org.argeo.osgi.useradmin.IpaUtils;
 
 public class SingleUserLoginModule implements LoginModule {
+       private final static Log log = LogFactory.getLog(SingleUserLoginModule.class);
+
        private Subject subject;
+       private Map<String, Object> sharedState = null;
 
+       @SuppressWarnings("unchecked")
        @Override
        public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
                        Map<String, ?> options) {
                this.subject = subject;
+               this.sharedState = (Map<String, Object>) sharedState;
        }
 
        @Override
        public boolean login() throws LoginException {
+               String username = System.getProperty("user.name");
+               if (!sharedState.containsKey(CmsAuthUtils.SHARED_STATE_NAME))
+                       sharedState.put(CmsAuthUtils.SHARED_STATE_NAME, username);
                return true;
        }
 
        @Override
        public boolean commit() throws LoginException {
-               String username = System.getProperty("user.name");
-               X500Principal principal = new X500Principal("uid=" + username + ",dc=localhost,dc=localdomain");
+               X500Principal principal;
+               KerberosPrincipal kerberosPrincipal = CmsAuthUtils.getSinglePrincipal(subject, KerberosPrincipal.class);
+               if (kerberosPrincipal != null) {
+                       LdapName userDn = IpaUtils.kerberosToDn(kerberosPrincipal.getName());
+                       principal = new X500Principal(userDn.toString());
+               } else {
+                       Object username = sharedState.get(CmsAuthUtils.SHARED_STATE_NAME);
+                       if (username == null)
+                               throw new LoginException("No username available");
+                       String hostname;
+                       try {
+                               hostname = InetAddress.getLocalHost().getHostName();
+                       } catch (UnknownHostException e) {
+                               log.warn("Using localhost as hostname", e);
+                               hostname = "localhost";
+                       }
+                       String baseDn = ("." + hostname).replaceAll("\\.", ",dc=");
+                       principal = new X500Principal(LdapAttrs.uid + "=" + username + baseDn);
+               }
                Set<Principal> principals = subject.getPrincipals();
                principals.add(principal);
                principals.add(new ImpliedByPrincipal(NodeConstants.ROLE_ADMIN, principal));
index ef2872e38c52fa452318d50dd4fc8f010c525bcd..27de54be35567496e81c14477b3ae3f9b4768eb5 100644 (file)
@@ -17,6 +17,7 @@ import org.ietf.jgss.GSSException;
 import org.ietf.jgss.GSSManager;
 import org.ietf.jgss.GSSName;
 
+/** SPNEGO login */
 public class SpnegoLoginModule implements LoginModule {
        private final static Log log = LogFactory.getLog(SpnegoLoginModule.class);
 
@@ -41,22 +42,20 @@ public class SpnegoLoginModule implements LoginModule {
                gssContext = checkToken(spnegoToken);
                if (gssContext == null)
                        return false;
-               try {
-                       String clientName = gssContext.getSrcName().toString();
-                       String role = clientName.substring(clientName.indexOf('@') + 1);
-
-                       log.debug("SpnegoUserRealm: established a security context");
-                       log.debug("Client Principal is: " + gssContext.getSrcName());
-                       log.debug("Server Principal is: " + gssContext.getTargName());
-                       log.debug("Client Default Role: " + role);
-               } catch (GSSException e) {
-                       // TODO Auto-generated catch block
-                       e.printStackTrace();
-               }
-
-               // TODO log in
-
-               return false;
+               else
+                       return true;
+               // try {
+               // String clientName = gssContext.getSrcName().toString();
+               // String role = clientName.substring(clientName.indexOf('@') + 1);
+               //
+               // log.debug("SpnegoUserRealm: established a security context");
+               // log.debug("Client Principal is: " + gssContext.getSrcName());
+               // log.debug("Server Principal is: " + gssContext.getTargName());
+               // log.debug("Client Default Role: " + role);
+               // } catch (GSSException e) {
+               // // TODO Auto-generated catch block
+               // e.printStackTrace();
+               // }
        }
 
        @Override
@@ -67,29 +66,47 @@ public class SpnegoLoginModule implements LoginModule {
                try {
                        Class<?> gssUtilsClass = Class.forName("com.sun.security.jgss.GSSUtil");
                        Method createSubjectMethod = gssUtilsClass.getMethod("createSubject", GSSName.class, GSSCredential.class);
-                       Subject gssSubject = (Subject) createSubjectMethod.invoke(null, gssContext.getSrcName(),
-                                       gssContext.getDelegCred());
+                       Subject gssSubject;
+                       if (gssContext.getCredDelegState())
+                               gssSubject = (Subject) createSubjectMethod.invoke(null, gssContext.getSrcName(),
+                                               gssContext.getDelegCred());
+                       else
+                               gssSubject = (Subject) createSubjectMethod.invoke(null, gssContext.getSrcName(), null);
                        subject.getPrincipals().addAll(gssSubject.getPrincipals());
                        subject.getPrivateCredentials().addAll(gssSubject.getPrivateCredentials());
                        return true;
                } catch (Exception e) {
-                       // TODO Auto-generated catch block
-                       e.printStackTrace();
-                       return false;
+                       throw new LoginException("Cannot commit SPNEGO " + e);
                }
 
        }
 
        @Override
        public boolean abort() throws LoginException {
-               // TODO Auto-generated method stub
-               return false;
+               if (gssContext != null) {
+                       try {
+                               gssContext.dispose();
+                       } catch (GSSException e) {
+                               if (log.isTraceEnabled())
+                                       log.warn("Could not abort", e);
+                       }
+                       gssContext = null;
+               }
+               return true;
        }
 
        @Override
        public boolean logout() throws LoginException {
-               // TODO Auto-generated method stub
-               return false;
+               if (gssContext != null) {
+                       try {
+                               gssContext.dispose();
+                       } catch (GSSException e) {
+                               if (log.isTraceEnabled())
+                                       log.warn("Could not abort", e);
+                       }
+                       gssContext = null;
+               }
+               return true;
        }
 
        private GSSContext checkToken(byte[] authToken) {
index 71049fac9bfb4db09753af3468ca2db9a86c52da..b571fcf8602519c10be1baa01d6a83302259fc6d 100644 (file)
@@ -75,6 +75,9 @@ public class CmsSessionImpl implements CmsSession {
 
        @Override
        public synchronized Session getDataSession(String cn, String workspace, Repository repository) {
+               // FIXME make it more robust
+               if (workspace == null)
+                       workspace = "main";
                String path = cn + '/' + workspace;
                if (dataSessionsInUse.contains(path)) {
                        try {
@@ -167,7 +170,7 @@ public class CmsSessionImpl implements CmsSession {
        }
 
        public String toString() {
-               return "CMS Session #" + localSessionId;
+               return "CMS Session local=" + localSessionId + ", uuid=" + uuid;
        }
 
        public static CmsSession getByLocalId(String localId) {
index c0284f4c72967b7630cff88622d64d9cf57a3fff..e398fc95641e18927fe1ed5cbdc0b192c1948005 100644 (file)
@@ -12,6 +12,8 @@ import javax.security.auth.login.LoginContext;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.jackrabbit.server.SessionProvider;
 import org.argeo.cms.CmsException;
 import org.argeo.cms.auth.CmsSession;
@@ -25,8 +27,7 @@ import org.argeo.node.NodeConstants;
 class CmsSessionProvider implements SessionProvider, Serializable {
        private static final long serialVersionUID = -1358136599534938466L;
 
-       // private final static Log log =
-       // LogFactory.getLog(CmsSessionProvider.class);
+       private final static Log log = LogFactory.getLog(CmsSessionProvider.class);
 
        private final String alias;
 
@@ -42,6 +43,9 @@ class CmsSessionProvider implements SessionProvider, Serializable {
                CmsSession cmsSession = WebCmsSessionImpl.getCmsSession(request);
                if (cmsSession == null)
                        return anonymousSession(request, rep, workspace);
+               if (log.isTraceEnabled()) {
+                       log.debug("Get JCR session from " + cmsSession);
+               }
                Session session = cmsSession.getDataSession(alias, workspace, rep);
                cmsSessions.put(session, cmsSession);
                return session;
index 581d6cbc1323c7add3783035da642a017c3fc5f3..b98419d8005dac5c7926c785f216a876bf630ad7 100644 (file)
@@ -2,30 +2,16 @@ package org.argeo.cms.internal.http;
 
 import java.io.IOException;
 import java.net.URL;
-import java.util.StringTokenizer;
 
-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.LoginContext;
 import javax.security.auth.login.LoginException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.auth.HttpRequestCallback;
 import org.argeo.cms.auth.HttpRequestCallbackHandler;
 import org.argeo.node.NodeConstants;
-import org.ietf.jgss.GSSContext;
-import org.ietf.jgss.GSSCredential;
-import org.ietf.jgss.GSSException;
-import org.ietf.jgss.GSSManager;
-import org.ietf.jgss.GSSName;
-import org.ietf.jgss.Oid;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.service.http.HttpContext;
@@ -36,7 +22,17 @@ class DataHttpContext implements HttpContext {
        private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
 
        // FIXME Make it more unique
-       private String httpAuthRealm = "Argeo";
+       private final String httpAuthRealm;
+       private final boolean forceBasic;
+
+       public DataHttpContext(String httpAuthrealm, boolean forceBasic) {
+               this.httpAuthRealm = httpAuthrealm;
+               this.forceBasic = forceBasic;
+       }
+
+       public DataHttpContext(String httpAuthrealm) {
+               this(httpAuthrealm, false);
+       }
 
        @Override
        public boolean handleSecurity(final HttpServletRequest request, HttpServletResponse response) throws IOException {
@@ -49,20 +45,24 @@ class DataHttpContext implements HttpContext {
                        lc.login();
                        // return true;
                } catch (LoginException e) {
-                       CallbackHandler token = extractHttpAuth(request, response);
-                       if (token != null) {
-                               try {
-                                       lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, token);
-                                       lc.login();
-                               } catch (LoginException e1) {
-                                       throw new CmsException("Could not login", e1);
-                               }
-                       } else {
-                               lc = processUnauthorized(request, response);
-                               if (lc == null)
-                                       return false;
-                       }
+                       // CallbackHandler token = extractHttpAuth(request, response);
+                       // String token = request.getHeader(HttpUtils.HEADER_AUTHORIZATION);
+                       // if (token != null) {
+                       // try {
+                       // lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER);
+                       // lc.login();
+                       // } catch (LoginException e1) {
+                       // throw new CmsException("Could not login", e1);
+                       // }
+                       // } else {
+                       lc = processUnauthorized(request, response);
+                       if (lc == null)
+                               return false;
+                       // }
                }
+
+               // still required by open session in view
+               // TODO remove it
                request.setAttribute(NodeConstants.LOGIN_CONTEXT_USER, lc);
                return true;
        }
@@ -90,91 +90,111 @@ class DataHttpContext implements HttpContext {
                }
        }
 
-       protected CallbackHandler extractHttpAuth(final HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
-               String authHeader = httpRequest.getHeader(HttpUtils.HEADER_AUTHORIZATION);
-               if (authHeader != null) {
-                       StringTokenizer st = new StringTokenizer(authHeader);
-                       if (st.hasMoreTokens()) {
-                               String basic = st.nextToken();
-                               if (basic.equalsIgnoreCase("Basic")) {
-                                       try {
-                                               // TODO manipulate char[]
-                                               String credentials = new String(Base64.decodeBase64(st.nextToken()), "UTF-8");
-                                               // log.debug("Credentials: " + credentials);
-                                               int p = credentials.indexOf(":");
-                                               if (p != -1) {
-                                                       final String login = credentials.substring(0, p).trim();
-                                                       final char[] password = credentials.substring(p + 1).trim().toCharArray();
-                                                       return new CallbackHandler() {
-                                                               public void handle(Callback[] callbacks) {
-                                                                       for (Callback cb : callbacks) {
-                                                                               if (cb instanceof NameCallback)
-                                                                                       ((NameCallback) cb).setName(login);
-                                                                               else if (cb instanceof PasswordCallback)
-                                                                                       ((PasswordCallback) cb).setPassword(password);
-                                                                               else if (cb instanceof HttpRequestCallback) {
-                                                                                       ((HttpRequestCallback) cb).setRequest(httpRequest);
-                                                                                       ((HttpRequestCallback) cb).setResponse(httpResponse);
-                                                                               }
-                                                                       }
-                                                               }
-                                                       };
-                                               } else {
-                                                       throw new CmsException("Invalid authentication token");
-                                               }
-                                       } catch (Exception e) {
-                                               throw new CmsException("Couldn't retrieve authentication", e);
-                                       }
-                               } else if (basic.equalsIgnoreCase("Negotiate")) {
-                                       // FIXME generalise
-                                       String _targetName = "HTTP/mostar.desktop.argeo.pro";
-                                       String spnegoToken = st.nextToken();
-                                       byte[] authToken = Base64.decodeBase64(spnegoToken);
-                                       GSSManager manager = GSSManager.getInstance();
-                                       try {
-                                               Oid krb5Oid = new Oid("1.3.6.1.5.5.2"); // http://java.sun.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html
-                                               GSSName gssName = manager.createName(_targetName, null);
-                                               GSSCredential serverCreds = manager.createCredential(gssName, GSSCredential.INDEFINITE_LIFETIME,
-                                                               krb5Oid, GSSCredential.ACCEPT_ONLY);
-                                               GSSContext gContext = manager.createContext(serverCreds);
-
-                                               if (gContext == null) {
-                                                       log.debug("SpnegoUserRealm: failed to establish GSSContext");
-                                               } else {
-                                                       while (!gContext.isEstablished()) {
-                                                               byte[] outToken = gContext.acceptSecContext(authToken, 0, authToken.length);
-                                                               String outTokenStr = Base64.encodeBase64String(outToken);
-                                                               httpResponse.setHeader("WWW-Authenticate", "Negotiate " + outTokenStr);
-                                                       }
-                                                       if (gContext.isEstablished()) {
-                                                               String clientName = gContext.getSrcName().toString();
-                                                               String role = clientName.substring(clientName.indexOf('@') + 1);
-
-                                                               log.debug("SpnegoUserRealm: established a security context");
-                                                               log.debug("Client Principal is: " + gContext.getSrcName());
-                                                               log.debug("Server Principal is: " + gContext.getTargName());
-                                                               log.debug("Client Default Role: " + role);
-
-                                                               // TODO log in
-                                                       }
-                                               }
-
-                                       } catch (GSSException gsse) {
-                                               log.warn(gsse, gsse);
-                                       }
-
-                               }
-                       }
-               }
-               return null;
-       }
+       // protected CallbackHandler extractHttpAuth(final HttpServletRequest
+       // httpRequest, HttpServletResponse httpResponse) {
+       // String authHeader =
+       // httpRequest.getHeader(HttpUtils.HEADER_AUTHORIZATION);
+       // if (authHeader != null) {
+       // StringTokenizer st = new StringTokenizer(authHeader);
+       // if (st.hasMoreTokens()) {
+       // String basic = st.nextToken();
+       // if (basic.equalsIgnoreCase("Basic")) {
+       // try {
+       // // TODO manipulate char[]
+       // String credentials = new String(Base64.decodeBase64(st.nextToken()),
+       // "UTF-8");
+       // // log.debug("Credentials: " + credentials);
+       // int p = credentials.indexOf(":");
+       // if (p != -1) {
+       // final String login = credentials.substring(0, p).trim();
+       // final char[] password = credentials.substring(p +
+       // 1).trim().toCharArray();
+       // return new CallbackHandler() {
+       // public void handle(Callback[] callbacks) {
+       // for (Callback cb : callbacks) {
+       // if (cb instanceof NameCallback)
+       // ((NameCallback) cb).setName(login);
+       // else if (cb instanceof PasswordCallback)
+       // ((PasswordCallback) cb).setPassword(password);
+       // else if (cb instanceof HttpRequestCallback) {
+       // ((HttpRequestCallback) cb).setRequest(httpRequest);
+       // ((HttpRequestCallback) cb).setResponse(httpResponse);
+       // }
+       // }
+       // }
+       // };
+       // } else {
+       // throw new CmsException("Invalid authentication token");
+       // }
+       // } catch (Exception e) {
+       // throw new CmsException("Couldn't retrieve authentication", e);
+       // }
+       // } else if (basic.equalsIgnoreCase("Negotiate")) {
+       // // FIXME generalise
+       // String _targetName;
+       // try {
+       // _targetName = NodeHttp.DEFAULT_SERVICE + "@"
+       // + InetAddress.getLocalHost().getCanonicalHostName();
+       // } catch (UnknownHostException e) {
+       // throw new CmsException("Cannot determins target name", e);
+       // }
+       // String spnegoToken = st.nextToken();
+       // byte[] authToken = Base64.decodeBase64(spnegoToken);
+       // GSSManager manager = GSSManager.getInstance();
+       // try {
+       //// Oid krb5Oid = new Oid("1.3.6.1.5.5.2"); //
+       // http://java.sun.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html
+       //// GSSName gssName = manager.createName(_targetName,
+       // GSSName.NT_HOSTBASED_SERVICE, krb5Oid);
+       //// GSSCredential serverCreds = manager.createCredential(gssName,
+       // GSSCredential.INDEFINITE_LIFETIME,
+       //// krb5Oid, GSSCredential.ACCEPT_ONLY);
+       // GSSCredential serverCreds = Activator.getAcceptorCredentials();
+       // if(serverCreds==null)
+       // throw new CmsException("No GSS server credentials available");
+       // GSSContext gContext = manager.createContext(serverCreds);
+       //
+       // if (gContext == null) {
+       // log.debug("SpnegoUserRealm: failed to establish GSSContext");
+       // } else {
+       // while (!gContext.isEstablished()) {
+       // byte[] outToken = gContext.acceptSecContext(authToken, 0,
+       // authToken.length);
+       // String outTokenStr = Base64.encodeBase64String(outToken);
+       // httpResponse.setHeader("WWW-Authenticate", "Negotiate " + outTokenStr);
+       // }
+       // if (gContext.isEstablished()) {
+       // String clientName = gContext.getSrcName().toString();
+       // String role = clientName.substring(clientName.indexOf('@') + 1);
+       //
+       // log.debug("SpnegoUserRealm: established a security context");
+       // log.debug("Client Principal is: " + gContext.getSrcName());
+       // log.debug("Server Principal is: " + gContext.getTargName());
+       // log.debug("Client Default Role: " + role);
+       //
+       // // TODO log in
+       // }
+       // }
+       //
+       // } catch (GSSException gsse) {
+       // log.warn(gsse, gsse);
+       // }
+       //
+       // }
+       // }
+       // }
+       // return null;
+       // }
 
        protected void askForWwwAuth(HttpServletRequest request, HttpServletResponse response) {
                response.setStatus(401);
-               response.setHeader(HttpUtils.HEADER_WWW_AUTHENTICATE, "basic realm=\"" + httpAuthRealm + "\"");
+               // response.setHeader(HttpUtils.HEADER_WWW_AUTHENTICATE, "basic
+               // realm=\"" + httpAuthRealm + "\"");
+               if (org.argeo.cms.internal.kernel.Activator.getAcceptorCredentials() != null && !forceBasic)// SPNEGO
+                       response.setHeader(HttpUtils.HEADER_WWW_AUTHENTICATE, "Negotiate");
+               else
+                       response.setHeader(HttpUtils.HEADER_WWW_AUTHENTICATE, "Basic realm=\"" + httpAuthRealm + "\"");
 
-               // SPNEGO
-               // response.setHeader(HEADER_WWW_AUTHENTICATE, "Negotiate");
                // response.setDateHeader("Date", System.currentTimeMillis());
                // response.setDateHeader("Expires", System.currentTimeMillis() + (24 *
                // 60 * 60 * 1000));
index 901731aa063eb2c113d1376c33e85477a1413f4e..98eee87d24d35445e843ccf03006b0cf381b7e71 100644 (file)
@@ -32,42 +32,20 @@ import org.osgi.util.tracker.ServiceTracker;
 public class NodeHttp implements KernelConstants {
        private final static Log log = LogFactory.getLog(NodeHttp.class);
 
-       // Filters
-       // private final RootFilter rootFilter;
-
-       // private final DoSFilter dosFilter;
-       // private final QoSFilter qosFilter;
+       public final static String DEFAULT_SERVICE = "HTTP";
 
        private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
 
        private ServiceTracker<Repository, Repository> repositories;
        private final ServiceTracker<HttpService, HttpService> httpServiceTracker;
 
-       public NodeHttp() {
-               // rootFilter = new RootFilter();
-               // dosFilter = new CustomDosFilter();
-               // qosFilter = new QoSFilter();
+       private String httpRealm = "Argeo";
 
+       public NodeHttp() {
                httpServiceTracker = new PrepareHttpStc();
                httpServiceTracker.open();
        }
 
-       // class CustomDosFilter extends DoSFilter {
-       // @Override
-       // protected String extractUserId(ServletRequest request) {
-       // HttpSession httpSession = ((HttpServletRequest) request)
-       // .getSession();
-       // if (isSessionAuthenticated(httpSession)) {
-       // String userId = ((SecurityContext) httpSession
-       // .getAttribute(SPRING_SECURITY_CONTEXT_KEY))
-       // .getAuthentication().getName();
-       // return userId;
-       // }
-       // return super.extractUserId(request);
-       //
-       // }
-       // }
-
        public void destroy() {
                repositories.close();
        }
@@ -111,7 +89,7 @@ public class NodeHttp implements KernelConstants {
                Properties ip = new Properties();
                ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, HttpUtils.WEBDAV_CONFIG);
                ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
-               httpService.registerServlet(path, webdavServlet, ip, new DataHttpContext());
+               httpService.registerServlet(path, webdavServlet, ip, new DataHttpContext(httpRealm));
        }
 
        void registerFilesServlet(HttpService httpService, String alias, Repository repository)
@@ -121,7 +99,7 @@ public class NodeHttp implements KernelConstants {
                Properties ip = new Properties();
                ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, HttpUtils.WEBDAV_CONFIG);
                ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
-               httpService.registerServlet(path, filesServlet, ip, new PrivateHttpContext());
+               httpService.registerServlet(path, filesServlet, ip, new PrivateHttpContext(httpRealm, true));
        }
 
        void registerRemotingServlet(HttpService httpService, String alias, Repository repository)
@@ -130,6 +108,7 @@ public class NodeHttp implements KernelConstants {
                String path = remotingPath(alias);
                Properties ip = new Properties();
                ip.setProperty(JcrRemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
+               ip.setProperty(JcrRemotingServlet.INIT_PARAM_AUTHENTICATE_HEADER, "Negotiate");
 
                // Looks like a bug in Jackrabbit remoting init
                Path tmpDir;
@@ -142,7 +121,7 @@ public class NodeHttp implements KernelConstants {
                ip.setProperty(RemotingServlet.INIT_PARAM_TMP_DIRECTORY, "remoting_" + alias);
                ip.setProperty(RemotingServlet.INIT_PARAM_PROTECTED_HANDLERS_CONFIG, HttpUtils.DEFAULT_PROTECTED_HANDLERS);
                ip.setProperty(RemotingServlet.INIT_PARAM_CREATE_ABSOLUTE_URI, "false");
-               httpService.registerServlet(path, remotingServlet, ip, new PrivateHttpContext());
+               httpService.registerServlet(path, remotingServlet, ip, new PrivateHttpContext(httpRealm));
        }
 
        private String webdavPath(String alias) {
@@ -304,6 +283,7 @@ public class NodeHttp implements KernelConstants {
        }
 
        private class RemotingServlet extends JcrRemotingServlet {
+               private final Log log = LogFactory.getLog(RemotingServlet.class);
                private static final long serialVersionUID = 4605238259548058883L;
                private final Repository repository;
                private final SessionProvider sessionProvider;
@@ -331,6 +311,8 @@ public class NodeHttp implements KernelConstants {
                        // Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
                        // @Override
                        // public Void run() throws Exception {
+                       if (log.isTraceEnabled())
+                               HttpUtils.logRequest(log, request);
                        RemotingServlet.super.service(request, response);
                        // return null;
                        // }
index e0d713224bfd76d00259aaadbad20858c071ed4e..2babd188b2c0843fcd11dfc1485c0f350dfc39da 100644 (file)
@@ -4,8 +4,17 @@ import javax.security.auth.login.LoginContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+/** Requests authorisation */
 class PrivateHttpContext extends DataHttpContext {
 
+       public PrivateHttpContext(String httpAuthrealm, boolean forceBasic) {
+               super(httpAuthrealm, forceBasic);
+       }
+
+       public PrivateHttpContext(String httpAuthrealm) {
+               super(httpAuthrealm);
+       }
+
        @Override
        protected LoginContext processUnauthorized(HttpServletRequest request, HttpServletResponse response) {
                askForWwwAuth(request, response);
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java b/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java
new file mode 100644 (file)
index 0000000..7d5278e
--- /dev/null
@@ -0,0 +1,166 @@
+package org.argeo.cms.internal.http.client;
+
+import java.net.URL;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Base64;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+
+import org.apache.commons.httpclient.Credentials;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.URIException;
+import org.apache.commons.httpclient.auth.AuthPolicy;
+import org.apache.commons.httpclient.auth.AuthScheme;
+import org.apache.commons.httpclient.auth.AuthenticationException;
+import org.apache.commons.httpclient.auth.CredentialsProvider;
+import org.apache.commons.httpclient.auth.MalformedChallengeException;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.params.DefaultHttpParams;
+import org.apache.commons.httpclient.params.HttpParams;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+
+/** Implementation of the SPNEGO auth scheme. */
+public class SpnegoAuthScheme implements AuthScheme {
+       private final static Log log = LogFactory.getLog(SpnegoAuthScheme.class);
+
+       public static final String NAME = "Negotiate";
+       private final static Oid KERBEROS_OID;
+       static {
+               try {
+                       KERBEROS_OID = new Oid("1.3.6.1.5.5.2");
+               } catch (GSSException e) {
+                       throw new IllegalStateException("Cannot create Kerberos OID", e);
+               }
+       }
+
+       private boolean complete = false;
+       private String realm;
+
+       @Override
+       public void processChallenge(String challenge) throws MalformedChallengeException {
+               log.debug("processChallenge " + challenge);
+
+       }
+
+       @Override
+       public String getSchemeName() {
+               return NAME;
+       }
+
+       @Override
+       public String getParameter(String name) {
+               log.debug("getParameter " + name);
+               return null;
+       }
+
+       @Override
+       public String getRealm() {
+               return realm;
+       }
+
+       @Override
+       public String getID() {
+               return NAME;
+       }
+
+       @Override
+       public boolean isConnectionBased() {
+               return true;
+       }
+
+       @Override
+       public boolean isComplete() {
+               log.debug("isComplete");
+               return complete;
+       }
+
+       @Override
+       public String authenticate(Credentials credentials, String method, String uri) throws AuthenticationException {
+               log.debug("authenticate " + method + " " + uri);
+               return null;
+       }
+
+       @Override
+       public String authenticate(Credentials credentials, HttpMethod method) throws AuthenticationException {
+               log.debug("authenticate " + method);
+               GSSContext context = null;
+               String tokenStr = null;
+               String hostname;
+               try {
+                       hostname = method.getURI().getHost();
+               } catch (URIException e1) {
+                       throw new IllegalStateException("Cannot authenticate", e1);
+               }
+               String serverPrinc = "HTTP@" + hostname;
+
+               try {
+                       // Get service's principal name
+                       GSSManager manager = GSSManager.getInstance();
+                       GSSName serverName = manager.createName(serverPrinc, GSSName.NT_HOSTBASED_SERVICE, KERBEROS_OID);
+
+                       // Get the context for authentication
+                       context = manager.createContext(serverName, KERBEROS_OID, null, GSSContext.DEFAULT_LIFETIME);
+                       // context.requestMutualAuth(true); // Request mutual authentication
+                       // context.requestConf(true); // Request confidentiality
+                       context.requestCredDeleg(true);
+
+                       byte[] token = new byte[0];
+
+                       // token is ignored on the first call
+                       token = context.initSecContext(token, 0, token.length);
+
+                       // Send a token to the server if one was generated by
+                       // initSecContext
+                       if (token != null) {
+                               tokenStr = Base64.getEncoder().encodeToString(token);
+                               // complete=true;
+                       }
+                       return "Negotiate " + tokenStr;
+               } catch (GSSException e) {
+                       throw new AuthenticationException("Cannot authenticate to " + serverPrinc, e);
+               }
+       }
+
+       public static void main(String[] args) {
+               if (args.length == 0) {
+                       System.err.println("usage: java " + SpnegoAuthScheme.class.getName() + " <url>");
+                       System.exit(1);
+                       return;
+               }
+               String url = args[0];
+
+               URL jaasUrl = SpnegoAuthScheme.class.getResource("jaas.cfg");
+               System.setProperty("java.security.auth.login.config", jaasUrl.toExternalForm());
+               try {
+                       LoginContext lc = new LoginContext("SINGLE_USER");
+                       lc.login();
+
+                       AuthPolicy.registerAuthScheme(SpnegoAuthScheme.NAME, SpnegoAuthScheme.class);
+                       HttpParams params = DefaultHttpParams.getDefaultParams();
+                       ArrayList<String> schemes = new ArrayList<>();
+                       schemes.add(SpnegoAuthScheme.NAME);
+                       params.setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, schemes);
+                       params.setParameter(CredentialsProvider.PROVIDER, new SpnegoCredentialProvider());
+
+                       int responseCode = Subject.doAs(lc.getSubject(), new PrivilegedExceptionAction<Integer>() {
+                               public Integer run() throws Exception {
+                                       HttpClient httpClient = new HttpClient();
+                                       return httpClient.executeMethod(new GetMethod(url));
+                               }
+                       });
+                       System.out.println("Reponse code: " + responseCode);
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+       }
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoCredentialProvider.java b/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoCredentialProvider.java
new file mode 100644 (file)
index 0000000..fff27c0
--- /dev/null
@@ -0,0 +1,18 @@
+package org.argeo.cms.internal.http.client;
+
+import org.apache.commons.httpclient.Credentials;
+import org.apache.commons.httpclient.auth.AuthScheme;
+import org.apache.commons.httpclient.auth.CredentialsNotAvailableException;
+import org.apache.commons.httpclient.auth.CredentialsProvider;
+
+/** SPNEGO credential provider */
+public class SpnegoCredentialProvider implements CredentialsProvider {
+
+       @Override
+       public Credentials getCredentials(AuthScheme scheme, String host, int port, boolean proxy)
+                       throws CredentialsNotAvailableException {
+               return new Credentials() {
+               };
+       }
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/client/jaas.cfg b/org.argeo.cms/src/org/argeo/cms/internal/http/client/jaas.cfg
new file mode 100644 (file)
index 0000000..21176b9
--- /dev/null
@@ -0,0 +1,5 @@
+SINGLE_USER {
+    com.sun.security.auth.module.Krb5LoginModule optional
+     principal="${user.name}"
+     useTicketCache=true;
+};
index 7983ea771428e740f771ec3b6f9b5cbba00c7e6c..34806762d2e5b1095bf2488717b874835afc4f93 100644 (file)
@@ -8,6 +8,7 @@ import java.net.UnknownHostException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
 import java.util.Iterator;
 
 import javax.security.auth.Subject;
@@ -20,9 +21,18 @@ import javax.security.auth.login.Configuration;
 import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
 
+import org.apache.commons.httpclient.auth.AuthPolicy;
+import org.apache.commons.httpclient.auth.CredentialsProvider;
+import org.apache.commons.httpclient.cookie.CookiePolicy;
+import org.apache.commons.httpclient.params.DefaultHttpParams;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+import org.apache.commons.httpclient.params.HttpParams;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.argeo.cms.CmsException;
+import org.argeo.cms.internal.http.NodeHttp;
+import org.argeo.cms.internal.http.client.SpnegoAuthScheme;
+import org.argeo.cms.internal.http.client.SpnegoCredentialProvider;
 import org.argeo.naming.DnsBrowser;
 import org.argeo.node.NodeConstants;
 import org.ietf.jgss.GSSCredential;
@@ -32,10 +42,10 @@ import org.ietf.jgss.GSSName;
 import org.ietf.jgss.Oid;
 
 /** Low-level kernel security */
-class CmsSecurity implements KernelConstants {
+public class CmsSecurity implements KernelConstants {
        private final static Log log = LogFactory.getLog(CmsSecurity.class);
        // http://java.sun.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html
-       private final static Oid KERBEROS_OID;
+       public final static Oid KERBEROS_OID;
        static {
                try {
                        KERBEROS_OID = new Oid("1.3.6.1.5.5.2");
@@ -61,7 +71,17 @@ class CmsSecurity implements KernelConstants {
 
        private Path nodeKeyTab = KernelUtils.getOsgiInstancePath(KernelConstants.NODE_KEY_TAB_PATH);
 
-       public CmsSecurity() {
+       CmsSecurity() {
+               // Register client-side SPNEGO auth scheme
+               AuthPolicy.registerAuthScheme(SpnegoAuthScheme.NAME, SpnegoAuthScheme.class);
+               HttpParams params = DefaultHttpParams.getDefaultParams();
+               ArrayList<String> schemes = new ArrayList<>();
+               schemes.add(SpnegoAuthScheme.NAME);
+               params.setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, schemes);
+               params.setParameter(CredentialsProvider.PROVIDER, new SpnegoCredentialProvider());
+               params.setParameter(HttpMethodParams.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY);
+//             params.setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
+
                if (!DeployConfig.isInitialized()) // first init
                        FirstInit.prepareInstanceArea();
 
@@ -96,7 +116,7 @@ class CmsSecurity implements KernelConstants {
                                res = DEPLOYED;
                        } else {
                                res = STANDALONE;
-                               kerberosDomain = null;
+                               // kerberosDomain = null;
                                // FIXME make state more robust
                        }
                } catch (UnknownHostException e) {
@@ -118,7 +138,8 @@ class CmsSecurity implements KernelConstants {
 
                CallbackHandler callbackHandler;
                if (Files.exists(nodeKeyTab)) {
-                       service = NodeConstants.NODE_SERVICE;
+                       service = NodeHttp.DEFAULT_SERVICE;
+                       // service = NodeConstants.NODE_SERVICE;
                        callbackHandler = new CallbackHandler() {
 
                                @Override
@@ -146,7 +167,7 @@ class CmsSecurity implements KernelConstants {
                        // throw new CmsException("Cannot create text callback handler", e);
                        // }
                        try {
-                               LoginContext kernelLc = new LoginContext(NodeConstants.LOGIN_CONTEXT_SINGLE_USER, nodeSubject);
+                               LoginContext kernelLc = new LoginContext(NodeConstants.LOGIN_CONTEXT_NODE, nodeSubject);
                                kernelLc.login();
                        } catch (LoginException e) {
                                throw new CmsException("Cannot log in kernel", e);
index 52bf4c37567456048d55a19d441dd27d21ae6d09..550de107484ebc3764b125889ac6a61247832efe 100644 (file)
@@ -20,7 +20,9 @@ NODE {
 
 SINGLE_USER {
     com.sun.security.auth.module.Krb5LoginModule optional
+     principal="${user.name}"
      storeKey=true
+     useTicketCache=true
      debug=true;
     org.argeo.cms.auth.SingleUserLoginModule requisite;
 };
index 83d36d927695cb9a7b7226e6eddcd39ee0043da8..9ce961b28c2f25fff87d9f32f3a904119a2d5e5e 100644 (file)
@@ -16,7 +16,11 @@ KEYRING {
 };
 
 SINGLE_USER {
-    com.sun.security.auth.module.UnixLoginModule requisite;
+    com.sun.security.auth.module.Krb5LoginModule optional
+     principal="${user.name}"
+     storeKey=true
+     useTicketCache=true
+     debug=true;
     org.argeo.cms.auth.SingleUserLoginModule requisite;
 };
 
index 978b9a6692b24de651bd02c16a59391b4356a86d..7486e3ecf25b38685d6fe8d996ba573f6325ff07 100644 (file)
@@ -120,7 +120,7 @@ public class LdapUserAdmin extends AbstractUserDirectory {
                                throw new UserDirectoryException("Unsupported LDAP type for " + name);
                        return res;
                } catch (NamingException e) {
-                       log.error("Cannot get role: " + e.getMessage());
+                       log.error("Cannot get role: " + name, e);
                        return null;
                }
        }