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