blob: cd143f976d0e40804c2c9ec1f106828cf5f77372 [file] [log] [blame]
igorcovbb898fb2016-12-01 16:07:561// Copyright 2016 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/chromeos/certificate_provider/pin_dialog_manager.h"
6
Maksim Ivanov73dfec32019-08-01 22:43:127#include "base/bind.h"
8#include "base/logging.h"
Maksim Ivanov5fd579032019-08-02 01:15:159#include "base/stl_util.h"
igorcovbb898fb2016-12-01 16:07:5610
11namespace chromeos {
12
13// Define timeout for issued sign_request_id.
Maksim Ivanov86866b72019-07-25 02:15:0614constexpr base::TimeDelta kSignRequestIdTimeout =
15 base::TimeDelta::FromMinutes(10);
igorcovbb898fb2016-12-01 16:07:5616
Maksim Ivanov86866b72019-07-25 02:15:0617PinDialogManager::PinDialogManager() = default;
igorcovbb898fb2016-12-01 16:07:5618
Maksim Ivanov73dfec32019-08-01 22:43:1219PinDialogManager::~PinDialogManager() = default;
igorcovbb898fb2016-12-01 16:07:5620
Maksim Ivanov2b8136f2019-08-09 14:03:5921void PinDialogManager::AddSignRequestId(
22 const std::string& extension_id,
23 int sign_request_id,
24 const base::Optional<AccountId>& authenticating_user_account_id) {
igorcovbb898fb2016-12-01 16:07:5625 ExtensionNameRequestIdPair key(extension_id, sign_request_id);
Maksim Ivanov2b8136f2019-08-09 14:03:5926 sign_requests_.insert(
27 std::make_pair(key, SignRequestState(/*begin_time=*/base::Time::Now(),
28 authenticating_user_account_id)));
igorcovbb898fb2016-12-01 16:07:5629}
30
Maksim Ivanov7652d0d2019-09-04 19:12:1931void PinDialogManager::AbortSignRequest(const std::string& extension_id,
32 int sign_request_id) {
33 if (active_dialog_state_ &&
34 active_dialog_state_->extension_id == extension_id &&
35 active_dialog_state_->sign_request_id == sign_request_id) {
36 CloseActiveDialog();
37 }
38
39 ExtensionNameRequestIdPair key(extension_id, sign_request_id);
40 sign_requests_.erase(key);
41}
42
Maksim Ivanov86866b72019-07-25 02:15:0643PinDialogManager::RequestPinResult PinDialogManager::RequestPin(
igorcovbb898fb2016-12-01 16:07:5644 const std::string& extension_id,
45 const std::string& extension_name,
46 int sign_request_id,
Fabian Sommer195d1f32020-02-27 01:51:0247 security_token_pin::CodeType code_type,
48 security_token_pin::ErrorLabel error_label,
igorcovbb898fb2016-12-01 16:07:5649 int attempts_left,
Maksim Ivanov86866b72019-07-25 02:15:0650 RequestPinCallback callback) {
Maksim Ivanov73dfec32019-08-01 22:43:1251 DCHECK_GE(attempts_left, -1);
52 const bool accept_input = (attempts_left != 0);
53
Maksim Ivanov2b8136f2019-08-09 14:03:5954 // Check the validity of sign_request_id.
55 const SignRequestState* const sign_request_state =
56 FindSignRequestState(extension_id, sign_request_id);
57 if (!sign_request_state)
58 return RequestPinResult::kInvalidId;
59
Maksim Ivanov73dfec32019-08-01 22:43:1260 // Start from sanity checks, as the extension might have issued this call
61 // incorrectly.
62 if (active_dialog_state_) {
63 // The active dialog exists already, so we need to make sure it belongs to
64 // the same extension and the user submitted some input.
65 if (extension_id != active_dialog_state_->extension_id)
Maksim Ivanov86866b72019-07-25 02:15:0666 return RequestPinResult::kOtherFlowInProgress;
Maksim Ivanov73dfec32019-08-01 22:43:1267 if (active_dialog_state_->request_pin_callback ||
68 active_dialog_state_->stop_pin_request_callback) {
69 // Extension requests a PIN without having received any input from its
70 // previous request. Reject the new request.
Maksim Ivanov86866b72019-07-25 02:15:0671 return RequestPinResult::kDialogDisplayedAlready;
Maksim Ivanov73dfec32019-08-01 22:43:1272 }
73 } else {
Maksim Ivanov2b8136f2019-08-09 14:03:5974 // Check that the sign request hasn't timed out yet.
Maksim Ivanov73dfec32019-08-01 22:43:1275 const base::Time current_time = base::Time::Now();
Maksim Ivanov2b8136f2019-08-09 14:03:5976 if (current_time - sign_request_state->begin_time > kSignRequestIdTimeout)
Maksim Ivanov73dfec32019-08-01 22:43:1277 return RequestPinResult::kInvalidId;
igorcovbb898fb2016-12-01 16:07:5678
Maksim Ivanov73dfec32019-08-01 22:43:1279 // A new dialog will be opened, so initialize the related internal state.
Maksim Ivanov5fd579032019-08-02 01:15:1580 active_dialog_state_.emplace(GetHostForNewDialog(), extension_id,
Maksim Ivanov2b8136f2019-08-09 14:03:5981 extension_name, sign_request_id, code_type);
igorcovbb898fb2016-12-01 16:07:5682 }
83
Maksim Ivanov73dfec32019-08-01 22:43:1284 active_dialog_state_->request_pin_callback = std::move(callback);
85 active_dialog_state_->host->ShowSecurityTokenPinDialog(
86 extension_name, code_type, accept_input, error_label, attempts_left,
Maksim Ivanov2b8136f2019-08-09 14:03:5987 sign_request_state->authenticating_user_account_id,
Maksim Ivanov73dfec32019-08-01 22:43:1288 base::BindOnce(&PinDialogManager::OnPinEntered,
89 weak_factory_.GetWeakPtr()),
90 base::BindOnce(&PinDialogManager::OnPinDialogClosed,
91 weak_factory_.GetWeakPtr()));
igorcovbb898fb2016-12-01 16:07:5692
Maksim Ivanov86866b72019-07-25 02:15:0693 return RequestPinResult::kSuccess;
igorcovbb898fb2016-12-01 16:07:5694}
95
Maksim Ivanov86866b72019-07-25 02:15:0696PinDialogManager::StopPinRequestResult
Maksim Ivanovb2985b12019-08-08 15:23:5897PinDialogManager::StopPinRequestWithError(
98 const std::string& extension_id,
Fabian Sommer195d1f32020-02-27 01:51:0299 security_token_pin::ErrorLabel error_label,
Maksim Ivanovb2985b12019-08-08 15:23:58100 StopPinRequestCallback callback) {
Fabian Sommer195d1f32020-02-27 01:51:02101 DCHECK_NE(error_label, security_token_pin::ErrorLabel::kNone);
Maksim Ivanov73dfec32019-08-01 22:43:12102
103 // Perform sanity checks, as the extension might have issued this call
104 // incorrectly.
105 if (!active_dialog_state_ ||
106 active_dialog_state_->extension_id != extension_id) {
Maksim Ivanov86866b72019-07-25 02:15:06107 return StopPinRequestResult::kNoActiveDialog;
igorcovbb898fb2016-12-01 16:07:56108 }
Maksim Ivanov73dfec32019-08-01 22:43:12109 if (active_dialog_state_->request_pin_callback ||
110 active_dialog_state_->stop_pin_request_callback) {
Maksim Ivanov86866b72019-07-25 02:15:06111 return StopPinRequestResult::kNoUserInput;
Maksim Ivanov73dfec32019-08-01 22:43:12112 }
igorcovbb898fb2016-12-01 16:07:56113
Maksim Ivanov2b8136f2019-08-09 14:03:59114 const SignRequestState* const sign_request_state =
115 FindSignRequestState(extension_id, active_dialog_state_->sign_request_id);
116 if (!sign_request_state)
117 return StopPinRequestResult::kNoActiveDialog;
118
Maksim Ivanov73dfec32019-08-01 22:43:12119 active_dialog_state_->stop_pin_request_callback = std::move(callback);
120 active_dialog_state_->host->ShowSecurityTokenPinDialog(
121 active_dialog_state_->extension_name, active_dialog_state_->code_type,
122 /*enable_user_input=*/false, error_label,
Maksim Ivanov2b8136f2019-08-09 14:03:59123 /*attempts_left=*/-1, sign_request_state->authenticating_user_account_id,
Maksim Ivanov73dfec32019-08-01 22:43:12124 base::BindOnce(&PinDialogManager::OnPinEntered,
125 weak_factory_.GetWeakPtr()),
126 base::BindOnce(&PinDialogManager::OnPinDialogClosed,
127 weak_factory_.GetWeakPtr()));
128
Maksim Ivanov86866b72019-07-25 02:15:06129 return StopPinRequestResult::kSuccess;
igorcovbb898fb2016-12-01 16:07:56130}
131
Maksim Ivanov86866b72019-07-25 02:15:06132bool PinDialogManager::LastPinDialogClosed(
133 const std::string& extension_id) const {
134 auto iter = last_response_closed_.find(extension_id);
135 return iter != last_response_closed_.end() && iter->second;
igorcovbb898fb2016-12-01 16:07:56136}
137
138bool PinDialogManager::CloseDialog(const std::string& extension_id) {
Maksim Ivanov73dfec32019-08-01 22:43:12139 // Perform sanity checks, as the extension might have issued this call
140 // incorrectly.
141 if (!active_dialog_state_ ||
142 extension_id != active_dialog_state_->extension_id) {
igorcovbb898fb2016-12-01 16:07:56143 LOG(ERROR) << "StopPinRequest called by unexpected extension: "
144 << extension_id;
145 return false;
146 }
147
Maksim Ivanov5ccb96c2019-08-08 13:25:41148 CloseActiveDialog();
igorcovbb898fb2016-12-01 16:07:56149 return true;
150}
151
152void PinDialogManager::ExtensionUnloaded(const std::string& extension_id) {
Maksim Ivanov73dfec32019-08-01 22:43:12153 if (active_dialog_state_ &&
154 active_dialog_state_->extension_id == extension_id) {
Maksim Ivanov5ccb96c2019-08-08 13:25:41155 CloseActiveDialog();
Maksim Ivanov73dfec32019-08-01 22:43:12156 }
igorcovbb898fb2016-12-01 16:07:56157
158 last_response_closed_[extension_id] = false;
159
Maksim Ivanov2b8136f2019-08-09 14:03:59160 for (auto it = sign_requests_.cbegin(); it != sign_requests_.cend();) {
Maksim Ivanov86866b72019-07-25 02:15:06161 if (it->first.first == extension_id)
Maksim Ivanov2b8136f2019-08-09 14:03:59162 sign_requests_.erase(it++);
Maksim Ivanov86866b72019-07-25 02:15:06163 else
igorcovbb898fb2016-12-01 16:07:56164 ++it;
igorcovbb898fb2016-12-01 16:07:56165 }
166}
167
Maksim Ivanov5fd579032019-08-02 01:15:15168void PinDialogManager::AddPinDialogHost(
169 SecurityTokenPinDialogHost* pin_dialog_host) {
170 DCHECK(!base::Contains(added_dialog_hosts_, pin_dialog_host));
171 added_dialog_hosts_.push_back(pin_dialog_host);
172}
173
174void PinDialogManager::RemovePinDialogHost(
175 SecurityTokenPinDialogHost* pin_dialog_host) {
Maksim Ivanov5ccb96c2019-08-08 13:25:41176 if (active_dialog_state_ && active_dialog_state_->host == pin_dialog_host)
177 CloseActiveDialog();
Maksim Ivanov5fd579032019-08-02 01:15:15178 DCHECK(base::Contains(added_dialog_hosts_, pin_dialog_host));
179 base::Erase(added_dialog_hosts_, pin_dialog_host);
180}
181
Maksim Ivanov2b8136f2019-08-09 14:03:59182PinDialogManager::SignRequestState::SignRequestState(
183 base::Time begin_time,
184 const base::Optional<AccountId>& authenticating_user_account_id)
185 : begin_time(begin_time),
186 authenticating_user_account_id(authenticating_user_account_id) {}
187
188PinDialogManager::SignRequestState::SignRequestState(const SignRequestState&) =
189 default;
190PinDialogManager::SignRequestState& PinDialogManager::SignRequestState::
191operator=(const SignRequestState&) = default;
192
193PinDialogManager::SignRequestState::~SignRequestState() = default;
194
Maksim Ivanov73dfec32019-08-01 22:43:12195PinDialogManager::ActiveDialogState::ActiveDialogState(
196 SecurityTokenPinDialogHost* host,
197 const std::string& extension_id,
198 const std::string& extension_name,
Maksim Ivanov2b8136f2019-08-09 14:03:59199 int sign_request_id,
Fabian Sommer195d1f32020-02-27 01:51:02200 security_token_pin::CodeType code_type)
Maksim Ivanov73dfec32019-08-01 22:43:12201 : host(host),
202 extension_id(extension_id),
203 extension_name(extension_name),
Maksim Ivanov2b8136f2019-08-09 14:03:59204 sign_request_id(sign_request_id),
Maksim Ivanov73dfec32019-08-01 22:43:12205 code_type(code_type) {}
Maksim Ivanovab8e8582019-08-01 21:49:13206
Maksim Ivanov73dfec32019-08-01 22:43:12207PinDialogManager::ActiveDialogState::~ActiveDialogState() = default;
208
Maksim Ivanov2b8136f2019-08-09 14:03:59209PinDialogManager::SignRequestState* PinDialogManager::FindSignRequestState(
210 const std::string& extension_id,
211 int sign_request_id) {
212 const ExtensionNameRequestIdPair key(extension_id, sign_request_id);
213 const auto sign_request_iter = sign_requests_.find(key);
214 if (sign_request_iter == sign_requests_.end())
215 return nullptr;
216 return &sign_request_iter->second;
217}
218
Maksim Ivanov73dfec32019-08-01 22:43:12219void PinDialogManager::OnPinEntered(const std::string& user_input) {
220 DCHECK(!active_dialog_state_->stop_pin_request_callback);
221 last_response_closed_[active_dialog_state_->extension_id] = false;
222 if (active_dialog_state_->request_pin_callback)
223 std::move(active_dialog_state_->request_pin_callback).Run(user_input);
Maksim Ivanovab8e8582019-08-01 21:49:13224}
225
226void PinDialogManager::OnPinDialogClosed() {
Maksim Ivanov73dfec32019-08-01 22:43:12227 DCHECK(!active_dialog_state_->request_pin_callback ||
228 !active_dialog_state_->stop_pin_request_callback);
Maksim Ivanovab8e8582019-08-01 21:49:13229
Maksim Ivanov73dfec32019-08-01 22:43:12230 last_response_closed_[active_dialog_state_->extension_id] = true;
231 if (active_dialog_state_->request_pin_callback) {
232 std::move(active_dialog_state_->request_pin_callback)
233 .Run(/*user_input=*/std::string());
234 }
235 if (active_dialog_state_->stop_pin_request_callback)
236 std::move(active_dialog_state_->stop_pin_request_callback).Run();
237 active_dialog_state_.reset();
Maksim Ivanovab8e8582019-08-01 21:49:13238}
239
Maksim Ivanov5fd579032019-08-02 01:15:15240SecurityTokenPinDialogHost* PinDialogManager::GetHostForNewDialog() {
241 if (added_dialog_hosts_.empty())
242 return &default_dialog_host_;
243 return added_dialog_hosts_.back();
244}
245
Maksim Ivanov5ccb96c2019-08-08 13:25:41246void PinDialogManager::CloseActiveDialog() {
247 if (!active_dialog_state_)
248 return;
249
250 // Ignore any further callbacks from the host. Instead of relying on the host
251 // to call the closing callback, run OnPinDialogClosed() below explicitly.
252 weak_factory_.InvalidateWeakPtrs();
253
254 active_dialog_state_->host->CloseSecurityTokenPinDialog();
255 OnPinDialogClosed();
256 DCHECK(!active_dialog_state_);
257}
258
igorcovbb898fb2016-12-01 16:07:56259} // namespace chromeos