]> git.argeo.org Git - lgpl/argeo-commons.git/blobdiff - security/plugins/org.argeo.security.equinox/src/main/java/org/argeo/security/equinox/SpringLoginModule.java
Update license headers
[lgpl/argeo-commons.git] / security / plugins / org.argeo.security.equinox / src / main / java / org / argeo / security / equinox / SpringLoginModule.java
index c357a9ea7f1ae44e0e756bacfd2f15125486748e..553b0994da0ff39c763ceaa5fb29858d7a3e81d3 100644 (file)
@@ -1,28 +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.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 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() {
 
        }
@@ -31,83 +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();
        }
 
@@ -127,8 +195,26 @@ public class SpringLoginModule extends SecurityContextLoginModule {
                this.authenticationManager = authenticationManager;
        }
 
-       // protected Subject getSubject() {
-       // return subject.get();
-       // }
+       /** 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;
+       }
 
 }