diff options
| author | Mica White <botahamec@outlook.com> | 2025-12-08 20:08:21 -0500 |
|---|---|---|
| committer | Mica White <botahamec@outlook.com> | 2025-12-08 20:08:21 -0500 |
| commit | 608ce1d9910cd68ce825838ea313e02c598f908e (patch) | |
| tree | 0bd4ad26f86e5c873f97308983112b0ffe593df3 /src/models | |
| parent | 93fd2e82e8fdc5ee62739053385f8ccffc660f02 (diff) | |
Diffstat (limited to 'src/models')
| -rw-r--r-- | src/models/client.rs | 330 | ||||
| -rw-r--r-- | src/models/mod.rs | 4 | ||||
| -rw-r--r-- | src/models/user.rs | 98 |
3 files changed, 216 insertions, 216 deletions
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<str>, - secret: Option<PasswordHash>, - allowed_scopes: Box<[Box<str>]>, - default_scopes: Option<Box<[Box<str>]>>, - 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<H: std::hash::Hasher>(&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<str>]>, - default_scopes: Option<Box<[Box<str>]>>, - redirect_uris: &[Url], - trusted: bool, - ) -> Result<Self, Expect<CreateClientError>> { - 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<u8> { - self.secret.as_ref().map(|s| s.version()) - } - - pub fn allowed_scopes(&self) -> String { - self.allowed_scopes.join(" ") - } - - pub fn default_scopes(&self) -> Option<String> { - self.default_scopes.clone().map(|s| s.join(" ")) - } - - pub fn is_trusted(&self) -> bool { - self.trusted - } - - pub fn check_secret(&self, secret: &str) -> Option<Result<bool, RawUnexpected>> { - 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<str>,
+ secret: Option<PasswordHash>,
+ allowed_scopes: Box<[Box<str>]>,
+ default_scopes: Option<Box<[Box<str>]>>,
+ 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<H: std::hash::Hasher>(&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<str>]>,
+ default_scopes: Option<Box<[Box<str>]>>,
+ redirect_uris: &[Url],
+ trusted: bool,
+ ) -> Result<Self, Expect<CreateClientError>> {
+ 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<u8> {
+ self.secret.as_ref().map(|s| s.version())
+ }
+
+ pub fn allowed_scopes(&self) -> String {
+ self.allowed_scopes.join(" ")
+ }
+
+ pub fn default_scopes(&self) -> Option<String> {
+ self.default_scopes.clone().map(|s| s.join(" "))
+ }
+
+ pub fn is_trusted(&self) -> bool {
+ self.trusted
+ }
+
+ pub fn check_secret(&self, secret: &str) -> Option<Result<bool, RawUnexpected>> {
+ 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<str>, - 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<H: std::hash::Hasher>(&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<bool, RawUnexpected> { - 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<str>,
+ 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<H: std::hash::Hasher>(&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<bool, RawUnexpected> {
+ self.password.check_password(password)
+ }
+}
|
