001: /*
002: * ====================================================================
003: * JAFFA - Java Application Framework For All
004: *
005: * Copyright (C) 2002 JAFFA Development Group
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: *
021: * Redistribution and use of this software and associated documentation ("Software"),
022: * with or without modification, are permitted provided that the following conditions are met:
023: * 1. Redistributions of source code must retain copyright statements and notices.
024: * Redistributions must also contain a copy of this document.
025: * 2. Redistributions in binary form must reproduce the above copyright notice,
026: * this list of conditions and the following disclaimer in the documentation
027: * and/or other materials provided with the distribution.
028: * 3. The name "JAFFA" must not be used to endorse or promote products derived from
029: * this Software without prior written permission. For written permission,
030: * please contact mail to: jaffagroup@yahoo.com.
031: * 4. Products derived from this Software may not be called "JAFFA" nor may "JAFFA"
032: * appear in their names without prior written permission.
033: * 5. Due credit should be given to the JAFFA Project (http://jaffa.sourceforge.net).
034: *
035: * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: */
049:
050: /* Generated by Together */
051:
052: package org.jaffa.security;
053:
054: import org.apache.log4j.Logger;
055: import java.lang.reflect.Method;
056: import javax.servlet.http.HttpServletRequest;
057: import javax.ejb.EJBContext;
058: import java.security.PrivilegedAction;
059: import java.security.PrivilegedExceptionAction;
060: import java.security.PrivilegedActionException;
061: import java.security.AccessControlException;
062: import java.lang.SecurityException;
063: import java.security.Principal;
064:
065: /** Security Manager is the main interface to the BusinessFunction and Component Security Architecture.
066: * It provide a mechanism for setting the security context for a thread of execution, and then
067: * provide a guard for securing code in that thread. The access to the
068: * guarded code is derived from a role based security policy file.
069: *
070: */
071: public class SecurityManager {
072:
073: /** Set up Logging for Log4J */
074: private static Logger log = Logger.getLogger(SecurityManager.class);
075:
076: // ------------------------------------------------------------------------
077: // Public Methods
078: // ------------------------------------------------------------------------
079:
080: /** Bind a security context to a thread and contine executing the thread by running the
081: * supplied method against the specified object with the supplied paramters.
082: * In this case the security context is derived from a HttpServletRequest.
083: * @param ctx Web Server Request Context to use
084: * @param obj The object contains the method to execute under the thread security context
085: * @param method The name of the method to execute in the specified object
086: * @param args An Object array of argument to pass to the method. If there are no parameters for the method null can be passed
087: * @throws Exception Returns any Exception that the method being invoked may return
088: * @return Returns any Object that the method being invoked can return
089: */
090: public static Object runWithContext(HttpServletRequest ctx,
091: Object obj, String method, Object[] args) throws Exception {
092:
093: return runWithContext(new SecurityContext(ctx), obj, method,
094: args);
095: }
096:
097: /** Bind a security context to a thread and contine executing the thread by running the
098: * supplied method against the specified object with the supplied paramters.
099: * In this case the security context is derived from a HttpServletRequest.
100: * @return Returns any Object that the method being invoked can return
101: * @param sig This is an array of classes that represent the signature to the supplied method. This will be used for introspection for
102: * the supplied method on the given object
103: * @param ctx Web Server Request Context to use
104: * @param obj The object contains the method to execute under the thread security context
105: * @param method The name of the method to execute in the specified object
106: * @param args An Object array of argument to pass to the method. If there are no parameters for the method null can be passed
107: * @throws Exception Returns any Exception that the method being invoked may return
108: */
109: public static Object runWithContext(HttpServletRequest ctx,
110: Object obj, String method, Object[] args, Class[] sig)
111: throws Exception {
112:
113: return runWithContext(new SecurityContext(ctx), obj, method,
114: args, sig);
115: }
116:
117: /** Bind a security context to a thread and contine executing the thread by running the
118: * supplied method against the specified object with the supplied paramters. The signature of
119: * the method is introspected used the classes associated to the objects in the parameter array
120: * If these classes are not able to specified the methods signiture, use the variation of this method
121: * that allows the class[] singature to be supplied.
122: * In this case the security context is derived from a HttpServletRequest.
123: * @return Returns any Object that the method being invoked can return
124: * the supplied method on the given object
125: * @param ctx Web Server Request Context to use
126: * @param obj The object contains the method to execute under the thread security context
127: * @param method The name of the method to execute in the specified object
128: * @param args An Object array of argument to pass to the method. If there are no parameters for the method null can be passed
129: * @throws Exception Returns any Exception that the method being invoked may return
130: */
131: public static Object runWithContext(HttpServletRequest ctx,
132: Object obj, Method method, Object[] args) throws Exception {
133:
134: return runWithContext(new SecurityContext(ctx), obj, method,
135: args);
136: }
137:
138: /** Bind a security context to a thread and contine executing the thread by running the
139: * supplied method against the specified object with the supplied paramters.
140: * In this case the security context is derived from an EJBContext.
141: * @param ctx Web Server Request Context to use
142: * @param obj The object contains the method to execute under the thread security context
143: * @param method The name of the method to execute in the specified object
144: * @param args An Object array of argument to pass to the method. If there are no parameters for the method null can be passed
145: * @throws Exception Returns any Exception that the method being invoked may return
146: * @return Returns any Object that the method being invoked can return
147: */
148: public static Object runWithContext(EJBContext ctx, Object obj,
149: String method, Object[] args) throws Exception {
150:
151: return runWithContext(new SecurityContext(ctx), obj, method,
152: args);
153: }
154:
155: /** Bind a security context to a thread and contine executing the thread by running the
156: * supplied method against the specified object with the supplied paramters.
157: * In this case the security context is derived from an EJBContext.
158: * @return Returns any Object that the method being invoked can return
159: * @param sig This is an array of classes that represent the signature to the supplied method. This will be used for introspection for
160: * the supplied method on the given object
161: * @param ctx Web Server Request Context to use
162: * @param obj The object contains the method to execute under the thread security context
163: * @param method The name of the method to execute in the specified object
164: * @param args An Object array of argument to pass to the method. If there are no parameters for the method null can be passed
165: * @throws Exception Returns any Exception that the method being invoked may return
166: */
167: public static Object runWithContext(EJBContext ctx, Object obj,
168: String method, Object[] args, Class[] sig) throws Exception {
169:
170: return runWithContext(new SecurityContext(ctx), obj, method,
171: args, sig);
172: }
173:
174: /** Bind a security context to a thread and contine executing the thread by running the
175: * supplied method against the specified object with the supplied paramters. The signature of
176: * the method is introspected used the classes associated to the objects in the parameter array
177: * If these classes are not able to specified the methods signiture, use the variation of this method
178: * that allows the class[] singature to be supplied.
179: * In this case the security context is derived from an EJBContext.
180: * @return Returns any Object that the method being invoked can return
181: * the supplied method on the given object
182: * @param ctx Web Server Request Context to use
183: * @param obj The object contains the method to execute under the thread security context
184: * @param method The name of the method to execute in the specified object
185: * @param args An Object array of argument to pass to the method. If there are no parameters for the method null can be passed
186: * @throws Exception Returns any Exception that the method being invoked may return
187: */
188: public static Object runWithContext(EJBContext ctx, Object obj,
189: Method method, Object[] args) throws Exception {
190:
191: return runWithContext(new SecurityContext(ctx), obj, method,
192: args);
193: }
194:
195: /** Run the guarded business function, only if the current thread has access
196: * @param functionName Name of the business function being guarded
197: * @param action An action object which will be executed, this should contain the guarded code
198: * @throws AccessControlException This is thrown if the user doesn't have authorization for this function
199: * @return Returns back the object that the guarded code returned
200: */
201: public static Object runFunction(String functionName,
202: PrivilegedAction action) throws AccessControlException {
203:
204: if (hasAccess(functionName))
205: return action.run();
206: else {
207: // Access Defined
208: log.info("Access Denied To Business Function: "
209: + functionName);
210: throw new AccessControlException("Business Function:"
211: + functionName);
212: }
213: }
214:
215: /** Run the guarded business function, only if the current thread has access.
216: * This guarded function may throw a PrivilegedActionException which will contain
217: * the real exception
218: * @return Returns back the object that the guarded code returned
219: * @param functionName Name of the business function being guarded
220: * @param action An action object which will be executed, this should contain the guarded code
221: * @throws PrivilegedActionException This is the wrapped exception the the guarded code threw
222: * @throws AccessControlException This is thrown if the user doesn't have authorization for this function
223: */
224: public static Object runFunction(String functionName,
225: PrivilegedExceptionAction action)
226: throws PrivilegedActionException, AccessControlException {
227:
228: if (hasAccess(functionName))
229: try {
230: return action.run();
231: } catch (Exception e) {
232: throw new PrivilegedActionException(e);
233: }
234: else {
235: // Access Defined
236: log.info("Access Denied To Business Function: "
237: + functionName);
238: throw new AccessControlException("Business Function:"
239: + functionName);
240: }
241: }
242:
243: /** See if the current thread has access to the named component.
244: * This can be used by a Component Manager to preempt a security violation
245: * @param componentName Name of component to check
246: * @return true, if the current thread has access to this component, otherwise false is returned
247: */
248: public static boolean checkComponentAccess(String componentName) {
249: return hasComponentAccess(componentName, null);
250: }
251:
252: /** See if the current thread has access to the named business function.
253: * @param functionName Name of business function to check
254: * @return true, if the current thread has access to this business function, otherwise false is returned
255: */
256: public static boolean checkFunctionAccess(String functionName) {
257: return hasAccess(functionName, null);
258: }
259:
260: /** Get the Security Prinipal Object for the Current User. If this is called
261: * 'outsite' or the Jaffa framework it will return null. Typically jaffa security
262: * is backed by either Web Container or EJB Container security, and this will
263: * return Principle as created by the Web/EJB container that is associated to
264: * the thread of execution calling this method
265: * <p>
266: * In a typical web environment <code>SecurityManager.getPrincipal().getName()</code>
267: * will return you the username used to log on.
268: * <p>
269: * @return The security principal associated to the current thread
270: */
271: public static Principal getPrincipal() {
272: SecurityContext ctx = getCurrentContext();
273: // No context, no access!!!
274: if (ctx == null)
275: return null;
276: else
277: return ctx.getPrincipal();
278: }
279:
280: // ------------------------------------------------------------------------
281: // Package Access Methods
282: // ------------------------------------------------------------------------
283:
284: /** Add this context to the current thread
285: */
286: static void bindToThread(SecurityContext ctx) {
287: ((SecurityContextStack) stack.get()).push(ctx);
288: }
289:
290: /** Remove the current context from the current thread
291: */
292: static void unbindFromThread() {
293: ((SecurityContextStack) stack.get()).pop();
294: }
295:
296: /** Return the current security context for this thread
297: */
298: static SecurityContext getCurrentContext() {
299: return ((SecurityContextStack) stack.get()).getContext();
300: }
301:
302: /** See if the give context has access to a function
303: * Use by the tag libraries, doesn't require the context to be bound to the thread
304: */
305: static boolean checkFunctionAccess(String functionName,
306: SecurityContext ctx) throws SecurityException {
307: if (ctx == null)
308: throw new SecurityException(
309: "No Context Supplied For Security Check");
310:
311: return hasAccess(functionName, ctx);
312: }
313:
314: /** See if the give context has access to a component
315: * Use by the tag libraries, doesn't require the context to be bound to the thread
316: */
317: static boolean checkComponentAccess(String componentName,
318: SecurityContext ctx) throws SecurityException {
319: if (ctx == null)
320: throw new SecurityException(
321: "No Context Supplied For Security Check");
322:
323: return hasComponentAccess(componentName, ctx);
324: }
325:
326: // ------------------------------------------------------------------------
327: // Private Methods
328: // ------------------------------------------------------------------------
329:
330: /** Bind a security context to a thread and contine executing the thread by running the
331: * supplied method against the specified object with the supplied paramters.
332: */
333: private static Object runWithContext(SecurityContext ctx,
334: Object obj, String method, Object[] args) throws Exception {
335: // Try to look up the method from the name.
336: // First build a method signature based on the arument list
337: Class[] sig = null;
338: if (args == null || args.length == 0) {
339: sig = new Class[] {};
340: // Create empty args for later use
341: args = new Object[] {};
342: } else {
343: if (log.isDebugEnabled())
344: log.debug("Building Method Signature");
345: sig = new Class[args.length];
346: for (int i = 0; i < args.length; i++) {
347: sig[i] = args[i].getClass();
348: if (log.isDebugEnabled())
349: log.debug("Parm " + i + " is class "
350: + sig[i].getName());
351: }
352: }
353:
354: // Now look up method
355: Method m = null;
356: try {
357: m = obj.getClass().getMethod(method, sig);
358: } catch (SecurityException e) {
359: log.error("No Access To Introspect For Method " + method
360: + " on Class " + obj.getClass(), e);
361: throw e;
362: } catch (NoSuchMethodException e) {
363: log.error("No Such Method " + method + " on Class "
364: + obj.getClass(), e);
365: throw e;
366: }
367:
368: // Now run this method with context
369: return runWithContext(ctx, obj, m, args);
370: }
371:
372: /** Bind a security context to a thread and contine executing the thread by running the
373: * supplied method against the specified object with the supplied paramters.
374: */
375: private static Object runWithContext(SecurityContext ctx,
376: Object obj, String method, Object[] args, Class[] sig)
377: throws Exception {
378: if (sig == null)
379: sig = new Class[] {};
380: if (args == null)
381: args = new Object[] {};
382: if (sig.length != args.length)
383: throw new RuntimeException(
384: "Parameter List and Signature List are different sizes!");
385:
386: // Now look up method
387: Method m = null;
388: try {
389: m = obj.getClass().getMethod(method, sig);
390: } catch (SecurityException e) {
391: log.error("No Access To Introspect For Method " + method
392: + " on Class " + obj.getClass(), e);
393: throw e;
394: } catch (NoSuchMethodException e) {
395: log.error("No Such Method " + method + " on Class "
396: + obj.getClass(), e);
397: throw e;
398: }
399:
400: // Now run this method with context
401: return runWithContext(ctx, obj, m, args);
402: }
403:
404: /** Bind a security context to a thread and contine executing the thread by running the
405: * supplied method against the specified object with the supplied paramters.
406: */
407: private static Object runWithContext(SecurityContext ctx,
408: Object obj, Method method, Object[] args) throws Exception {
409:
410: // Attach the security context to the thread
411: bindToThread(ctx);
412: try {
413: // Now invoke the method
414: return method.invoke(obj, args);
415: } finally {
416: // As the last thing to do before returning either an object or an exception
417: // Remove the current context from the thread
418: unbindFromThread();
419: }
420: }
421:
422: /** Create a ThreadLocal variable for storing the SecurityContextStack
423: */
424: private static class ThreadLocalContextStack extends ThreadLocal {
425: /** Set the thread local variable to hold a new security context stack
426: * @return this doesn't return anything
427: */
428: public Object initialValue() {
429: return new SecurityContextStack();
430: }
431: }
432:
433: /** Make an instance of the SecurityContextStack For each new thread as they
434: * go through the security manager
435: */
436: private static ThreadLocalContextStack stack = new ThreadLocalContextStack();
437:
438: /** Check the Policy and the Current SecurityContext to see if access to this function
439: * should be granted.
440: */
441: private static boolean hasAccess(String functionName) {
442: return hasAccess(functionName, null);
443: }
444:
445: /** Check the Policy and the Current SecurityContext to see if access to this function
446: * should be granted.
447: *
448: * If no security context is supplied (i.e it is null) it uses the context of the
449: * current thread
450: */
451: private static boolean hasAccess(String functionName,
452: SecurityContext ctx) {
453:
454: // Get the list of roles which grant access to this function
455: String[] roles = PolicyManager
456: .getRolesForFunction(functionName);
457: if (roles == null) {
458: if (log.isDebugEnabled())
459: log.debug("No Roles have access to function "
460: + functionName);
461: return false;
462: }
463:
464: // Get current context
465: if (ctx == null)
466: ctx = getCurrentContext();
467:
468: // No context, no access!!!
469: if (ctx == null || ctx.getPrincipal() == null) {
470: if (log.isDebugEnabled())
471: log
472: .debug("hasAccess(): No Security Context, therefore No Access To Function : "
473: + functionName);
474: return false;
475: }
476:
477: if (log.isDebugEnabled())
478: log.debug("Checking Access To Business Function : "
479: + functionName + " for User "
480: + ctx.getPrincipal().getName());
481:
482: // Loop through and see if context has any role that grants access...
483: for (int i = 0; i < roles.length; i++) {
484: if (ctx.inRole(roles[i]))
485: // The user has a role that grants access to this function...
486: return true;
487: else if (log.isDebugEnabled())
488: log.debug("User " + ctx.getPrincipal().getName()
489: + " Has No Access To Role : " + roles[i]);
490: }
491: // All roles checked, no goive access!
492: return false;
493: }
494:
495: /** Check the Policy and the Current SecurityContext to see if access to this component
496: * should be granted.
497: *
498: * If no security context is supplied (i.e it is null) it uses the context of the
499: * current thread.
500: */
501: private static boolean hasComponentAccess(String componentName,
502: SecurityContext ctx) {
503:
504: // Get the list of roles which grant access to this component
505: String[] roles = PolicyManager
506: .getRolesForComponent(componentName);
507: // If all roles have access, null will have been returned
508: if (roles == null)
509: return true;
510: // If no roles have access, an empty list will have been returned
511: if (roles.length == 0) {
512: if (log.isDebugEnabled())
513: log.debug("No Roles have access to component "
514: + componentName);
515: return false;
516: }
517:
518: // Get current context
519: if (ctx == null)
520: ctx = getCurrentContext();
521:
522: // No context, no access!!!
523: if (ctx == null || ctx.getPrincipal() == null) {
524: if (log.isDebugEnabled())
525: log
526: .debug("hasAccess(): No Security Context, therefore No Access To Component : "
527: + componentName);
528: return false;
529: }
530:
531: if (log.isDebugEnabled())
532: log.debug("Checking Access To Component : " + componentName
533: + " for User " + ctx.getPrincipal().getName());
534:
535: // Loop through and see if context has any role that grants access...
536: for (int i = 0; i < roles.length; i++) {
537: if (ctx.inRole(roles[i]))
538: // The user has a role that grans access to this function...
539: return true;
540: else if (log.isDebugEnabled())
541: log.debug("User " + ctx.getPrincipal().getName()
542: + " Has No Access To Role : " + roles[i]);
543: }
544: // All roles checked, no goive access!
545: return false;
546: }
547: }
|