Skip to content

Commit

Permalink
impl: API key auth over REST (#14785)
Browse files Browse the repository at this point in the history
  • Loading branch information
dbolduc authored Oct 14, 2024
1 parent 3605bb2 commit 8abc170
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 3 deletions.
2 changes: 2 additions & 0 deletions google/cloud/google_cloud_cpp_rest_internal.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ google_cloud_cpp_rest_internal_hdrs = [
"internal/make_jwt_assertion.h",
"internal/oauth2_access_token_credentials.h",
"internal/oauth2_anonymous_credentials.h",
"internal/oauth2_api_key_credentials.h",
"internal/oauth2_authorized_user_credentials.h",
"internal/oauth2_cached_credentials.h",
"internal/oauth2_compute_engine_credentials.h",
Expand Down Expand Up @@ -94,6 +95,7 @@ google_cloud_cpp_rest_internal_srcs = [
"internal/make_jwt_assertion.cc",
"internal/oauth2_access_token_credentials.cc",
"internal/oauth2_anonymous_credentials.cc",
"internal/oauth2_api_key_credentials.cc",
"internal/oauth2_authorized_user_credentials.cc",
"internal/oauth2_cached_credentials.cc",
"internal/oauth2_compute_engine_credentials.cc",
Expand Down
3 changes: 3 additions & 0 deletions google/cloud/google_cloud_cpp_rest_internal.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ add_library(
internal/oauth2_access_token_credentials.h
internal/oauth2_anonymous_credentials.cc
internal/oauth2_anonymous_credentials.h
internal/oauth2_api_key_credentials.cc
internal/oauth2_api_key_credentials.h
internal/oauth2_authorized_user_credentials.cc
internal/oauth2_authorized_user_credentials.h
internal/oauth2_cached_credentials.cc
Expand Down Expand Up @@ -257,6 +259,7 @@ if (BUILD_TESTING)
internal/make_jwt_assertion_test.cc
internal/oauth2_access_token_credentials_test.cc
internal/oauth2_anonymous_credentials_test.cc
internal/oauth2_api_key_credentials_test.cc
internal/oauth2_authorized_user_credentials_test.cc
internal/oauth2_cached_credentials_test.cc
internal/oauth2_compute_engine_credentials_test.cc
Expand Down
1 change: 1 addition & 0 deletions google/cloud/google_cloud_cpp_rest_internal_unit_tests.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ google_cloud_cpp_rest_internal_unit_tests = [
"internal/make_jwt_assertion_test.cc",
"internal/oauth2_access_token_credentials_test.cc",
"internal/oauth2_anonymous_credentials_test.cc",
"internal/oauth2_api_key_credentials_test.cc",
"internal/oauth2_authorized_user_credentials_test.cc",
"internal/oauth2_cached_credentials_test.cc",
"internal/oauth2_compute_engine_credentials_test.cc",
Expand Down
38 changes: 38 additions & 0 deletions google/cloud/internal/oauth2_api_key_credentials.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "google/cloud/internal/oauth2_api_key_credentials.h"

namespace google {
namespace cloud {
namespace oauth2_internal {
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN

ApiKeyCredentials::ApiKeyCredentials(std::string api_key)
: api_key_(std::move(api_key)) {}

StatusOr<AccessToken> ApiKeyCredentials::GetToken(
std::chrono::system_clock::time_point tp) {
return AccessToken{std::string{}, tp};
}

StatusOr<std::pair<std::string, std::string>>
ApiKeyCredentials::AuthenticationHeader(std::chrono::system_clock::time_point) {
return std::make_pair(std::string{"x-goog-api-key"}, api_key_);
}

GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace oauth2_internal
} // namespace cloud
} // namespace google
52 changes: 52 additions & 0 deletions google/cloud/internal/oauth2_api_key_credentials.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_OAUTH2_API_KEY_CREDENTIALS_H
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_OAUTH2_API_KEY_CREDENTIALS_H

#include "google/cloud/internal/credentials_impl.h"
#include "google/cloud/internal/oauth2_credentials.h"
#include "google/cloud/status_or.h"
#include "google/cloud/version.h"
#include <string>
#include <utility>

namespace google {
namespace cloud {
namespace oauth2_internal {
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN

/**
* A `Credentials` type representing a Bearer Token OAuth 2.0 credentials.
*/
class ApiKeyCredentials : public oauth2_internal::Credentials {
public:
explicit ApiKeyCredentials(std::string api_key);

StatusOr<AccessToken> GetToken(
std::chrono::system_clock::time_point tp) override;

StatusOr<std::pair<std::string, std::string>> AuthenticationHeader(
std::chrono::system_clock::time_point) override;

private:
std::string api_key_;
};

GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace oauth2_internal
} // namespace cloud
} // namespace google

#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_OAUTH2_API_KEY_CREDENTIALS_H
49 changes: 49 additions & 0 deletions google/cloud/internal/oauth2_api_key_credentials_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "google/cloud/internal/oauth2_api_key_credentials.h"
#include "google/cloud/testing_util/status_matchers.h"
#include <gmock/gmock.h>
#include <chrono>

namespace google {
namespace cloud {
namespace oauth2_internal {
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
namespace {

using ::google::cloud::testing_util::IsOkAndHolds;
using ::testing::IsEmpty;
using ::testing::Pair;

TEST(ApiKeyCredentials, EmptyToken) {
ApiKeyCredentials creds("api-key");
auto const now = std::chrono::system_clock::now();
auto const token = creds.GetToken(now);
ASSERT_STATUS_OK(token);
EXPECT_THAT(token->token, IsEmpty());
}

TEST(ApiKeyCredentials, SetsXGoogApiKeyHeader) {
ApiKeyCredentials creds("api-key");
auto const now = std::chrono::system_clock::now();
EXPECT_THAT(creds.AuthenticationHeader(now),
IsOkAndHolds(Pair("x-goog-api-key", "api-key")));
}

} // namespace
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace oauth2_internal
} // namespace cloud
} // namespace google
7 changes: 4 additions & 3 deletions google/cloud/internal/unified_rest_credentials.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "google/cloud/internal/make_jwt_assertion.h"
#include "google/cloud/internal/oauth2_access_token_credentials.h"
#include "google/cloud/internal/oauth2_anonymous_credentials.h"
#include "google/cloud/internal/oauth2_api_key_credentials.h"
#include "google/cloud/internal/oauth2_decorate_credentials.h"
#include "google/cloud/internal/oauth2_error_credentials.h"
#include "google/cloud/internal/oauth2_external_account_credentials.h"
Expand Down Expand Up @@ -137,9 +138,9 @@ std::shared_ptr<oauth2_internal::Credentials> MapCredentials(
cfg.options());
}

void visit(ApiKeyConfig const&) override {
// TODO(#14759) - Support API key authentication over REST
result = std::make_shared<oauth2_internal::AnonymousCredentials>();
void visit(ApiKeyConfig const& cfg) override {
result =
std::make_shared<oauth2_internal::ApiKeyCredentials>(cfg.api_key());
}

private:
Expand Down
15 changes: 15 additions & 0 deletions google/cloud/internal/unified_rest_credentials_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
namespace {

using ::google::cloud::MakeAccessTokenCredentials;
using ::google::cloud::MakeApiKeyCredentials;
using ::google::cloud::MakeGoogleDefaultCredentials;
using ::google::cloud::MakeInsecureCredentials;
using ::google::cloud::testing_util::IsOk;
using ::google::cloud::testing_util::IsOkAndHolds;
using ::google::cloud::testing_util::MakeMockHttpPayloadSuccess;
using ::google::cloud::testing_util::MockRestClient;
using ::google::cloud::testing_util::MockRestResponse;
Expand All @@ -56,6 +58,7 @@ using ::testing::HasSubstr;
using ::testing::IsEmpty;
using ::testing::IsSupersetOf;
using ::testing::MatcherCast;
using ::testing::NotNull;
using ::testing::Pair;
using ::testing::Property;
using ::testing::Return;
Expand Down Expand Up @@ -459,6 +462,18 @@ TEST(UnifiedRestCredentialsTest, ExternalAccount) {
StatusIs(StatusCode::kPermissionDenied, "uh-oh - STS exchange"));
}

TEST(UnifiedRestCredentialsTest, ApiKey) {
auto creds = MakeApiKeyCredentials("api-key");
ASSERT_THAT(creds, NotNull());

auto oauth2_creds = MapCredentials(*creds);
ASSERT_THAT(oauth2_creds, NotNull());

auto header =
oauth2_creds->AuthenticationHeader(std::chrono::system_clock::now());
EXPECT_THAT(header, IsOkAndHolds(Pair("x-goog-api-key", "api-key")));
}

TEST(UnifiedRestCredentialsTest, LoadError) {
// Create a name for a non-existing file, try to load it, and verify it
// returns errors.
Expand Down

0 comments on commit 8abc170

Please sign in to comment.