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