Fix DDS variant E2E + expand DDS capability docs
Real run against ADB 23.26.2.2.0 surfaced two issues: - END USERs can't be direct grantees of a regular ROLE (ORA-01917). Connection privilege must flow through a DATA ROLE — added connect_only_role for ddsuser_none so it can authenticate without holding any data grant (mirrors VPDUSER_NONE UX). - DDS Data Grants on top of the shared v_customers_* views silently returned 0 rows because the VPD policy on those views evaluates 1=0 for sessions whose LOGON trigger didn't load the VPD context (i.e. all ddsuser_*). Created dedicated DDS-only views (v_dds_customers_pg / v_dds_customers_my) so DDS Data Grants are the sole authority. E2E matrix now passes (ddsuser_my MY=17, ddsuser_pg PG=12, ddsuser_both 12/17, ddsuser_none ORA-00942 on both). Notably DDS returns ORA-00942 where VPD returned 0 rows — stronger object hiding. Expanded docs/05-dds-variant.md with: - §1.1 capability matrix (End User, Data Role, Data Grant, MAC, ORA_END_USER_CONTEXT, OAuth2 federation, End User Context Object, ORA_IS_COLUMN_AUTHORIZED, dictionary views) - §1.2 VPD/RAS-vs-DDS comparison - §1.3 best-fit scenarios (multi-tenant SaaS, agentic AI, HR/PHI, federated identity, compliance) - §1.4 limitations - §5 operation-level grant example (manager-only UPDATE salary) - §8 actual E2E results table Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,21 +4,65 @@
|
||||
> 그대로 동작합니다. 이 문서는 동일한 4-user 매트릭스를 Oracle AI Database
|
||||
> **26ai 신기능 Deep Data Security (DDS)** 로 재구현한 `sql/adb/13_dds_variant.sql`
|
||||
> 의 사용법과, VPD ↔ DDS 1:1 매핑을 다룹니다.
|
||||
>
|
||||
> 본 변형은 **2026-05-26 실제 ADB (23.26.2.2.0) 에서 E2E 검증** 됨 (8장 결과).
|
||||
|
||||
---
|
||||
|
||||
## 1. 왜 DDS 도 같이 다루는가
|
||||
## 1. Deep Data Security 는 무엇을 할 수 있는가
|
||||
|
||||
Oracle 은 **2026-04-09** 에 VPD/RAS 의 공식 후계자로 **Deep Data Security**
|
||||
(이하 DDS) 를 발표했습니다 (Oracle AI Database 26ai). 공식 한 줄:
|
||||
Oracle 이 **2026-04-09** 에 VPD/RAS 의 공식 후계자로 발표한 native authorization
|
||||
시스템 (Oracle AI Database 26ai). 공식 한 줄:
|
||||
|
||||
> "Extends and modernizes Oracle Virtual Private Database and Real Application
|
||||
> Security, moving from procedural PL/SQL and API-based controls **to
|
||||
> declarative policies in SQL**."
|
||||
|
||||
본 POC 는 의도적으로 **VPD 를 직접 구현하는 경로** 를 보여주는데, 같은 요건을
|
||||
DDS 로 표현하면 코드량과 메커니즘이 어떻게 줄어드는지 함께 보여주면 의사결정
|
||||
근거가 명확해집니다.
|
||||
### 1.1 핵심 기능 분류
|
||||
|
||||
| 영역 | DDS 가 제공하는 것 | 어떤 객체로 표현 |
|
||||
|---|---|---|
|
||||
| **인증 / 식별** | 전통적 DB 사용자와 분리된 schemaless "**End User**" (스키마/객체 없음, 로그인만) | `CREATE END USER` |
|
||||
| **권한 묶음** | 일반 ROLE 과 별도 namespace 의 "**Data Role**" (외부 IdP claim 매핑 가능) | `CREATE DATA ROLE [MAPPED TO 'AZURE_ROLE=manager']` |
|
||||
| **세분화된 데이터 접근** | row + column + cell 단위 access control 을 **DDL 한 줄** 로 | `CREATE DATA GRANT ... AS SELECT (cols) ON tbl WHERE ...` |
|
||||
| **연산 단위 권한** | SELECT 외에도 UPDATE / INSERT / DELETE 를 컬럼별 세분화 | `AS SELECT (a,b), UPDATE (salary)` |
|
||||
| **컬럼 동적 마스킹** | 허용되지 않은 컬럼은 **NULL** 로 자동 반환 (별도 Redaction 정책 불필요) | `AS SELECT (ALL COLUMNS EXCEPT ssn)` |
|
||||
| **Mandatory Access Control** | 기존 GRANT / SELECT ANY TABLE / DBA 권한도 우회 못 함 — 데이터 grant 가 단일 권한 출처 | `SET USE DATA GRANTS ONLY ON tbl ENABLED` |
|
||||
| **세션 식별자 전파** | LOGON 트리거 없이 자동. OAuth2 token 의 claim 까지 in-memory JSON context 로 보관 | `ORA_END_USER_CONTEXT.username` / `.<claim>` |
|
||||
| **OAuth2 federated identity** | OCI IAM, Microsoft Entra ID 의 토큰을 DB 가 직접 검증하고 claim 활용 | OCI IAM / Entra ID OIDC 연동 |
|
||||
| **Application identity** | 사람이 아닌 application (ORDS / agentic AI / micro-service) 도 1급 시민으로 인증 | `CREATE APPLICATION` + JWT-bearer |
|
||||
| **End-User Context Object** | 데이터 grant 안에서 참조 가능한 임의 JSON 컨텍스트를 IdP claim 매핑으로 정의 | `CREATE END USER CONTEXT ... USING JSON SCHEMA` |
|
||||
| **객체 hiding** | 권한이 없으면 "**0 rows**" 가 아니라 `ORA-00942 (table/view does not exist)` — 객체 자체가 안 보임 | DATA GRANT 부재 시 기본 동작 |
|
||||
| **권한 점검 함수** | 정책 안 또는 응용 PL/SQL 에서 "이 사용자가 이 컬럼/operation 권한 있나?" 동적 체크 | `ORA_IS_COLUMN_AUTHORIZED()`, `ORA_CHECK_DATA_PRIVILEGE()` |
|
||||
| **감사** | grant / role / end-user 일체가 dictionary view 로 노출 | `DBA_DATA_GRANTS`, `DBA_DATA_ROLES`, `DBA_END_USERS` |
|
||||
|
||||
### 1.2 VPD/RAS 대비 무엇이 달라졌나
|
||||
|
||||
| 측면 | VPD/RAS (procedural) | DDS (declarative) |
|
||||
|---|---|---|
|
||||
| 정책 작성 | PL/SQL **함수** 가 `WHERE` 문자열을 빌드해서 반환 | **DDL 한 줄** (`CREATE DATA GRANT ... WHERE ...`) |
|
||||
| 식별자 전달 | LOGON 트리거 + `DBMS_SESSION.SET_CONTEXT` + 직접 만든 namespace | 세션 시작 시 자동, `ORA_END_USER_CONTEXT.username` |
|
||||
| 외부 IdP 연동 | 별도 CMU + 트리거 핸들링 필요 | OCI IAM / Entra ID OAuth2 **native** 검증 |
|
||||
| 권한 모델 | DAC (DBA 가 GRANT 으로 우회 가능) | MAC option 으로 GRANT 도 무시 가능 |
|
||||
| 컬럼 마스킹 | **별도** Data Redaction 정책 필요 | grant 안에 `ALL COLUMNS EXCEPT` 로 통합 |
|
||||
| 행 + 컬럼 + 연산 | 따로 따로 설정 | 한 statement 안에 통합 |
|
||||
| 인증 단위 | 풀 데이터베이스 사용자 (스키마 소유) | schemaless END USER (스키마 없음, 로그인만) |
|
||||
| 디버깅 | 정책 함수가 빌드한 동적 SQL 추적 | grant 의 WHERE 절이 그대로 plan 에 보임 |
|
||||
|
||||
### 1.3 어떤 시나리오에 가장 유용한가
|
||||
|
||||
* **Multi-tenant SaaS** — 단일 테이블/뷰에 수십~수백 tenant 의 데이터, 각 사용자는 자기 tenant 만. DDS 의 `WHERE tenant_id = ORA_END_USER_CONTEXT.tenant_id` 한 줄.
|
||||
* **Agentic AI / LLM-driven 쿼리** — 에이전트(ORDS/MCP 서버)가 **사용자 대신 쿼리** 해도 사용자 권한 모델이 유지되어야 함. End User identity 가 application 토큰과 분리되어 흐름.
|
||||
* **HR / 의료 / 금융** — row 단위 (담당자 = 본인 record) + column 단위 (SSN/salary 마스킹) + operation 단위 (manager 만 UPDATE salary) 가 동시에 필요한 경우.
|
||||
* **Federated identity** — Entra ID / OCI IAM 의 group/role 을 DB 권한에 직접 매핑 (`CREATE DATA ROLE x MAPPED TO 'AZURE_ROLE=hr_manager'`). 별도 sync 잡 불필요.
|
||||
* **Compliance** (HIPAA / PCI / GDPR) — MAC 모드에서는 DBA 도 grant 가 없으면 데이터를 못 본다. Audit 도 declarative grant 단위로 명확.
|
||||
|
||||
### 1.4 무엇은 못/안 하는가
|
||||
|
||||
* 본질적으로 **Oracle DB 안의 데이터** 에 대한 정책. 원격 DB 데이터에 적용하려면 본 POC 처럼 **로컬 뷰** 를 거쳐야 함 (VPD 와 동일한 제약).
|
||||
* 동적 외부 호출 (예: 외부 ACL 서버 콜) 은 grant WHERE 절로 표현하기 어려움 — 그런 케이스는 여전히 application-tier 권한이 필요.
|
||||
* MAC (`USE DATA GRANTS ONLY`) 를 켜면 기존 application 의 normal GRANT 가 무력화되어, 마이그레이션 시 한꺼번에 전환해야 함.
|
||||
* 26ai (23.26.2+) 미만에서는 동작 안 함. ADB 의 경우 자동 패치 채널이지만 on-prem 은 명시적 업그레이드 필요.
|
||||
|
||||
---
|
||||
|
||||
@@ -26,12 +70,13 @@ DDS 로 표현하면 코드량과 메커니즘이 어떻게 줄어드는지 함
|
||||
|
||||
| 항목 | 요건 |
|
||||
|---|---|
|
||||
| Oracle 버전 | AI Database **23.26.2 이상** (`v$version` 확인) |
|
||||
| 초기화 파라미터 | `COMPATIBLE >= 20.0` (`SELECT value FROM v$parameter WHERE name='compatible'`) |
|
||||
| Oracle 버전 | AI Database **23.26.2 이상** (`SELECT banner_full FROM v$version`) |
|
||||
| 초기화 파라미터 | `COMPATIBLE >= 20.0` |
|
||||
| 관리자 권한 | `CREATE END USER`, `CREATE DATA ROLE`, `GRANT DATA ROLE`, `CREATE DATA GRANT` (ADB ADMIN 은 기본 보유) |
|
||||
| Dictionary view | `DBA_END_USERS`, `DBA_DATA_ROLES`, `DBA_DATA_GRANTS` 존재 여부로 가용성 확인 |
|
||||
|
||||
> ADB 가 아직 26ai 로 업그레이드되지 않은 환경이라면 이 스크립트는 `ORA-00942`
|
||||
> 류로 실패합니다. 본 디렉토리의 VPD 경로는 그대로 동작합니다.
|
||||
> ADB 가 아직 26ai 로 업그레이드되지 않은 환경이라면 `CREATE END USER` 부터
|
||||
> `ORA-00942` 류로 실패합니다. 본 디렉토리의 VPD 경로는 그대로 동작합니다.
|
||||
|
||||
---
|
||||
|
||||
@@ -45,23 +90,30 @@ sqlplus "$ADB_USER/$ADB_PASSWORD@$ADB_TNS" @sql/adb/13_dds_variant.sql
|
||||
스크립트가 만드는 객체:
|
||||
|
||||
```
|
||||
END USER ddsuser_my ddsuser_pg ddsuser_both ddsuser_none
|
||||
ROLE dds_db_role (CREATE SESSION 보유)
|
||||
DATA ROLE my_only_role pg_only_role both_sources_role
|
||||
VIEW v_dds_customers_pg v_dds_customers_my (VPD policy 없음 — DDS-only)
|
||||
END USER ddsuser_my ddsuser_pg ddsuser_both ddsuser_none
|
||||
ROLE dds_db_role (CREATE SESSION 보유)
|
||||
DATA ROLE my_only_role pg_only_role both_sources_role connect_only_role
|
||||
DATA GRANT
|
||||
dds_my_only_grant_mysql -> v_customers_my TO my_only_role
|
||||
dds_pg_only_grant_pg -> v_customers_pg TO pg_only_role
|
||||
dds_both_grant_pg -> v_customers_pg TO both_sources_role
|
||||
dds_both_grant_mysql -> v_customers_my TO both_sources_role
|
||||
dds_my_only_grant_mysql -> v_dds_customers_my TO my_only_role
|
||||
dds_pg_only_grant_pg -> v_dds_customers_pg TO pg_only_role
|
||||
dds_both_grant_pg -> v_dds_customers_pg TO both_sources_role
|
||||
dds_both_grant_mysql -> v_dds_customers_my TO both_sources_role
|
||||
```
|
||||
|
||||
> **왜 별도 뷰?** 메인 데모의 `v_customers_pg/my` 에는 VPD policy 가 살아
|
||||
> 있어 `ddsuser_*` 세션에서는 정책 함수가 `1=0` 을 반환합니다. DDS 가
|
||||
> "허용"해도 VPD 가 "0 rows" 로 깎기 때문에 의도한 결과가 안 나옴.
|
||||
> 그래서 DDS 전용 뷰 `v_dds_customers_*` 를 따로 만들어 grant 의 단일
|
||||
> 권한 출처로 만듭니다.
|
||||
|
||||
검증:
|
||||
|
||||
```bash
|
||||
sqlplus '"ddsuser_pg"'/"$DDSUSER_PG_PASSWORD"@"$ADB_TNS" <<'EOF'
|
||||
SELECT ORA_END_USER_CONTEXT.username FROM dual;
|
||||
SELECT COUNT(*) FROM admin.v_customers_pg; -- 12 (기대값)
|
||||
SELECT COUNT(*) FROM admin.v_customers_my; -- ORA-... 권한 없음 (기대)
|
||||
SELECT COUNT(*) FROM admin.v_dds_customers_pg; -- 12 (기대)
|
||||
SELECT COUNT(*) FROM admin.v_dds_customers_my; -- ORA-00942 (기대)
|
||||
EOF
|
||||
```
|
||||
|
||||
@@ -74,12 +126,13 @@ EOF
|
||||
| 사용자 생성 | `CREATE USER vpduser_my IDENTIFIED BY ...` | `CREATE END USER "ddsuser_my" IDENTIFIED BY ...` |
|
||||
| 사용자 = 스키마? | 예 (Oracle 의 전통적 모델) | **아니오** — END USER 는 스키마/객체 없음 |
|
||||
| 그룹/역할 매핑 | `app_group` + `user_group` 매핑 테이블 | `CREATE DATA ROLE` + `GRANT DATA ROLE x TO "user"` |
|
||||
| 정책 표현 | `dbms_rls.add_policy` + PL/SQL 함수 `vpd_region_filter` | `CREATE DATA GRANT ... AS SELECT ON tbl WHERE ... TO role` (선언형 SQL) |
|
||||
| 정책 표현 | `dbms_rls.add_policy` + PL/SQL 함수 `vpd_region_filter` | `CREATE DATA GRANT ... AS SELECT ON tbl WHERE ... TO role` |
|
||||
| 식별자 전파 | LOGON 트리거 → `DBMS_SESSION.SET_CONTEXT('VPD_CTX', ...)` | 세션 자동: `ORA_END_USER_CONTEXT.username` |
|
||||
| 행 필터 | 정책 함수가 `WHERE region IN (...)` 반환 | `CREATE DATA GRANT ... WHERE region IN ('APAC','EMEA')` |
|
||||
| 컬럼 마스킹 | 별도 `DBMS_REDACT.ADD_POLICY` (06a_redaction.sql) | 같은 grant 안에 `AS SELECT (ALL COLUMNS EXCEPT email)` |
|
||||
| 권한 부여 단위 | `permission` 테이블 INSERT | DDL 한 줄 (`CREATE DATA GRANT`) |
|
||||
| 우회 차단 | GRANT 안 주기 + EXEMPT ACCESS POLICY 안 주기 + DBMS_RLS 안 주기 | `SET USE DATA GRANTS ONLY ON tbl ENABLED` (MAC) 한 줄 |
|
||||
| 미허용 객체 응답 | 0 rows (silent) | `ORA-00942` (객체 자체 hiding) |
|
||||
|
||||
---
|
||||
|
||||
@@ -94,7 +147,7 @@ UPDATE permission SET allowed_regions = 'APAC' WHERE group_id = 30 ...;
|
||||
-- DDS: grant 한 줄
|
||||
CREATE OR REPLACE DATA GRANT admin.dds_both_grant_pg
|
||||
AS SELECT
|
||||
ON admin.v_customers_pg
|
||||
ON admin.v_dds_customers_pg
|
||||
WHERE region = 'APAC'
|
||||
TO both_sources_role;
|
||||
```
|
||||
@@ -109,24 +162,34 @@ DBMS_REDACT.ADD_POLICY(object_schema=>'ADMIN', object_name=>'V_CUSTOMERS_PG',
|
||||
-- DDS: 같은 grant 안에서
|
||||
CREATE OR REPLACE DATA GRANT admin.dds_both_grant_pg
|
||||
AS SELECT (ALL COLUMNS EXCEPT email)
|
||||
ON admin.v_customers_pg
|
||||
ON admin.v_dds_customers_pg
|
||||
TO both_sources_role;
|
||||
```
|
||||
|
||||
**Operation 별 세분화 (manager 만 salary 업데이트):**
|
||||
|
||||
```sql
|
||||
-- VPD/RAS 로는 별도 정책 필요. DDS 는 한 grant:
|
||||
CREATE DATA GRANT admin.manager_compensation_grant
|
||||
AS SELECT (ALL COLUMNS EXCEPT ssn), UPDATE (salary)
|
||||
ON admin.employees
|
||||
WHERE manager = ORA_END_USER_CONTEXT.username
|
||||
TO manager_role;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 한계와 주의
|
||||
|
||||
* DDS Data Grant 는 **테이블·뷰** 모두에 적용 가능. 본 POC 의 `v_customers_*`
|
||||
* DDS Data Grant 는 **테이블·뷰** 모두에 적용 가능. 본 POC 의 `v_dds_customers_*`
|
||||
뷰는 DB Link 패스스루이므로 grant 가 뷰 단에서 평가됩니다. 원격 push-down 은
|
||||
여전히 ADB → 원격 옵티마이저 협상에 의존합니다 (VPD 와 동일한 성능 특성).
|
||||
* 본 스크립트는 의도적으로 `USE DATA GRANTS ONLY` (MAC) 를 **켜지 않습니다**.
|
||||
켜면 `vpduser_*` (regular GRANT 경로) 가 같은 뷰에 접근하지 못해 VPD 데모가
|
||||
깨집니다. 운영에서는 MAC 활성화가 권장됩니다.
|
||||
운영에서는 MAC 활성화가 권장 (단일 권한 출처).
|
||||
* OAuth2 identity propagation (OCI IAM / Microsoft Entra ID claim) 은 본
|
||||
문서에선 다루지 않습니다. 직접 로그온 시나리오만 다룹니다. agentic AI / ORDS
|
||||
중간 계층에서 사용자 ID 를 전파하는 경우 4장 (OCI IAM) / 5장 (Entra ID) 의
|
||||
공식 가이드 (`G50191`) 를 참고하세요.
|
||||
중간 계층에서 사용자 ID 를 전파하는 경우 공식 가이드 (`G50191`) 의 4장
|
||||
(OCI IAM) / 5장 (Entra ID) 참고.
|
||||
|
||||
---
|
||||
|
||||
@@ -141,9 +204,29 @@ DROP DATA GRANT admin.dds_both_grant_mysql;
|
||||
DROP DATA ROLE my_only_role;
|
||||
DROP DATA ROLE pg_only_role;
|
||||
DROP DATA ROLE both_sources_role;
|
||||
DROP DATA ROLE connect_only_role;
|
||||
DROP ROLE dds_db_role;
|
||||
DROP END USER "ddsuser_my";
|
||||
DROP END USER "ddsuser_pg";
|
||||
DROP END USER "ddsuser_both";
|
||||
DROP END USER "ddsuser_none";
|
||||
DROP VIEW v_dds_customers_pg;
|
||||
DROP VIEW v_dds_customers_my;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. E2E 검증 결과 (2026-05-26, ADB 23.26.2.2.0)
|
||||
|
||||
`sql/adb/13_dds_variant.sql` 실행 후 4명의 ddsuser 로 `v_dds_customers_*` 조회:
|
||||
|
||||
| 사용자 | `v_dds_customers_pg` | `v_dds_customers_my` | 판정 |
|
||||
|---|---|---|---|
|
||||
| `ddsuser_my` | `ORA-00942` (hidden) | **17 rows** | ✓ |
|
||||
| `ddsuser_pg` | **12 rows** | `ORA-00942` (hidden) | ✓ |
|
||||
| `ddsuser_both` | **12 rows** | **17 rows** | ✓ |
|
||||
| `ddsuser_none` | `ORA-00942` (hidden) | `ORA-00942` (hidden) | ✓ |
|
||||
|
||||
**관찰:** VPD 가 "0 rows" 로 조용히 깎는 자리에 DDS 는 **`ORA-00942 (table or
|
||||
view does not exist)`** — 객체 자체가 안 보입니다. enumeration 측면에서 더
|
||||
강한 hiding (공격자가 "객체는 있지만 막혀있다" 는 사실 자체를 알 수 없음).
|
||||
|
||||
@@ -31,16 +31,19 @@
|
||||
-- ------------------------------------------------------------
|
||||
-- This script creates *new* end users with the `ddsuser_` prefix
|
||||
-- so it does NOT collide with the VPD demo users (`vpduser_*`).
|
||||
-- Both paths can coexist on the same ADB. The shared objects
|
||||
-- (views v_customers_pg / v_customers_my, db_source table, etc.)
|
||||
-- are reused — DDS adds a parallel access path, it does not
|
||||
-- replace anything.
|
||||
--
|
||||
-- We deliberately do NOT enable `SET USE DATA GRANTS ONLY` on
|
||||
-- the views — doing so would block the existing VPD users who
|
||||
-- rely on the regular GRANT SELECT path. The DDS users get their
|
||||
-- access exclusively through DATA GRANTs (no regular SELECT
|
||||
-- grant is issued), so MAC is not required for this demo.
|
||||
-- It also creates **dedicated DDS-only views** (`v_dds_customers_pg`,
|
||||
-- `v_dds_customers_my`) — separate from the VPD demo's
|
||||
-- `v_customers_pg`/`v_customers_my`. Reason: the VPD views have a
|
||||
-- live `DBMS_RLS` policy that returns `1=0` for any session whose
|
||||
-- LOGON trigger didn't load the VPD application context — i.e. for
|
||||
-- our DDS end users. Putting DDS Data Grants on top of the VPD
|
||||
-- views would silently return 0 rows (DDS allows but VPD blocks).
|
||||
-- Dedicated views with no VPD policy let DDS be the sole gatekeeper.
|
||||
--
|
||||
-- We deliberately do NOT enable `SET USE DATA GRANTS ONLY` on the
|
||||
-- DDS views in this demo — but doing so is the recommended
|
||||
-- production posture (single declarative policy plane).
|
||||
--
|
||||
-- DEFINE: &DDSUSER_MY_PASSWORD, &DDSUSER_PG_PASSWORD,
|
||||
-- &DDSUSER_BOTH_PASSWORD, &DDSUSER_NONE_PASSWORD
|
||||
@@ -49,6 +52,25 @@ SET ECHO OFF
|
||||
SET FEEDBACK ON
|
||||
SET DEFINE ON
|
||||
|
||||
PROMPT === 0. Creating DDS-only views (no VPD policy attached) ===
|
||||
-- Functionally identical to v_customers_pg / v_customers_my but
|
||||
-- kept separate so that DDS Data Grants are the sole authority.
|
||||
CREATE OR REPLACE VIEW v_dds_customers_pg AS
|
||||
SELECT "customer_id" AS customer_id,
|
||||
"full_name" AS full_name,
|
||||
"email" AS email,
|
||||
"signup_date" AS signup_date,
|
||||
"region" AS region
|
||||
FROM "public"."customers"@RDS_POSTGRES_LINK;
|
||||
|
||||
CREATE OR REPLACE VIEW v_dds_customers_my AS
|
||||
SELECT "customer_id" AS customer_id,
|
||||
"full_name" AS full_name,
|
||||
"email" AS email,
|
||||
"signup_date" AS signup_date,
|
||||
"region" AS region
|
||||
FROM "ecommerce_poc"."customers"@RDS_LINK;
|
||||
|
||||
PROMPT === 1. Creating local END USERs (schemaless, no objects) ===
|
||||
-- End users in DDS do not own a schema and cannot create objects.
|
||||
-- They authenticate, then receive access purely via DATA GRANTs.
|
||||
@@ -67,21 +89,23 @@ GRANT CREATE SESSION TO dds_db_role;
|
||||
CREATE DATA ROLE my_only_role;
|
||||
CREATE DATA ROLE pg_only_role;
|
||||
CREATE DATA ROLE both_sources_role;
|
||||
-- (No role for the "none" user — absence of a data role = default deny.)
|
||||
-- A "connect only" data role for ddsuser_none — they need to be able
|
||||
-- to log in (so we can prove "authenticated but no data visible"),
|
||||
-- but they must hold NO data grants. END USERs can't be grantees of
|
||||
-- a regular ROLE directly (ORA-01917) — connection privilege must
|
||||
-- flow through a DATA ROLE.
|
||||
CREATE DATA ROLE connect_only_role;
|
||||
|
||||
GRANT dds_db_role TO my_only_role;
|
||||
GRANT dds_db_role TO pg_only_role;
|
||||
GRANT dds_db_role TO both_sources_role;
|
||||
GRANT dds_db_role TO connect_only_role;
|
||||
|
||||
PROMPT === 3. Mapping end users to data roles ===
|
||||
GRANT DATA ROLE my_only_role TO "ddsuser_my";
|
||||
GRANT DATA ROLE pg_only_role TO "ddsuser_pg";
|
||||
GRANT DATA ROLE both_sources_role TO "ddsuser_both";
|
||||
-- ddsuser_none gets nothing — they can authenticate (no, actually
|
||||
-- they cannot, because they have no data role carrying dds_db_role).
|
||||
-- Grant CREATE SESSION directly so they can prove "logged in but
|
||||
-- denied at the data layer" — same UX as VPDUSER_NONE in the VPD demo.
|
||||
GRANT dds_db_role TO "ddsuser_none";
|
||||
GRANT DATA ROLE connect_only_role TO "ddsuser_none";
|
||||
|
||||
PROMPT === 4. Creating DATA GRANTs (the declarative equivalent of the VPD policy) ===
|
||||
-- These five lines replace the entire vpd_region_filter PL/SQL
|
||||
@@ -91,24 +115,24 @@ PROMPT === 4. Creating DATA GRANTs (the declarative equivalent of the VPD policy
|
||||
-- DDSUSER_MY -> ALL rows from v_customers_my, no PG access.
|
||||
CREATE DATA GRANT admin.dds_my_only_grant_mysql
|
||||
AS SELECT
|
||||
ON admin.v_customers_my
|
||||
ON admin.v_dds_customers_my
|
||||
TO my_only_role;
|
||||
|
||||
-- DDSUSER_PG -> ALL rows from v_customers_pg, no MY access.
|
||||
CREATE DATA GRANT admin.dds_pg_only_grant_pg
|
||||
AS SELECT
|
||||
ON admin.v_customers_pg
|
||||
ON admin.v_dds_customers_pg
|
||||
TO pg_only_role;
|
||||
|
||||
-- DDSUSER_BOTH -> both views.
|
||||
CREATE DATA GRANT admin.dds_both_grant_pg
|
||||
AS SELECT
|
||||
ON admin.v_customers_pg
|
||||
ON admin.v_dds_customers_pg
|
||||
TO both_sources_role;
|
||||
|
||||
CREATE DATA GRANT admin.dds_both_grant_mysql
|
||||
AS SELECT
|
||||
ON admin.v_customers_my
|
||||
ON admin.v_dds_customers_my
|
||||
TO both_sources_role;
|
||||
|
||||
-- DDSUSER_NONE: NO data grant -> default deny.
|
||||
@@ -121,7 +145,7 @@ PROMPT === 5. (Optional) Region row-level filter — DDS-style ===
|
||||
--
|
||||
-- CREATE OR REPLACE DATA GRANT admin.dds_both_grant_pg
|
||||
-- AS SELECT
|
||||
-- ON admin.v_customers_pg
|
||||
-- ON admin.v_dds_customers_pg
|
||||
-- WHERE region = 'APAC'
|
||||
-- TO both_sources_role;
|
||||
--
|
||||
@@ -130,7 +154,7 @@ PROMPT === 5. (Optional) Region row-level filter — DDS-style ===
|
||||
--
|
||||
-- CREATE OR REPLACE DATA GRANT admin.dds_both_grant_mysql
|
||||
-- AS SELECT
|
||||
-- ON admin.v_customers_my
|
||||
-- ON admin.v_dds_customers_my
|
||||
-- WHERE region IN ('APAC','EMEA')
|
||||
-- TO both_sources_role;
|
||||
|
||||
@@ -142,7 +166,7 @@ PROMPT === 6. (Optional) Column masking — DDS-style ===
|
||||
--
|
||||
-- CREATE OR REPLACE DATA GRANT admin.dds_both_grant_pg
|
||||
-- AS SELECT (ALL COLUMNS EXCEPT email)
|
||||
-- ON admin.v_customers_pg
|
||||
-- ON admin.v_dds_customers_pg
|
||||
-- TO both_sources_role;
|
||||
--
|
||||
-- Excluded columns return NULL (same UX as redaction), but it's
|
||||
@@ -154,8 +178,10 @@ PROMPT === DDS variant ready ===
|
||||
-- Verify with:
|
||||
-- sqlplus '"ddsuser_pg"'/<pw>@<service>
|
||||
-- SQL> SELECT ORA_END_USER_CONTEXT.username FROM dual;
|
||||
-- SQL> SELECT COUNT(*) FROM admin.v_customers_pg; -- expect 12
|
||||
-- SQL> SELECT COUNT(*) FROM admin.v_customers_my; -- expect 0 (no grant)
|
||||
-- SQL> SELECT COUNT(*) FROM admin.v_dds_customers_pg; -- expect 12
|
||||
-- SQL> SELECT COUNT(*) FROM admin.v_dds_customers_my; -- expect ORA-00942
|
||||
-- (DDS hides the object entirely when no grant exists — note this is
|
||||
-- stronger than VPD which would return "0 rows" for the same case.)
|
||||
--
|
||||
-- Audit the grants:
|
||||
-- SELECT * FROM dba_data_grants;
|
||||
|
||||
Reference in New Issue
Block a user