세분화된 액세스 제어로 데이터베이스 액세스

이 페이지에서는 세분화된 액세스 제어 사용자가 Spanner 데이터베이스에 액세스하는 방법을 설명합니다.

세분화된 액세스 제어에 대한 자세한 내용은 세분화된 액세스 제어 정보를 참조하세요.

세분화된 액세스 제어 사용자는 SQL 문 및 쿼리를 실행하고 데이터베이스에서 행 작업을 수행하는 데 사용할 데이터베이스 역할을 선택해야 합니다. 선택한 역할은 역할을 변경할 때까지 세션 동안 유지됩니다.

쿼리, DML 또는 행 작업을 제출하면 Spanner가 다음 규칙을 사용하여 승인을 확인합니다.

Google Cloud 콘솔

Spanner는 먼저 데이터베이스 수준 IAM 권한이 있는지 확인합니다. 권한이 있으면 Google Cloud 콘솔에 데이터베이스 역할 선택기가 표시되지 않고 세션이 데이터베이스 수준 권한으로 진행됩니다.

  • 세분화된 액세스 제어 권한만 있고 IAM 데이터베이스 수준 권한은 없는 경우 spanner_sys_reader 시스템 역할 또는 구성원 역할 중 하나에 대한 액세스 권한을 부여받아야 합니다. Google Cloud 콘솔 세션이 필요한 권한으로 진행되도록 데이터베이스 개요 페이지에서 역할을 선택합니다.

Google Cloud SDK

쿼리, DML 또는 행 작업을 제출할 때 데이터베이스 역할을 지정하면 Spanner에서 세분화된 액세스 제어 권한을 확인합니다. 확인에 실패하면 Spanner가 데이터베이스 수준 IAM 권한을 확인하지 않고 작업이 실패합니다.

데이터베이스 역할을 지정하지 않으면 Spanner가 데이터베이스 수준 IAM 권한을 확인하고 확인에 성공하면 세션이 데이터베이스 수준 권한으로 진행됩니다.

Spanner 데이터베이스에 액세스할 때 데이터베이스 역할을 지정하려면 다음 방법을 사용합니다.

콘솔

  1. 데이터베이스를 선택한 후 데이터베이스의개요 페이지에서 현재 역할 필드 옆에 있는 데이터베이스 역할 변경(연필) 아이콘을 클릭합니다.

    기본적으로 세분화된 액세스 제어 사용자가 로그인할 때 이 필드의 값은 public입니다. public 시스템 역할에 대한 자세한 내용은 세분화된 액세스 제어 시스템 역할을 참조하세요.

  2. 데이터베이스 역할 변경 대화상자의 사용 가능한 역할 목록에서 다른 역할을 선택합니다.

  3. 업데이트를 클릭합니다.

    현재 역할 필드에 새 역할이 표시됩니다.

gcloud

  • 다음과 같이 gcloud spanner databases execute-sql 명령어에 --database-role 옵션을 추가합니다.

    gcloud spanner databases execute-sql DATABASE_NAME \
    --instance=INSTANCE_NAME \
    --sql="SELECT * from TABLE_NAME;" \
    --database-role=ROLE_NAME
    

클라이언트 라이브러리

C++

void ReadDataWithDatabaseRole(std::string const& project_id,
                              std::string const& instance_id,
                              std::string const& database_id,
                              std::string const& role) {
  namespace spanner = ::google::cloud::spanner;
  auto client = spanner::Client(spanner::MakeConnection(
      spanner::Database(project_id, instance_id, database_id),
      google::cloud::Options{}.set<spanner::SessionCreatorRoleOption>(role)));
  spanner::SqlStatement select_star("SELECT * FROM Singers");
  auto rows = client.ExecuteQuery(std::move(select_star));
  using RowType =
      std::tuple<std::int64_t, std::string, std::string, spanner::Bytes>;
  for (auto& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::move(row).status();
    std::cout << "SingerId: " << std::get<0>(*row) << ", "
              << "FirstName: " << std::get<1>(*row) << ", "
              << "LastName: " << std::get<2>(*row) << "\n";
  }
}

C#


using Google.Cloud.Spanner.Data;
using System.Collections.Generic;
using System.Threading.Tasks;

public class ReadDataWithDatabaseRoleAsyncSample
{
    public class Singer
    {
        public int SingerId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

    public async Task<List<Singer>> ReadDataWithDatabaseRoleAsync(string projectId, string instanceId, string databaseId, string databaseRole)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";
        string tableName = "Singers";

        var spannerConnectionStringBuilder = new SpannerConnectionStringBuilder
        {
            ConnectionString = connectionString,
            DatabaseRole = databaseRole
        };
        using var connection = new SpannerConnection(spannerConnectionStringBuilder);
        var createSelectCmd = connection.CreateSelectCommand($"SELECT * FROM {tableName}");
        using var reader = await createSelectCmd.ExecuteReaderAsync();
        var singers = new List<Singer>();
        while (await reader.ReadAsync())
        {
            singers.Add(new Singer
            {
                SingerId = reader.GetFieldValue<int>("SingerId"),
                FirstName = reader.GetFieldValue<string>("FirstName"),
                LastName = reader.GetFieldValue<string>("LastName"),
            });
        }
        return singers;
    }
}

Go


import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/spanner"
	"google.golang.org/api/iterator"
)

func readDataWithDatabaseRole(w io.Writer, db string, databaseRole string) error {
	// databaseRole = "parent"
	ctx := context.Background()
	cfg := spanner.ClientConfig{
		DatabaseRole: databaseRole,
	}
	client, err := spanner.NewClientWithConfig(ctx, db, cfg)
	if err != nil {
		return err
	}
	defer client.Close()

	// Read all albums.
	iter := client.Single().Read(ctx, "Albums", spanner.AllKeys(),
		[]string{"SingerId", "AlbumId", "AlbumTitle"})
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			return nil
		}
		if err != nil {
			return err
		}
		var singerID, albumID int64
		var albumTitle string
		if err := row.Columns(&singerID, &albumID, &albumTitle); err != nil {
			return err
		}
		fmt.Fprintf(w, "%d %d %s\n", singerID, albumID, albumTitle)
	}
}

Java

import com.google.cloud.spanner.DatabaseClient;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.KeySet;
import com.google.cloud.spanner.ResultSet;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerOptions;
import java.util.Arrays;

public class ReadDataWithDatabaseRole {

  static void readDataWithDatabaseRole() {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project";
    String instanceId = "my-instance";
    String databaseId = "my-database";
    String role = "my-role";
    readDataWithDatabaseRole(projectId, instanceId, databaseId, role);
  }

  static void readDataWithDatabaseRole(
      String projectId, String instanceId, String databaseId, String role) {
    try (Spanner spannerWithRole =
        SpannerOptions.newBuilder()
            .setProjectId(projectId)
            .setDatabaseRole(role)
            .build()
            .getService()) {
      DatabaseClient dbClient =
          spannerWithRole.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId));
      ResultSet resultSet =
          dbClient
              .singleUse()
              .read(
                  "Singers",
                  KeySet.all(),
                  Arrays.asList("SingerId", "FirstName", "LastName"));
      while (resultSet.next()) {
        System.out.printf("SingerId: %d\n", resultSet.getLong(0));
        System.out.printf("FirstName: %s\n", resultSet.getString(1));
        System.out.printf("LastName: %s\n", resultSet.getString(2));
      }
    }
  }
}

Node.js

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const projectId = 'my-project-id';
// Imports the Google Cloud Spanner client library
const {Spanner} = require('@google-cloud/spanner');

// Instantiates a client
const spanner = new Spanner({
  projectId: projectId,
});

async function readDataWithDatabaseRole() {
  // Gets a reference to a Cloud Spanner instance and database.
  const instance = spanner.instance(instanceId);
  // Connect to a database using the 'parent' database role. This means that the connection will only have the permissions that have explicitly been granted to the 'parent' role.
  const options = {
    databaseRole: 'parent',
  };
  const database = instance.database(databaseId, options);

  try {
    const query = {
      sql: 'SELECT SingerId, FirstName, LastName FROM Singers',
    };
    const [rows] = await database.run(query);

    for (const row of rows) {
      const json = row.toJSON();

      console.log(
        `SingerId: ${json.SingerId}, FirstName: ${json.FirstName}, LastName: ${json.LastName}`
      );
    }
  } catch (err) {
    console.error('ERROR:', err);
  } finally {
    // Close the database when finished.
    await database.close();
  }
}
readDataWithDatabaseRole();

PHP

use Google\Cloud\Spanner\SpannerClient;

/**
 * Read database with a database role.
 * Example:
 * ```
 * read_data_with_database_role($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function read_data_with_database_role(string $instanceId, string $databaseId): void
{
    $spanner = new SpannerClient();
    $databaseRole = 'new_parent';
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId, ['databaseRole' => $databaseRole]);
    $results = $database->execute('SELECT * FROM Singers');

    foreach ($results as $row) {
        printf('SingerId: %s, Firstname: %s, LastName: %s' . PHP_EOL, $row['SingerId'], $row['FirstName'], $row['LastName']);
    }
}

Python

# instance_id = "your-spanner-instance"
# database_id = "your-spanner-db-id"
spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
role = "new_parent"
database = instance.database(database_id, database_role=role)

with database.snapshot() as snapshot:
    results = snapshot.execute_sql("SELECT * FROM Singers")
    for row in results:
        print("SingerId: {}, FirstName: {}, LastName: {}".format(*row))

Ruby

require "google/cloud/spanner"

def spanner_read_data_with_database_role project_id:, instance_id:, database_id:
  # project_id  = "Your Google Cloud project ID"
  # instance_id = "Your Spanner instance ID"
  # database_id = "Your Spanner database ID"

  role = "new_parent"
  spanner = Google::Cloud::Spanner.new project: project_id
  client = spanner.client instance_id, database_id, database_role: role

  result = client.execute_sql "SELECT * FROM Singers"

  result.rows.each do |row|
    puts "SingerId: #{row[:SingerId]}"
    puts "FirstName: #{row[:FirstName]}"
    puts "LastName: #{row[:LastName]}"
  end
end