Skip to main content

openapi_nexus/spec/oas32/spec/
security_scheme.rs

1use serde::{Deserialize, Serialize};
2
3use super::{ErrorRef, Flows, FromRef, OpenApiV32Spec, Ref, RefType};
4
5/// Defines a security scheme that can be used by the operations.
6#[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}