Advertisement
Guest User

Phalcon ACL database adapter with export to memory adapter

a guest
Sep 24th, 2015
284
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 16.99 KB | None | 0 0
  1. <?php
  2. /*
  3.   +------------------------------------------------------------------------+
  4.   | Phalcon Framework                                                      |
  5.   +------------------------------------------------------------------------+
  6.   | Copyright (c) 2011-2014 Phalcon Team (http://www.phalconphp.com)       |
  7.   +------------------------------------------------------------------------+
  8.   | This source file is subject to the New BSD License that is bundled     |
  9.   | with this package in the file docs/LICENSE.txt.                        |
  10.   |                                                                        |
  11.   | If you did not receive a copy of the license and are unable to         |
  12.   | obtain it through the world-wide-web, please send an email             |
  13.   | to license@phalconphp.com so we can send you a copy immediately.       |
  14.   +------------------------------------------------------------------------+
  15.   | Authors: Andres Gutierrez <andres@phalconphp.com>                      |
  16.   |          Eduar Carvajal <eduar@phalconphp.com>                         |
  17.   +------------------------------------------------------------------------+
  18. */
  19. namespace Phalcon\Acl\Adapter;
  20.  
  21. use Phalcon\Db;
  22. use Phalcon\Acl\Adapter;
  23. use Phalcon\Acl\AdapterInterface;
  24. use Phalcon\Acl\Exception;
  25. use Phalcon\Acl\Resource;
  26. use Phalcon\Acl;
  27. use Phalcon\Acl\Role;
  28.  
  29. /**
  30.  * Phalcon\Acl\Adapter\Database
  31.  * Manages ACL lists in memory
  32.  */
  33. class Database extends Adapter implements AdapterInterface
  34. {
  35.  
  36.     /**
  37.      * @var array
  38.      */
  39.     protected $options;
  40.  
  41.     /**
  42.      * @var array
  43.      */
  44.     protected $_fullAcl = array();
  45.  
  46.     /**
  47.      * Class constructor.
  48.      *
  49.      * @param  array                  $options
  50.      * @throws \Phalcon\Acl\Exception
  51.      */
  52.     public function __construct(array $options)
  53.     {
  54.         if (!isset($options['db'])) {
  55.             throw new Exception("Parameter 'db' is required");
  56.         }
  57.  
  58.         if (!isset($options['roles'])) {
  59.             throw new Exception("Parameter 'roles' is required");
  60.         }
  61.  
  62.         if (!isset($options['resources'])) {
  63.             throw new Exception("Parameter 'resources' is required");
  64.         }
  65.  
  66.         if (!isset($options['resourcesAccesses'])) {
  67.             throw new Exception("Parameter 'resourcesAccesses' is required");
  68.         }
  69.  
  70.         if (!isset($options['accessList'])) {
  71.             throw new Exception("Parameter 'accessList' is required");
  72.         }
  73.  
  74.         $this->options = $options;
  75.     }
  76.  
  77.     /**
  78.      * {@inheritdoc}
  79.      * Example:
  80.      * <code>$acl->addRole(new Phalcon\Acl\Role('administrator'), 'consultor');</code>
  81.      * <code>$acl->addRole('administrator', 'consultor');</code>
  82.      *
  83.      * @param  \Phalcon\Acl\Role|string $role
  84.      * @param  string                   $accessInherits
  85.      * @return boolean
  86.      */
  87.     public function addRole($role, $accessInherits = null)
  88.     {
  89.         if (!is_object($role)) {
  90.             $role = new Role($role);
  91.         }
  92.  
  93.         $exists = $this->options['db']->fetchOne(
  94.             'SELECT COUNT(*) FROM ' . $this->options['roles'] . ' WHERE name = ?',
  95.             null,
  96.             array($role->getName())
  97.         );
  98.  
  99.         if (!$exists[0]) {
  100.             $this->options['db']->execute(
  101.                 'INSERT INTO ' . $this->options['roles'] . ' VALUES (?, ?)',
  102.                 array($role->getName(), $role->getDescription())
  103.             );
  104.  
  105.             $this->options['db']->execute(
  106.                 'INSERT INTO ' . $this->options['accessList'] . ' VALUES (?, ?, ?, ?)',
  107.                 array($role->getName(), '*', '*', $this->_defaultAccess)
  108.             );
  109.         }
  110.  
  111.         if ($accessInherits) {
  112.             return $this->addInherit($role->getName(), $accessInherits);
  113.         }
  114.  
  115.         return true;
  116.     }
  117.  
  118.     /**
  119.      * {@inheritdoc}
  120.      *
  121.      * @param  string                 $roleName
  122.      * @param  string                 $roleToInherit
  123.      * @throws \Phalcon\Acl\Exception
  124.      */
  125.     public function addInherit($roleName, $roleToInherit)
  126.     {
  127.         $sql = 'SELECT COUNT(*) FROM ' . $this->options['roles'] . ' WHERE name = ?';
  128.         $exists = $this->options['db']->fetchOne($sql, null, array($roleName));
  129.         if (!$exists[0]) {
  130.             throw new Exception("Role '" . $roleName . "' does not exist in the role list");
  131.         }
  132.  
  133.         $exists = $this->options['db']->fetchOne(
  134.             'SELECT COUNT(*) FROM ' . $this->options['rolesInherits'] . ' WHERE roles_name = ? AND roles_inherit = ?',
  135.             null,
  136.             array($roleName, $roleToInherit)
  137.         );
  138.  
  139.         if (!$exists[0]) {
  140.             $this->options['db']->execute(
  141.                 'INSERT INTO ' . $this->options['rolesInherits'] . ' VALUES (?, ?)',
  142.                 array($roleName, $roleToInherit)
  143.             );
  144.         }
  145.     }
  146.  
  147.     /**
  148.      * {@inheritdoc}
  149.      *
  150.      * @param  string  $roleName
  151.      * @return boolean
  152.      */
  153.     public function isRole($roleName)
  154.     {
  155.         $exists = $this->options['db']->fetchOne(
  156.             'SELECT COUNT(*) FROM ' . $this->options['roles'] . ' WHERE name = ?',
  157.             null,
  158.             array($roleName)
  159.         );
  160.  
  161.         return (bool) $exists[0];
  162.     }
  163.  
  164.     /**
  165.      * {@inheritdoc}
  166.      *
  167.      * @param  string  $resourceName
  168.      * @return boolean
  169.      */
  170.     public function isResource($resourceName)
  171.     {
  172.         $exists = $this->options['db']->fetchOne(
  173.             'SELECT COUNT(*) FROM ' . $this->options['resources'] . ' WHERE name = ?',
  174.             null,
  175.             array($resourceName)
  176.         );
  177.  
  178.         return (bool) $exists[0];
  179.     }
  180.  
  181.     /**
  182.      * {@inheritdoc}
  183.      * Example:
  184.      * <code>
  185.      * //Add a resource to the the list allowing access to an action
  186.      * $acl->addResource(new Phalcon\Acl\Resource('customers'), 'search');
  187.      * $acl->addResource('customers', 'search');
  188.      * //Add a resource  with an access list
  189.      * $acl->addResource(new Phalcon\Acl\Resource('customers'), array('create', 'search'));
  190.      * $acl->addResource('customers', array('create', 'search'));
  191.      * </code>
  192.      *
  193.      * @param  \Phalcon\Acl\Resource|string $resource
  194.      * @param  array|string                 $accessList
  195.      * @return boolean
  196.      */
  197.     public function addResource($resource, $accessList = null)
  198.     {
  199.         if (!is_object($resource)) {
  200.             $resource = new Resource($resource);
  201.         }
  202.  
  203.         $exists = $this->options['db']->fetchOne(
  204.             'SELECT COUNT(*) FROM ' . $this->options['resources'] . ' WHERE name = ?',
  205.             null,
  206.             array($resource->getName())
  207.         );
  208.  
  209.         if (!$exists[0]) {
  210.             $this->options['db']->execute(
  211.                 'INSERT INTO ' . $this->options['resources'] . ' VALUES (?, ?)',
  212.                 array($resource->getName(), $resource->getDescription())
  213.             );
  214.         }
  215.  
  216.         if ($accessList) {
  217.             return $this->addResourceAccess($resource->getName(), $accessList);
  218.         }
  219.  
  220.         return true;
  221.     }
  222.  
  223.     /**
  224.      * {@inheritdoc}
  225.      *
  226.      * @param  string                 $resourceName
  227.      * @param  array|string           $accessList
  228.      * @return boolean
  229.      * @throws \Phalcon\Acl\Exception
  230.      */
  231.     public function addResourceAccess($resourceName, $accessList)
  232.     {
  233.         if (!$this->isResource($resourceName)) {
  234.             throw new Exception("Resource '" . $resourceName . "' does not exist in ACL");
  235.         }
  236.  
  237.         $sql = 'SELECT COUNT(*) FROM ' .
  238.             $this->options['resourcesAccesses'] .
  239.             ' WHERE resources_name = ? AND access_name = ?';
  240.  
  241.         if (!is_array($accessList)) {
  242.             $accessList = array($accessList);
  243.         }
  244.  
  245.         foreach ($accessList as $accessName) {
  246.             $exists = $this->options['db']->fetchOne($sql, null, array($resourceName, $accessName));
  247.             if (!$exists[0]) {
  248.                 $this->options['db']->execute(
  249.                     'INSERT INTO ' . $this->options['resourcesAccesses'] . ' VALUES (?, ?)',
  250.                     array($resourceName, $accessName)
  251.                 );
  252.             }
  253.         }
  254.  
  255.         return true;
  256.     }
  257.  
  258.     /**
  259.      * {@inheritdoc}
  260.      *
  261.      * @return \Phalcon\Acl\Resource[]
  262.      */
  263.     public function getResources()
  264.     {
  265.         $resources = array();
  266.         $sql       = 'SELECT * FROM ' . $this->options['resources'];
  267.  
  268.         foreach ($this->options['db']->fetchAll($sql, Db::FETCH_ASSOC) as $row) {
  269.             $resources[] = new Resource($row['name'], $row['description']);
  270.         }
  271.  
  272.         return $resources;
  273.     }
  274.  
  275.     /**
  276.      * {@inheritdoc}
  277.      *
  278.      * @return \Phalcon\Acl\Role[]
  279.      */
  280.     public function getRoles()
  281.     {
  282.         $roles = array();
  283.         $sql   = 'SELECT * FROM ' . $this->options['roles'];
  284.  
  285.         foreach ($this->options['db']->fetchAll($sql, Db::FETCH_ASSOC) as $row) {
  286.             $roles[] = new Role($row['name'], $row['description']);
  287.         }
  288.  
  289.         return $roles;
  290.     }
  291.  
  292.     /**
  293.      * {@inheritdoc}
  294.      *
  295.      * @param string       $resourceName
  296.      * @param array|string $accessList
  297.      */
  298.     public function dropResourceAccess($resourceName, $accessList)
  299.     {
  300.     }
  301.  
  302.     /**
  303.      * {@inheritdoc}
  304.      * You can use '*' as wildcard
  305.      * Example:
  306.      * <code>
  307.      * //Allow access to guests to search on customers
  308.      * $acl->allow('guests', 'customers', 'search');
  309.      * //Allow access to guests to search or create on customers
  310.      * $acl->allow('guests', 'customers', array('search', 'create'));
  311.      * //Allow access to any role to browse on products
  312.      * $acl->allow('*', 'products', 'browse');
  313.      * //Allow access to any role to browse on any resource
  314.      * $acl->allow('*', '*', 'browse');
  315.      * </code>
  316.      *
  317.      * @param string       $roleName
  318.      * @param string       $resourceName
  319.      * @param array|string $access
  320.      */
  321.     public function allow($roleName, $resourceName, $access)
  322.     {
  323.         $this->allowOrDeny($roleName, $resourceName, $access, Acl::ALLOW);
  324.     }
  325.  
  326.     /**
  327.      * {@inheritdoc}
  328.      * You can use '*' as wildcard
  329.      * Example:
  330.      * <code>
  331.      * //Deny access to guests to search on customers
  332.      * $acl->deny('guests', 'customers', 'search');
  333.      * //Deny access to guests to search or create on customers
  334.      * $acl->deny('guests', 'customers', array('search', 'create'));
  335.      * //Deny access to any role to browse on products
  336.      * $acl->deny('*', 'products', 'browse');
  337.      * //Deny access to any role to browse on any resource
  338.      * $acl->deny('*', '*', 'browse');
  339.      * </code>
  340.      *
  341.      * @param  string       $roleName
  342.      * @param  string       $resourceName
  343.      * @param  array|string $access
  344.      * @return boolean
  345.      */
  346.     public function deny($roleName, $resourceName, $access)
  347.     {
  348.         $this->allowOrDeny($roleName, $resourceName, $access, Acl::DENY);
  349.     }
  350.  
  351.     /**
  352.      * {@inheritdoc}
  353.      * Example:
  354.      * <code>
  355.      * //Does Andres have access to the customers resource to create?
  356.      * $acl->isAllowed('Andres', 'Products', 'create');
  357.      * //Do guests have access to any resource to edit?
  358.      * $acl->isAllowed('guests', '*', 'edit');
  359.      * </code>
  360.      *
  361.      * @param string $role
  362.      * @param string $resource
  363.      * @param string $access
  364.      *
  365.      * @return bool
  366.      */
  367.     public function isAllowed($role, $resource, $access)
  368.     {
  369.         $sql = implode(' ', array(
  370.             'SELECT allowed FROM', $this->options['accessList'], 'AS a',
  371.             // role_name in:
  372.             'WHERE roles_name IN (',
  373.                 // given 'role'-parameter
  374.                 'SELECT ? ',
  375.                 // inherited role_names
  376.                 'UNION SELECT roles_inherit FROM', $this->options['rolesInherits'], 'WHERE roles_name = ?',
  377.                 // or 'any'
  378.                 "UNION SELECT '*'",
  379.             ')',
  380.             // resources_name should be given one or 'any'
  381.             "AND resources_name IN (?, '*')",
  382.             // access_name should be given one or 'any'
  383.             "AND access_name IN (?, '*')",
  384.             // order be the sum of bools for 'literals' before 'any'
  385.             "ORDER BY CAST((roles_name != '*') AS INT)+CAST((resources_name != '*') AS INT)+CAST((access_name != '*') AS INT) DESC",
  386.             // get only one...
  387.             'LIMIT 1'
  388.         ));
  389.  
  390.         // fetch one entry...
  391.         $allowed = $this->options['db']->fetchOne($sql, Db::FETCH_NUM, array($role, $role, $resource, $access));
  392.         if (is_array($allowed)) {
  393.             return (bool) $allowed[0];
  394.         }
  395.  
  396.         /**
  397.          * Return the default access action
  398.          */
  399.  
  400.         return $this->_defaultAccess;
  401.     }
  402.  
  403.     /**
  404.      * Inserts/Updates a permission in the access list
  405.      *
  406.      * @param  string                 $roleName
  407.      * @param  string                 $resourceName
  408.      * @param  string                 $accessName
  409.      * @param  integer                $action
  410.      * @return boolean
  411.      * @throws \Phalcon\Acl\Exception
  412.      */
  413.     protected function insertOrUpdateAccess($roleName, $resourceName, $accessName, $action)
  414.     {
  415.         /**
  416.          * Check if the access is valid in the resource
  417.          */
  418.         $sql = 'SELECT COUNT(*) FROM ' .
  419.             $this->options['resourcesAccesses'] .
  420.             ' WHERE resources_name = ? AND access_name = ?';
  421.         $exists = $this->options['db']->fetchOne($sql, null, array($resourceName, $accessName));
  422.         if (!$exists[0]) {
  423.             throw new Exception(
  424.                 "Access '" . $accessName . "' does not exist in resource '" . $resourceName . "' in ACL"
  425.             );
  426.         }
  427.  
  428.         /**
  429.          * Update the access in access_list
  430.          */
  431.         $sql = 'SELECT COUNT(*) FROM ' .
  432.             $this->options['accessList'] .
  433.             ' WHERE roles_name = ? AND resources_name = ? AND access_name = ?';
  434.         $exists = $this->options['db']->fetchOne($sql, null, array($roleName, $resourceName, $accessName));
  435.         if (!$exists[0]) {
  436.             $sql = 'INSERT INTO ' . $this->options['accessList'] . ' VALUES (?, ?, ?, ?)';
  437.             $params = array($roleName, $resourceName, $accessName, $action);
  438.         } else {
  439.             $sql = 'UPDATE ' .
  440.                 $this->options['accessList'] .
  441.                 ' SET allowed = ? WHERE roles_name = ? AND resources_name = ? AND access_name = ?';
  442.             $params = array($action, $roleName, $resourceName, $accessName);
  443.         }
  444.  
  445.         $this->options['db']->execute($sql, $params);
  446.  
  447.         /**
  448.          * Update the access '*' in access_list
  449.          */
  450.         $sql = 'SELECT COUNT(*) FROM ' .
  451.             $this->options['accessList'] .
  452.             ' WHERE roles_name = ? AND resources_name = ? AND access_name = ?';
  453.         $exists = $this->options['db']->fetchOne($sql, null, array($roleName, $resourceName, '*'));
  454.         if (!$exists[0]) {
  455.             $sql = 'INSERT INTO ' . $this->options['accessList'] . ' VALUES (?, ?, ?, ?)';
  456.             $this->options['db']->execute($sql, array($roleName, $resourceName, '*', $this->_defaultAccess));
  457.         }
  458.  
  459.         $this->_fullAcl[$roleName][$resourceName][] = $accessName;
  460.  
  461.         return true;
  462.     }
  463.  
  464.     /**
  465.      * Inserts/Updates a permission in the access list
  466.      *
  467.      * @param  string                 $roleName
  468.      * @param  string                 $resourceName
  469.      * @param  array|string           $access
  470.      * @param  integer                $action
  471.      * @throws \Phalcon\Acl\Exception
  472.      */
  473.     protected function allowOrDeny($roleName, $resourceName, $access, $action)
  474.     {
  475.         if (!$this->isRole($roleName)) {
  476.             throw new Exception('Role "' . $roleName . '" does not exist in the list');
  477.         }
  478.  
  479.         if (!is_array($access)) {
  480.             $access = array($access);
  481.         }
  482.  
  483.         foreach ($access as $accessName) {
  484.             $this->insertOrUpdateAccess($roleName, $resourceName, $accessName, $action);
  485.         }
  486.     }
  487.  
  488.     /**
  489.      * {@inheritdoc}
  490.      *
  491.      * @return StdClass
  492.      */
  493.     public function getData()
  494.     {
  495.         return (object)[
  496.             "defaultAction" => $this->getDefaultAction(),
  497.             "roles"         => $this->getRoles(),
  498.             "resources"     => $this->getResources(),
  499.             "accessList"    => $this->_fullAcl
  500.         ];
  501.     }
  502.  
  503.     /**
  504.      * {@inheritdoc}
  505.      *
  506.      * @return \Phalcon\Acl\Adapter\Memory
  507.      */
  508.     public function toMemory()
  509.     {
  510.         $adapter = new \Phalcon\Acl\Adapter\Memory();
  511.         $object  = $this->getData();
  512.  
  513.         $adapter->setDefaultAction($object->defaultAction);
  514.  
  515.         foreach ($object->accessList as $role => $resources) {
  516.             $adapter->addRole($role);
  517.             foreach ($resources as $resource => $accesses) {
  518.                 $adapter->addResource($resource, $accesses);
  519.                 foreach ($accesses as $access) {
  520.                     $adapter->allow($role, $resource, $access);
  521.                 }
  522.             }
  523.         }
  524.  
  525.         return $adapter;
  526.     }
  527. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement