blob: a7739157f74fdff3512d6d26d0900e95c349d99b [file] [log] [blame]
// 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 <optional>
#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);
}
std::optional<RecordMetadata> CrosFpRecordManager::GetRecordMetadata(
const std::string& record_id) {
auto entry = records_metadata_.find(record_id);
if (entry == records_metadata_.end())
return std::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