@@ -36,19 +36,16 @@ import (
36
36
"io/ioutil"
37
37
"net/http"
38
38
"net/url"
39
- "reflect"
40
39
"strings"
41
40
"sync"
42
41
"sync/atomic"
43
42
"time"
44
43
45
44
"github.com/coreos/go-oidc"
46
45
celgo "github.com/google/cel-go/cel"
46
+ "github.com/google/cel-go/common/types"
47
47
"github.com/google/cel-go/common/types/ref"
48
48
49
- authenticationv1 "k8s.io/api/authentication/v1"
50
- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
51
- "k8s.io/apimachinery/pkg/runtime"
52
49
"k8s.io/apimachinery/pkg/util/net"
53
50
"k8s.io/apimachinery/pkg/util/sets"
54
51
"k8s.io/apimachinery/pkg/util/wait"
@@ -58,6 +55,7 @@ import (
58
55
authenticationcel "k8s.io/apiserver/pkg/authentication/cel"
59
56
authenticationtokenjwt "k8s.io/apiserver/pkg/authentication/token/jwt"
60
57
"k8s.io/apiserver/pkg/authentication/user"
58
+ "k8s.io/apiserver/pkg/cel/lazy"
61
59
certutil "k8s.io/client-go/util/cert"
62
60
"k8s.io/klog/v2"
63
61
)
@@ -708,36 +706,31 @@ func (a *jwtAuthenticator) AuthenticateToken(ctx context.Context, token string)
708
706
}
709
707
}
710
708
711
- var claimsUnstructured * unstructured. Unstructured
712
- // Convert the claims to unstructured so that we can evaluate the CEL expressions
709
+ var claimsValue * lazy. MapValue
710
+ // Convert the claims to traits.Mapper so that we can evaluate the CEL expressions
713
711
// against the claims. This is done once here so that we don't have to convert
714
- // the claims to unstructured multiple times in the CEL mapper for each mapping.
712
+ // the claims to traits.Mapper multiple times in the CEL mapper for each mapping.
715
713
// Only perform this conversion if any of the mapping or validation rules contain
716
- // CEL expressions.
717
- // TODO(aramase): In the future when we look into making distributed claims work,
718
- // we should see if we can skip this function and use a dynamic type resolver for
719
- // both json.RawMessage and the distributed claim fetching.
714
+ // CEL expressions. The traits.Mapper is lazily evaluated against the expressions.
720
715
if a .celMapper .Username != nil || a .celMapper .Groups != nil || a .celMapper .UID != nil || a .celMapper .Extra != nil || a .celMapper .ClaimValidationRules != nil {
721
- if claimsUnstructured , err = convertObjectToUnstructured (& c ); err != nil {
722
- return nil , false , fmt .Errorf ("oidc: could not convert claims to unstructured: %w" , err )
723
- }
716
+ claimsValue = newClaimsValue (c )
724
717
}
725
718
726
719
var username string
727
- if username , err = a .getUsername (ctx , c , claimsUnstructured ); err != nil {
720
+ if username , err = a .getUsername (ctx , c , claimsValue ); err != nil {
728
721
return nil , false , err
729
722
}
730
723
731
724
info := & user.DefaultInfo {Name : username }
732
- if info .Groups , err = a .getGroups (ctx , c , claimsUnstructured ); err != nil {
725
+ if info .Groups , err = a .getGroups (ctx , c , claimsValue ); err != nil {
733
726
return nil , false , err
734
727
}
735
728
736
- if info .UID , err = a .getUID (ctx , c , claimsUnstructured ); err != nil {
729
+ if info .UID , err = a .getUID (ctx , c , claimsValue ); err != nil {
737
730
return nil , false , err
738
731
}
739
732
740
- extra , err := a .getExtra (ctx , c , claimsUnstructured )
733
+ extra , err := a .getExtra (ctx , c , claimsValue )
741
734
if err != nil {
742
735
return nil , false , err
743
736
}
@@ -762,7 +755,7 @@ func (a *jwtAuthenticator) AuthenticateToken(ctx context.Context, token string)
762
755
}
763
756
764
757
if a .celMapper .ClaimValidationRules != nil {
765
- evalResult , err := a .celMapper .ClaimValidationRules .EvalClaimMappings (ctx , claimsUnstructured )
758
+ evalResult , err := a .celMapper .ClaimValidationRules .EvalClaimMappings (ctx , claimsValue )
766
759
if err != nil {
767
760
return nil , false , fmt .Errorf ("oidc: error evaluating claim validation expression: %w" , err )
768
761
}
@@ -778,15 +771,13 @@ func (a *jwtAuthenticator) AuthenticateToken(ctx context.Context, token string)
778
771
}
779
772
780
773
if a .celMapper .UserValidationRules != nil {
781
- // Convert the user info to unstructured so that we can evaluate the CEL expressions
774
+ // Convert the user info to traits.Mapper so that we can evaluate the CEL expressions
782
775
// against the user info. This is done once here so that we don't have to convert
783
- // the user info to unstructured multiple times in the CEL mapper for each mapping.
784
- userInfoUnstructured , err := convertUserInfoToUnstructured (info )
785
- if err != nil {
786
- return nil , false , fmt .Errorf ("oidc: could not convert user info to unstructured: %w" , err )
787
- }
776
+ // the user info to traits.Mapper multiple times in the CEL mapper for each mapping.
777
+ // The traits.Mapper is lazily evaluated against the expressions.
778
+ userInfoVal := newUserInfoValue (info )
788
779
789
- evalResult , err := a .celMapper .UserValidationRules .EvalUser (ctx , userInfoUnstructured )
780
+ evalResult , err := a .celMapper .UserValidationRules .EvalUser (ctx , userInfoVal )
790
781
if err != nil {
791
782
return nil , false , fmt .Errorf ("oidc: error evaluating user info validation rule: %w" , err )
792
783
}
@@ -812,9 +803,9 @@ func (a *jwtAuthenticator) HealthCheck() error {
812
803
return nil
813
804
}
814
805
815
- func (a * jwtAuthenticator ) getUsername (ctx context.Context , c claims , claimsUnstructured * unstructured. Unstructured ) (string , error ) {
806
+ func (a * jwtAuthenticator ) getUsername (ctx context.Context , c claims , claimsValue * lazy. MapValue ) (string , error ) {
816
807
if a .celMapper .Username != nil {
817
- evalResult , err := a .celMapper .Username .EvalClaimMapping (ctx , claimsUnstructured )
808
+ evalResult , err := a .celMapper .Username .EvalClaimMapping (ctx , claimsValue )
818
809
if err != nil {
819
810
return "" , fmt .Errorf ("oidc: error evaluating username claim expression: %w" , err )
820
811
}
@@ -860,7 +851,7 @@ func (a *jwtAuthenticator) getUsername(ctx context.Context, c claims, claimsUnst
860
851
return username , nil
861
852
}
862
853
863
- func (a * jwtAuthenticator ) getGroups (ctx context.Context , c claims , claimsUnstructured * unstructured. Unstructured ) ([]string , error ) {
854
+ func (a * jwtAuthenticator ) getGroups (ctx context.Context , c claims , claimsValue * lazy. MapValue ) ([]string , error ) {
864
855
groupsClaim := a .jwtAuthenticator .ClaimMappings .Groups .Claim
865
856
if len (groupsClaim ) > 0 {
866
857
if _ , ok := c [groupsClaim ]; ok {
@@ -888,7 +879,7 @@ func (a *jwtAuthenticator) getGroups(ctx context.Context, c claims, claimsUnstru
888
879
return nil , nil
889
880
}
890
881
891
- evalResult , err := a .celMapper .Groups .EvalClaimMapping (ctx , claimsUnstructured )
882
+ evalResult , err := a .celMapper .Groups .EvalClaimMapping (ctx , claimsValue )
892
883
if err != nil {
893
884
return nil , fmt .Errorf ("oidc: error evaluating group claim expression: %w" , err )
894
885
}
@@ -900,7 +891,7 @@ func (a *jwtAuthenticator) getGroups(ctx context.Context, c claims, claimsUnstru
900
891
return groups , nil
901
892
}
902
893
903
- func (a * jwtAuthenticator ) getUID (ctx context.Context , c claims , claimsUnstructured * unstructured. Unstructured ) (string , error ) {
894
+ func (a * jwtAuthenticator ) getUID (ctx context.Context , c claims , claimsValue * lazy. MapValue ) (string , error ) {
904
895
uidClaim := a .jwtAuthenticator .ClaimMappings .UID .Claim
905
896
if len (uidClaim ) > 0 {
906
897
var uid string
@@ -914,7 +905,7 @@ func (a *jwtAuthenticator) getUID(ctx context.Context, c claims, claimsUnstructu
914
905
return "" , nil
915
906
}
916
907
917
- evalResult , err := a .celMapper .UID .EvalClaimMapping (ctx , claimsUnstructured )
908
+ evalResult , err := a .celMapper .UID .EvalClaimMapping (ctx , claimsValue )
918
909
if err != nil {
919
910
return "" , fmt .Errorf ("oidc: error evaluating uid claim expression: %w" , err )
920
911
}
@@ -925,7 +916,7 @@ func (a *jwtAuthenticator) getUID(ctx context.Context, c claims, claimsUnstructu
925
916
return evalResult .EvalResult .Value ().(string ), nil
926
917
}
927
918
928
- func (a * jwtAuthenticator ) getExtra (ctx context.Context , c claims , claimsUnstructured * unstructured. Unstructured ) (map [string ][]string , error ) {
919
+ func (a * jwtAuthenticator ) getExtra (ctx context.Context , c claims , claimsValue * lazy. MapValue ) (map [string ][]string , error ) {
929
920
extra := make (map [string ][]string )
930
921
931
922
if credentialID := getCredentialID (c ); len (credentialID ) > 0 {
@@ -936,7 +927,7 @@ func (a *jwtAuthenticator) getExtra(ctx context.Context, c claims, claimsUnstruc
936
927
return extra , nil
937
928
}
938
929
939
- evalResult , err := a .celMapper .Extra .EvalClaimMappings (ctx , claimsUnstructured )
930
+ evalResult , err := a .celMapper .Extra .EvalClaimMappings (ctx , claimsValue )
940
931
if err != nil {
941
932
return nil , err
942
933
}
@@ -1023,7 +1014,7 @@ func (c claims) unmarshalClaim(name string, v interface{}) error {
1023
1014
if ! ok {
1024
1015
return fmt .Errorf ("claim not present" )
1025
1016
}
1026
- return json .Unmarshal ([] byte ( val ) , v )
1017
+ return json .Unmarshal (val , v )
1027
1018
}
1028
1019
1029
1020
func (c claims ) hasClaim (name string ) bool {
@@ -1033,6 +1024,25 @@ func (c claims) hasClaim(name string) bool {
1033
1024
return true
1034
1025
}
1035
1026
1027
+ func newClaimsValue (c claims ) * lazy.MapValue {
1028
+ lazyMap := lazy .NewMapValue (types .NewObjectType ("kubernetes.claims" ))
1029
+ for name , msg := range c { // TODO add distributed claims support
1030
+ lazyMap .Append (name , func (_ * lazy.MapValue ) ref.Val {
1031
+ data , err := msg .MarshalJSON ()
1032
+ if err != nil {
1033
+ return types .WrapErr (err ) // impossible since RawMessage never errors
1034
+ }
1035
+
1036
+ var value any // TODO how do we do multiple levels of lazy decoding?
1037
+ if err := json .Unmarshal (data , & value ); err != nil {
1038
+ return types .NewErr ("claim %q failed to unmarshal: %w" , name , err )
1039
+ }
1040
+ return types .DefaultTypeAdapter .NativeToValue (value )
1041
+ })
1042
+ }
1043
+ return lazyMap
1044
+ }
1045
+
1036
1046
// convertCELValueToStringList converts the CEL value to a string list.
1037
1047
// The CEL value needs to be either a string or a list of strings.
1038
1048
// "", [] are treated as not being present and will return nil.
@@ -1113,50 +1123,17 @@ func checkValidationRulesEvaluation(results []authenticationcel.EvaluationResult
1113
1123
return nil
1114
1124
}
1115
1125
1116
- func convertObjectToUnstructured (obj interface {}) (* unstructured.Unstructured , error ) {
1117
- if obj == nil || reflect .ValueOf (obj ).IsNil () {
1118
- return & unstructured.Unstructured {Object : nil }, nil
1119
- }
1120
- ret , err := runtime .DefaultUnstructuredConverter .ToUnstructured (obj )
1121
- if err != nil {
1122
- return nil , err
1123
- }
1124
- return & unstructured.Unstructured {Object : ret }, nil
1125
- }
1126
-
1127
- func convertUserInfoToUnstructured (info user.Info ) (* unstructured.Unstructured , error ) {
1128
- userInfo := & authenticationv1.UserInfo {
1129
- Extra : make (map [string ]authenticationv1.ExtraValue ),
1130
- Groups : info .GetGroups (),
1131
- UID : info .GetUID (),
1132
- Username : info .GetName (),
1133
- }
1134
- // Convert the extra information in the user object
1135
- for key , val := range info .GetExtra () {
1136
- userInfo .Extra [key ] = authenticationv1 .ExtraValue (val )
1137
- }
1138
-
1139
- // Convert the user info to unstructured so that we can evaluate the CEL expressions
1140
- // against the user info. This is done once here so that we don't have to convert
1141
- // the user info to unstructured multiple times in the CEL mapper for each mapping.
1142
- userInfoUnstructured , err := convertObjectToUnstructured (userInfo )
1143
- if err != nil {
1144
- return nil , err
1145
- }
1146
-
1147
- // check if the user info contains the required fields. If not, set them to empty values.
1148
- // This is done because the CEL expressions expect these fields to be present.
1149
- if userInfoUnstructured .Object ["username" ] == nil {
1150
- userInfoUnstructured .Object ["username" ] = ""
1151
- }
1152
- if userInfoUnstructured .Object ["uid" ] == nil {
1153
- userInfoUnstructured .Object ["uid" ] = ""
1154
- }
1155
- if userInfoUnstructured .Object ["groups" ] == nil {
1156
- userInfoUnstructured .Object ["groups" ] = []string {}
1157
- }
1158
- if userInfoUnstructured .Object ["extra" ] == nil {
1159
- userInfoUnstructured .Object ["extra" ] = map [string ]authenticationv1.ExtraValue {}
1126
+ func newUserInfoValue (info user.Info ) * lazy.MapValue {
1127
+ lazyMap := lazy .NewMapValue (types .NewObjectType ("kubernetes.UserInfo" ))
1128
+ field := func (name string , get func () any ) {
1129
+ lazyMap .Append (name , func (_ * lazy.MapValue ) ref.Val {
1130
+ value := get ()
1131
+ return types .DefaultTypeAdapter .NativeToValue (value )
1132
+ })
1160
1133
}
1161
- return userInfoUnstructured , nil
1134
+ field ("username" , func () any { return info .GetName () })
1135
+ field ("uid" , func () any { return info .GetUID () })
1136
+ field ("groups" , func () any { return info .GetGroups () })
1137
+ field ("extra" , func () any { return info .GetExtra () })
1138
+ return lazyMap
1162
1139
}
0 commit comments