1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "google_apis/google_api_keys.h"
6
7// If you add more includes to this list, you also need to add them to
8// google_api_keys_unittest.cc.
9#include "base/command_line.h"
10#include "base/environment.h"
11#include "base/lazy_instance.h"
12#include "base/logging.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/strings/stringize_macros.h"
15#include "google_apis/gaia/gaia_switches.h"
16
17#if defined(GOOGLE_CHROME_BUILD) || defined(USE_OFFICIAL_GOOGLE_API_KEYS)
18#include "google_apis/internal/google_chrome_api_keys.h"
19#endif
20
21// Used to indicate an unset key/id/secret.  This works better with
22// various unit tests than leaving the token empty.
23#define DUMMY_API_TOKEN "dummytoken"
24
25#if !defined(GOOGLE_API_KEY)
26#define GOOGLE_API_KEY DUMMY_API_TOKEN
27#endif
28
29#if !defined(GOOGLE_CLIENT_ID_MAIN)
30#define GOOGLE_CLIENT_ID_MAIN DUMMY_API_TOKEN
31#endif
32
33#if !defined(GOOGLE_CLIENT_SECRET_MAIN)
34#define GOOGLE_CLIENT_SECRET_MAIN DUMMY_API_TOKEN
35#endif
36
37#if !defined(GOOGLE_CLIENT_ID_CLOUD_PRINT)
38#define GOOGLE_CLIENT_ID_CLOUD_PRINT DUMMY_API_TOKEN
39#endif
40
41#if !defined(GOOGLE_CLIENT_SECRET_CLOUD_PRINT)
42#define GOOGLE_CLIENT_SECRET_CLOUD_PRINT DUMMY_API_TOKEN
43#endif
44
45#if !defined(GOOGLE_CLIENT_ID_REMOTING)
46#define GOOGLE_CLIENT_ID_REMOTING DUMMY_API_TOKEN
47#endif
48
49#if !defined(GOOGLE_CLIENT_SECRET_REMOTING)
50#define GOOGLE_CLIENT_SECRET_REMOTING DUMMY_API_TOKEN
51#endif
52
53#if !defined(GOOGLE_CLIENT_ID_REMOTING_HOST)
54#define GOOGLE_CLIENT_ID_REMOTING_HOST DUMMY_API_TOKEN
55#endif
56
57#if !defined(GOOGLE_CLIENT_SECRET_REMOTING_HOST)
58#define GOOGLE_CLIENT_SECRET_REMOTING_HOST DUMMY_API_TOKEN
59#endif
60
61// These are used as shortcuts for developers and users providing
62// OAuth credentials via preprocessor defines or environment
63// variables.  If set, they will be used to replace any of the client
64// IDs and secrets above that have not been set (and only those; they
65// will not override already-set values).
66#if !defined(GOOGLE_DEFAULT_CLIENT_ID)
67#define GOOGLE_DEFAULT_CLIENT_ID ""
68#endif
69#if !defined(GOOGLE_DEFAULT_CLIENT_SECRET)
70#define GOOGLE_DEFAULT_CLIENT_SECRET ""
71#endif
72
73namespace google_apis {
74
75// This is used as a lazy instance to determine keys once and cache them.
76class APIKeyCache {
77 public:
78  APIKeyCache() {
79    scoped_ptr<base::Environment> environment(base::Environment::Create());
80    CommandLine* command_line = CommandLine::ForCurrentProcess();
81
82    api_key_ = CalculateKeyValue(GOOGLE_API_KEY,
83                                 STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY),
84                                 NULL,
85                                 std::string(),
86                                 environment.get(),
87                                 command_line);
88
89    std::string default_client_id =
90        CalculateKeyValue(GOOGLE_DEFAULT_CLIENT_ID,
91                          STRINGIZE_NO_EXPANSION(GOOGLE_DEFAULT_CLIENT_ID),
92                          NULL,
93                          std::string(),
94                          environment.get(),
95                          command_line);
96    std::string default_client_secret =
97        CalculateKeyValue(GOOGLE_DEFAULT_CLIENT_SECRET,
98                          STRINGIZE_NO_EXPANSION(GOOGLE_DEFAULT_CLIENT_SECRET),
99                          NULL,
100                          std::string(),
101                          environment.get(),
102                          command_line);
103
104    // We currently only allow overriding the baked-in values for the
105    // default OAuth2 client ID and secret using a command-line
106    // argument, since that is useful to enable testing against
107    // staging servers, and since that was what was possible and
108    // likely practiced by the QA team before this implementation was
109    // written.
110    client_ids_[CLIENT_MAIN] = CalculateKeyValue(
111        GOOGLE_CLIENT_ID_MAIN,
112        STRINGIZE_NO_EXPANSION(GOOGLE_CLIENT_ID_MAIN),
113        switches::kOAuth2ClientID,
114        default_client_id,
115        environment.get(),
116        command_line);
117    client_secrets_[CLIENT_MAIN] = CalculateKeyValue(
118        GOOGLE_CLIENT_SECRET_MAIN,
119        STRINGIZE_NO_EXPANSION(GOOGLE_CLIENT_SECRET_MAIN),
120        switches::kOAuth2ClientSecret,
121        default_client_secret,
122        environment.get(),
123        command_line);
124
125    client_ids_[CLIENT_CLOUD_PRINT] = CalculateKeyValue(
126        GOOGLE_CLIENT_ID_CLOUD_PRINT,
127        STRINGIZE_NO_EXPANSION(GOOGLE_CLIENT_ID_CLOUD_PRINT),
128        NULL,
129        default_client_id,
130        environment.get(),
131        command_line);
132    client_secrets_[CLIENT_CLOUD_PRINT] = CalculateKeyValue(
133        GOOGLE_CLIENT_SECRET_CLOUD_PRINT,
134        STRINGIZE_NO_EXPANSION(GOOGLE_CLIENT_SECRET_CLOUD_PRINT),
135        NULL,
136        default_client_secret,
137        environment.get(),
138        command_line);
139
140    client_ids_[CLIENT_REMOTING] = CalculateKeyValue(
141        GOOGLE_CLIENT_ID_REMOTING,
142        STRINGIZE_NO_EXPANSION(GOOGLE_CLIENT_ID_REMOTING),
143        NULL,
144        default_client_id,
145        environment.get(),
146        command_line);
147    client_secrets_[CLIENT_REMOTING] = CalculateKeyValue(
148        GOOGLE_CLIENT_SECRET_REMOTING,
149        STRINGIZE_NO_EXPANSION(GOOGLE_CLIENT_SECRET_REMOTING),
150        NULL,
151        default_client_secret,
152        environment.get(),
153        command_line);
154
155    client_ids_[CLIENT_REMOTING_HOST] = CalculateKeyValue(
156        GOOGLE_CLIENT_ID_REMOTING_HOST,
157        STRINGIZE_NO_EXPANSION(GOOGLE_CLIENT_ID_REMOTING_HOST),
158        NULL,
159        default_client_id,
160        environment.get(),
161        command_line);
162    client_secrets_[CLIENT_REMOTING_HOST] = CalculateKeyValue(
163        GOOGLE_CLIENT_SECRET_REMOTING_HOST,
164        STRINGIZE_NO_EXPANSION(GOOGLE_CLIENT_SECRET_REMOTING_HOST),
165        NULL,
166        default_client_secret,
167        environment.get(),
168        command_line);
169  }
170
171  std::string api_key() const { return api_key_; }
172
173  std::string GetClientID(OAuth2Client client) const {
174    DCHECK_LT(client, CLIENT_NUM_ITEMS);
175    return client_ids_[client];
176  }
177
178  std::string GetClientSecret(OAuth2Client client) const {
179    DCHECK_LT(client, CLIENT_NUM_ITEMS);
180    return client_secrets_[client];
181  }
182
183 private:
184  // Gets a value for a key.  In priority order, this will be the value
185  // provided via a command-line switch, the value provided via an
186  // environment variable, or finally a value baked into the build.
187  // |command_line_switch| may be NULL.
188  static std::string CalculateKeyValue(const char* baked_in_value,
189                                       const char* environment_variable_name,
190                                       const char* command_line_switch,
191                                       const std::string& default_if_unset,
192                                       base::Environment* environment,
193                                       CommandLine* command_line) {
194    std::string key_value = baked_in_value;
195    std::string temp;
196    if (environment->GetVar(environment_variable_name, &temp)) {
197      key_value = temp;
198      VLOG(1) << "Overriding API key " << environment_variable_name
199              << " with value " << key_value << " from environment variable.";
200    }
201
202    if (command_line_switch && command_line->HasSwitch(command_line_switch)) {
203      key_value = command_line->GetSwitchValueASCII(command_line_switch);
204      VLOG(1) << "Overriding API key " << environment_variable_name
205              << " with value " << key_value << " from command-line switch.";
206    }
207
208    if (key_value == DUMMY_API_TOKEN) {
209#if defined(GOOGLE_CHROME_BUILD)
210      // No key should be unset in an official build except the
211      // GOOGLE_DEFAULT_* keys.  The default keys don't trigger this
212      // check as their "unset" value is not DUMMY_API_TOKEN.
213      CHECK(false);
214#endif
215      if (default_if_unset.size() > 0) {
216        VLOG(1) << "Using default value \"" << default_if_unset
217                << "\" for API key " << environment_variable_name;
218        key_value = default_if_unset;
219      }
220    }
221
222    // This should remain a debug-only log.
223    DVLOG(1) << "API key " << environment_variable_name << "=" << key_value;
224
225    return key_value;
226  }
227
228  std::string api_key_;
229  std::string client_ids_[CLIENT_NUM_ITEMS];
230  std::string client_secrets_[CLIENT_NUM_ITEMS];
231};
232
233static base::LazyInstance<APIKeyCache> g_api_key_cache =
234    LAZY_INSTANCE_INITIALIZER;
235
236bool HasKeysConfigured() {
237  if (GetAPIKey() == DUMMY_API_TOKEN)
238    return false;
239
240  for (size_t client_id = 0; client_id < CLIENT_NUM_ITEMS; ++client_id) {
241    OAuth2Client client = static_cast<OAuth2Client>(client_id);
242    if (GetOAuth2ClientID(client) == DUMMY_API_TOKEN ||
243        GetOAuth2ClientSecret(client) == DUMMY_API_TOKEN) {
244      return false;
245    }
246  }
247
248  return true;
249}
250
251std::string GetAPIKey() {
252  return g_api_key_cache.Get().api_key();
253}
254
255std::string GetOAuth2ClientID(OAuth2Client client) {
256  return g_api_key_cache.Get().GetClientID(client);
257}
258
259std::string GetOAuth2ClientSecret(OAuth2Client client) {
260  return g_api_key_cache.Get().GetClientSecret(client);
261}
262
263bool IsGoogleChromeAPIKeyUsed() {
264#if defined(GOOGLE_CHROME_BUILD) || defined(USE_OFFICIAL_GOOGLE_API_KEYS)
265  return true;
266#else
267  return false;
268#endif
269}
270
271}  // namespace google_apis
272