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