@@ -11,7 +11,6 @@ import (
1111 "fmt"
1212 "net/http"
1313 "net/http/httptest"
14- "strings"
1514 "testing"
1615 "time"
1716
@@ -863,7 +862,9 @@ func TestValidateGCPCredentialsParams(t *testing.T) {
863862func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials (t * testing.T ) {
864863 bspNamespace := "default"
865864 bspName := "test-gcp-policy"
866- tests := []struct {
865+
866+ // Test cases for validation errors.
867+ validationTests := []struct {
867868 name string
868869 bsp * aigv1a1.BackendSecurityPolicySpec
869870 expectedErrMsg string
@@ -884,11 +885,23 @@ func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials(t *test
884885 },
885886 expectedErrMsg : "invalid GCP credentials configuration: projectName cannot be empty" ,
886887 },
888+ {
889+ name : "neither oidc nor credentials file configured" ,
890+ bsp : & aigv1a1.BackendSecurityPolicySpec {
891+ Type : aigv1a1 .BackendSecurityPolicyTypeGCPCredentials ,
892+ GCPCredentials : & aigv1a1.BackendSecurityPolicyGCPCredentials {
893+ ProjectName : "test-project" ,
894+ Region : "us-central1" ,
895+ // Neither WorkloadIdentityFederationConfig nor CredentialsFile is set.
896+ },
897+ },
898+ expectedErrMsg : "one of service account key json file or oidc must be defined" ,
899+ },
887900 }
888901
889902 c := NewBackendSecurityPolicyController (fake .NewFakeClient (), fake2 .NewClientset (), ctrl .Log , nil )
890903
891- for _ , tt := range tests {
904+ for _ , tt := range validationTests {
892905 bsp := & aigv1a1.BackendSecurityPolicy {
893906 ObjectMeta : metav1.ObjectMeta {
894907 Name : bspName ,
@@ -897,7 +910,6 @@ func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials(t *test
897910 Spec : * tt .bsp ,
898911 }
899912 t .Run (tt .name , func (t * testing.T ) {
900- // Initial rotation should create a new secret.
901913 res , err := c .rotateCredential (context .Background (), bsp )
902914
903915 switch {
@@ -906,7 +918,7 @@ func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials(t *test
906918 case tt .expectedErrMsg == "" && err != nil :
907919 t .Errorf ("unexpected error: %v" , err )
908920 case tt .expectedErrMsg != "" && err != nil :
909- strings .Contains (err .Error (), tt .expectedErrMsg )
921+ require .Contains (t , err .Error (), tt .expectedErrMsg )
910922 default :
911923 require .NoError (t , err )
912924 require .NotZero (t , res .RequeueAfter )
@@ -915,6 +927,211 @@ func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials(t *test
915927 }
916928}
917929
930+ func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials_OIDC (t * testing.T ) {
931+ eventCh := internaltesting .NewControllerEventChan [* aigv1a1.AIServiceBackend ]()
932+ cl := fake .NewClientBuilder ().WithScheme (Scheme ).Build ()
933+ c := NewBackendSecurityPolicyController (cl , fake2 .NewClientset (), ctrl .Log , eventCh .Ch )
934+ bspName := "gcp-oidc-policy"
935+ bspNamespace := "default"
936+
937+ oidcSecretName := "gcp-oidc-secret" // #nosec G101
938+ oidcSecret := corev1.Secret {
939+ ObjectMeta : metav1.ObjectMeta {
940+ Name : oidcSecretName ,
941+ Namespace : bspNamespace ,
942+ },
943+ Data : map [string ][]byte {
944+ "client-secret" : []byte ("client-secret" ),
945+ },
946+ }
947+ require .NoError (t , cl .Create (t .Context (), & oidcSecret , & client.CreateOptions {}))
948+
949+ // Create backend security policy with GCP OIDC config.
950+ oidc := egv1a1.OIDC {
951+ Provider : egv1a1.OIDCProvider {
952+ Issuer : "https://fake-issuer.com" ,
953+ },
954+ ClientID : ptr .To ("some-client-id" ),
955+ ClientSecret : gwapiv1.SecretObjectReference {
956+ Name : gwapiv1 .ObjectName (oidcSecretName ),
957+ Namespace : (* gwapiv1 .Namespace )(& bspNamespace ),
958+ },
959+ }
960+ bsp := & aigv1a1.BackendSecurityPolicy {
961+ ObjectMeta : metav1.ObjectMeta {Name : bspName , Namespace : bspNamespace },
962+ Spec : aigv1a1.BackendSecurityPolicySpec {
963+ Type : aigv1a1 .BackendSecurityPolicyTypeGCPCredentials ,
964+ GCPCredentials : & aigv1a1.BackendSecurityPolicyGCPCredentials {
965+ ProjectName : "test-project" ,
966+ Region : "us-central1" ,
967+ WorkloadIdentityFederationConfig : & aigv1a1.GCPWorkloadIdentityFederationConfig {
968+ ProjectID : "test-project-id" ,
969+ WorkloadIdentityPoolName : "test-pool" ,
970+ WorkloadIdentityProviderName : "test-provider" ,
971+ OIDCExchangeToken : aigv1a1.GCPOIDCExchangeToken {
972+ BackendSecurityPolicyOIDC : aigv1a1.BackendSecurityPolicyOIDC {
973+ OIDC : oidc ,
974+ },
975+ },
976+ },
977+ },
978+ },
979+ }
980+ err := cl .Create (t .Context (), bsp )
981+ require .NoError (t , err )
982+
983+ // Test that the OIDC path validation passes and the method attempts to create OIDC provider
984+ // (which will fail due to network issues, but that confirms the OIDC path logic is working).
985+ res , err := c .rotateCredential (t .Context (), bsp )
986+
987+ // We expect an error due to network calls in the OIDC provider initialization.
988+ require .Error (t , err )
989+ require .Contains (t , err .Error (), "failed to initialize OIDC provider" )
990+ require .Equal (t , ctrl.Result {}, res )
991+ }
992+
993+ func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials_CredentialsFile (t * testing.T ) {
994+ eventCh := internaltesting .NewControllerEventChan [* aigv1a1.AIServiceBackend ]()
995+ cl := fake .NewClientBuilder ().WithScheme (Scheme ).Build ()
996+ c := NewBackendSecurityPolicyController (cl , fake2 .NewClientset (), ctrl .Log , eventCh .Ch )
997+ bspName := "gcp-sa-policy"
998+ bspNamespace := "default"
999+
1000+ // Create a secret containing an invalid service account JSON structure to ensure controlled test failure.
1001+ serviceAccountJSON := `{
1002+ "type": "service_account",
1003+ "project_id": "test-project",
1004+ "private_key_id": "key-id",
1005+ "private_key": "invalid-private-key-data",
1006+ "client_email": "[email protected] ", 1007+ "client_id": "123456789",
1008+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
1009+ "token_uri": "https://oauth2.googleapis.com/token"
1010+ }` // #nosec G101
1011+
1012+ serviceAccountSecretName := "gcp-sa-secret" // #nosec G101
1013+ serviceAccountSecret := corev1.Secret {
1014+ ObjectMeta : metav1.ObjectMeta {
1015+ Name : serviceAccountSecretName ,
1016+ Namespace : bspNamespace ,
1017+ },
1018+ Data : map [string ][]byte {
1019+ rotators .GCPServiceAccountJSON : []byte (serviceAccountJSON ),
1020+ },
1021+ }
1022+ require .NoError (t , cl .Create (t .Context (), & serviceAccountSecret , & client.CreateOptions {}))
1023+
1024+ // Create backend security policy with GCP service account credentials file config.
1025+ bsp := & aigv1a1.BackendSecurityPolicy {
1026+ ObjectMeta : metav1.ObjectMeta {Name : bspName , Namespace : bspNamespace },
1027+ Spec : aigv1a1.BackendSecurityPolicySpec {
1028+ Type : aigv1a1 .BackendSecurityPolicyTypeGCPCredentials ,
1029+ GCPCredentials : & aigv1a1.BackendSecurityPolicyGCPCredentials {
1030+ ProjectName : "test-project" ,
1031+ Region : "us-central1" ,
1032+ CredentialsFile : & aigv1a1.GCPCredentialsFile {
1033+ SecretRef : & gwapiv1.SecretObjectReference {
1034+ Name : gwapiv1 .ObjectName (serviceAccountSecretName ),
1035+ },
1036+ },
1037+ },
1038+ },
1039+ }
1040+ err := cl .Create (t .Context (), bsp )
1041+ require .NoError (t , err )
1042+
1043+ // Test that credentials file path is correctly selected and reaches token provider creation.
1044+ res , err := c .rotateCredential (t .Context (), bsp )
1045+
1046+ // The test behavior varies depending on environment mocking, but both outcomes are valid:
1047+ // 1. Error at token provider creation confirms the credentials file path worked
1048+ // 2. Success indicates test environment provides full mocking.
1049+ if err != nil {
1050+ require .Contains (t , err .Error (), "private key" )
1051+ } else {
1052+ require .NotZero (t , res .RequeueAfter )
1053+ }
1054+ }
1055+
1056+ func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials_MissingSecret (t * testing.T ) {
1057+ eventCh := internaltesting .NewControllerEventChan [* aigv1a1.AIServiceBackend ]()
1058+ cl := fake .NewClientBuilder ().WithScheme (Scheme ).Build ()
1059+ c := NewBackendSecurityPolicyController (cl , fake2 .NewClientset (), ctrl .Log , eventCh .Ch )
1060+ bspName := "gcp-missing-secret-policy"
1061+ bspNamespace := "default"
1062+
1063+ // Create backend security policy with GCP credentials file that doesn't exist.
1064+ bsp := & aigv1a1.BackendSecurityPolicy {
1065+ ObjectMeta : metav1.ObjectMeta {Name : bspName , Namespace : bspNamespace },
1066+ Spec : aigv1a1.BackendSecurityPolicySpec {
1067+ Type : aigv1a1 .BackendSecurityPolicyTypeGCPCredentials ,
1068+ GCPCredentials : & aigv1a1.BackendSecurityPolicyGCPCredentials {
1069+ ProjectName : "test-project" ,
1070+ Region : "us-central1" ,
1071+ CredentialsFile : & aigv1a1.GCPCredentialsFile {
1072+ SecretRef : & gwapiv1.SecretObjectReference {
1073+ Name : gwapiv1 .ObjectName ("non-existent-secret" ),
1074+ },
1075+ },
1076+ },
1077+ },
1078+ }
1079+ err := cl .Create (t .Context (), bsp )
1080+ require .NoError (t , err )
1081+
1082+ // Test that rotation fails when the referenced secret doesn't exist.
1083+ res , err := c .rotateCredential (t .Context (), bsp )
1084+ require .Error (t , err )
1085+ require .Contains (t , err .Error (), "secrets \" non-existent-secret\" not found" )
1086+ require .Equal (t , ctrl.Result {}, res )
1087+ }
1088+
1089+ func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials_MissingSecretKey (t * testing.T ) {
1090+ eventCh := internaltesting .NewControllerEventChan [* aigv1a1.AIServiceBackend ]()
1091+ cl := fake .NewClientBuilder ().WithScheme (Scheme ).Build ()
1092+ c := NewBackendSecurityPolicyController (cl , fake2 .NewClientset (), ctrl .Log , eventCh .Ch )
1093+ bspName := "gcp-missing-key-policy"
1094+ bspNamespace := "default"
1095+
1096+ // Create a secret without the required GCP service account JSON key.
1097+ serviceAccountSecretName := "gcp-incomplete-secret" // #nosec G101
1098+ serviceAccountSecret := corev1.Secret {
1099+ ObjectMeta : metav1.ObjectMeta {
1100+ Name : serviceAccountSecretName ,
1101+ Namespace : bspNamespace ,
1102+ },
1103+ Data : map [string ][]byte {
1104+ "wrong-key" : []byte ("some-data" ),
1105+ },
1106+ }
1107+ require .NoError (t , cl .Create (t .Context (), & serviceAccountSecret , & client.CreateOptions {}))
1108+
1109+ // Create backend security policy with GCP credentials file config pointing to incomplete secret.
1110+ bsp := & aigv1a1.BackendSecurityPolicy {
1111+ ObjectMeta : metav1.ObjectMeta {Name : bspName , Namespace : bspNamespace },
1112+ Spec : aigv1a1.BackendSecurityPolicySpec {
1113+ Type : aigv1a1 .BackendSecurityPolicyTypeGCPCredentials ,
1114+ GCPCredentials : & aigv1a1.BackendSecurityPolicyGCPCredentials {
1115+ ProjectName : "test-project" ,
1116+ Region : "us-central1" ,
1117+ CredentialsFile : & aigv1a1.GCPCredentialsFile {
1118+ SecretRef : & gwapiv1.SecretObjectReference {
1119+ Name : gwapiv1 .ObjectName (serviceAccountSecretName ),
1120+ },
1121+ },
1122+ },
1123+ },
1124+ }
1125+ err := cl .Create (t .Context (), bsp )
1126+ require .NoError (t , err )
1127+
1128+ // Test that rotation fails when the secret doesn't contain the required key.
1129+ res , err := c .rotateCredential (t .Context (), bsp )
1130+ require .Error (t , err )
1131+ require .Contains (t , err .Error (), "missing gcp service account key service_account.json" )
1132+ require .Equal (t , ctrl.Result {}, res )
1133+ }
1134+
9181135func TestGetBSPGeneratedSecretName (t * testing.T ) {
9191136 tests := []struct {
9201137 name string
0 commit comments