github.com/redhat-appstudio/e2e-tests@v0.0.0-20240520140907-9709f6f59323/tests/spi/access-control.go (about)

     1  package spi
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/redhat-appstudio/e2e-tests/pkg/constants"
    11  	"github.com/redhat-appstudio/e2e-tests/pkg/utils"
    12  	"github.com/redhat-appstudio/service-provider-integration-operator/api/v1beta1"
    13  
    14  	"github.com/avast/retry-go/v4"
    15  	. "github.com/onsi/ginkgo/v2"
    16  	. "github.com/onsi/gomega"
    17  	. "github.com/onsi/gomega/gstruct"
    18  	client "github.com/redhat-appstudio/e2e-tests/pkg/clients/kubernetes"
    19  	"github.com/redhat-appstudio/e2e-tests/pkg/framework"
    20  	corev1 "k8s.io/api/core/v1"
    21  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    22  	"k8s.io/apimachinery/pkg/types"
    23  	"k8s.io/client-go/tools/clientcmd"
    24  	"k8s.io/client-go/tools/remotecommand"
    25  	"k8s.io/kubectl/pkg/scheme"
    26  	crclient "sigs.k8s.io/controller-runtime/pkg/client"
    27  )
    28  
    29  type User struct {
    30  	Framework                    *framework.Framework
    31  	LinkedAccessTokenName        string
    32  	SPIGithubWorkSpaceSecretName string
    33  	WorkspaceURL                 string
    34  	APIProxyClientA              *crclient.Client
    35  	APIProxyClientB              *crclient.Client
    36  	APIProxyClientC              *crclient.Client
    37  }
    38  
    39  /*
    40   * Component: spi
    41   * Description: SVPI-495 - Test automation to ensure that a user can't access and use secrets from another workspace
    42  
    43   * User A is the owner of workspace A and has access to workspace C as the maintainer
    44   * User B is the owner of workspace B
    45   * User C is the owner of workspace C
    46  
    47   * Test cases:
    48  	* check if user can access the SPIAccessToken from another workspace
    49  	* check if user can read the GitHub repo from another workspace
    50  	* check if user can read the secret from another workspace
    51  	* check if user's pod deployed in the user's workspace can read the GitHub repo from another workspace
    52  */
    53  
    54  var _ = framework.SPISuiteDescribe(Label("spi-suite", "access-control"), func() {
    55  
    56  	defer GinkgoRecover()
    57  
    58  	var userA, userB, userC User
    59  
    60  	Describe("SVPI-495 - Test automation to ensure that a user can't access and use secrets from another workspace", Ordered, func() {
    61  
    62  		delete := func(user User) {
    63  			Expect(user.Framework.AsKubeAdmin.SPIController.DeleteAllBindingTokensInASpecificNamespace(user.Framework.UserNamespace)).To(Succeed())
    64  			Expect(user.Framework.AsKubeAdmin.SPIController.DeleteAllAccessTokensInASpecificNamespace(user.Framework.UserNamespace)).To(Succeed())
    65  			Expect(user.Framework.AsKubeAdmin.SPIController.DeleteAllAccessTokenDataInASpecificNamespace(user.Framework.UserNamespace)).To(Succeed())
    66  			Expect(user.Framework.SandboxController.DeleteUserSignup(user.Framework.UserName)).To(BeTrue())
    67  		}
    68  
    69  		createAPIProxyClient := func(userToken, proxyURL string) *crclient.Client {
    70  			APIProxyClient, err := client.CreateAPIProxyClient(userToken, proxyURL)
    71  			Expect(err).NotTo(HaveOccurred())
    72  			client := APIProxyClient.KubeRest()
    73  			return &client
    74  		}
    75  
    76  		createSPITokenBinding := func(user User) {
    77  			namespace := user.Framework.UserNamespace
    78  			secretName := user.SPIGithubWorkSpaceSecretName
    79  
    80  			// creates SPITokenBinding
    81  			SPITokenBinding, err := user.Framework.AsKubeDeveloper.SPIController.CreateSPIAccessTokenBinding(SPITokenBindingName, namespace, GithubPrivateRepoURL, secretName, "kubernetes.io/basic-auth")
    82  			Expect(err).NotTo(HaveOccurred())
    83  
    84  			// start of upload token
    85  			// SPITokenBinding to be in AwaitingTokenData phase
    86  			// wait SPITokenBinding to be in AwaitingTokenData phase before trying to upload a token
    87  			Eventually(func() v1beta1.SPIAccessTokenBindingPhase {
    88  				SPITokenBinding, err = user.Framework.AsKubeDeveloper.SPIController.GetSPIAccessTokenBinding(SPITokenBinding.Name, namespace)
    89  				Expect(err).NotTo(HaveOccurred())
    90  
    91  				return SPITokenBinding.Status.Phase
    92  			}, 1*time.Minute, 5*time.Second).Should(Equal(v1beta1.SPIAccessTokenBindingPhaseAwaitingTokenData), fmt.Sprintf("SPIAccessTokenBinding %s/%s is not in %s phase", SPITokenBinding.GetNamespace(), SPITokenBinding.GetName(), v1beta1.SPIAccessTokenBindingPhaseAwaitingTokenData))
    93  
    94  			// uploads username and token using rest endpoint
    95  			// the UploadUrl in SPITokenBinding should be available before uploading the token
    96  			Eventually(func() string {
    97  				SPITokenBinding, err = user.Framework.AsKubeDeveloper.SPIController.GetSPIAccessTokenBinding(SPITokenBinding.Name, namespace)
    98  				Expect(err).NotTo(HaveOccurred())
    99  
   100  				return SPITokenBinding.Status.UploadUrl
   101  			}, 1*time.Minute, 10*time.Second).ShouldNot(BeEmpty(), fmt.Sprintf(".Status.UploadUrl for SPIAccessTokenBinding %s/%s is not set", SPITokenBinding.GetNamespace(), SPITokenBinding.GetName()))
   102  			Expect(err).NotTo(HaveOccurred())
   103  
   104  			// LinkedAccessToken should exist
   105  			linkedAccessTokenName := SPITokenBinding.Status.LinkedAccessTokenName
   106  			Expect(linkedAccessTokenName).NotTo(BeEmpty())
   107  
   108  			// keep LinkedAccessToken name
   109  			username := user.Framework.UserName
   110  			if strings.HasPrefix(username, "spi-user-a") {
   111  				userA.LinkedAccessTokenName = linkedAccessTokenName
   112  			} else if strings.HasPrefix(username, "spi-user-b") {
   113  				userB.LinkedAccessTokenName = linkedAccessTokenName
   114  			} else {
   115  				userC.LinkedAccessTokenName = linkedAccessTokenName
   116  			}
   117  
   118  			// get the url to manually upload the token
   119  			uploadURL := SPITokenBinding.Status.UploadUrl
   120  
   121  			// Get the token for the current openshift user
   122  			bearerToken, err := utils.GetOpenshiftToken()
   123  			Expect(err).NotTo(HaveOccurred())
   124  
   125  			// build and upload the payload using the uploadURL. it should return 204
   126  			oauthCredentials := `{"access_token":"` + utils.GetEnv(constants.GITHUB_TOKEN_ENV, "") + `"}`
   127  			statusCode, err := user.Framework.AsKubeDeveloper.SPIController.UploadWithRestEndpoint(uploadURL, oauthCredentials, bearerToken)
   128  			Expect(err).NotTo(HaveOccurred())
   129  			Expect(statusCode).Should(Equal(204))
   130  
   131  			// SPITokenBinding to be in Injected phase
   132  			Eventually(func() v1beta1.SPIAccessTokenBindingPhase {
   133  				SPITokenBinding, err = user.Framework.AsKubeDeveloper.SPIController.GetSPIAccessTokenBinding(SPITokenBinding.Name, namespace)
   134  				Expect(err).NotTo(HaveOccurred())
   135  				return SPITokenBinding.Status.Phase
   136  			}, 5*time.Minute, 5*time.Second).Should(Equal(v1beta1.SPIAccessTokenBindingPhaseInjected), fmt.Sprintf("SPIAccessTokenBinding %s/%s is not in %s phase", SPITokenBinding.GetNamespace(), SPITokenBinding.GetName(), v1beta1.SPIAccessTokenBindingPhaseInjected))
   137  
   138  			// SPIAccessToken exists and is in Read phase
   139  			Eventually(func() (v1beta1.SPIAccessTokenPhase, error) {
   140  				SPIAccessToken, err := user.Framework.AsKubeDeveloper.SPIController.GetSPIAccessToken(SPITokenBinding.Status.LinkedAccessTokenName, namespace)
   141  				if err != nil {
   142  					return "", fmt.Errorf("can't get SPI access token %s/%s: %+v", namespace, SPITokenBinding.Status.LinkedAccessTokenName, err)
   143  				}
   144  
   145  				return SPIAccessToken.Status.Phase, nil
   146  			}, 1*time.Minute, 5*time.Second).Should(Equal(v1beta1.SPIAccessTokenPhaseReady), fmt.Sprintf("SPIAccessToken %s/%s should be in ready phase", namespace, SPITokenBinding.Status.LinkedAccessTokenName))
   147  			// end of upload token
   148  		}
   149  
   150  		// check if guestUser can access a primary's secret in the primary's workspace
   151  		checkSecretAccess := func(client crclient.Client, primaryUser User, shouldAccess bool) {
   152  			// checks that guest user can access a primary's secret in primary's workspace
   153  			spiAccessToken := &v1beta1.SPIAccessToken{}
   154  			err := client.Get(context.Background(), types.NamespacedName{Name: primaryUser.LinkedAccessTokenName, Namespace: primaryUser.Framework.UserNamespace}, spiAccessToken)
   155  
   156  			if shouldAccess {
   157  				Expect(err).NotTo(HaveOccurred())
   158  			} else {
   159  				Expect(err).To(HaveOccurred())
   160  			}
   161  		}
   162  
   163  		// check if guest user can read a primary's GitHub repo in the primary's workspace
   164  		checkRepositoryReading := func(client crclient.Client, primaryUser User, shouldRead bool) {
   165  			// create SPIFileContentRequest in primary's workspace URL
   166  			spiFcr := v1beta1.SPIFileContentRequest{
   167  				ObjectMeta: metav1.ObjectMeta{
   168  					GenerateName: "spi-file-content-request-",
   169  					Namespace:    primaryUser.Framework.UserNamespace,
   170  				},
   171  				Spec: v1beta1.SPIFileContentRequestSpec{RepoUrl: GithubPrivateRepoURL, FilePath: GithubPrivateRepoFilePath},
   172  			}
   173  			err := client.Create(context.Background(), &spiFcr)
   174  
   175  			if shouldRead {
   176  				Expect(err).NotTo(HaveOccurred())
   177  
   178  				// check that guest user can read a primary's GitHub repo in the primary's workspace
   179  				Eventually(func() v1beta1.SPIFileContentRequestStatus {
   180  					err = client.Get(context.Background(), types.NamespacedName{Name: spiFcr.Name, Namespace: spiFcr.Namespace}, &spiFcr)
   181  					Expect(err).NotTo(HaveOccurred())
   182  
   183  					return spiFcr.Status
   184  				}, 2*time.Minute, 10*time.Second).Should(MatchFields(IgnoreExtras, Fields{
   185  					"Content": Not(BeEmpty()),
   186  					"Phase":   Equal(v1beta1.SPIFileContentRequestPhaseDelivered),
   187  				}), fmt.Sprintf("content not provided by SPIFileContentRequest %s/%s", primaryUser.Framework.UserNamespace, spiFcr.Name))
   188  			} else {
   189  				Expect(err).To(HaveOccurred())
   190  			}
   191  		}
   192  
   193  		// check if guest user can read a primary's secret in the primary's workspace
   194  		checkSecretReading := func(client crclient.Client, primaryUser User, shouldRead bool) {
   195  			resultSecret := &corev1.Secret{}
   196  			err := client.Get(context.Background(), types.NamespacedName{Name: primaryUser.SPIGithubWorkSpaceSecretName, Namespace: primaryUser.Framework.UserNamespace}, resultSecret)
   197  
   198  			if shouldRead {
   199  				Expect(err).ToNot(HaveOccurred())
   200  				token := resultSecret.Data["password"]
   201  				Expect(token).ToNot(BeEmpty())
   202  			} else {
   203  				Expect(err).To(HaveOccurred())
   204  			}
   205  		}
   206  
   207  		// check that guest user can make a request to create a SPIFileContentRequest in the primary's workspace
   208  		makeGhReadRequestFromPod := func(guestUser, primaryUser User, podName, podNamespace, spiFcrName string, shouldAccess bool) {
   209  			namespace := primaryUser.Framework.UserNamespace
   210  			spiFcrData := fmt.Sprintf(`---
   211  apiVersion: appstudio.redhat.com/v1beta1
   212  kind: SPIFileContentRequest
   213  metadata:
   214    name: %s
   215    namespace: %s
   216  spec:
   217    filePath: README.md
   218    repoUrl: %s
   219  `, spiFcrName, namespace, GithubPrivateRepoURL)
   220  			readRequest := fmt.Sprintf(
   221  				"curl '%s' \\"+
   222  					"-k \\"+
   223  					"-H 'Authorization: Bearer %s' \\"+
   224  					"-X POST \\"+
   225  					"-H 'Content-Type: application/yaml' \\"+
   226  					"--connect-timeout 30 \\"+
   227  					"--max-time 300 \\"+
   228  					"-d '%s'",
   229  				fmt.Sprintf("%s/apis/appstudio.redhat.com/v1beta1/namespaces/%s/spifilecontentrequests", primaryUser.WorkspaceURL, namespace),
   230  				guestUser.Framework.UserToken,
   231  				spiFcrData,
   232  			)
   233  			request := guestUser.Framework.AsKubeAdmin.CommonController.KubeInterface().CoreV1().RESTClient().
   234  				Post().Namespace(podNamespace).
   235  				Resource("pods").
   236  				Name(podName).
   237  				SubResource("exec").
   238  				VersionedParams(&corev1.PodExecOptions{
   239  					Command: []string{
   240  						"/bin/sh",
   241  						"-c",
   242  						readRequest,
   243  					},
   244  					Stdin:  false,
   245  					Stdout: true,
   246  					Stderr: true,
   247  					TTY:    true,
   248  				}, scheme.ParameterCodec)
   249  
   250  			config := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
   251  				clientcmd.NewDefaultClientConfigLoadingRules(),
   252  				&clientcmd.ConfigOverrides{},
   253  			)
   254  			restConfig, err := config.ClientConfig()
   255  			Expect(err).NotTo(HaveOccurred())
   256  
   257  			Eventually(func() bool {
   258  				stop := false
   259  				exec, err := remotecommand.NewSPDYExecutor(restConfig, "POST", request.URL())
   260  				Expect(err).NotTo(HaveOccurred())
   261  
   262  				buffer := &bytes.Buffer{}
   263  				errBuffer := &bytes.Buffer{}
   264  				err = exec.StreamWithContext(context.TODO(), remotecommand.StreamOptions{
   265  					Stdout: buffer,
   266  					Stderr: errBuffer,
   267  				})
   268  				Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("error: %v", err))
   269  				Expect(errBuffer.String()).To(BeEmpty(), fmt.Sprintf("stderr: %v", errBuffer.String()))
   270  
   271  				// default of attempts: 10
   272  				err = retry.Do(
   273  					func() error {
   274  						_, err = primaryUser.Framework.AsKubeDeveloper.SPIController.GetSPIFileContentRequest(spiFcrName, namespace)
   275  						if err != nil && shouldAccess {
   276  							GinkgoWriter.Printf("buffer info: %s\n", buffer.String())
   277  							return err
   278  						}
   279  						stop = true
   280  						return nil
   281  					},
   282  				)
   283  				return stop
   284  			}, 5*time.Minute, 5*time.Second).Should(BeTrue(), fmt.Sprintf("SPIFileContentRequest '%s' in namespace '%s' was not created", spiFcrName, namespace))
   285  		}
   286  
   287  		// check if guest user's pod deployed in guest user's workspace should be able to construct an API request that reads code in the Github repo for primary's user workspace
   288  		checkRepoReadingFromPod := func(client crclient.Client, guestUser, primaryUser User, shouldRead bool) {
   289  			namespace := guestUser.Framework.UserNamespace
   290  			p := &corev1.Pod{
   291  				ObjectMeta: metav1.ObjectMeta{
   292  					Namespace:    namespace,
   293  					GenerateName: "pod-",
   294  				},
   295  				Spec: corev1.PodSpec{
   296  					RestartPolicy: corev1.RestartPolicyNever,
   297  					Containers: []corev1.Container{
   298  						{
   299  							Name:  "read",
   300  							Image: "quay.io/redhat-appstudio/buildah:v1.28",
   301  							// workaround to force the pod to be running
   302  							Command: []string{
   303  								"sleep",
   304  								"600",
   305  							},
   306  						},
   307  					},
   308  				},
   309  			}
   310  
   311  			// create pod
   312  			pod, err := guestUser.Framework.AsKubeAdmin.CommonController.KubeInterface().CoreV1().Pods(namespace).Create(context.Background(), p, metav1.CreateOptions{})
   313  			Expect(err).NotTo(HaveOccurred())
   314  
   315  			// check that pod started
   316  			Eventually(func() corev1.PodPhase {
   317  				pod, err := guestUser.Framework.AsKubeAdmin.CommonController.GetPod(namespace, pod.Name)
   318  				Expect(err).NotTo(HaveOccurred())
   319  
   320  				return pod.Status.Phase
   321  			}, 8*time.Minute, 5*time.Second).Should(Equal(corev1.PodRunning), fmt.Sprintf("Pod %s/%s not created successfully", namespace, pod.Name))
   322  			Expect(err).NotTo(HaveOccurred())
   323  
   324  			// make read request
   325  			spiFcrName := utils.GetGeneratedNamespace("pod-spi-file-content-request")
   326  			makeGhReadRequestFromPod(guestUser, primaryUser, pod.Name, pod.Namespace, spiFcrName, shouldRead)
   327  
   328  			spiFcr := v1beta1.SPIFileContentRequest{}
   329  			if shouldRead {
   330  				// check that guest user's pod can read a primary's GitHub repo in the primary's workspace
   331  				Eventually(func() v1beta1.SPIFileContentRequestStatus {
   332  					err = client.Get(context.Background(), types.NamespacedName{Name: spiFcrName, Namespace: primaryUser.Framework.UserNamespace}, &spiFcr)
   333  					Expect(err).NotTo(HaveOccurred())
   334  
   335  					return spiFcr.Status
   336  				}, 2*time.Minute, 10*time.Second).Should(MatchFields(IgnoreExtras, Fields{
   337  					"Content": Not(BeEmpty()),
   338  					"Phase":   Equal(v1beta1.SPIFileContentRequestPhaseDelivered),
   339  				}), fmt.Sprintf("content not provided by SPIFileContentRequest %s/%s", primaryUser.Framework.UserNamespace, spiFcrName))
   340  			} else {
   341  				// check that guest user's pod can not read a primary's GitHub repo in the primary's workspace
   342  				err = client.Get(context.Background(), types.NamespacedName{Name: spiFcrName, Namespace: primaryUser.Framework.UserNamespace}, &spiFcr)
   343  				Expect(err).To(HaveOccurred())
   344  			}
   345  		}
   346  
   347  		BeforeAll(func() {
   348  			// Initialize the tests controllers for user A
   349  			// test user A is the owner and have access to the workspace A
   350  			fwUserA, err := framework.NewFramework(utils.GetGeneratedNamespace("spi-user-a"))
   351  			Expect(err).NotTo(HaveOccurred())
   352  			namespaceUserA := fwUserA.UserNamespace
   353  			Expect(namespaceUserA).NotTo(BeEmpty())
   354  			userA = User{
   355  				Framework:                    fwUserA,
   356  				SPIGithubWorkSpaceSecretName: "e2e-github-secret-workspace-a",
   357  				WorkspaceURL:                 fmt.Sprintf("%s/workspaces/%s", fwUserA.ProxyUrl, fwUserA.UserName),
   358  			}
   359  
   360  			// Initialize the tests controllers for user B
   361  			// test user B is the owner and have access to the workspace B
   362  			fwUserB, err := framework.NewFramework(utils.GetGeneratedNamespace("spi-user-b"))
   363  			Expect(err).NotTo(HaveOccurred())
   364  			namespaceUserB := fwUserB.UserNamespace
   365  			Expect(namespaceUserB).NotTo(BeEmpty())
   366  			userB = User{
   367  				Framework:                    fwUserB,
   368  				SPIGithubWorkSpaceSecretName: "e2e-github-secret-workspace-b",
   369  				WorkspaceURL:                 fmt.Sprintf("%s/workspaces/%s", fwUserB.ProxyUrl, fwUserB.UserName),
   370  			}
   371  
   372  			// Initialize the tests controllers for user C
   373  			// test user C is the owner and have access to the workspace C
   374  			fwUserC, err := framework.NewFramework(utils.GetGeneratedNamespace("spi-user-c"))
   375  			Expect(err).NotTo(HaveOccurred())
   376  			namespaceUserC := fwUserC.UserNamespace
   377  			Expect(namespaceUserC).NotTo(BeEmpty())
   378  			userC = User{
   379  				Framework:                    fwUserC,
   380  				SPIGithubWorkSpaceSecretName: "e2e-github-secret-workspace-c",
   381  				WorkspaceURL:                 fmt.Sprintf("%s/workspaces/%s", fwUserC.ProxyUrl, fwUserC.UserName),
   382  			}
   383  
   384  			// create api proxy client with user A token and user's A workspace URL
   385  			userA.APIProxyClientA = createAPIProxyClient(userA.Framework.UserToken, userA.WorkspaceURL)
   386  			// create api proxy client with user A token and user's B workspace URL
   387  			userA.APIProxyClientB = createAPIProxyClient(userA.Framework.UserToken, userB.WorkspaceURL)
   388  			// create api proxy client with user A token and user's C workspace URL
   389  			userA.APIProxyClientC = createAPIProxyClient(userA.Framework.UserToken, userC.WorkspaceURL)
   390  
   391  			// create api proxy client with user B token and user's A workspace URL
   392  			userB.APIProxyClientA = createAPIProxyClient(userB.Framework.UserToken, userA.WorkspaceURL)
   393  			// create api proxy client with user B token and user's B workspace URL
   394  			userB.APIProxyClientB = createAPIProxyClient(userB.Framework.UserToken, userB.WorkspaceURL)
   395  			// create api proxy client with user B token and user's C workspace URL
   396  			userB.APIProxyClientC = createAPIProxyClient(userB.Framework.UserToken, userC.WorkspaceURL)
   397  
   398  			// create api proxy client with user C token and user's A workspace URL
   399  			userC.APIProxyClientA = createAPIProxyClient(userC.Framework.UserToken, userA.WorkspaceURL)
   400  			// create api proxy client with user C token and user's B workspace URL
   401  			userC.APIProxyClientB = createAPIProxyClient(userC.Framework.UserToken, userB.WorkspaceURL)
   402  			// create api proxy client with user C token and user's C workspace URL
   403  			userC.APIProxyClientC = createAPIProxyClient(userC.Framework.UserToken, userC.WorkspaceURL)
   404  
   405  			// share workspace C with user A with maintainer roles
   406  			_, err = fwUserC.AsKubeAdmin.CommonController.CreateSpaceBinding(fwUserA.UserName, fwUserC.UserName, "maintainer")
   407  			Expect(err).NotTo(HaveOccurred())
   408  
   409  			// check if workspace C was shared with user A
   410  			Eventually(func() error {
   411  				err = fwUserC.AsKubeAdmin.CommonController.CheckWorkspaceShare(fwUserA.UserName, fwUserC.UserNamespace)
   412  				return err
   413  			}, 1*time.Minute, 5*time.Second).Should(BeNil(), fmt.Sprintf("error checking if the workspace C (%s) was shared with user A (%s)", fwUserC.UserNamespace, fwUserA.UserName))
   414  
   415  			// check if user C is provisioned after workspace share
   416  			Eventually(func() error {
   417  				_, err := fwUserC.SandboxController.GetUserProvisionedNamespace(fwUserC.UserName)
   418  				return err
   419  			}, 1*time.Minute, 5*time.Second).Should(BeNil(), fmt.Sprintf("error getting provisioned usernamespace for user %s", fwUserC.UserName))
   420  
   421  			// create SPITokenBinding for user A
   422  			createSPITokenBinding(userA)
   423  
   424  			// create SPITokenBinding for user B
   425  			createSPITokenBinding(userB)
   426  
   427  			// create SPITokenBinding for user C
   428  			createSPITokenBinding(userC)
   429  		})
   430  
   431  		AfterAll(func() {
   432  			if !CurrentSpecReport().Failed() {
   433  				delete(userA)
   434  				delete(userB)
   435  				delete(userC)
   436  			}
   437  		})
   438  
   439  		It("checks that user A can access the SPIAccessToken A in workspace A", func() {
   440  			checkSecretAccess(*userA.APIProxyClientA, userA, true)
   441  		})
   442  
   443  		It("checks that user A can not access the SPIAccessToken B in workspace B", func() {
   444  			checkSecretAccess(*userA.APIProxyClientB, userB, false)
   445  		})
   446  
   447  		It("checks that user A can access the SPIAccessToken C in workspace C", func() {
   448  			// workspace C was shared with user A
   449  			checkSecretAccess(*userA.APIProxyClientC, userC, true)
   450  		})
   451  
   452  		It("checks that user B can not access the SPIAccessToken A in workspace A", func() {
   453  			checkSecretAccess(*userB.APIProxyClientA, userA, false)
   454  		})
   455  
   456  		It("checks that user B can access the SPIAccessToken B in workspace B", func() {
   457  			checkSecretAccess(*userB.APIProxyClientB, userB, true)
   458  		})
   459  
   460  		It("checks that user B can not access the SPIAccessToken C in workspace C", func() {
   461  			checkSecretAccess(*userB.APIProxyClientC, userC, false)
   462  		})
   463  
   464  		It("checks that user C can not access the SPIAccessToken A in workspace A", func() {
   465  			checkSecretAccess(*userC.APIProxyClientA, userA, false)
   466  		})
   467  
   468  		It("checks that user C can not access the SPIAccessToken B in workspace B", func() {
   469  			checkSecretAccess(*userC.APIProxyClientB, userB, false)
   470  		})
   471  
   472  		It("checks that user C can access the SPIAccessToken C in workspace C", func() {
   473  			checkSecretAccess(*userC.APIProxyClientC, userC, true)
   474  		})
   475  
   476  		It("checks that user A can read the GitHub repo in workspace A", func() {
   477  			checkRepositoryReading(*userA.APIProxyClientA, userA, true)
   478  		})
   479  
   480  		It("checks that user A can not read the GitHub repo in workspace B", func() {
   481  			checkRepositoryReading(*userA.APIProxyClientB, userB, false)
   482  		})
   483  
   484  		It("checks that user A can read the GitHub repo in workspace C", func() {
   485  			// workspace C was shared with user A
   486  			checkRepositoryReading(*userA.APIProxyClientC, userC, true)
   487  		})
   488  
   489  		It("checks that user B can not read the GitHub repo in workspace A", func() {
   490  			checkRepositoryReading(*userB.APIProxyClientA, userA, false)
   491  		})
   492  
   493  		It("checks that user B can read the GitHub repo in workspace B", func() {
   494  			checkRepositoryReading(*userB.APIProxyClientB, userB, true)
   495  		})
   496  
   497  		It("checks that user B can not read the GitHub repo in workspace C", func() {
   498  			checkRepositoryReading(*userB.APIProxyClientC, userC, false)
   499  		})
   500  
   501  		It("checks that user C can not read the GitHub repo in workspace A", func() {
   502  			checkRepositoryReading(*userC.APIProxyClientA, userA, false)
   503  		})
   504  
   505  		It("checks that user C can not read the GitHub repo in workspace B", func() {
   506  			checkRepositoryReading(*userC.APIProxyClientB, userB, false)
   507  		})
   508  
   509  		It("checks that user C can read the GitHub repo in workspace C", func() {
   510  			checkRepositoryReading(*userC.APIProxyClientC, userC, true)
   511  		})
   512  
   513  		It("checks that user A can read the secret in workspace A", func() {
   514  			checkSecretReading(*userA.APIProxyClientA, userA, true)
   515  		})
   516  
   517  		It("checks that user A can not read the secret in workspace B", func() {
   518  			checkSecretReading(*userA.APIProxyClientB, userB, false)
   519  		})
   520  
   521  		It("checks that user A can not read the secret in workspace C", func() {
   522  			// although workspace C is shared with user A, the role given is maintainer,
   523  			// which does not have any permissions for secrets object
   524  			checkSecretReading(*userA.APIProxyClientC, userC, false)
   525  		})
   526  
   527  		It("checks that user B can not read the secret in workspace A", func() {
   528  			checkSecretReading(*userB.APIProxyClientA, userA, false)
   529  		})
   530  
   531  		It("checks that user B can read the secret in workspace B", func() {
   532  			checkSecretReading(*userB.APIProxyClientB, userB, true)
   533  		})
   534  
   535  		It("checks that user B can not read the secret in workspace C", func() {
   536  			checkSecretReading(*userB.APIProxyClientC, userC, false)
   537  		})
   538  
   539  		It("checks that user C can not read the secret in workspace A", func() {
   540  			checkSecretReading(*userC.APIProxyClientA, userA, false)
   541  		})
   542  
   543  		It("checks that user C can not read the secret in workspace B", func() {
   544  			checkSecretReading(*userC.APIProxyClientB, userB, false)
   545  		})
   546  
   547  		It("checks that user C can read the secret in workspace C", func() {
   548  			checkSecretReading(*userC.APIProxyClientC, userC, true)
   549  		})
   550  
   551  		It("checks that a user's A pod deployed in workspace A should be able to construct an API request that reads code in the Github repo for workspace A", func() {
   552  			checkRepoReadingFromPod(*userA.APIProxyClientA, userA, userA, true)
   553  		})
   554  
   555  		It("checks that a user's A pod deployed in workspace A should not be able to construct an API request that reads code in the Github repo for workspace B", func() {
   556  			checkRepoReadingFromPod(*userA.APIProxyClientB, userA, userB, false)
   557  		})
   558  
   559  		It("checks that a user's A pod deployed in workspace A should be able to construct an API request that reads code in the Github repo for workspace C", func() {
   560  			// workspace C was shared with user A
   561  			checkRepoReadingFromPod(*userA.APIProxyClientC, userA, userC, true)
   562  		})
   563  
   564  		It("checks that a user's B pod deployed in workspace B should not be able to construct an API request that reads code in the Github repo for workspace A", func() {
   565  			checkRepoReadingFromPod(*userB.APIProxyClientA, userB, userA, false)
   566  		})
   567  
   568  		It("checks that a user's B pod deployed in workspace B should be able to construct an API request that reads code in the Github repo for workspace B", func() {
   569  			checkRepoReadingFromPod(*userB.APIProxyClientB, userB, userB, true)
   570  		})
   571  
   572  		It("checks that a user's B pod deployed in workspace B should not be able to construct an API request that reads code in the Github repo for workspace C", func() {
   573  			checkRepoReadingFromPod(*userB.APIProxyClientC, userB, userC, false)
   574  		})
   575  
   576  		It("checks that a user's C pod deployed in workspace C should not be able to construct an API request that reads code in the Github repo for workspace A", func() {
   577  			checkRepoReadingFromPod(*userC.APIProxyClientA, userC, userA, false)
   578  		})
   579  
   580  		It("checks that a user's C pod deployed in workspace C should not be able to construct an API request that reads code in the Github repo for workspace B", func() {
   581  			checkRepoReadingFromPod(*userC.APIProxyClientB, userC, userB, false)
   582  		})
   583  
   584  		It("checks that a user's C pod deployed in workspace C should be able to construct an API request that reads code in the Github repo for workspace C", func() {
   585  			checkRepoReadingFromPod(*userC.APIProxyClientC, userC, userC, true)
   586  		})
   587  	})
   588  })