Skip to content

Commit

Permalink
feat(GCS+gRPC): implement TestBucketIamPermissions() (googleapis#8427)
Browse files Browse the repository at this point in the history
  • Loading branch information
coryan authored Feb 22, 2022
1 parent efd2a20 commit 1e165e8
Show file tree
Hide file tree
Showing 19 changed files with 190 additions and 3 deletions.
1 change: 0 additions & 1 deletion generator/generator_config.textproto
Original file line number Diff line number Diff line change
Expand Up @@ -875,7 +875,6 @@ service {
omit_stub_factory: true
omitted_rpcs: [
"SetIamPolicy",
"TestIamPermissions",
"DeleteNotification",
"GetNotification",
"CreateNotification",
Expand Down
19 changes: 19 additions & 0 deletions google/cloud/storage/internal/grpc_bucket_request_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,25 @@ NativeIamPolicy GrpcBucketRequestParser::FromProto(
return result;
}

google::iam::v1::TestIamPermissionsRequest GrpcBucketRequestParser::ToProto(
TestBucketIamPermissionsRequest const& request) {
google::iam::v1::TestIamPermissionsRequest result;
result.set_resource("projects/_/buckets/" + request.bucket_name());
for (auto const& p : request.permissions()) {
result.add_permissions(p);
}
return result;
}

TestBucketIamPermissionsResponse GrpcBucketRequestParser::FromProto(
google::iam::v1::TestIamPermissionsResponse const& response) {
TestBucketIamPermissionsResponse result;
for (auto const& p : response.permissions()) {
result.permissions.push_back(p);
}
return result;
}

StatusOr<google::storage::v2::UpdateBucketRequest>
GrpcBucketRequestParser::ToProto(PatchBucketRequest const& request) {
google::storage::v2::UpdateBucketRequest result;
Expand Down
5 changes: 5 additions & 0 deletions google/cloud/storage/internal/grpc_bucket_request_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ struct GrpcBucketRequestParser {
static NativeIamBinding FromProto(google::iam::v1::Binding const& b);
static NativeIamPolicy FromProto(google::iam::v1::Policy const& response);

static google::iam::v1::TestIamPermissionsRequest ToProto(
TestBucketIamPermissionsRequest const& request);
static TestBucketIamPermissionsResponse FromProto(
google::iam::v1::TestIamPermissionsResponse const& response);

static StatusOr<google::storage::v2::UpdateBucketRequest> ToProto(
PatchBucketRequest const& request);
static google::storage::v2::UpdateBucketRequest ToProto(
Expand Down
33 changes: 33 additions & 0 deletions google/cloud/storage/internal/grpc_bucket_request_parser_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,39 @@ TEST(GrpcBucketRequestParser, NativeIamPolicy) {
ASSERT_THAT(actual.bindings()[0].condition(), match_expr(b0.condition()));
}

TEST(GrpcBucketRequestParser, TestBucketIamPermissionsRequest) {
google::iam::v1::TestIamPermissionsRequest expected;
ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(
R"pb(
resource: "projects/_/buckets/test-bucket"
permissions: "test-only.permission.1"
permissions: "test-only.permission.2"
)pb",
&expected));

TestBucketIamPermissionsRequest req(
"test-bucket", {"test-only.permission.1", "test-only.permission.2"});
req.set_multiple_options(UserProject("test-user-project"),
QuotaUser("test-quota-user"),
UserIp("test-user-ip"));
auto const actual = GrpcBucketRequestParser::ToProto(req);
EXPECT_THAT(actual, IsProtoEqual(expected));
}

TEST(GrpcBucketRequestParser, TestBucketIamPermissionsResponse) {
google::iam::v1::TestIamPermissionsResponse input;
ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(
R"pb(
permissions: "test-only.permission.1"
permissions: "test-only.permission.2"
)pb",
&input));

auto const actual = GrpcBucketRequestParser::FromProto(input);
EXPECT_THAT(actual.permissions,
ElementsAre("test-only.permission.1", "test-only.permission.2"));
}

TEST(GrpcBucketRequestParser, PatchBucketRequestAllOptions) {
google::storage::v2::UpdateBucketRequest expected;
ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(
Expand Down
9 changes: 7 additions & 2 deletions google/cloud/storage/internal/grpc_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,13 @@ StatusOr<NativeIamPolicy> GrpcClient::SetNativeBucketIamPolicy(
}

StatusOr<TestBucketIamPermissionsResponse> GrpcClient::TestBucketIamPermissions(
TestBucketIamPermissionsRequest const&) {
return Status(StatusCode::kUnimplemented, __func__);
TestBucketIamPermissionsRequest const& request) {
auto proto = GrpcBucketRequestParser::ToProto(request);
grpc::ClientContext context;
ApplyQueryParameters(context, request);
auto response = stub_->TestIamPermissions(context, proto);
if (!response) return std::move(response).status();
return GrpcBucketRequestParser::FromProto(*response);
}

StatusOr<BucketMetadata> GrpcClient::LockBucketRetentionPolicy(
Expand Down
22 changes: 22 additions & 0 deletions google/cloud/storage/internal/grpc_client_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,28 @@ TEST_F(GrpcClientTest, GetNativeBucketIamPolicy) {
EXPECT_EQ(response.status(), PermanentError());
}

TEST_F(GrpcClientTest, TestBucketIamPermissions) {
auto mock = std::make_shared<testing::MockStorageStub>();
EXPECT_CALL(*mock, TestIamPermissions)
.WillOnce([this](
grpc::ClientContext& context,
google::iam::v1::TestIamPermissionsRequest 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.resource(), "projects/_/buckets/test-bucket");
return PermanentError();
});
auto client = CreateTestClient(mock);
auto response = client->TestBucketIamPermissions(
TestBucketIamPermissionsRequest(
"test-bucket", {"test.permission.1", "test.permission.2"})
.set_multiple_options(Fields("field1,field2"),
QuotaUser("test-quota-user")));
EXPECT_EQ(response.status(), PermanentError());
}

TEST_F(GrpcClientTest, InsertObjectMedia) {
auto mock = std::make_shared<testing::MockStorageStub>();
EXPECT_CALL(*mock, WriteObject)
Expand Down
9 changes: 9 additions & 0 deletions google/cloud/storage/internal/storage_auth_decorator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ StatusOr<google::iam::v1::Policy> StorageAuth::GetIamPolicy(
return child_->GetIamPolicy(context, request);
}

StatusOr<google::iam::v1::TestIamPermissionsResponse>
StorageAuth::TestIamPermissions(
grpc::ClientContext& context,
google::iam::v1::TestIamPermissionsRequest const& request) {
auto status = auth_->ConfigureContext(context);
if (!status.ok()) return status;
return child_->TestIamPermissions(context, request);
}

StatusOr<google::storage::v2::Bucket> StorageAuth::UpdateBucket(
grpc::ClientContext& context,
google::storage::v2::UpdateBucketRequest const& request) {
Expand Down
4 changes: 4 additions & 0 deletions google/cloud/storage/internal/storage_auth_decorator.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ class StorageAuth : public StorageStub {
grpc::ClientContext& context,
google::iam::v1::GetIamPolicyRequest const& request) override;

StatusOr<google::iam::v1::TestIamPermissionsResponse> TestIamPermissions(
grpc::ClientContext& context,
google::iam::v1::TestIamPermissionsRequest const& request) override;

StatusOr<google::storage::v2::Bucket> UpdateBucket(
grpc::ClientContext& context,
google::storage::v2::UpdateBucketRequest const& request) override;
Expand Down
12 changes: 12 additions & 0 deletions google/cloud/storage/internal/storage_logging_decorator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ StatusOr<google::iam::v1::Policy> StorageLogging::GetIamPolicy(
context, request, __func__, tracing_options_);
}

StatusOr<google::iam::v1::TestIamPermissionsResponse>
StorageLogging::TestIamPermissions(
grpc::ClientContext& context,
google::iam::v1::TestIamPermissionsRequest const& request) {
return google::cloud::internal::LogWrapper(
[this](grpc::ClientContext& context,
google::iam::v1::TestIamPermissionsRequest const& request) {
return child_->TestIamPermissions(context, request);
},
context, request, __func__, tracing_options_);
}

StatusOr<google::storage::v2::Bucket> StorageLogging::UpdateBucket(
grpc::ClientContext& context,
google::storage::v2::UpdateBucketRequest const& request) {
Expand Down
4 changes: 4 additions & 0 deletions google/cloud/storage/internal/storage_logging_decorator.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ class StorageLogging : public StorageStub {
grpc::ClientContext& context,
google::iam::v1::GetIamPolicyRequest const& request) override;

StatusOr<google::iam::v1::TestIamPermissionsResponse> TestIamPermissions(
grpc::ClientContext& context,
google::iam::v1::TestIamPermissionsRequest const& request) override;

StatusOr<google::storage::v2::Bucket> UpdateBucket(
grpc::ClientContext& context,
google::storage::v2::UpdateBucketRequest const& request) override;
Expand Down
8 changes: 8 additions & 0 deletions google/cloud/storage/internal/storage_metadata_decorator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ StatusOr<google::iam::v1::Policy> StorageMetadata::GetIamPolicy(
return child_->GetIamPolicy(context, request);
}

StatusOr<google::iam::v1::TestIamPermissionsResponse>
StorageMetadata::TestIamPermissions(
grpc::ClientContext& context,
google::iam::v1::TestIamPermissionsRequest const& request) {
SetMetadata(context, {});
return child_->TestIamPermissions(context, request);
}

StatusOr<google::storage::v2::Bucket> StorageMetadata::UpdateBucket(
grpc::ClientContext& context,
google::storage::v2::UpdateBucketRequest const& request) {
Expand Down
4 changes: 4 additions & 0 deletions google/cloud/storage/internal/storage_metadata_decorator.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ class StorageMetadata : public StorageStub {
grpc::ClientContext& context,
google::iam::v1::GetIamPolicyRequest const& request) override;

StatusOr<google::iam::v1::TestIamPermissionsResponse> TestIamPermissions(
grpc::ClientContext& context,
google::iam::v1::TestIamPermissionsRequest const& request) override;

StatusOr<google::storage::v2::Bucket> UpdateBucket(
grpc::ClientContext& context,
google::storage::v2::UpdateBucketRequest const& request) override;
Expand Down
7 changes: 7 additions & 0 deletions google/cloud/storage/internal/storage_round_robin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ StatusOr<google::iam::v1::Policy> StorageRoundRobin::GetIamPolicy(
return Child()->GetIamPolicy(context, request);
}

StatusOr<google::iam::v1::TestIamPermissionsResponse>
StorageRoundRobin::TestIamPermissions(
grpc::ClientContext& context,
google::iam::v1::TestIamPermissionsRequest const& request) {
return Child()->TestIamPermissions(context, request);
}

StatusOr<google::storage::v2::Bucket> StorageRoundRobin::UpdateBucket(
grpc::ClientContext& context,
google::storage::v2::UpdateBucketRequest const& request) {
Expand Down
4 changes: 4 additions & 0 deletions google/cloud/storage/internal/storage_round_robin.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ class StorageRoundRobin : public StorageStub {
grpc::ClientContext& context,
google::iam::v1::GetIamPolicyRequest const& request) override;

StatusOr<google::iam::v1::TestIamPermissionsResponse> TestIamPermissions(
grpc::ClientContext& context,
google::iam::v1::TestIamPermissionsRequest const& request) override;

StatusOr<google::storage::v2::Bucket> UpdateBucket(
grpc::ClientContext& context,
google::storage::v2::UpdateBucketRequest const& request) override;
Expand Down
19 changes: 19 additions & 0 deletions google/cloud/storage/internal/storage_round_robin_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,25 @@ TEST(StorageRoundRobinTest, GetIamPolicy) {
}
}

TEST(StorageRoundRobinTest, TestIamPermissions) {
auto mocks = MakeMocks();
InSequence sequence;
for (int i = 0; i != kRepeats; ++i) {
for (auto& m : mocks) {
EXPECT_CALL(*m, TestIamPermissions)
.WillOnce(Return(Status(StatusCode::kPermissionDenied, "uh-oh")));
}
}

StorageRoundRobin under_test(AsPlainStubs(mocks));
for (size_t i = 0; i != kRepeats * mocks.size(); ++i) {
grpc::ClientContext context;
google::iam::v1::TestIamPermissionsRequest request;
auto response = under_test.TestIamPermissions(context, request);
EXPECT_THAT(response, StatusIs(StatusCode::kPermissionDenied));
}
}

TEST(StorageRoundRobinTest, UpdateBucket) {
auto mocks = MakeMocks();
InSequence sequence;
Expand Down
13 changes: 13 additions & 0 deletions google/cloud/storage/internal/storage_stub.cc
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,19 @@ StatusOr<google::iam::v1::Policy> DefaultStorageStub::GetIamPolicy(
return response;
}

StatusOr<google::iam::v1::TestIamPermissionsResponse>
DefaultStorageStub::TestIamPermissions(
grpc::ClientContext& client_context,
google::iam::v1::TestIamPermissionsRequest const& request) {
google::iam::v1::TestIamPermissionsResponse response;
auto status =
grpc_stub_->TestIamPermissions(&client_context, request, &response);
if (!status.ok()) {
return google::cloud::MakeStatusFromRpcError(status);
}
return response;
}

StatusOr<google::storage::v2::Bucket> DefaultStorageStub::UpdateBucket(
grpc::ClientContext& client_context,
google::storage::v2::UpdateBucketRequest const& request) {
Expand Down
9 changes: 9 additions & 0 deletions google/cloud/storage/internal/storage_stub.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ class StorageStub {
grpc::ClientContext& context,
google::iam::v1::GetIamPolicyRequest const& request) = 0;

virtual StatusOr<google::iam::v1::TestIamPermissionsResponse>
TestIamPermissions(
grpc::ClientContext& context,
google::iam::v1::TestIamPermissionsRequest const& request) = 0;

virtual StatusOr<google::storage::v2::Bucket> UpdateBucket(
grpc::ClientContext& context,
google::storage::v2::UpdateBucketRequest const& request) = 0;
Expand Down Expand Up @@ -143,6 +148,10 @@ class DefaultStorageStub : public StorageStub {
grpc::ClientContext& client_context,
google::iam::v1::GetIamPolicyRequest const& request) override;

StatusOr<google::iam::v1::TestIamPermissionsResponse> TestIamPermissions(
grpc::ClientContext& client_context,
google::iam::v1::TestIamPermissionsRequest const& request) override;

StatusOr<google::storage::v2::Bucket> UpdateBucket(
grpc::ClientContext& client_context,
google::storage::v2::UpdateBucketRequest const& request) override;
Expand Down
5 changes: 5 additions & 0 deletions google/cloud/storage/testing/mock_storage_stub.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ class MockStorageStub : public storage_internal::StorageStub {
(grpc::ClientContext&,
google::iam::v1::GetIamPolicyRequest const&),
(override));
MOCK_METHOD(StatusOr<google::iam::v1::TestIamPermissionsResponse>,
TestIamPermissions,
(grpc::ClientContext&,
google::iam::v1::TestIamPermissionsRequest const&),
(override));
MOCK_METHOD(StatusOr<google::storage::v2::Bucket>, UpdateBucket,
(grpc::ClientContext&,
google::storage::v2::UpdateBucketRequest const&),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ namespace {
using ::google::cloud::internal::GetEnv;
using ::google::cloud::testing_util::ScopedEnvironment;
using ::google::cloud::testing_util::StatusIs;
using ::testing::Contains;
using ::testing::ElementsAre;
using ::testing::IsEmpty;
using ::testing::IsSupersetOf;
Expand Down Expand Up @@ -120,6 +121,11 @@ TEST_F(GrpcBucketMetadataIntegrationTest, ObjectMetadataCRUD) {
"roles/storage.legacyBucketWriter",
"roles/storage.legacyBucketReader"}));

auto permissions = client->TestBucketIamPermissions(
bucket_name, {"storage.objects.list", "storage.buckets.update"});
ASSERT_STATUS_OK(permissions);
EXPECT_THAT(*permissions, Contains("storage.buckets.update"));

auto delete_status = client->DeleteBucket(bucket_name);
ASSERT_STATUS_OK(delete_status);

Expand Down

0 comments on commit 1e165e8

Please sign in to comment.