You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by ja...@apache.org on 2009/04/29 23:01:25 UTC

svn commit: r769928 - in /ofbiz/trunk/framework/security: data/ entitydef/ src/org/ofbiz/security/authz/ src/org/ofbiz/security/authz/da/

Author: jaz
Date: Wed Apr 29 21:01:25 2009
New Revision: 769928

URL: http://svn.apache.org/viewvc?rev=769928&view=rev
Log:
implementation of new authorization (authz) functionality -- JIRA OFBIZ-2381

NOTE: Even though there are a lot of commonalities with this and the older security framework, this was implemented to not conflict with the existing implementation; the purpose of this is so they both can run side by side during the migration process. Code not migrated will still function as expected and once this is complete, the old implementation will be removed.

Added:
    ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/
    ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/AbtractAuthorization.java
    ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/Authorization.java
    ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/AuthorizationFactory.java
    ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/EntityAuthorization.java
    ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/
    ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/DynamicAccess.java
    ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/DynamicAccessFactory.java
    ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/DynamicAccessHandler.java
    ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/GroovyDaHandler.java
    ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/ObjectDaHandler.java
Modified:
    ofbiz/trunk/framework/security/data/SecurityData.xml
    ofbiz/trunk/framework/security/entitydef/entitymodel.xml

Modified: ofbiz/trunk/framework/security/data/SecurityData.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/security/data/SecurityData.xml?rev=769928&r1=769927&r2=769928&view=diff
==============================================================================
--- ofbiz/trunk/framework/security/data/SecurityData.xml (original)
+++ ofbiz/trunk/framework/security/data/SecurityData.xml Wed Apr 29 21:01:25 2009
@@ -20,10 +20,29 @@
 <entity-engine-xml>
     <!-- OFBiz Core security -->
     <!-- Target file: framework/security/data/SecurityData.xml -->
+    
+    <!--  administrative security groups -->
     <SecurityGroup groupId="FULLADMIN" description="Full Admin group, has all general permissions."/>
     <SecurityGroup groupId="FLEXADMIN" description="Flexible Admin group, has all granular permissions."/>
     <SecurityGroup groupId="VIEWADMIN" description="Demo Admin group, has all view permissions."/>
-
+    
+    <!--  Security 2.0 base permissions -->
+    <SecurityPermission permissionId="access" description="Base ACCESS permission"/>
+    <SecurityPermission permissionId="create" description="Base CREATE permission"/>
+    <SecurityPermission permissionId="read" description="Base READ permission"/>
+    <SecurityPermission permissionId="update" description="Base UPDATE permission"/>
+    <SecurityPermission permissionId="delete" description="Base DELETE permission"/>
+    
+    <!-- base permissions to groups -->
+    <SecurityGroupPermission groupId="FULLADMIN" permissionId="access"/>
+    <SecurityGroupPermission groupId="FULLADMIN" permissionId="create"/>
+    <SecurityGroupPermission groupId="FULLADMIN" permissionId="read"/>
+    <SecurityGroupPermission groupId="FULLADMIN" permissionId="update"/>
+    <SecurityGroupPermission groupId="FULLADMIN" permissionId="access"/>
+    
+    <SecurityGroupPermission groupId="VIEWADMIN" permissionId="access"/>
+    <SecurityGroupPermission groupId="VIEWADMIN" permissionId="read"/>
+       
     <SecurityGroup groupId="BIZADMIN" description="Full Business Applications permission group, has all business app admin permissions, not technical permissions."/>
 
     <!-- general admin tools permission -->

Modified: ofbiz/trunk/framework/security/entitydef/entitymodel.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/security/entitydef/entitymodel.xml?rev=769928&r1=769927&r2=769928&view=diff
==============================================================================
--- ofbiz/trunk/framework/security/entitydef/entitymodel.xml (original)
+++ ofbiz/trunk/framework/security/entitydef/entitymodel.xml Wed Apr 29 21:01:25 2009
@@ -159,8 +159,21 @@
             title="Security Component - Security Permission Entity">
       <field name="permissionId" type="id-long-ne"></field>
       <field name="description" type="description"></field>
+      <field name="dynamicAccess" type="value"></field>
       <prim-key field="permissionId"/>
     </entity>
+    <entity entity-name="SecurityPermissionAutoGrant"
+            package-name="org.ofbiz.security.securitygroup"
+            default-resource-name="SecurityEntityLabels"
+            title="Security Component - Security Permission Auto Grant Entity">
+      <field name="permissionId" type="id-long-ne"></field>
+      <field name="grantPermission" type="id-vlong-ne"></field>
+      <prim-key field="permissionId"/>
+      <prim-key field="grantPermission"/>
+      <relation type="one" fk-name="SEC_PERM_AUTO_GRNT" rel-entity-name="SecurityPermission">
+        <key-map field-name="permissionId"/>
+      </relation>
+    </entity>
     <view-entity entity-name="UserLoginAndSecurityGroup"
           package-name="org.ofbiz.security.securitygroup"
           never-cache="true"

Added: ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/AbtractAuthorization.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/AbtractAuthorization.java?rev=769928&view=auto
==============================================================================
--- ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/AbtractAuthorization.java (added)
+++ ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/AbtractAuthorization.java Wed Apr 29 21:01:25 2009
@@ -0,0 +1,147 @@
+package org.ofbiz.security.authz;
+
+import java.util.List;
+import java.util.Map;
+
+import javolution.util.FastList;
+
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.UtilValidate;
+import org.ofbiz.base.util.string.FlexibleStringExpander;
+
+public abstract class AbtractAuthorization implements Authorization {
+	
+    private static final String module = AbtractAuthorization.class.getName();
+    
+	/**
+	 * Used to manage Auto-Grant permissions for the current "request"
+	 */
+	private static ThreadLocal<List<String>> autoGrant = new ThreadLocal<List<String>>();
+	private static ThreadLocal<String> uid = new ThreadLocal<String>();
+	
+	/**
+	 * Checks to see if the user has a static permission
+	 * 
+	 * @param userId the user's userId
+	 * @param permission the expanded permission string
+	 * @param context name/value pairs used for permission lookup
+	 * @return true if the user has permission
+	 */
+	public abstract boolean hasStaticPermission(String userId, String permission, Map<String, ? extends Object> context);
+	
+	/**
+	 * Locates the Dynamic Access implementation for the permissions and invokes it
+	 * 
+	 * @param userId the user's userId
+	 * @param permission the expanded permission string
+	 * @param context name/value pairs used for permission lookup
+	 * @return true if the user has permission
+	 */
+	public abstract boolean hasDynamicPermission(String userId, String permission, Map<String, ? extends Object> context);
+	
+	/**
+	 * Obtains a list of permissions auto-granted by the given permission
+	 * 
+	 * @param userId the user's userId
+	 * @param permission the expanded permission string
+	 * @param context name/value pairs used for permission lookup
+	 * @return a List of permission strings to auto-grant the user
+	 */
+	public abstract List<String> getAutoGrantPermissions(String userId, String permission, Map<String, ? extends Object> context);
+	
+	/**
+	 * Test to see if the specified user has permission
+	 * 
+	 * @param userId the user's userId
+	 * @param permission the raw permission string
+	 * @param context name/value pairs used for permission lookup
+	 * @param expanded true if the permission string is already expanded, false if it will contain ${} context values
+	 * @return true if the user has permission
+	 */
+	public boolean hasPermission(String userId, String permission, Map<String, ? extends Object> context, boolean expanded) {
+	    // expand the permission string
+		String expandedPermission;
+		if (!expanded) {
+			expandedPermission = FlexibleStringExpander.expandString(permission, context);
+		} else {
+			expandedPermission = permission;
+		}
+		
+		// verify the ThreadLocal data; make sure it isn't stale (from a thread pool)
+        String threadUid = uid.get();
+        if (!userId.equals(threadUid)) {
+            autoGrant.remove();
+            uid.remove();
+        }
+		
+		// split the permission string; so we can walk up the levels
+		String[] permSplit = expandedPermission.split(":");
+		StringBuffer joined = new StringBuffer();
+		int index = 1;
+		
+		if (permSplit != null && permSplit.length > 1) {
+		    if (Debug.verboseOn()) Debug.logVerbose("Security 2.0 schema found -- walking tree : " + expandedPermission, module);
+    		// start walking
+    		for (String perm : permSplit) {
+    		    if (permSplit.length >= index) {
+        			if (joined.length() > 0) {
+        				joined.append(":");
+        			}
+        			joined.append(perm);
+        			
+        			// first check auto-granted permissions
+        			List<String> grantedPerms = autoGrant.get();
+        			if (grantedPerms != null && grantedPerms.size() > 0) {
+        				for (String granted : grantedPerms) {
+        					if (joined.toString().equals(granted)) {
+        					    // permission granted
+        					    handleAutoGrantPermissions(userId, expandedPermission, context);
+        						return true;
+        					}
+        				}
+        			}
+        			
+        			// next check static permission
+        			if (hasStaticPermission(userId, joined.toString(), context)) {
+        				// permission granted
+        				handleAutoGrantPermissions(userId, expandedPermission, context);
+        				return true;
+        			}
+    		    }
+    			index++;
+    		}
+    		
+    		// finally check dynamic permission (outside the loop)
+    		if (hasDynamicPermission(userId, expandedPermission, context)) {
+    		    // permission granted
+    		    handleAutoGrantPermissions(userId, expandedPermission, context);
+    		    return true;
+    		}
+		} else {
+		    // legacy mode; only call static permission check; no auto grants
+		    Debug.logVerbose("Legacy permission detected; falling back to static permission check", module);
+		    return hasStaticPermission(userId, expandedPermission, context);
+		}
+		return false;
+	}
+	
+	protected void handleAutoGrantPermissions(String userId, String expandedPermission, Map<String, ? extends Object> context) {	    	    
+	    List<String> granted = getAutoGrantPermissions(userId, expandedPermission, context);
+	    if (granted != null && granted.size() > 0) {
+            List<String> alreadyGranted = autoGrant.get();
+            if (alreadyGranted == null) {
+                alreadyGranted = FastList.newInstance();
+            }
+            
+            // expand the auto-grant permissions
+            for (String toGrant : granted) {
+                if (UtilValidate.isNotEmpty(toGrant)) {
+                    if (Debug.verboseOn()) Debug.logVerbose("Adding auto-grant permission -- " + toGrant, module);
+                    alreadyGranted.add(FlexibleStringExpander.expandString(toGrant, context)); 
+                }
+            }
+            autoGrant.set(granted);
+            uid.set(userId);
+        }
+	}
+}

Added: ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/Authorization.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/Authorization.java?rev=769928&view=auto
==============================================================================
--- ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/Authorization.java (added)
+++ ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/Authorization.java Wed Apr 29 21:01:25 2009
@@ -0,0 +1,39 @@
+package org.ofbiz.security.authz;
+
+import java.util.Map;
+
+import javax.servlet.http.HttpSession;
+
+import org.ofbiz.entity.GenericDelegator;
+
+public interface Authorization {
+
+	/**
+	 * Test to see if the specified user has permission
+	 * 
+	 * @param userId the user's userId
+	 * @param permission the raw permission string
+	 * @param context name/value pairs used for permission lookup
+	 * @param expanded true if the permission string is already expanded, false if it will contain ${} context values
+	 * @return true if the user has permission
+	 */
+	public boolean hasPermission(String userId, String permission, Map<String, ? extends Object> context, boolean expanded);
+	
+	/**
+     * Test to see if the specified user has permission
+     * 
+     * @param session HttpSession used to obtain the userId
+     * @param permission the raw permission string
+     * @param context name/value pairs used for permission lookup
+     * @param expanded true if the permission string is already expanded, false if it will contain ${} context values
+     * @return true if the user has permission
+     */
+    public boolean hasPermission(HttpSession session, String permission, Map<String, ? extends Object> context, boolean expanded);
+	
+    /**
+     * Method for injecting the delegator object
+     * 
+     * @param delegator the GenericDelegator object to use for the Authorization implementation
+     */
+    public void setDelegator(GenericDelegator delegator);
+}

Added: ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/AuthorizationFactory.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/AuthorizationFactory.java?rev=769928&view=auto
==============================================================================
--- ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/AuthorizationFactory.java (added)
+++ ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/AuthorizationFactory.java Wed Apr 29 21:01:25 2009
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.ofbiz.security.authz;
+
+import org.ofbiz.base.config.GenericConfigException;
+import org.ofbiz.base.config.SecurityConfigUtil;
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.UtilProperties;
+import org.ofbiz.base.util.UtilValidate;
+import org.ofbiz.entity.GenericDelegator;
+import org.ofbiz.security.SecurityConfigurationException;
+import org.w3c.dom.Element;
+
+/**
+ * Authorization Factory
+ *
+ * This Factory class returns an instance of a security authorization implementation.
+ *
+ * Setting the security implementation className is done in security.xml.
+ * If no customized security name is given, the default implementation will be used (EntityAuthorization)
+ */
+public class AuthorizationFactory {
+
+    public static final String module = AuthorizationFactory.class.getName();
+    public static final String DEFAULT_AUTHORIZATION = "org.ofbiz.security.authz.EntityAuthorization";
+
+    private static String securityName = null;
+    private static Element rootElement = null;
+    private static SecurityConfigUtil.SecurityInfo securityInfo = null;
+
+    /**
+     * Returns an instance of a Security implementation as defined in the security.xml by defined name
+     * in security.properties.
+     *
+     * @param delegator the generic delegator
+     * @return instance of security implementation (default: OFBizSecurity)
+     */
+    @SuppressWarnings("unchecked")
+    public static Authorization getInstance(GenericDelegator delegator) throws SecurityConfigurationException {
+        Authorization security = null;
+
+        // Make securityName a singleton
+        if (securityName == null) {
+            String _securityName = UtilProperties.getPropertyValue("security.properties", "security.context");
+            securityName = _securityName;
+        }
+
+        if (Debug.verboseOn()) Debug.logVerbose("[AuthorizationFactory.getInstance] Security implementation context name from security.properties: " + securityName, module);
+
+        synchronized (AuthorizationFactory.class) {
+            try {
+                ClassLoader loader = Thread.currentThread().getContextClassLoader();
+                Class c = loader.loadClass(getAuthorizationClass(securityName));
+                security = (Authorization) c.newInstance();
+                security.setDelegator(delegator);
+            } catch (ClassNotFoundException cnf) {
+                throw new SecurityConfigurationException("Cannot load security implementation class", cnf);
+            } catch (InstantiationException ie) {
+                throw new SecurityConfigurationException("Cannot get instance of the security implementation", ie);
+            } catch (IllegalAccessException iae) {
+                throw new SecurityConfigurationException(iae.getMessage(), iae);
+            }
+        }
+
+        if (Debug.verboseOn()) Debug.logVerbose("[AuthorizationFactory.getInstance] Security implementation successfully loaded!!!", module);
+
+        return security;
+    }
+
+    /**
+     * Returns the class name of  a custom Authorization implementation.
+     * The default class name (org.ofbiz.security.EntityAuthorization) may be overridden by a customized implementation
+     * class name in security.xml.
+     *
+     * @param securityName the security context name to be looked up
+     * @return className the class name of the security implementation
+     * @throws SecurityConfigurationException
+     */
+    private static String getAuthorizationClass(String securityName) throws SecurityConfigurationException {
+        String className = null;
+
+        if (Debug.verboseOn())
+            Debug.logVerbose("[AuthorizationFactory.getSecurityClass] Security implementation context name: " + securityName, module);
+
+        // Only load rootElement again, if not yet loaded (singleton)
+        if (rootElement == null) {
+            try {
+                SecurityConfigUtil.getXmlDocument();
+                Element _rootElement = SecurityConfigUtil.getXmlRootElement();
+
+                rootElement = _rootElement;
+            } catch (GenericConfigException e) {
+                Debug.logError(e, "Error getting Security Config XML root element", module);
+                return null;
+            }
+        }
+
+        if (securityInfo == null) {
+            SecurityConfigUtil.SecurityInfo _securityInfo = SecurityConfigUtil.getSecurityInfo(securityName);
+
+            // Make sure, that the security context name is defined and present
+            if (_securityInfo == null) {
+                throw new SecurityConfigurationException("ERROR: no security definition was found with the name " + securityName + " in security.xml");
+            }
+            securityInfo = _securityInfo;
+        }
+
+        // This is the default implementation and uses org.ofbiz.security.OFBizSecurity
+        if (UtilValidate.isEmpty(securityInfo.className)) {
+            className = DEFAULT_AUTHORIZATION;
+        } else {
+            // Use a customized security
+            className = securityInfo.className;
+        }
+
+        if (Debug.verboseOn()) Debug.logVerbose("[AuthorizationFactory.getSecurity] Authorization implementation " + className + " for security name " + securityName + " successfully loaded!!!", module);
+        return className;
+    }
+}

Added: ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/EntityAuthorization.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/EntityAuthorization.java?rev=769928&view=auto
==============================================================================
--- ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/EntityAuthorization.java (added)
+++ ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/EntityAuthorization.java Wed Apr 29 21:01:25 2009
@@ -0,0 +1,210 @@
+package org.ofbiz.security.authz;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpSession;
+
+import javolution.util.FastList;
+
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.UtilMisc;
+import org.ofbiz.base.util.UtilValidate;
+import org.ofbiz.base.util.cache.UtilCache;
+import org.ofbiz.entity.GenericDelegator;
+import org.ofbiz.entity.GenericEntityException;
+import org.ofbiz.entity.GenericValue;
+import org.ofbiz.entity.util.EntityUtil;
+import org.ofbiz.security.authz.da.DynamicAccessFactory;
+import org.ofbiz.security.authz.da.DynamicAccessHandler;
+
+public class EntityAuthorization extends AbtractAuthorization {
+
+    private static final String module = EntityAuthorization.class.getName();
+    
+	/**
+     * UtilCache to cache a Collection of UserLoginSecurityGroup entities for each UserLogin, by userLoginId.
+     */
+    private static UtilCache<String, List<GenericValue>> userLoginSecurityGroupByUserLoginId = new UtilCache<String, List<GenericValue>>("security.UserLoginSecurityGroupByUserLoginId");
+
+    /**
+     * UtilCache to cache whether or not a certain SecurityGroupPermission row exists or not.
+     * For each SecurityGroupPermissionPK there is a Boolean in the cache specifying whether or not it exists.
+     * In this way the cache speeds things up whether or not the user has a permission.
+     */
+    private static UtilCache<GenericValue, Boolean> securityGroupPermissionCache = new UtilCache<GenericValue, Boolean>("security.SecurityGroupPermissionCache");
+
+    /**
+     * UtilCache to cache Permission Auto Grant permissions
+     */
+    private static UtilCache<String, List<String>> permissionAutoGrantCache = new UtilCache<String, List<String>>("security.PermissionAutoGrantCache");
+    
+    protected GenericDelegator delegator; 
+    
+	@Override
+	public List<String> getAutoGrantPermissions(String userId, String permission, Map<String, ? extends Object> context) {
+	    if (Debug.verboseOn()) Debug.logVerbose("Running getAutoGrantPermissions()", module);
+	    boolean checking = true;
+	    String checkString = permission;
+	    
+	    while (checking) {
+	        List<String> autoGrant = getPermissionAutoGrant(checkString);
+	        if (autoGrant != null && autoGrant.size() > 0) {
+	            return autoGrant;
+	        }
+	        if (checkString.indexOf(":") > -1) {
+	            checkString = checkString.substring(0, checkString.lastIndexOf(":"));
+	        } else {
+	            checking = false;
+	        }
+	    }
+		return null;
+	}
+
+	@Override
+	public boolean hasDynamicPermission(String userId, String permission, Map<String, ? extends Object> context) {
+	    if (Debug.verboseOn()) Debug.logVerbose("Running hasDynamicPermission()", module);	    
+		String permissionId = permission.substring(0, permission.lastIndexOf(":"));
+		boolean checking = true;
+		
+		// find the dynamic access implementation
+		String dynamicAccess = null;
+		while (checking) {
+		    if (Debug.verboseOn()) Debug.logVerbose("Looking for dynamic access for permission -- " + permissionId, module);
+		    dynamicAccess = getPermissionDynamicAccess(permissionId);
+		    if (UtilValidate.isEmpty(dynamicAccess)) {
+		        if (permissionId.indexOf(":") > -1) {
+		            permissionId = permissionId.substring(0, permissionId.lastIndexOf(":"));
+		        } else {
+		            Debug.logVerbose("No sections left to check; no dynamic access implementation found", module);
+		            checking = false;
+		        }
+		    } else {
+		        if (Debug.verboseOn()) Debug.logVerbose("Dynamic access implementation found : " + dynamicAccess, module);
+		        checking = false;
+		    }
+		}
+		
+		// if one exists invoke it
+		if (UtilValidate.isNotEmpty(dynamicAccess)) {
+		    // load the dynamic access handler and invoke it
+		    if (Debug.verboseOn()) Debug.logVerbose("Loading DynamicAccessHandler for -- " + dynamicAccess, module);
+		    DynamicAccessHandler dah = DynamicAccessFactory.getDynamicAccessHandler(delegator, dynamicAccess);
+		    if (dah != null) {
+		        if (Debug.verboseOn()) Debug.logVerbose("Calling DynamicAccessHandler : " + dah.getClass().getName(), module);
+		        return dah.handleDynamicAccess(dynamicAccess, userId, permission, context);
+		    } else {
+		        if (Debug.verboseOn()) {
+		            Debug.logVerbose("No DynamicAccessHandler found for pattern matching -- " + dynamicAccess, module);
+		        }
+		    }
+		}
+		return false;
+	}
+
+	@Override
+	public boolean hasStaticPermission(String userId, String permission, Map<String, ? extends Object> context) {
+	    if (Debug.verboseOn()) Debug.logVerbose("Running hasStaticPermission()", module);
+	    Iterator<GenericValue> iterator = getUserLoginSecurityGroupByUserLoginId(userId);
+        GenericValue userLoginSecurityGroup = null;
+
+        while (iterator.hasNext()) {
+            userLoginSecurityGroup = iterator.next();
+            if (securityGroupHasPermission(userLoginSecurityGroup.getString("groupId"), permission)) {
+                return true;
+            }
+        }
+        return false;
+	}
+	
+	public boolean hasPermission(HttpSession session, String permission, Map<String, ? extends Object> context, boolean expanded) {
+        GenericValue userLogin = (GenericValue) session.getAttribute("userLogin");
+        if (userLogin != null) {
+            return hasPermission(userLogin.getString("userLoginId"), permission, context, expanded);
+        }
+        return false;
+    }
+	
+	public void setDelegator(GenericDelegator delegator) {
+	    this.delegator = delegator;
+	}
+	
+	private Iterator<GenericValue> getUserLoginSecurityGroupByUserLoginId(String userId) {
+        List<GenericValue> collection = userLoginSecurityGroupByUserLoginId.get(userId);
+
+        if (collection == null) {
+            try {
+                collection = delegator.findByAnd("UserLoginSecurityGroup", UtilMisc.toMap("userLoginId", userId), null);
+                
+                // make an empty collection to speed up the case where a userLogin belongs to no security groups, only with no exception of course
+                if (collection == null) {
+                    collection = FastList.newInstance();
+                }
+                userLoginSecurityGroupByUserLoginId.put(userId, collection);
+            } catch (GenericEntityException e) {
+                Debug.logWarning(e, module);
+            }
+        }
+        
+        // filter each time after cache retrieval, i.e. cache will contain entire list
+        collection = EntityUtil.filterByDate(collection, true);
+        return collection.iterator();
+    }
+	
+    private boolean securityGroupHasPermission(String groupId, String permission) {
+        GenericValue securityGroupPermissionValue = delegator.makeValue("SecurityGroupPermission",
+                UtilMisc.toMap("groupId", groupId, "permissionId", permission));
+        Boolean exists = (Boolean) securityGroupPermissionCache.get(securityGroupPermissionValue);
+
+        if (exists == null) {
+            try {
+                if (delegator.findOne(securityGroupPermissionValue.getEntityName(), securityGroupPermissionValue, false) != null) {
+                    exists = Boolean.TRUE;
+                } else {
+                    exists = Boolean.FALSE;
+                }
+            } catch (GenericEntityException e) {
+                exists = Boolean.FALSE;
+                Debug.logWarning(e, module);
+            }
+            securityGroupPermissionCache.put(securityGroupPermissionValue, exists);
+        }
+        return exists.booleanValue();
+    }	
+    
+    private List<String> getPermissionAutoGrant(String permission) {
+        List<String> autoGrants = permissionAutoGrantCache.get(permission);
+        if (autoGrants == null) {
+            autoGrants = FastList.newInstance();
+            
+            List<GenericValue> values = null;
+            try {
+                values = delegator.findByAnd("SecurityPermissionAutoGrant", UtilMisc.toMap("permissionId", permission), null);
+            } catch (GenericEntityException e) {
+                Debug.logWarning(e, module);
+            }
+            
+            if (values != null && values.size() > 0) {
+                for (GenericValue v : values) {
+                    autoGrants.add(v.getString("grantPermission"));
+                }
+            }
+            permissionAutoGrantCache.put(permission, autoGrants);
+        }
+        return autoGrants;
+    }
+    
+    private String getPermissionDynamicAccess(String perm) {
+        GenericValue permission = null;
+        try {
+            permission = delegator.findOne("SecurityPermission", UtilMisc.toMap("permissionId", perm), true);
+        } catch (GenericEntityException e) {
+            Debug.logWarning(e, module);
+        }
+        if (permission != null) {
+            return permission.getString("dynamicAccess");
+        }
+        return null;
+    }
+}

Added: ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/DynamicAccess.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/DynamicAccess.java?rev=769928&view=auto
==============================================================================
--- ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/DynamicAccess.java (added)
+++ ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/DynamicAccess.java Wed Apr 29 21:01:25 2009
@@ -0,0 +1,31 @@
+package org.ofbiz.security.authz.da;
+
+import java.util.Map;
+
+import org.ofbiz.entity.GenericDelegator;
+
+public interface DynamicAccess {
+
+	/**
+	 * Processes the dynamic permission check
+	 * 
+	 * @param userId the user's userId
+	 * @param permission the raw permission string
+	 * @param context name/value pairs needed for permission lookup
+	 * @return true if the user has permission
+	 */
+	public boolean hasPermission(String userId, String permission, Map<String, ? extends Object> context);
+
+	/**
+	 * Returns the name of the permission this object handles
+	 * @return permission name
+	 */
+	public String getPermissionName();
+	
+	/**
+     * Method for injecting the delegator object
+     * 
+     * @param delegator the GenericDelegator object to use for the Authorization implementation
+     */
+    public void setDelegator(GenericDelegator delegator);
+}

Added: ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/DynamicAccessFactory.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/DynamicAccessFactory.java?rev=769928&view=auto
==============================================================================
--- ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/DynamicAccessFactory.java (added)
+++ ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/DynamicAccessFactory.java Wed Apr 29 21:01:25 2009
@@ -0,0 +1,133 @@
+package org.ofbiz.security.authz.da;
+
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javolution.util.FastList;
+
+import org.ofbiz.base.util.AbstractResolver;
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.ObjectType;
+import org.ofbiz.base.util.cache.UtilCache;
+import org.ofbiz.entity.GenericDelegator;
+
+public class DynamicAccessFactory {
+    
+    /**
+     * Cache to store the DynamicAccess implementations
+     */
+    private static UtilCache<String,DynamicAccessHandler> dynamicAccessHandlerCache = new UtilCache<String,DynamicAccessHandler>("security.DynamicAccessHandlerCache");
+    private static final String module = DynamicAccessFactory.class.getName();
+    private static AccessHandlerResolver resolver = new AccessHandlerResolver();
+    
+    public static DynamicAccessHandler getDynamicAccessHandler(GenericDelegator delegator, String accessString) {
+        if (dynamicAccessHandlerCache.size() == 0) { // should always be at least 1
+            loadAccessHandlers(delegator);
+        }
+        
+        Set<? extends String> patterns = dynamicAccessHandlerCache.getCacheLineKeys();
+        for (String pattern : patterns) {
+            if (!pattern.equals("*")) { // ignore the default pattern for now
+                Debug.logInfo("Checking DOH pattern : " + pattern, module);
+                Pattern p = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
+                Matcher m = p.matcher(accessString);
+                if (m.find()) {
+                    Debug.logInfo("Pattern [" + pattern + "] matched -- " + accessString, module);
+                    return dynamicAccessHandlerCache.get(pattern); 
+                }
+            }
+        }
+        
+        return dynamicAccessHandlerCache.get("*");
+    }
+    
+    private static void loadAccessHandlers(GenericDelegator delegator) {
+        List<DynamicAccessHandler> handlers = resolver.getHandlers();
+        for (DynamicAccessHandler handler : handlers) {
+            handler.setDelegator(delegator);
+            dynamicAccessHandlerCache.put(handler.getPattern(), handler);
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    public static DynamicAccess loadDynamicAccessObject(GenericDelegator delegator, String accessString) {
+        DynamicAccess da = null;
+        Class<DynamicAccess> clazz;
+        try {
+            clazz = (Class<DynamicAccess>) ObjectType.loadClass(accessString);
+        } catch (ClassNotFoundException e) {
+            Debug.logError(e, module);
+            return null;
+        } catch (ClassCastException e) {
+            Debug.logError(e, module);
+            return null;
+        }
+        
+        if (clazz != null) {
+            try {
+                da = clazz.newInstance();
+                da.setDelegator(delegator);
+            } catch (InstantiationException e) {
+                Debug.logError(e, module);
+                return null;             
+            } catch (IllegalAccessException e) {
+                Debug.logError(e, module);
+                return null; 
+            }
+        }
+        
+        return da;
+    }
+    
+    static class AccessHandlerResolver extends AbstractResolver {
+        
+        protected List<DynamicAccessHandler> handlers;
+                
+        protected List<DynamicAccessHandler> getHandlers() {
+            handlers = FastList.newInstance();
+            find("org.ofbiz");
+            return handlers;
+        }
+        
+        @SuppressWarnings("unchecked")
+        @Override
+        public void resolveClass(Class clazz) {
+            Class theClass = clazz;
+            boolean checking = true;
+            boolean found = false;
+            
+            while (checking) {
+                Class[] ifaces = theClass.getInterfaces();
+                for (Class iface : ifaces) {
+                    if (DynamicAccessHandler.class.equals(iface)) {
+                        loadHandler(theClass);
+                        found = true;
+                    }
+                }
+                
+                if (!found) {
+                    theClass = theClass.getSuperclass();
+                    if (theClass == null) {
+                        checking = false;
+                    }
+                } else {
+                    checking = false;
+                }
+            }   
+        }
+            
+        private void loadHandler(Class<DynamicAccessHandler> clazz) {
+            DynamicAccessHandler handler = null;
+            try {
+                handler = clazz.newInstance();
+                handlers.add(handler);
+            } catch (InstantiationException e) {
+                Debug.logError(e, module);       
+            } catch (IllegalAccessException e) {
+                Debug.logError(e, module);
+            }
+        }
+    }
+}

Added: ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/DynamicAccessHandler.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/DynamicAccessHandler.java?rev=769928&view=auto
==============================================================================
--- ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/DynamicAccessHandler.java (added)
+++ ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/DynamicAccessHandler.java Wed Apr 29 21:01:25 2009
@@ -0,0 +1,35 @@
+package org.ofbiz.security.authz.da;
+
+import java.util.Map;
+
+import org.ofbiz.entity.GenericDelegator;
+
+public interface DynamicAccessHandler {
+    
+    /**
+     * Method invoked to call the DynamicAccess implementation
+     * 
+     * @param accessString Access string for the permission
+     * @param userId the user's userId
+     * @param permission the raw permission string
+     * @param context name/value pairs needed for permission lookup
+     * @return the value returned from the DynamicAccess implementation
+     */
+    public boolean handleDynamicAccess(String accessString, String userId, String permission, Map<String, ? extends Object> context);
+    
+    /**
+     * Returns the handlers matching pattern. 
+     * Example: ^service:(.*)$ 
+     * Example: (^.*\.groovy$)
+     * 
+     * @return String containing the pattern this handler will control
+     */
+    public String getPattern();
+    
+    /**
+     * Method for injecting the delegator object
+     * 
+     * @param delegator the GenericDelegator object to use for the Authorization implementation
+     */
+    public void setDelegator(GenericDelegator delegator);
+}

Added: ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/GroovyDaHandler.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/GroovyDaHandler.java?rev=769928&view=auto
==============================================================================
--- ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/GroovyDaHandler.java (added)
+++ ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/GroovyDaHandler.java Wed Apr 29 21:01:25 2009
@@ -0,0 +1,63 @@
+package org.ofbiz.security.authz.da;
+
+import java.util.Map;
+
+import javolution.util.FastMap;
+
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.GeneralException;
+import org.ofbiz.base.util.GroovyUtil;
+import org.ofbiz.entity.GenericDelegator;
+
+public class GroovyDaHandler implements DynamicAccessHandler {
+
+    private static final String module = GroovyDaHandler.class.getName();
+    protected GenericDelegator delegator;
+    
+    public String getPattern() {
+        return "(^.*\\.groovy$)";
+    }
+
+    public boolean handleDynamicAccess(String accessString, String userId, String permission, Map<String, ? extends Object> context) {
+        Map<String,Object> bindings = FastMap.newInstance();
+        bindings.put("delegator", delegator);
+        bindings.put("accessString", accessString);
+        bindings.put("permission", permission);
+        bindings.put("userId", userId);
+        bindings.put("context", context);
+        
+        Debug.log("Attempting to call groovy script : " + accessString, module);
+        Object result = null;
+        
+        if (accessString.startsWith("component://")) {
+            // loaded using the OFBiz location API            
+            try {
+                result = GroovyUtil.runScriptAtLocation(accessString, bindings);
+            } catch (GeneralException e) {
+                Debug.logWarning(e, module);
+            }
+            
+        } else {
+            // try the standard class path
+            String classpathString = accessString.substring(0, accessString.lastIndexOf("."));
+            try {
+                result = GroovyUtil.runScriptFromClasspath(classpathString, bindings);
+            } catch (GeneralException e) {
+                Debug.logWarning(e, module);
+            }
+        }
+       
+        // parse the result
+        if (result != null && (result instanceof Boolean)) {
+            return (Boolean) result;
+        } else {
+            Debug.logWarning("Groovy DynamicAccess implementation did not return a boolean [" + accessString + "]", module);
+        }
+        
+        return false;
+    }
+
+    public void setDelegator(GenericDelegator delegator) {
+        this.delegator = delegator;        
+    }       
+}

Added: ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/ObjectDaHandler.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/ObjectDaHandler.java?rev=769928&view=auto
==============================================================================
--- ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/ObjectDaHandler.java (added)
+++ ofbiz/trunk/framework/security/src/org/ofbiz/security/authz/da/ObjectDaHandler.java Wed Apr 29 21:01:25 2009
@@ -0,0 +1,44 @@
+package org.ofbiz.security.authz.da;
+
+import java.util.Map;
+
+import org.ofbiz.base.util.cache.UtilCache;
+import org.ofbiz.entity.GenericDelegator;
+
+public class ObjectDaHandler implements DynamicAccessHandler {
+
+    private static UtilCache<String,DynamicAccess> dynamicAccessCache = new UtilCache<String,DynamicAccess>("security.DynamicAccessCache");
+    
+    protected GenericDelegator delegator;
+    
+    public void setDelegator(GenericDelegator delegator) {
+        this.delegator = delegator;   
+    }
+    
+    public String getPattern() {
+        // returns "*" as a fall back pattern (catch all)
+        // if no other handler comes back this handler will catch
+        return "*";
+    }
+
+    public boolean handleDynamicAccess(String accessString, String userId, String permission, Map<String, ? extends Object> context) {
+        DynamicAccess da = getDynamicAccessObject(accessString);
+        if (da != null) {
+            return da.hasPermission(userId, permission, context);
+        }
+        return false;
+    }
+    
+    private DynamicAccess getDynamicAccessObject(String name) {
+        DynamicAccess da = dynamicAccessCache.get(name);
+        
+        if (da == null) {
+            da = DynamicAccessFactory.loadDynamicAccessObject(delegator, name);
+            if (da != null) {
+                dynamicAccessCache.put(name, da);
+            }
+        }
+        
+        return da;
+    }   
+}