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