1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.acegisecurity.adapters.jboss;
17
18 import org.acegisecurity.AccountExpiredException;
19 import org.acegisecurity.Authentication;
20 import org.acegisecurity.AuthenticationException;
21 import org.acegisecurity.AuthenticationManager;
22 import org.acegisecurity.CredentialsExpiredException;
23
24 import org.acegisecurity.adapters.PrincipalAcegiUserToken;
25
26 import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
27
28 import org.jboss.security.SimpleGroup;
29 import org.jboss.security.SimplePrincipal;
30 import org.jboss.security.auth.spi.AbstractServerLoginModule;
31
32 import org.springframework.beans.factory.access.*;
33 import org.springframework.beans.factory.access.SingletonBeanFactoryLocator;
34
35 import org.springframework.context.support.ClassPathXmlApplicationContext;
36
37 import java.security.Principal;
38 import java.security.acl.Group;
39
40 import java.util.Map;
41
42 import javax.security.auth.Subject;
43 import javax.security.auth.callback.Callback;
44 import javax.security.auth.callback.CallbackHandler;
45 import javax.security.auth.callback.NameCallback;
46 import javax.security.auth.callback.PasswordCallback;
47 import javax.security.auth.callback.UnsupportedCallbackException;
48 import javax.security.auth.login.FailedLoginException;
49 import javax.security.auth.login.LoginException;
50
51
52 /***
53 * Adapter to enable JBoss to authenticate via the Acegi Security System for
54 * Spring.
55 *
56 * <p>
57 * Returns a {@link PrincipalAcegiUserToken} to JBoss' authentication system,
58 * which is subsequently available from
59 * <code>java:comp/env/security/subject</code>.
60 * </p>
61 *
62 * @author Ben Alex
63 * @author Sergio Bern�
64 * @version $Id: JbossAcegiLoginModule.java,v 1.11 2005/11/25 00:26:30 benalex Exp $
65 */
66 public class JbossAcegiLoginModule extends AbstractServerLoginModule {
67
68
69 private AuthenticationManager authenticationManager;
70 private Principal identity;
71 private String key;
72 private char[] credential;
73
74
75
76 public void initialize(Subject subject, CallbackHandler callbackHandler,
77 Map sharedState, Map options) {
78 super.initialize(subject, callbackHandler, sharedState, options);
79
80 if (super.log.isInfoEnabled()) {
81 super.log.info("initializing jboss login module");
82 }
83
84 this.key = (String) options.get("key");
85
86 if ((key == null) || "".equals(key)) {
87 throw new IllegalArgumentException("key must be defined");
88 }
89
90 String singletonId = (String) options.get("singletonId");
91
92 String appContextLocation = (String) options.get("appContextLocation");
93
94 if ((((singletonId == null) || "".equals(singletonId))
95 && (appContextLocation == null)) || "".equals(appContextLocation)) {
96 throw new IllegalArgumentException(
97 "appContextLocation must be defined");
98 }
99
100 String beanName = (String) options.get("authenticationManager");
101
102
103 if ((singletonId == null) || "".equals(singletonId)) {
104 if (Thread.currentThread().getContextClassLoader().getResource(appContextLocation) == null) {
105 if (super.log.isInfoEnabled()) {
106 super.log.info("cannot locate " + appContextLocation);
107 }
108
109 throw new IllegalArgumentException("Cannot locate "
110 + appContextLocation);
111 }
112 }
113
114 ClassPathXmlApplicationContext ctx = null;
115
116 if ((singletonId == null) || "".equals(singletonId)) {
117 try {
118 ctx = new ClassPathXmlApplicationContext(appContextLocation);
119 } catch (Exception e) {
120 if (super.log.isInfoEnabled()) {
121 super.log.info("error loading spring context "
122 + appContextLocation + " " + e);
123 }
124
125 throw new IllegalArgumentException(
126 "error loading spring context " + appContextLocation + " "
127 + e);
128 }
129 } else {
130 if (super.log.isInfoEnabled()) {
131 super.log.debug("retrieving singleton instance " + singletonId);
132 }
133
134 BeanFactoryLocator bfl = SingletonBeanFactoryLocator.getInstance();
135 BeanFactoryReference bf = bfl.useBeanFactory(singletonId);
136 ctx = (ClassPathXmlApplicationContext) bf.getFactory();
137
138 if (ctx == null) {
139 if (super.log.isInfoEnabled()) {
140 super.log.info("singleton " + beanName + " does not exists");
141 }
142
143 throw new IllegalArgumentException("singleton " + singletonId
144 + " does not exists");
145 }
146 }
147
148 if ((beanName == null) || "".equals(beanName)) {
149 Map beans = null;
150
151 try {
152 beans = ctx.getBeansOfType(AuthenticationManager.class, true,
153 true);
154 } catch (Exception e) {
155 if (super.log.isInfoEnabled()) {
156 super.log.info("exception in getBeansOfType " + e);
157 }
158
159 throw new IllegalStateException(
160 "spring error in get beans by class");
161 }
162
163 if (beans.size() == 0) {
164 throw new IllegalArgumentException(
165 "Bean context must contain at least one bean of type AuthenticationManager");
166 }
167
168 beanName = (String) beans.keySet().iterator().next();
169 }
170
171 authenticationManager = (AuthenticationManager) ctx.getBean(beanName);
172
173 if (super.log.isInfoEnabled()) {
174 super.log.info("Successfully started JbossSpringLoginModule");
175 }
176 }
177
178 public boolean login() throws LoginException {
179 super.loginOk = false;
180
181 String[] info = getUsernameAndPassword();
182 String username = info[0];
183 String password = info[1];
184
185 if ((username == null) && (password == null)) {
186 identity = null;
187 super.log.trace("Authenticating as unauthenticatedIdentity="
188 + identity);
189 }
190
191 if (username == null) {
192 username = "";
193 }
194
195 if (password == null) {
196 password = "";
197 }
198
199 if (super.log.isDebugEnabled()) {
200 super.log.debug("checking identity");
201 }
202
203 if (identity == null) {
204 super.log.debug("creating usernamepassword token");
205
206 Authentication request = new UsernamePasswordAuthenticationToken(username,
207 password);
208 Authentication response = null;
209
210 try {
211 if (super.log.isDebugEnabled()) {
212 super.log.debug("attempting authentication");
213 }
214
215 response = authenticationManager.authenticate(request);
216
217 if (super.log.isDebugEnabled()) {
218 super.log.debug("authentication succeded");
219 }
220 } catch (CredentialsExpiredException cee) {
221 if (super.log.isDebugEnabled()) {
222 super.log.debug("Credential has expired");
223 }
224
225 throw new javax.security.auth.login.CredentialExpiredException(
226 "The credential used to identify the user has expired");
227 } catch (AccountExpiredException cee) {
228 if (super.log.isDebugEnabled()) {
229 super.log.debug(
230 "Account has expired, throwing jaas exception");
231 }
232
233 throw new javax.security.auth.login.AccountExpiredException(
234 "The account specified in login has expired");
235 } catch (AuthenticationException failed) {
236 if (super.log.isDebugEnabled()) {
237 super.log.debug("Bad password for username=" + username);
238 }
239
240 throw new FailedLoginException(
241 "Password Incorrect/Password Required");
242 }
243
244 super.log.debug("user is logged. redirecting to jaas classes");
245
246 identity = new PrincipalAcegiUserToken(this.key,
247 response.getName(), response.getCredentials().toString(),
248 response.getAuthorities(), response.getPrincipal());
249 }
250
251 if (getUseFirstPass() == true) {
252
253 sharedState.put("javax.security.auth.login.name", username);
254 sharedState.put("javax.security.auth.login.password", credential);
255 }
256
257 super.loginOk = true;
258 super.log.trace("User '" + identity + "' authenticated, loginOk="
259 + loginOk);
260
261 return true;
262 }
263
264 protected Principal getIdentity() {
265 return this.identity;
266 }
267
268 protected Group[] getRoleSets() throws LoginException {
269 SimpleGroup roles = new SimpleGroup("Roles");
270 Group[] roleSets = {roles};
271
272 if (this.identity instanceof Authentication) {
273 Authentication user = (Authentication) this.identity;
274
275 for (int i = 0; i < user.getAuthorities().length; i++) {
276 roles.addMember(new SimplePrincipal(
277 user.getAuthorities()[i].getAuthority()));
278 }
279 }
280
281 return roleSets;
282 }
283
284 protected String[] getUsernameAndPassword() throws LoginException {
285 String[] info = {null, null};
286
287
288 if (callbackHandler == null) {
289 throw new LoginException("Error: no CallbackHandler available "
290 + "to collect authentication information");
291 }
292
293 NameCallback nc = new NameCallback("User name: ", "guest");
294 PasswordCallback pc = new PasswordCallback("Password: ", false);
295 Callback[] callbacks = {nc, pc};
296 String username = null;
297 String password = null;
298
299 try {
300 callbackHandler.handle(callbacks);
301 username = nc.getName();
302
303 char[] tmpPassword = pc.getPassword();
304
305 if (tmpPassword != null) {
306 credential = new char[tmpPassword.length];
307 System.arraycopy(tmpPassword, 0, credential, 0,
308 tmpPassword.length);
309 pc.clearPassword();
310 password = new String(credential);
311 }
312 } catch (java.io.IOException ioe) {
313 throw new LoginException(ioe.toString());
314 } catch (UnsupportedCallbackException uce) {
315 throw new LoginException("CallbackHandler does not support: "
316 + uce.getCallback());
317 }
318
319 info[0] = username;
320 info[1] = password;
321
322 return info;
323 }
324 }