Jump to content

ACL System Performance Hit?


ionik

Recommended Posts

Hello,

 

I have constructed a very in-depth and quite complex ACL system.

 

Question I am wondering is will it take a performance hit on checking permissions, the structure for the ACL is quite simple, there are roles and there are resources. A user can have an unlimited number of roles assigned.

 

Simple permission is based on a scale of 1-4 1 being guest 4 being root, all roles can be children of another role.

 

Resources work the same way as roles.

 

The resource permissions can be finely tuned down to allowing/denying a user access, the hierarchy for resource permission check with each one overwriting the previous permission.

 

Here is a traceback of a check

[*]Resource Parents are Check (if exist)

[*]Resource Level is checked against roles and all parent roles level

[*]Resource specific role access is checked

[*]Resource specific role deny is checked

[*]Resource specific user allow is checked

[*]Resource specific user deny is checked

 

I have it enabled to allow tracing of the resource permission checking it comes out quite large, while the resource has 4 parents and the role has 2 parents.

 

Specific allow/deny access is based soley on array comparison but the parent looping is qhat I am wondering will cause a performance hit.

 

Here is the code for the class



class Unus_Acl extends Unus_Object
{

    /*
    * Builds the registry
    */

    public function __construct()
    {
        $this->setData('roles', new Unus_Acl_Role())
            ->setData('resource', new Unus_Acl_Resource());
    }

    /*
    * Overload to the data object
    */

    public function __get($name)
    {
        return $this->getData($name);
    }

    /*
    * Overload to the data object
    */

    public function __set($name, $value)
    {
        $this->setData($name, $value);
    }


    /*
    * Checks if a user is allowed to access a resource
    *
    * Resource permission are checked in the following order each check overwrites the previous
    *
    * 1. Parents (if exists)
    * 2. Level
    * 3. Role Specific Permissions (if set)
    * 4. User Specific Permissions (if set)
    *
    *
    * @param  string  resource  Resource name to check
    */

    public function isAllowed($resource)
    {
        $traceRoute = array();
        $return = false;

        // get the resource level
        $level = $this->resource->getLevel($resource);


        if (!$this->resource->hasParent($resource)) {
            $traceRoute[] = 'Resource has no parents; inheritance checks will be skipped';
        } else {
            // Check Parent Information
            $parent = $this->resource->getResource($this->resource->getParent($resource));
            $traceRoute[] = 'Resource is child of '.$parent->getIdentifier().' checking inheritance permissions';

            $traceRoute[] = 'Begin Parent Checks';

            if ($this->isAllowed($parent->getIdentifier())) {
                $return = true;
                $traceRoute[] = 'Parent resource allowes permission';
            } else {
                $traceRoute[] = 'Parent resource denies permission';
            }

            // add parent trace to trace
            $traceRoute = array_merge($traceRoute, $this->getData('traceroute_'.$parent->getIdentifier()));

            $traceRoute[] = 'End Parent Checks';

        }

        // allowed roles
        $rolesAllow = $this->resource->getRolesAllow($resource);

        // Allow Trace
        if (count($rolesAllow) == 0) {
            $traceRoute[] = 'Resource has no role defined allow access checks will be skipped';
        }

        // denied roles
        $rolesDeny = $this->resource->getRolesDeny($resource);

        // Deny Trace
        if (null == $rolesAllow) {
            $traceRoute[] = 'Resource has no role defined deny access checks will be skipped';
        }

        // allowed users
        $usersAllow = $this->resource->getUsersAllow($resource);

        // Allow Trace
        if (null == $usersAllow) {
            $traceRoute[] = 'Resource has no user defined allow access checks will be skipped';
        }

        // denied users
        $usersDeny = $this->resource->getUsersDeny($resource);

        // Deny Trace
        if (null == $usersDeny) {
            $traceRoute[] = 'Resource has no user defined deny access checks will be skipped';
        }

        // get a user's roles
        $roles = Unus::registry('user')->getRoles();
        if (is_array($roles)) {
            foreach ($roles as $k => $v) {
                $return = $this->_checkRolePermission($v, $level, $rolesAllow, $rolesDeny);
                // add parent trace to trace
                $traceRoute = array_merge($traceRoute, $this->getData('tmp_trace_'.$v->getIdentifier()));
                $this->unsetData('tmp_trace_'.$v->getIdentifier());
            }
        } else {
            $return = $this->_checkPermission($roles);
            $traceRoute = array_merge($traceRoute, $this->getData('tmp_trace_'.$v->getIdentifier()));
            $this->unsetData('tmp_trace_'.$v->getIdentifier());
        }

        // User Based Permissions
        if (null != $usersAllow) {
            if (in_array(Unus::registry('user')->id, $usersAllow)) {
                $traceRoute[] = 'User is allowed based on per-user permissions';
                $return = true;
            }
        }

        if (null != $usersDeny) {
            if (in_array(Unus::registry('user')->id, $usersDeny)) {
                $traceRoute[] = 'User is denied based on per-user permissions';
                $return = false;
            }
        }

        // Set the traceroute

        $this->setTraceRoute($resource, $traceRoute);

        return $return;
    }

    private function _checkRolePermission($role, $level, $roleAllow, $roleDeny)
    {
        $traceRoute = array();
        // check if this role has parents build array
        if ($role->hasParents()) {
            $traceRoute[] = 'Role '.$role->getIdentifier().' is a child role checking parents';
            if(is_array($role->getParents())) {
                foreach($role->getParents() as $pk => $pv) {
                    $pObject = $this->roles->getRole($pv);
                    $traceRoute[] = 'Checking parent role '.$pObject->getIdentifier().' permissions';
                    if ($pObject->getLevel() >= $level) {
                        $return = true;
                        $traceRoute[] = 'Parent Role '.$pObject->getIdentifier().' allows access';
                    } else {
                        $traceRoute[] = 'Parent Role '.$pObject->getIdentifier().' denies access';
                    }
                }
            } else {
                $pObject = $this->roles->getRole($role->getParents());
                $traceRoute[] = 'Checking parent role '.$pObject->getIdentifier().' permissions';
                if ($pObject->getLevel() >= $level) {
                    $return = true;
                    $traceRoute[] = 'Parent Role '.$pObject->getIdentifier().' allows access';
                } else {
                    $traceRoute[] = 'Parent Role '.$pObject->getIdentifier().' denies access';
                }
            }
        }

        // First we check the level
        if ($role->getLevel() >= $level) {
            $return = true;
            $traceRoute[] = 'Role '.$role->getIdentifier().' is allowed based on level';
        } else {
            $traceRoute[] = 'Role '.$role->getIdentifier().' is denied based on level';
        }

        // Then we check the roles
        // these overwrite the level permission
        // roles not in this will be set to deny
        // root roles are not affected by this
        if (null != $rolesAllow) {
            if (in_array($role->getId(), $rolesAllow)) {
                $return = true;
                $traceRoute[] = 'Role '.$role->getIdentifier().' is allowed based on specific roles';
            } elseif ($role->getId() != 4) {
                $return = false;
                $traceRoute[] = 'Role '.$role->getIdentifier().' is denied as based that it is not a Root role and not in allowed list';
            }
        }


        // DENY
        // This specifically denies roles only
        // roles not in this array are not considered being allowed
        if (null != $rolesDeny) {
            if (in_array($role->getId(), $rolesDeny) && $role->getId() != 4) {
                $return = true;
                $traceRoute[] = 'Role '.$role->getIdentifier().' is denied based on specific roles';
            }
        }

        $this->setData('tmp_trace_'.$role->getIdentifier(), $traceRoute);

        return $return;
    }

    /*
    * Builds a user's role permission traceroute
    *
    * @param  array  traceroute  Traced array of how the permission is parsed
    *
    * @return this
    */

    public function setTraceRoute($resource, $traceRoute)
    {
        $this->setData('traceroute_'.$resource, $traceRoute);
        return $this;
    }

    /*
    * Gets a resource traceroute check
    *
    * @param  array  traceroute  Traced array of how the permission is parsed
    *
    * @return
    */

    public function getTraceRoute($resource)
    {
        $trace = $this->getData('traceroute_'.$resource);

        $str = null;

        foreach ($trace as $k => $v) {
            $str .= '{'.$k.'} '.$v.' <br />';
        }

        return $str;
    }


}

 

There is a small bug with the tracing that keeps adding the same role name..



{0} Resource is child of test_3 checking inheritance permissions
{1} Begin Parent Checks
{2} Parent resource allowes permission
{3} Resource is child of test_2 checking inheritance permissions
{4} Begin Parent Checks
{5} Parent resource allowes permission
{6} Resource is child of test checking inheritance permissions
{7} Begin Parent Checks
{8} Parent resource allowes permission
{9} Resource is child of main checking inheritance permissions
{10} Begin Parent Checks
{11} Parent resource denies permission
{12} Resource has no parents; inheritance checks will be skipped
{13} Resource has no role defined allow access checks will be skipped
{14} Resource has no role defined deny access checks will be skipped
{15} Resource has no user defined allow access checks will be skipped
{16} Resource has no user defined deny access checks will be skipped
{17} Role Guest is denied based on level
{18} Role Ioana is a child role checking parents
{19} Checking parent role Ioana permissions
{20} Parent Role Ioana denies access
{21} Role Ioana is denied based on level
{22} End Parent Checks
{23} Resource has no role defined allow access checks will be skipped
{24} Resource has no role defined deny access checks will be skipped
{25} Resource has no user defined allow access checks will be skipped
{26} Resource has no user defined deny access checks will be skipped
{27} Role Guest is allowed based on level
{28} Role Ioana is a child role checking parents
{29} Checking parent role Ioana permissions
{30} Parent Role Ioana allows access
{31} Role Ioana is allowed based on level
{32} End Parent Checks
{33} Resource has no role defined allow access checks will be skipped
{34} Resource has no role defined deny access checks will be skipped
{35} Resource has no user defined allow access checks will be skipped
{36} Resource has no user defined deny access checks will be skipped
{37} Role Guest is allowed based on level
{38} Role Ioana is a child role checking parents
{39} Checking parent role Ioana permissions
{40} Parent Role Ioana allows access
{41} Role Ioana is allowed based on level
{42} End Parent Checks
{43} Resource has no role defined allow access checks will be skipped
{44} Resource has no role defined deny access checks will be skipped
{45} Resource has no user defined allow access checks will be skipped
{46} Resource has no user defined deny access checks will be skipped
{47} Role Guest is allowed based on level
{48} Role Ioana is a child role checking parents
{49} Checking parent role Ioana permissions
{50} Parent Role Ioana allows access
{51} Role Ioana is allowed based on level
{52} End Parent Checks
{53} Resource has no role defined allow access checks will be skipped
{54} Resource has no role defined deny access checks will be skipped
{55} Resource has no user defined allow access checks will be skipped
{56} Resource has no user defined deny access checks will be skipped
{57} Role Guest is allowed based on level
{58} Role Ioana is a child role checking parents
{59} Checking parent role Ioana permissions
{60} Parent Role Ioana allows access
{61} Role Ioana is allowed based on level

Link to comment
https://forums.phpfreaks.com/topic/156718-acl-system-performance-hit/
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.