1use serde::Serialize;
2use std::collections::BTreeMap;
3
4pub type OpenApiSecurityRequirement = BTreeMap<String, Vec<String>>;
6
7#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
9pub struct OpenApiSecurityScheme {
10 #[serde(rename = "type")]
11 pub ty: String,
12 #[serde(skip_serializing_if = "Option::is_none")]
13 pub scheme: Option<String>,
14 #[serde(rename = "bearerFormat", skip_serializing_if = "Option::is_none")]
15 pub bearer_format: Option<String>,
16 #[serde(rename = "in", skip_serializing_if = "Option::is_none")]
17 pub location: Option<OpenApiApiKeyLocation>,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub name: Option<String>,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub description: Option<String>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub flows: Option<OpenApiOAuthFlows>,
24 #[serde(rename = "openIdConnectUrl", skip_serializing_if = "Option::is_none")]
25 pub open_id_connect_url: Option<String>,
26}
27
28impl OpenApiSecurityScheme {
29 pub fn http_bearer() -> Self {
30 Self {
31 ty: "http".to_string(),
32 scheme: Some("bearer".to_string()),
33 bearer_format: Some("JWT".to_string()),
34 location: None,
35 name: None,
36 description: None,
37 flows: None,
38 open_id_connect_url: None,
39 }
40 }
41
42 pub fn api_key(location: OpenApiApiKeyLocation, name: impl Into<String>) -> Self {
43 Self {
44 ty: "apiKey".to_string(),
45 scheme: None,
46 bearer_format: None,
47 location: Some(location),
48 name: Some(name.into()),
49 description: None,
50 flows: None,
51 open_id_connect_url: None,
52 }
53 }
54
55 pub fn api_key_header(name: impl Into<String>) -> Self {
56 Self::api_key(OpenApiApiKeyLocation::Header, name)
57 }
58
59 pub fn api_key_query(name: impl Into<String>) -> Self {
60 Self::api_key(OpenApiApiKeyLocation::Query, name)
61 }
62
63 pub fn api_key_cookie(name: impl Into<String>) -> Self {
64 Self::api_key(OpenApiApiKeyLocation::Cookie, name)
65 }
66
67 pub fn oauth2(flows: OpenApiOAuthFlows) -> Self {
68 Self {
69 ty: "oauth2".to_string(),
70 scheme: None,
71 bearer_format: None,
72 location: None,
73 name: None,
74 description: None,
75 flows: Some(flows),
76 open_id_connect_url: None,
77 }
78 }
79
80 pub fn open_id_connect(url: impl Into<String>) -> Self {
81 Self {
82 ty: "openIdConnect".to_string(),
83 scheme: None,
84 bearer_format: None,
85 location: None,
86 name: None,
87 description: None,
88 flows: None,
89 open_id_connect_url: Some(url.into()),
90 }
91 }
92
93 pub fn with_description(mut self, description: impl Into<String>) -> Self {
94 self.description = Some(description.into());
95 self
96 }
97}
98
99#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
101pub struct OpenApiOAuthFlows {
102 #[serde(skip_serializing_if = "Option::is_none")]
103 pub implicit: Option<OpenApiOAuthFlow>,
104 #[serde(skip_serializing_if = "Option::is_none")]
105 pub password: Option<OpenApiOAuthFlow>,
106 #[serde(rename = "clientCredentials", skip_serializing_if = "Option::is_none")]
107 pub client_credentials: Option<OpenApiOAuthFlow>,
108 #[serde(rename = "authorizationCode", skip_serializing_if = "Option::is_none")]
109 pub authorization_code: Option<OpenApiOAuthFlow>,
110}
111
112impl OpenApiOAuthFlows {
113 pub fn new() -> Self {
114 Self::default()
115 }
116
117 pub fn implicit<I, K, V>(authorization_url: impl Into<String>, scopes: I) -> Self
118 where
119 I: IntoIterator<Item = (K, V)>,
120 K: Into<String>,
121 V: Into<String>,
122 {
123 Self::new().with_implicit(OpenApiOAuthFlow::implicit(authorization_url, scopes))
124 }
125
126 pub fn password<I, K, V>(token_url: impl Into<String>, scopes: I) -> Self
127 where
128 I: IntoIterator<Item = (K, V)>,
129 K: Into<String>,
130 V: Into<String>,
131 {
132 Self::new().with_password(OpenApiOAuthFlow::password(token_url, scopes))
133 }
134
135 pub fn client_credentials<I, K, V>(token_url: impl Into<String>, scopes: I) -> Self
136 where
137 I: IntoIterator<Item = (K, V)>,
138 K: Into<String>,
139 V: Into<String>,
140 {
141 Self::new().with_client_credentials(OpenApiOAuthFlow::client_credentials(token_url, scopes))
142 }
143
144 pub fn authorization_code<I, K, V>(
145 authorization_url: impl Into<String>,
146 token_url: impl Into<String>,
147 scopes: I,
148 ) -> Self
149 where
150 I: IntoIterator<Item = (K, V)>,
151 K: Into<String>,
152 V: Into<String>,
153 {
154 Self::new().with_authorization_code(OpenApiOAuthFlow::authorization_code(
155 authorization_url,
156 token_url,
157 scopes,
158 ))
159 }
160
161 pub fn with_implicit(mut self, flow: OpenApiOAuthFlow) -> Self {
162 self.implicit = Some(flow);
163 self
164 }
165
166 pub fn with_password(mut self, flow: OpenApiOAuthFlow) -> Self {
167 self.password = Some(flow);
168 self
169 }
170
171 pub fn with_client_credentials(mut self, flow: OpenApiOAuthFlow) -> Self {
172 self.client_credentials = Some(flow);
173 self
174 }
175
176 pub fn with_authorization_code(mut self, flow: OpenApiOAuthFlow) -> Self {
177 self.authorization_code = Some(flow);
178 self
179 }
180}
181
182#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
184pub struct OpenApiOAuthFlow {
185 #[serde(rename = "authorizationUrl", skip_serializing_if = "Option::is_none")]
186 pub authorization_url: Option<String>,
187 #[serde(rename = "tokenUrl", skip_serializing_if = "Option::is_none")]
188 pub token_url: Option<String>,
189 #[serde(rename = "refreshUrl", skip_serializing_if = "Option::is_none")]
190 pub refresh_url: Option<String>,
191 pub scopes: BTreeMap<String, String>,
192}
193
194impl OpenApiOAuthFlow {
195 pub fn implicit<I, K, V>(authorization_url: impl Into<String>, scopes: I) -> Self
196 where
197 I: IntoIterator<Item = (K, V)>,
198 K: Into<String>,
199 V: Into<String>,
200 {
201 Self {
202 authorization_url: Some(authorization_url.into()),
203 token_url: None,
204 refresh_url: None,
205 scopes: oauth_scopes(scopes),
206 }
207 }
208
209 pub fn password<I, K, V>(token_url: impl Into<String>, scopes: I) -> Self
210 where
211 I: IntoIterator<Item = (K, V)>,
212 K: Into<String>,
213 V: Into<String>,
214 {
215 Self {
216 authorization_url: None,
217 token_url: Some(token_url.into()),
218 refresh_url: None,
219 scopes: oauth_scopes(scopes),
220 }
221 }
222
223 pub fn client_credentials<I, K, V>(token_url: impl Into<String>, scopes: I) -> Self
224 where
225 I: IntoIterator<Item = (K, V)>,
226 K: Into<String>,
227 V: Into<String>,
228 {
229 Self {
230 authorization_url: None,
231 token_url: Some(token_url.into()),
232 refresh_url: None,
233 scopes: oauth_scopes(scopes),
234 }
235 }
236
237 pub fn authorization_code<I, K, V>(
238 authorization_url: impl Into<String>,
239 token_url: impl Into<String>,
240 scopes: I,
241 ) -> Self
242 where
243 I: IntoIterator<Item = (K, V)>,
244 K: Into<String>,
245 V: Into<String>,
246 {
247 Self {
248 authorization_url: Some(authorization_url.into()),
249 token_url: Some(token_url.into()),
250 refresh_url: None,
251 scopes: oauth_scopes(scopes),
252 }
253 }
254
255 pub fn with_refresh_url(mut self, refresh_url: impl Into<String>) -> Self {
256 self.refresh_url = Some(refresh_url.into());
257 self
258 }
259}
260
261fn oauth_scopes<I, K, V>(scopes: I) -> BTreeMap<String, String>
262where
263 I: IntoIterator<Item = (K, V)>,
264 K: Into<String>,
265 V: Into<String>,
266{
267 scopes
268 .into_iter()
269 .map(|(scope, description)| (scope.into(), description.into()))
270 .collect()
271}
272
273#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
275#[serde(rename_all = "lowercase")]
276pub enum OpenApiApiKeyLocation {
277 Query,
278 Header,
279 Cookie,
280}