1 // Copyright 2017 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/ui/webui/settings/chromeos/fingerprint_handler.h"
6
7 #include <algorithm>
8 #include <memory>
9
10 #include "base/bind.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "chrome/browser/chromeos/login/quick_unlock/auth_token.h"
16 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h"
17 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h"
18 #include "chrome/browser/chromeos/profiles/profile_helper.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/common/pref_names.h"
21 #include "chrome/grit/generated_resources.h"
22 #include "components/prefs/pref_service.h"
23 #include "content/public/browser/device_service.h"
24 #include "ui/base/l10n/l10n_util.h"
25
26 using session_manager::SessionManager;
27 using session_manager::SessionState;
28
29 namespace chromeos {
30 namespace settings {
31 namespace {
32
33 // The max number of fingerprints that can be stored.
34 constexpr int kMaxAllowedFingerprints = 3;
35
GetFingerprintsInfo(const std::vector<std::string> & fingerprints_list)36 std::unique_ptr<base::DictionaryValue> GetFingerprintsInfo(
37 const std::vector<std::string>& fingerprints_list) {
38 auto response = std::make_unique<base::DictionaryValue>();
39 auto fingerprints = std::make_unique<base::ListValue>();
40
41 DCHECK_LE(static_cast<int>(fingerprints_list.size()),
42 kMaxAllowedFingerprints);
43 for (auto& fingerprint_name: fingerprints_list) {
44 std::unique_ptr<base::Value> str =
45 std::make_unique<base::Value>(fingerprint_name);
46 fingerprints->Append(std::move(str));
47 }
48
49 response->Set("fingerprintsList", std::move(fingerprints));
50 response->SetBoolean("isMaxed", static_cast<int>(fingerprints_list.size()) >=
51 kMaxAllowedFingerprints);
52 return response;
53 }
54
55 } // namespace
56
FingerprintHandler(Profile * profile)57 FingerprintHandler::FingerprintHandler(Profile* profile) : profile_(profile) {
58 content::GetDeviceService().BindFingerprint(
59 fp_service_.BindNewPipeAndPassReceiver());
60 user_id_ = ProfileHelper::Get()->GetUserIdHashFromProfile(profile);
61 }
62
~FingerprintHandler()63 FingerprintHandler::~FingerprintHandler() {
64 }
65
RegisterMessages()66 void FingerprintHandler::RegisterMessages() {
67 // Note: getFingerprintsList must be called before observers will be added.
68 web_ui()->RegisterMessageCallback(
69 "getFingerprintsList",
70 base::BindRepeating(&FingerprintHandler::HandleGetFingerprintsList,
71 base::Unretained(this)));
72 web_ui()->RegisterMessageCallback(
73 "getNumFingerprints",
74 base::BindRepeating(&FingerprintHandler::HandleGetNumFingerprints,
75 base::Unretained(this)));
76 web_ui()->RegisterMessageCallback(
77 "startEnroll", base::BindRepeating(&FingerprintHandler::HandleStartEnroll,
78 base::Unretained(this)));
79 web_ui()->RegisterMessageCallback(
80 "cancelCurrentEnroll",
81 base::BindRepeating(&FingerprintHandler::HandleCancelCurrentEnroll,
82 base::Unretained(this)));
83 web_ui()->RegisterMessageCallback(
84 "getEnrollmentLabel",
85 base::BindRepeating(&FingerprintHandler::HandleGetEnrollmentLabel,
86 base::Unretained(this)));
87 web_ui()->RegisterMessageCallback(
88 "removeEnrollment",
89 base::BindRepeating(&FingerprintHandler::HandleRemoveEnrollment,
90 base::Unretained(this)));
91 web_ui()->RegisterMessageCallback(
92 "changeEnrollmentLabel",
93 base::BindRepeating(&FingerprintHandler::HandleChangeEnrollmentLabel,
94 base::Unretained(this)));
95 web_ui()->RegisterMessageCallback(
96 "startAuthentication",
97 base::BindRepeating(&FingerprintHandler::HandleStartAuthentication,
98 base::Unretained(this)));
99 web_ui()->RegisterMessageCallback(
100 "endCurrentAuthentication",
101 base::BindRepeating(&FingerprintHandler::HandleEndCurrentAuthentication,
102 base::Unretained(this)));
103 }
104
OnJavascriptAllowed()105 void FingerprintHandler::OnJavascriptAllowed() {
106 // SessionManager may not exist in some tests.
107 if (SessionManager::Get())
108 session_observer_.Add(SessionManager::Get());
109
110 fp_service_->AddFingerprintObserver(receiver_.BindNewPipeAndPassRemote());
111 }
112
OnJavascriptDisallowed()113 void FingerprintHandler::OnJavascriptDisallowed() {
114 session_observer_.RemoveAll();
115 receiver_.reset();
116 }
117
OnRestarted()118 void FingerprintHandler::OnRestarted() {}
119
OnEnrollScanDone(device::mojom::ScanResult scan_result,bool enroll_session_complete,int percent_complete)120 void FingerprintHandler::OnEnrollScanDone(device::mojom::ScanResult scan_result,
121 bool enroll_session_complete,
122 int percent_complete) {
123 VLOG(1) << "Receive fingerprint enroll scan result. scan_result="
124 << scan_result
125 << ", enroll_session_complete=" << enroll_session_complete
126 << ", percent_complete=" << percent_complete;
127 auto scan_attempt = std::make_unique<base::DictionaryValue>();
128 scan_attempt->SetInteger("result", static_cast<int>(scan_result));
129 scan_attempt->SetBoolean("isComplete", enroll_session_complete);
130 scan_attempt->SetInteger("percentComplete", percent_complete);
131
132 FireWebUIListener("on-fingerprint-scan-received", *scan_attempt);
133 }
134
OnAuthScanDone(device::mojom::ScanResult scan_result,const base::flat_map<std::string,std::vector<std::string>> & matches)135 void FingerprintHandler::OnAuthScanDone(
136 device::mojom::ScanResult scan_result,
137 const base::flat_map<std::string, std::vector<std::string>>& matches) {
138 VLOG(1) << "Receive fingerprint auth scan result. scan_result="
139 << scan_result;
140 if (SessionManager::Get()->session_state() == SessionState::LOCKED)
141 return;
142
143 // When the user touches the sensor, highlight the label(s) that finger is
144 // associated with, if it is registered with this user.
145 auto it = matches.find(user_id_);
146 if (it == matches.end() || it->second.size() < 1)
147 return;
148
149 auto fingerprint_ids = std::make_unique<base::ListValue>();
150
151 for (const std::string& matched_path : it->second) {
152 auto path_it = std::find(fingerprints_paths_.begin(),
153 fingerprints_paths_.end(), matched_path);
154 DCHECK(path_it != fingerprints_paths_.end());
155 fingerprint_ids->AppendInteger(
156 static_cast<int>(path_it - fingerprints_paths_.begin()));
157 }
158
159 auto fingerprint_attempt = std::make_unique<base::DictionaryValue>();
160 fingerprint_attempt->SetInteger("result", static_cast<int>(scan_result));
161 fingerprint_attempt->Set("indexes", std::move(fingerprint_ids));
162
163 FireWebUIListener("on-fingerprint-attempt-received", *fingerprint_attempt);
164 }
165
OnSessionFailed()166 void FingerprintHandler::OnSessionFailed() {
167 LOG(ERROR) << "Fingerprint session failed.";
168 }
169
OnSessionStateChanged()170 void FingerprintHandler::OnSessionStateChanged() {
171 SessionState state = SessionManager::Get()->session_state();
172
173 FireWebUIListener("on-screen-locked",
174 base::Value(state == SessionState::LOCKED));
175 }
176
HandleGetFingerprintsList(const base::ListValue * args)177 void FingerprintHandler::HandleGetFingerprintsList(
178 const base::ListValue* args) {
179 CHECK_EQ(1U, args->GetSize());
180 std::string callback_id;
181 CHECK(args->GetString(0, &callback_id));
182
183 AllowJavascript();
184 fp_service_->GetRecordsForUser(
185 user_id_, base::Bind(&FingerprintHandler::OnGetFingerprintsList,
186 weak_ptr_factory_.GetWeakPtr(), callback_id));
187 }
188
OnGetFingerprintsList(const std::string & callback_id,const base::flat_map<std::string,std::string> & fingerprints_list_mapping)189 void FingerprintHandler::OnGetFingerprintsList(
190 const std::string& callback_id,
191 const base::flat_map<std::string, std::string>& fingerprints_list_mapping) {
192 fingerprints_labels_.clear();
193 fingerprints_paths_.clear();
194 for (auto it = fingerprints_list_mapping.begin();
195 it != fingerprints_list_mapping.end(); ++it) {
196 fingerprints_paths_.push_back(it->first);
197 fingerprints_labels_.push_back(it->second);
198 }
199
200 profile_->GetPrefs()->SetInteger(prefs::kQuickUnlockFingerprintRecord,
201 fingerprints_list_mapping.size());
202
203 std::unique_ptr<base::DictionaryValue> fingerprint_info =
204 GetFingerprintsInfo(fingerprints_labels_);
205 ResolveJavascriptCallback(base::Value(callback_id), *fingerprint_info);
206 }
207
HandleGetNumFingerprints(const base::ListValue * args)208 void FingerprintHandler::HandleGetNumFingerprints(const base::ListValue* args) {
209 CHECK_EQ(1U, args->GetSize());
210 std::string callback_id;
211 CHECK(args->GetString(0, &callback_id));
212
213 int fingerprints_num =
214 profile_->GetPrefs()->GetInteger(prefs::kQuickUnlockFingerprintRecord);
215
216 AllowJavascript();
217 ResolveJavascriptCallback(base::Value(callback_id),
218 base::Value(fingerprints_num));
219 }
220
HandleStartEnroll(const base::ListValue * args)221 void FingerprintHandler::HandleStartEnroll(const base::ListValue* args) {
222 AllowJavascript();
223
224 std::string auth_token;
225 CHECK(args->GetString(0, &auth_token));
226
227 // Auth token expiration will trigger password prompt.
228 // Silently fail if auth token is incorrect.
229 quick_unlock::QuickUnlockStorage* quick_unlock_storage =
230 quick_unlock::QuickUnlockFactory::GetForProfile(profile_);
231 if (!quick_unlock_storage->GetAuthToken())
232 return;
233 if (auth_token != quick_unlock_storage->GetAuthToken()->Identifier())
234 return;
235
236 // Determines what the newly added fingerprint's name should be.
237 for (int i = 1; i <= kMaxAllowedFingerprints; ++i) {
238 std::string fingerprint_name = l10n_util::GetStringFUTF8(
239 IDS_SETTINGS_PEOPLE_LOCK_SCREEN_NEW_FINGERPRINT_DEFAULT_NAME,
240 base::NumberToString16(i));
241 if (!base::Contains(fingerprints_labels_, fingerprint_name)) {
242 fp_service_->StartEnrollSession(user_id_, fingerprint_name);
243 break;
244 }
245 }
246 }
247
HandleCancelCurrentEnroll(const base::ListValue * args)248 void FingerprintHandler::HandleCancelCurrentEnroll(
249 const base::ListValue* args) {
250 AllowJavascript();
251 fp_service_->CancelCurrentEnrollSession(
252 base::Bind(&FingerprintHandler::OnCancelCurrentEnrollSession,
253 weak_ptr_factory_.GetWeakPtr()));
254 }
255
OnCancelCurrentEnrollSession(bool success)256 void FingerprintHandler::OnCancelCurrentEnrollSession(bool success) {
257 if (!success)
258 LOG(ERROR) << "Failed to cancel current fingerprint enroll session.";
259 }
260
HandleGetEnrollmentLabel(const base::ListValue * args)261 void FingerprintHandler::HandleGetEnrollmentLabel(const base::ListValue* args) {
262 CHECK_EQ(2U, args->GetSize());
263 std::string callback_id;
264 int index;
265 CHECK(args->GetString(0, &callback_id));
266 CHECK(args->GetInteger(1, &index));
267 DCHECK_LT(index, static_cast<int>(fingerprints_labels_.size()));
268
269 AllowJavascript();
270 fp_service_->RequestRecordLabel(
271 fingerprints_paths_[index],
272 base::Bind(&FingerprintHandler::OnRequestRecordLabel,
273 weak_ptr_factory_.GetWeakPtr(), callback_id));
274 }
275
OnRequestRecordLabel(const std::string & callback_id,const std::string & label)276 void FingerprintHandler::OnRequestRecordLabel(const std::string& callback_id,
277 const std::string& label) {
278 ResolveJavascriptCallback(base::Value(callback_id), base::Value(label));
279 }
280
HandleRemoveEnrollment(const base::ListValue * args)281 void FingerprintHandler::HandleRemoveEnrollment(const base::ListValue* args) {
282 CHECK_EQ(2U, args->GetSize());
283 std::string callback_id;
284 int index;
285 CHECK(args->GetString(0, &callback_id));
286 CHECK(args->GetInteger(1, &index));
287 DCHECK_LT(index, static_cast<int>(fingerprints_paths_.size()));
288
289 AllowJavascript();
290 fp_service_->RemoveRecord(
291 fingerprints_paths_[index],
292 base::Bind(&FingerprintHandler::OnRemoveRecord,
293 weak_ptr_factory_.GetWeakPtr(), callback_id));
294 }
295
OnRemoveRecord(const std::string & callback_id,bool success)296 void FingerprintHandler::OnRemoveRecord(const std::string& callback_id,
297 bool success) {
298 if (!success)
299 LOG(ERROR) << "Failed to remove fingerprint record.";
300 ResolveJavascriptCallback(base::Value(callback_id), base::Value(success));
301 }
302
HandleChangeEnrollmentLabel(const base::ListValue * args)303 void FingerprintHandler::HandleChangeEnrollmentLabel(
304 const base::ListValue* args) {
305 CHECK_EQ(3U, args->GetSize());
306 std::string callback_id;
307 int index;
308 std::string new_label;
309
310 CHECK(args->GetString(0, &callback_id));
311 CHECK(args->GetInteger(1, &index));
312 CHECK(args->GetString(2, &new_label));
313
314 AllowJavascript();
315 fp_service_->SetRecordLabel(
316 new_label, fingerprints_paths_[index],
317 base::Bind(&FingerprintHandler::OnSetRecordLabel,
318 weak_ptr_factory_.GetWeakPtr(), callback_id));
319 }
320
OnSetRecordLabel(const std::string & callback_id,bool success)321 void FingerprintHandler::OnSetRecordLabel(const std::string& callback_id,
322 bool success) {
323 if (!success)
324 LOG(ERROR) << "Failed to set fingerprint record label.";
325 ResolveJavascriptCallback(base::Value(callback_id), base::Value(success));
326 }
327
HandleStartAuthentication(const base::ListValue * args)328 void FingerprintHandler::HandleStartAuthentication(
329 const base::ListValue* args) {
330 AllowJavascript();
331 fp_service_->StartAuthSession();
332 }
333
HandleEndCurrentAuthentication(const base::ListValue * args)334 void FingerprintHandler::HandleEndCurrentAuthentication(
335 const base::ListValue* args) {
336 AllowJavascript();
337 fp_service_->EndCurrentAuthSession(
338 base::Bind(&FingerprintHandler::OnEndCurrentAuthSession,
339 weak_ptr_factory_.GetWeakPtr()));
340 }
341
OnEndCurrentAuthSession(bool success)342 void FingerprintHandler::OnEndCurrentAuthSession(bool success) {
343 if (!success)
344 LOG(ERROR) << "Failed to end current fingerprint authentication session.";
345 }
346
347 } // namespace settings
348 } // namespace chromeos
349