Skip to content

feat(sso): Test Login admin flow across Custom OIDC, SAML, LDAP#28034

Open
siddhant1 wants to merge 53 commits into
mainfrom
sid/sso-layout
Open

feat(sso): Test Login admin flow across Custom OIDC, SAML, LDAP#28034
siddhant1 wants to merge 53 commits into
mainfrom
sid/sso-layout

Conversation

@siddhant1
Copy link
Copy Markdown
Member

@siddhant1 siddhant1 commented May 11, 2026

Summary

  • Adds a Test Login admin flow that verifies an SSO configuration end-to-end before it's saved: popup-driven for OIDC and SAML, in-page modal for LDAP. Backend handlers (TestLoginHandler, TestSamlHandler, TestLdapHandler) talk to the IdP using the form's draft config so admins can validate credentials, certs, and discovery URIs without ever applying a broken config.
  • Lockout-safe save: the Save button is gated behind a fresh Test Login whenever a lockout-risk field (clientId, clientSecret, idpX509Certificate, dnAdminPassword, etc.) is edited. Includes a mode=existing overlay so admins can re-verify a saved config without retyping secrets, and a claim selector that surfaces every claim the IdP returned and remembers the chosen emailClaim.
  • SSO Configuration form rebuilt on Untitled UI primitives; trimmed SAML/LDAP main sections to IdP-only / bind-only fields; full E2E coverage in a new sso-test-login Playwright project plus unit-test parity for TestLdapHandlerTest / TestLoginHandlerTest / TestSamlHandlerTest.

Test plan

  • docker compose -f docker/development/docker-compose.yml --profile sso-test up -d brings up mock-OIDC (:9090) and OpenLDAP (:1389)
  • KEYCLOAK_SAML_PORT=8081 docker compose -f docker/local-sso/keycloak-saml/docker-compose.yml up -d for the SAML fixture
  • SSO_USERNAME=azure.saml@openmetadata.local SSO_PASSWORD=OpenMetadata@123 KEYCLOAK_SAML_BASE_URL=http://localhost:8081 yarn playwright test --project=sso-test-login — 13 tests cover happy path / failure / mode=existing / lockout-risk gate per provider; 5× back-to-back locally showed zero flakes
  • Backend integration: mvn test -pl openmetadata-service -Dtest=TestLdapHandlerTest,TestLoginHandlerTest,TestSamlHandlerTest
  • Manual smoke: configure Google/Okta/SAML/LDAP from the SSO Configuration page; verify Test Login → claim selector → Save persists the canonical shape; verify Save is blocked with toast when a lockout-risk field is edited without re-testing

🤖 Generated with Claude Code


Summary by Gitar

  • Refined test infrastructure:
    • Standardized Playwright import ordering and improved error message formatting in ssoAuth.ts and sso.ts.
    • Minor cleanup of redundant logic in popupLifecycle.ts and UI test spec files to ensure stable test execution.

This will update automatically on new commits.

aji-aju and others added 30 commits April 29, 2026 16:22
…ocol selector (#27312)

Backend:
- Add discoveryUri and emailClaim fields to authenticationConfiguration schema
- Add emailClaim priority chain in SecurityUtil (claimsMapping > emailClaim > fallback)
- Add syncFieldsFromDiscoveryUri auto-sync on save (authority, clientId, callbackUrl)
- Add TestLoginHandler with OIDC popup flow (initiate + callback) and LDAP inline test
- Add Test Login endpoints in SystemResource (initiate, callback, LDAP)
- Add SAML email attribute check before NameID fallback
- Exclude test-login callback from JwtFilter

Frontend:
- Add protocol selector (OIDC / SAML / LDAP) replacing 8 provider cards
- Add OIDC provider dropdown options with Discovery URI templates
- Add TestLoginButton component (popup + postMessage listener)
- Add ClaimSelector component (claims table + email claim selection)
- Add AuthModeWidget replacing Public/Confidential jargon
- Hide auto-derived fields (authority, publicKeyUrls, tokenValidationAlgorithm)
- Wire Test Login into SSOConfigurationForm

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ields

- Add test-login/initiate to JwtFilter EXCLUDED_ENDPOINTS (popup window
  doesn't carry JWT token — endpoint only redirects to IdP, no data exposure)
- Hide authorizer fields: botPrincipals, principalDomain, enforcePrincipalDomain,
  enableSecureSocketConnection, useRolesFromProvider, allowedEmailRegistrationDomains,
  allowedDomains, defaultOAuthRole (these will be auto-filled by Test Login
  or moved to advanced settings)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e-validation to Test Login

- Hide emailClaim, discoveryUri from form (set via Test Login, not manual input)
- Hide adminPrincipals, enableSelfSignup, enableAutoRedirect (auto-filled or advanced)
- Test Login now validates configuration before opening popup — calls
  validateSecurityConfiguration first, shows error inline if validation fails,
  only opens popup if config is valid

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The 404 was caused by Dropwizard/Jersey rejecting void return type
for @get methods. Rewritten to return proper JAX-RS Response objects:
- handleInitiate returns Response.temporaryRedirect(authUrl) instead
  of HttpServletResponse.sendRedirect
- handleCallback returns Response.ok(html, "text/html") instead of
  writing to HttpServletResponse directly
- Removed HttpServletResponse dependency from endpoints

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Moved prompt to hidden in all provider-specific UI schemas
(Google, Azure, Okta, Auth0/Cognito, Custom OIDC) — this is
an advanced setting that rarely needs user configuration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause of 404: ConfigResource owns @path("/v1/system/config") and
SystemResource is at @path("/v1/system"). When the test-login endpoints
were in SystemResource at @path("/config/auth/test-login/..."), Jersey
routed them to ConfigResource (which owns /config/*) instead. ConfigResource
had no matching sub-path, so it returned 404.

Fix: moved all test-login endpoints to ConfigResource where they belong
under the /config/auth path prefix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rm data

TestLoginHandler no longer depends on AuthenticationCodeFlowHandler
being initialized. Instead, it:

1. Reads discoveryUri, clientId, clientSecret, scope from query params
   (passed by frontend from the unsaved form data)
2. Fetches OIDC discovery document directly via OIDCProviderMetadata.resolve()
3. Builds authorization URL from the discovery metadata
4. On callback, builds token request directly using Nimbus SDK
   (ClientSecretBasic for confidential, ClientID-only for public)
5. Stores clientId/secret/discoveryUri in HTTP session for the callback

This means Test Login works BEFORE saving — user fills fields, clicks
Test Login to validate, then saves. No dependency on server state.

Frontend: TestLoginButton now passes form data (discoveryUri, clientId,
clientSecret, scope) as query params to the initiate endpoint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ery URI

resolve() expects an Issuer (base URL) and appends .well-known/openid-configuration.
We have the full discovery URI, so use parse() with direct HTTP fetch instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…_mismatch

Root cause: Test Login used a separate callback URL (/api/v1/system/config/
auth/test-login/callback) that wasn't registered in the IdP's OAuth app.
Google (and other IdPs) reject unregistered redirect URIs with 400.

Fix: Reuse the same /callback URL that's already registered in the IdP.
Differentiate Test Login callbacks using state prefix "test-login:".

Changes:
- TestLoginHandler.handleInitiate(): uses registered callbackUrl (from form
  data or defaults to /callback), prefixes state with "test-login:"
- AuthCallbackServlet.doGet(): checks state prefix, routes test-login
  callbacks to TestLoginHandler.handleCallback()
- Removed separate /auth/test-login/callback endpoint from ConfigResource
- Removed callback from JwtFilter excluded endpoints (servlet handles it)
- Frontend passes callbackUrl from form data to initiate endpoint

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ailClaim set

Bug 1: "Client ID is required" on first click — formData was missing from
useCallback dependency array, causing stale closure. Added formData to deps.

Bug 2: Save button now disabled for new OIDC setups until emailClaim is
confirmed via Test Login → Claim Selector flow. Existing configs can
still save freely.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…, Other)

- Provider dropdown appears at top of OIDC config form
- Each option shows icon, label, and IdP-specific help text
- Selecting a provider pre-fills Discovery URI template and updates
  provider/providerName/oidcConfiguration.type in form data
- Default changed from Google to Custom OIDC (generic)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. TestLoginButton: reject Discovery URIs with unresolved placeholders
   ({tenant-id}, {your-domain}) — show clear message to replace them

2. SystemResource.validateSecurityConfig: call syncFieldsFromDiscoveryUri
   and autoPopulatePublicKeyUrlsIfNeeded BEFORE validation, so authority
   and publicKeyUrls are auto-derived from discoveryUri. Fixes "Authority
   is required" error when saving with discoveryUri set.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…est Login

1. Restored OIDC prompt field to visible in all provider UI schemas
   (was hidden, now shows in advanced config)

2. Discovery URI: only Google pre-fills a default value. Azure, Okta,
   Auth0, Cognito now show empty field with format hint in help text.
   Prevents users from accidentally using placeholder templates.

3. Validation before Test Login:
   - Backend: validateSecurityConfiguration accepts ?context=testLogin
     query param. When set, skips adminPrincipals and principalDomain
     validation (these are set via Claim Selector after Test Login)
   - Frontend: TestLoginButton calls validation API with context=testLogin
     before opening popup. Shows validation errors inline if config invalid.
   - Save button has no separate validation — Test Login already validated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The parameter was added to the outer method but not passed to the
private validateAuthorizerConfiguration method where the actual
adminPrincipals/principalDomain checks happen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…h method

1. Default OIDC scope changed from "openid email profile" to
   "openid email profile offline_access" — ensures refresh tokens
   for Azure/Okta/Auth0/Cognito. Google ignores unsupported scopes.
   No regression for existing configs (their saved scope is unchanged).

2. Scope field hidden from main form (default covers most cases)

3. OIDC prompt field hidden from main form (advanced setting)

4. Okta clientAuthenticationMethod hidden (advanced setting)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…errors on OIDC

The @Valid annotation triggers Jakarta Bean Validation on the entire
SecurityConfiguration object tree, including nested ldapConfiguration
and samlConfiguration schemas. This causes 400 errors with messages
like "userBaseDN must not be null" when validating an OIDC config.

Removed @Valid — our custom validateSecurityConfiguration() handles
provider-specific validation correctly (only checks relevant fields
based on the provider type).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The JSON schema default was updated but 6 hardcoded fallbacks in
frontend code still used "openid email profile" without offline_access.

Updated:
- SSOUtils.ts (2 occurrences)
- AuthProvider.util.ts (3 occurrences)
- TestLoginButton.component.tsx (1 occurrence)
- conf/openmetadata.yaml (OIDC_SCOPE default)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…uthMethod to visible

1. Popup communication: replaced window.opener.postMessage with
   localStorage. window.opener is null after redirect chains (initiate
   → IdP → callback). localStorage works across same-origin windows
   regardless of redirects. Popup stores result, parent reads via
   storage event + close-check fallback. Popup always closes itself.

2. Restored scope, prompt to visible in all 5 provider UI schemas
   (they were hidden but should be in advanced config = visible).

3. Restored Okta clientAuthenticationMethod to visible.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…veryUri

discoveryUri is a top-level field in authenticationConfiguration — the
single source of truth for both public and confidential flows. The
nested oidcConfiguration.discoveryUri is synced from it on save.

- Made top-level discoveryUri visible with title and placeholder
- Hidden nested oidcConfiguration.discoveryUri in all 6 provider
  schemas (OIDC, Google, Azure, Okta, Auth0/Cognito, Custom OIDC)
- One discoveryUri field visible regardless of client type

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…estLoginButton

handleOidcProviderChange was writing discoveryUri to the nested
oidcConfiguration.discoveryUri (hidden), not the top-level
discoveryUri (visible). TestLoginButton was also checking nested
first. Fixed both to use the top-level field as primary.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… default

Test Login now mirrors the actual OIDC flow by passing:
- prompt (consent/login/none)
- maxAge
- clientAuthenticationMethod (client_secret_basic/client_secret_post)

Backend: TestLoginHandler reads these from query params, applies to
auth URL, and uses correct client auth method for token exchange.

Also reverted offline_access from default scope — back to
"openid email profile" everywhere (schema, SSOUtils, AuthProvider.util,
TestLoginButton, openmetadata.yaml).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…normalization

- backend: add normalizeForPersistence and hydrateForResponse in SystemRepository.
  deterministic priority-based mirroring between root and nested OIDC fields,
  always-overwrite derivation of authority and publicKeyUrls from discoveryUri,
  Azure tenant extraction across commercial/gov/china cloud variants.
- backend: SystemResource calls normalizeForPersistence on validate/PUT,
  hydrateForResponse on GET. Legacy configs without discoveryUri are
  preserved unchanged — zero regression for existing customers.
- ui: mirror canonical and legacy OIDC fields in form onChange so RJSF's
  schema required check passes before hitting the API (workaround only;
  backend handles partial payloads on its own).
- ui: validation state machine — Validate button posts to
  /validate?context=testLogin, enables Test Login on success. Save gated
  on Test Login + claim-selector confirm. Reset only when actual
  validation inputs change, not post-test fields.
- ui: hide Azure tenant (backend-derived). Advanced Configuration accordion
  collapses non-essential OIDC fields via SSO_ADVANCED_OIDC_FIELDS.
- tests: SystemRepositoryNormalizeTest covers mirror directions, Azure
  tenant extraction (all cloud variants), legacy preservation, null safety,
  no-network-call on hydrate.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
adminPrincipals is often configured by email (e.g. "alice@company.com")
while the derived userName is typically the email local part ("alice").
The previous check only looked up userName in adminPrincipals, so a user
configured by email never got promoted to admin on login.

- SecurityUtil: new isAdminPrincipal(adminPrincipals, userName, email)
  helper that matches either form.
- AuthenticationCodeFlowHandler: use helper on existing and new OIDC
  user paths.
- SamlAuthServletHandler: same for existing and new SAML user paths.
- SamlAssertionConsumerServlet: same for JWT token admin flag.

LDAP intentionally unchanged — LDAP principals are usernames, not emails.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two coupled changes enabling the full Test Login gated flow across both
OIDC and SAML:

## SAML Test Login

- New TestSamlHandler builds a temporary Saml2Settings from form-provided
  IdP details, redirects popup to IdP with RelayState="saml-test-login:<uuid>",
  and on callback verifies the SAMLResponse + flattens attributes into the
  same {success, claims} payload that OIDC Test Login produces.
- New POST /v1/system/config/auth/test-login/saml-initiate endpoint using
  @FormParam so Jersey reliably parses the form-urlencoded body.
- AuthCallbackServlet.doPost now routes SAML Test Login callbacks by
  RelayState prefix (parallel to the existing doGet state-prefix routing
  for OIDC Test Login).
- TestLoginResponses extracted from TestLoginHandler so both OIDC and SAML
  use the same popup HTML / localStorage handoff.
- UI: TestLoginButton branches by provider; SAML uses a hidden-form POST
  with target=_blank so the cert fits cleanly in the body.
- UI: SAML main form now shows only IdP Entity ID, SSO Login URL, X.509
  Certificate, NameID Format. SP fields are surfaced in a banner and
  derived server-side. SP certs, signing flags, and security options move
  to an Advanced Configuration accordion via SSO_ADVANCED_SAML_FIELDS.
- ClaimSelector handles multi-valued SAML attributes (arrays joined for
  display, single values stringified as before).

## OIDC validator refinement

- New SystemRepository.validateDiscoveryUriReachable runs at the top of
  validateSecurityConfiguration for any OIDC provider with a discoveryUri.
  Short-circuits with a single clear error ("Could not reach Discovery
  URI") when the URI is unreachable, invalid JSON, missing issuer/jwks_uri,
  or (for Azure) doesn't match the Azure AD URL shape.
- Removed duplicate discoveryUri fetches from Azure, Okta, Auth0, and
  Cognito validators now that the upstream check handles reachability.
  Each validator keeps only its provider-specific semantic checks (issuer
  format, required endpoints, client credential validation).
- Improved error messages where fields are backend-derived:
  Azure: "Tenant could not be determined from Discovery URI…"
  Okta/Auth0/Cognito: "domain/URL could not be verified…"
  Custom OIDC: "Discovery document is missing required endpoints…"

## Validation state machine (shared across OIDC and SAML)

- areMainFieldsFilled and didValidationInputsChange now branch on
  provider so SAML uses IdP fields and OIDC uses discoveryUri + client
  credentials. validationStatus resets only when fields that actually
  affect validation change.
- Test Login and Validate button gates now accept either OIDC or SAML.

## Tests

8 new tests in SystemRepositoryNormalizeTest for the upstream discoveryUri
check (unreachable, invalid JSON, missing issuer, Azure shape mismatch,
LDAP scope skip, valid happy path, invalid URL format, legacy config
fall-through).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- TestLoginHandler: PKCE code_challenge sent by default (unless
  disablePkce=true), code_verifier stored in session and included in
  token exchange. Nonce now conditional on useNonce param. customParams
  parsed from JSON and merged into auth URL. Session cleanup includes
  CODE_VERIFIER. clientAuthMethod extracted to constant.
- TestSamlHandler: replaced HttpSession storage with ConcurrentHashMap
  keyed by RelayState UUID. Fixes "Session expired" error caused by
  SameSite=Lax blocking cookies on IdP's cross-origin POST callback.
  Entries auto-cleaned after 5 min TTL.
- SystemRepository: normalizeForPersistence now populates serverUrl
  (derived from callbackUrl). discoveryUri mirror changed to one-way
  root→nested only (Option B) — legacy configs with only nested
  discoveryUri no longer trigger derivation on unrelated PATCHes.
  Removed dead autoPopulatePublicKeyUrlsIfNeeded method.
- SystemResource: PATCH handler calls normalizeForPersistence after
  patch apply. PUT handler @Valid removed (fires before normalize,
  rejects partial payloads that normalize would fill).
- TestLoginButton: passes disablePkce, useNonce, customParams to
  backend for OIDC Test Login.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Switch OIDC Test Login initiate from GET+query-params to POST+form-body
so clientSecret stays out of URLs, server logs, and browser history.

- ConfigResource: endpoint changed to POST with @FormParam for each
  OIDC field (same pattern as SAML initiate).
- TestLoginHandler: accepts params as method arguments instead of
  req.getParameter() (Jersey consumes form body before handler runs).
- Redirect changed from 307 (temporaryRedirect) to 302 so the browser
  converts POST→GET when following the redirect to the IdP. 307
  preserved the POST method, causing Azure to reject with
  "client_id missing in body".
- TestLoginButton: OIDC now uses hidden-form POST with target=_blank
  (same pattern as SAML). Both protocols use identical transport.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LDAP pre-save Test Login so admins can verify form-provided LDAP config
without requiring a saved authenticator. UI collects user credentials
via an inline modal (no popup — LDAP has no external IdP to redirect
to). ClaimSelector is skipped since LDAP's email attribute is explicit;
principalDomain and adminPrincipal are auto-filled from the test user's
resolved email.

## Backend

- TestLdapHandler: stateless handler that accepts form-provided
  LdapConfiguration + user credentials. Admin bind → search by
  mailAttributeName → user bind → read mail attribute. Uses UnboundID
  LDAPConnection directly; no singletons, no DB writes, no user
  creation. Try-with-resources for connection lifecycle.
- ConfigResource: POST /v1/system/config/auth/test-login/ldap-initiate
  (JSON body with ldapConfiguration, email, password). Inline
  LdapTestLoginRequest DTO.
- JwtFilter: whitelisted the new endpoint.
- LdapAuthenticator: admin promotion now checks both userName and
  email via SecurityUtil.isAdminPrincipal — matches the fix already
  applied to OIDC/SAML. Fixes the case where adminPrincipals is saved
  as a full email but LDAP login was comparing against the local-part
  username.

## Frontend

- TestLoginButton: LDAP branch opens an Ant Design Modal with email
  and password inputs. Submits JSON to /ldap-initiate, calls onSuccess
  with derivedPrincipalDomain + suggestedAdminPrincipal. No popup.
- SSOConfigurationForm: handleTestLoginSuccess branches on provider —
  LDAP bypasses ClaimSelector entirely and directly applies
  principalDomain + adminPrincipals (emailClaim is left unset so the
  jwtPrincipalClaims fallback picks up "email" from the OM-issued JWT).
  areMainFieldsFilled and didValidationInputsChange handle LDAP fields.
- TestLogin.interface: ldapConfiguration added to form data type.

## Tests (44 new, 67 total)

- TestLoginHandlerTest (17) — OIDC validation branches, callback
  validation, buildClientAuthentication helper (basic/post/fallback),
  buildTestLoginResult (timestamp filtering, email detection, domain
  derivation, null handling).
- TestSamlHandlerTest (14) — SAML validation branches, RelayState
  routing, buildSamlSettings (IdP fields, NameID format, security
  flags off), buildClaimsFromAuth (single/multi-value flattening,
  empty/null handling).
- TestLdapHandlerTest (13) — uses UnboundID InMemoryDirectoryServer
  (real LDAP in-process). Covers input validation, happy path, wrong
  admin password, user not found, wrong user password, wrong
  userBaseDN, wrong mailAttributeName, server unreachable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Formatting-only changes: import reordering and long argument list splitting per Google Java Format. No logic changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace antd Card/Button/Typography/Upload.Dragger in
SSOConfigurationForm with @openmetadata/ui-core-components Button +
FileTrigger; keep RJSF and its widgets untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@siddhant1 siddhant1 removed the request for review from karanh37 May 11, 2026 08:31
@siddhant1 siddhant1 added UI UI specific issues safe to test Add this label to run secure Github workflows on PRs labels May 11, 2026
@github-actions
Copy link
Copy Markdown
Contributor

✅ TypeScript Types Auto-Updated

The generated TypeScript types have been automatically updated based on JSON schema changes in this PR.

Copilot AI review requested due to automatic review settings May 11, 2026 08:36
@siddhant1 siddhant1 review requested due to automatic review settings May 11, 2026 08:36
CI UI Checkstyle failed because prettier and organize-imports produced
a diff against committed SSO playwright fixtures, three SSOTestLogin
spec files, and one popupLifecycle component; this commit lands the
formatter's output so the tree matches the checkstyle baseline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 11, 2026 08:48
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 80 out of 85 changed files in this pull request and generated 8 comments.

Comment on lines +233 to +237
@POST
@Path("/auth/test-login/initiate")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Operation(
operationId = "testLoginInitiate",
Comment on lines +108 to +111
"v1/collate/apps/support/login",
"v1/system/config/auth/test-login/initiate",
"v1/system/config/auth/test-login/saml-initiate",
"v1/system/config/auth/test-login/ldap-initiate");
Comment on lines +67 to +73
static Response buildHtmlErrorResponse(String message) {
String sanitized = message.replace("'", "\\'").replace("\n", " ");
String html =
"<!DOCTYPE html><html><body>"
+ "<p>Test Login Error: "
+ message
+ "</p>"
Comment on lines +1063 to +1067
* Confidential (secret filled), the payload is just cleaned. For Public
* (secret blank), id/discoveryUri are promoted to root clientId/authority,
* the secret is dropped, and a slim oidcConfiguration with just discoveryUri
* is kept so backend validators that dereference oidcConfig.discoveryUri
* (e.g. CustomOidcValidator.extractDiscoveryUri) don't NPE. Non-OIDC
Comment on lines +145 to +149
<span
className="tw:text-xs tw:text-tertiary tw:truncate"
title={row.value}>
{row.value || '—'}
</span>
Comment on lines +178 to +183
<dt className="tw:text-tertiary">{t('label.refresh-token')}</dt>
<dd>
{result?.hasRefreshToken
? `✓ ${t('label.received')}`
: `⚠ ${t('message.no-refresh-token')}`}
</dd>
Comment on lines +41 to +46
className="tw:text-sm tw:font-medium tw:text-primary"
data-testid="email-claim-status-set">
<code>{emailClaim}</code>
{' ✓ '}
<span className="tw:text-xs tw:text-tertiary tw:font-normal">
{t('message.email-claim-verified')}
Comment on lines +42 to +45
} catch {
// HEAD against ldap:// won't speak HTTP; the connection refusing here
// means the port isn't bound. Skip rather than fail.
}
@gitar-bot
Copy link
Copy Markdown

gitar-bot Bot commented May 11, 2026

Code Review 🚫 Blocked 0 resolved / 4 findings

Implements an SSO Test Login flow for OIDC, SAML, and LDAP with gating mechanisms, but critical security findings include unauthenticated SSRF via excluded endpoints, reflected XSS in error responses, and arbitrary credential probing in the LDAP handler.

🚨 Security: Test-login endpoints bypass authentication — unauthenticated SSRF

📄 openmetadata-service/src/main/java/org/openmetadata/service/security/JwtFilter.java:109-111 📄 openmetadata-service/src/main/java/org/openmetadata/service/security/auth/TestLoginHandler.java:158-162 📄 openmetadata-service/src/main/java/org/openmetadata/service/resources/system/ConfigResource.java:233-247

The three new test-login endpoints (/v1/system/config/auth/test-login/initiate, saml-initiate, ldap-initiate) are added to EXCLUDED_ENDPOINTS in JwtFilter, making them publicly accessible without any authentication. Combined with the fact that TestLoginHandler.handleInitiate() fetches a user-supplied discoveryUri server-side (line 160: new HTTPRequest(HTTPRequest.Method.GET, new URI(discoveryUri).toURL()).send()), this creates an unauthenticated Server-Side Request Forgery (SSRF) vector.

Any anonymous user can POST to /api/v1/system/config/auth/test-login/initiate with discoveryUri=http://169.254.169.254/latest/meta-data/ (or any internal service URL) and the server will fetch it, potentially leaking cloud instance metadata, internal service responses, or enabling port scanning of the internal network.

The LDAP endpoint is similarly dangerous: an unauthenticated attacker can probe arbitrary LDAP servers from the application's network position.

Suggested fix
Remove these endpoints from EXCLUDED_ENDPOINTS in JwtFilter so they require a valid JWT. Additionally, add admin-role authorization (e.g., @RolesAllowed("Admin")) and validate that discoveryUri is an HTTPS URL with a public IP (not RFC-1918, link-local, or loopback) before fetching.
🚨 Security: Reflected XSS in test-login HTML error responses

📄 openmetadata-service/src/main/java/org/openmetadata/service/security/auth/TestLoginResponses.java:50 📄 openmetadata-service/src/main/java/org/openmetadata/service/security/auth/TestLoginResponses.java:68-72

Both buildHtmlErrorResponse and buildPostMessageResponse in TestLoginResponses.java embed error messages directly into HTML without HTML entity encoding. Since these endpoints are unauthenticated and the error message can derive from user-supplied input (e.g., a malformed discoveryUri that returns an error containing <script>alert(1)</script>), this is a reflected XSS vulnerability.

At line 72, message is concatenated directly into <p>Test Login Error: " + message + "</p>". The sanitized variable at line 68 only escapes single quotes for the JavaScript string literal but the unsanitized message is used in the HTML body. Similarly, in buildPostMessageResponse line 50, the error string goes unescaped into the <p> tag.

Suggested fix
HTML-encode all dynamic content before embedding in the response. Use a utility like `org.owasp.encoder.Encode.forHtml(message)` or Apache Commons `StringEscapeUtils.escapeHtml4(message)` for both the HTML body text and the JavaScript string literal. Example:

String htmlSafe = org.owasp.encoder.Encode.forHtml(message);
String jsSafe = org.owasp.encoder.Encode.forJavaScriptBlock(message);
⚠️ Security: LDAP test-login endpoint exposes credential-probing capability

📄 openmetadata-service/src/main/java/org/openmetadata/service/security/auth/TestLdapHandler.java:73-87 📄 openmetadata-service/src/main/java/org/openmetadata/service/security/auth/TestLdapHandler.java:177-191

The /auth/test-login/ldap-initiate endpoint (ConfigResource line 334) accepts arbitrary LDAP host/port/credentials and attempts connections to them. Even if authentication is added, this allows an admin to:

  1. Use the server as an LDAP connection proxy to scan internal networks
  2. Exfiltrate the admin bind password via mode=existing overlay if the merge logic can be tricked

The mode=existing path in TestLdapHandler.maybeMergeWithSaved() reads the saved config (including dnAdminPassword) and merges it with user input. If the merged result is logged or returned in an error message, the saved password could leak.

Suggested fix
Ensure the endpoint requires admin authentication. Validate that the LDAP host is not a private/internal IP. Never include the admin password in error messages. Consider restricting `mode=existing` to only allow connecting to the already-saved host:port.
⚠️ Security: JavaScript injection via unsanitized JSON in localStorage script

📄 openmetadata-service/src/main/java/org/openmetadata/service/security/auth/TestLoginResponses.java:53-58 📄 openmetadata-service/src/main/java/org/openmetadata/service/security/auth/TestLoginResponses.java:78-83

In buildHtmlErrorResponse (line 79), the error message sanitization (replace("'", "\'")) is insufficient for a JavaScript context. Characters like </script>, backslashes, or Unicode escapes can break out of the JS string literal and inject arbitrary script. The buildPostMessageResponse method passes json (from JsonUtils.pojoToJson) directly into a JSON.stringify() call inside a <script> tag — if any claim value from the IdP contains </script>, it will terminate the script block early and enable XSS.

Suggested fix
Use proper JavaScript string encoding (escape </script> as <\/script>, encode all special chars). Better yet, pass the data via a <script type="application/json"> data island and parse it with JSON.parse in a separate script tag, avoiding inline interpolation entirely.
🤖 Prompt for agents
Code Review: Implements an SSO Test Login flow for OIDC, SAML, and LDAP with gating mechanisms, but critical security findings include unauthenticated SSRF via excluded endpoints, reflected XSS in error responses, and arbitrary credential probing in the LDAP handler.

1. 🚨 Security: Test-login endpoints bypass authentication — unauthenticated SSRF
   Files: openmetadata-service/src/main/java/org/openmetadata/service/security/JwtFilter.java:109-111, openmetadata-service/src/main/java/org/openmetadata/service/security/auth/TestLoginHandler.java:158-162, openmetadata-service/src/main/java/org/openmetadata/service/resources/system/ConfigResource.java:233-247

   The three new test-login endpoints (`/v1/system/config/auth/test-login/initiate`, `saml-initiate`, `ldap-initiate`) are added to `EXCLUDED_ENDPOINTS` in JwtFilter, making them publicly accessible without any authentication. Combined with the fact that `TestLoginHandler.handleInitiate()` fetches a user-supplied `discoveryUri` server-side (line 160: `new HTTPRequest(HTTPRequest.Method.GET, new URI(discoveryUri).toURL()).send()`), this creates an unauthenticated Server-Side Request Forgery (SSRF) vector.
   
   Any anonymous user can POST to `/api/v1/system/config/auth/test-login/initiate` with `discoveryUri=http://169.254.169.254/latest/meta-data/` (or any internal service URL) and the server will fetch it, potentially leaking cloud instance metadata, internal service responses, or enabling port scanning of the internal network.
   
   The LDAP endpoint is similarly dangerous: an unauthenticated attacker can probe arbitrary LDAP servers from the application's network position.

   Suggested fix:
   Remove these endpoints from EXCLUDED_ENDPOINTS in JwtFilter so they require a valid JWT. Additionally, add admin-role authorization (e.g., @RolesAllowed("Admin")) and validate that discoveryUri is an HTTPS URL with a public IP (not RFC-1918, link-local, or loopback) before fetching.

2. 🚨 Security: Reflected XSS in test-login HTML error responses
   Files: openmetadata-service/src/main/java/org/openmetadata/service/security/auth/TestLoginResponses.java:50, openmetadata-service/src/main/java/org/openmetadata/service/security/auth/TestLoginResponses.java:68-72

   Both `buildHtmlErrorResponse` and `buildPostMessageResponse` in `TestLoginResponses.java` embed error messages directly into HTML without HTML entity encoding. Since these endpoints are unauthenticated and the error message can derive from user-supplied input (e.g., a malformed `discoveryUri` that returns an error containing `<script>alert(1)</script>`), this is a reflected XSS vulnerability.
   
   At line 72, `message` is concatenated directly into `<p>Test Login Error: " + message + "</p>"`. The `sanitized` variable at line 68 only escapes single quotes for the JavaScript string literal but the *unsanitized* `message` is used in the HTML body. Similarly, in `buildPostMessageResponse` line 50, the `error` string goes unescaped into the `<p>` tag.

   Suggested fix:
   HTML-encode all dynamic content before embedding in the response. Use a utility like `org.owasp.encoder.Encode.forHtml(message)` or Apache Commons `StringEscapeUtils.escapeHtml4(message)` for both the HTML body text and the JavaScript string literal. Example:
   
   String htmlSafe = org.owasp.encoder.Encode.forHtml(message);
   String jsSafe = org.owasp.encoder.Encode.forJavaScriptBlock(message);

3. ⚠️ Security: LDAP test-login endpoint exposes credential-probing capability
   Files: openmetadata-service/src/main/java/org/openmetadata/service/security/auth/TestLdapHandler.java:73-87, openmetadata-service/src/main/java/org/openmetadata/service/security/auth/TestLdapHandler.java:177-191

   The `/auth/test-login/ldap-initiate` endpoint (ConfigResource line 334) accepts arbitrary LDAP host/port/credentials and attempts connections to them. Even if authentication is added, this allows an admin to:
   1. Use the server as an LDAP connection proxy to scan internal networks
   2. Exfiltrate the admin bind password via `mode=existing` overlay if the merge logic can be tricked
   
   The `mode=existing` path in `TestLdapHandler.maybeMergeWithSaved()` reads the saved config (including `dnAdminPassword`) and merges it with user input. If the merged result is logged or returned in an error message, the saved password could leak.

   Suggested fix:
   Ensure the endpoint requires admin authentication. Validate that the LDAP host is not a private/internal IP. Never include the admin password in error messages. Consider restricting `mode=existing` to only allow connecting to the already-saved host:port.

4. ⚠️ Security: JavaScript injection via unsanitized JSON in localStorage script
   Files: openmetadata-service/src/main/java/org/openmetadata/service/security/auth/TestLoginResponses.java:53-58, openmetadata-service/src/main/java/org/openmetadata/service/security/auth/TestLoginResponses.java:78-83

   In `buildHtmlErrorResponse` (line 79), the error message sanitization (`replace("'", "\'")`) is insufficient for a JavaScript context. Characters like `</script>`, backslashes, or Unicode escapes can break out of the JS string literal and inject arbitrary script. The `buildPostMessageResponse` method passes `json` (from `JsonUtils.pojoToJson`) directly into a `JSON.stringify()` call inside a `<script>` tag — if any claim value from the IdP contains `</script>`, it will terminate the script block early and enable XSS.

   Suggested fix:
   Use proper JavaScript string encoding (escape </script> as <\/script>, encode all special chars). Better yet, pass the data via a <script type="application/json"> data island and parse it with JSON.parse in a separate script tag, avoiding inline interpolation entirely.

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqubecloud
Copy link
Copy Markdown

@github-actions
Copy link
Copy Markdown
Contributor

🔴 Playwright Results — 26 failure(s), 18 flaky

✅ 4041 passed · ❌ 26 failed · 🟡 18 flaky · ⏭️ 86 skipped

Shard Passed Failed Flaky Skipped
✅ Shard 1 299 0 0 4
🟡 Shard 2 761 0 7 8
🔴 Shard 3 752 26 3 7
🟡 Shard 4 787 0 3 18
🟡 Shard 5 708 0 1 41
🟡 Shard 6 734 0 4 8

Genuine Failures (failed on all attempts)

Features/SSOConfiguration.spec.ts › should show correct fields for Google provider with confidential client (shard 3)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeChecked�[2m(�[22m�[2m)�[22m failed

Locator: getByRole('radio', { name: /confidential/i })
Expected: checked
Timeout: 15000ms
Error: element(s) not found

Call log:
�[2m  - Expect "toBeChecked" with timeout 15000ms�[22m
�[2m  - waiting for getByRole('radio', { name: /confidential/i })�[22m

Features/SSOConfiguration.spec.ts › should show correct fields for Auth0 provider with confidential client (shard 3)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeChecked�[2m(�[22m�[2m)�[22m failed

Locator: getByRole('radio', { name: /confidential/i })
Expected: checked
Timeout: 15000ms
Error: element(s) not found

Call log:
�[2m  - Expect "toBeChecked" with timeout 15000ms�[22m
�[2m  - waiting for getByRole('radio', { name: /confidential/i })�[22m

Features/SSOConfiguration.spec.ts › should show correct fields for Okta provider with confidential client (shard 3)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeChecked�[2m(�[22m�[2m)�[22m failed

Locator: getByRole('radio', { name: /confidential/i })
Expected: checked
Timeout: 15000ms
Error: element(s) not found

Call log:
�[2m  - Expect "toBeChecked" with timeout 15000ms�[22m
�[2m  - waiting for getByRole('radio', { name: /confidential/i })�[22m

Features/SSOConfiguration.spec.ts › should show correct fields when selecting SAML provider (shard 3)
Error: Field not found: SP Entity ID
Features/SSOConfiguration.spec.ts › should show correct fields when selecting LDAP provider (shard 3)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed

Locator:  getByLabel('Enable SSL').first()
Expected: visible
Received: hidden
Timeout:  15000ms

Call log:
�[2m  - Expect "toBeVisible" with timeout 15000ms�[22m
�[2m  - waiting for getByLabel('Enable SSL').first()�[22m
�[2m    19 × locator resolved to <input type="checkbox" id="root/authenticationConfiguration/ldapConfiguration/sslEnabled" name="root/authenticationConfiguration/ldapConfiguration/sslEnabled" aria-describedby="root/authenticationConfiguration/ldapConfiguration/sslEnabled__error root/authenticationConfiguration/ldapConfiguration/sslEnabled__description root/authenticationConfiguration/ldapConfiguration/sslEnabled__help"/>�[22m
�[2m       - unexpected value "hidden"�[22m

Features/SSOConfiguration.spec.ts › should show correct fields when selecting Google provider (shard 3)
�[31mTest timeout of 60000ms exceeded.�[39m
Features/SSOConfiguration.spec.ts › should show correct fields when selecting Auth0 provider (shard 3)
�[31mTest timeout of 60000ms exceeded.�[39m
Features/SSOConfiguration.spec.ts › should show correct fields when selecting Okta provider (shard 3)
�[31mTest timeout of 60000ms exceeded.�[39m
Features/SSOConfiguration.spec.ts › should show OIDC Callback URL as readonly for Google provider (shard 3)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeChecked�[2m(�[22m�[2m)�[22m failed

Locator: getByRole('radio', { name: /confidential/i })
Expected: checked
Timeout: 15000ms
Error: element(s) not found

Call log:
�[2m  - Expect "toBeChecked" with timeout 15000ms�[22m
�[2m  - waiting for getByRole('radio', { name: /confidential/i })�[22m

Features/SSOConfiguration.spec.ts › should show OIDC Callback URL as readonly for Auth0 provider (shard 3)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed

Locator: locator('[id="root/authenticationConfiguration/oidcConfiguration/callbackUrl"]')
Expected: visible
Timeout: 15000ms
Error: element(s) not found

Call log:
�[2m  - Expect "toBeVisible" with timeout 15000ms�[22m
�[2m  - waiting for locator('[id="root/authenticationConfiguration/oidcConfiguration/callbackUrl"]')�[22m

Features/SSOConfiguration.spec.ts › should show OIDC Callback URL as readonly for Okta provider (shard 3)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed

Locator: locator('[id="root/authenticationConfiguration/oidcConfiguration/callbackUrl"]')
Expected: visible
Timeout: 15000ms
Error: element(s) not found

Call log:
�[2m  - Expect "toBeVisible" with timeout 15000ms�[22m
�[2m  - waiting for locator('[id="root/authenticationConfiguration/oidcConfiguration/callbackUrl"]')�[22m

Features/SSOConfiguration.spec.ts › should show OIDC Callback URL as readonly for Azure AD provider (shard 3)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed

Locator: locator('[id="root/authenticationConfiguration/oidcConfiguration/callbackUrl"]')
Expected: visible
Timeout: 15000ms
Error: element(s) not found

Call log:
�[2m  - Expect "toBeVisible" with timeout 15000ms�[22m
�[2m  - waiting for locator('[id="root/authenticationConfiguration/oidcConfiguration/callbackUrl"]')�[22m

Features/SSOConfiguration.spec.ts › should show SAML SP Entity ID and ACS URL as readonly (shard 3)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed

Locator: locator('[id="root/authenticationConfiguration/samlConfiguration/sp/entityId"]')
Expected: visible
Timeout: 15000ms
Error: element(s) not found

Call log:
�[2m  - Expect "toBeVisible" with timeout 15000ms�[22m
�[2m  - waiting for locator('[id="root/authenticationConfiguration/samlConfiguration/sp/entityId"]')�[22m

Features/SSOConfiguration.spec.ts › should display advanced config collapse for OIDC provider (shard 3)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed

Locator: locator('.sso-advanced-properties-collapse')
Expected: visible
Timeout: 15000ms
Error: element(s) not found

Call log:
�[2m  - Expect "toBeVisible" with timeout 15000ms�[22m
�[2m  - waiting for locator('.sso-advanced-properties-collapse')�[22m

Features/SSOConfiguration.spec.ts › should show advanced fields when advanced config is expanded (shard 3)
�[31mTest timeout of 60000ms exceeded.�[39m
Features/SSOConfiguration.spec.ts › should hide publicKeyUrls field for confidential OIDC providers (shard 3)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeChecked�[2m(�[22m�[2m)�[22m failed

Locator: getByRole('radio', { name: /confidential/i })
Expected: checked
Timeout: 15000ms
Error: element(s) not found

Call log:
�[2m  - Expect "toBeChecked" with timeout 15000ms�[22m
�[2m  - waiting for getByRole('radio', { name: /confidential/i })�[22m

Features/SSOConfiguration.spec.ts › should hide preferredJwsAlgorithm and responseType for OIDC providers (shard 3)
�[31mTest timeout of 60000ms exceeded.�[39m
Features/SSOConfiguration.spec.ts › should hide clientAuthenticationMethod for Auth0 provider (shard 3)
�[31mTest timeout of 60000ms exceeded.�[39m
Features/SSOConfiguration.spec.ts › should show clientAuthenticationMethod for Okta provider (shard 3)
�[31mTest timeout of 60000ms exceeded.�[39m
Features/SSOConfiguration.spec.ts › should hide tenant field for Auth0 provider (shard 3)
�[31mTest timeout of 60000ms exceeded.�[39m
Features/SSOConfiguration.spec.ts › should show tenant field for Azure provider (shard 3)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed

Locator: locator('[id="root/authenticationConfiguration/oidcConfiguration/tenant"]')
Expected: visible
Timeout: 15000ms
Error: element(s) not found

Call log:
�[2m  - Expect "toBeVisible" with timeout 15000ms�[22m
�[2m  - waiting for locator('[id="root/authenticationConfiguration/oidcConfiguration/tenant"]')�[22m

Features/SSOConfiguration.spec.ts › should collapse advanced config by default (shard 3)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).not.�[22mtoHaveClass�[2m(�[22m�[32mexpected�[39m�[2m)�[22m failed

Locator: locator('.sso-advanced-properties-collapse .ant-collapse-item')
Expected pattern: not �[32m/ant-collapse-item-active/�[39m
Timeout: 15000ms
Error: element(s) not found

Call log:
�[2m  - Expect "not toHaveClass" with timeout 15000ms�[22m
�[2m  - waiting for locator('.sso-advanced-properties-collapse .ant-collapse-item')�[22m

Features/SSOConfiguration.spec.ts › should expand and collapse advanced config when clicked (shard 3)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).not.�[22mtoHaveClass�[2m(�[22m�[32mexpected�[39m�[2m)�[22m failed

Locator: locator('.sso-advanced-properties-collapse .ant-collapse-item')
Expected pattern: not �[32m/ant-collapse-item-active/�[39m
Timeout: 15000ms
Error: element(s) not found

Call log:
�[2m  - Expect "not toHaveClass" with timeout 15000ms�[22m
�[2m  - waiting for locator('.sso-advanced-properties-collapse .ant-collapse-item')�[22m

Features/SSOConfiguration.spec.ts › should support full LDAP role mapping flow: add, fill, open roles dropdown, detect and resolve duplicates, and remove (shard 3)
�[31mTest timeout of 60000ms exceeded.�[39m
Features/SSOConfiguration.spec.ts › should render authReassignRoles as a searchable dropdown and support role selection, removal, and search filtering (shard 3)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed

Locator:  getByTestId('sso-configuration-form-array-field-template-authReassignRoles')
Expected: visible
Received: hidden
Timeout:  15000ms

Call log:
�[2m  - Expect "toBeVisible" with timeout 15000ms�[22m
�[2m  - waiting for getByTestId('sso-configuration-form-array-field-template-authReassignRoles')�[22m
�[2m    18 × locator resolved to <div data-testid="sso-configuration-form-array-field-template-authReassignRoles" class="ant-select m-t-xss w-full ant-select-multiple ant-select-allow-clear ant-select-show-search">…</div>�[22m
�[2m       - unexpected value "hidden"�[22m

Features/SSOConfiguration.spec.ts › should parse valid SAML metadata XML and populate form fields, then clear fields on invalid XML (shard 3)
Error: locator.setInputFiles: Error: Node is not an HTMLInputElement
Call log:
�[2m  - waiting for getByTestId('file-uploader')�[22m
�[2m    - locator resolved to <div tabindex="0" role="button" data-testid="file-uploader" class="saml-metadata-upload-drop-zone">…</div>�[22m

🟡 18 flaky test(s) (passed on retry)
  • Features/ActivityAPI.spec.ts › Activity event shows the actor who made the change (shard 2, 1 retry)
  • Features/BulkEditEntity.spec.ts › Glossary (shard 2, 1 retry)
  • Features/Glossary/GlossaryWorkflow.spec.ts › should start term as Draft when glossary has reviewers (shard 2, 1 retry)
  • Features/KnowledgeCenter.spec.ts › Article mentions in description should working for Knowledge Center (shard 2, 1 retry)
  • Features/KnowledgeCenterTextEditor.spec.ts › Rich Text Editor - Text Formatting (shard 2, 1 retry)
  • Features/KnowledgeCenterTextEditor.spec.ts › Rich Text Editor - Text Formatting (shard 2, 1 retry)
  • Features/KnowledgeCenterTextEditor.spec.ts › Rich Text Editor - Text Formatting (shard 2, 1 retry)
  • Features/RTL.spec.ts › Verify Following widget functionality (shard 3, 1 retry)
  • Features/Table.spec.ts › Tags term should be consistent for search (shard 3, 1 retry)
  • Features/Workflows/WorkflowOssRestrictions.spec.ts › save, cancel, and validate buttons visible; delete absent in edit mode (shard 3, 1 retry)
  • Pages/CustomProperties.spec.ts › Should display custom properties for apiCollection in right panel (shard 4, 1 retry)
  • Pages/DataContracts.spec.ts › Create Data Contract and validate for Database Schema (shard 4, 1 retry)
  • Pages/DomainUIInteractions.spec.ts › Clear domain selection returns to All Domains (shard 4, 1 retry)
  • Pages/ExplorePageRightPanel_KnowledgeCenter.spec.ts › Should remove user owner for knowledgeCenter (shard 5, 1 retry)
  • Pages/Glossary.spec.ts › Drag and Drop Glossary Term (shard 6, 1 retry)
  • Pages/Lineage/LineageFilters.spec.ts › Verify lineage schema filter selection (shard 6, 1 retry)
  • Pages/Login.spec.ts › Refresh should work (shard 6, 1 retry)
  • Pages/ODCSImportExport.spec.ts › Multi-object ODCS contract - object selector shows all schema objects (shard 6, 1 retry)

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants