Jump to content

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

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • 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.