1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.jaas.sp;
35
36
37 import info.magnolia.cms.security.Realm;
38 import info.magnolia.cms.security.User;
39 import info.magnolia.cms.security.auth.callback.RealmCallback;
40 import info.magnolia.cms.security.auth.callback.UserCallback;
41 import info.magnolia.cms.util.BooleanUtil;
42
43 import java.io.IOException;
44 import java.util.LinkedHashSet;
45 import java.util.Map;
46 import java.util.Set;
47
48 import javax.security.auth.Subject;
49 import javax.security.auth.callback.Callback;
50 import javax.security.auth.callback.CallbackHandler;
51 import javax.security.auth.callback.NameCallback;
52 import javax.security.auth.callback.PasswordCallback;
53 import javax.security.auth.callback.UnsupportedCallbackException;
54 import javax.security.auth.login.LoginException;
55 import javax.security.auth.spi.LoginModule;
56
57 import org.apache.commons.lang.ArrayUtils;
58 import org.apache.commons.lang.StringUtils;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62
63
64
65
66
67
68 public abstract class AbstractLoginModule implements LoginModule {
69
70
71
72 public static final String OPTION_SKIP_ON_PREVIOUS_SUCCESS = "skip_on_previous_success";
73
74 public static final String OPTION_REALM = "realm";
75
76 public static final String OPTION_USE_REALM_CALLBACK= "use_realm_callback";
77
78 public static final String STATUS = "statusValue";
79
80 public static final int STATUS_SUCCEEDED = 1;
81
82
83
84
85 public static final int STATUS_SUCCEDED = STATUS_SUCCEEDED;
86
87 public static final int STATUS_FAILED = 2;
88
89 public static final int STATUS_SKIPPED = 3;
90
91 public static final int STATUS_UNAVAILABLE = 4;
92
93
94
95
96
97
98 public static final String TRY_FIRST_PASS = "try_first_pass";
99
100
101
102
103 public static final String USE_FIRST_PASS = "use_first_pass";
104
105
106
107
108 public static final String TRY_MAPPED_PASS = "try_mapped_pass";
109
110
111
112
113 public static final String USE_MAPPED_PASS = "use_mapped_pass";
114
115 public Subject subject;
116
117 public CallbackHandler callbackHandler;
118
119 public Map sharedState;
120
121 public Map options;
122
123 public String name;
124
125 public char[] pswd;
126
127
128
129
130 protected String realm = Realm.REALM_ALL;
131
132
133
134
135 protected boolean useRealmCallback;
136
137
138 public boolean success;
139
140 protected Logger log = LoggerFactory.getLogger(getClass());
141
142 private boolean skipOnPreviousSuccess;
143
144
145
146
147
148 public AbstractLoginModule() {
149
150 }
151
152 public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
153 this.subject = subject;
154 this.callbackHandler = callbackHandler;
155 this.sharedState = sharedState;
156 this.options = options;
157
158 if (this.sharedState.get("groupNames") == null)
159 {
160 this.sharedState.put("groupNames", new LinkedHashSet());
161 }
162 if (this.sharedState.get("roleNames") == null)
163 {
164 this.sharedState.put("roleNames", new LinkedHashSet());
165 }
166 this.realm = StringUtils.defaultIfEmpty((String) options.get(OPTION_REALM), Realm.DEFAULT_REALM);
167
168 this.useRealmCallback = BooleanUtil.toBoolean((String) options.get(OPTION_USE_REALM_CALLBACK), true);
169 this.skipOnPreviousSuccess = BooleanUtil.toBoolean((String) options.get(OPTION_SKIP_ON_PREVIOUS_SUCCESS), false);
170 }
171
172 public boolean login() throws LoginException {
173 if (this.getSkip()) {
174 return true;
175 }
176
177 if (this.callbackHandler == null) {
178 throw new LoginException("Error: no CallbackHandler available");
179 }
180
181 Callback[] callbacks = new Callback[2];
182 callbacks[0] = new NameCallback("name");
183 callbacks[1] = new PasswordCallback("pswd", false);
184
185
186
187 if(this.useRealmCallback){
188 callbacks = (Callback[]) ArrayUtils.add(callbacks, new RealmCallback());
189 }
190
191 this.success = false;
192 try {
193 this.callbackHandler.handle(callbacks);
194 this.name = ((NameCallback) callbacks[0]).getName();
195 this.pswd = ((PasswordCallback) callbacks[1]).getPassword();
196 if(this.useRealmCallback){
197 this.realm = StringUtils.defaultIfEmpty(((RealmCallback)callbacks[2]).getRealm(), this.realm);
198 }
199
200 this.validateUser();
201
202
203 if(this instanceof UserAwareLoginModule){
204 User user = ((UserAwareLoginModule)this).getUser();
205 if(user != null){
206 this.callbackHandler.handle(new Callback[]{new UserCallback(user)});
207 }
208 }
209 } catch (IOException ioe) {
210 if (log.isDebugEnabled()) {
211 log.debug("Exception caught", ioe);
212 }
213 throw new LoginException(ioe.toString());
214 } catch (UnsupportedCallbackException ce) {
215 if (log.isDebugEnabled()) {
216 log.debug(ce.getMessage(), ce);
217 }
218 throw new LoginException(ce.getCallback().toString() + " not available");
219 }
220
221 this.success = true;
222
223 this.setSharedStatus(STATUS_SUCCEEDED);
224 return this.success;
225 }
226
227
228
229
230
231 public boolean commit() throws LoginException {
232
233
234
235
236 if (!this.success) {
237 return false;
238 }
239 this.setEntity();
240 this.setACL();
241 return true;
242 }
243
244 public boolean abort() throws LoginException {
245 return this.release();
246 }
247
248 public boolean logout() throws LoginException {
249 return this.release();
250 }
251
252
253
254
255 public int getSharedStatus() {
256 Integer status = (Integer) this.sharedState.get(STATUS);
257 if (null != status) {
258 return status.intValue();
259 }
260 return STATUS_UNAVAILABLE;
261 }
262
263
264
265
266 public void setSharedStatus(int status) {
267 this.sharedState.put(STATUS, new Integer(status));
268 }
269
270
271
272
273 protected boolean getSkip() {
274 return skipOnPreviousSuccess && this.getSharedStatus() == STATUS_SUCCEEDED;
275 }
276
277 public void setGroupNames(Set names) {
278 this.getGroupNames().addAll(names);
279 }
280
281 public void addGroupName(String groupName) {
282 getGroupNames().add(groupName);
283 }
284
285 public Set getGroupNames() {
286 return (Set) this.sharedState.get("groupNames");
287 }
288
289 public void setRoleNames(Set names) {
290 this.getRoleNames().addAll(names);
291 }
292
293 public void addRoleName(String roleName) {
294 getRoleNames().add(roleName);
295 }
296
297 public Set getRoleNames() {
298 return (Set) this.sharedState.get("roleNames");
299 }
300
301
302
303
304 public abstract boolean release();
305
306
307
308
309
310 public abstract void validateUser() throws LoginException;
311
312
313
314
315 public abstract void setEntity();
316
317
318
319
320 public abstract void setACL();
321
322 }