Skip to content

Commit 03a3c0c

Browse files
authored
Merge pull request kubernetes#131573 from enj/enj/t/oidc_nested_cel
jwt: add unit tests for using CEL with deeply nested claims
2 parents 6c05c5e + 5441f5f commit 03a3c0c

File tree

1 file changed

+327
-2
lines changed
  • staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc

1 file changed

+327
-2
lines changed

staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc_test.go

Lines changed: 327 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2471,6 +2471,327 @@ func TestToken(t *testing.T) {
24712471
Name: "jane",
24722472
},
24732473
},
2474+
{
2475+
name: "claim mappings with expressions and deeply nested claim - success",
2476+
options: Options{
2477+
JWTAuthenticator: apiserver.JWTAuthenticator{
2478+
Issuer: apiserver.Issuer{
2479+
URL: "https://auth.example.com",
2480+
Audiences: []string{"my-client"},
2481+
},
2482+
ClaimValidationRules: []apiserver.ClaimValidationRule{
2483+
{
2484+
Expression: "claims.turtle.foo.other1.bit1 && !claims.turtle.foo.bar.other1.bit2",
2485+
},
2486+
},
2487+
ClaimMappings: apiserver.ClaimMappings{
2488+
Username: apiserver.PrefixedClaimOrExpression{
2489+
Expression: "claims.turtle.foo.bar.baz.panda[0]",
2490+
},
2491+
UID: apiserver.ClaimOrExpression{
2492+
Expression: "claims.turtle.foo.bar.baz.panda[1]",
2493+
},
2494+
Groups: apiserver.PrefixedClaimOrExpression{
2495+
Expression: "claims.turtle.foo.bar.baz.panda",
2496+
},
2497+
Extra: []apiserver.ExtraMapping{
2498+
{
2499+
Key: "bio.snorlax.org/1",
2500+
ValueExpression: "string(claims.turtle.foo.bar.other1.bit2)",
2501+
},
2502+
{
2503+
Key: "bio.snorlax.org/2",
2504+
ValueExpression: "string(claims.turtle.foo.bar.baz.other1.bit3)",
2505+
},
2506+
{
2507+
Key: "bio.snorlax.org/3",
2508+
ValueExpression: "[string(claims.turtle.foo.bar.baz.other1.bit1)] + ['a', 'b', 'c']",
2509+
},
2510+
},
2511+
},
2512+
UserValidationRules: []apiserver.UserValidationRule{
2513+
{
2514+
Expression: `user.username != "bad"`,
2515+
},
2516+
{
2517+
Expression: `user.uid == "007"`,
2518+
},
2519+
{
2520+
Expression: `"claus" in user.groups`,
2521+
},
2522+
{
2523+
Expression: `user.extra["bio.snorlax.org/3"].size() == 4`,
2524+
},
2525+
},
2526+
},
2527+
now: func() time.Time { return now },
2528+
},
2529+
signingKey: loadRSAPrivKey(t, "testdata/rsa_1.pem", jose.RS256),
2530+
pubKeys: []*jose.JSONWebKey{
2531+
loadRSAKey(t, "testdata/rsa_1.pem", jose.RS256),
2532+
},
2533+
claims: fmt.Sprintf(`{
2534+
"iss": "https://auth.example.com",
2535+
"aud": "my-client",
2536+
"exp": %d,
2537+
"turtle": {
2538+
"foo": {
2539+
"1": "a",
2540+
"2": "b",
2541+
"other1": {
2542+
"bit1": true,
2543+
"bit2": false,
2544+
"bit3": 1
2545+
},
2546+
"bar": {
2547+
"3": "c",
2548+
"4": "d",
2549+
"other1": {
2550+
"bit1": true,
2551+
"bit2": false,
2552+
"bit3": 1
2553+
},
2554+
"baz": {
2555+
"5": "e",
2556+
"6": "f",
2557+
"panda": [
2558+
"snorlax",
2559+
"007",
2560+
"santa",
2561+
"claus"
2562+
],
2563+
"other1": {
2564+
"bit1": true,
2565+
"bit2": false,
2566+
"bit3": 1
2567+
}
2568+
}
2569+
}
2570+
}
2571+
}
2572+
}`, valid.Unix()),
2573+
want: &user.DefaultInfo{
2574+
Name: "snorlax",
2575+
UID: "007",
2576+
Groups: []string{"snorlax", "007", "santa", "claus"},
2577+
Extra: map[string][]string{
2578+
"bio.snorlax.org/1": {"false"},
2579+
"bio.snorlax.org/2": {"1"},
2580+
"bio.snorlax.org/3": {"true", "a", "b", "c"},
2581+
},
2582+
},
2583+
},
2584+
{
2585+
name: "claim mappings with expressions and deeply nested claim - success via optional",
2586+
options: Options{
2587+
JWTAuthenticator: apiserver.JWTAuthenticator{
2588+
Issuer: apiserver.Issuer{
2589+
URL: "https://auth.example.com",
2590+
Audiences: []string{"my-client"},
2591+
},
2592+
ClaimValidationRules: []apiserver.ClaimValidationRule{
2593+
{
2594+
Expression: "claims.turtle.foo.other1.bit1 && !claims.turtle.foo.bar.other1.bit2",
2595+
},
2596+
},
2597+
ClaimMappings: apiserver.ClaimMappings{
2598+
Username: apiserver.PrefixedClaimOrExpression{
2599+
Expression: "claims.turtle.foo.bar.baz.panda[0]",
2600+
},
2601+
UID: apiserver.ClaimOrExpression{
2602+
Expression: "claims.turtle.foo.bar.baz.panda[1]",
2603+
},
2604+
Groups: apiserver.PrefixedClaimOrExpression{
2605+
Expression: "claims.turtle.foo.bar.baz.?a.b.c.d.orValue([ 'claus' ])", // this passes because of the optional
2606+
},
2607+
Extra: []apiserver.ExtraMapping{
2608+
{
2609+
Key: "bio.snorlax.org/1",
2610+
ValueExpression: "string(claims.turtle.foo.bar.other1.bit2)",
2611+
},
2612+
{
2613+
Key: "bio.snorlax.org/2",
2614+
ValueExpression: "string(claims.turtle.foo.bar.baz.other1.bit3)",
2615+
},
2616+
{
2617+
Key: "bio.snorlax.org/3",
2618+
ValueExpression: "[string(claims.turtle.foo.bar.baz.other1.bit1)] + ['a', 'b', 'c']",
2619+
},
2620+
},
2621+
},
2622+
UserValidationRules: []apiserver.UserValidationRule{
2623+
{
2624+
Expression: `user.username != "bad"`,
2625+
},
2626+
{
2627+
Expression: `user.uid == "007"`,
2628+
},
2629+
{
2630+
Expression: `"claus" in user.groups`,
2631+
},
2632+
{
2633+
Expression: `user.extra["bio.snorlax.org/3"].size() == 4`,
2634+
},
2635+
},
2636+
},
2637+
now: func() time.Time { return now },
2638+
},
2639+
signingKey: loadRSAPrivKey(t, "testdata/rsa_1.pem", jose.RS256),
2640+
pubKeys: []*jose.JSONWebKey{
2641+
loadRSAKey(t, "testdata/rsa_1.pem", jose.RS256),
2642+
},
2643+
claims: fmt.Sprintf(`{
2644+
"iss": "https://auth.example.com",
2645+
"aud": "my-client",
2646+
"exp": %d,
2647+
"turtle": {
2648+
"foo": {
2649+
"1": "a",
2650+
"2": "b",
2651+
"other1": {
2652+
"bit1": true,
2653+
"bit2": false,
2654+
"bit3": 1
2655+
},
2656+
"bar": {
2657+
"3": "c",
2658+
"4": "d",
2659+
"other1": {
2660+
"bit1": true,
2661+
"bit2": false,
2662+
"bit3": 1
2663+
},
2664+
"baz": {
2665+
"5": "e",
2666+
"6": "f",
2667+
"panda": [
2668+
"snorlax",
2669+
"007",
2670+
"santa",
2671+
"claus"
2672+
],
2673+
"other1": {
2674+
"bit1": true,
2675+
"bit2": false,
2676+
"bit3": 1
2677+
}
2678+
}
2679+
}
2680+
}
2681+
}
2682+
}`, valid.Unix()),
2683+
want: &user.DefaultInfo{
2684+
Name: "snorlax",
2685+
UID: "007",
2686+
Groups: []string{"claus"},
2687+
Extra: map[string][]string{
2688+
"bio.snorlax.org/1": {"false"},
2689+
"bio.snorlax.org/2": {"1"},
2690+
"bio.snorlax.org/3": {"true", "a", "b", "c"},
2691+
},
2692+
},
2693+
},
2694+
{
2695+
name: "claim mappings with expressions and deeply nested claim - failure without optional",
2696+
options: Options{
2697+
JWTAuthenticator: apiserver.JWTAuthenticator{
2698+
Issuer: apiserver.Issuer{
2699+
URL: "https://auth.example.com",
2700+
Audiences: []string{"my-client"},
2701+
},
2702+
ClaimValidationRules: []apiserver.ClaimValidationRule{
2703+
{
2704+
Expression: "claims.turtle.foo.other1.bit1 && !claims.turtle.foo.bar.other1.bit2",
2705+
},
2706+
},
2707+
ClaimMappings: apiserver.ClaimMappings{
2708+
Username: apiserver.PrefixedClaimOrExpression{
2709+
Expression: "claims.turtle.foo.bar.baz.panda[0]",
2710+
},
2711+
UID: apiserver.ClaimOrExpression{
2712+
Expression: "claims.turtle.foo.bar.baz.panda[1]",
2713+
},
2714+
Groups: apiserver.PrefixedClaimOrExpression{
2715+
Expression: "claims.turtle.foo.bar.baz.a.b.c.d", // this fails because the key does not exist
2716+
},
2717+
Extra: []apiserver.ExtraMapping{
2718+
{
2719+
Key: "bio.snorlax.org/1",
2720+
ValueExpression: "string(claims.turtle.foo.bar.other1.bit2)",
2721+
},
2722+
{
2723+
Key: "bio.snorlax.org/2",
2724+
ValueExpression: "string(claims.turtle.foo.bar.baz.other1.bit3)",
2725+
},
2726+
{
2727+
Key: "bio.snorlax.org/3",
2728+
ValueExpression: "[string(claims.turtle.foo.bar.baz.other1.bit1)] + ['a', 'b', 'c']",
2729+
},
2730+
},
2731+
},
2732+
UserValidationRules: []apiserver.UserValidationRule{
2733+
{
2734+
Expression: `user.username != "bad"`,
2735+
},
2736+
{
2737+
Expression: `user.uid == "007"`,
2738+
},
2739+
{
2740+
Expression: `"claus" in user.groups`,
2741+
},
2742+
{
2743+
Expression: `user.extra["bio.snorlax.org/3"].size() == 4`,
2744+
},
2745+
},
2746+
},
2747+
now: func() time.Time { return now },
2748+
},
2749+
signingKey: loadRSAPrivKey(t, "testdata/rsa_1.pem", jose.RS256),
2750+
pubKeys: []*jose.JSONWebKey{
2751+
loadRSAKey(t, "testdata/rsa_1.pem", jose.RS256),
2752+
},
2753+
claims: fmt.Sprintf(`{
2754+
"iss": "https://auth.example.com",
2755+
"aud": "my-client",
2756+
"exp": %d,
2757+
"turtle": {
2758+
"foo": {
2759+
"1": "a",
2760+
"2": "b",
2761+
"other1": {
2762+
"bit1": true,
2763+
"bit2": false,
2764+
"bit3": 1
2765+
},
2766+
"bar": {
2767+
"3": "c",
2768+
"4": "d",
2769+
"other1": {
2770+
"bit1": true,
2771+
"bit2": false,
2772+
"bit3": 1
2773+
},
2774+
"baz": {
2775+
"5": "e",
2776+
"6": "f",
2777+
"panda": [
2778+
"snorlax",
2779+
"007",
2780+
"santa",
2781+
"claus"
2782+
],
2783+
"other1": {
2784+
"bit1": true,
2785+
"bit2": false,
2786+
"bit3": 1
2787+
}
2788+
}
2789+
}
2790+
}
2791+
}
2792+
}`, valid.Unix()),
2793+
wantErr: "oidc: error evaluating group claim expression: expression 'claims.turtle.foo.bar.baz.a.b.c.d' resulted in error: no such key: a",
2794+
},
24742795
{
24752796
name: "groups claim mapping with expression",
24762797
options: Options{
@@ -3506,8 +3827,12 @@ func TestToken(t *testing.T) {
35063827

35073828
var successTestCount, failureTestCount int
35083829
for _, test := range tests {
3509-
t.Run(test.name, test.run)
3510-
if test.wantSkip || len(test.wantInitErr) > 0 || len(test.wantHealthErrPrefix) > 0 {
3830+
var called bool
3831+
t.Run(test.name, func(t *testing.T) {
3832+
called = true
3833+
test.run(t)
3834+
})
3835+
if test.wantSkip || len(test.wantInitErr) > 0 || len(test.wantHealthErrPrefix) > 0 || !called {
35113836
continue
35123837
}
35133838
// check metrics for success and failure

0 commit comments

Comments
 (0)