TS: Rechteverwaltung

Hallo und guten Tag,

ich habe eine Frage zum Datenbankmodell und den möglichen Abfragen für meine kleine Rechteverwaltung. Für die reine User<->Rechteverwaltung funktioniert das Modell schon gut, soll aber nun auch für Gruppen erweitert werden. Die User können also Mitglieder in Gruppen werden, die dann schon gewisse Grundrechte haben.

DBMS = MySQL Engine = InnoDB

Die Rechte stehen in "rights". Die User haben ihren Stammeintrag in "user". Erweiterte Userdaten stehen in "user_address" (sind hier aber nebensächlich) Die Gruppenzugehörigkeit user<->gruppe steht in "groups"

  

user                                                 rights
==========                                           ==========
id --+---------------------------------------------> id_user (fk)
     |                                        +----> id_groups (fk) 
     |                                        |      project
     |                                        |      module
     |                 groups                 |      function
     |                 ==========             |               
     |                 id --------------------+ 
     +---------------> id_user (fk)

.  

Bisher funktioniert das so: Ist der User angemeldet (authentifiziert + login), dann werden über die Usernummer alle Rechte zum aktuellen Projekt, dem aktuellen Modul und der gewünschten Funktion aus der Tabelle geholt und verdichtet.

Verdichtet bedeutet:
hat der User schon das Recht "all" auf einem Projekt, hat er automatisch auch alle Rechte für alle Module darin und alle Funktionen des Moduls.

Hat der User z.B. nur das Recht "use" auf ein Projekt, das Recht "use" auf ein Modul, dann benötigt er auch explizit das Recht "use" für eine Funktion, um diese nutzen zu dürfen.

Ich möchte diese Abfragelogik für die Rechtetebelle möglichst nicht viel ändern.

Bei Abfrage, welche Rechte der User hat, muss ich vorher auch noch die Gruppenzugehörigkeit des Users erfragen und dann z.B. mit einem "where id_user = $user and $id_groups in ..."

Wie muss mein (...) lauten, damit es klappt?

Oder sollte ich das ganz anders machen?

Grüße
TS

--
es wachse der Freifunk
http://freifunk-oberharz.de

akzeptierte Antworten

  1. Tach!

    Bei Abfrage, welche Rechte der User hat, muss ich vorher auch noch die Gruppenzugehörigkeit des Users erfragen und dann z.B. mit einem "where id_user = $user and $id_groups in ..."

    Wie muss mein (...) lauten, damit es klappt?

    Wie lautet denn der Teil vor dem WHERE? Sprich: was soll denn das konkrete Ergebenis sein?

    Oder sollte ich das ganz anders machen?

    Userrechte UNION Gruppenrechte. Kann auch gleich doppelte Ergebnisse wegfiltern.

    dedlfix.

  2. Hallo TS,

    aus der Hüfte würde ich diesen Schuss (hoffentlich nicht Stuss) abgeben:

    SELECT DISTINCT projekt, modul, funktion
    FROM rechte
    WHERE r.id_user = $user
      OR  r.id_group IN (SELECT id FROM groups g WHERE g.id_user = $user)
    

    Es gibt allerdings DB-Systeme, bei denen eine OR-Bedingung extrem inperformant ist und zu einem Table-Scan führt. Auf unserem IBM Großrechner war OR darum lange verboten. Ob InnoDB in MySQL dazu gehört, weiß ich nicht. Wenn Du nicht viele User hast, ist das wurscht. Wenn Du ein Performance-Problem befürchtest, kannst Du es auch so machen:

    SELECT r1.projekt, r1.modul, r1.funktion
    FROM rechte r1
    WHERE r1.id_user = $user
    UNION
    SELECT r2.projekt, r2.modul, r2.funktion
    FROM rechte r2
    WHERE r2.id_group IN (SELECT g.id FROM groups g WHERE g.id_user = $user)
    

    SELECT DISTINCT im ersten Beispiel bzw. der UNION im zweiten Beispiel eliminieren Duplikate, d.h. wenn Du ein Recht auf mehreren Wegen erhältst, bekommst Du trotzdem nur eine Ergebniszeile dafür. Damit diese Vorverdichtung greift, musst Du die selektierten Spalten auf die reinen Rechte begrenzen und die IDs heraushalten. Wenn Du die Duplikate unbedingt haben willst, musst Du den DISTINCT weglassen bzw. dem UNION ein ALL hinzufügen.

    Ob Du die Spalten so exakt qualifizieren musst, wie ich das getan habe, weiß ich nicht. Ich habe schon so oft Fehler wegen mehrdeutiger Spaltenangaben bekommen, dass ich lieber zu viel als zu wenig qualifiziere.

    Rolf

    1. Hallo und guten Tag Rolf,

      SELECT DISTINCT projekt, modul, funktion
      FROM rechte
      WHERE r.id_user = $user
        OR  r.id_group IN (SELECT id FROM groups g WHERE g.id_user = $user)
      

      So hatte ich es wohl im Bauch, kam aber nicht mehr auf die richtige Syntax. Bin eben eingerostet.

      Es gibt allerdings DB-Systeme, bei denen eine OR-Bedingung extrem inperformant ist und zu einem Table-Scan führt. Auf unserem IBM Großrechner war OR darum lange verboten. Ob InnoDB in MySQL dazu gehört, weiß ich nicht. Wenn Du nicht viele User hast, ist das wurscht. Wenn Du ein Performance-Problem befürchtest, kannst Du es auch so machen:

      SELECT r1.projekt, r1.modul, r1.funktion
      FROM rechte r1
      WHERE r1.id_user = $user
      UNION
      SELECT r2.projekt, r2.modul, r2.funktion
      FROM rechte r2
      WHERE r2.id_group IN (SELECT g.id FROM groups g WHERE g.id_user = $user)
      

      Aber das gefällt mir besser, wenn man damit gleichzeitig die Gefahr beseitigt, das DBMS zum Stehen zu bringen.

      Es könnten mittelfristig ca. 300.000 User werden, die aber selbstverständlich nicht alle gleichzeitig und jeden Tag zugreifen.

      SELECT DISTINCT im ersten Beispiel bzw. der UNION im zweiten Beispiel eliminieren Duplikate, d.h. wenn Du ein Recht auf mehreren Wegen erhältst, bekommst Du trotzdem nur eine Ergebniszeile dafür. Damit diese Vorverdichtung greift, musst Du die selektierten Spalten auf die reinen Rechte begrenzen und die IDs heraushalten. Wenn Du die Duplikate unbedingt haben willst, musst Du den DISTINCT weglassen bzw. dem UNION ein ALL hinzufügen.

      Duplikate dürften eigentlich nicht vorkommen, da entweder das Feld id_user, oder das Feld id_group eines Datensatzes belegt sein darf. Darum weiß ich auch nicht, ob das Design nicht noch eine Macke hat.

      Alternativ könnte man Numernkreise vergeben oder bei einer gemeinsamen FK-ID in der Tebelle rights mit Präfix arbeiten... Aber das würde das Ganze bei der Abfrage vermutlich auch wieder langsam machen.

      Grüße
      TS

      --
      es wachse der Freifunk
      http://freifunk-oberharz.de
      1. Duplikate dürften eigentlich nicht vorkommen, da entweder das Feld id_user, oder das Feld id_group eines Datensatzes belegt sein darf. Darum weiß ich auch nicht, ob das Design nicht noch eine Macke hat.

        Das meine ich nicht. Es könnte zum Beispiel sein, dass ein User das Recht "read" im Modul "bar" des projekts "foo" hat. Darüber hinaus ist er in der Gruppe "snafu", die dieses Recht ebenfalls hat. Und dann noch in der Gruppe "hurz", die das AUCH darf. Sag mir nicht, dass das nicht vorkommt. Bei 300.000 Usern hast Du eine Menge Gruppen und dann wirst Du schneller Huddel in den Rechten haben als Du "Administratorensorgfalt" buchstabieren kannst.

        Die automatische Doublettenbeseitigung durch SELECT DISTINCT oder UNION sorgt dafür, dass diese Mehrfachnennung des gleichen Rechts in deinem Rechtecontroller nicht sichtbar wird.

        Deine Design-Bedenken sind natürlich berechtigt. Rein formal handelt es sich hier um drei Entitäten: User, Group und Recht, und zwischen allen dreien besteht je eine m:n Beziehung.

        • Ein User kann in m Gruppen sein / Eine Gruppe kann n User enthalten
        • Ein User kann m Rechte haben / Ein Recht kann n Usern erteilt werden
        • Eine Gruppe kann m Rechte haben / Ein Recht kann n Gruppen erteilt werden

        Ach so, und dann gibt's da noch diese neckische kleine 1:n Abhängigkeit:

        • Ein Recht kann n andere Rechte implizieren

        D.h. für ein KONZEPTIONELLES Modell hast Du 3 Entitäten "K_User", "K_Group" und "K_Right" und vier Beziehungen dazwischen. Die rekursive Rechtebeziehung ist aber, wenn ich das richtig verstehe, sehr eng definiert und du willst sie als Programmcode abbilden. Wenn Du das übrige konzeptionelle Modell 1:1 in die DB überträgst, entstehen demnach 6 Tabellen. "K_User", "K_Group", "K_Right", "K_UserGroup", "K_UserRight", "K_GroupRight".

        Kann man so bauen. Tun auch viele. Und riskieren, bei einer großen DB in die Knie zu gehen.

        Man kann durchaus ein technisches Modell designen, das weniger Tabellen nutzt und bewusst Redundanzen in Kauf nimmt, um in den häufigen Operationen performanter zu sein. Das ist bei deiner "rights" Tabelle der Fall. Sie integriert "K_Right", "K_UserRight" und "K_GroupRight" und erlaubt das Lesen von Rechten ohne Joins. Aber wenn Du ein Projekt umbenennst, änderst Du sehr viele Zeilen. Was aber nur selten vorkommt. Deswegen würde ich sagen: Ist ok, mach das so. Aber sei Dir bewusst, dass das eine technische Optimierung ist, für die Du Nachteile in Kauf nimmst.

        Deine "groups" Tabelle entspricht der K_UserGroup. Eine Zusammenfassung von K_Group und K_UserGroup würde ich nicht in Betracht ziehen. Für die Gruppenverwaltung brauchst Du mit Sicherheit sowas wie einen Gruppennamen und den würde ich nicht redundant durch die groups-Tabelle streuen. Eventuell kommt ja noch was hinzu wie ein Anlegedatum oder weitere Informationen. Deswegen: Benenne die id-Spalte in groups um in "id_group" und mach eine eigene Gruppentabelle.

        Dann müsstest Du ggf. noch überlegen, ob Du in "rights" mit Gültigkeitsintervallen arbeitest. Vielleicht willst Du Rechte nur zeitweilig einräumen. Ein Ablaufdatum in "rights" verhindert, dass der Admin vergisst, ein Recht zu entfernen.

        Rolf

        1. Hallo Rolf,

          Duplikate dürften eigentlich nicht vorkommen, da entweder das Feld id_user, oder das Feld id_group eines Datensatzes belegt sein darf. Darum weiß ich auch nicht, ob das Design nicht noch eine Macke hat.

          Das meine ich nicht. Es könnte zum Beispiel sein, dass ein User das Recht "read" im Modul "bar" des projekts "foo" hat. Darüber hinaus ist er in der Gruppe "snafu", die dieses Recht ebenfalls hat. Und dann noch in der Gruppe "hurz", die das AUCH darf. Sag mir nicht, dass das nicht vorkommt. Bei 300.000 Usern hast Du eine Menge Gruppen und dann wirst Du schneller Huddel in den Rechten haben als Du "Administratorensorgfalt" buchstabieren kannst.

          Die automatische Doublettenbeseitigung durch SELECT DISTINCT oder UNION sorgt dafür, dass diese Mehrfachnennung des gleichen Rechts in deinem Rechtecontroller nicht sichtbar wird.

          Deine Design-Bedenken sind natürlich berechtigt. Rein formal handelt es sich hier um drei Entitäten: User, Group und Recht, und zwischen allen dreien besteht je eine m:n Beziehung.

          • Ein User kann in m Gruppen sein / Eine Gruppe kann n User enthalten
          • Ein User kann m Rechte haben / Ein Recht kann n Usern erteilt werden
          • Eine Gruppe kann m Rechte haben / Ein Recht kann n Gruppen erteilt werden

          Ach so, und dann gibt's da noch diese neckische kleine 1:n Abhängigkeit:

          • Ein Recht kann n andere Rechte implizieren

          D.h. für ein KONZEPTIONELLES Modell hast Du 3 Entitäten "K_User", "K_Group" und "K_Right" und vier Beziehungen dazwischen. Die rekursive Rechtebeziehung ist aber, wenn ich das richtig verstehe, sehr eng definiert und du willst sie als Programmcode abbilden. Wenn Du das übrige konzeptionelle Modell 1:1 in die DB überträgst, entstehen demnach 6 Tabellen. "K_User", "K_Group", "K_Right", "K_UserGroup", "K_UserRight", "K_GroupRight".

          Kann man so bauen. Tun auch viele. Und riskieren, bei einer großen DB in die Knie zu gehen.

          Man kann durchaus ein technisches Modell designen, das weniger Tabellen nutzt und bewusst Redundanzen in Kauf nimmt, um in den häufigen Operationen performanter zu sein. Das ist bei deiner "rights" Tabelle der Fall. Sie integriert "K_Right", "K_UserRight" und "K_GroupRight" und erlaubt das Lesen von Rechten ohne Joins. Aber wenn Du ein Projekt umbenennst, änderst Du sehr viele Zeilen. Was aber nur selten vorkommt. Deswegen würde ich sagen: Ist ok, mach das so. Aber sei Dir bewusst, dass das eine technische Optimierung ist, für die Du Nachteile in Kauf nimmst.

          Deine "groups" Tabelle entspricht der K_UserGroup. Eine Zusammenfassung von K_Group und K_UserGroup würde ich nicht in Betracht ziehen. Für die Gruppenverwaltung brauchst Du mit Sicherheit sowas wie einen Gruppennamen und den würde ich nicht redundant durch die groups-Tabelle streuen. Eventuell kommt ja noch was hinzu wie ein Anlegedatum oder weitere Informationen. Deswegen: Benenne die id-Spalte in groups um in "id_group" und mach eine eigene Gruppentabelle.

          Dann müsstest Du ggf. noch überlegen, ob Du in "rights" mit Gültigkeitsintervallen arbeitest. Vielleicht willst Du Rechte nur zeitweilig einräumen. Ein Ablaufdatum in "rights" verhindert, dass der Admin vergisst, ein Recht zu entfernen.

          Danke für die wertvollen Anregungen. Werde ich doch gleich zu meinen Unterlagen nehmen.

          BTW: vielen Dank an denjenigen, der die Druckfunktion (CSS) des Forums so schön gemacht hat. Gefällt mir Daumen hoch :-)

          Grüße
          TS

          --
          es wachse der Freifunk
          http://freifunk-oberharz.de
          1. Wieso immer diese Fullquotes? Wer wissen will, worauf du antwortest, kann hochscrollen.

        2. Hi,

          Es könnte zum Beispiel sein, dass ein User das Recht "read" im Modul "bar" des projekts "foo" hat. Darüber hinaus ist er in der Gruppe "snafu", die dieses Recht ebenfalls hat. Und dann noch in der Gruppe "hurz", die das AUCH darf.

          das ist ja der harmlose Fall. Interessant wird's, wenn die Berechtigung in der einen Gruppe "read", in der anderen Gruppe "write" (was "read" impliziert) und beim Benutzer auf "forbidden" steht.

          cu,
          Andreas a/k/a MudGuard

          1. Hallo Andreas,

            Es könnte zum Beispiel sein, dass ein User das Recht "read" im Modul "bar" des projekts "foo" hat. Darüber hinaus ist er in der Gruppe "snafu", die dieses Recht ebenfalls hat. Und dann noch in der Gruppe "hurz", die das AUCH darf.

            das ist ja der harmlose Fall. Interessant wird's, wenn die Berechtigung in der einen Gruppe "read", in der anderen Gruppe "write" (was "read" impliziert) und beim Benutzer auf "forbidden" steht.

            Dann müüste ich die NDS oder ActiveDirectory nachbauen. Die nehmen darauf Rücksicht, auch inherited und filtered zu berücksichtigen. Ist aber kein Problem, das einzubauen, wenn es notwendig ist. Die Verdichtung der Rechte findet erst in der API statt. Man muss dann nur die Prioritäten festlegen.

            Ich arbeite hier erstmal nur mit positiven Rechten. Man könnte aber auch festlegen, dass das kleinste festgelegte Recht gilt:

            
            User       n/a
            Group_1    use
            Group_2    forbidden 
            --------------------
            forbidden
            
            User       use
            Group_1    n/a
            Group_2    all
            --------------------
            use
            
            User       n/a
            Group_1    n/a
            Group_2    n/a
            --------------------
            not permitted (~= forbidden)
            
            .
            
            

            Da sich die Auswertung in einer einzigen Funktion abspielt, gibt es also auch kein "wo muss ich überall ändern"-Chaos.

            Grüße
            TS

            --
            es wachse der Freifunk
            http://freifunk-oberharz.de
  3. Hallo und guten Tag,

    und noch eine Frage dazu...

    gibt es eine Möglichkeit, ein Update auf eine Tabelle und nur wenn dies erfolgreich war, die vorgesehene Abfrage durchzuführen und zwar in einem gebundenen Statement (atomar)?

    Bei Stored Routines werden die einzelnen Statements ja trotzdem nicht gebunden, oder ist das jetzt geändert?

    Grüße
    TS

    --
    es wachse der Freifunk
    http://freifunk-oberharz.de
    1. Ich weiß nicht ob ich dich jetzt richtig verstehe. Ich kenne gebundene Parameter in einem prepared statement. Ein gebundenes Statement sagt mir nichts. Aber was Du da beschreibst, klingt nach einer Transaktion. InnoDB unterstützt das. Schau Dir dazu auch den Isolation Level für die Transaktion an, damit Du sicher bist, dass die für deinen Zweck richtigen Sperren gesetzt werden.

      Pseudocode:

      BEGIN TRANSACTION
      UPDATE blablabla
      IF erfolgreich
         SELECT blablabla
         COMMIT
      ELSE
         ROLLBACK
      ENDIF
      

      Transaktionen kannst Du auch in einer Stored Procedure durchführen. Ohne Transaktion sind meines Wissens nur die einzelnen Statements atomar. Infos z.B. hier.

      Rolf

      1. Hallo Rolf,

        Was Du da beschreibst, klingt nach einer Transaktion. InnoDB unterstützt das. Schau Dir dazu auch den Isolation Level für die Transaktion an, damit Du sicher bist, dass die für deinen Zweck richtigen Sperren gesetzt werden.

        Genau das meinte ich.

        Pseudocode:

        BEGIN TRANSACTION
        UPDATE blablabla
        IF erfolgreich
           SELECT blablabla
           COMMIT
        ELSE
           ROLLBACK
        ENDIF
        

        Transaktionen kannst Du auch in einer Stored Procedure durchführen. Ohne Transaktion sind meines Wissens nur die einzelnen Statements atomar.

        Diesen Fehler wollte ich ausmerzen. Die Raceconndition wäre an dieser Stelle zwar nicht tödlich, könnte aber doch zu lästigen "whitescreens" führen.

        Infos z.B. hier.

        Bleibt jetzt nur die Frage, wie sehr eine Transaktion die Datenbank bremst. Im "is-logged"-Modus würde die bei jedem Request jedes Users ausgelöst werden.

        Danke für deine Unterstützung. Nun bin ich doch endlich mal wieder ein Stück weitergekommen ;-)

        Grüße
        TS

        --
        es wachse der Freifunk
        http://freifunk-oberharz.de
        1. Natürlich machen Transaktionen - bzw. schon die Transaktionsfähigkeit - eine DB langsamer. InnoDB ist deutlich behäbiger als MyISAM. Wenn eine Transaktion einen Lock setzt, müssen andere warten, bis der Lock freigegeben wird. Deshalb auch die Überlegungen zum Isolation Level, bzw. man muss auch genau gucken welche Rows durch ein SQL Statement gelockt werden, damit nicht zu viel gesperrt wird.

          Du musst aber nicht jedes Statement in eine Transaktion einschließen. Solange Du nicht mehrere Statements zu einer Unit-Of-Work zusammenschließen musst, oder ein "ganz oder gar nicht" garantieren musst, lässt Du sie weg. Sperren anderer Statements werden trotzdem beachtet. Die Transaktion gibt dir nur die Rollbackfähigkeit und eine längere Lebensdauer von gesetzten Sperren bis zum Transaktionsende.

          Sobald Du COMMIT oder ROLLBACK gemacht hast, ist die Transaktion zu Ende und die folgenden Befehle laufen wieder normal.

          Wenn Du Logging in eine Datenbank durchführst, empfehle ich Dir übrigens eine MyISAM Tabelle. Die sind deutlich flinker und ich wüsste nicht, warum man transaktional loggen sollte. Jeder INSERT in die Log-Table steht doch für sich - oder?

          Rolf

          1. Hallo Rolf,

            Wenn Du Logging in eine Datenbank durchführst, empfehle ich Dir übrigens eine MyISAM Tabelle. Die sind deutlich flinker und ich wüsste nicht, warum man transaktional loggen sollte. Jeder INSERT in die Log-Table steht doch für sich - oder?

            MyISAM kann aber mWn keine Relations (also FK mit refenenzieller Integrität und Änderungs/Löschweitergabe), oder?

            Grüße
            TS

            --
            es wachse der Freifunk
            http://freifunk-oberharz.de
            1. Das weiß ich nicht. Ich bin von meinem Arbeitgeber geprägt, bei dem all diese DB-Automatiken als Performancekiller verboten waren, und mache alles von Hand. Mag in MySQL falsch sein, weiß ich nicht.

              Rolf

          2. InnoDB ist deutlich behäbiger als MyISAM.

            Das galt einmal - vor 10 Jahren. Alleine schon die Tatsache, dass InnoDB Row-Level-Locking kann, ist gegenüber MyISAM ein riesiger Vorteil. MyISAM hat nur noch in Ausnahmefällen noch seine Daseinsberechtigung.

            1. InnoDB ist deutlich behäbiger als MyISAM. Das galt einmal - vor 10 Jahren.

              Dann hat der Kumpel, auf dessen Webseite ich was gemacht habe, entweder ein uraltes MySQL oder ich hab was falsch gemacht...

              1. Hallo und guten Tag Ihr Zwei,

                InnoDB ist deutlich behäbiger als MyISAM. Das galt einmal - vor 10 Jahren.

                Dann hat der Kumpel, auf dessen Webseite ich was gemacht habe, entweder ein uraltes MySQL oder ich hab was falsch gemacht...

                Bitte nicht prügeln. Jeder Hinweis zählt.

                Wie könnte ich das jetzt mal möglichst faul kontrollieren? So ganz ohne Belang scheint es mir nach Internet-Recherche doch noch nicht zu sein!

                Grüße
                TS

                --
                es wachse der Freifunk
                http://freifunk-oberharz.de
                1. Lass ein kleines PHP Script ganz behäbige 1000 Inserts in eine (ID, Text) Table machen. Oder 10000 falls es zu schnell geht. Die ID nummerierst Du durch.

                  Und wenn das fertig ist, mach 10000 Single-Row Selects auf eine zufällige ID im zuvor verwendeten Bereich. Eventuell cached der Server das noch hinreichend. Hast Du genug Platz für die DB? Dann nimm 100.000 Zeilen mit je 100 Bytes Text. Wenn der SQL Server genug zu tun hat, dann cached er das nicht mehr.

                  Ich denke, dass vor allem die Inserts langsamer sind, weil InnoDB dabei mehr zu managen hat.

                  Rolf

                  1. Auf diese Art zu messen, ist schwierig. Selbst, wenn die DB nicht aus Cache arbeiten kann (was sich mittels flush query cache auch komfortabel verhinderen lässt), kann Dir das BS noch mit Caching einen Strich durch die Rechnung machen.

                    Besser ist, man sichtet das slow-query.log und optimiert die Abfragen, setzt passende Indizes... Dazu schaut man sich auch gerne den Execution-Plan mittels "EXPLAIN..." an.

                    1. Hallo und guten Tag,

                      Auf diese Art zu messen, ist schwierig.

                      Es sollte mMn auch wenigstens nahzu realistisch sein und daher wirklich konkurrierenden Betrieb betreffen, das heißt also, von mehreren unabhängigen Clients über diverse Connections...

                      Ich wäre da mit einem Versuch einverstanden!

                      Wer macht mit?
                      Wer dokumentiert?

                      Grüße
                      TS

                      --
                      es wachse der Freifunk
                      http://freifunk-oberharz.de
                2. Das ist Fallabhängig. Es kann sich evtl lohnen, MyISAM einzusetzen. Bleiben wir mal bei der Tabelle mit 300.000 Usern. Ich gehe mal davon aus, dass hierauf im Livebetrieb regelmäßig auch Updates kommen? Parallel dazu viele Lesezugriffe? Dann hast Du unter MyISAM ganz schnell ein Problem, weil das nur Table-Locks kann. Das schaukelt sich in einem frequentierten Sysem schnell hoch = vorbei. InnoDB kann Row-Level-Locking, das entschärft das merklich.

                  1. Hallo und guten Tag,

                    Das ist Fallabhängig. Es kann sich evtl lohnen, MyISAM einzusetzen. Bleiben wir mal bei der Tabelle mit 300.000 Usern. Ich gehe mal davon aus, dass hierauf im Livebetrieb regelmäßig auch Updates kommen?

                    Ja, auf die relevante User-Authentication-&-Login-Tabelle muss bei bei jedem Request des Users ein erfolgreiches Update geschehen. Das ist gerade der Kern der Klasse. Es wird requestbasiertes Rechtemnagement betrieben und nicht sessionbasiertes.

                    Das heißt, dass sich die Rechte eines Users von Request zu Request ändern können, sogar innerhalb eines Dokuments aufgrund von primary und dependent Requests.

                    Parallel dazu viele Lesezugriffe? Dann hast Du unter MyISAM ganz schnell ein Problem, weil das nur Table-Locks kann. Das schaukelt sich in einem frequentierten Sysem schnell hoch = vorbei. InnoDB kann Row-Level-Locking, das entschärft das merklich.

                    Manche der Lesezugriffe sind eben nur dann sinvoll, wenn der dicht davor erfolgte Update erfolgreich war.

                    Ich werde über das Wochenende mal ein wenig experimentieren und ggf. hier auch mal einen Zugang per PHP-API und TLS zur MySQL-Datenbank freigeben. Wer würde sich denn an dem Versuch beteiligen?

                    Wie wir da die Schlüssel austauschen, weiß ich noch nicht :-O

                    Das Ganze muss ohnehin getestet werden, bevor der Server in den Produktivbetrieb wechselt.

                    Grüße
                    TS

                    --
                    es wachse der Freifunk
                    http://freifunk-oberharz.de
                    1. Ah. Dass MyISAM immer nur Table-Locks macht, wusste ich gar nicht. Immer dieses Halbwissen bei den LAMP-Autodidakten...

                      Ja, auf die relevante User-Authentication-&-Login-Tabelle muss bei bei jedem Request des Users ein erfolgreiches Update geschehen. Das ist gerade der Kern der Klasse. Es wird requestbasiertes Rechtemnagement betrieben und nicht sessionbasiertes

                      Ja gut. Das ist eine Sache. Du musst pro Session irgendwo festhalten, wie alt sie ist und welcher User darin angemeldet ist. Eventuell hältst Du darin auch den serialisierten Sessionstate. Aber das ist eine Row pro User und es ist normalerweise eine eigene Tabelle, nicht die eigentliche User-Tabelle. Hier ist Locking auf Zeilenebene auf jeden Fall sinnvoll und InnoDB angesagt.

                      Das heißt, dass sich die Rechte eines Users von Request zu Request ändern können, sogar innerhalb eines Dokuments aufgrund von primary und dependent Requests.

                      Hab ich jetzt nicht ganz verstanden, das sind jetzt die Tiefen deines Konzepts. Für mich klingt das jetzt nach einem Rechte-Manager, der den aktuellen User kennt und der von den Requests (primary oder dependent) gefragt wird, ob denn das Recht R für Modul X im Projekt Y gegeben ist. Der Mananger liest dann die hinterlegten Berechtigungen für User, Projekt und Modul, legt sie in einen requestbezogenen Cache und gibt dann "true" oder "false" zurück. Oder die vorhandene Rechtestufe. Wie auch immer. Jedenfalls würde ich mir gut überlegen, ob der Rechtemanager proaktiv beim Start des Requests alle Rechte eines Users einsammeln sollte. Wenn sich ein Request nur auf ein Projekt bezieht, der User aber in 20 Projekten steckt, dann liest Du viel zu viel.

                      Aber das ist ein anderes Thema als die Session, oder? Die hinterlegten Berechtigungen eines Users sollten sich doch im NORMALFALL selten ändern, die werden einmal festgelegt und dann zu 99,99% nur noch gelesen. Ob es Row- oder Table-Locks gibt, ist dann fast egal. Das Update auf die Session-Tabelle muss auch nicht transaktional an das Lesen der Berechtigungen gebunden sein. Wenn das Session-Update fehlschlägt, würde ich 2-3 Retries versuchen und dann den Request abbrechen, denn das stinkt nach einem Grundsatzproblem.

                      Rolf

                      1. Hallo und guten Tag,

                        Das heißt, dass sich die Rechte eines Users von Request zu Request ändern können, sogar innerhalb eines Dokuments aufgrund von primary und dependent Requests.

                        Hab ich jetzt nicht ganz verstanden, das sind jetzt die Tiefen deines Konzepts.

                        Das ist einfach nur der Unterschied zwischen Rechteupdate pro Session -> (1)
                        und Rechteuptadate pro Request -> (2)

                        (1) Beim Start einer Session, also nach dem Authenticate und dem Login werden alle Rechte unter dem User-Auth der Session gespeichert und gelten solange, bis die Session endet.

                        (2) Beim Start einer Session wird der User nur authentifiziert. Bei jedem Request werden dann seine effektiven Rechte geprüft. Hierbei kann es dann auch vorkommen, dass z. B. zwischen einem Request auf http://example.org/index.xxx und den darin enthaltenen <img scr="xxx"> wieder genug Zeit vergangen ist, dass für das mitgesendete Session-Cookie keine Zugriffsrechte mehr bestehen auf die Images (Subrequests).

                        Grüße
                        TS

                        --
                        es wachse der Freifunk
                        http://freifunk-oberharz.de
                        1. Ok. Aber wenn Du viele User hast, prophezeihe ich Dir Verschleißrillen auf der Festplatte, wo die Rechtedatenbank liegt. Ich würde sagen, dass Du da irgendwie requestübergreifend cachen musst. Schau Dir im PHP Handbuch mal memcache oder APC an. Du kannst die Lebensdauer eines Cache-Eintrags ja auf eine Minute begrenzen, aber es ist nicht gut, wenn ein HTML-Request z.B. 50 Bilder nachlädt, du die über ein PHP Script auf Berechtigung validieren willst und JEDESMAL über die Rechtetabelle rubbelst.

                          In meiner "Muttersprache" ASP.NET gibt es dieses Feature frei Haus, da gibt es den "Application State" der für alle Requests eines Web übergreifend gilt. Darin könnte man dann pro Session oder pro User einen Eintrag hinterlassen.

                          Ein In-Memory Cache braucht natürlich Housekeeping, sonst läuft der Speicher voll. Bei 300.000 Usern kann man dieses Rechtemanagement schon fast auf einen eigenen Server auslagern, ich würde es dann aber ggf. nicht mit PHP realisieren sondern als eigenene Dienst mit HTTP Listener. Hast Du bei so vielen Leuten eigentlich schon über Load Balancing nachgedacht? Über einen multi-tier Architektur mit Application Server (ggf auch als Cluster), um Web-Rendering und Application Logik zu trennen? Du willst Da ein ordentliches Monster wuppen.

                          Rolf

                          1. Hallo und guten Tag Rolf,

                            Ok. Aber wenn Du viele User hast, prophezeihe ich Dir Verschleißrillen auf der Festplatte,

                            Da gibt es dann noch die Authorisierung per Hauptdokument. Man kann alle statischen Ressourcen (weiter bin ich noch nicht) die einem primary Request zugeordnet werden können, über die Session authorisieren. Das jeweilige Script schaut also zuerst in der Sessiondatei, ob dort bereits die Pfade zu authorisierten Ressourcen eingetragen sind. Erst wenn das nicht der Fall ist, wird die Datenbank befragt.

                            Je nach Ressource lässt sich das skalieren.

                            • Dauerzugriff erlaubt
                            • Einmalzugriff erlaubt
                            • nur nach erneuter Authorisierung auf das Objekt
                            • nur nach erneuter Authorisierung auf das Modul, das das Objekt liefern darf

                            Für dynamische Zugriffsrechte, also Objekte, die als Datensätze in der DB liegen, ist eine Stored Procedure vorgeshen, die die vertikalen Rechte "Owner only", Zeitfenster, usw. berücksichtigt. Da wird das Problem also delegiert. Das Ergebnis wird dann ja i. d. R. in einer einzigen Response geliefert. Ich weiß nur noch nicht, wie ich mit Blättern umgehen sollte.

                            Grüße
                            TS

                            --
                            es wachse der Freifunk
                            http://freifunk-oberharz.de
                            1. Sessiondatei

                              300.000 Files in einem Ordner? Unter Windows eine Katastrophe, wenn ich mich recht erinnere. Unix nicht?

                              1. Hallo,

                                300.000 Files in einem Ordner? Unter Windows eine Katastrophe, wenn ich mich recht erinnere.

                                Hunderttausende von Dateien in einem Verzeichnis habe ich selbst noch nicht erlebt. Aber Windows wird AFAIR schon bei wenigen tausend Einträgen spürbar zäh bei Dateioperationen. Immerhin ergeben 300k Einträge schon ein Datenvolumen von mindestens 10MB für das Verzeichnis an sich, eher mehr (je nach durchschnittlicher Länge der Dateinamen). Und da die Verzeichniseinträge nicht sortiert sind, muss bei jedem Zugriff auf eine dieser Dateien das ganze Verzeichnis sequentiell durchsucht werden.
                                Natürlich cacht Windows einen Großteil dieser Informationen im RAM; die Sucherei bleibt trotzdem.

                                Unix nicht?

                                Doch, aber nicht ganz so schlimm, und es hängt wahrscheinlich stark vom verwendeten Filesystem ab. Mit ext3 entsteht beim Zugriff auf eine aus Tausenden von Dateien auch schon eine kurze, aber merkliche Verzögerung, Ich vermute aber, dass andere Dateisysteme damit effizienter umgehen können, z.B. weil sie die Dateieinträge automatisch sortieren oder indizieren.

                                Wobei ... sorry, das war jetzt eine Einschätzung für Linux. Ob ein echtes Unix da vielleicht besser abschneidet, kann ich nicht sagen.

                                So long,
                                 Martin

                                --
                                Nothing travels faster than the speed of light with the possible exception of bad news, which obeys its own special laws.
                                - Douglas Adams, The Hitchhiker's Guide To The Galaxy
                                1. Hallo und guten Tag,

                                  300.000 Files in einem Ordner? Unter Windows eine Katastrophe, wenn ich mich recht erinnere.

                                  Bei Windows bleibt das Dateisystem quasi stehen. Bei Linux habe ich mit knapp einer Million noch keine Probleme gehabt, es sei denn, irgend ein Idiont hat versucht, die über einen Windows-Client zu behandeln (also temporär zu locken).

                                  Debian und PHP bieten außerdem eine Möglichkeit, Dateien zu verteilen. Aber mehr als eine zusätzliche Ebene macht der GC nicht mit, auch der von Debian benutzte Cron-Job nicht. Aber das ist dann doch wenigstens schon mal Faktor 26!

                                  Hunderttausende von Dateien in einem Verzeichnis habe ich selbst noch nicht erlebt. Aber Windows wird AFAIR schon bei wenigen tausend Einträgen spürbar zäh bei Dateioperationen. Immerhin ergeben 300k Einträge schon ein Datenvolumen von mindestens 10MB für das Verzeichnis an sich, eher mehr (je nach durchschnittlicher Länge der Dateinamen). Und da die Verzeichniseinträge nicht sortiert sind, muss bei jedem Zugriff auf eine dieser Dateien das ganze Verzeichnis sequentiell durchsucht werden.
                                  Natürlich cacht Windows einen Großteil dieser Informationen im RAM; die Sucherei bleibt trotzdem.

                                  ...

                                  Grüße
                                  TS

                                  --
                                  es wachse der Freifunk
                                  http://freifunk-oberharz.de
  4. Hallo und guten Tag,

    ich habe eine Frage zum Datenbankmodell und den möglichen Abfragen für meine kleine Rechteverwaltung. Für die reine User<->Rechteverwaltung funktioniert das Modell schon gut, soll aber nun auch für Gruppen erweitert werden. Die User können also Mitglieder in Gruppen werden, die dann schon gewisse Grundrechte haben.

    Ist Muttersprache und als Anregung eventuell brauchbar PDF und nicht so lang.

    Oder hier kann man sich Anregungen holen. Ich weis ja nicht wie filigran das Endresultat sein soll.