Yanli Dong
Yanli Dong

Reputation: 37

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!

Upvotes: 3

Views: 561

Answers (2)

Lukasz Szozda
Lukasz Szozda

Reputation: 175556

The previous answer could be improved by using new feature: Tag-based Masking Policies:

A tag-based masking policy combines the object tagging and masking policy features to allow a masking policy to be set on a tag using an ALTER TAG command. When the data type in the masking policy signature and the data type of the column match, the tagged column is automatically protected by the conditions in the masking policy. This simplifies the data protection efforts because column data that should be protected no longer needs a masking policy manually applied to the column to protect the data. A column can be protected by a masking policy directly assigned to a column and a tag-based masking policy

SYSTEM$GET_TAG_ON_CURRENT_COLUMN:

SYSTEM$GET_TAG_ON_CURRENT_COLUMN( '<tag_name>' )

Returns the tag string value assigned to the column based upon the specified tag or NULL if a tag is not assigned to the specified column.


For this scenario:

CREATE OR REPLACE TAG TAG_NAME;

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

ALTER TAG TAG_NAME SET MASKING POLICY TAGS_MASKING;

Table:

CREATE OR REPLACE TABLE mytable(col1 STRING); 
INSERT INTO mytable(col1) VALUES ('Test');
SELECT * FROM mytable;
-- COL1
-- Test

Assigning tag:

ALTER TABLE mytable ALTER COLUMN col1 SET TAG TAG_NAME='PROTECTED';

Role outside ADMIN_ROLE/ANALYST_ROLE:

SELECT * FROM mytable;
-- COL1
-- null

Switching to ANALYST_ROLE(column tagged with PROTECTED value):

USE ROLE ANALYST_ROLE;

SELECT * FROM mytable;
-- COL1
-- ****MASKED****

Upvotes: 1

Lukasz Szozda
Lukasz Szozda

Reputation: 175556

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****

Upvotes: 2

Related Questions