001: /*
002: * Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: */
007: package winstone.auth;
008:
009: import java.io.IOException;
010: import java.util.List;
011: import java.util.Set;
012:
013: import javax.servlet.ServletException;
014: import javax.servlet.ServletRequest;
015: import javax.servlet.ServletResponse;
016: import javax.servlet.http.HttpServletRequest;
017: import javax.servlet.http.HttpServletResponse;
018:
019: import org.w3c.dom.Node;
020:
021: import winstone.AuthenticationHandler;
022: import winstone.AuthenticationRealm;
023: import winstone.Logger;
024: import winstone.WebAppConfiguration;
025: import winstone.WinstoneResourceBundle;
026:
027: /**
028: * Base class for managers of authentication within Winstone. This class also
029: * acts as a factory, loading the appropriate subclass for the requested auth
030: * type.
031: *
032: * @author mailto: <a href="rick_knowles@hotmail.com">Rick Knowles</a>
033: * @version $Id: BaseAuthenticationHandler.java,v 1.6 2006/02/28 07:32:47 rickknowles Exp $
034: */
035: public abstract class BaseAuthenticationHandler implements
036: AuthenticationHandler {
037: static final String ELEM_REALM_NAME = "realm-name";
038:
039: protected SecurityConstraint constraints[];
040: protected AuthenticationRealm realm;
041: protected String realmName;
042: public final static WinstoneResourceBundle AUTH_RESOURCES = new WinstoneResourceBundle(
043: "winstone.auth.LocalStrings");
044:
045: /**
046: * Factory method - this parses the web.xml nodes and builds the correct
047: * subclass for handling that auth type.
048: */
049: protected BaseAuthenticationHandler(Node loginConfigNode,
050: List constraintNodes, Set rolesAllowed,
051: AuthenticationRealm realm) {
052: this .realm = realm;
053:
054: for (int m = 0; m < loginConfigNode.getChildNodes().getLength(); m++) {
055: Node loginElm = loginConfigNode.getChildNodes().item(m);
056: if (loginElm.getNodeType() != Node.ELEMENT_NODE)
057: continue;
058: else if (loginElm.getNodeName().equals(ELEM_REALM_NAME))
059: realmName = WebAppConfiguration
060: .getTextFromNode(loginElm);
061: }
062:
063: // Build security constraints
064: this .constraints = new SecurityConstraint[constraintNodes
065: .size()];
066: for (int n = 0; n < constraints.length; n++)
067: this .constraints[n] = new SecurityConstraint(
068: (Node) constraintNodes.get(n), rolesAllowed, n);
069: }
070:
071: /**
072: * Evaluates any authentication constraints, intercepting if auth is
073: * required. The relevant authentication handler subclass's logic is used to
074: * actually authenticate.
075: *
076: * @return A boolean indicating whether to continue after this request
077: */
078: public boolean processAuthentication(ServletRequest inRequest,
079: ServletResponse inResponse, String pathRequested)
080: throws IOException, ServletException {
081: Logger.log(Logger.FULL_DEBUG, AUTH_RESOURCES,
082: "BaseAuthenticationHandler.StartAuthCheck");
083:
084: HttpServletRequest request = (HttpServletRequest) inRequest;
085: HttpServletResponse response = (HttpServletResponse) inResponse;
086:
087: // Give previous attempts a chance to be validated
088: if (!validatePossibleAuthenticationResponse(request, response,
089: pathRequested)) {
090: return false;
091: } else {
092: return doRoleCheck(request, response, pathRequested);
093: }
094: }
095:
096: protected boolean doRoleCheck(HttpServletRequest request,
097: HttpServletResponse response, String pathRequested)
098: throws IOException, ServletException {
099: // Loop through constraints
100: boolean foundApplicable = false;
101: for (int n = 0; (n < this .constraints.length)
102: && !foundApplicable; n++) {
103: Logger.log(Logger.FULL_DEBUG, AUTH_RESOURCES,
104: "BaseAuthenticationHandler.EvalConstraint",
105: this .constraints[n].getName());
106:
107: // Find one that applies, then
108: if (this .constraints[n].isApplicable(pathRequested, request
109: .getMethod())) {
110: Logger
111: .log(
112: Logger.FULL_DEBUG,
113: AUTH_RESOURCES,
114: "BaseAuthenticationHandler.ApplicableConstraint",
115: this .constraints[n].getName());
116: foundApplicable = true;
117:
118: if (this .constraints[n].needsSSL()
119: && !request.isSecure()) {
120: Logger
121: .log(
122: Logger.DEBUG,
123: AUTH_RESOURCES,
124: "BaseAuthenticationHandler.ConstraintNeedsSSL",
125: this .constraints[n].getName());
126: response
127: .sendError(
128: HttpServletResponse.SC_FORBIDDEN,
129: AUTH_RESOURCES
130: .getString(
131: "BaseAuthenticationHandler.ConstraintNeedsSSL",
132: this .constraints[n]
133: .getName()));
134: return false;
135: }
136:
137: else if (!this .constraints[n].isAllowed(request)) {
138: // Logger.log(Logger.FULL_DEBUG, "Not allowed - requesting auth");
139: requestAuthentication(request, response,
140: pathRequested);
141: return false;
142: } else {
143: // Logger.log(Logger.FULL_DEBUG, "Allowed - authorization accepted");
144: // Ensure that secured resources are not cached
145: setNoCache(response);
146: }
147: }
148: }
149:
150: // If we made it this far without a check being run, there must be none applicable
151: Logger.log(Logger.FULL_DEBUG, AUTH_RESOURCES,
152: "BaseAuthenticationHandler.PassedAuthCheck");
153: return true;
154: }
155:
156: protected void setNoCache(HttpServletResponse response) {
157: response.setHeader("Pragma", "No-cache");
158: response.setHeader("Cache-Control", "No-cache");
159: response.setDateHeader("Expires", 1);
160: }
161:
162: /**
163: * The actual auth request implementation.
164: */
165: protected abstract void requestAuthentication(
166: HttpServletRequest request, HttpServletResponse response,
167: String pathRequested) throws IOException, ServletException;
168:
169: /**
170: * Handling the (possible) response
171: */
172: protected abstract boolean validatePossibleAuthenticationResponse(
173: HttpServletRequest request, HttpServletResponse response,
174: String pathRequested) throws ServletException, IOException;
175: }
|