# Sinnvolle User & Rechteverwaltung



## MiLa (2. Dezember 2003)

Halli Hallo,
ich schon wieder...

Also, diesmal mit einer etwas anspruchsvolleren Frage, wie ich denke...
Ich bin grade am Überarbeiten eines Rechtesystems.
Hier gibt es viele Möglichkeiten, ich möchte es bis ins kleinste Detail verwalten können.
Also die Seite ist Modular aufgebaut, in jedem Modul kann man mindestens einmal Hinzufügen/Bearbeiten/Entfernen und bisher habe ich es mit maximal 30 Modulen laufen.

So, nun stellt sich die Frage, wie realisiere ich am geschicktesten ein Rechtesystem, in dem möglichst genau eingestellt werden kann, wer was darf udn was nicht...

Danke schonmal für jeden konstruktiven Post


----------



## Fabian H (2. Dezember 2003)

Hm, mein letztes Rechtesystem hab' ich so gemacht:

Jeder User hat eine ID, und ist Mitglied einer Gruppe.

Die erste MySQL-Tabelle beschreibt die Rechte der jeweiligen Gruppe:

```
id   |    action   |    actor    |    privilege
```
*id* ist eine eindeutige Identifikation, eigentlich überflüssig..
*action* würde in deinem Fall die ID des Moduls sein
*actor* ist die ID der Gruppe
*privilege* ist ein enum Feld, mit den Werten _Y_, _N_ und _X_ für Spezialfälle.

Nun ist aber die privilege-Spalte für deine Zwecke ungeeignet.
Eine Lösung wäre z.B. keine enum-Typ zu nehmen, sondern einen Integer.
Der hätte dann Werte wie beim Linux-Rechtesystem.
Also z.B.:

```
1 => Hinzufügen
2 => Bearbeiten
4 => Löschen
```
Wenn die entsprechnde Gruppe in dem Modul beispielsweise gar nichts machen darf, wird diese Spalte auf 0 gesetzt.
Wenn Bearbeitet und gelöscht werden darf: 2 + 4 (6), Hinzufügen und Löschen: 1 + 4 (5) usw...

Dann machst du noch eine MySQL-Tabelle mit genau den gleichen Spalten, nimmst aber statt Gruppen für Actor Userids, womit man dann spezial-Rechte für bestimmte User setzen kann.

Zusätzlich musst du noch entscheiden, ob von Grund aus alles verboten ist und die Tabellen diese Verbote wieder aufheben, oder umgekehrt.
Sinnvoller ist wohl ersteres, weil man nur so präzise die _privielege_-Spalte ausnutzen kann.

hth


----------



## MiLa (2. Dezember 2003)

Okay, das mit dem habe ich auch schon überlegt, nur ich bin dann an dem überprüfen hängen geblieben...

1=Lesen
2=Hinzufügen
4=Bearbeiten
8=Löschen


```
if($rechte==2)
{
  //Hinzufügen
}
if($rechte==4)
{
  //Bearbeiten
}
```

Aber wie stelle ich das an, wenn ich das zusammenzähle, ich kann mir da grade Keine Formel herleiten mit der ich das auslesen kann...

Klasischer Fall von Denkkfehler, was ?


----------



## Patrick Kamin (2. Dezember 2003)

*-*

Dann lies dir doch einfach dieses Tutorial durch.
http://www.tutorials.de/tutorials119693.html


----------



## MiLa (2. Dezember 2003)

Oh, nice 
Wär ich nicht drauf gekommen dass es soetwas hier in der Tutorials Sektion gibt 

Super...danke  Werd ich mir mal reintun


----------



## MiLa (2. Dezember 2003)

Also das hat mich schonmal  "leicht" weitergebracht, woran ich nun hänge:
Ist es sinnvoll das ganze so aufzufädeln?:

1= News hinzufügen
2= News bearbeiten
4= News löschen
8= Matches hinzufügen
16= Matches bearbeiten
32= Matches löschen
64= Team hinzufügen
128= Team bearbeiten
...
Und das gaze geht wie gesagt bei 30 Modulen ziemlich in die Höhe :/
Ich hab da irgendwie meine Denkblockade, wenn es darum geht....


----------



## Fabian H (2. Dezember 2003)

Naja, Matches, Teams und News sind ja eigene Module!

Dann machst du einfach mehrere Zeilen in die erste MySQL-Tabelle.
Für jedes Modul eigene Zeilen, dann brauchst du da jeweils nur 1, 2 und 4!

Also pro Gruppe 30 Einträge. Ist zwar viel, lässt sich aber leicht mit einem PHP-Script machen. Ausserdem empfehl ich dir, ein Interface dafür zu machen, dann wird es noch einfacher.


----------



## bn (2. Dezember 2003)

Grundsätzlich ist es wohl am sinnvollsten, wenn User Rechte von Gruppenrichtlinien vererben und dennoch über eigene Rechte verfügen können.
So hat man zwar anfangs etwas Mühe, die Gruppen zu erstellen, kann diese im Nachhinein jedoch ohne weiteres einem Benutzer zuordnen.
Zusätzlich sollten jedem User nicht gruppenspezifische Rechte zuweisbar sein, um dem gesamten Usersystem mehr Flexibilität einzuflössen.

Lösungsansatz:
Benutzertabelle - usersystem_users
---------------------------
user_id, group_id, user_vorname, user_name, user_email,....
->group_id ist ein Foreignkey der nachfolgenden Gruppentabelle...der Rest ist selbsterklärend

Gruppentabelle - usersystem_groups
-----------------------
group_id, name, permission1, permission2,...
->permission steht hier für ein Modul deiner Webseite und kann mit Werten in zuvor geposteten Beiträgen gefüllt werden (1,2,4)

Spezial-Benutzer-Rechte - usersystem_user_permissions
--------------------------------------
user_id, permission1, permission2,....
->user_id ist ein Foreignkey der Benutzertabelle...permission steht auch hier für die einzelnen Module der Webseite

##########################################################
Um die Berechtigungen nun auf jeder Seite parat zu haben, empfielt es sich diese  am Anfang eines jeden Scriptes in einem Array zur Verfügung zu stellen.
1.) Userdaten auslesen

```
//user_id sollte ja bekannt sein...
$sql = "SELECT * FROM ".USER_TABLE." WHERE user_id = '".user_id."'";
$result = mysql_query($sql,$db);
//die komplette Reihe entählt nun alle Userdaten
$userdata = mysql_fetch_array($result);
//$userdata['user_id'], $userdata['group_id'],$userdata['user_vorname'],...
```
Nun fehlen uns nur noch die Berechtigungen dieses Users
2.) Berechtigungen auslesen

```
//...Gruppenrechte
$sql = "SELECT * FROM ".GROUP_TABLE." WHERE group_id = '".$userdata['group_id']."'";
$result = mysql_query($sql,$db);
//die komplette Reihe als Array sind die Permissions
$permissions = mysql_fetch_array($result);
//Nun haben wir schon mal die Gruppenrechte dieses Users in einem Array vorzuliegen...
//...die individuellen Rechte haben allerdings Vorrang und werden daher
//...etwaige Gruppenrechte überschreiben...
$sql = "SELECT * FROM ".USER_PERMISSION_TABLE." WHERE user_id = '".$userdata['user_id']."'"; 
$result = mysql_query($sql,$db);
//Sind Einzelrechte vorhanden?
if(mysql_num_rows($result) > 0)
{
   //es sind Einzelrechte vorhanden...
   //...die Gruppenrechte werden überschrieben
   //...andernfalls existieren ja Gruppenrechte oder keine Rechte 
   $permissions = mysql_fetch_array($result);
}

//In $permissions liegen nun eben alle Rechte in folgender Form vor
//...$permissions['permission1'], $permissions['permission2'], $permissions['permission3'],
```

Ich habe mir weiterhin angewöhnt, Rechte und Tabellennamen als Konstanten in einer gesonderten PHP-Datei festzulegen und irgendwo zu Beginn eines jeden Scriptes zu includieren.
Bsp.:

```
//Berechtigungen
define(NEWS_MODUL, "permission1");
define(BLA_MODUL, "permission2");
define(BLUB_MODUL, "permission3");

//Tabellen
$prefix = 'usersystem_';
define(USER_TABLE, $prefix ."users");
define(GROUP_TABLE, $prefix ."groups");
define(USER_PERMISSION_TABLE, $prefix ."user_permissions");
```
..was eine aussagefähige Bezeichnung der Felder innerhalb der Tabellen überflüssig macht - es reicht also permission1, permission2, permission3.

Im Querry werden diese Konstanten dann so verwendet:

```
// ".GROUP_TABLE."
SELECT * FROM ".GROUP_TABLE." WHERE group_id = '".$userdata['group_id']."'

//und beim Überprüfen:
switch($permissions[NEWS_MODUL] )
{
   case 1:
      //du darfst lesen
   break;
   case 2:
      //du darfst schreiben und lesen aber nicht editieren
   break;
   case 4
      //du darfst alles
   break;
}
```

Ich hoffe, das ich etwas brauchbares zu eurer Diskussion beitragen konnte und entschuldige mich schonmal für die Überlänge meines Postings 

Bloddy Newbie


----------



## MiLa (2. Dezember 2003)

@ Fabian: 
Genau das habe ich ja zur Zeit, optimum wäre es nun für mich, wenn ich pro User nur eine Zahl/String hab und mit irgendeinem Algorythmus die Rechte daraus auslese.
Und exakt das ist mein Problem, das andere hätte ich auch selbst hinbekommen 

Aber danke an alle, die hier  bisher ihren Senf dazu gegeben haben, besonders an den "bloddy newbie", das wird mit Sicherheit einigen weiterhelfen (mir leider nicht)

Also, hat jemand eine Idee für dieses Prinzip, was ich mir vorstelle ?!


----------



## alive (2. Dezember 2003)

man könnte... erst aus diese Tabelle USER_RECHTE rauslesen ob die Úseríd und die Moduleid, wo er sich gerade befindet, vorhanden ist.Dann fällt schon mal raus, dass man das Recht NULL brauch...

Dann kann man, wie oben schon gesagt..
Lesen 1 --> r_lesen
Schreiben 2 --> r_schreiben
Löschen 3 --> r_loeschen
Edit 4 --> r_edit

und speichern: r_lesen;r_schreiben;r_loeschen;r_edit

Dann könnte man die Rechtespalte aus der Tabelle per explode() teilen, und in einer Funktion nachschauen ob das jeweilige Recht vorhanden ist...Diese Funktion würde TRUE und FALSE zurückgeben...dann könnte man die Funktion
halt so gestalten, das sie als Argumente check_rights($Moduleid,$USERID,$gesuchtes_recht) aussehen...

schwift nun zwar en bissl ab... aber das mit dem 1+3 usw habe ich net ganz sooo durchblickt 

cu alive


----------



## MiLa (2. Dezember 2003)

Alles schön und gut, aber es geht mir immernoch darum das so zu machen, wie in meinem letzten Post beschrieben...

Können die "Profis" nicht mal was zu sagen?! Nehmt das als Hilferuf von mir...

Tim...?! HILFEEE !


----------



## js-mueller (2. Dezember 2003)

Muss ja nicht unbedingt eine komplette zahl sein oder? 
Du kannst ne zahl machen die du immer durch # trennst. So hast du die rechte abgeschottet.
Dein Skript muss sich nur merken welches recht an welcher Stelle steht. Und mit dem Wissen kann man doch ganz leicht die stelle rausnehmen bit operatoren ansetzen und dann wieder an die gewünschte stelle setzen.
Ich hoffe ich hab mich verständlich ausgedrückt. Ich versuchs mal in nem beispiel


```
1001#110001#00110#11001 // das mit explode trennen.
0 = mdl1, 1 = mdl2, 2 = mdl3 // Das aus einer mysql spalte auslesen, wo sich das skript merkt welcher bereich zu welchem modul gehört
110001 // Modul 1 rauspicken

110001 & 001000 // Bitoperatoren

// Am schluss mit ner for schleife die rechte für das modul wieder an die richtige stelle rücken.
```

Das war mein bescheidener Lösungsvorschlag, vieleicht kann ja wirklich einer von den Profis das verbessern oder was komplett neues in den Raum werfen.


----------



## Fabian H (2. Dezember 2003)

Hm, du könntest versuchen, einen Varchar mit lauter Nullen und Einsen zu machen (Varchar deshalb, weil ja eine Null an erster Stellen liegen könnte)

Sagen wir, jedes Modul hätte vier Stellen in dem String. Die Reihenfolge ist natürlich fest.
Der String könnte dann z.B. so aussehen:

```
01000100101011011100
\  /\  /\  /\  /\  /
 \/  \/  \/  \/  \/
  |   |   |  |   |
  |   |   |  |  usw...
  |   |   | Modul 4
  |   |  Modul 3
  |  Rechte für Modul 2
Rechte für Modul 1
```
Logischerweise steht eine Null für "verboten" und eine Eins für "erlaubt".
Die Reihenfolge, was die Null und Einsen in einen Block darstellen muss natürlich auch fest sein.

Frage:
Wieso willst du das nicht auf mehrere Zeilen verteilen?
Wäre doch *wesentlich* übersichtlicher so!

//Edit: Da war wohl jemand schneller


----------



## MiLa (2. Dezember 2003)

Joa, den Gedanken hatte ich in Ansätzen auch schon...

Bisher sind hier ja ganz nette Lösungsmöglichkeiten erschienen, aber für mich persönlich noch nicht das wahre 

Vieleicht kann man es ja aber auch mit irgendeiner Summe lösen?!

Irgendwo war das meiner Meinung nach doch so,
dass man die Summe von mehreren zahlen angeben konnte und damit Eindeutige Rechte anzugeben, könnt sein, dass das bei Adminmod für Half-Life war  
Weiss nicht so genau...

Aber vieleicht hat ja jmd. anderes noch nen Vorschlag in meien Richtung... *hoff*


----------



## MiLa (2. Dezember 2003)

Überlege mal, ich habe 2000 User in einer Community,
und diese haben unterschiedliche Zugriffsrechte auf 30 Module...

...sind das nicht ein paar überflüssige Datensätze(60000), die ich mit etwas Rechnerei und Überlegungen vereinfachen kann ?!

Also ichw erde mir nochmal etwas Gedanken dazu machen, wie ich das nun lösen werd...
Das gefällt mir irgendwie ganz gut, muss mal sehn...


```
01000100101011011100
\  /\  /\  /\  /\  /
 \/  \/  \/  \/  \/
  |   |   |  |   |
  |   |   |  |  usw...
  |   |   | Modul 4
  |   |  Modul 3
  |  Rechte für Modul 2
Rechte für Modul 1
```

*überleg*


----------



## Fabian H (2. Dezember 2003)

MySQL ist nicht umsonst verdammt schnell!

Aber Datenabnkleistung hin oder her: Jo, du hast recht, wenn man es auf einen String verkürzt, ist das ganze wohl effektiver.


//Edit:


> *überleg*


Hm, so schwer ist das (zumindest in meiner Theorie )  gar nicht.

Nehmen wir an,die Module haben eine interne Nummer. Diese ist von 1 bis meinetwegen 30 durchnummeriert. Es gibt keine Lücken (also: 5, 6, 7, 9, 10)

Zugreiffen ist dann schonmal einfach:

```
//module23.php:
$iModuleId = 12;

$sRighrs = getRights($iModuleId);

[...]

function getRights( $a_iModuleId )
{
    //Hier kriegst du von irgendwo den langen eins-null String her:
    $sRightData = "1011010100101010101111010101010110110";

    return substr($sRightData, ($a_iModuleId - 1) * 4, 4);
}
```

Du kannst auch das _-1_ weglassen, wenn deine Modul-Ids bei null anfangen.

Naja, und ne Routine zum Speichern sollte auch nicht allzu schwer sein.

Das wäre mal meine Idee

hth


----------



## MiLa (2. Dezember 2003)

Joa, ich geh das heut kurz vorm einschlafen nochmal durch und kritzel mir die Struktur auf, vieleicht wird daraus ja was einigermaaßen gescheites... 

Also, soviel dazu...
ich mach mich nu Bettfertig und geb mir mal ne schöpferische Pause 

Aber wenn jemand noch etwas produktives dazu beizutragen hat,
morgen ist auch noch ein Tag!


----------



## MiLa (3. Dezember 2003)

OK, ich habe jetzt die Methode von Fabian in einem kleinen Testscript übernommen und ein bisschen rummodifiziert 
Das ganze sieht nurn so aus...


```
<?PHP
/*
$modul			- gibt die ID des Moduls an
getrights() 	- holt Rechte für dieses Modul
check()			- überprüft die Rechte des Users

0	- nichts
1 	- lesen
2 	- schreiben
4 	- ändern
16	- löschen
*/
$modul=1;

if(check(getrights($modul),1))
{
	echo"Du darfst lesen<br>";	
}
else
{
	echo"Du darfst nicht lesen<br>";	
}
if(check(getrights($modul),2))
{
	echo"Du darfst schreiben<br>";	
}
else
{
	echo"Du darfst nicht schreiben<br>";	
}
if(check(getrights($modul),4))
{
	echo"Du darfst ändern<br>";	
}
else
{
	echo"Du darfst nicht ändern<br>";	
}
if(check(getrights($modul),16))
{
	echo"Du darfst löschen<br>";	
}
else
{
	echo"Du darfst nicht löschen<br>";	
}
function check($user,$check)
{
    if(($user & $check)==$check)
    {
        return true;
    }
    else
    {
        return false;
    }
}
function getrights($modul_id)
{
    $rights = "111101110000";
    return substr($rights, $modul_id  * 4, 4);
}
?>
```

Mein größter wunsch, um das ganze nun noch zu perfektionieren, ist es das irgendwie so hinzubiegen, dass die Module keine durchgehende Reihenfolge mehr haben müssen, sondern das irgendwie anders gelöst wird.
Es sollte weiterhin alles von einem Wert ausgehen nur sollte es auch Sprünge in den Modulnummern geben können...

Überlegung:
Man könnte vor die jeweiligen "Blöcke" im $rights String einen Indexwert setzen, nur gibt sich dann ein Problem, wenn wir in den zweistelligen Bereich kommen 

Jemand ne Idee?


----------



## Fabian H (3. Dezember 2003)

Also:
Ich denke mal, dass du hier die Nummeron 1, 2, 4, 16, usw. nicht mehr brauchst, du kannst beliebige Nummern nehmen.

Mein Vorschlag wäre folgender:
Du lässt die Modul-Ids, machst aber eine Funktion, mit der man mehrere Werten auf einmal auslesen kann.
Zusätzlich kannst du auch gleich Konstanten für die Privilegien festlegen (Modul-Ids fangen bei eins an):

```
define("PR_READ", 0);
define("PR_WRITE", 1);
define("PR_CHANGE", 2);
define("PR_DELETE", 3);

function getRights( $a_mRights, $a_iModuleId )
{
    // Hier irgendwo dein eins-null String herbekommen
    $sPrivileges = "0100101011011010110101";

    if (!is_array($a_mRights)) {
        $aRights = array( $a_mRights );
    } else {
        $aRights = $a_mRights;
    }

    $sModuledata = substr($sPrivileges, $a_iModuleId - 1, 4);
    $aRetArray   = array();

    foreach( $aRights as $iOneRight) {
        if ($sModuleData{$iOneRight} == "1") {
            $aRetArray[$iOneRight] = "Y";
        } else {
            $aRetArray[$iOneRight] = "N";
        }
    }

    return $aRetArray;
}

// Modul 4:
$iModuleId = 4;
$aModuleRights = GetRights( array( PR_READ, PR_WRITE, PR_DELETE),
                                    $iModuleId );

if (in_array("Y", $aModuleRights)) {
    if ($aModuleRights[PR_WRITE] == "Y") {
        echo "Schreibrechte";
    } elseif ($aModuleRights[PR_DELETE]) {
        echo "Du darfst löschen";
    }  //Und so weiter;
} else {
    echo "Keine Rechte!";
}
```

Sorry für die schlechte Einrückung, aber bei mir geht grad Copy und Paste net, sprich ich kann den Code nich in einem Editor vorschreiben

hth


----------



## MiLa (3. Dezember 2003)

Hm, bist du dir sicher, dass das Script so funktioniert?

Ich habs bei mir ausprobiert und habe permanent keine Rechte...

Ich seh im Endeffekt auch keinen Vorteil bei dir, der Unterschied ist eigendlich auch minimal...
Nur dass du die Funktion einmal aufrufst und das über Arrays machst und ich die Funktion einfach mehrmals aufrufe.

Oder sehe ich da was falsch?!


----------



## Fabian H (3. Dezember 2003)

Eine gescheite php.ini hätte geholfen!

```
$sModuledata = substr($sPrivileges, $a_iModuleId - 1, 4);
```
wird zu

```
$sModuleData = substr($sPrivileges, $a_iModuleId - 1, 4);
```

Der Vorteil: naja, a) musst du wissen b) du hast die Nummern [1, 2, 4, 16] benutzt. c) der Code ist aus meiner Sicht etwas klarer.


----------



## MiLa (3. Dezember 2003)

> Eine gescheite php.ini hätte geholfen!


Was ist an meiner denn ungescheit?



> ```
> $sModuledata = substr($sPrivileges, $a_iModuleId - 1, 4);
> ```
> wird zu
> ...



Versteh die Aussage daran nicht 

Wolltest du sagen dass es so wird: ?

```
$sModuledata = substr($sPrivileges, ($a_iModuleId - 1)*4, 4);
```

Das mit den Nummern lässt sich ja relativ beliebig anpassen...

Und das mit der Code Übersicht/Klarheit ist relativ, nech ?!


----------



## Tim C. (3. Dezember 2003)

Zu den Nummern nur mal kurz eingeworfen - habe jetzt keine Lust den ganzen Thread nachzuvollziehen - die Bitreihe geht 1,2,4,8 und nicht 1,2,4,16


----------



## MiLa (3. Dezember 2003)

Hab ich mitlerweile auch gemerkt, danke Tim 

Ne...erm...könntest du mir den Gefallen tun und dir trotzdem alles mal durchzulesen ?!
Du weisst immer soviel 

Bitte!


----------



## Fabian H (3. Dezember 2003)

Ich hab versehentlich einen Buchstaben in der Variable klein geschrieben (das "d" von "data" -> "Data")

Eine gescheite php.ini hätte deshalb geholfen, weil du dann eine Notice bekommen hättest und den Fehler (hoffentlich) selber beheben hättest können!

//Edit: ach ja, das _* 4_ hab ich auch vergessen 
Die korrekte Zeile würde dann so aussehen:

```
$sModuledata = substr($sPrivileges, ($a_iModuleId - 1) * 4, 4);
```


----------



## MiLa (3. Dezember 2003)

Naja, wie auch immer... 
So gravierend ist der Unterschied von meinem zu deinem Code ja nicht...

Von daher werde ich wohl die Lösung nehmen, wenn hier keine andere gescheite auftaucht.


----------



## Tim C. (3. Dezember 2003)

> _Original geschrieben von Lars Michelsen _
> *Hab ich mitlerweile auch gemerkt, danke Tim
> 
> Ne...erm...könntest du mir den Gefallen tun und dir trotzdem alles mal durchzulesen ?!
> ...


Danke für die Blumen, aber Fabian weiss mindestens genauso viel 

Ausserdem kämpf ich grade mit nem eigenen mini Rechtesystem. Da würde ich nur dauernd was durcheinanderschmeissen und meine Variablen sind gar nicht so schön an die Hungarian Notes angelehnt wie die von Fabian


----------



## MiLa (3. Dezember 2003)

Hm, alles am Rechtesystem basteln 

Aber könntest uns ja wohl mal dein Konzept unterbreiten, oder ist das Top Secret?!


----------

