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