Configura controllo dell'accesso granulare

Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.

Questa pagina spiega come configurare il controllo dell'accesso granulare per i database Cloud Spanner.

Per saperne di più sul controllo dell'accesso granulare, vedi Informazioni sul controllo dell'accesso granulare.

Per configurare un controllo dell'accesso granulare sono necessari i seguenti passaggi:

  1. Crea ruoli database e concedi privilegi.

  2. (Facoltativo) Crea una gerarchia di ruoli con ereditarietà.

  3. Concedi l'accesso ai ruoli database alle entità Identity and Access Management (IAM).

Gli utenti di controllo dell'accesso granulare devono quindi specificare un ruolo di database per eseguire query, DML o operazioni di riga sul database.

Crea ruoli database e concedi privilegi

Un ruolo di database è una raccolta di privilegi di accesso granulari. Puoi creare fino a 100 ruoli di database per ogni database.

Decidi i ruoli e le gerarchie di ruoli nel tuo database e codificali in DDL. Come per altre modifiche allo schema in Spanner, consigliamo vivamente di inviare le modifiche allo schema in un batch anziché separatamente. Per maggiori informazioni, consulta la pagina Limitare la frequenza degli aggiornamenti dello schema.

Console

Per creare un ruolo database e concedergli privilegi di accesso granulari, segui questi passaggi:

  1. Vai alla pagina Istanze nella console Google Cloud.

    Istanze

  2. Seleziona l'istanza contenente il database a cui vuoi aggiungere il ruolo.

  3. Seleziona il database.

  4. Nella pagina Panoramica, fai clic su Write DDL (Scrivi DDL).

  5. Nella pagina Scrivi istruzioni DDL per ciascun ruolo di database per cui vuoi creare e concedere privilegi, segui questi passaggi:

    1. Per creare il ruolo, inserisci la seguente istruzione:

      CREATE ROLE ROLE_NAME;
      

      Non fare ancora clic su Invia.

    2. Per concedere i privilegi al ruolo, inserisci un'istruzione GRANT nella riga successiva dopo l'istruzione CREATE ROLE.

      Per i dettagli sulla sintassi dell'istruzione GRANT, consulta il linguaggio di definizione dei dati di Google SQL. Per informazioni sui privilegi, vedi Privilegi di controllo dell'accesso granulare.

      Ad esempio, per concedere SELECT, INSERT e UPDATE nelle tabelle employees e contractors al ruolo di database hr_manager, inserisci la seguente istruzione:

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

      Puoi utilizzare un modello DDL per l'istruzione GRANT. Nel menu a discesa MODELLI DDL, seleziona Ruoli database e seleziona un modello nella sezione Concedi privilegi di ruolo.

  6. Fai clic su Invia.

    Se sono presenti errori nel DDL, la console Google Cloud restituisce un errore.

gcloud

Per creare un ruolo database e concedergli privilegi di accesso granulari, utilizza il comando gcloud spanner databases ddl update con le istruzioni CREATE ROLE e GRANT.

Per i dettagli sulla sintassi delle istruzioni CREATE ROLE e GRANT, consulta la pagina sul linguaggio di definizione dei dati di Google SQL.

Ad esempio, utilizza il comando seguente per creare un ruolo di database e assegnargli privilegi su una o più tabelle.

gcloud spanner databases ddl update DATABASE_NAME --instance=INSTANCE_NAME \
--ddl='CREATE ROLE ROLE_NAME; GRANT PRIVILEGES ON TABLE TABLES TO ROLE ROLE_NAME;'
  • PRIVILEGES è un elenco delimitato da virgole di privilegi di controllo dell'accesso granulari. Per informazioni sui privilegi, consulta Privilegi di controllo dell'accesso granulare.

  • TABLES è un elenco di tabelle delimitate da virgole.

Ad esempio, per concedere SELECT, INSERT e UPDATE nelle tabelle employees e contractors al ruolo di database hr_analyst nel database hrdb1 nell'istanza hr, inserisci la seguente istruzione:

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;'

Librerie client

Questi esempi di codice creano e rilasciano un ruolo di database.

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 "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
)

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.api.gax.longrunning.OperationFuture;
import com.google.cloud.spanner.DatabaseAdminClient;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerOptions;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata;
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 = "my-new-parent-role";
    String childRole = "my-new-child-role";
    addAndDropDatabaseRole(projectId, instanceId, databaseId, parentRole, childRole);
  }

  static void addAndDropDatabaseRole(
      String projectId, String instanceId, String databaseId, String parentRole, String childRole) {
    try (Spanner spanner =
                 SpannerOptions.newBuilder()
                         .setProjectId(projectId)
                         .build()
                         .getService())  {
      final DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient();
      OperationFuture<Void, UpdateDatabaseDdlMetadata> operation =
          adminClient.updateDatabaseDdl(
              instanceId,
              databaseId,
              ImmutableList.of(
                  "CREATE ROLE " + parentRole,
                  "GRANT SELECT ON TABLE Albums TO ROLE " + parentRole,
                  "CREATE ROLE " + childRole,
                  "GRANT ROLE " + parentRole + " TO ROLE " + childRole),
              null);
      try {
        System.out.println("Waiting for role create operation to complete...");
        operation.get(5, TimeUnit.MINUTES);
        System.out.printf(
            "Created roles %s and %s and granted privileges%n", parentRole, childRole);
        // Delete role and membership.
        operation =
            adminClient.updateDatabaseDdl(
                instanceId,
                databaseId,
                ImmutableList.of(
                    "REVOKE ROLE " + parentRole + " FROM ROLE " + childRole,
                    "DROP ROLE " + childRole),
                null);
        System.out.println("Waiting for role revoke & drop operation to complete...");
        operation.get(5, TimeUnit.MINUTES);
        System.out.printf("Revoked privileges and dropped role %s%n", childRole);
      } catch (ExecutionException | TimeoutException e) {
        System.out.printf(
            "Error: AddAndDropDatabaseRole failed with error message %s\n", e.getMessage());
        e.printStackTrace();
      } catch (InterruptedException e) {
        System.out.println(
            "Error: Waiting for AddAndDropDatabaseRole operation to finish was interrupted");
      }
    }
  }
}

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 addAndDropNewDatabaseRole() {
  // Gets a reference to a Cloud Spanner instance and database.
  const instance = spanner.instance(instanceId);
  const database = instance.database(databaseId);

  // Creates a new user defined role and grant permissions
  try {
    const request = [
      'CREATE ROLE parent',
      'GRANT SELECT ON TABLE Singers TO ROLE parent',
      'CREATE ROLE child',
      'GRANT ROLE parent TO ROLE child',
    ];
    const [operation] = await database.updateSchema(request);

    console.log('Waiting for operation to complete...');
    await operation.promise();

    console.log('Created roles child and parent and granted privileges');
  } catch (err) {
    console.error('ERROR:', err);
  }

  // Revoke permissions and drop child role.
  // A role can't be dropped until all its permissions are revoked.
  try {
    const request = ['REVOKE ROLE parent FROM ROLE child', 'DROP ROLE child'];
    const [operation] = await database.updateSchema(request);

    console.log('Waiting for operation to complete...');
    await operation.promise();

    console.log('Revoked privileges and dropped role child');
  } catch (err) {
    console.error('ERROR:', err);
  } finally {
    // Close the database when finished.
    await database.close();
  }
}
addAndDropNewDatabaseRole();

PHP

use Google\Cloud\Spanner\SpannerClient;

/**
 * Adds and drops roles to the Singers table in the example database.
 * Example:
 * ```
 * add_drop_database_role($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function add_drop_database_role(string $instanceId, string $databaseId): void
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $roleParent = 'new_parent';
    $roleChild = 'new_child';

    $operation = $database->updateDdlBatch([
        sprintf('CREATE ROLE %s', $roleParent),
        sprintf('GRANT SELECT ON TABLE Singers TO ROLE %s', $roleParent),
        sprintf('CREATE ROLE %s', $roleChild),
        sprintf('GRANT ROLE %s TO ROLE %s', $roleParent, $roleChild)
    ]);

    printf('Waiting for create role and grant operation to complete...%s', PHP_EOL);
    $operation->pollUntilComplete();

    printf('Created roles %s and %s and granted privileges%s', $roleParent, $roleChild, PHP_EOL);

    $operation = $database->updateDdlBatch([
        sprintf('REVOKE ROLE %s FROM ROLE %s', $roleParent, $roleChild),
        sprintf('DROP ROLE %s', $roleChild)
    ]);

    printf('Waiting for revoke role and drop role operation to complete...%s', PHP_EOL);
    $operation->pollUntilComplete();

    printf('Revoked privileges and dropped role %s%s', $roleChild, PHP_EOL);
}

Python

# instance_id = "your-spanner-instance"
# database_id = "your-spanner-db-id"
spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)
role_parent = "new_parent"
role_child = "new_child"

operation = database.update_ddl(
    [
        "CREATE ROLE {}".format(role_parent),
        "GRANT SELECT ON TABLE Singers TO ROLE {}".format(role_parent),
        "CREATE ROLE {}".format(role_child),
        "GRANT ROLE {} TO ROLE {}".format(role_parent, role_child),
    ]
)
operation.result(OPERATION_TIMEOUT_SECONDS)
print(
    "Created roles {} and {} and granted privileges".format(role_parent, role_child)
)

operation = database.update_ddl(
    [
        "REVOKE ROLE {} FROM ROLE {}".format(role_parent, role_child),
        "DROP ROLE {}".format(role_child),
    ]
)
operation.result(OPERATION_TIMEOUT_SECONDS)
print("Revoked privileges and dropped role {}".format(role_child))

Ruby

require "google/cloud/spanner"

def spanner_add_and_drop_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"

  admin_client = Google::Cloud::Spanner::Admin::Database::V1::DatabaseAdmin::Client.new
  role_parent = "new_parent"
  role_child = "new_child"

  db_path = admin_client.database_path project: project_id, instance: instance_id, database: database_id

  job = admin_client.update_database_ddl database: db_path, 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}"
  ]

  job.wait_until_done!
  puts "Created roles #{role_parent} and #{role_child} and granted privileges"

  job = admin_client.update_database_ddl database: db_path, statements: [
    "REVOKE ROLE #{role_parent} FROM ROLE #{role_child}",
    "DROP ROLE #{role_child}"
  ]

  job.wait_until_done!
  puts "Revoked privileges and dropped role #{role_child}"
end

Crea una gerarchia di ruoli con ereditarietà

Puoi creare una gerarchia di ruoli di database assegnando un ruolo di database a un altro. I ruoli secondari (noti come ruoli membro) ereditano i privilegi dall'elemento principale.

Per concedere un ruolo di database a un altro ruolo di database, utilizza la seguente istruzione:

GRANT ROLE role1 TO ROLE role2;

Per ulteriori informazioni, vedi Gerarchie ed ereditarietà dei ruoli del database.

Concedi l'accesso ai ruoli database alle entità IAM

Puoi utilizzare IAM per concedere l'accesso ai ruoli del database.

Console

Per concedere l'accesso ai ruoli database a un'entità IAM, segui questi passaggi:

  1. Nella pagina Panoramica del database, fai clic su MOSTRA RIQUADRO INFORMAZIONI se il riquadro Informazioni non è già aperto.

  2. Fai clic su AGGIUNGI ENTITÀ.

  3. Nel riquadro Concedi l'accesso a nome_database, in Aggiungi entità, specifica una o più entità IAM.

  4. In Assegna ruoli, nel menu Seleziona un ruolo, seleziona Cloud Spanner > Utente con accesso granulare a Cloud Spanner.

    Devi concedere questo ruolo una sola volta a ciascuna entità. In questo modo, l'entità è un utente granulare del controllo dell'accesso.

  5. Fai clic su AGGIUNGI UN ALTRO RUOLO.

  6. Nel menu Seleziona un ruolo, seleziona Cloud Spanner > Utente ruolo database Cloud Spanner.

  7. Segui questi passaggi per creare la condizione IAM che specifica i ruoli da concedere.

    1. Accanto al ruolo Utente ruolo database Cloud Spanner, fai clic su AGGIUNGI CONDIZIONE IAM.

    2. Nel riquadro Aggiungi condizione, inserisci un titolo e una descrizione facoltativa per la condizione.

      Se stai concedendo un solo ruolo di database, in genere devi includere il nome del ruolo nel titolo della condizione. Se assegni più ruoli, potresti indicare qualcosa sull'insieme di ruoli.

    3. Fai clic su EDITOR CONDIZIONE.

    4. Nel campo Espression, inserisci il seguente codice:

      resource.type == "spanner.googleapis.com/DatabaseRole" &&
      resource.name.endsWith("/ROLE")
      

      Sostituisci ROLE con il nome del tuo ruolo.

      In alternativa, per concedere l'accesso all'entità a più di un ruolo, aggiungi più condizioni con l'operatore or (||), come mostrato nell'esempio seguente:

      resource.type == "spanner.googleapis.com/DatabaseRole" &&
      (resource.name.endsWith("/ROLE1") || resource.name.endsWith("/ROLE2"))
      

      Questo codice concede due ruoli. Sostituisci ROLE1 e ROLE2 con i nomi dei ruoli. Per concedere più di due ruoli, aggiungi più condizioni or.

      Puoi utilizzare qualsiasi espressione di condizione supportata da IAM. Per ulteriori informazioni, consulta la panoramica delle condizioni IAM.

    5. Fai clic su Salva.

    6. Torna nel riquadro precedente, verifica che la condizione venga visualizzata nella colonna Condizione IAM accanto al campo Ruolo.

    7. Fai clic su Salva.

      Nel riquadro Informazioni, in Ruolo/Entità, nota che per ogni condizione definita viene visualizzato Utente ruolo database Cloud Spanner.

      Il numero tra parentesi accanto alla condizione indica il numero di entità a cui è stato concesso il ruolo database per quella condizione. Puoi fare clic sulla freccia di espansione per visualizzare l'elenco delle entità.

    8. Per correggere gli errori nei nomi o nelle condizioni dei ruoli database o per aggiungere altri ruoli database per un'entità, segui questi passaggi:

      1. Espandi la voce Utente ruolo database Cloud Spanner per visualizzare la condizione desiderata.

      2. Fai clic sull'icona Modifica (a forma di matita) accanto a un'entità.

      3. Nel riquadro Modifica accesso a database_name, esegui una delle seguenti operazioni:

        • Fai clic su AGGIUNGI UN ALTRO RUOLO.

        • Per modificare la condizione, fai clic sull'icona Modifica (a forma di matita) accanto al nome della condizione. Nella pagina Modifica condizione, fai clic su EDITOR CONDIZIONE, apporta le correzioni necessarie e fai clic due volte su Salva.

gcloud

Per concedere l'accesso ai ruoli database a un'entità IAM, segui questi passaggi:

  1. Abilita un controllo dell'accesso granulare per l'entità utilizzando il comando gcloud spanner databases add-iam-policy-binding come segue:

    gcloud spanner databases add-iam-policy-binding DATABASE_NAME \
    --instance=INSTANCE_NAME \
    --role=roles/spanner.fineGrainedAccessUser \
    --member=MEMBER_NAME \
    --condition=None
    
    • MEMBER_NAME è l'identificatore del entità. Deve essere nel formato user|group|serviceAccount:email o domain:domain.

    • Questo comando rende l'entità un utente con controllo dell'accesso granulare. Invia questo comando solo una volta per ogni entità.

    • In caso di esito positivo, il comando restituisce l'intero criterio per il database.

  2. Concedi l'autorizzazione a utilizzare uno o più ruoli di database utilizzando il comando gcloud spanner databases add-iam-policy-binding come segue:

    gcloud spanner databases add-iam-policy-binding DATABASE_NAME \
    --instance=INSTANCE_NAME \
    --role=roles/spanner.databaseRoleUser \
    --member=MEMBER_NAME \
    --condition=CONDITION
    
    • MEMBER_NAME è l'identificatore del entità. Deve essere nel formato user|group|serviceAccount:email o domain:domain.

    • CONDITION è un'espressione di una condizione IAM che specifica i ruoli da concedere all'entità.

      CONDITION ha il seguente formato:

      --condition='expression=(resource.type == "spanner.googleapis.com/DatabaseRole" && resource.name.endsWith("/ROLE1")),title=TITLE,description=DESCRIPTION'
      

      In alternativa, per concedere l'accesso all'entità a più di un ruolo, aggiungi più condizioni con l'operatore or (||), come mostrato nell'esempio seguente:

      --condition='expression=(resource.type == "spanner.googleapis.com/DatabaseRole" && (resource.name.endsWith("/ROLE1") || resource.name.endsWith("/ROLE2"))),title=TITLE,description=DESCRIPTION'
      

      Questo codice concede due ruoli. Sostituisci ROLE1 e ROLE2 con i nomi dei ruoli. Per concedere più di due ruoli, aggiungi più condizioni o con l'operatore ||.

      Puoi utilizzare qualsiasi espressione di condizione supportata da IAM. Per ulteriori informazioni, consulta la panoramica delle condizioni IAM.

    In caso di esito positivo, il comando restituisce l'intero criterio per il database.

    L'esempio seguente concede i ruoli di database hr_rep e hr_manager all'entità jsmith@example.com.

    gcloud spanner databases add-iam-policy-binding myDatabase \
      --instance=myInstance \
      --role=roles/spanner.databaseRoleUser \
      --member=user:jsmith@example.com \
      --condition='expression=(resource.type == "spanner.googleapis.com/DatabaseRole" && (resource.name.endsWith("/hr_rep") || resource.name.endsWith("/hr_manager"))),title=HR roles,description=Grant permissions on HR roles'
    

Librerie client

C++

void EnableFineGrainedAccess(
    google::cloud::spanner_admin::DatabaseAdminClient client,
    std::string const& project_id, std::string const& instance_id,
    std::string const& database_id, std::string const& iam_member,
    std::string const& database_role, std::string const& title) {
  google::cloud::spanner::Database database(project_id, instance_id,
                                            database_id);

  google::iam::v1::GetIamPolicyRequest request;
  request.set_resource(database.FullName());
  request.mutable_options()->set_requested_policy_version(3);
  auto policy = client.GetIamPolicy(request);
  if (!policy) throw std::move(policy).status();
  if (policy->version() < 3) policy->set_version(3);

  auto& binding = *policy->add_bindings();
  binding.set_role("roles/spanner.fineGrainedAccessUser");
  binding.add_members(iam_member);
  auto& condition = *binding.mutable_condition();
  condition.set_expression("resource.name.endsWith(\"/databaseRoles/" +
                           database_role + "\")");
  condition.set_title(title);

  auto new_policy =
      client.SetIamPolicy(database.FullName(), *std::move(policy));
  if (!new_policy) throw std::move(new_policy).status();
  std::cout << "Enabled fine-grained access in IAM. New policy has version "
            << new_policy->version() << "\n";
}

C#


using Google.Api.Gax;
using Google.Cloud.Iam.V1;
using Google.Cloud.Spanner.Admin.Database.V1;

public class EnableFineGrainedAccessSample
{
    public Policy EnableFineGrainedAccess(
        string projectId, string instanceId, string databaseId,
        string databaseRole, string iamMember)
    {
        var resourceName = new UnparsedResourceName($"projects/{projectId}/instances/{instanceId}/databases/{databaseId}");

        var client = new DatabaseAdminClientBuilder().Build();

        // Request policy version 3 as earlier versions do not support condition field in role binding.
        // For more information see https://cloud.google.com/iam/docs/policies#versions.

        GetIamPolicyRequest getIamPolicyRequest = new GetIamPolicyRequest
        {
            ResourceAsResourceName = resourceName,
            Options = new GetPolicyOptions
            {
                RequestedPolicyVersion = 3
            }
        };

        var policy = client.GetIamPolicy(getIamPolicyRequest);

        // Gives the given IAM member access to the all the database roles
        // with resource name ending in ../databaseRoles/{databaseRole}.
        // For more information see https://cloud.google.com/iam/docs/conditions-overview.
        Binding newBinding = new Binding
        {
            Role = "roles/spanner.fineGrainedAccessUser",
            Members = { iamMember },
            Condition = new Google.Type.Expr
            {
                Title = "DatabaseRoleBindingTitle",
                Expression = $"resource.name.endsWith('/databaseRoles/{databaseRole}')"
            }
        };

        policy.Bindings.Add(newBinding);
        if (policy.Version < 3)
        {
            policy.Version = 3;
        }
        SetIamPolicyRequest setIamPolicyRequest = new SetIamPolicyRequest
        {
            Policy = policy,
            ResourceAsResourceName = resourceName,
        };
        var updatedPolicy = client.SetIamPolicy(setIamPolicyRequest);
        return updatedPolicy;
    }
}

Go


import (
	"context"
	"fmt"
	"io"

	database "cloud.google.com/go/spanner/admin/database/apiv1"
	iampb "google.golang.org/genproto/googleapis/iam/v1"
	expr "google.golang.org/genproto/googleapis/type/expr"
)

func enableFineGrainedAccess(w io.Writer, db string, iamMember string, databaseRole string, title string) error {
	// iamMember = "user:alice@example.com"
	// databaseRole = "parent"
	// title = "condition title"
	ctx := context.Background()
	adminClient, err := database.NewDatabaseAdminClient(ctx)
	if err != nil {
		return err
	}
	defer adminClient.Close()

	policy, err := adminClient.GetIamPolicy(ctx, &iampb.GetIamPolicyRequest{
		Resource: db,
		Options: &iampb.GetPolicyOptions{
			// IAM conditions need at least version 3
			RequestedPolicyVersion: 3,
		},
	})
	if err != nil {
		return err
	}

	// IAM conditions need at least version 3
	if policy.Version < 3 {
		policy.Version = 3
	}
	policy.Bindings = append(policy.Bindings, []*iampb.Binding{
		{
			Role:    "roles/spanner.fineGrainedAccessUser",
			Members: []string{iamMember},
		},
		{
			Role:    "roles/spanner.databaseRoleUser",
			Members: []string{iamMember},
			Condition: &expr.Expr{
				Expression: fmt.Sprintf(`resource.name.endsWith("/databaseRoles/%s")`, databaseRole),
				Title:      title,
			},
		},
	}...)
	_, err = adminClient.SetIamPolicy(ctx, &iampb.SetIamPolicyRequest{
		Resource: db,
		Policy:   policy,
	})
	if err != nil {
		return err
	}

	fmt.Fprintf(w, "Enabled fine-grained access in IAM.\n")
	return nil
}

Java

import com.google.cloud.Binding;
import com.google.cloud.Condition;
import com.google.cloud.Policy;
import com.google.cloud.spanner.DatabaseAdminClient;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerOptions;
import com.google.common.collect.ImmutableList;

public class EnableFineGrainedAccess {

  static void enableFineGrainedAccess() {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project";
    String instanceId = "my-instance";
    String databaseId = "my-database";
    String iamMember = "user:alice@example.com";
    String role = "my-role";
    String title = "my-condition-title";
    enableFineGrainedAccess(projectId, instanceId, databaseId, iamMember, title, role);
  }

  static void enableFineGrainedAccess(
      String projectId,
      String instanceId,
      String databaseId,
      String iamMember,
      String title,
      String role) {
    try (Spanner spanner =
                 SpannerOptions.newBuilder()
                         .setProjectId(projectId)
                         .build()
                         .getService()) {
      final DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient();
      Policy policy = adminClient.getDatabaseIAMPolicy(instanceId, databaseId, 3);
      int policyVersion = policy.getVersion();
      // The policy in the response from getDatabaseIAMPolicy might use the policy version
      // that you specified, or it might use a lower policy version. For example, if you
      // specify version 3, but the policy has no conditional role bindings, the response
      // uses version 1. Valid values are 0, 1, and 3.
      if (policy.getVersion() < 3) {
        // conditional role bindings work with policy version 3
        policyVersion = 3;
      }

      Binding binding1 =
          Binding.newBuilder()
              .setRole("roles/spanner.fineGrainedAccessUser")
              .setMembers(ImmutableList.of(iamMember))
              .build();

      Binding binding2 =
          Binding.newBuilder()
              .setRole("roles/spanner.databaseRoleUser")
              .setCondition(
                  Condition.newBuilder()
                      .setDescription(title)
                      .setExpression(
                          String.format("resource.name.endsWith(\"/databaseRoles/%s\")", role))
                      .setTitle(title)
                      .build())
              .setMembers(ImmutableList.of(iamMember))
              .build();
      ImmutableList<Binding> bindings =
          ImmutableList.<Binding>builder()
              .addAll(policy.getBindingsList())
              .add(binding1)
              .add(binding2)
              .build();
      Policy policyWithConditions =
          Policy.newBuilder()
              .setVersion(policyVersion)
              .setEtag(policy.getEtag())
              .setBindings(bindings)
              .build();
      Policy response =
          adminClient.setDatabaseIAMPolicy(instanceId, databaseId, policyWithConditions);
      System.out.printf(
          "Enabled fine-grained access in IAM with version %d%n", response.getVersion());
    }
  }
}

Node.js

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

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

async function enableFineGrainedAccess() {
  // Gets a reference to a Cloud Spanner instance and database.
  const instance = spanner.instance(instanceId);
  const database = instance.database(databaseId);

  const [policy] = await database.getIamPolicy({requestedPolicyVersion: 3});
  if (policy.version < 3) {
    policy.version = 3;
  }

  const newBinding = {
    role: 'roles/spanner.fineGrainedAccessUser',
    members: [`user:${iamMember}`],
    condition: {
      title: title,
      expression: `resource.name.endsWith("/databaseRoles/${databaseRole}")`,
    },
  };
  policy.bindings.push(newBinding);
  await database.setIamPolicy({policy: policy});
  // Requested Policy Version is Optional. The maximum policy version that will be used to format the policy.
  // Valid values are 0, 1, and 3. Requests specifying an invalid value will be rejected.
  const newPolicy = await database.getIamPolicy({requestedPolicyVersion: 3});
  console.log(newPolicy);
}
enableFineGrainedAccess();

PHP

use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient;
use \Google\Cloud\Iam\V1\Binding;
use \Google\Type\Expr;

/**
 * Enable Fine Grained Access.
 * Example:
 * ```
 * enable_fine_grained_access($projectId, $instanceId, $databaseId, $iamMember, $databaseRole, $title);
 * ```
 *
 * @param string $projectId The Google cloud project ID
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 * @param string $iamMember The IAM member. Eg: `user:{emailid}`,
 *        `serviceAccount:{emailid}`, `group:{emailid}`, `domain:{domain}`
 * @param string $databaseRole The database role bound to
 *        the IAM member.
 * @param string $title Condition title.
 */
function enable_fine_grained_access(
    string $projectId,
    string $instanceId,
    string $databaseId,
    string $iamMember,
    string $databaseRole,
    string $title
): void {
    $adminClient = new DatabaseAdminClient();
    $resource = sprintf('projects/%s/instances/%s/databases/%s', $projectId, $instanceId, $databaseId);
    $policy = $adminClient->getIamPolicy($resource);

    // IAM conditions need at least version 3
    if ($policy->getVersion() != 3) {
        $policy->setVersion(3);
    }

    $binding = new Binding([
        'role' => 'roles/spanner.fineGrainedAccessUser',
        'members' => [$iamMember],
        'condition' => new Expr([
            'title' => $title,
            'expression' => sprintf("resource.name.endsWith('/databaseRoles/%s')", $databaseRole)
        ])
    ]);
    $policy->setBindings([$binding]);
    $adminClient->setIamPolicy($resource, $policy);

    printf('Enabled fine-grained access in IAM' . PHP_EOL);
}

Python

# instance_id = "your-spanner-instance"
# database_id = "your-spanner-db-id"
# iam_member = "user:alice@example.com"
# database_role = "new_parent"
# title = "condition title"
spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)

# The policy in the response from getDatabaseIAMPolicy might use the policy version
# that you specified, or it might use a lower policy version. For example, if you
# specify version 3, but the policy has no conditional role bindings, the response
# uses version 1. Valid values are 0, 1, and 3.
policy = database.get_iam_policy(3)
if policy.version < 3:
    policy.version = 3

new_binding = policy_pb2.Binding(
    role="roles/spanner.fineGrainedAccessUser",
    members=[iam_member],
    condition=expr_pb2.Expr(
        title=title,
        expression=f'resource.name.endsWith("/databaseRoles/{database_role}")',
    ),
)

policy.version = 3
policy.bindings.append(new_binding)
database.set_iam_policy(policy)

new_policy = database.get_iam_policy(3)
print(
    f"Enabled fine-grained access in IAM. New policy has version {new_policy.version}"
)

Ruby

require "google/cloud/spanner"

def spanner_enable_fine_grained_access project_id:, instance_id:, database_id:, iam_member:, database_role:, title:
  # project_id  = "Your Google Cloud project ID"
  # instance_id = "Your Spanner instance ID"
  # database_id = "Your Spanner database ID"
  # iam_member = "user:alice@example.com"
  # database_role = "new_parent"
  # title = "condition title"

  admin_client = Google::Cloud::Spanner::Admin::Database::V1::DatabaseAdmin::Client.new
  db_path = admin_client.database_path project: project_id, instance: instance_id, database: database_id

  policy = admin_client.get_iam_policy resource: db_path, options: { requested_policy_version: 3 }

  policy.version = 3 if policy.version < 3

  binding = Google::Iam::V1::Binding.new(
    role: "roles/spanner.fineGrainedAccessUser",
    members: [iam_member],
    condition: Google::Type::Expr.new(
      title: title,
      expression: "resource.name.endsWith('/databaseRoles/#{database_role}')"
    )
  )

  policy.bindings << binding
  result = admin_client.set_iam_policy resource: db_path, policy: policy

  puts "Enabled fine-grained access in IAM."
end

Esegui la transizione di un'entità a un controllo dell'accesso granulare

Per eseguire la transizione di un'entità IAM dal controllo dell'accesso a livello di database al controllo dell'accesso granulare, segui questi passaggi:

  1. Abilita il controllo dell'accesso granulare per l'entità e concedi l'accesso a tutti i ruoli di database richiesti, come descritto in Concedere l'accesso ai ruoli di database alle entità IAM.

  2. Aggiorna tutte le applicazioni eseguite come entità. Specifica i ruoli database appropriati nelle chiamate ai metodi della libreria client.

  3. Revocare tutte le autorizzazioni IAM a livello di database dall'entità. In questo modo, l'accesso all'entità è regolato da un solo metodo.

    Per revocare i ruoli IAM a livello di database, segui le istruzioni riportate in Rimuovere le autorizzazioni a livello di database.

Elencare i ruoli del database

Puoi elencare i ruoli di database associati a un database.

Console

Per elencare i ruoli del database, inserisci la query seguente nella pagina Query del database:

SELECT * FROM INFORMATION_SCHEMA.ROLES;

La risposta include il ruolo attuale e i ruoli i cui privilegi possono essere utilizzati dall'ereditarietà. Per recuperare tutti i ruoli, utilizza il comando gcloud.

gcloud

Per ottenere un elenco non filtrato di ruoli database, inserisci il comando seguente. Richiede l'autorizzazione spanner.databaseRoles.list.

gcloud spanner databases roles list --database=DATABASE_NAME --instance=INSTANCE_NAME

Librerie client

C++

void ListDatabaseRoles(google::cloud::spanner_admin::DatabaseAdminClient client,
                       std::string const& project_id,
                       std::string const& instance_id,
                       std::string const& database_id) {
  google::cloud::spanner::Database database(project_id, instance_id,
                                            database_id);
  std::cout << "Database Roles are:\n";
  for (auto& role : client.ListDatabaseRoles(database.FullName())) {
    if (!role) throw std::move(role).status();
    std::cout << role->name() << "\n";
  }
}

C#


using Google.Api.Gax;
using Google.Cloud.Spanner.Admin.Database.V1;
using System;

public class ListDatabaseRolesSample
{
    public PagedEnumerable<ListDatabaseRolesResponse, DatabaseRole> ListDatabaseRoles(string projectId, string instanceId, string databaseId)
    {
        string parent = $"projects/{projectId}/instances/{instanceId}/databases/{databaseId}";
        var client = DatabaseAdminClient.Create();
        PagedEnumerable<ListDatabaseRolesResponse, DatabaseRole> databaseRoles = client.ListDatabaseRoles(parent);
        foreach (var dbRole in databaseRoles)
        {
            Console.WriteLine($"Database Role: {dbRole.DatabaseRoleName}");
        }
        return databaseRoles;
    }
}

Go


import (
	"context"
	"fmt"
	"io"
	"strings"

	"google.golang.org/api/iterator"

	database "cloud.google.com/go/spanner/admin/database/apiv1"
	adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
)

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

	iter := adminClient.ListDatabaseRoles(ctx, &adminpb.ListDatabaseRolesRequest{
		Parent: db,
	})
	rolePrefix := db + "/databaseRoles/"
	for {
		role, err := iter.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return err
		}
		if !strings.HasPrefix(role.Name, rolePrefix) {
			return fmt.Errorf("Role %v does not have prefix %v", role.Name, rolePrefix)
		}
		fmt.Fprintf(w, "%s\n", strings.TrimPrefix(role.Name, rolePrefix))
	}
	return nil
}

Java

import com.google.cloud.spanner.DatabaseAdminClient;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.DatabaseRole;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerOptions;
import java.util.concurrent.ExecutionException;

public class ListDatabaseRoles {

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

  static void listDatabaseRoles(String projectId, String instanceId, String databaseId) {
    try (Spanner spanner =
                 SpannerOptions.newBuilder()
                         .setProjectId(projectId)
                         .build()
                         .getService()) {
      final DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient();
      String databasePath = DatabaseId.of(projectId, instanceId, databaseId).getName();
      System.out.println("List of Database roles");
      for (DatabaseRole role : adminClient.listDatabaseRoles(instanceId, databaseId).iterateAll()) {
        System.out.printf("%s%n", role.getName());
      }
    }
  }
}

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 getDatabaseRoles() {
  // Gets a reference to a Cloud Spanner instance and database.
  const instance = spanner.instance(instanceId);
  const database = instance.database(databaseId);

  // Fetching database roles
  const [databaseRoles] = await database.getDatabaseRoles();
  console.log(`Roles for Database: ${database.formattedName_}`);
  databaseRoles.forEach(role => {
    console.log(`Role: ${role.name}`);
  });
}
getDatabaseRoles();

PHP

use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient;

/**
 * List Database roles in the given database.
 * Example:
 * ```
 * list_database_roles($projectId, $instanceId, $databaseId);
 * ```
 *
 * @param string $projectId The Google cloud project ID
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function list_database_roles(
    string $projectId,
    string $instanceId,
    string $databaseId
): void {
    $adminClient = new DatabaseAdminClient();
    $resource = sprintf('projects/%s/instances/%s/databases/%s', $projectId, $instanceId, $databaseId);

    $roles = $adminClient->listDatabaseRoles($resource);
    printf('List of Database roles:' . PHP_EOL);
    foreach ($roles as $role) {
        printf($role->getName() . PHP_EOL);
    }
}

Python

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

# List database roles.
print("Database Roles are:")
for role in database.list_database_roles():
    print(role.name.split("/")[-1])

Ruby

require "google/cloud/spanner"

def spanner_list_database_roles 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"

  admin_client = Google::Cloud::Spanner::Admin::Database::V1::DatabaseAdmin::Client.new

  db_path = admin_client.database_path project: project_id, instance: instance_id, database: database_id

  result = admin_client.list_database_roles parent: db_path

  puts "List of Database roles:"
  result.each do |role|
    puts role.name
  end
end

Visualizzare i privilegi concessi a un ruolo di database

Per visualizzare i privilegi concessi a un ruolo, esegui le query seguenti:

SELECT * FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES WHERE grantee = 'ROLE_NAME';
SELECT * FROM INFORMATION_SCHEMA.COLUMN_PRIVILEGES WHERE grantee = 'ROLE_NAME';
SELECT * FROM INFORMATION_SCHEMA.CHANGE_STREAM_PRIVILEGES WHERE grantee = 'ROLE_NAME';

INFORMATION_SCHEMA.TABLE_PRIVILEGES restituisce i privilegi sia nelle tabelle che nelle viste. I privilegi SELECT, INSERT e UPDATE in TABLE_PRIVILEGES vengono mostrati anche in COLUMN_PRIVILEGES.

Visualizza utenti granulari di controllo dell'accesso

Per visualizzare un elenco di entità che sono utenti di controllo dell'accesso granulari, esegui questo comando. Per eseguire il comando, devi aver abilitato l'API Cloud Asset sul tuo progetto e devi disporre dell'autorizzazione IAM cloudasset.assets.searchAllIamPolicies.

gcloud asset search-all-iam-policies \
--scope=projects/PROJECT_NAME \
--query='roles=roles/spanner.fineGrainedAccessUser AND resource=//spanner.googleapis.com/projects/PROJECT_NAME/instances/INSTANCE_NAME/databases/DATABASE_NAME' \
--flatten=policy.bindings[].members[] \
--format='table(policy.bindings.members)'

L'output è simile al seguente:

MEMBERS
user:222larabrown@gmail.com
user:baklavainthebalkans@gmail.com
serviceAccount:cs-fgac-sa-1@cloud-spanner-demo.google.com.iam.gserviceaccount.com
serviceAccount:cs-fgac-sa-2@cloud-spanner-demo.google.com.iam.gserviceaccount.com

Per saperne di più, consulta Abilitare un'API nel tuo progetto Google Cloud.

Visualizza entità IAM con accesso a un ruolo di database

Per visualizzare un elenco di entità a cui è stato concesso l'accesso a un determinato ruolo del database, esegui il comando seguente. Per eseguire il comando, devi avere abilitato l'API Cloud Asset nel progetto e disporre dell'autorizzazione IAM cloudasset.assets.searchAllIamPolicies.

gcloud asset search-all-iam-policies \
--scope=projects/PROJECT_NAME \
--query='roles=roles/spanner.databaseRoleUser AND policy:"resource.name" AND policy:/ROLE_NAME AND resource=//spanner.googleapis.com/projects/PROJECT_NAME/instances/INSTANCE_NAME/databases/DATABASE_NAME' \
--flatten=policy.bindings[].members[] \
--format='table(policy.bindings.members)'

L'output è simile al seguente:

MEMBERS
222larabrown@gmail.com

Visualizza le condizioni IAM per un'entità

Per visualizzare un elenco di condizioni IAM specificate al momento della concessione del ruolo Utente ruolo database Cloud Spanner a un'entità, esegui il comando seguente:

gcloud asset search-all-iam-policies \
--scope=projects/PROJECT_NAME \
--query='roles=roles/spanner.databaseRoleUser AND policy:resource.name AND policy:"PRINCIPAL_IDENTIFIER" AND resource=//spanner.googleapis.com/projects/PROJECT_NAME/instances/INSTANCE_NAME/databases/DATABASE_NAME' \
--flatten=policy.bindings[] \
--format='table(policy.bindings.condition.expression)'

dove PRINCIPAL_IDENTIFIER è:

  { user:user-account-name | serviceAccount:service-account-name }

Esempi di PRINCIPAL_IDENTIFIER:

user:222larabrown@gmail.com
serviceAccount:cs-fgac-sa-1@cloud-spanner-demo.google.com.iam.gserviceaccount.com

Il seguente output di esempio mostra due espressioni di condizioni.

EXPRESSION
resource.type == "spanner.googleapis.com/DatabaseRole" &&
resource.name.endsWith("/hr_analyst")
resource.type == "spanner.googleapis.com/DatabaseRole" &&
resource.name.endsWith("/hr_manager")

Verifica che i criteri IAM non includano le condizioni dei ruoli database

Dopo aver concesso l'accesso ai ruoli di database alle entità, ti consigliamo di assicurarti che a ogni associazione IAM sia specificata una condizione.

Per eseguire questo controllo, esegui il comando seguente:

gcloud asset search-all-iam-policies \
--scope=projects/PROJECT_NAME \
--query='roles:roles/spanner.databaseRoleUser AND resource=//spanner.googleapis.com/projects/PROJECT_NAME/instances/INSTANCE_NAME/databases/DATABASE_NAME'
--flatten=policy.bindings[].members[]

L'output è simile al seguente:

ROLE                              MEMBERS                         EXPRESSION
roles/spanner.databaseRoleUser    serviceAccount:cs-fgac-sa-1@...
roles/spanner.databaseRoleUser    serviceAccount:cs-fgac-sa-2@... resource.type == "spanner…"

Tieni presente che nel primo risultato manca una condizione, pertanto le entità in questa associazione hanno accesso a tutti i ruoli del database.

Rilascia un ruolo di database

Se il ruolo viene eliminato dal database, l'appartenenza ad altri ruoli viene revocata automaticamente e l'appartenenza agli altri ruoli viene revocata.

Per eliminare un ruolo del database, devi prima eseguire le seguenti operazioni:

  1. Revocare dal ruolo tutti i privilegi granulari di controllo dell'accesso.
  2. Rimuovere eventuali associazioni di criteri IAM che fanno riferimento a tale ruolo, in modo che un ruolo di database creato in seguito con lo stesso nome non erediti tali associazioni.

Console

Per eliminare un ruolo di database, segui questi passaggi:

  1. Nella pagina Panoramica del database, fai clic su Write DDL (Scrivi DDL).

  2. Per revocare i privilegi del ruolo, inserisci un'istruzione REVOKE.

    Per i dettagli sulla sintassi dell'istruzione REVOKE, consulta il linguaggio di definizione dei dati di Google SQL. Per informazioni sui privilegi, vedi Privilegi di controllo dell'accesso granulare.

    Ad esempio, per revocare SELECT, INSERT e UPDATE nelle tabelle employees e contractors dal ruolo di database hr_manager, inserisci la seguente istruzione:

    REVOKE SELECT, INSERT, UPDATE ON TABLE employees, contractors FROM ROLE hr_manager;
    

    Puoi utilizzare un modello DDL per l'istruzione REVOKE. Nel menu a discesa MODELLI DDL, seleziona Ruoli database e seleziona un modello in Revoca i privilegi di ruolo.

  3. Eliminare qualsiasi condizione IAM associata al ruolo.

    1. Nell'elenco dei ruoli nel riquadro Informazioni, individua il ruolo Utente ruolo database Cloud Spanner con accanto il titolo della condizione di interesse, quindi espandi il ruolo per visualizzare le entità che hanno accesso al ruolo.

    2. Per una delle entità, fai clic sull'icona Modifica entità (a forma di matita).

    3. Nella pagina Modifica accesso, fai clic sull'icona Elimina ruolo (Cestino) accanto al ruolo Utente ruolo database Cloud Spanner.

    4. Fai clic su Salva.

    5. Ripeti i tre passaggi precedenti per altre entità elencate nella condizione.

  4. Per eliminare il ruolo, vai alla pagina Scrivi DDL e inserisci la seguente istruzione:

    DROP ROLE ROLE_NAME;
    

  5. Fai clic su Invia.

gcloud

  1. Per revocare tutti i privilegi di un ruolo e poi rilasciarlo, utilizza il comando gcloud spanner databases ddl update come segue:

    gcloud spanner databases ddl update DATABASE_NAME \
    --instance=INSTANCE_NAME \
    --ddl='REVOKE PERMISSIONS ON TABLE TABLE_NAME FROM ROLE ROLE_NAME; DROP ROLE ROLE_NAME;'
    

    I valori validi per PERMISSIONS sono SELECT, INSERT, UPDATE e DELETE.

  2. Per eliminare eventuali condizioni IAM correlate, utilizza il comando gcloud spanner databases remove-iam-policy-binding come segue:

    gcloud spanner databases remove-iam-policy-binding DATABASE_NAME \
    --instance=INSTANCE_NAME \
    --role=ROLE_NAME \
    --member=MEMBER_NAME \
    --condition=CONDITION
    
    • MEMBER_NAME è l'identificatore del entità. Deve essere nel formato user|group|serviceAccount:email o domain:domain.

    • CONDITION è un'espressione di una condizione IAM che specifica i ruoli da concedere all'entità.

      CONDITION ha il seguente formato:

      --condition='expression=(resource.type == "spanner.googleapis.com/DatabaseRole" && (resource.name.endsWith("/ROLE1") || resource.name.endsWith("/ROLE2"))),title=TITLE,description=DESCRIPTION'
      

      L'intera specifica della condizione deve corrispondere esattamente alla specifica della condizione utilizzata nel comando che ha concesso l'autorizzazione, inclusi titolo e descrizione.

Librerie client

Questi esempi di codice creano e rilasciano un ruolo di database.

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 "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
)

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.api.gax.longrunning.OperationFuture;
import com.google.cloud.spanner.DatabaseAdminClient;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerOptions;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata;
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 = "my-new-parent-role";
    String childRole = "my-new-child-role";
    addAndDropDatabaseRole(projectId, instanceId, databaseId, parentRole, childRole);
  }

  static void addAndDropDatabaseRole(
      String projectId, String instanceId, String databaseId, String parentRole, String childRole) {
    try (Spanner spanner =
                 SpannerOptions.newBuilder()
                         .setProjectId(projectId)
                         .build()
                         .getService())  {
      final DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient();
      OperationFuture<Void, UpdateDatabaseDdlMetadata> operation =
          adminClient.updateDatabaseDdl(
              instanceId,
              databaseId,
              ImmutableList.of(
                  "CREATE ROLE " + parentRole,
                  "GRANT SELECT ON TABLE Albums TO ROLE " + parentRole,
                  "CREATE ROLE " + childRole,
                  "GRANT ROLE " + parentRole + " TO ROLE " + childRole),
              null);
      try {
        System.out.println("Waiting for role create operation to complete...");
        operation.get(5, TimeUnit.MINUTES);
        System.out.printf(
            "Created roles %s and %s and granted privileges%n", parentRole, childRole);
        // Delete role and membership.
        operation =
            adminClient.updateDatabaseDdl(
                instanceId,
                databaseId,
                ImmutableList.of(
                    "REVOKE ROLE " + parentRole + " FROM ROLE " + childRole,
                    "DROP ROLE " + childRole),
                null);
        System.out.println("Waiting for role revoke & drop operation to complete...");
        operation.get(5, TimeUnit.MINUTES);
        System.out.printf("Revoked privileges and dropped role %s%n", childRole);
      } catch (ExecutionException | TimeoutException e) {
        System.out.printf(
            "Error: AddAndDropDatabaseRole failed with error message %s\n", e.getMessage());
        e.printStackTrace();
      } catch (InterruptedException e) {
        System.out.println(
            "Error: Waiting for AddAndDropDatabaseRole operation to finish was interrupted");
      }
    }
  }
}

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 addAndDropNewDatabaseRole() {
  // Gets a reference to a Cloud Spanner instance and database.
  const instance = spanner.instance(instanceId);
  const database = instance.database(databaseId);

  // Creates a new user defined role and grant permissions
  try {
    const request = [
      'CREATE ROLE parent',
      'GRANT SELECT ON TABLE Singers TO ROLE parent',
      'CREATE ROLE child',
      'GRANT ROLE parent TO ROLE child',
    ];
    const [operation] = await database.updateSchema(request);

    console.log('Waiting for operation to complete...');
    await operation.promise();

    console.log('Created roles child and parent and granted privileges');
  } catch (err) {
    console.error('ERROR:', err);
  }

  // Revoke permissions and drop child role.
  // A role can't be dropped until all its permissions are revoked.
  try {
    const request = ['REVOKE ROLE parent FROM ROLE child', 'DROP ROLE child'];
    const [operation] = await database.updateSchema(request);

    console.log('Waiting for operation to complete...');
    await operation.promise();

    console.log('Revoked privileges and dropped role child');
  } catch (err) {
    console.error('ERROR:', err);
  } finally {
    // Close the database when finished.
    await database.close();
  }
}
addAndDropNewDatabaseRole();

PHP

use Google\Cloud\Spanner\SpannerClient;

/**
 * Adds and drops roles to the Singers table in the example database.
 * Example:
 * ```
 * add_drop_database_role($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function add_drop_database_role(string $instanceId, string $databaseId): void
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $roleParent = 'new_parent';
    $roleChild = 'new_child';

    $operation = $database->updateDdlBatch([
        sprintf('CREATE ROLE %s', $roleParent),
        sprintf('GRANT SELECT ON TABLE Singers TO ROLE %s', $roleParent),
        sprintf('CREATE ROLE %s', $roleChild),
        sprintf('GRANT ROLE %s TO ROLE %s', $roleParent, $roleChild)
    ]);

    printf('Waiting for create role and grant operation to complete...%s', PHP_EOL);
    $operation->pollUntilComplete();

    printf('Created roles %s and %s and granted privileges%s', $roleParent, $roleChild, PHP_EOL);

    $operation = $database->updateDdlBatch([
        sprintf('REVOKE ROLE %s FROM ROLE %s', $roleParent, $roleChild),
        sprintf('DROP ROLE %s', $roleChild)
    ]);

    printf('Waiting for revoke role and drop role operation to complete...%s', PHP_EOL);
    $operation->pollUntilComplete();

    printf('Revoked privileges and dropped role %s%s', $roleChild, PHP_EOL);
}

Python

# instance_id = "your-spanner-instance"
# database_id = "your-spanner-db-id"
spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)
role_parent = "new_parent"
role_child = "new_child"

operation = database.update_ddl(
    [
        "CREATE ROLE {}".format(role_parent),
        "GRANT SELECT ON TABLE Singers TO ROLE {}".format(role_parent),
        "CREATE ROLE {}".format(role_child),
        "GRANT ROLE {} TO ROLE {}".format(role_parent, role_child),
    ]
)
operation.result(OPERATION_TIMEOUT_SECONDS)
print(
    "Created roles {} and {} and granted privileges".format(role_parent, role_child)
)

operation = database.update_ddl(
    [
        "REVOKE ROLE {} FROM ROLE {}".format(role_parent, role_child),
        "DROP ROLE {}".format(role_child),
    ]
)
operation.result(OPERATION_TIMEOUT_SECONDS)
print("Revoked privileges and dropped role {}".format(role_child))

Ruby

require "google/cloud/spanner"

def spanner_add_and_drop_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"

  admin_client = Google::Cloud::Spanner::Admin::Database::V1::DatabaseAdmin::Client.new
  role_parent = "new_parent"
  role_child = "new_child"

  db_path = admin_client.database_path project: project_id, instance: instance_id, database: database_id

  job = admin_client.update_database_ddl database: db_path, 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}"
  ]

  job.wait_until_done!
  puts "Created roles #{role_parent} and #{role_child} and granted privileges"

  job = admin_client.update_database_ddl database: db_path, statements: [
    "REVOKE ROLE #{role_parent} FROM ROLE #{role_child}",
    "DROP ROLE #{role_child}"
  ]

  job.wait_until_done!
  puts "Revoked privileges and dropped role #{role_child}"
end

Informazioni dettagliate