已获授权的视图和具体化视图

本文档介绍了如何在 BigQuery 中创建已获授权的视图和具体化视图。

已获授权的视图和已获授权的具体化视图让您可与特定用户和群组共享查询结果,而无需为其授予底层源数据的访问权限。视图或具体化视图被授予访问数据的权限,而不是用户。您还可以使用创建视图或具体化视图的 SQL 查询来限制用户可查询的列和字段。

在其他数据集中创建已获授权的视图或具体化视图时,源数据数据集和已获授权的视图数据集必须位于同一区域级位置

如需了解如何向数据集中的所有视图授权(而不是向单个视图授权),请参阅授权数据集

准备工作

授予为用户提供执行本文档中的每个任务所需权限的 Identity and Access Management (IAM) 角色。

所需权限

如需创建或更新授权视图,您需要拥有包含该视图的数据集的权限,以及提供对该视图访问权限的数据集的权限。

对包含视图的数据集的权限

在 BigQuery 中,视图作为表资源进行处理,因此创建视图所需的权限与创建表相同。您还必须拥有查询视图的 SQL 查询所引用的任何表的权限。

如需创建视图,您需要拥有 bigquery.tables.create IAM 权限。预定义的 IAM 角色 roles/bigquery.dataEditor 可提供创建视图所需的权限。

此外,如果您拥有 bigquery.datasets.create 权限,则可以在自己创建的数据集中创建视图。如需针对非自己拥有的数据创建视图,您必须拥有表的 bigquery.tables.getData 权限。

如需详细了解 BigQuery 中的 IAM 角色和权限,请参阅预定义的角色和权限

对数据集的权限(可授予对视图的访问权限)

如需更新数据集属性,您需要拥有以下 IAM 权限:

  • bigquery.datasets.update
  • bigquery.datasets.setIamPolicy(仅当在 Google Cloud 控制台中更新数据集访问权限控制时才需要)

预定义的 IAM 角色 roles/bigquery.dataOwner 可提供更新数据集属性所需的权限。

此外,如果您拥有 bigquery.datasets.create 权限,则可以更新自己创建的数据集的属性。

如需详细了解 BigQuery 中的 IAM 角色和权限,请参阅预定义的角色和权限

向视图授权

如需为视图授予对数据集的访问权限,请按照以下步骤操作:

控制台

  1. 在 Google Cloud 控制台中转到 BigQuery 页面。

    转到 BigQuery

  2. 浏览器窗格中,展开您的项目,然后选择数据集。

  3. 点击 查看操作,然后点击打开

  4. 数据集信息窗格中,点击 共享,然后选择向视图授权

  5. 向视图授权部分,输入要授权的视图的名称。

  6. 点击添加授权

  7. 点击关闭

bq

  1. 使用 bq show 命令将现有数据集信息(包括访问权限控制)写入 JSON 文件。如果数据集不在默认项目中,请按以下格式将相应项目 ID 添加到数据集名称中:project_id:dataset

    bq show \
    --format=prettyjson \
    project_id:dataset > path_to_file
    

    其中:

    • project_id 是您的项目 ID。
    • dataset 是您的数据集的名称。
    • path_to_file 是本地机器上 JSON 文件的路径。

    示例:

    输入以下命令可将 mydataset 的访问权限控制写入 JSON 文件。mydataset 属于默认项目。

    bq show --format=prettyjson mydataset > /tmp/mydataset.json
    

    输入以下命令可将 mydataset 的访问权限控制写入 JSON 文件。mydataset 属于 myotherproject

    bq show --format=prettyjson \
    myotherproject:mydataset > /tmp/mydataset.json
    
  2. 将授权视图添加到 JSON 文件的“access”部分。

    例如,数据集 JSON 文件的“access”部分可能如下所示:

    {
     "access": [
      {
       "role": "READER",
       "specialGroup": "projectReaders"
      },
      {
       "role": "WRITER",
       "specialGroup": "projectWriters"
      },
      {
       "role": "OWNER",
       "specialGroup": "projectOwners"
      }
      {
       "role": "READER",
       "specialGroup": "allAuthenticatedUsers"
      }
      {
       "role": "READER",
       "domain": "[DOMAIN_NAME]"
      }
      {
       "role": "WRITER",
       "userByEmail": "[USER_EMAIL]"
      }
      {
       "role": "READER",
       "groupByEmail": "[GROUP_EMAIL]"
      },
      {
       "view":{
       "datasetId": "[DATASET_NAME]",
       "projectId": "[PROJECT_NAME]",
       "tableId": "[VIEW_NAME]"
       }
      }
     ],
    }
    

  3. 修改完成后,运行 bq update 命令,并用 --source 标志包括该 JSON 文件。如果数据集不在默认项目中,请按以下格式将相应项目 ID 添加到数据集名称中:project_id:dataset

    bq update \
    --source path_to_file \
    project_id:dataset
    

    其中:

    • path_to_file 是本地机器上 JSON 文件的路径。
    • project_id 是您的项目 ID。
    • dataset 是您的数据集的名称。

    示例:

    输入以下命令可更新 mydataset 的访问权限控制。mydataset 属于默认项目。

     bq update --source /tmp/mydataset.json mydataset
    

    输入以下命令可更新 mydataset 的访问权限控制。mydataset 属于 myotherproject

     bq update --source /tmp/mydataset.json myotherproject:mydataset
    
  4. 如需验证您的访问权限控制发生了变化,请再次输入 show 命令,但不要将信息写入文件。

    bq show --format=prettyjson [DATASET]
    

    bq show --format=prettyjson [PROJECT_ID]:[DATASET]
    

API

调用 datasets.patch 并使用 access 属性更新访问权限控制设置。如需了解详情,请参阅数据集

由于 datasets.update 方法会替换整个数据集资源,因此建议使用 datasets.patch 方法来更新访问权限控制设置。

Go

试用此示例之前,请按照 BigQuery 快速入门:使用客户端库中的 Go 设置说明进行操作。如需了解详情,请参阅 BigQuery Go API 参考文档

如需向 BigQuery 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为客户端库设置身份验证

import (
	"context"
	"fmt"

	"cloud.google.com/go/bigquery"
)

// updateViewDelegated demonstrates the setup of an authorized view, which allows access to a view's results
// without the caller having direct access to the underlying source data.
func updateViewDelegated(projectID, srcDatasetID, viewDatasetID, viewID string) error {
	// projectID := "my-project-id"
	// srcDatasetID := "sourcedata"
	// viewDatasetID := "views"
	// viewID := "myview"
	ctx := context.Background()
	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	srcDataset := client.Dataset(srcDatasetID)
	viewDataset := client.Dataset(viewDatasetID)
	view := viewDataset.Table(viewID)

	// First, we'll add a group to the ACL for the dataset containing the view.  This will allow users within
	// that group to query the view, but they must have direct access to any tables referenced by the view.
	vMeta, err := viewDataset.Metadata(ctx)
	if err != nil {
		return err
	}
	vUpdateMeta := bigquery.DatasetMetadataToUpdate{
		Access: append(vMeta.Access, &bigquery.AccessEntry{
			Role:       bigquery.ReaderRole,
			EntityType: bigquery.GroupEmailEntity,
			Entity:     "example-analyst-group@google.com",
		}),
	}
	if _, err := viewDataset.Update(ctx, vUpdateMeta, vMeta.ETag); err != nil {
		return err
	}

	// Now, we'll authorize a specific view against a source dataset, delegating access enforcement.
	// Once this has been completed, members of the group previously added to the view dataset's ACL
	// no longer require access to the source dataset to successfully query the view.
	srcMeta, err := srcDataset.Metadata(ctx)
	if err != nil {
		return err
	}
	srcUpdateMeta := bigquery.DatasetMetadataToUpdate{
		Access: append(srcMeta.Access, &bigquery.AccessEntry{
			EntityType: bigquery.ViewEntity,
			View:       view,
		}),
	}
	if _, err := srcDataset.Update(ctx, srcUpdateMeta, srcMeta.ETag); err != nil {
		return err
	}
	return nil
}

Java

试用此示例之前,请按照 BigQuery 快速入门:使用客户端库中的 Java 设置说明进行操作。如需了解详情,请参阅 BigQuery Java API 参考文档

如需向 BigQuery 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为客户端库设置身份验证

import com.google.cloud.bigquery.Acl;
import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.Dataset;
import com.google.cloud.bigquery.DatasetId;
import com.google.cloud.bigquery.Table;
import java.util.ArrayList;
import java.util.List;

// Sample to grant view access on dataset
public class GrantViewAccess {

  public static void runGrantViewAccess() {
    // TODO(developer): Replace these variables before running the sample.
    String srcDatasetId = "MY_DATASET_ID";
    String viewDatasetId = "MY_VIEW_DATASET_ID";
    String viewId = "MY_VIEW_ID";
    grantViewAccess(srcDatasetId, viewDatasetId, viewId);
  }

  public static void grantViewAccess(String srcDatasetId, String viewDatasetId, String viewId) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      Dataset srcDataset = bigquery.getDataset(DatasetId.of(srcDatasetId));
      Dataset viewDataset = bigquery.getDataset(DatasetId.of(viewDatasetId));
      Table view = viewDataset.get(viewId);

      // First, we'll add a group to the ACL for the dataset containing the view. This will allow
      // users within that group to query the view, but they must have direct access to any tables
      // referenced by the view.
      List<Acl> viewAcl = new ArrayList<>();
      viewAcl.addAll(viewDataset.getAcl());
      viewAcl.add(Acl.of(new Acl.Group("example-analyst-group@google.com"), Acl.Role.READER));
      viewDataset.toBuilder().setAcl(viewAcl).build().update();

      // Now, we'll authorize a specific view against a source dataset, delegating access
      // enforcement. Once this has been completed, members of the group previously added to the
      // view dataset's ACL no longer require access to the source dataset to successfully query the
      // view
      List<Acl> srcAcl = new ArrayList<>();
      srcAcl.addAll(srcDataset.getAcl());
      srcAcl.add(Acl.of(new Acl.View(view.getTableId())));
      srcDataset.toBuilder().setAcl(srcAcl).build().update();
      System.out.println("Grant view access successfully");
    } catch (BigQueryException e) {
      System.out.println("Grant view access was not success. \n" + e.toString());
    }
  }
}

Python

试用此示例之前,请按照 BigQuery 快速入门:使用客户端库中的 Python 设置说明进行操作。如需了解详情,请参阅 BigQuery Python API 参考文档

如需向 BigQuery 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为客户端库设置身份验证

from google.cloud import bigquery

client = bigquery.Client()

# To use a view, the analyst requires ACLs to both the view and the source
# table. Create an authorized view to allow an analyst to use a view
# without direct access permissions to the source table.
view_dataset_id = "my-project.my_view_dataset"
# Make an API request to get the view dataset ACLs.
view_dataset = client.get_dataset(view_dataset_id)

analyst_group_email = "data_analysts@example.com"
access_entries = view_dataset.access_entries
access_entries.append(
    bigquery.AccessEntry("READER", "groupByEmail", analyst_group_email)
)
view_dataset.access_entries = access_entries

# Make an API request to update the ACLs property of the view dataset.
view_dataset = client.update_dataset(view_dataset, ["access_entries"])
print(f"Access to view: {view_dataset.access_entries}")

# Group members of "data_analysts@example.com" now have access to the view,
# but they require access to the source table to use it. To remove this
# restriction, authorize the view to access the source dataset.
source_dataset_id = "my-project.my_source_dataset"
# Make an API request to set the source dataset ACLs.
source_dataset = client.get_dataset(source_dataset_id)

view_reference = {
    "projectId": "my-project",
    "datasetId": "my_view_dataset",
    "tableId": "my_authorized_view",
}
access_entries = source_dataset.access_entries
access_entries.append(bigquery.AccessEntry(None, "view", view_reference))
source_dataset.access_entries = access_entries

# Make an API request to update the ACLs property of the source dataset.
source_dataset = client.update_dataset(source_dataset, ["access_entries"])
print(f"Access to source: {source_dataset.access_entries}")

移除视图授权

如需移除视图授权,请执行以下操作:

控制台

  1. 在 Google Cloud 控制台中转到 BigQuery 页面。

    转到 BigQuery

  2. 浏览器窗格中,展开您的项目,然后选择数据集。

  3. 点击 共享 > 向视图授权

  4. 点击 移除授权

  5. 点击关闭

配额和限制

  • 获授权视图受到数据集限制的约束。如需了解详情,请参阅数据集限制
  • 如果您移除某个已获授权的视图,则最长可能需要 24 小时才会从系统中移除对该视图的所有引用。为避免出现错误,请先等待 24 小时,然后再重复使用已移除视图的名称,或者为您的视图创建唯一名称。

使用视图强制执行行级访问权限

视图可用于限制对特定列(字段)的访问。如果您想要限制用户或群组只能访问表中的个别行,不需要为他们分别创建单独的视图,而可以使用 SESSION_USER() 函数返回当前用户的电子邮件地址。

为了向不同的用户显示不同的行,请在表中添加另一个字段,并在其中添加您要允许查看该行的用户,然后再创建一个使用 SESSION_USER() 函数的视图。在以下示例中,用户名存储在 allowed_viewer 字段中:

SELECT
  COLUMN_1,
  COLUMN_2
FROM
  `dataset.view`
WHERE
  allowed_viewer = SESSION_USER()

这种方法的局限性在于,您一次只能为一名用户授予访问权限。如要解决这项限制,您可以将 allowed_viewer 设为重复字段。此方法可让您为每行提供一个用户列表。但是,即便您使用了重复字段,将用户名存储在表中后,您仍然需要手动跟踪拥有各行访问权限的个别用户。

因此,更好的做法是在 allowed_viewer 字段中填入群组名称,然后创建一个单独的表来将群组映射到用户。将群组映射到用户的表采用存储群组名称和用户名的架构,例如 {group:string, user_name:string}。这种方法可让您通过包含相关数据的表单独管理用户和群组信息。

如果映射表的名称为 private.access_control,则用于创建授权视图的 SQL 查询将如下所示:

SELECT
  c.customer,
  c.id
FROM
  `private.customers` c
INNER JOIN (
  SELECT
    group
  FROM
    `private.access_control`
  WHERE
    SESSION_USER() = user_name) g
ON
  c.allowed_group = g.group

后续步骤