Skip to content

Commit ce311b2

Browse files
committed
refactor: rename validateObject to Validate, restore missing consumer tests, update CRD docs
- Rename crdSchemaValidator.validateObject -> Validate (exported, cleaner API) - Restore 4 consumer JWT tests that were incorrectly removed: AsymmetricWithWhitespaceOnlyPublicKey, AsymmetricES256WithoutAnyKey, AsymmetricEdDSAWithoutAnyKey, AsymmetricWithEmptyPublicKey (enterprise CRD gained this CEL rule via master #406) - Regenerate api-reference.md (CRD docs check was failing in CI)
1 parent dd377dd commit ce311b2

4 files changed

Lines changed: 99 additions & 17 deletions

File tree

api/v2/apisixconsumer_validation_test.go

Lines changed: 90 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,30 @@ func TestApisixConsumer_JwtAuth_SymmetricHS256(t *testing.T) {
4949
},
5050
},
5151
}
52-
assert.NoError(t, v.validateObject(t, ac))
52+
assert.NoError(t, v.Validate(t, ac))
53+
}
54+
55+
// TestApisixConsumer_JwtAuth_AsymmetricWithWhitespaceOnlyPublicKey verifies
56+
// that a whitespace-only public_key is treated as absent and rejected for
57+
// asymmetric algorithms.
58+
func TestApisixConsumer_JwtAuth_AsymmetricWithWhitespaceOnlyPublicKey(t *testing.T) {
59+
v := loadApisixConsumerSchema(t)
60+
ac := &apisixv2.ApisixConsumer{
61+
Spec: apisixv2.ApisixConsumerSpec{
62+
AuthParameter: apisixv2.ApisixConsumerAuthParameter{
63+
JwtAuth: &apisixv2.ApisixConsumerJwtAuth{
64+
Value: &apisixv2.ApisixConsumerJwtAuthValue{
65+
Key: "my-key",
66+
Algorithm: "RS256",
67+
PublicKey: " ",
68+
},
69+
},
70+
},
71+
},
72+
}
73+
err := v.Validate(t, ac)
74+
require.Error(t, err)
75+
assert.Contains(t, err.Error(), "algorithms other than HS256/HS384/HS512")
5376
}
5477

5578
func TestApisixConsumer_JwtAuth_SymmetricHS512(t *testing.T) {
@@ -67,7 +90,7 @@ func TestApisixConsumer_JwtAuth_SymmetricHS512(t *testing.T) {
6790
},
6891
},
6992
}
70-
assert.NoError(t, v.validateObject(t, ac))
93+
assert.NoError(t, v.Validate(t, ac))
7194
}
7295

7396
func TestApisixConsumer_JwtAuth_NoAlgorithmDefaultsToSymmetric(t *testing.T) {
@@ -84,7 +107,7 @@ func TestApisixConsumer_JwtAuth_NoAlgorithmDefaultsToSymmetric(t *testing.T) {
84107
},
85108
},
86109
}
87-
assert.NoError(t, v.validateObject(t, ac))
110+
assert.NoError(t, v.Validate(t, ac))
88111
}
89112

90113
func TestApisixConsumer_JwtAuth_AsymmetricRS256WithPublicKey(t *testing.T) {
@@ -102,7 +125,7 @@ func TestApisixConsumer_JwtAuth_AsymmetricRS256WithPublicKey(t *testing.T) {
102125
},
103126
},
104127
}
105-
assert.NoError(t, v.validateObject(t, ac))
128+
assert.NoError(t, v.Validate(t, ac))
106129
}
107130

108131
func TestApisixConsumer_JwtAuth_AsymmetricRS256WithPrivateKey(t *testing.T) {
@@ -120,7 +143,7 @@ func TestApisixConsumer_JwtAuth_AsymmetricRS256WithPrivateKey(t *testing.T) {
120143
},
121144
},
122145
}
123-
assert.NoError(t, v.validateObject(t, ac))
146+
assert.NoError(t, v.Validate(t, ac))
124147
}
125148

126149
func TestApisixConsumer_JwtAuth_AsymmetricRS256WithBothKeys(t *testing.T) {
@@ -139,7 +162,7 @@ func TestApisixConsumer_JwtAuth_AsymmetricRS256WithBothKeys(t *testing.T) {
139162
},
140163
},
141164
}
142-
assert.NoError(t, v.validateObject(t, ac))
165+
assert.NoError(t, v.Validate(t, ac))
143166
}
144167

145168
// TestApisixConsumer_JwtAuth_AsymmetricRS256WithoutAnyKey verifies that RS256
@@ -158,7 +181,66 @@ func TestApisixConsumer_JwtAuth_AsymmetricRS256WithoutAnyKey(t *testing.T) {
158181
},
159182
},
160183
}
161-
err := v.validateObject(t, ac)
184+
err := v.Validate(t, ac)
185+
require.Error(t, err)
186+
assert.Contains(t, err.Error(), "algorithms other than HS256/HS384/HS512")
187+
}
188+
189+
func TestApisixConsumer_JwtAuth_AsymmetricES256WithoutAnyKey(t *testing.T) {
190+
v := loadApisixConsumerSchema(t)
191+
ac := &apisixv2.ApisixConsumer{
192+
Spec: apisixv2.ApisixConsumerSpec{
193+
AuthParameter: apisixv2.ApisixConsumerAuthParameter{
194+
JwtAuth: &apisixv2.ApisixConsumerJwtAuth{
195+
Value: &apisixv2.ApisixConsumerJwtAuthValue{
196+
Key: "my-key",
197+
Algorithm: "ES256",
198+
},
199+
},
200+
},
201+
},
202+
}
203+
err := v.Validate(t, ac)
204+
require.Error(t, err)
205+
assert.Contains(t, err.Error(), "algorithms other than HS256/HS384/HS512")
206+
}
207+
208+
func TestApisixConsumer_JwtAuth_AsymmetricEdDSAWithoutAnyKey(t *testing.T) {
209+
v := loadApisixConsumerSchema(t)
210+
ac := &apisixv2.ApisixConsumer{
211+
Spec: apisixv2.ApisixConsumerSpec{
212+
AuthParameter: apisixv2.ApisixConsumerAuthParameter{
213+
JwtAuth: &apisixv2.ApisixConsumerJwtAuth{
214+
Value: &apisixv2.ApisixConsumerJwtAuthValue{
215+
Key: "my-key",
216+
Algorithm: "EdDSA",
217+
},
218+
},
219+
},
220+
},
221+
}
222+
err := v.Validate(t, ac)
223+
require.Error(t, err)
224+
assert.Contains(t, err.Error(), "algorithms other than HS256/HS384/HS512")
225+
}
226+
227+
func TestApisixConsumer_JwtAuth_AsymmetricWithEmptyPublicKey(t *testing.T) {
228+
v := loadApisixConsumerSchema(t)
229+
ac := &apisixv2.ApisixConsumer{
230+
Spec: apisixv2.ApisixConsumerSpec{
231+
AuthParameter: apisixv2.ApisixConsumerAuthParameter{
232+
JwtAuth: &apisixv2.ApisixConsumerJwtAuth{
233+
Value: &apisixv2.ApisixConsumerJwtAuthValue{
234+
Key: "my-key",
235+
Algorithm: "RS256",
236+
// PublicKey is empty string — omitempty means it won't appear
237+
// in the serialized JSON, same effect as not set
238+
},
239+
},
240+
},
241+
},
242+
}
243+
err := v.Validate(t, ac)
162244
require.Error(t, err)
163245
assert.Contains(t, err.Error(), "algorithms other than HS256/HS384/HS512")
164246
}
@@ -182,5 +264,5 @@ func TestApisixConsumer_JwtAuth_EmptyAlgorithmTreatedAsSymmetric(t *testing.T) {
182264
},
183265
},
184266
}
185-
assert.NoError(t, v.validateObject(t, ac))
267+
assert.NoError(t, v.Validate(t, ac))
186268
}

api/v2/apisixroute_types_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,21 +74,21 @@ func newRouteWithBodyExpr(ingressClass, fieldName, value string) *apisixv2.Apisi
7474
// simple field name passes CRD schema validation.
7575
func TestApisixRoute_BodyScope_SimpleField(t *testing.T) {
7676
v := loadApisixRouteSchema(t)
77-
assert.NoError(t, v.validateObject(t, newRouteWithBodyExpr("apisix", "action", "login")))
77+
assert.NoError(t, v.Validate(t, newRouteWithBodyExpr("apisix", "action", "login")))
7878
}
7979

8080
// TestApisixRoute_BodyScope_NestedJSONPath verifies that a Body scope expr with
8181
// a dot-notation JSON path passes CRD schema validation.
8282
func TestApisixRoute_BodyScope_NestedJSONPath(t *testing.T) {
8383
v := loadApisixRouteSchema(t)
84-
assert.NoError(t, v.validateObject(t, newRouteWithBodyExpr("apisix", "model.version", "gpt-4")))
84+
assert.NoError(t, v.Validate(t, newRouteWithBodyExpr("apisix", "model.version", "gpt-4")))
8585
}
8686

8787
// TestApisixRoute_BodyScope_EmptyName verifies that a Body scope expr with an
8888
// empty name is rejected by the CEL XValidation rule.
8989
func TestApisixRoute_BodyScope_EmptyName(t *testing.T) {
9090
v := loadApisixRouteSchema(t)
91-
err := v.validateObject(t, newRouteWithBodyExpr("apisix", "", "login"))
91+
err := v.Validate(t, newRouteWithBodyExpr("apisix", "", "login"))
9292
require.Error(t, err)
9393
assert.Contains(t, err.Error(), "name is required when scope is not Path")
9494
}
@@ -123,5 +123,5 @@ func TestApisixRoute_PathScope_EmptyName(t *testing.T) {
123123
},
124124
},
125125
}
126-
assert.NoError(t, v.validateObject(t, ar))
126+
assert.NoError(t, v.Validate(t, ar))
127127
}

api/v2/crd_schema_validator_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ type crdSchemaValidator struct {
3838
internal *apiextensions.JSONSchemaProps
3939
}
4040

41-
// validateObject marshals obj to JSON then runs the CRD's OpenAPI schema validator
41+
// Validate marshals obj to JSON then runs the CRD's OpenAPI schema validator
4242
// followed by any CEL x-kubernetes-validations rules.
43-
func (v *crdSchemaValidator) validateObject(t *testing.T, obj any) error {
43+
func (v *crdSchemaValidator) Validate(t *testing.T, obj any) error {
4444
t.Helper()
4545

4646
data, err := json.Marshal(obj)

docs/en/latest/reference/api-reference.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,7 +1089,7 @@ ApisixRouteHTTPMatch defines the conditions used to match incoming HTTP requests
10891089
| `hosts` _string array_ | Hosts specifies Host header values to match. Supports exact and wildcard domains. Only one level of wildcard is allowed (e.g., `*.example.com` is valid, but `*.*.example.com` is not). |
10901090
| `remoteAddrs` _string array_ | RemoteAddrs is a list of source IP addresses or CIDR ranges to match. Supports both IPv4 and IPv6 formats. |
10911091
| `exprs` _[ApisixRouteHTTPMatchExprs](#apisixroutehttpmatchexprs)_ | NginxVars defines match conditions based on Nginx variables. |
1092-
| `filter_func` _string_ | FilterFunc is a user-defined function for advanced request filtering. The function can use Nginx variables through the `vars` parameter. This field is supported in APISIX but not in API7 Enterprise. |
1092+
| `filter_func` _string_ | FilterFunc is a user-defined function for advanced request filtering. The function can use Nginx variables through the `vars` parameter. |
10931093

10941094

10951095
_Appears in:_
@@ -1104,7 +1104,7 @@ ApisixRouteHTTPMatchExpr represents a binary expression used to match requests b
11041104

11051105
| Field | Description |
11061106
| --- | --- |
1107-
| `subject` _[ApisixRouteHTTPMatchExprSubject](#apisixroutehttpmatchexprsubject)_ | Subject defines the left-hand side of the expression. It can be any [built-in variable](/apisix/reference/built-in-variables) or string literal. |
1107+
| `subject` _[ApisixRouteHTTPMatchExprSubject](#apisixroutehttpmatchexprsubject)_ | Subject defines the left-hand side of the expression. It can be any [APISIX variable](https://apisix.apache.org/docs/apisix/apisix-variable) or string literal. |
11081108
| `op` _string_ | Op specifies the operator used in the expression. Can be `Equal`, `NotEqual`, `GreaterThan`, `GreaterThanEqual`, `LessThan`, `LessThanEqual`, `RegexMatch`, `RegexNotMatch`, `RegexMatchCaseInsensitive`, `RegexNotMatchCaseInsensitive`, `In`, or `NotIn`. |
11091109
| `set` _string array_ | Set provides a list of acceptable values for the expression. This should be used when Op is `In` or `NotIn`. |
11101110
| `value` _string_ | Value defines a single value to compare against the subject. This should be used when Op is not `In` or `NotIn`. Set and Value are mutually exclusive—only one should be set at a time. |
@@ -1138,7 +1138,7 @@ _Base type:_ `[ApisixRouteHTTPMatchExpr](#apisixroutehttpmatchexpr)`
11381138

11391139
| Field | Description |
11401140
| --- | --- |
1141-
| `subject` _[ApisixRouteHTTPMatchExprSubject](#apisixroutehttpmatchexprsubject)_ | Subject defines the left-hand side of the expression. It can be any [built-in variable](/apisix/reference/built-in-variables) or string literal. |
1141+
| `subject` _[ApisixRouteHTTPMatchExprSubject](#apisixroutehttpmatchexprsubject)_ | Subject defines the left-hand side of the expression. It can be any [APISIX variable](https://apisix.apache.org/docs/apisix/apisix-variable) or string literal. |
11421142
| `op` _string_ | Op specifies the operator used in the expression. Can be `Equal`, `NotEqual`, `GreaterThan`, `GreaterThanEqual`, `LessThan`, `LessThanEqual`, `RegexMatch`, `RegexNotMatch`, `RegexMatchCaseInsensitive`, `RegexNotMatchCaseInsensitive`, `In`, or `NotIn`. |
11431143
| `set` _string array_ | Set provides a list of acceptable values for the expression. This should be used when Op is `In` or `NotIn`. |
11441144
| `value` _string_ | Value defines a single value to compare against the subject. This should be used when Op is not `In` or `NotIn`. Set and Value are mutually exclusive—only one should be set at a time. |

0 commit comments

Comments
 (0)