Can register new clients
This commit is contained in:
parent
bfbc2a690b
commit
28b64b4475
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -1483,6 +1483,9 @@ name = "ipnet"
|
|||||||
version = "2.11.0"
|
version = "2.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is_terminal_polyfill"
|
name = "is_terminal_polyfill"
|
||||||
@ -1608,6 +1611,7 @@ dependencies = [
|
|||||||
"askama",
|
"askama",
|
||||||
"clap",
|
"clap",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"ipnet",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"light-openid",
|
"light-openid",
|
||||||
"log",
|
"log",
|
||||||
@ -1619,6 +1623,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror 2.0.11",
|
"thiserror 2.0.11",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2887,6 +2892,16 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.15",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
@ -20,4 +20,6 @@ rand = "0.9.0-beta.3"
|
|||||||
rust-embed = "8.5.0"
|
rust-embed = "8.5.0"
|
||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
askama = "0.12.1"
|
askama = "0.12.1"
|
||||||
urlencoding = "2.1.3"
|
urlencoding = "2.1.3"
|
||||||
|
uuid = { version = "1.12.1", features = ["v4", "serde"] }
|
||||||
|
ipnet = { version = "2.11.0", features = ["serde"] }
|
@ -1,8 +1,3 @@
|
|||||||
# This compose file is compatible with Compose itself, it might need some
|
|
||||||
# adjustments to run properly with stack.
|
|
||||||
|
|
||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
synapse:
|
synapse:
|
||||||
image: docker.io/matrixdotorg/synapse:latest
|
image: docker.io/matrixdotorg/synapse:latest
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
use crate::app_config::AppConfig;
|
use crate::app_config::AppConfig;
|
||||||
use crate::constants::{STATE_KEY, USER_SESSION_KEY};
|
use crate::constants::{STATE_KEY, USER_SESSION_KEY};
|
||||||
use crate::server::{HttpFailure, HttpResult};
|
use crate::server::{HttpFailure, HttpResult};
|
||||||
use crate::user::{User, UserConfig, UserID};
|
use crate::user::{APIClient, User, UserConfig, UserID};
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
use actix_session::Session;
|
use actix_session::Session;
|
||||||
use actix_web::{web, HttpResponse};
|
use actix_web::{web, HttpResponse};
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
|
use ipnet::IpNet;
|
||||||
use light_openid::primitives::OpenIDConfig;
|
use light_openid::primitives::OpenIDConfig;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
/// Static assets
|
/// Static assets
|
||||||
#[derive(rust_embed::Embed)]
|
#[derive(rust_embed::Embed)]
|
||||||
@ -36,17 +38,21 @@ struct HomeTemplate {
|
|||||||
error_message: Option<String>,
|
error_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update matrix token request
|
/// HTTP form request
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
pub struct UpdateMatrixToken {
|
pub struct FormRequest {
|
||||||
|
/// Update matrix token
|
||||||
new_matrix_token: Option<String>,
|
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
|
/// Main route
|
||||||
pub async fn home(
|
pub async fn home(session: Session, form_req: Option<web::Form<FormRequest>>) -> HttpResult {
|
||||||
session: Session,
|
|
||||||
update_matrix_token: Option<web::Form<UpdateMatrixToken>>,
|
|
||||||
) -> HttpResult {
|
|
||||||
// Get user information, requesting authentication if information is missing
|
// Get user information, requesting authentication if information is missing
|
||||||
let Some(user): Option<User> = session.get(USER_SESSION_KEY)? else {
|
let Some(user): Option<User> = session.get(USER_SESSION_KEY)? else {
|
||||||
// Generate auth state
|
// Generate auth state
|
||||||
@ -73,9 +79,9 @@ pub async fn home(
|
|||||||
.await
|
.await
|
||||||
.map_err(HttpFailure::FetchUserConfig)?;
|
.map_err(HttpFailure::FetchUserConfig)?;
|
||||||
|
|
||||||
// Update matrix token, if requested
|
if let Some(form_req) = form_req {
|
||||||
if let Some(update_matrix_token) = update_matrix_token {
|
// Update matrix token, if requested
|
||||||
if let Some(t) = update_matrix_token.0.new_matrix_token {
|
if let Some(t) = form_req.0.new_matrix_token {
|
||||||
if t.len() < 3 {
|
if t.len() < 3 {
|
||||||
error_message = Some("Specified Matrix token is too short!".to_string());
|
error_message = Some("Specified Matrix token is too short!".to_string());
|
||||||
} else {
|
} else {
|
||||||
@ -85,6 +91,28 @@ pub async fn home(
|
|||||||
success_message = Some("Matrix token was successfully updated!".to_string());
|
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
|
// Render page
|
||||||
|
37
src/user.rs
37
src/user.rs
@ -1,10 +1,11 @@
|
|||||||
use crate::app_config::AppConfig;
|
|
||||||
use crate::utils::curr_time;
|
|
||||||
use s3::error::S3Error;
|
use s3::error::S3Error;
|
||||||
use s3::request::ResponseData;
|
use s3::request::ResponseData;
|
||||||
use s3::{Bucket, BucketConfiguration};
|
use s3::{Bucket, BucketConfiguration};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::app_config::AppConfig;
|
||||||
|
use crate::utils::{curr_time, rand_str};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum UserError {
|
pub enum UserError {
|
||||||
#[error("failed to fetch user configuration: {0}")]
|
#[error("failed to fetch user configuration: {0}")]
|
||||||
@ -27,6 +28,34 @@ pub struct User {
|
|||||||
pub email: String,
|
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)]
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
pub struct UserConfig {
|
pub struct UserConfig {
|
||||||
/// Target user ID
|
/// Target user ID
|
||||||
@ -40,6 +69,9 @@ pub struct UserConfig {
|
|||||||
|
|
||||||
/// Current user matrix token
|
/// Current user matrix token
|
||||||
pub matrix_token: String,
|
pub matrix_token: String,
|
||||||
|
|
||||||
|
/// API clients
|
||||||
|
pub clients: Vec<APIClient>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserConfig {
|
impl UserConfig {
|
||||||
@ -97,6 +129,7 @@ impl UserConfig {
|
|||||||
created: curr_time()?,
|
created: curr_time()?,
|
||||||
updated: curr_time()?,
|
updated: curr_time()?,
|
||||||
matrix_token: "".to_string(),
|
matrix_token: "".to_string(),
|
||||||
|
clients: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(e) => Err(UserError::FetchUserConfig(e).into()),
|
Err(e) => Err(UserError::FetchUserConfig(e).into()),
|
||||||
|
@ -46,6 +46,29 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- New client -->
|
||||||
|
<div class="card border-light mb-3">
|
||||||
|
<div class="card-header">New client</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form action="/" method="post">
|
||||||
|
<div>
|
||||||
|
<label for="new_client_desc" class="form-label">Description</label>
|
||||||
|
<input type="text" class="form-control" id="new_client_desc" required minlength="3"
|
||||||
|
aria-describedby="new_client_desc" placeholder="New client description..." name="new_client_desc" />
|
||||||
|
<small class="form-text text-muted">Client description helps with identification.</small>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="ip_network" class="form-label">Allowed IP network</label>
|
||||||
|
<input type="text" class="form-control" id="ip_network" aria-describedby="ip_network"
|
||||||
|
placeholder="Client network (x.x.x.x/x or x:x:x:x:x:x/x" name="ip_network" />
|
||||||
|
<small class="form-text text-muted">Restrict the networks this IP address can be used from.</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="submit" class="btn btn-primary" value="Create client"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Matrix authentication token -->
|
<!-- Matrix authentication token -->
|
||||||
<div class="card border-light mb-3">
|
<div class="card border-light mb-3">
|
||||||
<div class="card-header">Matrix authentication token</div>
|
<div class="card-header">Matrix authentication token</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user