[Solar-svn] Revision 3141

pmjones at solarphp.com pmjones at solarphp.com
Sat May 3 12:48:26 CDT 2008


Various changes and (very weak) breaks to Solar_Access, and adding an Sql adapter.

Solar_Access_Adapter
--------------------

* [BRK] Method isAllowed() no longer honors 'process' key.  Removed this per
  discussion with Antti Holvikari, Jeff Surgeson, and others.

* [BRK] Method isAllowed() now uses a different 3rd param, $content, and
  optionally checks for ownership of that content when an 'owner' control
  type is requested (vice 'handle' or 'role').  The ownership check is
  implemented via the new isOwner() method.

* [ADD] Abstract method isOwner($content), to check if the user is the owner
  of a particular piece of content.  This is expected to be very application-
  specific in implementation, so if you need this, override the adapter to
  provide your own mechanism for checking ownership on content.

* [CHG] Method load() now optionally takes a Solar_Auth_Adapter object and 
  Solar_Role_Adapter object in place of a handle string and role array,
  respectively.  If objects are passed as the params, the objects are
  retained in the new $_auth and $_role properties.  This is to facilitate
  content ownership checks in the new isOwner() method.


Solar_Access_Adapter_File
-------------------------

* [CHG] Method fetch() now allows for comment lines starting with #, as well
  as for blank lines.

* [CHG] Method fetch() now returns the 'type' and 'name' values in the access
  list array, but no longer returns the 'process' value (since it has been
  removed from Solar_Access_Adapter).

* [CHG] Method fetch() now picks up 'owner' access control types.

* [ADD] Method isOwner() implemented in a brain-dead fashion to always return
  true. This is to allow existing applications to implement ownership checks
  at lower application levels without having to override Solar_Access_Adapter
  until they're ready.


Solar_Access_Adapter_None
-------------------------

* [CHG] Method fetch() now returns the 'type' and 'name' values in the access
  list array, but no longer returns the 'process' value (since it has been
  removed from Solar_Access_Adapter).

* [ADD] Method isOwner() implemented to always return false, since all other
  permissions are false as well.


Solar_Access_Adapter_Open
-------------------------

* [CHG] Method fetch() now returns the 'type' and 'name' values in the access
  list array, but no longer returns the 'process' value (since it has been
  removed from Solar_Access_Adapter).

* [ADD] Method isOwner() implemented to always return true, since all other
  permissions are true as well.


Solar_Access_Adapter_Sql
------------------------

* [NEW] Thanks, Antti Holvikari, for providing this new adapter class from
  Lux. Modified to group the logic portions, and to honor 'owner' control
  types.
  
  Note that method isOwner() is implemented in a brain-dead fashion to always
  return true. This is to allow existing applications to implement ownership
  checks at lower application levels without having to override
  Solar_Access_Adapter until they're ready.



Modified: trunk/Solar/Access/Adapter/File.php
===================================================================
--- trunk/Solar/Access/Adapter/File.php	2008-05-02 18:24:46 UTC (rev 3140)
+++ trunk/Solar/Access/Adapter/File.php	2008-05-03 17:48:25 UTC (rev 3141)
@@ -77,42 +77,48 @@
         
         foreach ($lines as $line) {
             
-            $trim = trim($line);
+            $line = trim($line);
             
             // allow blank lines
-            if ($trim == '') {
+            if ($line == '') {
                 continue;
             }
             
             // allow comment lines
-            $char = substr($trim, 0, 1);
+            $char = substr($line, 0, 1);
             if ($char == '#') {
                 continue;
             }
             
             // $info keys are ...
             // 0 => "allow" or "deny"
-            // 1 => "handle" or "role"
-            // 2 => handle/role name
+            // 1 => "handle", "role", or "owner"
+            // 2 => handle/role name (not used by 'owner' type)
             // 3 => class name
             // 4 => action name
-            // 5 => process name
             $info = explode(' ', $line);
             if ($info[1] == 'handle' && $info[2] == $handle ||        // direct user handle match
                 $info[1] == 'handle' && $info[2] == '+' && $handle || // any authenticated user
                 $info[1] == 'handle' && $info[2] == '*' ||            // any user (incl anon)
                 $info[1] == 'role'   && in_array($info[2], $roles) || // direct role match
-                $info[1] == 'role'   && $info[2] == '*') {            // any role (incl anon)
+                $info[1] == 'role'   && $info[2] == '*'               // any role (incl anon)
+                $info[1] == 'owner' ) {                               // content owner
                 
                 // keep the line
                 $list[] = array(
                     'allow'   => ($info[0] == 'allow' ? true : false),
+                    'type'    => $info[1],
+                    'name'    => $info[2],
                     'class'   => $info[3],
                     'action'  => $info[4],
-                    'process' => $info[5],
                 );
             }
         }
         return $list;
     }
+    
+    public function isOwner($content)
+    {
+        return true;
+    }
 }

Modified: trunk/Solar/Access/Adapter/None.php
===================================================================
--- trunk/Solar/Access/Adapter/None.php	2008-05-02 18:24:46 UTC (rev 3140)
+++ trunk/Solar/Access/Adapter/None.php	2008-05-03 17:48:25 UTC (rev 3141)
@@ -32,10 +32,16 @@
         return array(
             array(
                 'allow'   => false,
+                'type'    => '*',
+                'name'    => '*',
                 'class'   => '*',
                 'action'  => '*',
-                'process' => '*',
             ),
         );
     }
+    
+    public function isOwner($content)
+    {
+        return false;
+    }
 }

Modified: trunk/Solar/Access/Adapter/Open.php
===================================================================
--- trunk/Solar/Access/Adapter/Open.php	2008-05-02 18:24:46 UTC (rev 3140)
+++ trunk/Solar/Access/Adapter/Open.php	2008-05-03 17:48:25 UTC (rev 3141)
@@ -32,10 +32,16 @@
         return array(
             array(
                 'allow'   => true,
+                'type'    => '*',
+                'name'    => '*',
                 'class'   => '*',
                 'action'  => '*',
-                'process' => '*',
             ),
         );
     }
+    
+    public function isOwner($content)
+    {
+        return true;
+    }
 }

Added: trunk/Solar/Access/Adapter/Sql.php
===================================================================
--- trunk/Solar/Access/Adapter/Sql.php	                        (rev 0)
+++ trunk/Solar/Access/Adapter/Sql.php	2008-05-03 17:48:25 UTC (rev 3141)
@@ -0,0 +1,188 @@
+<?php
+/**
+ * 
+ * Class for reading access privileges from a database table.
+ * 
+ * @category Solar
+ * 
+ * @package Solar_Access
+ * 
+ * @author Antti Holvikari <anttih at gmail.com>
+ * 
+ * @author Paul M. Jones <pmjones at solarphp.com>
+ * 
+ * @license http://opensource.org/licenses/bsd-license.php BSD
+ * 
+ */
+
+/**
+ * 
+ * Class for reading access privileges from a database table.
+ * 
+ *     0:flag 1:type 2:name 3:class 4:action
+ * 
+ * @category Solar
+ * 
+ * @package Solar_Access
+ * 
+ */
+class Solar_Access_Adapter_Sql extends Solar_Access_Adapter {
+    
+    /**
+     * 
+     * Config keys
+     *
+     * `sql`
+     * : (string|array) How to get the SQL object.  If a string, is
+     *   treated as a [[Solar_Registry::get()]] object name.  If array, treated as
+     *   config for a standalone Solar_Sql object.
+     *
+     * `table`
+     * : (string) Name of the table holding access data.
+     * 
+     * `flag_col`
+     * : (string) Name of the column with privilege flag (the stored value in
+     *   the column should be like a boolean, such as allow/deny, t/f, T/F,
+     *   y/n, Y/N, or 0/1).
+     * 
+     * `type_col`
+     * : (string) Name of the column with access type info ('handle' or
+     *   'role').
+     * 
+     * `name_col`
+     * : (string) Name of the column with the handle or role name.
+     * 
+     * `class_col`
+     * : (string) Name of the column with the class name.
+     * 
+     * `action_col`
+     * : (string) Name of the column with the action name.
+     * 
+     * `process_col`
+     * : (string) Name of the column with the submit key name.
+     * 
+     * @var array
+     * 
+     */
+    protected $_Solar_Access_Adapter_Sql = array(
+        'sql'         => 'sql',
+        'table'       => 'acl',
+        'flag_col'    => 'flag',
+        'type_col'    => 'type',
+        'name_col'    => 'name',
+        'class_col'   => 'class_name',
+        'action_col'  => 'action_name',
+        'order_col'   => 'position',
+    );
+    
+    /**
+     * 
+     * Fetches access privileges for a user handle and roles.
+     * 
+     * Uses a SELECT similar to the following:
+     * 
+     * {{code: sql
+     *     SELECT $cols
+     *     FROM $table
+     *     WHERE (type = 'handle' AND name IN ($handle_list))
+     *     OR (type = 'role' AND name IN ($role_list))
+     *     OR (type = 'owner')
+     *     ORDER BY $order
+     * }}
+     * 
+     * @param string $handle User handle.
+     * 
+     * @param array $roles User roles.
+     * 
+     * @return array
+     * 
+     */
+    public function fetch($handle, $roles)
+    {
+        /**
+         * prepare query elements
+         */
+        
+        // columns to select
+        $cols = array(
+            $this->_config['flag_col']    . ' AS allow',
+            $this->_config['type_col']    . ' AS type',
+            $this->_config['name_col']    . ' AS name',
+            $this->_config['class_col']   . ' AS class',
+            $this->_config['action_col']  . ' AS action',
+        );
+        
+        // the "handle" condition
+        // `(type = 'handle' AND name IN (...))`
+        $handle_cond = "({$this->_config['type_col']} = :handle_type"
+                     . " AND {$this->_config['name_col']} IN (:handle_list))";
+        
+        // the handle list
+        if ($handle) {
+            // user is authenticated
+            $handle_list = array($handle, '*', '+');
+        } else {
+            // user is anonymous
+            $handle_list = array('*');
+        }
+        
+        // the "role" condition
+        // `(type = 'role' AND name IN (...))`
+        $role_cond = "({$this->_config['type_col']} = :role_type"
+                   . " AND {$this->_config['name_col']} IN (:role_list))";
+        
+        // the role list
+        $role_list = (array) $roles;
+        $role_list[] = '*';
+        
+        // the "owner" condition
+        // `type = 'owner'`
+        $owner_cond = "({$this->_config['type_col']} = :owner_type)";
+        
+        // collect data to bind into the query
+        $data = array(
+            'handle_type' => 'handle',
+            'handle_list' => $handle_list,
+            'role_type'   => 'role',
+            'role_list'   => $role_list,
+            'owner_type'  => 'owner',
+        );
+        
+        /**
+         * build and execute the query
+         */
+        
+        // get the dependency object of class Solar_Sql
+        $sql = Solar::dependency('Solar_Sql', $this->_config['sql']);
+        
+        // get a selection tool using the dependency object
+        $select = Solar::factory( 'Solar_Sql_Select', array(
+            'sql' => $sql
+        ));
+        
+        // build the select
+        $select->from($this->_config['table'], $cols)
+               ->where($handle_cond)
+               ->orWhere($role_cond)
+               ->orWhere($owner_cond)
+               ->order($this->_config['order_col'])
+               ->bind($data);
+        
+        // fetch the access list
+        $access = $select->fetchAll();
+        
+        // set 'allow' flag to boolean on each access item
+        $allow = array('allow', 't', 'T', 'y', 'Y', '1');
+        foreach ($access as $key => $val) {
+            $access[$key]['allow'] = (bool) in_array($val['allow'], $allow);
+        }
+        
+        // return access list
+        return $access;
+    }
+    
+    public function isOwner($content)
+    {
+        return true;
+    }
+}

Modified: trunk/Solar/Access/Adapter.php
===================================================================
--- trunk/Solar/Access/Adapter.php	2008-05-02 18:24:46 UTC (rev 3140)
+++ trunk/Solar/Access/Adapter.php	2008-05-03 17:48:25 UTC (rev 3141)
@@ -27,64 +27,117 @@
     
     /**
      * 
+     * A Solar_Auth object representing the current user.
+     * 
+     * @var Solar_Auth_Adapter
+     * 
+     */
+    protected $_auth;
+    
+    /**
+     * 
+     * A Solar_Role object representing the current user.
+     * 
+     * @var Solar_Role_Adapter
+     * 
+     */
+    protected $_role;
+    
+    /**
+     * 
      * Fetches the access list from the adapter into $this->list.
      * 
-     * @param string $handle The username handle to fetch access
-     * controls for.
+     * @param string|Solar_Auth_Adapter $auth_spec Fetch access controls for
+     * this user handle.  If a string, is assumed to be the handle directly;
+     * otherwise, the handle is pulled from a Solar_Auth_Adapter object.
      * 
-     * @param array $roles The user roles to fetch access controls for.
+     * @param array|Solar_Auth_Adapter $auth_spec Fetch access controls for
+     * these user roles.  If an array, is assumed to be the roles directly;
+     * otherwise, the roles are pulled from a Solar_Role_Adapter object.
      * 
      * @return void
      * 
      */
-    public function load($handle, $roles)
+    public function load($auth_spec, $role_spec)
     {
+        // clear out previous values
         $this->reset();
+        
+        if ($auth_spec instanceof Solar_Auth_Adapter) {
+            $this->_auth = $auth_spec;
+            $handle = $this->_auth->handle;
+        } else {
+            $handle = $auth_spec;
+        }
+        
+        if ($role_spec instanceof Solar_Role_Adapter) {
+            $this->_role = $role_spec;
+            $roles = $this->_role->list;
+        } else {
+            $roles = $role_spec;
+        }
+        
+        // get the access list
+        $list = $this->fetch($handle, $roles);
+        
         // reverse so that last ones are checked first
-        $this->list = array_reverse($this->fetch($handle, $roles));
+        $this->list = array_reverse($list);
     }
     
     /**
      * 
      * Tells whether or not to allow access to a class/action/process combination.
      * 
-     * @param string $class The class name of the control; use '*' for
+     * @param string $class The name of the class to control; use '*' for
      * all values.
      * 
      * @param string $action The action within that class; use '*' for
-     * all values.
+     * all values.  For handle types, use '+' to indicate any non-empty
+     * handle (i.e., any authenticated user).
      * 
-     * @param string $process The process value within the action; use
-     * '*' for all values.
+     * @param mixed $content A content item (application-specific) to check
+     * ownership on.
      * 
      * @return bool True if the current handle or role is allowed 
      * access, false if not.
      * 
+     * @see isOwner()
+     * 
      */
-    public function isAllowed($class = '*', $action = '*', $process = '*')
+    public function isAllowed($class = '*', $action = '*', $content = null)
     {
         foreach ($this->list as $info) {
             $class_match   = ($info['class']   == $class   || $info['class']  == '*');
             $action_match  = ($info['action']  == $action  || $info['action'] == '*');
-            $process_match = ($info['process'] == $process || $info['process'] == '*');
-            if ($class_match && $action_match && $process_match) {
-                // all params match, return the flag (true or false)
+            if ($class_match && $action_match) {
+                // do we also need to be the owner?
+                if ($info['type'] == 'owner' && ! $this->isOwner($content)) {
+                    // not the owner, skip to the next control item
+                    continue;
+                }
+                
+                // class and action matched (and optionally owner).
+                // return the flag.
                 return (bool) $info['allow'];
             }
         }
+        
         // no matching params, deny by default
         return false;
     }
     
     /**
      * 
-     * Resets the current access controls to a blank array.
+     * Resets the current access controls to a blank array, along with the 
+     * $_auth and $_role properties.
      * 
      * @return void
      * 
      */
     public function reset()
     {
+        $this->_auth = null;
+        $this->_role = null;
         $this->list = array();
     }
     
@@ -100,4 +153,16 @@
      * 
      */
     abstract public function fetch($handle, $roles);
+    
+    /**
+     * 
+     * Checks to see if the current user is the owner of application-specific
+     * content.
+     * 
+     * @param mixed $content The content to check ownership of.
+     * 
+     * @return bool
+     * 
+     */
+    abstract public function isOwner($content);
 }




More information about the Solar-svn mailing list