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/curl_client.h"
16 #include "google/cloud/storage/iam_policy.h"
17 #include "google/cloud/storage/internal/curl_request_builder.h"
18 #include "google/cloud/storage/oauth2/credentials.h"
19 #include "google/cloud/storage/oauth2/google_credentials.h"
20 #include "google/cloud/internal/setenv.h"
21 #include "google/cloud/testing_util/scoped_environment.h"
22 #include <gmock/gmock.h>
23 #include <memory>
24 #include <utility>
25 #include <vector>
26 
27 namespace google {
28 namespace cloud {
29 namespace storage {
30 inline namespace STORAGE_CLIENT_NS {
31 namespace internal {
32 namespace {
33 
34 using ::google::cloud::storage::oauth2::Credentials;
35 using ::testing::HasSubstr;
36 
37 StatusCode const kStatusErrorCode = StatusCode::kUnavailable;
38 std::string const kStatusErrorMsg = "FailingCredentials doing its job, failing";
39 
40 // We create a credential class that always fails to fetch an access token; this
41 // allows us to check that CurlClient methods fail early when their setup steps
42 // (which include adding the authorization header) return a failure Status.
43 // Note that if the methods performing the setup steps were not templated, we
44 // could simply mock those out instead via MOCK_METHOD<N> macros.
45 class FailingCredentials : public Credentials {
46  public:
AuthorizationHeader()47   StatusOr<std::string> AuthorizationHeader() override {
48     return Status(kStatusErrorCode, kStatusErrorMsg);
49   }
50 };
51 
52 class CurlClientTest : public ::testing::Test,
53                        public ::testing::WithParamInterface<std::string> {
54  protected:
CurlClientTest()55   CurlClientTest() : endpoint_("CLOUD_STORAGE_TESTBENCH_ENDPOINT", {}) {}
56 
SetUp()57   void SetUp() override {
58     std::string const error_type = GetParam();
59     if (error_type == "credentials-failure") {
60       client_ = CurlClient::Create(std::make_shared<FailingCredentials>());
61       // We know exactly what error to expect, so setup the assertions to be
62       // very strict.
63       check_status_ = [](Status const& actual) {
64         EXPECT_EQ(kStatusErrorCode, actual.code());
65         EXPECT_THAT(actual.message(), HasSubstr(kStatusErrorMsg));
66       };
67     } else if (error_type == "libcurl-failure") {
68       google::cloud::internal::SetEnv("CLOUD_STORAGE_TESTBENCH_ENDPOINT",
69                                       "http://localhost:1");
70       client_ =
71           CurlClient::Create(ClientOptions(oauth2::CreateAnonymousCredentials())
72                                  .set_endpoint("http://localhost:1"));
73       // We do not know what libcurl will return. Some kind of error, but varies
74       // by version of libcurl. Just make sure it is an error and the CURL
75       // details are included in the error message.
76       check_status_ = [](Status const& actual) {
77         EXPECT_FALSE(actual.ok());
78         EXPECT_THAT(actual.message(), HasSubstr("CURL error"));
79       };
80     } else {
81       FAIL() << "Invalid test parameter value: " << error_type
82              << ", expected either credentials-failure or libcurl-failure";
83     }
84   }
85 
CheckStatus(Status const & actual)86   void CheckStatus(Status const& actual) { check_status_(actual); }
87 
TearDown()88   void TearDown() override { client_.reset(); }
89 
90   std::shared_ptr<CurlClient> client_;
91   std::function<void(Status const& status)> check_status_;
92   testing_util::ScopedEnvironment endpoint_;
93 };
94 
TEST_P(CurlClientTest,UploadChunk)95 TEST_P(CurlClientTest, UploadChunk) {
96   // Use an invalid port (0) to force a libcurl failure
97   auto actual = client_
98                     ->UploadChunk(UploadChunkRequest(
99                         "http://localhost:1/invalid-session-id", 0,
100                         {ConstBuffer{std::string{}}}, 0))
101                     .status();
102   CheckStatus(actual);
103 }
104 
TEST_P(CurlClientTest,QueryResumableUpload)105 TEST_P(CurlClientTest, QueryResumableUpload) {
106   // Use http://localhost:1 to force a libcurl failure
107   auto actual = client_
108                     ->QueryResumableUpload(QueryResumableUploadRequest(
109                         "http://localhost:9/invalid-session-id"))
110                     .status();
111   CheckStatus(actual);
112 }
113 
TEST_P(CurlClientTest,ListBuckets)114 TEST_P(CurlClientTest, ListBuckets) {
115   auto actual = client_->ListBuckets(ListBucketsRequest{"project_id"}).status();
116   CheckStatus(actual);
117 }
118 
TEST_P(CurlClientTest,CreateBucket)119 TEST_P(CurlClientTest, CreateBucket) {
120   auto actual = client_
121                     ->CreateBucket(CreateBucketRequest(
122                         "bkt", BucketMetadata().set_name("bkt")))
123                     .status();
124   CheckStatus(actual);
125 }
126 
TEST_P(CurlClientTest,GetBucketMetadata)127 TEST_P(CurlClientTest, GetBucketMetadata) {
128   auto actual =
129       client_->GetBucketMetadata(GetBucketMetadataRequest("bkt")).status();
130   CheckStatus(actual);
131 }
132 
TEST_P(CurlClientTest,DeleteBucket)133 TEST_P(CurlClientTest, DeleteBucket) {
134   auto actual = client_->DeleteBucket(DeleteBucketRequest("bkt")).status();
135   CheckStatus(actual);
136 }
137 
TEST_P(CurlClientTest,UpdateBucket)138 TEST_P(CurlClientTest, UpdateBucket) {
139   auto actual =
140       client_
141           ->UpdateBucket(UpdateBucketRequest(BucketMetadata().set_name("bkt")))
142           .status();
143   CheckStatus(actual);
144 }
145 
TEST_P(CurlClientTest,PatchBucket)146 TEST_P(CurlClientTest, PatchBucket) {
147   auto actual = client_
148                     ->PatchBucket(PatchBucketRequest(
149                         "bkt", BucketMetadata().set_name("bkt"),
150                         BucketMetadata().set_name("bkt")))
151                     .status();
152   CheckStatus(actual);
153 }
154 
TEST_P(CurlClientTest,GetBucketIamPolicy)155 TEST_P(CurlClientTest, GetBucketIamPolicy) {
156   auto actual =
157       client_->GetBucketIamPolicy(GetBucketIamPolicyRequest("bkt")).status();
158   CheckStatus(actual);
159 }
160 
TEST_P(CurlClientTest,GetNativeBucketIamPolicy)161 TEST_P(CurlClientTest, GetNativeBucketIamPolicy) {
162   auto actual =
163       client_->GetNativeBucketIamPolicy(GetBucketIamPolicyRequest("bkt"))
164           .status();
165   CheckStatus(actual);
166 }
167 
TEST_P(CurlClientTest,SetBucketIamPolicy)168 TEST_P(CurlClientTest, SetBucketIamPolicy) {
169   auto actual = client_
170                     ->SetBucketIamPolicy(SetBucketIamPolicyRequest(
171                         "bkt", google::cloud::IamPolicy{}))
172                     .status();
173   CheckStatus(actual);
174 }
175 
TEST_P(CurlClientTest,SetNativeBucketIamPolicy)176 TEST_P(CurlClientTest, SetNativeBucketIamPolicy) {
177   auto actual =
178       client_
179           ->SetNativeBucketIamPolicy(SetNativeBucketIamPolicyRequest(
180               "bkt", NativeIamPolicy(std::vector<NativeIamBinding>())))
181           .status();
182   CheckStatus(actual);
183 }
184 
TEST_P(CurlClientTest,TestBucketIamPermissions)185 TEST_P(CurlClientTest, TestBucketIamPermissions) {
186   auto actual =
187       client_
188           ->TestBucketIamPermissions(TestBucketIamPermissionsRequest("bkt", {}))
189           .status();
190   CheckStatus(actual);
191 }
192 
TEST_P(CurlClientTest,LockBucketRetentionPolicy)193 TEST_P(CurlClientTest, LockBucketRetentionPolicy) {
194   auto actual = client_
195                     ->LockBucketRetentionPolicy(
196                         LockBucketRetentionPolicyRequest("bkt", 0))
197                     .status();
198   CheckStatus(actual);
199 }
200 
TEST_P(CurlClientTest,InsertObjectMediaSimple)201 TEST_P(CurlClientTest, InsertObjectMediaSimple) {
202   auto actual = client_
203                     ->InsertObjectMedia(
204                         InsertObjectMediaRequest("bkt", "obj", "contents")
205                             .set_multiple_options(DisableMD5Hash(true),
206                                                   DisableCrc32cChecksum(true)))
207                     .status();
208   CheckStatus(actual);
209 }
210 
TEST_P(CurlClientTest,InsertObjectMediaMultipart)211 TEST_P(CurlClientTest, InsertObjectMediaMultipart) {
212   auto actual = client_
213                     ->InsertObjectMedia(
214                         InsertObjectMediaRequest("bkt", "obj", "contents"))
215                     .status();
216   CheckStatus(actual);
217 }
218 
TEST_P(CurlClientTest,InsertObjectMediaXml)219 TEST_P(CurlClientTest, InsertObjectMediaXml) {
220   auto actual =
221       client_
222           ->InsertObjectMedia(InsertObjectMediaRequest("bkt", "obj", "contents")
223                                   .set_multiple_options(Fields("")))
224           .status();
225   CheckStatus(actual);
226 }
227 
TEST_P(CurlClientTest,GetObjectMetadata)228 TEST_P(CurlClientTest, GetObjectMetadata) {
229   auto actual =
230       client_->GetObjectMetadata(GetObjectMetadataRequest("bkt", "obj"))
231           .status();
232   CheckStatus(actual);
233 }
234 
TEST_P(CurlClientTest,ReadObjectXml)235 TEST_P(CurlClientTest, ReadObjectXml) {
236   if (GetParam() == "libcurl-failure") {
237     return;
238   }
239   auto actual =
240       client_->ReadObject(ReadObjectRangeRequest("bkt", "obj")).status();
241   CheckStatus(actual);
242 }
243 
TEST_P(CurlClientTest,ReadObjectJson)244 TEST_P(CurlClientTest, ReadObjectJson) {
245   if (GetParam() == "libcurl-failure") {
246     return;
247   }
248   auto actual =
249       client_
250           ->ReadObject(ReadObjectRangeRequest("bkt", "obj")
251                            .set_multiple_options(IfGenerationNotMatch(0)))
252           .status();
253   CheckStatus(actual);
254 }
255 
TEST_P(CurlClientTest,ListObjects)256 TEST_P(CurlClientTest, ListObjects) {
257   auto actual = client_->ListObjects(ListObjectsRequest("bkt")).status();
258   CheckStatus(actual);
259 }
260 
TEST_P(CurlClientTest,DeleteObject)261 TEST_P(CurlClientTest, DeleteObject) {
262   auto actual =
263       client_->DeleteObject(DeleteObjectRequest("bkt", "obj")).status();
264   CheckStatus(actual);
265 }
266 
TEST_P(CurlClientTest,UpdateObject)267 TEST_P(CurlClientTest, UpdateObject) {
268   auto actual =
269       client_->UpdateObject(UpdateObjectRequest("bkt", "obj", ObjectMetadata()))
270           .status();
271   CheckStatus(actual);
272 }
273 
TEST_P(CurlClientTest,PatchObject)274 TEST_P(CurlClientTest, PatchObject) {
275   auto actual = client_
276                     ->PatchObject(PatchObjectRequest(
277                         "bkt", "obj", ObjectMetadata(), ObjectMetadata()))
278                     .status();
279   CheckStatus(actual);
280 }
281 
TEST_P(CurlClientTest,ComposeObject)282 TEST_P(CurlClientTest, ComposeObject) {
283   auto actual =
284       client_->ComposeObject(ComposeObjectRequest("bkt", {}, "obj")).status();
285   CheckStatus(actual);
286 }
287 
TEST_P(CurlClientTest,ListBucketAcl)288 TEST_P(CurlClientTest, ListBucketAcl) {
289   auto actual = client_->ListBucketAcl(ListBucketAclRequest("bkt")).status();
290   CheckStatus(actual);
291 }
292 
TEST_P(CurlClientTest,CopyObject)293 TEST_P(CurlClientTest, CopyObject) {
294   auto actual =
295       client_->CopyObject(CopyObjectRequest("bkt", "obj1", "bkt", "obj2"))
296           .status();
297   CheckStatus(actual);
298 }
299 
TEST_P(CurlClientTest,CreateBucketAcl)300 TEST_P(CurlClientTest, CreateBucketAcl) {
301   auto actual =
302       client_->CreateBucketAcl(CreateBucketAclRequest("bkt", "entity", "role"))
303           .status();
304   CheckStatus(actual);
305 }
306 
TEST_P(CurlClientTest,GetBucketAcl)307 TEST_P(CurlClientTest, GetBucketAcl) {
308   auto actual =
309       client_->GetBucketAcl(GetBucketAclRequest("bkt", "entity")).status();
310   CheckStatus(actual);
311 }
312 
TEST_P(CurlClientTest,DeleteBucketAcl)313 TEST_P(CurlClientTest, DeleteBucketAcl) {
314   auto actual =
315       client_->DeleteBucketAcl(DeleteBucketAclRequest("bkt", "entity"))
316           .status();
317   CheckStatus(actual);
318 }
319 
TEST_P(CurlClientTest,UpdateBucketAcl)320 TEST_P(CurlClientTest, UpdateBucketAcl) {
321   auto actual =
322       client_->UpdateBucketAcl(UpdateBucketAclRequest("bkt", "entity", "role"))
323           .status();
324   CheckStatus(actual);
325 }
326 
TEST_P(CurlClientTest,PatchBucketAcl)327 TEST_P(CurlClientTest, PatchBucketAcl) {
328   auto actual =
329       client_
330           ->PatchBucketAcl(PatchBucketAclRequest(
331               "bkt", "entity", BucketAccessControl(), BucketAccessControl()))
332           .status();
333   CheckStatus(actual);
334 }
335 
TEST_P(CurlClientTest,ListObjectAcl)336 TEST_P(CurlClientTest, ListObjectAcl) {
337   auto actual =
338       client_->ListObjectAcl(ListObjectAclRequest("bkt", "obj")).status();
339   CheckStatus(actual);
340 }
341 
TEST_P(CurlClientTest,CreateObjectAcl)342 TEST_P(CurlClientTest, CreateObjectAcl) {
343   auto actual = client_
344                     ->CreateObjectAcl(
345                         CreateObjectAclRequest("bkt", "obj", "entity", "role"))
346                     .status();
347   CheckStatus(actual);
348 }
349 
TEST_P(CurlClientTest,DeleteObjectAcl)350 TEST_P(CurlClientTest, DeleteObjectAcl) {
351   auto actual =
352       client_->DeleteObjectAcl(DeleteObjectAclRequest("bkt", "obj", "entity"))
353           .status();
354   CheckStatus(actual);
355 }
356 
TEST_P(CurlClientTest,GetObjectAcl)357 TEST_P(CurlClientTest, GetObjectAcl) {
358   auto actual =
359       client_->GetObjectAcl(GetObjectAclRequest("bkt", "obj", "entity"))
360           .status();
361   CheckStatus(actual);
362 }
363 
TEST_P(CurlClientTest,UpdateObjectAcl)364 TEST_P(CurlClientTest, UpdateObjectAcl) {
365   auto actual = client_
366                     ->UpdateObjectAcl(
367                         UpdateObjectAclRequest("bkt", "obj", "entity", "role"))
368                     .status();
369   CheckStatus(actual);
370 }
371 
TEST_P(CurlClientTest,PatchObjectAcl)372 TEST_P(CurlClientTest, PatchObjectAcl) {
373   auto actual = client_
374                     ->PatchObjectAcl(PatchObjectAclRequest(
375                         "bkt", "obj", "entity", ObjectAccessControl(),
376                         ObjectAccessControl()))
377                     .status();
378   CheckStatus(actual);
379 }
380 
TEST_P(CurlClientTest,RewriteObject)381 TEST_P(CurlClientTest, RewriteObject) {
382   auto actual = client_
383                     ->RewriteObject(RewriteObjectRequest("bkt", "obj", "bkt2",
384                                                          "obj2", "token"))
385                     .status();
386   CheckStatus(actual);
387 }
388 
TEST_P(CurlClientTest,CreateResumableSession)389 TEST_P(CurlClientTest, CreateResumableSession) {
390   auto actual = client_
391                     ->CreateResumableSession(
392                         ResumableUploadRequest("test-bucket", "test-object"))
393                     .status();
394   CheckStatus(actual);
395 }
396 
TEST_P(CurlClientTest,DeleteResumableUpload)397 TEST_P(CurlClientTest, DeleteResumableUpload) {
398   auto actual = client_
399                     ->DeleteResumableUpload(
400                         DeleteResumableUploadRequest("test-upload-session-url"))
401                     .status();
402   CheckStatus(actual);
403 }
404 
TEST_P(CurlClientTest,ListDefaultObjectAcl)405 TEST_P(CurlClientTest, ListDefaultObjectAcl) {
406   auto actual =
407       client_->ListDefaultObjectAcl(ListDefaultObjectAclRequest("bkt"))
408           .status();
409   CheckStatus(actual);
410 }
411 
TEST_P(CurlClientTest,CreateDefaultObjectAcl)412 TEST_P(CurlClientTest, CreateDefaultObjectAcl) {
413   auto actual = client_
414                     ->CreateDefaultObjectAcl(
415                         CreateDefaultObjectAclRequest("bkt", "entity", "role"))
416                     .status();
417   CheckStatus(actual);
418 }
419 
TEST_P(CurlClientTest,DeleteDefaultObjectAcl)420 TEST_P(CurlClientTest, DeleteDefaultObjectAcl) {
421   auto actual = client_
422                     ->DeleteDefaultObjectAcl(
423                         DeleteDefaultObjectAclRequest("bkt", "entity"))
424                     .status();
425   CheckStatus(actual);
426 }
427 
TEST_P(CurlClientTest,GetDefaultObjectAcl)428 TEST_P(CurlClientTest, GetDefaultObjectAcl) {
429   auto actual =
430       client_->GetDefaultObjectAcl(GetDefaultObjectAclRequest("bkt", "entity"))
431           .status();
432   CheckStatus(actual);
433 }
434 
TEST_P(CurlClientTest,UpdateDefaultObjectAcl)435 TEST_P(CurlClientTest, UpdateDefaultObjectAcl) {
436   auto actual = client_
437                     ->UpdateDefaultObjectAcl(
438                         UpdateDefaultObjectAclRequest("bkt", "entity", "role"))
439                     .status();
440   CheckStatus(actual);
441 }
442 
TEST_P(CurlClientTest,PatchDefaultObjectAcl)443 TEST_P(CurlClientTest, PatchDefaultObjectAcl) {
444   auto actual =
445       client_
446           ->PatchDefaultObjectAcl(PatchDefaultObjectAclRequest(
447               "bkt", "entity", ObjectAccessControl(), ObjectAccessControl()))
448           .status();
449   CheckStatus(actual);
450 }
451 
TEST_P(CurlClientTest,GetServiceAccount)452 TEST_P(CurlClientTest, GetServiceAccount) {
453   auto actual =
454       client_->GetServiceAccount(GetProjectServiceAccountRequest("project_id"))
455           .status();
456   CheckStatus(actual);
457 }
458 
TEST_P(CurlClientTest,ListHmacKeyRequest)459 TEST_P(CurlClientTest, ListHmacKeyRequest) {
460   auto status =
461       client_->ListHmacKeys(ListHmacKeysRequest("project_id")).status();
462   CheckStatus(status);
463 }
464 
TEST_P(CurlClientTest,CreateHmacKeyRequest)465 TEST_P(CurlClientTest, CreateHmacKeyRequest) {
466   auto actual =
467       client_
468           ->CreateHmacKey(CreateHmacKeyRequest("project_id", "service-account"))
469           .status();
470   CheckStatus(actual);
471 }
472 
TEST_P(CurlClientTest,SignBlob)473 TEST_P(CurlClientTest, SignBlob) {
474   auto actual =
475       client_
476           ->SignBlob(SignBlobRequest("test-service-account", "test-blob", {}))
477           .status();
478   CheckStatus(actual);
479 }
480 
TEST_P(CurlClientTest,ListNotifications)481 TEST_P(CurlClientTest, ListNotifications) {
482   auto actual =
483       client_->ListNotifications(ListNotificationsRequest("bkt")).status();
484   CheckStatus(actual);
485 }
486 
TEST_P(CurlClientTest,CreateNotification)487 TEST_P(CurlClientTest, CreateNotification) {
488   auto actual = client_
489                     ->CreateNotification(CreateNotificationRequest(
490                         "bkt", NotificationMetadata()))
491                     .status();
492   CheckStatus(actual);
493 }
494 
TEST_P(CurlClientTest,GetNotification)495 TEST_P(CurlClientTest, GetNotification) {
496   auto actual =
497       client_->GetNotification(GetNotificationRequest("bkt", "notification_id"))
498           .status();
499   CheckStatus(actual);
500 }
501 
TEST_P(CurlClientTest,DeleteNotification)502 TEST_P(CurlClientTest, DeleteNotification) {
503   auto actual = client_
504                     ->DeleteNotification(
505                         DeleteNotificationRequest("bkt", "notification_id"))
506                     .status();
507   CheckStatus(actual);
508 }
509 
510 INSTANTIATE_TEST_SUITE_P(CredentialsFailure, CurlClientTest,
511                          ::testing::Values("credentials-failure"));
512 
513 INSTANTIATE_TEST_SUITE_P(LibCurlFailure, CurlClientTest,
514                          ::testing::Values("libcurl-failure"));
515 
516 }  // namespace
517 }  // namespace internal
518 }  // namespace STORAGE_CLIENT_NS
519 }  // namespace storage
520 }  // namespace cloud
521 }  // namespace google
522