From 635daba641140038985476bea56e41d4eefadfda Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Fri, 28 Jun 2024 09:49:23 +0200 Subject: [PATCH] Implement `schemars::JsonSchema` for non-secret new types. Placed behind a `schemars` feature flag. --- Cargo.toml | 10 ++++++++- src/types.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 516aa46..8158a24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,10 @@ [package] name = "oauth2" -authors = ["Alex Crichton ", "Florin Lipan ", "David A. Ramos "] +authors = [ + "Alex Crichton ", + "Florin Lipan ", + "David A. Ramos ", +] version = "5.0.0-alpha.4" license = "MIT OR Apache-2.0" description = "An extensible, strongly-typed implementation of OAuth2" @@ -22,6 +26,7 @@ native-tls = ["reqwest/native-tls"] reqwest-blocking = ["reqwest/blocking"] rustls-tls = ["reqwest/rustls-tls"] timing-resistant-secret-traits = [] +schemars = ["dep:schemars"] [[example]] name = "github" @@ -61,6 +66,9 @@ url = { version = "2.1", features = ["serde"] } chrono = { version = "0.4.31", default-features = false, features = ["clock", "serde", "std", "wasmbind"] } serde_path_to_error = "0.1.2" +# Feature: schemars +schemars = { version = "0.8", optional = true } + [target.'cfg(target_arch = "wasm32")'.dependencies] getrandom = { version = "0.2", features = ["js"] } diff --git a/src/types.rs b/src/types.rs index 039bfa5..adf9555 100644 --- a/src/types.rs +++ b/src/types.rs @@ -77,7 +77,8 @@ macro_rules! new_type { } ) => { $(#[$attr])* - #[derive(Clone, Debug, PartialEq)] + #[derive(Clone, Debug, PartialEq, Eq, Hash, ::serde::Serialize, ::serde::Deserialize)] + #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct $name( $(#[$type_attr])* $type @@ -344,13 +345,28 @@ macro_rules! new_url_type { } } impl Eq for $name {} + + + #[cfg(feature = "schemars")] + impl schemars::JsonSchema for $name { + fn schema_name() -> String { + stringify!($name).to_owned() + } + + fn schema_id() -> std::borrow::Cow<'static, str> { + std::borrow::Cow::Borrowed(concat!("oauth2::", stringify!($name))) + } + + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + gen.subschema_for::() + } + } }; } new_type![ /// Client identifier issued to the client during the registration process described by /// [Section 2.2](https://tools.ietf.org/html/rfc6749#section-2.2). - #[derive(Deserialize, Serialize, Eq, Hash)] ClientId(String) ]; @@ -385,19 +401,16 @@ new_url_type![ new_type![ /// Authorization endpoint response (grant) type defined in /// [Section 3.1.1](https://tools.ietf.org/html/rfc6749#section-3.1.1). - #[derive(Deserialize, Serialize, Eq, Hash)] ResponseType(String) ]; new_type![ /// Resource owner's username used directly as an authorization grant to obtain an access /// token. - #[derive(Deserialize, Serialize, Eq, Hash)] ResourceOwnerUsername(String) ]; new_type![ /// Access token scope, as defined by the authorization server. - #[derive(Deserialize, Serialize, Eq, Hash)] Scope(String) ]; impl AsRef for Scope { @@ -409,7 +422,6 @@ impl AsRef for Scope { new_type![ /// Code Challenge Method used for [PKCE](https://tools.ietf.org/html/rfc7636) protection /// via the `code_challenge_method` parameter. - #[derive(Deserialize, Serialize, Eq, Hash)] PkceCodeChallengeMethod(String) ]; // This type intentionally does not implement Clone in order to make it difficult to reuse PKCE @@ -662,4 +674,39 @@ mod tests { "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", ); } + + #[cfg(feature = "schemars")] + mod json_schema { + use schemars::schema_for; + use serde_json::json; + + use crate::{ClientId, RedirectUrl}; + + #[test] + fn generates_new_type_json_schema() { + let expected_schema = json!({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ClientId", + "description": "Client identifier issued to the client during the registration process described by [Section 2.2](https://tools.ietf.org/html/rfc6749#section-2.2).", + "type": "string" + }); + + let schema = schema_for!(ClientId); + let actual_schema = serde_json::to_value(&schema).unwrap(); + assert_eq!(expected_schema, actual_schema) + } + + #[test] + fn generates_new_url_type_json_schema() { + let expected_schema = json!({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "RedirectUrl", + "type": "string" + }); + + let schema = schema_for!(RedirectUrl); + let actual_schema = serde_json::to_value(&schema).unwrap(); + assert_eq!(expected_schema, actual_schema); + } + } }