1 package encryption 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net/http" 9 "net/url" 10 "slices" 11 "testing" 12 "time" 13 14 apiclient "edge-infra.dev/pkg/edge/api/client" 15 "edge-infra.dev/pkg/edge/api/graph/model" 16 "edge-infra.dev/pkg/edge/constants" 17 "edge-infra.dev/pkg/edge/edgeencrypt" 18 "edge-infra.dev/pkg/edge/info" 19 "edge-infra.dev/pkg/lib/gcp/secretmanager" 20 "edge-infra.dev/test/f2" 21 "edge-infra.dev/test/f2/x/bslauth" 22 "edge-infra.dev/test/f2/x/ktest" 23 24 "github.com/shurcooL/graphql" 25 "gotest.tools/v3/assert" 26 "gotest.tools/v3/poll" 27 28 corev1 "k8s.io/api/core/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 ) 31 32 func TestEncryption(t *testing.T) { 33 var k *ktest.K8s 34 var edgeUser bslauth.User 35 var encryptedOutput string 36 var edgeInfo *info.EdgeInfo 37 portForward := ktest.PortForward{Namespace: edgeencrypt.EncryptionName} 38 feature := f2.NewFeature("Encryption E2E Test"). 39 Setup("Initial setup", func(ctx f2.Context, t *testing.T) f2.Context { 40 k = ktest.FromContextT(ctx, t) 41 auth, err := bslauth.FromContext(ctx) 42 assert.NilError(t, err) 43 44 edgeUser = auth.User(bslauth.BannerAdmin) 45 if !valid(&edgeUser) { 46 t.Fatalf("Invalid edge user: %v", edgeUser) 47 return ctx 48 } 49 50 edgeInfo, err = info.FromClient(ctx, k.Client) 51 assert.NilError(t, err) 52 return ctx 53 }). 54 Test("Workload App", func(ctx f2.Context, t *testing.T) f2.Context { 55 creds := apiclient.WithCredentials(edgeUser.Username, edgeUser.Password, edgeUser.Org) 56 client, err := apiclient.New(creds, apiclient.WithBaseURL(edgeInfo.EdgeAPIEndpoint)) 57 assert.NilError(t, err) 58 59 type helmWorkloads struct { 60 Name string `graphql:"name"` 61 Namespace string `graphql:"namespace"` 62 } 63 var q struct { 64 HelmWorkloads []helmWorkloads `graphql:"helmWorkloads(clusterEdgeId: $clusterEdgeId)"` 65 } 66 err = client.Query(ctx, &q, map[string]any{ 67 "clusterEdgeId": graphql.String(edgeInfo.ClusterEdgeID), 68 }) 69 assert.NilError(t, err) 70 71 workloadFound := slices.IndexFunc(q.HelmWorkloads, func(w helmWorkloads) bool { 72 return w.Name == DefaultWorkload && w.Namespace == DefaultWorkload 73 }) 74 75 if workloadFound <= 0 { 76 createHelmRelease(ctx, t, edgeInfo, client) 77 } 78 79 return ctx 80 }). 81 Setup("Datasync Sparrow PortForwarding", portForward.Forward("sparrow", 8080)). 82 Test("Health Check", func(ctx f2.Context, t *testing.T) f2.Context { 83 addr := portForward.Retrieve(t) 84 response, err := http.Get(fmt.Sprintf("http://%s/health", addr)) 85 assert.NilError(t, err) 86 body, err := io.ReadAll(response.Body) 87 defer response.Body.Close() 88 assert.NilError(t, err) 89 assert.Equal(t, string(body), "ok") 90 return ctx 91 }). 92 Test("Encrypt Data", func(ctx f2.Context, t *testing.T) f2.Context { 93 addr := portForward.Retrieve(t) 94 95 s := &corev1.Secret{ 96 TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Secret"}, 97 ObjectMeta: metav1.ObjectMeta{ 98 Name: fmt.Sprintf(edgeencrypt.EncryptionBearerToken, DefaultChannelName), 99 Namespace: DefaultWorkload, 100 }, 101 } 102 103 // poll for secret 104 k.WaitOn(t, k.ObjExists(s), poll.WithDelay(2*time.Second), poll.WithTimeout(3*time.Minute)) 105 106 body, err := json.Marshal(tlog) 107 assert.NilError(t, err) 108 109 req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%s/v1/encrypt", addr), bytes.NewReader(body)) 110 assert.NilError(t, err) 111 112 token := string(s.Data["token"]) 113 req.Header.Set("Authorization", "Bearer "+token) 114 req.Header.Set("Content-Type", "application/json") 115 116 resp, err := http.DefaultClient.Do(req) 117 assert.NilError(t, err) 118 defer resp.Body.Close() 119 120 encryptedData, err := io.ReadAll(resp.Body) 121 assert.NilError(t, err) 122 assert.Equal(t, http.StatusOK, resp.StatusCode) 123 encryptedOutput = string(encryptedData) 124 return ctx 125 }). 126 Test("Decrypt Data", func(ctx f2.Context, t *testing.T) f2.Context { 127 uri, err := url.Parse(edgeInfo.EdgeAPIEndpoint) 128 assert.NilError(t, err) 129 130 addr := fmt.Sprintf("%s://%s", uri.Scheme, uri.Host) 131 132 encryptedData := encryptedOutput 133 assert.Assert(t, encryptedData != "", "Encrypted data is missing") 134 135 decryptURL := fmt.Sprintf("%s/crypto/v1/decrypt/%s", addr, DefaultChannelName) 136 137 req, err := http.NewRequest(http.MethodPost, decryptURL, bytes.NewReader([]byte(encryptedData))) 138 assert.NilError(t, err) 139 140 sm, err := secretmanager.NewWithOptions(ctx, edgeInfo.ForemanProjectID) 141 assert.NilError(t, err) 142 143 secret, err := sm.GetLatestSecretValue(ctx, edgeencrypt.DecryptionTokenSecretManager) 144 assert.NilError(t, err) 145 146 var s edgeencrypt.Token 147 148 err = json.Unmarshal(secret, &s) 149 assert.NilError(t, err) 150 151 req.Header.Set("Authorization", "Bearer "+s.BearerToken) 152 req.Header.Set("Content-Type", "text/plain") 153 154 resp, err := http.DefaultClient.Do(req) 155 assert.NilError(t, err) 156 defer resp.Body.Close() 157 158 decryptedData, err := io.ReadAll(resp.Body) 159 assert.NilError(t, err) 160 assert.Equal(t, http.StatusOK, resp.StatusCode) 161 162 t.Logf("Decrypted Data: %s", string(decryptedData)) 163 164 return ctx 165 }). 166 Feature() 167 f.Test(t, feature) 168 } 169 170 func createHelmRelease(ctx f2.Context, t *testing.T, edgeInfo *info.EdgeInfo, client *apiclient.EdgeClient) { 171 cv := string(configValues) 172 installationType := model.WorkloadInstallationTypeServer 173 type helmReleasePayload struct { 174 model.HelmReleasePayload 175 } 176 hrp := helmReleasePayload{ 177 HelmReleasePayload: model.HelmReleasePayload{ 178 Name: DefaultWorkload, 179 Secret: constants.HelmRead, 180 HelmRepository: constants.NCRCharts, 181 HelmChart: DefaultWorkload, 182 Version: "6.2.2", 183 Namespace: DefaultWorkload, 184 ClusterEdgeID: &edgeInfo.ClusterEdgeID, 185 ConfigValues: &cv, 186 InstallationType: &installationType, 187 }, 188 } 189 190 var mutation struct { 191 CreateHelmRelease bool `graphql:"createHelmRelease(payload: $helmReleasePayload)"` 192 } 193 variables := map[string]interface{}{"helmReleasePayload": hrp} 194 195 err := client.Mutate(ctx, &mutation, variables) 196 assert.NilError(t, err) 197 } 198 199 func valid(user *bslauth.User) bool { 200 return user.Username != "" && user.Password != "" && user.Org != "" 201 } 202