Can register new clients

This commit is contained in:
2025-01-27 21:31:33 +01:00
parent bfbc2a690b
commit 28b64b4475
6 changed files with 114 additions and 18 deletions

View File

@ -1,12 +1,14 @@
use crate::app_config::AppConfig;
use crate::constants::{STATE_KEY, USER_SESSION_KEY};
use crate::server::{HttpFailure, HttpResult};
use crate::user::{User, UserConfig, UserID};
use crate::user::{APIClient, User, UserConfig, UserID};
use crate::utils;
use actix_session::Session;
use actix_web::{web, HttpResponse};
use askama::Template;
use ipnet::IpNet;
use light_openid::primitives::OpenIDConfig;
use std::str::FromStr;
/// Static assets
#[derive(rust_embed::Embed)]
@ -36,17 +38,21 @@ struct HomeTemplate {
error_message: Option<String>,
}
/// Update matrix token request
/// HTTP form request
#[derive(serde::Deserialize)]
pub struct UpdateMatrixToken {
pub struct FormRequest {
/// Update matrix token
new_matrix_token: Option<String>,
/// Create a new client
new_client_desc: Option<String>,
/// Restrict new client to a given network
ip_network: Option<String>,
}
/// Main route
pub async fn home(
session: Session,
update_matrix_token: Option<web::Form<UpdateMatrixToken>>,
) -> HttpResult {
pub async fn home(session: Session, form_req: Option<web::Form<FormRequest>>) -> HttpResult {
// Get user information, requesting authentication if information is missing
let Some(user): Option<User> = session.get(USER_SESSION_KEY)? else {
// Generate auth state
@ -73,9 +79,9 @@ pub async fn home(
.await
.map_err(HttpFailure::FetchUserConfig)?;
// Update matrix token, if requested
if let Some(update_matrix_token) = update_matrix_token {
if let Some(t) = update_matrix_token.0.new_matrix_token {
if let Some(form_req) = form_req {
// Update matrix token, if requested
if let Some(t) = form_req.0.new_matrix_token {
if t.len() < 3 {
error_message = Some("Specified Matrix token is too short!".to_string());
} else {
@ -85,6 +91,28 @@ pub async fn home(
success_message = Some("Matrix token was successfully updated!".to_string());
}
}
// Create a new client, if requested
if let Some(new_token_desc) = form_req.0.new_client_desc {
let ip_net = match form_req.0.ip_network.as_deref() {
None | Some("") => None,
Some(e) => match IpNet::from_str(e) {
Ok(n) => Some(n),
Err(e) => {
log::error!("Failed to parse IP network provided by user: {e}");
error_message = Some(format!("Failed to parse restricted IP network: {e}"));
None
}
},
};
if error_message.is_none() {
let token = APIClient::generate(new_token_desc, ip_net);
success_message = Some(format!("The secret of your new token is '{}'. Be sure to write it somewhere as you will not be able to recover it later!", token.secret));
config.clients.push(token);
config.save().await?;
}
}
}
// Render page

View File

@ -1,10 +1,11 @@
use crate::app_config::AppConfig;
use crate::utils::curr_time;
use s3::error::S3Error;
use s3::request::ResponseData;
use s3::{Bucket, BucketConfiguration};
use thiserror::Error;
use crate::app_config::AppConfig;
use crate::utils::{curr_time, rand_str};
#[derive(Error, Debug)]
pub enum UserError {
#[error("failed to fetch user configuration: {0}")]
@ -27,6 +28,34 @@ pub struct User {
pub email: String,
}
/// Single API client information
#[derive(serde::Serialize, serde::Deserialize)]
pub struct APIClient {
/// Client unique ID
pub id: uuid::Uuid,
/// Client description
pub description: String,
/// Restricted API network for token
pub network: Option<ipnet::IpNet>,
/// Client secret
pub secret: String,
}
impl APIClient {
/// Generate a new API client
pub fn generate(description: String, network: Option<ipnet::IpNet>) -> Self {
Self {
id: Default::default(),
description,
network,
secret: rand_str(20),
}
}
}
#[derive(serde::Serialize, serde::Deserialize)]
pub struct UserConfig {
/// Target user ID
@ -40,6 +69,9 @@ pub struct UserConfig {
/// Current user matrix token
pub matrix_token: String,
/// API clients
pub clients: Vec<APIClient>,
}
impl UserConfig {
@ -97,6 +129,7 @@ impl UserConfig {
created: curr_time()?,
updated: curr_time()?,
matrix_token: "".to_string(),
clients: vec![],
})
}
Err(e) => Err(UserError::FetchUserConfig(e).into()),