Hallo!
Ich bin neu hier im Forum. Ich wurde von einem anderen Nutzer auf dieses Forum aufmerksam und war sofort begeistert von den regen Diskussionen hier. Großes Kino!
Ich möchte euch eine Klasse vorstellen, die ich geschrieben habe, um Zugriffsrechte und Benutzer besser verwalten zu können. Für diese Klasse suche ich Tester und Leute, die ein paar Verbesserungsvorschläge einbringen. Zudem überlege ich anstatt auf Exceptions zurück zu greifen auf return true & false oder auf eine log()-Methode setzen soll. Was haltet ihr davon? Im nachfolgenden erkläre ich noch ein paar Dinge zu Funktionsweise der Klasse.
Die Klasse baut auf das Binär-System ähnlich wie auch das UNIX- oder auch Datei-Rechtesystem (chmod) . Die möglichen Rechte beim Zugriff auf Dateien sind euch vermutlich bekannt. Lesen, Schreiben und Ausführen (die Unterscheidung in Besitzer, Gruppen und Öffentliche Berechtigungen lassen wir außer Acht). Diesen drei Rechten wurden Zweierpotenzen zugewiesen.
Ausführen => 1 (2^0)
Schreiben => 2 (2^1)
Lesen => 4 (2^2)
Durch addieren der einzelnen Rechte kann man alle Rechte eines Users auf eine Zahl zusammenführen. Administratoren dürfen ja bekanntlich alles (=1+2+4=7) wohingegen andere User zum Beispiel nur Lesen und Schreiben dürfen (=2+4=6). Das besondere an einem binären Rechtesystem ist nun, dass man aus dieser Zahl (anders als beim Dezimalsystem) wieder alle Rechte extrahieren kann. Dies wird klarer wenn man die drei Zahlen für Lesen, Schreiben und Ausführen ins Binärsystem (Zählung von rechts) übersetzt.
Ausführen => 001
Schreiben => 010
Lesen => 100
Der Administrator besitzt also die rechte 111 und der vorhin erwähnte User 110.
Mein auf dieser Idee aufbauendes Rechtesystem ist sehr Performance freundlich und benötigt nur zwei Tabellen. Es soll eine Tabelle für die User-Daten und eine für die Daten der "privaten" Bereiche geben. Die User-Tabelle enthält also im einfachsten Fall id | name | password | permissions. Die Tabelle für die "priavten" oder einfacher gesagt, für die geschützten Bereiche, enthält allerdings nun nur noch den Namen des Bereichs und eine zugehörige ID. PHP bietet uns auch schon durch die binären Operatoren eine wundervolle Möglichkeit zur Überprüfung, ob ein Benutzer die nötigen Rechte besitzt. Es ist nämlich möglich über das &-Zeichen eine binäre UND-Verknüpfung zu schaffen. Mit Hilfe dieser Operation kann man zum Beispiel nun feststellen, ob an der dritten Stelle (Zählung von rechts) der binären Zahl, die die Rechte eines Users vereint, eine Eins steht, die dem User im obigen Beispiel das Lesen erlauben würde.
Wie findet man allerdings nun mit dem binären Wert aus der Permissions-Spalte der User Tabelle heraus, ob ein Benutzer zugriff auf einen bestimmten Bereich hat oder nicht? Oder auf welche Bereiche er überhaupt Zugriff hat? Die Lösung ist eigentlich einfach. Nimmt man die ID für einen bestimmten bereich in zum Beispiel einem CM-System und nutzt diesen als Exponent zur Basis zwei, so erhält man den binären Wert, den der User als Teil seines binären Wertes besitzen muss.
Möchte man nun also alle IDs der Bereiche herausfinden, zu denen ein User Zugriff besitz, kann man dies relativ einfach auszählen. Ein Beispiel:
Es gibt die Bereiche bla(ID 1), lol(ID 2), omg(ID 3) und nop(ID 4).
Möchte nun jemand zum Beispiel den benötigten Binärwert zu bla wissen, muss er nur 2^ID(lol) = 2^2 = 4 rechnen.
Möchte man also alle Bereiche wissen, zu denen ein Benutzer Zugriff besitzt, muss man einfach nur nach dem größten möglichen Exponenten zur Basis zwei suchen, bei dessen Subtraktion vom binären Permissions-Wert des Users nicht null herauskommt. Man durchläuft also eine Schleife solange, bis kein Restwert der Permissions mehr da ist. Gibt es also einen Nutzer Klaus mit dem binären Permissions-Wert 20 in meinem oben begonnenen pseudo CM-System, so setzen sich seine Werte wie folgt zusammen.
P(Klaus) = 2^ID(nop) + 2^ID(lol)
Der Benutzer Klaus hat also Zugriff auf die Bereiche nop und lol.
In PHP kann man dies, wie schon gesagt mit einer Schleife leicht errechnen.
Nach Durchlaufen der Schleife enthält das Array $mods alle IDs zu den geschützten bereichen zu denen Klaus Zutritt besitzt, also 2 und 4.
Ich hoffe, dass das System klar geworden ist. Bei Fragen stehe ich natürlich gerne zur Verfügung und für Anregungen bin ich auch immer offen.
Übersicht über die Methoden
__construct() - Is called when the class is instanced
addMod() - Adds a new protected section
delMod() - Removes section from table
getMods() - Returns all section-ids
addUser() - Updates the permissions of an user
delUser() - Removes user from table
editUser() - Changes the data of an user
getUser() - Returns data of the given user
getPerms() - Returns all rights of an user
calcPerms() - Calculates permission integer of an user
check() - Checks access rights
getId() - Returns the id to a user or secured area
Die Datenbank Struktur
Permissions.php
Ich bin neu hier im Forum. Ich wurde von einem anderen Nutzer auf dieses Forum aufmerksam und war sofort begeistert von den regen Diskussionen hier. Großes Kino!
Ich möchte euch eine Klasse vorstellen, die ich geschrieben habe, um Zugriffsrechte und Benutzer besser verwalten zu können. Für diese Klasse suche ich Tester und Leute, die ein paar Verbesserungsvorschläge einbringen. Zudem überlege ich anstatt auf Exceptions zurück zu greifen auf return true & false oder auf eine log()-Methode setzen soll. Was haltet ihr davon? Im nachfolgenden erkläre ich noch ein paar Dinge zu Funktionsweise der Klasse.
Die Klasse baut auf das Binär-System ähnlich wie auch das UNIX- oder auch Datei-Rechtesystem (chmod) . Die möglichen Rechte beim Zugriff auf Dateien sind euch vermutlich bekannt. Lesen, Schreiben und Ausführen (die Unterscheidung in Besitzer, Gruppen und Öffentliche Berechtigungen lassen wir außer Acht). Diesen drei Rechten wurden Zweierpotenzen zugewiesen.
Ausführen => 1 (2^0)
Schreiben => 2 (2^1)
Lesen => 4 (2^2)
Durch addieren der einzelnen Rechte kann man alle Rechte eines Users auf eine Zahl zusammenführen. Administratoren dürfen ja bekanntlich alles (=1+2+4=7) wohingegen andere User zum Beispiel nur Lesen und Schreiben dürfen (=2+4=6). Das besondere an einem binären Rechtesystem ist nun, dass man aus dieser Zahl (anders als beim Dezimalsystem) wieder alle Rechte extrahieren kann. Dies wird klarer wenn man die drei Zahlen für Lesen, Schreiben und Ausführen ins Binärsystem (Zählung von rechts) übersetzt.
Ausführen => 001
Schreiben => 010
Lesen => 100
Der Administrator besitzt also die rechte 111 und der vorhin erwähnte User 110.
Mein auf dieser Idee aufbauendes Rechtesystem ist sehr Performance freundlich und benötigt nur zwei Tabellen. Es soll eine Tabelle für die User-Daten und eine für die Daten der "privaten" Bereiche geben. Die User-Tabelle enthält also im einfachsten Fall id | name | password | permissions. Die Tabelle für die "priavten" oder einfacher gesagt, für die geschützten Bereiche, enthält allerdings nun nur noch den Namen des Bereichs und eine zugehörige ID. PHP bietet uns auch schon durch die binären Operatoren eine wundervolle Möglichkeit zur Überprüfung, ob ein Benutzer die nötigen Rechte besitzt. Es ist nämlich möglich über das &-Zeichen eine binäre UND-Verknüpfung zu schaffen. Mit Hilfe dieser Operation kann man zum Beispiel nun feststellen, ob an der dritten Stelle (Zählung von rechts) der binären Zahl, die die Rechte eines Users vereint, eine Eins steht, die dem User im obigen Beispiel das Lesen erlauben würde.
Wie findet man allerdings nun mit dem binären Wert aus der Permissions-Spalte der User Tabelle heraus, ob ein Benutzer zugriff auf einen bestimmten Bereich hat oder nicht? Oder auf welche Bereiche er überhaupt Zugriff hat? Die Lösung ist eigentlich einfach. Nimmt man die ID für einen bestimmten bereich in zum Beispiel einem CM-System und nutzt diesen als Exponent zur Basis zwei, so erhält man den binären Wert, den der User als Teil seines binären Wertes besitzen muss.
Möchte man nun also alle IDs der Bereiche herausfinden, zu denen ein User Zugriff besitz, kann man dies relativ einfach auszählen. Ein Beispiel:
Es gibt die Bereiche bla(ID 1), lol(ID 2), omg(ID 3) und nop(ID 4).
Möchte nun jemand zum Beispiel den benötigten Binärwert zu bla wissen, muss er nur 2^ID(lol) = 2^2 = 4 rechnen.
Möchte man also alle Bereiche wissen, zu denen ein Benutzer Zugriff besitzt, muss man einfach nur nach dem größten möglichen Exponenten zur Basis zwei suchen, bei dessen Subtraktion vom binären Permissions-Wert des Users nicht null herauskommt. Man durchläuft also eine Schleife solange, bis kein Restwert der Permissions mehr da ist. Gibt es also einen Nutzer Klaus mit dem binären Permissions-Wert 20 in meinem oben begonnenen pseudo CM-System, so setzen sich seine Werte wie folgt zusammen.
P(Klaus) = 2^ID(nop) + 2^ID(lol)
Der Benutzer Klaus hat also Zugriff auf die Bereiche nop und lol.
In PHP kann man dies, wie schon gesagt mit einer Schleife leicht errechnen.
PHP-Code:
// permissions of Klaus
$perms = 20;
//start counter
$i = 0;
// initiate array of ids
$mods = array();
// extrahate specific permissions
do
{
if( (($p-pow(2,$i)) < 0) )
{
$e = $i-1;
$p = $p-pow(2,$e);
$mods[] = $e;
$i = 0;
}
else
{
$i++;
}
}
while( (($p-pow(2,$i)) < 0) && ($p > 0) )
Ich hoffe, dass das System klar geworden ist. Bei Fragen stehe ich natürlich gerne zur Verfügung und für Anregungen bin ich auch immer offen.
Übersicht über die Methoden
__construct() - Is called when the class is instanced
addMod() - Adds a new protected section
delMod() - Removes section from table
getMods() - Returns all section-ids
addUser() - Updates the permissions of an user
delUser() - Removes user from table
editUser() - Changes the data of an user
getUser() - Returns data of the given user
getPerms() - Returns all rights of an user
calcPerms() - Calculates permission integer of an user
check() - Checks access rights
getId() - Returns the id to a user or secured area
Die Datenbank Struktur
Code:
CREATE TABLE `permissions` ( `id` tinyint(4) NOT NULL auto_increment, `name` varchar(30) NOT NULL default '', PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=0; INSERT INTO `permissions` VALUES (1, 'settings'); INSERT INTO `permissions` VALUES (2, 'articles'); CREATE TABLE `user` ( `id` tinyint(4) NOT NULL auto_increment, `name` varchar(20) NOT NULL default '', `password` varchar(32) NOT NULL default '', `email` varchar(60) NOT NULL default '', `perm` tinyint(3) unsigned NOT NULL default '0', PRIMARY KEY (`id`) UNIQUE KEY `name` (`name`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=0; INSERT INTO `user` VALUES (1, 'avedo', 'ddd6acc47c23379394ee7cdcbb4ea22b', 'pseudo@web.de', 30); INSERT INTO `user` VALUES (2, 'klaus', '0f5f53ea8fe0e8eeb72db72b47ddf351', 'klaus@avedo.net', 14);
PHP-Code:
<?php
error_reporting(E_ALL);
/***
* Class Permissions
*
* The Permissions class enables the easy management
* of access rights an user data. Reading and editting
* access rights to secured areas and the creation
* of such secured areas is one part of this class.
* On the other hand this class accounts for the
* creation and editting of user data.
* ZodiacXP from tutorials.de optimized the performance
* of the class. Thank you for your help.
*
* @package Permissions
* @version 0.5
* @author Andreas Wilhelm <Andreas2209@web.de>
* @copyright Andreas Wilhelm
**/
class Permissions
{
// private class variables
private $db;
private $userTab = "";
private $modTab = "";
/**
* Constructor - Is called when the class is instanced
*
* @access public
* @param Obj $db
* @param Str $userTab
* @param Str $modTab
* @return NONE
*/
public function __construct(mysqli $db, $userTab, $modTab)
{
// save parameters to class variables
$this->db = $db;
$this->modTab = trim($modTab);
$this->userTab = trim($userTab);
}
/**
* addMod() - Adds a new protected section
*
* @access public
* @param Str $name
* @return Boolean
*/
public function addMod($name)
{
// add new section
$sql = "INSERT INTO " . $this->modTab . "
(`name`)
VALUES
('" . $name . "')";
// send sql-query
$this->db->query($sql);
// check result
if( $this->db->errno )
{
throw new Exception("Cannot create section " . $name);
}
return true;
}
/**
* delMod() - Removes section from table
*
* @access public
* @param Mix $ident
* @return Boolean
*/
public function delMod($ident)
{
// get id of the section
$id = $this->getId($ident, $this->modTab);
// delete section
$sql = "DELETE
FROM
" . $this->modTab . "
WHERE
id = " . $id;
// send sql-query
$this->db->query($sql);
// check result
if( $this->db->errno )
{
throw new Exception("Cannot delete Mod " . $ident);
}
return true;
}
/**
* getMods() - Returns all section-ids
*
* @access public
* @return Array
*/
public function getMods()
{
// get section-ids
$sql = "SELECT
name
FROM
" . $this->modTab;
// send sql-query
$result = $this->db->query($sql);
// check result
if(!$result)
{
throw new Exception("Cannot get sections.");
}
// save perms to array
$mods = array();
while( $row = $result->fetch_assoc() )
{
$mods[] = $row;
}
return $mods;
}
/**
* addUser() - Updates the permissions of an user
*
* @access public
* @param Arr $fields
* @param Arr $values
* @return NONE
*/
public function addUser($fields = array(), $values = array())
{
// save size of arrays
$i = count($fields);
$j = count($values);
// check count of fields and values
if( $i === $j)
{
// create sql-query
$sql = "INSERT INTO " . $this->userTab . " (";
if ($i)
{
// insert field names
$sql .= "`" . implode("`, `", $fields) . "`";
}
$sql .= ") VALUES (";
if ($j)
{
// insert values
$sql .= "'" . implode("', '", $values) . "'";
}
$sql .= ")";
}
else
{
throw new Exception("Invalid user data.");
}
// create an new user
$this->db->query($sql);
// check result
if( $this->db->errno )
{
throw new Exception("Cannot create user.");
}
}
/**
* delUser() - Removes user from table
*
* @access public
* @param Mix $ident
* @return Boolean
*/
public function delUser($ident)
{
// get id of the section
$id = $this->getId($ident, $this->userTab);
// delete section
$sql = "DELETE
FROM
" . $this->userTab . "
WHERE
id = " . $id;
// send sql-query
$this->db->query($sql);
// check result
if( $this->db->errno )
{
throw new Exception("Cannot delete user " . $ident);
}
return true;
}
/**
* editUser() - Changes the data of an user
*
* @access public
* @param Str $user
* @param Arr $fields
* @param Arr $values
* @return NONE
*/
public function editUser($user, $fields = array(), $values = array())
{
// get id of the user
if( !$id = $this->getId($user, $this->userTab) )
{
return false;
}
// get number of entries
$i = count($fields);
$j = count($values);
// check count of fields and values
if( $i === $j )
{
// create sql-query
$sql = "UPDATE " . $this->userTab;
$sql .= " SET ";
// update values
for($i = $i-1; $i >= 0; $i--)
{
$sql .= $fields[$i] . " = '" . $values[$i] . "'";
if($i > 0)
{
$sql .= ', ';
}
}
$sql .= " WHERE id = '" . $id . "'";
}
else
{
throw new Exception("Invalid user data.");
}
// edit user data
$this->db->query($sql);
// check result
if( $this->db->errno )
{
throw new Exception("Cannot change user data.");
}
return true;
}
/**
* getUser() - Returns data of the given user
*
* @access public
* @param Mix $ident
* @return Array
*/
public function getUser($ident)
{
// get id of the user
$id = $this->getId($ident, $this->userTab);
// get user data
$sql = "SELECT
*
FROM
" . $this->userTab . "
WHERE
id = '" . $id . "'
LIMIT 1";
// send sql-query
if ( $result = $this->db->query($sql) )
{
// save result into an array
return $result->fetch_assoc();
}
else
{
throw new Exception("Cannot get " . $ident);
}
}
/**
* getPerms() - Returns all rights of an user
*
* @access public
* @param Mix $ident
* @return Array
*/
public function getPerms($ident)
{
// get id of the user
$id = $this->getId($ident, $this->userTab);
// get permissions of the user
$sql = "SELECT
`perm`
FROM
" . $this->userTab . "
WHERE
id = " . $id . "
LIMIT 1";
// send sql-query
if ( $result = $this->db->query($sql) )
{
// save result into an array
$array = $result->fetch_row();
// get perm from array
$perm = $array[0];
}
else
{
throw new Exception("Cannot get permissions of user " . $ident);
}
//start counter
$i = 0;
// initiate array of ids
$mods = array();
// extrahate specific permissions
$i = 1;
do
{
if( ($perm & $i) == $i )
{
$mods[] = $i;
}
$i <<= 1;
}
while( $perm >= $i );
return $mods;
}
/**
* calcPerms() - Calculates permission integer of an user
*
* @access public
* @param Arr $mods
* @return Integer
*/
public function calcPerms($mods)
{
// create permission string
$perms = 0;
// create binary permission data
foreach($mods as $id)
{
// create mod perms
$perms += 1 << $id;
}
return $perms;
}
/**
* check() - Checks access rights
*
* @access public
* @param Mix $user
* @param Mix $mod
* @return Boolean
*/
public function check($user, $mod)
{
// get id of the user
$userId = $this->getId($user, $this->userTab);
// get permissions of the user
$sql = "SELECT
`perm`
FROM
" . $this->userTab . "
WHERE
id = " . $userId . "
LIMIT 1";
// send sql-query
if ( $result = $this->db->query($sql) )
{
// save result into an array
$array = $result->fetch_row();
// get perm from array
$perms = $array[0];
}
else
{
throw new Exception("Cannot get permissions of " . $user);
}
// get needed perms
$perms >>= $this->getId($mod, $this->modTab);
return $perms & 1;
}
/**
* getId() - Returns the id to a user or secured area
*
* @access private
* @param Mix $ident
* @param Str $table
* @return Integer
*/
private function getId($ident, $table)
{
// check if identifier is an integer
if( is_numeric($ident) )
{
return $ident;
}
else
{
// get id
$sql = "SELECT
`id`
FROM
" . $table . "
WHERE
name = '" . $ident . "'
LIMIT 1";
// send sql-query
if ( $result = $this->db->query($sql) )
{
// save result into an array
$array = $result->fetch_row();
// get id from array
return $array[0];
}
else
{
return false;
}
}
}
}
?>
Kommentar