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