Revisado por Nestor Cayllahua
Definição do Oracle Virtual Private Database:
O Oracle Virtual Private Database (VPD) é uma feature do Enterprise Edition para reforçar a segurança, introduzida no Oracle Database 8i. Como você provavelmente sabe, alguns casos quando o discretionary access control (DAC) é insuficiente para suprir os requerimentos de segurança da aplicação desde que seja usado concessão ou restrição do acesso ao nível de objeto.
Além disso, o DAC pode ser a origem de muitas ameaças, como conceder mais privilégios de acesso a dados que o necessário para executar funções de trabalho. Além disso, as contas de aplicativo que acessam os dados do banco de dados pode ser conhecidas. Portanto, com o Banco de dados privado virtual, é possível ativar o controle de acesso refinado no nível da linha e da coluna. As restrições de acesso são aplicadas diretamente aos dados em si no nível de tabela, exibição ou sinônimo, por meio do uso de políticas aplicadas dinamicamente durante a execução das instruções SELECT, INSERT, UPDATE, DELETE e INDEX.
Quando um usuário acessa uma tabela, view ou sinônimo protegido com uma política do Oracle VPD, o oracle VPD adiciona de forma transparente uma cláusula Where dinâmica a uma instrução SQL. Essa cláusula dinâmica é chamada de predicado e é retornada por uma função que implementa a política de segurança.
Por exemplo, um usuário realiza a seguinte query:
Select * from order_details;
A política do Oracle VPD dinamicamente inclui a consulta uma cláusula where. Por exemplo:
Select * from order_details where id_order=222;
Então, o usuário somente pode ver os detalhes das compras que possuem o ID=222.
Benefĩcios do Oracle Virtual Private Database:
As políticas do Oracle Private Private Database fornecem os seguintes benefícios:
- Baseando diretivas de segurança em objetos de banco de dados em vez de aplicativos
- Controle de como o banco de dados Oracle avalia as funções de diretiva
- Otimizando o desempenho
- Facilita a manutenção do código
- Facilita a auditoria de ações do usuário
Arquitetura do Oracle Virtual Private Database:
Implementação
Ao anexar políticas aos objetos, o comportamento padrão do VPD é adicionar e aplicar uma nova cláusula WHERE ao SQL de origem no momento da execução da instrução para impor restrições de acesso no nível do objeto, independentemente do aplicativo que o executa e do usuário que tenta acessar os dados em vez de seu papel e privilégios, desde que não seja concedida uma isenção desta regra.
Portanto, as etapas usuais para implementar a política de VPD são mostradas abaixo:
Figura 1 – Implementação the VPD policy
- Um contexto de direção: é um contexto de aplicativo que possui pelo menos um atributo. Embora não seja necessário, seu uso nas políticas do VPD é uma técnica de design eficaz que é frequentemente usada. Ele é usado nas implementações do VPD para recuperar informações da sessão. De acordo com isso, determina qual grupo de políticas será aplicado para permitir ou bloquear o acesso a determinados dados. A inicialização dos contextos do aplicativo é realizada usando um pacote PL/SQL usado em sua definição.
Os contextos de aplicativos baseados em sessões do banco de dados podem ser inicializados localmente, externamente ou globalmente. No modo de inicialização local, os dados da sessão são recuperados para a Área Global do Usuário (UGA). A inicialização externa pode ser implementada usando um aplicativo externo (OCI, JDBC), um processo na fila de tarefas ou conectado através de um database link. A inicialização global pode ser implementada usando um local externo, como LDAP ou OID.
- VPD policy: Há 5 tipos de políticas baseadas em quão frequente as políticas são testadas:
DBMS_RLS.DYNAMIC é a padrão.
DMBS_RLS.STATIC
DBMS_RLS.SHARED_STATIC
DBMS_RLS.CONTEXT_SENSITIVE
DBMS_RLS.SHARED_CONTEXT_SENSITIVE
- Policy function: é usado para retornar um predicado que será aplicado na cláusula WHERE da instrução.
Como vemos que o comportamento padrão do VPD é qualquer política definida em uma tabela ou uma view é aplicada a todas as instruções SQL, independentemente da aplicação que a executa.
Portanto, no contexto de vários aplicativos que compartilham uma tabela ou uma view, é recomendável criar e usar grupos de políticas para determinar qual predicado deve ser retornado e quais políticas devem ser aplicadas. Se as políticas já estiverem definidas, você deverá identificar quais políticas devem estar em vigor quando cada aplicativo acessar a tabela ou view. Cada objeto tem um grupo de políticas padrão predefinido (SYS_DEFAULT), e as políticas definidas neste grupo são sempre aplicadas a esse objeto específico. Um contexto determinante determina qual outro grupo de políticas também será aplicado naquele momento.
Suponha que haja dois aplicativos (A e B) que acessam dados na tabela TEST. Suas políticas específicas são definidas em dois grupos de políticas (GRP_A e GRP_B), e as políticas que devem ser aplicadas em qualquer caso são definidas no grupo padrão (GRP _DEFAULT). Quando o aplicativo A acessa os dados, são aplicadas políticas que pertencem aos grupos GRP_A e GRP _DEFAULT e, quando o aplicativo B acessa os dados, são aplicadas políticas que pertencem aos grupos GRP_B e GRP _DEFAULT.
Figura 2 - Implementação de VPD policy groups
1. Examplos: Passo a Passo
1.1 Contextos de aplicação baseados em sessão:
Neste exemplo, criaremos um pacote PL/SQL para definir um contexto de aplicativo. Criamos um tipo de contexto inicializado localmente, usando um procedimento que contém um valor que inicializa um valor do contexto. Os valores serão gerados usando um gatilho de logon.
Primeiro, criamos o VPD do usuário e criamos as tabelas 'personnel', 'service', 'job', 'department' e 'region' no esquema VPD: SQL> Connect system/manager@TEST Connected.
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 seguir, como usuário VPD criamos 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.
Nós disparamos inserts nas tabelas no schema VPD:
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.
Neste passo, nós criamos uma view chamada VIEW_DATA_1 baseada nas tabelas personnel, service, job, department no schema VPD.
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 seguir, como system criamos os users vpdhrm, vpdacnt, vpdit, vpdfin e concedemos privilégio de 'Select' na view VIEW_DATA_1 para vpdhrm, vpdacnt, vpdit, vpdfin:
Como system ainda, criamos o package 'set_service_context_pkg' para configurar o application context 'hr_serv_context':
SQL> ----------Application-Context----------------
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.
A seguir, nós criamos uma trigger de logon para setar o valor serv_id no contexto 'hr_serv_context' quando o usuário:
SQL> --------------------Trigger-Loggon--------------------------------
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.
Nesse passo, conecte-se como vpdhrm, vpdacnt, vpdit e vpdfin e execute um SELECT na view VIEW_DATA_1. Os valores serão retornados baseados nas condições dinamicas configurados por SYS_CONTEXT('HR_SERV_CONTEXT','serv_id'):
SQL> ----------TEST-----------------
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
1.2 Implementando políticas acesso a nível de linha:
1.2.1 Caso SELECT:
Como usuário VPD criamos a tabela TABLE_DATA1 e concedemos privilégio de select para os usuários vpdhrm, vpdacnt, vpdit e 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 usuário criamos a função Serv_ID_Func para a política VPD:
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.
Em seguida, nós definimos a política chamada SELECT_SERVICE_POLICY, definida no objeto TABLE_DATA_1 do schema VPD, e aplicável somente para os SELECTs:
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.
Conecte como vpdhrm, vpdacnt, vpdit e vpdfin, nós disparamos o SELECT para checar se a política de VPD SELECT_SERVICE_POLICY VPD foi corretamente aplicada:
SQL> ----------TEST-----------------------
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 usuário SYSTEM, criamos outro usuário NO_VPD e concedemos privilégio de select na tabela TABLE_DATA_1. Se tentarmos disparar o SELECT conectado como NO_VPD, nenhuma linha 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 Caso INSERT:
No seguintes passos iremos criar uma tabela vazia e uma política VPD aplicável a INSERTs.
Como VPD user, criamos a tabela TABLE_DATA_2 baseada na estrutura da tabela 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 usuário SYSTEM criamos a função Serv_ID_add_Func para a política VPD pque irá permitir inserts somente para as linhas correspondentes ao serviço 'IT':
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.
Depois, iremos adicionar a polícita de insert chamada INSERT_IT_POLICY. Para garantir a política VPD na execução dos INSERT iremos habilitar o 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 nossa política VPD:
Primeiro, tentamos inserir alguns valores que não são compatíveis com INSERT_IT_POLICY.
Em seguida, emitimos instruções de inserção compatíveis com a política.
SQL> CONNECT VPD/VPD@test
Connected.
SQL> --------Violate_Insert_Condition-------------
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 Caso DELETE:
Nas etapas a seguir, criaremos uma função de política e uma política de VPD para lidar com a instrução DELETE.
Conectados como SYSTEM, criamos a função de política VPD chamada Salary_delete_Func, que será aplicada aos salários acima de 2000 e ao serviço 'IT_service' da seguinte maneira:
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.
Depois, criamos a política aplicável a tabela TABLE_DATA_2 usando a função Salary_delete_Func para DELETEs chamada de 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, chacamos a tabela Table_Data_2 e faremos o 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 acesso em nível de coluna:
Ao implementar restrições no nível da linha, a política é aplicada independentemente das colunas selecionadas. No entanto, nas restrições de acesso no nível da coluna, a política de acesso é aplicada apenas quando as colunas protegidas pela política são incluídas na instrução DML. Além disso, é possível mascarar os dados da coluna quando desejado, usando políticas no nível da coluna. Portanto, as linhas que não estão em conformidade com os critérios definidos têm seus valores de coluna ocultos pela política e exibidos como nulos.
Como usuário do VPD, criamos uma tabela TABLE_DATA_3 com base na tabela TABLE_DATA_2 no esquema vpd e concedemos o privilégio de seleção na tabela TABLE_DATA_3 da seguinte maneira:
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.
Primeiro, conectado com SYSTEM, criamos a função da política chamada 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.
Depois, criamos uma política a nível de coluna chamada 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, conectado como usuário vpdhrm e vpdit, emitimos uma instrução SELECT, sem incluir as colunas protegidas salário e job_title da seguinte maneira
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.
Então, a política não é aplicada porque as colunas protegidas pela política salary e job_title não estão inclusas na instrução SELECT.
Agora vamos emitir um SELECT que inclui a coluna SALARY ou JOB_TITLE ou as duas:
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
Nesse passo, nós iremos ver como mascarar colunas. Primeiro, como usuário SYSTEM iremos desabilitar a política 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.
Depois, iremos definir uma nova política chamada saljob_plc_mask usando a opção data masking (Mascaramento de dados):
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;
/
Agora conectados como vpdhrm e vpdit, iremos emitir um SELECT que inclua a coluna SALARY e 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, SALARY e JOB_TITLE retornaram apenas os valores de vpdhrm que foram definidas para o serviço 'hrm' e para as outras, as colunas SALARY e JOB_TITLE foram mostradas como NULL. E para o vpdit, as colunas SALARY e JOB_TIITLE mostraram apenas valores para o serviço 'it'.
1.4 Implementando políticas de VPD agrupadas:
As políticas agrupadas por recurso são usadas na aplicação de políticas diferentes do VPD no mesmo objeto. Nesses casos, as políticas agrupadas do VPD são usadas para atribuir políticas a diferentes grupos e acioná-las, dependendo de determinadas condições. A habilitação de uma política ou de outra será decidida pelo contexto do driver, de acordo com certos parâmetros declarados no nível do aplicativo.
A seguir, criaremos uma tabela que conterá três categorias de departamento diferentes.
Vamos conceder o privilégio de SELECT aos usuários: vpdhrm, vpdacnt, vpdit, vpdfin. Para cada grupo de departamentos, uma diretiva de grupo será definida. Essas políticas agrupadas isolarão a função de cada grupo com base na associação do usuário. Cada usuário verá seu departamento determinado por um contexto de driver.
Primeiro, nos conectamos como o usuário VPD e criamos a tabela DEPT_CATEG e concedemos o privilégio de SELECT aos usuários da seguinte maneira:
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.
Em seguida, inserimos dados em DEPT_CATEG. Os dados serão usados pelo contexto de direção:
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 usuário SYSTEM nós criamos um contexto dept_categ_context:
SQL> connect system/manager
Connected.
SQL> CREATE OR REPLACE CONTEXT dept_categ_context USING dept_categ_pkg;
Context created.
Nesse passo nós criaremos uma política para cada categoria:
- Criar uma política de grupo 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.
- Criar uma política de grupo 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.
- Criar uma política de grupo 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.
No próximo passo, iremos criar 3 funções de políticas, que irão ser assinadaladas a 3 políticas de grupo.
- Crie a função de política para a categoria um chamada vpd_func_dept_categ1:
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.
- Crie a função de política para a categoria um chamada 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.
- Crie a função de política para a categoria um chamada vpd_func_dept_categ3:
SQL> 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.
Nesse passo, iremos adicionar políticas agrupadas para cada categoria de departamento.
- Criar uma política de grupo chamada vpd_func_dept_categ1_plc para a categoria um :
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.
- Criar uma política de grupo chamada vpd_func_dept_categ2_plc para a categoria dois:
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.
- Criar a política de grupo chamada vpd_func_dept_categ3_plc para a categoria três:
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.
Depois, criar uma package e um package body dept_categ_pkg associado com o 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.
Depois, iremos asssinar o contexto dept_categ_context ao 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.
Depois, nós criamos uma nova trigger de logon para configurar o contexto depois de conectar ao banco de dados:
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.
Depois, conectados como vpdhrm, iremos checar o valor de plc_grp pelo contexto e, emitir o SELECT sobre DEPT_CATEG para checar se política de grupo foi aplicada:
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 vemos, apenas os departamentos da categoria 1 são visíveis para vpdhrm. Conectado como vpdacnt, verificamos o valor plc_grp no contexto e emitimos uma instrução SELECT em DEPT_CATEG para verificar se a política agrupada foi aplicada:
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
E, finalmente, conectado como o usuário vpdit, verificamos o valor plc_grp no contexto e emitimos uma instrução SELECT em DEPT_CATEG para verificar se a política agrupada foi aplicada:
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
Nas políticas agrupadas, o contexto determina a política ativa. No nosso exemplo, o procedimento DBMS_RLS. add_policy_context define nosso contexto e seu atributo de acordo com o qual o usuário se conecta.
1.5 Concedendo exceções a políticas VPD:
O comportamento padrão do VPDé uma vez que a política foi declarada em um objeto ela não pode ser burlada, apenas pelos privilégios dos usuários. Mas, em alguns casos nós precisamos acessar todos os dados de um objeto que tem uma política aplicada.
A seguir, mostraremos como isentar o usuário VPDHRM de todas as políticas declaradas no esquema VPD.
Primeiro, nos conectamos como o usuário vpdhrm e emitimos uma instrução SELECT na visualização da tabela TABLE_DATA_1 da seguinte maneira:
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
Depois, iremos conectar com o usuário SYSTEM e isentar o usuário vpdhrm de qualquer política VPD:
SQL> Connect system/manager
Connected.
SQL> GRANT EXEMPT ACCESS POLICY TO vpdhrm;
Grant succeeded.
Nós conectamos como usuário vpdhrm novamente e emitimos o SELECT do passo 1:
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.
Conclusão:
As políticas do Oracle Virtual Private Database fornecem os seguintes benefícios:
- Baseia políticas de segurança em objetos e não em applicações
- Control a como o Oracle testa e aplica as funções de uma política
- Optimização de Performance
Y V Ravi Kumar é um Oracle ACE e Oracle Certified Master (OCM) com 18 anos de experiência em instituições financeiras, serviços financeiros e seguros (BFSI) e atuou em diversos papeis como Senior Database Architect e Production DBA. Ele também é OCP em Oracle 8i, 9i, 10g, 11g & 12c e Certificado em Golden Gate, RAC, Performance Tuning& Oracle Exadata. Ele continua motivando muitos DBAs e ajudando a Oracle Community publicando suas dicas /ideias/sugestões/soluções em seu blog. Ele escreveu 40+ artigos OTN sobre Oracle Exadata, Oracle RAC e Oracle GoldenGate para a OTN em Espanhol, OTN em Português e OTN em inglês e 19 artigos para a TOAD World, 2 Artigos para o UKOUG, 3 Artigos para OTech Magazine e 2 Artigos para a Redgate. Ele é membro do AllIndia Oracle UserGroup (AIOUG) e frequente Oracle speaker in @NYOUG, @OTN, AIOUG, Sangam e IOUG. Ele desenha, projeta e implementa Core Banking System (CBS) Databases para o Central Banks em dois países – India e Mahe, Seychelles. Ele é Co-Founder do OraWorld (www.oraworld.com). Leia mais sobre o seu perfil na LaserSoft.
Hela Khazri é Oracle Professional Certified Administrator (OCP 12c R1) com 8 anos de experiência como Senior Oracle Database Administrator e Project Manager usando a metodologia scrum master. Com uma habilidade comprovada em manter sistemas de alta disponibilidade usando RAC e ASM e para monitorar a performance usando AWR, ASH, ADDM e segurança de bancos de dados. Tem bons conhecimentos anaítico e troubleshooting skills para resolver problemas de banco e ajudar suportes em níveis (L2 e L3) no dia a dia.
Rodrigo Mufalani é um Oracle ACE member e Oracle Certified Master (OCM) com 15 anos de experiência, começou com o Oracle 8i, mas teve a oportunidade de dar suporte a Oracle 7.3.4 em diante. É especialista em banco de dados Oracle com foco principal em Engineered Systems, MAA, Performance & Tuning. Ele é fundador, presidente e também palestrante do LuxOUG. É palestrante em eventos de Oracle como: OTN LAD TOUR e OTN EMEA TOUR e outros. Atualmente trabalha como Principal DBA na eProseed. Twitter @mufalani / blog Mufalani.worpress.com
Este artigo foi revisto pela equipe de produtos Oracle e está em conformidade com as normas e práticas para o uso de produtos Oracle.