Skip to content

Commit 9a5f155

Browse files
committed
Support for managing access grants
- Implemented macros oracle__get_show_grant_sql() and oracle__call_dcl_statements() - Updated materializations to fetch and update grants - Added new testcases for test grants - Updated Github actions workflow to create new test users DBT_TEST_USER_1, DBT_TEST_USER_2 and DBT_TEST_USER_3
1 parent 96d672a commit 9a5f155

File tree

10 files changed

+167
-9
lines changed

10 files changed

+167
-9
lines changed

.github/workflows/oracle-xe-adapter-tests.yml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,17 @@ jobs:
3838
chmod +x ${{ github.workspace }}/.github/scripts/create_new_user.sh
3939
docker cp ${{ github.workspace }}/.github/scripts/create_new_user.sh oracle_db_xe:/home/oracle/create_new_user.sh
4040
41-
- name: Create dbt test user
41+
- name: Create dbt test users
4242
run: |
4343
docker exec oracle_db_xe /home/oracle/create_new_user.sh dbt_test ${{ secrets.DBT_ORACLE_PASSWORD }}
44+
docker exec oracle_db_xe /home/oracle/create_new_user.sh dbt_test_user_1 ${{ secrets.DBT_ORACLE_PASSWORD }}
45+
docker exec oracle_db_xe /home/oracle/create_new_user.sh dbt_test_user_2 ${{ secrets.DBT_ORACLE_PASSWORD }}
46+
docker exec oracle_db_xe /home/oracle/create_new_user.sh dbt_test_user_3 ${{ secrets.DBT_ORACLE_PASSWORD }}
4447
4548
- name: Install dbt-oracle with core dependencies
4649
run: |
4750
python -m pip install --upgrade pip
48-
pip install pytest dbt-tests-adapter==1.2.0
51+
pip install pytest dbt-tests-adapter==1.2.1
4952
pip install -r requirements.txt
5053
pip install -e .
5154
@@ -68,6 +71,9 @@ jobs:
6871
DBT_ORACLE_PROTOCOL: tcp
6972
LD_LIBRARY_PATH: /opt/oracle/instantclient_21_6
7073
TNS_ADMIN: /opt/tns_admin
74+
DBT_TEST_USER_1: DBT_TEST_USER_1
75+
DBT_TEST_USER_2: DBT_TEST_USER_2
76+
DBT_TEST_USER_3: DBT_TEST_USER_3
7177

7278
- name: Run adapter tests - ORA_PYTHON_DRIVER_TYPE => THICK
7379
run: |
@@ -84,6 +90,9 @@ jobs:
8490
DBT_ORACLE_PROTOCOL: tcp
8591
LD_LIBRARY_PATH: /opt/oracle/instantclient_21_6
8692
TNS_ADMIN: /opt/tns_admin
93+
DBT_TEST_USER_1: DBT_TEST_USER_1
94+
DBT_TEST_USER_2: DBT_TEST_USER_2
95+
DBT_TEST_USER_3: DBT_TEST_USER_3
8796

8897
- name: Run adapter tests - ORA_PYTHON_DRIVER_TYPE => THIN
8998
run: |
@@ -100,3 +109,7 @@ jobs:
100109
DBT_ORACLE_PROTOCOL: tcp
101110
DISABLE_OOB: on
102111
TNS_ADMIN: /opt/tns_admin
112+
DBT_TEST_USER_1: DBT_TEST_USER_1
113+
DBT_TEST_USER_2: DBT_TEST_USER_2
114+
DBT_TEST_USER_3: DBT_TEST_USER_3
115+

dbt/adapters/oracle/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@
1414
See the License for the specific language governing permissions and
1515
limitations under the License.
1616
"""
17-
version = "1.2.0"
17+
version = "1.2.1"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{#
2+
Copyright (c) 2022, Oracle and/or its affiliates.
3+
Copyright (c) 2020, Vitor Avancini
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
https://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
#}
17+
18+
{% macro oracle__get_show_grant_sql(relation) %}
19+
{# SQL that returns the current grants (grantee-privilege pairs) #}
20+
SELECT grantee as "grantee", privilege as "privilege_type"
21+
FROM SYS.ALL_TAB_PRIVS
22+
WHERE UPPER(table_name) = UPPER('{{ relation.identifier }}')
23+
{% if relation.schema %}
24+
AND UPPER(table_schema) = UPPER('{{ relation.schema }}')
25+
{% endif %}
26+
{% endmacro %}
27+
28+
{% macro oracle__call_dcl_statements(dcl_statement_list) %}
29+
{# Run each grant/revoke statement against the database. This is the culmination of apply_grants() #}
30+
{% for dcl_statement in dcl_statement_list %}
31+
{% do run_query(dcl_statement) %}
32+
{% endfor %}
33+
{% endmacro %}

dbt/include/oracle/macros/materializations/incremental/incremental.sql

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
{% set existing_relation = load_relation(this) %}
2424
{% set tmp_relation = make_temp_relation(this) %}
2525
{% set on_schema_change = incremental_validate_on_schema_change(config.get('on_schema_change'), default='ignore') %}
26-
26+
{% set grant_config = config.get('grants') %}
2727

2828
{{ run_hooks(pre_hooks, inside_transaction=False) }}
2929

@@ -77,6 +77,9 @@
7777

7878
{{ run_hooks(post_hooks, inside_transaction=False) }}
7979

80+
{% set should_revoke = should_revoke(existing_relation.is_table, full_refresh_mode) %}
81+
{% do apply_grants(target_relation, grant_config, should_revoke=should_revoke) %}
82+
8083
{{ return({'relations': [target_relation]}) }}
8184

8285
{%- endmaterialization %}

dbt/include/oracle/macros/materializations/snapshot/snapshot.sql

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@
208208

209209
{%- set strategy_name = config.get('strategy') -%}
210210
{%- set unique_key = config.get('unique_key') %}
211-
211+
{%- set grant_config = config.get('grants') -%}
212212
{% set model_database = model.database %}
213213
{% if model_database == 'None' or model_database is undefined or model_database is none %}
214214
{% set model_database = get_database_name() %}
@@ -285,6 +285,9 @@
285285
{{ final_sql }}
286286
{% endcall %}
287287

288+
{% set should_revoke = should_revoke(target_relation_exists, full_refresh_mode=False) %}
289+
{% do apply_grants(target_relation, grant_config, should_revoke=should_revoke) %}
290+
288291
{% do persist_docs(target_relation, model) %}
289292

290293
{{ run_hooks(post_hooks, inside_transaction=True) }}

dbt/include/oracle/macros/materializations/table/table.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#}
1717
{% materialization table, adapter='oracle' %}
1818
{% set identifier = model['alias'] %}
19+
{% set grant_config = config.get('grants') %}
1920
{% set tmp_identifier = model['name'] + '__dbt_tmp' %}
2021
{% set backup_identifier = model['name'] + '__dbt_backup' %}
2122
{% set old_relation = adapter.get_relation(database=database, schema=schema, identifier=identifier) %}
@@ -90,5 +91,8 @@
9091

9192
{{ run_hooks(post_hooks, inside_transaction=False) }}
9293

94+
{% set should_revoke = should_revoke(old_relation, full_refresh_mode=True) %}
95+
{% do apply_grants(target_relation, grant_config, should_revoke=should_revoke) %}
96+
9397
{{ return({'relations': [target_relation]}) }}
9498
{% endmaterialization %}

dbt/include/oracle/macros/materializations/view/view.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
{%- materialization view, adapter='oracle' -%}
1818

1919
{%- set identifier = model['alias'] -%}
20+
{%- set grant_config = config.get('grants') -%}
2021
{%- set backup_identifier = model['name'] + '__dbt_backup' -%}
2122

2223
{%- set old_relation = adapter.get_relation(database=database, schema=schema, identifier=identifier) -%}
@@ -71,6 +72,9 @@
7172

7273
{{ run_hooks(post_hooks, inside_transaction=False) }}
7374

75+
{% set should_revoke = should_revoke(old_relation, full_refresh_mode=True) %}
76+
{% do apply_grants(target_relation, grant_config, should_revoke=should_revoke) %}
77+
7478
{{ return({'relations': [target_relation]}) }}
7579

7680
{%- endmaterialization -%}

setup.cfg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ zip_safe = False
3232
packages = find:
3333
include_package_data = True
3434
install_requires =
35-
dbt-core==1.2.0
35+
dbt-core==1.2.1
3636
cx_Oracle==8.3.0
3737
oracledb==1.0.3
3838
test_suite=tests
3939
test_requires =
40-
dbt-tests-adapter==1.2.0
40+
dbt-tests-adapter==1.2.1
4141
pytest
4242
scripts =
4343
bin/create-pem-from-p12

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@
3232

3333

3434
requirements = [
35-
"dbt-core==1.2.0",
35+
"dbt-core==1.2.1",
3636
"cx_Oracle==8.3.0",
3737
"oracledb==1.0.3"
3838
]
3939

4040
test_requirements = [
41-
"dbt-tests-adapter==1.2.0",
41+
"dbt-tests-adapter==1.2.1",
4242
"pytest"
4343
]
4444

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
"""
2+
Copyright (c) 2022, Oracle and/or its affiliates.
3+
Copyright (c) 2020, Vitor Avancini
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
https://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
"""
17+
18+
import pytest
19+
20+
from dbt.tests.adapter.grants.test_model_grants import BaseModelGrants, model_schema_yml
21+
from dbt.tests.adapter.grants.test_incremental_grants import BaseIncrementalGrants, incremental_model_schema_yml
22+
from dbt.tests.adapter.grants.test_invalid_grants import BaseInvalidGrants
23+
from dbt.tests.adapter.grants.test_seed_grants import BaseSeedGrants
24+
from dbt.tests.adapter.grants.test_snapshot_grants import BaseSnapshotGrants, snapshot_schema_yml
25+
26+
my_model_sql = """
27+
select 1 as fun from dual
28+
"""
29+
30+
my_incremental_model_sql = """
31+
select 1 as fun from dual
32+
"""
33+
34+
my_invalid_model_sql = """
35+
select 1 as fun from dual
36+
"""
37+
38+
my_snapshot_sql = """
39+
{% snapshot my_snapshot %}
40+
{{ config(
41+
check_cols='all', unique_key='id', strategy='check',
42+
target_database=database, target_schema=schema
43+
) }}
44+
select 1 as id, cast('blue' as {{ type_string() }}) as color from dual
45+
{% endsnapshot %}
46+
""".strip()
47+
48+
49+
class TestSeedGrantsOracle(BaseSeedGrants):
50+
pass
51+
52+
53+
class TestModelGrantsOracle(BaseModelGrants):
54+
55+
@pytest.fixture(scope="class")
56+
def models(self):
57+
updated_schema = self.interpolate_name_overrides(model_schema_yml)
58+
59+
return {
60+
"my_model.sql": my_model_sql,
61+
"schema.yml": updated_schema,
62+
}
63+
64+
65+
class TestIncrementalGrantsOracle(BaseIncrementalGrants):
66+
67+
@pytest.fixture(scope="class")
68+
def models(self):
69+
updated_schema = self.interpolate_name_overrides(incremental_model_schema_yml)
70+
return {
71+
"my_incremental_model.sql": my_incremental_model_sql,
72+
"schema.yml": updated_schema,
73+
}
74+
75+
76+
class TestInvalidGrantsOracle(BaseInvalidGrants):
77+
78+
def grantee_does_not_exist_error(self):
79+
return "ORA-01917: user or role"
80+
81+
def privilege_does_not_exist_error(self):
82+
return "ORA-00990: missing or invalid privilege"
83+
84+
@pytest.fixture(scope="class")
85+
def models(self):
86+
return {
87+
"my_invalid_model.sql": my_invalid_model_sql,
88+
}
89+
90+
91+
class TestSnapshotGrantsOracle(BaseSnapshotGrants):
92+
93+
@pytest.fixture(scope="class")
94+
def snapshots(self):
95+
return {
96+
"my_snapshot.sql": my_snapshot_sql,
97+
"schema.yml": self.interpolate_name_overrides(snapshot_schema_yml),
98+
}

0 commit comments

Comments
 (0)