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 for (final IWindowsAccount group : windowsIdentity.getGroups()) {
168 this.principals.addAll(getRolePrincipals(group, this.roleFormat));
169 }
170 }
171
172 this.username = windowsIdentity.getFqn();
173 WindowsLoginModule.LOGGER.debug("successfully logged in {} ({})", this.username,
174 windowsIdentity.getSidString());
175 } finally {
176 windowsIdentity.dispose();
177 }
178
179 return true;
180 }
181
182
183
184
185
186
187
188
189
190 @Override
191 public boolean abort() throws LoginException {
192 return this.logout();
193 }
194
195
196
197
198
199
200
201
202
203 @Override
204 public boolean commit() throws LoginException {
205 if (this.principals == null) {
206 return false;
207 }
208
209 if (this.subject.isReadOnly()) {
210 throw new LoginException("Subject cannot be read-only.");
211 }
212
213 final Set<Principal> principalsSet = this.subject.getPrincipals();
214 principalsSet.addAll(this.principals);
215
216 WindowsLoginModule.LOGGER.debug("committing {} principals",
217 Integer.valueOf(this.subject.getPrincipals().size()));
218 if (this.debug) {
219 for (final Principal principal : principalsSet) {
220 WindowsLoginModule.LOGGER.debug(" principal: {}", principal.getName());
221 }
222 }
223
224 return true;
225 }
226
227
228
229
230
231
232
233
234
235 @Override
236 public boolean logout() throws LoginException {
237 if (this.subject.isReadOnly()) {
238 throw new LoginException("Subject cannot be read-only.");
239 }
240
241 this.subject.getPrincipals().clear();
242
243 if (this.username != null) {
244 WindowsLoginModule.LOGGER.debug("logging out {}", this.username);
245 }
246
247 return true;
248 }
249
250
251
252
253
254
255 public boolean isDebug() {
256 return this.debug;
257 }
258
259
260
261
262
263
264 public IWindowsAuthProvider getAuth() {
265 return this.auth;
266 }
267
268
269
270
271
272
273
274 public void setAuth(final IWindowsAuthProvider provider) {
275 this.auth = provider;
276 }
277
278
279
280
281
282
283
284
285
286
287
288 private static List<Principal> getUserPrincipals(final IWindowsIdentity windowsIdentity,
289 final PrincipalFormat principalFormat) {
290
291 final List<Principal> principalsList = new ArrayList<>();
292 switch (principalFormat) {
293 case FQN:
294 principalsList.add(new UserPrincipal(windowsIdentity.getFqn()));
295 break;
296 case SID:
297 principalsList.add(new UserPrincipal(windowsIdentity.getSidString()));
298 break;
299 case BOTH:
300 principalsList.add(new UserPrincipal(windowsIdentity.getFqn()));
301 principalsList.add(new UserPrincipal(windowsIdentity.getSidString()));
302 break;
303 case NONE:
304 default:
305 break;
306 }
307 return principalsList;
308 }
309
310
311
312
313
314
315
316
317
318
319
320 private static List<Principal> getRolePrincipals(final IWindowsAccount group,
321 final PrincipalFormat principalFormat) {
322
323 final List<Principal> principalsList = new ArrayList<>();
324 switch (principalFormat) {
325 case FQN:
326 principalsList.add(new RolePrincipal(group.getFqn()));
327 break;
328 case SID:
329 principalsList.add(new RolePrincipal(group.getSidString()));
330 break;
331 case BOTH:
332 principalsList.add(new RolePrincipal(group.getFqn()));
333 principalsList.add(new RolePrincipal(group.getSidString()));
334 break;
335 case NONE:
336 break;
337 default:
338 break;
339 }
340 return principalsList;
341 }
342
343
344
345
346
347
348 public boolean isAllowGuestLogin() {
349 return this.allowGuestLogin;
350 }
351
352
353
354
355
356
357
358
359 public void setAllowGuestLogin(final boolean value) {
360 this.allowGuestLogin = value;
361 }
362 }