通过将安全视图分配给角色并提供针对多个条件的过滤器,在 Snowflake 上实现行级安全性 (RLS)

Posted

技术标签:

【中文标题】通过将安全视图分配给角色并提供针对多个条件的过滤器,在 Snowflake 上实现行级安全性 (RLS)【英文标题】:Achieve Row Level Security(RLS) on Snowflake by assigning Secure views to roles and provide filter on multiple conditions 【发布时间】:2021-04-24 06:32:51 【问题描述】:

假设我们有一张桌子水果的详细信息,

Country Fruit
USA Apple
India Mango
Italy Kiwi
Australia Guava

我们有 3 个角色,即 region1_roleregion2_roleglobal_role。 我希望region1_role 可以访问美国和澳大利亚的数据,region2_role 可以访问印度和意大利的数据,global_role 可以访问该国的所有数据。

我们当然可以使用安全视图来实现这一点,但是否可以只使用一个安全视图来实现?

【问题讨论】:

【参考方案1】:

我们当然可以使用安全视图来实现这一点但是否可以只使用一个安全视图来实现

我不确定我是否正确理解了您的问题,但视图是一种视图,您可以编写任何您想要的逻辑:

CREATE OR REPLACE SECURE VIEW Fruits_v
COPY GRANTS
AS
SELECT *
FROM Fruits
WHERE (current_role(), region) IN ( ('region1_role', 'USA',
                                   ,('region1_role', 'Australia')
                                   ,('region2_role', 'Italy')
                                   ,('region2_role', 'India')
                                  )
  OR current_role() = 'global_role';

悬而未决的问题是关于保持这种方法。添加新角色时,您需要更新视图定义,而不是向驱动表添加新行。


推荐阅读:Understanding Row-level Security 并使用策略方法。

【讨论】:

嘿@Lukasz!我想要实现的是让一个角色访问多个国家。我真的被你的做法吓到了。谢谢!我已经为此工作了一段时间,并提出了我自己的 2 个解决方案,我已将其发布为对此的答案。 这里面临的问题是,在更新视图之后,每次都必须将其访问权限授予所有角色,而在 javascript UDTF 或使用多对多关系表的情况下,我们只会更新 UDTF 或表。在我的情况下,PowerBI 在 DirectQuery 模式下查询视图。所以如果视图的定义改变了,就会导致PowerBI出错,直到被授予访问权限。 @MohammedTurky "The problem faced here would be that after updating the view, its access must be granted to all the roles each time,",您可以使用 COPY GRANTS 来保留已添加的赠款。 "you could use COPY GRANTS to preserve grants already added. ",但是 COPY GRANT 不是只对表起作用吗? 来自文档docs.snowflake.com/en/sql-reference/sql/create-view.html: "Retains the access permissions from the original view when a new view is created using the OR REPLACE clause."【参考方案2】:

让我们从实现区域仅限于一个国家/地区的 RLS 开始。 让这个表驻留在里面 数据库:Fruits_DB 架构:Fruits_Schema 表:Fruits

Country Fruit
USA Apple
India Mango
Italy Kiwi
Australia Guava

假设有 3 个角色 REGION1_ROLEREGION2_ROLEGLOBAL_ROLEMaster_role 是可以访问数据库中所有表等的角色

// Create roles using Master_role
CREATE ROLE REGION1_ROLE;
CREATE ROLE REGION2_ROLE;
CREATE ROLE GLOBAL_ROLE;

// Grant access to Database and Schema to these roles
GRANT USAGE ON DATABASE Fruits_DB TO ROLE REGION1_ROLE;
GRANT USAGE ON SCHEMA Fruits_DB.Fruits_Schema TO ROLE REGION1_ROLE;

GRANT USAGE ON DATABASE Fruits_DB TO ROLE REGION2_ROLE;
GRANT USAGE ON SCHEMA Fruits_DB.Fruits_Schema TO ROLE REGION2_ROLE;

GRANT USAGE ON DATABASE Fruits_DB TO ROLE GLOBAL_ROLE;
GRANT USAGE ON SCHEMA Fruits_DB.Fruits_Schema TO ROLE GLOBAL_ROLE;

GLOBAL_REGION 创建安全视图

//Secure view DDL
CREATE SECURE VIEW GLOBAL_VIEW
AS
SELECT * FROM Fruits_DB.Fruits_Schema.Fruits;

// Grant Access to this View to Global_role
GRANT SELECT ON GLOBAL_VIEW TO ROLE GLOBAL_ROLE;

REGION1_ROLEREGION2_ROLE 创建安全视图

//Secure view DDL
CREATE SECURE VIEW REGIONAL_VIEW
AS
SELECT * FROM Fruits_DB.Fruits_Schema.Fruits
WHERE COUNTRY =
  CASE
    WHEN CURRENT_ROLE() = 'REGION1_ROLE' THEN 'USA'
    WHEN CURRENT_ROLE() = 'REGION2_ROLE' THEN 'INDIA'
  END;
// Note that Function CURRENT_ROLE() evaluates to the role this Function is being executed in

// Grant Access to this View to REGIONAL ROLES
GRANT SELECT ON REGIONAL_VIEW TO ROLE REGION1_ROLE;
GRANT SELECT ON REGIONAL_VIEW TO ROLE REGION2_ROLE;

上述安全视图提供了所需的 RLS,但存在两个限制:

    必须创建 2 个视图而不是一个。 单个角色只能访问一个国家/地区,因为CASE WHEN 仅返回一个值,这是限制的根本原因。 我们也不能使用其他条件语句,因为它们也返回单个值。
CASE
  WHEN
  THEN
END

解决方案 1: 我们使用 Snowflake 的 JavaScript UDTF 让我们返回多个国家/地区。

CREATE OR REPLACE FUNCTION Fruits_DB.Fruits_Schema.ROLES_REGIONS(CURRENT_ROLE STRING, COUNTRY STRING)
RETURNS TABLE (COUNTRY VARCHAR)
LANGUAGE JAVASCRIPT
AS
$$
  
    processRow: function f(row, rowWriter, context)  
    
        var role = row.CURRENT_ROLE;
        
        if (role == 'GLOBAL_ROLE')
        
            rowWriter.writeRow( COUNTRY: row.COUNTRY);
   
        
        else if (role == 'REGION1_ROLE')
        
            while(this.count == 0)
            
                rowWriter.writeRow( COUNTRY: 'USA');
                rowWriter.writeRow( COUNTRY: 'AUSTRALIA');
                this.count = 1;
            
            
        
        else if (role == 'REGION2_ROLE')
        
            while(this.count == 0)
            
                rowWriter.writeRow( COUNTRY: 'INDIA');
                rowWriter.writeRow( COUNTRY: 'ITALY');
                this.count = 1;
            
            
        
    ,
    
    initialize: function(argumentInfo, context) 
    
       this.count = 0;
    
  
  
$$;

在上述 JavaScript UDTF 中,我们从 Fruits 表中传递 CURRENT_ROLE()Country 列,并根据角色应用 If-Else 条件 该函数返回一列,其中包含特定于该角色的所有国家/地区 当 CURRENT_ROLE() 评估为 GLOBAL_ROLE 时,它会返回传递给它的所有国家/地区

新的统一安全视图:

CREATE SECURE VIEW REGIONAL_VIEW
AS
SELECT * FROM Fruits_DB.Fruits_Schema.Fruits
WHERE COUNTRY IN
  ( SELECT DISTINCT COUNTRY 
    FROM 
    Fruits_DB.Fruits_Schema.Fruits,
    TABLE(Fruits_DB.Fruits_Schema.ROLES_REGIONS(CURRENT_ROLE(),COUNTRY::STRING))
  );
// We use DISTINCT to eliminate any duplicate countries if present

解决方案 2: 使用多对多关系表 这是一种简单但繁琐的方法,因为它不是每个都需要维护

创建表 Roles_Relations

Role Country
REGION1_ROLE USA
REGION2_ROLE INDIA
REGION2_ROLE ITALY
REGION1_ROLE AUSTRALIA
GLOBAL_ROLE USA
GLOBAL_ROLE INDIA
GLOBAL_ROLE ITALY
GLOBAL_ROLE AUSTRALIA

创建安全视图:

// Secure view DDL
CREATE SECURE VIEW REGIONAL_VIEW
AS
SELECT * FROM Fruits_DB.Fruits_Schema.Fruits
WHERE COUNTRY IN
(
  SELECT COUNTRY FROM Roles_Relations 
  WHERE ROLE = CURRENT_ROLE()
);

当角色数量增加时,这种方法变得非常难以处理 这两种方法都不需要更新视图定义。更新视图的定义会撤销它对其他角色的访问权限,除非您使用 COPY GRANTS,并且更新 UDTF 或 Roles_Relations 表不需要一次又一次地授予角色视图

【讨论】:

【参考方案3】:

为什么不改用 UDF 和权利/映射表?同样的方式即将到来的行级访问将在 GA 时工作。我看到了雪花的推荐读物。现在UDF可以用在更多的地方。 或者您可以使用企业版中 Snowflake 的政策作为预览版。

【讨论】:

以上是关于通过将安全视图分配给角色并提供针对多个条件的过滤器,在 Snowflake 上实现行级安全性 (RLS)的主要内容,如果未能解决你的问题,请参考以下文章

POD安全策略评估顺序(多个角色)

YII框架一个请求的生命周期

创建vCenter用户以及设置维护帐号特性权限分配

cakePHP:根据用户角色在传递给视图之前过滤行

Django 将多个模型传递给一个模板

金蝶客户端使用