1 package org
.argeo
.security
.equinox
;
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
;
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
;
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";
29 private final static Log log
= LogFactory
.getLog(SpringLoginModule
.class);
31 private AuthenticationManager authenticationManager
;
33 private CallbackHandler callbackHandler
;
35 private Subject subject
;
37 private Long waitBetweenFailedLoginAttempts
= 5 * 1000l;
39 private Boolean remote
= false;
40 private Boolean anonymous
= false;
42 private String key
= null;
43 private String anonymousRole
= "ROLE_ANONYMOUS";
45 public SpringLoginModule() {
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
;
57 public boolean login() throws LoginException
{
59 // thread already logged in
60 if (SecurityContextHolder
.getContext().getAuthentication() != null)
63 if (remote
&& anonymous
)
64 throw new LoginException(
65 "Cannot have a Spring login module which is remote and anonymous");
67 // reset all principals and credentials
68 if (log
.isTraceEnabled())
69 log
.trace("Resetting all principals and credentials of "
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();
78 // deals first with public access since it's simple
80 // TODO integrate with JCR?
81 Object principal
= UUID
.randomUUID().toString();
82 GrantedAuthority
[] authorities
= { new GrantedAuthorityImpl(
84 AnonymousAuthenticationToken anonymousToken
= new AnonymousAuthenticationToken(
85 key
, principal
, authorities
);
86 Authentication auth
= authenticationManager
87 .authenticate(anonymousToken
);
88 registerAuthentication(auth
);
92 if (callbackHandler
== null)
93 throw new LoginException("No call back handler available");
95 // ask for username and password
96 NameCallback nameCallback
= new NameCallback("User");
97 PasswordCallback passwordCallback
= new PasswordCallback(
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",
103 NameCallback securityWorkspaceCallback
= new NameCallback(
104 "Security Workspace", defaultSecurityWorkspace
);
108 callbackHandler
.handle(new Callback
[] { nameCallback
,
109 passwordCallback
, urlCallback
,
110 securityWorkspaceCallback
});
112 callbackHandler
.handle(new Callback
[] { nameCallback
,
115 // create credentials
116 String username
= nameCallback
.getName();
117 if (username
== null || username
.trim().equals(""))
120 String password
= "";
121 if (passwordCallback
.getPassword() != null)
122 password
= String
.valueOf(passwordCallback
.getPassword());
124 NodeAuthenticationToken credentials
;
126 String url
= urlCallback
.getName();
127 String workspace
= securityWorkspaceCallback
.getName();
128 credentials
= new NodeAuthenticationToken(username
, password
,
131 credentials
= new NodeAuthenticationToken(username
, password
);
134 Authentication authentication
;
136 authentication
= authenticationManager
137 .authenticate(credentials
);
138 } catch (BadCredentialsException e
) {
139 // wait between failed login attempts
140 Thread
.sleep(waitBetweenFailedLoginAttempts
);
143 registerAuthentication(authentication
);
144 boolean res
= super.login();
146 } catch (LoginException e
) {
148 } catch (ThreadDeath e
) {
149 LoginException le
= new LoginException(
150 "Spring Security login thread died");
153 } catch (Exception e
) {
154 LoginException le
= new LoginException(
155 "Spring Security login failed");
162 public boolean logout() throws LoginException
{
163 subject
.getPrincipals().clear();
164 return super.logout();
168 * Register an {@link Authentication} in the security context.
170 * @param authentication
171 * has to implement {@link Authentication}.
173 protected void registerAuthentication(Object authentication
) {
174 SecurityContextHolder
.getContext().setAuthentication(
175 (Authentication
) authentication
);
178 public void setAuthenticationManager(
179 AuthenticationManager authenticationManager
) {
180 this.authenticationManager
= authenticationManager
;
183 /** Authenticates on a remote node */
184 public void setRemote(Boolean remote
) {
185 this.remote
= remote
;
189 * Request anonymous authentication (incompatible with remote)
191 public void setAnonymous(Boolean anonymous
) {
192 this.anonymous
= anonymous
;
195 /** Role identifying an anonymous user */
196 public void setAnonymousRole(String anonymousRole
) {
197 this.anonymousRole
= anonymousRole
;
201 public void setKey(String key
) {