1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package waffle.jaas;
25
26 import java.io.IOException;
27 import java.security.Principal;
28 import java.util.ArrayList;
29 import java.util.LinkedHashSet;
30 import java.util.List;
31 import java.util.Locale;
32 import java.util.Map;
33 import java.util.Set;
34
35 import javax.security.auth.Subject;
36 import javax.security.auth.callback.Callback;
37 import javax.security.auth.callback.CallbackHandler;
38 import javax.security.auth.callback.NameCallback;
39 import javax.security.auth.callback.PasswordCallback;
40 import javax.security.auth.callback.UnsupportedCallbackException;
41 import javax.security.auth.login.LoginException;
42 import javax.security.auth.spi.LoginModule;
43
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 import waffle.windows.auth.IWindowsAccount;
48 import waffle.windows.auth.IWindowsAuthProvider;
49 import waffle.windows.auth.IWindowsIdentity;
50 import waffle.windows.auth.PrincipalFormat;
51 import waffle.windows.auth.impl.WindowsAuthProviderImpl;
52
53
54
55
56
57
58 public class WindowsLoginModule implements LoginModule {
59
60
61 private static final Logger LOGGER = LoggerFactory.getLogger(WindowsLoginModule.class);
62
63
64 private String username;
65
66
67 private boolean debug;
68
69
70 private Subject subject;
71
72
73 private CallbackHandler callbackHandler;
74
75
76 private IWindowsAuthProvider auth = new WindowsAuthProviderImpl();
77
78
79 private Set<Principal> principals;
80
81
82 private PrincipalFormat principalFormat = PrincipalFormat.FQN;
83
84
85 private PrincipalFormat roleFormat = PrincipalFormat.FQN;
86
87
88 private boolean allowGuestLogin = true;
89
90 @Override
91 public void initialize(final Subject initSubject, final CallbackHandler initCallbackHandler,
92 final Map<String, ?> initSharedState, final Map<String, ?> initOptions) {
93
94 this.subject = initSubject;
95 this.callbackHandler = initCallbackHandler;
96
97 for (final Map.Entry<String, ?> option : initOptions.entrySet()) {
98 if ("debug".equalsIgnoreCase(option.getKey())) {
99 this.debug = Boolean.parseBoolean((String) option.getValue());
100 } else if ("principalFormat".equalsIgnoreCase(option.getKey())) {
101 this.principalFormat = PrincipalFormat
102 .valueOf(((String) option.getValue()).toUpperCase(Locale.ENGLISH));
103 } else if ("roleFormat".equalsIgnoreCase(option.getKey())) {
104 this.roleFormat = PrincipalFormat.valueOf(((String) option.getValue()).toUpperCase(Locale.ENGLISH));
105 }
106 }
107 }
108
109
110
111
112
113
114
115
116
117 @Override
118 public boolean login() throws LoginException {
119 if (this.callbackHandler == null) {
120 throw new LoginException("Missing callback to gather information from the user.");
121 }
122
123 final NameCallback usernameCallback = new NameCallback("user name: ");
124 final PasswordCallback passwordCallback = new PasswordCallback("password: ", false);
125
126 final Callback[] callbacks = new Callback[2];
127 callbacks[0] = usernameCallback;
128 callbacks[1] = passwordCallback;
129
130 final String userName;
131 final String password;
132
133 try {
134 this.callbackHandler.handle(callbacks);
135 userName = usernameCallback.getName();
136 password = passwordCallback.getPassword() == null ? "" : new String(passwordCallback.getPassword());
137 passwordCallback.clearPassword();
138 } catch (final IOException e) {
139 WindowsLoginModule.LOGGER.trace("", e);
140 throw new LoginException(e.toString());
141 } catch (final UnsupportedCallbackException e) {
142 WindowsLoginModule.LOGGER.trace("", e);
143 throw new LoginException("Callback {} not available to gather authentication information from the user."
144 .replace("{}", e.getCallback().getClass().getName()));
145 }
146
147 IWindowsIdentity windowsIdentity;
148 try {
149 windowsIdentity = this.auth.logonUser(userName, password);
150 } catch (final Exception e) {
151 WindowsLoginModule.LOGGER.trace("", e);
152 throw new LoginException(e.getMessage());
153 }
154
155 try {
156
157 if (!this.allowGuestLogin && windowsIdentity.isGuest()) {
158 WindowsLoginModule.LOGGER.debug("guest login disabled: {}", windowsIdentity.getFqn());
159 throw new LoginException("Guest login disabled");
160 }
161
162 this.principals = new LinkedHashSet<>();
163
164 this.principals.addAll(WindowsLoginModule.getUserPrincipals(windowsIdentity, this.principalFormat));
165 if (this.roleFormat != PrincipalFormat.NONE) {
166
167 final GroupPrincipal groupList = new GroupPrincipal("Roles");
168 for (final IWindowsAccount group : windowsIdentity.getGroups()) {
169 this.principals.addAll(WindowsLoginModule.getRolePrincipals(group, this.roleFormat));
170 for (final Principal role : WindowsLoginModule.getRolePrincipals(group, this.roleFormat)) {
171 WindowsLoginModule.LOGGER.debug(" group: {}", role.getName());
172 groupList.addMember(new RolePrincipal(role.getName()));
173 }
174 }
175
176 this.principals.add(groupList);
177 }
178
179 this.username = windowsIdentity.getFqn();
180 WindowsLoginModule.LOGGER.debug("successfully logged in {} ({})", this.username,
181 windowsIdentity.getSidString());
182 } finally {
183 windowsIdentity.dispose();
184 }
185
186 return true;
187 }
188
189
190
191
192
193
194
195
196
197 @Override
198 public boolean abort() throws LoginException {
199 return this.logout();
200 }
201
202
203
204
205
206
207
208
209
210 @Override
211 public boolean commit() throws LoginException {
212 if (this.principals == null) {
213 return false;
214 }
215
216 if (this.subject.isReadOnly()) {
217 throw new LoginException("Subject cannot be read-only.");
218 }
219
220 final Set<Principal> principalsSet = this.subject.getPrincipals();
221 principalsSet.addAll(this.principals);
222
223 WindowsLoginModule.LOGGER.debug("committing {} principals",
224 Integer.valueOf(this.subject.getPrincipals().size()));
225 if (this.debug) {
226 for (final Principal principal : principalsSet) {
227 WindowsLoginModule.LOGGER.debug(" principal: {}", principal.getName());
228 }
229 }
230
231 return true;
232 }
233
234
235
236
237
238
239
240
241
242 @Override
243 public boolean logout() throws LoginException {
244 if (this.subject.isReadOnly()) {
245 throw new LoginException("Subject cannot be read-only.");
246 }
247
248 this.subject.getPrincipals().clear();
249
250 if (this.username != null) {
251 WindowsLoginModule.LOGGER.debug("logging out {}", this.username);
252 }
253
254 return true;
255 }
256
257
258
259
260
261
262 public boolean isDebug() {
263 return this.debug;
264 }
265
266
267
268
269
270
271 public IWindowsAuthProvider getAuth() {
272 return this.auth;
273 }
274
275
276
277
278
279
280
281 public void setAuth(final IWindowsAuthProvider provider) {
282 this.auth = provider;
283 }
284
285
286
287
288
289
290
291
292
293
294
295 private static List<Principal> getUserPrincipals(final IWindowsIdentity windowsIdentity,
296 final PrincipalFormat principalFormat) {
297
298 final List<Principal> principalsList = new ArrayList<>();
299 switch (principalFormat) {
300 case FQN:
301 principalsList.add(new UserPrincipal(windowsIdentity.getFqn()));
302 break;
303 case SID:
304 principalsList.add(new UserPrincipal(windowsIdentity.getSidString()));
305 break;
306 case BOTH:
307 principalsList.add(new UserPrincipal(windowsIdentity.getFqn()));
308 principalsList.add(new UserPrincipal(windowsIdentity.getSidString()));
309 break;
310 case NONE:
311 default:
312 break;
313 }
314 return principalsList;
315 }
316
317
318
319
320
321
322
323
324
325
326
327 private static List<Principal> getRolePrincipals(final IWindowsAccount group,
328 final PrincipalFormat principalFormat) {
329
330 final List<Principal> principalsList = new ArrayList<>();
331 switch (principalFormat) {
332 case FQN:
333 principalsList.add(new RolePrincipal(group.getFqn()));
334 break;
335 case SID:
336 principalsList.add(new RolePrincipal(group.getSidString()));
337 break;
338 case BOTH:
339 principalsList.add(new RolePrincipal(group.getFqn()));
340 principalsList.add(new RolePrincipal(group.getSidString()));
341 break;
342 case NONE:
343 break;
344 default:
345 break;
346 }
347 return principalsList;
348 }
349
350
351
352
353
354
355 public boolean isAllowGuestLogin() {
356 return this.allowGuestLogin;
357 }
358
359
360
361
362
363
364
365
366 public void setAllowGuestLogin(final boolean value) {
367 this.allowGuestLogin = value;
368 }
369 }