diff options
| author | mrw1593 <botahamec@outlook.com> | 2023-06-05 21:08:07 -0400 |
|---|---|---|
| committer | mrw1593 <botahamec@outlook.com> | 2023-06-05 21:08:07 -0400 |
| commit | ce369403adc22bf9720433fb30054703eac8e6f6 (patch) | |
| tree | 82e40882bd87d2fe68794356370ce27f044ef8ac | |
| parent | 5e19831e02015047b2fb23592a82439d59b62767 (diff) | |
Update existing endpoints to have client scopes
| -rw-r--r-- | src/api/clients.rs | 40 | ||||
| -rw-r--r-- | src/models/client.rs | 23 | ||||
| -rw-r--r-- | src/services/db/client.rs | 35 |
3 files changed, 79 insertions, 19 deletions
diff --git a/src/api/clients.rs b/src/api/clients.rs index 59869ba..327a0a5 100644 --- a/src/api/clients.rs +++ b/src/api/clients.rs @@ -1,7 +1,7 @@ use actix_web::http::{header, StatusCode}; use actix_web::{get, post, put, web, HttpResponse, ResponseError, Scope}; use raise::yeet; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use sqlx::MySqlPool; use thiserror::Error; use url::Url; @@ -9,8 +9,37 @@ use uuid::Uuid; use crate::models::client::{Client, ClientType, NoSecretError}; use crate::services::crypto::PasswordHash; +use crate::services::db::ClientRow; use crate::services::{db, id}; +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +struct ClientResponse { + client_id: Uuid, + alias: Box<str>, + client_type: ClientType, + allowed_scopes: Box<[Box<str>]>, + default_scopes: Option<Box<[Box<str>]>>, +} + +impl From<ClientRow> for ClientResponse { + fn from(value: ClientRow) -> Self { + Self { + client_id: value.id, + alias: value.alias.into_boxed_str(), + client_type: value.client_type, + allowed_scopes: value + .allowed_scopes + .split_whitespace() + .map(Box::from) + .collect(), + default_scopes: value + .default_scopes + .map(|s| s.split_whitespace().map(Box::from).collect()), + } + } +} + #[derive(Debug, Clone, Copy, Error)] #[error("No client with the given client ID was found")] struct ClientNotFound { @@ -42,9 +71,10 @@ async fn get_client( }; let redirect_uris_link = format!("</clients/{client_id}/redirect-uris>; rel=\"redirect-uris\""); + let response: ClientResponse = client.into(); let response = HttpResponse::Ok() .append_header((header::LINK, redirect_uris_link)) - .json(client); + .json(response); Ok(response) } @@ -102,6 +132,8 @@ struct ClientRequest { ty: ClientType, redirect_uris: Box<[Url]>, secret: Option<Box<str>>, + allowed_scopes: Box<[Box<str>]>, + default_scopes: Option<Box<[Box<str>]>>, } #[derive(Debug, Clone, Error)] @@ -142,6 +174,8 @@ async fn create_client( &alias, body.ty, body.secret.as_deref(), + body.allowed_scopes.clone(), + body.default_scopes.clone(), &body.redirect_uris, ) .map_err(|e| e.unwrap())?; @@ -197,6 +231,8 @@ async fn update_client( &alias, body.ty, body.secret.as_deref(), + body.allowed_scopes.clone(), + body.default_scopes.clone(), &body.redirect_uris, ) .map_err(|e| e.unwrap())?; diff --git a/src/models/client.rs b/src/models/client.rs index 44079de..90c5902 100644 --- a/src/models/client.rs +++ b/src/models/client.rs @@ -4,7 +4,6 @@ use actix_web::{http::StatusCode, ResponseError}; use exun::{Expect, RawUnexpected}; use raise::yeet; use serde::{Deserialize, Serialize}; -use sqlx::FromRow; use thiserror::Error; use url::Url; use uuid::Uuid; @@ -34,17 +33,11 @@ pub struct Client { ty: ClientType, alias: Box<str>, secret: Option<PasswordHash>, + allowed_scopes: Box<[Box<str>]>, + default_scopes: Option<Box<[Box<str>]>>, redirect_uris: Box<[Url]>, } -#[derive(Debug, Clone, Serialize, FromRow)] -#[serde(rename_all = "camelCase")] -pub struct ClientResponse { - pub id: Uuid, - pub alias: String, - pub client_type: ClientType, -} - impl PartialEq for Client { fn eq(&self, other: &Self) -> bool { self.id == other.id @@ -85,6 +78,8 @@ impl Client { alias: &str, ty: ClientType, secret: Option<&str>, + allowed_scopes: Box<[Box<str>]>, + default_scopes: Option<Box<[Box<str>]>>, redirect_uris: &[Url], ) -> Result<Self, Expect<NoSecretError>> { let secret = if let Some(secret) = secret { @@ -102,6 +97,8 @@ impl Client { alias: Box::from(alias), ty: ClientType::Public, secret, + allowed_scopes, + default_scopes, redirect_uris: redirect_uris.into_iter().cloned().collect(), }) } @@ -134,6 +131,14 @@ impl Client { 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 check_secret(&self, secret: &str) -> Option<Result<bool, RawUnexpected>> { self.secret.as_ref().map(|s| s.check_password(secret)) } diff --git a/src/services/db/client.rs b/src/services/db/client.rs index 4643f49..ecf98a3 100644 --- a/src/services/db/client.rs +++ b/src/services/db/client.rs @@ -1,15 +1,26 @@ use std::str::FromStr; use exun::{RawUnexpected, ResultErrorExt}; -use sqlx::{mysql::MySqlQueryResult, query, query_as, query_scalar, Executor, MySql, Transaction}; +use sqlx::{ + mysql::MySqlQueryResult, query, query_as, query_scalar, Executor, FromRow, MySql, Transaction, +}; use url::Url; use uuid::Uuid; use crate::{ - models::client::{Client, ClientResponse, ClientType}, + models::client::{Client, ClientType}, services::crypto::PasswordHash, }; +#[derive(Debug, Clone, FromRow)] +pub struct ClientRow { + pub id: Uuid, + pub alias: String, + pub client_type: ClientType, + pub allowed_scopes: String, + pub default_scopes: Option<String>, +} + pub async fn client_id_exists<'c>( executor: impl Executor<'c, Database = MySql>, id: Uuid, @@ -39,12 +50,14 @@ pub async fn client_alias_exists<'c>( pub async fn get_client_response<'c>( executor: impl Executor<'c, Database = MySql>, id: Uuid, -) -> Result<Option<ClientResponse>, RawUnexpected> { +) -> Result<Option<ClientRow>, RawUnexpected> { let record = query_as!( - ClientResponse, + ClientRow, r"SELECT id as `id: Uuid`, alias, - type as `client_type: ClientType` + type as `client_type: ClientType`, + allowed_scopes, + default_scopes FROM clients WHERE id = ?", id ) @@ -153,14 +166,16 @@ pub async fn create_client<'c>( client: &Client, ) -> Result<(), sqlx::Error> { query!( - r"INSERT INTO clients (id, alias, type, secret_hash, secret_salt, secret_version) - VALUES ( ?, ?, ?, ?, ?, ?)", + r"INSERT INTO clients (id, alias, type, secret_hash, secret_salt, secret_version, allowed_scopes, default_scopes) + VALUES ( ?, ?, ?, ?, ?, ?, ?, ?)", client.id(), client.alias(), client.client_type(), client.secret_hash(), client.secret_salt(), client.secret_version(), + client.allowed_scopes(), + client.default_scopes() ) .execute(&mut transaction) .await?; @@ -180,13 +195,17 @@ pub async fn update_client<'c>( type = ?, secret_hash = ?, secret_salt = ?, - secret_version = ? + secret_version = ?, + allowed_scopes = ?, + default_scopes = ? WHERE id = ?", client.client_type(), client.alias(), client.secret_hash(), client.secret_salt(), client.secret_version(), + client.allowed_scopes(), + client.default_scopes(), client.id() ) .execute(&mut transaction) |
