001 package org.trails.security;
002
003 import java.util.ArrayList;
004 import java.util.HashMap;
005 import java.util.List;
006 import java.util.Map;
007
008 import org.acegisecurity.context.SecurityContext;
009 import org.acegisecurity.context.SecurityContextHolder;
010 import org.acegisecurity.ui.session.HttpSessionApplicationEvent;
011 import org.acegisecurity.ui.session.HttpSessionCreatedEvent;
012 import org.acegisecurity.ui.session.HttpSessionDestroyedEvent;
013 import org.apache.commons.logging.Log;
014 import org.apache.commons.logging.LogFactory;
015 import org.aspectj.lang.ProceedingJoinPoint;
016 import org.aspectj.lang.annotation.Around;
017 import org.aspectj.lang.annotation.Aspect;
018 import org.springframework.context.ApplicationEvent;
019 import org.springframework.context.ApplicationListener;
020 import org.trails.descriptor.IClassDescriptor;
021 import org.trails.descriptor.TrailsClassDescriptor;
022
023 @Aspect
024 public class DescriptorSecurity implements ApplicationListener
025 {
026 private static final Log log = LogFactory.getLog(DescriptorSecurity.class);
027 // Only used to log a warning each time an element is added to cache when no session events have been published
028 private static boolean sessionCreationDetected = false;
029
030 private SecurityService securityService;
031 Map<String, Map<String,IClassDescriptor>> perUserClassDescriptorCache = new HashMap<String, Map<String,IClassDescriptor>>();
032
033 @Around(
034 "execution(public org.trails.descriptor.IClassDescriptor org.trails.descriptor.DescriptorService+.getClassDescriptor(Class))")
035 public Object classDescriptorSecurity(ProceedingJoinPoint pjp) throws Throwable
036 {
037 IClassDescriptor desc = (IClassDescriptor) pjp.proceed();
038 if (desc != null) {
039 SecurityContext context = SecurityContextHolder.getContext();
040 if (context == null || context.getAuthentication() == null) return desc;
041 return applyRestrictions(desc, context);
042 }
043 else return null;
044 }
045
046 @Around("execution(public java.util.List org.trails.descriptor.DescriptorService+.getAllDescriptors())")
047 public Object getAllClassDescriptorSecurity(ProceedingJoinPoint pjp) throws Throwable
048 {
049 List<IClassDescriptor> descriptors = (List<IClassDescriptor>) pjp.proceed();
050 if (descriptors == null) return null;
051
052 SecurityContext context = SecurityContextHolder.getContext();
053 if (context == null || context.getAuthentication() == null) return descriptors;
054
055 List<IClassDescriptor> cloneDescriptors = new ArrayList<IClassDescriptor>(descriptors.size());
056 for (IClassDescriptor descriptor : descriptors) cloneDescriptors.add(applyRestrictions(descriptor, context) );
057 return cloneDescriptors;
058 }
059
060 protected IClassDescriptor applyRestrictions(IClassDescriptor descriptor, SecurityContext context)
061 {
062 // Return a clone specific to this user rather than modify the shared original
063 // See if the class descriptor for this user is already cached, otherwise cache
064 Map<String,IClassDescriptor> cachedDescriptors = perUserClassDescriptorCache.get(context.getAuthentication().getName());
065 IClassDescriptor cloneDescriptor = null;
066 if (cachedDescriptors != null) cloneDescriptor = cachedDescriptors.get(descriptor.getType().getSimpleName());
067 else {
068 cachedDescriptors = new HashMap<String,IClassDescriptor>();
069 perUserClassDescriptorCache.put(context.getAuthentication().getName(), cachedDescriptors);
070 if (!sessionCreationDetected) log.warn("This implementation caches security-enhanced class descriptors for each user\n "
071 + "but no session events are detected. Descriptors for expired sessions cannot be removed from the cache\n"
072 + "Check that you have configured <listener-class>org.acegisecurity.ui.session.HttpSessionEventPublisher</listener-class>?\n");
073 }
074
075 if (cloneDescriptor != null) return cloneDescriptor;
076
077 cloneDescriptor = new TrailsClassDescriptor(descriptor);
078 cachedDescriptors.put(descriptor.getType().getSimpleName(), cloneDescriptor);
079
080 List<SecurityRestriction> restrictions = securityService.findRestrictions(descriptor);
081 if (restrictions != null)
082 {
083 for (SecurityRestriction restriction : restrictions)
084 {
085 restriction.restrict(context.getAuthentication().getAuthorities(), cloneDescriptor);
086 }
087 }
088 return cloneDescriptor;
089 }
090
091 public void setSecurityService(SecurityService securityService)
092 {
093 this.securityService = securityService;
094 }
095
096 public void onApplicationEvent(ApplicationEvent event) {
097 if (!(event instanceof HttpSessionApplicationEvent)) return;
098
099 if (event instanceof HttpSessionDestroyedEvent) {
100 SecurityContext context = SecurityContextHolder.getContext();
101 // Do nothing if security context wasn't available
102 if (context == null || context.getAuthentication() == null) return;
103 Object object = perUserClassDescriptorCache.remove(context.getAuthentication().getName());
104 if (object != null && log.isDebugEnabled()) log.debug("Removing cached descriptors for user " + context.getAuthentication().getName());
105 }
106 else if (event instanceof HttpSessionCreatedEvent) sessionCreationDetected = true;
107 }
108 }