| // Copyright 2021 The Chromium OS 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 <algorithm> |
| #include <utility> |
| |
| #include "biod/cros_fp_record_manager.h" |
| #include "biod/utils.h" |
| |
| namespace biod { |
| |
| CrosFpRecordManager::CrosFpRecordManager( |
| std::unique_ptr<BiodStorageInterface> biod_storage) |
| : biod_storage_(std::move(biod_storage)) { |
| CHECK(biod_storage_); |
| } |
| |
| void CrosFpRecordManager::SetAllowAccess(bool allow) { |
| biod_storage_->set_allow_access(allow); |
| } |
| |
| base::Optional<RecordMetadata> CrosFpRecordManager::GetRecordMetadata( |
| const std::string& record_id) { |
| auto entry = records_metadata_.find(record_id); |
| if (entry == records_metadata_.end()) |
| return base::nullopt; |
| |
| return entry->second.metadata; |
| } |
| |
| void CrosFpRecordManager::MakeRecordsWithoutValidationValInvalid( |
| std::vector<Record>* records) { |
| for (auto& record : *records) { |
| if (record.valid && record.metadata.validation_val.empty()) { |
| LOG(INFO) << "Marking record " << LogSafeID(record.metadata.record_id) |
| << " as invalid because it doesn't contain" |
| << " validation value."; |
| record.valid = false; |
| } |
| } |
| } |
| |
| std::vector<Record> CrosFpRecordManager::GetRecordsForUser( |
| const std::string& user_id) { |
| std::vector<Record> result = biod_storage_->ReadRecordsForSingleUser(user_id); |
| |
| // We don't support records without validation value, so mark them as invalid. |
| MakeRecordsWithoutValidationValInvalid(&result); |
| |
| // Add records that are not present in records_metadata_. |
| // Please note that try_emplace changes the map only when no element with |
| // record_id key exists. |
| // Invalid records are added too, so they can be deleted when |
| // DeleteInvalidRecords or DeleteAllRecords is called (assuming that record |
| // metadata contains correct user_id and record_id). |
| for (const auto& record : result) { |
| records_metadata_.try_emplace( |
| record.metadata.record_id, |
| CrosFpRecordMetadata{record.metadata, record.valid}); |
| } |
| |
| // Remove invalid records from result before returning vector. |
| result.erase(std::remove_if(result.begin(), result.end(), |
| [](const auto& record) { return !record.valid; }), |
| result.end()); |
| |
| return result; |
| } |
| |
| bool CrosFpRecordManager::UserHasInvalidRecords(const std::string& user_id) { |
| return std::any_of(records_metadata_.begin(), records_metadata_.end(), |
| [&user_id](const auto& p) { |
| const auto& [id, record] = p; |
| return record.valid == false && |
| record.metadata.user_id == user_id; |
| }); |
| } |
| |
| bool CrosFpRecordManager::CreateRecord( |
| const BiodStorageInterface::RecordMetadata& record, |
| std::unique_ptr<VendorTemplate> templ) { |
| auto entry = records_metadata_.find(record.record_id); |
| if (entry != records_metadata_.end()) { |
| LOG(ERROR) << "Attempted to create record with existing RecordID " |
| << LogSafeID(record.record_id) << "."; |
| return false; |
| } |
| |
| std::string tmpl_base64 = TemplateToBase64(std::move(templ)); |
| if (!biod_storage_->WriteRecord(record, |
| base::Value(std::move(tmpl_base64)))) { |
| return false; |
| } |
| |
| std::string record_id = record.record_id; |
| records_metadata_.emplace(std::move(record_id), CrosFpRecordMetadata{record}); |
| |
| return true; |
| } |
| |
| bool CrosFpRecordManager::UpdateRecord(const RecordMetadata& record_metadata, |
| std::unique_ptr<VendorTemplate> templ) { |
| const auto& record_id = record_metadata.record_id; |
| const auto& user_id = record_metadata.user_id; |
| |
| // Updated record must exist. |
| CHECK(records_metadata_.find(record_id) != records_metadata_.end()); |
| const auto& current_user_id = records_metadata_[record_id].metadata.user_id; |
| |
| // Must be valid. |
| if (!records_metadata_[record_id].valid) { |
| LOG(WARNING) << "Attempt to update invalid record " << LogSafeID(record_id) |
| << " for user " << LogSafeID(current_user_id); |
| return false; |
| } |
| |
| // And UserId must match. |
| if (current_user_id != user_id) { |
| LOG(ERROR) << "UserID mismatch (current: " << LogSafeID(current_user_id) |
| << ", update: " << LogSafeID(user_id) << ") during attempt" |
| << " to update record " << LogSafeID(record_id); |
| return false; |
| } |
| |
| std::string tmpl_base64 = TemplateToBase64(std::move(templ)); |
| if (!biod_storage_->WriteRecord(record_metadata, |
| base::Value(std::move(tmpl_base64)))) { |
| return false; |
| } |
| |
| // Update metadata stored in this record manager. |
| records_metadata_[record_id].metadata = record_metadata; |
| |
| return true; |
| } |
| |
| bool CrosFpRecordManager::UpdateRecordMetadata( |
| const RecordMetadata& record_metadata) { |
| const auto& record_id = record_metadata.record_id; |
| const auto& user_id = record_metadata.user_id; |
| |
| // Updated record must exist. |
| CHECK(records_metadata_.find(record_id) != records_metadata_.end()); |
| const auto& current_user_id = records_metadata_[record_id].metadata.user_id; |
| |
| // Must be valid. |
| if (!records_metadata_[record_id].valid) { |
| LOG(WARNING) << "Attempt to update invalid record " << LogSafeID(record_id) |
| << " for user " << LogSafeID(current_user_id); |
| return false; |
| } |
| |
| // And UserId must match. |
| if (current_user_id != user_id) { |
| LOG(ERROR) << "UserID mismatch (current: " << LogSafeID(current_user_id) |
| << ", update: " << LogSafeID(user_id) << ") during attempt" |
| << " to update record " << LogSafeID(record_id); |
| return false; |
| } |
| |
| // Read existing record from storage. A complete record consists of both |
| // the metadata and the FPMCU template. We currently only have the metadata. |
| const auto record = biod_storage_->ReadSingleRecord(user_id, record_id); |
| if (!record || !record->valid) { |
| return false; |
| } |
| |
| if (!biod_storage_->WriteRecord(record_metadata, |
| base::Value(std::move(record->data)))) { |
| return false; |
| } |
| |
| // Update metadata stored in this record manager. |
| records_metadata_[record_id].metadata = record_metadata; |
| |
| return true; |
| } |
| |
| void CrosFpRecordManager::DeleteInvalidRecords() { |
| for (auto it = records_metadata_.cbegin(); it != records_metadata_.cend();) { |
| const auto record = it->second; |
| if (!record.valid) { |
| LOG(INFO) << "Deleting invalid record " |
| << LogSafeID(record.metadata.record_id) << " for user " |
| << LogSafeID(record.metadata.user_id); |
| biod_storage_->DeleteRecord(record.metadata.user_id, |
| record.metadata.record_id); |
| it = records_metadata_.erase(it); |
| } else { |
| it = std::next(it); |
| } |
| } |
| } |
| |
| bool CrosFpRecordManager::DeleteRecord(const std::string& record_id) { |
| auto entry = records_metadata_.find(record_id); |
| CHECK(entry != records_metadata_.end()); |
| |
| const std::string user_id = entry->second.metadata.user_id; |
| // Delete record from storage. |
| if (!biod_storage_->DeleteRecord(user_id, record_id)) { |
| return false; |
| } |
| |
| // Remove record metadata. |
| records_metadata_.erase(record_id); |
| |
| return true; |
| } |
| |
| bool CrosFpRecordManager::DeleteAllRecords() { |
| // Enumerate through records_metadata_ and delete each record. |
| bool delete_all_records = true; |
| for (const auto& [id, record] : records_metadata_) { |
| delete_all_records &= biod_storage_->DeleteRecord( |
| record.metadata.user_id, record.metadata.record_id); |
| } |
| RemoveRecordsFromMemory(); |
| return delete_all_records; |
| } |
| |
| void CrosFpRecordManager::RemoveRecordsFromMemory() { |
| records_metadata_.clear(); |
| } |
| |
| std::string CrosFpRecordManager::TemplateToBase64( |
| std::unique_ptr<VendorTemplate> templ) { |
| std::string tmpl_base64; |
| |
| base::StringPiece tmpl_sp(reinterpret_cast<char*>(templ->data()), |
| templ->size()); |
| base::Base64Encode(tmpl_sp, &tmpl_base64); |
| |
| return tmpl_base64; |
| } |
| |
| } // namespace biod |