Improve login
authorMathieu Baudier <mbaudier@argeo.org>
Fri, 13 Feb 2015 22:18:02 +0000 (22:18 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Fri, 13 Feb 2015 22:18:02 +0000 (22:18 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@7854 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

22 files changed:
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg
org.argeo.cms/src/org/argeo/cms/internal/useradmin/AbstractJcrUser.java
org.argeo.cms/src/org/argeo/cms/internal/useradmin/JcrAuthorization.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/useradmin/JcrEndUser.java
org.argeo.cms/src/org/argeo/cms/internal/useradmin/JcrGroup.java
org.argeo.cms/src/org/argeo/cms/internal/useradmin/JcrRole.java
org.argeo.cms/src/org/argeo/cms/internal/useradmin/JcrUserAdmin.java
org.argeo.security.core/src/org/argeo/security/ArgeoGroup.java [deleted file]
org.argeo.security.core/src/org/argeo/security/ArgeoUser.java [deleted file]
org.argeo.security.core/src/org/argeo/security/NodeAuthenticationToken.java
org.argeo.security.core/src/org/argeo/security/core/AuthorizationPrincipal.java [deleted file]
org.argeo.security.core/src/org/argeo/security/core/ConsoleCallbackHandler.java [deleted file]
org.argeo.security.core/src/org/argeo/security/jcr/jackrabbit/JackrabbitUserAdminService.java
org.argeo.security.core/src/org/argeo/security/login/AbstractSpringLoginModule.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/login/AbstractSpringSecurityLoginModule.java [deleted file]
org.argeo.security.core/src/org/argeo/security/login/AnonymousLoginModule.java
org.argeo.security.core/src/org/argeo/security/login/ConsoleCallbackHandler.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/login/EndUserLoginModule.java
org.argeo.security.core/src/org/argeo/security/login/SystemLoginModule.java
org.argeo.security.core/src/org/argeo/security/login/UserAdminLoginModule.java [deleted file]
org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoLoginModule.java

index be94cd5df7f3f6a716e68962ae38ff2ce75461e6..d0aec2023cbcaccbf32eca9594d00dfec0d77d0c 100644 (file)
@@ -7,6 +7,8 @@ import javax.jcr.RepositoryException;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.argeo.cms.CmsException;
+import org.argeo.cms.internal.useradmin.JcrUserAdmin;
+import org.argeo.security.SecurityUtils;
 import org.argeo.security.UserAdminService;
 import org.argeo.security.core.InternalAuthentication;
 import org.argeo.security.core.InternalAuthenticationProvider;
@@ -14,6 +16,7 @@ import org.argeo.security.jcr.SimpleJcrSecurityModel;
 import org.argeo.security.jcr.jackrabbit.JackrabbitUserAdminService;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.useradmin.UserAdmin;
 import org.springframework.security.authentication.AnonymousAuthenticationProvider;
 import org.springframework.security.authentication.AnonymousAuthenticationToken;
 import org.springframework.security.authentication.AuthenticationManager;
@@ -31,13 +34,13 @@ class NodeSecurity implements AuthenticationManager {
        private final InternalAuthenticationProvider internalAuth;
        private final AnonymousAuthenticationProvider anonymousAuth;
        private final JackrabbitUserAdminService userAdminService;
-       // private final JcrUserAdmin userAdmin;
+       private final JcrUserAdmin userAdmin;
 
        private ServiceRegistration<AuthenticationManager> authenticationManagerReg;
        private ServiceRegistration<UserAdminService> userAdminServiceReg;
        private ServiceRegistration<UserDetailsManager> userDetailsManagerReg;
 
-       // private ServiceRegistration<UserAdmin> userAdminReg;
+       private ServiceRegistration<UserAdmin> userAdminReg;
 
        public NodeSecurity(BundleContext bundleContext, JackrabbitNode node)
                        throws RepositoryException {
@@ -49,9 +52,9 @@ class NodeSecurity implements AuthenticationManager {
                this.bundleContext = bundleContext;
 
                internalAuth = new InternalAuthenticationProvider(
-                               KernelConstants.DEFAULT_SECURITY_KEY);
+                               SecurityUtils.getStaticKey());
                anonymousAuth = new AnonymousAuthenticationProvider(
-                               KernelConstants.DEFAULT_SECURITY_KEY);
+                               SecurityUtils.getStaticKey());
 
                // user admin
                userAdminService = new JackrabbitUserAdminService();
@@ -59,8 +62,8 @@ class NodeSecurity implements AuthenticationManager {
                userAdminService.setSecurityModel(new SimpleJcrSecurityModel());
                userAdminService.init();
 
-               // userAdmin = new JcrUserAdmin(bundleContext);
-               // userAdmin.setUserAdminService(userAdminService);
+               userAdmin = new JcrUserAdmin(bundleContext, node);
+               userAdmin.setUserAdminService(userAdminService);
        }
 
        public void publish() {
@@ -70,8 +73,8 @@ class NodeSecurity implements AuthenticationManager {
                                UserAdminService.class, userAdminService, null);
                userDetailsManagerReg = bundleContext.registerService(
                                UserDetailsManager.class, userAdminService, null);
-               // userAdminReg = bundleContext.registerService(UserAdmin.class,
-               // userAdmin, null);
+               userAdminReg = bundleContext.registerService(UserAdmin.class,
+                               userAdmin, null);
        }
 
        void destroy() {
@@ -83,7 +86,7 @@ class NodeSecurity implements AuthenticationManager {
                userDetailsManagerReg.unregister();
                userAdminServiceReg.unregister();
                authenticationManagerReg.unregister();
-               // userAdminReg.unregister();
+               userAdminReg.unregister();
        }
 
        @Override
index 0155fc5eee9eae292a04d372041ec173d0af1099..55194eab828d01e2632327b075350570485f82ca 100644 (file)
@@ -1,16 +1,16 @@
 USER {
     org.argeo.security.login.EndUserLoginModule requisite;
-    org.springframework.security.authentication.jaas.SecurityContextLoginModule required;
+    org.springframework.security.authentication.jaas.SecurityContextLoginModule requisite;
 };
 
 ANONYMOUS {
     org.argeo.security.login.AnonymousLoginModule requisite;
-    org.springframework.security.authentication.jaas.SecurityContextLoginModule required;
+    org.springframework.security.authentication.jaas.SecurityContextLoginModule requisite;
 };
 
 SYSTEM {
     org.argeo.security.login.SystemLoginModule requisite;
-    org.springframework.security.authentication.jaas.SecurityContextLoginModule required;
+    org.springframework.security.authentication.jaas.SecurityContextLoginModule requisite;
 };
 
 KEYRING {
index 25510f605897abec4174992b685d14dac40800bd..f13da0128e112713cd571d1578fa57365247550d 100644 (file)
@@ -2,9 +2,18 @@ package org.argeo.cms.internal.useradmin;
 
 import java.util.Dictionary;
 
-import org.argeo.security.ArgeoUser;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
 
-abstract class AbstractJcrUser extends JcrRole implements ArgeoUser {
+abstract class AbstractJcrUser extends JcrRole implements User {
+       public AbstractJcrUser(String name) {
+               super(name);
+       }
+
+       @Override
+       public int getType() {
+               return Role.USER;
+       }
 
        @Override
        public Dictionary<String, Object> getCredentials() {
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/useradmin/JcrAuthorization.java b/org.argeo.cms/src/org/argeo/cms/internal/useradmin/JcrAuthorization.java
new file mode 100644 (file)
index 0000000..76f67ce
--- /dev/null
@@ -0,0 +1,39 @@
+package org.argeo.cms.internal.useradmin;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.argeo.security.jcr.JcrUserDetails;
+import org.osgi.service.useradmin.Authorization;
+import org.springframework.security.core.GrantedAuthority;
+
+class JcrAuthorization implements Authorization {
+       private final String name;
+       private final List<String> roles;
+
+       public JcrAuthorization(JcrUserDetails userDetails) {
+               this.name = userDetails.getUsername();
+               List<String> t = new ArrayList<String>();
+               for (GrantedAuthority ga : userDetails.getAuthorities()) {
+                       t.add(ga.getAuthority());
+               }
+               roles = Collections.unmodifiableList(t);
+       }
+
+       @Override
+       public String getName() {
+               return name;
+       }
+
+       @Override
+       public boolean hasRole(String name) {
+               return roles.contains(name);
+       }
+
+       @Override
+       public String[] getRoles() {
+               return roles.toArray(new String[roles.size()]);
+       }
+
+}
index b2ce05f2154b7642826838f2ef45bc60adcea717..fbbbdd01fffe2a3f4bc5a4f0020be445ecd38ca3 100644 (file)
@@ -1,16 +1,31 @@
 package org.argeo.cms.internal.useradmin;
 
-import org.springframework.security.core.userdetails.UserDetails;
+import org.argeo.security.jcr.JcrUserDetails;
 
-class JcrEndUser extends AbstractJcrUser  {
-       private final UserDetails userDetails;
+class JcrEndUser extends AbstractJcrUser {
+       private final JcrUserDetails userDetails;
 
-       public JcrEndUser(UserDetails userDetails) {
+       public JcrEndUser(JcrUserDetails userDetails) {
+               super(userDetails.getUsername());
                this.userDetails = userDetails;
        }
 
-       UserDetails getUserDetails() {
+       JcrUserDetails getUserDetails() {
                return userDetails;
        }
 
+       public String toString() {
+               return "ArgeoUser: " + getName();
+       }
+
+       public boolean equals(Object obj) {
+               if (!(obj instanceof JcrEndUser))
+                       return false;
+               else
+                       return ((JcrEndUser) obj).getName().equals(getName());
+       }
+
+       public int hashCode() {
+               return getName().hashCode();
+       }
 }
index ffbeb1306df2c202fa357ad48102b56c8e165aa4..93f7587589169101188a5e2e90eea887573cedd6 100644 (file)
@@ -5,6 +5,15 @@ import org.osgi.service.useradmin.Role;
 
 class JcrGroup extends AbstractJcrUser implements Group {
        public JcrGroup(String name) {
+               super(name);
+       }
+
+       //
+       // OSGi MODEL
+       //
+       @Override
+       public int getType() {
+               return Role.GROUP;
        }
 
        @Override
@@ -37,4 +46,19 @@ class JcrGroup extends AbstractJcrUser implements Group {
                return null;
        }
 
+       public String toString() {
+               return "ArgeoGroup: " + getName();
+       }
+
+       public boolean equals(Object obj) {
+               if (!(obj instanceof JcrGroup))
+                       return false;
+               else
+                       return ((JcrGroup) obj).getName().equals(getName());
+       }
+
+       public int hashCode() {
+               return getName().hashCode();
+       }
+
 }
index 5bd0d578c3433494457eb7f836c52f8384b7b4aa..43ded9e5957c9d5fd569fb753ad79e80d15dfcd9 100644 (file)
@@ -4,18 +4,21 @@ import java.util.Dictionary;
 
 import org.osgi.service.useradmin.Role;
 
-class JcrRole implements Role {
+abstract class JcrRole implements Role {
+       private String name;
+
+       public JcrRole(String name) {
+               this.name = name;
+       }
 
        @Override
        public String getName() {
-               // TODO Auto-generated method stub
-               return null;
+               return name;
        }
 
        @Override
        public int getType() {
-               // TODO Auto-generated method stub
-               return 0;
+               return Role.ROLE;
        }
 
        @Override
index cc402887423326a1c0f8dc155ed1c34801a71d04..94051d92eb0401f79d3fbe45bbffb8a969139dff 100644 (file)
@@ -1,8 +1,5 @@
 package org.argeo.cms.internal.useradmin;
 
-import static org.argeo.jcr.ArgeoJcrConstants.ALIAS_NODE;
-import static org.argeo.jcr.ArgeoJcrConstants.JCR_REPOSITORY_ALIAS;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
@@ -14,6 +11,7 @@ import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 
 import org.argeo.ArgeoException;
+import org.argeo.jcr.JcrUtils;
 import org.argeo.security.UserAdminService;
 import org.argeo.security.jcr.JcrSecurityModel;
 import org.argeo.security.jcr.JcrUserDetails;
@@ -28,7 +26,6 @@ import org.osgi.service.useradmin.UserAdmin;
 import org.osgi.service.useradmin.UserAdminEvent;
 import org.osgi.service.useradmin.UserAdminListener;
 import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 
 public class JcrUserAdmin implements UserAdmin {
@@ -38,20 +35,19 @@ public class JcrUserAdmin implements UserAdmin {
        private final JcrSecurityModel jcrSecurityModel = new SimpleJcrSecurityModel();
        private final Session session;
 
-       public JcrUserAdmin(BundleContext bundleContext) {
+       public JcrUserAdmin(BundleContext bundleContext, Repository node) {
                try {
                        this.bundleContext = bundleContext;
-
-                       ServiceReference<Repository> nodeRepo = bundleContext
-                                       .getServiceReferences(Repository.class,
-                                                       "(" + JCR_REPOSITORY_ALIAS + "=" + ALIAS_NODE + ")")
-                                       .iterator().next();
-                       this.session = bundleContext.getService(nodeRepo).login();
+                       this.session = node.login();
                } catch (Exception e) {
                        throw new ArgeoException("Cannot initialize user admin", e);
                }
        }
 
+       public void destroy() {
+               JcrUtils.logoutQuietly(session);
+       }
+
        @Override
        public Role createRole(String name, int type) {
                try {
@@ -95,14 +91,15 @@ public class JcrUserAdmin implements UserAdmin {
 
        @Override
        public Role getRole(String name) {
-               if (userAdminService().listEditableRoles().contains(name))
-                       return new JcrGroup(name);
                try {
-                       UserDetails userDetails = userAdminService().loadUserByUsername(
-                                       name);
+                       JcrUserDetails userDetails = (JcrUserDetails) userAdminService()
+                                       .loadUserByUsername(name);
                        return new JcrEndUser(userDetails);
                } catch (UsernameNotFoundException e) {
-                       return null;
+                       if (userAdminService().listEditableRoles().contains(name))
+                               return new JcrGroup(name);
+                       else
+                               return null;
                }
        }
 
@@ -118,8 +115,9 @@ public class JcrUserAdmin implements UserAdmin {
                for (int i = 0; i < roles.size(); i++)
                        res[i] = new JcrGroup(roles.get(i));
                for (int i = 0; i < users.size(); i++)
-                       res[roles.size() + i] = new JcrEndUser(userAdminService()
-                                       .loadUserByUsername(users.get(i)));
+                       res[roles.size() + i] = new JcrEndUser(
+                                       (JcrUserDetails) userAdminService().loadUserByUsername(
+                                                       users.get(i)));
                return res;
        }
 
@@ -130,7 +128,7 @@ public class JcrUserAdmin implements UserAdmin {
 
        @Override
        public Authorization getAuthorization(User user) {
-               return null;
+               return new JcrAuthorization(((JcrEndUser) user).getUserDetails());
        }
 
        private synchronized UserAdminService userAdminService() {
diff --git a/org.argeo.security.core/src/org/argeo/security/ArgeoGroup.java b/org.argeo.security.core/src/org/argeo/security/ArgeoGroup.java
deleted file mode 100644 (file)
index 42c09e6..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.argeo.security;
-
-import org.osgi.service.useradmin.Group;
-
-public interface ArgeoGroup extends ArgeoUser, Group, java.security.acl.Group {
-
-}
diff --git a/org.argeo.security.core/src/org/argeo/security/ArgeoUser.java b/org.argeo.security.core/src/org/argeo/security/ArgeoUser.java
deleted file mode 100644 (file)
index 193858a..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.argeo.security;
-
-import java.security.Principal;
-
-import org.osgi.service.useradmin.User;
-
-public interface ArgeoUser extends Principal, User {
-
-}
index 4a802553b5050fbd5fe34fa123c11424137e126b..600811d75e09720199cf00a86f4f0b05680e82ed 100644 (file)
@@ -53,4 +53,17 @@ public class NodeAuthenticationToken extends
        public Boolean isRemote() {
                return url != null;
        }
+
+       public String toString() {
+               String username = getName();
+               StringBuilder buf = new StringBuilder("groups=");
+               for (GrantedAuthority ga : getAuthorities()) {
+                       if (!ga.getAuthority().equals(username)) {
+                               buf.append(ga.getAuthority());
+                               buf.append(',');
+                       }
+               }
+               buf.deleteCharAt(buf.length() - 1);
+               return "uid=" + getName() + " " + buf.toString();
+       }
 }
diff --git a/org.argeo.security.core/src/org/argeo/security/core/AuthorizationPrincipal.java b/org.argeo.security.core/src/org/argeo/security/core/AuthorizationPrincipal.java
deleted file mode 100644 (file)
index ac3b233..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.argeo.security.core;
-
-import java.security.Principal;
-
-import org.osgi.service.useradmin.Authorization;
-
-/** Wraps an OSGi {@link Authorization} as a JAAS {@link Principal} */
-public final class AuthorizationPrincipal implements Principal {
-       private Authorization authorization;
-
-       public AuthorizationPrincipal(Authorization authorization) {
-               this.authorization = authorization;
-       }
-
-       @Override
-       public String getName() {
-               return authorization.getName();
-       }
-
-       public Authorization getAuthorization() {
-               return authorization;
-       }
-
-}
diff --git a/org.argeo.security.core/src/org/argeo/security/core/ConsoleCallbackHandler.java b/org.argeo.security.core/src/org/argeo/security/core/ConsoleCallbackHandler.java
deleted file mode 100644 (file)
index faa81b0..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-package org.argeo.security.core;
-
-import java.io.Console;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Arrays;
-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.TextOutputCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-
-import org.argeo.ArgeoException;
-import org.argeo.util.LocaleCallback;
-
-/** Callback handler to be used with a command line UI. */
-public class ConsoleCallbackHandler implements CallbackHandler {
-
-       @Override
-       public void handle(Callback[] callbacks) throws IOException,
-                       UnsupportedCallbackException {
-               Console console = System.console();
-               if (console == null)
-                       throw new ArgeoException("No console available");
-
-               PrintWriter writer = console.writer();
-               for (int i = 0; i < callbacks.length; i++) {
-                       if (callbacks[i] instanceof TextOutputCallback) {
-                               TextOutputCallback callback = (TextOutputCallback) callbacks[i];
-                               writer.write(callback.getMessage());
-                       } else if (callbacks[i] instanceof NameCallback) {
-                               NameCallback callback = (NameCallback) callbacks[i];
-                               writer.write(callback.getPrompt());
-                               if (callback.getDefaultName() != null)
-                                       writer.write(" (" + callback.getDefaultName() + ")");
-                               writer.write(" : ");
-                               String answer = console.readLine();
-                               if (callback.getDefaultName() != null
-                                               && answer.trim().equals(""))
-                                       callback.setName(callback.getDefaultName());
-                               else
-                                       callback.setName(answer);
-                       } else if (callbacks[i] instanceof PasswordCallback) {
-                               PasswordCallback callback = (PasswordCallback) callbacks[i];
-                               writer.write(callback.getPrompt());
-                               char[] answer = console.readPassword();
-                               callback.setPassword(answer);
-                               Arrays.fill(answer, ' ');
-                       } else if (callbacks[i] instanceof LocaleCallback) {
-                               LocaleCallback callback = (LocaleCallback) callbacks[i];
-                               writer.write(callback.getPrompt());
-                               writer.write("\n");
-                               for (int j = 0; j < callback.getAvailableLocales().size(); j++) {
-                                       Locale locale = callback.getAvailableLocales().get(j);
-                                       writer.print(j + " : " + locale.getDisplayName() + "\n");
-                               }
-                               writer.write("(" + callback.getDefaultIndex() + ") : ");
-                               String answer = console.readLine();
-                               if (answer.trim().equals(""))
-                                       callback.setSelectedIndex(callback.getDefaultIndex());
-                               else
-                                       callback.setSelectedIndex(new Integer(answer.trim()));
-                       }
-               }
-       }
-
-}
index 711c9d598e031f50069bd624bface214fac2638c..c0b4b4b1508a29def68a681eadcd873953592a3c 100644 (file)
@@ -22,6 +22,7 @@ import org.apache.jackrabbit.core.security.authentication.CryptedSimpleCredentia
 import org.argeo.ArgeoException;
 import org.argeo.jcr.JcrUtils;
 import org.argeo.jcr.UserJcrUtils;
+import org.argeo.security.NodeAuthenticationToken;
 import org.argeo.security.UserAdminService;
 import org.argeo.security.jcr.JcrSecurityModel;
 import org.argeo.security.jcr.JcrUserDetails;
@@ -300,7 +301,7 @@ public class JackrabbitUserAdminService implements UserAdminService,
        // AUTHENTICATION PROVIDER
        public synchronized Authentication authenticate(
                        Authentication authentication) throws AuthenticationException {
-               UsernamePasswordAuthenticationToken siteAuth = (UsernamePasswordAuthenticationToken) authentication;
+               NodeAuthenticationToken siteAuth = (NodeAuthenticationToken) authentication;
                String username = siteAuth.getName();
                if (!(siteAuth.getCredentials() instanceof char[]))
                        throw new ArgeoException("Only char array passwords are supported");
@@ -334,8 +335,8 @@ public class JackrabbitUserAdminService implements UserAdminService,
                try {
                        JcrUserDetails userDetails = loadJcrUserDetails(adminSession,
                                        username);
-                       UsernamePasswordAuthenticationToken authenticated = new UsernamePasswordAuthenticationToken(
-                                       siteAuth, "", userDetails.getAuthorities());
+                       NodeAuthenticationToken authenticated = new NodeAuthenticationToken(
+                                       siteAuth, userDetails.getAuthorities());
                        authenticated.setDetails(userDetails);
                        return authenticated;
                } catch (RepositoryException e) {
diff --git a/org.argeo.security.core/src/org/argeo/security/login/AbstractSpringLoginModule.java b/org.argeo.security.core/src/org/argeo/security/login/AbstractSpringLoginModule.java
new file mode 100644 (file)
index 0000000..8a09a08
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * 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.osgi.service.useradmin.UserAdmin;
+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 AbstractSpringLoginModule implements LoginModule {
+       // private final static Log log = LogFactory
+       // .getLog(AbstractSpringLoginModule.class);
+       private CallbackHandler callbackHandler;
+       private Subject subject;
+
+       private Authentication authentication;
+
+       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) {
+                                       throw new LoginException(
+                                                       "Security context set but not Authentication principal");
+                               } else {
+                                       Authentication principal = subject
+                                                       .getPrincipals(Authentication.class).iterator()
+                                                       .next();
+                                       if (principal != currentAuth)
+                                               throw new LoginException(
+                                                               "Already authenticated with a different auth");
+                               }
+                               return true;
+                       }
+
+                       if (callbackHandler == null)
+                               throw new LoginException("No callback handler available");
+
+                       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 {
+               SecurityContextHolder.getContext().setAuthentication(null);
+               return true;
+       }
+
+       @Override
+       public boolean commit() throws LoginException {
+               return true;
+       }
+
+       @Override
+       public boolean abort() throws LoginException {
+               SecurityContextHolder.getContext().setAuthentication(null);
+               return true;
+       }
+
+       protected AuthenticationManager getAuthenticationManager(
+                       BundleContextCallback bundleContextCallback) {
+               BundleContext bc = bundleContextCallback.getBundleContext();
+               return bc.getService(bc
+                               .getServiceReference(AuthenticationManager.class));
+
+       }
+
+       protected UserAdmin getUserAdmin(BundleContextCallback bundleContextCallback) {
+               BundleContext bc = bundleContextCallback.getBundleContext();
+               return bc.getService(bc.getServiceReference(UserAdmin.class));
+       }
+
+       protected Subject getSubject() {
+               return subject;
+       }
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/login/AbstractSpringSecurityLoginModule.java b/org.argeo.security.core/src/org/argeo/security/login/AbstractSpringSecurityLoginModule.java
deleted file mode 100644 (file)
index 923646f..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * 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));
-
-       }
-}
index e94c3e0fec18bd86ec9ce05950d67c927da549b0..0a1279caeac3b730370bc34578619280ef8da99f 100644 (file)
@@ -33,7 +33,7 @@ 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 {
+public class AnonymousLoginModule extends AbstractSpringLoginModule {
        private String anonymousRole = "ROLE_ANONYMOUS";
        /** Comma separated list of locales */
        private String availableLocales = null;
diff --git a/org.argeo.security.core/src/org/argeo/security/login/ConsoleCallbackHandler.java b/org.argeo.security.core/src/org/argeo/security/login/ConsoleCallbackHandler.java
new file mode 100644 (file)
index 0000000..4c47ac6
--- /dev/null
@@ -0,0 +1,70 @@
+package org.argeo.security.login;
+
+import java.io.Console;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+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.TextOutputCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.argeo.ArgeoException;
+import org.argeo.util.LocaleCallback;
+
+/** Callback handler to be used with a command line UI. */
+public class ConsoleCallbackHandler implements CallbackHandler {
+
+       @Override
+       public void handle(Callback[] callbacks) throws IOException,
+                       UnsupportedCallbackException {
+               Console console = System.console();
+               if (console == null)
+                       throw new ArgeoException("No console available");
+
+               PrintWriter writer = console.writer();
+               for (int i = 0; i < callbacks.length; i++) {
+                       if (callbacks[i] instanceof TextOutputCallback) {
+                               TextOutputCallback callback = (TextOutputCallback) callbacks[i];
+                               writer.write(callback.getMessage());
+                       } else if (callbacks[i] instanceof NameCallback) {
+                               NameCallback callback = (NameCallback) callbacks[i];
+                               writer.write(callback.getPrompt());
+                               if (callback.getDefaultName() != null)
+                                       writer.write(" (" + callback.getDefaultName() + ")");
+                               writer.write(" : ");
+                               String answer = console.readLine();
+                               if (callback.getDefaultName() != null
+                                               && answer.trim().equals(""))
+                                       callback.setName(callback.getDefaultName());
+                               else
+                                       callback.setName(answer);
+                       } else if (callbacks[i] instanceof PasswordCallback) {
+                               PasswordCallback callback = (PasswordCallback) callbacks[i];
+                               writer.write(callback.getPrompt());
+                               char[] answer = console.readPassword();
+                               callback.setPassword(answer);
+                               Arrays.fill(answer, ' ');
+                       } else if (callbacks[i] instanceof LocaleCallback) {
+                               LocaleCallback callback = (LocaleCallback) callbacks[i];
+                               writer.write(callback.getPrompt());
+                               writer.write("\n");
+                               for (int j = 0; j < callback.getAvailableLocales().size(); j++) {
+                                       Locale locale = callback.getAvailableLocales().get(j);
+                                       writer.print(j + " : " + locale.getDisplayName() + "\n");
+                               }
+                               writer.write("(" + callback.getDefaultIndex() + ") : ");
+                               String answer = console.readLine();
+                               if (answer.trim().equals(""))
+                                       callback.setSelectedIndex(callback.getDefaultIndex());
+                               else
+                                       callback.setSelectedIndex(new Integer(answer.trim()));
+                       }
+               }
+       }
+
+}
index d89643919efea72cd412d5edb6164033bdfdd1fe..e01e714fd5db3eee7825d538833231920388b6ce 100644 (file)
@@ -32,7 +32,7 @@ import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.core.Authentication;
 
 /** Authenticates an end user */
-public class EndUserLoginModule extends AbstractSpringSecurityLoginModule {
+public class EndUserLoginModule extends AbstractSpringLoginModule {
        final static String NODE_REPO_URI = "argeo.node.repo.uri";
 
        private Long waitBetweenFailedLoginAttempts = 5 * 1000l;
@@ -92,9 +92,15 @@ public class EndUserLoginModule extends AbstractSpringSecurityLoginModule {
                        Thread.sleep(waitBetweenFailedLoginAttempts);
                        throw e;
                }
+
                if (selectedLocale != null)
                        LocaleUtils.threadLocale.set(selectedLocale);
 
                return auth;
        }
+
+       @Override
+       public boolean commit() throws LoginException {
+               return super.commit();
+       }
 }
index b1b1d3434bda3523f65a4bb5302a46830ec82ec7..54e80e84771e17a368d7cf739b66230c7b2c5267 100644 (file)
@@ -27,7 +27,7 @@ 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 {
+public class SystemLoginModule extends AbstractSpringLoginModule {
        @Override
        protected Authentication processLogin(CallbackHandler callbackHandler)
                        throws LoginException, UnsupportedCallbackException, IOException,
diff --git a/org.argeo.security.core/src/org/argeo/security/login/UserAdminLoginModule.java b/org.argeo.security.core/src/org/argeo/security/login/UserAdminLoginModule.java
deleted file mode 100644 (file)
index 530c060..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * 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
index a80f2668228acf6bc3a34ab525e282f2e8e0c309..6d8adeb086837653a35c0d485bd2f544fcc4b6e0 100644 (file)
@@ -24,7 +24,6 @@ import java.util.Set;
 import javax.jcr.Credentials;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
-import javax.jcr.SimpleCredentials;
 import javax.security.auth.callback.CallbackHandler;
 import javax.security.auth.login.LoginException;
 
@@ -41,49 +40,16 @@ import org.springframework.security.core.context.SecurityContextHolder;
 public class ArgeoLoginModule extends AbstractLoginModule {
        private String adminRole = "ROLE_ADMIN";
 
-       @SuppressWarnings("unused")
-       @Override
-       public boolean login() throws LoginException {
-               boolean loginOk = super.login();
-               if (!loginOk) {
-                       org.springframework.security.core.Authentication authen = (org.springframework.security.core.Authentication) SecurityContextHolder
-                                       .getContext().getAuthentication();
-               }
-               return loginOk;
-       }
-
-       @SuppressWarnings("unused")
-       @Override
-       public boolean commit() throws LoginException {
-               boolean commitOk = super.commit();
-               if (!commitOk) {
-                       org.springframework.security.core.Authentication authen = (org.springframework.security.core.Authentication) SecurityContextHolder
-                                       .getContext().getAuthentication();
-               }
-               return commitOk;
-       }
-
        /**
         * Returns the Spring {@link org.springframework.security.Authentication}
         * (which can be null)
         */
        @Override
        protected Principal getPrincipal(Credentials credentials) {
-               org.springframework.security.core.Authentication authen = SecurityContextHolder
-                               .getContext().getAuthentication();
-               return authen;
+               return SecurityContextHolder.getContext().getAuthentication();
        }
 
        protected Set<Principal> getPrincipals() {
-               // clear already registered Jackrabbit principals
-               // clearPrincipals(AdminPrincipal.class);
-               // clearPrincipals(AnonymousPrincipal.class);
-               // clearPrincipals(GrantedAuthorityPrincipal.class);
-
-               return syncPrincipals();
-       }
-
-       protected Set<Principal> syncPrincipals() {
                // use linked HashSet instead of HashSet in order to maintain the order
                // of principals (as in the Subject).
                org.springframework.security.core.Authentication authen = (org.springframework.security.core.Authentication) principal;
@@ -106,13 +72,10 @@ public class ArgeoLoginModule extends AbstractLoginModule {
                }
 
                // remove previous credentials
-               Set<SimpleCredentials> thisCredentials = subject
-                               .getPublicCredentials(SimpleCredentials.class);
-               if (thisCredentials != null)
-                       thisCredentials.clear();
-               // override credentials since we did not used the one passed to us
-               // credentials = new SimpleCredentials(authen.getName(), authen
-               // .getCredentials().toString().toCharArray());
+               // Set<SimpleCredentials> thisCredentials = subject
+               // .getPublicCredentials(SimpleCredentials.class);
+               // if (thisCredentials != null)
+               // thisCredentials.clear();
 
                return principals;
        }
@@ -128,12 +91,6 @@ public class ArgeoLoginModule extends AbstractLoginModule {
                clearPrincipals(ArgeoSystemPrincipal.class);
                clearPrincipals(AnonymousPrincipal.class);
                clearPrincipals(GrantedAuthorityPrincipal.class);
-
-               // we resync with Spring Security since the subject may have been reused
-               // in beetween
-               // TODO: check if this is clean
-               // subject.getPrincipals().addAll(syncPrincipals());
-
                return true;
        }