...
Tags can be used to classify views or columns in Denodo to make browsing easier, or for application of security policies. Here is a guide to Tags in Denodo: https://community.denodo.com/kb/en/view/document/How%20to%20use%20Denodo%20Tags
...
Note that in order for new tags to appear in the Data Catalog, they must be manually synced once in each Data Catalog environment after they’re created/deployed, after which they will update automatically as changes are made and deployed.
...
While data classification is still a work in progress at Tufts, below is a table of some draft classifications.
Data Class → | Generally Available (Green) | Confidential (Yellow) | Restricted (Red) |
---|---|---|---|
Audience | All data access roles (not public) | Dean/Division Leader Role | Reporting and Compliance Roles (University-wide access as needed) Others must provide justification for use |
Identified data | Reporting and Compliance Roles (University-wide access) | ||
Types of Data | •Name •Title (fac/staff) / Level (students) •HR Title (fac/staff) •Department / Program •Supervisor •Campus •Pronouns (user provided) •Email address •Affiliations (faculty/staff/student/...) •Classification (temp, post doc, RA, staff/grade, faculty) | •Compensation •Course registrations •Age range (..., 25-35, 36-45, …) •Financial transaction data •Leave/return dates •Service dates •Space assignments •Grant proposals and awards •Enrollment status •Home address •Local address •Alum/donor name/contact info •Citizenship | •Race/ethnicity •Gender identity •Religion •PHI •Marital status •Date of birth •Benefit selections •Admissions decisions (before release) •Grades •Alum/donor gift history •Social security number •Driver’s license number •Passport number •Visa/Citizenship info |
Tags, policies and roles have been added for Confidential and Restricted in the dev environment for testing. Ultimately, roles will exist in Grouper where their membership can be managed.
...
Restricted
VQL
Code Block |
---|
CREATE OR REPLACE ROLE confidentialcls_restricted '' GRANT CONNECT, METADATA, EXECUTE ON test ; CREATE OR REPLACE TAG confidential DESCRIPTION = 'Used to tag views or columns that fall under the Confidential data definition'; ROLE cls_restricted_dob '' GRANT CONNECT, METADATA, EXECUTE ON test; CREATE OR REPLACE ROLE cls_restricted_gender '' GRANT CONNECT, METADATA, EXECUTE ON test; CREATE OR REPLACE GLOBAL_SECURITY_POLICY mask_confidential_columns DESCRIPTION = 'For non-RLS views, masks all columns with the confidential tag for non-admins without the confidential role' ENABLED = TRUE AUDIENCE ( ROLE cls_restricted_race '' GRANT CONNECT, METADATA, EXECUTE ON test; CREATE OR REPLACE TAG cls DESCRIPTION = 'Tags views and columns that have CLS (column level security) applied'; CREATE OR REPLACE TAG cls_restricted DESCRIPTION = 'Tags NOT_IN ROLES (confidential, "grp_ds_denodo-admin", restricted) ) ELEMENTS (columns with restricted data that have column-level security (CLS) applied.'; CREATE OR REPLACE TAG cls_restricted_dob DESCRIPTION = 'Tags columns with VIEWSrestricted NOTdata TAGGEDof birth (rls_deptDOB) data to have column-level security (CLS) applied'; CREATE OR RESTRICTIONREPLACE (TAG cls_restricted_gender FILTERDESCRIPTION = ''Tags columns with restricted gender data to have MASKING ANYcolumn-level security (confidentialCLS) WITH (HIDE) (numbers WITH DEFAULT, datetimes WITH DEFAULT, texts WITH DEFAULT)applied'; CREATE OR REPLACE TAG cls_restricted_race DESCRIPTION ); |
...
Restricted
VQL
Code Block |
---|
CREATE OR REPLACE ROLE= 'Tags columns with restricted ''race data to GRANT CONNECT, METADATA, EXECUTE ON test;have column-level security (CLS) applied'; CREATE OR REPLACE TAG restrictedGLOBAL_SECURITY_POLICY cls_restricted_dob DESCRIPTION = 'UsedMasks toall tagcolumns viewswith or columns whose data fall under the Restricted definition' CREATE OR REPLACE GLOBAL_SECURITY_POLICY mask_restricted_columnsthe cls_restricted_dob tag for non-admins without the cls_restricted or cls_restricted_dob role' DESCRIPTIONENABLED = TRUE 'For non-RLS views, masks allAUDIENCE columns( with the confidential tag for non-admins without the confidential role' NOT_IN ROLES (cls_restricted, cls_restricted_dob) ENABLED = TRUE) AUDIENCEELEMENTS ( NOT_IN ROLES ("grp_ds_denodo-admin", restricted)ALL VIEWS ) ELEMENTSRESTRICTION ( VIEWS NOT TAGGED (rls_dept) ) RESTRICTION ( FILTER = '' MASKING ANY (cls_restricted_dob) WITH (HIDE) (numbers WITH DEFAULT, datetimes WITH DEFAULT, texts WITH DEFAULT) ); |
...
This can be expanded upon by leveraging user attributes from LDAP/SAML: https://community.denodo.com/kb/en/view/document/How%20to%20implement%20ABAC%20Capabilities%20with%20Global%20Security%20Policies
Row-level Security (RLS)
In addition to column-level security, you can employ row-level security and limit the rows users can see. The VQL below will create policies to hide all rows for all users except ones with the read_test
, data_strategy
or iam
roles. They will also hide
VQL
Code Block |
---|
CREATE OR REPLACE ROLE data_strategy '' GRANT CONNECT, METADATA, EXECUTE ON test; CREATE OR REPLACE ROLE iam '' GRANT CONNECT, METADATA, EXECUTE ON test; CREATE OR REPLACE ROLE read_test '' GRANT CONNECT, METADATA, EXECUTE ON test; CREATE OR REPLACE ROLE general '' GRANT CONNECT, METADATA, EXECUTE ON test; CREATE OR REPLACE TAG rls_dept DESCRIPTION = 'Used to tag views that should have RLS applied based on department'; CREATE OR REPLACE GLOBAL_SECURITY_POLICY rls_hide_all DESCRIPTION = 'Hides all rows for RLS-enabled views' ENABLED = TRUE AUDIENCE ( NOT_IN ROLES (data_strategy, iam, read_test) ) ELEMENTS CREATE OR REPLACE GLOBAL_SECURITY_POLICY cls_restricted_gender DESCRIPTION = 'Masks all columns with the cls_restricted_gender tag for non-admins without the cls_restricted or cls_restricted_gender role' ENABLED = TRUE AUDIENCE ( COLUMNS TAGGED ANY (rls_deptNOT_IN ROLES (cls_restricted, cls_restricted_gender) ) RESTRICTIONELEMENTS ( FILTERALL =VIEWS '0=1' ) REJECTRESTRICTION ( ); CREATE ORFILTER REPLACE GLOBAL_SECURITY_POLICY rls_dept_data_strategy= '' DESCRIPTION = 'RLS department policyMASKING for data strategy' ENABLED = TRUE AUDIENCE (ANY (cls_restricted_gender) WITH (HIDE) (numbers WITH DEFAULT, datetimes WITH DEFAULT, texts WITH DEFAULT) ); CREATE OR REPLACE ANY ROLES (data_strategy)GLOBAL_SECURITY_POLICY cls_restricted_race DESCRIPTION )= 'Masks all columns with ELEMENTS ( COLUMNS TAGGED ANY (rls_dept) ) RESTRICTIONthe cls_restricted_race tag for non-admins without the cls_restricted or cls_restricted_race role' ENABLED = TRUE AUDIENCE ( FILTERNOT_IN =ROLES 'rls_dept = ''DS'''(cls_restricted, cls_restricted_race) ) REJECT ELEMENTS ( ); CREATE OR REPLACE GLOBAL_SECURITY_POLICY rls_dept_iam ALL VIEWS DESCRIPTION = 'RLS) department policy for data strategy'RESTRICTION ( ENABLED = TRUE FILTER = AUDIENCE'' ( MASKING ANY ROLES (iamcls_restricted_race) WITH (HIDE) (numbers WITH )DEFAULT, datetimes WITH DEFAULT, ELEMENTStexts (WITH DEFAULT) ); COLUMNS TAGGED ANY (rls_dept) |
...
Confidential
VQL
Code Block |
---|
CREATE OR REPLACE ROLE cls_confidential '' GRANT )CONNECT, METADATA, EXECUTE ON test; RESTRICTION ( CREATE OR REPLACE TAG cls_confidential FILTER = 'rls_deptDESCRIPTION = ''IAM'''Used to tag columns that fall under the Confidential REJECTdata definition to apply column-level security (CLS)'; CREATE OR REPLACE GLOBAL_SECURITY_POLICY rls_dept_mask_restrictedcls_confidential DESCRIPTION = 'Applies Masks all columns with the cls_confidential CLStag tofor department-based RLS viewsnon-admins without the cls_confidential role' ENABLED = TRUE AUDIENCE ( ANYNOT_IN ROLES (data_strategycls_confidential, cls_restricted) ) ELEMENTS ( ALL VIEWS TAGGED ANY (rls_dept) ) RESTRICTION ( FILTER = '' MASKING ANY (restrictedcls_confidential) WITH (HIDE) (numbers WITH DEFAULT, datetimes WITH DEFAULT, texts WITH DEFAULT) ); |
...
This can be expanded upon by leveraging user attributes from LDAP/SAML: https://community.denodo.com/kb/en/view/document/How%20to%20implement%20ABAC%20Capabilities%20with%20Global%20Security%20Policies
Demo
...
);
|
...
Row-level Security (RLS)
In addition to column-level security, you can employ row-level security and limit the rows users can see. The VQL below will create a policy and roles for hiding rows based on department.
VQL
Code Block |
---|
CREATE OR REPLACE VIEWROLE cls_test FOLDER = '/02-transformation'( rls_data_strategy '' GRANT CONNECT, METADATA, EXECUTE ON test; CREATE OR REPLACE ROLE rls_iam '' GRANT CONNECT, METADATA, EXECUTE ON test; CREATE hire_dtOR REPLACE ROLE general '' GRANT TAGS(confidential)CONNECT, METADATA, EXECUTE ON test; CREATE OR termination_dtREPLACE TAG rls DESCRIPTION TAGS(confidential), annual_rt= 'Used to tag views and columns with RLS applied'; CREATE OR REPLACE TAGS(confidential),TAG rls_dept DESCRIPTION = 'Used to tag date_of_birthcolumns with department values for application of row-level security TAGS(restrictedRLS),'; CREATE OR REPLACE TAG rls_assigned_roles gender DESCRIPTION = 'Used to tag column containing TAGS(restricted),a users assigned roles'; CREATE OR REPLACE race GLOBAL_SECURITY_POLICY rls_dept DESCRIPTION = TAGS(restricted) ) AS 'Limits rows based on departments associated with users assigned roles' SELECT '0001' ASENABLED emplid,= TRUE AUDIENCE ( 'John Doe' AS name, ALL ) ELEMENTS cast('2024-11-01' AS TIMESTAMP) AS hire_dt, COLUMNS TAGGED ANY (rls_dept) cast('2024-11-02' AS TIMESTAMP) AS termination_dt,RESTRICTION ( FILTER 100000.0 AS annual_rt, cast('1990-01-01' AS DATE) AS date_of_birth, 'Male' AS gender,= '((strings_have_intersection(''rls_iam,grp_ds_denodo-ir-read'', rls_assigned_roles, '','') AND rls_dept = ''IAM'') OR (strings_have_intersection(''rls_data_strategy,grp_ds_denodo-ir-read'', rls_assigned_roles, '','') AND rls_dept = ''DS''))' REJECT 'Unknown' AS race FROM dual(); ) |
...
This can be expanded upon by leveraging user attributes from LDAP/SAML: https://community.denodo.com/kb/en/view/document/How%20to%20implement%20ABAC%20Capabilities%20with%20Global%20Security%20Policies
Demo
Code Block |
---|
CREATE OR REPLACE VIEW rlssecurity_test FOLDER = '/02-transformation' tagsTAGS (cls, rls_dept) ( dept tagsTAGS (rls, rls_dept), hire_dt TAGS (cls, cls_confidential), termination_dt TAGS ( (cls, cls_confidential), annual_rt TAGS (cls, cls_confidential), date_of_birth TAGS (cls, cls_restricted, cls_restricted_dob), gender TAGS (cls, cls_restricted, cls_restricted_gender), race TAGS (cls, cls_restricted, cls_restricted_race TAGS (restricted), assigned_roles TAGS (rls_assigned_roles) ) AS with st_union as ( SELECT '0001' AS emplid, 'John Doe' AS name, 'DS' AS dept, cast('2024-11-01' AS TIMESTAMP) AS hire_dt, cast('2024-11-02' AS TIMESTAMP) AS termination_dt, 100000.0 AS annual_rt, cast('1990-01-01' AS DATETIMESTAMP) AS datetermination_of_birthdt, 'Male'100000.0 AS genderannual_rt, 'Unknowncast('1990-01-01' AS race FROM dual(DATE) AS date_of_birth, UNION ALL SELECT '0002Male' AS emplid, gender, 'John Doe' AS name, 'Unknown' AS race 'IAM' AS dept,FROM dual() UNION ALL SELECT cast('2024-11-01'0002' AS TIMESTAMP) AS hire_dtemplid, cast('2024-11-02'John Doe' AS TIMESTAMP) AS termination_dtname, 100000.0'IAM' AS annual_rtdept, cast('19902024-0111-01' AS DATETIMESTAMP) AS datehire_of_birthdt, 'Male'cast('2024-11-02' AS TIMESTAMP) AS gendertermination_dt, 'Unknown'100000.0 AS race annual_rt, FROM dual() ) select * from st_union CONTEXT ('formatted' = 'yes'); |
...
The queries below demonstrate combinations of CLS and RLS
General
Code Block |
---|
select * from test.cls_test context('impersonate_roles' = 'general'); |
...
Confidential
Code Block |
---|
select * from test.cls_test context('impersonate_roles' = 'confidential'); |
...
Restricted
Note that the confidential role has been added to restricted so it can satisfy any confidential policy restriction as well
Code Block |
---|
select * from test.cls_test context('impersonate_roles' = 'restricted'); |
...
Department RLS, DS, Confidential
Code Block |
---|
select * from test.cls_test context('impersonate_roles'='data_strategy'); |
...
cast('1990-01-01' AS DATE) AS date_of_birth,
'Male' AS gender,
'Unknown' AS race
FROM dual()
)
select st_union.*, roles.assigned_roles from st_union left join admin.get_user_roles_string roles on 1=1 CONTEXT ('formatted' = 'yes'); |
...
The queries below demonstrate combinations of CLS and RLS
General
A user without any CLS or RLS roles
Code Block |
---|
select * from test.rlssecurity_test context('impersonate_roles'='iamgeneral'); |
...
...
Admin
Admin users and roles bypass global security polices so are able to see all rows and columns
Code Block |
---|
select * from test.security_test context('impersonate_roles'='data_strategy,iam'); |
...
grp_ds_denodo-admin'); |
...
Read all, confidential
A role that has “read all” access to rows, coupled with the confidential role
Code Block |
---|
select * from test.rlssecurity_test context('impersonate_roles'='grp_ds_denodo-ir-read,cls_testconfidential'); |
...
Department
...
role, restricted DOB
A department-specific role, with access to only restricted the DOB column
Code Block |
---|
select * from test.rlssecurity_test context('impersonate_roles'='general'); |
...
Info |
---|
Combining role-based policies for both CLS and RLS together is tricky in Denodo unless the RLS limiting role is also included in an appropriate CLS policy. To prevent role clashing, and simplify implementation, it’s best to implement RLS using session attributes, and CLS using roles. If a user has a role that isn’t included in the list of included roles of a policy, and that role has execute access to a view, they can bypass that policy for that view. Also, in order for a role in a list of excluded roles in a policy to be effective, it must have the execute privilege on the views in the policy.'rls_data_strategy,cls_restricted_dob'); |
...
Multiple department roles, restricted and implied confidential access
Code Block |
---|
select
*
from test.security_test
context('impersonate_roles'='rls_data_strategy,rls_iam,cls_restricted'); |
...
Caching
Denodo applies policies on top of cached data. Since policies do not apply to Administrator user types, users with the serveradmin
role, or admins of individual VDB’s, it’s recommended that an admin user be used for caching jobs since this will guarantee that no policies are applied during caching.
...
Note that if you have multiple data sources, users will be prompted for credentials for each data source. Thankfully Tableau can remember passwords for each user and data source and it’s recommended that users do this. Note that Tableau hashes credentials for a data source based on the server name NOT the data source name. Because of this, if you have multiple data sources based on the same server, Tableau will use the last set of credentials you enter for that server.
...
The above approach does require that data sources be Live. Because of this, all views should be optimized and cached to ensure adequate performance.
...
Tableau does not currently have an easy way to check for membership against a list of multiple groups.
Other Tableau User Function can be found here: https://help.tableau.com/current/pro/desktop/en-us/functions_functions_user.htm
...