001    package org.trails.hibernate;
002    
003    import java.io.Serializable;
004    
005    import org.acegisecurity.GrantedAuthority;
006    import org.acegisecurity.context.SecurityContext;
007    import org.acegisecurity.context.SecurityContextHolder;
008    import org.hibernate.criterion.DetachedCriteria;
009    import org.hibernate.criterion.Restrictions;
010    import org.hibernate.criterion.SimpleExpression;
011    import org.trails.security.EntityModificationInterception;
012    import org.trails.security.annotation.ViewRequiresAssociation;
013    import org.trails.security.annotation.ViewRequiresRole;
014    
015    public class SecurePersistenceServiceImpl extends HibernatePersistenceServiceImpl {
016            @Override
017            protected DetachedCriteria alterCriteria(Class type, DetachedCriteria criteria) {
018                    SecurityContext context = SecurityContextHolder.getContext();
019                    // Assume that context should have been established for each request, and if it's not,
020                    // it's an internal service call 
021                    if (context == null || context.getAuthentication() == null) return criteria;
022    
023                    // Check first if user has permission granted by a role 
024                    ViewRequiresRole viewRoleRestriction = (ViewRequiresRole)type.getAnnotation(ViewRequiresRole.class );
025                    if (viewRoleRestriction != null) for (GrantedAuthority authority : context.getAuthentication().getAuthorities())
026                            for (String role : viewRoleRestriction.value()) if (authority.getAuthority().equals(role) ) return criteria;
027    
028                    ViewRequiresAssociation viewRestriction = (ViewRequiresAssociation)type.getAnnotation(ViewRequiresAssociation.class );
029                    
030                    if (viewRoleRestriction == null && viewRestriction == null) return criteria;
031                    
032                    if (viewRestriction == null) {
033                            // At this point we know the user should have no access to the entities, because there's
034                            // a role restriction but user doesn't have a suitable role and there's no association
035                            // We can throw an exception here, but it wouldn't be consistent with how assiative restriction
036                            // is handled (just returns a query with no results) and would change the semantics
037                            // throw new EntitySecurityException(null, "No suitable role or association");
038                            
039                            // We should do:
040                            //criteria.setMaxResults(0);
041                            // but DetachedCriteria doesn't support setMaxResults() 
042                            // http://opensource.atlassian.com/projects/hibernate/browse/HHH-912
043                            // Ugly HACK instead
044                            return criteria.add(Restrictions.idEq(null) );
045                    }
046                    
047                    String currentUsername = context.getAuthentication().getName();
048                    String ownerPropertyAssociation = viewRestriction.value();
049                    // username as in Acegi UserDetails
050                    SimpleExpression usernameRestriction = Restrictions.eq("username",currentUsername);
051                    if ("".equals(ownerPropertyAssociation)) criteria.add(usernameRestriction);
052                    else criteria.createCriteria(ownerPropertyAssociation).add(usernameRestriction);                        
053                    return criteria;
054            }
055    
056            public <T> T loadInstance(final Class<T> type, Serializable id)
057            {
058                    return getInstance(type, id);
059            }
060            
061    }