|
| 1 | +{# ------- BOOLEAN MACROS --------- #} |
| 2 | + |
| 3 | +{# |
| 4 | + -- COPY GRANTS |
| 5 | + -- When a relational object (view or table) is replaced in this database, |
| 6 | + -- do previous grants carry over to the new object? This may depend on: |
| 7 | + -- whether we use alter-rename-swap versus CREATE OR REPLACE |
| 8 | + -- user-supplied configuration (e.g. copy_grants on Snowflake) |
| 9 | + -- By default, play it safe, assume TRUE: that grants ARE copied over. |
| 10 | + -- This means dbt will first "show" current grants and then calculate diffs. |
| 11 | + -- It may require an additional query than is strictly necessary, |
| 12 | + -- but better safe than sorry. |
| 13 | +#} |
| 14 | + |
| 15 | +{% macro copy_grants() %} |
| 16 | + {{ return(adapter.dispatch('copy_grants', 'dbt')()) }} |
| 17 | +{% endmacro %} |
| 18 | + |
| 19 | +{% macro default__copy_grants() %} |
| 20 | + {{ return(True) }} |
| 21 | +{% endmacro %} |
| 22 | + |
| 23 | + |
| 24 | +{# |
| 25 | + -- SUPPORT MULTIPLE GRANTEES PER DCL STATEMENT |
| 26 | + -- Does this database support 'grant {privilege} to {grantee_1}, {grantee_2}, ...' |
| 27 | + -- Or must these be separate statements: |
| 28 | + -- `grant {privilege} to {grantee_1}`; |
| 29 | + -- `grant {privilege} to {grantee_2}`; |
| 30 | + -- By default, pick the former, because it's what we prefer when available. |
| 31 | +#} |
| 32 | + |
| 33 | +{% macro support_multiple_grantees_per_dcl_statement() %} |
| 34 | + {{ return(adapter.dispatch('support_multiple_grantees_per_dcl_statement', 'dbt')()) }} |
| 35 | +{% endmacro %} |
| 36 | + |
| 37 | +{%- macro default__support_multiple_grantees_per_dcl_statement() -%} |
| 38 | + {{ return(True) }} |
| 39 | +{%- endmacro -%} |
| 40 | + |
| 41 | + |
| 42 | +{% macro should_revoke(existing_relation, full_refresh_mode=True) %} |
| 43 | + |
| 44 | + {% if not existing_relation %} |
| 45 | + {#-- The table doesn't already exist, so no grants to copy over --#} |
| 46 | + {{ return(False) }} |
| 47 | + {% elif full_refresh_mode %} |
| 48 | + {#-- The object is being REPLACED -- whether grants are copied over depends on the value of user config --#} |
| 49 | + {{ return(copy_grants()) }} |
| 50 | + {% else %} |
| 51 | + {#-- The table is being merged/upserted/inserted -- grants will be carried over --#} |
| 52 | + {{ return(True) }} |
| 53 | + {% endif %} |
| 54 | + |
| 55 | +{% endmacro %} |
| 56 | + |
| 57 | +{# ------- DCL STATEMENT TEMPLATES --------- #} |
| 58 | + |
| 59 | +{% macro get_show_grant_sql(relation) %} |
| 60 | + {{ return(adapter.dispatch("get_show_grant_sql", "dbt")(relation)) }} |
| 61 | +{% endmacro %} |
| 62 | + |
| 63 | +{% macro default__get_show_grant_sql(relation) %} |
| 64 | + show grants on {{ relation }} |
| 65 | +{% endmacro %} |
| 66 | + |
| 67 | + |
| 68 | +{% macro get_grant_sql(relation, privilege, grantees) %} |
| 69 | + {{ return(adapter.dispatch('get_grant_sql', 'dbt')(relation, privilege, grantees)) }} |
| 70 | +{% endmacro %} |
| 71 | + |
| 72 | +{%- macro default__get_grant_sql(relation, privilege, grantees) -%} |
| 73 | + grant {{ privilege }} on {{ relation }} to {{ grantees | join(', ') }} |
| 74 | +{%- endmacro -%} |
| 75 | + |
| 76 | + |
| 77 | +{% macro get_revoke_sql(relation, privilege, grantees) %} |
| 78 | + {{ return(adapter.dispatch('get_revoke_sql', 'dbt')(relation, privilege, grantees)) }} |
| 79 | +{% endmacro %} |
| 80 | + |
| 81 | +{%- macro default__get_revoke_sql(relation, privilege, grantees) -%} |
| 82 | + revoke {{ privilege }} on {{ relation }} from {{ grantees | join(', ') }} |
| 83 | +{%- endmacro -%} |
| 84 | + |
| 85 | + |
| 86 | +{# ------- RUNTIME APPLICATION --------- #} |
| 87 | + |
| 88 | +{% macro get_dcl_statement_list(relation, grant_config, get_dcl_macro) %} |
| 89 | + {{ return(adapter.dispatch('get_dcl_statement_list', 'dbt')(relation, grant_config, get_dcl_macro)) }} |
| 90 | +{% endmacro %} |
| 91 | + |
| 92 | +{%- macro default__get_dcl_statement_list(relation, grant_config, get_dcl_macro) -%} |
| 93 | + {# |
| 94 | + -- Unpack grant_config into specific privileges and the set of users who need them granted/revoked. |
| 95 | + -- Depending on whether this database supports multiple grantees per statement, pass in the list of |
| 96 | + -- all grantees per privilege, or (if not) template one statement per privilege-grantee pair. |
| 97 | + -- `get_dcl_macro` will be either `get_grant_sql` or `get_revoke_sql` |
| 98 | + #} |
| 99 | + {%- set dcl_statements = [] -%} |
| 100 | + {%- for privilege, grantees in grant_config.items() %} |
| 101 | + {%- if support_multiple_grantees_per_dcl_statement() and grantees -%} |
| 102 | + {%- set dcl = get_dcl_macro(relation, privilege, grantees) -%} |
| 103 | + {%- do dcl_statements.append(dcl) -%} |
| 104 | + {%- else -%} |
| 105 | + {%- for grantee in grantees -%} |
| 106 | + {% set dcl = get_dcl_macro(relation, privilege, [grantee]) %} |
| 107 | + {%- do dcl_statements.append(dcl) -%} |
| 108 | + {% endfor -%} |
| 109 | + {%- endif -%} |
| 110 | + {%- endfor -%} |
| 111 | + {{ return(dcl_statements) }} |
| 112 | +{%- endmacro %} |
| 113 | + |
| 114 | + |
| 115 | +{% macro call_dcl_statements(dcl_statement_list) %} |
| 116 | + {{ return(adapter.dispatch("call_dcl_statements", "dbt")(dcl_statement_list)) }} |
| 117 | +{% endmacro %} |
| 118 | + |
| 119 | +{% macro default__call_dcl_statements(dcl_statement_list) %} |
| 120 | + {# |
| 121 | + -- By default, supply all grant + revoke statements in a single semicolon-separated block, |
| 122 | + -- so that they're all processed together. |
| 123 | + |
| 124 | + -- Some databases do not support this. Those adapters will need to override this macro |
| 125 | + -- to run each statement individually. |
| 126 | + #} |
| 127 | + {% call statement('grants') %} |
| 128 | + {% for dcl_statement in dcl_statement_list %} |
| 129 | + {{ dcl_statement }}; |
| 130 | + {% endfor %} |
| 131 | + {% endcall %} |
| 132 | +{% endmacro %} |
| 133 | + |
| 134 | + |
| 135 | +{% macro apply_grants(relation, grant_config, should_revoke) %} |
| 136 | + {{ return(adapter.dispatch("apply_grants", "dbt")(relation, grant_config, should_revoke)) }} |
| 137 | +{% endmacro %} |
| 138 | + |
| 139 | +{% macro default__apply_grants(relation, grant_config, should_revoke=True) %} |
| 140 | + {#-- If grant_config is {} or None, this is a no-op --#} |
| 141 | + {% if grant_config %} |
| 142 | + {% if should_revoke %} |
| 143 | + {#-- We think previous grants may have carried over --#} |
| 144 | + {#-- Show current grants and calculate diffs --#} |
| 145 | + {% set current_grants_table = run_query(get_show_grant_sql(relation)) %} |
| 146 | + {% set current_grants_dict = adapter.standardize_grants_dict(current_grants_table) %} |
| 147 | + {% set needs_granting = diff_of_two_dicts(grant_config, current_grants_dict) %} |
| 148 | + {% set needs_revoking = diff_of_two_dicts(current_grants_dict, grant_config) %} |
| 149 | + {% if not (needs_granting or needs_revoking) %} |
| 150 | + {{ log('On ' ~ relation ~': All grants are in place, no revocation or granting needed.')}} |
| 151 | + {% endif %} |
| 152 | + {% else %} |
| 153 | + {#-- We don't think there's any chance of previous grants having carried over. --#} |
| 154 | + {#-- Jump straight to granting what the user has configured. --#} |
| 155 | + {% set needs_revoking = {} %} |
| 156 | + {% set needs_granting = grant_config %} |
| 157 | + {% endif %} |
| 158 | + {% if needs_granting or needs_revoking %} |
| 159 | + {% set revoke_statement_list = get_dcl_statement_list(relation, needs_revoking, get_revoke_sql) %} |
| 160 | + {% set grant_statement_list = get_dcl_statement_list(relation, needs_granting, get_grant_sql) %} |
| 161 | + {% set dcl_statement_list = revoke_statement_list + grant_statement_list %} |
| 162 | + {% if dcl_statement_list %} |
| 163 | + {{ call_dcl_statements(dcl_statement_list) }} |
| 164 | + {% endif %} |
| 165 | + {% endif %} |
| 166 | + {% endif %} |
| 167 | +{% endmacro %} |
0 commit comments