使用映射表进行动态访问控制

本教程介绍如何使用映射表控制行级和列级访问,而无需管理大量组。 单个查阅表格可驱动行筛选和列掩码。 访问更改只需要行更新。 无需创建新的组或重写策略。

本教程还演示了条件掩码:PII 列根据同一行上另一列的值以不同的方式屏蔽。 不论用户的权限等级如何,标记为 confidential 的订单,其个人身份信息已被完全遮盖。

有关映射表设计的一般指南,请参阅 使用映射表创建访问控制列表

先决条件

  • Databricks Runtime 16.4 或更高版本,或无服务器计算。
  • 帐户管理员或工作区管理员权限(用于创建受管理标记)。
  • MANAGE 目标目录或架构的权限。
  • EXECUTE 在 UDF 上下功夫。
  • SQL 笔记本或查询编辑器。

Scenario

你的组织拥有四个区域(中国东部 2、中国东部 3、中国北部 2、中国北部 3)和四个部门的员工。 每个用户应仅看到与其区域和部门匹配的行,并且 PII 列应根据两个因素进行屏蔽:存储在映射表中的用户权限级别(fullmaskednone),以及订单的order_priority

使用基于组的方法,需要为每个区域部门组合创建一个组。 例如,需要 16 个组用于四个区域和四个部门。 添加 PII 清关层会使计数增加到原来的三倍。 每个新区域或部门都需要新的组和策略更新。

映射表方法将此替换为单个查阅表格:每个用户一行,每个访问维度一列。 若要更改用户的访问权限,请更新某一行。

步骤 1:创建受管理标记

在运行任何 SQL 之前,请在目录浏览器界面中创建以下受控标签(目录>治理>受控标签>):

标记键 允许的值
region (仅键标签)
department (仅键标签)
pii nameemail
priority (仅键标签)

regiondepartment标签告知行筛选器策略哪些列要传递给筛选器 UDF。 该 pii 标记告知列掩码策略要屏蔽哪些列以及它们包含哪些 PII 类型。 标记 priority 允许列掩码策略将 order_priority 值传递给掩码 UDF 以用于条件掩码。

Warning

标记数据以纯文本形式存储,可全局复制。 不要使用标记名称、值或描述符,这些标记名称或描述符可能会损害资源的安全性。 例如,不要使用包含个人或敏感信息的标记名称、值或描述符。

步骤 2:生成示例数据

创建目录、架构和订单表。 列 order_priority 控制条件掩码:标记为 confidential 的订单的 PII 完全被遮蔽,即使对于具有高权限的用户也是如此。

CREATE CATALOG IF NOT EXISTS abac_tutorial;
USE CATALOG abac_tutorial;

CREATE SCHEMA IF NOT EXISTS mapping_demo;
USE SCHEMA mapping_demo;
CREATE OR REPLACE TABLE orders (
  order_id INT,
  customer_name STRING,
  customer_email STRING,
  sales_region STRING,
  dept STRING,
  amount DOUBLE,
  order_date DATE,
  order_priority STRING
);

INSERT INTO orders VALUES
  (1,  'Acme Corp',     'orders@acme.com',    'us_east', 'engineering', 50000,  '2025-01-15', 'standard'),
  (2,  'Beta Inc',      'sales@beta.com',     'us_east', 'sales',       75000,  '2025-02-01', 'confidential'),
  (3,  'Gamma LLC',     'info@gamma.com',     'us_west', 'engineering', 30000,  '2025-01-20', 'standard'),
  (4,  'Delta Co',      'deals@delta.com',    'us_west', 'sales',       95000,  '2025-03-01', 'confidential'),
  (5,  'Epsilon GmbH',  'kontakt@epsilon.de', 'eu',      'engineering', 45000,  '2025-02-15', 'standard'),
  (6,  'Zeta SA',       'contact@zeta.fr',    'eu',      'sales',       62000,  '2025-01-30', 'standard'),
  (7,  'Eta Ltd',       'hello@eta.sg',       'apac',    'marketing',   28000,  '2025-03-10', 'confidential'),
  (8,  'Theta Corp',    'biz@theta.com',      'us_east', 'marketing',   55000,  '2025-02-20', 'standard'),
  (9,  'Iota KK',       'info@iota.jp',       'apac',    'engineering', 41000,  '2025-01-25', 'standard'),
  (10, 'Kappa Inc',     'sales@kappa.com',    'us_west', 'marketing',   33000,  '2025-03-05', 'standard');

步骤 3:应用受治理的标记

给列加上标记,以便 ABAC 策略能够自动检测到它们。 该order_priority列使用仅priority键标记,以便列掩码策略可以通过MATCH COLUMNS与之匹配,并将其值传递给掩码 UDF。

ALTER TABLE abac_tutorial.mapping_demo.orders
  ALTER COLUMN sales_region SET TAGS ('region' = '');
ALTER TABLE abac_tutorial.mapping_demo.orders
  ALTER COLUMN dept SET TAGS ('department' = '');
ALTER TABLE abac_tutorial.mapping_demo.orders
  ALTER COLUMN customer_name SET TAGS ('pii' = 'name');
ALTER TABLE abac_tutorial.mapping_demo.orders
  ALTER COLUMN customer_email SET TAGS ('pii' = 'email');
ALTER TABLE abac_tutorial.mapping_demo.orders
  ALTER COLUMN order_priority SET TAGS ('priority' = '');

步骤 4:创建映射表

无需为每个区域、部门和权限组合创建组,而是维护一个表,每个用户占一行。 该 pii_access 列控制 PII 列的显示方式:

  • full — 查看实际值(对于标准优先级订单)
  • masked — 查看部分值,例如 A***o***@acme.com
  • none — 请参阅 ***REDACTED***

expires_on 列设置每个访问项的到期日期。 在此日期之后,行筛选器 UDF 停止与条目匹配,并且用户以无提示方式失去访问权限,无需手动吊销。 这对于承包商、临时数据共享协议或限时项目非常有用。

如果用户需要访问多个区域和部门组合,请添加其他行。

注释

保持映射表较小且简单。 针对受保护表的每个查询都会运行行筛选器和列掩码 UDF,这反过来又查询映射表。 大型映射表和复杂的 UDF 逻辑可能会影响查询性能。 尽可能使用窄架构并将 UDF 逻辑保留在单个查找中。

CREATE OR REPLACE TABLE abac_tutorial.mapping_demo.user_access (
  user_email STRING,
  region STRING,
  department STRING,
  pii_access STRING,
  expires_on DATE
);

INSERT INTO abac_tutorial.mapping_demo.user_access VALUES
  (current_user(),      'us_east', 'engineering', 'masked', '2099-12-31'),
  ('bob@example.com',   'us_west', 'sales',       'full',   '2099-12-31'),
  ('carol@example.com', 'eu',      'engineering', 'none',   '2099-12-31'),
  ('david@example.com', 'apac',    'marketing',   'masked', '2099-12-31');

步骤 5:创建行筛选器 UDF

此 UDF 接收行 sales_regiondept 值(通过标记匹配传入策略),查找映射表中的当前用户,并且仅当匹配项存在且未过期时才返回 TRUE 。 不在映射表中或者其访问权限已过期的用户看不到任何行(失败关闭的设计)。

CREATE OR REPLACE FUNCTION abac_tutorial.mapping_demo.access_filter(
  region_val STRING,
  dept_val STRING
)
RETURNS BOOLEAN
RETURN EXISTS (
  SELECT 1 FROM abac_tutorial.mapping_demo.user_access
  WHERE user_email = current_user()
    AND region = region_val
    AND department = dept_val
    AND expires_on >= current_date()
);

步骤 6:创建列掩码 UDF

此 UDF 控制 PII 列的显示方式。 它采用三个参数:列值、PII 类型('name''email')和行的order_priority。 掩码逻辑有两个层:

  • 第 1 层(条件掩码):order_priority如果是confidential,则无论用户的许可级别如何,PII 始终完全经过修订。
  • 第 2 层(用户许可): 对于标准行,UDF 会检查用户的 pii_access 级别映射表,并应用相应的掩码。 如果用户具有多个映射表条目(多区域访问),则所有行之间的最高清除量适用。
CREATE OR REPLACE FUNCTION abac_tutorial.mapping_demo.pii_mask(
  val STRING,
  pii_type STRING,
  order_pri STRING
)
RETURNS STRING
RETURN CASE
  WHEN order_pri = 'confidential' THEN '***REDACTED***'
  WHEN EXISTS (
    SELECT 1 FROM abac_tutorial.mapping_demo.user_access
    WHERE user_email = current_user() AND pii_access = 'full'
  ) THEN val
  WHEN EXISTS (
    SELECT 1 FROM abac_tutorial.mapping_demo.user_access
    WHERE user_email = current_user() AND pii_access = 'masked'
  ) THEN
    CASE pii_type
      WHEN 'email' THEN CONCAT(LEFT(val, 1), '***@', SUBSTRING_INDEX(val, '@', -1))
      WHEN 'name'  THEN CONCAT(LEFT(val, 1), '***')
      ELSE CONCAT(LEFT(val, 1), '***')
    END
  ELSE '***REDACTED***'
END;

步骤 7:创建策略

创建三个策略,全部由同一映射表驱动。 这两个列掩码策略都使用相同的 pii_mask 函数。 该 pii_type 参数告知函数要应用哪个掩码样式,因此不需要每个列类型单独的 UDF。

priority治理标记用于MATCH COLUMNS匹配order_priority列,并将其值作为order_pri传递给掩码 UDF。 这是如何实现条件掩码:策略在查询时将行的优先级值传递到 UDF。

CREATE POLICY user_access_filter
ON SCHEMA abac_tutorial.mapping_demo
ROW FILTER abac_tutorial.mapping_demo.access_filter
TO `account users`
FOR TABLES
MATCH COLUMNS has_tag('region') AS r, has_tag('department') AS d
USING COLUMNS (r, d);
CREATE POLICY pii_mask_name
ON SCHEMA abac_tutorial.mapping_demo
COLUMN MASK abac_tutorial.mapping_demo.pii_mask
TO `account users`
FOR TABLES
MATCH COLUMNS has_tag_value('pii', 'name') AS m,
  has_tag('priority') AS pri
ON COLUMN m
USING COLUMNS ('name', pri);

CREATE POLICY pii_mask_email
ON SCHEMA abac_tutorial.mapping_demo
COLUMN MASK abac_tutorial.mapping_demo.pii_mask
TO `account users`
FOR TABLES
MATCH COLUMNS has_tag_value('pii', 'email') AS m,
  has_tag('priority') AS pri
ON COLUMN m
USING COLUMNS ('email', pri);

步骤 8:验证结果

通过映射表条目,你可以通过许可访问us_east / engineeringmasked。 运行以下查询,验证您是否仅看到订单编号1,并且PII被部分屏蔽。

SELECT * FROM abac_tutorial.mapping_demo.orders;

订单 #1 包含 order_priority = 'standard',因此你的 masked 授权适用。

用户的预期结果:

订单编号 customer_name customer_email 销售区域 部门 订单日期 订单优先级
1 A*** o***@acme.com us_east 工程 50000 2025-01-15 标准

其他用户看到的内容:

User 可见订单 订单优先级 PII 行为
bob@example.comfull 清关) #4 (us_west,销售) 机密 ***REDACTED*** — 机密替代 full 许可权
carol@example.comnone 清关) #5 (欧盟, 工程) 标准 ***REDACTED***none 清理意味着完全删除
david@example.commasked 清关) #7(亚太地区,市场营销) 机密 ***REDACTED*** — 机密替代 masked 许可权
(目录所有者) 全部 10 个 所有未屏蔽(所有者不受策略限制)
(未列出的用户) 没有 行筛选器不返回任何行

请注意,bob 有 full 权限,但仍看到 ***REDACTED*** ,因为订单 #4 是 confidential。 这是一种条件掩码:行的优先级值会覆盖用户权限。

步骤 9:动态更新访问权限

映射表方法的主要优点是,可以通过更新表中的行来更改访问权限。 无需更新策略、UDF 或组成员身份。

重新分配到其他部门

将您的部门从engineering更改为sales。 Order #2(Beta Inc)是一个销售订单 confidential,因此其 PII 即使在 masked 授权下也被完全遮蔽。

UPDATE abac_tutorial.mapping_demo.user_access
SET department = 'sales'
WHERE user_email = current_user();

运行以下查询以验证。 应会看到具有 PII 的订单 #2 ***REDACTED***

SELECT * FROM abac_tutorial.mapping_demo.orders;

撤销更改:

UPDATE abac_tutorial.mapping_demo.user_access
SET department = 'engineering'
WHERE user_email = current_user();

升级个人身份信息清除

将你的许可从 masked 更改为 full。 对于标准优先级行,现在可以看到实际的 PII 值。

UPDATE abac_tutorial.mapping_demo.user_access
SET pii_access = 'full'
WHERE user_email = current_user();

运行以下查询以验证。 订单 #1 是standard优先级,拥有full清关,您应该能看到Acme Corporders@acme.com

SELECT * FROM abac_tutorial.mapping_demo.orders;

撤销更改:

UPDATE abac_tutorial.mapping_demo.user_access
SET pii_access = 'masked'
WHERE user_email = current_user();

授予对其他区域的访问权限

插入第二行以授予对欧盟工程的访问权限。 不需要新的组或策略。

INSERT INTO abac_tutorial.mapping_demo.user_access
VALUES (current_user(), 'eu', 'engineering', 'masked', '2099-12-31');

运行以下查询以验证。 现在,您应该看到订单 #1(us_east,工程)和订单 #5(eu,工程),其中个人身份信息(PII)已部分屏蔽。

SELECT * FROM abac_tutorial.mapping_demo.orders;

删除其他访问权限:

DELETE FROM abac_tutorial.mapping_demo.user_access
WHERE user_email = current_user() AND region = 'eu';

撤销访问权限

将访问条目设置为过去的日期。 行筛选器 UDF 会检查 expires_on >= current_date(),因此将无提示地忽略过期条目,并自动撤销访问权限。 这对于承包商、具有固定工期的数据共享协议或限时项目非常有用。

UPDATE abac_tutorial.mapping_demo.user_access
SET expires_on = current_date() - INTERVAL 1 DAY
WHERE user_email = current_user();

运行以下查询,验证是否看不到任何行。

SELECT * FROM abac_tutorial.mapping_demo.orders;

使用将来的到期日期还原访问权限:

UPDATE abac_tutorial.mapping_demo.user_access
SET expires_on = '2099-12-31'
WHERE user_email = current_user();

运行以下查询以验证是否已还原访问权限。

SELECT * FROM abac_tutorial.mapping_demo.orders;

总结

本教程演示了三种模式:

  • 映射表模式:单个查找表控制行过滤和列掩码。 访问更改是通过更新行进行的,不需要策略或组更改。
  • 条件性掩码:掩码 UDF 检查 order_priority 列的每一行,以确定如何屏蔽 PII。 无论用户的安全级别如何,机密行始终完全隐藏,通过标记 order_priority 并通过 MATCH COLUMNS 传递给 UDF 来实现。
  • 访问到期时间:映射表中包含一个日期 expires_on 。 行筛选器 UDF 会将此日期与 current_date() 进行检查,因此过期条目将无提示地被忽略,访问权限会自动吊销,无需手动干预。

清理

若要删除在本教程中创建的所有对象,请运行以下命令。

DROP POLICY user_access_filter ON SCHEMA abac_tutorial.mapping_demo;
DROP POLICY pii_mask_name ON SCHEMA abac_tutorial.mapping_demo;
DROP POLICY pii_mask_email ON SCHEMA abac_tutorial.mapping_demo;
DROP FUNCTION IF EXISTS abac_tutorial.mapping_demo.access_filter;
DROP FUNCTION IF EXISTS abac_tutorial.mapping_demo.pii_mask;
DROP TABLE IF EXISTS abac_tutorial.mapping_demo.orders;
DROP TABLE IF EXISTS abac_tutorial.mapping_demo.user_access;
DROP SCHEMA IF EXISTS abac_tutorial.mapping_demo CASCADE;

要删除由 regiondepartmentpiipriority 控制的标签,请使用目录资源管理器 UI。