[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