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