Database
 sql >> Datenbank >  >> RDS >> Database

Rollen und Status in einem System verwalten

Es gibt viele Möglichkeiten, ein Problem zu lösen, so auch bei der Verwaltung von Rollen und Benutzerstatus in Softwaresystemen. In diesem Artikel finden Sie eine einfache Weiterentwicklung dieser Idee sowie einige nützliche Tipps und Codebeispiele.

Grundidee

In den meisten Systemen müssen normalerweise Rollen vorhanden sein und Benutzer-Status .

Rollen beziehen sich auf Rechte die Benutzer bei der Nutzung eines Systems nach erfolgreicher Anmeldung haben. Beispiele für Rollen sind „Callcenter-Mitarbeiter“, „Callcenter-Manager“, „Backoffice-Mitarbeiter“, „Backoffice-Manager“ oder „Manager“. Im Allgemeinen bedeutet dies, dass ein Benutzer Zugriff auf einige Funktionen hat, wenn er oder sie die entsprechende Rolle hat. Es ist ratsam anzunehmen, dass ein Benutzer mehrere Rollen gleichzeitig haben kann.

Status sind viel strenger und bestimmen, ob der Benutzer berechtigt ist, sich beim System anzumelden oder nicht. Ein Benutzer kann nur einen Status haben zu einer Zeit. Beispiele für Status wären:„arbeitend“, „im Urlaub“, „krank“, „Vertrag beendet“.

Wenn wir den Status eines Benutzers ändern, können wir alle Rollen, die sich auf diesen Benutzer beziehen, unverändert beibehalten. Das ist sehr hilfreich, da wir meistens nur den Status des Benutzers ändern wollen. Wenn ein Benutzer, der als Call-Center-Mitarbeiter arbeitet, in den Urlaub geht, können wir seinen Status einfach auf „im Urlaub“ ändern und ihn nach seiner Rückkehr wieder auf den Status „arbeitend“ setzen.

Durch das Testen von Rollen und Status während der Anmeldung können wir entscheiden, was passieren wird. Vielleicht möchten wir zum Beispiel die Anmeldung verbieten, selbst wenn der Benutzername und das Passwort korrekt sind. Wir könnten dies tun, wenn der aktuelle Benutzerstatus nicht impliziert, dass er arbeitet oder wenn der Benutzer keine Rolle im System hat.

In allen unten angegebenen Modellen sind die Tabellen status und role sind gleich.

Tabelle status hat die Felder id und status_name und das Attribut is_active . Wenn das Attribut is_active auf „True“ gesetzt ist, bedeutet dies, dass der Benutzer mit diesem Status gerade arbeitet. Beispielsweise hätte der Status „in Arbeit“ das Attribut is_active mit dem Wert „True“, während andere („im Urlaub“, „Krankheit“, „Vertrag beendet“) den Wert „False“ haben würden.

Die Rollentabelle hat nur zwei Felder:id und role_name .

Das user_account Tabelle ist dasselbe wie user_account Tabelle in diesem Artikel vorgestellt. Nur im ersten Modell wird der user_account Tabelle enthalten zwei zusätzliche Attribute (role_id und status_id ).

Einige Modelle werden vorgestellt. Alle funktionieren und können verwendet werden, haben aber ihre Vor- und Nachteile.

Einfaches Modell

Die erste Idee könnte sein, dass wir einfach Fremdschlüsselbeziehungen zum user_account Tabelle, die auf Tabellen status und role . Sowohl role_id und status_id sind obligatorisch.




Das ist ziemlich einfach zu entwerfen und auch Daten mit Abfragen zu handhaben, hat aber ein paar Nachteile:

  1. Wir speichern keine historischen (oder zukünftigen) Daten.

    Wenn wir den Status oder die Rolle ändern, aktualisieren wir einfach status_id und role_id im user_account Tisch. Das wird vorerst gut funktionieren, also wenn wir eine Änderung vornehmen, wird es sich im System widerspiegeln. Das ist in Ordnung, wenn wir nicht wissen müssen, wie sich Status und Rollen historisch verändert haben. Außerdem gibt es ein Problem darin, dass wir future nicht hinzufügen können Rolle oder Status, ohne diesem Modell zusätzliche Tabellen hinzuzufügen. Eine Situation, in der wir diese Option wahrscheinlich gerne hätten, ist, wenn wir wissen, dass jemand ab nächsten Montag im Urlaub ist. Ein weiteres Beispiel ist, wenn wir einen neuen Mitarbeiter haben; Vielleicht möchten wir seinen Status und seine Rolle jetzt eintragen und irgendwann in der Zukunft gültig machen.

    Es gibt auch eine Komplikation für den Fall, dass wir geplante Ereignisse haben die Rollen und Status verwenden. Ereignisse, die Daten für den nächsten Arbeitstag vorbereiten, laufen normalerweise, wenn die meisten Benutzer das System nicht verwenden (z. B. nachts). Wenn also jemand morgen nicht arbeitet, müssen wir bis zum Ende des aktuellen Tages warten und dann seine Rolle und seinen Status entsprechend ändern. Wenn wir beispielsweise Mitarbeiter haben, die derzeit arbeiten und die Rolle „Callcenter-Mitarbeiter“ haben, erhalten sie eine Liste mit Kunden, die sie anrufen müssen. Wenn jemand versehentlich diesen Status und diese Rolle hatte, wird er auch seine Kunden bekommen und wir müssen Zeit damit verbringen, dies zu korrigieren.

  2. Benutzer können jeweils nur eine Rolle haben.

    Im Allgemeinen sollten Benutzer in der Lage sein, mehr als eine Rolle zu haben Im System. Vielleicht besteht zu dem Zeitpunkt, an dem Sie die Datenbank entwerfen, keine Notwendigkeit für so etwas. Denken Sie daran, dass es zu Änderungen im Arbeitsablauf/Prozess kommen kann. Beispielsweise könnte der Kunde irgendwann beschließen, zwei Rollen zu einer zusammenzuführen. Eine mögliche Lösung besteht darin, eine neue Rolle anzulegen und ihr alle Funktionalitäten aus den vorherigen Rollen zuzuweisen. Die andere Lösung (wenn Benutzer mehr als eine Rolle haben können) wäre, dass der Client einfach beide Rollen Benutzern zuweist, die sie benötigen. Natürlich ist diese zweite Lösung praktischer und gibt dem Kunden die Möglichkeit, das System schneller an seine Bedürfnisse anzupassen (was von diesem Modell nicht unterstützt wird).

Andererseits hat dieses Modell auch einen großen Vorteil gegenüber anderen. Es ist einfach und daher wären Abfragen zum Ändern von Status und Rollen ebenfalls einfach. Auch eine Abfrage, die prüft, ob der Benutzer Rechte hat, sich im System anzumelden, ist viel einfacher als in anderen Fällen:

select user_account.id, user_account.role_id
from user_account
left join status on user_account.status_id = status.id
where status.is_user_working = True
and user_account.user_name = @user_name
and user_account.password_hash_algorithm = @password;

@user_name und @password sind Variablen aus einem Eingabeformular, während die Abfrage die ID des Benutzers und die role_id zurückgibt, die er hat. In Fällen, in denen Benutzername oder Passwort ungültig sind, ein Paar Benutzername und Passwort nicht existiert oder der Benutzer einen nicht aktiven Status hat, gibt die Abfrage keine Ergebnisse zurück. Auf diese Weise können wir die Anmeldung verbieten.

Dieses Modell könnte in Fällen verwendet werden, in denen:

  • Wir sind sicher, dass es keine Prozessänderungen geben wird, die erfordern, dass Benutzer mehr als eine Rolle haben
  • Wir müssen Rollen-/Statusänderungen nicht im Verlauf nachverfolgen
  • wir erwarten nicht viel Rollen-/Statusverwaltung.

Zeitkomponente hinzugefügt

Wenn wir die Rolle und den Statusverlauf eines Benutzers verfolgen müssen, müssen wir viele bis viele Beziehungen zwischen dem user_account und role und das user_account und status . Natürlich entfernen wir role_id und status_id aus dem user_account Tisch. Neue Tabellen im Modell sind user_has_role und user_has_status und alle Felder darin, mit Ausnahme der Endzeiten, sind Pflichtfelder.




Die Tabelle user_has_role enthält Daten über alle Rollen, die Benutzer jemals im System hatten. Der alternative Schlüssel ist (user_account_id , role_id , role_start_time ), da es keinen Sinn macht, einem Benutzer gleichzeitig dieselbe Rolle mehr als einmal zuzuweisen.

Die Tabelle user_has_status enthält Daten über alle Status, die Benutzer jemals im System hatten. Der alternative Schlüssel hier ist (user_account_id , status_start_time ), da ein Benutzer nicht zwei Status haben kann, die genau zur selben Zeit beginnen.

Die Startzeit kann nicht null sein, denn wenn wir eine neue Rolle/einen neuen Status einfügen, kennen wir den Moment, ab dem er beginnt. Die Endzeit kann null sein, falls wir nicht wissen, wann die Rolle/der Status enden würde (z. B. ist die Rolle von morgen gültig, bis etwas in der Zukunft passiert).

Neben einer vollständigen Historie können wir jetzt in Zukunft Status und Rollen hinzufügen. Dies führt jedoch zu Komplikationen, da wir beim Einfügen oder Aktualisieren auf Überschneidungen prüfen müssen.

Beispielsweise kann ein Benutzer jeweils nur einen Status haben. Bevor wir einen neuen Status einfügen, müssen wir die Start- und Endzeit eines neuen Status mit allen vorhandenen Status für diesen Benutzer in der Datenbank vergleichen. Wir können eine Abfrage wie diese verwenden:

select *
from user_has_status
where user_has_status.user_account_id = @user_account_id
and 
(
# test if @start_time included in interval of some previous status
(user_has_status.status_start_time <= @start_time and ifnull(user_has_status.status_end_time, "2200-01-01") >= @start_time)
or
# test if @end_time included in interval of some previous status  
(user_has_status.status_start_time <= @end_time and ifnull(user_has_status.status_end_time, "2200-01-01") >= ifnull(@end_time, "2199-12-31"))  
or  
# if @end_time is null we cannot have any statuses after @start_time
(@end_time is null and user_has_status.status_start_time >= @start_time)  
or
# new status "includes" old satus (@start_time <= user_has_status.status_start_time <= @end_time)
(user_has_status.status_start_time >= @start_time and user_has_status.status_start_time <= ifnull(@end_time, "2199-12-31"))  
)

@start_time und @end_time sind Variablen, die die Start- und Endzeit eines Status enthalten, den wir einfügen möchten, und @user_account_id ist die Benutzer-ID, für die wir sie einfügen. @end_time kann null sein und muss in der Abfrage behandelt werden. Dazu werden Nullwerte mit dem ifnull() getestet Funktion. Wenn der Wert null ist, wird ein hoher Datumswert zugewiesen (hoch genug, dass wir schon lange weg sind, wenn jemand einen Fehler in der Abfrage bemerkt :). Die Abfrage prüft alle Kombinationen von Startzeit und Endzeit auf einen neuen Status im Vergleich zu Startzeit und Endzeit vorhandener Status. Wenn die Abfrage irgendwelche Datensätze zurückgibt, dann haben wir Überschneidungen mit bestehenden Status und wir sollten das Einfügen des neuen Status verbieten. Es wäre auch schön, einen benutzerdefinierten Fehler zu melden.

Wenn wir die Liste der aktuellen Rollen und Status (Benutzerrechte) überprüfen möchten, testen wir einfach mit Startzeit und Endzeit.

select user_account.id, user_has_role.id
from user_account
left join user_has_role on user_has_role.user_account_id = user_account.id
left join user_has_status on user_account.id = user_has_status.user_account_id
left join status on user_has_status.status_id = status.id
where user_account.user_name = @user_name
and user_account.password_hash_algorithm = @password
and user_has_role.role_start_time <= @time and ifnull(user_has_role.role_end_time,"2200-01-01") >= @time
and user_has_status.status_start_time <= @time and ifnull(user_has_status.status_end_time,"2200-01-01") >= @time
and status.is_user_working = True

@user_name und @password sind Variablen aus dem Eingabeformular, während @time könnte auf Now() gesetzt werden. Wenn ein Benutzer versucht, sich anzumelden, möchten wir zu diesem Zeitpunkt seine Rechte überprüfen. Das Ergebnis ist eine Liste aller Rollen, die ein Benutzer im System hat, falls Benutzername und Passwort übereinstimmen und der Benutzer derzeit einen aktiven Status hat. Wenn der Benutzer einen aktiven Status hat, aber keine Rollen zugewiesen sind, gibt die Abfrage nichts zurück.

Diese Abfrage ist einfacher als die in Abschnitt 3, und dieses Modell ermöglicht es uns, eine Historie von Status und Rollen zu haben. Außerdem können wir Status und Rollen für die Zukunft verwalten und alles wird gut funktionieren.

Endgültiges Modell

Dies ist nur eine Idee, wie das Vorgängermodell geändert werden könnte, wenn wir die Leistung verbessern wollten. Da ein Benutzer jeweils nur einen aktiven Status haben kann, könnten wir status_id hinzufügen in das user_account Tabelle (current_status_id ). Auf diese Weise können wir den Wert dieses Attributs testen und müssen nicht dem user_has_status Tisch. Die modifizierte Abfrage würde wie folgt aussehen:

select user_account.id, user_has_role.id
from user_account
left join user_has_role on user_has_role.user_account_id = user_account.id
left join status on user_account.current_status_id = status.id
where user_account.user_name = @user_name
and user_account.password_hash_algorithm = @password
and user_has_role.role_start_time <= @time and ifnull(user_has_role.role_end_time,"2200-01-01") >= @time
and status.is_user_working = True




Offensichtlich vereinfacht dies die Abfrage und führt zu einer besseren Leistung, aber es gibt ein größeres Problem, das gelöst werden müsste. Die current_status_id im user_account Tabelle sollte in folgenden Situationen überprüft und ggf. geändert werden:

  • bei jedem einfügen/aktualisieren/löschen in user_has_status Tabelle
  • In einem geplanten Event sollten wir jeden Tag überprüfen, ob sich der Status einer Person geändert hat (aktuell aktiver Status abgelaufen oder/und ein zukünftiger Status aktiv geworden) und ihn entsprechend aktualisieren

Es wäre ratsam, Werte zu speichern, die häufig von Abfragen verwendet werden. Auf diese Weise vermeiden wir, dass wir immer wieder dieselben Überprüfungen durchführen und die Arbeit aufteilen. Hier vermeiden wir es, dem user_has_status Tabelle und wir nehmen Änderungen an current_status_id vor nur wenn sie passieren (einfügen/aktualisieren/löschen) oder wenn das System nicht so oft verwendet wird (geplante Ereignisse werden normalerweise ausgeführt, wenn die meisten Benutzer das System nicht verwenden). Vielleicht würden wir in diesem Fall nicht viel von current_status_id aber betrachten Sie dies als eine Idee, die in ähnlichen Situationen helfen kann.