@@ -19,11 +19,11 @@ import (
1919	"testing" 
2020	"time" 
2121
22+ 	"github.com/containers/kubernetes-mcp-server/internal/test" 
2223	"github.com/coreos/go-oidc/v3/oidc" 
2324	"github.com/coreos/go-oidc/v3/oidc/oidctest" 
2425	"golang.org/x/sync/errgroup" 
2526	"k8s.io/client-go/tools/clientcmd" 
26- 	"k8s.io/client-go/tools/clientcmd/api" 
2727	"k8s.io/klog/v2" 
2828	"k8s.io/klog/v2/textlogger" 
2929
@@ -33,6 +33,7 @@ import (
3333
3434type  httpContext  struct  {
3535	klogState        klog.State 
36+ 	mockServer       * test.MockServer 
3637	LogBuffer        bytes.Buffer 
3738	HttpAddress      string              // HTTP server address 
3839	timeoutCancel    context.CancelFunc  // Release resources if test completes before the timeout 
@@ -42,21 +43,31 @@ type httpContext struct {
4243	OidcProvider     * oidc.Provider 
4344}
4445
46+ const  tokenReviewSuccessful  =  ` 
47+ 	{ 
48+ 		"kind": "TokenReview", 
49+ 		"apiVersion": "authentication.k8s.io/v1", 
50+ 		"spec": {"token": "valid-token"}, 
51+ 		"status": { 
52+ 			"authenticated": true, 
53+ 			"user": { 
54+ 				"username": "test-user", 
55+ 				"groups": ["system:authenticated"] 
56+ 			} 
57+ 		} 
58+ 	}` 
59+ 
4560func  (c  * httpContext ) beforeEach (t  * testing.T ) {
4661	t .Helper ()
4762	http .DefaultClient .Timeout  =  10  *  time .Second 
4863	if  c .StaticConfig  ==  nil  {
4964		c .StaticConfig  =  & config.StaticConfig {}
5065	}
66+ 	c .mockServer  =  test .NewMockServer ()
5167	// Fake Kubernetes configuration 
52- 	fakeConfig  :=  api .NewConfig ()
53- 	fakeConfig .Clusters ["fake" ] =  api .NewCluster ()
54- 	fakeConfig .Clusters ["fake" ].Server  =  "https://example.com" 
55- 	fakeConfig .Contexts ["fake-context" ] =  api .NewContext ()
56- 	fakeConfig .Contexts ["fake-context" ].Cluster  =  "fake" 
57- 	fakeConfig .CurrentContext  =  "fake-context" 
68+ 	mockKubeConfig  :=  c .mockServer .KubeConfig ()
5869	kubeConfig  :=  filepath .Join (t .TempDir (), "config" )
59- 	_  =  clientcmd .WriteToFile (* fakeConfig , kubeConfig )
70+ 	_  =  clientcmd .WriteToFile (* mockKubeConfig , kubeConfig )
6071	_  =  os .Setenv ("KUBECONFIG" , kubeConfig )
6172	// Capture logging 
6273	c .klogState  =  klog .CaptureState ()
@@ -100,6 +111,7 @@ func (c *httpContext) beforeEach(t *testing.T) {
100111
101112func  (c  * httpContext ) afterEach (t  * testing.T ) {
102113	t .Helper ()
114+ 	c .mockServer .Close ()
103115	c .StopServer ()
104116	err  :=  c .WaitForShutdown ()
105117	if  err  !=  nil  {
@@ -546,3 +558,81 @@ func TestAuthorizationUnauthorized(t *testing.T) {
546558		})
547559	})
548560}
561+ 
562+ // TestAuthorizationRequireOAuthFalse tests the scenario where OAuth is not required. 
563+ func  TestAuthorizationRequireOAuthFalse (t  * testing.T ) {
564+ 	testCaseWithContext (t , & httpContext {StaticConfig : & config.StaticConfig {RequireOAuth : false }}, func (ctx  * httpContext ) {
565+ 		resp , err  :=  http .Get (fmt .Sprintf ("http://%s/mcp" , ctx .HttpAddress ))
566+ 		if  err  !=  nil  {
567+ 			t .Fatalf ("Failed to get protected endpoint: %v" , err )
568+ 		}
569+ 		t .Cleanup (func () { _  =  resp .Body .Close () })
570+ 		t .Run ("Protected resource with MISSING Authorization header returns 200 - OK)" , func (t  * testing.T ) {
571+ 			if  resp .StatusCode  !=  http .StatusOK  {
572+ 				t .Errorf ("Expected HTTP 200 OK, got %d" , resp .StatusCode )
573+ 			}
574+ 		})
575+ 	})
576+ }
577+ 
578+ func  TestAuthorizationRawToken (t  * testing.T ) {
579+ 	testCaseWithContext (t , & httpContext {StaticConfig : & config.StaticConfig {RequireOAuth : true }}, func (ctx  * httpContext ) {
580+ 		ctx .mockServer .Handle (http .HandlerFunc (func (w  http.ResponseWriter , req  * http.Request ) {
581+ 			if  req .URL .EscapedPath () ==  "/apis/authentication.k8s.io/v1/tokenreviews"  {
582+ 				w .Header ().Set ("Content-Type" , "application/json" )
583+ 				_ , _  =  w .Write ([]byte (tokenReviewSuccessful ))
584+ 				return 
585+ 			}
586+ 		}))
587+ 		req , err  :=  http .NewRequest ("GET" , fmt .Sprintf ("http://%s/mcp" , ctx .HttpAddress ), nil )
588+ 		if  err  !=  nil  {
589+ 			t .Fatalf ("Failed to create request: %v" , err )
590+ 		}
591+ 		req .Header .Set ("Authorization" , "Bearer " + tokenBasicNotExpired )
592+ 		resp , err  :=  http .DefaultClient .Do (req )
593+ 		if  err  !=  nil  {
594+ 			t .Fatalf ("Failed to get protected endpoint: %v" , err )
595+ 		}
596+ 		t .Cleanup (func () { _  =  resp .Body .Close () })
597+ 		t .Run ("Protected resource with VALID Authorization header returns 200 - OK" , func (t  * testing.T ) {
598+ 			if  resp .StatusCode  !=  http .StatusOK  {
599+ 				t .Errorf ("Expected HTTP 200 OK, got %d" , resp .StatusCode )
600+ 			}
601+ 		})
602+ 	})
603+ }
604+ 
605+ func  TestAuthorizationOidcToken (t  * testing.T ) {
606+ 	key , oidcProvider , httpServer  :=  NewOidcTestServer (t )
607+ 	t .Cleanup (httpServer .Close )
608+ 	rawClaims  :=  `{ 
609+ 		"iss": "`  +  httpServer .URL  +  `", 
610+ 		"exp": `  +  strconv .FormatInt (time .Now ().Add (time .Hour ).Unix (), 10 ) +  `, 
611+ 		"aud": "mcp-server" 
612+ 	}` 
613+ 	validOidcToken  :=  oidctest .SignIDToken (key , "test-oidc-key-id" , oidc .RS256 , rawClaims )
614+ 	testCaseWithContext (t , & httpContext {StaticConfig : & config.StaticConfig {RequireOAuth : true }, OidcProvider : oidcProvider }, func (ctx  * httpContext ) {
615+ 		ctx .mockServer .Handle (http .HandlerFunc (func (w  http.ResponseWriter , req  * http.Request ) {
616+ 			if  req .URL .EscapedPath () ==  "/apis/authentication.k8s.io/v1/tokenreviews"  {
617+ 				w .Header ().Set ("Content-Type" , "application/json" )
618+ 				_ , _  =  w .Write ([]byte (tokenReviewSuccessful ))
619+ 				return 
620+ 			}
621+ 		}))
622+ 		req , err  :=  http .NewRequest ("GET" , fmt .Sprintf ("http://%s/mcp" , ctx .HttpAddress ), nil )
623+ 		if  err  !=  nil  {
624+ 			t .Fatalf ("Failed to create request: %v" , err )
625+ 		}
626+ 		req .Header .Set ("Authorization" , "Bearer " + validOidcToken )
627+ 		resp , err  :=  http .DefaultClient .Do (req )
628+ 		if  err  !=  nil  {
629+ 			t .Fatalf ("Failed to get protected endpoint: %v" , err )
630+ 		}
631+ 		t .Cleanup (func () { _  =  resp .Body .Close () })
632+ 		t .Run ("Protected resource with VALID OIDC Authorization header returns 200 - OK" , func (t  * testing.T ) {
633+ 			if  resp .StatusCode  !=  http .StatusOK  {
634+ 				t .Errorf ("Expected HTTP 200 OK, got %d" , resp .StatusCode )
635+ 			}
636+ 		})
637+ 	})
638+ }
0 commit comments