Viele APEX Anwendungen benötigen zur korrekten Ausführung User-spezifische nicht veränderbare Variablen. Auf diese Variablen bezieht man sich, um sicherheitsrelevante Abfragen für den angemeldeten User auszuführen.
Hier eine kurze Liste an möglichen Anwendungsfällen:
- User ID
- User basierte Parameter
(Land / Region / Ort / Niederlassung / Firmen- und Anwendungsspezifische Hintergründe)
- Rechte und Rollen
- Berechtigungen auf Zeilenebene
- Schreib- und Leserechte
Diese Variablen sollten je nach Inhalt und Verwendung auf eine der folgenden Arten gespeichert werden:
- APEX Application Items (Mit Protection Level: Restricted - May not be set from browser)
- APEX Collections (Eine Zeile)
- Eigene User- und Session spezifische Tabelle
Hinweis:
Die genannten Inhalte sollten nicht in Page-Items abgespeichert werden, auf diese Sie sich später beziehen.
Beispiel: P1_USER_ID
Die Vorteile ergeben sich wie folgt:
Application Items lassen sich leicht auslesen (Debugging) und verarbeiten (da diese immer pro Session gespeichert werden).
Collections und Custom-Tabellen haben erhebliche Performancevorteile bei Verwendung innerhalb von Datenbank-Views. Außerdem können mehr als 4000 Zeichen (CLOB) abgespeichert werden.
Beim Debugging und entwickeln (beispielsweise Views) sind eigene Tabellen (Imho) einfacher zu verarbeiten, dafür werden Collections automatisch pro Session angelegt und wieder gelöscht.
Was ist nun zu empfehlen?
Wenn die Größe einer Anwendung nicht feststeht oder es sich generell um eine kleine bis mittelgroße Anwendung handelt (10-60 Seiten), dann sollte man mit Application Items starten und im späteren Verlauf die Technik bei Notwendigkeit umstellen.
Wenn von vornherein klar ist, dass die Anwendung mehr als 100 Seiten groß wird und viele Nutzer mit vielen unterschiedlichen Attributen und Berechtigungen arbeiten, dann sind Collections oder eigene Tabellen zu bevorzugen.
Wie kann ein solcher Prozess aussehen?
Prinzipiell sollte ein solcher Prozess als "Application Process" angelegt sein. Außerdem sollte die komplette Logik in eine Package Prozedur ausgelagert werden und als Übergabeparameter den :APP_USER beinhalten.
Aufruf-Beispiel:
APP_PACKAGE.START_APPLICATION (P_APP_USER => UPPER(:APP_USER));
Die Ausführung sollte dann nur einmal nach der Anmeldung geschehen:
Process Point: On New Instance (new session)
Info:
Hierbei hatte ich das ein oder andere Mal Probleme bei der korrekten Ausführung.
Daher ist alternativ auch eine PL/SQL Prüfung möglich:
:APP_USER <> 'nobody' and :AI_USER_ID IS NULL
Hinweis:
Achten Sie innerhalb ihrer Package Prozedur darauf, dass Sie Fehler in eine entsprechende Fehlertabelle wegschreiben.
Code Beispiel zum setzen der User ID:
-- ID auslesen
SELECT ID
INTO V_USER_ID
FROM MY_USER_TABELLE
WHERE UPPER(USER_NAME) = P_APP_USER;
-- Item setzen
APEX_UTIL.SET_SESSION_STATE (
P_NAME => 'AI_USER_ID',
P_VALUE => V_USER_ID
);
Komplettes Beispiel:
Zentraler - Application Process
Package Prozedur:
Basierend auf einem alten Package-Beispiel, könnte der Code wie folgt aussehen:
Hier eine kurze Liste an möglichen Anwendungsfällen:
- User ID
- User basierte Parameter
(Land / Region / Ort / Niederlassung / Firmen- und Anwendungsspezifische Hintergründe)
- Rechte und Rollen
- Berechtigungen auf Zeilenebene
- Schreib- und Leserechte
Diese Variablen sollten je nach Inhalt und Verwendung auf eine der folgenden Arten gespeichert werden:
- APEX Application Items (Mit Protection Level: Restricted - May not be set from browser)
- APEX Collections (Eine Zeile)
- Eigene User- und Session spezifische Tabelle
Hinweis:
Die genannten Inhalte sollten nicht in Page-Items abgespeichert werden, auf diese Sie sich später beziehen.
Beispiel: P1_USER_ID
Die Vorteile ergeben sich wie folgt:
Application Items lassen sich leicht auslesen (Debugging) und verarbeiten (da diese immer pro Session gespeichert werden).
Collections und Custom-Tabellen haben erhebliche Performancevorteile bei Verwendung innerhalb von Datenbank-Views. Außerdem können mehr als 4000 Zeichen (CLOB) abgespeichert werden.
Beim Debugging und entwickeln (beispielsweise Views) sind eigene Tabellen (Imho) einfacher zu verarbeiten, dafür werden Collections automatisch pro Session angelegt und wieder gelöscht.
Was ist nun zu empfehlen?
Wenn die Größe einer Anwendung nicht feststeht oder es sich generell um eine kleine bis mittelgroße Anwendung handelt (10-60 Seiten), dann sollte man mit Application Items starten und im späteren Verlauf die Technik bei Notwendigkeit umstellen.
Wenn von vornherein klar ist, dass die Anwendung mehr als 100 Seiten groß wird und viele Nutzer mit vielen unterschiedlichen Attributen und Berechtigungen arbeiten, dann sind Collections oder eigene Tabellen zu bevorzugen.
Wie kann ein solcher Prozess aussehen?
Prinzipiell sollte ein solcher Prozess als "Application Process" angelegt sein. Außerdem sollte die komplette Logik in eine Package Prozedur ausgelagert werden und als Übergabeparameter den :APP_USER beinhalten.
Aufruf-Beispiel:
APP_PACKAGE.START_APPLICATION (P_APP_USER => UPPER(:APP_USER));
Die Ausführung sollte dann nur einmal nach der Anmeldung geschehen:
Process Point: On New Instance (new session)
Info:
Hierbei hatte ich das ein oder andere Mal Probleme bei der korrekten Ausführung.
Daher ist alternativ auch eine PL/SQL Prüfung möglich:
:APP_USER <> 'nobody' and :AI_USER_ID IS NULL
Hinweis:
Achten Sie innerhalb ihrer Package Prozedur darauf, dass Sie Fehler in eine entsprechende Fehlertabelle wegschreiben.
Code Beispiel zum setzen der User ID:
-- ID auslesen
SELECT ID
INTO V_USER_ID
FROM MY_USER_TABELLE
WHERE UPPER(USER_NAME) = P_APP_USER;
-- Item setzen
APEX_UTIL.SET_SESSION_STATE (
P_NAME => 'AI_USER_ID',
P_VALUE => V_USER_ID
);
Komplettes Beispiel:
Zentraler - Application Process
Package Prozedur:
Basierend auf einem alten Package-Beispiel, könnte der Code wie folgt aussehen:
create or replace package body pkg_login_example as /* Package Variables */ gv_proc_name varchar2(100); gv_action varchar2(4000); gv_ora_error varchar2(4000); gv_custom_error varchar2(4000); gv_parameter varchar2(4000); -- Globale User-Variable direkt im Package, Übergabeparameter ist so nicht notwendig gv_user varchar2(20) := upper(nvl(v('APP_USER'),user)); /* Save errors */ /* -------------------------------------------------------- -- DDL for Table ERR_LOG -------------------------------------------------------- CREATE TABLE "ERR_LOG" ( "PROC_NAME" VARCHAR2(200), "ACTION" VARCHAR2(4000), "APP_ID" NUMBER, "APP_PAGE_ID" NUMBER, "APP_USER" VARCHAR2(20), "ORA_ERROR" VARCHAR2(4000), "CUSTOM_ERROR" VARCHAR2(4000), "PARAMETER" VARCHAR2(4000), "TIME_STAMP" DATE ) ; / */ procedure add_err is pragma autonomous_transaction; begin insert into err_log ( proc_name,action,app_id,app_page_id,app_user,ora_error,custom_error,parameter,time_stamp ) values ( gv_proc_name,gv_action,nvl(v('APP_ID'),0),nvl(v('APP_PAGE_ID'),0),nvl(nvl(v('APP_USER'),user),'Unknown'), gv_ora_error,gv_custom_error,gv_parameter,sysdate ); commit; end; /* ************************************************************************************************************************************** */ /* Anwendungsparameter zu setzen */ /* ************************************************************************************************************************************** */ procedure start_application is v_user_id my_user_table.id%type; v_user_role my_user_table.user_role%type; begin gv_proc_name := 'pkg_login_example.start_application'; gv_parameter := ''; gv_action := '1. Get USER_ID'; select id into v_user_id from my_user_table where upper(user_name) = gv_user; apex_util.set_session_state (p_name => 'AI_USER_ID',p_value => v_user_id); gv_action := '2. Get USER_ROLE'; select user_role into v_user_role from my_user_table where upper(user_name) = gv_user; apex_util.set_session_state (p_name => 'AI_USER_ROLE',p_value => v_user_role); gv_action := '3. Get REGION_ROLES or whatever'; -- ... commit; exception when others then gv_ora_error := sqlerrm; gv_custom_error := 'Internal Error. Action canceled.'; rollback; add_err; raise_application_error(-20001, gv_custom_error); end; end pkg_login_example;