001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.geronimo.jetty6.handler;
017:
018: import java.io.IOException;
019: import java.security.AccessControlContext;
020: import java.security.AccessControlException;
021: import java.security.Principal;
022:
023: import javax.security.auth.Subject;
024: import javax.security.jacc.PolicyContext;
025: import javax.security.jacc.WebResourcePermission;
026: import javax.security.jacc.WebUserDataPermission;
027: import javax.servlet.ServletException;
028: import javax.servlet.http.HttpServletRequest;
029: import javax.servlet.http.HttpServletResponse;
030:
031: import org.apache.geronimo.common.GeronimoSecurityException;
032: import org.apache.geronimo.jetty6.JAASJettyPrincipal;
033: import org.apache.geronimo.jetty6.JAASJettyRealm;
034: import org.apache.geronimo.jetty6.JettyContainer;
035: import org.apache.geronimo.security.Callers;
036: import org.apache.geronimo.security.ContextManager;
037: import org.mortbay.jetty.HttpException;
038: import org.mortbay.jetty.Request;
039: import org.mortbay.jetty.Response;
040: import org.mortbay.jetty.security.Authenticator;
041: import org.mortbay.jetty.security.FormAuthenticator;
042: import org.mortbay.jetty.security.SecurityHandler;
043:
044: public class JettySecurityHandler extends SecurityHandler {
045:
046: private String policyContextID;
047:
048: private JAASJettyPrincipal defaultPrincipal;
049:
050: private String formLoginPath;
051:
052: private JAASJettyRealm realm;
053:
054: public JettySecurityHandler(Authenticator authenticator,
055: JAASJettyRealm userRealm, String policyContextID,
056: Subject defaultSubject) {
057: setAuthenticator(authenticator);
058: this .policyContextID = policyContextID;
059:
060: if (authenticator instanceof FormAuthenticator) {
061: String formLoginPath = ((FormAuthenticator) authenticator)
062: .getLoginPage();
063: if (formLoginPath.indexOf('?') > 0) {
064: formLoginPath = formLoginPath.substring(0,
065: formLoginPath.indexOf('?'));
066: }
067: this .formLoginPath = formLoginPath;
068: } else {
069: formLoginPath = null;
070: }
071:
072: /**
073: * Register our default principal with the ContextManager
074: */
075: if (defaultSubject == null) {
076: defaultSubject = ContextManager.EMPTY;
077: }
078: this .defaultPrincipal = generateDefaultPrincipal(defaultSubject);
079:
080: setUserRealm(userRealm);
081: this .realm = userRealm;
082: assert realm != null;
083: }
084:
085: public boolean hasConstraints() {
086: return true;
087: }
088:
089: public void doStop(JettyContainer jettyContainer) throws Exception {
090: try {
091: super .doStop();
092: } finally {
093: jettyContainer.removeRealm(realm.getSecurityRealmName());
094: }
095: }
096:
097: /* ------------------------------------------------------------ */
098: /*
099: * @see org.mortbay.jetty.security.SecurityHandler#handle(java.lang.String,
100: * javax.servlet.http.HttpServletRequest,
101: * javax.servlet.http.HttpServletResponse, int)
102: */
103: public void handle(String target, HttpServletRequest request,
104: HttpServletResponse response, int dispatch)
105: throws IOException, ServletException {
106: String old_policy_id = PolicyContext.getContextID();
107: Callers oldCallers = ContextManager.getCallers();
108:
109: try {
110: PolicyContext.setContextID(policyContextID);
111: PolicyContext.setHandlerData(request);
112:
113: super .handle(target, request, response, dispatch);
114: } finally {
115: PolicyContext.setContextID(old_policy_id);
116: ContextManager.popCallers(oldCallers);
117: }
118: }
119:
120: // public static Subject getCurrentRoleDesignate(String role) {
121: // return ((JettySecurityHandler) (WebAppContext.getCurrentWebAppContext()
122: // .getSecurityHandler())).getRoleDesignate(role);
123: // }
124: //
125: // private Subject getRoleDesignate(String roleName) {
126: // return (Subject) roleDesignates.get(roleName);
127: // }
128:
129: /**
130: * Check the security constraints using JACC.
131: *
132: * @param pathInContext path in context
133: * @param request HTTP request
134: * @param response HTTP response
135: * @return true if the path in context passes the security check, false if
136: * it fails or a redirection has occured during authentication.
137: */
138: public boolean checkSecurityConstraints(String pathInContext,
139: Request request, Response response) throws IOException {
140: if (formLoginPath != null) {
141: String pathToBeTested = (pathInContext.indexOf('?') > 0 ? pathInContext
142: .substring(0, pathInContext.indexOf('?'))
143: : pathInContext);
144:
145: if (pathToBeTested.equals(formLoginPath)) {
146: return true;
147: }
148: }
149:
150: try {
151: String transportType;
152: if (request.isSecure()) {
153: transportType = "CONFIDENTIAL";
154: } else if (request.getConnection().isIntegral(request)) {
155: transportType = "INTEGRAL";
156: } else {
157: transportType = "NONE";
158: }
159: String substitutedPathInContext = pathInContext;
160: if (substitutedPathInContext.indexOf("%3A") > -1)
161: substitutedPathInContext = substitutedPathInContext
162: .replaceAll("%3A", "%3A%3A");
163: if (substitutedPathInContext.indexOf(":") > -1)
164: substitutedPathInContext = substitutedPathInContext
165: .replaceAll(":", "%3A");
166:
167: Authenticator authenticator = getAuthenticator();
168: boolean isAuthenticated = false;
169:
170: if (authenticator instanceof FormAuthenticator
171: && pathInContext
172: .endsWith(FormAuthenticator.__J_SECURITY_CHECK)) {
173: /**
174: * This is a post request to __J_SECURITY_CHECK. Stop now after authentication.
175: * Whether or not authentication succeeded, we return.
176: */
177: authenticator.authenticate(realm, pathInContext,
178: request, response);
179: return false;
180: }
181: // attempt to access an unprotected resource that is not the
182: // j_security_check.
183: // if we are logged in, return the logged in principal.
184: if (request != null) {
185: // null response appears to prevent redirect to login page
186: Principal user = authenticator.authenticate(realm,
187: pathInContext, request, null);
188: if (user == null || user == SecurityHandler.__NOBODY) {
189: //TODO use run-as as nextCaller if present
190: ContextManager.setCallers(defaultPrincipal
191: .getSubject(), defaultPrincipal
192: .getSubject());
193: request.setUserPrincipal(new NotChecked());
194: } else if (user != null) {
195: isAuthenticated = true;
196: }
197: }
198:
199: AccessControlContext acc = ContextManager
200: .getCurrentContext();
201:
202: /**
203: * JACC v1.0 section 4.1.1
204: */
205: WebUserDataPermission wudp = new WebUserDataPermission(
206: substitutedPathInContext, new String[] { request
207: .getMethod() }, transportType);
208: acc.checkPermission(wudp);
209:
210: WebResourcePermission webResourcePermission = new WebResourcePermission(
211: request);
212: /**
213: * JACC v1.0 section 4.1.2
214: */
215: if (isAuthenticated) {
216: //current user is logged in, this is the actual check
217: acc.checkPermission(webResourcePermission);
218: } else {
219: //user is not logged in: if access denied, try to log them in.
220: try {
221: acc.checkPermission(webResourcePermission);
222: } catch (AccessControlException e) {
223: //not logged in: try to log them in.
224: Principal user = authenticator.authenticate(realm,
225: pathInContext, request, response);
226: if (user == SecurityHandler.__NOBODY) {
227: return true;
228: }
229: if (user == null) {
230: throw e;
231: }
232: }
233: }
234:
235: } catch (HttpException he) {
236: response.sendError(he.getStatus(), he.getReason());
237: return false;
238: } catch (AccessControlException ace) {
239: if (!response.isCommitted()) {
240: response.sendError(403);
241: }
242: return false;
243: }
244: return true;
245: }
246:
247: /**
248: * Generate the default principal from the security config.
249: *
250: * @param defaultSubject The default subject.
251: * @return the default principal
252: * @throws org.apache.geronimo.common.GeronimoSecurityException
253: * if the default principal cannot be constructed
254: */
255: protected JAASJettyPrincipal generateDefaultPrincipal(
256: Subject defaultSubject) throws GeronimoSecurityException {
257:
258: if (defaultSubject == null) {
259: throw new GeronimoSecurityException(
260: "Unable to generate default principal");
261: }
262:
263: JAASJettyPrincipal result = new JAASJettyPrincipal("default");
264:
265: result.setSubject(defaultSubject);
266:
267: return result;
268: }
269:
270: }
|