diff --git a/config/ui-extensions/kustomization.yaml b/config/ui-extensions/kustomization.yaml index 3ecd2fef7f..55d297b664 100644 --- a/config/ui-extensions/kustomization.yaml +++ b/config/ui-extensions/kustomization.yaml @@ -7,6 +7,7 @@ resources: - authorizationpolicies - sidecars - telemetries +- requestauthentications labels: - includeSelectors: true diff --git a/config/ui-extensions/requestauthentications/dataSources b/config/ui-extensions/requestauthentications/dataSources new file mode 100644 index 0000000000..6fd8ac330c --- /dev/null +++ b/config/ui-extensions/requestauthentications/dataSources @@ -0,0 +1,5 @@ +podSelector: + resource: + kind: Pod + version: v1 + filter: $matchByLabelSelector($item, $root.spec.selector.matchLabels) \ No newline at end of file diff --git a/config/ui-extensions/requestauthentications/details b/config/ui-extensions/requestauthentications/details new file mode 100644 index 0000000000..53ccdfafbc --- /dev/null +++ b/config/ui-extensions/requestauthentications/details @@ -0,0 +1,51 @@ +body: + - source: spec.jwtRules + name: spec.jwtRules + showHeader: false + widget: Table + collapsibleTitle: '"Issuer " & $item.issuer' + collapsible: + - source: issuer + name: spec.jwtRules.issuer + widget: Text + - source: jwksUri + name: spec.jwtRules.jwksUri + widget: Text + - source: audiences + name: spec.jwtRules.audiences + widget: JoinedArray + - source: fromParams + name: spec.jwtRules.fromParams + widget: JoinedArray + - source: fromCookies + name: spec.jwtRules.fromCookies + widget: JoinedArray + - source: fromHeaders + name: spec.jwtRules.fromHeaders + widget: Table + showHeader: false + collapsibleTitle: '"Header " & $item.name' + collapsible: + - source: name + name: spec.jwtRules.fromHeaders.name + widget: Text + - source: prefix + name: spec.jwtRules.fromHeaders.prefix + widget: Text + - widget: Panel + name: spec.selector.matchLabels + disablePadding: true + children: + - source: $podSelector() + widget: ResourceList + disableCreate: true + visibility: $exists($root.spec.selector.matchLabels) and $boolean($root.spec.selector.matchLabels) + - source: spec.selector + widget: Panel + name: selector.matchesAllPods + visibility: $not($exists($value)) or $not($boolean($value)) + header: + - source: spec.selector.matchLabels + widget: Labels + name: spec.selector.matchLabels + visibility: $exists($value) and $boolean($value) diff --git a/config/ui-extensions/requestauthentications/form b/config/ui-extensions/requestauthentications/form new file mode 100644 index 0000000000..bdf739fcff --- /dev/null +++ b/config/ui-extensions/requestauthentications/form @@ -0,0 +1,29 @@ +- path: spec.selector.matchLabels + widget: KeyValuePair + defaultExpanded: true +- path: spec.jwtRules + name: spec.jwtRules + widget: GenericList + simple: true + children: + - path: '[].issuer' + - path: '[].jwksUri' + name: spec.jwtRules.jwksUri + - path: '[].audiences' + widget: SimpleList + children: + - path: '[]' + - path: '[].fromParams' + widget: SimpleList + children: + - path: '[]' + - path: '[].fromCookies' + widget: SimpleList + children: + - path: '[]' + - path: '[].fromHeaders' + simple: true + widget: GenericList + children: + - path: '[].name' + - path: '[].prefix' \ No newline at end of file diff --git a/config/ui-extensions/requestauthentications/general b/config/ui-extensions/requestauthentications/general new file mode 100644 index 0000000000..529440f82f --- /dev/null +++ b/config/ui-extensions/requestauthentications/general @@ -0,0 +1,12 @@ +resource: + kind: RequestAuthentication + group: security.istio.io + version: v1beta1 +urlPath: requestauthentications +category: Istio +name: Request Authentications +scope: namespace +description: >- + {{[Istio Request Authentication](https://istio.io/latest/docs/reference/config/security/request_authentication)}} + defines what request authentication methods are supported by a workload. + diff --git a/config/ui-extensions/requestauthentications/kustomization.yaml b/config/ui-extensions/requestauthentications/kustomization.yaml new file mode 100644 index 0000000000..48c8cacf92 --- /dev/null +++ b/config/ui-extensions/requestauthentications/kustomization.yaml @@ -0,0 +1,14 @@ +configMapGenerator: +- name: requestauthentications-ui.operator.kyma-project.io + namespace: kyma-system + files: + - general + - dataSources + - details + - form + - translations + options: + disableNameSuffixHash: true + labels: + busola.io/extension: resource + busola.io/extension-version: "0.5" \ No newline at end of file diff --git a/config/ui-extensions/requestauthentications/translations b/config/ui-extensions/requestauthentications/translations new file mode 100644 index 0000000000..ecd8e8d283 --- /dev/null +++ b/config/ui-extensions/requestauthentications/translations @@ -0,0 +1,13 @@ +en: + spec.jwtRules: JWT Rules + spec.jwtRules.issuer: Issuer + spec.jwtRules.jwksUri: JWKS URI + spec.jwtRules.jwks: JWKS + spec.jwtRules.audiences: Audiences + spec.jwtRules.fromParams: From Params + spec.jwtRules.fromCookies: From Cookies + spec.jwtRules.fromHeaders: From Headers + spec.jwtRules.fromHeaders.name: Name + spec.jwtRules.fromHeaders.prefix: Prefix + spec.selector.matchLabels: Selector + selector.matchesAllPods: Matches all Pods in the Namespace \ No newline at end of file diff --git a/docs/release-notes/1.7.0.md b/docs/release-notes/1.7.0.md index 6200a620c6..ab8cbfee05 100644 --- a/docs/release-notes/1.7.0.md +++ b/docs/release-notes/1.7.0.md @@ -2,4 +2,5 @@ - Allow for opting out of the **ENABLE_EXTERNAL_NAME_ALIAS** Istio pilot environment variable in the Istio custom resource. This allows for retaining behavior that was present in Istio prior to version 1.21. See issue [#787](https://github.com/kyma-project/istio/issues/787 ). - Update the Istio version to 1.21.2 [#802](https://github.com/kyma-project/istio/pull/802). Read [Istio 1.21.2 Release Announcement](https://istio.io/latest/news/releases/1.21.x/announcing-1.21.2/) and [Change Notes](https://istio.io/latest/news/releases/1.21.x/announcing-1.21/change-notes/) for more details. +- Add Request Authentication UI for Kyma dashboard [#816](https://github.com/kyma-project/istio/pull/816) - Now Pods with Istio Sidecar, which contain custom image annotations, are not restarted by the Istio Operator. See issue [#698](https://github.com/kyma-project/istio/issues/698) and the [Istio documentation](https://istio.io/latest/docs/reference/config/annotations/#SidecarProxyImage) for more details. \ No newline at end of file diff --git a/docs/user/troubleshooting/03-70-cannot-connect-to-hana-db.md b/docs/user/troubleshooting/03-70-cannot-connect-to-hana-db.md new file mode 100644 index 0000000000..607432c7a6 --- /dev/null +++ b/docs/user/troubleshooting/03-70-cannot-connect-to-hana-db.md @@ -0,0 +1,60 @@ +# Issues with Connection to SAP HANA Database + +## Symptom + +You're unable to connect an application to a SAP HANA Database instance. + +## Troubleshooting + +The Istio module's default configuration does not restrict outbound traffic. This means that the application should have no issues connecting to the SAP HANA Database instance. +To determine the cause of the connection issue, follow the troubleshooting steps. + +### Connect to the SAP HANA Database Instance from Outside of the Cluster +1. Download SAP HANA Client for your operating system from the [SAP Development Tools](https://tools.hana.ondemand.com/#hanatools). +2. Unpack the downloaded archive. +3. Install SAP HANA Client. +4. Connect to SAP HANA Database instance using the following command: + ```bash + hdbsql -n {HANA_DB_INSTANCE_ADDRESS} -u {HANA_DB_USER} -p {HANA_DB_PASSWORD} + ``` + For example: + ```bash + hdbsql -n aaa.bbb.ccc.ddd:30015 -u my_user -p mypassword + ``` +5. If the connection is successful and you can execute queries, the issue is not related to the SAP HANA Database instance. +### Connect to the SAP HANA Database Instance from Inside of the Cluster +1. Build a Docker image with the SAP HANA Client installed. You can use the following Dockerfile: + ```Dockerfile + FROM eclipse-temurin:17 + WORKDIR /build + COPY client.tar client.tar + RUN tar -xvf client.tar + RUN echo "/usr/local/bin" | ./client/hdbinst + + ENTRYPOINT ["sleep", "8000"] + ``` + Download the SAP HANA Client for Linux x86 64-bit from [SAP Development Tools](https://tools.hana.ondemand.com/#hanatools) and save it as `client.tar` in the same directory as the Dockerfile. Then, run the following command to build the image: + ```bash + docker buildx build --platform=linux/amd64 -t hdbsql . + ``` +2. To test your image, run the following command: + ```bash + docker run --entrypoint "hdbsql" hdbsql -v + ``` + You get an output similar to this example: + ``` + HDBSQL version 2.20.20.1712178305, the SAP HANA Database interactive terminal. + Copyright 2000-2024 by SAP SE. + ``` +3. Publish the image to a container registry. +4. Run the image in the Kubernetes cluster: + ```bash + kubectl create deployment hdbsql --image={PUBLISHED_IMAGE_NAME} + ``` +5. Attach to the Pod and try to connect to the SAP HANA Database instance using the following command: + ```bash + hdbsql -n {HANA_DB_INSTANCE_ADDRESS} -u {HANA_DB_USER} -p {HANA_DB_PASSWORD} + ``` +6. If the connection is successful and you can execute queries, the issue is not related to the setup of the cluster. +7. Check the connection from a Pod that has the Istio sidecar injected. In that case, create the Deployment in a namespace with Istio sidecar injection enabled. The connection should be successful. + diff --git a/tests/ui/cypress.config.ts b/tests/ui/cypress.config.ts index 97da7f392b..2b117a1ddb 100644 --- a/tests/ui/cypress.config.ts +++ b/tests/ui/cypress.config.ts @@ -33,4 +33,7 @@ module.exports = defineConfig({ ], supportFile: 'support/index.ts', }, + retries: { + runMode: 2, + }, }); diff --git a/tests/ui/fixtures/pod-httpbin-sleep.yaml b/tests/ui/fixtures/pod-httpbin-sleep.yaml new file mode 100644 index 0000000000..cc08921cb8 --- /dev/null +++ b/tests/ui/fixtures/pod-httpbin-sleep.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Pod +metadata: + name: placeholderName + namespace: placeholderNamespace + labels: + app: placeholderName +spec: + containers: + - name: test-container + image: eu.gcr.io/kyma-project/external/kennethreitz/httpbin + command: ["/bin/sh", "-c"] + args: + - while true; do sleep 1; done; \ No newline at end of file diff --git a/tests/ui/package-lock.json b/tests/ui/package-lock.json index 42219a8b86..643873a122 100644 --- a/tests/ui/package-lock.json +++ b/tests/ui/package-lock.json @@ -7,7 +7,7 @@ "name": "ui-tests", "devDependencies": { "@kubernetes/client-node": "0.21.0", - "cypress": "13.8.1", + "cypress": "13.9.0", "cypress-file-upload": "5.0.8", "js-yaml": "4.1.0", "typescript": "5.4.5" @@ -786,9 +786,9 @@ } }, "node_modules/cypress": { - "version": "13.8.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.8.1.tgz", - "integrity": "sha512-Uk6ovhRbTg6FmXjeZW/TkbRM07KPtvM5gah1BIMp4Y2s+i/NMxgaLw0+PbYTOdw1+egE0FP3mWRiGcRkjjmhzA==", + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.9.0.tgz", + "integrity": "sha512-atNjmYfHsvTuCaxTxLZr9xGoHz53LLui3266WWxXJHY7+N6OdwJdg/feEa3T+buez9dmUXHT1izCOklqG82uCQ==", "dev": true, "hasInstallScript": true, "dependencies": { diff --git a/tests/ui/package.json b/tests/ui/package.json index 9e4c306168..abc119cc2b 100644 --- a/tests/ui/package.json +++ b/tests/ui/package.json @@ -9,7 +9,7 @@ "start-k3d": "./scripts/k3d-local-dev.sh" }, "devDependencies": { - "cypress": "13.8.1", + "cypress": "13.9.0", "cypress-file-upload": "5.0.8", "js-yaml": "4.1.0", "typescript": "5.4.5", diff --git a/tests/ui/support/input.ts b/tests/ui/support/input.ts index e391563acb..ca729bb841 100644 --- a/tests/ui/support/input.ts +++ b/tests/ui/support/input.ts @@ -1,12 +1,13 @@ Cypress.Commands.add('inputClearAndType', (selector: string, newValue: string, filterFilledInputs = false): void => { - let input = cy.get(selector) - .find('input:visible'); + let input = cy.get(selector); if (filterFilledInputs) { input = input.filterWithNoValue(); } - input.click() + input.scrollIntoView() + .find('input:visible') + .click() .clear({force: true}) .type(newValue, {force: true}); }); diff --git a/tests/ui/support/k8sclient/commands.ts b/tests/ui/support/k8sclient/commands.ts index 2cf32b221d..c5af01b4de 100644 --- a/tests/ui/support/k8sclient/commands.ts +++ b/tests/ui/support/k8sclient/commands.ts @@ -3,4 +3,5 @@ export interface Commands { createService(namespace: string, name: string): void createNamespace(name: string): void deleteNamespace(name: string): void + createHttpbinSleepPod(name: string, namespace: string): void } \ No newline at end of file diff --git a/tests/ui/support/k8sclient/index.ts b/tests/ui/support/k8sclient/index.ts index bba00f1053..a3798934fb 100644 --- a/tests/ui/support/k8sclient/index.ts +++ b/tests/ui/support/k8sclient/index.ts @@ -1,6 +1,7 @@ import "./authorizationPolicy" import "./namespace" import "./service" +import "./pod"; export {Commands as K8sClientCommands} from "./commands"; export {KubernetesConfig, getK8sCurrentContext} from "./kubeconfig"; diff --git a/tests/ui/support/k8sclient/pod.ts b/tests/ui/support/k8sclient/pod.ts new file mode 100644 index 0000000000..bbd22526e7 --- /dev/null +++ b/tests/ui/support/k8sclient/pod.ts @@ -0,0 +1,16 @@ +import {loadFixture} from "./loadFile"; +import * as k8s from "@kubernetes/client-node"; +import {postApi} from "./httpClient"; + +Cypress.Commands.add('createHttpbinSleepPod', (name: string, namespace: string) => { + // @ts-ignore Typing of cy.then is not good enough + cy.wrap(loadFixture('pod-httpbin-sleep.yaml')).then((s: k8s.V1Pod): void => { + s.metadata!.name = name + s.metadata!.namespace = namespace + s.metadata!.labels ? s.metadata!.labels["app"] = name : s.metadata!.labels = {"app": name} + // We have to use cy.wrap, since the post command uses a cy.fixture internally + cy.wrap(postApi(`v1/namespaces/${namespace}/pods`, s)).should("be.true"); + }) +}); + + diff --git a/tests/ui/support/navigation.ts b/tests/ui/support/navigation.ts index 91fc1219b8..dfa8b82d46 100644 --- a/tests/ui/support/navigation.ts +++ b/tests/ui/support/navigation.ts @@ -7,6 +7,8 @@ export interface NavigationCommands { navigateToAuthorizationPolicies(namespace: string): void navigateToTelemetries(namespace: string): void navigateToTelemetry(name: string, namespace: string): void + navigateToRequestAuthentications(namespace: string): void + navigateToRequestAuthentication(name: string, namespace: string): void navigateToDestinationRule(name: string, namespace: string): void navigateToDestinationRules(namespace: string): void navigateToGateway(name: string, namespace: string): void @@ -116,3 +118,17 @@ Cypress.Commands.add('navigateToSidecars', (namespace: string): void => { cy.wait(2000); }); }); + +Cypress.Commands.add('navigateToRequestAuthentication', (namespace: string, name: string): void => { + cy.wrap(getK8sCurrentContext()).then((context) => { + cy.visit(`${config.clusterAddress}/cluster/${context}/namespaces/${namespace}/requestauthentications/${name}`) + cy.wait(2000); + }); +}); + +Cypress.Commands.add('navigateToRequestAuthentications', (namespace: string): void => { + cy.wrap(getK8sCurrentContext()).then((context) => { + cy.visit(`${config.clusterAddress}/cluster/${context}/namespaces/${namespace}/requestauthentications`) + cy.wait(2000); + }); +}); diff --git a/tests/ui/support/resource/commands.ts b/tests/ui/support/resource/commands.ts index e31d9f9c47..4caf70b8d0 100644 --- a/tests/ui/support/resource/commands.ts +++ b/tests/ui/support/resource/commands.ts @@ -5,7 +5,8 @@ import { VirtualServiceCommands } from "./virtualService"; import { ServiceEntryCommands } from "./serviceEntry"; import { SidecarCommands } from "./sidecar"; import { TelemetryCommands } from "./telemetries"; +import {RequestAuthenticationCommands} from "./requestAuthentication"; export interface Commands extends AuthorizationPolicyCommands, TelemetryCommands, DestinationRuleCommands, GatewayCommands, - VirtualServiceCommands, ServiceEntryCommands, SidecarCommands { + VirtualServiceCommands, ServiceEntryCommands, SidecarCommands, RequestAuthenticationCommands { } diff --git a/tests/ui/support/resource/index.ts b/tests/ui/support/resource/index.ts index 505be7a68f..c9111ee74b 100644 --- a/tests/ui/support/resource/index.ts +++ b/tests/ui/support/resource/index.ts @@ -5,5 +5,6 @@ import './virtualService'; import './serviceEntry'; import './sidecar'; import './telemetries'; +import './requestAuthentication'; export { Commands as ResourceCommands } from "./commands"; diff --git a/tests/ui/support/resource/requestAuthentication.ts b/tests/ui/support/resource/requestAuthentication.ts new file mode 100644 index 0000000000..0867aad4d9 --- /dev/null +++ b/tests/ui/support/resource/requestAuthentication.ts @@ -0,0 +1,74 @@ +export interface RequestAuthenticationCommands { + requestAuthenticationTypeName(name: string): void + requestAuthenticationAddJwtRule(rule: JwtRule, numberOfJwtRule?: number): void + requestAuthenticationAddJwtRules(rules: JwtRule[]): void + requestAuthenticationAddMatchLabel(label: Label): void +} + +export type JwtRule = { + issuer: string; + jwksUri: string; + audiences: string[]; + fromParams: string[]; + fromCookies: string[]; + fromHeaders: FromHeaders[]; +} + +export type FromHeaders = { + name: string; + prefix: string; +} + +export type Label = { + key: string; + value: string; +} + +Cypress.Commands.add('requestAuthenticationTypeName', (name: string): void => { + cy.inputClearAndType('ui5-input[aria-label="RequestAuthentication name"]', name); +}); +Cypress.Commands.add('requestAuthenticationAddMatchLabel', (label: Label): void => { + cy.inputClearAndType('ui5-input[placeholder="Enter key"]:visible', label.key); + cy.get('ui5-input[placeholder="Enter value"]:visible') + .eq(0) + .scrollIntoView() + .find('input:visible') + .click() + .clear({force: true}) + .type(label.value, {force: true}); +}); + +Cypress.Commands.add('requestAuthenticationAddJwtRules', (rules: JwtRule[]): void => { + rules.forEach((jwt, index) => { + cy.requestAuthenticationAddJwtRule(jwt, index); + }); +}); + +Cypress.Commands.add('requestAuthenticationAddJwtRule', (rule: JwtRule, numberOfJwtRule= 0): void => { + cy.addFormGroupItem('[aria-label="expand JWT Rules"]:visible'); + cy.inputClearAndType(`ui5-input[data-testid="spec.jwtRules.${numberOfJwtRule}.issuer"]`, rule.issuer); + cy.inputClearAndType(`ui5-input[data-testid="spec.jwtRules.${numberOfJwtRule}.jwksUri"]`, rule.jwksUri); + + cy.get('[aria-label="expand Audiences"]:visible').click(); + rule.audiences.forEach((audience, index) => { + cy.inputClearAndType(`ui5-input[data-testid="spec.jwtRules.${numberOfJwtRule}.audiences.${index}"]`, audience); + }); + + cy.get('[aria-label="expand From Params"]:visible').click(); + rule.fromParams.forEach((param, index) => { + cy.inputClearAndType(`ui5-input[data-testid="spec.jwtRules.${numberOfJwtRule}.fromParams.${index}"]`, param); + }); + + cy.get('[aria-label="expand From Cookies"]:visible').click(); + rule.fromCookies.forEach((cookie, index) => { + cy.inputClearAndType(`ui5-input[data-testid="spec.jwtRules.${numberOfJwtRule}.fromCookies.${index}"]`, cookie); + }); + + rule.fromHeaders.forEach((header, index) => { + cy.addFormGroupItem(`[aria-label="expand From Headers"]:visible`); + cy.inputClearAndType(`ui5-input[data-testid="spec.jwtRules.${numberOfJwtRule}.fromHeaders.${index}.name"]`, header.name); + cy.inputClearAndType(`ui5-input[data-testid="spec.jwtRules.${numberOfJwtRule}.fromHeaders.${index}.prefix"]`, header.prefix); + }); + + cy.get('[aria-label="expand JWT Rule"]:visible').eq(numberOfJwtRule).click(); +}); diff --git a/tests/ui/support/resource/serviceEntry.ts b/tests/ui/support/resource/serviceEntry.ts index 70a684aa6c..f510a25f83 100644 --- a/tests/ui/support/resource/serviceEntry.ts +++ b/tests/ui/support/resource/serviceEntry.ts @@ -27,6 +27,6 @@ Cypress.Commands.add('serviceEntrySelectLocation', (value: string): void => { Cypress.Commands.add('serviceEntryTypeAddress', (value: string): void => { cy.get('[aria-label="expand Addresses"]:visible').click(); - cy.inputClearAndType('[placeholder="For example, 127.0.0.1"]:visible', value); + cy.inputClearAndType('[data-testid="spec.addresses.0"]', value); }); diff --git a/tests/ui/support/resource/sidecar.ts b/tests/ui/support/resource/sidecar.ts index 3d12598052..3c275e05bc 100644 --- a/tests/ui/support/resource/sidecar.ts +++ b/tests/ui/support/resource/sidecar.ts @@ -18,8 +18,8 @@ Cypress.Commands.add('sidecarAddIngress', (ingress: SidecarIngress): void => { cy.addFormGroupItem('[aria-label="expand Ingress"]:visible'); cy.get('[aria-label="expand Port"]:visible').click(); - cy.inputClearAndType('[placeholder="Enter the port number"]', ingress.port.toString()); + cy.inputClearAndType('[data-testid="spec.ingress.0.port.number"]', ingress.port.toString()); cy.chooseComboboxOption('[data-testid="spec.ingress.0.port.protocol"]', ingress.protocol); cy.inputClearAndType('[aria-label="Sidecar name"]', ingress.name, true); - cy.inputClearAndType('[placeholder="For example, 127.0.0.1:PORT"]', ingress.defaultEndpoint); + cy.inputClearAndType('[data-testid="spec.ingress.0.defaultEndpoint"]', ingress.defaultEndpoint); }); diff --git a/tests/ui/tests/requestAuthentications.spec.ts b/tests/ui/tests/requestAuthentications.spec.ts new file mode 100644 index 0000000000..e1f3b7e62f --- /dev/null +++ b/tests/ui/tests/requestAuthentications.spec.ts @@ -0,0 +1,109 @@ +import {generateNamespaceName, generateRandomName} from "../support"; +import {JwtRule} from "../support/resource/requestAuthentication"; + +context('RequestAuthentications', () => { + + let namespaceName: string; + let reqAuthName: string; + + beforeEach(() => { + namespaceName = generateNamespaceName(); + reqAuthName = generateRandomName("test-"); + cy.loginAndSelectCluster(); + cy.createNamespace(namespaceName); + }); + + afterEach(() => { + cy.deleteNamespace(namespaceName); + }); + + it('should create new with two JWT Rules and no selector', () => { + cy.navigateToRequestAuthentications(namespaceName); + cy.clickCreateButton(); + + cy.requestAuthenticationTypeName(reqAuthName); + + const jwtRule: JwtRule = { + issuer: "test-issuer@example.com", + jwksUri: "https://test-jwksUri@example.com", + audiences: ["test-audience1", "test-audience2"], + fromParams: ["test-fromParams1", "test-fromParams2"], + fromCookies: ["test-fromCookies1", "test-fromCookies2"], + fromHeaders: [ + { + name: "test-header1", + prefix: "test-prefix1" + }, + { + name: "test-header2", + prefix: "test-prefix2" + }] + } + const jwtRule2: JwtRule = { + issuer: "second-issuer@example.com", + jwksUri: "https://second-jwksUri@example.com", + audiences: ["second-test-audience1", "second-test-audience2"], + fromParams: ["second-test-fromParams1", "second-test-fromParams2"], + fromCookies: ["second-test-fromCookies1", "second-test-fromCookies2"], + fromHeaders: [ + { + name: "second-test-header1", + prefix: "second-test-prefix1" + }, + { + name: "second-test-header2", + prefix: "second-test-prefix2" + }] + } + cy.requestAuthenticationAddJwtRules([jwtRule, jwtRule2]); + + cy.clickCreateButton(); + + // Verify the details + cy.contains(reqAuthName).should('be.visible'); + + assertJwtRule(jwtRule); + assertJwtRule(jwtRule2); + + cy.contains("Matches all Pods in the Namespace") + }); + + it('should render pod for selector in details', () => { + + const podName = generateRandomName("reqauth-pod") + cy.createHttpbinSleepPod(podName, namespaceName); + cy.navigateToRequestAuthentications(namespaceName); + cy.clickCreateButton(); + + cy.requestAuthenticationTypeName(reqAuthName); + cy.requestAuthenticationAddMatchLabel({key: "app", value: podName}); + cy.clickCreateButton(); + + // Verify the details + cy.contains(`${podName}`).should('be.visible'); + cy.contains(`app=${podName}`).should('be.visible'); + }); +}) + +function assertJwtRule(rule: JwtRule) { + cy.contains(`Issuer ${rule.issuer}`).should('be.visible').click(); + cy.get('[data-testid="collapse-content"]').contains(rule.issuer).should('be.visible'); + cy.get('[data-testid="collapse-content"]').contains(rule.jwksUri).should('be.visible'); + + rule.audiences.forEach((audience) => { + cy.get('[data-testid="collapse-content"]').contains(audience).should('be.visible'); + }); + rule.fromParams.forEach((param) => { + cy.get('[data-testid="collapse-content"]').contains(param).should('be.visible'); + }); + rule.fromCookies.forEach((cookie) => { + cy.get('[data-testid="collapse-content"]').contains(cookie).should('be.visible'); + }); + rule.fromHeaders.forEach((header) => { + cy.contains(`Header ${header.name}`).should('be.visible').click(); + cy.contains(header.prefix).should('be.visible'); + }); + + cy.contains(`Issuer ${rule.issuer}`).should('be.visible').click(); +} +