
RAP - Feature Control (Teil 2)
Wie sieht es eigentlich mit den Feature Controls in RAP aus und welche Änderungen gab es nun mittlerweile? Hier schauen wir noch einmal auf die Features für unsere Sales App und welche Updates sich ergeben haben.
Inhaltsverzeichnis
Der erste Teile der Feature Controls hatten wir vor einiger Zeit erstellt und es gab in der Zwischenzeit kleinere Anpassungen, auf die wir in diesem Artikel eingehen wollen.
Einleitung
Bisher hatten wir das Berechtigungsobjekt und den Restriction Type für unsere Sales App implementiert. Deshalb wollen wir uns noch einmal genauer anschauen, wie eigentlich Feature Controls innerhalb unserer Anwendung gesetzt werden, um anhand der Berechtigungen zu steuern, ob bestimmte Aktionen möglich sind oder diese nicht mehr verfügbar sind. Im letzten Artikel hatten wir uns dazu die Sichtbarkeit von Datensätzen über Core Data Services angeschaut und wollen diesmal auf den programmatischen Teil innerhalb des RAP-Objekts eingehen, um auf die verschiedenen Berechtigungen zu reagieren.
Vorbereitung
Als Vorbereitung legen wir eine erste Klasse an, um eine Berechtigungsprüfung durchführen zu können. Dabei definieren wir eine konstante Struktur mit den verschiedenen Aktivitäten, die diese Berechtigungsprüfung zulässt, und eine zusätzliche Methode, mit der wir prüfen können, ob wir Berechtigung haben. Der Methode übergeben wir die Aktivität, die wir überprüfen wollen, und eine Partner-ID. Die Partner-IDs lassen wir erst einmal optional, da wir zum Beispiel in einem Fall nur die Aktivität prüfen wollen, ob diese allgemein verfügbar ist.
CONSTANTS:
BEGIN OF activities,
create TYPE activ_auth VALUE '01',
change TYPE activ_auth VALUE '02',
display TYPE activ_auth VALUE '03',
delete TYPE activ_auth VALUE '06',
END OF activities.
"! Check if the user has authorization for the Activity and Partner ID
"! @parameter activity | Activity
"! @parameter parnter_id | Partner ID (optional)
"! @parameter result | X = Auth ok, '' = No Auth
METHODS has_authorization
IMPORTING activity TYPE activ_auth
parnter_id TYPE zbs_demo_sa_partner OPTIONAL
RETURNING VALUE(result) TYPE abap_boolean.
Die Methode prägen wir dann aus und prüfen gegen unser Berechtigungsobjekt. Hier haben wir zwei Szenarien, die wir implementieren: Einmal, dass wir keine Partner-ID haben, dann prüfen wir unser Feld gegen den Wert DUMMY. Oder, wenn beide Felder übergeben wurden, prüfen wir die vollständige Berechtigung. Das Ergebnis geben wir dann als Booleschen Wert an den Aufrufer zurück, damit er weiß, ob der User aktuell Berechtigung hat oder nicht. Diese Art der Klasse macht es besonders praktisch, nicht jedes Mal die Berechtigungsprüfung zu implementieren oder die verschiedenen Aktivitäten zu kennen, die für das Berechtigungsobjekt maßgeblich sind.
METHOD has_authorization.
IF parnter_id IS INITIAL.
AUTHORITY-CHECK OBJECT 'ZBSDMOPART'
ID 'ACTVT' FIELD activity
ID 'ZBSDMOPART' DUMMY.
ELSE.
AUTHORITY-CHECK OBJECT 'ZBSDMOPART'
ID 'ACTVT' FIELD activity
ID 'ZBSDMOPART' FIELD parnter_id.
ENDIF.
RETURN xsdbool( sy-subrc = 0 ).
ENDMETHOD.
Implementierung
Da wir aktuell ein Beispiel abgelegt haben, wo wir Aktivitäten mit Business Partnern begrenzen, können wir uns nicht mehr nur auf die statische Feature Control verlassen. Hier müssen wir eine instanzbasierte Implementierung durchführen, um pro Datensatz die Aktionen zu definieren.
Erweiterung
Im ersten Schritt erweitern wir dazu unsere Verhaltensimplementierung und ergänzen jeweils die Feature Controls. So haben wir zum Beispiel statische Aktionen, bei denen wir nur features:global verwenden können, ebenso wie für die Create-Action. Update und Delete können wir jeweils dann instanzbasiert definieren. Da wir das Create nur auf globaler Ebene definieren können und nicht auf der Partnerebene, müssen wir eine zusätzliche Validierung implementieren, die das Feld Partner nochmal prüft. Für die Anlage und den Update, damit wir jeweils nur Partner-Daten verwenden können, für die wir auch berechtigt sind.
Nachdem wir dann die Anpassungen durchgeführt haben, müssen wir noch über den Quick Fix gehen, um drei neue Methoden in der Verhaltensimplementierung anzulegen. Diese werden automatisch generiert, sodass wir keine Arbeit mit den Methoden haben.
Typ
Implementieren wir nun die ersten Berechtigungsprüfungen und weisen dann Werte zu, So wie wir es in der Vergangenheit beziehungsweise im ersten Artikel gemacht haben, kann es sein, dass wir nun eine Warnmeldung erhalten. Hierbei handelt es sich um Änderungen, die SAP in der Zwischenzeit vorgenommen hat, um Verwirrungen oder falsche Kennzeichen zu vermeiden.
Solltest du diese Warnmeldung erhalten, dann hast du den falschen Datentyp verwendet. Mittlerweile gibt es dazu ein neues Feature Control, welches du mit den verschiedenen Datentypen ausrüsten kannst. Dieses unterscheidet sich entsprechend auf die Felder oder die verschiedenen Operationen, die du aktivieren oder deaktivieren kannst. Mit dem neuen Datentyp soll deshalb ausgeschlossen werden, dass es zu Problemen kommt, wenn falsche Flags gesetzt werden oder diese aus einem anderen Typen stammen.
Global
In der Methode erhalten wir dann entsprechend zwei Strukturen und verschiedene Reporting-Strukturen, die wir verwenden sollten. Die erste Struktur ist die "requested_features"-Struktur. Diese definiert, ob angefragt wird, ob für eine entsprechende Aktion die Berechtigungen geprüft werden sollen. Das hat zur Folge, dass wir zum Beispiel Performance sparen können, wenn es sich um eine performanceintensive Aktion handelt, die Berechtigung zu ermitteln. Die zweite wichtige Struktur ist die "result"-Struktur, denn darüber können wir dann definieren, ob die entsprechende Aktion vorhanden ist, berechtigt ist oder deaktiviert ist. Wie du am Typen der Rückgabewerte siehst, handelt es sich mittlerweile um die neuen Feature-Control-Flags. Deshalb könnte es hier zu Warnmeldungen kommen, wenn wir die falschen Typen verwenden.
Für die Implementierung reicht es dann, einmal die Berechtigungen abzufragen, da wir hier im globalen Bereich ohne den Business Partner arbeiten. Das heißt, wir können einmal prüfen, ob der User Create-Berechtigung hat, und im nächsten Schritt prüfen, ob auch das Create-Feature gerade abgefragt wird. Wenn die Berechtigungen vorhanden sind, aktivieren wir die Create-Action, ansonsten würden wir die Aktion deaktivieren. Dies machen wir dann auch für alle drei vorhandenen Aktionen.
DATA(can_create) = NEW zcl_bs_demo_rap_sales_auth( )->has_authorization(
activity = zcl_bs_demo_rap_sales_auth=>activities-create ).
IF requested_features-%create = if_abap_behv=>mk-on.
IF can_create = abap_true.
result-%create = if_abap_behv=>fc-o-enabled.
ELSE.
result-%create = if_abap_behv=>fc-o-disabled.
ENDIF.
ENDIF.
Instanz
Arbeiten wir auf der instanzbasierten Seite, müssen wir zuvor erst über das EML-Statement alle betroffenen Datensätze lesen. Hier reicht es, zusätzlich die Partnernummer zu lesen, damit wir die Informationen für unsere Berechtigungsprüfung haben. Im Anschluss gehen wir über alle Datensätze und bestimmen die Berechtigung für diese. Da wir in diesem Fall mehrere Prüfungen gegen mehrere Aktionen haben, fügen wir am Anfang des Loops einen neuen Datensatz in das Ergebnis ein und verwenden hierbei den %TKY, welcher den Schlüssel und Draftstatus enthält. Wir können dann später auf diesen Datensatz zugreifen.
FINAL(auth) = NEW zcl_bs_demo_rap_sales_auth( ).
READ ENTITIES OF zbs_r_sasale IN LOCAL MODE
ENTITY SASale
FIELDS ( PartnerNumber ) WITH CORRESPONDING #( keys )
RESULT FINAL(check_sales).
LOOP AT check_sales INTO FINAL(check_sale).
INSERT VALUE #( %tky = check_sale-%tky ) INTO TABLE result REFERENCE INTO DATA(result_auth).
ENDLOOP.
Die Prüfung läuft dann relativ ähnlich ab wie auch bei den globalen Features: Wir prüfen erst einmal, ob diese Anfrage auch gewollt ist und ob wir das Feld prüfen wollen, dann führen wir eine Berechtigungsprüfung aus. Bei dieser übergeben wir auch den Partner, da wir hier auf Ebene des Partners prüfen. Sind die Berechtigungen vorhanden, setzen wir in der "result"-Struktur das entsprechende Feature für die Aktion.
IF requested_features-%update = if_abap_behv=>mk-on.
IF auth->has_authorization( activity = auth->activities-change
parnter_id = check_sale-PartnerNumber ).
result_auth->%features-%update = if_abap_behv=>fc-o-enabled.
ELSE.
result_auth->%features-%update = if_abap_behv=>fc-o-disabled.
ENDIF.
ENDIF.
Validierung
Bei der eigentlichen Partnervalidierung gehen wir ähnlich vor wie bei den instanzbasierten Datensätzen. Wir lesen mittels EML erst einmal die betroffenen Partnernummern aus und führen dann eine Validierung durch. In diesem Fall prüfen wir einmal, ob wir Berechtigung haben. Haben wir keine Berechtigung, erzeugen wir eine Fehlermeldung, indem wir die "reported"-Struktur befüllen und einen Fehler an den Aufrufer zurückgeben. Damit kann der Datensatz nicht gespeichert werden, da die Validierung fehlschlägt. Grundsätzlich ist dann immer noch ein Discard Draft möglich, um die Änderungen zurückzunehmen.
IF NOT auth->has_authorization( activity = auth->activities-change
parnter_id = check_sale-PartnerNumber ).
INSERT VALUE #( %tky = check_sale-%tky
%msg = new_message( id = 'ZBS_DEMO_RAP'
number = '010'
severity = if_abap_behv_message=>severity-error
v1 = check_sale-PartnerNumber ) )
INTO TABLE reported-sasale.
ENDIF.
Performance
Grundsätzlich kann auch das Thema Performance eine wichtige Rolle spielen, wenn es um das Thema Berechtigungsprüfung geht. Benötigt nämlich die Berechtigungsprüfung zu lange oder ist sie performanceintensiv, wird der User zwar Datensätze im UI sehen können, es kann aber durchaus vorkommen, dass er noch nicht mit diesen arbeiten kann, da die Berechtigungsprüfung noch im Hintergrund läuft. In der Vergangenheit wurden dazu auch keine Indikatoren angezeigt, das heißt, der User konnte einfach die Datensätze nicht anklicken, was ein typischer Indikator dafür war, dass noch nicht alle Prüfungen durchgelaufen sind. Deshalb solltest du bei der Überprüfung darauf achten, dass diese nicht zu lange laufen. Ein einfacher Workaround ist, die Prüfung erst durchzuführen, während die Aktion läuft, um beim Speichern noch einmal zu prüfen, ob die Berechtigung vorhanden ist. Dauert es länger, beeinflusst das zwar den Speicherprozess, aber es müssen auch nicht alle Datensätze vorab geprüft werden und das initiale Arbeiten klappt.
Test
Führen wir nun einen Test im UI durch und schauen uns die Berechtigungen und Abgrenzungen im Detail an. Dabei schauen wir auf die verschiedenen Aktionen und vergebenen Berechtigungen.
Update
Aktuell haben wir keine Berechtigung zum Anlegen, Ändern und Löschen von Objekten. Allerdings: Wenn wir die Anwendung starten und uns einen Datensatz ansehen, sehen wir, dass die Edit-Action nicht eingeschränkt ist. Grundsätzlich haben wir im Code den Update deaktiviert und wir haben auch keine Berechtigung für ein Update.
Allerdings befinden wir uns hier in einem Draft-Szenario. Das bedeutet, wir haben neben der Update-Action auch die Edit-Action, welche für den Draft-State wichtig ist. Diese wird auch hier im UI angezeigt, und da wir diese nicht eingeschränkt haben, haben wir entsprechend immer noch die Berechtigung dafür. Das heißt, wir müssen auch in diesem Fall das Feature Control implementieren und in unsere Logik mit aufnehmen, um diese zu deaktivieren.
Berechtigung
Wie wir im Schritt zuvor bereits festgestellt hatten, haben wir keine Berechtigung, einen Datensatz zu ändern oder zu löschen. Im letzten Artikel hatten wir uns eigentlich Berechtigungen gegeben, Datensätze anzuzeigen. Dies wurde über die IAM App gemacht. Die dynamische Pflege über die eigentliche Business Role definiert nur die Business Partner, für die wir Berechtigung haben. Woher kommen also die Aktivitäten, die wir benötigen? In diesem Fall kommen sie weiterhin aus der IAM-App, der wir das Berechtigungsobjekt zugeordnet haben. Im Fall, dass wir das über einen Restriction Type pflegen, überschreiben wir deshalb zwar den Business Partner, aber wir können nicht die Aktivität überschreiben, die durch diese IAM-App gegeben wird. Das heißt, in diesem Fall haben wir nur Display-Berechtigung und können deshalb keine Änderungen durchführen, neue Datensätze anlegen oder löschen.
Daher passen wir nun einmal die IAM-App an und vergeben Berechtigungen für das Anlegen, Ändern und Anzeigen, ohne die Löschberechtigung. So können wir im nächsten Schritt einen Test durchführen.
Funktionen
Passen wir dazu noch einmal die Berechtigungen an und ergänzen die Leseberechtigung durch einen weiteren Business Partner mit der 5 am Ende. Damit haben wir aktuell Schreibberechtigung auf die Nummern 3 und 4 und können die 5 lesen, was wir dann gleich auch im Test ausprobieren werden.
Lassen wir nur einen Datensatz mit einer 5 am Ende zu, sollten wir eigentlich nur Leseberechtigung haben. Das sehen wir daran, dass der Edit- und der Delete-Button ausgegraut sind. Somit hat das Feature Control gegriffen und die entsprechenden Berechtigungen greifen. Eine Änderung des Datensatzes ist nicht möglich, dort können wir lediglich anzeigen oder Aktionen durchführen, die keine Berechtigungsprüfung haben, wie zum Beispiel das Prüfen der Konsistenz.
Wählen wir einen Datensatz aus, zu dem wir die Berechtigung haben, dann sehen wir auch, dass der Edit-Button aktiv ist. Grundsätzlich fehlt hier die Berechtigung für das Delete. Diese haben wir über die IAM nicht zugeordnet. Das heißt, außer einem Administrator zum Beispiel kann aktuell keiner Datensätze löschen, sondern wir können nur neue Datensätze anlegen und diese anpassen.
Vollständiges Beispiel
Das vollständige Beispiel findest du in GitHub im entsprechenden Paket für die Sales App. Die Änderungen aus diesem Artikel findest du in diesem Commit und kannst damit die Änderungen, plus die Zusatzinformationen, nachvollziehen.
Fazit
Das Feature Control ist eine wichtige Funktion, um Aktionen und Aktivitäten zu aktivieren oder zu deaktivieren, je nachdem, wie die Berechtigungen sind oder wie der Status des Objektes ist. Mittlerweile hat sich auch mit den neueren Releases etwas bei der Typisierung der entsprechenden Flags getan. Da solltest du aufpassen und auf mögliche Warnmeldungen achten und diese beheben. Es könnte sonst passieren, dass du keine Berechtigung hast, obwohl du das On-Flag zum Beispiel verwendet hast, weil du es aus einem falschen Typen verwendest und es dann eine andere Bedeutung hat.










