Add UI tests for the profile page and the Wizard

This commit is contained in:
Théophile Diot 2023-12-29 16:15:08 +00:00
parent 13f477b758
commit d1d82aa300
No known key found for this signature in database
GPG key ID: 248FEA4BAE400D06
5 changed files with 306 additions and 31 deletions

View file

@ -7,9 +7,8 @@ services:
dockerfile: src/bw/Dockerfile
ports:
- 80:80
- 443:8443
environment:
SERVER_NAME: "www.example.com"
SERVER_NAME: ""
MULTISITE: "yes"
HTTP_PORT: "80"
API_WHITELIST_IP: "127.0.0.0/8 10.20.30.0/24"
@ -20,12 +19,7 @@ services:
USE_CLIENT_CACHE: "yes"
USE_GZIP: "yes"
DATASTORE_MEMORY_SIZE: "384m"
www.example.com_USE_UI: "yes"
www.example.com_SERVE_FILES: "no"
www.example.com_USE_REVERSE_PROXY: "yes"
www.example.com_REVERSE_PROXY_URL: "/admin"
www.example.com_REVERSE_PROXY_HOST: "http://bw-ui:7000"
www.example.com_INTERCEPTED_ERROR_CODES: "400 405 413 429 500 501 502 503 504"
UI_HOST: "http://bw-ui:7000"
labels:
- "bunkerweb.INSTANCE=yes"
networks:
@ -57,8 +51,6 @@ services:
volumes:
- bw-data:/data
environment:
ADMIN_USERNAME: "admin"
ADMIN_PASSWORD: "S$$cr3tP@ssw0rd"
DOCKER_HOST: "tcp://docker-proxy:2375"
networks:
- net-docker

View file

@ -6,9 +6,8 @@ services:
pull_policy: never
ports:
- 80:80
- 443:8443
environment:
SERVER_NAME: "www.example.com"
SERVER_NAME: ""
MULTISITE: "yes"
HTTP_PORT: "80"
API_WHITELIST_IP: "127.0.0.0/8 10.20.30.0/24"
@ -20,13 +19,8 @@ services:
USE_CLIENT_CACHE: "yes"
USE_GZIP: "yes"
DATASTORE_MEMORY_SIZE: "384m"
www.example.com_USE_UI: "yes"
www.example.com_SERVE_FILES: "no"
www.example.com_USE_REVERSE_PROXY: "yes"
www.example.com_REVERSE_PROXY_URL: "/admin"
www.example.com_REVERSE_PROXY_HOST: "http://bw-ui:7000"
www.example.com_INTERCEPTED_ERROR_CODES: "400 405 413 429 500 501 502 503 504"
CUSTOM_CONF_SERVER_HTTP_port-redirect: "port_in_redirect on;"
UI_HOST: "http://bw-ui:7000"
labels:
- "bunkerweb.INSTANCE=yes"
networks:
@ -54,8 +48,6 @@ services:
- bw
- bw-docker-proxy
environment:
ADMIN_USERNAME: "admin"
ADMIN_PASSWORD: "S$$cr3tP@ssw0rd"
DOCKER_HOST: "tcp://bw-docker-proxy:2375"
volumes:
- bw-data:/data

View file

@ -7,6 +7,7 @@ from pathlib import Path
from time import sleep
from traceback import format_exc
from typing import List, Union
from pyotp import TOTP
from requests import get
from requests.exceptions import RequestException
from selenium import webdriver
@ -23,7 +24,7 @@ ready = False
retries = 0
while not ready:
with suppress(RequestException):
status_code = get("http://www.example.com/admin/login").status_code
status_code = get("http://127.0.0.1/setup").status_code
if status_code > 500 and status_code != 502:
print("An error occurred with the server, exiting ...", flush=True)
@ -178,9 +179,41 @@ with driver_func() as driver:
driver.maximize_window()
driver_wait = WebDriverWait(driver, 60)
print("Navigating to http://www.example.com/admin/login ...", flush=True)
print("Navigating to http://127.0.0.1/setup ...", flush=True)
driver.get("http://www.example.com/admin/login")
driver.get("http://127.0.0.1/setup")
### WIZARD PAGE
try:
title = driver_wait.until(EC.presence_of_element_located((By.XPATH, "/html/body/main/div/div/h1")))
if title.text != "Setup Wizard":
print("Didn't get redirected to setup page, exiting ...", flush=True)
exit(1)
except TimeoutException:
print("Didn't get redirected to setup page, exiting ...", flush=True)
exit(1)
print("Setup page loaded successfully, filling the form ...", flush=True)
admin_username_input = safe_get_element(driver, By.ID, "admin_username")
password_input = safe_get_element(driver, By.ID, "admin_password")
password_check_input = safe_get_element(driver, By.ID, "admin_password_check")
ui_url = safe_get_element(driver, By.ID, "ui_url").get_attribute("value")
admin_username_input.send_keys("admin")
password_input.send_keys("S$cr3tP@ssw0rd")
password_check_input.send_keys("S$cr3tP@ssw0rd")
assert_button_click(driver, "//button[@id='setup-button']")
print("Submitted the form, waiting for the wizard to finish ...", flush=True)
current_time = datetime.now()
while current_time + timedelta(minutes=5) > datetime.now() and not driver.current_url.endswith("/login"):
sleep(1)
### LOGIN PAGE
@ -197,7 +230,7 @@ with driver_func() as driver:
flush=True,
)
driver.get("http://www.example.com/admin/home")
driver.get(f"http://www.example.com{ui_url}/home")
print("Waiting for toast ...", flush=True)
@ -508,9 +541,9 @@ with driver_func() as driver:
print("The service is not present, exiting ...", flush=True)
exit(1)
if service.find_element(By.TAG_NAME, "h6").text.strip() != "scheduler":
if service.find_element(By.TAG_NAME, "h6").text.strip() != "ui":
print(
"The service should have been created by the scheduler, exiting ...",
"The service should have been created by the ui, exiting ...",
flush=True,
)
exit(1)
@ -1260,8 +1293,8 @@ location /hello {
current_date = datetime.now()
resp = get(
f"http://www.example.com/admin/logs/{first_instance}?from_date={int(current_date.timestamp() - 86400000)}&to_date={int((current_date - timedelta(days=1)).timestamp())}",
headers={"Host": "www.example.com"},
f"http://www.example.com{ui_url}/logs/{first_instance}?from_date={int(current_date.timestamp() - 86400000)}&to_date={int((current_date - timedelta(days=1)).timestamp())}",
headers={"Host": "www.example.com", "User-Agent": driver.execute_script("return navigator.userAgent;")},
cookies={"session": driver.get_cookies()[0]["value"]},
)
@ -1396,13 +1429,173 @@ location /hello {
sleep(0.3)
resp = get("http://www.example.com/admin/jobs/download?job_name=mmdb-country&file_name=country.mmdb")
resp = get(f"http://www.example.com{ui_url}/jobs/download?job_name=mmdb-country&file_name=country.mmdb")
if resp.status_code != 200:
print("The cache download is not working, exiting ...", flush=True)
exit(1)
print("Cache download is working, trying to log out ...", flush=True)
print("Cache download is working, trying profile page ...", flush=True)
access_page(driver, driver_wait, "/html/body/aside[1]/div[1]/div[2]/ul/li[10]/a", "profile")
### PROFILE PAGE
username_input = safe_get_element(driver, By.ID, "admin_username")
if username_input.get_attribute("value") != "admin":
print("The username is not correct, exiting ...", flush=True)
exit(1)
username_input.clear()
username_input.send_keys("admin2")
password_input = safe_get_element(driver, By.ID, "curr_password")
if password_input.get_attribute("value") != "":
print("The current password is not empty, exiting ...", flush=True)
exit(1)
password_input.send_keys("S$cr3tP@ssw0rd")
assert_button_click(driver, "//button[@id='profile-button' and @class='edit-btn']")
try:
title = driver_wait.until(EC.presence_of_element_located((By.XPATH, "/html/body/main/div[1]/div/h1")))
if title.text != "Log in":
print("Didn't get redirected to login page, exiting ...", flush=True)
exit(1)
except TimeoutException:
print("Login page didn't load in time, exiting ...", flush=True)
exit(1)
print("Successfully changed username, trying to log in with new username ...", flush=True)
username_input = safe_get_element(driver, By.ID, "username")
password_input = safe_get_element(driver, By.ID, "password")
username_input.send_keys("admin2")
password_input.send_keys("S$cr3tP@ssw0rd")
access_page(
driver,
driver_wait,
"//button[@value='login']",
"profile",
)
username_input = safe_get_element(driver, By.ID, "admin_username")
if username_input.get_attribute("value") != "admin2":
print("The username is not correct, exiting ...", flush=True)
exit(1)
print("Successfully logged in with new username, trying to change password ...", flush=True)
password_input = safe_get_element(driver, By.ID, "curr_password")
if password_input.get_attribute("value") != "":
print("The current password is not empty, exiting ...", flush=True)
exit(1)
password_input.send_keys("S$cr3tP@ssw0rd")
new_password_input = safe_get_element(driver, By.ID, "admin_password")
if new_password_input.get_attribute("value") != "":
print("The new password is not empty, exiting ...", flush=True)
exit(1)
new_password_input.send_keys("P@ssw0rd")
new_password_check_input = safe_get_element(driver, By.ID, "admin_password_check")
if new_password_check_input.get_attribute("value") != "":
print("The new password check is not empty, exiting ...", flush=True)
exit(1)
new_password_check_input.send_keys("P@ssw0rd")
assert_button_click(driver, "//button[@id='profile-button' and @class='edit-btn']")
try:
title = driver_wait.until(EC.presence_of_element_located((By.XPATH, "/html/body/main/div[1]/div/h1")))
if title.text != "Log in":
print("Didn't get redirected to login page, exiting ...", flush=True)
exit(1)
except TimeoutException:
print("Login page didn't load in time, exiting ...", flush=True)
exit(1)
print("Successfully changed username, trying to log in with new password ...", flush=True)
username_input = safe_get_element(driver, By.ID, "username")
password_input = safe_get_element(driver, By.ID, "password")
username_input.send_keys("admin2")
password_input.send_keys("P@ssw0rd")
access_page(
driver,
driver_wait,
"//button[@value='login']",
"profile",
)
print("Successfully logged in with new password, trying 2FA ...", flush=True)
assert_button_click(driver, "//button[@data-tab-handler='totp']")
secret_token_input = safe_get_element(driver, By.ID, "secret_token")
secret_token = secret_token_input.get_attribute("value")
driver.refresh()
driver_wait.until(EC.presence_of_element_located((By.XPATH, "/html/body/div/header/div/nav/h6")))
assert_button_click(driver, "//button[@data-tab-handler='totp']")
secret_token_input = safe_get_element(driver, By.ID, "secret_token")
new_secret_token = secret_token_input.get_attribute("value")
if new_secret_token == secret_token:
print("The secret token hasn't been changed, exiting ...", flush=True)
exit(1)
print("The secret token has been changed, trying to activate 2FA ...", flush=True)
totp = TOTP(new_secret_token)
totp_input = safe_get_element(driver, By.ID, "totp_token")
totp_input.send_keys(totp.now())
password_input = safe_get_element(driver, By.ID, "totp_password")
if password_input.get_attribute("value") != "":
print("The new password check is not empty, exiting ...", flush=True)
exit(1)
password_input.send_keys("P@ssw0rd")
access_page(
driver,
driver_wait,
"//button[@id='profile-button' and @class='valid-btn']",
"profile",
)
assert_button_click(driver, "//button[@data-tab-handler='totp']")
try:
totp_state = safe_get_element(driver, By.XPATH, "/html/body/main/div/div/form[2]/h5")
if totp_state.text != "TOTP IS CURRENTLY ON":
print("TOTP is not activated, exiting ...", flush=True)
exit(1)
except TimeoutException:
print("TOTP has not been activated, exiting ...", flush=True)
exit(1)
print("2FA has been activated, trying to log out ...", flush=True)
assert_button_click(driver, "//a[@href='logout']")
@ -1416,7 +1609,100 @@ location /hello {
print("Login page didn't load in time, exiting ...", flush=True)
exit(1)
print("Successfully logged out, tests are done", flush=True)
print("Successfully logged out, trying to log in with 2FA ...", flush=True)
username_input = safe_get_element(driver, By.ID, "username")
password_input = safe_get_element(driver, By.ID, "password")
username_input.send_keys("admin2")
password_input.send_keys("P@ssw0rd")
assert_button_click(driver, "//button[@value='login']")
try:
totp_input = safe_get_element(driver, By.ID, "totp_token")
except TimeoutException:
print("Didn't get redirected to 2FA page, exiting ...", flush=True)
exit(1)
totp_input.send_keys("0000000")
assert_button_click(driver, "//button[@value='login']")
sleep(5)
if not driver.current_url.endswith("/totp"):
print("Didn't get redirected back to 2FA page, exiting ...", flush=True)
exit(1)
totp_input = safe_get_element(driver, By.ID, "totp_token")
totp_input.send_keys(totp.now())
access_page(
driver,
driver_wait,
"//button[@value='login']",
"home",
)
print("Successfully logged in with 2FA, trying to deactivate 2FA ...", flush=True)
access_page(driver, driver_wait, "/html/body/aside[1]/div[1]/div[2]/ul/li[10]/a", "profile")
assert_button_click(driver, "//button[@data-tab-handler='totp']")
totp_input = safe_get_element(driver, By.ID, "totp_token")
totp_input.send_keys(totp.now())
password_input = safe_get_element(driver, By.ID, "totp_password")
password_input.send_keys("P@ssw0rd")
access_page(
driver,
driver_wait,
"//button[@id='profile-button' and @class='delete-btn']",
"profile",
)
assert_button_click(driver, "//button[@data-tab-handler='totp']")
try:
totp_state = safe_get_element(driver, By.XPATH, "/html/body/main/div/div/form[2]/h5")
if totp_state.text != "TOTP IS CURRENTLY OFF":
print("TOTP is not deactivated, exiting ...", flush=True)
exit(1)
except TimeoutException:
print("TOTP has not been deactivated, exiting ...", flush=True)
exit(1)
print("2FA has been deactivated, trying to log out ...", flush=True)
assert_button_click(driver, "//a[@href='logout']")
try:
title = driver_wait.until(EC.presence_of_element_located((By.XPATH, "/html/body/main/div[1]/div/h1")))
if title.text != "Log in":
print("Didn't get redirected to login page, exiting ...", flush=True)
exit(1)
except TimeoutException:
print("Login page didn't load in time, exiting ...", flush=True)
exit(1)
print("Successfully logged out, trying to log in without 2FA ...", flush=True)
username_input = safe_get_element(driver, By.ID, "username")
password_input = safe_get_element(driver, By.ID, "password")
username_input.send_keys("admin2")
password_input.send_keys("P@ssw0rd")
access_page(
driver,
driver_wait,
"//button[@value='login']",
"home",
)
print("Successfully logged in without 2FA, tests are done, exiting ...", flush=True)
except SystemExit:
exit(1)
except:

View file

@ -1,2 +1,3 @@
pyotp==2.9.0
requests==2.31.0
selenium==4.16.0

View file

@ -128,6 +128,10 @@ outcome==1.3.0.post0 \
--hash=sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8 \
--hash=sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b
# via trio
pyotp==2.9.0 \
--hash=sha256:346b6642e0dbdde3b4ff5a930b664ca82abfa116356ed48cc42c7d6590d36f63 \
--hash=sha256:81c2e5865b8ac55e825b0358e496e1d9387c811e85bb40e71a3b29b288963612
# via -r requirements.in
pysocks==1.7.1 \
--hash=sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299 \
--hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \