1// Copyright 2014 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 "chrome/browser/signin/easy_unlock_toggle_flow.h"
6
7#include <vector>
8
9#include "base/logging.h"
10#include "base/strings/stringprintf.h"
11#include "chrome/browser/extensions/extension_service.h"
12#include "chrome/browser/profiles/profile.h"
13#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
14#include "chrome/browser/signin/signin_manager_factory.h"
15#include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h"
16#include "chrome/common/extensions/extension_constants.h"
17#include "components/signin/core/browser/profile_oauth2_token_service.h"
18#include "components/signin/core/browser/signin_manager.h"
19#include "extensions/browser/extension_system.h"
20#include "google_apis/gaia/oauth2_api_call_flow.h"
21#include "net/url_request/url_fetcher.h"
22
23namespace {
24
25const char kEasyUnlockToggleUrl[] =
26    "https://www.googleapis.com/cryptauth/v1/deviceSync/toggleeasyunlock";
27
28std::vector<std::string> GetScopes() {
29  std::vector<std::string> scopes;
30  scopes.push_back("https://www.googleapis.com/auth/proximity_auth");
31  scopes.push_back("https://www.googleapis.com/auth/cryptauth");
32  return scopes;
33}
34
35std::string GetEasyUnlockAppClientId(Profile * profile) {
36  extensions::ExtensionSystem* extension_system =
37      extensions::ExtensionSystem::Get(profile);
38  ExtensionService* extension_service = extension_system->extension_service();
39  const extensions::Extension* easy_unlock_app =
40      extension_service->GetInstalledExtension(
41          extension_misc::kEasyUnlockAppId);
42  if (!easy_unlock_app)
43    return std::string();
44
45  const extensions::OAuth2Info& oauth2_info =
46      extensions::OAuth2Info::GetOAuth2Info(easy_unlock_app);
47  return oauth2_info.client_id;
48}
49
50}  // namespace
51
52class EasyUnlockToggleFlow::ToggleApiCall : public OAuth2ApiCallFlow {
53 public:
54  ToggleApiCall(EasyUnlockToggleFlow* flow,
55                net::URLRequestContextGetter* context,
56                const std::string& access_token,
57                const std::string& phone_public_key,
58                bool toggle_enable);
59  virtual ~ToggleApiCall();
60
61  // OAuth2ApiCallFlow
62  virtual GURL CreateApiCallUrl() OVERRIDE;
63  virtual std::string CreateApiCallBody() OVERRIDE;
64  virtual std::string CreateApiCallBodyContentType() OVERRIDE;
65  virtual void ProcessApiCallSuccess(const net::URLFetcher* source) OVERRIDE;
66  virtual void ProcessApiCallFailure(const net::URLFetcher* source) OVERRIDE;
67  virtual void ProcessNewAccessToken(const std::string& access_token) OVERRIDE;
68  virtual void ProcessMintAccessTokenFailure(
69      const GoogleServiceAuthError& error) OVERRIDE;
70
71 private:
72  EasyUnlockToggleFlow* flow_;
73  const std::string phone_public_key_;
74  const bool toggle_enable_;
75
76  DISALLOW_COPY_AND_ASSIGN(ToggleApiCall);
77};
78
79EasyUnlockToggleFlow::ToggleApiCall::ToggleApiCall(
80    EasyUnlockToggleFlow* flow,
81    net::URLRequestContextGetter* context,
82    const std::string& access_token,
83    const std::string& phone_public_key,
84    bool toggle_enable)
85    : OAuth2ApiCallFlow(context,
86                        std::string(),
87                        access_token,
88                        GetScopes()),
89      flow_(flow),
90      phone_public_key_(phone_public_key),
91      toggle_enable_(toggle_enable) {
92}
93
94EasyUnlockToggleFlow::ToggleApiCall::~ToggleApiCall() {
95}
96
97GURL EasyUnlockToggleFlow::ToggleApiCall::CreateApiCallUrl() {
98  return GURL(kEasyUnlockToggleUrl);
99}
100
101std::string EasyUnlockToggleFlow::ToggleApiCall::CreateApiCallBody() {
102  const char kBodyFormat[] = "{\"enable\":%s,\"publicKey\":\"%s\"}";
103  return base::StringPrintf(
104      kBodyFormat,
105      toggle_enable_ ? "true" : "false",
106      phone_public_key_.c_str());
107}
108
109std::string
110EasyUnlockToggleFlow::ToggleApiCall::CreateApiCallBodyContentType() {
111  return "application/json";
112}
113
114void EasyUnlockToggleFlow::ToggleApiCall::ProcessApiCallSuccess(
115    const net::URLFetcher* source) {
116  flow_->ReportToggleApiCallResult(true);
117}
118
119void EasyUnlockToggleFlow::ToggleApiCall::ProcessApiCallFailure(
120    const net::URLFetcher* source) {
121  flow_->ReportToggleApiCallResult(false);
122}
123
124void EasyUnlockToggleFlow::ToggleApiCall::ProcessNewAccessToken(
125    const std::string& access_token) {
126  NOTREACHED();
127}
128
129void EasyUnlockToggleFlow::ToggleApiCall::ProcessMintAccessTokenFailure(
130    const GoogleServiceAuthError& error) {
131  NOTREACHED();
132}
133
134EasyUnlockToggleFlow::EasyUnlockToggleFlow(Profile* profile,
135                                           const std::string& phone_public_key,
136                                           bool toggle_enable,
137                                           const ToggleFlowCallback& callback)
138    : OAuth2TokenService::Consumer("easy_unlock_toggle"),
139      profile_(profile),
140      phone_public_key_(phone_public_key),
141      toggle_enable_(toggle_enable),
142      callback_(callback) {
143}
144
145EasyUnlockToggleFlow::~EasyUnlockToggleFlow() {
146}
147
148void EasyUnlockToggleFlow::Start() {
149  ProfileOAuth2TokenService* token_service =
150      ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
151  SigninManagerBase* signin_manager =
152      SigninManagerFactory::GetForProfile(profile_);
153  token_request_ =
154      token_service->StartRequest(signin_manager->GetAuthenticatedAccountId(),
155                                  OAuth2TokenService::ScopeSet(),
156                                  this);
157}
158
159void EasyUnlockToggleFlow::OnGetTokenSuccess(
160    const OAuth2TokenService::Request* request,
161    const std::string& access_token,
162    const base::Time& expiration_time) {
163  DCHECK_EQ(token_request_.get(), request);
164  token_request_.reset();
165
166  mint_token_flow_.reset(
167      new OAuth2MintTokenFlow(profile_->GetRequestContext(),
168                              this,
169                              OAuth2MintTokenFlow::Parameters(
170                                  access_token,
171                                  extension_misc::kEasyUnlockAppId,
172                                  GetEasyUnlockAppClientId(profile_),
173                                  GetScopes(),
174                                  OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE)));
175  mint_token_flow_->Start();
176}
177
178void EasyUnlockToggleFlow::OnGetTokenFailure(
179    const OAuth2TokenService::Request* request,
180    const GoogleServiceAuthError& error) {
181  DCHECK_EQ(token_request_.get(), request);
182  token_request_.reset();
183
184  LOG(ERROR) << "Easy unlock toggle flow, failed to get access token,"
185             << "error=" << error.state();
186  callback_.Run(false);
187}
188
189void EasyUnlockToggleFlow::OnMintTokenSuccess(const std::string& access_token,
190                                              int time_to_live) {
191  toggle_api_call_.reset(new ToggleApiCall(this,
192                                           profile_->GetRequestContext(),
193                                           access_token,
194                                           phone_public_key_,
195                                           toggle_enable_));
196  toggle_api_call_->Start();
197}
198
199void EasyUnlockToggleFlow::OnMintTokenFailure(
200    const GoogleServiceAuthError& error) {
201  LOG(ERROR) << "Easy unlock toggle flow, failed to mint access token,"
202             << "error=" << error.state();
203  callback_.Run(false);
204}
205
206void EasyUnlockToggleFlow::OnIssueAdviceSuccess(
207    const IssueAdviceInfo& issue_advice) {
208  NOTREACHED();
209}
210
211void EasyUnlockToggleFlow::ReportToggleApiCallResult(bool success) {
212  callback_.Run(success);
213}
214