Search code examples
snowflake-cloud-data-platformmaskingtagging

Snowflake masking policy: can the input be a constant string variable?


I am trying to create a masking policy with tagging:

CREATE OR REPLACE MASKING POLICY TAGS_MASKING
AS (val VARCHAR, col_name STRING) RETURNS VARCHAR ->
CASE        
   WHEN CURRENT_ROLE() IN ('ADMIN_ROLE') THEN val
   WHEN CURRENT_ROLE() IN ('ANALYST_ROLE') AND (SELECT SYSTEM$GET_TAG('TAG_NAME', col_name , 'COLUMN') = 'PUBLIC') THEN val
   WHEN CURRENT_ROLE() IN ('ANALYST_ROLE') AND (SELECT SYSTEM$GET_TAG('TAG_NAME', col_name , 'COLUMN') IN ('PROTECTED')) THEN '****MASKED****'
 END; 

Here, col_name is a string (e.g. 'mytable.col1'), so that I can assign this masking policy to any columns I want to. But when I used the following query to assign it to one column of one table, it failed:

ALTER TABLE IF EXISTS db.masking.mytable MODIFY COLUMN col1
SET MASKING POLICY TAGS_MASKING using (col1, 'mytable.col1');

The error message is:

Syntax error: unexpected "mytable.col1"

How should I figure this out? Thanks!


Solution

  • I have not found a way to parametrize the column name(passing it as optional second parameter) so I have used a differnt approach.

    It automatically creates a masking policy per colum using Snowflake Scripting.

    Setup:

    CREATE OR REPLACE TAG TAG_NAME;
    
    CREATE OR REPLACE TABLE mytable(col1 STRING); 
    ALTER TABLE mytable SET TAG TAG_NAME='PUBLIC';
    
    INSERT INTO mytable(col1) VALUES ('Test');
    
    SELECT * FROM mytable;
    -- Test
    

    Procedure:

     CREATE OR REPLACE PROCEDURE test(schema_name STRING, tab_name STRING, col_name STRING)
     RETURNS STRING
     LANGUAGE SQL
     AS
     $$
     DECLARE 
       sql_masking_policy   STRING;
       sql_alter_table      STRING;
       masking_policy_name  STRING := CONCAT_WS('_', 'TAGS_MASKING_', SCHEMA_NAME, TAB_NAME, COL_NAME);
     BEGIN
     sql_masking_policy := '
     CREATE OR REPLACE MASKING POLICY <masking_policy_name>
     AS (val VARCHAR) RETURNS VARCHAR ->
     CASE        
        WHEN CURRENT_ROLE() IN (''ADMIN_ROLE'') THEN val
        WHEN CURRENT_ROLE() IN (''ANALYST_ROLE'') AND (SYSTEM$GET_TAG(''TAG_NAME'', ''<col_name>'', ''COLUMN'') = ''PUBLIC'') THEN val
        WHEN CURRENT_ROLE() IN (''ANALYST_ROLE'') AND (SYSTEM$GET_TAG(''TAG_NAME'', ''<col_name>'', ''COLUMN'') IN (''PROTECTED'')) THEN ''****MASKED****''
     END;';
                                 
    sql_alter_table := 'ALTER TABLE IF EXISTS <tab_name> MODIFY COLUMN <col_name>
    SET MASKING POLICY <masking_policy_name>;';
    
    sql_masking_policy := REPLACE(sql_masking_policy, '<masking_policy_name>', :masking_policy_name);
    sql_masking_policy := REPLACE(sql_masking_policy, '<col_name>', CONCAT_WS('.', schema_name, tab_name, col_name));
    sql_alter_table    := REPLACE(sql_alter_table,    '<masking_policy_name>', :masking_policy_name);
    sql_alter_table    := REPLACE(sql_alter_table,    '<tab_name>', CONCAT_WS('.', schema_name, tab_name));
    sql_alter_table    := REPLACE(sql_alter_table,    '<col_name>', col_name);
    
    EXECUTE IMMEDIATE :sql_masking_policy;
    EXECUTE IMMEDIATE :sql_alter_table;
        
    RETURN sql_masking_policy || CHR(10) || sql_alter_table;
    END;
    $$;
    

    Call:

    CALL test('public', 'mytable', 'col1');
    

    Output:

    CREATE OR REPLACE MASKING POLICY TAGS_MASKING__public_mytable_col1
    AS (val VARCHAR) RETURNS VARCHAR ->                 
    CASE                         
    WHEN CURRENT_ROLE() IN ('ADMIN_ROLE') THEN val                 
    WHEN CURRENT_ROLE() IN ('ANALYST_ROLE') AND (SYSTEM$GET_TAG('TAG_NAME', 'public.mytable.col1', 'COLUMN') = 'PUBLIC') THEN val
    WHEN CURRENT_ROLE() IN ('ANALYST_ROLE') AND (SYSTEM$GET_TAG('TAG_NAME', 'public.mytable.col1', 'COLUMN') IN ('PROTECTED')) THEN '****MASKED****'
    END; 
      
    ALTER TABLE IF EXISTS public.mytable MODIFY COLUMN col1 SET MASKING POLICY TAGS_MASKING__public_mytable_col1; 
    

    Check:

    SHOW MASKING POLICIES;
    

    Output:

    enter image description here

    Test of select using POLICY_CONTEXT:

    execute using policy_context(current_role => 'PUBLIC')
    AS
    SELECT * FROM public.mytable;
    -- NULL
    
    execute using policy_context(current_role => 'ADMIN_ROLE')
    AS
    SELECT * FROM public.mytable;
    -- Test
    
    execute using policy_context(current_role => 'ANALYST_ROLE')
    AS
    SELECT * FROM public.mytable;
    -- Test
    
    ALTER TABLE mytable SET TAG TAG_NAME='PROTECTED';
    
    execute using policy_context(current_role => 'ANALYST_ROLE')
    AS
    SELECT * FROM public.mytable;
    -- ****MASKED****