-
Notifications
You must be signed in to change notification settings - Fork 138
/
ProgrammaticLogin.java
443 lines (405 loc) · 17.4 KB
/
ProgrammaticLogin.java
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package com.sun.enterprise.security.ee.authentication;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.api.ServiceLocator;
import org.jvnet.hk2.annotations.Service;
import com.sun.appserv.security.ProgrammaticLoginPermission;
import com.sun.enterprise.security.SecurityServicesUtil;
import com.sun.enterprise.security.UsernamePasswordStore;
import com.sun.enterprise.security.auth.login.LoginContextDriver;
import com.sun.enterprise.security.common.SecurityConstants;
import com.sun.enterprise.security.common.Util;
import com.sun.enterprise.security.ee.web.integration.WebProgrammaticLogin;
import com.sun.logging.LogDomains;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* Implement programmatic login.
*
* <P>
* This class allows deployed applications to supply a name and password directly to the security service. This info
* will be used to attempt to login to the current realm. If authentication succeeds, a security context is established
* as this user.
*
* <P>
* This allows applications to programmatically handle authentication. The use of this mechanism is not recommended
* since it bypasses the standard J2EE mechanisms and places all burden on the application developer.
*
* <P>
* Invoking this method requires the permission ProgrammaticLoginPermission with the method name being invoked.
*
* <P>
* There are two forms of the login method, one which includes the HTTP request and response objects for use by servlets
* and one which can be used by EJBs.
*
*
*/
@Service
@PerLookup
public class ProgrammaticLogin {
private WebProgrammaticLogin webProgrammaticLogin;
private static final Logger logger = LogDomains.getLogger(ProgrammaticLogin.class, LogDomains.SECURITY_LOGGER);
private static ProgrammaticLoginPermission plLogin = new ProgrammaticLoginPermission("login");
private static ProgrammaticLoginPermission plLogout = new ProgrammaticLoginPermission("logout");
private static final String DEFAULT_WEBPROGRAMMATICLOGIN_IMPL = "com.sun.web.security.WebProgrammaticLoginImpl";
/*
* V3:Commented private static boolean isServer = (ApplicationServer.getServerContext() != null);
*/
private static javax.security.auth.callback.CallbackHandler handler = new com.sun.enterprise.security.auth.login.LoginCallbackHandler(
false);
public ProgrammaticLogin() {
if (SecurityServicesUtil.getInstance() != null) {
resolveWebProgrammaticLogin();
}
}
/**
* Password should be used as a char[]
*/
@Deprecated
public Boolean login(final String user, final String password, final String realm, boolean errors) throws Exception {
return login(user, password.toCharArray(), realm, errors);
}
/**
* Attempt to login.
*
* <P>
* Upon successful return from this method the SecurityContext will be set in the name of the given user as its Subject.
*
* <p>
* On client side, realm and errors parameters will be ignored and the actual login will not occur until we actually
* access a resource requiring a login. And a java.rmi.AccessException with COBRA NO_PERMISSION will occur when actual
* login is failed.
*
* <P>
* This method is intented primarily for EJBs wishing to do programmatic login. If servlet code used this method the
* established identity will be propagated to EJB calls but will not be used for web container manager authorization. In
* general servlets should use the servlet-specific version of login instead.
*
* @param user User name.
* @param password Password for user.
* @param realm the realm name in which the user should be logged in.
* @param errors errors=true, propagate any exception encountered to the user errors=false, no exceptions are
* propagated.
* @return Boolean containing true or false to indicate success or failure of login.
* @throws Exception any exception encountered during Login.
*/
public Boolean login(final String user, final char[] password, final String realm, boolean errors) throws Exception {
Boolean authenticated = null;
// check permission to login
try {
// exception thrown on failure
checkLoginPermission(user);
// try to login. doPrivileged is used since application code does
// not have permissions to process the jaas login.
authenticated = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public java.lang.Boolean run() {
// if realm is null, LCD will log into the default realm
if (SecurityServicesUtil.getInstance() != null && SecurityServicesUtil.getInstance().isServer()
|| Util.isEmbeddedServer()) {
LoginContextDriver.login(user, password, realm);
} else {
int type = SecurityConstants.USERNAME_PASSWORD;
// should not set realm here
// Bugfix# 6387278. The UsernamePasswordStore
// abstracts the thread-local/global details
UsernamePasswordStore.set(user, password);
try {
LoginContextDriver.doClientLogin(type, handler);
} finally {
// For security, if thread-local no need to
// save the username/password state
UsernamePasswordStore.resetThreadLocalOnly();
}
}
return true;
}
});
} catch (Exception e) {
logger.log(Level.SEVERE, "prog.login.failed", e);
if (errors == true) { // propagate the exception ahead
throw e;
}
authenticated = false;
}
return authenticated;
}
/*
* Use of the char[] as password is encouraged
*/
@Deprecated
public Boolean login(final String user, final String password) {
return login(user, password.toCharArray());
}
/**
* Attempt to login.
*
* <P>
* Upon successful return from this method the SecurityContext will be set in the name of the given user as its Subject.
*
* <p>
* On client side, the actual login will not occur until we actually access a resource requiring a login. And a
* java.rmi.AccessException with COBRA NO_PERMISSION will occur when actual login is failed.
*
* <P>
* This method is intented primarily for EJBs wishing to do programmatic login. If servlet code used this method the
* established identity will be propagated to EJB calls but will not be used for web container manager authorization. In
* general servlets should use the servlet-specific version of login instead.
*
* @param user User name.
* @param password Password for user.
* @return Boolean containing true or false to indicate success or failure of login.
*/
public Boolean login(final String user, final char[] password) {
// call login with realm-name = null and request for errors = false
Boolean authenticated = null;
try {
authenticated = login(user, password, null, false);
} catch (Exception e) {
// sanity checking, will never come here
authenticated = false;
}
return authenticated;
}
/*
* Use of the char[] as password is encouraged
*/
@Deprecated
public Boolean login(final String user, final String password, final String realm, final HttpServletRequest request,
final HttpServletResponse response, boolean errors) throws Exception {
return login(user, password.toCharArray(), realm, request, response, errors);
}
/**
* Attempt to login. This method is specific to servlets (and JSPs).
*
* <P>
* Upon successful return from this method the SecurityContext will be set in the name of the given user as its Subject.
* In addition, the principal stored in the request is set to the user name. If a session is available, its principal is
* also set to the user provided.
*
* @returns Boolean containing true or false to indicate success or failure of login.
* @param realm
* @param errors
* @param user User name.
* @param password Password for user.
* @param request HTTP request object provided by caller application. It should be an instance of HttpRequestFacade.
* @param response HTTP response object provided by called application. It should be an instance of HttpServletResponse.
* @throws Exception any exceptions encountered during login
* @return Boolean indicating true for successful login and false otherwise
*/
public Boolean login(final String user, final char[] password, final String realm, final HttpServletRequest request,
final HttpServletResponse response, boolean errors) throws Exception {
Boolean authenticated = null;
try {
// check permission to login
checkLoginPermission(user);
// try to login. doPrivileged is used since application code does
// not have permissions to process the jaas login.
authenticated = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return webProgrammaticLogin.login(user, password, realm, request, response);
}
});
} catch (Exception e) {
if (errors == true) {
throw e;
}
authenticated = false;
}
return authenticated;
}
/*
* Use of char[] as password is encouraged
*/
@Deprecated
public Boolean login(final String user, final String password, final HttpServletRequest request, final HttpServletResponse response) {
return login(user, password.toCharArray(), request, response);
}
/**
* Attempt to login. This method is specific to servlets (and JSPs).
*
* <P>
* Upon successful return from this method the SecurityContext will be set in the name of the given user as its Subject.
* In addition, the principal stored in the request is set to the user name. If a session is available, its principal is
* also set to the user provided.
*
* @param user User name.
* @param password Password for user.
* @param request HTTP request object provided by caller application. It should be an instance of HttpRequestFacade.
* @param response HTTP response object provided by called application. It should be an instance of HttpServletResponse.
* @return Boolean containing true or false to indicate success or failure of login.
*
*/
public Boolean login(final String user, final char[] password, final HttpServletRequest request, final HttpServletResponse response) {
Boolean authenticated = null;
try {
// pass a null realmname and errors=false
authenticated = login(user, password, null, request, response, false);
} catch (Exception e) {
// sanity check will never come here
authenticated = false;
}
return authenticated;
}
/**
* Attempt to logout.
*
* @returns Boolean containing true or false to indicate success or failure of logout.
*
*/
public Boolean logout() {
Boolean loggedout = null;
try {
loggedout = logout(false);
} catch (Exception e) {
// sanity check will never come here
loggedout = false;
}
return loggedout;
}
/**
* Attempt to logout.
*
* @param errors, errors = true, the method will propagate the exceptions encountered while logging out, errors=false
* will return a Boolean value of false indicating failure of logout
* @return Boolean containing true or false to indicate success or failure of logout.
* @throws Exception encountered while logging out, if errors==false
*
*/
public Boolean logout(boolean errors) throws Exception {
Boolean loggedout = null;
// check logout permission
try {
checkLogoutPermission();
AccessController.doPrivileged(new PrivilegedAction<>() {
@Override
public java.lang.Object run() {
// V3:Commentedif (isServer) {
if (SecurityServicesUtil.getInstance() != null && SecurityServicesUtil.getInstance().isServer()) {
LoginContextDriver.logout();
} else {
// Reset the username/password state on logout
UsernamePasswordStore.reset();
LoginContextDriver.doClientLogout();
// If user try to access a protected resource after here
// then it will prompt for password in appclient or
// just fail in standalone client.
}
return null;
}
});
loggedout = true;
} catch (Exception e) {
logger.log(Level.WARNING, "prog.logout.failed", e);
if (errors) {
throw e;
}
loggedout = false;
}
return loggedout;
}
/**
* Attempt to logout. Also removes principal from request (and session if available).
*
* @returns Boolean containing true or false to indicate success or failure of logout.
*
*/
public Boolean logout(final HttpServletRequest request, final HttpServletResponse response) {
Boolean loggedout = null;
try {
loggedout = logout(request, response, false);
} catch (Exception e) {
// sanity check, will never come here
loggedout = false;
}
return loggedout;
}
/**
* Attempt to logout. Also removes principal from request (and session if available).
*
* @param errors, errors = true, the method will propagate the exceptions encountered while logging out, errors=false
* will return a Boolean value of false indicating failure of logout
*
* @return Boolean containing true or false to indicate success or failure of logout.
* @throws Exception, exception encountered while logging out and if errors == true
*/
public Boolean logout(final HttpServletRequest request, final HttpServletResponse response, boolean errors) throws Exception {
// check logout permission
Boolean loggedout = null;
try {
checkLogoutPermission();
loggedout = AccessController.doPrivileged(new PrivilegedExceptionAction<Boolean>() {
@Override
public Boolean run() throws Exception {
return webProgrammaticLogin.logout(request, response);
}
});
} catch (Exception e) {
if (errors) {
throw e;
}
loggedout = false;
}
return loggedout;
}
/**
* Check whether caller has login permission.
*
*/
private void checkLoginPermission(String user) throws Exception {
try {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "ProgrammaticLogin.login() called for user: " + user);
}
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(plLogin);
}
} catch (Exception e) {
logger.warning("proglogin.noperm");
throw e;
}
}
/**
* Check if caller has logout permission.
*
*/
private void checkLogoutPermission() throws Exception {
try {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "ProgrammaticLogin.logout() called.");
}
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(plLogout);
}
} catch (Exception e) {
logger.warning("prologout.noperm");
throw e;
}
}
private void resolveWebProgrammaticLogin() {
ServiceLocator habitat = SecurityServicesUtil.getInstance().getHabitat();
this.webProgrammaticLogin = habitat.getService(WebProgrammaticLogin.class);
}
}