web-dev-qa-db-ja.com

2つのOracleデータベースロールを比較する

企業のユーザー管理を再設計しようとしています。すべての役割を互いに比較して、重複する役割を見つけたいと考えています。手作業でやるのは本当に疲れるし、150ほどの役割を比較する必要があります。私は次のスクリプトを書きました:

DEFINE ROLE_1 = &INPUT_1
DEFINE ROLE_2 = &INPUT_2

SET FEEDBACK OFF
SET VERIFY OFF
SET HEADING ON
SET LINESIZE 140

COLUMN PRIVILEGE    FORMAT A20    HEADING "PRIVILEGE"
COLUMN TABLE_NAME   FORMAT A30    HEADING "TABLE_NAME"
COLUMN GRANTABLE    FORMAT A5     HEADING "GRANTABLE"

Prompt
PROMPT OVERLAPPINGS BETWEEN 1st ROLE AND 2nd ROLE
Prompt

SELECT
    PRIVILEGE,
    TABLE_NAME,
    GRANTABLE
FROM
    SYS.DBA_TAB_PRIVS
WHERE
    GRANTEE IN ( '&ROLE_1' )

INTERSECT

SELECT
    PRIVILEGE,
    TABLE_NAME,
    GRANTABLE
FROM
    SYS.DBA_TAB_PRIVS
WHERE
    GRANTEE IN ( '&ROLE_2' )
ORDER BY TABLE_NAME, PRIVILEGE ASC
/

Prompt
PROMPT PRIVILEGES IN 1st ROLE BUT NOT IN 2nd ROLE
Prompt

SELECT
    PRIVILEGE,
    TABLE_NAME,
    GRANTABLE
FROM
    SYS.DBA_TAB_PRIVS
WHERE
    GRANTEE IN ( '&ROLE_1' )

MINUS

SELECT
    PRIVILEGE,
    TABLE_NAME,
    GRANTABLE
FROM
    SYS.DBA_TAB_PRIVS
WHERE
    GRANTEE IN ( '&ROLE_2' )
ORDER BY TABLE_NAME, PRIVILEGE ASC
/

Prompt
PROMPT PRIVILEGES IN 2nd ROLE BUT NOT IN 1st ROLE
Prompt

SELECT
    PRIVILEGE,
    TABLE_NAME,
    GRANTABLE
FROM
    SYS.DBA_TAB_PRIVS
WHERE
    GRANTEE IN ( '&ROLE_2' )

MINUS

SELECT
    PRIVILEGE,
    TABLE_NAME,
    GRANTABLE
FROM
    SYS.DBA_TAB_PRIVS
WHERE
    GRANTEE IN ( '&ROLE_1' )
ORDER BY TABLE_NAME, PRIVILEGE ASC
/

すべて正常に動作しますが、何らかの理由でテーブルヘッドが表示されません。

私が尋ねている本当の問題は、すべての役割を自動的に相互に比較するより快適な方法はありますか?これをすべて行うには、きちんとした方法がありますか?私はPL/SQLに非常に慣れていないので、このことをループする方法を本当に知りません。テーブルの対応する列を表示する可能性のある方法さえありますか?

4
Chris.V

これは、重複するrole_tab_privsを見つけるクエリです。 Oracle LISTAGG集約関数を使用して、各ロール、所有者、テーブルの特権のリストを作成します。

SELECT arole, brole, aowner, atable, aprivlist
FROM 
      --join to each grouped view by role, owner, table
      (
      SELECT A.ROLE AS arole, A.owner AS aowner, A.table_name AS atable,
                  --one field for all privileges concatenated with | separator
                  listagg(a.privilege, ' | ') WITHIN GROUP(ORDER BY a.privilege) AS aprivlist
      FROM role_tab_privs a
      GROUP BY A.ROLE, A.owner, A.table_name
      )
JOIN 
      (
      SELECT b.ROLE as brole, b.owner as bowner, b.table_name as btable,
                  listagg(b.privilege, ' | ') WITHIN GROUP(ORDER BY b.privilege) AS bprivlist
      FROM role_tab_privs b
      GROUP BY b.ROLE, b.owner, b.table_name
      )
  -- the roles cannot be equal but the owner, table, and privileges must be
  ON AROLE != bROLE
 AND aowner = bowner
 AND atable = btable
 AND aprivlist = bprivlist
ORDER BY aowner, atable, arole 
;

LISTAGGは、ロール、所有者、テーブルグループ内の権限でのみ使用されることに注意してください。 `LISTAGG '関数の結果は、max varchar(4000)またはエラー内に収まる必要があります。

ORA-01489: result of string concatenation is too long

このクエリは、テーブルの権限がどのように関連しているかをよく示しています。

シェルスクリプトを使用してそれを行います。

以下のコードはテストされていません。ファイル名のIFSとスペースに問題がある可能性があります。また、最適化が欠如しており、すべてのロールを何度も繰り返します。これは、各役割に対してデータベースへの新しい接続を作成するため、「抽出」部分で特に問題になるはずです。

でも、それでも一発物なら、あまり気にしません。

あなたは必要になるでしょう :

ロールの特権を抽出するためのSQLファイル(リクエスト、後で比較するため、order by句を追加しました)

--extract_privileges.sql
SELECT
  PRIVILEGE,
  TABLE_NAME,
  GRANTABLE
FROM
  SYS.DBA_TAB_PRIVS
WHERE
  GRANTEE IN ( '&1' )
ORDER BY
  PRIVILEGE,
  TABLE_NAME,
  GRANTABLE;

テストする役割を含む構成ファイル:

ROLE_1
ROLE_2
...

とシェルスクリプト:

ROLE_FILE='/path/to/the/configuration/file'
EXTRACT_PRIVILEGES_FILE='/path/to/the/sql/script'
TEMP_DIR='/path/to/temp/dir/'
CONNECTION_STRING='user/pass@instance'

IFS='
'

# Make sure we have temp dirs
EXTRACT_DIR='${TEMP_DIR}/extract/'
COMPARE_DIR='${TEMP_DIR}/compare/'

mkdir -p ${EXTRACT_DIR}
mkdir -p ${COMPARE_DIR}

# Extract all privileges
for role in `cat ${ROLE_FILE}`
do
  ${Oracle_HOME}/bin/sqlplus ${CONNECTION_STRING} @${EXTRACT_PRIVILEGES_FILE} ${role} > ${EXTRACT_DIR}/${role}
done

# Compare privileges
for role1 in `cat ${ROLE_FILE}`
do
  for role2 in `cat ${ROLE_FILE}`
  do
    diff ${EXTRACT_DIR}/${role1} ${EXTRACT_DIR}/${role2} > ${COMPARE_DIR}/${role1}_${role2}
  done
done

# Now you have all the diffs, you can look for those with nothing in it (privileges are the same)
for file in `ls ${COMPARE_DIR}`
do
  #There is certainly a more elegant way to do this.
  if [[`wc -l ${file}` -eq 0 ]]
  then
    echo '${file} is empty'
  fi
done
0
Louis Carrese