github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/verify-install/web/web_test.go (about) 1 // Copyright (c) 2020, 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package web_test 5 6 import ( 7 "context" 8 "crypto/x509" 9 "fmt" 10 "net/http" 11 "time" 12 13 "github.com/hashicorp/go-retryablehttp" 14 . "github.com/onsi/ginkgo/v2" 15 . "github.com/onsi/gomega" 16 "github.com/verrazzano/verrazzano/pkg/k8sutil" 17 "github.com/verrazzano/verrazzano/tests/e2e/pkg" 18 "github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework" 19 networkingv1 "k8s.io/api/networking/v1" 20 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 "k8s.io/client-go/kubernetes" 22 ) 23 24 const ( 25 waitTimeout = 15 * time.Minute 26 pollingInterval = 5 * time.Second 27 ) 28 29 var t = framework.NewTestFramework("web") 30 31 var serverURL string 32 var isManagedClusterProfile bool 33 var isTestSupported bool 34 35 var beforeSuite = t.BeforeSuiteFunc(func() { 36 var ingress *networkingv1.Ingress 37 var clientset *kubernetes.Clientset 38 isManagedClusterProfile = pkg.IsManagedClusterProfile() 39 if isManagedClusterProfile { 40 return 41 } 42 43 Eventually(func() (*kubernetes.Clientset, error) { 44 var err error 45 clientset, err = k8sutil.GetKubernetesClientset() 46 return clientset, err 47 }, waitTimeout, pollingInterval).ShouldNot(BeNil()) 48 Eventually(func() (*networkingv1.Ingress, error) { 49 var err error 50 ingress, err = clientset.NetworkingV1().Ingresses("verrazzano-system").Get(context.TODO(), "verrazzano-ingress", v1.GetOptions{}) 51 return ingress, err 52 }, waitTimeout, pollingInterval).ShouldNot(BeNil()) 53 54 Expect(len(ingress.Spec.Rules)).To(Equal(1)) 55 ingressRules := ingress.Spec.Rules 56 serverURL = fmt.Sprintf("https://%s/", ingressRules[0].Host) 57 var err error 58 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 59 if err != nil { 60 Fail(fmt.Sprintf("Failed to get default kubeconfig path: %s", err.Error())) 61 } 62 isTestSupported, err = pkg.IsVerrazzanoMinVersion("1.1.0", kubeconfigPath) 63 if err != nil { 64 Fail(err.Error()) 65 } 66 }) 67 68 var _ = BeforeSuite(beforeSuite) 69 70 var afterSuite = t.AfterSuiteFunc(func() { 71 t.Logs.Debug("executing after suite") 72 }) 73 74 var _ = AfterSuite(afterSuite) 75 76 var _ = t.AfterEach(func() {}) 77 78 var _ = t.Describe("Verrazzano Web UI,", Label("f:platform-lcm.install", 79 "f:ui.api"), func() { 80 t.When("when configured,", func() { 81 t.It("can be accessed", func() { 82 if !isManagedClusterProfile { 83 Eventually(func() (*pkg.HTTPResponse, error) { 84 return pkg.GetWebPage(serverURL, "") 85 }, waitTimeout, pollingInterval).Should(And(pkg.HasStatus(http.StatusOK), pkg.BodyNotEmpty())) 86 } 87 }) 88 89 t.It("has the correct SSL certificate", func() { 90 if !isManagedClusterProfile { 91 var certs []*x509.Certificate 92 Eventually(func() ([]*x509.Certificate, error) { 93 var err error 94 certs, err = pkg.GetCertificates(serverURL) 95 return certs, err 96 }, waitTimeout, pollingInterval).ShouldNot(BeNil()) 97 98 // There will normally be several certs, but we only need to check the 99 // first one -- might want to refactor the checks out into a pkg.IsCertValid() 100 // function so we can use it from other test suites too?? 101 t.Logs.Debug("Issuer Common Name: " + certs[0].Issuer.CommonName) 102 t.Logs.Debug("Subject Common Name: " + certs[0].Subject.CommonName) 103 t.Logs.Debug("Not Before: " + certs[0].NotBefore.String()) 104 t.Logs.Debug("Not After: " + certs[0].NotAfter.String()) 105 Expect(time.Now().After(certs[0].NotBefore)).To(BeTrue()) 106 Expect(time.Now().Before(certs[0].NotAfter)).To(BeTrue()) 107 } 108 }) 109 110 t.It("should return no Server header", func() { 111 if !isManagedClusterProfile { 112 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 113 Expect(err).ShouldNot(HaveOccurred()) 114 httpClient, err := pkg.GetVerrazzanoHTTPClient(kubeconfigPath) 115 Expect(err).ShouldNot(HaveOccurred()) 116 req, err := retryablehttp.NewRequest("GET", serverURL, nil) 117 Expect(err).ShouldNot(HaveOccurred()) 118 // There should be no server header found and no errors should occur during the request 119 Eventually(func() error { 120 return pkg.CheckStatusAndResponseHeaderAbsent(httpClient, req, "server", 0) 121 }, waitTimeout, pollingInterval).Should(BeNil()) 122 } 123 }) 124 125 t.It("should not return CORS Access-Control-Allow-Origin header when no Origin header is provided", func() { 126 if !isManagedClusterProfile { 127 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 128 Expect(err).ShouldNot(HaveOccurred()) 129 httpClient, err := pkg.GetVerrazzanoHTTPClient(kubeconfigPath) 130 Expect(err).ShouldNot(HaveOccurred()) 131 req, err := retryablehttp.NewRequest("GET", serverURL, nil) 132 Expect(err).ShouldNot(HaveOccurred()) 133 // HTTP Access-Control-Allow-Origin header should never be returned. 134 Eventually(func() error { 135 return pkg.CheckStatusAndResponseHeaderAbsent( 136 httpClient, req, "access-control-allow-origin", 0) 137 }, waitTimeout, pollingInterval).Should(BeNil()) 138 } 139 }) 140 141 t.It("should not return CORS Access-Control-Allow-Origin header when Origin: * is provided", func() { 142 if !isManagedClusterProfile { 143 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 144 Expect(err).ShouldNot(HaveOccurred()) 145 httpClient, err := pkg.GetVerrazzanoHTTPClient(kubeconfigPath) 146 Expect(err).ShouldNot(HaveOccurred()) 147 req, err := retryablehttp.NewRequest("GET", serverURL, nil) 148 req.Header.Add("Origin", "*") 149 Expect(err).ShouldNot(HaveOccurred()) 150 Eventually(func() error { 151 return pkg.CheckStatusAndResponseHeaderAbsent( 152 httpClient, req, "access-control-allow-origin", 0) 153 }, waitTimeout, pollingInterval).Should(BeNil()) 154 } 155 }) 156 157 t.It("should not return CORS Access-Control-Allow-Origin header when Origin: null is provided", func() { 158 if !isManagedClusterProfile { 159 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 160 Expect(err).ShouldNot(HaveOccurred()) 161 httpClient, err := pkg.GetVerrazzanoHTTPClient(kubeconfigPath) 162 Expect(err).ShouldNot(HaveOccurred()) 163 req, err := retryablehttp.NewRequest("GET", serverURL, nil) 164 req.Header.Add("Origin", "null") 165 Expect(err).ShouldNot(HaveOccurred()) 166 Eventually(func() error { 167 return pkg.CheckStatusAndResponseHeaderAbsent( 168 httpClient, req, "access-control-allow-origin", 0) 169 }, waitTimeout, pollingInterval).Should(BeNil()) 170 } 171 }) 172 173 t.It("can be logged out", func() { 174 if !isManagedClusterProfile && isTestSupported { 175 Eventually(func() (*pkg.HTTPResponse, error) { 176 return pkg.GetWebPage(fmt.Sprintf("%s%s", serverURL, "_logout"), "") 177 }, waitTimeout, pollingInterval).Should(And(pkg.HasStatus(http.StatusOK))) 178 } 179 }) 180 181 t.It("should not allow malformed requests", func() { 182 if !isManagedClusterProfile && isTestSupported { 183 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 184 Expect(err).ShouldNot(HaveOccurred()) 185 httpClient, err := pkg.GetVerrazzanoHTTPClient(kubeconfigPath) 186 Expect(err).ShouldNot(HaveOccurred()) 187 body := []byte(` 188 0 189 POST /mal formed ZZZZ/9.7 190 Q: W`) 191 req, err := retryablehttp.NewRequest("POST", serverURL, body) 192 Expect(err).ShouldNot(HaveOccurred()) 193 req.Header.Add("Content-Length", "36") 194 req.Header.Add("Transfer-Encoding", "chunked") 195 Eventually(func() error { 196 return pkg.CheckStatusAndResponseHeaderAbsent(httpClient, req, "", 400) 197 }, waitTimeout, pollingInterval).Should(BeNil()) 198 } 199 }) 200 201 t.It("should not allow state changing requests without valid origin header", func() { 202 if !isManagedClusterProfile && isTestSupported { 203 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 204 Expect(err).ShouldNot(HaveOccurred()) 205 httpClient, err := pkg.GetVerrazzanoHTTPClient(kubeconfigPath) 206 Expect(err).ShouldNot(HaveOccurred()) 207 req, err := retryablehttp.NewRequest("POST", serverURL, nil) 208 Expect(err).ShouldNot(HaveOccurred()) 209 req.Header.Add("Origin", "https://invalid-origin") 210 Eventually(func() error { 211 return pkg.CheckStatusAndResponseHeaderAbsent(httpClient, req, "", 403) 212 }, waitTimeout, pollingInterval).Should(BeNil()) 213 } 214 }) 215 216 t.It("should allow non state changing requests without valid origin header but not populate Access-Control-Allow-Origin header", func() { 217 if !isManagedClusterProfile && isTestSupported { 218 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 219 Expect(err).ShouldNot(HaveOccurred()) 220 httpClient, err := pkg.GetVerrazzanoHTTPClient(kubeconfigPath) 221 Expect(err).ShouldNot(HaveOccurred()) 222 req, err := retryablehttp.NewRequest("GET", serverURL, nil) 223 Expect(err).ShouldNot(HaveOccurred()) 224 req.Header.Add("Origin", "https://invalid-origin") 225 Eventually(func() error { 226 return pkg.CheckStatusAndResponseHeaderAbsent(httpClient, req, "access-control-allow-origin", 200) 227 }, waitTimeout, pollingInterval).Should(BeNil()) 228 } 229 }) 230 }) 231 })