Display the list of registered clients
This commit is contained in:
		
							
								
								
									
										62
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										62
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -290,6 +290,21 @@ dependencies = [
 | 
			
		||||
 "alloc-no-stdlib",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "android-tzdata"
 | 
			
		||||
version = "0.1.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "android_system_properties"
 | 
			
		||||
version = "0.1.5"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "libc",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "anstream"
 | 
			
		||||
version = "0.6.18"
 | 
			
		||||
@@ -584,6 +599,20 @@ version = "1.0.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "chrono"
 | 
			
		||||
version = "0.4.39"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "android-tzdata",
 | 
			
		||||
 "iana-time-zone",
 | 
			
		||||
 "js-sys",
 | 
			
		||||
 "num-traits",
 | 
			
		||||
 "wasm-bindgen",
 | 
			
		||||
 "windows-targets",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cipher"
 | 
			
		||||
version = "0.4.4"
 | 
			
		||||
@@ -1314,6 +1343,29 @@ dependencies = [
 | 
			
		||||
 "tracing",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "iana-time-zone"
 | 
			
		||||
version = "0.1.61"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "android_system_properties",
 | 
			
		||||
 "core-foundation-sys",
 | 
			
		||||
 "iana-time-zone-haiku",
 | 
			
		||||
 "js-sys",
 | 
			
		||||
 "wasm-bindgen",
 | 
			
		||||
 "windows-core",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "iana-time-zone-haiku"
 | 
			
		||||
version = "0.1.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cc",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "icu_collections"
 | 
			
		||||
version = "1.5.0"
 | 
			
		||||
@@ -1609,6 +1661,7 @@ dependencies = [
 | 
			
		||||
 "actix-web",
 | 
			
		||||
 "anyhow",
 | 
			
		||||
 "askama",
 | 
			
		||||
 "chrono",
 | 
			
		||||
 "clap",
 | 
			
		||||
 "env_logger",
 | 
			
		||||
 "ipnet",
 | 
			
		||||
@@ -3051,6 +3104,15 @@ dependencies = [
 | 
			
		||||
 "windows-sys 0.59.0",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows-core"
 | 
			
		||||
version = "0.52.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "windows-targets",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows-registry"
 | 
			
		||||
version = "0.2.0"
 | 
			
		||||
 
 | 
			
		||||
@@ -22,4 +22,5 @@ mime_guess = "2.0.5"
 | 
			
		||||
askama = "0.12.1"
 | 
			
		||||
urlencoding = "2.1.3"
 | 
			
		||||
uuid = { version = "1.12.1", features = ["v4", "serde"] }
 | 
			
		||||
ipnet = { version = "2.11.0", features = ["serde"] }
 | 
			
		||||
ipnet = { version = "2.11.0", features = ["serde"] }
 | 
			
		||||
chrono = "0.4.39"
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
.body-content {
 | 
			
		||||
    max-width: 700px;
 | 
			
		||||
    max-width: 900px;
 | 
			
		||||
    margin: 50px auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,3 +3,6 @@ pub const STATE_KEY: &str = "oidc-state";
 | 
			
		||||
 | 
			
		||||
/// Session key for user information
 | 
			
		||||
pub const USER_SESSION_KEY: &str = "user";
 | 
			
		||||
 | 
			
		||||
/// Token length
 | 
			
		||||
pub const TOKEN_LEN: usize = 20;
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,7 @@ pub async fn static_file(path: web::Path<String>) -> HttpResult {
 | 
			
		||||
struct HomeTemplate {
 | 
			
		||||
    name: String,
 | 
			
		||||
    matrix_token: String,
 | 
			
		||||
    clients: Vec<APIClient>,
 | 
			
		||||
    success_message: Option<String>,
 | 
			
		||||
    error_message: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
@@ -122,6 +123,7 @@ pub async fn home(session: Session, form_req: Option<web::Form<FormRequest>>) ->
 | 
			
		||||
            HomeTemplate {
 | 
			
		||||
                name: user.name,
 | 
			
		||||
                matrix_token: config.obfuscated_matrix_token(),
 | 
			
		||||
                clients: config.clients,
 | 
			
		||||
                success_message,
 | 
			
		||||
                error_message,
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								src/user.rs
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								src/user.rs
									
									
									
									
									
								
							@@ -4,7 +4,8 @@ use s3::{Bucket, BucketConfiguration};
 | 
			
		||||
use thiserror::Error;
 | 
			
		||||
 | 
			
		||||
use crate::app_config::AppConfig;
 | 
			
		||||
use crate::utils::{curr_time, rand_str};
 | 
			
		||||
use crate::constants::TOKEN_LEN;
 | 
			
		||||
use crate::utils::{curr_time, format_time, rand_str};
 | 
			
		||||
 | 
			
		||||
#[derive(Error, Debug)]
 | 
			
		||||
pub enum UserError {
 | 
			
		||||
@@ -42,16 +43,34 @@ pub struct APIClient {
 | 
			
		||||
 | 
			
		||||
    /// Client secret
 | 
			
		||||
    pub secret: String,
 | 
			
		||||
 | 
			
		||||
    /// Client creation time
 | 
			
		||||
    pub created: u64,
 | 
			
		||||
 | 
			
		||||
    /// Client last usage time
 | 
			
		||||
    pub used: u64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl APIClient {
 | 
			
		||||
    pub fn fmt_created(&self) -> String {
 | 
			
		||||
        format_time(self.created).unwrap_or_default()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn fmt_used(&self) -> String {
 | 
			
		||||
        format_time(self.used).unwrap_or_default()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl APIClient {
 | 
			
		||||
    /// Generate a new API client
 | 
			
		||||
    pub fn generate(description: String, network: Option<ipnet::IpNet>) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            id: Default::default(),
 | 
			
		||||
            id: uuid::Uuid::new_v4(),
 | 
			
		||||
            description,
 | 
			
		||||
            network,
 | 
			
		||||
            secret: rand_str(20),
 | 
			
		||||
            secret: rand_str(TOKEN_LEN),
 | 
			
		||||
            created: curr_time().unwrap(),
 | 
			
		||||
            used: curr_time().unwrap(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,3 +12,9 @@ pub fn curr_time() -> anyhow::Result<u64> {
 | 
			
		||||
        .duration_since(UNIX_EPOCH)
 | 
			
		||||
        .map(|t| t.as_secs())?)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Format time
 | 
			
		||||
pub fn format_time(time: u64) -> Option<String> {
 | 
			
		||||
    let time = chrono::DateTime::from_timestamp(time as i64, 0)?;
 | 
			
		||||
    Some(time.naive_local().to_string())
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
<header data-bs-theme="dark">
 | 
			
		||||
    <div class="navbar navbar-dark bg-dark shadow-sm">
 | 
			
		||||
        <div class="container">
 | 
			
		||||
            <a href="#" class="navbar-brand d-flex align-items-center">
 | 
			
		||||
            <a href="/" class="navbar-brand d-flex align-items-center">
 | 
			
		||||
                <svg xxmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" stroke="currentColor"
 | 
			
		||||
                     stroke-linecap="round" stroke-linejoin="round" stroke-width="1" aria-hidden="true" class="me-2"
 | 
			
		||||
                     viewBox="0 0 24 24">
 | 
			
		||||
@@ -46,6 +46,51 @@
 | 
			
		||||
    </div>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
 | 
			
		||||
    <!-- Display clients list -->
 | 
			
		||||
    <div class="card border-light mb-3">
 | 
			
		||||
        <div class="card-header">Registered clients</div>
 | 
			
		||||
        <div class="card-body">
 | 
			
		||||
            {% if clients.len() > 0 %}
 | 
			
		||||
            <table class="table table-hover">
 | 
			
		||||
                <thead>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <th scope="col">ID</th>
 | 
			
		||||
                    <th scope="col">Description</th>
 | 
			
		||||
                    <th scope="col">Network</th>
 | 
			
		||||
                    <th scope="col">Created</th>
 | 
			
		||||
                    <th scope="col">Used</th>
 | 
			
		||||
                    <th scope="col"></th>
 | 
			
		||||
                </tr>
 | 
			
		||||
                </thead>
 | 
			
		||||
                <tbody>
 | 
			
		||||
                {% for client in clients %}
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <th scope="row">{{ client.id }}</th>
 | 
			
		||||
                    <td>{{ client.description }}</td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        {% if let Some(net) = client.network %}
 | 
			
		||||
                        {{ net }}
 | 
			
		||||
                        {% else %}
 | 
			
		||||
                        <i>Unrestricted</i>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </td>
 | 
			
		||||
                    <td>{{ client.fmt_created() }}</td>
 | 
			
		||||
                    <td>{{ client.fmt_used() }}</td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        <button type="button" class="btn btn-danger btn-sm">Delete</button>
 | 
			
		||||
                    </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                {% endfor %}
 | 
			
		||||
                </tbody>
 | 
			
		||||
            </table>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
 | 
			
		||||
            {% if clients.len() == 0 %}
 | 
			
		||||
            <p>No client registered yet!</p>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!-- New client -->
 | 
			
		||||
    <div class="card border-light mb-3">
 | 
			
		||||
        <div class="card-header">New client</div>
 | 
			
		||||
@@ -54,13 +99,14 @@
 | 
			
		||||
                <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" />
 | 
			
		||||
                           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" />
 | 
			
		||||
                           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>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user