Beneficios e implementación de Oracle Virtual Private Database (VPD)
Por Y V Ravi Kumar Oracle ACE director, Hela Khazri, Julio César Ayapán Oracle Associate,
Publicado en Diciembre 2019
Revisado por Francisco Riccio
Definición de Oracle Virtual Private Database:
Oracle Virtual Private Database (VPD) es una utilidad para reforzar la seguridad incorporada en la edición Enterprise de Oracle Database e introducida en la versión 8i. Como sabemos, en algunos casos el control de acceso discrecional (DAC) es insuficiente para cumplir con los requisitos de seguridad de la aplicación, ya que generalmente se utiliza para otorgar o restringir el acceso a nivel de objeto. DAC puede ser el origen de muchas amenazas, como asignar privilegios a objetos ajenos a las funciones de trabajo de un usuario, además, el usuario dueño de la aplicación puede ser objetivo de ataques o vulnerabilidades.
Con Virtual Private Database es posible controlar el acceso a los datos de manera granular a nivel de filas y columnas. Los accesos a los datos son aplicados directamente en las tablas, vistas o sinónimos a través del uso de políticas que se aplican de manera dinámica durante la ejecución de un SELECT, INSERT, UPDATE, DELETE y sentencias sobre índices.
Cuando un usuario accede a una tabla, vista o sinónimo que esta protegido por una política de Oracle VPD, la política añade de manera transparente y dinámica una clausula WHERE a la sentencia SQL. Esta cláusula dinámica es llamada Predicado y es generada por una función implementada por la política de seguridad.
Por ejemplo, un usuario ejecuta la siguiente consulta:
Select * from order_details;
La política de Oracle VPD agrega de manera dinámica la cláusula where a la sentencia ejecutada, de esta manera la clausula que se ejecuta será:
Select * from order_details where id_order=222;
Y así, el usuario podrá ver únicamente el detalle de la orden con ID=222.
Beneficios de Oracle Virtual Private Database:
Oracle PDV provee los siguientes beneficios:
- Las políticas de seguridad se basan en objetos de la base de datos en lugar de aplicaciones
- Facilita el mantenimiento de código
- Facilita la auditoria de las acciones de usuario
- Permite controlar cuando y bajo qué circunstancias la base de datos aplica el predicado
Implementación:
Cuando se añaden políticas a objetos, el comportamiento default de Oracle VPD es agregar y aplicar una nueva clausula WHERE a la sentencia SQL original al momento de ser ejecutada para restringir el acceso a nivel de objeto, independiente de la aplicación que lo ejecute y del usuario que este tratando de acceder a los datos en lugar de evaluar el rol y privilegios, esto ocurrirá siempre y cuando no se haya agregado una exención a esta regla.
A continuación, se muestra el proceso normal para la implementación de una política VPD:
Figura 1 – Implementación de una política VPD
Contexto de la aplicación: contexto de la aplicación que cuenta al menos con un atributo. Aunque su uso no es obligatorio dentro de una política VPD, es una efectiva técnica de diseño que a menudo se utiliza. Su función principal en una implementación de VPD es recuperar información sobre la sesión y determinar de acuerdo con esta, que grupo de políticas serán aplicadas para permitir o denegar acceso a ciertos datos. La inicialización del contexto de la aplicación se realiza mediante un paquete PL/SQL usado en su definición.
Los contextos de aplicación basados en sesiones de base de datos pueden ser inicializados de manera local, externa, o global. En modo global, los datos de sesión son recuperados por el Area Global de Usuario (User Global Area – UGA). En modo externo, los datos se pueden obtener desde una aplicación externa (OCI, JDBC), una cola de procesos, o a través de un objecto Database Link conectado a la base de datos.
- Función de política: es utilizada para retornar un predicado que será aplicado en la cláusula WHERE de la sentencia SQL.
- Política VPD: existen cinco tipos de políticas basadas en la frecuencia con que se evalúa una función de política:
DBMS_RLS.DYNAMIC es el default.
DMBS_RLS.STATIC
DBMS_RLS.SHARED_STATIC
DBMS_RLS.CONTEXT_SENSITIVE
DBMS_RLS.SHARED_CONTEXT_SENSITIVE
Como podemos ver, el comportamiento default de VPD es: cualquier política definida sobre una tabla o vista es aplicada para todas las sentencias SQL siempre que una aplicación la ejecute.
Así que, en caso de que muchas aplicaciones compartan la misma tabla o vista, es recomendado crear y utilizar grupos de políticas para determinar el predicado que debería ser retornado y que políticas deberían ser aplicadas. Si ya existen políticas definidas, se deben identificar qué políticas deberían estar vigentes cuando cada aplicación acceda a la tabla o vista. Cada objeto tiene un grupo de políticas predeterminado ya definido (SYS_DEFAULT), y las políticas definidas en este grupo serán siempre aplicadas para ese objeto particular. Un contexto de aplicación puede ayudar en este caso a determinar qué otro grupo de políticas se aplicarán también en ese momento.
Imaginemos que hay dos aplicaciones (A y B) que acceden a la tabla TEST. Sus políticas especificas son determinadas por dos grupos de políticas (GRP_A y GRP_B) y todas las demás políticas que debe aplicarse en cualquier caso están definidas en el grupo predeterminado (GRP_DEFAULT). Cuando la aplicación A accede los datos, las políticas que pertenecen a GRP_A y GRP_DEFAULT son aplicadas; y cuando la aplicación B accede los datos, las políticas que pertenecen a GRP_B y GRP_DEFAULT son aplicadas.
Figura 2 – Implementación los grupos de políticas VPD
1. Ejemplos: Paso a Paso
1.1 Contexto de aplicación basado en sesión:
En este ejemplo, aplicaremos un paquete PL/SQL para establecer el contexto de la aplicación. Crearemos un tipo de contexto inicializado localmente, utilizando un procedimiento que inicializara el valor para el contexto. El valor será generado utilizando un trigger de inicio de sesión.
En primer lugar, creamos el usuario VPD y la tablas ‘personnel’, ‘service’, ‘job’, ‘department’ y ‘region’ en el esquema VPD:
SQL> Connect system/manager@TEST
Connected.
SQL> create user VPD IDENTIFIED BY VPD profile DEFAULT;
User created.
SQL> grant dba to VPD;
Grant succeeded.
SQL> connect VPD/VPD@TEST
Connected.
SQL> ---Schema-----
SQL> Create table personnel (
2 Pers_id VARCHAR2(10),
3 Pers_name VARCHAR2(40)not null,
4 Hire_date DATE,
5 Job_id NUMBER(4) not null,
6 Serv_id VARCHAR2(10) not null,
7 Region_id NUMBER(4),
8 Salary VARCHAR2(20)
9 );
Table created.
SQL> Create table service (
2 Serv_id VARCHAR2(10),
3 Serv_name VARCHAR2(40),
4 Dept_id VARCHAR2(10) not null
5 );
Table created.
SQL> Create table job (
2 Job_id NUMBER(4),
3 Job_title VARCHAR2(40)
4 );
Table created.
SQL> Create table department (
2 Dept_id VARCHAR2(10) ,
3 Dept_name VARCHAR2(40)
4 );
Table created.
SQL> Create table region (
2 Region_id NUMBER(4),
3 Region_name VARCHAR2(40)
4 );
Table created.
A continuación, con el usuario VPD añadimos los siguientes constraints:
SQL> alter table PERSONNEL add constraint PK_PERSONNEL primary key (Pers_id);
Table altered.
SQL> alter table service add constraint PK_Service primary key (serv_id);
Table altered.
SQL> alter table job add constraint PK_JOB primary key (job_id);
Table altered.
SQL> alter table department add constraint PK_Departement primary key (Dept_id);
Table altered.
SQL> alter table region add constraint PK_Region primary key (region_id);
Table altered.
SQL> alter table PERSONNEL add constraint FK_Service foreign key (serv_id)
references Service (serv_id);
Table altered.
SQL> alter table PERSONNEL add constraint FK_Job foreign key (job_id)
references Job (job_id);
Table altered.
SQL> alter table PERSONNEL add constraint FK_Region foreign key (Region_id)
references Region (Region_id);
Table altered.
Ejecutamos los siguientes insert sobre las talas creadas:
SQL> insert into SERVICE values ('00110','It_serv','0100');
1 row created.
SQL> insert into SERVICE values ('00210','Account_Serv','0200');
1 row created.
SQL> insert into SERVICE values ('00220','Finance_Serv','0200');
1 row created.
SQL> insert into SERVICE values ('00310','Rs_Manag','0300');
1 row created.
SQL> insert into department values ('0100','It_Dep');
1 row created.
SQL> insert into department values ('0200','Account_Fin_Dep');
1 row created.
SQL> insert into department values ('0300','RUM_Dep');
1 row created.
SQL> insert into job values ('500','PDG');
1 row created.
SQL> insert into job values ('400','Director_Serv');
1 row created.
SQL> insert into job values ('350','Engineer');
1 row created.
SQL> insert into job values ('330','Pharmacist');
1 row created.
SQL> insert into job values ('320','Mastery');
1 row created.
SQL> insert into job values ('210','controller');
1 row created.
SQL> insert into job values ('110','worker');
1 row created.
SQL> insert into Personnel values ('002110', 'aaaa',null,'400','00210',null,null);
1 row created.
SQL> insert into Personnel values ('002120', 'bbbb',null,'320','00210',null,null);
1 row created.
SQL> insert into Personnel values ('002130', 'cccc',null,'210','00210',null,null);
1 row created.
SQL> insert into Personnel values ('002210', 'dddd',null,'400','00220',null,null);
1 row created.
SQL> insert into Personnel values ('002220', 'eeee',null,'320','00220',null,null);
1 row created.
SQL> insert into Personnel values ('002230', 'ffff',null,'210','00220',null,null);
1 row created.
SQL> insert into Personnel values ('001110', 'gggg',null,'400','00110',null,null);
1 row created.
SQL> insert into Personnel values ('001120', 'eeee',null,'350','00110',null,null);
1 row created.
SQL> insert into Personnel values ('001130', 'llll',null,'320','00110',null,null);
1 row created.
SQL> insert into Personnel values ('003110', 'gggg',null,'400','00310',null,null);
1 row created.
SQL> insert into Personnel values ('003120', 'eeee',null,'210','00310',null,null);
1 row created.
SQL> insert into Personnel values ('003130', 'llll',null,'110','00310',null,null);
1 row created.
En el siguiente paso, dentro del esquema VPD, creamos una vista llamada VIEW_DATA_1 basada en las tablas personnel, service, job y department.
SQL> CREATE VIEW VIEW_DATA_1 AS
SELECT Pers_id, Pers_name, Hire_date,
Job_title, Serv_name, Dept_name, Salary
FROM personnel a, job b, service c, department d
WHERE a.Job_id = b.Job_id
AND a.Serv_id = c.Serv_id
AND c.Dept_id = d.Dept_id;
View created.
A continuación, como usuario system creamos los usuarios vpdhrm, vpdacnt, vpdit y vpdfin y les otorgamos privilegios de ‘Select’ sobre la vista VIEW_DATA_1 creada en el paso anterior.
Con el usuario system creamos el paquete ‘set_service_context_pkg’ para crear el contexto ‘hr_serv_context’:
SQL> ----------Contexto----------------
SQL> create or replace context HR_SERV_CONTEXT using set_service_context_pkg;
Context created.
SQL> create or replace PACKAGE set_service_context_pkg IS
2 PROCEDURE set_service_id;
3 END;
4 /
Package created.
SQL> CREATE OR REPLACE PACKAGE BODY set_service_context_pkg IS
2 PROCEDURE set_service_id IS
3 v_serv_id VARCHAR2(10);
4 BEGIN
5 IF (SYS_CONTEXT('USERENV', 'SESSION_USER')='VPDHRM')
6 THEN
7 v_serv_id:='00310';
8 ELSIF (SYS_CONTEXT('USERENV', 'SESSION_USER')='VPDACNT')
9 THEN
10 v_serv_id:='00210';
11 ELSIF (SYS_CONTEXT('USERENV', 'SESSION_USER')='VPDFIN')
12 THEN
13 v_serv_id:='00220';
14 ELSIF (SYS_CONTEXT('USERENV', 'SESSION_USER')='VPDIT')
15 THEN
16 v_serv_id:='00110';
17 END IF;
18 DBMS_SESSION.SET_CONTEXT('HR_SERV_CONTEXT','serv_id',v_serv_id);
19 END set_service_id;
20 END set_service_context_pkg;
21 /
Package body created.
El siguiente paso será crear un trigger para establecer el valor de serv_id en el contexto ‘hr_serv_context’ cada vez que el usuario inicie sesión:
SQL> --------------------Trigger--------------------------------
SQL> CREATE OR REPLACE TRIGGER set_service_context_trg
2 AFTER LOGON ON DATABASE
3 BEGIN
4 set_service_context_pkg.set_service_id ;
5 END;
6 /
Trigger created.
A continuación, nos conectaremos con los usuarios vpdhrm, vpdacnt, vpdit y vpdfin y ejecutaremos una sentencia SELECT desde la vista VIEW_DATA_1. Los valores serán obtenidos basados en las condiciones dinámicas establecidas por SYS_CONTEXT('HR_SERV_CONTEXT','serv_id'):
SQL> ----------PRUEBA-----------------
SQL> CONNECT vpdhrm/hrm@test
Connected.
SQL> select (SYS_CONTEXT('HR_SERV_CONTEXT','serv_id')) as Service_id from dual;
SERVICE_ID
-----------------------------------------------------------------------
00310
SQL> select t.Pers_id,t.serv_id,t.Serv_name from VPD.VIEW_DATA_1 t where Serv_id =
SYS_CONTEXT('HR_SERV_CONTEXT','serv_id') ;
PERS_ID SERV_ID SERV_NAME
---------- ---------- ----------------------------------------
003110 00310 Rs_Manag
003120 00310 Rs_Manag
003130 00310 Rs_Manag
SQL> CONNECT vpdacnt/acnt@test
Connected.
SQL> select (SYS_CONTEXT('HR_SERV_CONTEXT','serv_id')) as Service_id from dual;
SERVICE_ID
--------------------------------------------------------------------------
00210
SQL> select t.Pers_id,t.serv_id,t.Serv_name from VPD.VIEW_DATA_1 t where Serv_id =
SYS_CONTEXT('HR_SERV_CONTEXT','serv_id');
PERS_ID SERV_ID SERV_NAME
---------- ---------- ----------------------------------------
002110 00210 Account_Serv
002120 00210 Account_Serv
002130 00210 Account_Serv
SQL> CONNECT vpdit/it@test
Connected.
SQL> select (SYS_CONTEXT('HR_SERV_CONTEXT','serv_id')) as Service_id from dual;
SERVICE_ID
--------------------------------------------------------------------------
00110
SQL> select t.Pers_id,t.serv_id,t.Serv_name from VPD.VIEW_DATA_1 t where Serv_id =
SYS_CONTEXT('HR_SERV_CONTEXT','serv_id');
PERS_ID SERV_ID SERV_NAME
---------- ---------- ----------------------------------------
001110 00110 It_serv
001120 00110 It_serv
001130 00110 It_serv
SQL> CONNECT vpdfin/fin@test
Connected.
SQL> select (SYS_CONTEXT('HR_SERV_CONTEXT','serv_id')) as Service_id from dual;
SERVICE_ID
--------------------------------------------------------------------------
00220
SQL> select t.Pers_id,t.serv_id,t.Serv_name from VPD.VIEW_DATA_1 t where Serv_id =
SYS_CONTEXT('HR_SERV_CONTEXT','serv_id');
PERS_ID SERV_ID SERV_NAME
---------- ---------- ----------------------------------------
002210 00220 Finance_Serv
002220 00220 Finance_Serv
002230 00220 Finance_Serv
Como podemos observar, el valor del contexto del usuario es establecido por el paquete y mostrado cada vez que se acceden a los datos de la vista. Con estos valores podemos crear políticas de acceso utilizando VPD.
1.2 Implementando políticas de acceso a nivel de fila:
1.2.1 Sentencia SELECT:
Como usuario VPD creamos una tabla TABLE_DATA_1 y le otorgaremos privilegios de SELECT sobre está a los usuarios vpdhrm, vpdacnt, vpdit y vpdfin:
SQL> CREATE Table Table_DATA_1 AS
2 SELECT p.Pers_id,
3 p.Pers_name,
4 j.Job_title,
5 p.serv_id,
6 s.Serv_name,
7 d.Dept_name,
8 p.region_id,
9 p.salary
10 FROM vpd.personnel p
11 JOIN vpd.service s
12 ON p.serv_id=s.serv_id
13 JOIN vpd.job j
14 ON p.job_id=j.job_id
15 JOIN vpd.department d
16 ON s.dept_id=d.dept_id;
Table created.
SQL> grant select on vpd.Table_DATA_1 to vpdhrm, vpdacnt, vpdit, vpdfin;
Grant succeeded.
Como usuario system crearemos la función de política VPD ‘Serv_ID_Func’, este será nuestro predicado dinámico:
SQL> CREATE OR REPLACE
2 FUNCTION serv_id_func
3 (
4 schema_v IN VARCHAR2,
5 tbl_v VARCHAR2)
6 RETURN VARCHAR2
7 IS
8 ret_val VARCHAR2(200);
9 BEGIN
10 ret_val := 'serv_id =sys_context(''hr_serv_context'', ''serv_id'')';
11 RETURN ret_val;
12 END;
13 /
Function created.
Ahora, definiremos una política llamada ‘SELECT_SERVICE_POLICY’, definida sobre el objeto TABLE_DATA_1 desde el esquema VPD y aplicable solo para las sentencias SELECT:
SQL> BEGIN
2 DBMS_RLS.ADD_POLICY (object_schema => 'VPD'
3 , object_name =>'TABLE_DATA_1'
4 , policy_name => 'SELECT_Service_POLICY'
5 , function_schema => 'SYSTEM'
6 , policy_function =>'serv_id_func'
7 , statement_types => 'select');
8 END;
9 /
PL/SQL procedure successfully completed.
Nos conectamos con cada uno de los usuarios vpdhrm, vpdacnt, vpdit y vpdfin y ejecutamos una sentencia SELECT para verificar si la política SELECT_SERVICE_POLICY es correctamente aplicada:
SQL> ----------PRUEBA-----------------------
SQL> CONNECT vpdhrm/hrm@test
Connected.
SQL> select (SYS_CONTEXT('HR_SERV_CONTEXT','serv_id')) as Service_id from dual;
SERVICE_ID
--------------------------------------------------------------------
00310
SQL> select t.Pers_id,t.serv_id,t.Serv_name,t.Dept_name from vpd.Table_DATA_1 t;
PERS_ID SERV_ID SERV_NAME DEPT_NAME
=================================================
003110 00310 Rs_Manag RUM_Dep
003120 00310 Rs_Manag RUM_Dep
003130 00310 Rs_Manag RUM_Dep
SQL> CONNECT vpdacnt/acnt@test
Connected.
SQL> select (SYS_CONTEXT('HR_SERV_CONTEXT','serv_id')) as Service_id from dual;
SERVICE_ID
-------------------------------------------------------------------
00210
SQL> select t.Pers_id,t.serv_id,t.Serv_name,t.Dept_name from vpd.Table_DATA_1 t;
PERS_ID SERV_ID SERV_NAME DEPT_NAME
=================================================
002110 00210 Account_Serv Account_Fin_Dep
002120 00210 Account_Serv Account_Fin_Dep
002130 00210 Account_Serv Account_Fin_Dep
SQL> CONNECT vpdit/it@test
Connected.
SQL> select (SYS_CONTEXT('HR_SERV_CONTEXT','serv_id')) as Service_id from dual;
SERVICE_ID
-------------------------------------------------------------------
00110
SQL> select t.Pers_id,t.serv_id,t.Serv_name,t.Dept_name from vpd.Table_DATA_1 t;
PERS_ID SERV_ID SERV_NAME DEPT_NAME
============================================
001110 00110 It_serv It_Dep
001120 00110 It_serv It_Dep
001130 00110 It_serv It_Dep
SQL> CONNECT vpdfin/fin@test
Connected.
SQL> select (SYS_CONTEXT('HR_SERV_CONTEXT','serv_id')) as Service_id from dual;
SERVICE_ID
-------------------------------------------------------------------
00220
SQL> select t.Pers_id,t.serv_id,t.Serv_name,t.Dept_name from vpd.Table_DATA_1 t;
PERS_ID SERV_ID SERV_NAME DEPT_NAME
=============================================================
002210 00220 Finance_Serv Account_Fin_Dep
002220 00220 Finance_Serv Account_Fin_Dep
002230 00220 Finance_Serv Account_Fin_Dep
Como usuario system creamos otro usuario no_vpd y le otorgaremos privilegios de SELECT sobre la tabla VPD.TABLE_DATA_1.
Si tratamos de ejecutar una sentencia select sobre la tabla VPD.TABLE_DATA_1 conectados como usuario no_vpd, ninguna fila será retornada.
SQL> Connect system/manager
Connected.
SQL> create user no_vpd IDENTIFIED BY no_vpd profile DEFAULT;
User created.
SQL> Grant create session to no_vpd;
Grant succeeded.
SQL> Grant select on vpd.Table_DATA_1 to no_vpd;
Grant succeeded.
SQL> CONNECT no_vpd /no_vpd@test
Connected.
SQL> select (SYS_CONTEXT('HR_SERV_CONTEXT','serv_id')) as Service_id from dual;
SERVICE_ID
-------------------------------------------------------------------
SQL> select t.Pers_id, t.Pers_name, t.Job_title, t.serv_id, t.Serv_name,
t.Dept_name from vpd.Table_DATA_1 t ;
no rows selected
1.2.2 Sentencia INSERT:
En los siguientes pasos crearemos una tabla vacía y una política VPD aplicable a sentencias INSERT.
Como usuario VPD creamos una tabla vacía llamada TABLE_DATA_2 basada en la estructura de la tabla TABLE_DATA_1:
SQL> Connect VPD/VPD
Connected.
SQL> CREATE Table vpd.Table_DATA_2 AS SELECT * from vpd.Table_DATA_1;
Table created.
Como usuario system creamos la función de política VPD ‘Serv_ID_add_Func’ que permitirá inserciones solo para las filas correspondientes al servicio ‘IT’ (00110):
SQL> Connect system/manager
Connected.
SQL> CREATE OR REPLACE
2 FUNCTION Serv_ID_add_Func
3 (
4 schema_v IN VARCHAR2,
5 tbl_v VARCHAR2)
6 RETURN VARCHAR2
7 IS
8 ret_val VARCHAR2(200);
9 BEGIN
10 ret_val := 'serv_id = 00110';
11 RETURN ret_val;
12 END;
13 /
Function created.
A continuación, crearemos una política de inserción llamada ‘INSERT_IT_POLICY’. Para aplicar la política VPD en una instrucción INSERT, debemos habilitar el parámetro update_check.
SQL> BEGIN
2 DBMS_RLS.ADD_POLICY (object_schema =>'VPD', object_name=>
'Table_DATA_2',policy_name =>'INSERT_IT_POLICY'
3 , function_schema => 'SYSTEM', policy_function =>'Serv_ID_add_Func',
statement_types => 'insert',update_check=>true);
4 END;
5 /
PL/SQL procedure successfully completed.
Para verificar nuestra política VPD:
Primero, trataremos de insertar algunos valores que no cumplen con INSERT_IT_POLICY. Después, ejecutamos sentencias insert que si cumplirán con la política.
SQL> CONNECT VPD/VPD@test
Connected.
SQL> --------violación de la política-------------
SQL> insert into vpd.Table_DATA_2 values ('001110', 'hela',null, '00220',
'Finance_Serv','0200',10,1500);
insert into vpd.Table_DATA_2 values ('001110', 'hela',null, '00220',
'Finance_Serv','0200',10,1500)
*
ERROR at line 1:
ORA-28115: policy with check option violation
SQL> insert into vpd.Table_DATA_2 values ('001110', 'hela1',null, '00110',
'It_serv','0100',10,1500);
1 row created.
SQL> insert into vpd.Table_DATA_2 values ('001110', 'hela2',null, '00110',
'It_serv','0100',10,3000);
1 row created.
1.2.3 Sentencia DELETE:
En los siguientes pasos crearemos una política de función y una política VPD para lidiar con una sentencia DELETE.
Conectado como usuario system creamos una función de política VPD llamada ‘Salary_delete_Func’ que será aplicada a salarios mayores de 2000 y al servicio ‘IT service’:
SQL> Connect system/manager
Connected.
SQL> CREATE OR REPLACE
2 FUNCTION Salary_delete_Func
3 (
4 schema_v IN VARCHAR2,
5 tbl_v VARCHAR2)
6 RETURN VARCHAR2
7 IS
8 ret_val VARCHAR2(200);
9 BEGIN
10 ret_val := 'salary > 2000 and serv_id=''00110'' ';
11 RETURN ret_val;
12 END;
13 /
Function created.
A continuación, creamos una política sobre la tabla TABLE_DATA_2 utilizando la función ‘Salary_delete_Func’ para sentencias DELETE, llamada ‘Salary_delete_policy’:
SQL> BEGIN
2 DBMS_RLS.ADD_POLICY (object_schema => 'VPD'
3 , object_name => 'Table_DATA_2'
4 , policy_name =>'SALARY_Delete_POLICY'
5 , function_schema => 'SYSTEM'
6 , policy_function => 'Salary_delete_Func '
7 , statement_types => 'delete');
8 END;
9 /
PL/SQL procedure successfully completed.
Finalmente, verificamos la tabla Table_Data_2 y ejecutamos una sentencia DELETE:
SQL> select count(*) from vpd.Table_DATA_2;
COUNT(*)
----------
2
SQL> connect VPD/VPD
Connected.
SQL> delete from vpd.table_DATA_2;
1 row deleted.
SQL> select count(*) from vpd.Table_DATA_2;
COUNT(*)
--------------
1
SQL> select * from vpd.Table_DATA_2;
PERS_ID PERS_NAME
---------- ----------------------------------------
JOB_TITLE SERV_ID
---------------------------------------- ----------
SERV_NAME
----------------------------------------
DEPT_NAME REGION_ID SALARY
---------------------------------------- ---------- ---------------
001110 hela1
00110
It_serv
0100 10 1500
1.3 Implementando políticas de acceso a nivel de columna:
Cuando implementamos restricciones a nivel de fila, la política es aplicada sin importar las columnas seleccionadas. Sin embargo, con restricciones a nivel de columna, la política de acceso se aplica solo cuando las columnas protegidas por la política son incluidas en la sentencia DML. Además, es posible enmascarar los datos de la columna en cualquier momento. Por lo tanto, las filas que no se ajustan a los criterios definidos tienen sus valores de columna ocultos por la política y se muestran como nulos.
Como usuario VPD creamos una tabla Table_DATA_3 basada en la tabla Table_DATA_2 en el esquema VPD y otorgamos privilegios de select sobre esta a los usuarios vpdhrm, vpdacnt, vpdit y vpdfin:
SQL> Connect VPD/VPD
Connected.
SQL> CREATE Table vpd.Table_DATA_3 AS SELECT * from vpd.Table_DATA_1;
Table created.
SQL> Grant select on vpd.Table_DATA_3 to vpdhrm, vpdacnt, vpdit, vpdfin;
Grant succeeded.
SQL> insert into vpd.Table_DATA_3 values ('001130', 'name1', 'Director_Serv',
'00110','It_serv', 'It_Dep',10, 3500);
1 row created.
SQL> insert into vpd.Table_DATA_3 values ('001140', 'name2', 'Engineer', '00110',
'It_serv', 'It_Dep',20,2500);
1 row created.
SQL> insert into vpd.Table_DATA_3 values ('001150', 'name3', 'worker', '00110',
'It_serv', 'It_Dep',30, 500);
1 row created.
SQL> insert into vpd.Table_DATA_3 values ('003130', 'name4', 'Director_Serv',
'00310','Rs_Manag', 'RUM_Dep',40, 3500);
1 row created.
SQL> insert into vpd.Table_DATA_3 values ('003140', 'name5', 'Mastery', '00310',
'Rs_Manag', 'RUM_Dep',50,1500);
1 row created.
SQL> insert into vpd.Table_DATA_3 values ('003150', 'name6', 'worker', '00310',
'Rs_Manag', 'RUM_Dep',60, 500);
1 row created.
Primero, conectado como usuario system creamos una función de política llamada ‘saljob_plc_func’:
SQL> Connect system/manager
Connected.
SQL> CREATE OR REPLACE
2 FUNCTION saljob_plc_func
3 (
4 schema_v IN VARCHAR2,
5 tbl_v VARCHAR2)
6 RETURN VARCHAR2
7 IS
8 ret_val VARCHAR2(200);
9 BEGIN
10 ret_val := 'serv_id = sys_context(''hr_serv_context'',''serv_id'')';
11 RETURN ret_val;
12 END;
13 /
Function created.
A continuación, definiremos una política a nivel de columna llamada ‘saljob_plc’:
SQL> BEGIN
2 DBMS_RLS.add_policy (object_schema => 'VPD'
3 , object_name => ' Table_DATA_3', policy_name => ' saljob_plc '
4 , policy_function => ' saljob_plc_func ', statement_types => 'SELECT'
5 , sec_relevant_cols => 'Job_title, SALARY');
6 END;
7 /
PL/SQL procedure successfully completed.
Para verificar el resultado, conectado como usuario vpdhrm y vpdit ejecutamos una sentencia SELECT, sin incluir las columnas protegidas Salary y Job_title:
SQL> Connect vpdhrm/hrm@test
Connected.
SQL> Select p.Pers_id, serv_id from vpd.Table_DATA_3 p;
PERS_ID SERV_ID
---------- ----------
001130 00110
001140 00110
001150 00110
003130 00310
003140 00310
003150 00310
6 rows selected.
SQL> Connect vpdit/it@test
Connected.
SQL> Select p.Pers_id, serv_id from vpd.Table_DATA_3 p;
PERS_ID SERV_ID
---------- ----------
001130 00110
001140 00110
001150 00110
003130 00310
003140 00310
003150 00310
6 rows selected.
La política no se aplica, ya que las columnas protegidas salary y job_title no se incluyen en la sentencia SELECT.
Ahora, ejecutamos la sentencia SELECT que incluya las columnas salary o job_title y también las dos al mismo tiempo:
SQL> Connect vpdhrm/hrm@test
Connected.
SQL> Select p.Pers_id, serv_id, p.job_title from vpd.Table_DATA_3 p;
PERS_ID SERV_ID JOB_TITLE
---------- ---------- ----------------------------------------
003130 00310 Director_Serv
003140 00310 Mastery
003150 00310 worker
SQL> Select p.Pers_id, serv_id, p.salary from vpd.Table_DATA_3 p;
PERS_ID SERV_ID SALARY
---------- ---------- --------------------
003130 00310 3500
003140 00310 1500
003150 00310 500
SQL> Select p.Pers_id, serv_id, p.job_title , p.salary from vpd.Table_DATA_3 p;
PERS_ID SERV_ID JOB_TITLE SALARY
----------------------------------------------------------------
003130 00310 Director_Serv 3500
003140 00310 Mastery 1500
003150 00310 worker 500
SQL> Connect vpdit/it@test
Connected.
SQL> Select p.Pers_id, serv_id, p.job_title from vpd.Table_DATA_3 p;
PERS_ID SERV_ID JOB_TITLE
---------- ---------- ----------------------------------------
001130 00110 Director_Serv
001140 00110 Engineer
001150 00110 worker
SQL> Select p.Pers_id, serv_id, p.salary from vpd.Table_DATA_3 p;
PERS_ID SERV_ID SALARY
---------- ---------- --------------------
001130 00110 3500
001140 00110 2500
001150 00110 500
SQL> Select p.Pers_id, serv_id, p.job_title , p.salary from vpd.Table_DATA_3 p;
PERS_ID SERV_ID JOB_TITLE SALARY
---------------------------------------------------------------
001130 00110 Director_Serv 3500
001140 00110 Engineer 2500
001150 00110 worker 500
En este paso, veremos como enmascarar columnas. Primero, como usuario system deshabilitaremos la ‘policita saljob_plc’:
SQL> connect system/manager@Test
Connected.
SQL> BEGIN
2 dbms_rls.enable_policy(policy_name=>'saljob_plc'
3 ,object_name=>' Table_DATA_3'
4 , object_schema=>'VPD',enable=>FALSE);
5 END;
6 /
PL/SQL procedure successfully completed.
A continuación, definiremos una nueva política llamada ‘saljob_plc_mask’ usando la opción data masking:
BEGIN
DBMS_RLS.add_policy (object_schema => 'VPD'
, object_name => ' Table_DATA_3', policy_name => 'saljob_plc_mask'
, policy_function => ' saljob_plc_func ', statement_types => 'SELECT'
, sec_relevant_cols => 'Job_title, SALARY'
, sec_relevant_cols_opt =>DBMS_RLS.all_rows);
END;
/
Conectado como vpdhrm y vpdit, ejecutamos una sentencia SELET que incluya las columnas salary y job_title:
SQL> Connect vpdhrm/hrm@test
Connected.
SQL> Select p.Pers_id,p.serv_id,p.job_title , p.salary from vpd.Table_DATA_3 p;
PERS_ID SERV_ID JOB_TITLE SALARY
----------------------------------------------------------------
001130 00110
001140 00110
001150 00110
003130 00310 Director_Serv 3500
003140 00310 Mastery 1500
003150 00310 worker 500
6 rows selected.
SQL> Connect vpdit/it@test
Connected.
SQL> Select p.Pers_id,p.serv_id,p.job_title , p.salary from vpd.Table_DATA_3 p;
PERS_ID SERV_ID JOB_TITLE SALARY
=============================================
001130 00110 Director_Serv 3500
001140 00110 Engineer 2500
001150 00110 worker 500
003130 00310
003140 00310
003150 00310
6 rows selected.
Como podemos ver, las columnas salary y job_title retornadas por la consulta de vpdhrm contiene valores solo para el nombre de servicio ‘hrm’ y para el resto de las filas el valor desplegado es null. Y además, para vpdit, las columnas salay y job_title tienen valores solo para el servicio ‘it’.
1.4 Implementando políticas grupales VPD:
La característica políticas grupales es usada para aplicar diferentes políticas VPD sobre el mismo objeto. En algunos casos las políticas grupales son usadas para asignar políticas a diferentes grupos y ejecutarlas dependiendo de ciertas condiciones. La activación de una política o de otra dependerá del contexto de una aplicación de acuerdo con ciertos parámetros declarados previamente.
A continuación, crearemos una tabla que contendrá tres diferentes categorías diferentes de departamentos. Otorgaremos privilegios de select a los usuarios vpdhrm, vpdacnt, vpdit y vpdfin. Para cada grupo de departamentos definiremos una política grupal. Estas políticas grupales aislarán la función de cada grupo en función de la membresía de cada uno. Cada usuario podrá ver su propio departamento determinado por un valor de contexto de aplicación.
Primero, nos conectamos como usuario VPD y creamos la tabla DEPT_CATEG. Otorgamos privilegios de SELECT los usuarios vpdhrm, vpdacnt, vpdit y vpdfin:
SQL> Connect VPD/VPD
Connected.
SQL> CREATE TABLE DEPT_CATEG
2 (
3 CAT1_ID NUMBER,
4 CAT1_Name VARCHAR2(100),
5 CAT2_ID NUMBER,
6 CAT2_Name VARCHAR2(100),
7 CAT3_ID NUMBER,
8 CAT3_Name VARCHAR2(100)
9 );
Table created.
SQL> grant select on vpd.DEPT_CATEG to vpdhrm, vpdacnt, vpdit, vpdfin ;
Grant succeeded.
A continuación, insertamos datos en la tabla DEPT_CATEG. Los datos serán usados por el contexto:
SQL> Insert into DEPT_CATEG values (101,'Admin_RH', 201,'Admin_Act_Fin', 301,'Admin_IT');
1 row created.
SQL> Insert into DEPT_CATEG values (102,'Control_RH', 202,' Control_Act_Fin',
302,' Control_IT');
1 row created.
SQL> Insert into DEPT_CATEG values (103,'Publ_Relat_RH', 203, 'Publ_Relat_Act_Fin',
303,'Publ_Relat_IT');
1 row created.
SQL> Insert into DEPT_CATEG values (104,'Executive_RH', 204,'Executive _Act_Fin',
304,'Executive_IT');
1 row created.
SQL> Insert into DEPT_CATEG values (105,'Recruit_RH', 205,'Recruit _Act_Fin',
305,'Recruit_IT');
1 row created.
SQL> Commit;
Commit complete.
Conectado como usuario system creamos el contexto dept_categ_context:
SQL> connect system/manager
Connected.
SQL> CREATE OR REPLACE CONTEXT dept_categ_context USING dept_categ_pkg;
Context created.
En este paso crearemos una política para cada categoría:
- Creamos la política grupal dept_categ1:
SQL> BEGIN
2 DBMS_RLS.CREATE_POLICY_GROUP(object_schema =>'VPD'
3 , object_name => 'DEPT_CATEG'
4 , policy_group =>'dept_categ1');
5 END;
6 /
PL/SQL procedure successfully completed.
- Creamos la política grupal dept_categ2:
SQL> BEGIN
2 DBMS_RLS.CREATE_POLICY_GROUP(object_schema =>'VPD'
3 , object_name =>'DEPT_CATEG'
4 , policy_group =>'dept_categ2');
5 END;
6 /
PL/SQL procedure successfully completed.
- Creamos la política grupal dept_categ3:
SQL> BEGIN
2 DBMS_RLS.CREATE_POLICY_GROUP(object_schema => 'VPD'
3 ,object_name => 'DEPT_CATEG'
4 ,policy_group =>'dept_categ3');
5 END;
6 /
PL/SQL procedure successfully completed.
En el siguiente paso, crearemos tres funciones de política que serán asignadas a cada política agrupada.
- Creamos la función de política para la categoría uno llamada ‘vpd_func_dept_cate1’:
SQL> CREATE OR REPLACE
2 FUNCTION vpd_func_dept_categ1
3 (
4 V_SCHEMA IN VARCHAR2,
5 V_TABLE IN VARCHAR2)
6 RETURN VARCHAR2
7 AS
8 PREDICATE VARCHAR2(8) DEFAULT NULL;
9 BEGIN
10 IF (SYS_CONTEXT('USERENV','SESSION_USER')) = 'VPDHRM' THEN
11 predicate := '1=2';
12 ELSE NULL;
13 END IF;
14 RETURN predicate;
15 END;
16 /
Function created.
- Creamos la función de política para la categoría dos, llamada ‘vpd_func_dept_categ2’:
SQL> CREATE OR REPLACE
2 FUNCTION vpd_func_dept_categ2
3 (
4 V_SCHEMA IN VARCHAR2,
5 V_TABLE IN VARCHAR2)
6 RETURN VARCHAR2
7 AS
8 PREDICATE VARCHAR2(8) DEFAULT NULL;
9 BEGIN
10 IF (SYS_CONTEXT('USERENV','SESSION_USER'))= 'VPDACNT' THEN
11 predicate := '1=2';
12 ELSE NULL;
13 END IF;
14 RETURN predicate;
15 END;
16 /
Function created.
- Creamos la función de política para la categoría tres, llamada ‘vpd_func_dept_categ3’:
QL> CREATE OR REPLACE
2 FUNCTION vpd_func_dept_categ3
3 (
4 V_SCHEMA IN VARCHAR2,
5 V_TABLE IN VARCHAR2)
6 RETURN VARCHAR2
7 AS
8 PREDICATE VARCHAR2(8) DEFAULT NULL;
9 BEGIN
10 IF (SYS_CONTEXT('USERENV','SESSION_USER')) = 'VPDIT' THEN
11 predicate := '1=2';
12 ELSE NULL;
13 END IF;
14 RETURN predicate;
15 END;
16 /
Function created.
En este paso, crearemos la política grupal para cada categoría de departamento.
- Creamos la política grupal llamada ‘vpd_func_dept_categ1_plc’ para la categoría uno:
SQL> BEGIN
2 DBMS_RLS.ADD_GROUPED_POLICY( object_schema => 'VPD'
3 , object_name =>'DEPT_CATEG'
4 , policy_group => 'dept_categ1'
5 , policy_name => 'vpd_func_dept_categ1_plc'
6 , policy_function =>'vpd_func_dept_categ1'
7 , statement_types => 'select'
8 , policy_type => DBMS_RLS.CONTEXT_SENSITIVE
9 , sec_relevant_cols=> 'CAT2_ID, CAT2_Name, CAT3_ID,CAT3_Name'
10 , sec_relevant_cols_opt => DBMS_RLS.ALL_ROWS);
11 END;
12 /
PL/SQL procedure successfully completed.
- Creamos la política grupal llamada ‘vpd_func_dept_categ2_plc’ para la categoría dos:
SQL> BEGIN
2 DBMS_RLS.ADD_GROUPED_POLICY( object_schema =>'VPD'
3 , object_name => 'DEPT_CATEG'
4 , policy_group => 'dept_categ2'
5 , policy_name => 'vpd_func_dept_categ2_plc'
6 , policy_function =>' vpd_func_dept_categ2'
7 , statement_types => 'select'
8 , policy_type => DBMS_RLS.CONTEXT_SENSITIVE
9 , sec_relevant_cols=> 'CAT1_ID, CAT1_Name,CAT3_ID,CAT3_Name'
10 , sec_relevant_cols_opt => DBMS_RLS.ALL_ROWS);
11 END;
12 /
PL/SQL procedure successfully completed.
- Creamos la política grupal llamada ‘vpd_func_dept_categ3_plc’ para la categoría tres:
SQL> BEGIN
2 DBMS_RLS.ADD_GROUPED_POLICY( object_schema =>'VPD'
3 , object_name => 'DEPT_CATEG'
4 , policy_group => 'dept_categ3'
5 , policy_name => 'vpd_func_dept_categ3_plc '
6 , policy_function =>' vpd_func_dept_categ3 '
7 , statement_types => 'select'
8 , policy_type => DBMS_RLS.CONTEXT_SENSITIVE
9 , sec_relevant_cols=> 'CAT1_ID, CAT1_Name,CAT2_ID,CAT2_Name'
10 , sec_relevant_cols_opt => DBMS_RLS.ALL_ROWS);
11 END;
12 /
PL/SQL procedure successfully completed.
A continuación, creamos el package y el package body ‘dept_categ_pkg’ con el contexto ‘dept_categ_context’:
SQL> CREATE OR REPLACE PACKAGE dept_categ_pkg
2 IS
3 PROCEDURE set_dept_categ_context
4 (
5 plc_grp VARCHAR2 DEFAULT NULL
6 );
7 END;
8 /
Package created.
SQL> CREATE OR REPLACE PACKAGE BODY dept_categ_pkg
2 AS
3 PROCEDURE set_dept_categ_context
4 (plc_grp VARCHAR2 DEFAULT NULL)
5 IS
6 BEGIN
7 CASE (SYS_CONTEXT('USERENV','SESSION_USER'))
8 WHEN 'VPDHRM'
9 THEN
10 DBMS_SESSION.SET_CONTEXT('dept_categ_context'
11 ,'plc_grp','dept_categ1');
12 WHEN 'VPDACNT'
13 THEN
14 DBMS_SESSION.SET_CONTEXT('dept_categ_context'
15 ,'plc_grp','dept_categ2');
16 WHEN 'VPDIT' THEN
17 DBMS_SESSION.SET_CONTEXT('dept_categ_context'
18 ,'plc_grp','dept_categ3');
19 ELSE
20 NULL;
21 END CASE;
22 EXCEPTION
23 WHEN NO_DATA_FOUND THEN
24 NULL;
25 END set_dept_categ_context;
26 END;
27 /
Package body created.
Ahora, asignaremos el contexto ‘dept_categ_context’ al contexto de aplicación DEPT_CATEG:
SQL> BEGIN
2 DBMS_RLS.ADD_POLICY_CONTEXT(object_schema =>'VPD'
3 , object_name =>'DEPT_CATEG'
4 , namespace =>'dept_categ_context'
5 ,attribute =>'plc_grp');
6 END;
7 /
PL/SQL procedure successfully completed.
En el siguiente paso, crearemos un nuevo trigger que se activara con el evento de inicio de sesión para activar el contexto de aplicación:
SQL> CREATE OR REPLACE TRIGGER set_dep_categ_context_trg AFTER LOGON ON DATABASE
2 BEGIN
3 dept_categ_pkg.set_dept_categ_context;
4 END;
5 /
Trigger created.
Finalmente, conectados como usuario vpdhrm, verificamos el valor plc_grp desde el contexto de la aplicación y ejecutamos una sentencia SELECT sobre DEPT_CATEG para verificar si la política agrupada se aplica:
SQL> Connect VPDHRM/hrm
Connected.
SQL> select CAT1_ID, CAT1_NAME, CAT2_ID, CAT2_NAME, CAT3_ID, CAT3_NAME from vpd.DEPT_CATEG;
CAT1_ID CAT1_NAME CAT2_ID C CAT3_ID C
==============================================
101 Admin_RH
102 Control_RH
103 Publ_Relat_RH
104 Executive_RH
105 Recruit_RH
Como podemos ver solamente los departamentos desde la categoría 1 son visibles para vpdrm.
Como podemos ver solamente los departamentos desde la categoría 1 son visibles para vpdrm.
Conectado como usuario vpdacnt, verificamos el valor de plc_grp desde el contexto de aplicación y ejecutamos una sentencia SELECT sobre DEPT_CATEG para verificar si la política agrupada se aplica:
SQL> Connect vpdacnt/acnt
Connected.
SQL> select CAT1_ID, CAT1_NAME, CAT2_ID, CAT2_NAME, CAT3_ID, CAT3_NAME from vpd.DEPT_CATEG;
CAT1_ID C CAT2_ID CAT2_NAME CAT3_ID C
--------------------------------------------------------------------
201 Admin_Act_Fin
202 Control_Act_Fin
203 Publ_Relat_Act_Fin
204 Executive _Act_Fin
205 Recruit _Act_Fin
Y finalmente conectado como usuario vpdit, verificamos el valor de plc_grp desde el contexto de aplicación y ejecutamos una sentencia SELECT sobre DEPT_CATEG para verificar si la política agrupada se aplica:
SQL> Connect vpdit/it
Connected.
SQL> select CAT1_ID, CAT1_NAME, CAT2_ID, CAT2_NAME, CAT3_ID, CAT3_NAME from vpd.DEPT_CATEG;
CAT1_ID C CAT2_ID C CAT3_ID CAT3_NAME
--------------------------------------------------------------------
301 Admin_IT
302 Control_IT
303 Publ_Relat_IT
304 Executive_IT
305 Recruit_IT
En políticas agrupadas, el contexto de la aplicación determina la política activa. En nuestro ejemplo, el procedimiento DBMS_RLS.add_policy_context fefine nuestro contexto de aplicación y sus atributos según el usuario que se conecte.
1.5 Otorgando exenciones a las políticas VPD
El comportamiento default de VPD es, una vez la política se haya declarado sobre un objeto, esta no puede ser omitida independientemente de los privilegios del usuario. Pero, en algunos casos, necesitamos acceder a todos los datos de un objeto que tiene una política aplicada.
A continuación, usando el mismo escenario, mostraremos como eximir al usuario VPDHRM de todas las políticas declaradas dentro del esquema VPD.
Primero, nos conectamos como el usuario vpdhrm y ejecutamos una sentencia SELECT sobre la tabla TABLE_DATA_1:
SQL> Connect Vpdhrm/hrm
Connected.
SQL> select t.pers_id, t.serv_id from vpd.Table_DATA_1 t ;
PERS_ID SERV_ID
---------- ----------
003110 00310
003120 00310
003130 00310
Luego, nos conectamos como usuario system y eximimos al usuario vpdhrm de cualquier política VPD:
SQL> Connect system/manager
Connected.
SQL> GRANT EXEMPT ACCESS POLICY TO vpdhrm;
Grant succeeded.
Nos conectamos como usuario vpdhrm nuevamente y ejecutamos una sentencia SELECT similar al primer paso:
SQL> Connect Vpdhrm/hrm
Connected.
SQL> select t.pers_id, t.serv_id from vpd.Table_DATA_1 t ;
PERS_ID SERV_ID
---------- ----------
002110 00210
002120 00210
002130 00210
002210 00220
002220 00220
002230 00220
001110 00110
001120 00110
001130 00110
003110 00310
003120 00310
003130 00310
12 rows selected.
Resumen:
Las políticas Oracle Virtual Private Database proporcionan los siguientes beneficios:
- Basan las políticas de seguridad en objetos de bases de datos en lugar de aplicaciones
- Permite controlar la manera en que las políticas son evaluadas mediante una alta granularidad
- Mediante la creación de políticas basadas en objetos el rendimiento no se ve impactado dentro de la base de datos.
YV Ravi Kumar un Oracle ACE Director y Oracle Certified Master (OCM) con 20 años de experiencia en Banca, Servicios financieros y seguros (BFSI) y ha ocupado varios puestos como Vise Presidente (IT), Senior Database Architect y Production DBA. Tambien es OCP en Oracle 8i, 9i, 10g, 11g y 12c y posee certificaciones en Golden Gate, RAC, Performance Tuning y Oracle Exadata. Ha publicado mas de 100 articulos para OTN-Español, OTN-Portugues, OTN-Ingles, TOAD World,
UKOUG, OTech Magazine y Redgate. Ha sido speaker en @OOW, @NYOUG, @OTN, @AIOUG, Samgam y @IOUG. Diseñó, arquitectó e implementó la Base de Datos del Sistema Bancario Central (CBS) para los Bancos Centrales de dos países: India y Mahe, Seychelles. Es cofundador de OraWorld (www.oraworld.com). Aprenda mas sobre su perfil en LaseSoft.
Hela Khazri es Oracle Certified Professional (OCP 12c R1) con 8 años de experiencia como Administrador de base de datos Senior y Project Manager utilizando metodologías scrum master. Hela posee una capacidad comprobada para mantener la alta disponibilidad usando Oracle RAC y ASM y para monitorear el rendimiento de la base de datos usando AWR, ASH, ADDM y la seguridad de las bases de datos mediante habilidades analíticas y orientadas a los detalles para resolver problemas de la base de datos y ayudar el día a día soporte diurno.
Julio Ayapán es Ingeniero en Ciencias y Sistemas, Administrador de base de datos Oracle con más de 4 años de experiencia en proyectos de infraestructura, Bases de Datos Oracle 10g, 11g y 12c sobre Linux y Solaris. Posee la certificación “Oracle Certified Professional 11g y 12c” y Oracle ACE Associate. Ha sido Conferencista en OTN Tour Latinoamericano 2016 y 2017 en Guatemala. Es parte de la junta directiva del Grupo de Usuarios Oracle de Guatemala (GOUG). Actualmente es Consultor de Bases de datos Oracle en eProseed Central America. Publica artículos frecuentemente en su blog http://oraclehomegt.blogspot.com. Twitter @jayapangt.
Este artículo ha sido revisado por el equipo de productos Oracle y se encuentra en cumplimiento de las normas y prácticas para el uso de los productos Oracle.