@@ -30,6 +30,7 @@ import (
3030
3131 "github.com/golang-jwt/jwt/v5"
3232 jumpstarterdevv1alpha1 "github.com/jumpstarter-dev/jumpstarter/controller/api/v1alpha1"
33+ "github.com/jumpstarter-dev/jumpstarter/controller/internal/config"
3334 jlog "github.com/jumpstarter-dev/jumpstarter/controller/internal/log"
3435 pb "github.com/jumpstarter-dev/jumpstarter/controller/internal/protocol/jumpstarter/v1"
3536 "google.golang.org/grpc"
@@ -39,11 +40,13 @@ import (
3940 "google.golang.org/grpc/status"
4041 "k8s.io/apimachinery/pkg/api/meta"
4142 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
43+ k8sruntime "k8s.io/apimachinery/pkg/runtime"
4244 "k8s.io/apiserver/pkg/authentication/authenticator"
4345 "k8s.io/apiserver/pkg/authentication/user"
4446 "k8s.io/apiserver/pkg/authorization/authorizer"
4547 logf "sigs.k8s.io/controller-runtime/pkg/log"
4648 ctrlzap "sigs.k8s.io/controller-runtime/pkg/log/zap"
49+ "sigs.k8s.io/controller-runtime/pkg/client/fake"
4750)
4851
4952const testRouterToken = "tok"
@@ -2061,6 +2064,57 @@ func (noopAuthorizer) Authorize(_ context.Context, _ authorizer.Attributes) (aut
20612064 return authorizer .DecisionNoOpinion , "" , nil
20622065}
20632066
2067+ // passingAuthenticator always authenticates successfully with a fixed user name.
2068+ type passingAuthenticator struct { userName string }
2069+
2070+ func (p * passingAuthenticator ) AuthenticateContext (_ context.Context ) (* authenticator.Response , bool , error ) {
2071+ return & authenticator.Response {User : & user.DefaultInfo {Name : p .userName }}, true , nil
2072+ }
2073+
2074+ // exporterAttributesGetter returns attributes that identify a fixed Exporter object.
2075+ type exporterAttributesGetter struct { namespace , name string }
2076+
2077+ func (e * exporterAttributesGetter ) ContextAttributes (_ context.Context , u user.Info ) (authorizer.Attributes , error ) {
2078+ return authorizer.AttributesRecord {
2079+ User : u ,
2080+ Namespace : e .namespace ,
2081+ Resource : "Exporter" ,
2082+ Name : e .name ,
2083+ }, nil
2084+ }
2085+
2086+ // passingAuthorizer always allows.
2087+ type passingAuthorizer struct {}
2088+
2089+ func (passingAuthorizer ) Authorize (_ context.Context , _ authorizer.Attributes ) (authorizer.Decision , string , error ) {
2090+ return authorizer .DecisionAllow , "" , nil
2091+ }
2092+
2093+ // authSuccessServiceCtx builds a ControllerService whose authentication always
2094+ // succeeds. A pre-populated Exporter object is stored in the fake client so
2095+ // that VerifyExporterObjectToken can fetch it.
2096+ func authSuccessServiceCtx (t * testing.T , cfg * config.Telemetry ) (* ControllerService , context.Context ) {
2097+ t .Helper ()
2098+
2099+ scheme := k8sruntime .NewScheme ()
2100+ if err := jumpstarterdevv1alpha1 .AddToScheme (scheme ); err != nil {
2101+ t .Fatalf ("failed to add scheme: %v" , err )
2102+ }
2103+ exporter := & jumpstarterdevv1alpha1.Exporter {
2104+ ObjectMeta : metav1.ObjectMeta {Name : "test-exporter" , Namespace : "default" },
2105+ }
2106+ fakeClient := fake .NewClientBuilder ().WithScheme (scheme ).WithObjects (exporter ).Build ()
2107+
2108+ svc := & ControllerService {
2109+ Client : fakeClient ,
2110+ Authn : & passingAuthenticator {userName : "test-user" },
2111+ Authz : passingAuthorizer {},
2112+ Attr : & exporterAttributesGetter {namespace : "default" , name : "test-exporter" },
2113+ TelemetryConfig : cfg ,
2114+ }
2115+ return svc , context .Background ()
2116+ }
2117+
20642118// authFailureServiceCtx builds a ControllerService whose authentication always
20652119// fails, plus a context carrying a peer address, a captured logger, and the
20662120// jlog.LogContext enrichment applied by the gRPC interceptors in production.
0 commit comments