001: /*
002: * Copyright 1999-2002,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.catalina.security;
017:
018: import java.io.IOException;
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021: import java.security.Principal;
022: import java.security.PrivilegedActionException;
023: import java.security.PrivilegedExceptionAction;
024: import java.util.HashMap;
025:
026: import javax.security.auth.Subject;
027: import javax.servlet.Filter;
028: import javax.servlet.Servlet;
029: import javax.servlet.ServletException;
030: import javax.servlet.UnavailableException;
031: import javax.servlet.http.HttpServletRequest;
032: import javax.servlet.http.HttpSession;
033:
034: import org.apache.catalina.Globals;
035: import org.apache.catalina.util.StringManager;
036:
037: /**
038: * This utility class associates a <code>Subject</code> to the current
039: * <code>AccessControlContext</code>. When a <code>SecurityManager</code> is
040: * used, * the container will always associate the called thread with an
041: * AccessControlContext * containing only the principal of the requested
042: * Servlet/Filter.
043: *
044: * This class uses reflection to invoke the invoke methods.
045: *
046: * @author Jean-Francois Arcand
047: */
048:
049: public final class SecurityUtil {
050:
051: private final static int INIT = 0;
052: private final static int SERVICE = 1;
053: private final static int DOFILTER = 1;
054: private final static int DESTROY = 2;
055:
056: private final static String INIT_METHOD = "init";
057: private final static String DOFILTER_METHOD = "doFilter";
058: private final static String SERVICE_METHOD = "service";
059: private final static String DESTROY_METHOD = "destroy";
060:
061: /**
062: * Cache every object for which we are creating method on it.
063: */
064: private static HashMap objectCache = new HashMap();
065:
066: private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
067: .getLog(SecurityUtil.class);
068:
069: private static String PACKAGE = "org.apache.catalina.security";
070:
071: /**
072: * The string resources for this package.
073: */
074: private static final StringManager sm = StringManager
075: .getManager(PACKAGE);
076:
077: /**
078: * Perform work as a particular </code>Subject</code>. Here the work
079: * will be granted to a <code>null</code> subject.
080: *
081: * @param methodName the method to apply the security restriction
082: * @param targetObject the <code>Servlet</code> on which the method will
083: * be called.
084: */
085: public static void doAsPrivilege(final String methodName,
086: final Servlet targetObject) throws java.lang.Exception {
087: doAsPrivilege(methodName, targetObject, null, null, null);
088: }
089:
090: /**
091: * Perform work as a particular </code>Subject</code>. Here the work
092: * will be granted to a <code>null</code> subject.
093: *
094: * @param methodName the method to apply the security restriction
095: * @param targetObject the <code>Servlet</code> on which the method will
096: * be called.
097: * @param targetType <code>Class</code> array used to instanciate a i
098: * <code>Method</code> object.
099: * @param targetArguments <code>Object</code> array contains the runtime
100: * parameters instance.
101: */
102: public static void doAsPrivilege(final String methodName,
103: final Servlet targetObject, final Class[] targetType,
104: final Object[] targetArguments) throws java.lang.Exception {
105:
106: doAsPrivilege(methodName, targetObject, targetType,
107: targetArguments, null);
108: }
109:
110: /**
111: * Perform work as a particular </code>Subject</code>. Here the work
112: * will be granted to a <code>null</code> subject.
113: *
114: * @param methodName the method to apply the security restriction
115: * @param targetObject the <code>Servlet</code> on which the method will
116: * be called.
117: * @param targetType <code>Class</code> array used to instanciate a
118: * <code>Method</code> object.
119: * @param targetArguments <code>Object</code> array contains the
120: * runtime parameters instance.
121: * @param principal the <code>Principal</code> to which the security
122: * privilege apply..
123: */
124: public static void doAsPrivilege(final String methodName,
125: final Servlet targetObject, final Class[] targetType,
126: final Object[] targetArguments, Principal principal)
127: throws java.lang.Exception {
128:
129: Method method = null;
130: Method[] methodsCache = null;
131: if (objectCache.containsKey(targetObject)) {
132: methodsCache = (Method[]) objectCache.get(targetObject);
133: method = findMethod(methodsCache, methodName);
134: if (method == null) {
135: method = createMethodAndCacheIt(methodsCache,
136: methodName, targetObject, targetType);
137: }
138: } else {
139: method = createMethodAndCacheIt(methodsCache, methodName,
140: targetObject, targetType);
141: }
142:
143: execute(method, targetObject, targetArguments, principal);
144: }
145:
146: /**
147: * Perform work as a particular </code>Subject</code>. Here the work
148: * will be granted to a <code>null</code> subject.
149: *
150: * @param methodName the method to apply the security restriction
151: * @param targetObject the <code>Filter</code> on which the method will
152: * be called.
153: */
154: public static void doAsPrivilege(final String methodName,
155: final Filter targetObject) throws java.lang.Exception {
156:
157: doAsPrivilege(methodName, targetObject, null, null);
158: }
159:
160: /**
161: * Perform work as a particular </code>Subject</code>. Here the work
162: * will be granted to a <code>null</code> subject.
163: *
164: * @param methodName the method to apply the security restriction
165: * @param targetObject the <code>Filter</code> on which the method will
166: * be called.
167: * @param targetType <code>Class</code> array used to instanciate a
168: * <code>Method</code> object.
169: * @param targetArguments <code>Object</code> array contains the
170: * runtime parameters instance.
171: */
172: public static void doAsPrivilege(final String methodName,
173: final Filter targetObject, final Class[] targetType,
174: final Object[] targetArguments) throws java.lang.Exception {
175: Method method = null;
176:
177: Method[] methodsCache = null;
178: if (objectCache.containsKey(targetObject)) {
179: methodsCache = (Method[]) objectCache.get(targetObject);
180: method = findMethod(methodsCache, methodName);
181: if (method == null) {
182: method = createMethodAndCacheIt(methodsCache,
183: methodName, targetObject, targetType);
184: }
185: } else {
186: method = createMethodAndCacheIt(methodsCache, methodName,
187: targetObject, targetType);
188: }
189:
190: execute(method, targetObject, targetArguments, null);
191: }
192:
193: /**
194: * Perform work as a particular </code>Subject</code>. Here the work
195: * will be granted to a <code>null</code> subject.
196: *
197: * @param methodName the method to apply the security restriction
198: * @param targetObject the <code>Servlet</code> on which the method will
199: * be called.
200: * @param targetArguments <code>Object</code> array contains the
201: * runtime parameters instance.
202: * @param principal the <code>Principal</code> to which the security
203: * privilege apply..
204: */
205: private static void execute(final Method method,
206: final Object targetObject, final Object[] targetArguments,
207: Principal principal) throws java.lang.Exception {
208:
209: try {
210: Subject subject = null;
211: PrivilegedExceptionAction pea = new PrivilegedExceptionAction() {
212: public Object run() throws Exception {
213: method.invoke(targetObject, targetArguments);
214: return null;
215: }
216: };
217:
218: // The first argument is always the request object
219: if (targetArguments != null
220: && targetArguments[0] instanceof HttpServletRequest) {
221: HttpServletRequest request = (HttpServletRequest) targetArguments[0];
222:
223: HttpSession session = request.getSession(false);
224: if (session != null) {
225: subject = (Subject) session
226: .getAttribute(Globals.SUBJECT_ATTR);
227: }
228:
229: if (subject == null) {
230: subject = new Subject();
231:
232: if (principal != null) {
233: subject.getPrincipals().add(principal);
234: }
235: }
236:
237: if (session != null)
238: session.setAttribute(Globals.SUBJECT_ATTR, subject);
239: }
240:
241: Subject.doAsPrivileged(subject, pea, null);
242: } catch (PrivilegedActionException pe) {
243: Throwable e = ((InvocationTargetException) pe
244: .getException()).getTargetException();
245:
246: if (log.isDebugEnabled()) {
247: log
248: .debug(
249: sm
250: .getString("SecurityUtil.doAsPrivilege"),
251: e);
252: }
253:
254: if (e instanceof UnavailableException)
255: throw (UnavailableException) e;
256: else if (e instanceof ServletException)
257: throw (ServletException) e;
258: else if (e instanceof IOException)
259: throw (IOException) e;
260: else if (e instanceof RuntimeException)
261: throw (RuntimeException) e;
262: else
263: throw new ServletException(e.getMessage(), e);
264: }
265: }
266:
267: /**
268: * Find a method stored within the cache.
269: * @param methodsCache the cache used to store method instance
270: * @param methodName the method to apply the security restriction
271: * @return the method instance, null if not yet created.
272: */
273: private static Method findMethod(Method[] methodsCache,
274: String methodName) {
275: if (methodName.equalsIgnoreCase(INIT_METHOD)
276: && methodsCache[INIT] != null) {
277: return methodsCache[INIT];
278: } else if (methodName.equalsIgnoreCase(DESTROY_METHOD)
279: && methodsCache[DESTROY] != null) {
280: return methodsCache[DESTROY];
281: } else if (methodName.equalsIgnoreCase(SERVICE_METHOD)
282: && methodsCache[SERVICE] != null) {
283: return methodsCache[SERVICE];
284: } else if (methodName.equalsIgnoreCase(DOFILTER_METHOD)
285: && methodsCache[DOFILTER] != null) {
286: return methodsCache[DOFILTER];
287: }
288: return null;
289: }
290:
291: /**
292: * Create the method and cache it for further re-use.
293: * @param methodsCache the cache used to store method instance
294: * @param methodName the method to apply the security restriction
295: * @param targetObject the <code>Servlet</code> on which the method will
296: * be called.
297: * @param targetType <code>Class</code> array used to instanciate a
298: * <code>Method</code> object.
299: * @return the method instance.
300: */
301: private static Method createMethodAndCacheIt(Method[] methodsCache,
302: String methodName, Object targetObject, Class[] targetType)
303: throws Exception {
304:
305: if (methodsCache == null) {
306: methodsCache = new Method[3];
307: }
308:
309: Method method = targetObject.getClass().getMethod(methodName,
310: targetType);
311:
312: if (methodName.equalsIgnoreCase(INIT_METHOD)) {
313: methodsCache[INIT] = method;
314: } else if (methodName.equalsIgnoreCase(DESTROY_METHOD)) {
315: methodsCache[DESTROY] = method;
316: } else if (methodName.equalsIgnoreCase(SERVICE_METHOD)) {
317: methodsCache[SERVICE] = method;
318: } else if (methodName.equalsIgnoreCase(DOFILTER_METHOD)) {
319: methodsCache[DOFILTER] = method;
320: }
321:
322: objectCache.put(targetObject, methodsCache);
323:
324: return method;
325: }
326:
327: /**
328: * Remove the object from the cache.
329: *
330: * @param cachedObject The object to remove
331: */
332: public static void remove(Object cachedObject) {
333: objectCache.remove(cachedObject);
334: }
335: }
|