
ABAP Unit - TDF (Berechtigungen)
Kannst du eine Berechtigungsprüfung eigentlich Mocken? In diesem Artikel schauen wir uns den Test Double für Berechtigungsprüfungen an.
Inhaltsverzeichnis
Wir legen in diesem Artikel ein neuen Berechtigungsobjekt (ABAP Cloud) an, implementieren eine Methode mit einer Berechtigungsprüfung und mocken dann die eigentliche Prüfung für den Test.
Einleitung
Berechtigungsprüfungen sind in der ABAP Entwicklung Standard und sollen sicherstellen, dass nicht jeder Zugriff auf bestimmte Funktionalitäten im System hat. Allerdings war bisher das Thema bei ABAP Unit Tests nicht sehr gut abgedeckt und der Test verschiedener Berechtigungsprüfungen nicht immer so leicht durchführbar. Mittlerweile gibt es einen Test Double zum Mocken der Prüfung.
Vorbereitung
Damit wir mit dem Test beginnen können, müssen wir noch einige Vorarbeiten durchführen, diese sind in diesem Kapitel beschrieben.
Berechtigungsobjekt
Legen wir dazu im ersten Schritt ein neues Berechtigungsobjekt an. Mit ABAP Cloud kannst du dies über die ABAP Development Tools machen, wenn du in den Objekten suchst.
Wie bisher auch, ist die Länge des Namens auf 10 Zeichen beschränkt, daher können wir hier leider nach wie vor nicht mit sprechenden Namen arbeiten. Wir benennen das Objekt ZBS_TDFCHK.
Nach der Anlage erhalten wir eine Eingabemaske für das neue Berechtigungsobjekt. Im oberen Teil ist das Objekt für Cloud Development markiert. Automatisch wurde die Aktivität (ACTVT) mit einigen Ausprägungen ergänzt. Hier benötigen wir allerdings nur 02 und 03 als Varianten für unseren Test.
Klasse
Dann definieren wir uns eine Klasse, die eine Methode mit der Berechtigungsprüfung beinhaltet. Damit wir verschiedene Aktivitäten testen können, legen wir noch Konstanten an. Die Methode führt eine Berechtigungsprüfung durch und gibt einen booleschen Wert zurück für das Ergebnis.
CLASS zcl_bs_demo_tdf_auth DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES auth_activity TYPE c LENGTH 2.
CONSTANTS: BEGIN OF activities,
change TYPE auth_activity VALUE '02',
display TYPE auth_activity VALUE '03',
END OF activities.
METHODS has_authority
IMPORTING activity TYPE auth_activity
RETURNING VALUE(result) TYPE abap_boolean.
ENDCLASS.
CLASS zcl_bs_demo_tdf_auth IMPLEMENTATION.
METHOD has_authority.
AUTHORITY-CHECK OBJECT 'ZBS_TDFCHK'
ID 'ACTVT' FIELD activity.
RETURN xsdbool( sy-subrc = 0 ).
ENDMETHOD.
ENDCLASS.
Zuweisung
Als letzten Schritt der Vorbereitung müssen wir noch die Berechtigungen unserem User zuweisen. Aktuell kann das Framework nur Berechtigungen einschränken, wir können uns damit keine neuen Berechtigungen geben. Für den Test und die Nutzung ist dies daher ein wichtiger Punkt. In einem Cloud System weisen wir uns das Berechtigungsobjekt über eine IAM-App und einen Business Katalog zu. On-Prem erweiterst du eine PFCG-Rolle oder du legst eine Neue an und ordnest sie dir zu.
Test Double
Die Grundlagen sind nun im System vorhanden, daher beginnen wir mit der Umsetzung der Testklasse und dem Aufbau des Test Doubles.
Grundaufbau
Legen wir dazu im ersten Schritt eine Grundkonfiguration an und validieren, dass wir im Standard alle Berechtigungen haben. Da wir das Objekt neu angelegt haben, sollte es in noch keiner Rolle vorhanden sein, mit dem letzten Schritt haben wir es uns aber zugeordnet. Unsere Testinstanz CUT lassen wir uns für jeden Unit Test über die Setup Methode erzeugen. Mehr Details zum Flow und den Grundlagen in diesem Artikel.
CLASS ltc_authority DEFINITION FINAL
FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.
PRIVATE SECTION.
DATA cut TYPE REF TO zcl_bs_demo_tdf_auth.
METHODS setup.
METHODS change_auth FOR TESTING RAISING cx_static_check.
METHODS display_auth FOR TESTING RAISING cx_static_check.
ENDCLASS.
CLASS ltc_authority IMPLEMENTATION.
METHOD setup.
cut = NEW #( ).
ENDMETHOD.
METHOD change_auth.
DATA(result) = cut->has_authority( cut->activities-change ).
cl_abap_unit_assert=>assert_true( result ).
ENDMETHOD.
METHOD display_auth.
DATA(result) = cut->has_authority( cut->activities-display ).
cl_abap_unit_assert=>assert_true( result ).
ENDMETHOD.
ENDCLASS.
Double aufsetzen
Im nächsten Schritt wollen wir den Double aufsetzen und verwenden. Dazu verwenden wir die Klasse CL_AUNIT_AUTHORITY_CHECK um einen Controller zu erzeugen, mit dem wir unseren Unit Test steuern können. Die Klasse enthält verschiedene Methoden, womit wir Berechtigungen einschränken können oder zum Beispiel auch Erwartungen definieren können.
Dazu erzeugen wir en Controller im CLASS_SETUP, um diesen für alle Unit Tests zur Verfügung zu stellen.
METHOD class_setup.
auth_controller = cl_aunit_authority_check=>get_controller( ).
ENDMETHOD.
Damit der Controller keine Auswirkungen auf die Tests mit echten Berechtigungen hat, setzen wir nach jedem Unit Test den Controller über die Teardown Methode zurück.
METHOD teardown.
auth_controller->reset( ).
ENDMETHOD.
Wollen wir nun die Berechtigungen einschränken, definieren wir uns im ersten Schritt die neuen Berechtigungen. Für diese interne Tabelle lassen wir uns über die Methode CREATE_AUTH_OBJECT_SET ein Objektset erzeugen. Zum Abschluss übergeben wir das Set an den Controller, damit die Berechtigungen eingeschränkt werden.
METHOD set_only_display_auth.
DATA(auth_display) = VALUE cl_aunit_auth_check_types_def=>role_auth_objects(
( object = 'ZBS_TDFCHK'
authorizations = VALUE #( ( VALUE #( ( fieldname = 'ACTVT'
fieldvalues = VALUE #( ( lower_value = '03' ) ) ) ) ) ) ) ).
DATA(auth_objectset) = cl_aunit_authority_check=>create_auth_object_set(
VALUE cl_aunit_auth_check_types_def=>user_role_authorizations( ( role_authorizations = auth_display ) ) ).
auth_controller->restrict_authorizations_to( auth_objectset ).
ENDMETHOD.
Durch den RESET stellen wir sicher, dass der nächste Unit Test wieder die vollen Berechtigungen zur Verfügung hat. Dazu definieren wir uns weitere Testmethoden zur Prüfung der vorhandenen Berechtigungen und ob weiterhin die Berechtigungen eingeschränkt sind.
Logs
Nach der Ausführung der Prüfung, können wir uns über den Controller das Ausführungslog zurückgeben lassen. Über dieses Objekt können wir weitere Prüfungen vornehmen. Macht vor allem dann Sinn, wenn das Ergebnis der Methode nicht die Berechtigungsprüfung widerspiegelt.
DATA(execution_log) = auth_controller->get_auth_check_execution_log( ).
Das Log bietet uns verschiedene Methoden, um an Informationen für die Ausführung zu kommen. Über GET_EXECUTION_STATUS erhalten wir zum Beispiel eine Information zur Anzahl der ausgeführten Prüfungen und welche Fehlgeschlagen sind.
Schauen wir die fehlgeschlagenen Prüfungen an, dann finden wir die letzte Prüfung, die für unseren User negativ ausgefallen ist. So haben wir noch einmal die Möglichkeit die durchgeführten Prüfungen zu monitoren.
Vollständiges Beispiel
Hier findest du noch einmal das komplette Beispiel des Unit Tests. Die globale Klasse findest du im Kapitel Vorbereitung, diese haben wir nicht noch einmal übernommen.
CLASS ltc_authority DEFINITION FINAL
FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.
PRIVATE SECTION.
CLASS-DATA auth_controller TYPE REF TO if_aunit_auth_check_controller.
DATA cut TYPE REF TO zcl_bs_demo_tdf_auth.
CLASS-METHODS class_setup.
METHODS setup.
METHODS teardown.
METHODS set_only_display_auth
RAISING cx_abap_auth_check_exception.
METHODS change_auth FOR TESTING RAISING cx_static_check.
METHODS display_auth FOR TESTING RAISING cx_static_check.
METHODS double_without_auth FOR TESTING RAISING cx_static_check.
METHODS double_with_auth FOR TESTING RAISING cx_static_check.
METHODS check_logs FOR TESTING RAISING cx_static_check.
ENDCLASS.
CLASS ltc_authority IMPLEMENTATION.
METHOD class_setup.
auth_controller = cl_aunit_authority_check=>get_controller( ).
ENDMETHOD.
METHOD setup.
cut = NEW #( ).
ENDMETHOD.
METHOD teardown.
auth_controller->reset( ).
ENDMETHOD.
METHOD change_auth.
DATA(result) = cut->has_authority( cut->activities-change ).
cl_abap_unit_assert=>assert_true( result ).
ENDMETHOD.
METHOD display_auth.
DATA(result) = cut->has_authority( cut->activities-display ).
cl_abap_unit_assert=>assert_true( result ).
ENDMETHOD.
METHOD double_without_auth.
set_only_display_auth( ).
DATA(result) = cut->has_authority( cut->activities-change ).
cl_abap_unit_assert=>assert_false( result ).
ENDMETHOD.
METHOD double_with_auth.
set_only_display_auth( ).
DATA(result) = cut->has_authority( cut->activities-display ).
cl_abap_unit_assert=>assert_true( result ).
ENDMETHOD.
METHOD check_logs.
set_only_display_auth( ).
DATA(result) = cut->has_authority( cut->activities-change ).
DATA(execution_log) = auth_controller->get_auth_check_execution_log( ).
execution_log->get_execution_status( IMPORTING failed_execution = DATA(failed_execution) ).
cl_abap_unit_assert=>assert_false( result ).
cl_abap_unit_assert=>assert_equals( exp = 1
act = lines( failed_execution ) ).
ENDMETHOD.
METHOD set_only_display_auth.
DATA(auth_display) = VALUE cl_aunit_auth_check_types_def=>role_auth_objects(
( object = 'ZBS_TDFCHK'
authorizations = VALUE #( ( VALUE #( ( fieldname = 'ACTVT'
fieldvalues = VALUE #( ( lower_value = '03' ) ) ) ) ) ) ) ).
DATA(auth_objectset) = cl_aunit_authority_check=>create_auth_object_set(
VALUE cl_aunit_auth_check_types_def=>user_role_authorizations( ( role_authorizations = auth_display ) ) ).
auth_controller->restrict_authorizations_to( auth_objectset ).
ENDMETHOD.
ENDCLASS.
Fazit
Mit dem Double für Berechtigungsprüfungen, können wir fehlende Berechtigungen simulieren und so besser das Verhalten unserer Logik prüfen. Aus Sicherheitsgründen können keine zusätzlichen Berechtigungen vergeben werden, was allerdings auch Sinn ergibt.
Quelle:
Release - ABAP Feature Matrix
SAP Help - Managing Dependencies on ABAP Authority Checks