This post is also available in: English
Durante a minha última apresentação no GUOB Tech Tour 2016 - Oracle Technology Tour LA - Brazil, demonstrei como poderíamos facilmente usar um privilégio de CREATE PUBLIC SYNONYM para escalonar até um privilégio DBA.
Neste artigo, eu vou compartilhar com vocês uma package que criei e uso em meus sistemas para permitir que os usuários sejam capazes de criar os seus próprios sinônimos públicos sem comprometer a segurança do Banco de Dados.
Então vamos começar.
Como funciona?
Essa package funciona permitindo a um determinado usuário que apenas crie sinônimos públicos aos seus próprios objetos ou que remova sinônimos públicos que estejam apontando para um de seus objetos.
Conceda os grants necessários ao dono da package
Antes de mais nada, essa package precisa ser criada em um usuário que possua os seguintes privilégios mínimos concedidos diretamente (não via ROLE):
- SELECT on DBA_SYNONYMS
- CREATE PUBLIC SYNONYM
- DROP PUBLIC SYNONYM
1. Crie a package
CREATE OR REPLACE PACKAGE MANAGE_PUBLIC_SYNONYM AS -- Created by Rodrigo Jorge - www.dbarj.com.br -- PROCEDURE CREATE_SYNONYM(SYNONYM_NAME IN VARCHAR2, OBJECT_NAME IN VARCHAR2); PROCEDURE DROP_SYNONYM(SYNONYM_NAME IN VARCHAR2); END; /
2 Crie o package body
CREATE OR REPLACE PACKAGE BODY MANAGE_PUBLIC_SYNONYM AS -- Created by Rodrigo Jorge - www.dbarj.com.br -- FUNCTION CHECK_EXISTS(SYN_NAME IN VARCHAR2) RETURN BOOLEAN IS OUT_RESULT NUMBER; BEGIN SELECT 1 INTO OUT_RESULT FROM DBA_SYNONYMS WHERE OWNER = 'PUBLIC' AND SYNONYM_NAME = SYN_NAME; RETURN TRUE; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN FALSE; END; FUNCTION GET_PUBLIC_SYN_OWNER(SYN_NAME IN VARCHAR2) RETURN VARCHAR2 IS OUT_RESULT VARCHAR2(30); BEGIN SELECT TABLE_OWNER INTO OUT_RESULT FROM DBA_SYNONYMS WHERE OWNER = 'PUBLIC' AND SYNONYM_NAME = SYN_NAME; RETURN OUT_RESULT; END; PROCEDURE RAISE_ERROR(IN_CODE IN NUMBER) IS BEGIN CASE IN_CODE WHEN -20001 THEN RAISE_APPLICATION_ERROR(IN_CODE, 'Synonym already exists.'); WHEN -20002 THEN RAISE_APPLICATION_ERROR(IN_CODE, 'Synonym does not exist.'); WHEN -20003 THEN RAISE_APPLICATION_ERROR(IN_CODE, 'Synonym is not yours.'); ELSE RAISE_APPLICATION_ERROR(-20999, 'Generic error.'); END CASE; END; PROCEDURE CREATE_SYNONYM(SYNONYM_NAME IN VARCHAR2, OBJECT_NAME IN VARCHAR2) IS SESS_USER VARCHAR2(30); BEGIN IF CHECK_EXISTS(SYNONYM_NAME) = TRUE THEN RAISE_ERROR(-20001); END IF; SESS_USER := SYS_CONTEXT('USERENV', 'SESSION_USER'); EXECUTE IMMEDIATE 'CREATE PUBLIC SYNONYM ' || DBMS_ASSERT.ENQUOTE_NAME(SYNONYM_NAME, FALSE) || ' FOR ' || DBMS_ASSERT.ENQUOTE_NAME(SESS_USER, FALSE) || '.' || DBMS_ASSERT.ENQUOTE_NAME(OBJECT_NAME, FALSE); END; PROCEDURE DROP_SYNONYM(SYNONYM_NAME IN VARCHAR2) IS OBJ_OWNER VARCHAR2(30); SESS_USER VARCHAR2(30); BEGIN IF CHECK_EXISTS(SYNONYM_NAME) = FALSE THEN RAISE_ERROR(-20002); END IF; OBJ_OWNER := GET_PUBLIC_SYN_OWNER(SYNONYM_NAME); SESS_USER := SYS_CONTEXT('USERENV', 'SESSION_USER'); IF OBJ_OWNER <> SESS_USER THEN RAISE_ERROR(-20003); END IF; EXECUTE IMMEDIATE 'DROP PUBLIC SYNONYM ' || DBMS_ASSERT.ENQUOTE_NAME(SYNONYM_NAME, FALSE); END; END; /
3 Dê privilégio na package aos usuários que precisam de Sinônimos Públicos
Examplo:
GRANT EXECUTE ON MANAGE_PUBLIC_SYNONYM TO SCOTT;
Opcionalmente, você também pode criar um sinônimo para evitar que o owner da package seja sempre digitado:
CREATE SYNONYM SCOTT.MANAGE_PUBLIC_SYNONYM FOR MANAGE_PUBLIC_SYNONYM;
E é isso.
Como usar?
Para criar um sinônimo público
BEGIN MANAGE_PUBLIC_SYNONYM.CREATE_SYNONYM('EMP','EMP'); END; /
Para remover um sinônimo público
BEGIN MANAGE_PUBLIC_SYNONYM.DROP_SYNONYM('EMP'); END; /
E lembre-se: não será possível tocar em um sinônimo público de outro usuário.
Gostou? Não deixe de comentar ou deixar um 👍!