세분화된 액세스 제어 구성

이 페이지에서는 GoogleSQL 언어 데이터베이스 및 PostgreSQL 언어 데이터베이스의 Spanner에 대해 세분화된 액세스 제어를 구성하는 방법을 설명합니다.

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

다음 단계에 따라 세분화된 액세스 제어를 구성합니다.

  1. 데이터베이스 역할을 만들고 권한 부여

  2. 선택사항: 상속을 사용하여 역할 계층 구조 만들기

  3. 주 구성원에게 데이터베이스 역할 부여

  4. 사용자와 개발자에게 데이터베이스 역할 사용 안내

세분화된 액세스 제어 사용자가 데이터베이스에 대한 쿼리, DML 또는 행 작업을 수행할 데이터베이스 역할을 지정해야 합니다.

시작하기 전에

세분화된 액세스 제어 사용자가 될 각 주 구성원에게 Cloud Spanner Viewer IAM 역할(roles/spanner.viewer)이 부여되었는지 확인합니다. 이 역할은 Google Cloud 콘솔에서 Spanner 리소스와 상호작용해야 하는 사용자를 위해 프로젝트 수준에서 권장됩니다.

자세한 내용은 주 구성원에 권한 부여를 참조하세요.

데이터베이스 역할 만들기 및 권한 부여

데이터베이스 역할은 세분화된 액세스 권한 모음입니다. 데이터베이스마다 최대 100개의 데이터베이스 역할을 만들 수 있습니다.

데이터베이스의 역할 및 역할 계층 구조를 결정하고 DDL로 인코딩합니다. Spanner에서 다른 스키마 변경과 마찬가지로, 스키마 변경을 개별적으로 실행하는 대신 일괄적으로 실행하는 것이 좋습니다. 자세한 내용은 스키마 업데이트의 실행 빈도 제한을 참조하세요.

콘솔

데이터베이스 역할을 만들고 여기에 세분화된 액세스 권한을 부여하려면 다음 단계를 따르세요.

  1. Google Cloud 콘솔에서 인스턴스 페이지로 이동합니다.

    인스턴스

  2. 역할을 추가할 데이터베이스가 포함된 인스턴스를 선택합니다.

  3. 데이터베이스를 선택합니다.

  4. 개요 페이지에서 Spanner 스튜디오를 클릭합니다.

  5. Spanner 스튜디오 페이지에서 권한을 만들고 부여할 각 데이터베이스 역할에 다음 단계를 수행합니다.

    1. 역할을 만들려면 다음 문을 입력합니다.

      CREATE ROLE ROLE_NAME;

      아직은 제출을 클릭하지 마세요.

    2. 역할에 권한을 부여하려면 CREATE ROLE 문 다음 줄에 GRANT 문을 입력합니다.

      GRANT 문의 문법 세부정보는 GoogleSQL 데이터 정의 언어를 참조하세요. 권한에 대한 자세한 내용은 세분화된 액세스 제어 권한을 참조하세요.

      예를 들어 데이터베이스 역할 hr_manager에 테이블 employeescontractors에 대한 SELECT, INSERT, UPDATE 권한을 부여하려면 다음 문을 입력합니다.

      GoogleSQL

      GRANT SELECT, INSERT, UPDATE ON TABLE employees, contractors TO ROLE hr_manager;
      

      PostgreSQL

      GRANT SELECT, INSERT, UPDATE ON TABLE employees, contractors TO hr_manager;
      

      GRANT 문에 DDL 템플릿을 사용할 수 있습니다. 탐색기 창에서 권한을 부여할 역할로 이동합니다. 작업 보기를 클릭하고 이 역할에 대한 액세스 권한을 부여하려는 권한 유형을 선택합니다. 새 편집기 탭에 GRANT 템플릿 문이 채워집니다.

  6. 제출을 클릭합니다.

    DDL에 오류가 있으면 Google Cloud 콘솔에서 오류를 반환합니다.

gcloud

데이터베이스 역할을 만들고 여기에 세분화된 액세스 권한을 부여하려면 CREATE ROLEGRANT 문과 함께 gcloud spanner databases ddl update 명령어를 사용합니다.

CREATE ROLEGRANT 문의 문법 세부정보는 GoogleSQL 데이터 정의 언어를 참조하세요.

예를 들어 다음 명령어를 사용하여 데이터베이스 역할을 만들고 하나 이상의 테이블에 대한 권한을 부여합니다.

GoogleSQL

gcloud spanner databases ddl update DATABASE_NAME --instance=INSTANCE_NAME \
--ddl='CREATE ROLE ROLE_NAME; GRANT PRIVILEGES ON TABLE TABLES TO ROLE ROLE_NAME;'

PostgreSQL

gcloud spanner databases ddl update DATABASE_NAME --instance=INSTANCE_NAME \
--ddl='CREATE ROLE ROLE_NAME; GRANT PRIVILEGES ON TABLE TABLES TO ROLE_NAME;'

다음을 바꿉니다.

  • PRIVILEGES는 세분화된 액세스 제어 권한의 쉼표로 구분된 목록입니다. 권한에 대한 자세한 내용은 세분화된 액세스 제어 권한을 참조하세요.

  • TABLES는 쉼표로 구분된 테이블 목록입니다.

employeescontractors 테이블에 대한 SELECT, INSERT, UPDATEhr 인스턴스의 hrdb1 데이터베이스에 있는 데이터베이스 역할 hr_analyst에 부여하려면 다음 문을 입력합니다.

GoogleSQL

gcloud spanner databases ddl update hrdb1 --instance=hr \
--ddl='CREATE ROLE hr_analyst; GRANT SELECT, INSERT, UPDATE ON TABLE employees, contractors TO ROLE hr_analyst;'

PostgreSQL

gcloud spanner databases ddl update hrdb1 --instance=hr \
--ddl='CREATE ROLE hr_analyst; GRANT SELECT, INSERT, UPDATE ON TABLE employees, contractors TO hr_analyst;'

클라이언트 라이브러리

모두 데이터베이스 역할을 만들고 삭제하는 코드 샘플입니다.

C++

void AddAndDropDatabaseRole(
    google::cloud::spanner_admin::DatabaseAdminClient client,
    std::string const& project_id, std::string const& instance_id,
    std::string const& database_id, std::string const& role_parent,
    std::string const& role_child) {
  google::cloud::spanner::Database database(project_id, instance_id,
                                            database_id);
  std::vector<std::string> grant_statements = {
      "CREATE ROLE " + role_parent,
      "GRANT SELECT ON TABLE Singers TO ROLE " + role_parent,
      "CREATE ROLE " + role_child,
      "GRANT ROLE " + role_parent + " TO ROLE " + role_child,
  };
  auto metadata =
      client.UpdateDatabaseDdl(database.FullName(), grant_statements).get();
  google::cloud::spanner_testing::LogUpdateDatabaseDdl(  //! TODO(#4758)
      client, database, metadata.status());              //! TODO(#4758)
  if (!metadata) throw std::move(metadata).status();
  std::cout << "Created roles " << role_parent << " and " << role_child
            << " and granted privileges\n";

  std::vector<std::string> revoke_statements = {
      "REVOKE ROLE " + role_parent + " FROM ROLE " + role_child,
      "DROP ROLE " + role_child,
  };
  metadata =
      client.UpdateDatabaseDdl(database.FullName(), revoke_statements).get();
  google::cloud::spanner_testing::LogUpdateDatabaseDdl(  //! TODO(#4758)
      client, database, metadata.status());              //! TODO(#4758)
  if (!metadata) throw std::move(metadata).status();
  std::cout << "Revoked privileges and dropped role " << role_child << "\n";
}

C#


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

public class AddAndDropDatabaseRoleAsyncSample
{
    public async Task AddDatabaseRoleAsync(string projectId, string instanceId, string databaseId, string databaseRole)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";
        string createRoleStatement = $"CREATE ROLE {databaseRole}";

        // Creates the given database role.
        using var connection = new SpannerConnection(connectionString);
        using var updateCmd = connection.CreateDdlCommand(createRoleStatement);
        await updateCmd.ExecuteNonQueryAsync();
    }

    public async Task DropDatabaseRoleAsync(string projectId, string instanceId, string databaseId, string databaseRole)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";
        string deleteRoleStatement = $"DROP ROLE {databaseRole}";

        // Drops the given database role.
        using var connection = new SpannerConnection(connectionString);
        using var updateCmd = connection.CreateDdlCommand(deleteRoleStatement);
        await updateCmd.ExecuteNonQueryAsync();
    }
}

Go


import (
	"context"
	"io"

	database "cloud.google.com/go/spanner/admin/database/apiv1"
	adminpb "cloud.google.com/go/spanner/admin/database/apiv1/databasepb"
)

func addAndDropDatabaseRole(w io.Writer, db string) error {
	ctx := context.Background()
	adminClient, err := database.NewDatabaseAdminClient(ctx)
	if err != nil {
		return err
	}
	defer adminClient.Close()

	// Set up database roles and membership. After database roles are created,
	// users can be granted roles by setting IAM policies.
	op, err := adminClient.UpdateDatabaseDdl(ctx, &adminpb.UpdateDatabaseDdlRequest{
		Database: db,
		Statements: []string{
			"CREATE ROLE parent",
			"GRANT SELECT ON TABLE Albums TO ROLE parent",
			"CREATE ROLE child",
			"GRANT ROLE parent TO ROLE child",
		},
	})
	if err != nil {
		return err
	}
	if err := op.Wait(ctx); err != nil {
		return err
	}

	// Delete role and membership.
	op, err = adminClient.UpdateDatabaseDdl(ctx, &adminpb.UpdateDatabaseDdlRequest{
		Database: db,
		Statements: []string{
			"REVOKE ROLE parent FROM ROLE child",
			"DROP ROLE child",
		},
	})
	if err != nil {
		return err
	}
	if err := op.Wait(ctx); err != nil {
		return err
	}
	return nil
}

Java


import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.DatabaseName;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class AddAndDropDatabaseRole {

  static void addAndDropDatabaseRole() {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project";
    String instanceId = "my-instance";
    String databaseId = "my-database";
    String parentRole = "parent_role";
    String childRole = "child_role";
    addAndDropDatabaseRole(projectId, instanceId, databaseId, parentRole, childRole, "Albums");
  }

  static void addAndDropDatabaseRole(
      String projectId, String instanceId, String databaseId