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.auth.callback.RealmCallback;
39 import info.magnolia.cms.util.BooleanUtil;
40
41 import java.io.IOException;
42 import java.util.LinkedHashSet;
43 import java.util.Map;
44 import java.util.Set;
45
46 import javax.security.auth.Subject;
47 import javax.security.auth.callback.Callback;
48 import javax.security.auth.callback.CallbackHandler;
49 import javax.security.auth.callback.NameCallback;
50 import javax.security.auth.callback.PasswordCallback;
51 import javax.security.auth.callback.UnsupportedCallbackException;
52 import javax.security.auth.login.LoginException;
53 import javax.security.auth.spi.LoginModule;
54
55 import org.apache.commons.lang.ArrayUtils;
56 import org.apache.commons.lang.StringUtils;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60
61
62
63
64
65
66 public abstract class AbstractLoginModule implements LoginModule {
67
68
69
70 public static final String OPTION_SKIP_ON_PREVIOUS_SUCCESS = "skip_on_previous_success";
71
72 public static final String OPTION_REALM = "realm";
73
74 public static final String OPTION_USE_REALM_CALLBACK= "use_realm_callback";
75
76 public static final String STATUS = "statusValue";
77
78 public static final int STATUS_SUCCEEDED = 1;
79
80
81
82
83 @Deprecated
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 public static final String TRY_FIRST_PASS = "try_first_pass";
98
99
100
101
102 public static final String USE_FIRST_PASS = "use_first_pass";
103
104
105
106
107 public static final String TRY_MAPPED_PASS = "try_mapped_pass";
108
109
110
111
112 public static final String USE_MAPPED_PASS = "use_mapped_pass";
113
114 public Subject subject;
115
116 public CallbackHandler callbackHandler;
117
118 public Map<String, Object> sharedState;
119
120 public Map<String, Object> options;
121
122 public String name;
123
124 public char[] pswd;
125
126
127
128
129 protected Realm realm = Realm.REALM_ALL;
130
131
132
133
134 protected boolean useRealmCallback;
135
136
137 public boolean success;
138
139 protected Logger log = LoggerFactory.getLogger(getClass());
140
141 private boolean skipOnPreviousSuccess;
142
143
144
145
146
147 public AbstractLoginModule() {
148
149 }
150
151 @Override
152 public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, final 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 this.sharedState.put("groupNames", new LinkedHashSet<String>());
160 }
161 if (this.sharedState.get("roleNames") == null) {
162 this.sharedState.put("roleNames", new LinkedHashSet<String>());
163 }
164 String realmName = (String) options.get(OPTION_REALM);
165 this.realm = StringUtils.isBlank(realmName) ? Realm.DEFAULT_REALM : Realm.Factory.newRealm(realmName);
166
167 this.useRealmCallback = BooleanUtil.toBoolean((String) options.get(OPTION_USE_REALM_CALLBACK), true);
168 this.skipOnPreviousSuccess = BooleanUtil.toBoolean((String) options.get(OPTION_SKIP_ON_PREVIOUS_SUCCESS), false);
169 }
170
171 @Override
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 String aRealm = ((RealmCallback) callbacks[2]).getRealm();
198 this.realm = StringUtils.isBlank(aRealm) ? this.realm : Realm.Factory.newRealm(aRealm);
199 }
200
201 this.validateUser();
202 } catch (IOException ioe) {
203 log.debug("Exception caught", ioe);
204 throw new LoginException(ioe.toString());
205 } catch (UnsupportedCallbackException ce) {
206 log.debug(ce.getMessage(), ce);
207 throw new LoginException(ce.getCallback().toString() + " not available");
208 }
209
210
211 this.success = true;
212 this.setSharedStatus(STATUS_SUCCEEDED);
213 return this.success;
214 }
215
216
217
218
219
220 @Override
221 public boolean commit() throws LoginException {
222
223
224
225
226 if (!this.success) {
227 return false;
228 }
229 this.setEntity();
230 this.setACL();
231 return true;
232 }
233
234 @Override
235 public boolean abort() throws LoginException {
236 return this.release();
237 }
238
239 @Override
240 public boolean logout() throws LoginException {
241 return this.release();
242 }
243
244
245
246
247 public int getSharedStatus() {
248 Integer status = (Integer) this.sharedState.get(STATUS);
249 if (null != status) {
250 return status.intValue();
251 }
252 return STATUS_UNAVAILABLE;
253 }
254
255
256
257
258 public void setSharedStatus(int status) {
259 this.sharedState.put(STATUS, new Integer(status));
260 }
261
262
263
264
265 protected boolean getSkip() {
266 return skipOnPreviousSuccess && this.getSharedStatus() == STATUS_SUCCEEDED;
267 }
268
269 public void setGroupNames(Set<String> names) {
270 this.getGroupNames().addAll(names);
271 }
272
273 public void addGroupName(String groupName) {
274 getGroupNames().add(groupName);
275 }
276
277 public Set<String> getGroupNames() {
278 return (Set<String>) this.sharedState.get("groupNames");
279 }
280
281 public void setRoleNames(Set<String> names) {
282 this.getRoleNames().addAll(names);
283 }
284
285 public void addRoleName(String roleName) {
286 getRoleNames().add(roleName);
287 }
288
289 public Set<String> getRoleNames() {
290 return (Set<String>) this.sharedState.get("roleNames");
291 }
292
293
294
295
296 public boolean release() {
297 return true;
298 }
299
300
301
302
303
304 public abstract void validateUser() throws LoginException;
305
306
307
308
309 public abstract void setEntity();
310
311
312
313
314 public abstract void setACL();
315
316 }