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  })