From 76a4024c4df72face65bb35843f79bbdac6ed733 Mon Sep 17 00:00:00 2001 From: Carlos O'Ryan Date: Mon, 18 Jul 2022 08:25:25 -0700 Subject: [PATCH] feat(GCS+gRPC): implement `PatchDefaultObjectAcl()` (#9487) --- google/cloud/storage/internal/grpc_client.cc | 11 +++- .../storage/internal/grpc_client_test.cc | 53 +++++++++++++++++++ ...rpc_default_object_acl_integration_test.cc | 19 +++++++ 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/google/cloud/storage/internal/grpc_client.cc b/google/cloud/storage/internal/grpc_client.cc index 70dc80f502809..b9d724bfc179b 100644 --- a/google/cloud/storage/internal/grpc_client.cc +++ b/google/cloud/storage/internal/grpc_client.cc @@ -918,8 +918,15 @@ StatusOr GrpcClient::UpdateDefaultObjectAcl( } StatusOr GrpcClient::PatchDefaultObjectAcl( - PatchDefaultObjectAclRequest const&) { - return Status(StatusCode::kUnimplemented, __func__); + PatchDefaultObjectAclRequest const& request) { + auto get_request = GetBucketMetadataRequest(request.bucket_name()); + request.ForEachOption(CopyCommonOptions(get_request)); + auto updater = [&request](std::vector acl) { + return UpsertAcl(std::move(acl), request.entity(), + GrpcObjectAccessControlParser::Role(request.patch())); + }; + return FindDefaultObjectAccessControl( + ModifyDefaultAccessControl(get_request, updater), request.entity()); } StatusOr GrpcClient::GetServiceAccount( diff --git a/google/cloud/storage/internal/grpc_client_test.cc b/google/cloud/storage/internal/grpc_client_test.cc index 5a76bedcf448a..2474bf9896e54 100644 --- a/google/cloud/storage/internal/grpc_client_test.cc +++ b/google/cloud/storage/internal/grpc_client_test.cc @@ -1700,6 +1700,59 @@ TEST_F(GrpcClientTest, UpdateDefaultObjectAclPatchFails) { EXPECT_THAT(response, StatusIs(StatusCode::kUnavailable)); } +TEST_F(GrpcClientTest, PatchDefaultObjectAclFailure) { + auto mock = std::make_shared(); + EXPECT_CALL(*mock, GetBucket) + .WillOnce([this](grpc::ClientContext& context, + v2::GetBucketRequest const& request) { + auto metadata = GetMetadata(context); + EXPECT_THAT(metadata, UnorderedElementsAre( + Pair("x-goog-quota-user", "test-quota-user"), + Pair("x-goog-fieldmask", "field1,field2"))); + EXPECT_THAT(request.name(), "projects/_/buckets/test-bucket-name"); + return PermanentError(); + }); + + auto client = CreateTestClient(mock); + auto response = client->PatchDefaultObjectAcl( + PatchDefaultObjectAclRequest( + "test-bucket-name", "test-entity3", + ObjectAccessControlPatchBuilder().set_role("updated-role")) + .set_multiple_options(Fields("field1,field2"), + QuotaUser("test-quota-user"), + UserProject("test-user-project"))); + EXPECT_EQ(response.status(), PermanentError()); +} + +TEST_F(GrpcClientTest, PatchDefaultObjectAclPatchFails) { + auto mock = std::make_shared(); + EXPECT_CALL(*mock, GetBucket) + .WillOnce([&](grpc::ClientContext&, v2::GetBucketRequest const&) { + v2::Bucket response; + EXPECT_TRUE(TextFormat::ParseFromString(kBucketProtoText, &response)); + return response; + }); + EXPECT_CALL(*mock, UpdateBucket) + .WillOnce([](grpc::ClientContext&, + v2::UpdateBucketRequest const& request) { + EXPECT_EQ(request.bucket().name(), "projects/_/buckets/test-bucket-id"); + auto expected = v2::ObjectAccessControl(); + expected.set_entity("test-entity3"); + expected.set_role("updated-role"); + EXPECT_THAT(request.bucket().default_object_acl(), + Contains(IsProtoEqual(expected))); + EXPECT_THAT(request.update_mask().paths(), + ElementsAre("default_object_acl")); + return Status(StatusCode::kFailedPrecondition, "conflict"); + }); + + auto client = CreateTestClient(mock); + auto response = client->PatchDefaultObjectAcl(PatchDefaultObjectAclRequest( + "test-bucket-id", "test-entity3", + ObjectAccessControlPatchBuilder().set_role("updated-role"))); + EXPECT_THAT(response, StatusIs(StatusCode::kUnavailable)); +} + TEST_F(GrpcClientTest, GetServiceAccount) { auto mock = std::make_shared(); EXPECT_CALL(*mock, GetServiceAccount) diff --git a/google/cloud/storage/tests/grpc_default_object_acl_integration_test.cc b/google/cloud/storage/tests/grpc_default_object_acl_integration_test.cc index 5f5187c69a6c5..325edd5665fa3 100644 --- a/google/cloud/storage/tests/grpc_default_object_acl_integration_test.cc +++ b/google/cloud/storage/tests/grpc_default_object_acl_integration_test.cc @@ -120,6 +120,25 @@ TEST_F(GrpcDefaultObjectAclIntegrationTest, AclCRUD) { ObjectAccessControl::ROLE_OWNER())); ASSERT_STATUS_OK(updated_acl); + auto patched_acl = + client->PatchDefaultObjectAcl(bucket_name, viewers, + ObjectAccessControlPatchBuilder().set_role( + ObjectAccessControl::ROLE_READER())); + ASSERT_STATUS_OK(patched_acl); + EXPECT_EQ(patched_acl->entity(), create_acl->entity()); + EXPECT_EQ(patched_acl->role(), ObjectAccessControl::ROLE_READER()); + + // "Patching" an entity that does not exist should create the entity + delete_acl = client->DeleteDefaultObjectAcl(bucket_name, viewers); + ASSERT_STATUS_OK(delete_acl); + patched_acl = + client->PatchDefaultObjectAcl(bucket_name, viewers, + ObjectAccessControlPatchBuilder().set_role( + ObjectAccessControl::ROLE_READER())); + ASSERT_STATUS_OK(patched_acl); + EXPECT_EQ(patched_acl->entity(), create_acl->entity()); + EXPECT_EQ(patched_acl->role(), ObjectAccessControl::ROLE_READER()); + delete_acl = client->DeleteDefaultObjectAcl(bucket_name, viewers); ASSERT_STATUS_OK(delete_acl);