Skip to content

Commit

Permalink
initial success tests
Browse files Browse the repository at this point in the history
  • Loading branch information
aumetra committed Jan 15, 2025
1 parent 951dcc8 commit 2ffbf43
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 20 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ hickory-resolver = { version = "0.25.0-alpha.4", default-features = false, featu
http = "1.2.0"
http-body = "1.0.1"
http-body-util = "0.1.2"
http-serde = "2.1.1"
httpdate = "1.0.3"
human-size = { version = "0.4.3", features = ["serde"] }
hyper = "1.5.2"
Expand Down
13 changes: 8 additions & 5 deletions kitsune/src/http/handler/oauth/authorize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,14 @@ pub async fn get(
.await
})?;

let mut scopes = authorizer
.scope()
.iter()
.filter_map(|scope| OAuthScope::from_str(scope).ok())
.collect::<Vec<OAuthScope>>();
let mut scopes = if let Some(scope) = authorizer.scope() {
scope
.iter()
.filter_map(|scope| OAuthScope::from_str(scope).ok())
.collect()
} else {
Vec::new()
};

if scopes.is_empty() {
// default to read scope if no scopes are defined
Expand Down
2 changes: 2 additions & 0 deletions lib/komainu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ http.workspace = true
http-body.workspace = true
http-body-util.workspace = true
indexmap.workspace = true
insta.workspace = true
itertools.workspace = true
memchr.workspace = true
serde.workspace = true
Expand All @@ -38,6 +39,7 @@ url.workspace = true
divan.workspace = true
futures-test.workspace = true
headers.workspace = true
http-serde.workspace = true
insta.workspace = true
rand.workspace = true
rstest.workspace = true
Expand Down
36 changes: 25 additions & 11 deletions lib/komainu/src/code_grant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
error::Error, flow::pkce, params::ParamStorage, scope::Scope, AuthInstruction, Client,
ClientExtractor,
};
use std::{borrow::Cow, future::Future, ops::Deref, str::FromStr};
use std::{borrow::Cow, future::Future, str::FromStr};
use strum::{AsRefStr, Display};
use thiserror::Error;

Expand Down Expand Up @@ -63,7 +63,7 @@ where
) -> Result<Authorizer<'a, I>, GrantError> {
let client_id = req.query.get("client_id").or_invalid_request()?;
let response_type = req.query.get("response_type").or_invalid_request()?;
let scope = req.query.get("scope").map_or("", Deref::deref);
let scope = req.query.get("scope");
let state = req.query.get("state").map(|state| &**state);

let client = self.client_extractor.extract(client_id, None).await?;
Expand All @@ -79,13 +79,27 @@ where
return Err(GrantError::AccessDenied);
}

let request_scopes = scope.parse().unwrap();
let request_scopes = if let Some(scope) = scope {
let scope = match Scope::from_str(scope) {
Ok(val) => val,
// Infallible so we have to do this shit to signal to the compiler "hey, this actually can't ever happen".
// Thanks for not stabilizing the never type.
//
// I'm so close to hacking the actual never type in here, I swear.
Err(err) => match err {},
};

// Check whether the client can actually perform the grant
if !client.scopes.can_perform(&request_scopes) {
debug!(?client_id, client_scopes = ?client.scopes, ?request_scopes, "client can't issue the requested scopes");
// apparently clients do that. weird smh.
//return Err(GrantError::AccessDenied);
Some(scope)
} else {
None
};

if let Some(ref request_scopes) = request_scopes {
// Check whether the client can actually perform the grant
if !client.scopes.can_perform(request_scopes) {
debug!(?client_id, client_scopes = ?client.scopes, ?request_scopes, "client can't issue the requested scopes");
return Err(GrantError::AccessDenied);
}
}

let pkce_payload = if let Some(challenge) = req.query.get("code_challenge") {
Expand Down Expand Up @@ -144,7 +158,7 @@ pub struct Authorizer<'a, I> {
issuer: &'a I,
client: Client<'a>,
pkce_payload: Option<pkce::Payload<'a>>,
scope: Scope,
scope: Option<Scope>,
query: &'a ParamStorage<Cow<'a, str>, Cow<'a, str>>,
state: Option<&'a str>,
}
Expand All @@ -159,8 +173,8 @@ where
}

#[must_use]
pub fn scope(&self) -> &Scope {
&self.scope
pub fn scope(&self) -> Option<&Scope> {
self.scope.as_ref()
}

#[must_use]
Expand Down
4 changes: 0 additions & 4 deletions lib/komainu/tests/code_grant.rs

This file was deleted.

76 changes: 76 additions & 0 deletions lib/komainu/tests/flows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use self::fixtures::Fixture;
use bytes::Bytes;
use http_body_util::Empty;
use komainu::scope::Scope;
use serde::Serialize;
use std::str::FromStr;

mod fixtures;

#[derive(Serialize)]
struct SerdeResponse {
body: Option<String>,
#[serde(with = "http_serde::header_map")]
headers: http::HeaderMap,
#[serde(with = "http_serde::status_code")]
status: http::StatusCode,
}

impl From<http::Response<()>> for SerdeResponse {
#[inline]
fn from(value: http::Response<()>) -> Self {
let (parts, _body) = value.into_parts();

Self {
body: None,
headers: parts.headers,
status: parts.status,
}
}
}

impl From<http::Response<Bytes>> for SerdeResponse {
#[inline]
fn from(value: http::Response<Bytes>) -> Self {
let (parts, body) = value.into_parts();
let body = String::from_utf8(body.to_vec()).unwrap();

let mut response: Self = http::Response::from_parts(parts, ()).into();
response.body = Some(body);
response
}
}

#[futures_test::test]
async fn success() {
let fixture = Fixture::generate();

let uri = http::Uri::builder()
.scheme("http")
.authority("komainu.example")
.path_and_query("/oauth/authorize?response_type=code&client_id=client_1")
.build()
.unwrap();

let req = http::Request::builder()
.uri(uri)
.body(Empty::<Bytes>::new())
.unwrap();
let req = komainu::Request::read_from(req).await.unwrap();

let acceptor = {
let handle = fixture.code_grant.extract_raw(&req).await.unwrap();
handle
.accept("user id".into(), &Scope::from_str("read").unwrap())
.await
.unwrap()
};

let deny = {
let handle = fixture.code_grant.extract_raw(&req).await.unwrap();
handle.deny()
};

insta::assert_json_snapshot!(SerdeResponse::from(acceptor.into_response()));
insta::assert_json_snapshot!(SerdeResponse::from(deny));
}
11 changes: 11 additions & 0 deletions lib/komainu/tests/snapshots/flows__success-2.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
source: lib/komainu/tests/flows.rs
expression: "SerdeResponse::from(deny)"
---
{
"body": null,
"headers": {
"location": "http://client_1.example/?error=access_denied"
},
"status": 302
}
11 changes: 11 additions & 0 deletions lib/komainu/tests/snapshots/flows__success.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
source: lib/komainu/tests/flows.rs
expression: "SerdeResponse::from(acceptor.into_response())"
---
{
"body": null,
"headers": {
"location": "http://client_1.example/?code=sn7A6s3FVFsptwK6"
},
"status": 302
}

0 comments on commit 2ffbf43

Please sign in to comment.