Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .github/workflows/mock-idp-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,15 @@ jobs:

# RSW_VERSION is the Workbench image tag baked into the compose build
# (FROM rstudio/rstudio-workbench:${RSW_VERSION}). `release` maps to the
# `latest` tag — the stack's existing default — so the PR/newest leg is
# unchanged; pinned versions map to the `jammy-<version>` tags.
# bare `jammy` tag, which Docker Hub rolls forward with every release, so
# the PR/newest leg always tracks current without a manual bump; `latest`
# is stale (last updated 2022-08-19, years behind `jammy`) and must not be
# used. Pinned versions map to the `jammy-<version>` tags.
- name: Start mock-IdP stack (Keycloak + Connect + Workbench)
env:
RSC_LICENSE: ${{ secrets.CONNECT_LICENSE }}
RSW_LICENSE: ${{ secrets.WORKBENCH_LICENSE }}
RSW_VERSION: ${{ matrix.workbench-version == 'release' && 'latest' || format('jammy-{0}', matrix.workbench-version) }}
RSW_VERSION: ${{ matrix.workbench-version == 'release' && 'jammy' || format('jammy-{0}', matrix.workbench-version) }}
run: docker compose -f compose.mock-idp.yml up -d --build --wait

# The default `docker` driver can't export to the GHA cache backend
Expand Down
11 changes: 11 additions & 0 deletions src/vip_tests/workbench/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,17 @@ def workbench_login(
if homepage_logo.is_visible():
return

# A valid session cookie can redirect straight into a running session's IDE
# view instead of the homepage -- that view has none of Homepage's chrome, so
# the check above misses it and the login-page probe below also misses it
# (it's neither a login page nor the homepage). Same case test_sessions.py
# handles when navigating back from a session: go to /home explicitly.
if "/s/" in page.url:
page.goto(f"{workbench_url}/home")
page.wait_for_load_state("load")
if homepage_logo.is_visible():
return

# Check if we landed on a login/IdP page
if _on_login_page(page.url):
# The sign-in page renders client-side after ``load``; wait once for
Expand Down
50 changes: 34 additions & 16 deletions src/vip_tests/workbench/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,23 +53,32 @@ def _restore_session_after_signout(page: Page, workbench_url: str):


@pytest.fixture
def page(request: pytest.FixtureRequest, browser: Browser, browser_context_args: dict):
def page(
request: pytest.FixtureRequest,
browser: Browser,
browser_context_args: dict,
auth_provider: str,
):
"""Override the default page fixture for the login-form test only.

The login scenario must genuinely exercise the password login form, so it
needs a *logged-out* context: storage_state (injected by --interactive-auth
/ --headless-auth) is stripped. Every other test in this module — notably
the sign-out scenario — must stay *logged in* via that session, so they
keep storage_state. Stripping it for sign-out would leave the browser
anonymous and, under SSO, unable to re-authenticate (no password), so the
"I am logged in" precondition could never be met.
The login scenario must genuinely exercise the password login form, so
under password auth it needs a *logged-out* context: storage_state
(injected by --interactive-auth / --headless-auth) is stripped. Under
SSO/OIDC, storage_state instead carries the pre-loaded IdP session that
workbench_login's silent SSO round-trip depends on, so it must stay --
stripping it would leave the browser with no IdP session to reuse. Every
other test in this module — notably the sign-out scenario — must stay
*logged in* via that session regardless of auth provider, so they keep
storage_state too.

All other context args (TLS, CA bundle, etc.) are preserved so this page
behaves consistently with the rest of the suite. The autouse
_cleanup_sessions fixture in workbench/conftest.py uses this same page,
keeping cleanup and execution in the same context.
"""
strip_storage_state = request.node.name.startswith("test_workbench_login")
strip_storage_state = (
request.node.name.startswith("test_workbench_login") and auth_provider == "password"
)
args = {
k: v
for k, v in browser_context_args.items()
Expand All @@ -84,11 +93,7 @@ def page(request: pytest.FixtureRequest, browser: Browser, browser_context_args:


@given("Workbench is accessible at the configured URL")
def workbench_accessible(workbench_client, auth_provider: str):
# This test only validates password-based login form flow
if auth_provider != "password":
pytest.skip(f"test_auth only supports password auth, not {auth_provider!r}")

def workbench_accessible(workbench_client):
assert workbench_client is not None, "Workbench client not configured"
status = workbench_client.health()
assert status < 400, f"Workbench health-check returned HTTP {status}"
Expand All @@ -100,9 +105,22 @@ def navigate_and_login(
workbench_url: str,
test_username: str,
test_password: str,
auth_provider: str,
interactive_auth: bool,
auth_mode: str,
workbench_auth_error: str | None,
):
"""Log in using password auth form."""
workbench_login(page, workbench_url, test_username, test_password)
"""Log in using password auth form, or the real SSO round-trip under SSO/OIDC."""
workbench_login(
page,
workbench_url,
test_username,
test_password,
auth_provider,
interactive_auth,
auth_mode=auth_mode,
workbench_auth_error=workbench_auth_error,
)


@then("the Workbench homepage is displayed")
Expand Down
Loading