Skip to content

Commit 117042a

Browse files
authored
[Rust-Axum] Fix wrong validation for non-model Body (#22155)
* Fix Issue 21143 * Update
1 parent c680f39 commit 117042a

File tree

58 files changed

+735
-111
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+735
-111
lines changed

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,24 @@ private boolean postProcessOperationWithModels(final CodegenOperation op) {
861861
}
862862

863863
if (op.bodyParam != null) {
864+
final var dataType = op.bodyParam.dataType;
865+
if (dataType.startsWith(vecType + "<String")) {
866+
op.bodyParam.vendorExtensions.put("is-vec-string", true);
867+
} else if (dataType.startsWith(vecType + "<models::")) {
868+
op.bodyParam.vendorExtensions.put("is-vec-nested", true);
869+
} else if (dataType.startsWith(mapType + "<String, String")) {
870+
op.bodyParam.vendorExtensions.put("is-map-string", true);
871+
} else if (dataType.startsWith(mapType + "<String, models::")) {
872+
op.bodyParam.vendorExtensions.put("is-map-nested", true);
873+
} else if (dataType.startsWith(mapType + "<String")) {
874+
op.bodyParam.vendorExtensions.put("is-map", true);
875+
} else if (dataType.startsWith("models::")) {
876+
op.bodyParam.isModel = true;
877+
} else if (dataType.equals("String")) {
878+
op.bodyParam.isString = true;
879+
op.bodyParam.vendorExtensions.put("is-string", true);
880+
}
881+
864882
if (consumesJson) {
865883
op.bodyParam.vendorExtensions.put("x-consumes-json", true);
866884
} else if (consumesFormUrlEncoded) {

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

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ use validator::Validate;
77
use crate::header;
88
use crate::{models, types::*};
99

10+
#[allow(dead_code)]
11+
fn from_validation_error(e: validator::ValidationError) -> validator::ValidationErrors {
12+
let mut errs = validator::ValidationErrors::new();
13+
errs.add("na", e);
14+
errs
15+
}
16+
1017
#[allow(dead_code)]
1118
pub fn check_xss_string(v: &str) -> std::result::Result<(), validator::ValidationError> {
1219
if ammonia::is_html(v) {
@@ -465,7 +472,7 @@ pub fn check_xss_map<T>(v: &std::collections::HashMap<String, T>) -> std::result
465472
#[serde(default)]
466473
{{/required}}
467474
pub {{{paramName}}}: {{{dataType}}},
468-
{{/isArray}}
475+
{{/isArray}}
469476
{{^isArray}}
470477
{{#required}}
471478
pub {{{paramName}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}},
@@ -580,10 +587,36 @@ impl std::str::FromStr for {{{classname}}} {
580587
#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)]
581588
{{/isMap}}
582589
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
583-
pub struct {{{classname}}}({{{dataType}}});
590+
pub struct {{{classname}}}(pub {{{dataType}}});
584591
585592
impl validator::Validate for {{{classname}}} {
586593
fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> {
594+
{{^isNullable}}
595+
{{#vendorExtensions.is-nested}}
596+
let _ = self.0.validate()?;
597+
{{/vendorExtensions.is-nested}}
598+
{{#vendorExtensions.is-string}}
599+
let _ = check_xss_string(&self.0).map_err(from_validation_error)?;
600+
{{/vendorExtensions.is-string}}
601+
{{#vendorExtensions.is-vec-nested}}
602+
for v in self.0.iter() {
603+
let _ = v.validate()?;
604+
}
605+
{{/vendorExtensions.is-vec-nested}}
606+
{{#vendorExtensions.is-vec-string}}
607+
let _ = check_xss_vec_string(&self.0).map_err(from_validation_error)?;
608+
{{/vendorExtensions.is-vec-string}}
609+
{{#vendorExtensions.is-map-nested}}
610+
let _ = check_xss_map_nested(&self.0).map_err(from_validation_error)?;
611+
{{/vendorExtensions.is-map-nested}}
612+
{{#vendorExtensions.is-map}}
613+
let _ = check_xss_map(&self.0).map_err(from_validation_error)?;
614+
{{/vendorExtensions.is-map}}
615+
{{#vendorExtensions.is-map-string}}
616+
let _ = check_xss_map_string(&self.0).map_err(from_validation_error)?;
617+
{{/vendorExtensions.is-map-string}}
618+
{{/isNullable}}
619+
587620
std::result::Result::Ok(())
588621
}
589622
}
@@ -656,7 +689,7 @@ impl ::std::str::FromStr for {{{classname}}} {
656689
{{! vec}}
657690
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
658691
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
659-
pub struct {{{classname}}}(Vec<{{{arrayModelType}}}>);
692+
pub struct {{{classname}}}(pub Vec<{{{arrayModelType}}}>);
660693

661694
impl validator::Validate for {{{classname}}} {
662695
fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@ use crate::{header, types::*};
1111

1212
#[allow(unused_imports)]
1313
use crate::{apis, models};
14+
15+
#[allow(unused_imports)]
16+
use crate::{models::check_xss_string, models::check_xss_vec_string, models::check_xss_map_string, models::check_xss_map_nested, models::check_xss_map};

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

Lines changed: 94 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -5,67 +5,108 @@
55
#[derive(validator::Validate)]
66
#[allow(dead_code)]
77
struct {{{operationIdCamelCase}}}BodyValidator<'a> {
8-
{{#hasValidation}}
9-
#[validate(
10-
{{#maxLength}}
11-
{{#minLength}}
12-
length(min = {{minLength}}, max = {{maxLength}}),
13-
{{/minLength}}
14-
{{^minLength}}
15-
length(max = {{maxLength}}),
16-
{{/minLength}}
17-
{{/maxLength}}
18-
{{^maxLength}}
19-
{{#minLength}}
20-
length(min = {{minLength}}),
21-
{{/minLength}}
22-
{{/maxLength}}
23-
{{#pattern}}
8+
{{^x-consumes-plain-text}}
9+
{{#isModel}}
10+
#[validate(nested)]
11+
{{/isModel}}
12+
{{^isModel}}
13+
{{#hasValidation}}
14+
#[validate(
15+
{{#maxLength}}
16+
{{#minLength}}
17+
length(min = {{minLength}}, max = {{maxLength}}),
18+
{{/minLength}}
19+
{{^minLength}}
20+
length(max = {{maxLength}}),
21+
{{/minLength}}
22+
{{/maxLength}}
23+
{{^maxLength}}
24+
{{#minLength}}
25+
length(min = {{minLength}}),
26+
{{/minLength}}
27+
{{/maxLength}}
28+
{{#pattern}}
29+
{{#isString}}
30+
regex(path = *RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}BodyValidator{{/lambda.uppercase}}),
31+
{{/isString}}
32+
{{^isString}}
33+
custom(function = "validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}BodyValidator{{/lambda.lowercase}}"),
34+
{{/isString}}
35+
{{/pattern}}
2436
{{#isString}}
25-
regex(path = *RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}BodyValidator{{/lambda.uppercase}}),
26-
{{/isString}}
27-
{{^isString}}
28-
custom(function = "validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}BodyValidator{{/lambda.lowercase}}"),
37+
custom(function = "check_xss_string"),
2938
{{/isString}}
30-
{{/pattern}}
31-
{{#maximum}}
32-
{{#minimum}}
33-
range(min = {{minimum}}, max = {{maximum}}),
34-
{{/minimum}}
35-
{{^minimum}}
36-
range(max = {{maximum}}),
37-
{{/minimum}}
38-
{{/maximum}}
39-
{{#minimum}}
40-
{{^maximum}}
41-
range(min = {{minimum}}),
42-
{{/maximum}}
43-
{{/minimum}}
44-
{{#maxItems}}
45-
{{#minItems}}
46-
length(min = {{minItems}}, max = {{maxItems}}),
47-
{{/minItems}}
48-
{{^minItems}}
49-
length(max = {{maxItems}}),
50-
{{/minItems}}
51-
{{/maxItems}}
52-
{{^maxItems}}
53-
{{#minItems}}
54-
length(min = {{minItems}}),
55-
{{/minItems}}
56-
{{/maxItems}}
57-
)]
58-
{{/hasValidation}}
59-
{{^x-consumes-plain-text}}
39+
{{#maximum}}
40+
{{#minimum}}
41+
range(min = {{minimum}}, max = {{maximum}}),
42+
{{/minimum}}
43+
{{^minimum}}
44+
range(max = {{maximum}}),
45+
{{/minimum}}
46+
{{/maximum}}
47+
{{#minimum}}
48+
{{^maximum}}
49+
range(min = {{minimum}}),
50+
{{/maximum}}
51+
{{/minimum}}
52+
{{#maxItems}}
53+
{{#minItems}}
54+
length(min = {{minItems}}, max = {{maxItems}}),
55+
{{/minItems}}
56+
{{^minItems}}
57+
length(max = {{maxItems}}),
58+
{{/minItems}}
59+
{{/maxItems}}
60+
{{^maxItems}}
61+
{{#minItems}}
62+
length(min = {{minItems}}),
63+
{{/minItems}}
64+
{{/maxItems}}
65+
{{#vendorExtensions.is-vec-nested}}
66+
nested,
67+
{{/vendorExtensions.is-vec-nested}}
68+
{{#vendorExtensions.is-vec-string}}
69+
custom(function = "check_xss_vec_string"),
70+
{{/vendorExtensions.is-vec-string}}
71+
{{#vendorExtensions.is-map-nested}}
72+
custom(function = "check_xss_map_nested"),
73+
{{/vendorExtensions.is-map-nested}}
74+
{{#vendorExtensions.is-map}}
75+
custom(function = "check_xss_map"),
76+
{{/vendorExtensions.is-map}}
77+
{{#vendorExtensions.is-map-string}}
78+
custom(function = "check_xss_map_string"),
79+
{{/vendorExtensions.is-map-string}} )]
80+
{{/hasValidation}}
6081
{{^hasValidation}}
61-
{{^isMap}}
62-
#[validate(nested)]
63-
{{/isMap}}
82+
{{#vendorExtensions.is-nested}}
83+
#[validate(nested)]
84+
{{/vendorExtensions.is-nested}}
85+
{{#vendorExtensions.is-string}}
86+
#[validate(custom(function = "check_xss_string"))]
87+
{{/vendorExtensions.is-string}}
88+
{{#vendorExtensions.is-vec-nested}}
89+
#[validate(nested)]
90+
{{/vendorExtensions.is-vec-nested}}
91+
{{#vendorExtensions.is-vec-string}}
92+
#[validate(custom(function = "check_xss_vec_string"))]
93+
{{/vendorExtensions.is-vec-string}}
94+
{{#vendorExtensions.is-map-nested}}
95+
#[validate(custom(function = "check_xss_map_nested"))]
96+
{{/vendorExtensions.is-map-nested}}
97+
{{#vendorExtensions.is-map}}
98+
#[validate(custom(function = "check_xss_map"))]
99+
{{/vendorExtensions.is-map}}
100+
{{#vendorExtensions.is-map-string}}
101+
#[validate(custom(function = "check_xss_map_string"))]
102+
{{/vendorExtensions.is-map-string}}
64103
{{/hasValidation}}
104+
{{/isModel}}
65105
body: &'a {{{dataType}}},
66106
{{/x-consumes-plain-text}}
67107
{{#x-consumes-plain-text}}
68108
{{#isString}}
109+
#[validate(custom(function = "check_xss_string"))]
69110
body: &'a String,
70111
{{/isString}}
71112
{{^isString}}

modules/openapi-generator/src/test/resources/3_0/rust-axum/rust-axum-oneof.yaml

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,56 @@ paths:
1919
"application/json":
2020
schema:
2121
"$ref": "#/components/schemas/Message"
22+
"/issue21143_1":
23+
post:
24+
operationId: i21143_1
25+
requestBody:
26+
required: true
27+
content:
28+
"application/json":
29+
schema:
30+
type: array
31+
items:
32+
type: integer
33+
responses:
34+
"200":
35+
description: Re-serialize and echo the request data
36+
content:
37+
"application/json":
38+
schema:
39+
"$ref": "#/components/schemas/Message"
40+
"/issue21143_2":
41+
post:
42+
operationId: i21143_2
43+
requestBody:
44+
required: true
45+
content:
46+
"application/json":
47+
schema:
48+
type: string
49+
responses:
50+
"200":
51+
description: Re-serialize and echo the request data
52+
content:
53+
"application/json":
54+
schema:
55+
"$ref": "#/components/schemas/Message"
56+
"/issue21143_3":
57+
post:
58+
operationId: i21143_3
59+
requestBody:
60+
required: true
61+
content:
62+
"application/json":
63+
schema:
64+
type: integer
65+
responses:
66+
"200":
67+
description: Re-serialize and echo the request data
68+
content:
69+
"application/json":
70+
schema:
71+
"$ref": "#/components/schemas/Message"
2272
components:
2373
schemas:
2474
Message:
@@ -87,7 +137,7 @@ components:
87137
- d
88138
SomethingCompletelyDifferent:
89139
oneOf:
90-
- items:
91-
type: object
92-
type: array
93-
- type: object
140+
- items:
141+
type: object
142+
type: array
143+
- type: object
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
7.16.0-SNAPSHOT
1+
7.17.0-SNAPSHOT

samples/server/petstore/rust-axum/output/apikey-authorization/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ server, you can easily generate a server stub.
1212
To see how to make this your own, look here: [README]((https://openapi-generator.tech))
1313

1414
- API version: 1.0.0
15-
- Generator version: 7.16.0-SNAPSHOT
15+
- Generator version: 7.17.0-SNAPSHOT
1616

1717

1818

samples/server/petstore/rust-axum/output/apikey-authorization/src/models.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ use validator::Validate;
77
use crate::header;
88
use crate::{models, types::*};
99

10+
#[allow(dead_code)]
11+
fn from_validation_error(e: validator::ValidationError) -> validator::ValidationErrors {
12+
let mut errs = validator::ValidationErrors::new();
13+
errs.add("na", e);
14+
errs
15+
}
16+
1017
#[allow(dead_code)]
1118
pub fn check_xss_string(v: &str) -> std::result::Result<(), validator::ValidationError> {
1219
if ammonia::is_html(v) {

samples/server/petstore/rust-axum/output/apikey-authorization/src/server/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ use crate::{header, types::*};
1212
#[allow(unused_imports)]
1313
use crate::{apis, models};
1414

15+
#[allow(unused_imports)]
16+
use crate::{
17+
models::check_xss_map, models::check_xss_map_nested, models::check_xss_map_string,
18+
models::check_xss_string, models::check_xss_vec_string,
19+
};
20+
1521
/// Setup API Server.
1622
pub fn new<I, A, E, C>(api_impl: I) -> Router
1723
where
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
7.16.0-SNAPSHOT
1+
7.17.0-SNAPSHOT

0 commit comments

Comments
 (0)