blob: 8b39bfa811a48c615cd606e6e3fb2ce182f7a2ce [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
extern crate serde;
extern crate serde_json;
use log::warn;
use std::fs::{self, File};
use std::io::{BufReader, BufWriter, Write};
use std::path::{Path, PathBuf};
use account_common::{AccountManagerError, LocalAccountId};
use fidl_fuchsia_auth_account::Status;
use serde_derive::{Deserialize, Serialize};
/// Name of account list file (one per account manager), within the account list dir.
const ACCOUNT_LIST_DOC: &str = "list.json";
/// Name of temporary account list file, within the account list dir.
const ACCOUNT_LIST_DOC_TMP: &str = "list.json.tmp";
#[derive(Serialize, Deserialize)]
pub struct StoredAccountMetadata {
/// Local account id for this account
account_id: LocalAccountId,
}
impl StoredAccountMetadata {
pub fn new(account_id: LocalAccountId) -> StoredAccountMetadata {
Self { account_id }
}
pub fn account_id(&self) -> &LocalAccountId {
&self.account_id
}
}
/// Json-representation of the set of Fuchsia accounts on device. As this format evolves,
/// cautiousness is encouraged to ensure backwards compatibility.
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct StoredAccountList {
accounts: Vec<StoredAccountMetadata>,
}
impl StoredAccountList {
/// Create a new stored account. No side effects.
pub fn new(accounts: Vec<StoredAccountMetadata>) -> StoredAccountList {
Self { accounts }
}
/// List of each account's metadata which are part of this list.
pub fn accounts(&self) -> &Vec<StoredAccountMetadata> {
&self.accounts
}
/// Load StoredAccountList from disk
pub fn load(account_list_dir: &Path) -> Result<StoredAccountList, AccountManagerError> {
let path = Self::path(account_list_dir);
if !path.exists() {
warn!("Created account list dir: {:?}", path);
return Ok(StoredAccountList::new(vec![]));
};
let file = BufReader::new(File::open(path).map_err(|err| {
warn!("Failed to read account list: {:?}", err);
AccountManagerError::new(Status::IoError).with_cause(err)
})?);
serde_json::from_reader(file).map_err(|err| {
warn!("Failed to parse account list: {:?}", err);
AccountManagerError::new(Status::InternalError).with_cause(err)
})
}
/// Convenience path to the list file, given the account_list_dir
fn path(account_list_dir: &Path) -> PathBuf {
account_list_dir.join(ACCOUNT_LIST_DOC)
}
/// Convenience path to the list temp file, given the account_list_dir; used for safe writing
fn tmp_path(account_list_dir: &Path) -> PathBuf {
account_list_dir.join(ACCOUNT_LIST_DOC_TMP)
}
/// Write StoredAccountList to disk, ensuring the file is either written completely or not
/// modified.
pub fn save(&self, account_list_dir: &Path) -> Result<(), AccountManagerError> {
let path = Self::path(account_list_dir);
let tmp_path = Self::tmp_path(account_list_dir);
{
let mut tmp_file = BufWriter::new(File::create(&tmp_path).map_err(|err| {
warn!("Failed to create account tmp list: {:?}", err);
AccountManagerError::new(Status::IoError).with_cause(err)
})?);
serde_json::to_writer(&mut tmp_file, self).map_err(|err| {
warn!("Failed to serialize account list: {:?}", err);
AccountManagerError::new(Status::IoError).with_cause(err)
})?;
tmp_file.flush().map_err(|err| {
warn!("Failed to flush serialized account list: {:?}", err);
AccountManagerError::new(Status::IoError).with_cause(err)
})?;
}
fs::rename(&tmp_path, &path).map_err(|err| {
warn!("Failed to rename account list: {:?}", err);
AccountManagerError::new(Status::IoError).with_cause(err)
})
}
}