summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/api/oauth.rs9
-rw-r--r--src/services/authorization.rs108
2 files changed, 77 insertions, 40 deletions
diff --git a/src/api/oauth.rs b/src/api/oauth.rs
index 7941735..a563ac8 100644
--- a/src/api/oauth.rs
+++ b/src/api/oauth.rs
@@ -9,7 +9,7 @@ use url::Url;
use uuid::Uuid;
use crate::resources::{languages, templates};
-use crate::services::db;
+use crate::services::{authorization, db};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
@@ -35,6 +35,7 @@ struct AuthorizeCredentials {
#[post("/authorize")]
async fn authorize(
+ db: web::Data<MySqlPool>,
query: web::Query<AuthorizationParameters>,
credentials: web::Form<AuthorizeCredentials>,
) -> HttpResponse {
@@ -69,7 +70,11 @@ struct TokenRequest {
}
#[post("/token")]
-async fn token(db: web::Data<MySqlPool>, req: web::Form<TokenRequest>) -> HttpResponse {
+async fn token(
+ db: web::Data<MySqlPool>,
+ req: web::Form<TokenRequest>,
+ authorization: web::Header<authorization::BasicAuthorization>, // TODO make this optional
+) -> HttpResponse {
todo!()
}
diff --git a/src/services/authorization.rs b/src/services/authorization.rs
index b9d57ae..bfbbb5a 100644
--- a/src/services/authorization.rs
+++ b/src/services/authorization.rs
@@ -1,50 +1,82 @@
+use actix_web::{
+ error::ParseError,
+ http::header::{self, Header, HeaderName, HeaderValue, InvalidHeaderValue, TryIntoHeaderValue},
+};
use base64::Engine;
use raise::yeet;
-use thiserror::Error;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Error)]
-pub enum ParseBasicError {
- #[error("Basic Authorization is required")]
- NotBasic,
- #[error("No credentials were provided for authorization")]
- NoCredentials,
- #[error("The credentials provided were not base64")]
- InvalidBase64,
- #[error("The decoded base64 credentials were not UTF-8")]
- NotUtf8,
- #[error("A colon (:) must be used to delimit the username and password")]
- NoColon,
+
+#[derive(Clone)]
+pub struct BasicAuthorization {
+ username: Box<str>,
+ password: Box<str>,
}
-/// Returns a username and a password from a Basic authorization header
-pub fn parse_basic(value: &str) -> Result<(Box<str>, Box<str>), ParseBasicError> {
- if !value.starts_with("Basic") {
- yeet!(ParseBasicError::NotBasic);
- }
+impl TryIntoHeaderValue for BasicAuthorization {
+ type Error = InvalidHeaderValue;
- let value: String = value
- .chars()
- .skip(5)
- .skip_while(|ch| ch.is_whitespace())
- .collect();
+ fn try_into_value(self) -> Result<HeaderValue, Self::Error> {
+ let username = self.username;
+ let password = self.password;
+ let utf8 = format!("{username}:{password}");
+ let b64 = base64::engine::general_purpose::STANDARD.encode(utf8);
+ let value = format!("Basic {b64}");
+ HeaderValue::from_str(&value)
+ }
+}
- if value.is_empty() {
- yeet!(ParseBasicError::NoCredentials);
+impl Header for BasicAuthorization {
+ fn name() -> HeaderName {
+ header::AUTHORIZATION
}
- let Ok(bytes) = base64::engine::general_purpose::STANDARD.decode(value) else {
- yeet!(ParseBasicError::InvalidBase64)
- };
+ fn parse<M: actix_web::HttpMessage>(msg: &M) -> Result<Self, actix_web::error::ParseError> {
+ let Some(value) = msg.headers().get(Self::name()) else {
+ yeet!(ParseError::Header)
+ };
+
+ let Ok(value) = value.to_str() else {
+ yeet!(ParseError::Header)
+ };
+
+ if !value.starts_with("Basic") {
+ yeet!(ParseError::Header);
+ }
+
+ let value: String = value
+ .chars()
+ .skip(5)
+ .skip_while(|ch| ch.is_whitespace())
+ .collect();
+
+ if value.is_empty() {
+ yeet!(ParseError::Header);
+ }
- let Ok(value) = String::from_utf8(bytes) else {
- yeet!(ParseBasicError::NotUtf8)
- };
+ let Ok(bytes) = base64::engine::general_purpose::STANDARD.decode(value) else {
+ yeet!(ParseError::Header)
+ };
- let mut parts = value.split(':');
- let username = parts.next().unwrap();
- let Some(password) = parts.next() else {
- yeet!(ParseBasicError::NoColon)
- };
+ let Ok(value) = String::from_utf8(bytes) else {
+ yeet!(ParseError::Header)
+ };
- Ok((Box::from(username), Box::from(password)))
+ let mut parts = value.split(':');
+ let username = Box::from(parts.next().unwrap());
+ let Some(password) = parts.next() else {
+ yeet!(ParseError::Header)
+ };
+ let password = Box::from(password);
+
+ Ok(Self { username, password })
+ }
+}
+
+impl BasicAuthorization {
+ pub fn username(&self) -> &str {
+ &self.username
+ }
+
+ pub fn password(&self) -> &str {
+ &self.password
+ }
}