From 608ce1d9910cd68ce825838ea313e02c598f908e Mon Sep 17 00:00:00 2001 From: Mica White Date: Mon, 8 Dec 2025 20:08:21 -0500 Subject: Stuff --- src/models/client.rs | 330 +++++++++++++++++++++++++-------------------------- src/models/mod.rs | 4 +- src/models/user.rs | 98 +++++++-------- 3 files changed, 216 insertions(+), 216 deletions(-) (limited to 'src/models') diff --git a/src/models/client.rs b/src/models/client.rs index 38be37f..6d0c909 100644 --- a/src/models/client.rs +++ b/src/models/client.rs @@ -1,165 +1,165 @@ -use std::{hash::Hash, marker::PhantomData}; - -use actix_web::{http::StatusCode, ResponseError}; -use exun::{Expect, RawUnexpected}; -use raise::yeet; -use serde::{Deserialize, Serialize}; -use thiserror::Error; -use url::Url; -use uuid::Uuid; - -use crate::services::crypto::PasswordHash; - -/// There are two types of clients, based on their ability to maintain the -/// security of their client credentials. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)] -#[sqlx(rename_all = "lowercase")] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum ClientType { - /// A client that is capable of maintaining the confidentiality of their - /// credentials, or capable of secure client authentication using other - /// means. An example would be a secure server with restricted access to - /// the client credentials. - Confidential, - /// A client that is incapable of maintaining the confidentiality of their - /// credentials and cannot authenticate securely by any other means, such - /// as an installed application, or a web-browser based application. - Public, -} - -#[derive(Debug, Clone)] -pub struct Client { - id: Uuid, - ty: ClientType, - alias: Box, - secret: Option, - allowed_scopes: Box<[Box]>, - default_scopes: Option]>>, - redirect_uris: Box<[Url]>, - trusted: bool, -} - -impl PartialEq for Client { - fn eq(&self, other: &Self) -> bool { - self.id == other.id - } -} - -impl Eq for Client {} - -impl Hash for Client { - fn hash(&self, state: &mut H) { - state.write_u128(self.id.as_u128()) - } -} - -#[derive(Debug, Clone, Copy, Error)] -#[error("Confidential clients must have a secret, but it was not provided")] -pub enum CreateClientError { - #[error("Confidential clients must have a secret, but it was not provided")] - NoSecret, - #[error("Only confidential clients may be trusted")] - TrustedError, - #[error("Redirect URIs must not include a fragment component")] - UriFragment, - #[error("Redirect URIs must use HTTPS")] - NonHttpsUri, -} - -impl ResponseError for CreateClientError { - fn status_code(&self) -> StatusCode { - StatusCode::BAD_REQUEST - } -} - -impl Client { - pub fn new( - id: Uuid, - alias: &str, - ty: ClientType, - secret: Option<&str>, - allowed_scopes: Box<[Box]>, - default_scopes: Option]>>, - redirect_uris: &[Url], - trusted: bool, - ) -> Result> { - let secret = if let Some(secret) = secret { - Some(PasswordHash::new(secret)?) - } else { - None - }; - - if ty == ClientType::Confidential && secret.is_none() { - yeet!(CreateClientError::NoSecret.into()); - } - - if ty == ClientType::Public && trusted { - yeet!(CreateClientError::TrustedError.into()); - } - - for redirect_uri in redirect_uris { - if redirect_uri.scheme() != "https" { - yeet!(CreateClientError::NonHttpsUri.into()) - } - - if redirect_uri.fragment().is_some() { - yeet!(CreateClientError::UriFragment.into()) - } - } - - Ok(Self { - id, - alias: Box::from(alias), - ty, - secret, - allowed_scopes, - default_scopes, - redirect_uris: redirect_uris.into_iter().cloned().collect(), - trusted, - }) - } - - pub fn id(&self) -> Uuid { - self.id - } - - pub fn alias(&self) -> &str { - &self.alias - } - - pub fn client_type(&self) -> ClientType { - self.ty - } - - pub fn redirect_uris(&self) -> &[Url] { - &self.redirect_uris - } - - pub fn secret_hash(&self) -> Option<&[u8]> { - self.secret.as_ref().map(|s| s.hash()) - } - - pub fn secret_salt(&self) -> Option<&[u8]> { - self.secret.as_ref().map(|s| s.salt()) - } - - pub fn secret_version(&self) -> Option { - self.secret.as_ref().map(|s| s.version()) - } - - pub fn allowed_scopes(&self) -> String { - self.allowed_scopes.join(" ") - } - - pub fn default_scopes(&self) -> Option { - self.default_scopes.clone().map(|s| s.join(" ")) - } - - pub fn is_trusted(&self) -> bool { - self.trusted - } - - pub fn check_secret(&self, secret: &str) -> Option> { - self.secret.as_ref().map(|s| s.check_password(secret)) - } -} +use std::{hash::Hash, marker::PhantomData}; + +use actix_web::{http::StatusCode, ResponseError}; +use exun::{Expect, RawUnexpected}; +use raise::yeet; +use serde::{Deserialize, Serialize}; +use thiserror::Error; +use url::Url; +use uuid::Uuid; + +use crate::services::crypto::PasswordHash; + +/// There are two types of clients, based on their ability to maintain the +/// security of their client credentials. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)] +#[sqlx(rename_all = "lowercase")] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum ClientType { + /// A client that is capable of maintaining the confidentiality of their + /// credentials, or capable of secure client authentication using other + /// means. An example would be a secure server with restricted access to + /// the client credentials. + Confidential, + /// A client that is incapable of maintaining the confidentiality of their + /// credentials and cannot authenticate securely by any other means, such + /// as an installed application, or a web-browser based application. + Public, +} + +#[derive(Debug, Clone)] +pub struct Client { + id: Uuid, + ty: ClientType, + alias: Box, + secret: Option, + allowed_scopes: Box<[Box]>, + default_scopes: Option]>>, + redirect_uris: Box<[Url]>, + trusted: bool, +} + +impl PartialEq for Client { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl Eq for Client {} + +impl Hash for Client { + fn hash(&self, state: &mut H) { + state.write_u128(self.id.as_u128()) + } +} + +#[derive(Debug, Clone, Copy, Error)] +#[error("Confidential clients must have a secret, but it was not provided")] +pub enum CreateClientError { + #[error("Confidential clients must have a secret, but it was not provided")] + NoSecret, + #[error("Only confidential clients may be trusted")] + TrustedError, + #[error("Redirect URIs must not include a fragment component")] + UriFragment, + #[error("Redirect URIs must use HTTPS")] + NonHttpsUri, +} + +impl ResponseError for CreateClientError { + fn status_code(&self) -> StatusCode { + StatusCode::BAD_REQUEST + } +} + +impl Client { + pub fn new( + id: Uuid, + alias: &str, + ty: ClientType, + secret: Option<&str>, + allowed_scopes: Box<[Box]>, + default_scopes: Option]>>, + redirect_uris: &[Url], + trusted: bool, + ) -> Result> { + let secret = if let Some(secret) = secret { + Some(PasswordHash::new(secret)?) + } else { + None + }; + + if ty == ClientType::Confidential && secret.is_none() { + yeet!(CreateClientError::NoSecret.into()); + } + + if ty == ClientType::Public && trusted { + yeet!(CreateClientError::TrustedError.into()); + } + + for redirect_uri in redirect_uris { + if redirect_uri.scheme() != "https" { + yeet!(CreateClientError::NonHttpsUri.into()) + } + + if redirect_uri.fragment().is_some() { + yeet!(CreateClientError::UriFragment.into()) + } + } + + Ok(Self { + id, + alias: Box::from(alias), + ty, + secret, + allowed_scopes, + default_scopes, + redirect_uris: redirect_uris.into_iter().cloned().collect(), + trusted, + }) + } + + pub fn id(&self) -> Uuid { + self.id + } + + pub fn alias(&self) -> &str { + &self.alias + } + + pub fn client_type(&self) -> ClientType { + self.ty + } + + pub fn redirect_uris(&self) -> &[Url] { + &self.redirect_uris + } + + pub fn secret_hash(&self) -> Option<&[u8]> { + self.secret.as_ref().map(|s| s.hash()) + } + + pub fn secret_salt(&self) -> Option<&[u8]> { + self.secret.as_ref().map(|s| s.salt()) + } + + pub fn secret_version(&self) -> Option { + self.secret.as_ref().map(|s| s.version()) + } + + pub fn allowed_scopes(&self) -> String { + self.allowed_scopes.join(" ") + } + + pub fn default_scopes(&self) -> Option { + self.default_scopes.clone().map(|s| s.join(" ")) + } + + pub fn is_trusted(&self) -> bool { + self.trusted + } + + pub fn check_secret(&self, secret: &str) -> Option> { + self.secret.as_ref().map(|s| s.check_password(secret)) + } +} diff --git a/src/models/mod.rs b/src/models/mod.rs index 633f846..1379893 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,2 +1,2 @@ -pub mod client; -pub mod user; +pub mod client; +pub mod user; diff --git a/src/models/user.rs b/src/models/user.rs index 8555ee2..493a267 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -1,49 +1,49 @@ -use std::hash::Hash; - -use exun::RawUnexpected; -use uuid::Uuid; - -use crate::services::crypto::PasswordHash; - -#[derive(Debug, Clone)] -pub struct User { - pub id: Uuid, - pub username: Box, - pub password: PasswordHash, -} - -impl PartialEq for User { - fn eq(&self, other: &Self) -> bool { - self.id == other.id - } -} - -impl Eq for User {} - -impl Hash for User { - fn hash(&self, state: &mut H) { - state.write_u128(self.id.as_u128()) - } -} - -impl User { - pub fn username(&self) -> &str { - &self.username - } - - pub fn password_hash(&self) -> &[u8] { - self.password.hash() - } - - pub fn password_salt(&self) -> &[u8] { - self.password.salt() - } - - pub fn password_version(&self) -> u8 { - self.password.version() - } - - pub fn check_password(&self, password: &str) -> Result { - self.password.check_password(password) - } -} +use std::hash::Hash; + +use exun::RawUnexpected; +use uuid::Uuid; + +use crate::services::crypto::PasswordHash; + +#[derive(Debug, Clone)] +pub struct User { + pub id: Uuid, + pub username: Box, + pub password: PasswordHash, +} + +impl PartialEq for User { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl Eq for User {} + +impl Hash for User { + fn hash(&self, state: &mut H) { + state.write_u128(self.id.as_u128()) + } +} + +impl User { + pub fn username(&self) -> &str { + &self.username + } + + pub fn password_hash(&self) -> &[u8] { + self.password.hash() + } + + pub fn password_salt(&self) -> &[u8] { + self.password.salt() + } + + pub fn password_version(&self) -> u8 { + self.password.version() + } + + pub fn check_password(&self, password: &str) -> Result { + self.password.check_password(password) + } +} -- cgit v1.2.3