授权视图

本文介绍了如何在 BigQuery 中创建授权视图。

您可以通过以下方式在 BigQuery 中创建授权视图:

  • 使用 Cloud Console。
  • 使用 bq mk 命令。
  • 调用 tables.insert API 方法。
  • 使用客户端库。

概览

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

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

有关如何创建授权视图的教程,请参创建授权视图

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

准备工作

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

所需权限

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

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

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

如需创建视图,您需要拥有 bigquery.tables.create IAM 权限。

以下每个预定义 IAM 角色都包含创建视图所需的权限:

  • roles/bigquery.dataEditor
  • roles/bigquery.dataOwner
  • roles/bigquery.admin

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

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

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

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

  • bigquery.datasets.update
  • bigquery.datasets.get

以下预定义 IAM 角色都包含更新数据集属性所需的权限:

  • roles/bigquery.dataOwner
  • roles/bigquery.admin

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

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

向视图授权

如需为视图授予数据集访问权限,请执行以下操作:

控制台

  1. 浏览器面板中,展开您的项目并选择数据集。

  2. 展开 操作选项,然后点击打开

  3. 在详细信息面板中,点击共享数据集

  4. 数据集权限面板中,选择已获授权的视图标签页。

  5. 共享已获授权的视图部分中:

    • 对于选择项目,验证项目名称。如果视图位于不同的项目中,请务必选择该项目。
    • 对于选择数据集,请选择包含该视图的数据集。
    • 对于选择视图,选择您正在授权的视图。
  6. 点击添加,然后点击完成

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 参考文档

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 参考文档

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 参考文档

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}")

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

视图可用于限制对特定列(字段)的访问。如果您想要限制用户或群组只能访问表中的个别行,不需要为他们分别创建单独的视图,而可以使用 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

后续步骤