Merge pull request #889 from bunkerity/dev

Merge banch "dev" into branch "ui"
This commit is contained in:
Théophile Diot 2024-01-25 10:49:51 +01:00 committed by GitHub
commit 07dce6a265
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 525 additions and 7351 deletions

View file

@ -170,6 +170,10 @@ Another core component of BunkerWeb is the ModSecurity Web Application Firewall
## Database
<p align="center">
<img alt="Database model" src="https://github.com/bunkerity/bunkerweb/raw/v1.5.5/docs/assets/img/bunkerweb_db.svg" />
</p>
State of the current configuration of BunkerWeb is stored in a backend database which contains the following data :
- Settings defined for all the services

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 903 KiB

View file

@ -1,10 +1,12 @@
# Quickstart guide
!!! info "Prerequisites"
We assume that you're already familiar with the [core concepts](concepts.md) and you have followed the [integrations instructions](integrations.md) for your environment.
!!! tip "Going further"
To demonstrate the use of BunkerWeb, we will deploy a dummy "Hello World" web application as an example. See the [examples folder](https://github.com/bunkerity/bunkerweb/tree/v1.5.5/examples) of the repository to get real-world examples.
To demonstrate the use of BunkerWeb, we will deploy a dummy "Hello World" web application as an example. See the [examples folder](https://github.com/bunkerity/bunkerweb/tree/v1.5.5/examples) of the repository to get real-world examples.
## Protect HTTP applications
@ -1117,7 +1119,8 @@ REAL_IP_HEADER=proxy_protocol
## Protect UDP/TCP applications
!!! warning "Feature is in beta"
This feature is not production-ready. Feel free to test it and report us any bug using [issues](https://github.com/bunkerity/bunkerweb/issues) in the GitHub repository.
This feature is not production-ready. Feel free to test it and report us any bug using [issues](https://github.com/bunkerity/bunkerweb/issues) in the GitHub repository.
BunkerWeb offers the capability to function as a **generic UDP/TCP reverse proxy**, allowing you to protect any network-based applications operating at least on layer 4 of the OSI model. Instead of utilizing the "classical" HTTP module, BunkerWeb leverages the [stream module](https://nginx.org/en/docs/stream/ngx_stream_core_module.html) of NGINX.
@ -2329,7 +2332,8 @@ BunkerWeb supports PHP using external or remote [PHP-FPM](https://www.php.net/ma
## IPv6
!!! warning "Feature is in beta"
This feature is not production-ready. Feel free to test it and report us any bug using [issues](https://github.com/bunkerity/bunkerweb/issues) in the GitHub repository.
This feature is not production-ready. Feel free to test it and report us any bug using [issues](https://github.com/bunkerity/bunkerweb/issues) in the GitHub repository.
By default, BunkerWeb will only listen on IPv4 addresses and won't use IPv6 for network communications. If you want to enable IPv6 support, you need to set `USE_IPV6=yes`. Please note that IPv6 configuration of your network and environment is out-of-the-scope of this documentation.

View file

@ -636,6 +636,7 @@ Review your final BunkerWeb UI URL and then click on the `Setup` button. Once th
labels:
app: bunkerweb-ui
spec:
serviceAccountName: sa-bunkerweb
containers:
- name: bunkerweb-ui
image: bunkerity/bunkerweb-ui:1.5.5
@ -1475,6 +1476,7 @@ After a successful login/password combination, you will be prompted to enter you
labels:
app: bunkerweb-ui
spec:
serviceAccountName: sa-bunkerweb
containers:
- name: bunkerweb-ui
image: bunkerity/bunkerweb-ui:1.5.5

View file

@ -317,7 +317,7 @@ utils.get_reason = function(ctx)
end
local banned, _ = datastore:get("bans_ip_" .. ip)
if banned then
return banned, {}
return decode(banned)["reason"], {}
end
-- unknown
if ngx.status == utils.get_deny_status() then
@ -645,9 +645,9 @@ utils.is_banned = function(ip)
local ok, ttl = datastore:ttl("bans_ip_" .. ip)
local ban_data = decode(result)
if not ok then
return true, ban_data, -1
return true, ban_data["reason"], -1
end
return true, ban_data, ttl
return true, ban_data["reason"], ttl
end
-- Redis case
local use_redis, err = utils.get_variable("USE_REDIS", false)
@ -694,7 +694,7 @@ utils.is_banned = function(ip)
if not ok then
return nil, "datastore:set() error : " .. err
end
return true, data[1], data[2]
return true, decode(data[1])["reason"], data[2]
end
clusterstore:close()
return false, "not banned"

View file

@ -13,6 +13,16 @@
"label": "The database URI",
"regex": "^(postgresql|mysql|mariadb|sqlite|oracle)(\\+[\\w\\-]+)?:.+$",
"type": "text"
},
"DATABASE_LOG_LEVEL": {
"context": "global",
"default": "warning",
"help": "The level to use for database logs.",
"id": "database-log-level",
"label": "Database log level",
"regex": "^(debug|info|warn|warning|error)$",
"type": "select",
"select": ["debug", "info", "warn", "warning", "error"]
}
}
}

View file

@ -1,6 +1,6 @@
local cjson = require "cjson"
local class = require "middleclass"
local datastore = require "datastore"
local datastore = require "bunkerweb.datastore"
local plugin = require "bunkerweb.plugin"
local utils = require "bunkerweb.utils"
@ -50,12 +50,12 @@ function metrics:log()
end
end
local request = {
date = self.ctx.bw.local_time,
date = os.time(),
ip = self.ctx.bw.remote_addr,
country = country,
method = self.ctx.bw.request_method,
url = self.ctx.bw.request_uri,
code = ngx.status,
status = ngx.status,
["user-agent"] = self.ctx.bw.http_user_agent or "",
reason = reason,
data = data,

View file

@ -30,12 +30,14 @@ basicConfig(
level=default_level,
)
getLogger("sqlalchemy.orm.mapper.Mapper").setLevel(default_level if default_level != INFO else WARNING)
getLogger("sqlalchemy.orm.relationships.RelationshipProperty").setLevel(default_level if default_level != INFO else WARNING)
getLogger("sqlalchemy.orm.strategies.LazyLoader").setLevel(default_level if default_level != INFO else WARNING)
getLogger("sqlalchemy.pool.impl.QueuePool").setLevel(default_level if default_level != INFO else WARNING)
getLogger("sqlalchemy.pool.impl.SingletonThreadPool").setLevel(default_level if default_level != INFO else WARNING)
getLogger("sqlalchemy.engine.Engine").setLevel(default_level if default_level != INFO else WARNING)
database_default_level = _nameToLevel.get(getenv("DATABASE_LOG_LEVEL", "WARNING").upper(), WARNING)
getLogger("sqlalchemy.orm.mapper.Mapper").setLevel(database_default_level)
getLogger("sqlalchemy.orm.relationships.RelationshipProperty").setLevel(database_default_level)
getLogger("sqlalchemy.orm.strategies.LazyLoader").setLevel(database_default_level)
getLogger("sqlalchemy.pool.impl.QueuePool").setLevel(database_default_level)
getLogger("sqlalchemy.pool.impl.SingletonThreadPool").setLevel(database_default_level)
getLogger("sqlalchemy.engine.Engine").setLevel(database_default_level)
# Edit the default levels of the logging module
addLevelName(CRITICAL, "🚨")

View file

@ -31,6 +31,7 @@ from jinja2 import Template
from kubernetes import client as kube_client
from kubernetes import config as kube_config
from kubernetes.client.exceptions import ApiException as kube_ApiException
from redis import Redis, Sentinel
from regex import compile as re_compile, match as regex_match
from requests import get
from shutil import move, rmtree
@ -1599,47 +1600,15 @@ def logs_container(container_id):
@app.route("/reports", methods=["GET"])
@login_required
def reports():
# TODO : Get block requests from database to send it
reports = [
{
"user_agent": "Version 0.6.1 - Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.5a) Gecko/20030728 Mozilla Firebird/0.6.1",
"ip": "124.0.0.1",
"country": "FR",
"url": "/test",
"date": "12/51/9851",
"reason": "antibot",
"method": "GET",
"status": 403,
"data": "{fesfmk fesfsf sfesfes}",
},
{
"user_agent": "Version 0.6.1 - Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.5a) Gecko/20030728 Mozilla Firebird/0.6.1",
"ip": "124.0.0.2",
"country": "EN",
"url": "/test",
"date": "12/51/9851",
"reason": "test",
"method": "GET",
"status": 403,
"data": "{fesfmk fesfsf sfesfes}",
},
{
"user_agent": "Version 0.6.1 - Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.5a) Gecko/20030728 Mozilla Firebird/0.6.1",
"ip": "124.0.0.3",
"country": "ES",
"url": "/test",
"date": "12/51/9851",
"reason": "antibot",
"method": "GET",
"status": 403,
"data": "{fesfmk fesfsf sfesfes}",
},
]
reports = app.config["INSTANCES"].get_reports()
total_reports = len(reports)
reports = reports[:100]
# Prepare data
reasons = {}
codes = {}
for report in reports:
for i, report in enumerate(deepcopy(reports)):
reports[i]["date"] = datetime.fromtimestamp(floor(reports[i]["date"])).strftime("%d/%m/%Y %H:%M:%S")
# Get top reasons
if not report["reason"] in reasons:
reasons[report["reason"]] = 0
@ -1649,12 +1618,13 @@ def reports():
codes[report["status"]] = 0
codes[report["status"]] = codes[report["status"]] + 1
top_reason = [k for k, v in reasons.items() if v == max(reasons.values())][0]
top_code = [k for k, v in codes.items() if v == max(codes.values())][0]
top_reason = ([k for k, v in reasons.items() if v == max(reasons.values())] or [""])[0]
top_code = ([k for k, v in codes.items() if v == max(codes.values())] or [""])[0]
return render_template(
"reports.html",
reports=reports,
total_reports=total_reports,
top_code=top_code,
top_reason=top_reason,
username=current_user.get_id(),
@ -1665,6 +1635,76 @@ def reports():
@app.route("/bans", methods=["GET", "POST"])
@login_required
def bans():
redis_client = None
db_config = app.config["CONFIG"].get_config(methods=False)
use_redis = db_config.get("USE_REDIS", "no") == "yes"
redis_host = db_config.get("REDIS_HOST")
if use_redis and redis_host:
redis_port = db_config.get("REDIS_PORT", "6379")
if not redis_port.isdigit():
redis_port = "6379"
redis_port = int(redis_port)
redis_db = db_config.get("REDIS_DB", "0")
if not redis_db.isdigit():
redis_db = "0"
redis_db = int(redis_db)
redis_timeout = db_config.get("REDIS_TIMEOUT", "1000.0")
try:
redis_timeout = float(redis_timeout)
except ValueError:
redis_timeout = 1000.0
redis_keepalive_pool = db_config.get("REDIS_KEEPALIVE_POOL", "10")
if not redis_keepalive_pool.isdigit():
redis_keepalive_pool = "10"
redis_keepalive_pool = int(redis_keepalive_pool)
redis_ssl = db_config.get("REDIS_SSL", "no") == "yes"
username = db_config.get("REDIS_USERNAME", None) or None
password = db_config.get("REDIS_PASSWORD", None) or None
sentinel_hosts = db_config.get("REDIS_SENTINEL_HOSTS", [])
if isinstance(sentinel_hosts, str):
sentinel_hosts = [host.split(":") if ":" in host else host for host in sentinel_hosts.split(" ") if host]
if sentinel_hosts:
sentinel_username = db_config.get("REDIS_SENTINEL_USERNAME", None) or None
sentinel_password = db_config.get("REDIS_SENTINEL_PASSWORD", None) or None
sentinel_master = db_config.get("REDIS_SENTINEL_MASTER", "")
sentinel = Sentinel(
sentinel_hosts,
username=sentinel_username,
password=sentinel_password,
ssl=redis_ssl,
socket_timeout=redis_timeout,
socket_connect_timeout=redis_timeout,
socket_keepalive=True,
max_connections=redis_keepalive_pool,
)
redis_client = sentinel.slave_for(sentinel_master, db=redis_db, username=username, password=password)
else:
redis_client = Redis(
host=redis_host,
port=redis_port,
db=redis_db,
username=username,
password=password,
socket_timeout=redis_timeout,
socket_connect_timeout=redis_timeout,
socket_keepalive=True,
max_connections=redis_keepalive_pool,
ssl=redis_ssl,
)
try:
redis_client.ping()
except BaseException:
redis_client = None
flash("Couldn't connect to redis, ban list might be incomplete", "error")
if request.method == "POST":
# Check variables
if not request.form:
@ -1683,6 +1723,7 @@ def bans():
data = json_loads(request.form["data"])
assert isinstance(data, list)
except BaseException:
app.logger.exception(f"Couldn't load data: {request.form['data']}")
flash("Data must be a list of dict", "error")
return redirect(url_for("bans"))
@ -1691,9 +1732,18 @@ def bans():
try:
unban = json_loads(unban.replace('"', '"').replace("'", '"'))
except BaseException:
flash(f"Invalid unban: {unban}, skipping it ...", "error")
app.logger.exception(f"Couldn't unban {unban['ip']}")
continue
if "ip" not in unban:
flash(f"Invalid unban: {unban}, skipping it ...", "error")
continue
if redis_client:
if not redis_client.delete(f"bans_ip_{unban['ip']}"):
flash(f"Couldn't unban {unban['ip']} on redis", "error")
resp = app.config["INSTANCES"].unban(unban["ip"])
if resp:
flash(f"Couldn't unban {unban['ip']} on the following instances: {', '.join(resp)}", "error")
@ -1701,17 +1751,26 @@ def bans():
flash(f"Successfully unbanned {unban['ip']}")
elif request.form["operation"] == "ban":
for ban in data:
try:
ban = json_loads(ban.replace('"', '"').replace("'", '"'))
except BaseException:
if not isinstance(ban, dict) or "ip" not in ban:
flash(f"Invalid ban: {ban}, skipping it ...", "error")
continue
if "ip" not in ban:
continue
try:
ban_end = float(ban.get("ban_end", 86400))
except BaseException:
continue
resp = app.config["INSTANCES"].ban(ban["ip"], ban_end, ban.get("reason", "manual"))
reason = ban.get("reason", "ui")
ban_end = 86400.0
if "ban_end" in ban:
try:
ban_end = float(ban["ban_end"])
except ValueError:
continue
ban_end = (datetime.fromtimestamp(ban_end) - datetime.now()).total_seconds()
if redis_client:
ok = redis_client.set(f"bans_ip_{ban['ip']}", dumps({"reason": reason, "date": time()}))
if not ok:
flash(f"Couldn't ban {ban['ip']} on redis", "error")
redis_client.expire(f"bans_ip_{ban['ip']}", int(ban_end))
resp = app.config["INSTANCES"].ban(ban["ip"], ban_end, reason)
if resp:
flash(f"Couldn't ban {ban['ip']} on the following instances: {', '.join(resp)}", "error")
else:
@ -1722,12 +1781,27 @@ def bans():
return redirect(url_for("loading", next=url_for("bans"), message="Update bans"))
bans = app.config["INSTANCES"].get_bans()[:100]
bans = []
if redis_client:
for key in redis_client.scan_iter("bans_ip_*"):
ip = key.decode("utf-8").replace("bans_ip_", "")
data = redis_client.get(key)
if not data:
continue
exp = redis_client.ttl(key)
bans.append({"ip": ip, "exp": exp} | json_loads(data)) # type: ignore
instance_bans = app.config["INSTANCES"].get_bans()
# Prepare data
reasons = {}
timestamp_now = time()
for ban in instance_bans:
if not any(b["ip"] == ban["ip"] for b in bans):
bans.append(ban)
bans = bans[:100]
for ban in bans:
exp = ban.pop("exp")
# Add remain

View file

@ -115,6 +115,9 @@ class Instance:
def unban(self, ip: str) -> bool:
return self.apiCaller.send_to_apis("POST", "/unban", data={"ip": ip})
def reports(self) -> Tuple[bool, dict[str, Any]]:
return self.apiCaller.send_to_apis("GET", "/metrics/requests", response=True)
class Instances:
def __init__(self, docker_client, kubernetes_client, integration: str):
@ -342,3 +345,22 @@ class Instances:
return f"Can't unban {ip} on {instance.name}"
return [instance.name for instance in self.get_instances() if not instance.unban(ip)]
def get_reports(self, _id: Optional[int] = None) -> List[dict[str, Any]]:
if _id:
instance = self.__instance_from_id(_id)
resp, instance_reports = instance.reports()
if not resp:
return []
return instance_reports[instance.name].get("msg", [])
reports: List[dict[str, Any]] = []
for instance in self.get_instances():
resp, instance_reports = instance.reports()
if not resp:
continue
reports.extend(instance_reports[instance.name].get("msg", []))
reports.sort(key=lambda x: x["date"], reverse=True)
return reports

View file

@ -1,24 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/images/favicon.ico" />
<link rel="stylesheet" href="/css/style.css" />
<link rel="stylesheet" href="/css/flag-icons.min.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BunkerWeb | Account</title>
<script
type="module"
crossorigin
src="/assets/account-ba9110db.js"
></script>
<link rel="modulepreload" crossorigin href="/assets/lang-f5f8ee65.js" />
<link rel="modulepreload" crossorigin href="/assets/State-99778106.js" />
<link rel="modulepreload" crossorigin href="/assets/Base-fc8c7eae.js" />
<link rel="modulepreload" crossorigin href="/assets/Input-294600dd.js" />
<link rel="stylesheet" href="/assets/State-26c5bb69.css" />
</head>
<body>
<div id="app"></div>
</body>
</html>

View file

@ -1,32 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/images/favicon.ico" />
<link rel="stylesheet" href="/css/style.css" />
<link rel="stylesheet" href="/css/flag-icons.min.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BunkerWeb | Actions</title>
<script
type="module"
crossorigin
src="/assets/actions-43d7100c.js"
></script>
<link rel="modulepreload" crossorigin href="/assets/lang-f5f8ee65.js" />
<link rel="modulepreload" crossorigin href="/assets/State-99778106.js" />
<link rel="modulepreload" crossorigin href="/assets/List-82cc034b.js" />
<link rel="modulepreload" crossorigin href="/assets/Input-294600dd.js" />
<link rel="modulepreload" crossorigin href="/assets/Select-55aa1b49.js" />
<link rel="modulepreload" crossorigin href="/assets/Item-e1d34516.js" />
<link
rel="modulepreload"
crossorigin
href="/assets/Datepicker-349b9bba.js"
/>
<link rel="stylesheet" href="/assets/State-26c5bb69.css" />
<link rel="stylesheet" href="/assets/Datepicker-b3d9355d.css" />
</head>
<body>
<div id="app"></div>
</body>
</html>

View file

@ -1,31 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/images/favicon.ico" />
<link rel="stylesheet" href="/css/style.css" />
<link rel="stylesheet" href="/css/flag-icons.min.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BunkerWeb | Bans</title>
<script type="module" crossorigin src="/assets/bans-04a6a92e.js"></script>
<link rel="modulepreload" crossorigin href="/assets/lang-f5f8ee65.js" />
<link rel="modulepreload" crossorigin href="/assets/State-99778106.js" />
<link rel="modulepreload" crossorigin href="/assets/Input-294600dd.js" />
<link rel="modulepreload" crossorigin href="/assets/Select-55aa1b49.js" />
<link rel="modulepreload" crossorigin href="/assets/List-82cc034b.js" />
<link rel="modulepreload" crossorigin href="/assets/Item-e1d34516.js" />
<link
rel="modulepreload"
crossorigin
href="/assets/Datepicker-349b9bba.js"
/>
<link rel="modulepreload" crossorigin href="/assets/Warning-42bddb5b.js" />
<link rel="modulepreload" crossorigin href="/assets/Base-fc8c7eae.js" />
<link rel="modulepreload" crossorigin href="/assets/Checkbox-a81c9afa.js" />
<link rel="stylesheet" href="/assets/State-26c5bb69.css" />
<link rel="stylesheet" href="/assets/Datepicker-b3d9355d.css" />
</head>
<body>
<div id="app"></div>
</body>
</html>

View file

@ -1,28 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/images/favicon.ico" />
<link rel="stylesheet" href="/css/style.css" />
<link rel="stylesheet" href="/css/flag-icons.min.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BunkerWeb | Configs</title>
<script
type="module"
crossorigin
src="/assets/configs-86035b54.js"
></script>
<link rel="modulepreload" crossorigin href="/assets/lang-f5f8ee65.js" />
<link rel="modulepreload" crossorigin href="/assets/State-99778106.js" />
<link rel="modulepreload" crossorigin href="/assets/List-82cc034b.js" />
<link rel="modulepreload" crossorigin href="/assets/Input-294600dd.js" />
<link rel="modulepreload" crossorigin href="/assets/Checkbox-a81c9afa.js" />
<link rel="modulepreload" crossorigin href="/assets/Base-27eb0461.js" />
<link rel="modulepreload" crossorigin href="/assets/Base-fc8c7eae.js" />
<link rel="modulepreload" crossorigin href="/assets/v4-4a60fe23.js" />
<link rel="stylesheet" href="/assets/State-26c5bb69.css" />
</head>
<body>
<div id="app"></div>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -21,7 +21,7 @@ class Filter {
setTimeout(() => {
const value = document
.querySelector(
`[data-${this.prefix}-setting-select-text="reason"]`,
`[data-${this.prefix}-setting-select-text="reason"]`
)
.textContent.trim();
@ -159,7 +159,7 @@ class Dropdown {
const btn = e.target.closest("button");
const btnValue = btn.getAttribute("value");
const btnSetting = btn.getAttribute(
`data-${this.prefix}-setting-select-dropdown-btn`,
`data-${this.prefix}-setting-select-dropdown-btn`
);
//stop if same value to avoid new fetching
const isSameVal = this.isSameValue(btnSetting, btnValue);
@ -185,7 +185,7 @@ class Dropdown {
closeAllDrop() {
const drops = document.querySelectorAll(
`[data-${this.prefix}-setting-select-dropdown]`,
`[data-${this.prefix}-setting-select-dropdown]`
);
drops.forEach((drop) => {
drop.classList.add("hidden");
@ -193,8 +193,8 @@ class Dropdown {
document
.querySelector(
`svg[data-${this.prefix}-setting-select="${drop.getAttribute(
`data-${this.prefix}-setting-select-dropdown`,
)}"]`,
`data-${this.prefix}-setting-select-dropdown`
)}"]`
)
.classList.remove("rotate-180");
});
@ -202,7 +202,7 @@ class Dropdown {
isSameValue(btnSetting, value) {
const selectCustom = document.querySelector(
`[data-${this.prefix}-setting-select-text="${btnSetting}"]`,
`[data-${this.prefix}-setting-select-text="${btnSetting}"]`
);
const currVal = selectCustom.textContent;
return currVal === value ? true : false;
@ -210,30 +210,30 @@ class Dropdown {
setSelectNewValue(btnSetting, value) {
const selectCustom = document.querySelector(
`[data-${this.prefix}-setting-select="${btnSetting}"]`,
`[data-${this.prefix}-setting-select="${btnSetting}"]`
);
selectCustom.querySelector(
`[data-${this.prefix}-setting-select-text]`,
`[data-${this.prefix}-setting-select-text]`
).textContent = value;
}
hideDropdown(btnSetting) {
//hide dropdown
const dropdownEl = document.querySelector(
`[data-${this.prefix}-setting-select-dropdown="${btnSetting}"]`,
`[data-${this.prefix}-setting-select-dropdown="${btnSetting}"]`
);
dropdownEl.classList.add("hidden");
dropdownEl.classList.remove("flex");
//svg effect
const dropdownChevron = document.querySelector(
`svg[data-${this.prefix}-setting-select="${btnSetting}"]`,
`svg[data-${this.prefix}-setting-select="${btnSetting}"]`
);
dropdownChevron.classList.remove("rotate-180");
}
changeDropBtnStyle(btnSetting, selectedBtn) {
const dropdownEl = document.querySelector(
`[data-${this.prefix}-setting-select-dropdown="${btnSetting}"]`,
`[data-${this.prefix}-setting-select-dropdown="${btnSetting}"]`
);
//reset dropdown btns
const btnEls = dropdownEl.querySelectorAll("button");
@ -243,7 +243,7 @@ class Dropdown {
"bg-primary",
"dark:bg-primary",
"text-gray-300",
"text-gray-300",
"text-gray-300"
);
btn.classList.add("bg-white", "dark:bg-slate-700", "text-gray-700");
});
@ -251,7 +251,7 @@ class Dropdown {
selectedBtn.classList.remove(
"bg-white",
"dark:bg-slate-700",
"text-gray-700",
"text-gray-700"
);
selectedBtn.classList.add("dark:bg-primary", "bg-primary", "text-gray-300");
}
@ -262,10 +262,10 @@ class Dropdown {
.getAttribute(`data-${this.prefix}-setting-select`);
//toggle dropdown
const dropdownEl = document.querySelector(
`[data-${this.prefix}-setting-select-dropdown="${attribute}"]`,
`[data-${this.prefix}-setting-select-dropdown="${attribute}"]`
);
const dropdownChevron = document.querySelector(
`svg[data-${this.prefix}-setting-select="${attribute}"]`,
`svg[data-${this.prefix}-setting-select="${attribute}"]`
);
dropdownEl.classList.toggle("hidden");
dropdownEl.classList.toggle("flex");
@ -318,7 +318,7 @@ class Unban {
setTimeout(() => {
// Check if at least one item is selected
const selected = this.listEl.querySelectorAll(
`input[data-checked="true"]`,
`input[data-checked="true"]`
);
// Case true, enable unban button
@ -340,7 +340,7 @@ class Unban {
if (this.unbanBtn.hasAttribute("disabled")) return;
// Get all selected items
const selected = this.listEl.querySelectorAll(
`input[data-checked="true"]`,
`input[data-checked="true"]`
);
const getDatas = [];
selected.forEach((el) => {
@ -366,10 +366,10 @@ class AddBanModal {
this.listEl = document.querySelector(`[data-bans-add-ban-list]`);
this.submitBtn = document.querySelector(`button[data-bans-modal-submit]`);
this.removeAllFieldBtn = document.querySelector(
"button[data-add-ban-delete-all-item]",
"button[data-add-ban-delete-all-item]"
);
this.formEl = document.querySelector("form[data-ban-add-form]");
this.itemCount = 1;
this.itemCount = 0;
this.setDatepicker("0"); // for default field
this.init();
}
@ -437,8 +437,8 @@ class AddBanModal {
reason: "ui",
});
});
console.log(data);
this.addBanInp.setAttribute("value", data);
this.addBanInp.setAttribute("value", JSON.stringify(data));
this.addBanInp.value = JSON.stringify(data);
this.formEl.submit();
});
}
@ -502,17 +502,33 @@ class AddBanModal {
/>
</div>
<div class="mx-1.5 col-span-5">
<label for="ban-end-${this.itemCount}" class="sr-only">Ban end</label>
<input
data-bans-add-ban-end
type="text"
id="ban-end-${this.itemCount}"
name="ban-end-${this.itemCount}"
class="dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-3 py-1 font-normal text-gray-700 transition-all placeholder:text-gray-500"
placeholder="01/01/2021 00:00:00"
pattern="(.*?)"
required
/>
<label for="ban-end-ban-end-${this.itemCount}" class="sr-only">Ban end</label>
<div class="relative">
<input
data-bans-add-ban-end
type="text"
id="ban-end-${this.itemCount}"
name="ban-end-${this.itemCount}"
class="dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-3 py-1 font-normal text-gray-700 transition-all placeholder:text-gray-500"
placeholder="01/01/2021 00:00:00"
pattern="(.*?)"
required
/>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="pointer-events-none absolute top-1 right-2 w-6 h-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5"
/>
</svg>
</div>
</div>
<div class="mx-1.5 col-span-2 flex justify-center items-center">
<button
@ -543,26 +559,30 @@ class AddBanModal {
}
setDatepicker(id) {
const defaultDate = +(Date.now() + 3600000 * 24)
.toString()
.substring(0, 10);
const inpEl = document.querySelector(`input#ban-end-${id}`);
inpEl.setAttribute("data-timestamp", defaultDate);
// instantiate datepicker
const dateOptions = {
locale: "en",
dateFormat: "m/d/Y H:i:S",
defaultDate: false,
defaultDate: defaultDate,
enableTime: true,
enableSeconds: true,
time_24hr: true,
minuteIncrement: 1,
onChange(selectedDates, dateStr, instance) {
const inpEl = document.querySelector(`input#ban-end-${id}`);
// Get date to timestamp
const pickStamp = +Date.parse(new Date(dateStr).toString());
const nowStamp = +Date.now();
// Case pick is before current date
if (pickStamp < nowStamp) {
inpEl.setAttribute("data-timestamp", Date.now());
return instance.setDate(nowStamp);
inpEl.setAttribute("data-timestamp", defaultDate);
return instance.setDate(defaultDate);
}
// Case right value

View file

@ -32,9 +32,17 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
</div>
<!-- end actions -->
<div class=" {% if bans|length == 0 %}w-full overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words{%else%}hidden{% endif%} "
> <div class="col-span-12 flex flex-col justify-center items-center h-fit">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="mb-2 w-8 h-8 stroke-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607ZM10.5 7.5v6m3-3h-6" />
</svg>
<h5 class="font-bold dark:text-white/90 mx-2 text-white">No bans found</h5>
</div></div>
<!-- info-->
<div
class="h-fit col-span-12 md:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
class="{% if bans|length == 0 %}hidden{% endif%} h-fit col-span-12 md:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
>
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
<div class="mx-1 flex items-center my-4">
@ -49,7 +57,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
{{bans|length}}
</p>
</div>
<div class="mx-1 flex items-center my-4">
<div class="{% if bans|length == 0 %}hidden{% endif%} mx-1 flex items-center my-4">
<p
class="transition duration-300 ease-in-out font-bold mb-0 font-sans text-sm leading-normal uppercase dark:text-gray-500 dark:opacity-80"
>
@ -67,7 +75,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
<!-- filter -->
<div
data-{{current_endpoint}}-filter
class="h-fit col-span-12 md:col-span-8 2xl:col-span-6 3xl:col-span-5 p-4 relative flex flex-col min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
class="{% if bans|length == 0 %}hidden{% endif%} h-fit col-span-12 md:col-span-8 2xl:col-span-6 3xl:col-span-5 p-4 relative flex flex-col min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
>
<h5 class="mb-2 font-bold dark:text-white/90">FILTER</h5>
<div class="mx-2 grid grid-cols-12 gap-x-4 gap-y-2">
@ -218,7 +226,9 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
</div>
<!-- end filter -->
<div class="w-full overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
<div class=" {% if bans|length == 0 %}hidden{% endif%} w-full overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
>
<div class="col-span-12">
<h5 class="mb-4 mt-2 font-bold dark:text-white/90 mx-2">BANS LIST</h5>

View file

@ -1,11 +1,11 @@
<!-- modal -->
<div
data-bans-modal
class="dark:brightness-110 hidden w-screen h-screen fixed bg-gray-600/50 z-[1001] top-0 left-0 justify-center items-center"
class="dark:brightness-110 hidden w-screen h-screen fixed bg-gray-600/50 z-[1001] top-0 left-0 justify-center items-start"
>
<div
data-bans-modal-card
class="overflow-y-auto mx-0 sm:mx-6 lg:mx-8 my-3 px-4 pt-4 pb-8 w-full sm:min-w-[500px] max-w-[650px] flex flex-col justify-between break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
class="overflow-y-auto mx-0 sm:mx-6 lg:mx-8 my-3 px-4 pt-4 pb-8 w-full sm:min-w-[500px] max-w-[650px] mt-[15vh] max-h-[70vh] flex flex-col justify-between break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
>
<div>
<div class="w-full flex justify-between mb-2">
@ -80,7 +80,7 @@
</svg>
</button>
</div>
<div class="col-span-12 overflow-y-auto overflow-x-auto">
<div class="col-span-12 overflow-y-auto overflow-x-auto max-h-[250px]">
<div data-{{current_endpoint}}-bans-list>
<!-- list container-->
<div
@ -135,8 +135,19 @@
pattern="(.*?)"
required
/>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="pointer-events-none absolute top-1 right-2 w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5" />
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="pointer-events-none absolute top-1 right-2 w-6 h-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5"
/>
</svg>
</div>
</div>

File diff suppressed because one or more lines are too long

View file

@ -5,7 +5,7 @@
>
<div
data-plugins-modal-card
class="overflow-y-auto mx-0 sm:mx-6 lg:mx-8 my-3 px-4 pt-4 pb-8 w-full sm:min-w-[500px] h-[90vh] flex flex-col break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
class="overflow-y-auto mx-0 sm:mx-6 lg:mx-8 my-3 px-4 pt-4 pb-8 w-[50vw] max-w-[700px] sm:min-w-[500px] max-h-[90vh] flex flex-col break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
>
<div class="w-full flex justify-between mb-2">
<p
@ -50,15 +50,15 @@
</div>
<!-- action button -->
<div class="w-full justify-center flex mt-10">
<button
<button
data-plugins-modal-close
class="dark:brightness-90 mr-3 inline-block px-6 py-3 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-red-500 hover:bg-red-500/80 focus:bg-red-500/80 leading-normal text-md ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md"
class="close-btn mb-4 mr-3 text-base"
>
Close
</button>
<button
type="submit"
class="dark:brightness-90 inline-block px-6 py-3 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-sky-500 hover:bg-sky-500/80 focus:bg-sky-500/80 leading-normal text-md ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md"
class="delete-btn mb-4 mr-3 text-base"
>
Delete
</button>

View file

@ -22,9 +22,18 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
{% endif %}
{% endfor %}
<div class=" {% if reports|length == 0 %}w-full overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words{%else%}hidden{% endif%} "
> <div class="col-span-12 flex flex-col justify-center items-center h-fit">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="mb-2 w-8 h-8 stroke-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607ZM10.5 7.5v6m3-3h-6" />
</svg>
<h5 class="font-bold dark:text-white/90 mx-2 text-white">No reports found</h5>
</div></div>
<!-- info-->
{% if reports|length != 0 %}
<div
class="h-fit col-span-12 md:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
class=" h-fit col-span-12 md:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
>
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
<div class="mx-1 flex items-center my-4">
@ -36,7 +45,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
<p
class="transition duration-300 ease-in-out pl-2 col-span-1 mb-0 font-sans text-sm font-semibold leading-normal uppercase dark:text-white dark:opacity-80"
>
{{reports|length}}
{{ total_reports }}
</p>
</div>
<div class="mx-1 flex items-center my-4">
@ -479,5 +488,6 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
</div>
<!-- end list container--></div>
</div>
{%endif%}
{% endblock %}

View file

@ -1186,131 +1186,127 @@ location /hello {
sleep(3)
print("The cache file content is correct, trying logs page ...", flush=True)
print("The cache file content is correct, trying reporting page ...", flush=True)
access_page(driver, driver_wait, "/html/body/aside[1]/div[1]/div[3]/ul/li[8]/a", "logs")
get(f"http://www.example.com{ui_url}/home?id=/etc/passwd")
### LOGS PAGE
access_page(driver, driver_wait, "/html/body/aside[1]/div[1]/div[3]/ul/li[8]/a", "reports")
print("Selecting correct instance ...", flush=True)
### REPORTS PAGE
assert_button_click(driver, "//button[@data-logs-setting-select='instances']")
print("Trying to filter the reports ...", flush=True)
instances = safe_get_element(
driver,
By.XPATH,
"//div[@data-logs-setting-select-dropdown='instances']/button",
multiple=True,
)
reports_list = safe_get_element(driver, By.XPATH, "//ul[@data-reports-list='']/li", multiple=True)
first_instance = instances[0].text
if len(instances) == 0:
print("No instances found, exiting ...", flush=True)
if not reports_list:
print("No reports found, exiting ...", flush=True)
exit(1)
assert_button_click(driver, instances[0])
assert_button_click(driver, safe_get_element(driver, By.ID, "submit-data"))
sleep(3)
logs_list = safe_get_element(driver, By.XPATH, "//ul[@data-logs-list='']/li", multiple=True)
if len(logs_list) == 0:
print("No logs found, exiting ...", flush=True)
exit(1)
print("Logs found, trying auto refresh ...", flush=True)
assert_button_click(driver, safe_get_element(driver, By.ID, "live-update"))
assert_button_click(driver, safe_get_element(driver, By.ID, "submit-live"))
sleep(3)
if len(logs_list) == len(
safe_get_element(
driver,
By.XPATH,
"//ul[@data-logs-list='']/li[not(contains(@class, 'hidden'))]",
multiple=True,
)
):
print("Auto refresh is not working, exiting ...", flush=True)
exit(1)
print("Auto refresh is working, deactivating it ...", flush=True)
assert_button_click(driver, safe_get_element(driver, By.ID, "live-update"))
assert_button_click(driver, safe_get_element(driver, By.ID, "submit-data"))
sleep(3)
logs_list = safe_get_element(driver, By.XPATH, "//ul[@data-logs-list='']/li", multiple=True)
print("Trying filters ...", flush=True)
filter_input = safe_get_element(driver, By.ID, "keyword")
filter_input.send_keys("abcde")
filter_input.send_keys("gen")
with suppress(TimeoutException):
WebDriverWait(driver, 2).until(
EC.presence_of_element_located(
(
By.XPATH,
"//ul[@data-reports-list='']/li[not(contains(@class, 'hidden'))]",
)
)
)
print("The keyword filter is not working, exiting ...", flush=True)
exit(1)
print("The reports have been filtered, trying bans page ...", flush=True)
### BANS PAGE
access_page(driver, driver_wait, "/html/body/aside[1]/div[1]/div[3]/ul/li[9]/a", "bans")
try:
safe_get_element(driver, By.XPATH, "/html/body/main/div/div[2]/div/h5", error=True)
except TimeoutException:
print("Bans present even though they shouldn't be, exiting ...", flush=True)
exit(1)
print("No bans found, as expected, trying to add a ban ...", flush=True)
assert_button_click(driver, "//button[@data-add-ban='']")
try:
WebDriverWait(driver, 2).until(
EC.presence_of_element_located(
(
By.XPATH,
"//ul[@data-bans-add-ban-list='']/li",
)
)
)
except TimeoutException:
print("No bans found, exiting ...", flush=True)
exit(1)
assert_button_click(driver, "//button[@data-add-ban-delete-all-item='']")
with suppress(TimeoutException):
WebDriverWait(driver, 2).until(
EC.presence_of_element_located(
(
By.XPATH,
"//ul[@data-bans-add-ban-list='']/li",
)
)
)
print("Bans present even though they shouldn't be, exiting ...", flush=True)
exit(1)
print("No bans found, as expected, trying to add multiple bans ...", flush=True)
add_entry_button = safe_get_element(driver, By.XPATH, "//button[@data-ban-add-new='']")
assert_button_click(driver, add_entry_button)
ip_input = safe_get_element(driver, By.ID, "ip-1")
ip_input.send_keys("127.0.0.1")
sleep(3)
if len(logs_list) == len(
safe_get_element(
driver,
By.XPATH,
"//ul[@data-logs-list='']/li[not(contains(@class, 'hidden'))]",
multiple=True,
)
):
print("The keyword filter is not working, exiting ...", flush=True)
assert_button_click(driver, add_entry_button)
ip_input = safe_get_element(driver, By.ID, "ip-2")
ip_input.send_keys("8.8.8.8")
access_page(driver, driver_wait, "//button[@data-bans-modal-submit='']", "bans")
try:
entries = safe_get_element(driver, By.XPATH, "//ul[@data-bans-list='']/li", multiple=True, error=True)
except TimeoutException:
print("No ban found, exiting ...", flush=True)
exit(1)
filter_input.clear()
print("Keyword filter is working, trying type filter ...", flush=True)
assert_button_click(driver, "//button[@data-logs-setting-select='types']")
assert_button_click(
driver,
"//div[@data-logs-setting-select-dropdown='types']/button[@value='warn']",
)
if len(logs_list) == len(
safe_get_element(
driver,
By.XPATH,
"//ul[@data-logs-list='']/li[not(contains(@class, 'hidden'))]",
multiple=True,
)
):
print("The keyword filter is not working, exiting ...", flush=True)
if len(entries) != 2:
print("The bans are present but there should be 2, exiting ...", flush=True)
exit(1)
assert_button_click(driver, "//button[@data-logs-setting-select='types']")
print("Bans found, trying to delete them ...", flush=True)
assert_button_click(
driver,
"//div[@data-logs-setting-select-dropdown='types']/button[@value='all']",
)
assert_button_click(driver, "//input[@id='ban-item-2']")
print("Type filter is working, trying to filter by date ...", flush=True)
access_page(driver, driver_wait, "//button[@data-unban-btn='']", "bans")
current_date = datetime.now()
resp = get(
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"]},
)
if len(resp.json()["logs"]) != 0:
print("The date filter is not working, exiting ...", flush=True)
try:
entries = safe_get_element(driver, By.XPATH, "//ul[@data-bans-list='']/li", multiple=True, error=True)
except TimeoutException:
print("No bans found, exiting ...", flush=True)
exit(1)
print("Date filter is working, trying jobs page ...", flush=True)
if len(entries) != 1:
print("The bans are present but there should be 1, exiting ...", flush=True)
exit(1)
access_page(driver, driver_wait, "/html/body/aside[1]/div[1]/div[3]/ul/li[9]/a", "jobs")
print("Ban deleted successfully, trying jobs page ...", flush=True)
access_page(driver, driver_wait, "/html/body/aside[1]/div[1]/div[3]/ul/li[10]/a", "jobs")
### JOBS PAGE
@ -1441,7 +1437,129 @@ location /hello {
print("The cache download is not working, exiting ...", flush=True)
exit(1)
print("Cache download is working, trying account page ...", flush=True)
print("Jobs cache download is working, trying logs page ...", flush=True)
access_page(driver, driver_wait, "/html/body/aside[1]/div[1]/div[3]/ul/li[11]/a", "logs")
### LOGS PAGE
print("Selecting correct instance ...", flush=True)
assert_button_click(driver, "//button[@data-logs-setting-select='instances']")
instances = safe_get_element(
driver,
By.XPATH,
"//div[@data-logs-setting-select-dropdown='instances']/button",
multiple=True,
)
first_instance = instances[0].text
if len(instances) == 0:
print("No instances found, exiting ...", flush=True)
exit(1)
assert_button_click(driver, instances[0])
assert_button_click(driver, safe_get_element(driver, By.ID, "submit-data"))
sleep(3)
logs_list = safe_get_element(driver, By.XPATH, "//ul[@data-logs-list='']/li", multiple=True)
if len(logs_list) == 0:
print("No logs found, exiting ...", flush=True)
exit(1)
print("Logs found, trying auto refresh ...", flush=True)
assert_button_click(driver, safe_get_element(driver, By.ID, "live-update"))
assert_button_click(driver, safe_get_element(driver, By.ID, "submit-live"))
sleep(3)
if len(logs_list) == len(
safe_get_element(
driver,
By.XPATH,
"//ul[@data-logs-list='']/li[not(contains(@class, 'hidden'))]",
multiple=True,
)
):
print("Auto refresh is not working, exiting ...", flush=True)
exit(1)
print("Auto refresh is working, deactivating it ...", flush=True)
assert_button_click(driver, safe_get_element(driver, By.ID, "live-update"))
assert_button_click(driver, safe_get_element(driver, By.ID, "submit-data"))
sleep(3)
logs_list = safe_get_element(driver, By.XPATH, "//ul[@data-logs-list='']/li", multiple=True)
print("Trying filters ...", flush=True)
filter_input = safe_get_element(driver, By.ID, "keyword")
filter_input.send_keys("gen")
sleep(3)
if len(logs_list) == len(
safe_get_element(
driver,
By.XPATH,
"//ul[@data-logs-list='']/li[not(contains(@class, 'hidden'))]",
multiple=True,
)
):
print("The keyword filter is not working, exiting ...", flush=True)
exit(1)
filter_input.clear()
print("Keyword filter is working, trying type filter ...", flush=True)
assert_button_click(driver, "//button[@data-logs-setting-select='types']")
assert_button_click(
driver,
"//div[@data-logs-setting-select-dropdown='types']/button[@value='warn']",
)
if len(logs_list) == len(
safe_get_element(
driver,
By.XPATH,
"//ul[@data-logs-list='']/li[not(contains(@class, 'hidden'))]",
multiple=True,
)
):
print("The keyword filter is not working, exiting ...", flush=True)
exit(1)
assert_button_click(driver, "//button[@data-logs-setting-select='types']")
assert_button_click(
driver,
"//div[@data-logs-setting-select-dropdown='types']/button[@value='all']",
)
print("Type filter is working, trying to filter by date ...", flush=True)
current_date = datetime.now()
resp = get(
f"http://www.example.com{ui_url}/logs/{first_instance}?from_date={int((current_date - timedelta(weeks=1)).timestamp())}&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"]},
)
if len(resp.json()["logs"]) != 0:
print("The date filter is not working, exiting ...", flush=True)
exit(1)
print("Date filter is working, trying jobs page ...", flush=True)
access_page(driver, driver_wait, "/html/body/aside[1]/div[1]/div[2]/a", "account")
@ -1700,6 +1818,8 @@ location /hello {
)
print("Successfully logged in without 2FA, tests are done, exiting ...", flush=True)
except KeyboardInterrupt:
pass
except SystemExit:
exit(1)
except: