1// Copyright 2013 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 "components/policy/core/common/cloud/cloud_policy_client_registration_helper.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/logging.h"
10#include "base/time/time.h"
11#include "base/values.h"
12#include "google_apis/gaia/gaia_constants.h"
13#include "google_apis/gaia/gaia_urls.h"
14#include "google_apis/gaia/google_service_auth_error.h"
15#include "google_apis/gaia/oauth2_token_service.h"
16#include "net/url_request/url_request_context_getter.h"
17
18#if !defined(OS_ANDROID)
19#include "google_apis/gaia/oauth2_access_token_consumer.h"
20#include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
21#endif
22
23namespace policy {
24
25// The key under which the hosted-domain value is stored in the UserInfo
26// response.
27const char kGetHostedDomainKey[] = "hd";
28
29typedef base::Callback<void(const std::string&)> StringCallback;
30
31// This class fetches an OAuth2 token scoped for the userinfo and DM services.
32// On Android, we use a special API to allow us to fetch a token for an account
33// that is not yet logged in to allow fetching the token before the sign-in
34// process is finished.
35class CloudPolicyClientRegistrationHelper::TokenServiceHelper
36    : public OAuth2TokenService::Consumer {
37 public:
38  TokenServiceHelper();
39
40  void FetchAccessToken(
41      OAuth2TokenService* token_service,
42      const std::string& username,
43      const StringCallback& callback);
44
45 private:
46  // OAuth2TokenService::Consumer implementation:
47  virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
48                                 const std::string& access_token,
49                                 const base::Time& expiration_time) OVERRIDE;
50  virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
51                                 const GoogleServiceAuthError& error) OVERRIDE;
52
53  StringCallback callback_;
54  scoped_ptr<OAuth2TokenService::Request> token_request_;
55};
56
57CloudPolicyClientRegistrationHelper::TokenServiceHelper::TokenServiceHelper()
58    : OAuth2TokenService::Consumer("cloud_policy") {}
59
60void CloudPolicyClientRegistrationHelper::TokenServiceHelper::FetchAccessToken(
61    OAuth2TokenService* token_service,
62    const std::string& account_id,
63    const StringCallback& callback) {
64  DCHECK(!token_request_);
65  // Either the caller must supply a username, or the user must be signed in
66  // already.
67  DCHECK(!account_id.empty());
68  DCHECK(token_service->RefreshTokenIsAvailable(account_id));
69
70  callback_ = callback;
71
72  OAuth2TokenService::ScopeSet scopes;
73  scopes.insert(GaiaConstants::kDeviceManagementServiceOAuth);
74  scopes.insert(GaiaConstants::kOAuthWrapBridgeUserInfoScope);
75  token_request_ = token_service->StartRequest(account_id, scopes, this);
76}
77
78void CloudPolicyClientRegistrationHelper::TokenServiceHelper::OnGetTokenSuccess(
79    const OAuth2TokenService::Request* request,
80    const std::string& access_token,
81    const base::Time& expiration_time) {
82  DCHECK_EQ(token_request_.get(), request);
83  callback_.Run(access_token);
84}
85
86void CloudPolicyClientRegistrationHelper::TokenServiceHelper::OnGetTokenFailure(
87    const OAuth2TokenService::Request* request,
88    const GoogleServiceAuthError& error) {
89  DCHECK_EQ(token_request_.get(), request);
90  callback_.Run("");
91}
92
93#if !defined(OS_ANDROID)
94// This class fetches the OAuth2 token scoped for the userinfo and DM services.
95// It uses an OAuth2AccessTokenFetcher to fetch it, given a login refresh token
96// that can be used to authorize that request. This class is not needed on
97// Android because we can use OAuth2TokenService to fetch tokens for accounts
98// even before they are signed in.
99class CloudPolicyClientRegistrationHelper::LoginTokenHelper
100    : public OAuth2AccessTokenConsumer {
101 public:
102  LoginTokenHelper();
103
104  void FetchAccessToken(const std::string& login_refresh_token,
105                        net::URLRequestContextGetter* context,
106                        const StringCallback& callback);
107
108 private:
109  // OAuth2AccessTokenConsumer implementation:
110  virtual void OnGetTokenSuccess(const std::string& access_token,
111                                 const base::Time& expiration_time) OVERRIDE;
112  virtual void OnGetTokenFailure(
113      const GoogleServiceAuthError& error) OVERRIDE;
114
115  StringCallback callback_;
116  scoped_ptr<OAuth2AccessTokenFetcher> oauth2_access_token_fetcher_;
117};
118
119CloudPolicyClientRegistrationHelper::LoginTokenHelper::LoginTokenHelper() {}
120
121void CloudPolicyClientRegistrationHelper::LoginTokenHelper::FetchAccessToken(
122    const std::string& login_refresh_token,
123    net::URLRequestContextGetter* context,
124    const StringCallback& callback) {
125  DCHECK(!oauth2_access_token_fetcher_);
126  callback_ = callback;
127
128  // Start fetching an OAuth2 access token for the device management and
129  // userinfo services.
130  oauth2_access_token_fetcher_.reset(
131      new OAuth2AccessTokenFetcherImpl(this, context, login_refresh_token));
132  GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
133  oauth2_access_token_fetcher_->Start(
134      gaia_urls->oauth2_chrome_client_id(),
135      gaia_urls->oauth2_chrome_client_secret(),
136      GetScopes());
137}
138
139void CloudPolicyClientRegistrationHelper::LoginTokenHelper::OnGetTokenSuccess(
140    const std::string& access_token,
141    const base::Time& expiration_time) {
142  callback_.Run(access_token);
143}
144
145void CloudPolicyClientRegistrationHelper::LoginTokenHelper::OnGetTokenFailure(
146    const GoogleServiceAuthError& error) {
147  callback_.Run("");
148}
149
150#endif
151
152CloudPolicyClientRegistrationHelper::CloudPolicyClientRegistrationHelper(
153    CloudPolicyClient* client,
154    enterprise_management::DeviceRegisterRequest::Type registration_type)
155    : context_(client->GetRequestContext()),
156      client_(client),
157      registration_type_(registration_type) {
158  DCHECK(context_.get());
159  DCHECK(client_);
160}
161
162CloudPolicyClientRegistrationHelper::~CloudPolicyClientRegistrationHelper() {
163  // Clean up any pending observers in case the browser is shutdown while
164  // trying to register for policy.
165  if (client_)
166    client_->RemoveObserver(this);
167}
168
169
170void CloudPolicyClientRegistrationHelper::StartRegistration(
171    OAuth2TokenService* token_service,
172    const std::string& account_id,
173    const base::Closure& callback) {
174  DVLOG(1) << "Starting registration process with username";
175  DCHECK(!client_->is_registered());
176  callback_ = callback;
177  client_->AddObserver(this);
178
179  token_service_helper_.reset(new TokenServiceHelper());
180  token_service_helper_->FetchAccessToken(
181      token_service,
182      account_id,
183      base::Bind(&CloudPolicyClientRegistrationHelper::OnTokenFetched,
184                 base::Unretained(this)));
185}
186
187#if !defined(OS_ANDROID)
188void CloudPolicyClientRegistrationHelper::StartRegistrationWithLoginToken(
189    const std::string& login_refresh_token,
190    const base::Closure& callback) {
191  DVLOG(1) << "Starting registration process with login token";
192  DCHECK(!client_->is_registered());
193  callback_ = callback;
194  client_->AddObserver(this);
195
196  login_token_helper_.reset(
197      new CloudPolicyClientRegistrationHelper::LoginTokenHelper());
198  login_token_helper_->FetchAccessToken(
199      login_refresh_token,
200      context_.get(),
201      base::Bind(&CloudPolicyClientRegistrationHelper::OnTokenFetched,
202                 base::Unretained(this)));
203}
204
205void CloudPolicyClientRegistrationHelper::StartRegistrationWithAccessToken(
206    const std::string& access_token,
207    const base::Closure& callback) {
208  DCHECK(!client_->is_registered());
209  callback_ = callback;
210  client_->AddObserver(this);
211  OnTokenFetched(access_token);
212}
213
214// static
215std::vector<std::string>
216CloudPolicyClientRegistrationHelper::GetScopes() {
217  std::vector<std::string> scopes;
218  scopes.push_back(GaiaConstants::kDeviceManagementServiceOAuth);
219  scopes.push_back(GaiaConstants::kOAuthWrapBridgeUserInfoScope);
220  return scopes;
221}
222#endif
223
224void CloudPolicyClientRegistrationHelper::OnTokenFetched(
225    const std::string& access_token) {
226#if !defined(OS_ANDROID)
227  login_token_helper_.reset();
228#endif
229  token_service_helper_.reset();
230
231  if (access_token.empty()) {
232    DLOG(WARNING) << "Could not fetch access token for "
233                  << GaiaConstants::kDeviceManagementServiceOAuth;
234    RequestCompleted();
235    return;
236  }
237
238  // Cache the access token to be used after the GetUserInfo call.
239  oauth_access_token_ = access_token;
240  DVLOG(1) << "Fetched new scoped OAuth token:" << oauth_access_token_;
241  // Now we've gotten our access token - contact GAIA to see if this is a
242  // hosted domain.
243  user_info_fetcher_.reset(new UserInfoFetcher(this, context_.get()));
244  user_info_fetcher_->Start(oauth_access_token_);
245}
246
247void CloudPolicyClientRegistrationHelper::OnGetUserInfoFailure(
248    const GoogleServiceAuthError& error) {
249  DVLOG(1) << "Failed to fetch user info from GAIA: " << error.state();
250  user_info_fetcher_.reset();
251  RequestCompleted();
252}
253
254void CloudPolicyClientRegistrationHelper::OnGetUserInfoSuccess(
255    const base::DictionaryValue* data) {
256  user_info_fetcher_.reset();
257  if (!data->HasKey(kGetHostedDomainKey)) {
258    DVLOG(1) << "User not from a hosted domain - skipping registration";
259    RequestCompleted();
260    return;
261  }
262  DVLOG(1) << "Registering CloudPolicyClient for user from hosted domain";
263  // The user is from a hosted domain, so it's OK to register the
264  // CloudPolicyClient and make requests to DMServer.
265  if (client_->is_registered()) {
266    // Client should not be registered yet.
267    NOTREACHED();
268    RequestCompleted();
269    return;
270  }
271
272  // Kick off registration of the CloudPolicyClient with our newly minted
273  // oauth_access_token_.
274  client_->Register(registration_type_, oauth_access_token_,
275                    std::string(), false, std::string(), std::string());
276}
277
278void CloudPolicyClientRegistrationHelper::OnPolicyFetched(
279    CloudPolicyClient* client) {
280  // Ignored.
281}
282
283void CloudPolicyClientRegistrationHelper::OnRegistrationStateChanged(
284    CloudPolicyClient* client) {
285  DVLOG(1) << "Client registration succeeded";
286  DCHECK_EQ(client, client_);
287  DCHECK(client->is_registered());
288  RequestCompleted();
289}
290
291void CloudPolicyClientRegistrationHelper::OnClientError(
292    CloudPolicyClient* client) {
293  DVLOG(1) << "Client registration failed";
294  DCHECK_EQ(client, client_);
295  RequestCompleted();
296}
297
298void CloudPolicyClientRegistrationHelper::RequestCompleted() {
299  if (client_) {
300    client_->RemoveObserver(this);
301    // |client_| may be freed by the callback so clear it now.
302    client_ = NULL;
303    callback_.Run();
304  }
305}
306
307}  // namespace policy
308