blob: 7b2f6b16018514e6f7e75869bdfa681d7ad22245 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "google_apis/api_key_cache.h"
#include <stddef.h>
#include <algorithm>
#include <memory>
#include <string>
#include "base/command_line.h"
#include "base/environment.h"
#include "base/feature_list.h"
#include "base/features.h"
#include "base/logging.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/stringize_macros.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "google_apis/buildflags.h"
#include "google_apis/default_api_keys.h"
#include "google_apis/gaia/gaia_config.h"
#include "google_apis/gaia/gaia_switches.h"
#if BUILDFLAG(IS_APPLE)
#include "google_apis/google_api_keys_mac.h"
#endif
namespace google_apis {
namespace {
const base::FeatureParam<std::string> kOverrideAPIKeyFeatureParam{
&kOverrideAPIKeyFeature, /*name=*/"api_key", /*default_value=*/""};
std::string GetAPIKeyOverrideViaFeature() {
if (base::FeatureList::IsEnabled(kOverrideAPIKeyFeature)) {
std::string override_api_key = kOverrideAPIKeyFeatureParam.Get();
if (!override_api_key.empty()) {
return override_api_key;
}
}
return std::string();
}
// Gets a value for a key. In priority order, this will be the value
// provided via:
// 1. Command-line switch
// 2. Config file
// 3. Environment variable
// 4. Value passed via a feature flag.
// 5. On macOS and iOS, the value passed in Info.plist
// 6. Baked into the build.
// |command_line_switch| may be NULL. Official Google Chrome builds will not
// use the value provided by an environment variable.
static std::string CalculateKeyValue(const char* baked_in_value,
const char* environment_variable_name,
const std::string& feature_value,
const char* command_line_switch,
const std::string& default_if_unset,
base::Environment* environment,
base::CommandLine* command_line,
GaiaConfig* gaia_config,
bool allow_override_via_environment,
bool allow_unset_values) {
std::string key_value = baked_in_value;
std::string temp;
#if BUILDFLAG(IS_APPLE)
// macOS and iOS can also override the API key with a value from the
// Info.plist.
temp = GetAPIKeyFromInfoPlist(environment_variable_name);
if (!temp.empty()) {
key_value = temp;
VLOG(1) << "Overriding API key " << environment_variable_name
<< " with value from Info.plist.";
}
#endif
if (!feature_value.empty()) {
key_value = feature_value;
// `feature_value` should not be logged.
VLOG(1) << "Overriding API key " << environment_variable_name
<< " with value passed via feature.";
}
if (allow_override_via_environment) {
// Don't allow using the environment to override API keys for official
// Google Chrome builds. There have been reports of mangled environments
// affecting users (crbug.com/710575).
if (environment->GetVar(environment_variable_name, &temp)) {
key_value = temp;
VLOG(1) << "Overriding API key " << environment_variable_name
<< " with value " << key_value << " from environment variable.";
}
}
if (gaia_config &&
gaia_config->GetAPIKeyIfExists(environment_variable_name, &temp)) {
key_value = temp;
VLOG(1) << "Overriding API key " << environment_variable_name
<< " with value " << key_value << " from gaia config.";
}
if (command_line_switch && command_line->HasSwitch(command_line_switch)) {
key_value = command_line->GetSwitchValueASCII(command_line_switch);
VLOG(1) << "Overriding API key " << environment_variable_name
<< " with value " << key_value << " from command-line switch.";
}
if (key_value == DefaultApiKeys::kUnsetApiToken) {
// No key should be unset in an official build except the
// GOOGLE_DEFAULT_* keys. The default keys don't trigger this
// check as their "unset" value is not DefaultApiKeys::kUnsetApiToken.
CHECK(allow_unset_values);
if (default_if_unset.size() > 0) {
VLOG(1) << "Using default value \"" << default_if_unset
<< "\" for API key " << environment_variable_name;
key_value = default_if_unset;
}
}
// This should remain a debug-only log.
DVLOG(1) << "API key " << environment_variable_name << "=" << key_value;
return key_value;
}
} // namespace
BASE_FEATURE(kOverrideAPIKeyFeature,
"OverrideAPIKey",
base::FEATURE_DISABLED_BY_DEFAULT);
ApiKeyCache::ApiKeyCache(const DefaultApiKeys& default_api_keys) {
std::unique_ptr<base::Environment> environment(base::Environment::Create());
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
GaiaConfig* gaia_config = GaiaConfig::GetInstance();
std::string api_key_from_feature = GetAPIKeyOverrideViaFeature();
api_key_ = CalculateKeyValue(default_api_keys.google_api_key,
STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY),
api_key_from_feature, nullptr, std::string(),
environment.get(), command_line, gaia_config,
default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
base::UmaHistogramBoolean("Signin.APIKeyMatchesFeatureOnStartup",
api_key_from_feature == api_key_);
// A special non-stable key is at the moment defined only for Android Chrome.
#if BUILDFLAG(IS_ANDROID)
api_key_non_stable_ = CalculateKeyValue(
default_api_keys.google_api_key_android_non_stable,
STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_ANDROID_NON_STABLE), std::string(),
nullptr, std::string(), environment.get(), command_line, gaia_config,
default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
#else
api_key_non_stable_ = api_key_;
#endif
api_key_remoting_ = CalculateKeyValue(
default_api_keys.google_api_key_remoting,
STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_REMOTING), std::string(), nullptr,
std::string(), environment.get(), command_line, gaia_config,
default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
api_key_soda_ = CalculateKeyValue(
default_api_keys.google_api_key_soda,
STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_SODA), std::string(), nullptr,
std::string(), environment.get(), command_line, gaia_config,
default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
#if !BUILDFLAG(IS_ANDROID)
api_key_hats_ = CalculateKeyValue(
default_api_keys.google_api_key_hats,
STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_HATS), std::string(), nullptr,
std::string(), environment.get(), command_line, gaia_config,
default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
api_key_sharing_ = CalculateKeyValue(
default_api_keys.google_api_key_sharing,
STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_SHARING), std::string(), nullptr,
std::string(), environment.get(), command_line, gaia_config,
default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
api_key_read_aloud_ = CalculateKeyValue(
default_api_keys.google_api_key_read_aloud,
STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_READ_ALOUD), std::string(), nullptr,
std::string(), environment.get(), command_line, gaia_config,
default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
api_key_fresnel_ = CalculateKeyValue(
default_api_keys.google_api_key_fresnel,
STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_FRESNEL), std::string(), nullptr,
std::string(), environment.get(), command_line, gaia_config,
default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
api_key_boca_ = CalculateKeyValue(
default_api_keys.google_api_key_boca,
STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_BOCA), std::string(), nullptr,
std::string(), environment.get(), command_line, gaia_config,
default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
#endif
metrics_key_ = CalculateKeyValue(
default_api_keys.google_metrics_signing_key,
STRINGIZE_NO_EXPANSION(GOOGLE_METRICS_SIGNING_KEY), std::string(),
nullptr, std::string(), environment.get(), command_line, gaia_config,
default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
std::string default_client_id = CalculateKeyValue(
default_api_keys.google_default_client_id,
STRINGIZE_NO_EXPANSION(GOOGLE_DEFAULT_CLIENT_ID), std::string(), nullptr,
std::string(), environment.get(), command_line, gaia_config,
default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
std::string default_client_secret = CalculateKeyValue(
default_api_keys.google_default_client_secret,
STRINGIZE_NO_EXPANSION(GOOGLE_DEFAULT_CLIENT_SECRET), std::string(),
nullptr, std::string(), environment.get(), command_line, gaia_config,
default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
// We currently only allow overriding the baked-in values for the
// default OAuth2 client ID and secret using a command-line
// argument and gaia config, since that is useful to enable testing against
// staging servers, and since that was what was possible and
// likely practiced by the QA team before this implementation was
// written.
client_ids_[CLIENT_MAIN] = CalculateKeyValue(
default_api_keys.google_client_id_main,
STRINGIZE_NO_EXPANSION(GOOGLE_CLIENT_ID_MAIN), std::string(),
::switches::kOAuth2ClientID, default_client_id, environment.get(),
command_line, gaia_config,
default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
client_secrets_[CLIENT_MAIN] = CalculateKeyValue(
default_api_keys.google_client_secret_main,
STRINGIZE_NO_EXPANSION(GOOGLE_CLIENT_SECRET_MAIN), std::string(),
::switches::kOAuth2ClientSecret, default_client_secret, environment.get(),
command_line, gaia_config,
default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
client_ids_[CLIENT_REMOTING] = CalculateKeyValue(
default_api_keys.google_client_id_remoting,
STRINGIZE_NO_EXPANSION(GOOGLE_CLIENT_ID_REMOTING), std::string(), nullptr,
default_client_id, environment.get(), command_line, gaia_config,
default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
client_secrets_[CLIENT_REMOTING] = CalculateKeyValue(
default_api_keys.google_client_secret_remoting,
STRINGIZE_NO_EXPANSION(GOOGLE_CLIENT_SECRET_REMOTING), std::string(),
nullptr, default_client_secret, environment.get(), command_line,
gaia_config, default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
client_ids_[CLIENT_REMOTING_HOST] = CalculateKeyValue(
default_api_keys.google_client_id_remoting_host,
STRINGIZE_NO_EXPANSION(GOOGLE_CLIENT_ID_REMOTING_HOST), std::string(),
nullptr, default_client_id, environment.get(), command_line, gaia_config,
default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
client_secrets_[CLIENT_REMOTING_HOST] = CalculateKeyValue(
default_api_keys.google_client_secret_remoting_host,
STRINGIZE_NO_EXPANSION(GOOGLE_CLIENT_SECRET_REMOTING_HOST), std::string(),
nullptr, default_client_secret, environment.get(), command_line,
gaia_config, default_api_keys.allow_override_via_environment,
default_api_keys.allow_unset_values);
}
ApiKeyCache::~ApiKeyCache() = default;
const std::string& ApiKeyCache::GetClientID(OAuth2Client client) const {
DCHECK_LT(client, CLIENT_NUM_ITEMS);
return client_ids_[client];
}
const std::string& ApiKeyCache::GetClientSecret(OAuth2Client client) const {
DCHECK_LT(client, CLIENT_NUM_ITEMS);
return client_secrets_[client];
}
#if BUILDFLAG(SUPPORT_EXTERNAL_GOOGLE_API_KEY)
void ApiKeyCache::SetClientID(OAuth2Client client,
const std::string& client_id) {
client_ids_[client] = client_id;
}
void ApiKeyCache::SetClientSecret(OAuth2Client client,
const std::string& client_secret) {
client_secrets_[client] = client_secret;
}
#endif // BUILDFLAG(SUPPORT_EXTERNAL_GOOGLE_API_KEY)
bool ApiKeyCache::HasAPIKeyConfigured() const {
return api_key_ != DefaultApiKeys::kUnsetApiToken;
}
bool ApiKeyCache::HasOAuthClientConfigured() const {
auto is_unset = [](const std::string& value) {
return value == DefaultApiKeys::kUnsetApiToken;
};
return std::ranges::none_of(client_ids_, is_unset) &&
std::ranges::none_of(client_secrets_, is_unset);
}
} // namespace google_apis