Announcement Announcement Module
Collapse
No announcement yet.
Dynamic Authorization Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • #16
    Hi Ben,
    Why are you even writing a database-driven MethodDefinitionSource? I can't see a strong need for database metadata for method invocations - unlike web URIs where I can see a lot of potential value in some applications.
    From my point of view , URIs interception is coarse grained security and method invocation is fine grained.
    Once you use URIs interception , you have to categorize the similar functions to a page , each page look like a function's (method) category , sometime you can do this , but sometime you cannot because of the cross boundary business logic , so method blocking is a way out.

    l do think that database-driven MethodDefinitionSource is a normal need. AspectJ in action use a whole chapter (Chapter 10) to discuss Authentication and authorization , so let's see some code snipplet in pg 339 ,
    Code:
    public void credit(float amount) {
    
       AccessController.checkPermission(new BankingPermission("credit"));
    ...
    }
    public void debit(float amount)
         throws InsufficientBalanceException {
       AccessController.checkPermission(new BankingPermission("debit"));
    ...
    }
    this is a typical JAAS sample code , AccessController.checkPermission(..) look like a "method interception" for me , and this is a need in JAAS , . Further , the view no. of this thread show a lot people interested in dynamic authorization.

    Back to the question of database-driven MethodDefinitionSource , l think it is nice to have this feature support in acegi security , but how to do it ?

    l think there are some criteria for the implemention . let's assume that l have 3 tables,

    Users <--M:M--> Roles <--M:M--> AccessMethods

    Criteria :

    1. the whole relationship (Roles and AccessMethods) must be retrieved from the database to form the (method = ROLE_1,ROLE_2,...) pairs once the application start , that means l have the whole authorization table from the start.

    2. we then caching authorization table after that.

    3. if some of the relationship change , the authorization table have to be refresh , and cache it agian.

    But , it seem to me that , neither MethodDefinitionMap nor MethodDefinitionAttributes provide support for this .

    moon

    Comment


    • #17
      l have make some code , but l don't know whether it is "save" or not , so comments is needed ,

      DBDrivenMethodDefinitionSource.java
      Code:
      package org.acegi.intercept.method;
      
      import java.lang.reflect.Method;
      import java.util.ArrayList;
      import java.util.HashMap;
      import java.util.Iterator;
      import java.util.List;
      import java.util.Map;
      
      import org.acegi.providers.dao.MethodMapCache;
      import org.acegi.providers.dao.cache.NullMethodMapCache;
      import org.acegisecurity.ConfigAttribute;
      import org.acegisecurity.ConfigAttributeEditor;
      import org.acegisecurity.ConfigAttributeDefinition;
      import org.acegisecurity.intercept.method.AbstractMethodDefinitionSource;
      import org.apache.commons.logging.Log;
      import org.apache.commons.logging.LogFactory;
      import org.yourschool.library.domain.AccessMethod;
      import org.yourschool.library.domain.Role;
      import org.yourschool.library.domain.logic.SecurityFacade;
      
      public class  DBDrivenMethodDefinitionSource extends AbstractMethodDefinitionSource {
      	
          private static final Log logger = LogFactory.getLog(DBDrivenMethodDefinitionSource.class);
          
          private static final String METHOD_MAP = "methodMap";
          private Map methodMap;
          private Map nameMap;
          private SecurityFacade securityManager;
          private MethodMapCache methodMapCache = new NullMethodMapCache();
        	
      	public SecurityFacade getSecurityManager() {
      		return securityManager;
      	}
      
      	public void setSecurityManager(SecurityFacade securityManager) {
      		this.securityManager = securityManager;
      	}
      
      	public MethodMapCache getMethodMapCache() {
      		return methodMapCache;
      	}
      
      	public void setMethodMapCache(MethodMapCache methodMapCache) {
      		this.methodMapCache = methodMapCache;
      	}
      
      	public void buildMethodMapFromDB(){
      		
      		nameMap = new HashMap();
      		methodMap = new HashMap();
      
      		List accessMethods = (List) this.securityManager.loadAllAccessMethod();
      
      		ConfigAttributeEditor configAttrEditor = new ConfigAttributeEditor();
      
      		for(int i = 0 ; i < accessMethods.size() ; i++){
      
      			AccessMethod accessMethod = (AccessMethod)accessMethods.get(i);
      			Iterator it = accessMethod.getRoles().iterator();
      
      			StringBuffer rolesStr = new StringBuffer();
      			
      			while(it.hasNext()) {
      				Role role = (Role)it.next();
      				rolesStr.append(role.getRoleName()).append(",");
      			}
      			
      			String accessMethodName = accessMethod.getAccessMethodName();
      			
      			// System.out.println(rolesStr.toString().substring(0,rolesStr.length()-1));
      			configAttrEditor.setAsText(rolesStr.toString().substring(0,rolesStr.length()-1));
      			ConfigAttributeDefinition attr = 
      				(ConfigAttributeDefinition)configAttrEditor.getValue();
      			
      			// Register name and attribute
                  addSecureMethod(accessMethodName, attr);
                  
      		}
      		// put it into cache
      		this.methodMapCache.putMethodMapInCache(METHOD_MAP,this.methodMap);
      		
      	}
      
          public Iterator getConfigAttributeDefinitions() {
      
          	Map mMap = this.methodMapCache.getMethodMapFromCache(METHOD_MAP);
          	if(mMap != null)
          		return mMap.values().iterator();
              return null;
      
          }
      
          public int getMethodMapSize() {
       	
          	Map mMap = this.methodMapCache.getMethodMapFromCache(METHOD_MAP);
          	if(mMap != null)
          		return mMap.size();
              return 0;
       
          }
      
          private void addSecureMethod(Method method, ConfigAttributeDefinition attr) {
              logger.info("Adding secure method [" + method + "] with attributes ["
                      + attr + "]");
              this.methodMap.put(method, attr);
          }
      
          private void addSecureMethod(String name, ConfigAttributeDefinition attr) {
              int lastDotIndex = name.lastIndexOf(".");
      
              if (lastDotIndex == -1) {
                  throw new IllegalArgumentException("'" + name
                      + "' is not a valid method name: format is FQN.methodName");
              }
      
              String className = name.substring(0, lastDotIndex);
              String methodName = name.substring(lastDotIndex + 1);
      
              try {
                  Class clazz = Class.forName(className, true,
                          Thread.currentThread().getContextClassLoader());
                  addSecureMethod(clazz, methodName, attr);
              } catch (ClassNotFoundException ex) {
                  throw new IllegalArgumentException("Class '" + className
                      + "' not found");
              }
          }
      
          private void addSecureMethod(Class clazz, String mappedName,
              ConfigAttributeDefinition attr) {
              String name = clazz.getName() + '.' + mappedName;
      
              if (logger.isDebugEnabled()) {
                  logger.debug("Adding secure method [" + name
                      + "] with attributes [" + attr + "]");
              }
      
              Method[] methods = clazz.getDeclaredMethods();
              List matchingMethods = new ArrayList();
      
              for (int i = 0; i < methods.length; i++) {
                  if (methods[i].getName().equals(mappedName)
                      || isMatch(methods[i].getName(), mappedName)) {
                      matchingMethods.add(methods[i]);
                  }
              }
      
              if (matchingMethods.isEmpty()) {
                  throw new IllegalArgumentException("Couldn't find method '"
                      + mappedName + "' on " + clazz);
              }
      
              // register all matching methods
              for (Iterator it = matchingMethods.iterator(); it.hasNext();) {
                  Method method = (Method) it.next();
                  String regMethodName = (String) this.nameMap.get(method);
      
                  if ((regMethodName == null)
                      || (!regMethodName.equals(name)
                      && (regMethodName.length() <= name.length()))) {
                      // no already registered method name, or more specific
                      // method name specification now -> (re-)register method
                      if (regMethodName != null) {
                          logger.debug("Replacing attributes for secure method ["
                              + method + "]: current name [" + name
                              + "] is more specific than [" + regMethodName + "]");
                      }
      
                      this.nameMap.put(method, name);
                      addSecureMethod(method, attr);
                  } else {
                      logger.debug("Keeping attributes for secure method [" + method
                          + "]: current name [" + name
                          + "] is not more specific than [" + regMethodName + "]");
                  }
              }
          }
      
          private void checkMethodMap(){
          	
           	if(this.methodMapCache.getMethodMapFromCache(METHOD_MAP) == null){
          		buildMethodMapFromDB();
          	}
          }
          
          protected ConfigAttributeDefinition lookupAttributes(Method method) {
          	
              ConfigAttributeDefinition definition = new ConfigAttributeDefinition();
      
             	checkMethodMap();
      
              // Add attributes explictly defined for this method invocation
              ConfigAttributeDefinition directlyAssigned = 
              	(ConfigAttributeDefinition) this.methodMapCache.getMethodMapFromCache(METHOD_MAP).get(method);
              merge(definition, directlyAssigned);
      
              // Add attributes explicitly defined for this method invocation's interfaces
              Class[] interfaces = method.getDeclaringClass().getInterfaces();
      
              for (int i = 0; i < interfaces.length; i++) {
                  Class clazz = interfaces[i];
      
                  try {
                      // Look for the method on the current interface
                      Method interfaceMethod = clazz.getDeclaredMethod(method.getName(),
                              (Class[]) method.getParameterTypes());
                      ConfigAttributeDefinition interfaceAssigned = 
                      	(ConfigAttributeDefinition) this.methodMapCache.getMethodMapFromCache(METHOD_MAP).get(interfaceMethod);
                      merge(definition, interfaceAssigned);
                  } catch (Exception e) {
                      // skip this interface
                  }
              }
      
              // Return null if empty, as per abstract superclass contract
              if (definition.size() == 0) {
                  return null;
              } else {
                  return definition;
              }
          }
      
          private boolean isMatch(String methodName, String mappedName) {
              return (mappedName.endsWith("*")
              && methodName.startsWith(mappedName.substring(0, mappedName.length()
                      - 1)))
              || (mappedName.startsWith("*")
              && methodName.endsWith(mappedName.substring(1, mappedName.length())));
          }
      
          private void merge(ConfigAttributeDefinition definition,
              ConfigAttributeDefinition toMerge) {
              if (toMerge == null) {
                  return;
              }
      
              Iterator attribs = toMerge.getConfigAttributes();
      
              while (attribs.hasNext()) {
                  definition.addConfigAttribute((ConfigAttribute) attribs.next());
              }
          }
      
      }
      DBDrivenMethodDefinitionSource (most of it's methods are copy from MethodDefinitionMap with little modification) is serving the criteria 1 & 2 , with a little change of criteria 1 -- the authorization table is not make from the DB at the beginning , not until the first invocation of a method . l changed it because l meet an NPE if l do it this way ,
      Code:
      public class  DBDrivenMethodDefinitionSource extends AbstractMethodDefinitionSource {
         public DBDrivenMethodDefinitionSource () {
             buildMethodMapFromDB();
         }
      ...
      }
      this happened while loading applicationContext-common-authorization.xml (l don't know how to do this with spring ioc , )

      MethodMapCache.java
      Code:
      package org.acegi.providers.dao;
      
      import java.util.Map;
      
      public interface MethodMapCache {
      
          public Map getMethodMapFromCache(String mapName);
      
          public void putMethodMapInCache(String mapName,Map methodMap);
      
          public void removeMethodMapFromCache(String mapName);
      }
      to be continue ..
      Last edited by yfmoan; Dec 15th, 2005, 12:11 AM.

      Comment


      • #18
        EhCacheBasedMethodMapCache.java
        Code:
        package org.acegi.providers.dao.cache;
        
        import java.util.HashMap;
        import java.util.Map;
        
        import org.acegi.providers.dao.MethodMapCache;
        import net.sf.ehcache.Cache;
        import net.sf.ehcache.CacheException;
        import net.sf.ehcache.Element;
        
        import org.apache.commons.logging.Log;
        import org.apache.commons.logging.LogFactory;
        
        import org.springframework.beans.factory.InitializingBean;
        
        import org.springframework.dao.DataRetrievalFailureException;
        import org.springframework.util.Assert;
        
        public class EhCacheBasedMethodMapCache implements MethodMapCache, InitializingBean {
        
            private static final Log logger = LogFactory.getLog(EhCacheBasedMethodMapCache.class);
            private Cache cache;
        
            public void setCache(Cache cache) {
                this.cache = cache;
            }
        
            public Cache getCache() {
                return cache;
            }
        
            public Map getMethodMapFromCache(String mapName) {
                Element element = null;
        
                try {
                    element = cache.get(mapName);
                } catch (CacheException cacheException) {
                    throw new DataRetrievalFailureException("Cache failure: "
                        + cacheException.getMessage());
                }
        
                if (logger.isDebugEnabled()) {
                    logger.debug("Cache hit: " + (element != null) + "; mapName: "
                        + mapName);
                }
        
                if (element == null) {
                    return null;
                } else {
                    return (Map) element.getValue();
                }
            }
        
            public void afterPropertiesSet() throws Exception {
                Assert.notNull(cache, "cache mandatory");
            }
        
            public void putMethodMapInCache(String mapName,Map methodMap) {
                Element element = new Element(mapName, new HashMap(methodMap));
        
                if (logger.isDebugEnabled()) {
                    logger.debug("Cache put: " + element.getKey());
                }
        
                cache.put(element);
            }
        
            public void removeMethodMapFromCache(String mapName) {
                cache.remove(mapName);
            }
        }
        NullMethodMapCache.java
        Code:
        package org.acegi.providers.dao.cache;
        
        import java.util.Map;
        
        import org.acegi.providers.dao.MethodMapCache;
        
        public class NullMethodMapCache implements MethodMapCache {
        
        	public Map getMethodMapFromCache(String mapName){
        		return null;
        	}
        
            public void putMethodMapInCache(String mapName,Map methodMap) {}
        
            public void removeMethodMapFromCache(String mapName) {}
            
        }
        Remark:

        1. This is first part of the complete code , second part will be using an interceptor to detect the change of the modification of the authorization table . l am using hibernate , so it is nature to use hibernate interceptor to do the job , althought l not yet implement , but l think the idea can be work theoretically . May be the code above is garbage , but what l am trying to prove is that dynamic authorization is useful , and is worth more attention for a complete security system like acegisecurity.

        2. Because l avoid study all the codes , it may be not "save" , just to prove whether it can be implement easily into the current status of acegi (1.0 RC1).

        Is this a working idea or there is other way to do it ?

        moon

        Comment


        • #19
          detect the changes of authorization table by using hibernate event , and then caching the table again , this serve criteria 3.

          applicationContext.xml
          Code:
          ...
          	<bean id="authorizationSaveOrUpdateEventListener" class="org.acegi.providers.dao.hibernate.AuthorizationSaveOrUpdateEventListener">
          		<property name="methodDefSource"><ref bean="dbDrivenMethodDefinitionSource"/></property>
          	</bean>
          	
          	<bean id="authorizationDeleteEventListener" class="org.acegi.providers.dao.hibernate.AuthorizationDeleteEventListener">	
          		<property name="methodDefSource"><ref bean="dbDrivenMethodDefinitionSource"/></property>
          	</bean>
          		
          	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
          		<property name="dataSource"><ref local="dataSource"/></property>
          		<property name="eventListeners">
          			<map>
          				<entry key="save-update"><ref local="authorizationSaveOrUpdateEventListener" /></entry>
          				<entry key="delete"><ref local="authorizationDeleteEventListener" /></entry>
          			</map>
          		</property>
          		<property name="mappingResources">
          			<list>
          			.....
          			</list>
          		</property>
          		<property name="hibernateProperties">
          			<props>
          			.....
          			</props>
          		</property>
          	
          	</bean>
          ...
          AuthorizationSaveOrUpdateEventListener.java
          Code:
          package org.acegi.providers.dao.hibernate;
          
          import org.hibernate.HibernateException;
          import org.hibernate.event.SaveOrUpdateEvent;
          import org.hibernate.event.def.DefaultSaveOrUpdateEventListener;
          import org.yourschool.library.domain.Role;
          
          import org.acegi.intercept.method.DBDrivenMethodDefinitionSource;
          
          public class AuthorizationSaveOrUpdateEventListener extends DefaultSaveOrUpdateEventListener {
          
          	private DBDrivenMethodDefinitionSource methodDefSource;
          	
          	public void setMethodDefSource(DBDrivenMethodDefinitionSource methodDefSource) {
          		this.methodDefSource = methodDefSource;
          	}
          
          	public void onSaveOrUpdate(SaveOrUpdateEvent event) throws HibernateException {
          		
          		super.onSaveOrUpdate(event);
          		
          		if(event.getEntity() instanceof Role)
          			methodDefSource.buildMethodMapFromDB();
          		
          	}
          	
          }
          similar for AuthorizationDeleteEventListener.java.

          applicationContext-common-authorization.xml
          Code:
          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
          
          <beans>
          
             <!-- An access decision manager used by the business objects -->
             <bean id="businessAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
                <property name="allowIfAllAbstainDecisions"><value>false</value></property>
                <property name="decisionVoters">
                   <list>
                      <ref local="roleVoter"/>
                   </list>
                </property>
             </bean>
          
             <bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter"/>
             
             <!-- ================= METHOD INVOCATION AUTHORIZATION ==================== -->
          
          	<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
              
          	<bean id="methodMapCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
          		<property name="cacheManager">
          			<ref local="cacheManager"/>
          		</property>
          		<property name="cacheName">
          			<value>methodMapCache</value>
          		</property>
          	</bean>
             
          	<bean id="methodMapCache" class="org.acegi.providers.dao.cache.EhCacheBasedMethodMapCache">
          		<property name="cache"><ref local="methodMapCacheBackend"/></property>
          	</bean>
          		
          	<bean id="dbDrivenMethodDefinitionSource" class="org.acegi.intercept.method.DBDrivenMethodDefinitionSource">
          		<property name="securityManager"><ref bean="security"/></property>
          		<property name="methodMapCache"><ref local="methodMapCache"/></property>
          	</bean>
          		
          	<bean id="librarySecurity" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
          		<property name="authenticationManager"><ref bean="authenticationManager"/></property>
          		<property name="accessDecisionManager"><ref local="businessAccessDecisionManager"/></property>
          		<property name="objectDefinitionSource"><ref local="dbDrivenMethodDefinitionSource"/></property>
          	</bean>	
          
          </beans>
          moon

          Comment


          • #20
            Sorry , the AuthorizationSaveOrUpdateEventListener.java above is not working correctly when l first save a entity (whatever object that intercepted by the hibernate event), it cause an endless loop . l guess l used the wrong listeners , and l make it work again by using another 3 listeners , AuthorizationInsertEventListener , AuthorizationUpdateEventListener and AuthorizationDeleteEventListener .

            AuthorizationPostInsertEventListener.java
            Code:
            package org.acegi.providers.dao.hibernate;
            
            import org.hibernate.event.PostInsertEvent;
            import org.hibernate.event.PostInsertEventListener;
            import org.yourschool.library.domain.Role;
            import org.acegi.intercept.method.DBDrivenMethodDefinitionSource;
            
            public class AuthorizationPostInsertEventListener implements PostInsertEventListener {
            
            	private DBDrivenMethodDefinitionSource methodDefSource;
            
            	public void setMethodDefSource(
            			DBDrivenMethodDefinitionSource methodDefSource) {
            		this.methodDefSource = methodDefSource;
            	}
            
            	public void onPostInsert(PostInsertEvent arg0) {
            		
            		if (arg0.getEntity() instanceof Role){
            			methodDefSource.buildMethodMapFromDB();
            		}
            		
            	}
            
            }
            similar for AuthorizationPostUpdateEventListener.java and AuthorizationPostDeleteEventListener.java

            applicationContext.xml
            Code:
            	<bean id="authorizationPostInsertEventListener" class="org.acegi.providers.dao.hibernate.AuthorizationPostInsertEventListener">
            		<property name="methodDefSource"><ref bean="dbDrivenMethodDefinitionSource"/></property>
            	</bean>
            	
            		<bean id="authorizationPostUpdateEventListener" class="org.acegi.providers.dao.hibernate.AuthorizationPostUpdateEventListener">
            		<property name="methodDefSource"><ref bean="dbDrivenMethodDefinitionSource"/></property>
            	</bean>
            	
            	<bean id="authorizationPostDeleteEventListener" class="org.acegi.providers.dao.hibernate.AuthorizationPostDeleteEventListener">	
            		<property name="methodDefSource"><ref bean="dbDrivenMethodDefinitionSource"/></property>
            	</bean>
            		
            	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
            		<property name="dataSource"><ref local="dataSource"/></property>
            		<property name="eventListeners">
            			<map>
            				<entry key="post-commit-insert"><ref local="authorizationPostInsertEventListener" /></entry>
            				<entry key="post-commit-update"><ref local="authorizationPostUpdateEventListener" /></entry>
            				<entry key="post-commit-delete"><ref local="authorizationPostDeleteEventListener" /></entry>
            			</map>
            		</property>
            .....
            l am not confirm all this implementations correct or not , but working for me now.

            moon

            Comment


            • #21
              Originally posted by Ben Alex
              To answer question 1, this works because Spring automatically applies PropertyEditors. Because MethodSecurityInterceptor.objectDefinitionSource is a MethodDefinitionSource, Spring uses the MethodDefinitionSourceEditor. The MethodDefinitionSourceEditor gets passed the entire multi-line String that comprises the entire object definition. It then tokenizes it as if it were a standard Java Properties file. This results in the property name of each line (ie anything to the left of the equals sign) being treated as the method name and passed to the newly created MethodDefinitionMap, with the property value of each line (ie anything to the right of the equals sign) being passed to a ConfigAttributeEditor. ConfigAttributeEditor uses the comma delimiter to build a SecurityConfig instance of each token, putting them all into a ConfigAttributeDefinition that is then returned. The returned ConfigAttributeDefinition is passed to the MethodDefinitionMap.

              To answer question 2, I'd normalise the database so you have a SECURE_OBJECT table, a ROLES table, and a SECURE_OBJECT-ROLES table. Thus your DatabaseObjectDefinitionSource can be wired to either a MethodSecurityInterceptor or a FilterSecurityInterceptor. The SECURE_OBJECT table should have a TYPE column to denote whether it's for a MethodInvocation or a FilterInvocation declaration. Once you've got the schema sorted out, it's a simple matter to build a ConfigAttributeDefinition in response to a request. I'd consider returning null to the getConfigAttributeDefinitions() method, as it's only used by AbstractSecurityInterceptor to help with inital configuration checking.
              I would also suggest adding a "sort_order" column to the SECURE_OBJECT table to determine sort order if you plan on using this table to FilterInvocation definitions. With FilterInvocation defintions, the more specific URLs will need to be evaluted first.

              http://acegisecurity.org/docbook/ace...lterinvocation

              Comment


              • #22
                Originally posted by russpitre
                I would also suggest adding a "sort_order" column to the SECURE_OBJECT table to determine sort order if you plan on using this table to FilterInvocation definitions. With FilterInvocation defintions, the more specific URLs will need to be evaluted first.
                This is a good observation.

                Comment


                • #23
                  Hi,

                  I'm interested on implementing this approach in our app as well. Our admin user needs to be able to change property level access permissions based on the ROLE at runtime and save these permission in the database.

                  Is there any document or tutorial that I could use to guide me through this besides this thread?

                  Than you

                  Raul

                  Comment


                  • #24
                    I would suggest looking in the following:

                    http://www.springframework.org/articles

                    As far I could see this is a custom rolled solution.....it would be nice to see this in the acegi api, but I think since many people need different types of method level auth...that making a generic api for it my be difficult....although....I have been known to be wrong. LOL.

                    I would like to implement this....but will wait a while before I attempt it....

                    If someone does implement this....please added a tutorial in the articles or ask the senior members where to put it.....

                    Comment


                    • #25
                      Originally posted by raulraja View Post
                      Hi,

                      I'm interested on implementing this approach in our app as well. Our admin user needs to be able to change property level access permissions based on the ROLE at runtime and save these permission in the database.

                      Is there any document or tutorial that I could use to guide me through this besides this thread?

                      Than you

                      Raul
                      I am also very interested in this. I see that org.acegisecurity.userdetails.jdbc.JdbcDaoImpl allready implements this for the Users, it would be nice to have a
                      org.acegisecurity.intercept.web.jdbc.JdbcFilterInv ocationDefinitionSource
                      with cache, and properties for overriding the query.

                      I have searched in a lot of places and this doesn't seem to exist, anyone has some tips to share?

                      Comment


                      • #26
                        Here's a bit of code you may be able to adjust to your environment.


                        DatabasePathBasedFilterInvocationDefinitionMap.jav a

                        Code:
                        package com.mycompany.security.acegisecurity.intercept.web;
                        import java.util.HashSet;
                        import java.util.Iterator;
                        import java.util.List;
                        import java.util.Set;
                        import java.util.Vector;
                        import org.acegisecurity.ConfigAttributeDefinition;
                        import org.acegisecurity.SecurityConfig;
                        import org.acegisecurity.intercept.web.AbstractFilterInvocationDefinitionSource;
                        import org.acegisecurity.intercept.web.FilterInvocationDefinitionMap;
                        import org.apache.log4j.Logger;
                        import org.springframework.util.AntPathMatcher;
                        import org.springframework.util.PathMatcher;
                        import com.mycompany.model.security.Role;
                        import com.mycompany.model.security.SecureObject;
                        import com.mycompany.service.security.SecurityService;
                         
                        public class DatabasePathBasedFilterInvocationDefinitionMap extends AbstractFilterInvocationDefinitionSource implements  FilterInvocationDefinitionMap {
                         
                         protected class EntryHolder {
                                private String antPath;
                                private ConfigAttributeDefinition configAttributeDefinition;
                                protected EntryHolder() {
                                    throw new IllegalArgumentException("Cannot use default constructor");
                                }
                                public EntryHolder(String antPath, ConfigAttributeDefinition attr) {
                                    this.antPath = antPath;
                                    this.configAttributeDefinition = attr;
                                }
                                public String getAntPath() {
                                    return antPath;
                                }
                                public ConfigAttributeDefinition getConfigAttributeDefinition() {
                                    return configAttributeDefinition;
                                }
                            }
                         
                         private static final Logger logger = Logger.getLogger(DatabasePathBasedFilterInvocationDefinitionMap.class);
                         
                         private boolean convertUrlToLowercaseBeforeComparison = true;
                         
                         private PathMatcher pathMatcher = new AntPathMatcher();
                         
                         private List requestMap = new Vector();
                         
                         private SecurityService securityService;
                         
                         @SuppressWarnings("unchecked")
                         public void addSecureUrl(String antPath, ConfigAttributeDefinition attr) {
                                requestMap.add(new EntryHolder(antPath, attr));
                                if (logger.isDebugEnabled()) {
                                    logger.debug("Added Ant path: " + antPath + "; attributes: " + attr);
                                }
                            }
                         @SuppressWarnings("unchecked")
                         public Iterator getConfigAttributeDefinitions() {
                             initRequestMap();
                                Set set = new HashSet();
                                Iterator iter = requestMap.iterator();
                                while (iter.hasNext()) {
                                    EntryHolder entryHolder = (EntryHolder) iter.next();
                                    set.add(entryHolder.getConfigAttributeDefinition());
                                }
                                return set.iterator();
                            }
                            public int getMapSize() {
                                return this.requestMap.size();
                            }
                         
                            @SuppressWarnings({"unused","unchecked"})
                         private void initRequestMap() {
                          List<SecureObject> secureUrlObjects = securityService.getSecureFilterInvocationObjects();
                          ConfigAttributeDefinition def =  new ConfigAttributeDefinition();
                          for(SecureObject filterInvocation: secureUrlObjects) {
                           if(logger.isDebugEnabled()) {
                            logger.debug("<Setting Secure Object Filter Definition: " + filterInvocation.getSecureObject() );
                           }
                           def = new ConfigAttributeDefinition();
                           for(Role role : filterInvocation.getRoles()){
                            def.addConfigAttribute(new SecurityConfig(role.getName()));
                            requestMap.add(filterInvocation.getSortOrder() , new EntryHolder(filterInvocation.getSecureObject(), def) );
                           }
                          }
                         }
                         
                            public boolean isConvertUrlToLowercaseBeforeComparison() {
                                return convertUrlToLowercaseBeforeComparison;
                            }
                         @SuppressWarnings("unchecked")
                         @Override
                            public ConfigAttributeDefinition lookupAttributes(String url) {
                          //initRequestMap();
                         
                                // Strip anything after a question mark symbol, as per SEC-161.
                                int firstQuestionMarkIndex = url.lastIndexOf("?");
                                if (firstQuestionMarkIndex != -1) {
                                    url = url.substring(0, firstQuestionMarkIndex);
                                }
                                if (convertUrlToLowercaseBeforeComparison) {
                                    url = url.toLowerCase();
                                    if (logger.isDebugEnabled()) {
                                        logger.debug("Converted URL to lowercase, from: '" + url
                                            + "'; to: '" + url + "'");
                                    }
                                }
                                Iterator iter = requestMap.iterator();
                                while (iter.hasNext()) {
                                    EntryHolder entryHolder = (EntryHolder) iter.next();
                                    boolean matched = pathMatcher.match(entryHolder.getAntPath(), url);
                                    if (logger.isDebugEnabled()) {
                                        logger.debug("Candidate is: '" + url + "'; pattern is "
                                            + entryHolder.getAntPath() + "; matched=" + matched);
                                    }
                                    if (matched) {
                                        return entryHolder.getConfigAttributeDefinition();
                                    }
                                }
                                return null;
                            }
                         
                         public void setConvertUrlToLowercaseBeforeComparison(boolean convertUrlToLowercaseBeforeComparison) {
                          this.convertUrlToLowercaseBeforeComparison = convertUrlToLowercaseBeforeComparison;
                         }
                         
                         public SecurityService getSecurityService() {
                          return securityService;
                         }
                         
                         public void setSecurityService(SecurityService securityService) {
                          this.securityService = securityService;
                         }
                        }

                        SecureObject domain object....

                        Code:
                        package com.mycompany.model.security;
                        import java.util.Set;
                        import org.apache.commons.lang.builder.EqualsBuilder;
                        import org.apache.commons.lang.builder.HashCodeBuilder;
                        import com.mycompany.model.BaseObject;
                         
                        public class SecureObject extends BaseObject {
                         
                         private static final long serialVersionUID = 7093257566823171405L;
                         
                         private Integer secureObjectId;
                         private Integer secureObjectTypeId;
                         private String secureObject;
                         private Integer sortOrder;
                         
                         private Set<Role> roles;
                         
                         public Set<Role> getRoles() {
                          return roles;
                         }
                         public void setRoles(Set<Role> roles) {
                          this.roles = roles;
                         }
                         public String getSecureObject() {
                          return secureObject;
                         }
                         public void setSecureObject(String secureObject) {
                          this.secureObject = secureObject;
                         }
                         public Integer getSecureObjectId() {
                          return secureObjectId;
                         }
                         public void setSecureObjectId(Integer secureObjectId) {
                          this.secureObjectId = secureObjectId;
                         }
                         public Integer getSortOrder() {
                          return sortOrder;
                         }
                         public void setSortOrder(Integer sortOrder) {
                          this.sortOrder = sortOrder;
                         }
                         public Integer getSecureObjectTypeId() {
                          return secureObjectTypeId;
                         }
                         public void setSecureObjectTypeId(Integer secureObjectTypeId) {
                          this.secureObjectTypeId = secureObjectTypeId;
                         }
                         
                         public boolean equals(Object object) {
                          if (!(object instanceof SecureObject)) {
                           return false;
                          }
                          SecureObject rhs = (SecureObject) object;
                          return new EqualsBuilder().
                           append(this.secureObjectTypeId,rhs.getSecureObjectTypeId() ).
                           append(this.sortOrder, rhs.getSortOrder() ).
                           append(this.secureObject,rhs.getSecureObject() ).
                           isEquals();
                         }
                         public int hashCode() {
                          return new HashCodeBuilder(-1639360101, 1081503241).
                           append(this.secureObjectTypeId).
                           append(this.sortOrder).
                           append(this.roles).
                           append(this.secureObject).
                           toHashCode();
                         }
                         
                        }
                        Last edited by russpitre; Mar 13th, 2007, 08:59 PM.

                        Comment


                        • #27
                          cont'd...

                          Role domain object.

                          Code:
                          package com.mycompany.model.security;
                          import java.util.List;
                          import org.apache.commons.lang.StringUtils;
                          import org.apache.commons.lang.builder.EqualsBuilder;
                          import org.apache.commons.lang.builder.HashCodeBuilder;
                          import org.apache.commons.lang.builder.ToStringBuilder;
                          import com.mycompany.model.BaseObject;
                          import com.mycompany.model.activedirectory.ADGroup;
                          import com.mycompany.model.user.PrologUserGroup;
                           
                          public class Role extends BaseObject {
                           
                           private static final long serialVersionUID = 5977582917018176333L;
                           
                           private static String ROLE_PREFIX_ACTIVE_DIRECTORY = "ROLE_";
                           private static String ROLE_PREFIX_PROLOG = "ROLE_PROLOG_";
                           
                           private Integer roleId;
                           private String description;
                           private String originalName;
                           private String name;
                           private String sourcePrimaryKey;
                           private Integer typeId;
                           private RoleType roleType;
                           
                           private List<String> problemsHolder;
                           public String getDescription() {
                            return description;
                           }
                           public void setDescription(String description) {
                            this.description = description;
                           }
                           public String getName() {
                            return name;
                           }
                           public void setName(String name) {
                            this.name = name;
                           }
                           public String getOriginalName() {
                            return originalName;
                           }
                           public void setOriginalName(String originalName) {
                            this.originalName = originalName;
                           }
                           public Integer getRoleId() {
                            return roleId;
                           }
                           public void setRoleId(Integer roleId) {
                            this.roleId = roleId;
                           }
                           public RoleType getRoleType() {
                            return roleType;
                           }
                           public void setRoleType(RoleType roleType) {
                            this.roleType = roleType;
                           }
                           public String getSourcePrimaryKey() {
                            return sourcePrimaryKey;
                           }
                           public void setSourcePrimaryKey(String sourcePrimaryKey) {
                            this.sourcePrimaryKey = sourcePrimaryKey;
                           }
                           public Integer getTypeId() {
                            return typeId;
                           }
                           public void setTypeId(Integer typeId) {
                            this.typeId = typeId;
                           }
                           /**
                            * name is the business key.  Do not change.
                            * 
                            * @see java.lang.Object#equals(Object)
                            */
                           public boolean equals(Object object) {
                            if (!(object instanceof Role)) {
                             return false;
                            }
                            Role rhs = (Role) object;
                            return new EqualsBuilder().
                             append(this.name, rhs.getName()).isEquals();
                           }
                           
                           /**
                            * name is the business key.  Do not change.
                            * 
                            * @see java.lang.Object#hashCode()
                            */
                           public int hashCode() {
                            return new HashCodeBuilder(-2067913629, -1342727107).
                             append(this.name).toHashCode();
                           }
                           
                           /**
                            * @see java.lang.Object#toString()
                            */
                           public String toString() {
                            return new ToStringBuilder(this) .append("name", this.name)
                              .append("description", this.description).append("typeId",
                                this.typeId).append("roleId", this.roleId).append(
                                "originalName", this.originalName).append(
                                "sourcePrimaryKey", this.sourcePrimaryKey).toString();
                           }
                           
                           public static String formatActiveDirectoryGroupNameToRoleName(String groupName) {
                            try {
                             String retVal = groupName.toUpperCase();
                             retVal = StringUtils.replace(retVal, " ", "_");
                             retVal = StringUtils.replace(retVal, ",", "");
                             return ROLE_PREFIX_ACTIVE_DIRECTORY + retVal;
                            } catch (Exception e ) {
                             return null;
                            }
                           }
                           
                           public static String formatPrologGroupNameToRoleName(String groupName) {
                            StringBuffer strBuffer = new StringBuffer( groupName.trim() );
                            return ROLE_PREFIX_PROLOG + strBuffer.toString().toUpperCase().replaceAll(" ", "_").replaceAll("/","_");
                           }
                           
                           public static Role getRole(PrologUserGroup group) {
                            Role role = new Role();
                            role.setDescription(group.getDescription());
                            role.setName(Role.formatPrologGroupNameToRoleName(group.getName()));
                            role.setOriginalName(group.getName());
                            role.setSourcePrimaryKey(group.getGroupId() + "");
                            role.setTypeId(RoleType.PROLOG);
                            return role;
                           }
                           public static Role getRole(ADGroup group) {
                            Role role = new Role();
                            role.setDescription(group.getDescription());
                            role.setName(Role.formatActiveDirectoryGroupNameToRoleName(group.getCn()));
                            role.setOriginalName(group.getCn());
                            role.setSourcePrimaryKey(group.getDn());
                            role.setTypeId(RoleType.ACTIVE_DIRECTORY);
                            return role;
                           }
                           
                           public List<String> getProblems() {
                            return problemsHolder;
                           }
                           public void setProblems(List<String> problemsHolder) {
                            this.problemsHolder = problemsHolder;
                           }
                           
                           public boolean isProblemsExists(){
                            if(this.problemsHolder.size() > 0){
                             return true;
                            }else{
                             return false;
                            }
                           }
                          }
                          ...spring bean definition...

                          Code:
                          <bean id="filterSecurityInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"> <property name="validateConfigAttributes" value="true"/> <property name="authenticationManager" ref="authenticationManager"/> <property name="accessDecisionManager" ref="accessDecisionManager"/> <property name="objectDefinitionSource" ref="databasePathBasedFilterInvocationDefinitionMap"/> </bean>
                          <bean id="databasePathBasedFilterInvocationDefinitionMap" class="com.shawmut.security.acegisecurity.intercept.web.DatabasePathBasedFilterInvocationDefinitionMap"> <property name="securityService" ref="securityService"/>
                          </bean>
                          Last edited by russpitre; Mar 13th, 2007, 08:58 PM. Reason: `

                          Comment


                          • #28
                            thanks! I'll give it a try.

                            Comment


                            • #29
                              Originally posted by yfmoan View Post
                              Sorry , the AuthorizationSaveOrUpdateEventListener.java above is not working correctly when l first save a entity (whatever object that intercepted by the hibernate event), it cause an endless loop . l guess l used the wrong listeners , and l make it work again by using another 3 listeners , AuthorizationInsertEventListener , AuthorizationUpdateEventListener and AuthorizationDeleteEventListener .

                              l am not confirm all this implementations correct or not , but working for me now.
                              Instead of using a seperate class I guess it should work if implementing the 3 interfaces in one class too?

                              It still causes an endless loop and ultimately a Stack Overflow here...

                              Comment


                              • #30
                                New Idea

                                I am new to Ageci so I might missing the all point but I think you went the wrong way
                                I think it would be better give each method it own role and let the adminstrator decide which users or groups can have that role
                                You then effectivly can load the list of role for certain user when the session start.
                                If permission change during an open session you will need to refresh the list.
                                Something like hot deploy. This can be done easliy by being aware to all open sessions.
                                Is this sound right?

                                Comment

                                Working...
                                X