blob: de251faa0454dfcbca5f263730d42d9785816674 [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
21void PinDialogManager::AddSignRequestId(const std::string& extension_id,
22 int sign_request_id) {
23 ExtensionNameRequestIdPair key(extension_id, sign_request_id);
24 // Cache the ID with current timestamp.
Maksim Ivanov86866b72019-07-25 02:15:0625 sign_request_times_[key] = base::Time::Now();
igorcovbb898fb2016-12-01 16:07:5626}
27
Maksim Ivanov86866b72019-07-25 02:15:0628PinDialogManager::RequestPinResult PinDialogManager::RequestPin(
igorcovbb898fb2016-12-01 16:07:5629 const std::string& extension_id,
30 const std::string& extension_name,
31 int sign_request_id,
Maksim Ivanovb2985b12019-08-08 15:23:5832 SecurityTokenPinCodeType code_type,
33 SecurityTokenPinErrorLabel error_label,
igorcovbb898fb2016-12-01 16:07:5634 int attempts_left,
Maksim Ivanov86866b72019-07-25 02:15:0635 RequestPinCallback callback) {
Maksim Ivanov73dfec32019-08-01 22:43:1236 DCHECK_GE(attempts_left, -1);
37 const bool accept_input = (attempts_left != 0);
38
39 // Start from sanity checks, as the extension might have issued this call
40 // incorrectly.
41 if (active_dialog_state_) {
42 // The active dialog exists already, so we need to make sure it belongs to
43 // the same extension and the user submitted some input.
44 if (extension_id != active_dialog_state_->extension_id)
Maksim Ivanov86866b72019-07-25 02:15:0645 return RequestPinResult::kOtherFlowInProgress;
Maksim Ivanov73dfec32019-08-01 22:43:1246 if (active_dialog_state_->request_pin_callback ||
47 active_dialog_state_->stop_pin_request_callback) {
48 // Extension requests a PIN without having received any input from its
49 // previous request. Reject the new request.
Maksim Ivanov86866b72019-07-25 02:15:0650 return RequestPinResult::kDialogDisplayedAlready;
Maksim Ivanov73dfec32019-08-01 22:43:1251 }
52 } else {
53 // Check the validity of sign_request_id
54 const ExtensionNameRequestIdPair key(extension_id, sign_request_id);
55 if (sign_request_times_.find(key) == sign_request_times_.end())
56 return RequestPinResult::kInvalidId;
57 const base::Time current_time = base::Time::Now();
58 if (current_time - sign_request_times_[key] > kSignRequestIdTimeout)
59 return RequestPinResult::kInvalidId;
igorcovbb898fb2016-12-01 16:07:5660
Maksim Ivanov73dfec32019-08-01 22:43:1261 // A new dialog will be opened, so initialize the related internal state.
Maksim Ivanov5fd579032019-08-02 01:15:1562 active_dialog_state_.emplace(GetHostForNewDialog(), extension_id,
Maksim Ivanov73dfec32019-08-01 22:43:1263 extension_name, code_type);
igorcovbb898fb2016-12-01 16:07:5664 }
65
Maksim Ivanov73dfec32019-08-01 22:43:1266 active_dialog_state_->request_pin_callback = std::move(callback);
67 active_dialog_state_->host->ShowSecurityTokenPinDialog(
68 extension_name, code_type, accept_input, error_label, attempts_left,
69 base::BindOnce(&PinDialogManager::OnPinEntered,
70 weak_factory_.GetWeakPtr()),
71 base::BindOnce(&PinDialogManager::OnPinDialogClosed,
72 weak_factory_.GetWeakPtr()));
igorcovbb898fb2016-12-01 16:07:5673
Maksim Ivanov86866b72019-07-25 02:15:0674 return RequestPinResult::kSuccess;
igorcovbb898fb2016-12-01 16:07:5675}
76
Maksim Ivanov86866b72019-07-25 02:15:0677PinDialogManager::StopPinRequestResult
Maksim Ivanovb2985b12019-08-08 15:23:5878PinDialogManager::StopPinRequestWithError(
79 const std::string& extension_id,
80 SecurityTokenPinErrorLabel error_label,
81 StopPinRequestCallback callback) {
82 DCHECK_NE(error_label, SecurityTokenPinErrorLabel::kNone);
Maksim Ivanov73dfec32019-08-01 22:43:1283
84 // Perform sanity checks, as the extension might have issued this call
85 // incorrectly.
86 if (!active_dialog_state_ ||
87 active_dialog_state_->extension_id != extension_id) {
Maksim Ivanov86866b72019-07-25 02:15:0688 return StopPinRequestResult::kNoActiveDialog;
igorcovbb898fb2016-12-01 16:07:5689 }
Maksim Ivanov73dfec32019-08-01 22:43:1290 if (active_dialog_state_->request_pin_callback ||
91 active_dialog_state_->stop_pin_request_callback) {
Maksim Ivanov86866b72019-07-25 02:15:0692 return StopPinRequestResult::kNoUserInput;
Maksim Ivanov73dfec32019-08-01 22:43:1293 }
igorcovbb898fb2016-12-01 16:07:5694
Maksim Ivanov73dfec32019-08-01 22:43:1295 active_dialog_state_->stop_pin_request_callback = std::move(callback);
96 active_dialog_state_->host->ShowSecurityTokenPinDialog(
97 active_dialog_state_->extension_name, active_dialog_state_->code_type,
98 /*enable_user_input=*/false, error_label,
Maksim Ivanov86866b72019-07-25 02:15:0699 /*attempts_left=*/-1,
Maksim Ivanov73dfec32019-08-01 22:43:12100 base::BindOnce(&PinDialogManager::OnPinEntered,
101 weak_factory_.GetWeakPtr()),
102 base::BindOnce(&PinDialogManager::OnPinDialogClosed,
103 weak_factory_.GetWeakPtr()));
104
Maksim Ivanov86866b72019-07-25 02:15:06105 return StopPinRequestResult::kSuccess;
igorcovbb898fb2016-12-01 16:07:56106}
107
Maksim Ivanov86866b72019-07-25 02:15:06108bool PinDialogManager::LastPinDialogClosed(
109 const std::string& extension_id) const {
110 auto iter = last_response_closed_.find(extension_id);
111 return iter != last_response_closed_.end() && iter->second;
igorcovbb898fb2016-12-01 16:07:56112}
113
114bool PinDialogManager::CloseDialog(const std::string& extension_id) {
Maksim Ivanov73dfec32019-08-01 22:43:12115 // Perform sanity checks, as the extension might have issued this call
116 // incorrectly.
117 if (!active_dialog_state_ ||
118 extension_id != active_dialog_state_->extension_id) {
igorcovbb898fb2016-12-01 16:07:56119 LOG(ERROR) << "StopPinRequest called by unexpected extension: "
120 << extension_id;
121 return false;
122 }
123
Maksim Ivanov5ccb96c2019-08-08 13:25:41124 CloseActiveDialog();
igorcovbb898fb2016-12-01 16:07:56125 return true;
126}
127
128void PinDialogManager::ExtensionUnloaded(const std::string& extension_id) {
Maksim Ivanov73dfec32019-08-01 22:43:12129 if (active_dialog_state_ &&
130 active_dialog_state_->extension_id == extension_id) {
Maksim Ivanov5ccb96c2019-08-08 13:25:41131 CloseActiveDialog();
Maksim Ivanov73dfec32019-08-01 22:43:12132 }
igorcovbb898fb2016-12-01 16:07:56133
134 last_response_closed_[extension_id] = false;
135
136 for (auto it = sign_request_times_.cbegin();
137 it != sign_request_times_.cend();) {
Maksim Ivanov86866b72019-07-25 02:15:06138 if (it->first.first == extension_id)
igorcovbb898fb2016-12-01 16:07:56139 sign_request_times_.erase(it++);
Maksim Ivanov86866b72019-07-25 02:15:06140 else
igorcovbb898fb2016-12-01 16:07:56141 ++it;
igorcovbb898fb2016-12-01 16:07:56142 }
143}
144
Maksim Ivanov5fd579032019-08-02 01:15:15145void PinDialogManager::AddPinDialogHost(
146 SecurityTokenPinDialogHost* pin_dialog_host) {
147 DCHECK(!base::Contains(added_dialog_hosts_, pin_dialog_host));
148 added_dialog_hosts_.push_back(pin_dialog_host);
149}
150
151void PinDialogManager::RemovePinDialogHost(
152 SecurityTokenPinDialogHost* pin_dialog_host) {
Maksim Ivanov5ccb96c2019-08-08 13:25:41153 if (active_dialog_state_ && active_dialog_state_->host == pin_dialog_host)
154 CloseActiveDialog();
Maksim Ivanov5fd579032019-08-02 01:15:15155 DCHECK(base::Contains(added_dialog_hosts_, pin_dialog_host));
156 base::Erase(added_dialog_hosts_, pin_dialog_host);
157}
158
Maksim Ivanov73dfec32019-08-01 22:43:12159PinDialogManager::ActiveDialogState::ActiveDialogState(
160 SecurityTokenPinDialogHost* host,
161 const std::string& extension_id,
162 const std::string& extension_name,
Maksim Ivanovb2985b12019-08-08 15:23:58163 SecurityTokenPinCodeType code_type)
Maksim Ivanov73dfec32019-08-01 22:43:12164 : host(host),
165 extension_id(extension_id),
166 extension_name(extension_name),
167 code_type(code_type) {}
Maksim Ivanovab8e8582019-08-01 21:49:13168
Maksim Ivanov73dfec32019-08-01 22:43:12169PinDialogManager::ActiveDialogState::~ActiveDialogState() = default;
170
171void PinDialogManager::OnPinEntered(const std::string& user_input) {
172 DCHECK(!active_dialog_state_->stop_pin_request_callback);
173 last_response_closed_[active_dialog_state_->extension_id] = false;
174 if (active_dialog_state_->request_pin_callback)
175 std::move(active_dialog_state_->request_pin_callback).Run(user_input);
Maksim Ivanovab8e8582019-08-01 21:49:13176}
177
178void PinDialogManager::OnPinDialogClosed() {
Maksim Ivanov73dfec32019-08-01 22:43:12179 DCHECK(!active_dialog_state_->request_pin_callback ||
180 !active_dialog_state_->stop_pin_request_callback);
Maksim Ivanovab8e8582019-08-01 21:49:13181
Maksim Ivanov73dfec32019-08-01 22:43:12182 last_response_closed_[active_dialog_state_->extension_id] = true;
183 if (active_dialog_state_->request_pin_callback) {
184 std::move(active_dialog_state_->request_pin_callback)
185 .Run(/*user_input=*/std::string());
186 }
187 if (active_dialog_state_->stop_pin_request_callback)
188 std::move(active_dialog_state_->stop_pin_request_callback).Run();
189 active_dialog_state_.reset();
Maksim Ivanovab8e8582019-08-01 21:49:13190}
191
Maksim Ivanov5fd579032019-08-02 01:15:15192SecurityTokenPinDialogHost* PinDialogManager::GetHostForNewDialog() {
193 if (added_dialog_hosts_.empty())
194 return &default_dialog_host_;
195 return added_dialog_hosts_.back();
196}
197
Maksim Ivanov5ccb96c2019-08-08 13:25:41198void PinDialogManager::CloseActiveDialog() {
199 if (!active_dialog_state_)
200 return;
201
202 // Ignore any further callbacks from the host. Instead of relying on the host
203 // to call the closing callback, run OnPinDialogClosed() below explicitly.
204 weak_factory_.InvalidateWeakPtrs();
205
206 active_dialog_state_->host->CloseSecurityTokenPinDialog();
207 OnPinDialogClosed();
208 DCHECK(!active_dialog_state_);
209}
210
igorcovbb898fb2016-12-01 16:07:56211} // namespace chromeos