Fields Summary |
---|
private static org.apache.juli.logging.Log | log |
protected static final String | DEFAULT_ALGORITHMThe default message digest algorithm to use if we cannot use
the requested one. |
protected static final int | SESSION_ID_BYTESThe number of random bytes to include when generating a
session identifier. |
protected String | algorithmThe message digest algorithm to be used when generating session
identifiers. This must be an algorithm supported by the
java.security.MessageDigest class on your platform. |
protected boolean | cacheShould we cache authenticated Principals if the request is part of
an HTTP session? |
protected org.apache.catalina.Context | contextThe Context to which this Valve is attached. |
protected MessageDigest | digestReturn the MessageDigest implementation to be used when
creating session identifiers. |
protected String | entropyA String initialization parameter used to increase the entropy of
the initialization of our random number generator. |
protected static final String | infoDescriptive information about this implementation. |
protected boolean | disableProxyCachingFlag to determine if we disable proxy caching, or leave the issue
up to the webapp developer. |
protected boolean | securePagesWithPragmaFlag to determine if we disable proxy caching with headers incompatible
with IE |
protected org.apache.catalina.util.LifecycleSupport | lifecycleThe lifecycle event support for this component. |
protected Random | randomA random number generator to use when generating session identifiers. |
protected String | randomClassThe Java class name of the random number generator class to be used
when generating session identifiers. |
protected static final org.apache.catalina.util.StringManager | smThe string manager for this package. |
protected SingleSignOn | ssoThe SingleSignOn implementation in our request processing chain,
if there is one. |
protected boolean | startedHas this component been started? |
private static final String | DATE_ONE"Expires" header always set to Date(1), so generate once only |
Methods Summary |
---|
public void | addLifecycleListener(org.apache.catalina.LifecycleListener listener)Add a lifecycle event listener to this component.
lifecycle.addLifecycleListener(listener);
|
protected void | associate(java.lang.String ssoId, org.apache.catalina.Session session)Associate the specified single sign on identifier with the
specified Session.
if (sso == null)
return;
sso.associate(ssoId, session);
|
protected abstract boolean | authenticate(org.apache.catalina.connector.Request request, org.apache.catalina.connector.Response response, org.apache.catalina.deploy.LoginConfig config)Authenticate the user making this request, based on the specified
login configuration. Return true if any specified
constraint has been satisfied, or false if we have
created a response challenge already.
|
public org.apache.catalina.LifecycleListener[] | findLifecycleListeners()Get the lifecycle listeners associated with this lifecycle. If this
Lifecycle has no listeners registered, a zero-length array is returned.
return lifecycle.findLifecycleListeners();
|
protected synchronized java.lang.String | generateSessionId()Generate and return a new session identifier for the cookie that
identifies an SSO principal.
// Generate a byte array containing a session identifier
byte bytes[] = new byte[SESSION_ID_BYTES];
getRandom().nextBytes(bytes);
bytes = getDigest().digest(bytes);
// Render the result as a String of hexadecimal digits
StringBuffer result = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
byte b1 = (byte) ((bytes[i] & 0xf0) >> 4);
byte b2 = (byte) (bytes[i] & 0x0f);
if (b1 < 10)
result.append((char) ('0" + b1));
else
result.append((char) ('A" + (b1 - 10)));
if (b2 < 10)
result.append((char) ('0" + b2));
else
result.append((char) ('A" + (b2 - 10)));
}
return (result.toString());
|
public java.lang.String | getAlgorithm()Return the message digest algorithm for this Manager.
// ------------------------------------------------------------- Properties
return (this.algorithm);
|
public boolean | getCache()Return the cache authenticated Principals flag.
return (this.cache);
|
public org.apache.catalina.Container | getContainer()Return the Container to which this Valve is attached.
return (this.context);
|
protected synchronized java.security.MessageDigest | getDigest()Return the MessageDigest object to be used for calculating
session identifiers. If none has been created yet, initialize
one the first time this method is called.
if (this.digest == null) {
try {
this.digest = MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
try {
this.digest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
} catch (NoSuchAlgorithmException f) {
this.digest = null;
}
}
}
return (this.digest);
|
public boolean | getDisableProxyCaching()Return the flag that states if we add headers to disable caching by
proxies.
return disableProxyCaching;
|
public java.lang.String | getEntropy()Return the entropy increaser value, or compute a semi-useful value
if this String has not yet been set.
// Calculate a semi-useful value if this has not been set
if (this.entropy == null)
setEntropy(this.toString());
return (this.entropy);
|
public java.lang.String | getInfo()Return descriptive information about this Valve implementation.
return (info);
|
protected synchronized java.util.Random | getRandom()Return the random number generator instance we should use for
generating session identifiers. If there is no such generator
currently defined, construct and seed a new one.
if (this.random == null) {
try {
Class clazz = Class.forName(randomClass);
this.random = (Random) clazz.newInstance();
long seed = System.currentTimeMillis();
char entropy[] = getEntropy().toCharArray();
for (int i = 0; i < entropy.length; i++) {
long update = ((byte) entropy[i]) << ((i % 8) * 8);
seed ^= update;
}
this.random.setSeed(seed);
} catch (Exception e) {
this.random = new java.util.Random();
}
}
return (this.random);
|
public java.lang.String | getRandomClass()Return the random number generator class name.
return (this.randomClass);
|
public boolean | getSecurePagesWithPragma()Return the flag that states, if proxy caching is disabled, what headers
we add to disable the caching.
return securePagesWithPragma;
|
public void | invoke(org.apache.catalina.connector.Request request, org.apache.catalina.connector.Response response)Enforce the security restrictions in the web application deployment
descriptor of our associated Context.
if (log.isDebugEnabled())
log.debug("Security checking request " +
request.getMethod() + " " + request.getRequestURI());
LoginConfig config = this.context.getLoginConfig();
// Have we got a cached authenticated Principal to record?
if (cache) {
Principal principal = request.getUserPrincipal();
if (principal == null) {
Session session = request.getSessionInternal(false);
if (session != null) {
principal = session.getPrincipal();
if (principal != null) {
if (log.isDebugEnabled())
log.debug("We have cached auth type " +
session.getAuthType() +
" for principal " +
session.getPrincipal());
request.setAuthType(session.getAuthType());
request.setUserPrincipal(principal);
}
}
}
}
// Special handling for form-based logins to deal with the case
// where the login form (and therefore the "j_security_check" URI
// to which it submits) might be outside the secured area
String contextPath = this.context.getPath();
String requestURI = request.getDecodedRequestURI();
if (requestURI.startsWith(contextPath) &&
requestURI.endsWith(Constants.FORM_ACTION)) {
if (!authenticate(request, response, config)) {
if (log.isDebugEnabled())
log.debug(" Failed authenticate() test ??" + requestURI );
return;
}
}
Realm realm = this.context.getRealm();
// Is this request URI subject to a security constraint?
SecurityConstraint [] constraints
= realm.findSecurityConstraints(request, this.context);
if ((constraints == null) /* &&
(!Constants.FORM_METHOD.equals(config.getAuthMethod())) */ ) {
if (log.isDebugEnabled())
log.debug(" Not subject to any constraint");
getNext().invoke(request, response);
return;
}
// Make sure that constrained resources are not cached by web proxies
// or browsers as caching can provide a security hole
if (disableProxyCaching &&
// FIXME: Disabled for Mozilla FORM support over SSL
// (improper caching issue)
//!request.isSecure() &&
!"POST".equalsIgnoreCase(request.getMethod())) {
if (securePagesWithPragma) {
// FIXME: These cause problems with downloading office docs
// from IE under SSL and may not be needed for newer Mozilla
// clients.
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
} else {
response.setHeader("Cache-Control", "private");
}
response.setHeader("Expires", DATE_ONE);
}
int i;
// Enforce any user data constraint for this security constraint
if (log.isDebugEnabled()) {
log.debug(" Calling hasUserDataPermission()");
}
if (!realm.hasUserDataPermission(request, response,
constraints)) {
if (log.isDebugEnabled()) {
log.debug(" Failed hasUserDataPermission() test");
}
/*
* ASSERT: Authenticator already set the appropriate
* HTTP status code, so we do not have to do anything special
*/
return;
}
// Since authenticate modifies the response on failure,
// we have to check for allow-from-all first.
boolean authRequired = true;
for(i=0; i < constraints.length && authRequired; i++) {
if(!constraints[i].getAuthConstraint()) {
authRequired = false;
} else if(!constraints[i].getAllRoles()) {
String [] roles = constraints[i].findAuthRoles();
if(roles == null || roles.length == 0) {
authRequired = false;
}
}
}
if(authRequired) {
if (log.isDebugEnabled()) {
log.debug(" Calling authenticate()");
}
if (!authenticate(request, response, config)) {
if (log.isDebugEnabled()) {
log.debug(" Failed authenticate() test");
}
/*
* ASSERT: Authenticator already set the appropriate
* HTTP status code, so we do not have to do anything
* special
*/
return;
}
}
if (log.isDebugEnabled()) {
log.debug(" Calling accessControl()");
}
if (!realm.hasResourcePermission(request, response,
constraints,
this.context)) {
if (log.isDebugEnabled()) {
log.debug(" Failed accessControl() test");
}
/*
* ASSERT: AccessControl method has already set the
* appropriate HTTP status code, so we do not have to do
* anything special
*/
return;
}
// Any and all specified constraints have been satisfied
if (log.isDebugEnabled()) {
log.debug(" Successfully passed all security constraints");
}
getNext().invoke(request, response);
|
protected boolean | reauthenticateFromSSO(java.lang.String ssoId, org.apache.catalina.connector.Request request)Attempts reauthentication to the Realm using
the credentials included in argument entry .
if (sso == null || ssoId == null)
return false;
boolean reauthenticated = false;
Container parent = getContainer();
if (parent != null) {
Realm realm = parent.getRealm();
if (realm != null) {
reauthenticated = sso.reauthenticate(ssoId, realm, request);
}
}
if (reauthenticated) {
associate(ssoId, request.getSessionInternal(true));
if (log.isDebugEnabled()) {
log.debug(" Reauthenticated cached principal '" +
request.getUserPrincipal().getName() +
"' with auth type '" + request.getAuthType() + "'");
}
}
return reauthenticated;
|
protected void | register(org.apache.catalina.connector.Request request, org.apache.catalina.connector.Response response, java.security.Principal principal, java.lang.String authType, java.lang.String username, java.lang.String password)Register an authenticated Principal and authentication type in our
request, in the current session (if there is one), and with our
SingleSignOn valve, if there is one. Set the appropriate cookie
to be returned.
if (log.isDebugEnabled())
log.debug("Authenticated '" + principal.getName() + "' with type '"
+ authType + "'");
// Cache the authentication information in our request
request.setAuthType(authType);
request.setUserPrincipal(principal);
Session session = request.getSessionInternal(false);
// Cache the authentication information in our session, if any
if (cache) {
if (session != null) {
session.setAuthType(authType);
session.setPrincipal(principal);
if (username != null)
session.setNote(Constants.SESS_USERNAME_NOTE, username);
else
session.removeNote(Constants.SESS_USERNAME_NOTE);
if (password != null)
session.setNote(Constants.SESS_PASSWORD_NOTE, password);
else
session.removeNote(Constants.SESS_PASSWORD_NOTE);
}
}
// Construct a cookie to be returned to the client
if (sso == null)
return;
// Only create a new SSO entry if the SSO did not already set a note
// for an existing entry (as it would do with subsequent requests
// for DIGEST and SSL authenticated contexts)
String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
if (ssoId == null) {
// Construct a cookie to be returned to the client
ssoId = generateSessionId();
Cookie cookie = new Cookie(Constants.SINGLE_SIGN_ON_COOKIE, ssoId);
cookie.setMaxAge(-1);
cookie.setPath("/");
// Bugzilla 41217
cookie.setSecure(request.isSecure());
// Bugzilla 34724
String ssoDomain = sso.getCookieDomain();
if(ssoDomain != null) {
cookie.setDomain(ssoDomain);
}
response.addCookie(cookie);
// Register this principal with our SSO valve
sso.register(ssoId, principal, authType, username, password);
request.setNote(Constants.REQ_SSOID_NOTE, ssoId);
} else {
// Update the SSO session with the latest authentication data
sso.update(ssoId, principal, authType, username, password);
}
// Fix for Bug 10040
// Always associate a session with a new SSO reqistration.
// SSO entries are only removed from the SSO registry map when
// associated sessions are destroyed; if a new SSO entry is created
// above for this request and the user never revisits the context, the
// SSO entry will never be cleared if we don't associate the session
if (session == null)
session = request.getSessionInternal(true);
sso.associate(ssoId, session);
|
public void | removeLifecycleListener(org.apache.catalina.LifecycleListener listener)Remove a lifecycle event listener from this component.
lifecycle.removeLifecycleListener(listener);
|
public void | setAlgorithm(java.lang.String algorithm)Set the message digest algorithm for this Manager.
this.algorithm = algorithm;
|
public void | setCache(boolean cache)Set the cache authenticated Principals flag.
this.cache = cache;
|
public void | setContainer(org.apache.catalina.Container container)Set the Container to which this Valve is attached.
if (!(container instanceof Context))
throw new IllegalArgumentException
(sm.getString("authenticator.notContext"));
super.setContainer(container);
this.context = (Context) container;
|
public void | setDisableProxyCaching(boolean nocache)Set the value of the flag that states if we add headers to disable
caching by proxies.
disableProxyCaching = nocache;
|
public void | setEntropy(java.lang.String entropy)Set the entropy increaser value.
this.entropy = entropy;
|
public void | setRandomClass(java.lang.String randomClass)Set the random number generator class name.
this.randomClass = randomClass;
|
public void | setSecurePagesWithPragma(boolean securePagesWithPragma)Set the value of the flag that states what headers we add to disable
proxy caching.
this.securePagesWithPragma = securePagesWithPragma;
|
public void | start()Prepare for the beginning of active use of the public methods of this
component. This method should be called after configure() ,
and before any of the public methods of the component are utilized.
// Validate and update our current component state
if (started)
throw new LifecycleException
(sm.getString("authenticator.alreadyStarted"));
lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
// Look up the SingleSignOn implementation in our request processing
// path, if there is one
Container parent = context.getParent();
while ((sso == null) && (parent != null)) {
if (!(parent instanceof Pipeline)) {
parent = parent.getParent();
continue;
}
Valve valves[] = ((Pipeline) parent).getValves();
for (int i = 0; i < valves.length; i++) {
if (valves[i] instanceof SingleSignOn) {
sso = (SingleSignOn) valves[i];
break;
}
}
if (sso == null)
parent = parent.getParent();
}
if (log.isDebugEnabled()) {
if (sso != null)
log.debug("Found SingleSignOn Valve at " + sso);
else
log.debug("No SingleSignOn Valve is present");
}
|
public void | stop()Gracefully terminate the active use of the public methods of this
component. This method should be the last one called on a given
instance of this component.
// Validate and update our current component state
if (!started)
throw new LifecycleException
(sm.getString("authenticator.notStarted"));
lifecycle.fireLifecycleEvent(STOP_EVENT, null);
started = false;
sso = null;
|