Work on authentication
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 8 Oct 2015 17:09:29 +0000 (17:09 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 8 Oct 2015 17:09:29 +0000 (17:09 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@8465 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

33 files changed:
org.argeo.cms/bnd.bnd
org.argeo.cms/src/org/argeo/cms/AbstractCmsEntryPoint.java
org.argeo.cms/src/org/argeo/cms/KernelHeader.java [deleted file]
org.argeo.cms/src/org/argeo/cms/auth/ArgeoLoginContext.java [deleted file]
org.argeo.cms/src/org/argeo/cms/auth/AuthConstants.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/auth/LoginCanceledException.java [deleted file]
org.argeo.cms/src/org/argeo/cms/auth/LoginRequiredException.java [deleted file]
org.argeo.cms/src/org/argeo/cms/auth/NodeContextLoginModule.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/auth/NodeUserLoginModule.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/auth/KernelLoginModule.java
org.argeo.cms/src/org/argeo/cms/internal/auth/SimpleJcrSecurityModel.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/auth/UserAdminLoginModule.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/kernel/DataHttp.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeAuthorization.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg
org.argeo.cms/src/org/argeo/cms/util/UserMenu.java
org.argeo.cms/src/org/argeo/cms/util/UserMenuLink.java
org.argeo.eclipse.ui.workbench/src/org/argeo/eclipse/ui/workbench/commands/AddRemoteRepository.java
org.argeo.eclipse.ui.workbench/src/org/argeo/eclipse/ui/workbench/jcr/internal/model/RepositoryElem.java
org.argeo.security.core/src/org/argeo/osgi/auth/BundleContextCallback.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/auth/BundleContextCallbackHandler.java [deleted file]
org.argeo.security.core/src/org/argeo/security/SecurityUtils.java
org.argeo.security.ui.rap/bnd.bnd
org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/AnonymousEntryPoint.java
org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java
org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureRapActivator.java
org.argeo.security.ui/src/org/argeo/security/ui/internal/CurrentUser.java

index e98fea91999d0b51f6158b8180102ff15d4cf47b..6525a72bf5d184ff1c727f9221236ce1adc4b8d6 100644 (file)
@@ -18,4 +18,6 @@ org.apache.jackrabbit.*;resolution:=optional,\
 org.joda.time.*;resolution:=optional,\
 org.springframework.context,\
 org.springframework.core.io,\
+org.apache.jackrabbit.webdav.server,\
+org.apache.jackrabbit.webdav.jcr,\
 *
index a9ad03a12470055dfac85116237bf5f825235dcc..9f3fee857636101bcdc844fa104df5fea408524a 100644 (file)
@@ -3,9 +3,7 @@ package org.argeo.cms;
 import java.security.AccessControlContext;
 import java.security.PrivilegedAction;
 import java.util.HashMap;
-import java.util.Locale;
 import java.util.Map;
-import java.util.ResourceBundle;
 
 import javax.jcr.Node;
 import javax.jcr.Property;
@@ -14,6 +12,7 @@ import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.nodetype.NodeType;
 import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
 import javax.security.auth.x500.X500Principal;
 import javax.servlet.http.HttpServletRequest;
@@ -22,9 +21,7 @@ import javax.servlet.http.HttpSession;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.argeo.ArgeoException;
-import org.argeo.cms.auth.ArgeoLoginContext;
-import org.argeo.cms.auth.LoginRequiredException;
-import org.argeo.cms.i18n.Msg;
+import org.argeo.cms.auth.AuthConstants;
 import org.argeo.jcr.JcrUtils;
 import org.eclipse.rap.rwt.RWT;
 import org.eclipse.rap.rwt.application.AbstractEntryPoint;
@@ -71,7 +68,7 @@ public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint
                HttpServletRequest httpRequest = RWT.getRequest();
                final HttpSession httpSession = httpRequest.getSession();
                AccessControlContext acc = (AccessControlContext) httpSession
-                               .getAttribute(KernelHeader.ACCESS_CONTROL_CONTEXT);
+                               .getAttribute(AuthConstants.ACCESS_CONTROL_CONTEXT);
                if (acc != null
                                && Subject.getSubject(acc).getPrincipals(X500Principal.class)
                                                .size() == 1) {
@@ -81,13 +78,13 @@ public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint
 
                        // Initial login
                        try {
-                               new ArgeoLoginContext(KernelHeader.LOGIN_CONTEXT_USER, subject)
+                               new LoginContext(AuthConstants.LOGIN_CONTEXT_USER, subject)
                                                .login();
                        } catch (LoginException e) {
                                // if (log.isTraceEnabled())
                                // log.trace("Cannot authenticate user", e);
                                try {
-                                       new ArgeoLoginContext(KernelHeader.LOGIN_CONTEXT_ANONYMOUS,
+                                       new LoginContext(AuthConstants.LOGIN_CONTEXT_ANONYMOUS,
                                                        subject).login();
                                } catch (LoginException eAnonymous) {
                                        throw new ArgeoException("Cannot initialize subject",
@@ -149,8 +146,9 @@ public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint
         */
        protected Node getDefaultNode(Session session) throws RepositoryException {
                if (!session.hasPermission(defaultPath, "read")) {
-                       if (session.getUserID().equals("anonymous"))
-                               throw new LoginRequiredException();
+                       if (session.getUserID().equals(AuthConstants.ROLE_ANONYMOUS))
+                               // TODO throw a special exception
+                               throw new CmsException("Login required");
                        else
                                throw new CmsException("Unauthorized");
                }
@@ -193,11 +191,11 @@ public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint
                                                } catch (Exception e) {
                                                        try {
                                                                // TODO find a less hacky way to log out
-                                                               new ArgeoLoginContext(
-                                                                               KernelHeader.LOGIN_CONTEXT_ANONYMOUS,
+                                                               new LoginContext(
+                                                                               AuthConstants.LOGIN_CONTEXT_ANONYMOUS,
                                                                                subject).logout();
-                                                               new ArgeoLoginContext(
-                                                                               KernelHeader.LOGIN_CONTEXT_ANONYMOUS,
+                                                               new LoginContext(
+                                                                               AuthConstants.LOGIN_CONTEXT_ANONYMOUS,
                                                                                subject).login();
                                                        } catch (LoginException eAnonymous) {
                                                                throw new ArgeoException(
diff --git a/org.argeo.cms/src/org/argeo/cms/KernelHeader.java b/org.argeo.cms/src/org/argeo/cms/KernelHeader.java
deleted file mode 100644 (file)
index 2e40491..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.argeo.cms;
-
-/** Public properties of the CMS Kernel */
-public interface KernelHeader {
-       final static String SECURITY_PROVIDER = "BC";// Bouncy Castle
-
-       // LOGIN CONTEXTS
-       final static String LOGIN_CONTEXT_USER = "USER";
-       final static String LOGIN_CONTEXT_ANONYMOUS = "ANONYMOUS";
-       final static String LOGIN_CONTEXT_SYSTEM = "SYSTEM";
-       final static String LOGIN_CONTEXT_SINGLE_USER = "SINGLE_USER";
-
-       // HTTP
-       final static String ACCESS_CONTROL_CONTEXT = "org.argeo.node.accessControlContext";
-
-       // RESERVED ROLES
-       public final static String ROLE_KERNEL = "OU=node";
-       public final static String ROLES_BASEDN = "ou=roles,ou=node";
-       public final static String ROLE_ADMIN = "cn=admin," + ROLES_BASEDN;
-       public final static String ROLE_GROUP_ADMIN = "cn=groupAdmin,"
-                       + ROLES_BASEDN;
-       public final static String ROLE_USER_ADMIN = "cn=userAdmin," + ROLES_BASEDN;
-       // Special system groups that cannot be edited:
-       // user U anonymous = everyone
-       public final static String ROLE_USER = "cn=user," + ROLES_BASEDN;
-       public final static String ROLE_ANONYMOUS = "cn=anonymous," + ROLES_BASEDN;
-
-       // RESERVED USERNAMES
-       public final static String USERNAME_ADMIN = "root";
-       public final static String USERNAME_DEMO = "demo";
-       @Deprecated
-       public final static String USERNAME_ANONYMOUS = "anonymous";
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/auth/ArgeoLoginContext.java b/org.argeo.cms/src/org/argeo/cms/auth/ArgeoLoginContext.java
deleted file mode 100644 (file)
index 474cfcc..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-package org.argeo.cms.auth;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-/**
- * Integrates JAAS with the Argeo platform, by using the Argeo CMS bundle
- * classloader as context classloader.
- */
-public class ArgeoLoginContext extends LoginContext {
-       private static ThreadLocal<ClassLoader> currentContextClassLoader = new ThreadLocal<ClassLoader>() {
-               @Override
-               protected ClassLoader initialValue() {
-                       return Thread.currentThread().getContextClassLoader();
-               }
-
-               @Override
-               public void set(ClassLoader value) {
-                       throw new IllegalAccessError("Current class loader is read-only");
-               }
-       };
-
-       public ArgeoLoginContext(String name, Subject subject,
-                       CallbackHandler callbackHandler) throws LoginException {
-               super(setContextClassLoaderForName(name), subject, callbackHandler);
-               // reset current context classloader
-               Thread.currentThread().setContextClassLoader(
-                               currentContextClassLoader.get());
-               currentContextClassLoader.remove();
-       }
-
-       public ArgeoLoginContext(String name, Subject subject)
-                       throws LoginException {
-               super(setContextClassLoaderForName(name), subject);
-               // reset current context classloader
-               Thread.currentThread().setContextClassLoader(
-                               currentContextClassLoader.get());
-               currentContextClassLoader.remove();
-       }
-
-       /**
-        * Set the context classloader
-        * 
-        * @return the passed name, in order to chain calls in the constructor
-        */
-       private static String setContextClassLoaderForName(String name) {
-               // store current context class loader;
-               currentContextClassLoader.get();
-               Thread.currentThread().setContextClassLoader(
-                               ArgeoLoginContext.class.getClassLoader());
-               return name;
-       }
-
-       @Override
-       public void login() throws LoginException {
-               super.login();
-       }
-
-       @Override
-       public void logout() throws LoginException {
-               super.logout();
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/auth/AuthConstants.java b/org.argeo.cms/src/org/argeo/cms/auth/AuthConstants.java
new file mode 100644 (file)
index 0000000..b5b3c77
--- /dev/null
@@ -0,0 +1,29 @@
+package org.argeo.cms.auth;
+
+/** Public properties of the CMS Kernel */
+public interface AuthConstants {
+       // LOGIN CONTEXTS
+       final static String LOGIN_CONTEXT_USER = "USER";
+       final static String LOGIN_CONTEXT_ANONYMOUS = "ANONYMOUS";
+       final static String LOGIN_CONTEXT_SYSTEM = "SYSTEM";
+       final static String LOGIN_CONTEXT_SINGLE_USER = "SINGLE_USER";
+
+       // HTTP
+       final static String ACCESS_CONTROL_CONTEXT = "org.argeo.node.accessControlContext";
+
+       // RESERVED ROLES
+       public final static String ROLE_KERNEL = "OU=node";
+       public final static String ROLES_BASEDN = "ou=roles,ou=node";
+       public final static String ROLE_ADMIN = "cn=admin," + ROLES_BASEDN;
+       public final static String ROLE_GROUP_ADMIN = "cn=groupAdmin,"
+                       + ROLES_BASEDN;
+       public final static String ROLE_USER_ADMIN = "cn=userAdmin," + ROLES_BASEDN;
+       // Special system groups that cannot be edited:
+       // user U anonymous = everyone
+       public final static String ROLE_USER = "cn=user," + ROLES_BASEDN;
+       public final static String ROLE_ANONYMOUS = "cn=anonymous," + ROLES_BASEDN;
+
+       // SHARED STATE KEYS
+       public final static String BUNDLE_CONTEXT_KEY = "org.argeo.security.bundleContext";
+       public final static String AUTHORIZATION_KEY = "org.argeo.security.authorization";
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/auth/LoginCanceledException.java b/org.argeo.cms/src/org/argeo/cms/auth/LoginCanceledException.java
deleted file mode 100644 (file)
index 731cdd1..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-package org.argeo.cms.auth;
-
-import javax.security.auth.login.LoginException;
-
-public class LoginCanceledException extends LoginException {
-       private static final long serialVersionUID = 8289162094013471043L;
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/auth/LoginRequiredException.java b/org.argeo.cms/src/org/argeo/cms/auth/LoginRequiredException.java
deleted file mode 100644 (file)
index 8b082d0..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.argeo.cms.auth;
-
-import org.argeo.cms.CmsException;
-
-/** Throwing this exception triggers redirection to a login page. */
-public class LoginRequiredException extends CmsException {
-       private static final long serialVersionUID = 7009402894657958151L;
-
-       public LoginRequiredException() {
-               super("Login is required");
-       }
-
-       public LoginRequiredException(String message, Throwable e) {
-               super(message, e);
-       }
-
-       public LoginRequiredException(String message) {
-               super(message);
-       }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/auth/NodeContextLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/NodeContextLoginModule.java
new file mode 100644 (file)
index 0000000..d898e6c
--- /dev/null
@@ -0,0 +1,72 @@
+package org.argeo.cms.auth;
+
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+import org.argeo.cms.internal.kernel.Activator;
+import org.eclipse.swt.widgets.Display;
+import org.osgi.service.useradmin.Authorization;
+
+/** Populates the shared state with this node context. */
+public class NodeContextLoginModule implements LoginModule, AuthConstants {
+       private Subject subject;
+       private Map<String, Object> sharedState;
+
+       @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 {
+               sharedState.put(AuthConstants.BUNDLE_CONTEXT_KEY, Activator.getBundleContext());
+               Display display = Display.getCurrent();
+               if (display != null) {
+                       Authorization authorization = (Authorization) display
+                                       .getData(AuthConstants.AUTHORIZATION_KEY);
+                       if (authorization != null)
+                               sharedState.put(AuthConstants.AUTHORIZATION_KEY, authorization);
+               }
+               return true;
+       }
+
+       @Override
+       public boolean commit() throws LoginException {
+               Display display = Display.getCurrent();
+               if (display != null) {
+                       Authorization authorization = subject
+                                       .getPrivateCredentials(Authorization.class).iterator()
+                                       .next();
+                       display.setData(AuthConstants.AUTHORIZATION_KEY, authorization);
+               }
+               return true;
+       }
+
+       @Override
+       public boolean abort() throws LoginException {
+               sharedState.remove(AuthConstants.BUNDLE_CONTEXT_KEY);
+               sharedState.remove(AuthConstants.AUTHORIZATION_KEY);
+               Display display = Display.getCurrent();
+               if (display != null)
+                       display.setData(AuthConstants.AUTHORIZATION_KEY, null);
+               return true;
+       }
+
+       @Override
+       public boolean logout() throws LoginException {
+               sharedState.remove(AuthConstants.BUNDLE_CONTEXT_KEY);
+               sharedState.remove(AuthConstants.AUTHORIZATION_KEY);
+               Display display = Display.getCurrent();
+               if (display != null)
+                       display.setData(AuthConstants.AUTHORIZATION_KEY, null);
+               return true;
+       }
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/auth/NodeUserLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/NodeUserLoginModule.java
new file mode 100644 (file)
index 0000000..74fe4e4
--- /dev/null
@@ -0,0 +1,156 @@
+package org.argeo.cms.auth;
+
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+import javax.security.auth.x500.X500Principal;
+
+import org.apache.jackrabbit.core.security.AnonymousPrincipal;
+import org.apache.jackrabbit.core.security.SecurityConstants;
+import org.apache.jackrabbit.core.security.principal.AdminPrincipal;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.internal.auth.ImpliedByPrincipal;
+import org.osgi.service.useradmin.Authorization;
+
+public class NodeUserLoginModule implements LoginModule {
+       private Subject subject;
+
+       private final static LdapName ROLE_KERNEL_NAME, ROLE_ADMIN_NAME,
+                       ROLE_ANONYMOUS_NAME, ROLE_USER_NAME;
+       private final static List<LdapName> RESERVED_ROLES;
+       private final static X500Principal ROLE_ANONYMOUS_PRINCIPAL;
+       static {
+               try {
+                       ROLE_KERNEL_NAME = new LdapName(AuthConstants.ROLE_KERNEL);
+                       ROLE_ADMIN_NAME = new LdapName(AuthConstants.ROLE_ADMIN);
+                       ROLE_USER_NAME = new LdapName(AuthConstants.ROLE_USER);
+                       ROLE_ANONYMOUS_NAME = new LdapName(AuthConstants.ROLE_ANONYMOUS);
+                       RESERVED_ROLES = Collections.unmodifiableList(Arrays
+                                       .asList(new LdapName[] { ROLE_KERNEL_NAME, ROLE_ADMIN_NAME,
+                                                       ROLE_ANONYMOUS_NAME, ROLE_USER_NAME,
+                                                       new LdapName(AuthConstants.ROLE_GROUP_ADMIN),
+                                                       new LdapName(AuthConstants.ROLE_USER_ADMIN) }));
+                       ROLE_ANONYMOUS_PRINCIPAL = new X500Principal(
+                                       ROLE_ANONYMOUS_NAME.toString());
+               } catch (InvalidNameException e) {
+                       throw new Error("Cannot initialize login module class", e);
+               }
+       }
+
+       private Authorization authorization;
+
+       @Override
+       public void initialize(Subject subject, CallbackHandler callbackHandler,
+                       Map<String, ?> sharedState, Map<String, ?> options) {
+               this.subject = subject;
+       }
+
+       @Override
+       public boolean login() throws LoginException {
+               Iterator<Authorization> auth = subject.getPrivateCredentials(
+                               Authorization.class).iterator();
+               if (!auth.hasNext())
+                       return false;
+               authorization = auth.next();
+               return true;
+       }
+
+       @Override
+       public boolean commit() throws LoginException {
+               if (authorization != null) {
+                       Set<Principal> principals = subject.getPrincipals();
+                       try {
+                               String authName = authorization.getName();
+
+                               // determine user's principal
+                               final LdapName name;
+                               final Principal userPrincipal;
+                               if (authName == null) {
+                                       name = ROLE_ANONYMOUS_NAME;
+                                       userPrincipal = ROLE_ANONYMOUS_PRINCIPAL;
+                                       principals.add(userPrincipal);
+                                       principals.add(new AnonymousPrincipal());
+                               } else {
+                                       name = new LdapName(authName);
+                                       checkUserName(name);
+                                       userPrincipal = new X500Principal(name.toString());
+                                       principals.add(userPrincipal);
+                                       principals.add(new ImpliedByPrincipal(ROLE_USER_NAME,
+                                                       userPrincipal));
+                               }
+
+                               // Add roles provided by authorization
+                               for (String role : authorization.getRoles()) {
+                                       LdapName roleName = new LdapName(role);
+                                       if (roleName.equals(name)) {
+                                               // skip
+                                       } else {
+                                               checkImpliedPrincipalName(roleName);
+                                               principals.add(new ImpliedByPrincipal(roleName
+                                                               .toString(), userPrincipal));
+                                               if (roleName.equals(ROLE_ADMIN_NAME))
+                                                       principals.add(new AdminPrincipal(
+                                                                       SecurityConstants.ADMIN_ID));
+                                       }
+                               }
+
+                               return true;
+                       } catch (InvalidNameException e) {
+                               throw new CmsException("Cannot commit", e);
+                       }
+               } else
+                       return false;
+       }
+
+       @Override
+       public boolean abort() throws LoginException {
+               cleanUp();
+               return true;
+       }
+
+       @Override
+       public boolean logout() throws LoginException {
+               // TODO better deal with successive logout
+               if (subject == null)
+                       return true;
+               // TODO make it less brutal
+               subject.getPrincipals().removeAll(
+                               subject.getPrincipals(X500Principal.class));
+               subject.getPrincipals().removeAll(
+                               subject.getPrincipals(ImpliedByPrincipal.class));
+               subject.getPrincipals().removeAll(
+                               subject.getPrincipals(AdminPrincipal.class));
+               subject.getPrincipals().removeAll(
+                               subject.getPrincipals(AnonymousPrincipal.class));
+               cleanUp();
+               return true;
+       }
+
+       private void cleanUp() {
+               subject = null;
+               authorization = null;
+       }
+
+       private void checkUserName(LdapName name) {
+               if (RESERVED_ROLES.contains(name))
+                       throw new CmsException(name + " is a reserved name");
+       }
+
+       private void checkImpliedPrincipalName(LdapName roleName) {
+               if (ROLE_USER_NAME.equals(roleName)
+                               || ROLE_ANONYMOUS_NAME.equals(roleName)
+                               || ROLE_KERNEL_NAME.equals(roleName))
+                       throw new CmsException(roleName + " cannot be listed as role");
+       }
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java
new file mode 100644 (file)
index 0000000..3e44e65
--- /dev/null
@@ -0,0 +1,114 @@
+package org.argeo.cms.auth;
+
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.CredentialNotFoundException;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+import org.argeo.ArgeoException;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.useradmin.Authorization;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+
+public class UserAdminLoginModule implements LoginModule, AuthConstants {
+       private Subject subject;
+       private Map<String, Object> sharedState;
+       private CallbackHandler callbackHandler;
+       private boolean isAnonymous = false;
+
+       @SuppressWarnings("unchecked")
+       @Override
+       public void initialize(Subject subject, CallbackHandler callbackHandler,
+                       Map<String, ?> sharedState, Map<String, ?> options) {
+               try {
+                       this.subject = subject;
+                       this.sharedState = (Map<String, Object>) sharedState;
+                       this.callbackHandler = callbackHandler;
+                       if (options.containsKey("anonymous"))
+                               isAnonymous = Boolean.parseBoolean(options.get("anonymous")
+                                               .toString());
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot initialize login module", e);
+               }
+       }
+
+       @Override
+       public boolean login() throws LoginException {
+               BundleContext bc = (BundleContext) sharedState
+                               .get(AuthConstants.BUNDLE_CONTEXT_KEY);
+               UserAdmin userAdmin = bc.getService(bc
+                               .getServiceReference(UserAdmin.class));
+               Authorization authorization = (Authorization) sharedState
+                               .get(AuthConstants.AUTHORIZATION_KEY);
+               if (authorization == null)
+                       if (!isAnonymous) {
+                               // ask for username and password
+                               NameCallback nameCallback = new NameCallback("User");
+                               PasswordCallback passwordCallback = new PasswordCallback(
+                                               "Password", false);
+
+                               // handle callbacks
+                               try {
+                                       callbackHandler.handle(new Callback[] { nameCallback,
+                                                       passwordCallback });
+                               } catch (Exception e) {
+                                       throw new ArgeoException("Cannot handle callbacks", e);
+                               }
+
+                               // create credentials
+                               final String username = nameCallback.getName();
+                               if (username == null || username.trim().equals(""))
+                                       throw new CredentialNotFoundException(
+                                                       "No credentials provided");
+
+                               char[] password = {};
+                               if (passwordCallback.getPassword() != null)
+                                       password = passwordCallback.getPassword();
+                               else
+                                       throw new CredentialNotFoundException(
+                                                       "No credentials provided");
+
+                               User user = userAdmin.getUser(null, username);
+                               if (user == null)
+                                       return false;
+                               if (!user.hasCredential(null, password))
+                                       return false;
+                               authorization = userAdmin.getAuthorization(user);
+                       } else {
+                               authorization = userAdmin.getAuthorization(null);
+                       }
+               subject.getPrivateCredentials().add(authorization);
+               return true;
+       }
+
+       @Override
+       public boolean commit() throws LoginException {
+               return true;
+       }
+
+       @Override
+       public boolean abort() throws LoginException {
+               cleanUp();
+               return true;
+       }
+
+       @Override
+       public boolean logout() throws LoginException {
+               cleanUp();
+               return true;
+       }
+
+       private void cleanUp() {
+               subject.getPrivateCredentials().removeAll(
+                               subject.getPrivateCredentials(Authorization.class));
+               subject = null;
+       }
+
+}
index f96bc88808b76e39a51331d1c622f7ca5a6ec3ad..8983d65dc7427367e64d92049321a202350ae1ed 100644 (file)
@@ -14,7 +14,7 @@ import javax.security.auth.x500.X500PrivateCredential;
 
 import org.apache.jackrabbit.core.security.SecurityConstants;
 import org.apache.jackrabbit.core.security.principal.AdminPrincipal;
-import org.argeo.cms.KernelHeader;
+import org.argeo.cms.auth.AuthConstants;
 
 public class KernelLoginModule implements LoginModule {
        private Subject subject;
@@ -39,9 +39,9 @@ public class KernelLoginModule implements LoginModule {
                if (names.isEmpty() || names.size() > 1)
                        throw new LoginException("Kernel must have been named");
                X500Principal name = names.iterator().next();
-               if (!KernelHeader.ROLE_KERNEL.equals(name.getName()))
+               if (!AuthConstants.ROLE_KERNEL.equals(name.getName()))
                        throw new LoginException("Kernel must be named named "
-                                       + KernelHeader.ROLE_KERNEL);
+                                       + AuthConstants.ROLE_KERNEL);
                // Private certificate
                Set<X500PrivateCredential> privateCerts = subject
                                .getPrivateCredentials(X500PrivateCredential.class);
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/SimpleJcrSecurityModel.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/SimpleJcrSecurityModel.java
new file mode 100644 (file)
index 0000000..7cb682c
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.cms.internal.auth;
+
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.security.Privilege;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
+
+/**
+ * Manages data expected by the Argeo security model, such as user home and
+ * profile.
+ */
+public class SimpleJcrSecurityModel implements JcrSecurityModel {
+       private final static Log log = LogFactory
+                       .getLog(SimpleJcrSecurityModel.class);
+       // ArgeoNames not implemented as interface in order to ease derivation by
+       // Jackrabbit bundles
+
+       /** The home base path. */
+       private String homeBasePath = "/home";
+       private String peopleBasePath = ArgeoJcrConstants.PEOPLE_BASE_PATH;
+
+       @Override
+       public void init(Session adminSession) throws RepositoryException {
+               JcrUtils.mkdirs(adminSession, homeBasePath);
+               JcrUtils.mkdirs(adminSession, peopleBasePath);
+               adminSession.save();
+
+               JcrUtils.addPrivilege(adminSession, homeBasePath,
+                               AuthConstants.ROLE_USER_ADMIN, Privilege.JCR_READ);
+               JcrUtils.addPrivilege(adminSession, peopleBasePath,
+                               AuthConstants.ROLE_USER_ADMIN, Privilege.JCR_ALL);
+       }
+
+       public synchronized Node sync(Session session, String username,
+                       List<String> roles) {
+               // TODO check user name validity (e.g. should not start by ROLE_)
+
+               try {
+                       Node userHome = UserJcrUtils.getUserHome(session, username);
+                       if (userHome == null) {
+                               String homePath = generateUserPath(homeBasePath, username);
+                               userHome = JcrUtils.mkdirs(session, homePath);
+                               // userHome = JcrUtils.mkfolders(session, homePath);
+                               userHome.addMixin(ArgeoTypes.ARGEO_USER_HOME);
+                               userHome.setProperty(ArgeoNames.ARGEO_USER_ID, username);
+                               session.save();
+
+                               JcrUtils.clearAccessControList(session, homePath, username);
+                               JcrUtils.addPrivilege(session, homePath, username,
+                                               Privilege.JCR_ALL);
+                       } else {
+                               // for backward compatibility with pre 1.0 security model
+                               if (userHome.hasNode(ArgeoNames.ARGEO_PROFILE)) {
+                                       userHome.getNode(ArgeoNames.ARGEO_PROFILE).remove();
+                                       userHome.getSession().save();
+                               }
+                       }
+
+                       // Remote roles
+                       if (roles != null) {
+                               // writeRemoteRoles(userHome, roles);
+                       }
+
+                       Node userProfile = UserJcrUtils.getUserProfile(session, username);
+                       // new user
+                       if (userProfile == null) {
+                               String personPath = generateUserPath(peopleBasePath, username);
+                               Node personBase = JcrUtils.mkdirs(session, personPath);
+                               userProfile = personBase.addNode(ArgeoNames.ARGEO_PROFILE);
+                               userProfile.addMixin(ArgeoTypes.ARGEO_USER_PROFILE);
+                               userProfile.setProperty(ArgeoNames.ARGEO_USER_ID, username);
+                               userProfile.setProperty(ArgeoNames.ARGEO_ENABLED, true);
+                               userProfile.setProperty(ArgeoNames.ARGEO_ACCOUNT_NON_EXPIRED,
+                                               true);
+                               userProfile.setProperty(ArgeoNames.ARGEO_ACCOUNT_NON_LOCKED,
+                                               true);
+                               userProfile.setProperty(
+                                               ArgeoNames.ARGEO_CREDENTIALS_NON_EXPIRED, true);
+                               session.save();
+
+                               JcrUtils.clearAccessControList(session, userProfile.getPath(),
+                                               username);
+                               JcrUtils.addPrivilege(session, userProfile.getPath(), username,
+                                               Privilege.JCR_READ);
+                       }
+
+                       // Remote roles
+                       if (roles != null) {
+                               writeRemoteRoles(userProfile, roles);
+                       }
+                       return userProfile;
+               } catch (RepositoryException e) {
+                       JcrUtils.discardQuietly(session);
+                       throw new ArgeoException("Cannot sync node security model for "
+                                       + username, e);
+               }
+       }
+
+       /** Generate path for a new user home */
+       protected String generateUserPath(String base, String username) {
+               int atIndex = username.indexOf('@');
+               if (atIndex > 0) {
+                       String domain = username.substring(0, atIndex);
+                       String name = username.substring(atIndex + 1);
+                       return base + '/' + JcrUtils.firstCharsToPath(domain, 2) + '/'
+                                       + domain + '/' + JcrUtils.firstCharsToPath(name, 2) + '/'
+                                       + name;
+               } else if (atIndex == 0 || atIndex == (username.length() - 1)) {
+                       throw new ArgeoException("Unsupported username " + username);
+               } else {
+                       return base + '/' + JcrUtils.firstCharsToPath(username, 2) + '/'
+                                       + username;
+               }
+       }
+
+       /** Write remote roles used by remote access in the home directory */
+       protected void writeRemoteRoles(Node userHome, List<String> roles)
+                       throws RepositoryException {
+               boolean writeRoles = false;
+               if (userHome.hasProperty(ArgeoNames.ARGEO_REMOTE_ROLES)) {
+                       Value[] remoteRoles = userHome.getProperty(
+                                       ArgeoNames.ARGEO_REMOTE_ROLES).getValues();
+                       if (remoteRoles.length != roles.size())
+                               writeRoles = true;
+                       else
+                               for (int i = 0; i < remoteRoles.length; i++)
+                                       if (!remoteRoles[i].getString().equals(roles.get(i)))
+                                               writeRoles = true;
+               } else
+                       writeRoles = true;
+
+               if (writeRoles) {
+                       userHome.getSession().getWorkspace().getVersionManager()
+                                       .checkout(userHome.getPath());
+                       String[] roleIds = roles.toArray(new String[roles.size()]);
+                       userHome.setProperty(ArgeoNames.ARGEO_REMOTE_ROLES, roleIds);
+                       JcrUtils.updateLastModified(userHome);
+                       userHome.getSession().save();
+                       userHome.getSession().getWorkspace().getVersionManager()
+                                       .checkin(userHome.getPath());
+                       if (log.isDebugEnabled())
+                               log.debug("Wrote remote roles " + roles + " for "
+                                               + userHome.getProperty(ArgeoNames.ARGEO_USER_ID));
+               }
+
+       }
+
+       public void setHomeBasePath(String homeBasePath) {
+               this.homeBasePath = homeBasePath;
+       }
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/UserAdminLoginModule.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/UserAdminLoginModule.java
deleted file mode 100644 (file)
index 5fca43b..0000000
+++ /dev/null
@@ -1,206 +0,0 @@
-package org.argeo.cms.internal.auth;
-
-import java.security.Principal;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.naming.InvalidNameException;
-import javax.naming.ldap.LdapName;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.login.CredentialNotFoundException;
-import javax.security.auth.login.LoginException;
-import javax.security.auth.spi.LoginModule;
-import javax.security.auth.x500.X500Principal;
-
-import org.apache.jackrabbit.core.security.AnonymousPrincipal;
-import org.apache.jackrabbit.core.security.SecurityConstants;
-import org.apache.jackrabbit.core.security.principal.AdminPrincipal;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.KernelHeader;
-import org.argeo.cms.internal.kernel.Activator;
-import org.osgi.framework.BundleContext;
-import org.osgi.service.useradmin.Authorization;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
-
-public class UserAdminLoginModule implements LoginModule {
-       private Subject subject;
-       private CallbackHandler callbackHandler;
-       private boolean isAnonymous = false;
-
-       private final static LdapName ROLE_KERNEL_NAME, ROLE_ADMIN_NAME,
-                       ROLE_ANONYMOUS_NAME, ROLE_USER_NAME;
-       private final static List<LdapName> RESERVED_ROLES;
-       private final static X500Principal ROLE_ANONYMOUS_PRINCIPAL;
-       static {
-               try {
-                       ROLE_KERNEL_NAME = new LdapName(KernelHeader.ROLE_KERNEL);
-                       ROLE_ADMIN_NAME = new LdapName(KernelHeader.ROLE_ADMIN);
-                       ROLE_USER_NAME = new LdapName(KernelHeader.ROLE_USER);
-                       ROLE_ANONYMOUS_NAME = new LdapName(KernelHeader.ROLE_ANONYMOUS);
-                       RESERVED_ROLES = Collections.unmodifiableList(Arrays
-                                       .asList(new LdapName[] { ROLE_KERNEL_NAME, ROLE_ADMIN_NAME,
-                                                       ROLE_ANONYMOUS_NAME, ROLE_USER_NAME,
-                                                       new LdapName(KernelHeader.ROLE_GROUP_ADMIN),
-                                                       new LdapName(KernelHeader.ROLE_USER_ADMIN) }));
-                       ROLE_ANONYMOUS_PRINCIPAL = new X500Principal(
-                                       ROLE_ANONYMOUS_NAME.toString());
-               } catch (InvalidNameException e) {
-                       throw new Error("Cannot initialize login module class", e);
-               }
-       }
-
-       private Authorization authorization;
-
-       @Override
-       public void initialize(Subject subject, CallbackHandler callbackHandler,
-                       Map<String, ?> sharedState, Map<String, ?> options) {
-               try {
-                       this.subject = subject;
-                       this.callbackHandler = callbackHandler;
-                       if (options.containsKey("anonymous"))
-                               isAnonymous = Boolean.parseBoolean(options.get("anonymous")
-                                               .toString());
-                       // String ldifFile = options.get("ldifFile").toString();
-                       // InputStream in = new URL(ldifFile).openStream();
-                       // userAdmin = new LdifUserAdmin(in);
-               } catch (Exception e) {
-                       throw new CmsException("Cannot initialize login module", e);
-               }
-       }
-
-       @Override
-       public boolean login() throws LoginException {
-               // TODO use a callback in order to get the bundle context
-               BundleContext bc = Activator.getBundleContext();
-               UserAdmin userAdmin = bc.getService(bc
-                               .getServiceReference(UserAdmin.class));
-               final User user;
-
-               if (!isAnonymous) {
-                       // ask for username and password
-                       NameCallback nameCallback = new NameCallback("User");
-                       PasswordCallback passwordCallback = new PasswordCallback(
-                                       "Password", false);
-                       // handle callbacks
-                       try {
-                               callbackHandler.handle(new Callback[] { nameCallback,
-                                               passwordCallback });
-                       } catch (Exception e) {
-                               throw new CmsException("Cannot handle callbacks", e);
-                       }
-
-                       // create credentials
-                       final String username = nameCallback.getName();
-                       if (username == null || username.trim().equals(""))
-                               throw new CredentialNotFoundException("No credentials provided");
-
-                       char[] password = {};
-                       if (passwordCallback.getPassword() != null)
-                               password = passwordCallback.getPassword();
-                       else
-                               throw new CredentialNotFoundException("No credentials provided");
-
-                       user = userAdmin.getUser(null, username);
-                       if (user == null)
-                               return false;
-                       if (!user.hasCredential(null, password))
-                               return false;
-               } else
-                       // anonymous
-                       user = null;
-               this.authorization = userAdmin.getAuthorization(user);
-               return true;
-       }
-
-       @Override
-       public boolean commit() throws LoginException {
-               if (authorization != null) {
-                       Set<Principal> principals = subject.getPrincipals();
-                       try {
-                               String authName = authorization.getName();
-
-                               // determine user's principal
-                               final LdapName name;
-                               final Principal userPrincipal;
-                               if (authName == null) {
-                                       name = ROLE_ANONYMOUS_NAME;
-                                       userPrincipal = ROLE_ANONYMOUS_PRINCIPAL;
-                                       principals.add(userPrincipal);
-                                       principals.add(new AnonymousPrincipal());
-                               } else {
-                                       name = new LdapName(authName);
-                                       checkUserName(name);
-                                       userPrincipal = new X500Principal(name.toString());
-                                       principals.add(userPrincipal);
-                                       principals.add(new ImpliedByPrincipal(ROLE_USER_NAME,
-                                                       userPrincipal));
-                               }
-
-                               // Add roles provided by authorization
-                               for (String role : authorization.getRoles()) {
-                                       LdapName roleName = new LdapName(role);
-                                       if (roleName.equals(name)) {
-                                               // skip
-                                       } else {
-                                               checkImpliedPrincipalName(roleName);
-                                               principals.add(new ImpliedByPrincipal(roleName
-                                                               .toString(), userPrincipal));
-                                               if (roleName.equals(ROLE_ADMIN_NAME))
-                                                       principals.add(new AdminPrincipal(
-                                                                       SecurityConstants.ADMIN_ID));
-                                       }
-                               }
-
-                               return true;
-                       } catch (InvalidNameException e) {
-                               throw new CmsException("Cannot commit", e);
-                       }
-               } else
-                       return false;
-       }
-
-       @Override
-       public boolean abort() throws LoginException {
-               cleanUp();
-               return true;
-       }
-
-       @Override
-       public boolean logout() throws LoginException {
-               // TODO better deal with successive logout
-               if (subject == null)
-                       return true;
-               // TODO make it less brutal
-               subject.getPrincipals().removeAll(
-                               subject.getPrincipals(X500Principal.class));
-               subject.getPrincipals().removeAll(
-                               subject.getPrincipals(ImpliedByPrincipal.class));
-               cleanUp();
-               return true;
-       }
-
-       private void cleanUp() {
-               subject = null;
-               authorization = null;
-       }
-
-       private void checkUserName(LdapName name) {
-               if (RESERVED_ROLES.contains(name))
-                       throw new CmsException(name + " is a reserved name");
-       }
-
-       private void checkImpliedPrincipalName(LdapName roleName) {
-               if (ROLE_USER_NAME.equals(roleName)
-                               || ROLE_ANONYMOUS_NAME.equals(roleName)
-                               || ROLE_KERNEL_NAME.equals(roleName))
-                       throw new CmsException(roleName + " cannot be listed as role");
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/DataHttp.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/DataHttp.java
new file mode 100644 (file)
index 0000000..102bb77
--- /dev/null
@@ -0,0 +1,387 @@
+package org.argeo.cms.internal.kernel;
+
+import static org.argeo.cms.auth.AuthConstants.ACCESS_CONTROL_CONTEXT;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.URL;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.cert.X509Certificate;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.server.SessionProvider;
+import org.apache.jackrabbit.server.remoting.davex.JcrRemotingServlet;
+import org.apache.jackrabbit.webdav.simple.SimpleWebdavServlet;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.JcrUtils;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.http.NamespaceException;
+import org.osgi.service.useradmin.Authorization;
+
+/**
+ * Intercepts and enriches http access, mainly focusing on security and
+ * transactionality.
+ */
+class DataHttp implements KernelConstants, ArgeoJcrConstants {
+       private final static Log log = LogFactory.getLog(DataHttp.class);
+
+       private final static String ATTR_AUTH = "auth";
+       private final static String HEADER_AUTHORIZATION = "Authorization";
+       private final static String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate";
+
+       // private final AuthenticationManager authenticationManager;
+       private final HttpService httpService;
+
+       // FIXME Make it more unique
+       private String httpAuthRealm = "Argeo";
+
+       // WebDav / JCR remoting
+       private OpenInViewSessionProvider sessionProvider;
+
+       DataHttp(HttpService httpService, JackrabbitNode node) {
+               this.httpService = httpService;
+               sessionProvider = new OpenInViewSessionProvider();
+               registerRepositoryServlets(ALIAS_NODE, node);
+       }
+
+       public void destroy() {
+               unregisterRepositoryServlets(ALIAS_NODE);
+       }
+
+       void registerRepositoryServlets(String alias, Repository repository) {
+               try {
+                       registerWebdavServlet(alias, repository, true);
+                       registerWebdavServlet(alias, repository, false);
+                       registerRemotingServlet(alias, repository, true);
+                       registerRemotingServlet(alias, repository, false);
+               } catch (Exception e) {
+                       throw new CmsException(
+                                       "Could not register servlets for repository " + alias, e);
+               }
+       }
+
+       void unregisterRepositoryServlets(String alias) {
+               // FIXME unregister servlets
+       }
+
+       void registerWebdavServlet(String alias, Repository repository,
+                       boolean anonymous) throws NamespaceException, ServletException {
+               WebdavServlet webdavServlet = new WebdavServlet(repository,
+                               sessionProvider);
+               String pathPrefix = anonymous ? WEBDAV_PUBLIC : WEBDAV_PRIVATE;
+               String path = pathPrefix + "/" + alias;
+               Properties ip = new Properties();
+               ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, WEBDAV_CONFIG);
+               ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
+               // httpService.registerFilter(path, anonymous ? new AnonymousFilter()
+               // : new DavFilter(), null, null);
+               // Cast to servlet because of a weird behaviour in Eclipse
+               httpService.registerServlet(path, (Servlet) webdavServlet, ip,
+                               new DataHttpContext(anonymous));
+       }
+
+       void registerRemotingServlet(String alias, Repository repository,
+                       boolean anonymous) throws NamespaceException, ServletException {
+               String pathPrefix = anonymous ? REMOTING_PUBLIC : REMOTING_PRIVATE;
+               RemotingServlet remotingServlet = new RemotingServlet(repository,
+                               sessionProvider);
+               String path = pathPrefix + "/" + alias;
+               Properties ip = new Properties();
+               ip.setProperty(JcrRemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
+
+               // Looks like a bug in Jackrabbit remoting init
+               ip.setProperty(RemotingServlet.INIT_PARAM_HOME,
+                               KernelUtils.getOsgiInstanceDir() + "/tmp/jackrabbit");
+               ip.setProperty(RemotingServlet.INIT_PARAM_TMP_DIRECTORY, "remoting");
+               // in order to avoid annoying warning.
+               ip.setProperty(RemotingServlet.INIT_PARAM_PROTECTED_HANDLERS_CONFIG, "");
+               // Cast to servlet because of a weird behaviour in Eclipse
+               // httpService.registerFilter(path, anonymous ? new AnonymousFilter()
+               // : new DavFilter(), null, null);
+               httpService.registerServlet(path, (Servlet) remotingServlet, ip,
+                               new DataHttpContext(anonymous));
+       }
+
+       // private Boolean isSessionAuthenticated(HttpSession httpSession) {
+       // SecurityContext contextFromSession = (SecurityContext) httpSession
+       // .getAttribute(SPRING_SECURITY_CONTEXT_KEY);
+       // return contextFromSession != null;
+       // }
+
+       private void requestBasicAuth(HttpSession httpSession,
+                       HttpServletResponse response) {
+               response.setStatus(401);
+               response.setHeader(HEADER_WWW_AUTHENTICATE, "basic realm=\""
+                               + httpAuthRealm + "\"");
+               httpSession.setAttribute(ATTR_AUTH, Boolean.TRUE);
+       }
+
+       private CallbackHandler basicAuth(String authHeader) {
+               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 {
+                                                       throw new CmsException(
+                                                                       "Invalid authentication token");
+                                               }
+                                       } catch (Exception e) {
+                                               throw new CmsException(
+                                                               "Couldn't retrieve authentication", e);
+                                       }
+                               }
+                       }
+               }
+               throw new CmsException("Couldn't retrieve authentication");
+       }
+
+       private X509Certificate extractCertificate(HttpServletRequest req) {
+               X509Certificate[] certs = (X509Certificate[]) req
+                               .getAttribute("javax.servlet.request.X509Certificate");
+               if (null != certs && certs.length > 0) {
+                       return certs[0];
+               }
+               return null;
+       }
+
+       private Subject subjectFromRequest(HttpServletRequest request) {
+               HttpSession httpSession = request.getSession();
+               Authorization authorization = (Authorization) request
+                               .getAttribute(HttpContext.AUTHORIZATION);
+               if (authorization == null)
+                       throw new CmsException("Not authenticated");
+               AccessControlContext acc = (AccessControlContext) httpSession
+                               .getAttribute(AuthConstants.ACCESS_CONTROL_CONTEXT);
+               Subject subject = Subject.getSubject(acc);
+               return subject;
+       }
+
+       private class DataHttpContext implements HttpContext {
+               private final boolean anonymous;
+
+               DataHttpContext(boolean anonymous) {
+                       this.anonymous = anonymous;
+               }
+
+               @Override
+               public boolean handleSecurity(HttpServletRequest request,
+                               HttpServletResponse response) throws IOException {
+                       final Subject subject;
+
+                       if (anonymous) {
+                               subject = KernelUtils.anonymousLogin();
+                               Authorization authorization = subject
+                                               .getPrivateCredentials(Authorization.class).iterator()
+                                               .next();
+                               request.setAttribute(AUTHORIZATION, authorization);
+                               return true;
+                       }
+
+                       final HttpSession httpSession = request.getSession();
+                       AccessControlContext acc = (AccessControlContext) httpSession
+                                       .getAttribute(AuthConstants.ACCESS_CONTROL_CONTEXT);
+                       if (acc != null) {
+                               subject = Subject.getSubject(acc);
+                       } else {
+                               // Process basic auth
+                               String basicAuth = request.getHeader(HEADER_AUTHORIZATION);
+                               if (basicAuth != null) {
+                                       CallbackHandler token = basicAuth(basicAuth);
+                                       try {
+                                               LoginContext lc = new LoginContext(
+                                                               AuthConstants.LOGIN_CONTEXT_USER, token);
+                                               lc.login();
+                                               subject = lc.getSubject();
+                                       } catch (LoginException e) {
+                                               throw new CmsException("Could not login", e);
+                                       }
+                                       Subject.doAs(subject, new PrivilegedAction<Void>() {
+                                               public Void run() {
+                                                       // add security context to session
+                                                       httpSession.setAttribute(ACCESS_CONTROL_CONTEXT,
+                                                                       AccessController.getContext());
+                                                       return null;
+                                               }
+                                       });
+                               } else {
+                                       requestBasicAuth(httpSession, response);
+                                       return false;
+                               }
+                       }
+                       // authenticate request
+                       Authorization authorization = subject
+                                       .getPrivateCredentials(Authorization.class).iterator()
+                                       .next();
+                       request.setAttribute(AUTHORIZATION, authorization);
+                       return true;
+               }
+
+               @Override
+               public URL getResource(String name) {
+                       return Activator.getBundleContext().getBundle().getResource(name);
+               }
+
+               @Override
+               public String getMimeType(String name) {
+                       return null;
+               }
+
+       }
+
+       /**
+        * Implements an open session in view patter: a new JCR session is created
+        * for each request
+        */
+       private class OpenInViewSessionProvider implements SessionProvider,
+                       Serializable {
+               private static final long serialVersionUID = 2270957712453841368L;
+
+               public Session getSession(HttpServletRequest request, Repository rep,
+                               String workspace) throws javax.jcr.LoginException,
+                               ServletException, RepositoryException {
+                       return login(request, rep, workspace);
+               }
+
+               protected Session login(HttpServletRequest request,
+                               Repository repository, String workspace)
+                               throws RepositoryException {
+                       if (log.isTraceEnabled())
+                               log.trace("Login to workspace "
+                                               + (workspace == null ? "<default>" : workspace)
+                                               + " in web session " + request.getSession().getId());
+                       return repository.login(workspace);
+               }
+
+               public void releaseSession(Session session) {
+                       JcrUtils.logoutQuietly(session);
+                       if (log.isTraceEnabled())
+                               log.trace("Logged out remote JCR session " + session);
+               }
+       }
+
+       private class WebdavServlet extends SimpleWebdavServlet {
+               private static final long serialVersionUID = -4687354117811443881L;
+               private final Repository repository;
+
+               public WebdavServlet(Repository repository,
+                               SessionProvider sessionProvider) {
+                       this.repository = repository;
+                       setSessionProvider(sessionProvider);
+               }
+
+               public Repository getRepository() {
+                       return repository;
+               }
+
+               @Override
+               protected void service(final HttpServletRequest request,
+                               final HttpServletResponse response) throws ServletException,
+                               IOException {
+                       try {
+                               Subject subject = subjectFromRequest(request);
+                               Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
+                                       @Override
+                                       public Void run() throws Exception {
+                                               WebdavServlet.super.service(request, response);
+                                               return null;
+                                       }
+                               });
+                       } catch (PrivilegedActionException e) {
+                               throw new CmsException("Cannot process webdav request",
+                                               e.getException());
+                       }
+               }
+       }
+
+       private class RemotingServlet extends JcrRemotingServlet {
+               private static final long serialVersionUID = 4605238259548058883L;
+               private final Repository repository;
+               private final SessionProvider sessionProvider;
+
+               public RemotingServlet(Repository repository,
+                               SessionProvider sessionProvider) {
+                       this.repository = repository;
+                       this.sessionProvider = sessionProvider;
+               }
+
+               @Override
+               protected Repository getRepository() {
+                       return repository;
+               }
+
+               @Override
+               protected SessionProvider getSessionProvider() {
+                       return sessionProvider;
+               }
+
+               @Override
+               protected void service(final HttpServletRequest request,
+                               final HttpServletResponse response) throws ServletException,
+                               IOException {
+                       try {
+                               Subject subject = subjectFromRequest(request);
+                               Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
+                                       @Override
+                                       public Void run() throws Exception {
+                                               RemotingServlet.super.service(request, response);
+                                               return null;
+                                       }
+                               });
+                       } catch (PrivilegedActionException e) {
+                               throw new CmsException("Cannot process JCR remoting request",
+                                               e.getException());
+                       }
+               }
+       }
+}
index cb47a11b36368f37361b8624053420e90a0b8e7b..8486f8d7ea6e31d6fc86b1a090c4d322cebaa192 100644 (file)
@@ -4,10 +4,10 @@ import java.lang.management.ManagementFactory;
 import java.security.PrivilegedAction;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Properties;
 
 import javax.jcr.Repository;
 import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
 import javax.security.auth.Subject;
 import javax.transaction.TransactionManager;
 import javax.transaction.TransactionSynchronizationRegistry;
@@ -26,7 +26,7 @@ import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceEvent;
 import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
-import org.osgi.service.http.HttpService;
+import org.osgi.service.useradmin.UserAdmin;
 import org.osgi.util.tracker.ServiceTracker;
 
 /**
@@ -48,14 +48,14 @@ final class Kernel implements ServiceListener {
 
        ThreadGroup threadGroup = new ThreadGroup(Kernel.class.getSimpleName());
        JackrabbitNode node;
-
+       private NodeUserAdmin userAdmin;
        private SimpleTransactionManager transactionManager;
        private OsgiJackrabbitRepositoryFactory repositoryFactory;
-       private NodeHttp nodeHttp;
+       private DataHttp nodeHttp;
        private KernelThread kernelThread;
 
        public Kernel() {
-               nodeSecurity = new NodeSecurity(bundleContext);
+               nodeSecurity = new NodeSecurity();
        }
 
        final void init() {
@@ -89,8 +89,11 @@ final class Kernel implements ServiceListener {
                        repositoryFactory = new OsgiJackrabbitRepositoryFactory();
 
                        // Authentication
-                       nodeSecurity.getUserAdmin().setTransactionManager(
-                                       transactionManager);
+                       Session adminSession = node.login();
+                       userAdmin = new NodeUserAdmin(adminSession);
+                       userAdmin.setTransactionManager(transactionManager);
+                       bundleContext.registerService(UserAdmin.class, userAdmin,
+                                       userAdmin.currentState());
 
                        // Equinox dependency
                        // ExtendedHttpService httpService = waitForHttpService();
@@ -114,7 +117,6 @@ final class Kernel implements ServiceListener {
                                        TransactionSynchronizationRegistry.class,
                                        transactionManager.getTransactionSynchronizationRegistry(),
                                        null);
-                       nodeSecurity.publish();
                        node.publish(repositoryFactory);
                        bundleContext.registerService(RepositoryFactory.class,
                                        repositoryFactory, null);
@@ -143,8 +145,8 @@ final class Kernel implements ServiceListener {
 
                if (nodeHttp != null)
                        nodeHttp.destroy();
-               // if (nodeSecurity != null)
-               // nodeSecurity.destroy();
+               if (userAdmin != null)
+                       userAdmin.destroy();
                if (node != null)
                        node.destroy();
 
@@ -200,14 +202,14 @@ final class Kernel implements ServiceListener {
        }
 
        private void addHttpService(ServiceReference<?> sr) {
-//             for (String key : sr.getPropertyKeys())
-//                     log.debug(key + "=" + sr.getProperty(key));
+               // for (String key : sr.getPropertyKeys())
+               // log.debug(key + "=" + sr.getProperty(key));
                ExtendedHttpService httpService = (ExtendedHttpService) bundleContext
                                .getService(sr);
                // TODO find constants
                Object httpPort = sr.getProperty("http.port");
                Object httpsPort = sr.getProperty("https.port");
-               nodeHttp = new NodeHttp(httpService, node);
+               nodeHttp = new DataHttp(httpService, node);
                if (log.isDebugEnabled())
                        log.debug("HTTP " + httpPort
                                        + (httpsPort != null ? " - HTTPS " + httpsPort : ""));
index 1d7e0868e1e6102bce439f7d7ad31fb3c3aec64a..b2fb03d8eaf7b0cfbc8d426f933e2241c45487c9 100644 (file)
@@ -15,7 +15,7 @@ import javax.servlet.http.HttpServletRequest;
 
 import org.apache.commons.logging.Log;
 import org.argeo.cms.CmsException;
-import org.argeo.cms.KernelHeader;
+import org.argeo.cms.auth.AuthConstants;
 
 /** Package utilities */
 class KernelUtils implements KernelConstants {
@@ -74,7 +74,7 @@ class KernelUtils implements KernelConstants {
                Subject subject = new Subject();
                LoginContext lc;
                try {
-                       lc = new LoginContext(KernelHeader.LOGIN_CONTEXT_ANONYMOUS, subject);
+                       lc = new LoginContext(AuthConstants.LOGIN_CONTEXT_ANONYMOUS, subject);
                        lc.login();
                        return subject;
                } catch (LoginException e) {
index 2846069359a2dcb44e95864f824517a70fa5215b..416f3bf756fa8cf108b124e3a54ca3537869bdc4 100644 (file)
@@ -6,6 +6,8 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
+import javax.security.auth.x500.X500Principal;
+
 import org.osgi.service.useradmin.Authorization;
 
 class NodeAuthorization implements Authorization {
@@ -16,7 +18,7 @@ class NodeAuthorization implements Authorization {
 
        public NodeAuthorization(String name, String displayName,
                        Collection<String> systemRoles, String[] roles) {
-               this.name = name;
+               this.name = new X500Principal(name).getName();
                this.displayName = displayName;
                this.systemRoles = Collections.unmodifiableList(new ArrayList<String>(
                                systemRoles));
index de7561aa04ae0155dd9a9522cf0fa2be68efe8b3..edcf719d7b3170edbfc7a6c61e40ca915e2ad5ad 100644 (file)
@@ -1,6 +1,6 @@
 package org.argeo.cms.internal.kernel;
 
-import static org.argeo.cms.KernelHeader.ACCESS_CONTROL_CONTEXT;
+import static org.argeo.cms.auth.AuthConstants.ACCESS_CONTROL_CONTEXT;
 
 import java.io.IOException;
 import java.security.AccessControlContext;
@@ -31,7 +31,7 @@ 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.KernelHeader;
+import org.argeo.cms.auth.AuthConstants;
 import org.argeo.jackrabbit.servlet.OpenInViewSessionProvider;
 import org.argeo.jackrabbit.servlet.RemotingServlet;
 import org.argeo.jackrabbit.servlet.WebdavServlet;
@@ -43,6 +43,7 @@ import org.osgi.service.http.NamespaceException;
  * Intercepts and enriches http access, mainly focusing on security and
  * transactionality.
  */
+@Deprecated
 class NodeHttp implements KernelConstants, ArgeoJcrConstants {
        private final static Log log = LogFactory.getLog(NodeHttp.class);
 
@@ -337,7 +338,7 @@ class NodeHttp implements KernelConstants, ArgeoJcrConstants {
                                ServletException {
 
                        AccessControlContext acc = (AccessControlContext) httpSession
-                                       .getAttribute(KernelHeader.ACCESS_CONTROL_CONTEXT);
+                                       .getAttribute(AuthConstants.ACCESS_CONTROL_CONTEXT);
                        final Subject subject;
                        if (acc != null) {
                                subject = Subject.getSubject(acc);
@@ -348,7 +349,7 @@ class NodeHttp implements KernelConstants, ArgeoJcrConstants {
                                        CallbackHandler token = basicAuth(basicAuth);
                                        try {
                                                LoginContext lc = new LoginContext(
-                                                               KernelHeader.LOGIN_CONTEXT_USER, token);
+                                                               AuthConstants.LOGIN_CONTEXT_USER, token);
                                                lc.login();
                                                subject = lc.getSubject();
                                        } catch (LoginException e) {
index 910953e30e60a1b1ad742de376cbaf37f1fe262c..b436ac8d11e1e07262736f72d6ef0c78a0b02764 100644 (file)
@@ -3,12 +3,10 @@ package org.argeo.cms.internal.kernel;
 import java.io.File;
 import java.io.IOException;
 import java.net.URL;
-import java.nio.file.ProviderNotFoundException;
 import java.security.KeyStore;
 import java.security.Provider;
 import java.security.Security;
 import java.util.Arrays;
-import java.util.Hashtable;
 
 import javax.security.auth.Subject;
 import javax.security.auth.callback.Callback;
@@ -23,15 +21,14 @@ import javax.security.auth.x500.X500Principal;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.argeo.cms.CmsException;
-import org.argeo.cms.KernelHeader;
+import org.argeo.cms.auth.AuthConstants;
 import org.argeo.security.crypto.PkiUtils;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.useradmin.UserAdmin;
 
 /** Authentication and user management. */
 class NodeSecurity {
+       final static String SECURITY_PROVIDER = "BC";// Bouncy Castle
+
        private final static Log log;
        static {
                log = LogFactory.getLog(NodeSecurity.class);
@@ -42,27 +39,21 @@ class NodeSecurity {
                        log.error("Provider " + provider.getName()
                                        + " already installed and could not be set as default");
                Provider defaultProvider = Security.getProviders()[0];
-               if (!defaultProvider.getName().equals(KernelHeader.SECURITY_PROVIDER))
+               if (!defaultProvider.getName().equals(SECURITY_PROVIDER))
                        log.error("Provider name is " + defaultProvider.getName()
-                                       + " but it should be " + KernelHeader.SECURITY_PROVIDER);
+                                       + " but it should be " + SECURITY_PROVIDER);
        }
 
-       private final BundleContext bundleContext;
-       private final NodeUserAdmin userAdmin;
        private final Subject kernelSubject;
 
-       private ServiceRegistration<UserAdmin> userAdminReg;
-
-       public NodeSecurity(BundleContext bundleContext) {
+       public NodeSecurity() {
                // Configure JAAS first
                URL url = getClass().getClassLoader().getResource(
                                KernelConstants.JAAS_CONFIG);
                System.setProperty("java.security.auth.login.config",
                                url.toExternalForm());
 
-               this.bundleContext = bundleContext;
                this.kernelSubject = logKernel();
-               userAdmin = new NodeUserAdmin();
        }
 
        private Subject logKernel() {
@@ -75,7 +66,7 @@ class NodeSecurity {
                        public void handle(Callback[] callbacks) throws IOException,
                                        UnsupportedCallbackException {
                                // alias
-                               ((NameCallback) callbacks[1]).setName(KernelHeader.ROLE_KERNEL);
+                               ((NameCallback) callbacks[1]).setName(AuthConstants.ROLE_KERNEL);
                                // store pwd
                                ((PasswordCallback) callbacks[2]).setPassword("changeit"
                                                .toCharArray());
@@ -95,15 +86,7 @@ class NodeSecurity {
                return kernelSubject;
        }
 
-       public void publish() {
-               userAdminReg = bundleContext.registerService(UserAdmin.class,
-                               userAdmin, userAdmin.currentState());
-               }
-
        void destroy() {
-               userAdmin.destroy();
-               userAdminReg.unregister();
-
                // Logout kernel
                try {
                        LoginContext kernelLc = new LoginContext(
@@ -113,11 +96,7 @@ class NodeSecurity {
                        throw new CmsException("Cannot log in kernel", e);
                }
 
-               Security.removeProvider(KernelHeader.SECURITY_PROVIDER);
-       }
-
-       public NodeUserAdmin getUserAdmin() {
-               return userAdmin;
+               Security.removeProvider(SECURITY_PROVIDER);
        }
 
        public Subject getKernelSubject() {
@@ -134,7 +113,7 @@ class NodeSecurity {
                                keyStoreFile.getParentFile().mkdirs();
                                KeyStore keyStore = PkiUtils.getKeyStore(keyStoreFile, ksPwd);
                                PkiUtils.generateSelfSignedCertificate(keyStore,
-                                               new X500Principal(KernelHeader.ROLE_KERNEL), keyPwd);
+                                               new X500Principal(AuthConstants.ROLE_KERNEL), keyPwd);
                                PkiUtils.saveKeyStore(keyStoreFile, ksPwd, keyStore);
                        } catch (Exception e) {
                                throw new CmsException("Cannot create key store "
index 31295ae89fac8f58b04fe3ba0ed78247a229c086..75cd44491cb20278714a5b96f138cda04978385d 100644 (file)
@@ -13,6 +13,11 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.security.Privilege;
 import javax.naming.InvalidNameException;
 import javax.naming.ldap.LdapName;
 import javax.transaction.TransactionManager;
@@ -20,8 +25,14 @@ import javax.transaction.TransactionManager;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
 import org.argeo.cms.CmsException;
-import org.argeo.cms.KernelHeader;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
 import org.argeo.osgi.useradmin.LdapUserAdmin;
 import org.argeo.osgi.useradmin.LdifUserAdmin;
 import org.argeo.osgi.useradmin.UserAdminConf;
@@ -38,7 +49,7 @@ public class NodeUserAdmin implements UserAdmin {
        final static LdapName ROLES_BASE;
        static {
                try {
-                       ROLES_BASE = new LdapName(KernelHeader.ROLES_BASEDN);
+                       ROLES_BASE = new LdapName(AuthConstants.ROLES_BASEDN);
                } catch (InvalidNameException e) {
                        throw new UserDirectoryException("Cannot initialize "
                                        + NodeUserAdmin.class, e);
@@ -48,7 +59,13 @@ public class NodeUserAdmin implements UserAdmin {
        private UserAdmin nodeRoles = null;
        private Map<LdapName, UserAdmin> userAdmins = new HashMap<LdapName, UserAdmin>();
 
-       public NodeUserAdmin() {
+       /** The home base path. */
+       private String homeBasePath = "/home";
+       private String peopleBasePath = ArgeoJcrConstants.PEOPLE_BASE_PATH;
+       private Session adminSession;
+
+       public NodeUserAdmin(Session adminSession) {
+               this.adminSession = adminSession;
                File osgiInstanceDir = KernelUtils.getOsgiInstanceDir();
                File nodeBaseDir = new File(osgiInstanceDir, "node");
                nodeBaseDir.mkdirs();
@@ -109,10 +126,10 @@ public class NodeUserAdmin implements UserAdmin {
                                                + u.getScheme() + "] enabled.");
                }
 
-               // NOde roles
+               // Node roles
                String nodeRolesUri = KernelUtils
                                .getFrameworkProp(KernelConstants.ROLES_URI);
-               String baseNodeRoleDn = KernelHeader.ROLES_BASEDN;
+               String baseNodeRoleDn = AuthConstants.ROLES_BASEDN;
                if (nodeRolesUri == null) {
                        File nodeRolesFile = new File(nodeBaseDir, baseNodeRoleDn + ".ldif");
                        if (!nodeRolesFile.exists())
@@ -143,6 +160,9 @@ public class NodeUserAdmin implements UserAdmin {
                addUserAdmin(baseNodeRoleDn, (UserAdmin) nodeRoles);
                if (log.isTraceEnabled())
                        log.trace("Node roles enabled.");
+
+               // JCR
+               initJcr(adminSession);
        }
 
        Dictionary<String, ?> currentState() {
@@ -214,7 +234,7 @@ public class NodeUserAdmin implements UserAdmin {
 
        @Override
        public Authorization getAuthorization(User user) {
-               if (user == null) {
+               if (user == null) {// anonymous
                        return nodeRoles.getAuthorization(null);
                }
                UserAdmin userAdmin = findUserAdmin(user.getName());
@@ -226,16 +246,18 @@ public class NodeUserAdmin implements UserAdmin {
                                        .getRole(role));
                        systemRoles.addAll(Arrays.asList(auth.getRoles()));
                }
-               return new NodeAuthorization(rawAuthorization.getName(),
-                               rawAuthorization.toString(), systemRoles,
-                               rawAuthorization.getRoles());
+               Authorization authorization = new NodeAuthorization(
+                               rawAuthorization.getName(), rawAuthorization.toString(),
+                               systemRoles, rawAuthorization.getRoles());
+               syncJcr(adminSession, authorization);
+               return authorization;
        }
 
        //
        // USER ADMIN AGGREGATOR
        //
        public synchronized void addUserAdmin(String baseDn, UserAdmin userAdmin) {
-               if (baseDn.equals(KernelHeader.ROLES_BASEDN)) {
+               if (baseDn.equals(AuthConstants.ROLES_BASEDN)) {
                        nodeRoles = userAdmin;
                        return;
                }
@@ -252,7 +274,7 @@ public class NodeUserAdmin implements UserAdmin {
        }
 
        public synchronized void removeUserAdmin(String baseDn) {
-               if (baseDn.equals(KernelHeader.ROLES_BASEDN))
+               if (baseDn.equals(AuthConstants.ROLES_BASEDN))
                        throw new UserDirectoryException("Node roles cannot be removed.");
                LdapName base;
                try {
@@ -302,4 +324,143 @@ public class NodeUserAdmin implements UserAdmin {
                                                .setTransactionManager(transactionManager);
                }
        }
+
+       /*
+        * JCR
+        */
+       private void initJcr(Session adminSession) {
+               try {
+                       JcrUtils.mkdirs(adminSession, homeBasePath);
+                       JcrUtils.mkdirs(adminSession, peopleBasePath);
+                       adminSession.save();
+
+                       JcrUtils.addPrivilege(adminSession, homeBasePath,
+                                       AuthConstants.ROLE_USER_ADMIN, Privilege.JCR_READ);
+                       JcrUtils.addPrivilege(adminSession, peopleBasePath,
+                                       AuthConstants.ROLE_USER_ADMIN, Privilege.JCR_ALL);
+                       adminSession.save();
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot initialize node user admin", e);
+               }
+       }
+
+       private Node syncJcr(Session session, Authorization authorization) {
+               // TODO check user name validity (e.g. should not start by ROLE_)
+               String username = authorization.getName();
+               String[] roles = authorization.getRoles();
+               try {
+                       Node userHome = UserJcrUtils.getUserHome(session, username);
+                       if (userHome == null) {
+                               String homePath = generateUserPath(homeBasePath, username);
+                               if (session.itemExists(homePath))// duplicate user id
+                                       userHome = session.getNode(homePath).getParent()
+                                                       .addNode(JcrUtils.lastPathElement(homePath));
+                               else
+                                       userHome = JcrUtils.mkdirs(session, homePath);
+                               // userHome = JcrUtils.mkfolders(session, homePath);
+                               userHome.addMixin(ArgeoTypes.ARGEO_USER_HOME);
+                               userHome.setProperty(ArgeoNames.ARGEO_USER_ID, username);
+                               session.save();
+
+                               JcrUtils.clearAccessControList(session, homePath, username);
+                               JcrUtils.addPrivilege(session, homePath, username,
+                                               Privilege.JCR_ALL);
+                       }
+
+                       Node userProfile = UserJcrUtils.getUserProfile(session, username);
+                       // new user
+                       if (userProfile == null) {
+                               String personPath = generateUserPath(peopleBasePath, username);
+                               Node personBase;
+                               if (session.itemExists(personPath))// duplicate user id
+                                       personBase = session.getNode(personPath).getParent()
+                                                       .addNode(JcrUtils.lastPathElement(personPath));
+                               else
+                                       personBase = JcrUtils.mkdirs(session, personPath);
+                               userProfile = personBase.addNode(ArgeoNames.ARGEO_PROFILE);
+                               userProfile.addMixin(ArgeoTypes.ARGEO_USER_PROFILE);
+                               userProfile.setProperty(ArgeoNames.ARGEO_USER_ID, username);
+                               userProfile.setProperty(ArgeoNames.ARGEO_ENABLED, true);
+                               userProfile.setProperty(ArgeoNames.ARGEO_ACCOUNT_NON_EXPIRED,
+                                               true);
+                               userProfile.setProperty(ArgeoNames.ARGEO_ACCOUNT_NON_LOCKED,
+                                               true);
+                               userProfile.setProperty(
+                                               ArgeoNames.ARGEO_CREDENTIALS_NON_EXPIRED, true);
+                               session.save();
+
+                               JcrUtils.clearAccessControList(session, userProfile.getPath(),
+                                               username);
+                               JcrUtils.addPrivilege(session, userProfile.getPath(), username,
+                                               Privilege.JCR_READ);
+                       }
+
+                       // Remote roles
+                       if (roles != null) {
+                               writeRemoteRoles(userProfile, roles);
+                       }
+                       adminSession.save();
+                       return userProfile;
+               } catch (RepositoryException e) {
+                       JcrUtils.discardQuietly(session);
+                       throw new ArgeoException("Cannot sync node security model for "
+                                       + username, e);
+               }
+       }
+
+       /** Generate path for a new user home */
+       private String generateUserPath(String base, String username) {
+               LdapName dn;
+               try {
+                       dn = new LdapName(username);
+               } catch (InvalidNameException e) {
+                       throw new ArgeoException("Invalid name " + username, e);
+               }
+               String userId = dn.getRdn(dn.size() - 1).getValue().toString();
+               int atIndex = userId.indexOf('@');
+               if (atIndex > 0) {
+                       String domain = userId.substring(0, atIndex);
+                       String name = userId.substring(atIndex + 1);
+                       return base + '/' + JcrUtils.firstCharsToPath(domain, 2) + '/'
+                                       + domain + '/' + JcrUtils.firstCharsToPath(name, 2) + '/'
+                                       + name;
+               } else if (atIndex == 0 || atIndex == (userId.length() - 1)) {
+                       throw new ArgeoException("Unsupported username " + userId);
+               } else {
+                       return base + '/' + JcrUtils.firstCharsToPath(userId, 2) + '/'
+                                       + userId;
+               }
+       }
+
+       /** Write remote roles used by remote access in the home directory */
+       private void writeRemoteRoles(Node userHome, String[] roles)
+                       throws RepositoryException {
+               boolean writeRoles = false;
+               if (userHome.hasProperty(ArgeoNames.ARGEO_REMOTE_ROLES)) {
+                       Value[] remoteRoles = userHome.getProperty(
+                                       ArgeoNames.ARGEO_REMOTE_ROLES).getValues();
+                       if (remoteRoles.length != roles.length)
+                               writeRoles = true;
+                       else
+                               for (int i = 0; i < remoteRoles.length; i++)
+                                       if (!remoteRoles[i].getString().equals(roles[i]))
+                                               writeRoles = true;
+               } else
+                       writeRoles = true;
+
+               if (writeRoles) {
+                       userHome.getSession().getWorkspace().getVersionManager()
+                                       .checkout(userHome.getPath());
+                       userHome.setProperty(ArgeoNames.ARGEO_REMOTE_ROLES, roles);
+                       JcrUtils.updateLastModified(userHome);
+                       userHome.getSession().save();
+                       userHome.getSession().getWorkspace().getVersionManager()
+                                       .checkin(userHome.getPath());
+                       if (log.isDebugEnabled())
+                               log.debug("Wrote remote roles " + roles + " for "
+                                               + userHome.getProperty(ArgeoNames.ARGEO_USER_ID));
+               }
+
+       }
+
 }
index f97cf9113b9b2a9e4f9d8372491d20d69b6e0182..4f647cf8a667a38f8f4f2a06a8bc2237310a2862 100644 (file)
@@ -1,19 +1,13 @@
 USER {
-    org.argeo.cms.internal.auth.UserAdminLoginModule requisite;
-};
-
-OLD_USER {
-    org.argeo.cms.internal.auth.EndUserLoginModule requisite;
-    org.springframework.security.authentication.jaas.SecurityContextLoginModule requisite;
+    org.argeo.cms.auth.NodeContextLoginModule requisite;
+    org.argeo.cms.auth.UserAdminLoginModule requisite;
+    org.argeo.cms.auth.NodeUserLoginModule requisite;
 };
 
 ANONYMOUS {
-    org.argeo.cms.internal.auth.UserAdminLoginModule requisite anonymous=true;
-};
-
-OLD_ANONYMOUS {
-    org.argeo.cms.internal.auth.AnonymousLoginModule requisite;
-    org.springframework.security.authentication.jaas.SecurityContextLoginModule requisite;
+    org.argeo.cms.auth.NodeContextLoginModule requisite;
+    org.argeo.cms.auth.UserAdminLoginModule requisite anonymous=true;
+    org.argeo.cms.auth.NodeUserLoginModule requisite;
 };
 
 SYSTEM {
@@ -26,11 +20,6 @@ KERNEL {
     org.argeo.cms.internal.auth.KernelLoginModule requisite;
 };
 
-OLD_SYSTEM {
-    org.argeo.cms.internal.auth.SystemLoginModule requisite;
-    org.springframework.security.authentication.jaas.SecurityContextLoginModule requisite;
-};
-
 KEYRING {
     org.argeo.security.crypto.KeyringLoginModule required;
 };
@@ -44,4 +33,3 @@ SINGLE_USER {
 Jackrabbit {
    org.argeo.security.jackrabbit.SystemJackrabbitLoginModule requisite;
 };
-
index acbf1bebba61668fd936c5be597b24854f6ffb64..fc651e65b3290f471dd7b3a7c7f5a285841088b1 100644 (file)
@@ -1,8 +1,8 @@
 package org.argeo.cms.util;
 
-import static org.argeo.cms.KernelHeader.ACCESS_CONTROL_CONTEXT;
-import static org.argeo.cms.KernelHeader.LOGIN_CONTEXT_ANONYMOUS;
-import static org.argeo.cms.KernelHeader.LOGIN_CONTEXT_USER;
+import static org.argeo.cms.auth.AuthConstants.ACCESS_CONTROL_CONTEXT;
+import static org.argeo.cms.auth.AuthConstants.LOGIN_CONTEXT_ANONYMOUS;
+import static org.argeo.cms.auth.AuthConstants.LOGIN_CONTEXT_USER;
 
 import java.io.IOException;
 import java.security.AccessController;
@@ -21,10 +21,10 @@ import javax.servlet.http.HttpSession;
 
 import org.argeo.cms.CmsException;
 import org.argeo.cms.CmsMsg;
-import org.argeo.cms.CmsView;
 import org.argeo.cms.CmsStyles;
-import org.argeo.cms.KernelHeader;
-import org.argeo.cms.auth.ArgeoLoginContext;
+import org.argeo.cms.CmsView;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.security.SecurityUtils;
 import org.eclipse.rap.rwt.RWT;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.MouseAdapter;
@@ -50,13 +50,8 @@ public class UserMenu extends Shell implements CmsStyles, CallbackHandler {
                super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
                setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU);
 
-               // Authentication authentication = SecurityContextHolder.getContext()
-               // .getAuthentication();
-               // if (authentication == null)
-               // throw new CmsException("No authentication available");
-
-               String username = CurrentUserUtils.getUsername();
-               if (username.equalsIgnoreCase(KernelHeader.ROLE_ANONYMOUS)) {
+               String username = SecurityUtils.getUsername(CmsUtils.getCmsView().getSubject());
+               if (username.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS)) {
                        username = null;
                        anonymousUi();
                } else {
@@ -86,15 +81,6 @@ public class UserMenu extends Shell implements CmsStyles, CallbackHandler {
                c.setLayout(new GridLayout());
                c.setLayoutData(CmsUtils.fillAll());
 
-               // String username = SecurityContextHolder.getContext()
-               // .getAuthentication().getName();
-               //
-               // Label l = new Label(c, SWT.NONE);
-               // l.setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU_ITEM);
-               // l.setData(RWT.MARKUP_ENABLED, true);
-               // l.setLayoutData(CmsUtils.fillWidth());
-               // l.setText("<b>" + username + "</b>");
-
                specificUserUi(c);
 
                Label l = new Label(c, SWT.NONE);
@@ -113,12 +99,6 @@ public class UserMenu extends Shell implements CmsStyles, CallbackHandler {
                });
        }
 
-       // protected String getUsername() {
-       // // String username = SecurityContextHolder.getContext()
-       // // .getAuthentication().getName();
-       // return CurrentUserUtils.getUsername();
-       // }
-
        /** To be overridden */
        protected void specificUserUi(Composite parent) {
 
@@ -168,16 +148,15 @@ public class UserMenu extends Shell implements CmsStyles, CallbackHandler {
        }
 
        protected void login() {
-               CmsView cmsSession = (CmsView) getDisplay().getData(
-                               CmsView.KEY);
+               CmsView cmsSession = (CmsView) getDisplay().getData(CmsView.KEY);
                Subject subject = cmsSession.getSubject();
                try {
                        //
                        // LOGIN
                        //
-                       new ArgeoLoginContext(LOGIN_CONTEXT_ANONYMOUS, subject).logout();
-                       LoginContext loginContext = new ArgeoLoginContext(
-                                       LOGIN_CONTEXT_USER, subject, this);
+                       new LoginContext(LOGIN_CONTEXT_ANONYMOUS, subject).logout();
+                       LoginContext loginContext = new LoginContext(LOGIN_CONTEXT_USER,
+                                       subject, this);
                        loginContext.login();
 
                        // save context in session
@@ -193,7 +172,7 @@ public class UserMenu extends Shell implements CmsStyles, CallbackHandler {
                        });
                } catch (LoginException e1) {
                        try {
-                               new ArgeoLoginContext(LOGIN_CONTEXT_ANONYMOUS, subject).login();
+                               new LoginContext(LOGIN_CONTEXT_ANONYMOUS, subject).login();
                        } catch (LoginException e) {
                                throw new CmsException("Cannot authenticate anonymous", e1);
                        }
@@ -205,15 +184,14 @@ public class UserMenu extends Shell implements CmsStyles, CallbackHandler {
        }
 
        protected void logout() {
-               final CmsView cmsSession = (CmsView) getDisplay().getData(
-                               CmsView.KEY);
+               final CmsView cmsSession = (CmsView) getDisplay().getData(CmsView.KEY);
                Subject subject = cmsSession.getSubject();
                try {
                        //
                        // LOGOUT
                        //
-                       new ArgeoLoginContext(LOGIN_CONTEXT_USER, subject).logout();
-                       new ArgeoLoginContext(LOGIN_CONTEXT_ANONYMOUS, subject).login();
+                       new LoginContext(LOGIN_CONTEXT_USER, subject).logout();
+                       new LoginContext(LOGIN_CONTEXT_ANONYMOUS, subject).login();
 
                        HttpServletRequest httpRequest = RWT.getRequest();
                        HttpSession httpSession = httpRequest.getSession();
index 0dbb4ac5adf0429e678a1947f2ca4a339e08056f..f4c5f006305deeaff642958046c615239a169cdf 100644 (file)
@@ -4,7 +4,8 @@ import javax.jcr.Node;
 
 import org.argeo.cms.CmsMsg;
 import org.argeo.cms.CmsStyles;
-import org.argeo.cms.KernelHeader;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.security.SecurityUtils;
 import org.eclipse.swt.events.DisposeEvent;
 import org.eclipse.swt.events.DisposeListener;
 import org.eclipse.swt.events.MouseEvent;
@@ -22,13 +23,14 @@ public class UserMenuLink extends MenuLink {
 
        @Override
        public Control createUi(Composite parent, Node context) {
-               // String username = SecurityContextHolder.getContext()
-               // .getAuthentication().getName();
-               String username = CurrentUserUtils.getUsername();
-               if (username.equalsIgnoreCase(KernelHeader.ROLE_ANONYMOUS))
+               String username = SecurityUtils.getUsername(CmsUtils.getCmsView()
+                               .getSubject());
+               if (username.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS))
                        setLabel(CmsMsg.login.lead());
-               else
-                       setLabel(username);
+               else {
+                       setLabel(SecurityUtils.getDisplayName(CmsUtils.getCmsView()
+                                       .getSubject()));
+               }
                Label link = (Label) ((Composite) super.createUi(parent, context))
                                .getChildren()[0];
                link.addMouseListener(new UserMenuLinkController());
index 6ed419f0436372deaf4d8e8363fc152c5871f012..2ee8f896384cbd0a0fa0a07fa08e395fb5e4ace7 100644 (file)
@@ -153,7 +153,7 @@ public class AddRemoteRepository extends AbstractHandler implements
                                        char[] pwd = password.getText().toCharArray();
                                        SimpleCredentials sc = new SimpleCredentials(
                                                        username.getText(), pwd);
-                                       session = repository.login(sc);
+                                       session = repository.login(sc, "main");
                                        MessageDialog.openInformation(getParentShell(), "Success",
                                                        "Connection to '" + uri.getText() + "' successful");
                                }
index 6a42bd8bd9b50f2234fd9510faecff340400ca5b..422e5cfbe45788406b8fd271a352a862d4ecca2a 100644 (file)
@@ -43,7 +43,7 @@ public class RepositoryElem extends TreeParent {
 
        public void login() {
                try {
-                       defaultSession = repositoryLogin(null);
+                       defaultSession = repositoryLogin("main");
                        String[] wkpNames = defaultSession.getWorkspace()
                                        .getAccessibleWorkspaceNames();
                        for (String wkpName : wkpNames) {
diff --git a/org.argeo.security.core/src/org/argeo/osgi/auth/BundleContextCallback.java b/org.argeo.security.core/src/org/argeo/osgi/auth/BundleContextCallback.java
deleted file mode 100644 (file)
index 193da34..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.argeo.osgi.auth;
-
-import javax.security.auth.callback.Callback;
-import javax.security.auth.spi.LoginModule;
-
-import org.osgi.framework.BundleContext;
-
-/** Allows a {@link LoginModule} to as for a {@link BundleContext} */
-public class BundleContextCallback implements Callback {
-       private BundleContext bundleContext;
-
-       public BundleContext getBundleContext() {
-               return bundleContext;
-       }
-
-       public void setBundleContext(BundleContext bundleContext) {
-               this.bundleContext = bundleContext;
-       }
-
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/auth/BundleContextCallbackHandler.java b/org.argeo.security.core/src/org/argeo/osgi/auth/BundleContextCallbackHandler.java
deleted file mode 100644 (file)
index 37733e0..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.argeo.osgi.auth;
-
-import java.io.IOException;
-
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.UnsupportedCallbackException;
-
-import org.osgi.framework.BundleContext;
-
-public class BundleContextCallbackHandler implements CallbackHandler {
-       private final BundleContext bundleContext;
-
-       public BundleContextCallbackHandler(BundleContext bundleContext) {
-               this.bundleContext = bundleContext;
-       }
-
-       @Override
-       public void handle(Callback[] callbacks) throws IOException,
-                       UnsupportedCallbackException {
-               for (Callback callback : callbacks) {
-                       if (!(callback instanceof BundleContextCallback))
-                               throw new UnsupportedCallbackException(callback);
-                       ((BundleContextCallback) callback).setBundleContext(bundleContext);
-               }
-
-       }
-
-}
index b3b0f37f3687d7f66fea691e46083f3617e909b4..e1f7899a5f52227515f0e24b3157e64e7c32446d 100644 (file)
@@ -26,6 +26,7 @@ import javax.security.auth.Subject;
 import javax.security.auth.x500.X500Principal;
 
 import org.argeo.ArgeoException;
+import org.osgi.service.useradmin.Authorization;
 
 /** Static utilities */
 public final class SecurityUtils {
@@ -42,13 +43,16 @@ public final class SecurityUtils {
         *         anonymous
         */
        public static String getCurrentThreadUsername() {
-               return getUsername();
-       }
-
-       public final static String getUsername() {
                Subject subject = Subject.getSubject(AccessController.getContext());
                if (subject == null)
                        return null;
+               return getUsername(subject);
+       }
+
+       public final static String getUsername(Subject subject) {
+               // Subject subject = Subject.getSubject(AccessController.getContext());
+               // if (subject == null)
+               // return null;
                if (subject.getPrincipals(X500Principal.class).size() != 1)
                        return null;
                Principal principal = subject.getPrincipals(X500Principal.class)
@@ -57,6 +61,15 @@ public final class SecurityUtils {
 
        }
 
+       public final static String getDisplayName(Subject subject) {
+               return getAuthorization(subject).toString();
+       }
+
+       public final static Authorization getAuthorization(Subject subject) {
+               return subject.getPrivateCredentials(Authorization.class).iterator()
+                               .next();
+       }
+
        public final static Set<String> roles() {
                Set<String> roles = Collections.synchronizedSet(new HashSet<String>());
                Subject subject = Subject.getSubject(AccessController.getContext());
index c4b0deee7f3b98d13c65bf352a9dc9deddb0ef9e..dffe8ff61eeb644625ffa4674f1994e134ec9807 100644 (file)
@@ -6,5 +6,6 @@ Require-Bundle: org.eclipse.rap.ui,org.eclipse.core.runtime
 Import-Package: org.argeo.eclipse.spring,\
 org.argeo.eclipse.ui.specific,\
 org.argeo.cms,\
+org.argeo.cms.auth,\
 org.argeo.security.ui,\
 *
index 99536faa11827574aa64ba2bd791a8fec786ac16..7c1702d8d02d10c6eb282bbde018782c87799282 100644 (file)
@@ -24,8 +24,7 @@ import javax.security.auth.login.LoginException;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.argeo.ArgeoException;
-import org.argeo.cms.KernelHeader;
-import org.argeo.cms.auth.ArgeoLoginContext;
+import org.argeo.cms.auth.AuthConstants;
 import org.eclipse.rap.rwt.RWT;
 import org.eclipse.rap.rwt.application.EntryPoint;
 import org.eclipse.swt.widgets.Display;
@@ -57,8 +56,8 @@ public class AnonymousEntryPoint implements EntryPoint {
 
                final LoginContext loginContext;
                try {
-                       loginContext = new ArgeoLoginContext(
-                                       KernelHeader.LOGIN_CONTEXT_ANONYMOUS, subject);
+                       loginContext = new LoginContext(AuthConstants.LOGIN_CONTEXT_ANONYMOUS,
+                                       subject);
                        loginContext.login();
                } catch (LoginException e1) {
                        throw new ArgeoException("Cannot initialize login context", e1);
index c27fbc70e4dda02e09b089cf764dad9588d945fe..6a582f83f75e948dc60b8fb9680e3aa9b49033a4 100644 (file)
@@ -15,7 +15,7 @@
  */
 package org.argeo.security.ui.rap;
 
-import static org.argeo.cms.KernelHeader.ACCESS_CONTROL_CONTEXT;
+import static org.argeo.cms.auth.AuthConstants.ACCESS_CONTROL_CONTEXT;
 
 import java.security.AccessControlContext;
 import java.security.AccessController;
@@ -33,8 +33,7 @@ import javax.servlet.http.HttpSession;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.argeo.ArgeoException;
-import org.argeo.cms.KernelHeader;
-import org.argeo.cms.auth.ArgeoLoginContext;
+import org.argeo.cms.auth.AuthConstants;
 import org.argeo.cms.widgets.auth.DefaultLoginDialog;
 import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
 import org.argeo.util.LocaleUtils;
@@ -78,7 +77,7 @@ public class SecureEntryPoint implements EntryPoint {
                HttpServletRequest httpRequest = RWT.getRequest();
                final HttpSession httpSession = httpRequest.getSession();
                AccessControlContext acc = (AccessControlContext) httpSession
-                               .getAttribute(KernelHeader.ACCESS_CONTROL_CONTEXT);
+                               .getAttribute(AuthConstants.ACCESS_CONTROL_CONTEXT);
 
                final Subject subject;
                if (acc != null
@@ -92,8 +91,8 @@ public class SecureEntryPoint implements EntryPoint {
                        try {
                                CallbackHandler callbackHandler = new DefaultLoginDialog(
                                                display.getActiveShell());
-                               loginContext = new ArgeoLoginContext(
-                                               KernelHeader.LOGIN_CONTEXT_USER, subject,
+                               loginContext = new LoginContext(
+                                               AuthConstants.LOGIN_CONTEXT_USER, subject,
                                                callbackHandler);
                        } catch (LoginException e1) {
                                throw new ArgeoException("Cannot initialize login context", e1);
@@ -134,8 +133,8 @@ public class SecureEntryPoint implements EntryPoint {
                                if (log.isTraceEnabled())
                                        log.trace("Display disposed");
                                try {
-                                       LoginContext loginContext = new ArgeoLoginContext(
-                                                       KernelHeader.LOGIN_CONTEXT_USER, subject);
+                                       LoginContext loginContext = new LoginContext(
+                                                       AuthConstants.LOGIN_CONTEXT_USER, subject);
                                        loginContext.logout();
                                } catch (LoginException e) {
                                        log.error("Error when logging out", e);
@@ -205,8 +204,8 @@ public class SecureEntryPoint implements EntryPoint {
 
        private void fullLogout(Subject subject, String username) {
                try {
-                       LoginContext loginContext = new ArgeoLoginContext(
-                                       KernelHeader.LOGIN_CONTEXT_USER, subject);
+                       LoginContext loginContext = new LoginContext(
+                                       AuthConstants.LOGIN_CONTEXT_USER, subject);
                        loginContext.logout();
                        HttpServletRequest httpRequest = RWT.getRequest();
                        HttpSession httpSession = httpRequest.getSession();
index 1364eeb915fef9d00c991693974690ac4b5dec4d..b3d7c23378e412be98de5e462320943bf60c3c9b 100644 (file)
@@ -22,24 +22,17 @@ import org.osgi.framework.BundleContext;
 public class SecureRapActivator implements BundleActivator {
        public final static String ID = "org.argeo.security.ui.rap";
 
-       private BundleContext bundleContext;
-       private static SecureRapActivator activator = null;
+       private static BundleContext bundleContext;
 
-       public void start(BundleContext bundleContext) throws Exception {
-               activator = this;
-               this.bundleContext = bundleContext;
+       public void start(BundleContext bc) throws Exception {
+               bundleContext = bc;
        }
 
        public void stop(BundleContext context) throws Exception {
                bundleContext = null;
-               activator = null;
        }
 
-       public BundleContext getBundleContext() {
+       public static BundleContext getBundleContext() {
                return bundleContext;
        }
-
-       public static SecureRapActivator getActivator() {
-               return activator;
-       }
 }
index 73932a7a460991d6b366c85a673337e256a6d69e..7086de0afe689572f9c1d6eae16e33b5cd1b8775 100644 (file)
@@ -25,7 +25,7 @@ import org.argeo.security.SecurityUtils;
  */
 public class CurrentUser {
        public final static String getUsername() {
-               return SecurityUtils.getUsername();
+               return SecurityUtils.getCurrentThreadUsername();
        }
 
        public final static Set<String> roles() {