Hallo zusammen!
Ich bilde mir ein schon die Suchfunktion genutzt zu haben, aber konnte leider keine Einsteiger-Freundlichen Infos erhalten, womit ich aktuell sinnvoll etwas anfangen könnte.
Das will ich in etwa machen: Siehe Anhang.
Ziel ist es, -mit dem Sicherheitssystem von Windows- die Benutzer/Gruppen von Windows (bei Einzelsystemen) bzw. ActiveDirectory in Domänen für bestimmte Funktionen in meinem Programm zu berechtigen bzw. zu sperren. So dürfen dann beispielsweise alle Personen einer Gruppe das Programm starten und Daten betrachten, bestimmte Benutzer allerdings nur Daten verändern oder löschen.
Es gelten die üblichen Bedingungen (und Einschränkungen): Benutzer dürfen das Programm auch dann starten, wenn Sie nicht namentlich in [meiner] Liste aufgeführt sind, sondern auch wenn Sie Mitglieder in einer oder mehrerer Gruppen sind, die diese Berechtigung haben.
Das Sicherheitssystem von Windows (Zumindesten in Bezug auf die Sicherheitsrechte von Dateien und Registry-Keys) sollte den Meisten hier bekannt sein.
Da mir das auf
API-Ebene alles zu abstrakt ist verwendet ich die "Security Library" der
Jedi-Komponenten:
http://blog.delphi-jedi.net/security-library/
Bisherige, unter anderem verwendete Websites:
http://www.michael-puff.de/Programmi...kel/DACL.shtml (Danke Luckie!)
http://www.delphipraxis.net/86609-st...rogrammen.html
http://borland.newsgroups.archived.a...060121859.html
Leider ist es so, dass man mit der bei
Jedi mitgelieferten Dokumentation nirgendwo "einsteigen" kann. Dort wird stets nur in Abkürzungen gesprochen und direkt auf der Startseite gesagt nach dem Motto "Ihr müsst euch mit dem Windows-Sicherheitssystem auskennen, um die Komponente nutzen zu können." Hmja... Ohne Einstiegspunkt kann man sich das schlecht beibringen...
In dem o.g. Tutorial zum Sicherheitsdialog wird zwar schön beschrieben, wie man die Zugriffsrechte von
Dateien ändern kann, aber leider nicht, die das mit einem eigenen Objekt geht.
Klären wir vielleicht zunächst mal die Terminologie, korrigiert mich, wenn ich falsch liege:
DACL (discretionary
access control list)
Ist letztendlich die Liste mit allen Benutzern und allen Rechten (und "Verweigerungen") für das Objekt, die Datei oder den Registrykey.
ACE (
Access control entry)
Ist EIN Eintrag in der DACL, also z.B. "Gruppe A" darf die Datenbank bearbeiten oder "Gruppe Leiharbeiter" wird Bearbeiten verweigert. Eine DACL darf beliebig viele ACEs enthalten.
Wenn Benutzer "Alice" nun in "Gruppe A" ist, darf Sie also damit auch automatisch die Datenbank bearbeiten, es sei denn, Sie ist zusätzlich|stattdessen Mitglied in "Gruppe Leiharbeiter".
Korrektur: ACE ist ein RECHT, das immer nur für den aktuell ausgewählen Benutzer/Gruppe bearbeitet werden kann (z.B. "Lesen" oder "Schreiben")?
ACLEditor
Das Programm/Fenster aus dem Screenshot (siehe Anhang). Kann nativ über die Windows-
API "EditSecurity(GetActiveWindow, Self)" aufgerufen werden.
Vorgehensweise:
Anhand eines Beispiels der
Jedi-Komponenten habe ich mich zunächst für die "StringDescriptor"-Demo entschieden: rein optisch kommt diese meinem Vorhaben am Nächsten.
Der
ACL Editor wird vorbereitet
Delphi-Quellcode:
ACLEditor := TJwSecurityDescriptorDialog.Create(GetActiveWindow);
// ACLEditor.Flags := [sdfAdvanced, sdfEditDacl, sdfEditOwner, sdfEditEffective,
// sdfNoAdditionalPermission, sdfEditSacl];
ACLEditor.Flags := [sdfEditSacl];
ACLEditor.ObjectName := 'Beschreibung des Sicherheitsdialog DEMO';
ACLEditor.ServerName := '';
Erzeugen der Instanz des Editors, setzen der Flags (welche Buttons sollen sichtbar sein, etc.) und der Titelleiste des Editors.
Für die Demo: Erzeugen von ein paar ACEs, damit schon mal ein paar Leute Berechtigungen haben, wenn wir mit der
GUI vom Editor rumspielen:
Delphi-Quellcode:
SecurityDescriptor := TJwSecurityDescriptor.Create;
SecurityDescriptor.DACL.Add(TJwDiscretionaryAccessControlEntryAllow.Create(
nil,[],GENERIC_ALL,JwGuestsSID,True));
SecurityDescriptor.DACL.Add(TJwDiscretionaryAccessControlEntryAllow.Create(
nil,[],GENERIC_READ,JwWorldSID,false));
SecurityDescriptor.DACL.Add(TJwDiscretionaryAccessControlEntryAllow.Create(
nil,[],GENERIC_WRITE,JwLocalSystemSID,false));
ACLEditor.SecurityDescriptor := SecurityDescriptor;
// das Editor-GUI soll nun also ^-- diese Liste bearbeiten
Die Gruppe "Gäste" [meines PCs --> "JwGuestsSID"] darf alles machen, "Jeder" (JwWorldSID) darf nur lesen, und "SYSTEM" (JwLocalSystemSID) darf (nur) schreiben. Soweit so gut. Die Thematik der SIDs verstehe ich und soll hier nicht unnötiger Weise (?) näher beleuchtet werden. Parameter 3+4 habe ich also verstanden, der Rest ist noch eher unklar.
Nach dem Ausführen des Editors (if ACLEditor.ShowModal then) kann ich das Objekt SecurityDescriptor durchgehen und alle Einträge auslesen.
Wenn ich das richtig Verstanden habe, kann ich mir mit
Memo1.Text := ACLEditor.SecurityDescriptor.GetSecurityDescriptorString(JwAllSiFlags)
;
diese komplette DACL als String zurückgeben lassen, in dem alle zugelassenen/verweigerten Benutzer(-SIDs) stehen, sowie die Berechtigungen, so dass ich diese z.B. in meiner Datenbank speichern, und beim nächsten Programmstart wieder auslesen kann (?).
Nur kommt der Punkt, an dem ich hänge:
Wie weise ich dem Dialog "zeilenweise" die Rechte zu, die der Benutzer bearbeiten können soll?
Als Optionen sollen nur "Zulassen" und "Verweigern" verwendet werden. (Oder nur "Zulassen", wenn das wesentlich einfacher ist.)
In der o.g. Demo "StringDescriptor" wird folgendes für diese zeilenweisen Rechte verwendet:
Delphi-Quellcode:
Mapping := TJwSecurityGenericMappingClass(ComboBoxMapping.Items.Objects[ComboBoxMapping.ItemIndex]);
ACLEditor.Mapping := Mapping;
// zuweisung der o.g. Rechte an den Editor/GUI
ComboBoxMapping.Items.Objects[ComboBoxMapping.ItemIndex] bezieht sich hier in der Demo natürlich auf ein Objekt in einer ComboBox, die beim Programmstart befüllt wurde:
Delphi-Quellcode:
ComboBoxMapping.Items.AddObject('Generic',Pointer(TJwSecurityGenericMapping));
ComboBoxMapping.Items.AddObject('Process',Pointer(TJwSecurityProcessMapping));
ComboBoxMapping.Items.AddObject('Threads',Pointer(TJwSecurityThreadMapping));
ComboBoxMapping.Items.AddObject('Service',Pointer(TJwSecurityServiceMapping));
ComboBoxMapping.Items.AddObject('FileFolder',Pointer(TJwSecurityFileFolderMapping));
Hier wird also EIN Pointer auf eine KLASSE (nicht ein instanziertes Objekt!) übergeben, welche -abgeleitet von TJwSecurityGenericMapping- die Klassen-Funktionen "GetMapping", "MapAccessMaskToString" und "GetAccessNames" bereitstellt. Je nach verlinkert Klasse sind in dem Editor also andere ACE-Zeilen zu sehen.
Der DACL-Editor kann sich also über diese Callback-Funktionen ein Kontanten/Konstantes(?) Array holen, in dem die einzelnen Zeilen (ACEs?) stehen. Beispiel für Datei/Ordner-Sicherheit im Windows-Explorer:
Delphi-Quellcode:
FileFolderMapping: array[1..19] of TJwRightsMapping =
(
(Right: FILE_ALL_ACCESS; Name: 'Full control';
Flags: SI_ACCESS_GENERAL or SI_ACCESS_SPECIFIC),
(Right: FILE_GENERIC_READ or FILE_GENERIC_WRITE or FILE_GENERIC_EXECUTE or Delete;
Name: 'Modify';
Flags: SI_ACCESS_GENERAL or SI_ACCESS_SPECIFIC),
(Right: FILE_GENERIC_READ or FILE_GENERIC_EXECUTE;
Name: 'Read and execute';
etc.
Angenommen ich leite diese Klasse ab und erstelle mein statisches eigenes Array, sollte das ja dann klappen?
Die Frage ist nun halt auch noch, wie werden diese Werte abgespeichert, bzw. um was genau muss ich mich kümmern. Der DACL-Editor scheint ja selbst eine "Bit-Maske" zu erwarten, die er dann für jeden Benutzer/Gruppe füllen kann mit den Berechtigungen. Wie geht man das am schlausten an?
Wofür braucht der Editor
ACLEditor.OnSetSecurity := OnSetSecurity;
In der Demo wird kommentarlos in OnSetSecurity die var-Variable bSuccess := true gesetzt...
Letztendlich: Wie prüfe ich, ob Benutzer "Alice" (bzw. deren SID) das Recht "Datenbank bearbeiten" hat, wenn in der DACL nur "Gruppe A" und "Gruppe Leiharbeiter" aufgeführt sind? (Annahme: Alice ist Mitglied in Gruppe A)
Vielen Dank im Voraus, vielleicht hilft mein Beitrag auch mal jemand anderem...
Edit1: Scheinbar geht es auch ohne das Konstanten-Array. Seine eigenen Berechtigungen kann man erstellen, indem man von TJwSecurityGenericMapping eine Klasse ableitet und bestimmte Funktionen überschreibt:
Delphi-Quellcode:
type
TMySampleMapping =
class(TJwSecurityGenericMapping)
public
class function GetMapping: TGenericMapping;
override;
class function GetBitMappingString(Idx: Cardinal): TJwString;
override;
end;
class function TMySampleMapping.GetBitMappingString(Idx: Cardinal): TJwString;
begin
case idx
of
0: Result := '
Programm starten';
// Zeile 1 im ACL-Editor
1: Result := '
Daten schreiben';
// Zeile 2
else
Result := '
DEMO ' + IntToStr(Idx);
// Alle weiteren Zeilen, max. 32 insgesamt.
end
end;
class function TMySampleMapping.GetMapping: TGenericMapping;
begin
Result.GenericRead := READ_CONTROL
or STANDARD_RIGHTS_READ;
Result.GenericWrite := WRITE_DAC
or WRITE_OWNER
or Delete
or ACCESS_SYSTEM_SECURITY
or STANDARD_RIGHTS_WRITE;
Result.GenericExecute := STANDARD_RIGHTS_EXECUTE
or SYNCHRONIZE;
Result.GenericAll := Result.GenericRead
or Result.GenericWrite
or
Result.GenericExecute;
end;
//Im Hauptprogramm:
Mapping := TJwSecurityGenericMappingClass(Pointer(TMySampleMapping));
ACLEditor.Mapping := Mapping;
Die "GetMapping" Prozedur soll in der abgeleiteten Klasse unbedingt überschrieben werden. Warum?
Edit2:
So kann man die im Dialog eingestellten Optionen laden/speichern:
Delphi-Quellcode:
// Speichern:
if ACLEditor.ShowModal
then // Dialog ausführen, und wenn mit "Ok" beendet:
begin
// Einstellungen/Auswahl in einem String speichern (hier: Memo1.Text)
// diesen String kann man nun z.B. in der Datenbank speichern!
Memo1.Text := ACLEditor.SecurityDescriptor.GetSecurityDescriptorString(JwAllSiFlags);
end;
// Laden:
// hier muss der vorher gespeicherte Wert schon fertig drinnen geladen sein, sonst gibts Exception!
SecurityDescriptor := TJwSecurityDescriptor.Create(Memo1.Text);
ACLEditor.SecurityDescriptor := SecurityDescriptor;