Test config round trip
This commit is contained in:
parent
0058221e88
commit
fb18cb3c4f
6 changed files with 80 additions and 35 deletions
11
src/app.rs
11
src/app.rs
|
@ -195,15 +195,14 @@ impl App {
|
|||
|
||||
async fn get_or_create_auth_secret(path: &Path) -> Result<auth::Secret, Error> {
|
||||
match tokio::fs::read(&path).await {
|
||||
Ok(s) => Ok(auth::Secret {
|
||||
key: s
|
||||
.try_into()
|
||||
Ok(s) => Ok(auth::Secret(
|
||||
s.try_into()
|
||||
.map_err(|_| Error::AuthenticationSecretInvalid)?,
|
||||
}),
|
||||
)),
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||
let mut secret = auth::Secret::default();
|
||||
OsRng.fill_bytes(&mut secret.key);
|
||||
tokio::fs::write(&path, &secret.key)
|
||||
OsRng.fill_bytes(secret.as_mut());
|
||||
tokio::fs::write(&path, &secret)
|
||||
.await
|
||||
.map_err(|_| Error::AuthenticationSecretInvalid)?;
|
||||
Ok(secret)
|
||||
|
|
|
@ -9,8 +9,18 @@ use serde::{Deserialize, Serialize};
|
|||
use crate::app::Error;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Secret {
|
||||
pub key: [u8; 32],
|
||||
pub struct Secret(pub [u8; 32]);
|
||||
|
||||
impl AsRef<[u8]> for Secret {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<[u8]> for Secret {
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -55,7 +65,7 @@ pub fn generate_auth_token(
|
|||
serde_json::to_string(&authorization).or(Err(Error::AuthorizationTokenEncoding))?;
|
||||
branca::encode(
|
||||
serialized_authorization.as_bytes(),
|
||||
&auth_secret.key,
|
||||
auth_secret.as_ref(),
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
|
@ -75,7 +85,7 @@ pub fn decode_auth_token(
|
|||
Scope::PolarisAuth => 0, // permanent
|
||||
};
|
||||
let authorization =
|
||||
branca::decode(data, &auth_secret.key, ttl).map_err(|_| Error::InvalidAuthToken)?;
|
||||
branca::decode(data, auth_secret.as_ref(), ttl).map_err(|_| Error::InvalidAuthToken)?;
|
||||
let authorization: Authorization =
|
||||
serde_json::from_slice(&authorization[..]).map_err(|_| Error::InvalidAuthToken)?;
|
||||
if authorization.scope != scope {
|
||||
|
|
|
@ -24,7 +24,7 @@ pub struct Config {
|
|||
pub reindex_every_n_seconds: Option<u64>,
|
||||
pub album_art_pattern: Option<Regex>,
|
||||
pub ddns_url: Option<http::Uri>,
|
||||
pub mount_dirs: Vec<MountDir>,
|
||||
pub mount_dirs: HashMap<String, MountDir>,
|
||||
pub users: HashMap<String, User>,
|
||||
}
|
||||
|
||||
|
@ -34,16 +34,15 @@ impl TryFrom<storage::Config> for Config {
|
|||
fn try_from(c: storage::Config) -> Result<Self, Self::Error> {
|
||||
let mut users: HashMap<String, User> = HashMap::new();
|
||||
for user in c.users {
|
||||
if let Ok(user) = <storage::User as TryInto<User>>::try_into(user) {
|
||||
users.insert(user.name.clone(), user);
|
||||
}
|
||||
let user = <storage::User as TryInto<User>>::try_into(user)?;
|
||||
users.insert(user.name.clone(), user);
|
||||
}
|
||||
|
||||
let mount_dirs = c
|
||||
.mount_dirs
|
||||
.into_iter()
|
||||
.filter_map(|m| m.try_into().ok())
|
||||
.collect();
|
||||
let mut mount_dirs: HashMap<String, MountDir> = HashMap::new();
|
||||
for mount_dir in c.mount_dirs {
|
||||
let mount_dir = <storage::MountDir as TryInto<MountDir>>::try_into(mount_dir)?;
|
||||
mount_dirs.insert(mount_dir.name.clone(), mount_dir);
|
||||
}
|
||||
|
||||
let ddns_url = match c.ddns_url.map(http::Uri::try_from) {
|
||||
Some(Ok(u)) => Some(u),
|
||||
|
@ -58,7 +57,7 @@ impl TryFrom<storage::Config> for Config {
|
|||
};
|
||||
|
||||
Ok(Config {
|
||||
reindex_every_n_seconds: c.reindex_every_n_seconds, // TODO validate and warn
|
||||
reindex_every_n_seconds: c.reindex_every_n_seconds,
|
||||
album_art_pattern,
|
||||
ddns_url,
|
||||
mount_dirs,
|
||||
|
@ -72,7 +71,7 @@ impl From<Config> for storage::Config {
|
|||
Self {
|
||||
reindex_every_n_seconds: c.reindex_every_n_seconds,
|
||||
album_art_pattern: c.album_art_pattern.map(|p| p.as_str().to_owned()),
|
||||
mount_dirs: c.mount_dirs.into_iter().map(|d| d.into()).collect(),
|
||||
mount_dirs: c.mount_dirs.into_iter().map(|(_, d)| d.into()).collect(),
|
||||
ddns_url: c.ddns_url.map(|u| u.to_string()),
|
||||
users: c.users.into_iter().map(|(_, u)| u.into()).collect(),
|
||||
}
|
||||
|
@ -208,7 +207,8 @@ impl Manager {
|
|||
}
|
||||
|
||||
pub async fn get_mounts(&self) -> Vec<MountDir> {
|
||||
self.config.read().await.mount_dirs.clone()
|
||||
let config = self.config.read().await;
|
||||
config.mount_dirs.values().cloned().collect()
|
||||
}
|
||||
|
||||
pub async fn resolve_virtual_path<P: AsRef<Path>>(
|
||||
|
@ -219,8 +219,42 @@ impl Manager {
|
|||
config.resolve_virtual_path(virtual_path)
|
||||
}
|
||||
|
||||
pub async fn set_mounts(&self, mount_dirs: Vec<storage::MountDir>) {
|
||||
self.config.write().await.set_mounts(mount_dirs);
|
||||
pub async fn set_mounts(&self, mount_dirs: Vec<storage::MountDir>) -> Result<(), Error> {
|
||||
self.config.write().await.set_mounts(mount_dirs)
|
||||
// TODO persistence
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn can_read_config() {
|
||||
let config_path = PathBuf::from_iter(["test-data", "config.toml"]);
|
||||
let manager = Manager::new(&config_path, auth::Secret([0; 32]))
|
||||
.await
|
||||
.unwrap();
|
||||
let config: storage::Config = manager.config.read().await.clone().into();
|
||||
|
||||
assert_eq!(config.reindex_every_n_seconds, None);
|
||||
assert_eq!(
|
||||
config.album_art_pattern,
|
||||
Some(r#"^Folder\.(png|jpg|jpeg)$"#.to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
config.mount_dirs,
|
||||
vec![storage::MountDir {
|
||||
source: PathBuf::from("test-data/small-collection"),
|
||||
name: "root".to_owned(),
|
||||
}]
|
||||
);
|
||||
assert_eq!(config.users[0].name, "test_user");
|
||||
assert_eq!(config.users[0].admin, Some(true));
|
||||
assert_eq!(
|
||||
config.users[0].initial_password,
|
||||
Some("very_secret_password".to_owned())
|
||||
);
|
||||
assert!(config.users[0].hashed_password.is_some());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
ops::Deref,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
@ -38,17 +39,19 @@ impl From<MountDir> for storage::MountDir {
|
|||
}
|
||||
|
||||
impl Config {
|
||||
pub fn set_mounts(&mut self, mount_dirs: Vec<storage::MountDir>) {
|
||||
self.mount_dirs = mount_dirs
|
||||
.into_iter()
|
||||
.filter_map(|m| m.try_into().ok())
|
||||
.collect();
|
||||
pub fn set_mounts(&mut self, mount_dirs: Vec<storage::MountDir>) -> Result<(), Error> {
|
||||
let mut new_mount_dirs = HashMap::new();
|
||||
for mount_dir in mount_dirs {
|
||||
let mount_dir = <storage::MountDir as TryInto<MountDir>>::try_into(mount_dir)?;
|
||||
new_mount_dirs.insert(mount_dir.name.clone(), mount_dir);
|
||||
}
|
||||
self.mount_dirs = new_mount_dirs;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn resolve_virtual_path<P: AsRef<Path>>(&self, virtual_path: P) -> Result<PathBuf, Error> {
|
||||
for mount in &self.mount_dirs {
|
||||
let mount_path = Path::new(&mount.name);
|
||||
if let Ok(p) = virtual_path.as_ref().strip_prefix(mount_path) {
|
||||
for (name, mount) in &self.mount_dirs {
|
||||
if let Ok(p) = virtual_path.as_ref().strip_prefix(name) {
|
||||
return if p.components().count() == 0 {
|
||||
Ok(mount.source.clone())
|
||||
} else {
|
||||
|
|
|
@ -161,7 +161,7 @@ async fn put_mount_dirs(
|
|||
) -> Result<(), APIError> {
|
||||
let new_mount_dirs: Vec<config::storage::MountDir> =
|
||||
new_mount_dirs.iter().cloned().map(|m| m.into()).collect();
|
||||
config_manager.set_mounts(new_mount_dirs).await;
|
||||
config_manager.set_mounts(new_mount_dirs).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[settings]
|
||||
album_art_pattern = '^Folder\.(png|jpg|jpeg)$'
|
||||
|
||||
[[mount_dirs]]
|
||||
|
@ -7,5 +6,5 @@ source = 'test-data/small-collection'
|
|||
|
||||
[[users]]
|
||||
name = 'test_user'
|
||||
password = 'very_secret_password'
|
||||
initial_password = 'very_secret_password'
|
||||
admin = true
|
||||
|
|
Loading…
Add table
Reference in a new issue