Skip to content

Commit ee698f1

Browse files
authored
[Rust-Axum][Breaking Changes] Extracting Claims in Cookie/Header (#20097)
* [Rust-Axum][Breaking Changes] Extracting Claims in Cookie/Header * Update * Update
1 parent 975f4d4 commit ee698f1

File tree

28 files changed

+332
-241
lines changed

28 files changed

+332
-241
lines changed

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustAxumServerCodegen.java

+20-10
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
8484

8585
// Grouping (Method, Operation) by Path.
8686
private final Map<String, ArrayList<MethodOperation>> pathMethodOpMap = new HashMap<>();
87+
private boolean havingAuthMethods = false;
8788

8889
// Logger
8990
private final Logger LOGGER = LoggerFactory.getLogger(RustAxumServerCodegen.class);
@@ -595,21 +596,26 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
595596

596597
@Override
597598
public OperationsMap postProcessOperationsWithModels(final OperationsMap operationsMap, List<ModelMap> allModels) {
598-
OperationMap operations = operationsMap.getOperations();
599+
final OperationMap operations = operationsMap.getOperations();
599600
operations.put("classnamePascalCase", camelize(operations.getClassname()));
600-
List<CodegenOperation> operationList = operations.getOperation();
601601

602-
for (CodegenOperation op : operationList) {
603-
postProcessOperationWithModels(op);
602+
final boolean hasAuthMethod = operations.getOperation().stream()
603+
.map(this::postProcessOperationWithModels)
604+
.reduce(false, (a, b) -> a || b);
605+
if (hasAuthMethod) {
606+
operations.put("havingAuthMethod", true);
607+
operations.getOperation().forEach(op -> op.vendorExtensions.put("havingAuthMethod", true));
608+
this.havingAuthMethods = true;
604609
}
605610

606611
return operationsMap;
607612
}
608613

609-
private void postProcessOperationWithModels(final CodegenOperation op) {
614+
private boolean postProcessOperationWithModels(final CodegenOperation op) {
610615
boolean consumesJson = false;
611616
boolean consumesPlainText = false;
612617
boolean consumesFormUrlEncoded = false;
618+
boolean hasAuthMethod = false;
613619

614620
if (op.consumes != null) {
615621
for (Map<String, String> consume : op.consumes) {
@@ -666,17 +672,20 @@ private void postProcessOperationWithModels(final CodegenOperation op) {
666672
for (CodegenSecurity s : op.authMethods) {
667673
if (s.isApiKey && (s.isKeyInCookie || s.isKeyInHeader)) {
668674
if (s.isKeyInCookie) {
669-
op.vendorExtensions.put("x-has-cookie-auth-methods", "true");
670-
op.vendorExtensions.put("x-api-key-cookie-name", toModelName(s.keyParamName));
675+
op.vendorExtensions.put("x-has-cookie-auth-methods", true);
676+
op.vendorExtensions.put("x-api-key-cookie-name", s.keyParamName);
671677
} else {
672-
op.vendorExtensions.put("x-has-header-auth-methods", "true");
673-
op.vendorExtensions.put("x-api-key-header-name", toModelName(s.keyParamName));
678+
op.vendorExtensions.put("x-has-header-auth-methods", true);
679+
op.vendorExtensions.put("x-api-key-header-name", s.keyParamName);
674680
}
675681

676-
op.vendorExtensions.put("x-has-auth-methods", "true");
682+
op.vendorExtensions.put("x-has-auth-methods", true);
683+
hasAuthMethod = true;
677684
}
678685
}
679686
}
687+
688+
return hasAuthMethod;
680689
}
681690

682691
@Override
@@ -772,6 +781,7 @@ public Map<String, Object> postProcessSupportingFileData(Map<String, Object> bun
772781
.sorted(Comparator.comparing(a -> a.path))
773782
.collect(Collectors.toList());
774783
bundle.put("pathMethodOps", pathMethodOps);
784+
if (havingAuthMethods) bundle.put("havingAuthMethods", true);
775785

776786
return super.postProcessSupportingFileData(bundle);
777787
}

modules/openapi-generator/src/main/resources/rust-axum/Cargo.mustache

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ conversion = [
4040

4141
[dependencies]
4242
async-trait = "0.1"
43-
axum = { version = "0.7" }
43+
axum = "0.7"
4444
axum-extra = { version = "0.9", features = ["cookie", "multipart"] }
4545
base64 = "0.22"
4646
bytes = "1"
@@ -62,7 +62,7 @@ tokio = { version = "1", default-features = false, features = [
6262
] }
6363
tracing = { version = "0.1", features = ["attributes"] }
6464
uuid = { version = "1", features = ["serde"] }
65-
validator = { version = "0.18", features = ["derive"] }
65+
validator = { version = "0.19", features = ["derive"] }
6666

6767
[dev-dependencies]
6868
tracing-subscriber = "0.3"

modules/openapi-generator/src/main/resources/rust-axum/apis-mod.mustache

+10-2
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,22 @@ pub mod {{classFilename}};
88
{{#isApiKey}}
99
{{#isKeyInCookie}}
1010
/// Cookie Authentication.
11+
#[async_trait::async_trait]
1112
pub trait CookieAuthentication {
12-
fn extract_token_from_cookie(&self, cookies: &axum_extra::extract::CookieJar, key: &str) -> Option<String>;
13+
type Claims;
14+
15+
/// Extracting Claims from Cookie. Return None if the Claims is invalid.
16+
async fn extract_claims_from_cookie(&self, cookies: &axum_extra::extract::CookieJar, key: &str) -> Option<Self::Claims>;
1317
}
1418
{{/isKeyInCookie}}
1519
{{#isKeyInHeader}}
1620
/// API Key Authentication - Header.
21+
#[async_trait::async_trait]
1722
pub trait ApiKeyAuthHeader {
18-
fn extract_token_from_header(&self, headers: &axum::http::header::HeaderMap, key: &str) -> Option<String>;
23+
type Claims;
24+
25+
/// Extracting Claims from Header. Return None if the Claims is invalid.
26+
async fn extract_claims_from_header(&self, headers: &axum::http::header::HeaderMap, key: &str) -> Option<Self::Claims>;
1927
}
2028
{{/isKeyInHeader}}
2129
{{/isApiKey}}

modules/openapi-generator/src/main/resources/rust-axum/apis.mustache

+7-6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ use crate::{models, types::*};
1818
#[async_trait]
1919
#[allow(clippy::ptr_arg)]
2020
pub trait {{classnamePascalCase}} {
21+
{{#havingAuthMethod}}
22+
type Claims;
23+
24+
{{/havingAuthMethod}}
2125
{{#operation}}
2226
{{#summary}}
2327
/// {{{.}}}.
@@ -31,12 +35,9 @@ pub trait {{classnamePascalCase}} {
3135
host: Host,
3236
cookies: CookieJar,
3337
{{#vendorExtensions}}
34-
{{#x-has-cookie-auth-methods}}
35-
token_in_cookie: Option<String>,
36-
{{/x-has-cookie-auth-methods}}
37-
{{#x-has-header-auth-methods}}
38-
token_in_header: Option<String>,
39-
{{/x-has-header-auth-methods}}
38+
{{#x-has-auth-methods}}
39+
claims: Self::Claims,
40+
{{/x-has-auth-methods}}
4041
{{/vendorExtensions}}
4142
{{#headerParams.size}}
4243
header_params: models::{{{operationIdCamelCase}}}HeaderParams,

modules/openapi-generator/src/main/resources/rust-axum/server-operation.mustache

+20-15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/// {{{operationId}}} - {{{httpMethod}}} {{{basePathWithoutHost}}}{{{path}}}
22
#[tracing::instrument(skip_all)]
3-
async fn {{#vendorExtensions}}{{{x-operation-id}}}{{/vendorExtensions}}<I, A>(
3+
async fn {{#vendorExtensions}}{{{x-operation-id}}}{{/vendorExtensions}}<I, A{{#havingAuthMethod}}, C{{/havingAuthMethod}}>(
44
method: Method,
55
host: Host,
66
cookies: CookieJar,
@@ -54,25 +54,33 @@ async fn {{#vendorExtensions}}{{{x-operation-id}}}{{/vendorExtensions}}<I, A>(
5454
) -> Result<Response, StatusCode>
5555
where
5656
I: AsRef<A> + Send + Sync,
57-
A: apis::{{classFilename}}::{{classnamePascalCase}}{{#vendorExtensions}}{{#x-has-cookie-auth-methods}}+ apis::CookieAuthentication{{/x-has-cookie-auth-methods}}{{#x-has-header-auth-methods}}+ apis::ApiKeyAuthHeader{{/x-has-header-auth-methods}}{{/vendorExtensions}},
57+
A: apis::{{classFilename}}::{{classnamePascalCase}}{{#havingAuthMethod}}<Claims = C>{{/havingAuthMethod}}{{#vendorExtensions}}{{#x-has-cookie-auth-methods}}+ apis::CookieAuthentication<Claims = C>{{/x-has-cookie-auth-methods}}{{#x-has-header-auth-methods}}+ apis::ApiKeyAuthHeader<Claims = C>{{/x-has-header-auth-methods}}{{/vendorExtensions}},
5858
{
5959
{{#vendorExtensions}}
6060
{{#x-has-auth-methods}}
6161
// Authentication
6262
{{/x-has-auth-methods}}
6363
{{#x-has-cookie-auth-methods}}
64-
let token_in_cookie = api_impl.as_ref().extract_token_from_cookie(&cookies, "{{x-api-key-cookie-name}}");
64+
let claims_in_cookie = api_impl.as_ref().extract_claims_from_cookie(&cookies, "{{x-api-key-cookie-name}}").await;
6565
{{/x-has-cookie-auth-methods}}
6666
{{#x-has-header-auth-methods}}
67-
let token_in_header = api_impl.as_ref().extract_token_from_header(&headers, "{{x-api-key-header-name}}");
67+
let claims_in_header = api_impl.as_ref().extract_claims_from_header(&headers, "{{x-api-key-header-name}}").await;
6868
{{/x-has-header-auth-methods}}
6969
{{#x-has-auth-methods}}
70-
if let ({{#x-has-cookie-auth-methods}}None,{{/x-has-cookie-auth-methods}}{{#x-has-header-auth-methods}}None,{{/x-has-header-auth-methods}}) = ({{#x-has-cookie-auth-methods}}&token_in_cookie,{{/x-has-cookie-auth-methods}}{{#x-has-header-auth-methods}}&token_in_header,{{/x-has-header-auth-methods}}) {
70+
let claims = None
71+
{{#x-has-cookie-auth-methods}}
72+
.or(claims_in_cookie)
73+
{{/x-has-cookie-auth-methods}}
74+
{{#x-has-header-auth-methods}}
75+
.or(claims_in_header)
76+
{{/x-has-header-auth-methods}}
77+
;
78+
let Some(claims) = claims else {
7179
return Response::builder()
72-
.status(StatusCode::UNAUTHORIZED)
73-
.body(Body::empty())
74-
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR);
75-
}
80+
.status(StatusCode::UNAUTHORIZED)
81+
.body(Body::empty())
82+
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR);
83+
};
7684
{{/x-has-auth-methods}}
7785
{{/vendorExtensions}}
7886

@@ -182,12 +190,9 @@ where
182190
host,
183191
cookies,
184192
{{#vendorExtensions}}
185-
{{#x-has-cookie-auth-methods}}
186-
token_in_cookie,
187-
{{/x-has-cookie-auth-methods}}
188-
{{#x-has-header-auth-methods}}
189-
token_in_header,
190-
{{/x-has-header-auth-methods}}
193+
{{#x-has-auth-methods}}
194+
claims,
195+
{{/x-has-auth-methods}}
191196
{{/vendorExtensions}}
192197
{{#headerParams.size}}
193198
header_params,

modules/openapi-generator/src/main/resources/rust-axum/server-route.mustache

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
/// Setup API Server.
2-
pub fn new<I, A>(api_impl: I) -> Router
2+
pub fn new<I, A{{#havingAuthMethods}}, C{{/havingAuthMethods}}>(api_impl: I) -> Router
33
where
44
I: AsRef<A> + Clone + Send + Sync + 'static,
5-
A: {{#apiInfo}}{{#apis}}{{#operations}}apis::{{classFilename}}::{{classnamePascalCase}} + {{/operations}}{{/apis}}{{/apiInfo}}{{#authMethods}}{{#isApiKey}}{{#isKeyInCookie}}apis::CookieAuthentication + {{/isKeyInCookie}}{{#isKeyInHeader}}apis::ApiKeyAuthHeader + {{/isKeyInHeader}}{{/isApiKey}}{{/authMethods}}'static,
5+
A: {{#apiInfo}}{{#apis}}{{#operations}}apis::{{classFilename}}::{{classnamePascalCase}}{{#havingAuthMethod}}<Claims = C>{{/havingAuthMethod}} + {{/operations}}{{/apis}}{{/apiInfo}}{{#authMethods}}{{#isApiKey}}{{#isKeyInCookie}}apis::CookieAuthentication<Claims = C> + {{/isKeyInCookie}}{{#isKeyInHeader}}apis::ApiKeyAuthHeader<Claims = C> + {{/isKeyInHeader}}{{/isApiKey}}{{/authMethods}}'static,
6+
{{#havingAuthMethods}}C: Send + Sync + 'static,{{/havingAuthMethods}}
67
{
78
// build our application with a route
89
Router::new()
910
{{#pathMethodOps}}
1011
.route("{{{basePathWithoutHost}}}{{{path}}}",
11-
{{#methodOperations}}{{{method}}}({{{operationID}}}::<I, A>){{^-last}}.{{/-last}}{{/methodOperations}}
12+
{{#methodOperations}}{{{method}}}({{{operationID}}}::<I, A{{#vendorExtensions}}{{#havingAuthMethod}}, C{{/havingAuthMethod}}{{/vendorExtensions}}>){{^-last}}.{{/-last}}{{/methodOperations}}
1213
)
1314
{{/pathMethodOps}}
1415
.with_state(api_impl)

samples/server/petstore/rust-axum/output/apikey-auths/Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ conversion = [
1818

1919
[dependencies]
2020
async-trait = "0.1"
21-
axum = { version = "0.7" }
21+
axum = "0.7"
2222
axum-extra = { version = "0.9", features = ["cookie", "multipart"] }
2323
base64 = "0.22"
2424
bytes = "1"
@@ -40,7 +40,7 @@ tokio = { version = "1", default-features = false, features = [
4040
] }
4141
tracing = { version = "0.1", features = ["attributes"] }
4242
uuid = { version = "1", features = ["serde"] }
43-
validator = { version = "0.18", features = ["derive"] }
43+
validator = { version = "0.19", features = ["derive"] }
4444

4545
[dev-dependencies]
4646
tracing-subscriber = "0.3"
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
11
pub mod payments;
22

33
/// API Key Authentication - Header.
4+
#[async_trait::async_trait]
45
pub trait ApiKeyAuthHeader {
5-
fn extract_token_from_header(
6+
type Claims;
7+
8+
/// Extracting Claims from Header. Return None if the Claims is invalid.
9+
async fn extract_claims_from_header(
610
&self,
711
headers: &axum::http::header::HeaderMap,
812
key: &str,
9-
) -> Option<String>;
13+
) -> Option<Self::Claims>;
1014
}
1115
/// Cookie Authentication.
16+
#[async_trait::async_trait]
1217
pub trait CookieAuthentication {
13-
fn extract_token_from_cookie(
18+
type Claims;
19+
20+
/// Extracting Claims from Cookie. Return None if the Claims is invalid.
21+
async fn extract_claims_from_cookie(
1422
&self,
1523
cookies: &axum_extra::extract::CookieJar,
1624
key: &str,
17-
) -> Option<String>;
25+
) -> Option<Self::Claims>;
1826
}

samples/server/petstore/rust-axum/output/apikey-auths/src/apis/payments.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ pub enum PostMakePaymentResponse {
3939
#[async_trait]
4040
#[allow(clippy::ptr_arg)]
4141
pub trait Payments {
42+
type Claims;
43+
4244
/// Get payment method by id.
4345
///
4446
/// GetPaymentMethodById - GET /v71/paymentMethods/{id}
@@ -68,7 +70,7 @@ pub trait Payments {
6870
method: Method,
6971
host: Host,
7072
cookies: CookieJar,
71-
token_in_cookie: Option<String>,
73+
claims: Self::Claims,
7274
body: Option<models::Payment>,
7375
) -> Result<PostMakePaymentResponse, ()>;
7476
}

0 commit comments

Comments
 (0)