X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=security%2Fplugins%2Forg.argeo.security.equinox%2Fsrc%2Fmain%2Fjava%2Forg%2Fargeo%2Fsecurity%2Fequinox%2FSpringLoginModule.java;h=553b0994da0ff39c763ceaa5fb29858d7a3e81d3;hb=1d5afdce3e91054f07ddd3c98309c363b4cf1d46;hp=90e8b3decedae4932ed7b6d0ba9d20d05c3c313c;hpb=2f510fb09e18bc3d3e902c8131d0037763c5f279;p=lgpl%2Fargeo-commons.git diff --git a/security/plugins/org.argeo.security.equinox/src/main/java/org/argeo/security/equinox/SpringLoginModule.java b/security/plugins/org.argeo.security.equinox/src/main/java/org/argeo/security/equinox/SpringLoginModule.java index 90e8b3dec..553b0994d 100644 --- a/security/plugins/org.argeo.security.equinox/src/main/java/org/argeo/security/equinox/SpringLoginModule.java +++ b/security/plugins/org.argeo.security.equinox/src/main/java/org/argeo/security/equinox/SpringLoginModule.java @@ -1,30 +1,62 @@ +/* + * Copyright (C) 2007-2012 Mathieu Baudier + * + * 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.equinox; import java.util.Map; -import java.util.concurrent.Executor; +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.callback.TextOutputCallback; import javax.security.auth.login.LoginException; -import org.argeo.security.SiteAuthenticationToken; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.security.NodeAuthenticationToken; import org.springframework.security.Authentication; import org.springframework.security.AuthenticationManager; import org.springframework.security.BadCredentialsException; +import org.springframework.security.GrantedAuthority; +import org.springframework.security.GrantedAuthorityImpl; import org.springframework.security.context.SecurityContextHolder; +import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken; import org.springframework.security.providers.jaas.SecurityContextLoginModule; /** Login module which caches one subject per thread. */ public class SpringLoginModule extends SecurityContextLoginModule { + final static String NODE_REPO_URI = "argeo.node.repo.uri"; + + private final static Log log = LogFactory.getLog(SpringLoginModule.class); + private AuthenticationManager authenticationManager; - private Executor systemExecutor; private CallbackHandler callbackHandler; + private Subject subject; + + private Long waitBetweenFailedLoginAttempts = 5 * 1000l; + + private Boolean remote = false; + private Boolean anonymous = false; + + private String key = null; + private String anonymousRole = "ROLE_ANONYMOUS"; + public SpringLoginModule() { } @@ -33,84 +65,117 @@ public class SpringLoginModule extends SecurityContextLoginModule { public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { super.initialize(subject, callbackHandler, sharedState, options); - // this.subject.set(subject); this.callbackHandler = callbackHandler; + this.subject = subject; } public boolean login() throws LoginException { - // thread already logged in - if (SecurityContextHolder.getContext().getAuthentication() != null) - return super.login(); - - // if (getSubject().getPrincipals(Authentication.class).size() == 1) { - // registerAuthentication(getSubject() - // .getPrincipals(Authentication.class).iterator().next()); - // return super.login(); - // } else if (getSubject().getPrincipals(Authentication.class).size() > - // 1) { - // throw new LoginException( - // "Multiple Authentication principals not supported: " - // + getSubject().getPrincipals(Authentication.class)); - // } else { - // ask for username and password - Callback label = new TextOutputCallback(TextOutputCallback.INFORMATION, - "Required login"); - NameCallback nameCallback = new NameCallback("User"); - PasswordCallback passwordCallback = new PasswordCallback("Password", - false); - NameCallback urlCallback = new NameCallback("Site URL"); - - if (callbackHandler == null) { - throw new LoginException("No call back handler available"); - // return false; - } try { - callbackHandler.handle(new Callback[] { label, nameCallback, - passwordCallback, urlCallback }); - } catch (Exception e) { - LoginException le = new LoginException("Callback handling failed"); - le.initCause(e); - throw le; - } - - // Set user name and password - String username = nameCallback.getName(); - String password = ""; - if (passwordCallback.getPassword() != null) { - password = String.valueOf(passwordCallback.getPassword()); - } - String url = urlCallback.getName(); - // TODO: set it via system properties - String workspace = null; - - // UsernamePasswordAuthenticationToken credentials = new - // UsernamePasswordAuthenticationToken( - // username, password); - SiteAuthenticationToken credentials = new SiteAuthenticationToken( - username, password, url, workspace); - - try { - - Authentication authentication = authenticationManager - .authenticate(credentials); + // thread already logged in + if (SecurityContextHolder.getContext().getAuthentication() != null) + return super.login(); + + 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); + if (subject.getPrincipals() != null) + subject.getPrincipals().clear(); + if (subject.getPrivateCredentials() != null) + subject.getPrivateCredentials().clear(); + if (subject.getPublicCredentials() != null) + subject.getPublicCredentials().clear(); + + // deals first with public access since it's simple + if (anonymous) { + // TODO integrate with JCR? + Object principal = UUID.randomUUID().toString(); + GrantedAuthority[] authorities = { new GrantedAuthorityImpl( + anonymousRole) }; + AnonymousAuthenticationToken anonymousToken = new AnonymousAuthenticationToken( + key, principal, authorities); + Authentication auth = authenticationManager + .authenticate(anonymousToken); + registerAuthentication(auth); + return super.login(); + } + + 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 = "http://localhost:7070/org.argeo.jcr.webapp/remoting/node"; + final String defaultSecurityWorkspace = "security"; + NameCallback urlCallback = new NameCallback("Site URL", + defaultNodeUrl); + NameCallback securityWorkspaceCallback = new NameCallback( + "Security Workspace", defaultSecurityWorkspace); + + // handle callbacks + if (remote) + callbackHandler.handle(new Callback[] { nameCallback, + passwordCallback, urlCallback, + securityWorkspaceCallback }); + else + callbackHandler.handle(new Callback[] { nameCallback, + passwordCallback }); + + // create credentials + String username = nameCallback.getName(); + if (username == null || username.trim().equals("")) + return false; + + String password = ""; + if (passwordCallback.getPassword() != null) + password = String.valueOf(passwordCallback.getPassword()); + + NodeAuthenticationToken credentials; + if (remote) { + String url = urlCallback.getName(); + String workspace = securityWorkspaceCallback.getName(); + credentials = new NodeAuthenticationToken(username, password, + url, workspace); + } else { + credentials = new NodeAuthenticationToken(username, password); + } + + Authentication authentication; + try { + authentication = authenticationManager + .authenticate(credentials); + } catch (BadCredentialsException e) { + // wait between failed login attempts + Thread.sleep(waitBetweenFailedLoginAttempts); + throw e; + } registerAuthentication(authentication); boolean res = super.login(); - // if (log.isDebugEnabled()) - // log.debug("User " + username + " logged in"); return res; - } catch (BadCredentialsException bce) { - throw bce; + } 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 loginException = new LoginException( - "Bad credentials"); - loginException.initCause(e); - throw loginException; + LoginException le = new LoginException( + "Spring Security login failed"); + le.initCause(e); + throw le; } - // } } @Override public boolean logout() throws LoginException { + subject.getPrincipals().clear(); return super.logout(); } @@ -130,12 +195,26 @@ public class SpringLoginModule extends SecurityContextLoginModule { this.authenticationManager = authenticationManager; } - public void setSystemExecutor(Executor systemExecutor) { - this.systemExecutor = systemExecutor; + /** Authenticates on a remote node */ + public void setRemote(Boolean remote) { + this.remote = remote; } - // protected Subject getSubject() { - // return subject.get(); - // } + /** + * 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; + } }