1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};

/// Defines a security scheme that can be used by the operations.
/// Supported schemes are HTTP authentication, an API key (either as a
/// header or as a query parameter), OAuth2's common flows (implicit, password,
/// application and access code) as defined in RFC6749, and OpenID Connect Discovery.
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type")]
pub enum SecurityScheme {
    #[serde(rename = "apiKey")]
    APIKey {
        /// The location of the API key. Valid values are "query", "header" or
        /// "cookie".
        #[serde(rename = "in")]
        location: APIKeyLocation,
        /// The name of the header, query or cookie parameter to be used.
        name: String,
        /// A short description for security scheme. CommonMark syntax MAY be
        /// used for rich text representation.
        #[serde(skip_serializing_if = "Option::is_none")]
        description: Option<String>,

        /// Inline extensions to this object.
        #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
        extensions: IndexMap<String, serde_json::Value>,
    },
    #[serde(rename = "http")]
    HTTP {
        /// The name of the HTTP Authorization scheme to be used in the
        /// Authorization header as defined in RFC7235. The values used SHOULD
        /// be registered in the IANA Authentication Scheme registry.
        scheme: String,
        #[serde(rename = "bearerFormat", skip_serializing_if = "Option::is_none")]
        bearer_format: Option<String>,
        /// A short description for security scheme. CommonMark syntax MAY be
        /// used for rich text representation.
        #[serde(skip_serializing_if = "Option::is_none")]
        description: Option<String>,

        /// Inline extensions to this object.
        #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
        extensions: IndexMap<String, serde_json::Value>,
    },
    #[serde(rename = "oauth2")]
    OAuth2 {
        /// An object containing configuration information for the flow types
        /// supported.
        flows: OAuth2Flows,
        /// A short description for security scheme. CommonMark syntax MAY be
        /// used for rich text representation.
        #[serde(skip_serializing_if = "Option::is_none")]
        description: Option<String>,

        /// Inline extensions to this object.
        #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
        extensions: IndexMap<String, serde_json::Value>,
    },
    #[serde(rename = "openIdConnect")]
    OpenIDConnect {
        /// OpenId Connect URL to discover OAuth2 configuration values. This
        /// MUST be in the form of a URL.
        #[serde(rename = "openIdConnectUrl")]
        open_id_connect_url: String,
        /// A short description for security scheme. CommonMark syntax MAY be
        /// used for rich text representation.
        #[serde(skip_serializing_if = "Option::is_none")]
        description: Option<String>,

        /// Inline extensions to this object.
        #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
        extensions: IndexMap<String, serde_json::Value>,
    },
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum APIKeyLocation {
    Query,
    Header,
    Cookie,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct OAuth2Flows {
    /// Configuration for the OAuth Implicit flow
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub implicit: Option<ImplicitOAuth2Flow>,
    /// Configuration for the OAuth Resource Owner Password flow
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub password: Option<PasswordOAuth2Flow>,
    /// Configuration for the OAuth Client Credentials flow.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub client_credentials: Option<ClientCredentialsOAuth2Flow>,
    /// Configuration for the OAuth Authorization Code flow.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub authorization_code: Option<AuthorizationCodeOAuth2Flow>,

    /// Inline extensions to this object.
    #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
    pub extensions: IndexMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ImplicitOAuth2Flow {
    /// The authorization URL to be used for this flow. This MUST be in the
    /// form of a URL.
    authorization_url: String,
    /// The URL to be used for obtaining refresh tokens. This MUST be in the
    /// form of a URL.
    #[serde(skip_serializing_if = "Option::is_none")]
    refresh_url: Option<String>,
    /// The available scopes for the OAuth2 security scheme. A map between the
    /// scope name and a short description for it. The map MAY be empty.
    scopes: IndexMap<String, String>,

    /// Inline extensions to this object.
    #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
    pub extensions: IndexMap<String, serde_json::Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PasswordOAuth2Flow {
    /// The URL to be used for obtaining refresh tokens. This MUST be in the
    /// form of a URL.
    #[serde(skip_serializing_if = "Option::is_none")]
    refresh_url: Option<String>,
    /// The token URL to be used for this flow. This MUST be in the form of a
    /// URL.
    token_url: String,
    /// The available scopes for the OAuth2 security scheme. A map between the
    /// scope name and a short description for it. The map MAY be empty.
    scopes: IndexMap<String, String>,

    /// Inline extensions to this object.
    #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
    pub extensions: IndexMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ClientCredentialsOAuth2Flow {
    /// The URL to be used for obtaining refresh tokens. This MUST be in the
    /// form of a URL.
    #[serde(skip_serializing_if = "Option::is_none")]
    refresh_url: Option<String>,
    /// The token URL to be used for this flow. This MUST be in the form of a
    /// URL.
    token_url: String,
    /// The available scopes for the OAuth2 security scheme. A map between the
    /// scope name and a short description for it. The map MAY be empty.
    scopes: IndexMap<String, String>,

    /// Inline extensions to this object.
    #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
    pub extensions: IndexMap<String, serde_json::Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct AuthorizationCodeOAuth2Flow {
    /// The authorization URL to be used for this flow. This MUST be in the
    /// form of a URL.
    authorization_url: String,
    /// The token URL to be used for this flow. This MUST be in the form of a
    /// URL.
    token_url: String,
    /// The URL to be used for obtaining refresh tokens. This MUST be in the
    /// form of a URL.
    #[serde(skip_serializing_if = "Option::is_none")]
    refresh_url: Option<String>,
    /// The available scopes for the OAuth2 security scheme. A map between the
    /// scope name and a short description for it. The map MAY be empty.
    scopes: IndexMap<String, String>,

    /// Inline extensions to this object.
    #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
    pub extensions: IndexMap<String, serde_json::Value>,
}

#[cfg(test)]
mod tests {
    use crate::{OpenAPI, ReferenceOr, SecurityScheme};

    #[test]
    fn test_slack_auth() {
        let openapi: OpenAPI =
            serde_json::from_reader(std::fs::File::open("fixtures/slack.json").unwrap()).unwrap();

        assert!(matches!(
            openapi
                .components
                .as_ref()
                .unwrap()
                .security_schemes
                .get("slackAuth")
                .unwrap(),
            ReferenceOr::Item(SecurityScheme::OAuth2 { .. })
        ));
    }
}