Skip to content

Commit

Permalink
Add support for loading API keys from Info.plist on iOS and macOS.
Browse files Browse the repository at this point in the history
BUG=629511

Review-Url: https://codereview.chromium.org/2224473002
Cr-Commit-Position: refs/heads/master@{#411101}
  • Loading branch information
bzanotti authored and Commit bot committed Aug 10, 2016
1 parent bf8b385 commit bd01460
Show file tree
Hide file tree
Showing 8 changed files with 281 additions and 61 deletions.
20 changes: 20 additions & 0 deletions google_apis/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,17 @@ template("google_apis_tmpl") {
"drive/time_util.h",
]
}

if (is_mac || is_ios) {
set_sources_assignment_filter([])
sources += [
"google_api_keys_mac.h",
"google_api_keys_mac.mm",
]
set_sources_assignment_filter(sources_assignment_filter)

libs = [ "Foundation.framework" ]
}
}
}

Expand Down Expand Up @@ -230,6 +241,7 @@ test("google_apis_unittests") {
"gaia/oauth_request_signer_unittest.cc",
"gaia/ubertoken_fetcher_unittest.cc",
"google_api_keys_unittest.cc",
"google_api_keys_unittest.h",
]

data = [
Expand Down Expand Up @@ -260,4 +272,12 @@ test("google_apis_unittests") {
"drive/time_util_unittest.cc",
]
}

if (is_mac || is_ios) {
set_sources_assignment_filter([])
sources += [ "google_api_keys_mac_unittest.mm" ]
set_sources_assignment_filter(sources_assignment_filter)

deps += [ "//third_party/ocmock" ]
}
}
1 change: 1 addition & 0 deletions google_apis/DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ include_rules = [
"-content",
"+crypto",
"+net",
"+third_party/ocmock",
]
14 changes: 14 additions & 0 deletions google_apis/google_api_keys.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
#include "base/strings/stringize_macros.h"
#include "google_apis/gaia/gaia_switches.h"

#if defined(OS_MACOSX)
#include "google_apis/google_api_keys_mac.h"
#endif

#if defined(GOOGLE_CHROME_BUILD) || defined(USE_OFFICIAL_GOOGLE_API_KEYS)
#include "google_apis/internal/google_chrome_api_keys.h"
#endif
Expand Down Expand Up @@ -236,6 +240,16 @@ class APIKeyCache {
base::CommandLine* command_line) {
std::string key_value = baked_in_value;
std::string temp;
#if defined(OS_MACOSX)
// macOS and iOS can also override the API key with a value from the
// Info.plist.
temp = ::google_apis::GetAPIKeyFromInfoPlist(environment_variable_name);
if (!temp.empty()) {
key_value = temp;
VLOG(1) << "Overriding API key " << environment_variable_name
<< " with value " << key_value << " from Info.plist.";
}
#endif
if (environment->GetVar(environment_variable_name, &temp)) {
key_value = temp;
VLOG(1) << "Overriding API key " << environment_variable_name
Expand Down
16 changes: 16 additions & 0 deletions google_apis/google_api_keys_mac.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef GOOGLE_API_KEYS_MAC_H_
#define GOOGLE_API_KEYS_MAC_H_

#include <string>

namespace google_apis {

std::string GetAPIKeyFromInfoPlist(const std::string& key_name);

} // namespace google_apis

#endif // GOOGLE_API_KEYS_MAC_H_
22 changes: 22 additions & 0 deletions google_apis/google_api_keys_mac.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "google_api_keys_mac.h"

#import <Foundation/Foundation.h>

#include "base/mac/bundle_locations.h"
#include "base/mac/foundation_util.h"
#include "base/strings/sys_string_conversions.h"

namespace google_apis {

std::string GetAPIKeyFromInfoPlist(const std::string& key_name) {
NSString* keyName = base::SysUTF8ToNSString(key_name);
NSString* keyValue = base::mac::ObjCCast<NSString>(
[base::mac::FrameworkBundle() objectForInfoDictionaryKey:keyName]);
return base::SysNSStringToUTF8(keyValue);
}

} // namespace google_apis
124 changes: 124 additions & 0 deletions google_apis/google_api_keys_mac_unittest.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Unit tests for implementation of google_api_keys namespace.
//
// Because the file deals with a lot of preprocessor defines and
// optionally includes an internal header, the way we test is by
// including the .cc file multiple times with different defines set.
// This is a little unorthodox, but it lets us test the behavior as
// close to unmodified as possible.

#include "google_apis/google_api_keys_unittest.h"

#include "base/mac/bundle_locations.h"
#include "base/macros.h"
#include "build/build_config.h"
#include "google_apis/gaia/gaia_switches.h"
#include "testing/gtest/include/gtest/gtest.h"
#import "third_party/ocmock/OCMock/OCMock.h"

// We need to include everything included by google_api_keys.cc once
// at global scope so that things like STL and classes from base don't
// get defined when we re-include the google_api_keys.cc file
// below. We used to include that file in its entirety here, but that
// can cause problems if the linker decides the version of symbols
// from that file included here is the "right" version.

#include <stddef.h>

#include <string>
#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/strings/stringize_macros.h"
#include "google_apis/google_api_keys_mac.h"

// After this test, for the remainder of this compilation unit, we
// need official keys to not be used.
#undef GOOGLE_CHROME_BUILD
#undef USE_OFFICIAL_GOOGLE_API_KEYS

// Override some keys using both preprocessor defines and Info.plist entries.
// The Info.plist entries should win.
namespace override_some_keys_info_plist {

// We start every test by creating a clean environment for the
// preprocessor defines used in google_api_keys.cc
#undef DUMMY_API_TOKEN
#undef GOOGLE_API_KEY
#undef GOOGLE_CLIENT_ID_MAIN
#undef GOOGLE_CLIENT_SECRET_MAIN
#undef GOOGLE_CLIENT_ID_CLOUD_PRINT
#undef GOOGLE_CLIENT_SECRET_CLOUD_PRINT
#undef GOOGLE_CLIENT_ID_REMOTING
#undef GOOGLE_CLIENT_SECRET_REMOTING
#undef GOOGLE_CLIENT_ID_REMOTING_HOST
#undef GOOGLE_CLIENT_SECRET_REMOTING_HOST
#undef GOOGLE_DEFAULT_CLIENT_ID
#undef GOOGLE_DEFAULT_CLIENT_SECRET

#define GOOGLE_API_KEY "API_KEY"
#define GOOGLE_CLIENT_ID_MAIN "ID_MAIN"
#define GOOGLE_CLIENT_SECRET_MAIN "SECRET_MAIN"
#define GOOGLE_CLIENT_ID_CLOUD_PRINT "ID_CLOUD_PRINT"
#define GOOGLE_CLIENT_SECRET_CLOUD_PRINT "SECRET_CLOUD_PRINT"
#define GOOGLE_CLIENT_ID_REMOTING "ID_REMOTING"
#define GOOGLE_CLIENT_SECRET_REMOTING "SECRET_REMOTING"
#define GOOGLE_CLIENT_ID_REMOTING_HOST "ID_REMOTING_HOST"
#define GOOGLE_CLIENT_SECRET_REMOTING_HOST "SECRET_REMOTING_HOST"

// Undef include guard so things get defined again, within this namespace.
#undef GOOGLE_APIS_GOOGLE_API_KEYS_H_
#undef GOOGLE_APIS_INTERNAL_GOOGLE_CHROME_API_KEYS_
#include "google_apis/google_api_keys.cc"

} // namespace override_all_keys_env

TEST_F(GoogleAPIKeysTest, OverrideSomeKeysUsingInfoPlist) {
namespace testcase = override_some_keys_info_plist::google_apis;

id mock_bundle = [OCMockObject mockForClass:[NSBundle class]];
[[[mock_bundle stub] andReturn:@"plist-API_KEY"]
objectForInfoDictionaryKey:@"GOOGLE_API_KEY"];
[[[mock_bundle stub] andReturn:@"plist-ID_MAIN"]
objectForInfoDictionaryKey:@"GOOGLE_CLIENT_ID_MAIN"];
[[[mock_bundle stub] andReturn:nil] objectForInfoDictionaryKey:[OCMArg any]];
base::mac::SetOverrideFrameworkBundle(mock_bundle);

EXPECT_TRUE(testcase::HasKeysConfigured());

// Once the keys have been configured, the bundle isn't used anymore.
base::mac::SetOverrideFrameworkBundle(nil);

std::string api_key = testcase::g_api_key_cache.Get().api_key();
std::string id_main =
testcase::g_api_key_cache.Get().GetClientID(testcase::CLIENT_MAIN);
std::string secret_main =
testcase::g_api_key_cache.Get().GetClientSecret(testcase::CLIENT_MAIN);
std::string id_cloud_print =
testcase::g_api_key_cache.Get().GetClientID(testcase::CLIENT_CLOUD_PRINT);
std::string secret_cloud_print =
testcase::g_api_key_cache.Get().GetClientSecret(
testcase::CLIENT_CLOUD_PRINT);
std::string id_remoting =
testcase::g_api_key_cache.Get().GetClientID(testcase::CLIENT_REMOTING);
std::string secret_remoting = testcase::g_api_key_cache.Get().GetClientSecret(
testcase::CLIENT_REMOTING);
std::string id_remoting_host = testcase::g_api_key_cache.Get().GetClientID(
testcase::CLIENT_REMOTING_HOST);
std::string secret_remoting_host =
testcase::g_api_key_cache.Get().GetClientSecret(
testcase::CLIENT_REMOTING_HOST);

EXPECT_EQ("plist-API_KEY", api_key);
EXPECT_EQ("plist-ID_MAIN", id_main);
EXPECT_EQ("SECRET_MAIN", secret_main);
EXPECT_EQ("ID_CLOUD_PRINT", id_cloud_print);
EXPECT_EQ("SECRET_CLOUD_PRINT", secret_cloud_print);
EXPECT_EQ("ID_REMOTING", id_remoting);
EXPECT_EQ("SECRET_REMOTING", secret_remoting);
EXPECT_EQ("ID_REMOTING_HOST", id_remoting_host);
EXPECT_EQ("SECRET_REMOTING_HOST", secret_remoting_host);
}
103 changes: 42 additions & 61 deletions google_apis/google_api_keys_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,11 @@
// This is a little unorthodox, but it lets us test the behavior as
// close to unmodified as possible.

#include "google_apis/google_api_keys.h"

#include <memory>
#include "google_apis/google_api_keys_unittest.h"

#include "base/macros.h"
#include "build/build_config.h"
#include "google_apis/gaia/gaia_switches.h"
#include "testing/gtest/include/gtest/gtest.h"

// The Win builders fail (with a linker crash) when trying to link
// unit_tests, and the Android builders complain about multiply
Expand All @@ -38,74 +35,58 @@

#include <string>
#include "base/command_line.h"
#include "base/environment.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/strings/stringize_macros.h"

// This is the default baked-in value for OAuth IDs and secrets.
static const char kDummyToken[] = "dummytoken";

struct EnvironmentCache {
public:
EnvironmentCache() : variable_name(NULL), was_set(false) {}

const char* variable_name;
bool was_set;
std::string value;
};

class GoogleAPIKeysTest : public testing::Test {
public:
GoogleAPIKeysTest() : env_(base::Environment::Create()) {
env_cache_[0].variable_name = "GOOGLE_API_KEY";
env_cache_[1].variable_name = "GOOGLE_CLIENT_ID_MAIN";
env_cache_[2].variable_name = "GOOGLE_CLIENT_SECRET_MAIN";
env_cache_[3].variable_name = "GOOGLE_CLIENT_ID_CLOUD_PRINT";
env_cache_[4].variable_name = "GOOGLE_CLIENT_SECRET_CLOUD_PRINT";
env_cache_[5].variable_name = "GOOGLE_CLIENT_ID_REMOTING";
env_cache_[6].variable_name = "GOOGLE_CLIENT_SECRET_REMOTING";
env_cache_[7].variable_name = "GOOGLE_CLIENT_ID_REMOTING_HOST";
env_cache_[8].variable_name = "GOOGLE_CLIENT_SECRET_REMOTING_HOST";
env_cache_[9].variable_name = "GOOGLE_DEFAULT_CLIENT_ID";
env_cache_[10].variable_name = "GOOGLE_DEFAULT_CLIENT_SECRET";
}
#if defined(OS_MACOSX)
#include "google_apis/google_api_keys_mac.h"
#endif

GoogleAPIKeysTest::GoogleAPIKeysTest() : env_(base::Environment::Create()) {
static_assert(11 == 3 + 2 * google_apis::CLIENT_NUM_ITEMS,
"Unexpected number of key entries.");
env_cache_[0].variable_name = "GOOGLE_API_KEY";
env_cache_[1].variable_name = "GOOGLE_CLIENT_ID_MAIN";
env_cache_[2].variable_name = "GOOGLE_CLIENT_SECRET_MAIN";
env_cache_[3].variable_name = "GOOGLE_CLIENT_ID_CLOUD_PRINT";
env_cache_[4].variable_name = "GOOGLE_CLIENT_SECRET_CLOUD_PRINT";
env_cache_[5].variable_name = "GOOGLE_CLIENT_ID_REMOTING";
env_cache_[6].variable_name = "GOOGLE_CLIENT_SECRET_REMOTING";
env_cache_[7].variable_name = "GOOGLE_CLIENT_ID_REMOTING_HOST";
env_cache_[8].variable_name = "GOOGLE_CLIENT_SECRET_REMOTING_HOST";
env_cache_[9].variable_name = "GOOGLE_DEFAULT_CLIENT_ID";
env_cache_[10].variable_name = "GOOGLE_DEFAULT_CLIENT_SECRET";
}

void SetUp() override {
// Unset all environment variables that can affect these tests,
// for the duration of the tests.
for (size_t i = 0; i < arraysize(env_cache_); ++i) {
EnvironmentCache& cache = env_cache_[i];
cache.was_set = env_->HasVar(cache.variable_name);
cache.value.clear();
if (cache.was_set) {
env_->GetVar(cache.variable_name, &cache.value);
env_->UnSetVar(cache.variable_name);
}
GoogleAPIKeysTest::~GoogleAPIKeysTest() {}

void GoogleAPIKeysTest::SetUp() {
// Unset all environment variables that can affect these tests,
// for the duration of the tests.
for (size_t i = 0; i < arraysize(env_cache_); ++i) {
EnvironmentCache& cache = env_cache_[i];
cache.was_set = env_->HasVar(cache.variable_name);
cache.value.clear();
if (cache.was_set) {
env_->GetVar(cache.variable_name, &cache.value);
env_->UnSetVar(cache.variable_name);
}
}
}

void TearDown() override {
// Restore environment.
for (size_t i = 0; i < arraysize(env_cache_); ++i) {
EnvironmentCache& cache = env_cache_[i];
if (cache.was_set) {
env_->SetVar(cache.variable_name, cache.value);
}
void GoogleAPIKeysTest::TearDown() {
// Restore environment.
for (size_t i = 0; i < arraysize(env_cache_); ++i) {
EnvironmentCache& cache = env_cache_[i];
if (cache.was_set) {
env_->SetVar(cache.variable_name, cache.value);
}
}
}

private:
std::unique_ptr<base::Environment> env_;

// Why 3? It is for GOOGLE_API_KEY, GOOGLE_DEFAULT_CLIENT_ID and
// GOOGLE_DEFAULT_CLIENT_SECRET.
//
// Why 2 times CLIENT_NUM_ITEMS? This is the number of different
// clients in the OAuth2Client enumeration, and for each of these we
// have both an ID and a secret.
EnvironmentCache env_cache_[3 + 2 * google_apis::CLIENT_NUM_ITEMS];
};
// This is the default baked-in value for OAuth IDs and secrets.
static const char kDummyToken[] = "dummytoken";

#if defined(GOOGLE_CHROME_BUILD) || defined(USE_OFFICIAL_GOOGLE_API_KEYS)
// Test official build behavior, since we are in a checkout where this
Expand Down
Loading

0 comments on commit bd01460

Please sign in to comment.