--- /dev/null
+package org.argeo.cms;
+
+/** Public properties of the CMS Kernel */
+public interface KernelHeader {
+ final static String LOGIN_CONTEXT_USER = "USER";
+ final static String LOGIN_CONTEXT_ANONYMOUS = "ANONYMOUS";
+ final static String LOGIN_CONTEXT_SYSTEM = "SYSTEM";
+}
import org.argeo.ArgeoException;
import org.argeo.jackrabbit.OsgiJackrabbitRepositoryFactory;
import org.argeo.security.core.InternalAuthentication;
-import org.eclipse.rap.rwt.application.ApplicationConfiguration;
import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
import org.springframework.security.core.context.SecurityContextHolder;
/**
private NodeSecurity nodeSecurity;
private NodeHttp nodeHttp;
- private ServiceRegistration<ApplicationConfiguration> workbenchReg;
-
Kernel(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
bundleContext.registerService(RepositoryFactory.class,
repositoryFactory, null);
nodeHttp.publish();
-
-// if ("false".equals(bundleContext
-// .getProperty(PROP_WORKBENCH_AUTOSTART))) {
-// WorkbenchApplicationConfiguration wac = new WorkbenchApplicationConfiguration();
-// registerWorkbench(wac);
-// }
} catch (Exception e) {
log.error("Cannot initialize Argeo CMS", e);
throw new ArgeoException("Cannot initialize", e);
void destroy() {
long begin = System.currentTimeMillis();
- // OSGi
- workbenchReg.unregister();
-
nodeHttp = null;
nodeSecurity.destroy();
node.destroy();
-SPRING_SECURITY_CONTEXT {
- org.argeo.security.core.SpringLoginModule required;
+USER {
+ org.argeo.security.login.EndUserLoginModule requisite;
+ org.springframework.security.authentication.jaas.SecurityContextLoginModule required;
+};
+
+ANONYMOUS {
+ org.argeo.security.login.AnonymousLoginModule requisite;
+ org.springframework.security.authentication.jaas.SecurityContextLoginModule required;
+};
+
+SYSTEM {
+ org.argeo.security.login.SystemLoginModule requisite;
org.springframework.security.authentication.jaas.SecurityContextLoginModule required;
};
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.UUID;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
/** Static utilities */
-public class SecurityUtils {
+public final class SecurityUtils {
+ private final static String systemKey = UUID.randomUUID().toString();
private SecurityUtils() {
}
+ /**
+ * @return a String which is guaranteed to be unique between and constant
+ * within a Java static context (typically a VM launch)
+ */
+ public final static String getStaticKey() {
+ return systemKey;
+ }
+
/** Whether the current thread has the admin role */
public static boolean hasCurrentThreadAuthority(String authority) {
SecurityContext securityContext = SecurityContextHolder.getContext();
*/
package org.argeo.security.core;
+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.argeo.security.login.BundleContextCallbackHandler;
+import org.osgi.framework.BundleContext;
import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
// 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);
+ // 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 BundleContext bundleContext;
private String systemAuthenticationKey;
+ private String loginContextName = "SYSTEM";
/** Whether the current thread was authenticated by this component. */
private ThreadLocal<Boolean> authenticatedBySelf = new ThreadLocal<Boolean>() {
InternalAuthentication.SYSTEM_KEY_DEFAULT);
if (key == null)
throw new ArgeoException("No system key defined");
- Authentication auth = authenticationManager
- .authenticate(new InternalAuthentication(key));
- securityContext.setAuthentication(auth);
+ if (authenticationManager != null) {
+ Authentication auth = authenticationManager
+ .authenticate(new InternalAuthentication(key));
+ securityContext.setAuthentication(auth);
+ } else {
+ try {
+ // TODO test this
+ if (bundleContext == null)
+ throw new ArgeoException("bundleContext must be set");
+ BundleContextCallbackHandler callbackHandler = new BundleContextCallbackHandler(
+ bundleContext);
+ LoginContext loginContext = new LoginContext(loginContextName,
+ callbackHandler);
+ loginContext.login();
+ } catch (LoginException e) {
+ throw new BadCredentialsException("Cannot authenticate");
+ }
+ }
authenticatedBySelf.set(true);
if (log.isTraceEnabled())
log.trace("System authenticated");
return authenticatedBySelf.get();
}
+ @Deprecated
public void setAuthenticationManager(
AuthenticationManager authenticationManager) {
+ // log.warn("This approach is deprecated, inject bundleContext instead");
this.authenticationManager = authenticationManager;
}
+ @Deprecated
public void setSystemAuthenticationKey(String systemAuthenticationKey) {
this.systemAuthenticationKey = systemAuthenticationKey;
}
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
}
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
- // authenticateAsSystem();
return bean;
}
* Maintains a list of authentication providers injected in to a provider
* manager, in order to avoid issues with OSGi services and use packages.
*/
+@Deprecated
public class AuthenticationProvidersRegister implements InitializingBean {
private Log log = LogFactory.getLog(AuthenticationProvidersRegister.class);
+++ /dev/null
-package org.argeo.security.core;
-
-import javax.security.auth.callback.Callback;
-
-import org.osgi.framework.BundleContext;
-
-/** Gives access to the OSGi {@link BundleContext} */
-public class BundleContextCallback implements Callback {
- private BundleContext bundleContext;
-
- public BundleContext getBundleContext() {
- return bundleContext;
- }
-
- public void setBundleContext(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
-
-}
+++ /dev/null
-/*
- * 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.security.core;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.UUID;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.login.LoginException;
-import javax.security.auth.spi.LoginModule;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.security.NodeAuthenticationToken;
-import org.argeo.util.LocaleCallback;
-import org.argeo.util.LocaleUtils;
-import org.osgi.framework.BundleContext;
-import org.springframework.security.authentication.AnonymousAuthenticationToken;
-import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.security.authentication.BadCredentialsException;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.security.core.context.SecurityContextHolder;
-
-/** Login module which caches one subject per thread. */
-public class SpringLoginModule implements LoginModule {
- final static String NODE_REPO_URI = "argeo.node.repo.uri";
-
- private final static Log log = LogFactory.getLog(SpringLoginModule.class);
-
- private CallbackHandler callbackHandler;
-
- private Subject subject;
-
- private Long waitBetweenFailedLoginAttempts = 5 * 1000l;
-
- private Boolean remote = false;
- private Boolean anonymous = false;
- /** Comma separated list of locales */
- private String availableLocales = "";
-
- private String key = null;
- private String anonymousRole = "ROLE_ANONYMOUS";
-
- public SpringLoginModule() {
-
- }
-
- @SuppressWarnings("rawtypes")
- public void initialize(Subject subject, CallbackHandler callbackHandler,
- Map sharedState, Map options) {
- this.callbackHandler = callbackHandler;
- this.subject = subject;
- }
-
- public boolean login() throws LoginException {
- try {
- // thread already logged in
- Authentication currentAuth = SecurityContextHolder.getContext()
- .getAuthentication();
- if (currentAuth != null) {
- if (subject.getPrincipals(Authentication.class).size() == 0) {
- subject.getPrincipals().add(currentAuth);
- } else {
- Authentication principal = subject
- .getPrincipals(Authentication.class).iterator()
- .next();
- if (principal != currentAuth)
- throw new LoginException(
- "Already authenticated with a different auth");
- }
- return true;
- }
-
- if (remote && anonymous)
- throw new LoginException(
- "Cannot have a Spring login module which is remote and anonymous");
-
- // reset all principals and credentials
- if (log.isTraceEnabled())
- log.trace("Resetting all principals and credentials of "
- + subject);
- subject.getPrincipals().clear();
- subject.getPrivateCredentials().clear();
- subject.getPublicCredentials().clear();
-
- Locale selectedLocale = null;
- // deals first with public access since it's simple
- if (anonymous) {
- // FIXME Is this code still needed?
- AuthenticationManager authenticationManager = null;
-
- // multi locale
- if (callbackHandler != null && availableLocales != null
- && !availableLocales.trim().equals("")) {
- LocaleCallback localeCallback = new LocaleCallback(
- availableLocales);
- callbackHandler.handle(new Callback[] { localeCallback });
- selectedLocale = localeCallback.getSelectedLocale();
- }
-
- // TODO integrate with JCR?
- Object principal = UUID.randomUUID().toString();
- List<SimpleGrantedAuthority> authorities = Collections
- .singletonList(new SimpleGrantedAuthority(anonymousRole));
- AnonymousAuthenticationToken anonymousToken = new AnonymousAuthenticationToken(
- key, principal, authorities);
- Authentication auth = authenticationManager
- .authenticate(anonymousToken);
- registerAuthentication(auth);
- } else {
- if (callbackHandler == null)
- throw new LoginException("No call back handler available");
-
- // ask for username and password
- NameCallback nameCallback = new NameCallback("User");
- PasswordCallback passwordCallback = new PasswordCallback(
- "Password", false);
- final String defaultNodeUrl = System
- .getProperty(NODE_REPO_URI,
- "http://localhost:7070/org.argeo.jcr.webapp/remoting/node");
- NameCallback urlCallback = new NameCallback("Site URL",
- defaultNodeUrl);
- LocaleCallback localeCallback = new LocaleCallback(
- availableLocales);
- BundleContextCallback bundleContextCallback = new BundleContextCallback();
-
- // handle callbacks
- if (remote)
- callbackHandler.handle(new Callback[] { nameCallback,
- passwordCallback, urlCallback, localeCallback,
- bundleContextCallback });
- else
- callbackHandler.handle(new Callback[] { nameCallback,
- passwordCallback, localeCallback,
- bundleContextCallback });
-
- selectedLocale = localeCallback.getSelectedLocale();
-
- // create credentials
- final String username = nameCallback.getName();
- if (username == null || username.trim().equals(""))
- return false;
-
- char[] password = {};
- if (passwordCallback.getPassword() != null)
- password = passwordCallback.getPassword();
-
- NodeAuthenticationToken credentials;
- if (remote) {
- String url = urlCallback.getName();
- credentials = new NodeAuthenticationToken(username,
- password, url);
- } else {
- credentials = new NodeAuthenticationToken(username,
- password);
- }
-
- BundleContext bc = bundleContextCallback.getBundleContext();
- AuthenticationManager authenticationManager = bc.getService(bc
- .getServiceReference(AuthenticationManager.class));
-
- Authentication authentication;
- try {
- authentication = authenticationManager
- .authenticate(credentials);
- } catch (BadCredentialsException e) {
- // wait between failed login attempts
- Thread.sleep(waitBetweenFailedLoginAttempts);
- throw e;
- }
- registerAuthentication(authentication);
- subject.getPrincipals().add(authentication);
- }
-
- if (selectedLocale != null)
- LocaleUtils.threadLocale.set(selectedLocale);
-
- return true;
- } catch (LoginException e) {
- throw e;
- } catch (ThreadDeath e) {
- LoginException le = new LoginException(
- "Spring Security login thread died");
- le.initCause(e);
- throw le;
- } catch (Exception e) {
- LoginException le = new LoginException(
- "Spring Security login failed");
- le.initCause(e);
- throw le;
- }
- }
-
- @Override
- public boolean logout() throws LoginException {
- subject.getPrincipals().clear();
- return true;
- }
-
- @Override
- public boolean commit() throws LoginException {
- return true;
- }
-
- @Override
- public boolean abort() throws LoginException {
- return true;
- }
-
- /**
- * Register an {@link Authentication} in the security context.
- *
- * @param authentication
- * has to implement {@link Authentication}.
- */
- protected void registerAuthentication(Object authentication) {
- SecurityContextHolder.getContext().setAuthentication(
- (Authentication) authentication);
- }
-
- /** Authenticates on a remote node */
- public void setRemote(Boolean remote) {
- this.remote = remote;
- }
-
- /**
- * Request anonymous authentication (incompatible with remote)
- */
- public void setAnonymous(Boolean anonymous) {
- this.anonymous = anonymous;
- }
-
- /** Role identifying an anonymous user */
- public void setAnonymousRole(String anonymousRole) {
- this.anonymousRole = anonymousRole;
- }
-
- /** System key */
- public void setKey(String key) {
- this.key = key;
- }
-
- public void setAvailableLocales(String locales) {
- this.availableLocales = locales;
- }
-
-}
+++ /dev/null
-/*
- * 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.security.core;
-
-import java.util.Locale;
-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.LoginException;
-import javax.security.auth.spi.LoginModule;
-
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.util.LocaleCallback;
-import org.argeo.util.LocaleUtils;
-import org.osgi.framework.BundleContext;
-import org.osgi.service.useradmin.Authorization;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
-import org.springframework.security.authentication.encoding.LdapShaPasswordEncoder;
-
-/** Login module which caches one subject per thread. */
-public class UserAdminLoginModule implements LoginModule {
- // private final static Log log = LogFactory
- // .getLog(UserAdminLoginModule.class);
-
- private CallbackHandler callbackHandler;
-
- private Subject subject;
-
- private Long waitBetweenFailedLoginAttempts = 5 * 1000l;
-
- /** Comma separated list of locales */
- private String availableLocales = "";
-
- private AuthorizationPrincipal auth = null;
- private Locale selectedLocale = null;
-
- private LdapShaPasswordEncoder shaPasswordEncoder = new LdapShaPasswordEncoder();
-
- public UserAdminLoginModule() {
-
- }
-
- @SuppressWarnings("rawtypes")
- public void initialize(Subject subject, CallbackHandler callbackHandler,
- Map sharedState, Map options) {
- this.callbackHandler = callbackHandler;
- this.subject = subject;
- }
-
- public boolean login() throws LoginException {
- try {
- // TODO thread already logged in
- // AuthorizationPrincipal principal = subject
- // .getPrincipals(AuthorizationPrincipal.class).iterator();
-
- if (callbackHandler == null)
- throw new LoginException("No call back handler available");
-
- // ask for username and password
- NameCallback nameCallback = new NameCallback("User");
- PasswordCallback passwordCallback = new PasswordCallback(
- "Password", false);
- LocaleCallback localeCallback = new LocaleCallback(availableLocales);
- BundleContextCallback bundleContextCallback = new BundleContextCallback();
-
- callbackHandler.handle(new Callback[] { nameCallback,
- passwordCallback, localeCallback, bundleContextCallback });
-
- selectedLocale = localeCallback.getSelectedLocale();
-
- // create credentials
- final String username = nameCallback.getName();
- if (username == null || username.trim().equals(""))
- return false;
-
- char[] password = {};
- if (passwordCallback.getPassword() != null)
- password = passwordCallback.getPassword();
-
- BundleContext bc = bundleContextCallback.getBundleContext();
- UserAdmin userAdmin = bc.getService(bc
- .getServiceReference(UserAdmin.class));
-
- User user = (User) userAdmin.getRole(username);
- // TODO use hash
- boolean authenticated = user.hasCredential(
- ArgeoNames.ARGEO_PASSWORD, new String(password));
-
- if (!authenticated) {
- // wait between failed login attempts
- Thread.sleep(waitBetweenFailedLoginAttempts);
- return false;
- }
-
- Authorization authorization = userAdmin.getAuthorization(user);
- auth = new AuthorizationPrincipal(authorization);
- return true;
- } catch (LoginException e) {
- throw e;
- } catch (ThreadDeath e) {
- LoginException le = new LoginException(
- "Spring Security login thread died");
- le.initCause(e);
- throw le;
- } catch (Exception e) {
- LoginException le = new LoginException(
- "Spring Security login failed");
- le.initCause(e);
- throw le;
- }
- }
-
- @Override
- public boolean logout() throws LoginException {
- subject.getPrincipals(AuthorizationPrincipal.class).remove(auth);
- return true;
- }
-
- @Override
- public boolean commit() throws LoginException {
- subject.getPrincipals().add(auth);
- if (selectedLocale != null)
- LocaleUtils.threadLocale.set(selectedLocale);
- return true;
- }
-
- @Override
- public boolean abort() throws LoginException {
- auth = null;
- selectedLocale = null;
- return true;
- }
-
- public void setAvailableLocales(String locales) {
- this.availableLocales = locales;
- }
-}
\ No newline at end of file
--- /dev/null
+/*
+ * 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.security.login;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+import org.osgi.framework.BundleContext;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+/** Login module which caches one subject per thread. */
+abstract class AbstractSpringSecurityLoginModule implements LoginModule {
+ private CallbackHandler callbackHandler;
+ private Subject subject;
+
+ protected abstract Authentication processLogin(
+ CallbackHandler callbackHandler) throws LoginException,
+ UnsupportedCallbackException, IOException, InterruptedException;
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public void initialize(Subject subject, CallbackHandler callbackHandler,
+ Map sharedState, Map options) {
+ this.callbackHandler = callbackHandler;
+ this.subject = subject;
+ }
+
+ @Override
+ public boolean login() throws LoginException {
+ try {
+ // thread already logged in
+ Authentication currentAuth = SecurityContextHolder.getContext()
+ .getAuthentication();
+ if (currentAuth != null) {
+ if (subject.getPrincipals(Authentication.class).size() == 0) {
+ subject.getPrincipals().add(currentAuth);
+ } else {
+ Authentication principal = subject
+ .getPrincipals(Authentication.class).iterator()
+ .next();
+ if (principal != currentAuth)
+ throw new LoginException(
+ "Already authenticated with a different auth");
+ }
+ return true;
+ }
+
+ // reset all principals and credentials
+ // if (log.isTraceEnabled())
+ // log.trace("Resetting all principals and credentials of "
+ // + subject);
+ // subject.getPrincipals().clear();
+ // subject.getPrivateCredentials().clear();
+ // subject.getPublicCredentials().clear();
+
+ if (callbackHandler == null)
+ throw new LoginException("No callback handler available");
+
+ Authentication authentication = processLogin(callbackHandler);
+ if (authentication != null) {
+ SecurityContextHolder.getContext().setAuthentication(
+ authentication);
+ return true;
+ } else {
+ throw new LoginException("No authentication returned");
+ }
+ } catch (LoginException e) {
+ throw e;
+ } catch (ThreadDeath e) {
+ LoginException le = new LoginException(
+ "Spring Security login thread died");
+ le.initCause(e);
+ throw le;
+ } catch (Exception e) {
+ LoginException le = new LoginException(
+ "Spring Security login failed");
+ le.initCause(e);
+ throw le;
+ }
+ }
+
+ @Override
+ public boolean logout() throws LoginException {
+ // subject.getPrincipals().clear();
+ return true;
+ }
+
+ @Override
+ public boolean commit() throws LoginException {
+ return true;
+ }
+
+ @Override
+ public boolean abort() throws LoginException {
+ return true;
+ }
+
+ protected AuthenticationManager getAuthenticationManager(
+ BundleContextCallback bundleContextCallback) {
+ BundleContext bc = bundleContextCallback.getBundleContext();
+ return bc.getService(bc
+ .getServiceReference(AuthenticationManager.class));
+
+ }
+}
--- /dev/null
+/*
+ * 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.security.login;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginException;
+
+import org.argeo.security.SecurityUtils;
+import org.argeo.util.LocaleCallback;
+import org.argeo.util.LocaleUtils;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+
+/** Login module which caches one subject per thread. */
+public class AnonymousLoginModule extends AbstractSpringSecurityLoginModule {
+ private String anonymousRole = "ROLE_ANONYMOUS";
+ /** Comma separated list of locales */
+ private String availableLocales = null;
+
+ @Override
+ protected Authentication processLogin(CallbackHandler callbackHandler)
+ throws LoginException, UnsupportedCallbackException, IOException,
+ InterruptedException {
+ BundleContextCallback bundleContextCallback = new BundleContextCallback();
+ Locale selectedLocale = null;
+ // multi locale
+ if (availableLocales != null && !availableLocales.trim().equals("")) {
+ LocaleCallback localeCallback = new LocaleCallback(availableLocales);
+ callbackHandler.handle(new Callback[] { localeCallback,
+ bundleContextCallback });
+ selectedLocale = localeCallback.getSelectedLocale();
+ } else {
+ callbackHandler.handle(new Callback[] { bundleContextCallback });
+ }
+
+ List<SimpleGrantedAuthority> authorities = Collections
+ .singletonList(new SimpleGrantedAuthority(anonymousRole));
+ AnonymousAuthenticationToken anonymousToken = new AnonymousAuthenticationToken(
+ SecurityUtils.getStaticKey(), null, authorities);
+
+ Authentication auth = getAuthenticationManager(bundleContextCallback)
+ .authenticate(anonymousToken);
+
+ if (selectedLocale != null)
+ LocaleUtils.threadLocale.set(selectedLocale);
+ return auth;
+ }
+}
--- /dev/null
+package org.argeo.security.login;
+
+import javax.security.auth.callback.Callback;
+
+import org.osgi.framework.BundleContext;
+
+/** Gives access to the OSGi {@link BundleContext} */
+public class BundleContextCallback implements Callback {
+ private BundleContext bundleContext;
+
+ public BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+}
--- /dev/null
+package org.argeo.security.login;
+
+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;
+
+/**
+ * {@link CallbackHandler} that simply wraps a {@link BundleContext} and inject
+ * it in provided {@link BundleContextCallback}
+ */
+public class BundleContextCallbackHandler implements CallbackHandler {
+ private BundleContext bundleContext;
+
+ public BundleContextCallbackHandler() {
+ }
+
+ public BundleContextCallbackHandler(BundleContext bundleContext) {
+ super();
+ this.bundleContext = bundleContext;
+ }
+
+ @Override
+ public void handle(Callback[] callbacks) throws IOException,
+ UnsupportedCallbackException {
+ for (Callback callback : callbacks) {
+ if (callback instanceof BundleContextCallback)
+ ((BundleContextCallback) callback)
+ .setBundleContext(bundleContext);
+ }
+
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+}
--- /dev/null
+/*
+ * 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.security.login;
+
+import java.io.IOException;
+import java.util.Locale;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginException;
+
+import org.argeo.security.NodeAuthenticationToken;
+import org.argeo.util.LocaleCallback;
+import org.argeo.util.LocaleUtils;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+
+/** Authenticates an end user */
+public class EndUserLoginModule extends AbstractSpringSecurityLoginModule {
+ final static String NODE_REPO_URI = "argeo.node.repo.uri";
+
+ private Long waitBetweenFailedLoginAttempts = 5 * 1000l;
+
+ private Boolean remote = false;
+ /** Comma separated list of locales */
+ private String availableLocales = "";
+
+ @Override
+ protected Authentication processLogin(CallbackHandler callbackHandler)
+ throws LoginException, UnsupportedCallbackException, IOException,
+ InterruptedException {
+ // ask for username and password
+ NameCallback nameCallback = new NameCallback("User");
+ PasswordCallback passwordCallback = new PasswordCallback("Password",
+ false);
+ final String defaultNodeUrl = System.getProperty(NODE_REPO_URI,
+ "http://localhost:7070/org.argeo.jcr.webapp/remoting/node");
+ NameCallback urlCallback = new NameCallback("Site URL", defaultNodeUrl);
+ LocaleCallback localeCallback = new LocaleCallback(availableLocales);
+ BundleContextCallback bundleContextCallback = new BundleContextCallback();
+
+ // handle callbacks
+ if (remote)
+ callbackHandler.handle(new Callback[] { nameCallback,
+ passwordCallback, urlCallback, localeCallback,
+ bundleContextCallback });
+ else
+ callbackHandler.handle(new Callback[] { nameCallback,
+ passwordCallback, localeCallback, bundleContextCallback });
+
+ Locale selectedLocale = localeCallback.getSelectedLocale();
+
+ // create credentials
+ final String username = nameCallback.getName();
+ if (username == null || username.trim().equals(""))
+ throw new LoginCanceledException();
+
+ char[] password = {};
+ if (passwordCallback.getPassword() != null)
+ password = passwordCallback.getPassword();
+
+ NodeAuthenticationToken credentials;
+ if (remote) {
+ String url = urlCallback.getName();
+ credentials = new NodeAuthenticationToken(username, password, url);
+ } else {
+ credentials = new NodeAuthenticationToken(username, password);
+ }
+
+ Authentication auth;
+ try {
+ auth = getAuthenticationManager(bundleContextCallback)
+ .authenticate(credentials);
+ } catch (BadCredentialsException e) {
+ // wait between failed login attempts
+ Thread.sleep(waitBetweenFailedLoginAttempts);
+ throw e;
+ }
+ if (selectedLocale != null)
+ LocaleUtils.threadLocale.set(selectedLocale);
+
+ return auth;
+ }
+}
--- /dev/null
+package org.argeo.security.login;
+
+import javax.security.auth.login.LoginException;
+
+public class LoginCanceledException extends LoginException {
+ private static final long serialVersionUID = 8289162094013471043L;
+
+}
--- /dev/null
+/*
+ * 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.security.login;
+
+import java.io.IOException;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginException;
+
+import org.argeo.security.SecurityUtils;
+import org.argeo.security.core.InternalAuthentication;
+import org.springframework.security.core.Authentication;
+
+/** Login module which caches one subject per thread. */
+public class SystemLoginModule extends AbstractSpringSecurityLoginModule {
+ @Override
+ protected Authentication processLogin(CallbackHandler callbackHandler)
+ throws LoginException, UnsupportedCallbackException, IOException,
+ InterruptedException {
+ BundleContextCallback bundleContextCallback = new BundleContextCallback();
+ callbackHandler.handle(new Callback[] { bundleContextCallback });
+ InternalAuthentication anonymousToken = new InternalAuthentication(
+ SecurityUtils.getStaticKey());
+ return getAuthenticationManager(bundleContextCallback).authenticate(
+ anonymousToken);
+ }
+}
--- /dev/null
+/*
+ * 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.security.login;
+
+import java.util.Locale;
+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.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.security.core.AuthorizationPrincipal;
+import org.argeo.util.LocaleCallback;
+import org.argeo.util.LocaleUtils;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.useradmin.Authorization;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+import org.springframework.security.authentication.encoding.LdapShaPasswordEncoder;
+
+/** Login module which caches one subject per thread. */
+public class UserAdminLoginModule implements LoginModule {
+ // private final static Log log = LogFactory
+ // .getLog(UserAdminLoginModule.class);
+
+ private CallbackHandler callbackHandler;
+
+ private Subject subject;
+
+ private Long waitBetweenFailedLoginAttempts = 5 * 1000l;
+
+ /** Comma separated list of locales */
+ private String availableLocales = "";
+
+ private AuthorizationPrincipal auth = null;
+ private Locale selectedLocale = null;
+
+ @SuppressWarnings("unused")
+ private LdapShaPasswordEncoder shaPasswordEncoder = new LdapShaPasswordEncoder();
+
+ public UserAdminLoginModule() {
+
+ }
+
+ @SuppressWarnings("rawtypes")
+ public void initialize(Subject subject, CallbackHandler callbackHandler,
+ Map sharedState, Map options) {
+ this.callbackHandler = callbackHandler;
+ this.subject = subject;
+ }
+
+ public boolean login() throws LoginException {
+ try {
+ // TODO thread already logged in
+ // AuthorizationPrincipal principal = subject
+ // .getPrincipals(AuthorizationPrincipal.class).iterator();
+
+ if (callbackHandler == null)
+ throw new LoginException("No call back handler available");
+
+ // ask for username and password
+ NameCallback nameCallback = new NameCallback("User");
+ PasswordCallback passwordCallback = new PasswordCallback(
+ "Password", false);
+ LocaleCallback localeCallback = new LocaleCallback(availableLocales);
+ BundleContextCallback bundleContextCallback = new BundleContextCallback();
+
+ callbackHandler.handle(new Callback[] { nameCallback,
+ passwordCallback, localeCallback, bundleContextCallback });
+
+ selectedLocale = localeCallback.getSelectedLocale();
+
+ // create credentials
+ final String username = nameCallback.getName();
+ if (username == null || username.trim().equals(""))
+ return false;
+
+ char[] password = {};
+ if (passwordCallback.getPassword() != null)
+ password = passwordCallback.getPassword();
+
+ BundleContext bc = bundleContextCallback.getBundleContext();
+ UserAdmin userAdmin = bc.getService(bc
+ .getServiceReference(UserAdmin.class));
+
+ User user = (User) userAdmin.getRole(username);
+ // TODO use hash
+ boolean authenticated = user.hasCredential(
+ ArgeoNames.ARGEO_PASSWORD, new String(password));
+
+ if (!authenticated) {
+ // wait between failed login attempts
+ Thread.sleep(waitBetweenFailedLoginAttempts);
+ return false;
+ }
+
+ Authorization authorization = userAdmin.getAuthorization(user);
+ auth = new AuthorizationPrincipal(authorization);
+ return true;
+ } catch (LoginException e) {
+ throw e;
+ } catch (ThreadDeath e) {
+ LoginException le = new LoginException(
+ "Spring Security login thread died");
+ le.initCause(e);
+ throw le;
+ } catch (Exception e) {
+ LoginException le = new LoginException(
+ "Spring Security login failed");
+ le.initCause(e);
+ throw le;
+ }
+ }
+
+ @Override
+ public boolean logout() throws LoginException {
+ subject.getPrincipals(AuthorizationPrincipal.class).remove(auth);
+ return true;
+ }
+
+ @Override
+ public boolean commit() throws LoginException {
+ subject.getPrincipals().add(auth);
+ if (selectedLocale != null)
+ LocaleUtils.threadLocale.set(selectedLocale);
+ return true;
+ }
+
+ @Override
+ public boolean abort() throws LoginException {
+ auth = null;
+ selectedLocale = null;
+ return true;
+ }
+
+ public void setAvailableLocales(String locales) {
+ this.availableLocales = locales;
+ }
+}
\ No newline at end of file
+++ /dev/null
-UNIX {
- org.eclipse.equinox.security.auth.module.ExtensionLoginModule sufficient
- extensionId="org.argeo.security.equinox.unixLoginModule";
-};
-
-SPRING {
- org.eclipse.equinox.security.auth.module.ExtensionLoginModule sufficient
- extensionId="org.argeo.security.equinox.springLoginModule";
-};
-
-SPRING_ANONYMOUS {
- org.eclipse.equinox.security.auth.module.ExtensionLoginModule sufficient
- extensionId="org.argeo.security.equinox.anonymousSpringLoginModule";
-};
-
-SPRING_SECURITY_CONTEXT {
- org.eclipse.equinox.security.auth.module.ExtensionLoginModule sufficient
- extensionId="org.argeo.security.equinox.springSecurityContextLoginModule";
-};
-
-KEYRING {
- org.argeo.security.crypto.KeyringLoginModule required;
-};
org.springframework.core,\
org.argeo.eclipse.spring,\
org.argeo.eclipse.ui.specific,\
+org.argeo.cms,\
*
<artifactId>org.argeo.eclipse.ui.rap</artifactId>
<version>2.1.17-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>org.argeo.commons</groupId>
+ <artifactId>org.argeo.cms</artifactId>
+ <version>2.1.17-SNAPSHOT</version>
+ </dependency>
</dependencies>
</project>
\ No newline at end of file
*/
package org.argeo.security.ui.rap;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.eclipse.rap.rwt.RWT;
-import org.eclipse.rap.rwt.application.IEntryPoint;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.PlatformUI;
+import org.eclipse.rap.rwt.application.EntryPoint;
/**
* RAP entry point which authenticates the subject as anonymous, for public
* unauthenticated access.
*/
-public class AnonymousEntryPoint implements IEntryPoint {
- private final static Log log = LogFactory.getLog(AnonymousEntryPoint.class);
+public class AnonymousEntryPoint implements EntryPoint {
+ // private final static Log log =
+ // LogFactory.getLog(AnonymousEntryPoint.class);
/**
* How many seconds to wait before invalidating the session if the user has
* not yet logged in.
*/
private Integer loginTimeout = 1 * 60;
- private Integer sessionTimeout = 15 * 60;
@Override
public int createUI() {
// around too long
RWT.getRequest().getSession().setMaxInactiveInterval(loginTimeout);
- if (log.isDebugEnabled())
- log.debug("Anonymous THREAD=" + Thread.currentThread().getId()
- + ", sessionStore=" + RWT.getSessionStore().getId());
+ // if (log.isDebugEnabled())
+ // log.debug("Anonymous THREAD=" + Thread.currentThread().getId()
+ // + ", sessionStore=" + RWT.getSessionStore().getId());
// create display
- final Display display = PlatformUI.createDisplay();
+ // final Display display = PlatformUI.createDisplay();
// log in
-// final ILoginContext loginContext = SecureRapActivator
-// .createLoginContext(SecureRapActivator.CONTEXT_SPRING_ANONYMOUS);
-// Subject subject = null;
-// try {
-// loginContext.login();
-// subject = loginContext.getSubject();
-// } catch (LoginException e) {
-// throw new ArgeoException(
-// "Unexpected exception during authentication", e);
-// }
-//
-// // identify after successful login
-// if (log.isDebugEnabled())
-// log.debug("Authenticated " + subject);
-// final String username = subject.getPrincipals().iterator().next()
-// .getName();
-//
-// // Once the user is logged in, she can have a longer session timeout
-// RWT.getRequest().getSession().setMaxInactiveInterval(sessionTimeout);
-//
-// // Logout callback when the display is disposed
-// display.disposeExec(new Runnable() {
-// public void run() {
-// log.debug("Display disposed");
-// logout(loginContext, username);
-// }
-// });
-//
-// //
-// // RUN THE WORKBENCH
-// //
-// Integer returnCode = null;
-// try {
-// returnCode = Subject.doAs(subject, new PrivilegedAction<Integer>() {
-// public Integer run() {
-// RapWorkbenchAdvisor workbenchAdvisor = new RapWorkbenchAdvisor(
-// null);
-// int result = PlatformUI.createAndRunWorkbench(display,
-// workbenchAdvisor);
-// return new Integer(result);
-// }
-// });
-// logout(loginContext, username);
-// } finally {
-// display.dispose();
-// }
+ // final ILoginContext loginContext = SecureRapActivator
+ // .createLoginContext(SecureRapActivator.CONTEXT_SPRING_ANONYMOUS);
+ // Subject subject = null;
+ // try {
+ // loginContext.login();
+ // subject = loginContext.getSubject();
+ // } catch (LoginException e) {
+ // throw new ArgeoException(
+ // "Unexpected exception during authentication", e);
+ // }
+ //
+ // // identify after successful login
+ // if (log.isDebugEnabled())
+ // log.debug("Authenticated " + subject);
+ // final String username = subject.getPrincipals().iterator().next()
+ // .getName();
+ //
+ // // Once the user is logged in, she can have a longer session timeout
+ // RWT.getRequest().getSession().setMaxInactiveInterval(sessionTimeout);
+ //
+ // // Logout callback when the display is disposed
+ // display.disposeExec(new Runnable() {
+ // public void run() {
+ // log.debug("Display disposed");
+ // logout(loginContext, username);
+ // }
+ // });
+ //
+ // //
+ // // RUN THE WORKBENCH
+ // //
+ // Integer returnCode = null;
+ // try {
+ // returnCode = Subject.doAs(subject, new PrivilegedAction<Integer>() {
+ // public Integer run() {
+ // RapWorkbenchAdvisor workbenchAdvisor = new RapWorkbenchAdvisor(
+ // null);
+ // int result = PlatformUI.createAndRunWorkbench(display,
+ // workbenchAdvisor);
+ // return new Integer(result);
+ // }
+ // });
+ // logout(loginContext, username);
+ // } finally {
+ // display.dispose();
+ // }
return 1;
}
-// private void logout(ILoginContext secureContext, String username) {
-// try {
-// secureContext.logout();
-// log.info("Logged out " + (username != null ? username : "")
-// + " (THREAD=" + Thread.currentThread().getId() + ")");
-// } catch (LoginException e) {
-// log.error("Erorr when logging out", e);
-// }
-// }
+ // private void logout(ILoginContext secureContext, String username) {
+ // try {
+ // secureContext.logout();
+ // log.info("Logged out " + (username != null ? username : "")
+ // + " (THREAD=" + Thread.currentThread().getId() + ")");
+ // } catch (LoginException e) {
+ // log.error("Erorr when logging out", e);
+ // }
+ // }
}
*/
package org.argeo.security.ui.rap;
-import org.eclipse.rap.rwt.application.IEntryPoint;
+import org.eclipse.rap.rwt.application.EntryPoint;
import org.eclipse.ui.PlatformUI;
/**
* RAP entry point which does doesing except creating the display
*/
-public class NullEntryPoint implements IEntryPoint {
+public class NullEntryPoint implements EntryPoint {
@Override
public int createUI() {
// create display
// Handle window resize in Rap 2.1+ see
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=417254
Display.getCurrent().addListener(SWT.Resize, new Listener() {
+ private static final long serialVersionUID = 2970912561866704526L;
+
@Override
public void handleEvent(Event event) {
Rectangle bounds = event.display.getBounds();
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.argeo.ArgeoException;
+import org.argeo.cms.KernelHeader;
import org.argeo.eclipse.ui.workbench.ErrorFeedback;
+import org.argeo.security.login.LoginCanceledException;
import org.argeo.security.ui.dialogs.DefaultLoginDialog;
import org.argeo.util.LocaleUtils;
import org.eclipse.jface.dialogs.MessageDialog;
Subject subject = new Subject();
// log in
- // BundleContext bc =
- // SecureRapActivator.getActivator().getBundleContext();
Thread.currentThread().setContextClassLoader(
getClass().getClassLoader());
final LoginContext loginContext;
try {
- loginContext = new LoginContext(SPRING_SECURITY_CONTEXT_KEY,
+ loginContext = new LoginContext(KernelHeader.LOGIN_CONTEXT_USER,
subject, new DefaultLoginDialog(display.getActiveShell()));
} catch (LoginException e1) {
throw new ArgeoException("Cannot initialize login context", e1);
}
- // final LoginModule loginModule = bc.getService(bc
- // .getServiceReference(LoginModule.class));
- // loginModule.initialize(subject,
- // new DefaultLoginDialog(display.getActiveShell()), null, null);
+
tryLogin: while (subject.getPrincipals(Authentication.class).size() == 0) {
try {
loginContext.login();
if (t instanceof BadCredentialsException)
return (BadCredentialsException) t;
+ if (t instanceof LoginCanceledException)
+ return new BadCredentialsException("Login canceled");
+
if (t.getCause() != null)
return wasCausedByBadCredentials(t.getCause());
else
/** Configure Equinox login context from the bundle context. */
public class SecureRapActivator implements BundleActivator {
-
public final static String ID = "org.argeo.security.ui.rap";
- public final static String CONTEXT_SPRING = "SPRING";
- public final static String CONTEXT_SPRING_ANONYMOUS = "SPRING_ANONYMOUS";
- private static final String JAAS_CONFIG_FILE = "/META-INF/jaas_default.txt";
private BundleContext bundleContext;
private static SecureRapActivator activator = null;
public static SecureRapActivator getActivator() {
return activator;
}
-
-// static ILoginContext createLoginContext(String contextName) {
-// URL configUrl = getActivator().getBundleContext().getBundle()
-// .getEntry(JAAS_CONFIG_FILE);
-// return LoginContextFactory.createContext(contextName, configUrl);
-// }
}