From f7944a8accf7b9cfc3cffe6e6f5c611cd48f592c Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Sun, 6 Sep 2015 18:59:11 +0000 Subject: [PATCH] System authentication git-svn-id: https://svn.argeo.org/commons/trunk@8376 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- .../cms/internal/auth/ImpliedByPrincipal.java | 2 +- .../internal/auth/UserAdminLoginModule.java | 40 ++++-- .../argeo/cms/internal/kernel/Activator.java | 12 +- .../org/argeo/cms/internal/kernel/Kernel.java | 38 ++++- .../cms/internal/kernel/NodeSecurity.java | 2 + .../org/argeo/cms/internal/kernel/jaas.cfg | 4 +- .../src/org/argeo/security/SystemAuth.java | 28 ++++ .../core/AbstractSystemExecution.java | 136 +++++++++--------- ...catedApplicationContextInitialization.java | 106 ++++++++++---- .../security/core/SystemLoginModule.java | 45 ++++++ .../jackrabbit/JackrabbitAuthTest.java | 23 ++- .../security/jackrabbit/test_jaas.config | 4 + .../SystemJackrabbitLoginModule.java | 77 ++++++---- 13 files changed, 371 insertions(+), 146 deletions(-) create mode 100644 org.argeo.security.core/src/org/argeo/security/SystemAuth.java create mode 100644 org.argeo.security.core/src/org/argeo/security/core/SystemLoginModule.java diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/ImpliedByPrincipal.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/ImpliedByPrincipal.java index 417ea69ce..829c17e35 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/auth/ImpliedByPrincipal.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/auth/ImpliedByPrincipal.java @@ -85,6 +85,6 @@ public final class ImpliedByPrincipal implements Group { @Override public String toString() { - return name.toString() + ", implied by " + causes; + return name.toString() + " implied by " + causes; } } 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 index 63ca969b8..f59851521 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/auth/UserAdminLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/auth/UserAdminLoginModule.java @@ -5,6 +5,8 @@ import java.nio.CharBuffer; import java.nio.charset.Charset; import java.security.Principal; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Set; @@ -22,6 +24,9 @@ import javax.security.auth.x500.X500Principal; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.digest.DigestUtils; +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; @@ -35,12 +40,20 @@ public class UserAdminLoginModule implements LoginModule { private CallbackHandler callbackHandler; private boolean isAnonymous = false; - private final static LdapName ROLE_USER_NAME, ROLE_ANONYMOUS_NAME; + private final static LdapName ROLE_ADMIN_NAME, ROLE_USER_NAME, + ROLE_ANONYMOUS_NAME; + private final static List RESERVED_ROLES; private final static X500Principal ROLE_ANONYMOUS_PRINCIPAL; static { try { + 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_ANONYMOUS_NAME, + ROLE_USER_NAME, ROLE_ADMIN_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) { @@ -133,15 +146,17 @@ public class UserAdminLoginModule implements LoginModule { try { String authName = authorization.getName(); - // determine user'S principal + // 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, @@ -151,17 +166,15 @@ public class UserAdminLoginModule implements LoginModule { // Add roles provided by authorization for (String role : authorization.getRoles()) { LdapName roleName = new LdapName(role); - if (ROLE_USER_NAME.equals(roleName)) - throw new CmsException(ROLE_USER_NAME - + " cannot be listed as role"); - if (ROLE_ANONYMOUS_NAME.equals(roleName)) - throw new CmsException(ROLE_ANONYMOUS_NAME - + " cannot be listed as 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)); } } @@ -197,4 +210,15 @@ public class UserAdminLoginModule implements LoginModule { 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)) + throw new CmsException(roleName + " cannot be listed as role"); + } } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java index 6bad825dd..1b21f5663 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java @@ -2,6 +2,8 @@ package org.argeo.cms.internal.kernel; import java.util.UUID; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.argeo.security.SystemAuthentication; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; @@ -11,6 +13,8 @@ import org.osgi.framework.BundleContext; * access to kernel information for the rest of the bundle (and only it) */ public class Activator implements BundleActivator { + private final Log log = LogFactory.getLog(Activator.class); + private final static String systemKey; static { systemKey = UUID.randomUUID().toString(); @@ -25,8 +29,12 @@ public class Activator implements BundleActivator { assert bundleContext == null; assert kernel == null; bundleContext = context; - kernel = new Kernel(); - kernel.init(); + try { + kernel = new Kernel(); + kernel.init(); + } catch (Exception e) { + log.error("Cannot boot kernel", e); + } } @Override diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java index 83f21202e..67f0c3737 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java @@ -2,17 +2,22 @@ package org.argeo.cms.internal.kernel; import java.lang.management.ManagementFactory; import java.net.URL; +import java.security.PrivilegedAction; import java.util.HashMap; import java.util.Map; import javax.jcr.Repository; import javax.jcr.RepositoryFactory; +import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jackrabbit.util.TransientFileFactory; import org.argeo.ArgeoException; import org.argeo.cms.CmsException; +import org.argeo.cms.KernelHeader; import org.argeo.jackrabbit.OsgiJackrabbitRepositoryFactory; import org.argeo.jcr.ArgeoJcrConstants; import org.argeo.security.core.InternalAuthentication; @@ -47,12 +52,35 @@ final class Kernel implements ServiceListener { NodeHttp nodeHttp; private KernelThread kernelThread; - void init() { + private final Subject kernelSubject = new Subject(); + + public Kernel() { URL url = getClass().getClassLoader().getResource( KernelConstants.JAAS_CONFIG); System.setProperty("java.security.auth.login.config", url.toExternalForm()); + try { + LoginContext kernelLc = new LoginContext( + KernelHeader.LOGIN_CONTEXT_SYSTEM, kernelSubject); + kernelLc.login(); + } catch (LoginException e) { + throw new CmsException("Cannot log in kernel", e); + } + } + + final void init() { + Subject.doAs(kernelSubject, new PrivilegedAction() { + + @Override + public Void run() { + doInit(); + return null; + } + + }); + } + private void doInit() { ClassLoader currentContextCl = Thread.currentThread() .getContextClassLoader(); Thread.currentThread().setContextClassLoader( @@ -121,6 +149,14 @@ final class Kernel implements ServiceListener { // Clean hanging threads from Jackrabbit TransientFileFactory.shutdown(); + try { + LoginContext kernelLc = new LoginContext( + KernelHeader.LOGIN_CONTEXT_SYSTEM, kernelSubject); + kernelLc.logout(); + } catch (LoginException e) { + throw new CmsException("Cannot log in kernel", e); + } + long duration = System.currentTimeMillis() - begin; log.info("## ARGEO CMS DOWN in " + (duration / 1000) + "." + (duration % 1000) + "s ##"); diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java index 0a512fff9..13ecac4b0 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java @@ -131,6 +131,8 @@ class NodeSecurity implements AuthenticationManager { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { +// throw new UnsupportedOperationException( +// "Authentication manager is deprectaed and should not be used."); Authentication auth = null; if (authentication instanceof InternalAuthentication) auth = internalAuth.authenticate(authentication); diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg b/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg index 0e76f376a..783d1f71f 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg @@ -17,9 +17,7 @@ OLD_ANONYMOUS { }; SYSTEM { - org.argeo.cms.internal.auth.SystemLoginModule requisite; - org.springframework.security.authentication.jaas.SecurityContextLoginModule requisite; - org.argeo.security.jackrabbit.SystemJackrabbitLoginModule requisite; + org.argeo.security.core.SystemLoginModule requisite; }; OLD_SYSTEM { diff --git a/org.argeo.security.core/src/org/argeo/security/SystemAuth.java b/org.argeo.security.core/src/org/argeo/security/SystemAuth.java new file mode 100644 index 000000000..76a03fb8c --- /dev/null +++ b/org.argeo.security.core/src/org/argeo/security/SystemAuth.java @@ -0,0 +1,28 @@ +package org.argeo.security; + +import java.security.Principal; + +public final class SystemAuth implements Principal { + private final String name = "init"; + + @Override + public String getName() { + return name; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return this == obj; + } + + @Override + public String toString() { + return name.toString(); + } + +} diff --git a/org.argeo.security.core/src/org/argeo/security/core/AbstractSystemExecution.java b/org.argeo.security.core/src/org/argeo/security/core/AbstractSystemExecution.java index 3acf26c8a..42cf42eef 100644 --- a/org.argeo.security.core/src/org/argeo/security/core/AbstractSystemExecution.java +++ b/org.argeo.security.core/src/org/argeo/security/core/AbstractSystemExecution.java @@ -15,105 +15,103 @@ */ package org.argeo.security.core; +import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; +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.security.SystemAuthentication; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; /** Provides base method for executing code with system authorization. */ public abstract class AbstractSystemExecution { - static { - // Forces Spring Security to use inheritable strategy - // FIXME find a better place for forcing spring security mode - // doesn't work for the time being - // if (System.getProperty(SecurityContextHolder.SYSTEM_PROPERTY) == - // null) - // SecurityContextHolder - // .setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL); - } - private final static Log log = LogFactory .getLog(AbstractSystemExecution.class); - private AuthenticationManager authenticationManager; - private String systemAuthenticationKey; + // private AuthenticationManager authenticationManager; + private final Subject subject = new Subject(); + // private String systemAuthenticationKey; + + private final String loginModule = "SYSTEM"; /** Whether the current thread was authenticated by this component. */ - private ThreadLocal authenticatedBySelf = new ThreadLocal() { - protected Boolean initialValue() { - return false; - } - }; + // private ThreadLocal authenticatedBySelf = new + // ThreadLocal() { + // protected Boolean initialValue() { + // return false; + // } + // }; /** * Authenticate the calling thread to the underlying * {@link AuthenticationManager} */ protected void authenticateAsSystem() { - if (authenticatedBySelf.get()) - return; - SecurityContext securityContext = SecurityContextHolder.getContext(); - Authentication currentAuth = securityContext.getAuthentication(); - if (currentAuth != null) { - if (!(currentAuth instanceof SystemAuthentication)) - throw new ArgeoException( - "System execution on an already authenticated thread: " - + currentAuth + ", THREAD=" - + Thread.currentThread().getId()); - return; + try { + LoginContext lc = new LoginContext(loginModule, subject); + lc.login(); + } catch (LoginException e) { + throw new ArgeoException("Cannot login as system", e); } - // Subject subject = Subject.getSubject(AccessController.getContext()); - // if (subject != null - // && !subject.getPrincipals(Authentication.class).isEmpty()) + // if (authenticatedBySelf.get()) + // return; + // SecurityContext securityContext = SecurityContextHolder.getContext(); + // Authentication currentAuth = securityContext.getAuthentication(); + // if (currentAuth != null) { + // if (!(currentAuth instanceof SystemAuthentication)) // throw new ArgeoException( - // "There is already an authenticated subject: " + subject); - - String key = systemAuthenticationKey != null ? systemAuthenticationKey - : System.getProperty( - SystemAuthentication.SYSTEM_KEY_PROPERTY, - InternalAuthentication.SYSTEM_KEY_DEFAULT); - if (key == null) - throw new ArgeoException("No system key defined"); - if (authenticationManager == null) - throw new ArgeoException("Authentication manager cannot be null."); - Authentication auth = authenticationManager - .authenticate(new InternalAuthentication(key)); - securityContext.setAuthentication(auth); - - authenticatedBySelf.set(true); + // "System execution on an already authenticated thread: " + // + currentAuth + ", THREAD=" + // + Thread.currentThread().getId()); + // return; + // } + // + // String key = systemAuthenticationKey != null ? + // systemAuthenticationKey + // : System.getProperty( + // SystemAuthentication.SYSTEM_KEY_PROPERTY, + // InternalAuthentication.SYSTEM_KEY_DEFAULT); + // if (key == null) + // throw new ArgeoException("No system key defined"); + // if (authenticationManager == null) + // throw new ArgeoException("Authentication manager cannot be null."); + // Authentication auth = authenticationManager + // .authenticate(new InternalAuthentication(key)); + // securityContext.setAuthentication(auth); + // + // authenticatedBySelf.set(true); if (log.isTraceEnabled()) log.trace("System authenticated"); } - // /** Removes the authentication from the calling thread. */ - // protected void deauthenticateAsSystem() { - // // remove the authentication - // // SecurityContext securityContext = SecurityContextHolder.getContext(); - // // securityContext.setAuthentication(null); - // // authenticatedBySelf.set(false); - // if (log.isTraceEnabled()) { - // log.trace("System deauthenticated"); - // // Thread.dumpStack(); - // } - // } + protected void deauthenticateAsSystem() { + try { + LoginContext lc = new LoginContext(loginModule, subject); + lc.logout(); + } catch (LoginException e) { + throw new ArgeoException("Cannot logout as system", e); + } + } - /** - * Whether the current thread was authenticated by this component or a - * parent thread. - */ - protected Boolean isAuthenticatedBySelf() { - return authenticatedBySelf.get(); + protected Subject getSubject() { + return subject; } + // /** + // * Whether the current thread was authenticated by this component or a + // * parent thread. + // */ + // protected Boolean isAuthenticatedBySelf() { + // return authenticatedBySelf.get(); + // } + // public void setAuthenticationManager( AuthenticationManager authenticationManager) { - this.authenticationManager = authenticationManager; + log.warn("Use of authenticationManager is deprecated, remove this property from the configuration."); } public void setSystemAuthenticationKey(String systemAuthenticationKey) { - this.systemAuthenticationKey = systemAuthenticationKey; + log.warn("Use of systemAuthenticationKey is deprecated, remove this property from the configuration."); + // this.systemAuthenticationKey = systemAuthenticationKey; } } diff --git a/org.argeo.security.core/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java b/org.argeo.security.core/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java index 5faa2a751..6c477ee9f 100644 --- a/org.argeo.security.core/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java +++ b/org.argeo.security.core/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java @@ -16,12 +16,21 @@ package org.argeo.security.core; import java.beans.PropertyDescriptor; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.List; +import javax.security.auth.Subject; + +import org.eclipse.gemini.blueprint.context.DependencyInitializationAwareBeanPostProcessor; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValues; -import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; +import org.springframework.beans.factory.support.AbstractBeanFactory; +import org.springframework.beans.factory.support.SecurityContextProvider; +import org.springframework.beans.factory.support.SimpleSecurityContextProvider; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; @@ -31,41 +40,51 @@ import org.springframework.context.event.ContextRefreshedEvent; * methods of the application context where it has been defined. */ public class AuthenticatedApplicationContextInitialization extends - AbstractSystemExecution implements InstantiationAwareBeanPostProcessor, - ApplicationListener { + AbstractSystemExecution implements DependencyInitializationAwareBeanPostProcessor, + ApplicationListener, ApplicationContextAware { // private Log log = LogFactory // .getLog(AuthenticatedApplicationContextInitialization.class); /** If non empty, restricts to these beans */ private List beanNames = new ArrayList(); - @SuppressWarnings("rawtypes") - public Object postProcessBeforeInstantiation(Class beanClass, - String beanName) throws BeansException { - // we authenticate when any bean is instantiated - // we will deauthenticate only when the application context has been - // refreshed in order to be able to deal with factory beans has well - if (!isAuthenticatedBySelf()) { - if (beanNames.size() == 0) - authenticateAsSystem(); - else if (beanNames.contains(beanName)) - authenticateAsSystem(); - } - return null; - } - - public boolean postProcessAfterInstantiation(Object bean, String beanName) - throws BeansException { - return true; - } - - public PropertyValues postProcessPropertyValues(PropertyValues pvs, - PropertyDescriptor[] pds, Object bean, String beanName) - throws BeansException { - return pvs; - } +// @SuppressWarnings("rawtypes") +// public Object postProcessBeforeInstantiation(Class beanClass, +// String beanName) throws BeansException { +// // we authenticate when any bean is instantiated +// // we will deauthenticate only when the application context has been +// // refreshed in order to be able to deal with factory beans has well +// // if (!isAuthenticatedBySelf()) { +// // if (beanNames.size() == 0) +// // authenticateAsSystem(); +// // else if (beanNames.contains(beanName)) +// // authenticateAsSystem(); +// // } +// return null; +// } +// +// public boolean postProcessAfterInstantiation(Object bean, String beanName) +// throws BeansException { +// return true; +// } +// +// public PropertyValues postProcessPropertyValues(PropertyValues pvs, +// PropertyDescriptor[] pds, Object bean, String beanName) +// throws BeansException { +// return pvs; +// } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (beanNames.size() == 0 || beanNames.contains(beanName)) + authenticateAsSystem(); + // try { + // if (beanNames.size() == 0 || beanNames.contains(beanName)) { + // LoginContext lc = new LoginContext("INIT", subject); + // lc.login(); + // } + // } catch (LoginException e) { + // throw new ArgeoException("Cannot login as initialization", e); + // } return bean; } @@ -75,7 +94,17 @@ public class AuthenticatedApplicationContextInitialization extends // we expect the underlying thread to die and thus the system // authentication to be lost. We have currently no way to catch the // exception and perform the deauthentication by ourselves. - // deauthenticateAsSystem(); + if (beanNames.size() == 0 || beanNames.contains(beanName)) + deauthenticateAsSystem(); + // try { + // if (beanNames.size() == 0 || beanNames.contains(beanName)) { + // LoginContext lc = new LoginContext("INIT", subject); + // lc.logout(); + // } + // } catch (LoginException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } return bean; } @@ -91,4 +120,23 @@ public class AuthenticatedApplicationContextInitialization extends this.beanNames = beanNames; } + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + if (applicationContext.getAutowireCapableBeanFactory() instanceof AbstractBeanFactory) { + final AbstractBeanFactory beanFactory = ((AbstractBeanFactory) applicationContext + .getAutowireCapableBeanFactory()); + // retrieve subject's access control context + // and set it as the bean factory security context + Subject.doAs(getSubject(), new PrivilegedAction() { + @Override + public Void run() { + SecurityContextProvider scp = new SimpleSecurityContextProvider( + AccessController.getContext()); + beanFactory.setSecurityContextProvider(scp); + return null; + } + }); + } + } } diff --git a/org.argeo.security.core/src/org/argeo/security/core/SystemLoginModule.java b/org.argeo.security.core/src/org/argeo/security/core/SystemLoginModule.java new file mode 100644 index 000000000..a1d68b376 --- /dev/null +++ b/org.argeo.security.core/src/org/argeo/security/core/SystemLoginModule.java @@ -0,0 +1,45 @@ +package org.argeo.security.core; + +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.security.SystemAuth; + +public class SystemLoginModule implements LoginModule { + private Subject subject; + + @Override + public void initialize(Subject subject, CallbackHandler callbackHandler, + Map sharedState, Map options) { + this.subject = subject; + } + + @Override + public boolean login() throws LoginException { + // TODO check permission? + return true; + } + + @Override + public boolean commit() throws LoginException { + subject.getPrincipals().add(new SystemAuth()); + return true; + } + + @Override + public boolean abort() throws LoginException { + return true; + } + + @Override + public boolean logout() throws LoginException { + // remove ALL credentials (e.g. additional Jackrabbit credentials) + subject.getPrincipals().clear(); + return true; + } + +} diff --git a/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/JackrabbitAuthTest.java b/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/JackrabbitAuthTest.java index 45a6567aa..140dfa67f 100644 --- a/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/JackrabbitAuthTest.java +++ b/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/JackrabbitAuthTest.java @@ -1,9 +1,12 @@ package org.argeo.security.jackrabbit; import java.net.URL; +import java.security.PrivilegedExceptionAction; import javax.jcr.Repository; import javax.jcr.Session; +import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -13,13 +16,19 @@ public class JackrabbitAuthTest extends AbstractJackrabbitTestCase { private final Log log = LogFactory.getLog(JackrabbitAuthTest.class); public void testLogin() throws Exception { - // Subject subject = new Subject(); - // LoginContext loginContext = new LoginContext("UNIX",subject); - // loginContext.login(); - - Repository repository = getRepository(); - Session session = repository.login(); - log.debug(session.getUserID()); + Subject subject = new Subject(); + LoginContext loginContext = new LoginContext("SYSTEM", subject); + loginContext.login(); + Subject.doAs(subject, new PrivilegedExceptionAction() { + + @Override + public Void run() throws Exception { + Repository repository = getRepository(); + Session session = repository.login(); + log.debug(session.getUserID()); + return null; + } + }); } @Override diff --git a/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/test_jaas.config b/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/test_jaas.config index 3b4ee81a3..233af5461 100644 --- a/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/test_jaas.config +++ b/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/test_jaas.config @@ -1,3 +1,7 @@ +SYSTEM { + org.argeo.security.core.SystemLoginModule requisite; +}; + Jackrabbit { org.argeo.security.jackrabbit.SystemJackrabbitLoginModule requisite; }; diff --git a/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java b/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java index b11d7b4b5..9977938ec 100644 --- a/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java +++ b/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java @@ -1,6 +1,5 @@ package org.argeo.security.jackrabbit; -import java.security.Principal; import java.util.Map; import java.util.Set; @@ -8,9 +7,11 @@ 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.security.SystemAuth; public class SystemJackrabbitLoginModule implements LoginModule { @@ -29,32 +30,51 @@ public class SystemJackrabbitLoginModule implements LoginModule { @Override public boolean commit() throws LoginException { - Set principals = subject.getPrincipals(); - if (principals.isEmpty()) {// system - subject.getPrincipals().add(new AdminPrincipal("admin")); + Set initPrincipal = subject + .getPrincipals(SystemAuth.class); + if (!initPrincipal.isEmpty()) { + subject.getPrincipals().add( + new AdminPrincipal(SecurityConstants.ADMIN_ID)); return true; } - boolean isAdmin = false; - boolean isAnonymous = false; - // FIXME make it more generic - for (Principal principal : principals) { - if (principal.getName().equalsIgnoreCase( - "cn=admin,ou=roles,ou=node")) - isAdmin = true; - else if (principal.getName().equalsIgnoreCase( - "cn=anonymous,ou=roles,ou=node")) - isAnonymous = true; - } - if (isAnonymous && isAdmin) - throw new LoginException("Cannot be admin and anonymous"); + Set userPrincipal = subject + .getPrincipals(X500Principal.class); + if (userPrincipal.isEmpty()) + throw new LoginException("Subject must be pre-authenticated"); + if (userPrincipal.size() > 1) + throw new LoginException("Multiple user principals " + + userPrincipal); - // Add special Jackrabbit roles - if (isAdmin) - principals.add(new AdminPrincipal("admin")); - if (isAnonymous)// anonymous - principals.add(new AnonymousPrincipal()); return true; + + // Set principals = subject.getPrincipals(); + // if (principals.isEmpty()) {// system + // throw new LoginException("Subject must be pre-authenticated"); + // // subject.getPrincipals().add(new AdminPrincipal("admin")); + // // return true; + // } + // boolean isAdmin = false; + // boolean isAnonymous = false; + // // FIXME make it more generic + // for (Principal principal : principals) { + // if (principal.getName().equalsIgnoreCase( + // "cn=admin,ou=roles,ou=node")) + // isAdmin = true; + // else if (principal.getName().equalsIgnoreCase( + // "cn=anonymous,ou=roles,ou=node")) + // isAnonymous = true; + // } + // + // if (isAnonymous && isAdmin) + // throw new LoginException("Cannot be admin and anonymous"); + // + // // Add special Jackrabbit roles + // if (isAdmin) + // principals.add(new AdminPrincipal(SecurityConstants.ADMIN_ID)); + // if (isAnonymous)// anonymous + // principals.add(new AnonymousPrincipal()); + // return true; } @Override @@ -64,9 +84,14 @@ public class SystemJackrabbitLoginModule implements LoginModule { @Override public boolean logout() throws LoginException { - subject.getPrincipals().removeAll( - subject.getPrincipals(AdminPrincipal.class)); + Set initPrincipal = subject + .getPrincipals(SystemAuth.class); + if (!initPrincipal.isEmpty()) { + subject.getPrincipals(AdminPrincipal.class); + return true; + } + // subject.getPrincipals().removeAll( + // subject.getPrincipals(AdminPrincipal.class)); return true; } - } -- 2.30.2