1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "google/cloud/storage/internal/object_acl_requests.h"
16 #include "google/cloud/storage/internal/curl_request_builder.h"
17 #include "google/cloud/storage/internal/object_access_control_parser.h"
18 #include <gmock/gmock.h>
19 
20 namespace google {
21 namespace cloud {
22 namespace storage {
23 inline namespace STORAGE_CLIENT_NS {
24 namespace internal {
25 namespace {
26 
27 using ::testing::HasSubstr;
28 
29 /// @test Verify that we parse JSON objects into ObjectAccessControl objects.
TEST(ObjectAccessControlTest,Parse)30 TEST(ObjectAccessControlTest, Parse) {
31   std::string text = R"""({
32       "bucket": "foo-bar",
33       "domain": "example.com",
34       "email": "foobar@example.com",
35       "entity": "user-foobar",
36       "entityId": "user-foobar-id-123",
37       "etag": "XYZ=",
38       "generation": 42,
39       "id": "object-foo-bar-baz-acl-234",
40       "kind": "storage#objectAccessControl",
41       "object": "baz",
42       "projectTeam": {
43         "projectNumber": "3456789",
44         "team": "a-team"
45       },
46       "role": "OWNER"
47 })""";
48   auto actual = internal::ObjectAccessControlParser::FromString(text).value();
49 
50   EXPECT_EQ("foo-bar", actual.bucket());
51   EXPECT_EQ("example.com", actual.domain());
52   EXPECT_EQ("foobar@example.com", actual.email());
53   EXPECT_EQ("user-foobar", actual.entity());
54   EXPECT_EQ("user-foobar-id-123", actual.entity_id());
55   EXPECT_EQ("XYZ=", actual.etag());
56   EXPECT_EQ(42, actual.generation());
57   EXPECT_EQ("object-foo-bar-baz-acl-234", actual.id());
58   EXPECT_EQ("storage#objectAccessControl", actual.kind());
59   EXPECT_EQ("baz", actual.object());
60   EXPECT_EQ("3456789", actual.project_team().project_number);
61   EXPECT_EQ("a-team", actual.project_team().team);
62   EXPECT_EQ("OWNER", actual.role());
63 }
64 
65 /// @test Verify that the IOStream operator works as expected.
TEST(ObjectAccessControlTest,IOStream)66 TEST(ObjectAccessControlTest, IOStream) {
67   // The iostream operator is mostly there to support EXPECT_EQ() so it is
68   // rarely called, and that breaks our code coverage metrics.
69   std::string text = R"""({
70       "bucket": "foo-bar",
71       "domain": "example.com",
72       "email": "foobar@example.com",
73       "entity": "user-foobar",
74       "entityId": "user-foobar-id-123",
75       "etag": "XYZ=",
76       "generation": 42,
77       "id": "object-foo-bar-baz-acl-234",
78       "kind": "storage#objectAccessControl",
79       "object": "baz",
80       "projectTeam": {
81         "projectNumber": "3456789",
82         "team": "a-team"
83       },
84       "role": "OWNER"
85 })""";
86 
87   auto meta = internal::ObjectAccessControlParser::FromString(text).value();
88   std::ostringstream os;
89   os << meta;
90   auto actual = os.str();
91   using ::testing::HasSubstr;
92   EXPECT_THAT(actual, HasSubstr("ObjectAccessControl"));
93   EXPECT_THAT(actual, HasSubstr("bucket=foo-bar"));
94   EXPECT_THAT(actual, HasSubstr("object=baz"));
95   EXPECT_THAT(actual, HasSubstr("id=object-foo-bar-baz-acl-234"));
96 }
97 
TEST(ObjectAclRequestTest,List)98 TEST(ObjectAclRequestTest, List) {
99   ListObjectAclRequest request("my-bucket", "my-object");
100   request.set_multiple_options(UserProject("my-project"), Generation(7));
101   EXPECT_EQ("my-bucket", request.bucket_name());
102   EXPECT_EQ("my-object", request.object_name());
103 
104   std::ostringstream os;
105   os << request;
106   auto str = os.str();
107   EXPECT_THAT(str, HasSubstr("userProject=my-project"));
108   EXPECT_THAT(str, HasSubstr("generation=7"));
109   EXPECT_THAT(str, HasSubstr("my-bucket"));
110   EXPECT_THAT(str, HasSubstr("my-object"));
111 }
112 
TEST(ObjectAclRequestTest,ListResponse)113 TEST(ObjectAclRequestTest, ListResponse) {
114   std::string text = R"""({
115       "items": [{
116           "bucket": "foo-bar",
117           "object": "baz",
118           "entity": "user-qux",
119           "role": "OWNER"
120       }, {
121           "bucket": "foo-bar",
122           "object": "baz",
123           "entity": "user-quux",
124           "role": "READER"
125       }]})""";
126 
127   auto actual = ListObjectAclResponse::FromHttpResponse(text).value();
128   ASSERT_EQ(2UL, actual.items.size());
129   EXPECT_EQ("user-qux", actual.items.at(0).entity());
130   EXPECT_EQ("OWNER", actual.items.at(0).role());
131   EXPECT_EQ("user-quux", actual.items.at(1).entity());
132   EXPECT_EQ("READER", actual.items.at(1).role());
133 
134   std::ostringstream os;
135   os << actual;
136   auto str = os.str();
137   EXPECT_THAT(str, HasSubstr("entity=user-qux"));
138   EXPECT_THAT(str, HasSubstr("entity=user-quux"));
139   EXPECT_THAT(str, HasSubstr("object=baz"));
140   EXPECT_THAT(str, HasSubstr("ListObjectAclResponse={"));
141   EXPECT_THAT(str, HasSubstr("ObjectAccessControl={"));
142 }
143 
TEST(ObjectAclRequestTest,ListResponseParseFailure)144 TEST(ObjectAclRequestTest, ListResponseParseFailure) {
145   std::string text = R"""({123)""";
146 
147   StatusOr<ListObjectAclResponse> actual =
148       ListObjectAclResponse::FromHttpResponse(text);
149   EXPECT_FALSE(actual.ok());
150 }
151 
TEST(ObjectAclRequestTest,ListResponseParseFailureElements)152 TEST(ObjectAclRequestTest, ListResponseParseFailureElements) {
153   std::string text = R"""({"items": ["invalid-item"]})""";
154 
155   StatusOr<ListObjectAclResponse> actual =
156       ListObjectAclResponse::FromHttpResponse(text);
157   EXPECT_FALSE(actual.ok());
158 }
159 
TEST(ObjectAclRequestTest,Get)160 TEST(ObjectAclRequestTest, Get) {
161   GetObjectAclRequest request("my-bucket", "my-object", "user-test-user");
162   request.set_multiple_options(UserProject("my-project"));
163   EXPECT_EQ("my-bucket", request.bucket_name());
164   EXPECT_EQ("my-object", request.object_name());
165   EXPECT_EQ("user-test-user", request.entity());
166 
167   std::ostringstream os;
168   os << request;
169   auto str = os.str();
170   EXPECT_THAT(str, HasSubstr("userProject=my-project"));
171   EXPECT_THAT(str, HasSubstr("my-bucket"));
172   EXPECT_THAT(str, HasSubstr("user-test-user"));
173 }
174 
TEST(ObjectAclRequestTest,Delete)175 TEST(ObjectAclRequestTest, Delete) {
176   DeleteObjectAclRequest request("my-bucket", "my-object", "user-test-user");
177   request.set_multiple_options(UserProject("my-project"));
178   EXPECT_EQ("my-bucket", request.bucket_name());
179   EXPECT_EQ("my-object", request.object_name());
180   EXPECT_EQ("user-test-user", request.entity());
181 
182   std::ostringstream os;
183   os << request;
184   auto str = os.str();
185   EXPECT_THAT(str, HasSubstr("userProject=my-project"));
186   EXPECT_THAT(str, HasSubstr("my-bucket"));
187   EXPECT_THAT(str, HasSubstr("user-test-user"));
188 }
189 
TEST(ObjectAclRequestTest,Create)190 TEST(ObjectAclRequestTest, Create) {
191   CreateObjectAclRequest request("my-bucket", "my-object", "user-testuser",
192                                  "READER");
193   request.set_multiple_options(UserProject("my-project"), Generation(7));
194   EXPECT_EQ("my-bucket", request.bucket_name());
195   EXPECT_EQ("my-object", request.object_name());
196   EXPECT_EQ("user-testuser", request.entity());
197   EXPECT_EQ("READER", request.role());
198 
199   std::ostringstream os;
200   os << request;
201   auto str = os.str();
202   EXPECT_THAT(str, HasSubstr("userProject=my-project"));
203   EXPECT_THAT(str, HasSubstr("generation=7"));
204   EXPECT_THAT(str, HasSubstr("my-bucket"));
205   EXPECT_THAT(str, HasSubstr("my-object"));
206   EXPECT_THAT(str, HasSubstr("user-testuser"));
207   EXPECT_THAT(str, HasSubstr("READER"));
208 }
209 
TEST(ObjectAclRequestTest,Update)210 TEST(ObjectAclRequestTest, Update) {
211   UpdateObjectAclRequest request("my-bucket", "my-object", "user-testuser",
212                                  "READER");
213   request.set_multiple_options(UserProject("my-project"), Generation(7));
214   EXPECT_EQ("my-bucket", request.bucket_name());
215   EXPECT_EQ("my-object", request.object_name());
216   EXPECT_EQ("user-testuser", request.entity());
217   EXPECT_EQ("READER", request.role());
218 
219   std::ostringstream os;
220   os << request;
221   auto str = os.str();
222   EXPECT_THAT(str, HasSubstr("userProject=my-project"));
223   EXPECT_THAT(str, HasSubstr("generation=7"));
224   EXPECT_THAT(str, HasSubstr("my-bucket"));
225   EXPECT_THAT(str, HasSubstr("my-object"));
226   EXPECT_THAT(str, HasSubstr("user-testuser"));
227   EXPECT_THAT(str, HasSubstr("READER"));
228 }
229 
CreateObjectAccessControlForTest()230 ObjectAccessControl CreateObjectAccessControlForTest() {
231   std::string text = R"""({
232       "bucket": "foo-bar",
233       "domain": "example.com",
234       "email": "foobar@example.com",
235       "entity": "user-foobar",
236       "entityId": "user-foobar-id-123",
237       "etag": "XYZ=",
238       "generation": 42,
239       "id": "object-foo-bar-baz-acl-234",
240       "kind": "storage#objectAccessControl",
241       "object": "baz",
242       "projectTeam": {
243         "projectNumber": "3456789",
244         "team": "a-team"
245       },
246       "role": "OWNER"
247 })""";
248   return internal::ObjectAccessControlParser::FromString(text).value();
249 }
250 
TEST(ObjectAclRequestTest,PatchDiff)251 TEST(ObjectAclRequestTest, PatchDiff) {
252   ObjectAccessControl original = CreateObjectAccessControlForTest();
253   ObjectAccessControl new_acl =
254       CreateObjectAccessControlForTest().set_role("READER");
255 
256   PatchObjectAclRequest request("my-bucket", "my-object", "user-test-user",
257                                 original, new_acl);
258   nlohmann::json expected = {
259       {"role", "READER"},
260   };
261   auto actual = nlohmann::json::parse(request.payload());
262   EXPECT_EQ(expected, actual);
263 }
264 
TEST(ObjectAclRequestTest,PatchBuilder)265 TEST(ObjectAclRequestTest, PatchBuilder) {
266   PatchObjectAclRequest request(
267       "my-bucket", "my-object", "user-test-user",
268       ObjectAccessControlPatchBuilder().set_role("READER").delete_entity());
269   nlohmann::json expected = {{"role", "READER"}, {"entity", nullptr}};
270   auto actual = nlohmann::json::parse(request.payload());
271   EXPECT_EQ(expected, actual);
272 }
273 
TEST(PatchObjectAclRequestTest,PatchStream)274 TEST(PatchObjectAclRequestTest, PatchStream) {
275   ObjectAccessControl original = CreateObjectAccessControlForTest();
276   ObjectAccessControl new_acl =
277       CreateObjectAccessControlForTest().set_role("READER");
278 
279   PatchObjectAclRequest request("my-bucket", "my-object", "user-test-user",
280                                 original, new_acl);
281   request.set_multiple_options(UserProject("my-project"), Generation(7),
282                                IfMatchEtag("ABC="));
283   std::ostringstream os;
284   os << request;
285   auto str = os.str();
286   EXPECT_THAT(str, HasSubstr("userProject=my-project"));
287   EXPECT_THAT(str, HasSubstr("If-Match: ABC="));
288   EXPECT_THAT(str, HasSubstr("generation=7"));
289   EXPECT_THAT(str, HasSubstr("my-bucket"));
290   EXPECT_THAT(str, HasSubstr("my-object"));
291   EXPECT_THAT(str, HasSubstr("user-test-user"));
292   EXPECT_THAT(str, HasSubstr(request.payload()));
293 }
294 
295 }  // namespace
296 }  // namespace internal
297 }  // namespace STORAGE_CLIENT_NS
298 }  // namespace storage
299 }  // namespace cloud
300 }  // namespace google
301