Skip to content

Commit 0979710

Browse files
authored
Merge pull request #28 from boxdot/security-oauth2
Add security scheme for OAuth2.
2 parents 537328e + 324faef commit 0979710

File tree

1 file changed

+130
-12
lines changed

1 file changed

+130
-12
lines changed

src/v3_0/schema.rs

+130-12
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,12 @@ pub struct Info {
114114
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
115115
pub struct Url(#[serde(with = "url_serde")] url::Url);
116116

117+
impl Url {
118+
pub fn parse<S: AsRef<str>>(input: S) -> std::result::Result<Url, url::ParseError> {
119+
url::Url::parse(input.as_ref()).map(Url)
120+
}
121+
}
122+
117123
/// Contact information for the exposed API.
118124
///
119125
/// See <https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#contactObject>.
@@ -420,7 +426,7 @@ enum ParameterStyle {
420426
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
421427
pub struct Schema {
422428
/// [JSON reference](https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03)
423-
/// path to another defintion
429+
/// path to another definition
424430
#[serde(skip_serializing_if = "Option::is_none")]
425431
#[serde(rename = "$ref")]
426432
pub ref_path: Option<String>,
@@ -848,24 +854,80 @@ pub enum SecurityScheme {
848854
#[serde(rename = "bearerFormat")]
849855
bearer_format: String,
850856
},
851-
// FIXME: Implement
852-
// #[serde(rename = "oauth2")]
853-
// Oauth2 {
854-
// flow: String,
855-
// #[serde(rename = "authorizationUrl")]
856-
// authorization_url: String,
857-
// #[serde(rename = "tokenUrl")]
858-
// #[serde(skip_serializing_if = "Option::is_none")]
859-
// token_url: Option<String>,
860-
// scopes: BTreeMap<String, String>,
861-
// },
857+
#[serde(rename = "oauth2")]
858+
OAuth2 { flows: Flows },
862859
#[serde(rename = "openIdConnect")]
863860
OpenIdConnect {
864861
#[serde(rename = "openIdConnectUrl")]
865862
open_id_connect_url: String,
866863
},
867864
}
868865

866+
/// Allows configuration of the supported OAuth Flows.
867+
/// See [link]
868+
/// [link][https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#oauth-flows-object]
869+
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
870+
#[serde(rename_all = "camelCase")]
871+
pub struct Flows {
872+
#[serde(skip_serializing_if = "Option::is_none")]
873+
pub implicit: Option<ImplicitFlow>,
874+
#[serde(skip_serializing_if = "Option::is_none")]
875+
pub password: Option<PasswordFlow>,
876+
#[serde(skip_serializing_if = "Option::is_none")]
877+
pub client_credentials: Option<ClientCredentialsFlow>,
878+
#[serde(skip_serializing_if = "Option::is_none")]
879+
pub authorization_code: Option<AuthorizationCodeFlow>,
880+
}
881+
882+
/// Configuration details for a implicit OAuth Flow
883+
/// See [link]
884+
/// [link](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#oauth-flow-object)
885+
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
886+
#[serde(rename_all = "camelCase")]
887+
pub struct ImplicitFlow {
888+
pub authorization_url: Url,
889+
#[serde(skip_serializing_if = "Option::is_none")]
890+
pub refresh_url: Option<Url>,
891+
pub scopes: BTreeMap<String, String>,
892+
}
893+
894+
/// Configuration details for a password OAuth Flow
895+
/// See [link]
896+
/// [link](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#oauth-flow-object
897+
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
898+
#[serde(rename_all = "camelCase")]
899+
pub struct PasswordFlow {
900+
token_url: Url,
901+
#[serde(skip_serializing_if = "Option::is_none")]
902+
pub refresh_url: Option<Url>,
903+
pub scopes: BTreeMap<String, String>,
904+
}
905+
906+
/// Configuration details for a client credentials OAuth Flow
907+
/// See [link]
908+
/// [link](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#oauth-flow-object
909+
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
910+
#[serde(rename_all = "camelCase")]
911+
pub struct ClientCredentialsFlow {
912+
token_url: Url,
913+
#[serde(skip_serializing_if = "Option::is_none")]
914+
pub refresh_url: Option<Url>,
915+
pub scopes: BTreeMap<String, String>,
916+
}
917+
918+
/// Configuration details for a authorization code OAuth Flow
919+
/// See [link]
920+
/// [link](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#oauth-flow-object
921+
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
922+
#[serde(rename_all = "camelCase")]
923+
pub struct AuthorizationCodeFlow {
924+
pub authorization_url: Url,
925+
token_url: Url,
926+
#[serde(skip_serializing_if = "Option::is_none")]
927+
pub refresh_url: Option<Url>,
928+
pub scopes: BTreeMap<String, String>,
929+
}
930+
869931
// TODO: Implement
870932
/// A map of possible out-of band callbacks related to the parent operation. Each value in
871933
/// the map is a Path Item Object that describes a set of requests that may be initiated by
@@ -922,3 +984,59 @@ pub struct ExternalDoc {
922984
pub description: Option<String>,
923985
// TODO: Add "Specification Extensions" https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#specificationExtensions}
924986
}
987+
988+
#[cfg(test)]
989+
mod tests {
990+
use super::*;
991+
992+
#[test]
993+
fn test_security_scheme_oauth_deser() {
994+
const IMPLICIT_OAUTH2_SAMPLE: &str = r#"{
995+
"type": "oauth2",
996+
"flows": {
997+
"implicit": {
998+
"authorizationUrl": "https://example.com/api/oauth/dialog",
999+
"scopes": {
1000+
"write:pets": "modify pets in your account",
1001+
"read:pets": "read your pets"
1002+
}
1003+
},
1004+
"authorizationCode": {
1005+
"authorizationUrl": "https://example.com/api/oauth/dialog",
1006+
"tokenUrl": "https://example.com/api/oauth/token",
1007+
"scopes": {
1008+
"write:pets": "modify pets in your account",
1009+
"read:pets": "read your pets"
1010+
}
1011+
}
1012+
}
1013+
}"#;
1014+
let obj: SecurityScheme = serde_json::from_str(&IMPLICIT_OAUTH2_SAMPLE).unwrap();
1015+
match obj {
1016+
SecurityScheme::OAuth2 { flows } => {
1017+
assert!(flows.implicit.is_some());
1018+
let implicit = flows.implicit.unwrap();
1019+
assert_eq!(
1020+
implicit.authorization_url,
1021+
Url::parse("https://example.com/api/oauth/dialog").unwrap()
1022+
);
1023+
assert!(implicit.scopes.contains_key("write:pets"));
1024+
assert!(implicit.scopes.contains_key("read:pets"));
1025+
1026+
assert!(flows.authorization_code.is_some());
1027+
let auth_code = flows.authorization_code.unwrap();
1028+
assert_eq!(
1029+
auth_code.authorization_url,
1030+
Url::parse("https://example.com/api/oauth/dialog").unwrap()
1031+
);
1032+
assert_eq!(
1033+
auth_code.token_url,
1034+
Url::parse("https://example.com/api/oauth/token").unwrap()
1035+
);
1036+
assert!(implicit.scopes.contains_key("write:pets"));
1037+
assert!(implicit.scopes.contains_key("read:pets"));
1038+
}
1039+
_ => assert!(false, "wrong security scheme type"),
1040+
}
1041+
}
1042+
}

0 commit comments

Comments
 (0)