Implements more endpoints

This commit is contained in:
Antoine Gersant 2024-07-13 15:48:08 -07:00
parent 5c4631c673
commit 03d5568765
5 changed files with 193 additions and 13 deletions

View file

@ -34,6 +34,18 @@ impl FromRef<App> for app::config::Manager {
}
}
impl FromRef<App> for app::ddns::Manager {
fn from_ref(app: &App) -> Self {
app.ddns_manager.clone()
}
}
impl FromRef<App> for app::index::Index {
fn from_ref(app: &App) -> Self {
app.index.clone()
}
}
impl FromRef<App> for app::user::Manager {
fn from_ref(app: &App) -> Self {
app.user_manager.clone()
@ -45,3 +57,9 @@ impl FromRef<App> for app::settings::Manager {
app.settings_manager.clone()
}
}
impl FromRef<App> for app::vfs::Manager {
fn from_ref(app: &App) -> Self {
app.vfs_manager.clone()
}
}

View file

@ -1,15 +1,15 @@
use axum::{
extract::State,
routing::{get, put},
extract::{Path, State},
routing::{delete, get, post, put},
Json, Router,
};
use crate::{
app::{config, settings, user, App},
app::{config, ddns, index, settings, user, vfs, App},
server::{dto, error::APIError},
};
use super::auth::AdminRights;
use super::auth::{AdminRights, Auth};
pub fn router() -> Router<App> {
Router::new()
@ -18,6 +18,18 @@ pub fn router() -> Router<App> {
.route("/config", put(put_config))
.route("/settings", get(get_settings))
.route("/settings", put(put_settings))
.route("/mount_dirs", get(get_mount_dirs))
.route("/mount_dirs", put(put_mount_dirs))
.route("/ddns", get(get_ddns))
.route("/ddns", put(put_ddns))
.route("/auth", post(post_auth))
.route("/user", post(post_user))
.route("/user/:name", delete(delete_user))
.route("/user/:name", put(put_user))
.route("/users", get(get_users))
.route("/preferences", get(get_preferences))
.route("/preferences", put(put_preferences))
.route("/trigger_index", post(post_trigger_index))
}
async fn get_version() -> Json<dto::Version> {
@ -68,3 +80,142 @@ async fn put_settings(
.await?;
Ok(())
}
async fn get_mount_dirs(
_admin_rights: AdminRights,
State(vfs_manager): State<vfs::Manager>,
) -> Result<Json<Vec<dto::MountDir>>, APIError> {
let mount_dirs = vfs_manager.mount_dirs().await?;
let mount_dirs = mount_dirs.into_iter().map(|m| m.into()).collect();
Ok(Json(mount_dirs))
}
async fn put_mount_dirs(
_admin_rights: AdminRights,
State(vfs_manager): State<vfs::Manager>,
new_mount_dirs: Json<Vec<dto::MountDir>>,
) -> Result<(), APIError> {
let new_mount_dirs: Vec<vfs::MountDir> =
new_mount_dirs.iter().cloned().map(|m| m.into()).collect();
vfs_manager.set_mount_dirs(&new_mount_dirs).await?;
Ok(())
}
async fn get_ddns(
_admin_rights: AdminRights,
State(ddns_manager): State<ddns::Manager>,
) -> Result<Json<dto::DDNSConfig>, APIError> {
let ddns_config = ddns_manager.config().await?;
Ok(Json(ddns_config.into()))
}
async fn put_ddns(
_admin_rights: AdminRights,
State(ddns_manager): State<ddns::Manager>,
Json(new_ddns_config): Json<dto::DDNSConfig>,
) -> Result<(), APIError> {
ddns_manager.set_config(&new_ddns_config.into()).await?;
Ok(())
}
async fn post_auth(
State(user_manager): State<user::Manager>,
credentials: Json<dto::Credentials>,
) -> Result<Json<dto::Authorization>, APIError> {
let username = credentials.username.clone();
let user::AuthToken(token) = user_manager
.login(&credentials.username, &credentials.password)
.await?;
let is_admin = user_manager.is_admin(&credentials.username).await?;
let authorization = dto::Authorization {
username: username.clone(),
token,
is_admin,
};
Ok(Json(authorization))
}
async fn get_users(
_admin_rights: AdminRights,
State(user_manager): State<user::Manager>,
) -> Result<Json<Vec<dto::User>>, APIError> {
let users = user_manager.list().await?;
let users = users.into_iter().map(|u| u.into()).collect();
Ok(Json(users))
}
async fn post_user(
_admin_rights: AdminRights,
State(user_manager): State<user::Manager>,
Json(new_user): Json<dto::NewUser>,
) -> Result<(), APIError> {
user_manager.create(&new_user.into()).await?;
Ok(())
}
async fn put_user(
admin_rights: AdminRights,
State(user_manager): State<user::Manager>,
Path(name): Path<String>,
user_update: Json<dto::UserUpdate>,
) -> Result<(), APIError> {
if let Some(auth) = &admin_rights.get_auth() {
if auth.get_username() == name.as_str() && user_update.new_is_admin == Some(false) {
return Err(APIError::OwnAdminPrivilegeRemoval);
}
}
if let Some(password) = &user_update.new_password {
user_manager.set_password(&name, password).await?;
}
if let Some(is_admin) = &user_update.new_is_admin {
user_manager.set_is_admin(&name, *is_admin).await?;
}
Ok(())
}
async fn delete_user(
admin_rights: AdminRights,
State(user_manager): State<user::Manager>,
Path(name): Path<String>,
) -> Result<(), APIError> {
if let Some(auth) = &admin_rights.get_auth() {
if auth.get_username() == name.as_str() {
return Err(APIError::DeletingOwnAccount);
}
}
user_manager.delete(&name).await?;
Ok(())
}
async fn get_preferences(
auth: Auth,
State(user_manager): State<user::Manager>,
) -> Result<Json<user::Preferences>, APIError> {
let preferences = user_manager.read_preferences(auth.get_username()).await?;
Ok(Json(preferences))
}
async fn put_preferences(
auth: Auth,
State(user_manager): State<user::Manager>,
Json(preferences): Json<user::Preferences>,
) -> Result<(), APIError> {
user_manager
.write_preferences(auth.get_username(), &preferences)
.await?;
Ok(())
}
async fn post_trigger_index(
_admin_rights: AdminRights,
State(index): State<index::Index>,
) -> Result<(), APIError> {
index.trigger_reindex();
Ok(())
}

View file

@ -15,6 +15,12 @@ pub struct Auth {
username: String,
}
impl Auth {
pub fn get_username(&self) -> &String {
return &self.username;
}
}
#[async_trait]
impl<S> FromRequestParts<S> for Auth
where
@ -58,6 +64,12 @@ pub struct AdminRights {
auth: Option<Auth>,
}
impl AdminRights {
pub fn get_auth(&self) -> &Option<Auth> {
return &self.auth;
}
}
#[async_trait]
impl<S> FromRequestParts<S> for AdminRights
where
@ -69,14 +81,12 @@ where
async fn from_request_parts(parts: &mut Parts, app: &S) -> Result<Self, Self::Rejection> {
let user_manager = user::Manager::from_ref(app);
let auth_future = Auth::from_request_parts(parts, app);
let user_count = user_manager.count().await?;
if user_count == 0 {
return Ok(AdminRights { auth: None });
}
let auth = auth_future.await?;
let auth = Auth::from_request_parts(parts, app).await?;
if user_manager.is_admin(&auth.username).await? {
Ok(AdminRights { auth: Some(auth) })
} else {

View file

@ -8,7 +8,7 @@ impl IntoResponse for APIError {
let message = self.to_string();
let status_code = match self {
APIError::AuthorizationTokenEncoding => StatusCode::INTERNAL_SERVER_ERROR,
APIError::AdminPermissionRequired => StatusCode::UNAUTHORIZED,
APIError::AdminPermissionRequired => StatusCode::FORBIDDEN,
APIError::AudioFileIOError => StatusCode::NOT_FOUND,
APIError::AuthenticationRequired => StatusCode::UNAUTHORIZED,
APIError::BrancaTokenEncoding => StatusCode::INTERNAL_SERVER_ERROR,

View file

@ -32,13 +32,14 @@ async fn get_ddns_config_golden_path() {
#[tokio::test]
async fn put_ddns_config_requires_admin() {
let mut service = ServiceType::new(&test_name!()).await;
let request = protocol::put_ddns_config(dto::DDNSConfig {
host: "test".to_owned(),
username: "test".to_owned(),
password: "test".to_owned(),
});
service.complete_initial_setup().await;
let request = protocol::put_ddns_config(dto::DDNSConfig {
host: "host".to_owned(),
username: "ddns_user".to_owned(),
password: "ddns_password".to_owned(),
});
let response = service.fetch(&request).await;
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);