openapi_nexus/spec/oas32/spec/
security_scheme.rs1use serde::{Deserialize, Serialize};
2
3use super::{ErrorRef, Flows, FromRef, OpenApiV32Spec, Ref, RefType};
4
5#[allow(clippy::large_enum_variant)]
7#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
8#[serde(tag = "type")]
9pub enum SecurityScheme {
10 #[serde(rename = "apiKey")]
11 ApiKey {
12 #[serde(skip_serializing_if = "Option::is_none")]
13 description: Option<String>,
14
15 name: String,
16
17 #[serde(rename = "in")]
18 location: String,
19 },
20
21 #[serde(rename = "http")]
22 Http {
23 #[serde(skip_serializing_if = "Option::is_none")]
24 description: Option<String>,
25
26 scheme: String,
27
28 #[serde(rename = "bearerFormat")]
29 bearer_format: Option<String>,
30 },
31
32 #[serde(rename = "oauth2")]
33 OAuth2 {
34 #[serde(skip_serializing_if = "Option::is_none")]
35 description: Option<String>,
36
37 flows: Flows,
38
39 #[serde(skip_serializing_if = "Option::is_none")]
40 deprecated: Option<bool>,
41 },
42
43 #[serde(rename = "openIdConnect")]
44 OpenIdConnect {
45 #[serde(skip_serializing_if = "Option::is_none")]
46 description: Option<String>,
47
48 #[serde(rename = "openIdConnectUrl")]
49 open_id_connect_url: String,
50 },
51
52 #[serde(rename = "mutualTLS")]
53 MutualTls {
54 #[serde(skip_serializing_if = "Option::is_none")]
55 description: Option<String>,
56 },
57}
58
59impl FromRef for SecurityScheme {
60 fn from_ref(spec: &OpenApiV32Spec, path: &str) -> Result<Self, ErrorRef> {
61 let refpath = path.parse::<Ref>()?;
62
63 match refpath.kind {
64 RefType::SecurityScheme => spec
65 .components
66 .as_ref()
67 .and_then(|cs| cs.security_schemes.get(&refpath.name))
68 .ok_or_else(|| ErrorRef::Unresolvable {
69 path: path.to_owned(),
70 })
71 .and_then(|oor| oor.resolve(spec)),
72 typ => Err(ErrorRef::MismatchedType {
73 expected: typ,
74 actual: RefType::SecurityScheme,
75 }),
76 }
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use url::Url;
83
84 use super::SecurityScheme;
85
86 #[test]
87 fn test_http_basic_deser() {
88 const HTTP_BASIC_SAMPLE: &str = r#"{"type": "http", "scheme": "basic"}"#;
89 let obj: SecurityScheme = serde_json::from_str(HTTP_BASIC_SAMPLE).unwrap();
90
91 assert!(matches!(
92 obj,
93 SecurityScheme::Http {
94 description: None,
95 scheme,
96 bearer_format: None,
97 } if scheme == "basic"
98 ));
99 }
100
101 #[test]
102 fn test_security_scheme_oauth_deser() {
103 const IMPLICIT_OAUTH2_SAMPLE: &str = r#"{
104 "type": "oauth2",
105 "flows": {
106 "implicit": {
107 "authorizationUrl": "https://example.com/api/oauth/dialog",
108 "scopes": {
109 "write:pets": "modify pets in your account",
110 "read:pets": "read your pets"
111 }
112 },
113 "authorizationCode": {
114 "authorizationUrl": "https://example.com/api/oauth/dialog",
115 "tokenUrl": "https://example.com/api/oauth/token",
116 "scopes": {
117 "write:pets": "modify pets in your account",
118 "read:pets": "read your pets"
119 }
120 }
121 }
122 }"#;
123
124 let obj: SecurityScheme = serde_json::from_str(IMPLICIT_OAUTH2_SAMPLE).unwrap();
125 match obj {
126 SecurityScheme::OAuth2 {
127 description: _,
128 flows,
129 ..
130 } => {
131 assert!(flows.implicit.is_some());
132 let implicit = flows.implicit.unwrap();
133 assert_eq!(
134 implicit.authorization_url,
135 Url::parse("https://example.com/api/oauth/dialog").unwrap()
136 );
137 assert!(implicit.scopes.contains_key("write:pets"));
138 assert!(implicit.scopes.contains_key("read:pets"));
139
140 assert!(flows.authorization_code.is_some());
141 let auth_code = flows.authorization_code.unwrap();
142 assert_eq!(
143 auth_code.authorization_url,
144 Url::parse("https://example.com/api/oauth/dialog").unwrap()
145 );
146 assert_eq!(
147 auth_code.token_url,
148 Url::parse("https://example.com/api/oauth/token").unwrap()
149 );
150 }
151 _ => panic!("wrong security scheme type"),
152 }
153 }
154}