]> git.argeo.org Git - lgpl/argeo-commons.git/blob - security/plugins/org.argeo.security.equinox/src/main/java/org/argeo/security/equinox/SpringLoginModule.java
4d0cb6de65aaeb9124f766c4c213f97a15996c66
[lgpl/argeo-commons.git] / security / plugins / org.argeo.security.equinox / src / main / java / org / argeo / security / equinox / SpringLoginModule.java
1 package org.argeo.security.equinox;
2
3 import java.util.Map;
4 import java.util.UUID;
5
6 import javax.security.auth.Subject;
7 import javax.security.auth.callback.Callback;
8 import javax.security.auth.callback.CallbackHandler;
9 import javax.security.auth.callback.NameCallback;
10 import javax.security.auth.callback.PasswordCallback;
11 import javax.security.auth.login.LoginException;
12
13 import org.apache.commons.logging.Log;
14 import org.apache.commons.logging.LogFactory;
15 import org.argeo.security.NodeAuthenticationToken;
16 import org.springframework.security.Authentication;
17 import org.springframework.security.AuthenticationManager;
18 import org.springframework.security.BadCredentialsException;
19 import org.springframework.security.GrantedAuthority;
20 import org.springframework.security.GrantedAuthorityImpl;
21 import org.springframework.security.context.SecurityContextHolder;
22 import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken;
23 import org.springframework.security.providers.jaas.SecurityContextLoginModule;
24
25 /** Login module which caches one subject per thread. */
26 public class SpringLoginModule extends SecurityContextLoginModule {
27 final static String NODE_REPO_URI = "argeo.node.repo.uri";
28
29 private final static Log log = LogFactory.getLog(SpringLoginModule.class);
30
31 private AuthenticationManager authenticationManager;
32
33 private CallbackHandler callbackHandler;
34
35 private Subject subject;
36
37 private Long waitBetweenFailedLoginAttempts = 5 * 1000l;
38
39 private Boolean remote = false;
40 private Boolean anonymous = false;
41
42 private String key = null;
43 private String anonymousRole = "ROLE_ANONYMOUS";
44
45 public SpringLoginModule() {
46
47 }
48
49 @SuppressWarnings("rawtypes")
50 public void initialize(Subject subject, CallbackHandler callbackHandler,
51 Map sharedState, Map options) {
52 super.initialize(subject, callbackHandler, sharedState, options);
53 this.callbackHandler = callbackHandler;
54 this.subject = subject;
55 }
56
57 public boolean login() throws LoginException {
58 try {
59 // thread already logged in
60 if (SecurityContextHolder.getContext().getAuthentication() != null)
61 return super.login();
62
63 if (remote && anonymous)
64 throw new LoginException(
65 "Cannot have a Spring login module which is remote and anonymous");
66
67 // reset all principals and credentials
68 if (log.isTraceEnabled())
69 log.trace("Resetting all principals and credentials of "
70 + subject);
71 if (subject.getPrincipals() != null)
72 subject.getPrincipals().clear();
73 if (subject.getPrivateCredentials() != null)
74 subject.getPrivateCredentials().clear();
75 if (subject.getPublicCredentials() != null)
76 subject.getPublicCredentials().clear();
77
78 // deals first with public access since it's simple
79 if (anonymous) {
80 // TODO integrate with JCR?
81 Object principal = UUID.randomUUID().toString();
82 GrantedAuthority[] authorities = { new GrantedAuthorityImpl(
83 anonymousRole) };
84 AnonymousAuthenticationToken anonymousToken = new AnonymousAuthenticationToken(
85 key, principal, authorities);
86 Authentication auth = authenticationManager
87 .authenticate(anonymousToken);
88 registerAuthentication(auth);
89 return super.login();
90 }
91
92 if (callbackHandler == null)
93 throw new LoginException("No call back handler available");
94
95 // ask for username and password
96 NameCallback nameCallback = new NameCallback("User");
97 PasswordCallback passwordCallback = new PasswordCallback(
98 "Password", false);
99 final String defaultNodeUrl = "http://localhost:7070/org.argeo.jcr.webapp/remoting/node";
100 final String defaultSecurityWorkspace = "security";
101 NameCallback urlCallback = new NameCallback("Site URL",
102 defaultNodeUrl);
103 NameCallback securityWorkspaceCallback = new NameCallback(
104 "Security Workspace", defaultSecurityWorkspace);
105
106 // handle callbacks
107 if (remote)
108 callbackHandler.handle(new Callback[] { nameCallback,
109 passwordCallback, urlCallback,
110 securityWorkspaceCallback });
111 else
112 callbackHandler.handle(new Callback[] { nameCallback,
113 passwordCallback });
114
115 // create credentials
116 String username = nameCallback.getName();
117 if (username == null || username.trim().equals(""))
118 return false;
119
120 String password = "";
121 if (passwordCallback.getPassword() != null)
122 password = String.valueOf(passwordCallback.getPassword());
123
124 NodeAuthenticationToken credentials;
125 if (remote) {
126 String url = urlCallback.getName();
127 String workspace = securityWorkspaceCallback.getName();
128 credentials = new NodeAuthenticationToken(username, password,
129 url, workspace);
130 } else {
131 credentials = new NodeAuthenticationToken(username, password);
132 }
133
134 Authentication authentication;
135 try {
136 authentication = authenticationManager
137 .authenticate(credentials);
138 } catch (BadCredentialsException e) {
139 // wait between failed login attempts
140 Thread.sleep(waitBetweenFailedLoginAttempts);
141 throw e;
142 }
143 registerAuthentication(authentication);
144 boolean res = super.login();
145 return res;
146 } catch (LoginException e) {
147 throw e;
148 } catch (ThreadDeath e) {
149 LoginException le = new LoginException(
150 "Spring Security login thread died");
151 le.initCause(e);
152 throw le;
153 } catch (Exception e) {
154 LoginException le = new LoginException(
155 "Spring Security login failed");
156 le.initCause(e);
157 throw le;
158 }
159 }
160
161 @Override
162 public boolean logout() throws LoginException {
163 subject.getPrincipals().clear();
164 return super.logout();
165 }
166
167 /**
168 * Register an {@link Authentication} in the security context.
169 *
170 * @param authentication
171 * has to implement {@link Authentication}.
172 */
173 protected void registerAuthentication(Object authentication) {
174 SecurityContextHolder.getContext().setAuthentication(
175 (Authentication) authentication);
176 }
177
178 public void setAuthenticationManager(
179 AuthenticationManager authenticationManager) {
180 this.authenticationManager = authenticationManager;
181 }
182
183 /** Authenticates on a remote node */
184 public void setRemote(Boolean remote) {
185 this.remote = remote;
186 }
187
188 /**
189 * Request anonymous authentication (incompatible with remote)
190 */
191 public void setAnonymous(Boolean anonymous) {
192 this.anonymous = anonymous;
193 }
194
195 /** Role identifying an anonymous user */
196 public void setAnonymousRole(String anonymousRole) {
197 this.anonymousRole = anonymousRole;
198 }
199
200 /** System key */
201 public void setKey(String key) {
202 this.key = key;
203 }
204
205 }