| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chromeos/dbus/biod/fake_biod_client.h" |
| |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/macros.h" |
| #include "base/strings/string_util.h" |
| #include "base/test/test_simple_task_runner.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "chromeos/dbus/biod/test_utils.h" |
| #include "dbus/object_path.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using biod::SCAN_RESULT_SUCCESS; |
| |
| namespace chromeos { |
| |
| namespace { |
| |
| const char kTestUserId[] = "testuser@gmail.com"; |
| const char kTestLabel[] = "testLabel"; |
| // Template of a scan string to be used in GenerateTestFingerprint. The # and $ |
| // are two wildcards that will be replaced by numbers to ensure unique scans. |
| const char kTestScan[] = "finger#scan$"; |
| |
| } // namepsace |
| |
| class FakeBiodClientTest : public testing::Test { |
| public: |
| FakeBiodClientTest() |
| : task_runner_(new base::TestSimpleTaskRunner), |
| task_runner_handle_(task_runner_) {} |
| ~FakeBiodClientTest() override = default; |
| |
| // Returns the stored records for |user_id|. Verified to work in |
| // TestGetRecordsForUser. |
| std::vector<dbus::ObjectPath> GetRecordsForUser(const std::string& user_id) { |
| std::vector<dbus::ObjectPath> enrollment_paths; |
| fake_biod_client_.GetRecordsForUser( |
| user_id, |
| base::Bind(&test_utils::CopyObjectPathArray, &enrollment_paths)); |
| task_runner_->RunUntilIdle(); |
| return enrollment_paths; |
| } |
| |
| // Helper function which enrolls a fingerprint. Each element in |
| // |fingerprint_data| corresponds to a finger tap. |
| void EnrollFingerprint(const std::string& id, |
| const std::string& label, |
| const std::vector<std::string>& fingerprint_data) { |
| ASSERT_FALSE(fingerprint_data.empty()); |
| |
| dbus::ObjectPath returned_path; |
| fake_biod_client_.StartEnrollSession( |
| id, label, base::Bind(&test_utils::CopyObjectPath, &returned_path)); |
| task_runner_->RunUntilIdle(); |
| EXPECT_NE(dbus::ObjectPath(), returned_path); |
| |
| // Send |fingerprint_data| size - 1 incomplete scans, then finish the |
| // enrollment by sending a complete scan signal. |
| for (size_t i = 0; i < fingerprint_data.size(); ++i) { |
| fake_biod_client_.SendEnrollScanDone( |
| fingerprint_data[i], SCAN_RESULT_SUCCESS, |
| i == fingerprint_data.size() - 1 /* is_complete */, |
| -1 /* percent_complete */); |
| } |
| } |
| |
| // Helper function which enrolls |n| fingerprints with the same |id|, |label| |
| // and |fingerprint_data|. |
| void EnrollNTestFingerprints(const std::string& id, |
| const std::string& label, |
| const std::vector<std::string>& fingerprint_data, |
| int n) { |
| for (int i = 0; i < n; ++i) |
| EnrollFingerprint(id, label, fingerprint_data); |
| } |
| |
| // Creates a new unique fingerprint consisting of unique scans. |
| std::vector<std::string> GenerateTestFingerprint(int scans) { |
| EXPECT_GE(scans, 0); |
| num_test_fingerprints_++; |
| |
| std::vector<std::string> fingerprint; |
| for (int i = 0; i < scans; ++i) { |
| std::string scan = kTestScan; |
| base::ReplaceSubstringsAfterOffset( |
| &scan, 0, "#", std::to_string(num_test_fingerprints_)); |
| base::ReplaceSubstringsAfterOffset(&scan, 0, "$", std::to_string(i)); |
| fingerprint.push_back(scan); |
| } |
| return fingerprint; |
| } |
| |
| protected: |
| FakeBiodClient fake_biod_client_; |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner_; |
| base::ThreadTaskRunnerHandle task_runner_handle_; |
| |
| // This number is incremented each time GenerateTestFingerprint is called to |
| // ensure each fingerprint is unique. |
| int num_test_fingerprints_ = 0; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(FakeBiodClientTest); |
| }; |
| |
| TEST_F(FakeBiodClientTest, TestEnrollSessionWorkflow) { |
| test_utils::TestBiodObserver observer; |
| fake_biod_client_.AddObserver(&observer); |
| |
| const std::vector<std::string>& kTestFingerprint = GenerateTestFingerprint(2); |
| // Verify that successful enrollments get stored as expected. A fingerprint |
| // that was created with 2 scans should have 1 incomplete scan and 1 complete |
| // scan. |
| EnrollFingerprint(kTestUserId, kTestLabel, kTestFingerprint); |
| EXPECT_EQ(1u, GetRecordsForUser(kTestUserId).size()); |
| EXPECT_EQ(1, observer.num_incomplete_enroll_scans_received()); |
| EXPECT_EQ(1, observer.num_complete_enroll_scans_received()); |
| |
| // Verify that the enroll session worflow can be used repeatedly by enrolling |
| // 3 more fingerprints (each with 1 incomplete and 1 complete scan). |
| observer.ResetAllCounts(); |
| EnrollNTestFingerprints(kTestUserId, kTestLabel, kTestFingerprint, 3); |
| EXPECT_EQ(4u, GetRecordsForUser(kTestUserId).size()); |
| EXPECT_EQ(3, observer.num_incomplete_enroll_scans_received()); |
| EXPECT_EQ(3, observer.num_complete_enroll_scans_received()); |
| } |
| |
| // Test authentication when one user has one or more fingerprints registered. |
| // This should be the normal scenario. |
| TEST_F(FakeBiodClientTest, TestAuthSessionWorkflowSingleUser) { |
| test_utils::TestBiodObserver observer; |
| fake_biod_client_.AddObserver(&observer); |
| EXPECT_EQ(0u, GetRecordsForUser(kTestUserId).size()); |
| |
| const std::vector<std::string>& kTestFingerprint1 = |
| GenerateTestFingerprint(2); |
| const std::vector<std::string>& kTestFingerprint2 = |
| GenerateTestFingerprint(2); |
| const std::vector<std::string>& kTestFingerprint3 = |
| GenerateTestFingerprint(2); |
| const std::vector<std::string>& kTestFingerprint4 = |
| GenerateTestFingerprint(2); |
| |
| // Add two fingerprints |kTestFingerprint1| and |kTestFingerprint2| and start |
| // an auth session. |
| EnrollFingerprint(kTestUserId, kTestLabel, kTestFingerprint1); |
| EnrollFingerprint(kTestUserId, kTestLabel, kTestFingerprint2); |
| dbus::ObjectPath returned_path; |
| fake_biod_client_.StartAuthSession( |
| base::Bind(&test_utils::CopyObjectPath, &returned_path)); |
| task_runner_->RunUntilIdle(); |
| EXPECT_NE(returned_path, dbus::ObjectPath()); |
| |
| // Verify that by sending two attempt signals of fingerprints that have been |
| // enrolled, the observer should receive two matches and zero non-matches. |
| fake_biod_client_.SendAuthScanDone(kTestFingerprint1[0], SCAN_RESULT_SUCCESS); |
| fake_biod_client_.SendAuthScanDone(kTestFingerprint2[0], SCAN_RESULT_SUCCESS); |
| EXPECT_EQ(2, observer.num_matched_auth_scans_received()); |
| EXPECT_EQ(0, observer.num_unmatched_auth_scans_received()); |
| |
| // Verify that by sending two attempt signals of fingerprints that have not |
| // been enrolled, the observer should receive two non-matches and zero |
| // matches. |
| observer.ResetAllCounts(); |
| fake_biod_client_.SendAuthScanDone(kTestFingerprint3[0], SCAN_RESULT_SUCCESS); |
| fake_biod_client_.SendAuthScanDone(kTestFingerprint4[0], SCAN_RESULT_SUCCESS); |
| EXPECT_EQ(0, observer.num_matched_auth_scans_received()); |
| EXPECT_EQ(2, observer.num_unmatched_auth_scans_received()); |
| } |
| |
| // Test authentication when multiple users have fingerprints registered. Cover |
| // cases such as when both users use the same labels, a user had registered the |
| // same fingerprint multiple times, or two users use the same fingerprint. |
| TEST_F(FakeBiodClientTest, TestAuthenticateWorkflowMultipleUsers) { |
| test_utils::TestBiodObserver observer; |
| fake_biod_client_.AddObserver(&observer); |
| EXPECT_EQ(0u, GetRecordsForUser(kTestUserId).size()); |
| |
| // Add two users, who have scanned three fingers between the two of them. |
| const std::string kUserOne = std::string(kTestUserId) + "1"; |
| const std::string kUserTwo = std::string(kTestUserId) + "2"; |
| |
| const std::string kLabelOne = std::string(kTestLabel) + "1"; |
| const std::string kLabelTwo = std::string(kTestLabel) + "2"; |
| const std::string kLabelThree = std::string(kTestLabel) + "3"; |
| |
| // Generate 2 test fingerprints per user. |
| const std::vector<std::string>& kUser1Finger1 = GenerateTestFingerprint(2); |
| const std::vector<std::string>& kUser1Finger2 = GenerateTestFingerprint(2); |
| const std::vector<std::string>& kUser2Finger1 = GenerateTestFingerprint(2); |
| const std::vector<std::string>& kUser2Finger2 = GenerateTestFingerprint(2); |
| |
| EnrollFingerprint(kUserOne, kLabelOne, kUser1Finger1); |
| EnrollFingerprint(kUserOne, kLabelTwo, kUser1Finger2); |
| // User one has registered finger two twice. |
| EnrollFingerprint(kUserOne, kLabelThree, kUser1Finger2); |
| EnrollFingerprint(kUserTwo, kLabelOne, kUser2Finger1); |
| EnrollFingerprint(kUserTwo, kLabelTwo, kUser2Finger2); |
| // User two has allowed user one to unlock their account with their first |
| // finger. |
| EnrollFingerprint(kUserTwo, kLabelThree, kUser1Finger1); |
| |
| dbus::ObjectPath returned_path; |
| fake_biod_client_.StartAuthSession( |
| base::Bind(&test_utils::CopyObjectPath, &returned_path)); |
| task_runner_->RunUntilIdle(); |
| EXPECT_NE(returned_path, dbus::ObjectPath()); |
| |
| // Verify that if a user registers the same finger to two different labels, |
| // both ObjectPath that maps to the labels are returned as matches. |
| std::vector<dbus::ObjectPath> record_paths_user1 = |
| GetRecordsForUser(kUserOne); |
| EXPECT_EQ(3u, record_paths_user1.size()); |
| |
| AuthScanMatches expected_auth_scans_matches; |
| expected_auth_scans_matches[kUserOne] = {record_paths_user1[1], |
| record_paths_user1[2]}; |
| fake_biod_client_.SendAuthScanDone(kUser1Finger2[0], SCAN_RESULT_SUCCESS); |
| EXPECT_EQ(expected_auth_scans_matches, observer.last_auth_scan_matches()); |
| |
| // Verify that a fingerprint associated with one user and one label returns a |
| // match with one user and one ObjectPath that maps to that label. |
| std::vector<dbus::ObjectPath> record_paths_user2 = |
| GetRecordsForUser(kUserTwo); |
| EXPECT_EQ(3u, record_paths_user2.size()); |
| |
| expected_auth_scans_matches.clear(); |
| expected_auth_scans_matches[kUserTwo] = {record_paths_user2[0]}; |
| fake_biod_client_.SendAuthScanDone(kUser2Finger1[0], SCAN_RESULT_SUCCESS); |
| EXPECT_EQ(expected_auth_scans_matches, observer.last_auth_scan_matches()); |
| |
| // Verify if two users register the same fingerprint, the matches contain |
| // both users. |
| expected_auth_scans_matches.clear(); |
| expected_auth_scans_matches[kUserOne] = {record_paths_user1[0]}; |
| expected_auth_scans_matches[kUserTwo] = {record_paths_user2[2]}; |
| fake_biod_client_.SendAuthScanDone(kUser1Finger1[0], SCAN_RESULT_SUCCESS); |
| EXPECT_EQ(expected_auth_scans_matches, observer.last_auth_scan_matches()); |
| |
| // Verify if a unregistered finger is scanned, the matches are empty. |
| expected_auth_scans_matches.clear(); |
| fake_biod_client_.SendAuthScanDone("Unregistered", SCAN_RESULT_SUCCESS); |
| EXPECT_EQ(expected_auth_scans_matches, observer.last_auth_scan_matches()); |
| } |
| |
| TEST_F(FakeBiodClientTest, TestGetRecordsForUser) { |
| // Verify that initially |kTestUserId| will have no fingerprints. |
| EXPECT_EQ(0u, GetRecordsForUser(kTestUserId).size()); |
| |
| // Verify that after enrolling 2 fingerprints, a GetRecords call return 2 |
| // items. |
| EnrollNTestFingerprints(kTestUserId, kTestLabel, GenerateTestFingerprint(2), |
| 2); |
| EXPECT_EQ(2u, GetRecordsForUser(kTestUserId).size()); |
| |
| // Verify that GetRecords call for a user with no registered fingerprints |
| // should return 0 items. |
| EXPECT_EQ(0u, GetRecordsForUser("noRegisteredFingerprints@gmail.com").size()); |
| } |
| |
| TEST_F(FakeBiodClientTest, TestDestroyingRecords) { |
| // Verify that after enrolling 2 fingerprints and destroying them, 0 |
| // fingerprints will remain. |
| EnrollNTestFingerprints(kTestUserId, kTestLabel, GenerateTestFingerprint(2), |
| 2); |
| EXPECT_EQ(2u, GetRecordsForUser(kTestUserId).size()); |
| fake_biod_client_.DestroyAllRecords(EmptyVoidDBusMethodCallback()); |
| EXPECT_EQ(0u, GetRecordsForUser(kTestUserId).size()); |
| } |
| |
| TEST_F(FakeBiodClientTest, TestGetAndSetRecordLabels) { |
| const std::string kLabelOne = "Finger 1"; |
| const std::string kLabelTwo = "Finger 2"; |
| |
| EnrollFingerprint(kTestUserId, kLabelOne, GenerateTestFingerprint(2)); |
| EnrollFingerprint(kTestUserId, kLabelTwo, GenerateTestFingerprint(2)); |
| EXPECT_EQ(2u, GetRecordsForUser(kTestUserId).size()); |
| std::vector<dbus::ObjectPath> enrollment_paths; |
| fake_biod_client_.GetRecordsForUser( |
| kTestUserId, |
| base::Bind(&test_utils::CopyObjectPathArray, &enrollment_paths)); |
| task_runner_->RunUntilIdle(); |
| EXPECT_EQ(2u, enrollment_paths.size()); |
| |
| // Verify the labels we get using GetLabel are the same as the one we |
| // originally set. |
| std::string returned_str; |
| fake_biod_client_.RequestRecordLabel( |
| enrollment_paths[0], base::Bind(&test_utils::CopyString, &returned_str)); |
| task_runner_->RunUntilIdle(); |
| EXPECT_EQ(kLabelOne, returned_str); |
| |
| returned_str = ""; |
| fake_biod_client_.RequestRecordLabel( |
| enrollment_paths[1], base::Bind(&test_utils::CopyString, &returned_str)); |
| task_runner_->RunUntilIdle(); |
| EXPECT_EQ(kLabelTwo, returned_str); |
| |
| // Verify that by setting a new label, getting the label will return the value |
| // of the new label. |
| const std::string kNewLabelTwo = "Finger 2 New"; |
| fake_biod_client_.SetRecordLabel(enrollment_paths[1], kNewLabelTwo, |
| EmptyVoidDBusMethodCallback()); |
| fake_biod_client_.RequestRecordLabel( |
| enrollment_paths[1], base::Bind(&test_utils::CopyString, &returned_str)); |
| task_runner_->RunUntilIdle(); |
| EXPECT_EQ(kNewLabelTwo, returned_str); |
| } |
| |
| } // namespace chromeos |