Preserve order of mounts points and users

This commit is contained in:
Antoine Gersant 2024-10-09 23:48:22 -07:00
parent 497b3bb545
commit 08052c25a3
4 changed files with 64 additions and 55 deletions

View file

@ -1,5 +1,4 @@
use std::{
collections::HashMap,
path::{Path, PathBuf},
sync::Arc,
time::Duration,
@ -24,45 +23,33 @@ 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: HashMap<String, MountDir>,
pub users: HashMap<String, User>,
pub mount_dirs: Vec<MountDir>,
pub users: Vec<User>,
}
impl TryFrom<storage::Config> for Config {
type Error = Error;
fn try_from(c: storage::Config) -> Result<Self, Self::Error> {
let mut users: HashMap<String, User> = HashMap::new();
for user in c.users {
let user = <storage::User as TryInto<User>>::try_into(user)?;
users.insert(user.name.clone(), user);
}
let mut config = Config::default();
config.set_mounts(c.mount_dirs)?;
config.set_users(c.users)?;
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);
}
config.reindex_every_n_seconds = c.reindex_every_n_seconds;
let ddns_url = match c.ddns_url.map(http::Uri::try_from) {
Some(Ok(u)) => Some(u),
Some(Err(_)) => return Err(Error::DDNSUpdateURLInvalid),
None => None,
};
let album_art_pattern = match c.album_art_pattern.as_deref().map(Regex::new) {
config.album_art_pattern = match c.album_art_pattern.as_deref().map(Regex::new) {
Some(Ok(u)) => Some(u),
Some(Err(_)) => return Err(Error::IndexAlbumArtPatternInvalid),
None => None,
};
Ok(Config {
reindex_every_n_seconds: c.reindex_every_n_seconds,
album_art_pattern,
ddns_url,
mount_dirs,
users,
})
config.ddns_url = match c.ddns_url.map(http::Uri::try_from) {
Some(Ok(u)) => Some(u),
Some(Err(_)) => return Err(Error::DDNSUpdateURLInvalid),
None => None,
};
Ok(config)
}
}
@ -71,9 +58,9 @@ 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(),
users: c.users.into_iter().map(|u| u.into()).collect(),
}
}
}
@ -181,13 +168,15 @@ impl Manager {
}
pub async fn get_users(&self) -> Vec<User> {
self.config.read().await.users.values().cloned().collect()
self.config.read().await.users.iter().cloned().collect()
}
pub async fn get_user(&self, username: &str) -> Result<User, Error> {
let config = self.config.read().await;
let user = config.users.get(username);
user.cloned().ok_or(Error::UserNotFound)
config
.get_user(username)
.cloned()
.ok_or(Error::UserNotFound)
}
pub async fn create_user(
@ -230,7 +219,7 @@ impl Manager {
pub async fn get_mounts(&self) -> Vec<MountDir> {
let config = self.config.read().await;
config.mount_dirs.values().cloned().collect()
config.mount_dirs.iter().cloned().collect()
}
pub async fn resolve_virtual_path<P: AsRef<Path>>(

View file

@ -1,5 +1,4 @@
use std::{
collections::HashMap,
ops::Deref,
path::{Path, PathBuf},
};
@ -40,18 +39,19 @@ impl From<MountDir> for storage::MountDir {
impl Config {
pub fn set_mounts(&mut self, mount_dirs: Vec<storage::MountDir>) -> Result<(), Error> {
let mut new_mount_dirs = HashMap::new();
let mut new_mount_dirs = Vec::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);
new_mount_dirs.push(mount_dir);
}
new_mount_dirs.dedup_by(|a, b| a.name == b.name);
self.mount_dirs = new_mount_dirs;
Ok(())
}
pub fn resolve_virtual_path<P: AsRef<Path>>(&self, virtual_path: P) -> Result<PathBuf, Error> {
for (name, mount) in &self.mount_dirs {
if let Ok(p) = virtual_path.as_ref().strip_prefix(name) {
for mount in &self.mount_dirs {
if let Ok(p) = virtual_path.as_ref().strip_prefix(&mount.name) {
return if p.components().count() == 0 {
Ok(mount.source.clone())
} else {

View file

@ -48,6 +48,17 @@ impl From<User> for storage::User {
}
impl Config {
pub fn set_users(&mut self, users: Vec<storage::User>) -> Result<(), Error> {
let mut new_users = Vec::new();
for user in users {
let user = <storage::User as TryInto<User>>::try_into(user)?;
new_users.push(user);
}
new_users.dedup_by(|a, b| a.name == b.name);
self.users = new_users;
Ok(())
}
pub fn create_user(
&mut self,
username: &str,
@ -58,25 +69,34 @@ impl Config {
return Err(Error::EmptyUsername);
}
if self.users.contains_key(username) {
if self.exists(username) {
return Err(Error::DuplicateUsername);
}
let password_hash = auth::hash_password(&password)?;
self.users.insert(
username.to_owned(),
User {
name: username.to_owned(),
admin: Some(admin),
initial_password: None,
hashed_password: password_hash,
},
);
self.users.push(User {
name: username.to_owned(),
admin: Some(admin),
initial_password: None,
hashed_password: password_hash,
});
Ok(())
}
pub fn exists(&self, username: &str) -> bool {
self.users.iter().any(|u| u.name == username)
}
pub fn get_user(&self, username: &str) -> Option<&User> {
self.users.iter().find(|u| u.name == username)
}
pub fn get_user_mut(&mut self, username: &str) -> Option<&mut User> {
self.users.iter_mut().find(|u| u.name == username)
}
pub fn authenticate(
&self,
auth_token: &auth::Token,
@ -84,7 +104,7 @@ impl Config {
auth_secret: &auth::Secret,
) -> Result<auth::Authorization, Error> {
let authorization = auth::decode_auth_token(auth_token, scope, auth_secret)?;
if self.users.contains_key(&authorization.username) {
if self.exists(&authorization.username) {
Ok(authorization)
} else {
Err(Error::IncorrectUsername)
@ -97,7 +117,7 @@ impl Config {
password: &str,
auth_secret: &auth::Secret,
) -> Result<auth::Token, Error> {
let user = self.users.get(username).ok_or(Error::IncorrectUsername)?;
let user = self.get_user(username).ok_or(Error::IncorrectUsername)?;
if auth::verify_password(&user.hashed_password, password) {
let authorization = auth::Authorization {
username: username.to_owned(),
@ -110,19 +130,19 @@ impl Config {
}
pub fn set_is_admin(&mut self, username: &str, is_admin: bool) -> Result<(), Error> {
let user = self.users.get_mut(username).ok_or(Error::UserNotFound)?;
let user = self.get_user_mut(username).ok_or(Error::UserNotFound)?;
user.admin = Some(is_admin);
Ok(())
}
pub fn set_password(&mut self, username: &str, password: &str) -> Result<(), Error> {
let user = self.users.get_mut(username).ok_or(Error::UserNotFound)?;
let user = self.get_user_mut(username).ok_or(Error::UserNotFound)?;
user.hashed_password = auth::hash_password(password)?;
Ok(())
}
pub fn delete_user(&mut self, username: &str) {
self.users.remove(username);
self.users.retain(|u| u.name != username);
}
}

View file

@ -64,7 +64,7 @@ async fn put_settings_golden_path() {
let request = protocol::put_settings(dto::NewSettings {
album_art_pattern: Some("test_pattern".to_owned()),
reindex_every_n_seconds: Some(31),
ddns_update_url: Some("ddns_url".to_owned()),
ddns_update_url: Some("http://example.com/".to_owned()),
});
let response = service.fetch(&request).await;
assert_eq!(response.status(), StatusCode::OK);
@ -77,7 +77,7 @@ async fn put_settings_golden_path() {
&Settings {
album_art_pattern: "test_pattern".to_owned(),
reindex_every_n_seconds: 31,
ddns_update_url: "ddns_url".to_owned(),
ddns_update_url: "http://example.com/".to_owned(),
},
);
}