Datenbank-Rollen in APEX nutzen: Mit Datenbank-Links

Wie die meisten APEX-Anwender wissen, verwendet APEX ein ganz bestimmtes Sicherheitsmodell. Wenn Sie eine APEX-Seite aufrufen (sei es in einer Ihrer Applikationen oder in der Entwicklungsumgebung), so wird die zugrundeliegende Datenbanksitzung, welches der Webserver mit mod_plsql, der neue J2EE-Listener oder das Embedded Gateway aufbaut, stets mit einem bestimmten, niedrig privilegierten Benutzerkonto aufgebaut. Beim Embedded Gateway ist das der User ANONYMOUS, beim mod_plsql typischerweise der APEX_PUBLIC_USER.

Wenn man sich die Privilegien dieses Nutzerkontos näher ansieht, stellt man fest, dass dieser außer einem CREATE SESSION-Privileg keinerlei Rechte hat. Dennoch können Sie die Tabellen in Ihrem APEX Workspace betrachten und die PL/SQL-Funktionen, -Prozeduren und Packages im Schema ausführen.

Das liegt daran, dass die SQL-Kommandos und PL/SQL-Aufrufe nicht direkt ausgeführt werden, sonderm innerhalb der Application Express-Engine. Wenn Sie also im SQL-Workshop eine SQL-Abfrage eingeben, wird der PL/SQL-Code der APEX-Engine aufgerufen. Die Engine läuft im Schema APEX_030200 bei APEX 3.2 oder FLOWS_XXXXXX bei älteren APEX-Versionen. Die APEX-Engine führt die Anweisungen Ihrer Applikation allerdings nicht mit seinen eigenen Privilegien aus, sondern es setzt vorher die Privilegien Ihres Workspace Schemas. Abbildung 1, die freundlicherweise von Niels De Bruijn von der MT AG bereitgestellt wurde, zeigt den Ablauf einer APEX-Sitzung schematisch.

An einer APEX-Sitzung beteiligte Datenbankschemas

Abbildung 1: An einer APEX-Sitzung beteiligte Datenbankschemas

Für die Experten: Ihre SQL-Anweisungen werden am Ende mit dem Paket DBMS_SYS_SQL ausgeführt. DBMS_SYS_SQL ist undokumentiert, führt wie DBMS_SQL SQL-Anweisungen aus, erlaubt im Gegensatz zu diesem aber die Angabe eines Datenbankschemas, mit dessen Privilegien der Code ausgeführt werden soll. Das Ergebnis ist, dass die SQL- und PL/SQL-Anweisungen Ihrer Anwendungen mit den Privilegien des Workspace-Schemas ablaufen.

Während der ganzen Zeit sind Sie jedoch mit dem niedig privilegierten Nutzerkonto (ANONYMOUS bzw. APEX_PUBLIC_USER) verbunden. Den Unterschied können Sie im SQL Workshop sehr gut nachvollziehen - legen Sie dazu mit folgender SQL-Anweisung eine View an:

create view userinfo as
select 
  sys_context('userenv','SESSION_USER') connected_user,
  sys_context('userenv','CURRENT_USER') command_privs
from dual

Abbildung 2 zeigt eine Abfrage auf diese View. Man erkennt, dass die Datenbankverbindung selbst als ANONYMOUS erfolgt ist, das SQL jedoch mit den Privilegien von FSMASTER abläuft.

SESSION_USER vs. CURRENT_USER in einer APEX-Sitzung

Abbildung 2: SESSION_USER vs. CURRENT_USER in einer APEX-Sitzung

Die erste wichtige Feststellung im APEX-Umfeld ist also schonmal die, dass alle SQL- und PL/SQL-Aufrufe nicht direkt in der Datenbanksitzung ausgeführt werden, sondern von den PL/SQL-Funktionen der APEX-Engine, welche sicherstellt, dass die Privilegien des Workspace-Schemas angewendet werden. Dadurch wird der Code mit den Privilegien ausgeführt, die Sie erwarten - hat aber den Nebeneffekt, dass alle Rollen, die Ihrem Workspace-Schema zugewiesen sein könnten, abgeschaltet sind.

Manchmal kommt es allerdings vor, dass man ein Kommando gerne im gleichen Kontext ablaufen lassen würde, wie es in SQL*Plus oder im SQL Developer wäre ...

  • ... also mit allen Rollen, die dem Workspace-Schema zugewiesen sind ...
  • ... und mit einer Datenbanksitung, die effektiv mit dem Workspace-User aufgebaut wurde. Es gibt in der Oracle-Datenbank die eine oder andere Komponente, die zum einwandfreien Betrieb eben eine solche Datenbankverbindung braucht.

Die Lösung ist überraschend einfach!

  1. Legen Sie einen Datenbank-Link an, der auf das gleiche Schema in der gleichen Datenbank verweist: Wenn das Datenbankschema also FSMASTER heißt, legen Sie den Link wie folgt an (der Datenbank-Link soll LOOPBACK heißen; Sie können den Namen allerdings frei wählen).
    create database link loopback
    connect to FSMASTER identified by {passwort}
    using '{host}:{listener-port}/{service-name}
    
  2. Legen Sie eine Prozedur an, die ein beliebiges SQL-Kommando entgegennimmt und ausführt. Wenn Sie die Rollen, die Ihrem Schema zugewiesen sind, benötigen, ist es wichtig, dass Sie die Prozedur mit AUTHID CURRENT_USER anlegen. Das folgende Beispiel ist bewusst einfach gehalten; natürlich können Sie auch eine Funktion draus machen, die evtl. erhaltene Rückgabewerte entgegennimmt und wiederum an den APEX-Prozeß weiterreicht.
    Auf die Gefahren einer SQL Injection soll hier bewußt nicht eingegangen werden - wenn Sie dies in Applikationen verwenden, sollten Sie sicherstellen, dass durch das EXECUTE IMMEDIATE keine SQL-Injection-Löcher entstehen. Zum Thema gibt es einen eigenen Community-Tipp.
    create or replace procedure exec(
      p_cmd in varchar2
    ) authid current_user is
    begin
      execute immediate p_cmd;
    end;
    

Und das war's auch schon. Wenn Sie nun für die Ausführung eines PL/SQL-Prozesses aktivierte Rollen benötigen, führen Sie den Code einfach über diese Prozedur aus.

begin
 :
 exec@loopback('insert into table_roles_needed values (....)';
 :
end;

Mit diesem Konstrukt wird die Prozedur exec nicht "lokal", sondern "entfernt" über den Database Link aufgerufen - was die gleiche Prozedur ist. Allerdings wird eine andere Datenbanksitzung verwendet - diese ist keine APEX-Sitzung mehr und mit dem "richtigen" Datenbankuser aufgebaut. Das zeigt auch der erneute Test mit der View aus Abbildung 2. Fragt man diese View über den Datenbanklink ab, so erkennt man, dass diese Datenbanksitzung nun tatsächlich mit dem Workspace-Schema verbunden ist.

SESSION_USER vs. CURRENT_USER in einer Sitzung über einen Datenbanklink

Abbildung 3: SESSION_USER vs. CURRENT_USER in einer Sitzung über einen Datenbanklink

Wenn PL/SQL-Prozedur exec (oder eine andere) nun noch mit AUTHID CURRENT_USER angelegt wurde, so bleiben die Rollen, die dem Workspace-User zugewiesen wurden, aktiv. Auf diesem Weg ist es also durchaus möglich, einem Datenbankuser zugewiesene Rollen in APEX-Anwendungen nutzen zu können.

Einen solchen Datenbank-Link können Sie für Berichte nutzen. Auch hierzu ein Beispiel: Geben Sie dem Workspace-User auf einem Testsystem mal die Rolle SELECT_ANY_DICTIONARY; diese beinhaltet - wie der Name schon sagt - SELECT-Privilegien auf alle Tabellen des Data Dictionary (auch auf solche, auf die sonst nur der DBA Zugriff hat). Eine dieser Dictionary-Views ist DBA_TABLES; darin stehen Informationen über alle Tabellen in der Datenbank.

Loggen Sie sich danach in Ihrem APEX Workspace ein und versuchen Sie, in einer Applikation einen Bericht auf die View DBA_TABLES zu erzeugen. Sie werden feststellen, dass dies so einfach nicht geht (Abbildung 4).

Ein Berichts SQL, welches Rollen benötigt, funktioniert in APEX erstmal nicht

Abbildung 4: Ein Berichts SQL, welches Rollen benötigt, funktioniert in APEX erstmal nicht

Wenn Sie jedoch das Berichts-SQL umändern in ...

select * from dba_tables@loopback

... dann können Sie den Bericht erzeugen und ganz normal verwenden. Einige Restriktionen sollen aber nicht unerwähnt bleiben. So gibt es Beschränkungen, die Datentypen CLOB, BLOB, XMLTYPE oder benutzerdefinierte Datentypen über einen Datenbanklink zu verwenden. Wenn also ein BLOB an die Prozedur übergeben werden soll, funktioniert dies über den Datenbanklink nicht so einfach. Da der Link jedoch ins gleiche Schema auf der gleichen Datenbank zeigt, lässt sich dies einfach umgehen, indem man den BLOB oder CLOB in eine Tabelle zwischenspeichert.

Alles in allem sind die Datenbank-Links eine gute Möglichkeit, PL/SQL-Code oder SQL-Anweisungen in APEX so auszuführen wie in SQL*Plus oder dem SQL Developer. Mit dem Datenbank-Link bekommt man eine Datenbanksession, die effektiv mit dem Workspace-Schema arbeitet und nicht mit APEX_PUBLIC_USER oder ANONYMOUS. Außerdem können, wenn die verwendeten PL/SQL-Prozeduren mit AUTHID CURRENT_USER angelegt werden, evtl. vorhandene Rollenkonzepte weitergenutzt werden.

Zurück zur Community-Seite