הגדרת בקרת גישה פרטנית

בדף הזה מוסבר איך מגדירים בקרת גישה פרטנית למסדי נתונים של Spanner עם ניב GoogleSQL ומסדי נתונים עם ניב PostgreSQL.

מידע על בקרת גישה פרטנית מופיע במאמר מידע על בקרת גישה פרטנית.

כדי להגדיר בקרת גישה פרטנית:

  1. יצירת תפקידים במסד הנתונים והקצאת הרשאות.

  2. אופציונלי: יצירת היררכיה של תפקידים עם הרשאות בירושה.

  3. הקצאת תפקידים במסד נתונים לישויות מורשות

  4. מודיעים למשתמשים ולמפתחים להשתמש בתפקידים במסד הנתונים.

משתמשים עם בקרת גישה ברמת גרנולריות גבוהה צריכים לציין תפקיד במסד הנתונים כדי לבצע שאילתות, DML או פעולות בשורות במסד הנתונים.

לפני שמתחילים

צריך לוודא שלכל חשבון ראשי שרוצים להגדיר לו בקרת גישה ברמת גרנולריות גבוהה מוקצה תפקיד Cloud Spanner Viewer ניהול זהויות והרשאות גישה (IAM) (roles/spanner.viewer). מומלץ להקצות את התפקיד הזה ברמת הפרויקט למשתמשים שצריכים לבצע אינטראקציה עם משאבי Spanner במסוף Google Cloud .

הוראות מפורטות מופיעות במאמר בנושא הענקת הרשאות לישויות.

יצירת תפקידים במסד הנתונים והקצאת הרשאות

תפקיד במסד נתונים הוא אוסף של הרשאות גישה פרטניות. אפשר ליצור עד 100 תפקידים במסד נתונים לכל מסד נתונים.

מחליטים על תפקידים והיררכיות של תפקידים במסד הנתונים ומקודדים אותם ב-DDL. כמו בשינויים אחרים בסכימה ב-Spanner, מומלץ מאוד לבצע שינויים בסכימה בחבילה ולא בנפרד. מידע נוסף זמין במאמר בנושא הגבלת התדירות של עדכוני סכימה.

המסוף

כדי ליצור תפקיד במסד נתונים ולהעניק לו הרשאות גישה מפורטות, פועלים לפי השלבים הבאים:

  1. נכנסים לדף Instances במסוף Google Cloud .

    מכונות

  2. בוחרים את המופע שמכיל את מסד הנתונים שרוצים להוסיף לו את התפקיד.

  3. בוחרים את מסד הנתונים.

  4. בדף סקירה כללית, לוחצים על Spanner Studio.

  5. בדף Spanner Studio, לכל תפקיד במסד הנתונים שרוצים ליצור ולהעניק לו הרשאות, פועלים לפי השלבים הבאים:

    1. כדי ליצור את התפקיד, מזינים את ההצהרה הבאה:

      CREATE ROLE ROLE_NAME;

      אל תלחצו על שליחה עדיין.

    2. כדי להעניק הרשאות לתפקיד, מזינים הצהרת GRANT בשורה הבאה אחרי הצהרת CREATE ROLE.

      פרטים על התחביר של הצהרת GRANT זמינים במאמר בנושא שפת הגדרת נתונים ב-GoogleSQL. מידע על הרשאות זמין במאמר הרשאות של בקרת גישה ברמת פירוט גבוהה.

      לדוגמה, כדי להעניק את ההרשאות SELECT,‏ INSERT ו-UPDATE בטבלאות employees ו-contractors לתפקיד במסד הנתונים hr_manager, מזינים את ההצהרה הבאה:

      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;
      

      אפשר להשתמש בתבנית DDL עבור הצהרת GRANT. בחלונית Explorer, עוברים לתפקיד שרוצים להעניק לו הרשאות. לוחצים על הצגת פעולות ובוחרים את סוג ההרשאה שרוצים להעניק לתפקיד הזה. התבנית GRANT מאוכלסת בכרטיסייה חדשה של כלי העריכה.

  6. לוחצים על שליחה.

    אם יש שגיאות ב-DDL, במסוף מוחזרת שגיאה. Google Cloud

gcloud

כדי ליצור תפקיד במסד נתונים ולהעניק לו הרשאות גישה מפורטות, משתמשים בפקודה gcloud spanner databases ddl update עם ההצהרות CREATE ROLE ו-GRANT.

לפרטים על התחביר של ההצהרות CREATE ROLE ו-GRANT, אפשר לעיין במאמר בנושא שפת הגדרת הנתונים ב-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;'

מחליפים את מה שכתוב בשדות הבאים:

לדוגמה, כדי להעניק את ההרשאות SELECT,‏ INSERT ו-UPDATE בטבלאות employees ו-contractors לתפקיד hr_analyst במסד הנתונים hrdb1 במופע hr, מזינים את ההצהרה הבאה:

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,
      String parentRole, String childRole, String... tables) {
    try (Spanner spanner =
        SpannerOptions.newBuilder()
            .setProjectId(projectId)
            .build()
            .getService();
        DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
      System.out.println("Waiting for role create operation to complete...");
      List<String> roleStatements = new ArrayList<>(ImmutableList.of(
          String.format("CREATE ROLE %s", parentRole),
          String.format("CREATE ROLE %s", childRole),
          String.format("GRANT ROLE %s TO ROLE %s", parentRole, childRole)));
      for (String table : tables) {
        roleStatements.add(String.format("GRANT SELECT ON TABLE %s TO ROLE %s", table, parentRole));
      }
      databaseAdminClient.updateDatabaseDdlAsync(
              DatabaseName.of(projectId, instanceId, databaseId), roleStatements)
          .get(5, TimeUnit.MINUTES);
      System.out.printf(
          "Created roles %s and %s and granted privileges%n", parentRole, childRole);
      // Delete role and membership.
      System.out.println("Waiting for role revoke & drop operation to complete...");
      databaseAdminClient.updateDatabaseDdlAsync(
          DatabaseName.of(projectId, instanceId, databaseId),
          ImmutableList.of(
              String.format("REVOKE ROLE %s FROM ROLE %s", parentRole, childRole),
              String.format("DROP ROLE %s", childRole))).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 client library
const {Spanner} = require('@google-cloud/spanner');

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

const databaseAdminClient = spanner.getDatabaseAdminClient();

async function addAndDropNewDatabaseRole() {
  // 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 databaseAdminClient.updateDatabaseDdl({
      database: databaseAdminClient.databasePath(
        projectId,
        instanceId,
        databaseId,
      ),
      statements: 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 databaseAdminClient.updateDatabaseDdl({
      database: databaseAdminClient.databasePath(
        projectId,
        instanceId,
        databaseId,
      ),
      statements: 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 spanner client when finished.
    // The databaseAdminClient does not require explicit closure. The closure of the Spanner client will automatically close the databaseAdminClient.
    spanner.close();
  }
}
addAndDropNewDatabaseRole();

PHP

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

/**
 * Adds and drops roles to the Singers table in the example database.
 * Example:
 * ```
 * add_drop_database_role($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 add_drop_database_role(string $projectId, string $instanceId, string $databaseId): void
{
    $databaseAdminClient = new DatabaseAdminClient();
    $databaseName = DatabaseAdminClient::databaseName($projectId, $instanceId, $databaseId);

    $request = new UpdateDatabaseDdlRequest([
        'database' => $databaseName,
        'statements' => [
            'CREATE ROLE new_parent',
            'GRANT SELECT ON TABLE Singers TO ROLE new_parent',
            'CREATE ROLE new_child',
            'GRANT ROLE new_parent TO ROLE new_child'
        ]
    ]);

    $operation = $databaseAdminClient->updateDatabaseDdl($request);

    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', 'new_parent', 'new_child', PHP_EOL);

    $request = new UpdateDatabaseDdlRequest([
        'database' => $databaseName,
        'statements' => [
            'REVOKE ROLE new_parent FROM ROLE new_child',
            'DROP ROLE new_child'
        ]
    ]);

    $operation = $databaseAdminClient->updateDatabaseDdl($request);

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

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

Python

# instance_id = "your-spanner-instance"
# database_id = "your-spanner-db-id"

from google.cloud.spanner_admin_database_v1.types import spanner_database_admin

spanner_client = spanner.Client()
database_admin_api = spanner_client.database_admin_api

role_parent = "new_parent"
role_child = "new_child"

request = spanner_database_admin.UpdateDatabaseDdlRequest(
    database=database_admin_api.database_path(
        spanner_client.project, instance_id, database_id
    ),
    statements=[
        "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 = database_admin_api.update_database_ddl(request)

operation.result(OPERATION_TIMEOUT_SECONDS)
print(
    "Created roles {} and {} and granted privileges".format(role_parent, role_child)
)

request = spanner_database_admin.UpdateDatabaseDdlRequest(
    database=database_admin_api.database_path(
        spanner_client.project, instance_id, database_id
    ),
    statements=[
        "REVOKE ROLE {} FROM ROLE {}".format(role_parent, role_child),
        "DROP ROLE {}".format(role_child),
    ],
)
operation = database_admin_api.update_database_ddl(request)

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

יצירת היררכיה של תפקידים עם העברה אוטומטית

אפשר ליצור היררכיה של תפקידים במסד נתונים על ידי הקצאת תפקיד אחד במסד נתונים לתפקיד אחר. תפקידים צאצאים (שנקראים תפקידים של חברים) יורשים הרשאות מהתפקיד ההורה.

כדי להעניק תפקיד במסד נתונים לתפקיד אחר במסד הנתונים, משתמשים בהצהרה הבאה:

GoogleSQL

GRANT ROLE role1 TO ROLE role2;

PostgreSQL

GRANT role1 TO role2;

מידע נוסף זמין במאמר היררכיות וירושה של תפקידים במסד נתונים.

הקצאת תפקידים במסד נתונים לישויות מורשות

כדי לגשת למשאבי Spanner, צריך להקצות לחשבון ראשי את תפקיד מסד הנתונים הנדרש באמצעות אחת מהאפשרויות הבאות:

המסוף

  1. בדף Overview של מסד הנתונים, לוחצים על SHOW INFO PANEL אם חלונית המידע עדיין לא פתוחה.

  2. לוחצים על הוספת גורם ראשי.

  3. בקטע Add principals, בשדה New principals, מזינים חשבון משתמש אחד או יותר.

  4. בקטע Assign roles, בתפריט Select a role, בוחרים באפשרות Cloud Spanner > Cloud Spanner Fine-grained Access User.

    צריך להקצות את התפקיד הזה לכל ישות מורשית רק פעם אחת. היא הופכת את הגורם המורשה למשתמש עם בקרת גישה פרטנית.

  5. לוחצים על הוספת תפקיד נוסף.

  6. ברשימת התפקידים Select a role בוחרים באפשרות Cloud Spanner > Cloud Spanner Database Role User.

  7. כדי ליצור את תנאי ה-IAM שמציין את התפקידים להענקה:

    1. לצד התפקיד Cloud Spanner Database Role User, לוחצים על ADD IAM CONDITION (הוספת תנאי IAM).

    2. בחלונית Add condition מזינים שם ותיאור אופציונלי של התנאי.

      אם מעניקים תפקיד יחיד במסד נתונים, בדרך כלל כוללים את שם התפקיד בכותרת התנאי. אם מעניקים כמה תפקידים, אפשר לציין משהו לגבי קבוצת התפקידים.

    3. לוחצים על כלי התנאים.

    4. בשדה Expression, מזינים את הקוד הבא:

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

      מחליפים את ROLE בשם התפקיד.

      כדי לתת יותר מתפקיד אחד לחשבון הראשי, מוסיפים עוד תנאים עם האופרטור or (||), כמו בדוגמה הבאה:

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

      הקוד הזה מעניק שני תפקידים. מחליפים את ROLE1 ואת ROLE2 בשמות התפקידים. כדי להקצות יותר משני תפקידים, מוסיפים עוד תנאי או.

      אפשר להשתמש בכל ביטוי תנאי שנתמך על ידי IAM. מידע נוסף זמין במאמר סקירה כללית על תנאים ב-IAM.

    5. לוחצים על Save.

    6. מוודאים שהתנאי מופיע בעמודה IAM Condition לצד השדה Role.

    7. לוחצים על Save.

      בחזרה לחלונית המידע, בקטע תפקיד/גורם ראשי, שימו לב שהתפקיד משתמש בתפקיד מסד נתונים ב-Cloud Spanner מופיע לכל תנאי מוגדר.

      המספר בסוגריים לצד התנאי מציין את מספר החשבונות הראשיים שקיבלו את תפקיד מסד הנתונים באמצעות התנאי הזה. אפשר ללחוץ על החץ להרחבה כדי לראות את רשימת חשבונות המשתמשים.

    8. כדי לתקן שגיאות בשמות של תפקידים במסד נתונים או בתנאים, או כדי להוסיף תפקידים נוספים במסד נתונים עבור חשבון משתמש, פועלים לפי השלבים הבאים:

      1. מרחיבים את הערך Cloud Spanner Database Role User שבו מפורט התנאי הרצוי.

      2. לוחצים על סמל העריכה (עיפרון) לצד הגורם המרכזי.

      3. בחלונית גישת עריכה ל- database_name, מבצעים אחת מהפעולות הבאות:

        • לוחצים על הוספת תפקיד נוסף.

        • כדי לערוך את התנאי, לוחצים על סמל העריכה (עיפרון) לצד שם התנאי. לאחר מכן, בדף Edit condition, לוחצים על Condition editor, מבצעים את התיקונים ולוחצים על Save פעמיים.

gcloud

  1. מפעילים בקרת גישה פרטנית עבור הגורם הראשי באמצעות הפקודה gcloud spanner databases add-iam-policy-binding באופן הבא:

    gcloud spanner databases add-iam-policy-binding DATABASE_NAME \
    --instance=INSTANCE_NAME \
    --role=roles/spanner.fineGrainedAccessUser \
    --member=MEMBER_NAME \
    --condition=None
    • MEMBER_NAME הוא המזהה של החשבון הראשי. צריך להשתמש באחד מסוגי התחביר הבאים: user|group|serviceAccount:email או domain:domain.

    • הפקודה הזו הופכת את הגורם העיקרי למשתמש עם בקרת גישה ברמת דיוק גבוהה. צריך לשלוח את הפקודה הזו רק פעם אחת לכל גורם מרכזי.

    • אם הפקודה מסתיימת בלי שגיאות, היא מחזירה את כל המדיניות של מסד הנתונים.

  2. כדי להעניק הרשאה להשתמש בתפקיד אחד או יותר במסד נתונים, משתמשים בפקודה gcloud spanner databases add-iam-policy-binding באופן הבא:

    gcloud spanner databases add-iam-policy-binding DATABASE_NAME \
    --instance=INSTANCE_NAME \
    --role=roles/spanner.databaseRoleUser \
    --member=MEMBER_NAME \
    --condition=CONDITION
    • MEMBER_NAME הוא המזהה של החשבון הראשי. צריך להשתמש באחד מסוגי התחביר הבאים: user|group|serviceAccount:email או domain:domain.

    • CONDITION הוא ביטוי של תנאי IAM שמציין את התפקידים שיוקצו לישות המורשית.

      הפורמט של CONDITION הוא:

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

      לחלופין, כדי לתת לחשבון הראשי גישה ליותר מתפקיד אחד, מוסיפים עוד תנאים עם האופרטור or (||), כמו בדוגמה הבאה:

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

      הקוד הזה מעניק שני תפקידים. מחליפים את ROLE1 ואת ROLE2 בשמות התפקידים. כדי להעניק יותר משני תפקידים, מוסיפים עוד תנאי or עם האופרטור ||.

      אפשר להשתמש בכל ביטוי תנאי שנתמך על ידי IAM. מידע נוסף זמין במאמר סקירה כללית על תנאים ב-IAM.

    אם הפקודה מסתיימת בלי שגיאות, היא מחזירה את כל המדיניות של מסד הנתונים.

    בדוגמה הבאה מוקצים תפקידי מסד הנתונים hr_rep ו-hr_manager לחשבון המשתמש 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'
    

ספריות לקוח

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"

	"cloud.google.com/go/iam/apiv1/iampb"
	database "cloud.google.com/go/spanner/admin/database/apiv1"
	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.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.iam.v1.Binding;
import com.google.iam.v1.GetIamPolicyRequest;
import com.google.iam.v1.GetPolicyOptions;
import com.google.iam.v1.Policy;
import com.google.iam.v1.SetIamPolicyRequest;
import com.google.spanner.admin.database.v1.DatabaseName;
import com.google.type.Expr;

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();
        DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
      final GetPolicyOptions options =
          GetPolicyOptions.newBuilder().setRequestedPolicyVersion(3).build();
      final GetIamPolicyRequest getRequest =
          GetIamPolicyRequest.newBuilder()
              .setResource(DatabaseName.of(projectId, instanceId, databaseId).toString())
              .setOptions(options).build();
      final Policy policy = databaseAdminClient.getIamPolicy(getRequest);
      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")
              .addAllMembers(ImmutableList.of(iamMember))
              .build();

      Binding binding2 =
          Binding.newBuilder()
              .setRole("roles/spanner.databaseRoleUser")
              .setCondition(
                  Expr.newBuilder().setDescription(title).setExpression(
                      String.format("resource.name.endsWith(\"/databaseRoles/%s\")", role)
                  ).setTitle(title).build())
              .addAllMembers(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())
              .addAllBindings(bindings)
              .build();
      final SetIamPolicyRequest setRequest =
          SetIamPolicyRequest.newBuilder()
              .setResource(DatabaseName.of(projectId, instanceId, databaseId).toString())
              .setPolicy(policyWithConditions).build();
      final Policy response = databaseAdminClient.setIamPolicy(setRequest);
      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, protos} = require('@google-cloud/spanner');

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

async function enableFineGrainedAccess() {
  // Gets a reference to a Cloud Spanner Database Admin Client object
  const databaseAdminClient = spanner.getDatabaseAdminClient();

  const [policy] = await databaseAdminClient.getIamPolicy({
    resource: databaseAdminClient.databasePath(
      projectId,
      instanceId,
      databaseId,
    ),
    options: (protos.google.iam.v1.GetPolicyOptions = {
      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 databaseAdminClient.setIamPolicy({
    resource: databaseAdminClient.databasePath(
      projectId,
      instanceId,
      databaseId,
    ),
    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 databaseAdminClient.getIamPolicy({
    resource: databaseAdminClient.databasePath(
      projectId,
      instanceId,
      databaseId,
    ),
    options: (protos.google.iam.v1.GetPolicyOptions = {
      requestedPolicyVersion: 3,
    }),
  });
  console.log(newPolicy);
}
enableFineGrainedAccess();

PHP


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

/**
 * 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 = $adminC>lient-databaseName($projectId, $instanceId, $databaseId);
    $getIamPolicyRequest = (new GetIamPolicyRequest())
   >     -setResource($resource);
    $policy = $adminC>lient-getIamPolicy($getIamPolicyRequest);

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

    $binding = new Binding([
        'r>ole' = 'roles/spanner.fineGrainedAccessUser',
        'memb>ers' = [$iamMember],
        'condit>ion' = new Expr([
            'ti>tle' = $title,
            'express>ion' = sprintf("resource.name.endsWith('/databaseRoles/%s')", $databaseRole)
        ])
    ]);
    $p>olicy-setBindings([$binding]);
    $setIamPolicyRequest = (new SetIamPolicyRequest())
   >     -setResource($resource)
   >     -setPolicy($policy);
    $adminC>lient-setIamPolicy($setIamPolicyRequest);

    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"

from google.iam.v1 import iam_policy_pb2, options_pb2, policy_pb2
from google.type import expr_pb2

spanner_client = spanner.Client()
database_admin_api = spanner_client.database_admin_api

# 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.
request = iam_policy_pb2.GetIamPolicyRequest(
    resource=database_admin_api.database_path(
        spanner_client.project, instance_id, database_id
    ),
    options=options_pb2.GetPolicyOptions(requested_policy_version=3),
)
policy = database_admin_api.get_iam_policy(request=request)
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)
set_request = iam_policy_pb2.SetIamPolicyRequest(
    resource=database_admin_api.database_path(
        spanner_client.project, instance_id, database_id
    ),
    policy=policy,
)
database_admin_api.set_iam_policy(set_request)

new_policy = database_admin_api.get_iam_policy(request=request)
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

הודעה למשתמשים ולמפתחים להתחיל להשתמש בתפקידים במסד הנתונים

אחרי שמסיימים את ההגדרה הראשונית של בקרת גישה ברמת דיוק גבוהה, צריך להודיע למשתמשים ולמפתחי האפליקציות שהם צריכים להתחיל להשתמש בתפקידים במסד הנתונים.

  • משתמשים עם בקרת גישה ברמת דיוק גבוהה צריכים להתחיל לציין תפקיד במסד נתונים כשהם ניגשים למסדי נתונים של Spanner דרך מסוף Google Cloud או דרך Google Cloud CLI.

  • אפליקציות שמשתמשות בבקרת גישה ברמת גרנולריות גבוהה צריכות לציין תפקיד במסד הנתונים כשניגשים למסד הנתונים.

מידע נוסף זמין במאמר בנושא גישה למסד נתונים עם בקרת גישה מפורטת.

העברת ישות מורשית לבקרת גישה פרטנית

כדי להעביר ישות מורשית מבקרת גישה ברמת מסד הנתונים לבקרת גישה ברמת הגרנולריות, פועלים לפי השלבים הבאים:

  1. מפעילים בקרת גישה פרטנית לחשבון המשתמש ומעניקים גישה לכל תפקידי מסד הנתונים הנדרשים, כמו שמתואר במאמר הענקת תפקידי מסד נתונים לחשבונות משתמש.

  2. מעדכנים את כל האפליקציות שפועלות כמשתמש הראשי הזה. מציינים את תפקידי מסד הנתונים המתאימים בקריאות לשיטות של ספריית הלקוח.

  3. מבטלים את כל התפקידים ב-IAM ברמת מסד הנתונים מהחשבון הראשי. כך, הגישה של המנהל/ת כפופה לשיטה אחת בלבד.

    יוצא מן הכלל: כדי ליצור אינטראקציה עם משאבי Spanner בGoogle Cloud מסוף, כל המשתמשים צריכים לקבל את תפקיד IAM‏ roles/spanner.viewer.

    כדי לבטל תפקידים ברמת מסד הנתונים ב-IAM, פועלים לפי ההוראות במאמר בנושא הסרת הרשאות ברמת מסד הנתונים.

הצגת רשימת תפקידים במסד נתונים

אפשר לראות את רשימת התפקידים במסד הנתונים שמשויכים למסד נתונים.

המסוף

כדי להציג רשימה של תפקידים במסד נתונים, מזינים את השאילתה הבאה בדף Spanner Studio של מסד הנתונים:

GoogleSQL

SELECT * FROM INFORMATION_SCHEMA.ROLES;

PostgreSQL

SELECT * FROM information_schema.enabled_roles;

התשובה כוללת את התפקיד הנוכחי ואת התפקידים שההרשאות שלהם יכולות לשמש את התפקיד הנוכחי באמצעות ירושה. כדי לאחזר את כל התפקידים, משתמשים בפקודה של ה-CLI של gcloud.

gcloud

כדי לקבל רשימה לא מסוננת של תפקידים במסד הנתונים, מזינים את הפקודה הבאה. נדרשת ההרשאה spanner.databaseRoles.list.

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

ספריות לקוח

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 "cloud.google.com/go/spanner/admin/database/apiv1/databasepb"
)

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.Spanner;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient.ListDatabaseRolesPage;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient.ListDatabaseRolesPagedResponse;
import com.google.spanner.admin.database.v1.DatabaseName;
import com.google.spanner.admin.database.v1.DatabaseRole;

public class ListDatabaseRoles {

  static void listDatabaseRoles() {
    // 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();
        DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
      DatabaseName databaseName = DatabaseName.of(projectId, instanceId, databaseId);
      ListDatabaseRolesPagedResponse response
          = databaseAdminClient.listDatabaseRoles(databaseName);
      System.out.println("List of Database roles");
      for (ListDatabaseRolesPage page : response.iteratePages()) {
        for (DatabaseRole role : page.iterateAll()) {
          System.out.printf("Obtained role %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 client library
const {Spanner} = require('@google-cloud/spanner');

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

const databaseAdminClient = spanner.getDatabaseAdminClient();

async function getDatabaseRoles() {
  // Fetching database roles
  const [databaseRoles] = await databaseAdminClient.listDatabaseRoles({
    parent: databaseAdminClient.databasePath(
      projectId,
      instanceId,
      databaseId,
    ),
  });
  console.log(
    `Roles for Database: ${databaseAdminClient.databasePath(
      projectId,
      instanceId,
      databaseId,
    )}`,
  );
  databaseRoles.forEach(role => {
    console.log(`Role: ${role.name}`);
  });
}
getDatabaseRoles();

PHP

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

/**
 * 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 = $adminClient->databaseName($projectId, $instanceId, $databaseId);
    $listDatabaseRolesRequest = (new ListDatabaseRolesRequest())
        ->setParent($resource);

    $roles = $adminClient->listDatabaseRoles($listDatabaseRolesRequest);
    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"
from google.cloud.spanner_admin_database_v1.types import spanner_database_admin

spanner_client = spanner.Client()
database_admin_api = spanner_client.database_admin_api

request = spanner_database_admin.ListDatabaseRolesRequest(
    parent=database_admin_api.database_path(
        spanner_client.project, instance_id, database_id
    )
)
# List database roles.
print("Database Roles are:")
for role in database_admin_api.list_database_roles(request):
    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

הצגת ההרשאות שניתנו לתפקיד במסד נתונים

כדי לראות את ההרשאות שניתנו לתפקיד, מריצים את השאילתות הבאות:

GoogleSQL

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 מחזירה הרשאות גם בטבלאות וגם בתצוגות. הרשאות SELECT, INSERT ו-UPDATE ב-TABLE_PRIVILEGES מוצגות גם ב-COLUMN_PRIVILEGES.

PostgreSQL

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 מחזירה הרשאות גם בטבלאות וגם בתצוגות. הרשאות SELECT, INSERT וUPDATE ב-table_privileges מוצגות גם ב-column_privileges.

הצגת משתמשים עם בקרת גישה פרטנית

כדי להציג רשימה של גורמים שהם משתמשים בבקרת גישה ברמת דיוק גבוהה, מריצים את הפקודה הבאה. כדי להריץ את הפקודה, צריך להפעיל את Cloud Asset API בפרויקט וצריכה להיות לכם הרשאת 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)'

הפלט אמור להיראות כך:

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

מידע נוסף מופיע במאמר הפעלת API בפרויקט Google Cloud .

הצגת ישויות עם גישה למסד הנתונים

כדי לראות רשימה של חשבונות משתמשים שקיבלו גישה למסד נתונים מסוים, מריצים את הפקודות הבאות. כדי להריץ את הפקודות האלה, צריך להפעיל את Cloud Asset API בפרויקט, וצריכה להיות לכם הרשאת 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)'

הפלט אמור להיראות כך:

MEMBERS
222larabrown@gmail.com

צפייה בתנאי ה-IAM של גורם ראשי

כדי לראות רשימה של תנאי IAM שצוינו כשניתן לאנטיתי תפקיד משתמש בתפקיד מסד נתונים ב-Cloud Spanner, מריצים את הפקודה הבאה:

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

כאשר PRINCIPAL_IDENTIFIER הוא:

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

PRINCIPAL_IDENTIFIER דוגמאות:

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

בדוגמה הבאה של פלט מוצגים שני ביטויי תנאים.

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

בדיקת תנאים חסרים של תפקידי מסד נתונים במדיניות IAM

אחרי שנותנים לחשבונות משתמשים גישה לתפקידים במסד הנתונים, מומלץ לוודא שלכל ישות ב-IAM יש תנאי שצוין.

כדי לבצע את הבדיקה הזו, מריצים את הפקודה הבאה:

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[]

הפלט אמור להיראות כך:

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

שימו לב שבתוצאה הראשונה חסר תנאי, ולכן לחשבונות המשתמש בקישור הזה יש גישה לכל התפקידים במסד הנתונים.

הסרת תפקיד במסד נתונים

ביטול של תפקיד במסד נתונים מבטל אוטומטית את החברות של תפקידים אחרים בתפקיד, ואת החברות של התפקיד בתפקידים אחרים.

כדי לבטל הקצאה של תפקיד במסד נתונים, קודם צריך לבצע את הפעולות הבאות:

  1. ביטול כל ההרשאות של בקרת גישה פרטנית מהתפקיד.
  2. מסירים את כל הקישורים למדיניות IAM שמפנים לתפקיד הזה, כדי שתפקיד מסד נתונים שייווצר בהמשך עם אותו שם לא יירש את הקישורים האלה.

המסוף

כדי לבטל הקצאה של תפקיד במסד נתונים, פועלים לפי השלבים הבאים:

  1. בדף Overview של מסד הנתונים, לוחצים על Spanner Studio.

  2. כדי לבטל הרשאות מהתפקיד, מזינים הצהרת REVOKE.

    GoogleSQL

    פרטים על התחביר של הצהרת REVOKE זמינים במאמר בנושא שפת הגדרת נתונים ב-GoogleSQL. מידע על הרשאות זמין במאמר הרשאות של בקרת גישה ברמת פירוט גבוהה.

    לדוגמה, כדי לבטל את ההרשאות של SELECT, של INSERT ושל UPDATE בטבלאות employees ו-contractors מהתפקיד hr_manager במסד הנתונים, מזינים את ההצהרה הבאה:

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

    PostgreSQL

    לפרטים על התחביר של ההצהרה REVOKE, אפשר לעיין בשפת הגדרת הנתונים של PostgreSQL. מידע על הרשאות זמין במאמר הרשאות של בקרת גישה ברמת פירוט גבוהה.

    לדוגמה, כדי לבטל את ההרשאות SELECT, INSERT ו-UPDATE בטבלאות employees ו-contractors מהתפקיד במסד הנתונים hr_manager, מזינים את ההצהרה הבאה:

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

    אפשר להשתמש בתבנית DDL עבור הצהרת REVOKE. בחלונית Explorer, עוברים לתפקיד שרוצים לבטל את ההרשאה שלו. לוחצים על הצגת פעולות ובוחרים את סוג ההרשאה שרוצים לבטל את הגישה אליו עבור התפקיד הזה. התבנית REVOKE מאוכלסת בכרטיסייה חדשה של כלי העריכה.

  3. מוחקים את תנאי ה-IAM שמשויכים לתפקיד.

    1. ברשימת התפקידים בחלונית המידע, מאתרים את התפקיד Cloud Spanner Database Role User עם שם התנאי הרלוונטי שמופיע לידו, ואז מרחיבים את התפקיד כדי לראות את חשבונות המשתמשים שיש להם גישה לתפקיד.

    2. לוחצים על סמל עריכת הגורם המורשה (עיפרון) של אחד מהגורמים המורשים.

    3. בדף Edit access (עריכת הגישה), לוחצים על סמל מחיקת התפקיד (פח האשפה) לצד התפקיד Cloud Spanner Database Role User.

    4. לוחצים על Save.

    5. חוזרים על שלושת השלבים הקודמים עבור שאר הגורמים המורשים שמופיעים בקטע של התנאי.

  4. כדי לבטל את התפקיד, עוברים לדף Spanner Studio ומזינים את ההצהרה הבאה:

    DROP ROLE ROLE_NAME;

  5. לוחצים על שליחה.

gcloud

  1. כדי לבטל את כל ההרשאות של תפקיד מסוים ואז להסיר את התפקיד, משתמשים בפקודה gcloud spanner databases ddl update באופן הבא:

    GoogleSQL

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

    PostgreSQL

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

    הערכים התקפים של PERMISSIONS הם SELECT,‏ INSERT,‏ UPDATE ו-DELETE.

  2. כדי למחוק תנאים קשורים של IAM, משתמשים בפקודה gcloud spanner databases remove-iam-policy-binding באופן הבא:

    gcloud spanner databases remove-iam-policy-binding DATABASE_NAME \
    --instance=INSTANCE_NAME \
    --role=ROLE_NAME \
    --member=MEMBER_NAME \
    --condition=CONDITION
    • MEMBER_NAME הוא המזהה של החשבון הראשי. הערך צריך להיות בפורמט user|group|serviceAccount:email או domain:domain.

    • CONDITION הוא ביטוי של תנאי IAM שמציין את התפקידים שיוקצו לישות המורשית.

      הפורמט של CONDITION הוא:

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

      כל התנאים שצוינו חייבים להיות זהים לתנאים שצוינו בפקודה שבה ניתנה ההרשאה, כולל השם והתיאור.

ספריות לקוח

שתי דוגמאות הקוד האלה יוצרות תפקיד במסד נתונים ומבטלות אותו.

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,
      String parentRole, String childRole, String... tables) {
    try (Spanner spanner =
        SpannerOptions.newBuilder()
            .setProjectId(projectId)
            .build()
            .getService();
        DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
      System.out.println("Waiting for role create operation to complete...");
      List<String> roleStatements = new ArrayList<>(ImmutableList.of(
          String.format("CREATE ROLE %s", parentRole),
          String.format("CREATE ROLE %s", childRole),
          String.format("GRANT ROLE %s TO ROLE %s", parentRole, childRole)));
      for (String table : tables) {
        roleStatements.add(String.format("GRANT SELECT ON TABLE %s TO ROLE %s", table, parentRole));
      }
      databaseAdminClient.updateDatabaseDdlAsync(
              DatabaseName.of(projectId, instanceId, databaseId), roleStatements)
          .get(5, TimeUnit.MINUTES);
      System.out.printf(
          "Created roles %s and %s and granted privileges%n", parentRole, childRole);
      // Delete role and membership.
      System.out.println("Waiting for role revoke & drop operation to complete...");
      databaseAdminClient.updateDatabaseDdlAsync(
          DatabaseName.of(projectId, instanceId, databaseId),
          ImmutableList.of(
              String.format("REVOKE ROLE %s FROM ROLE %s", parentRole, childRole),
              String.format("DROP ROLE %s", childRole))).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 client library
const {Spanner} = require('@google-cloud/spanner');

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

const databaseAdminClient = spanner.getDatabaseAdminClient();

async function addAndDropNewDatabaseRole() {
  // 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 databaseAdminClient.updateDatabaseDdl({
      database: databaseAdminClient.databasePath(
        projectId,
        instanceId,
        databaseId,
      ),
      statements: 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 databaseAdminClient.updateDatabaseDdl({
      database: databaseAdminClient.databasePath(
        projectId,
        instanceId,
        databaseId,
      ),
      statements: 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 spanner client when finished.
    // The databaseAdminClient does not require explicit closure. The closure of the Spanner client will automatically close the databaseAdminClient.
    spanner.close();
  }
}
addAndDropNewDatabaseRole();

PHP

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

/**
 * Adds and drops roles to the Singers table in the example database.
 * Example:
 * ```
 * add_drop_database_role($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 add_drop_database_role(string $projectId, string $instanceId, string $databaseId): void
{
    $databaseAdminClient = new DatabaseAdminClient();
    $databaseName = DatabaseAdminClient::databaseName($projectId, $instanceId, $databaseId);

    $request = new UpdateDatabaseDdlRequest([
        'database' => $databaseName,
        'statements' => [
            'CREATE ROLE new_parent',
            'GRANT SELECT ON TABLE Singers TO ROLE new_parent',
            'CREATE ROLE new_child',
            'GRANT ROLE new_parent TO ROLE new_child'
        ]
    ]);

    $operation = $databaseAdminClient->updateDatabaseDdl($request);

    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', 'new_parent', 'new_child', PHP_EOL);

    $request = new UpdateDatabaseDdlRequest([
        'database' => $databaseName,
        'statements' => [
            'REVOKE ROLE new_parent FROM ROLE new_child',
            'DROP ROLE new_child'
        ]
    ]);

    $operation = $databaseAdminClient->updateDatabaseDdl($request);

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

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

Python

# instance_id = "your-spanner-instance"
# database_id = "your-spanner-db-id"

from google.cloud.spanner_admin_database_v1.types import spanner_database_admin

spanner_client = spanner.Client()
database_admin_api = spanner_client.database_admin_api

role_parent = "new_parent"
role_child = "new_child"

request = spanner_database_admin.UpdateDatabaseDdlRequest(
    database=database_admin_api.database_path(
        spanner_client.project, instance_id, database_id
    ),
    statements=[
        "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 = database_admin_api.update_database_ddl(request)

operation.result(OPERATION_TIMEOUT_SECONDS)
print(
    "Created roles {} and {} and granted privileges".format(role_parent, role_child)
)

request = spanner_database_admin.UpdateDatabaseDdlRequest(
    database=database_admin_api.database_path(
        spanner_client.project, instance_id, database_id
    ),
    statements=[
        "REVOKE ROLE {} FROM ROLE {}".format(role_parent, role_child),
        "DROP ROLE {}".format(role_child),
    ],
)
operation = database_admin_api.update_database_ddl(request)

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

מידע נוסף