Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tests-only][full-ci] Add e2e tests for access token renewal flows (iframe and refresh_token) #11296

Merged
merged 12 commits into from
Aug 12, 2024
106 changes: 71 additions & 35 deletions .drone.star
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ config = {
"search",
"shares",
],
"extraServerEnvironment": {
"FRONTEND_FULL_TEXT_SEARCH_ENABLED": True,
"SEARCH_EXTRACTOR_TYPE": "tika",
"SEARCH_EXTRACTOR_TIKA_TIKA_URL": "http://tika:9998",
"SEARCH_EXTRACTOR_CS3SOURCE_INSECURE": True,
},
},
"4": {
"earlyFail": True,
Expand All @@ -90,6 +96,36 @@ config = {
"suites": [
"app-provider",
],
"extraServerEnvironment": {
"GATEWAY_GRPC_ADDR": "0.0.0.0:9142",
"MICRO_REGISTRY": "nats-js-kv",
"MICRO_REGISTRY_ADDRESS": "0.0.0.0:9233",
"NATS_NATS_HOST": "0.0.0.0",
"NATS_NATS_PORT": 9233,
"COLLABORA_DOMAIN": "collabora:9980",
"ONLYOFFICE_DOMAIN": "onlyoffice:443",
"FRONTEND_APP_HANDLER_SECURE_VIEW_APP_ADDR": "com.owncloud.api.collaboration.Collabora",
"WEB_UI_CONFIG_FILE": None,
},
},
"oidc-refresh-token": {
"skip": False,
"features": [
"cucumber/features/oidc/refreshToken.feature",
],
"extraServerEnvironment": {
"IDP_ACCESS_TOKEN_EXPIRATION": 30,
"WEB_OIDC_SCOPE": "openid profile email offline_access",
},
},
"oidc-iframe": {
"skip": False,
"features": [
"cucumber/features/oidc/iframeTokenRenewal.feature",
],
"extraServerEnvironment": {
"IDP_ACCESS_TOKEN_EXPIRATION": 30,
},
},
},
"build": True,
Expand Down Expand Up @@ -505,8 +541,10 @@ def e2eTests(ctx):
"reportTracing": "false",
"db": "mysql:5.5",
"suites": [],
"features": [],
"tikaNeeded": False,
"failOnUncaughtConsoleError": "false",
"extraServerEnvironment": {},
}

e2e_trigger = {
Expand Down Expand Up @@ -560,22 +598,31 @@ def e2eTests(ctx):
steps += collaboraService() + \
onlyofficeService() + \
waitForServices("online-offices", ["collabora:9980", "onlyoffice:443"]) + \
ocisService("app-provider") + \
ocisService(extra_env_config = params["extraServerEnvironment"]) + \
wopiCollaborationService("collabora") + \
wopiCollaborationService("onlyoffice") + \
waitForServices("wopi", ["wopi-collabora:9300", "wopi-onlyoffice:9300"])
else:
# oCIS specific steps
steps += (tikaService() if params["tikaNeeded"] else []) + \
ocisService("e2e-tests", tika_enabled = params["tikaNeeded"])
ocisService(extra_env_config = params["extraServerEnvironment"])

command = "bash run-e2e.sh "
if "suites" in matrix:
command += "--suites %s" % ",".join(params["suites"])
elif "features" in matrix:
command += "%s" % " ".join(params["features"])
else:
print("Error: No suites or features defined for e2e test suite '%s'" % suite)
return []

steps += [{
"name": "e2e-tests",
"image": OC_CI_NODEJS,
"environment": environment,
"commands": [
"cd tests/e2e",
"bash run-e2e.sh --suites %s" % ",".join(params["suites"]),
command,
],
}] + \
uploadTracingResult(ctx) + \
Expand Down Expand Up @@ -862,7 +909,7 @@ def documentation(ctx):
},
]

def ocisService(type, tika_enabled = False, enforce_password_public_link = False):
def ocisService(extra_env_config = {}, enforce_password_public_link = False):
environment = {
"IDM_ADMIN_PASSWORD": "admin", # override the random admin password from `ocis init`
"OCIS_INSECURE": "true",
Expand All @@ -876,38 +923,11 @@ def ocisService(type, tika_enabled = False, enforce_password_public_link = False
"FRONTEND_OCS_ENABLE_DENIALS": True,
"OCIS_PASSWORD_POLICY_BANNED_PASSWORDS_LIST": "%s/tests/drone/banned-passwords.txt" % dir["web"],
"PROXY_CSP_CONFIG_FILE_LOCATION": "%s/tests/drone/csp.yaml" % dir["web"],
"WEB_UI_CONFIG_FILE": "%s" % dir["ocisConfig"],
}
if type == "keycloak":
environment["PROXY_AUTOPROVISION_ACCOUNTS"] = "true"
environment["PROXY_ROLE_ASSIGNMENT_DRIVER"] = "oidc"
environment["OCIS_OIDC_ISSUER"] = "https://keycloak:8443/realms/oCIS"
environment["PROXY_OIDC_REWRITE_WELLKNOWN"] = "true"
environment["WEB_OIDC_CLIENT_ID"] = "web"
environment["PROXY_USER_OIDC_CLAIM"] = "preferred_username"
environment["PROXY_USER_CS3_CLAIM"] = "username"
environment["OCIS_ADMIN_USER_ID"] = ""
environment["OCIS_EXCLUDE_RUN_SERVICES"] = "idp"
environment["GRAPH_ASSIGN_DEFAULT_USER_ROLE"] = "false"
environment["GRAPH_USERNAME_MATCH"] = "none"
environment["KEYCLOAK_DOMAIN"] = "keycloak:8443"

if type == "app-provider":
environment["GATEWAY_GRPC_ADDR"] = "0.0.0.0:9142"
environment["MICRO_REGISTRY"] = "nats-js-kv"
environment["MICRO_REGISTRY_ADDRESS"] = "0.0.0.0:9233"
environment["NATS_NATS_HOST"] = "0.0.0.0"
environment["NATS_NATS_PORT"] = 9233
environment["COLLABORA_DOMAIN"] = "collabora:9980"
environment["ONLYOFFICE_DOMAIN"] = "onlyoffice:443"
environment["FRONTEND_APP_HANDLER_SECURE_VIEW_APP_ADDR"] = "com.owncloud.api.collaboration.Collabora"
else:
environment["WEB_UI_CONFIG_FILE"] = "%s" % dir["ocisConfig"]

if tika_enabled:
environment["FRONTEND_FULL_TEXT_SEARCH_ENABLED"] = True
environment["SEARCH_EXTRACTOR_TYPE"] = "tika"
environment["SEARCH_EXTRACTOR_TIKA_TIKA_URL"] = "http://tika:9998"
environment["SEARCH_EXTRACTOR_CS3SOURCE_INSECURE"] = True
for config in extra_env_config:
environment[config] = extra_env_config[config]

if enforce_password_public_link:
environment["OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD"] = False
Expand Down Expand Up @@ -1782,7 +1802,23 @@ def e2eTestsOnKeycloak(ctx):
else:
steps += restoreOcisCache()

steps += ocisService("keycloak") + \
# configs to setup ocis with keycloak
environment = {
"PROXY_AUTOPROVISION_ACCOUNTS": "true",
"PROXY_ROLE_ASSIGNMENT_DRIVER": "oidc",
"OCIS_OIDC_ISSUER": "https://keycloak:8443/realms/oCIS",
"PROXY_OIDC_REWRITE_WELLKNOWN": "true",
"WEB_OIDC_CLIENT_ID": "web",
"PROXY_USER_OIDC_CLAIM": "preferred_username",
"PROXY_USER_CS3_CLAIM": "username",
"OCIS_ADMIN_USER_ID": "",
"OCIS_EXCLUDE_RUN_SERVICES": "idp",
"GRAPH_ASSIGN_DEFAULT_USER_ROLE": "false",
"GRAPH_USERNAME_MATCH": "none",
"KEYCLOAK_DOMAIN": "keycloak:8443",
}

steps += ocisService(extra_env_config = environment) + \
[
{
"name": "e2e-tests",
Expand Down
3 changes: 1 addition & 2 deletions dev/docker/ocis.web.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
"metadata_url": "https://host.docker.internal:9200/.well-known/openid-configuration",
"authority": "https://host.docker.internal:9200",
"client_id": "web",
"response_type": "code",
"scope": "openid profile email"
"response_type": "code"
},
"options": {
"contextHelpersReadMore": true
Expand Down
3 changes: 1 addition & 2 deletions tests/drone/config-ocis.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
"metadata_url": "https://ocis:9200/.well-known/openid-configuration",
"authority": "https://ocis:9200",
"client_id": "web",
"response_type": "code",
"scope": "openid profile email"
"response_type": "code"
},
"apps": ["files", "text-editor", "preview", "pdf-viewer", "search", "admin-settings"]
}
1 change: 1 addition & 0 deletions tests/e2e/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ exports.config = {
slowMo: parseInt(process.env.SLOW_MO) || 0,
timeout: parseInt(process.env.TIMEOUT) || 60,
minTimeout: parseInt(process.env.MIN_TIMEOUT) || 5,
tokenTimeout: parseInt(process.env.TOKEN_TIMEOUT) || 40,
headless: process.env.HEADLESS === 'true',
acceptDownloads: process.env.DOWNLOADS !== 'false',
browser: process.env.BROWSER ?? 'chrome',
Expand Down
29 changes: 29 additions & 0 deletions tests/e2e/cucumber/features/oidc/iframeTokenRenewal.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Feature: Token renewal using iframe

As a user
I want the web application to automatically renew access token just before access token expires using an iframe in the background,
So that I can be confident the application will not encounter issues related to expired access tokens.


Scenario: access token renewal via iframe
Given "Admin" creates following users using API
| id |
| Alice |
And "Admin" assigns following roles to the users using API
| id | role |
| Alice | Space Admin |
And "Alice" logs in
And "Alice" opens the "files" app
And "Alice" navigates to the projects space page
And "Alice" creates the following project spaces
| name | id |
| team | team.1 |
When "Alice" waits for token renewal via iframe
And "Alice" navigates to the project space "team.1"
And "Alice" creates the following resources
| resource | type |
| space-folder | folder |
Then following resources should be displayed in the files list for user "Alice"
| resource |
| space-folder |
And "Alice" logs out
36 changes: 36 additions & 0 deletions tests/e2e/cucumber/features/oidc/refreshToken.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
Feature: Token renewal using refresh token

As a user
I want the web application to automatically refresh its access token using a refresh token,
So that I can be confident the application will not encounter issues related to expired access tokens.


Scenario: access token renewal via refresh token
Given "Admin" creates following users using API
| id |
| Alice |
And "Admin" assigns following roles to the users using API
| id | role |
| Alice | Space Admin |
And "Alice" logs in
And "Alice" opens the "files" app
And "Alice" navigates to the projects space page
And "Alice" creates the following project spaces
| name | id |
| team | team.1 |
When "Alice" waits for token renewal via refresh token
And "Alice" navigates to the project space "team.1"
And "Alice" creates the following resources
| resource | type |
| space-folder | folder |
Then following resources should be displayed in the files list for user "Alice"
| resource |
| space-folder |
When "Alice" navigates to new tab
And "Alice" waits for token to expire
And "Alice" closes the current tab
And "Alice" opens the "files" app
And "Alice" creates the following resources
| resource | type | content |
| PARENT/parent.txt | txtFile | some text |
saw-jan marked this conversation as resolved.
Show resolved Hide resolved
And "Alice" logs out
39 changes: 39 additions & 0 deletions tests/e2e/cucumber/steps/ui/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,42 @@ When(
await page.locator('#web').waitFor()
}
)

When(
/^"([^"]*)" waits for token renewal via (iframe|refresh token)$/,
async function (this: World, stepUser: string, renewalType: string): Promise<void> {
const { page } = this.actorsEnvironment.getActor({ key: stepUser })
const application = new objects.runtime.Application({ page })

if (renewalType === 'iframe') {
await application.waitForTokenRenewalViaIframe()
} else {
await application.waitForTokenRenewalViaRefreshToken()
}
}
)

When(
'{string} waits for token to expire',
async function (this: World, stepUser: string): Promise<void> {
const { page } = this.actorsEnvironment.getActor({ key: stepUser })
// wait for the token to expire
await page.waitForTimeout(config.tokenTimeout * 1000)
}
)

When(
'{string} navigates to new tab',
async function (this: World, stepUser: string): Promise<void> {
const actor = this.actorsEnvironment.getActor({ key: stepUser })
await actor.newTab()
}
)

When(
'{string} closes the current tab',
async function (this: World, stepUser: string): Promise<void> {
const actor = this.actorsEnvironment.getActor({ key: stepUser })
await actor.closeCurrentTab()
}
)
25 changes: 21 additions & 4 deletions tests/e2e/support/environment/actor/actor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export class ActorEnvironment extends EventEmitter implements Actor {
private readonly options: ActorOptions
public context: BrowserContext
public page: Page
public tabs: Page[] = []

constructor(options: ActorOptions) {
super()
Expand All @@ -22,6 +23,7 @@ export class ActorEnvironment extends EventEmitter implements Actor {
}

this.page = await this.context.newPage()
this.tabs.push(this.page)

this.page.on('pageerror', (exception) => {
console.log(`[UNCAUGHT EXCEPTION] "${exception}"`)
Expand All @@ -32,12 +34,27 @@ export class ActorEnvironment extends EventEmitter implements Actor {
})
}

public async newPage(newPage: Page): Promise<Page> {
// close the old page
await this.page.close()
public savePage(newPage: Page) {
this.tabs.push(newPage)
// set the new page
this.page = newPage
return this.page
}

public async newTab(): Promise<Page> {
const page: Page = await this.context.newPage()
this.tabs.push(page)
this.page = page
return page
}

public async closeCurrentTab(): Promise<void> {
await this.page.close()
this.tabs.pop()
if (this.tabs.length === 0) {
this.page = null
return
}
this.page = this.tabs[this.tabs.length - 1]
}

async close(): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/support/objects/app-files/page/shares/withMe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ export class WithMe {
this.#page.locator(openShareWithMeButton).click()
])
await newTab.locator(shareWithMeNavSelector).waitFor()
await actor.newPage(newTab)
actor.savePage(newTab)
}
}
Loading