From 6a12b5da5330a3cf1ea68c4c9683995098376467 Mon Sep 17 00:00:00 2001 From: Kevin Chabowski Date: Sat, 9 Jun 2012 14:34:58 +0200 Subject: ACL class implemented. --- ratatoeskr/setup/create_tables.php | 7 + ratatoeskr/sys/models.php | 304 +++++++++++++++++++++++++++++++++++++ ratatoeskr/translations/de.php | 1 + ratatoeskr/translations/en.php | 1 + 4 files changed, 313 insertions(+) diff --git a/ratatoeskr/setup/create_tables.php b/ratatoeskr/setup/create_tables.php index 97a4cd5..521ae00 100644 --- a/ratatoeskr/setup/create_tables.php +++ b/ratatoeskr/setup/create_tables.php @@ -162,6 +162,13 @@ CREATE TABLE IF NOT EXISTS `PREFIX_article_extradata` ( `key` text COLLATE utf8_unicode_ci NOT NULL, `value` text COLLATE utf8_unicode_ci NOT NULL ) DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +CREATE TABLE IF NOT EXISTS `PREFIX_acls` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `type` int(11) NOT NULL, + `privileges` text COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`) +) DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; SQL; function create_mysql_tables() diff --git a/ratatoeskr/sys/models.php b/ratatoeskr/sys/models.php index 17afa67..9df77cf 100644 --- a/ratatoeskr/sys/models.php +++ b/ratatoeskr/sys/models.php @@ -66,6 +66,22 @@ define("ARTICLE_STATUS_HIDDEN", 0); define("ARTICLE_STATUS_LIVE", 1); define("ARTICLE_STATUS_STICKY", 2); +/* + * Constants: ACLTYPE_ + * Possible types for s + * + * ACLTYPE_GENERAL - General sitewide privileges. Only a single one should be in the database + * ACLTYPE_ARTICLE - privileges for an
+ * ACLTYPE_SECTION - privileges for a
+ * ACLTYPE_IMAGE - privileges for a + * ACLTYPE_PLUGIN - privileges for a + */ +define(ACLTYPE_GENERAL, 0); +define(ACLTYPE_ARTICLE, 1); +define(ACLTYPE_SECTION, 2); +define(ACLTYPE_IMAGE, 3); +define(ACLTYPE_PLUGIN, 4); + /* * Class: DoesNotExistError * This Exception is thrown by an ::by_*-constructor or any array-like object if the desired object is not present in the database. @@ -1291,6 +1307,294 @@ class Style extends BySQLRowEnabled } } +/* Global ACLs will be cached here... */ +$global_acl_cache = array(); + +/* + * Class: ACL + * The representation af a Access Control List in the database. + */ +class ACL extends BySQLRowEnabled +{ + private $id; + private $type; + private $privileges; + + protected function populate_by_sqlrow($sqlrow) + { + $this->id = $sqlrow["id"]; + $this->type = $sqlrow["type"]; + $this->privileges = unserialize(base64_decode($sqlrow["privileges"])); + } + + /* + * Functions: getters + * + * get_id - Get the database ID. + * get_type - Get the type of this ACL. + */ + public function get_id() { return $this->id; } + public function get_type() { return $this->type; } + + /* + * Function: test_type + * Test, if a type is a valid ACL type (i.e. one of *) + * + * Parameters: + * $type - Type to test. + * + * Returns: + * True, if the type is valid, False otherwise. + */ + public static function test_type($type) + { + return is_numeric($type) and ($type >= ACLTYPE_GENERAL) and ($type <= ACLTYPE_PLUGIN); + } + + /* + * Constructor: create + * Creates a new ACL. + * + * Parameters: + * $type - A * constant. + * + * Returns: + * A object. + * + * Throws: + * + */ + public static function create($type) + { + if(!self::test_type($type)) + throw new InvalidDataError("invalid_acl_type"); + + $obj = new self; + + $obj->type = $type; + $obj->privileges = array(); + + qdb("INSERT INTO `PREFIX_acls` (`type`, `privileges`) VALUES (%d, '%s')", $obj->type, base64_encode(serialize($obj->privileges))); + + $obj->id = mysql_insert_id(); + + return $obj; + } + + /* + * Constructor: by_id + * Get a by it's database ID. + * + * Parameters: + * $id - The ID of the ACL + * + * Returns: + * A object. + * + * Throws: + * + */ + public static function by_id($id) + { + $result = qdb("SELECT `id`, `type`, `privileges` FROM `PREFIX_acls` WHERE `id` = %d", $id); + $sqlrow = mysql_fetch_assoc($result); + if(!$sqlrow) + throw new DoesNotExistError(); + + return self::by_sqlrow($sqlrow); + } + + /* + * Function: global_by_type + * Get the global ACL of a given type. + * + * Parameters: + * $type - One of * + * + * Returns: + * The global ACL of this type. + * + * Throws: + * + */ + public static function global_by_type($type) + { + global $ratatoeskr_settings, $global_acl_cache; + + if(isset($global_acl_cache[$type])) + return $global_acl_cache[$type]; + + $acl = self::by_id($ratatoeskr_settings["global_privileges"][$type]); + $global_acl_cache[$type] = $acl; + return $acl; + } + + private static function user_matches($user, $ruleset) + { + if($ruleset["all"]) + return True; + + if($user === NULL) + return False; + + if(in_array($user->get_id(), $ruleset["users"])) + return True; + + foreach($ruleset["groups"] as $gid) + { + try + { + $group = Group::by_id($gid); + } + catch(DoesNotExistError $e) + { + continue; + } + + if($group->member_of($group)) + return True; + } + + return False; + } + + /* + * Function: check_privilege + * Check, if a privilege is granted to a User. + * + * Parameters: + * $privilege - Name of the privilege to check. + * $user - The to check. Can be NULL, if the anonymous user should be checked. Users in the admin will automatically be granted every privilege. + */ + public function check_privilege($privilege, $user, $default=True) + { + global $admin_grp; + + Group::load_admin_group(); + + if(($user !== NULL) and ($user->member_of($admin_grp))) + return True; + + $global_acl = self::global_by_type($this->type); + $unified_privileges = array_merge($global_acl->privileges, $this->privileges); + + if(!isset($unified_privileges[$privilege])) + return $default; + + $privilege = $unified_privileges[$privilege]; + if($privilege["allowdeny"]) + { + $okay = self::user_matches($user, $privilege["allow"]); + if(self::user_matches($user, $privilege["deny"])) + $okay = False; + } + else + { + $okay = !self::user_matches($user, $privilege["deny"]); + if(self::user_matches($user, $privilege["allow"])) + $okay = True; + } + + return $okay; + } + + /* + * Function: list_privileges + * Get the names of the set privileges. + * + * Returns: + * Array of privilege names + */ + public function list_privileges() + { + return array_keys($this->privileges); + } + + /* + * Function: get_privilege + * Get the settings of a privilege. + * + * Parameters: + * $privilege - Name of the privilege. + * + * Returns: + * Associative array with these keys: + * + * * "allowdeny" - True, if allow then deny, False if deny then deny. + * * "allow" - Associative array with the keys: "all": True, if allow for everyone, "groups": IDs of allowed s, "users": IDs af allowed . + * * "deny" - Associative array with the keys: "all": True, if deny for everyone, "groups": IDs of denied s, "users": IDs af denied . + * + * Throws: + * if privilege not set in this ACL. + */ + public function get_privilege($privilege) + { + if(!isset($this->privileges[$privilege])) + throw new DoesNotExistError("Privilege \"$privilege\" not set in ACL #{$this->id}."); + + return $this->privileges[$privilege]; + } + + /* + * Function: set_privilege + * Set the settings of a privilege. + * + * Parameters: + * $privilege - Name of the privilege. + * $allowdeny - True, if first allow then deny. False, if first deny then allow. + * $allow_all - True, if everyone should be allowed. + * $allow_groups - Array of the IDs of allowed s. + * $allow_users - Array of the IDs of allowed s. + * $deny_all - True, if everyone should be denied. + * $allow_groups - Array of the IDs of denied s. + * $deny_users - Array of the IDs of denied s. + */ + public function set_privilege($privilege, $allowdeny, $allow_all, $allow_groups, $allow_users, $deny_all, $deny_groups, $deny_users) + { + $this->privileges[$privilege] = array( + "allowdeny" => (bool) $allowdeny, + "allow" => array( + "all" => (bool) $allow_all, + "groups" => array_filter(array_map(function($x) { return is_numeric($x) ? ((int) $x) : NULL; }, (array) $allow_groups)), + "users" => array_filter(array_map(function($x) { return is_numeric($x) ? ((int) $x) : NULL; }, (array) $allow_users)) + ), + "deny" => array( + "all" => (bool) $deny_all, + "groups" => array_filter(array_map(function($x) { return is_numeric($x) ? ((int) $x) : NULL; }, (array) $deny_groups)), + "users" => array_filter(array_map(function($x) { return is_numeric($x) ? ((int) $x) : NULL; }, (array) $deny_users)) + ) + ); + } + + /* + * Function: delete_privilege + * Delete the settings of a privilege on this ACL. + * + * Parameters: + * $privilege - Name of the privilege to delete. + */ + public function delete_privilege($privilege) + { + unset($this->privileges[$privilege]); + } + + /* + * Function: save + */ + public function save() + { + qdb("UPDATE `PREFIX_acls` SET `privileges` = '%s' WHERE `id` = %d", base64_encode(serialize($this->privileges)), $this->id); + } + + /* + * Function: delete + */ + public function delete() + { + qdb("DELETE FROM `PREFIX_acls` WHERE `id` = %d", $this->id); + } +} + /* * Class: Plugin * The representation of a plugin in the database. diff --git a/ratatoeskr/translations/de.php b/ratatoeskr/translations/de.php index f49f379..57ee8b6 100644 --- a/ratatoeskr/translations/de.php +++ b/ratatoeskr/translations/de.php @@ -263,6 +263,7 @@ $translation = array( "debugmode_might_get_overwritten_by_config_file" => "(dies kann durch config.php überschrieben werden)", "debugmode_now_enabled" => "Debug-Modus ist nun aktiviert", "debugmode_now_disabled" => "Debug-Modus ist nun deaktiviert", + "invalid_acl_type" => "Ungültiger ACL-Typ", /* Very long texts here */ "linking_back_hint" => <<Verlinken auf die Seitenwurzel diff --git a/ratatoeskr/translations/en.php b/ratatoeskr/translations/en.php index a53617c..10d8dd3 100644 --- a/ratatoeskr/translations/en.php +++ b/ratatoeskr/translations/en.php @@ -263,6 +263,7 @@ $translation = array( "debugmode_might_get_overwritten_by_config_file" => "(this can be overwritten by config.php)", "debugmode_now_enabled" => "Debugmode now enabled", "debugmode_now_disabled" => "Debugmode now disabled", + "invalid_acl_type" => "Invalid ACL type", /* Very long texts here */ "linking_back_hint" => <<Linking back -- cgit v1.2.3-54-g00ecf