Properties CRUD mit benutzerdefinierten rollenbasierten Rechten

  • WPF

Es gibt 23 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Properties CRUD mit benutzerdefinierten rollenbasierten Rechten

    Hallo Leute,

    ich habe ein kleines grundsätzliches Problem, es geht mir aktuell nicht um einen Code sondern um eine Art "Best Practise" Lösung für mein Problem.

    Ausgangssituation:

    Ich möchte in meiner Applikation eine Art Benutzerrollenmodell implementieren d.h. jeder Benutzer kann n Rollen haben die wiederum mit m Rechten ausgestattet sind.
    Die Berechtigungen regeln z.b. ob eine bestimmte View z.b. Verkaufsangebote anzeigt, erstellt, bearbeitet, gelöscht (CRUD) werden darf.
    Parallel möchte ich für einige bestimmte Properties z.B. Artikeleinkaufspreis usw. ebenfalls CRUD definieren können.
    Natürlich wäre es jetzt simpel wenn man das statisch macht aber der Administrator soll ja eigene Rollen mit bestimmten Rechten erstellen dürfen.

    Aktuell haben ich mir in meinem Datenmodell:
    tblUser => Alle Benutzer
    tblUserGroups => Verfügbare Rollen
    tblRoleRights => verfügbare Berechtigungen angelegt.

    tblUser n:m tblUserGroups 1:x tblRoleRights

    Als 1 Benutzer kann mehreren Gruppen gehören die wiederum unterschiedliche Berechtigungen haben.
    Ich hoffe die Ausgangssituation ist verständlich beschrieben.

    Hier beginnt mein Problem:

    Ich habe mir grundsätzlich überlegt ich könnte die Commands welche die Views aufrufen im CanExecute entsprechend abfragen.
    Schwieriger sehe ich das aktuell bei einzelnen Properties.
    Ich könnte die entsprechende Property des Controls z.B. Textbox isVisbile oder CanEdit an ein Property im Mainmodel im Modus OneTime binden. Allerdings müsste ich dann für jede Property oder jedes View eine Property im Modell anlegen, das erscheint mir extrem aufwändig.
    Somit wollte ich eine Funktion programmieren die auf Basis eines Keys z.B. txtPurchasePrice in der tblRoleRights die Berechtigung prüft.
    Damit gibt es aber wiederum das Problem das ich ja irgendwie dem Viewmodel sagen muss welche Property überprüft werden soll. Hierzu müsste ich einen Binding Parameter manuell übergeben.
    Alternativ wäre dann noch die Möglichkeit einen Converter zu basteln der das erledigt, dieser müsste dann aber wiederum aufs Viewmodel verweisen...

    Bei allen meinen Ideen mache ich mir Sorgen um die Performance.
    Kann man das irgendwie über eine Art Unique ID für Properties lösen? View dynamisch aufbauen?
    Daher meine Frage hat jemand Erfahrung mit dem Problem und kann mir eventuell einen Guide geben oder eine Idee?

    Danke euch.
    mfG.
    Stephan
    Hallo

    Ich stand kürzlich erst vor dem selben problem und genau wie du hatte ich mir über die Performance sorgen gemacht.
    Und du bist ja auf dem richtigen Weg.

    Wie habe ich es gelöst...

    Jedes Recht (nicht die Rolle) hat einen Key. Wie dieser benannt ist, ist ja erstmal egal.
    Nachdem der User sich angemeldet hat (oder besser beim Anmeldevorgang) lade ich deren Rechte. Ja, die Rechte nicht die Rollen. Ich lade also alle Rechte welche in den Rollen welche der User besitzt enthalten sind. Natürlich Distinct.
    Diese (nur die Keys) speichere ich in ein Dictionary, denn ich benötige ja nur die des aktuellen Users, nicht von anderen Usern. Die Liste ist schlank und enthält nicht mehr als benötigt. Nur die Keys.
    Hierfür habe ich eine Singleton-Klasse, bekomme also von überall aus immer die selbe Instanz zurück.

    Jetzt kommen die Vorteile der WPF zu tragen. Du baust dir einen Converter (IValueConverter) welchem du als Parameter den Key übergibt. Also den Key des Rechts welches benötigt wird um etwas zu "dürfen".
    Benötigst du mehrere Keys weil man etwas bei mehreren Rechten darf dann mach noch einen Multiconverter (IMultiConverter).
    Die Converter benötigen auch kein ViewModel, wir haben ja die Singleton-Klasse.

    Ab sofort kannst du an jedes DependencyProperty diesen Converter ranpflanzen und voll automatisch macht dir die WPF den Rest.

    Solltest du Fragen haben tu dir keinen Zwang an.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    ich würde nochmal genau auf das Datenmodell des TE eingehen - das ist unklar formuliert.
    Ich hoffe, er hat wirklich ein relationales Modell, also dass die Relationen in der DB auch definiert sind.

    Übrigens habich neuerdings auch mit Rechte-Kram zu tun, bei uns ist ein Recht nicht einfach ja oder nein, sondern wir haben eine CRUD-Enumeration, also ein Recht assoziiert eine beliebige Kombination von Create, Read, Update, Delete mit einem Rechte-Key
    Abend,
    @Nofear23m Danke für das rasche Feedback, das war Gold wert.
    @ErfinderDesRades Jup ich habe ein relationales Modell die Abhängigkeiten sind schön in der DB vom SQL Server abgebildet.

    Ich habe mich jetzt an meine "eigene" Implementierung gemacht und möchte diese gerne kurz präsentieren eventuell kann man noch einiges verbessern.

    Zur Einleitung:
    Ich habe zu Beginn vergessen zu erwähnen das ich eine kleine Besonderheit habe, es kann nicht nur jeder Benutzer eine beliebige Anzahl von Benutzergruppen haben sondern auch direkt Rechte bekommen ohne einer Rolle anzugehören, nur als Hinweis damit die Datenstruktur in der Tabelle tblRoleRights klarer wird. Parallel wird hier auch zwischen den Rechten Read => genutzt um die Sichtbarkeit einzustellen; Write; Edit; Delete => Boolean Werte

    Die 3 Klassen ohne die Joining Klasse von User zu UserRoles
    Spoiler anzeigen



    Wichtig ist hier für mich die tblRoleRights
    Der "Key" welcher relevant ist baut sich dann später aus 1 Property im Falle von sogenannten Modulen und aus 2 Properties im Falle von einer Form und aus 3 Properties im Falle eines Feldes auf.
    Beispiel:
    Modul Verkauf wird über einen Eintrag mit "Modulname" = "sales" angesprochen
    Ein Formular für Kunden hinzufügen über: "Entityname" = "sales" FormName="ContactAddEdit"
    Ein Feld im oben genannten Form das z.B. an die Property "Name1" bindet: "Entityname" = "sales" FormName="ContactAddEdit" FieldName="Name1"
    Soweit zur Keylogik
    Die Entityname Property wird eventuell noch fallen aber ich möchte mir einfach Möglichkeiten offenhalten.


    Die Definition der Singleton Klasse ist ja recht einfach gehalten:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Collections.ObjectModel
    2. Public NotInheritable Class SingletonUserRights
    3. Private Shared ReadOnly vInstance As New Lazy(Of SingletonUserRights)(Function() New SingletonUserRights(), Threading.LazyThreadSafetyMode.ExecutionAndPublication)
    4. Private Sub New()
    5. End Sub
    6. Public Shared ReadOnly Property Instance As SingletonUserRights
    7. Get
    8. Return vInstance.Value
    9. End Get
    10. End Property
    11. Public Property UserRights As New ObservableCollection(Of tblRoleRights)
    12. End Class



    Nach dem Login des Users wird eine Function CheckUser ausgeführt.
    Diese Funktion benötige ich weil ein SQL User bei mir nicht auch gleichzeitig auf das Programm zugreifen darf und eine Art Mapping erfolgt:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Function CheckUser() As Boolean
    2. If vCurrentSQLUsername = Nothing OrElse vCurrentSQLUsername = String.Empty Then Return False
    3. 'Check if the SQL User has a corresponding user in tblUser:
    4. Dim vUser As tblUsers = dbo.tblUsers.Where(Function(x) x.SqlUsername = vCurrentSQLUsername AndAlso x.Aktiv = True).First
    5. If vUser Is Nothing Then
    6. Return False
    7. Else
    8. CurrentUser = vUser
    9. vUser = Nothing
    10. 'Get the current User Rights:
    11. Dim CurrentUserRights As SingletonUserRights = SingletonUserRights.Instance
    12. CurrentUserRights.UserRights.Clear()
    13. 'Check User Related Rights:
    14. For Each vRight As tblRoleRights In CurrentUser.NvUserRights
    15. CurrentUserRights.UserRights.Add(vRight)
    16. Next
    17. 'Check User Group related Rights:
    18. For Each vUserGroup As tblUserGroups In CurrentUser.NvUserGroups
    19. For Each vRight As tblRoleRights In vUserGroup.NvRoleRights
    20. CurrentUserRights.UserRights.Add(vRight)
    21. Next
    22. Next
    23. 'Remove possible Duplicates:
    24. CurrentUserRights.UserRights = New ObservableCollection(Of tblRoleRights)(CurrentUserRights.UserRights.Distinct)
    25. Return True
    26. End If
    27. End Function


    Die für den User verfügbaren Rechte sind nun in der CurrentUserRights Singleton Classe in einer Proprety UserRights gespeichert.
    Habe es mir hier einfach gemacht und die existierenden Navigationproperties verwendet und keine eigene SQL Abfrage gebastelt.

    Dann habe ich mir mehrere Converter gebaut, mit der aktuellen Implementierung bin ich noch nicht ganz glücklich eventuell habt ihr eine bessere Idee:
    (Als Beispiel der Converter für ein Feld auf Basis des Rechtes "Read" => Visbility (ja ich weiß Nomen ist Omen aber ich bin ja noch nicht mal in der Alpha ^^ )
    Converter zum Ansprechen von Modulen auf Prüfung des Rechtes "Read" das bei mir gleichbedeutend ist mit Visibility

    VB.NET-Quellcode

    1. Imports System.Globalization
    2. Namespace MyErp.Converters.RightConverters
    3. ''' <summary>
    4. ''' This Converter is used to handle the read Rights of Properties,Entities and so on
    5. ''' </summary>
    6. Public Class ClsConverterRightsRead
    7. Implements IValueConverter
    8. ''' <summary>
    9. ''' Convert a ReadRight to a Visibility
    10. ''' </summary>
    11. ''' <param name="value"></param>
    12. ''' <param name="targetType"></param>
    13. ''' <param name="parameter">Entityname|FormName|FieldName</param>
    14. ''' <param name="culture"></param>
    15. ''' <returns></returns>
    16. Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
    17. If parameter Is Nothing Then Return Visibility.Collapsed
    18. Dim Parameters As String() = parameter.ToString.Split("|")
    19. If Parameters.Length <> 3 Then Return Visibility.Collapsed
    20. 'Get the Singleton with all rights:
    21. Dim UserRights As SingletonUserRights = SingletonUserRights.Instance
    22. Dim vRight As tblRoleRights = UserRights.UserRights.Where(Function(x) x.EntityName <> Nothing AndAlso x.FormName <> Nothing AndAlso x.FieldName <> Nothing AndAlso x.EntityName.ToLower = Parameters(0).ToLower AndAlso x.FormName.ToLower = Parameters(1).ToLower AndAlso x.FieldName.ToLower = Parameters(2)).First
    23. If vRight Is Nothing Then
    24. Return Visibility.Collapsed
    25. Else
    26. If vRight.Read Then
    27. Return Visibility.Visible
    28. Else
    29. Return Visibility.Collapsed
    30. End If
    31. End If
    32. End Function
    33. Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
    34. Throw New NotImplementedException()
    35. End Function
    36. End Class
    37. End Namespace


    Im XAML rufe ich den Converter auf der Property Visibility wie folgt auf:

    XML-Quellcode

    1. <TextBox VerticalAlignment="Top" Margin="3">
    2. <TextBox.Visibility>
    3. <Binding Converter="{StaticResource RightReadConverter}" ConverterParameter="sales|contactaddedit|name1" Mode="OneTime"/>
    4. </TextBox.Visibility>
    5. </TextBox>


    Es funktioniert eigentlich alles so wie es soll aber irgendwie (auch wenn das jetzt blöd klingen mag) gefällt mir die Lösung nicht. Es ist irgendwie so undynamisch und 1 Tippfehler reicht aus und das Rechtesystem ist für nichts...
    Eventuell könnte ich als Binding den Namen des aktuellen Formulars mitgeben dann wäre der Parameter etwas kürzer aber bringen tut es nur bedingt etwas....
    mfG.
    Stephan
    Hallo

    kaifreeman schrieb:

    Ich habe zu Beginn vergessen zu erwähnen das ich eine kleine Besonderheit habe, es kann nicht nur jeder Benutzer eine beliebige Anzahl von Benutzergruppen haben sondern auch direkt Rechte bekommen ohne einer Rolle anzugehören, nur als Hinweis damit die Datenstruktur in der Tabelle tblRoleRights klarer wird.

    OK, etwas unüblich aber ok. Normalerweise wenn man mit Rollen arbeitet hat der Benutzer gewisse Rollen. In diesen sind Rechte. Möchte man eine spezielle Berechtigung "schaffen" lässt man den Admin eine neue Rolle anlegen welche genau diese Rechte beinhaltet.

    So ganz verstehe ich das System noch nicht. So wie du das machst wäre mir das auch zu "Heikel".
    Ich dachte an etwas anderes.
    In der Singleton Klasse einfach nur eine List(Of String) wo die Keys drinnen stehen. So ganz verstehe ich noch nicht warum die beim Converter mitgeben musst WO du sich befindest (sales|contactaddedit|name1).
    Ist das nicht im Recht hinterlegt. Also besagt nicht das Recht mit dem KEY "blabla" das man das darf?

    Ich hatte es so gemacht das ich in der Singleton einfach ein List(Of String) habe. String deshalb weil ich dann von der Singleton aus keine Referenz auf z.b. das Model haben muss. (wie du es hast (tblRoleRights))
    Dort sind die Rechte enthalten welche der User besitzt. Beispiel: addEditCustomers

    Dann muss ich im Converter einfach nur noch:

    VB.NET-Quellcode

    1. Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
    2. If parameter Is Nothing Then Return Visibility.Collapsed
    3. 'Get the Singleton with all rights:
    4. Dim UserRights As SingletonUserRights = SingletonUserRights.Instance
    5. if UserRights.Any(Function(r) r.tolower = parameter.ToString.Tolower) then
    6. Return Visibility.Visible
    7. else
    8. Return Visibility.Collapsed
    9. End If
    10. End Function


    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Moin,

    eigentlich hast du Recht... das ein User ein bestimmtes Recht hat kann ich mir eigentlich schenken. Hab mich da in etwas verlaufen (hatte vor kurzem die Situation in der Firma das wir für 1 User ein Feld freigeben mussten und eine Rolle anzulegen erschien mir als aufwändig). Das werde ich noch anpassen.

    Das Problem warum ich das so aufwendig machen muss ist ja folgendes, ich habe eben nicht nur das Recht lesen sondern eben auch schreiben, löschen und editieren. Wenn ich es mit einem Key machen müsste ich defacto für jede Form und für jedes Feld 4 Einträge in der Tabelle haben und das für jede Rolle. Ausserdem kann es z.B. sein das ein Feld in der Datenbank 2mal den selben Namen hat. z.B. das Feld "isAktiv" kommt in vielen Entitäten vor.

    Im Endeffekt muss mir der Key dann ja trotzdem sagen "wo" ich mich im Programm befinde bzw. welches "isAktiv" Feld jetzt gemeint ist.


    Edit:
    "Komische Tags im Text ausgebessert und Umlaut adaptiert... keine Ahnung was da beim Schreiben passiert ist.
    mfG.
    Stephan

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „kaifreeman“ ()

    OK, ich versuchs mal weil dein Beitrag echt schwer zu lesen ist. Vieleicht kannst du ihn bearbeiten. Danke.

    Ich beschreibe jetzt mal ein "normales" vorgehen. WIE DU es dann machst überlasse ich dir aber du handelst dir unnötige probleme und eine unnötige komplexität ein.

    Es gibt Rollen. z.b. Lagerleiter, Lagermitarbeiter, Lagerdisponent usw.
    Jede dieser Rollen kann X Rechte mitbringen. Es kann jemand auch zwei von diesen Rollen von mir aus haben, wenn da dann recht doppelt vorkommen ist das völlig egal.
    Für Löschen (wobei löschen fast nie ne kluge Idee ist, es wird nur löschmarkiert), bearbeiten, erstellen und lesen gibt es jeweils ein Recht und die Rollen müssen dann jeweil die Rechte beinhalten.

    So is eigendlich das "normale" vorgehen.

    PS: Wenn du DEIN system beibehalten willst empfehle ich dir eine Klasse zu machen welcher du das aktuelle Modul übergibst und diese Regelt den Rest. Die Instanz dieser kannst du in der ViewModel-Basisklasse haben und anschliessend komfortabel über einen Multiconverter handeln. Ist nicht nur schöner sondern Tippfehler sind ausgeschlossen.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Tut mir leid habe nicht gesehen das da irgendwie Tags und Umlaute mit reingerutscht sind. Beitrag ist überarbeitet.

    Ich hab mittlerweile am System weitergebastelt. Das Thema mit den User bezogenen Rechten habe ich eliminiert und nur mehr Rollen haben Rechte, jeder User kann n Rollen haben und fertig.

    Ich versuche mal deine Ausführungen für mich verständlich zu machen, aktuell sieht ein Rechteeintrag bei mir so aus:

    IdRoleIDModuleEntityFormFieldWriteReadDeleteEdit

    Die Struktur kommt daher das mein System in Module z.b Verkaufsmodul, Entitäten z.B. Verkaufs-Kontaktmodul, verschiedene Forms und natürlich Fields gegliedert ist. Aus diesen 4 Feldern kann ich mir den "Key" bauen der mir sagt wo ich mich im Programm befinde. Die CRUD Rechte sind gebündelt in einer Zeile.

    Dein Vorschlag wäre:
    IdRoldeIdKeyRight

    Wobei in der Spalte "Right" dann z.b. WRITE; READ; DELETE; EDIT steht. Key wäre dann der "sprechende" Identifizierer der das Recht dann für die View zugänglich macht.
    Habe ich das soweit richtig verstanden?

    Wo es allerdings bei mir im Hirn noch nicht ganz zusammen will ist die Geschichte mit: Wie sage ich dem Programm "Wo" wir gerade sind und welches Recht gesucht werden soll. Alle meine Lösungen laufen irgendwie immer wieder auf eine Art "sprechenden Key hinaus"....
    mfG.
    Stephan
    NE, wenn es nach mir ginge gibt es nur einen Key.

    Beispiel: Rolle "XY Abteilungsleiter" hat dann die Rechte "Modul XY lesen", "Modul XY löschen", "Modul XY bearbeiten".

    Du würdest also diese CRUD definitionen auf Rechtebend gar nicht benötigen. Wozu auch. Es gibt für jede Berechtigung ein Recht. Sagt der Ausdruck bereits. BeRECHTigung.

    Wenn in einer Rolle das Recht in einem Modul Datensätze zu löschen vorhanden ist dann kann der User welcher diese Rolle hat auch Datensätze löschen.
    So bist du auch flexibel. Früher oder später kommst du an einen Punkt wo man in einem Modul etwas löschen können soll aber evtl. nur unter gewissen umständen nur bearbeiten können soll.
    Beispiel: Der Verkäufer soll seine "eigenen" Kunden bearbeiten können aber die Kunden welche einem anderen Verkäufer zugeodnet sind eben nicht. Ein anderer User soll aber alle bearbeiten können.

    Also gibt es noch ein Recht extra: "Schreibrechte auf Kunden anderer Verkäufer".

    Ich versuche bei sowas immer so einfach wie möglich zu denken und mir selbst die komplexität zu nehmen.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Es tut mir leid aber irgendwie stehe ich komplett auf dem Schlauch....
    Wahrscheinlich denke ich einfach zu kompliziert aber wie soll das Recht wissen das es das Recht für löschen ist?
    Du schreibst:
    Wenn in einer Rolle das Recht in einem Modul Datensätze zu löschen vorhanden ist dann kann der User welcher diese Rolle hat auch Datensätze löschen.​

    Vielleicht hilft mir ein "Apfel und Birne" Beispiel, vielleicht könntest du mir folgendes Beispiel von Sprache auf "Datenbank" übersetzen?

    UserId: 1
    User: Max Mustermann
    Rolle: Vertriebsleiter

    Darf: Angebote sehen, Angebote löschen
    Darf nicht: Rechnungen sehen.

    Wie würdest du das in "deinem" Rechtssystem darstellen?

    Ps: Tut mir echt leid das ich so blöd nachfrage aber irgendwie sehe ich vor Bäumen den Wald nicht....
    mfG.
    Stephan
    Hallo

    Ne, kein problem, ich weis wie das ist. Oft meint man es ja wirklich ZU gut und will was feines machen, verliert aber das Ziel aus den Augen und will plötzlich ein Raumschiff basteln.

    Rolle: Vertriebsleiter
    Rolle beinhaltet folgende Rechte:
    • Angebot einsehen
    • Nicht gedruckte Angebote löschen
    • Bereits gedruckte Angebote löschen
    • Angebote bearbeiten/überarbeiten
    • Angebote drucken
    AUS

    Wenn er keine Rechnungen einsehen darf dann benötigt er kein Recht. Fertig aus. Alles wo er das Recht besitzt kann er machen. Darf er etwas nicht wird ihm das Recht nicht gegeben.
    Es stellt sich also gar nicht die frage was er NICHT darf. Sondern nur die Frage was er darf.

    Hintergrund: Vergisst du ihm ein - ich nenne es jetzt mal negativ-Recht - zuzuweisen dann könnte er es ja. da wird er sich sicher nicht melden und sagen: Ihr habt vergessen mir das "wegzunehmen".
    Vergisst du ihm allerdings ein Recht zu geben weil es in einer der Rollen nicht enthalten ist wird er sich sicher melden das es nicht geht. du bist hier also wiedermal sicherer unterwegs.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo

    Ich glaube mein Problem liegt aktuell mehr in der abstrakten Darstellung. Das ein User nichts darf bis er ein entsprechendes Recht hat also "pessimistischer" Ansatz leuchtet ein aber wo es absolut hackt ist wie ich den Eintrag "Angebot einsehen" so in die Datenbank bekomme das es dann auch entsprechend ausgelesen wird. Platt gesagt das "Mapping" zwischen dem Recht und dem Tun dürfen.

    Es erscheint mir wiedersinnig das als String in der DB stehen zu haben und dann "dumm" mit einer If abzufragen Ala:

    VB.NET-Quellcode

    1. if key.tolower = "Angebot_loeschen".tolower then CanDelete = True
    mfG.
    Stephan
    Ne.
    Du hast ja einen Button, ein Menuitem oder was anderes. Gebunden an ein Command.
    Dieses Command hat ein CanExecute und in diesem Fragst du deine Collection ab ob der aktuelle User das Recht hat. Seihe Post #2
    einfacher gehts kaum.

    Hat der User nicht das Recht Angebote einzusehen kann er den View erst gar nicht öffnen. Also ist der Button deaktiviert. (CanExecute = False)
    Du kannst den Button auch gar verbergen indem du es wie in den ersten Posts bereits geschrieben mittels Converter auf das Visibility Property bindest.

    Verstehst du nun was ich meine??

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Das ist mir ja alles klar, wie ich dem Command beibringe auf ein Recht zu reagieren mit CanExcute ist alles logisch.
    Ich glaube ich habe mich da einfach in das Thema der, ich nenne es einfach mal, Key Navigation verrannt also das das Programm wissen muss aus welchem Teil des Programmes kommt, aber wie du schon sagst es ist eigentlich egal weil der Key ja also solcher für das Recht selbst steht und die "Berechtigung" liefert.

    Ich glaube da muss ich nochmal zurück an den Schreibtisch und darüber nachdenken wie ich das sicher und gut abbilden kann.
    Danke für eine Geduld mit mir.
    lg
    mfG.
    Stephan
    Da du jemand bist der mitarbeitet und auch dein bestes gibst kann ich dir gerne ein kleines Demo machen falls notwendig.

    Zwar nur mit den "wichtigsten" Dingen ohne es viel auszuschmücken aber dennoch sollte klar werden was ich so meine.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo Sascha,

    danke für dein Angebot, wenn es für dich OK ist würde ich mich die Tage selbst mit einer Implementierung auseinandersetzen und den Code nochmal reinstellen.
    Hat für mich den Vorteil das ich einen höheren Lerneffekt erziele und wenn es komplett daneben ist, gerne auf dein Angebot zurückkommen würde.

    lg
    Stephan
    mfG.
    Stephan
    Natürlich, im Gegenteil. Das lobe ich mir. Du hast recht, der Lerneffekt ist so viel höher. Viel Glück ;)

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Sodalle es hat etwas gedauert aber endlich hatte ich Zeit mich um die Implementierung zu kümmern.
    Eine Besonderheit konnte ich mir nicht verkneifen ,es gibt das sogenannte "globaladministrator" Recht dieses setzt alle anderen außer Kraft und ist wirklich nur für den Gesamtadministrator gedacht.

    Beginnen wir von ganz vorne:
    Struktur in der Datenbank:
    Datentabelle: tblGroupRights => Id (GUID), GroupId (GUID), Authorization (String)
    Verknüpfungslogik: tblUser n:m tblUsergroups 1:n tblGroupRights

    Meine Funktion "CheckUser" die beim Starten des Programms ausgeführt wird:

    VB.NET-Quellcode

    1. Private Function CheckUser() As Boolean
    2. If vCurrentSQLUsername = Nothing OrElse vCurrentSQLUsername = String.Empty Then Return False
    3. 'Check if the SQL User has a corresponding user in tblUser:
    4. Dim vUser As tblUsers = dbo.tblUsers.Where(Function(x) x.SqlUsername = vCurrentSQLUsername AndAlso x.Aktiv = True).First
    5. If vUser Is Nothing Then
    6. Return False
    7. Else
    8. CurrentUser = vUser
    9. vUser = Nothing
    10. 'Get the current User Rights:
    11. Dim CurrentUserRights As SingletonUserRights = SingletonUserRights.Instance
    12. CurrentUserRights.UserRights.Clear()
    13. 'Check User Group related Rights:
    14. For Each vUserGroup As tblUserGroups In CurrentUser.NvUserGroups
    15. For Each vRight As tblGroupRights In vUserGroup.NvGroupRights
    16. If vRight.Authorization = "globaladministrator" Then CurrentUserRights.UserGlobalAdmin = True
    17. CurrentUserRights.UserRights.Add(vRight.Authorization.ToString)
    18. Next
    19. Next
    20. 'Remove possible Duplicates:
    21. CurrentUserRights.UserRights = New List(Of String)(CurrentUserRights.UserRights.Distinct)
    22. Return True
    23. End If
    24. End Function

    Somt habe ich jetzt in meiner Singleton Klasse "SingletonUserRights" eine Liste von Strings mit den Berechtigungen und einmal noch eine Boolean die angibt ob das "globaladministrator" Recht gesetzt ist (erspare ich mir das ich immer die Liste abgleiche).

    zur Prüfung ob ein Benutzer eine entsprechende Berechtigung hat habe ich 2 Möglichkeiten:
    1. Ich nutze die CanExecute Function meines Relaycommands (wenn die Property an ein solches gebunden ist)
    2. Für Felder die keine CanExecute Commands haben nutze in der GUI einfach einen Boolean to Visibility Converter.

    Meine "CheckAuthorization" Funktion:

    VB.NET-Quellcode

    1. Public Function CheckAuthorization(vKey As String) As Boolean
    2. 'Check if the current user owns the globaladministrator authorization:
    3. Dim vUserRights As SingletonUserRights = SingletonUserRights.Instance
    4. If vUserRights.UserGlobalAdmin Then Return True
    5. If vKey = String.Empty Then Return False
    6. Return vUserRights.UserRights.Contains(vKey)
    7. End Function


    Um eine Auswahl der verfügbaren Rechte zu haben schreibe ich eine XML Datei mit die folgende Struktur beinhaltet:

    XML-Quellcode

    1. <?xml version="1.0" encoding="utf-8" ?>
    2. <authorizations>
    3. <!--Administration:-->
    4. <authorization Key="globaladministrator" Name="Globales Administrationsrecht" Description="Achtung dieses Recht überschreibt alle anderen Rechte und sollte nur für Administratoren benutzt werden"/>
    5. <!--NumberRanges-->
    6. <authorization Key="access_numberranges" Name="Nummernkreise öffnen" Description="Berechtigung zum Öffnen des Moduls Nummernkreise"/>
    7. <authorization Key="add_numberranges" Name="Nummernkreis hinzufügen" Description="Berechtigung zum Erstellen eines Nummernkreises"/>
    8. <authorization Key="edit_numberranges" Name="Nummernkreis editieren" Description="Berechtigung zum Ändern eines Nummernkreises"/>
    9. <authorization Key="delete_numberranges" Name="Nummernkreis löschen" Description="Berechtigung zum Löschen eines Nummernkreises"/>
    10. </authorizations>


    Diese lade ich ebenfalls beim Programmstart in eine Singleton Klasse (während ich schreibe fällt mir gerade auf das ich mein Klassen eigentlich zu einer Klasse "mergen" könnte).

    Das ganze biete ich dann in der GUI mit 2 DataGrids an und kann so meiner Benutzergruppe Ihre Rechte geben:


    Soweit meine Implementierung, jetzt greife ich mir auch auf den Kopf und merke das ich definitiv viel zu kompliziert gedacht habe...
    Danke für deine Hilfe und Geduld.


    Edit: Tippfehler ausgebessert Beziehung zwischen Usern zu Gruppen ist nicht 1:n sondern n:m
    mfG.
    Stephan

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „kaifreeman“ ()

    Hallo

    Na schau, viel einfacher als gedacht oder. Ich kenne das. Oft sieht man den Wald vor lauter Bäumen nicht und denkt zu kompliziert.
    So ist das jetzt sauber und korrekt.

    OK, bis auf das mit dem Administrator. Eigentlich sollte es eine Rolle auch für diesen geben welche einfach alle Rechte beinhaltet. Aber gut.

    Deine Funktion würde ich etwas umschreiben, aber funzt beides.
    So aus dem Kopf:

    VB.NET-Quellcode

    1. Public Function CheckAuthorization(vKey As String) As Boolean
    2. If String.IsNullOrEmpty(vKey) then Throw new Exception("Der Key muss angegeben werden!")
    3. Return SingletonUserRights.Instance.UserGlobalAdmin OrElse SingletonUserRights.Instance.UserRights.Any(vKey)
    4. Function



    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Abend,

    Ohja manchmal braucht man eine Pause oder anderen Input weil man sich in etwas verrannt hat.
    Ja das mit dem Administrator habe ich mir auch nochmal überlegt, es gibt eine Rolle Administratoren durch die "globaladministrator" hebel ich mir das eigentlich wieder aus....
    Die Idee hinter dem "globaladministrator" war es sich die Mühe zu ersparen beim "Seeden" der Datenbank jedes Recht der Rolle Administrator geben zu müssen.
    Ich glaube das Konzept werde ich mir nochmals überlegen...

    zu deiner Funktion:

    VB.NET-Quellcode

    1. Public Function CheckAuthorization(vKey As String) As Boolean
    2. If String.IsNullOrEmpty(vKey) then Throw new Exception("Der Key muss angegeben werden!")
    3. Return SingletonUserRights.Instance.UserGlobalAdmin OrElse SingletonUserRights.Instance.UserRights.Any(vKey)
    4. Function



    Eine Exception zu werden erschien mir als zu hart ich werde das meinem Logger mitgegeben und ansonsten lasse ich das Programm laufen nur eben ohne die angeforderte "Nichts" Berechtigung.
    Deine Return Funktion ist mir auch nicht ganz klar. Die .Any erfordert eine Function(of boolean) als Argument den vKey kann ich damit nicht direkt übergeben. Was wäre der Vorteil es mit Any zu machen und nicht mit Contains?

    lg
    mfG.
    Stephan