From 10ba03993828d373b602436eb639632684db94b6 Mon Sep 17 00:00:00 2001 From: Izalia Mae Date: Thu, 14 Mar 2024 20:58:16 -0400 Subject: [PATCH] minor frontend tweaks and use javascript for managing domain bans --- relay/application.py | 39 +++++++++ relay/frontend/base.haml | 44 +++------- relay/frontend/page/admin-config.haml | 32 +++---- relay/frontend/page/admin-domain_bans.haml | 65 +++++++------- relay/frontend/page/admin-instances.haml | 79 ++++++++--------- relay/frontend/page/admin-software_bans.haml | 57 +++++++------ relay/frontend/page/admin-users.haml | 43 +++++----- relay/frontend/page/admin-whitelist.haml | 4 +- relay/frontend/page/home.haml | 35 +++++--- relay/frontend/page/login.haml | 17 ++-- relay/frontend/static/api.js | 90 ++++++++++++++++++++ relay/frontend/static/domain_ban.js | 85 ++++++++++++++++++ relay/frontend/static/menu.js | 21 +++++ relay/frontend/{ => static}/style.css | 12 ++- relay/views/api.py | 48 ----------- relay/views/base.py | 3 + relay/views/frontend.py | 32 ++++--- 17 files changed, 461 insertions(+), 245 deletions(-) create mode 100644 relay/frontend/static/api.js create mode 100644 relay/frontend/static/domain_ban.js create mode 100644 relay/frontend/static/menu.js rename relay/frontend/{ => static}/style.css (96%) diff --git a/relay/application.py b/relay/application.py index c5f9aaf..3743612 100644 --- a/relay/application.py +++ b/relay/application.py @@ -68,6 +68,8 @@ class Application(web.Application): for path, view in VIEWS: self.router.add_view(path, view) + self.add_routes([web.static('/static', get_resource('frontend/static'))]) + setup_swagger( self, ui_version = 3, @@ -124,6 +126,40 @@ class Application(web.Application): return timedelta(seconds=uptime.seconds) + def get_csp(self, request: Request) -> str: + data = [ + "default-src 'none'", + f"script-src 'nonce-{request['hash']}'", + f"style-src 'nonce-{request['hash']}'", + "form-action 'self'", + "connect-src 'self'", + "img-src 'self'", + "object-src 'none'", + "frame-ancestors 'none'" + ] + + return '; '.join(data) + ';' + + # data = { + # 'base-uri': '\'none\'', + # 'default-src': '\'none\'', + # 'frame-ancestors': '\'none\'', + # 'font-src': f'\'self\' https://{self.config.domain}', + # 'img-src': f'\'self\' https://{self.config.domain}', + # 'style-src': f'\'self\' https://{self.config.domain} \'nonce-randomstringhere\'', + # 'media-src': f'\'self\' https://{self.config.domain}', + # 'frame-src': f'\'self\' https:', + # 'manifest-src': f'\'self\' https://{self.config.domain}', + # 'form-action': f'\'self\'', + # 'child-src': f'\'self\' https://{self.config.domain}', + # 'worker-src': f'\'self\' https://{self.config.domain}', + # 'connect-src': f'\'self\' https://{self.config.domain} wss://{self.config.domain}', + # 'script-src': f'\'self\' https://{self.config.domain}' + # } + # + # return '; '.join(f'{key} {value}' for key, value in data.items()) + ';' + + def push_message(self, inbox: str, message: Message, instance: Row) -> None: self['push_queue'].put((inbox, message, instance)) @@ -269,6 +305,9 @@ async def handle_response_headers(request: web.Request, handler: Callable) -> Re resp = await handler(request) resp.headers['Server'] = 'ActivityRelay' + # if resp.content_type == 'text/html': + # resp.headers['Content-Security-Policy'] = Application.DEFAULT.get_csp(request) + if not request.app['dev'] and request.path.endswith(('.css', '.js')): # cache for 2 weeks resp.headers['Cache-Control'] = 'public,max-age=1209600,immutable' diff --git a/relay/frontend/base.haml b/relay/frontend/base.haml index 9d08ad7..992cfe7 100644 --- a/relay/frontend/base.haml +++ b/relay/frontend/base.haml @@ -11,8 +11,10 @@ %title << {{config.name}}: {{page}} %meta(charset="UTF-8") %meta(name="viewport" content="width=device-width, initial-scale=1") - %link(rel="stylesheet" type="text/css" href="/theme/{{config.theme}}.css") - %link(rel="stylesheet" type="text/css" href="/style.css") + %link(rel="stylesheet" type="text/css" href="/theme/{{config.theme}}.css" nonce="{{view.request['hash']}}") + %link(rel="stylesheet" type="text/css" href="/static/style.css" nonce="{{view.request['hash']}}") + %script(type="application/javascript" src="/static/menu.js" nonce="{{view.request['hash']}}", defer) + %script(type="application/javascript" src="/static/api.js" nonce="{{view.request['hash']}}", defer) -block head %body @@ -38,19 +40,18 @@ #container #header.section %span#menu-open << ⁞ - %span.title-container - %a.title(href="/") -> =config.name - - -if view.request.path not in ["/", "/login"] - .page -> =page - + %a.title(href="/") -> =config.name .empty -if error - .error.section -> =error + %fieldset.error.section + %legend << Error + =error -if message - .message.section -> =message + %fieldset.message.section + %legend << Message + =message #content(class="page-{{page.lower().replace(' ', '_')}}") -block content @@ -69,26 +70,3 @@ .version %a(href="https://git.pleroma.social/pleroma/relay") ActivityRelay/{{version}} - - %script(type="application/javascript") - const body = document.getElementById("container") - const menu = document.getElementById("menu"); - const menu_open = document.getElementById("menu-open"); - const menu_close = document.getElementById("menu-close"); - - menu_open.addEventListener("click", (event) => { - var new_value = menu.attributes.visible.nodeValue === "true" ? "false" : "true"; - menu.attributes.visible.nodeValue = new_value; - }); - - menu_close.addEventListener("click", (event) => { - menu.attributes.visible.nodeValue = "false" - }); - - body.addEventListener("click", (event) => { - if (event.target === menu_open) { - return; - } - - menu.attributes.visible.nodeValue = "false"; - }); diff --git a/relay/frontend/page/admin-config.haml b/relay/frontend/page/admin-config.haml index 08c16c7..ff6c4f6 100644 --- a/relay/frontend/page/admin-config.haml +++ b/relay/frontend/page/admin-config.haml @@ -2,24 +2,26 @@ -set page="Config" -import "functions.haml" as func -block content - %form.section(action="/admin/config" method="POST") - .grid-2col - %label(for="name") << Name - %input(id = "name" name="name" placeholder="Relay Name" value="{{config.name or ''}}") + %fieldset.section + %legend << Config + %form(action="/admin/config" method="POST") + .grid-2col + %label(for="name") << Name + %input(id = "name" name="name" placeholder="Relay Name" value="{{config.name or ''}}") - %label(for="description") << Description - %textarea(id="description" name="note" value="{{config.note or ''}}") << {{config.note}} + %label(for="description") << Description + %textarea(id="description" name="note" value="{{config.note or ''}}") << {{config.note}} - %label(for="theme") << Color Theme - =func.new_select("theme", config.theme, themes) + %label(for="theme") << Color Theme + =func.new_select("theme", config.theme, themes) - %label(for="log-level") << Log Level - =func.new_select("log-level", config.log_level.name, levels) + %label(for="log-level") << Log Level + =func.new_select("log-level", config.log_level.name, levels) - %label(for="whitelist-enabled") << Whitelist - =func.new_checkbox("whitelist-enabled", config.whitelist_enabled) + %label(for="whitelist-enabled") << Whitelist + =func.new_checkbox("whitelist-enabled", config.whitelist_enabled) - %label(for="approval-required") << Approval Required - =func.new_checkbox("approval-required", config.approval_required) + %label(for="approval-required") << Approval Required + =func.new_checkbox("approval-required", config.approval_required) - %input(type="submit" value="Save") + %input(type="submit" value="Save") diff --git a/relay/frontend/page/admin-domain_bans.haml b/relay/frontend/page/admin-domain_bans.haml index f874499..104ce15 100644 --- a/relay/frontend/page/admin-domain_bans.haml +++ b/relay/frontend/page/admin-domain_bans.haml @@ -1,48 +1,53 @@ -extends "base.haml" -set page="Domain Bans" + +-block head + %script(type="application/javascript" src="/static/domain_ban.js" nonce="{{view.request['hash']}}", defer) + -block content %details.section %summary << Ban Domain - %form(action="/admin/domain_bans" method="POST") - #add-item - %label(for="domain") << Domain - %input(type="domain" id="domain" name="domain" placeholder="Domain") + #add-item + %label(for="new-domain") << Domain + %input(type="domain" id="new-domain" name="domain" placeholder="Domain") - %label(for="reason") << Ban Reason - %textarea(id="reason" name="reason") << {{""}} + %label(for="new-reason") << Ban Reason + %textarea(id="new-reason" name="new") << {{""}} - %label(for="note") << Admin Note - %textarea(id="note" name="note") << {{""}} + %label(for="new-note") << Admin Note + %textarea(id="new-note" name="note") << {{""}} - %input(type="submit" value="Ban Domain") + %input(type="button" value="Ban Domain" onclick="ban();") - .data-table.section - %table - %thead - %tr - %td.domain << Instance - %td << Date - %td.remove + %fieldset.section + %legend << Domain Bans - %tbody - -for ban in bans + .data-table + %table#table + %thead %tr - %td.domain - %details - %summary -> =ban.domain - %form(action="/admin/domain_bans" method="POST") + %td.domain << Domain + %td << Date + %td.remove + + %tbody + -for ban in bans + %tr(id="{{ban.domain}}") + %td.domain + %details + %summary -> =ban.domain + .grid-2col .reason << Reason - %textarea.reason(id="reason" name="reason") << {{ban.reason or ""}} + %textarea.reason(id="{{ban.domain}}-reason" name="reason") << {{ban.reason or ""}} .note << Note - %textarea.note(id="note" name="note") << {{ban.note or ""}} + %textarea.note(id="{{ban.domain}}-note" name="note") << {{ban.note or ""}} - %input(type="hidden" name="domain" value="{{ban.domain}}") - %input(type="submit" value="Update") + %input(type="button" value="Update" onclick="update_ban('{{ban.domain}}')") - %td.date - =ban.created.strftime("%Y-%m-%d") + %td.date + =ban.created.strftime("%Y-%m-%d") - %td.remove - %a(href="/admin/domain_bans/delete/{{ban.domain}}" title="Unban domain") << ✖ + %td.remove + %a(href="#", onclick="unban('{{ban.domain}}')" title="Unban domain") << ✖ diff --git a/relay/frontend/page/admin-instances.haml b/relay/frontend/page/admin-instances.haml index 770ccc1..aaab1c9 100644 --- a/relay/frontend/page/admin-instances.haml +++ b/relay/frontend/page/admin-instances.haml @@ -20,56 +20,59 @@ %input(type="submit" value="Add Instance") -if requests - .data-table.section - .title << Requests + %fieldset.section + %legend << Follow Requests + .data-table + %table + %thead + %tr + %td.instance << Instance + %td.software << Software + %td.date << Joined + %td.approve + %td.deny + + %tbody + -for request in requests + %tr + %td.instance + %a(href="https://{{request.domain}}" target="_new") -> =request.domain + + %td.software + =request.software or "n/a" + + %td.date + =request.created.strftime("%Y-%m-%d") + + %td.approve + %a(href="/admin/instances/approve/{{request.domain}}" title="Approve Request") << ✓ + + %td.deny + %a(href="/admin/instances/deny/{{request.domain}}" title="Deny Request") << ✖ + + %fieldset.section + %legend << Instances + + .data-table %table %thead %tr %td.instance << Instance %td.software << Software %td.date << Joined - %td.approve - %td.deny + %td.remove %tbody - -for request in requests + -for instance in instances %tr %td.instance - %a(href="https://{{request.domain}}" target="_new") -> =request.domain + %a(href="https://{{instance.domain}}/" target="_new") -> =instance.domain %td.software - =request.software or "n/a" + =instance.software or "n/a" %td.date - =request.created.strftime("%Y-%m-%d") + =instance.created.strftime("%Y-%m-%d") - %td.approve - %a(href="/admin/instances/approve/{{request.domain}}" title="Approve Request") << ✓ - - %td.deny - %a(href="/admin/instances/deny/{{request.domain}}" title="Deny Request") << ✖ - - .data-table.section - .title << Instances - %table - %thead - %tr - %td.instance << Instance - %td.software << Software - %td.date << Joined - %td.remove - - %tbody - -for instance in instances - %tr - %td.instance - %a(href="https://{{instance.domain}}/" target="_new") -> =instance.domain - - %td.software - =instance.software or "n/a" - - %td.date - =instance.created.strftime("%Y-%m-%d") - - %td.remove - %a(href="/admin/instances/delete/{{instance.domain}}" title="Remove Instance") << ✖ + %td.remove + %a(href="/admin/instances/delete/{{instance.domain}}" title="Remove Instance") << ✖ diff --git a/relay/frontend/page/admin-software_bans.haml b/relay/frontend/page/admin-software_bans.haml index 7ac9d07..1ce5664 100644 --- a/relay/frontend/page/admin-software_bans.haml +++ b/relay/frontend/page/admin-software_bans.haml @@ -16,33 +16,36 @@ %input(type="submit" value="Ban Software") - .data-table.section - %table - %thead - %tr - %td.name << Instance - %td << Date - %td.remove + %fieldset.section + %legend << Software Bans - %tbody - -for ban in bans + .data-table + %table + %thead %tr - %td.name - %details - %summary -> =ban.name - %form(action="/admin/software_bans" method="POST") - .grid-2col - .reason << Reason - %textarea.reason(id="reason" name="reason") << {{ban.reason or ""}} - - .note << Note - %textarea.note(id="note" name="note") << {{ban.note or ""}} - - %input(type="hidden" name="name" value="{{ban.name}}") - %input(type="submit" value="Update") - - %td.date - =ban.created.strftime("%Y-%m-%d") - + %td.name << Name + %td << Date %td.remove - %a(href="/admin/software_bans/delete/{{ban.name}}" title="Unban software") << ✖ + + %tbody + -for ban in bans + %tr + %td.name + %details + %summary -> =ban.name + %form(action="/admin/software_bans" method="POST") + .grid-2col + .reason << Reason + %textarea.reason(id="reason" name="reason") << {{ban.reason or ""}} + + .note << Note + %textarea.note(id="note" name="note") << {{ban.note or ""}} + + %input(type="hidden" name="name" value="{{ban.name}}") + %input(type="submit" value="Update") + + %td.date + =ban.created.strftime("%Y-%m-%d") + + %td.remove + %a(href="/admin/software_bans/delete/{{ban.name}}" title="Unban software") << ✖ diff --git a/relay/frontend/page/admin-users.haml b/relay/frontend/page/admin-users.haml index a87c0db..67ab7b1 100644 --- a/relay/frontend/page/admin-users.haml +++ b/relay/frontend/page/admin-users.haml @@ -19,26 +19,29 @@ %input(type="submit" value="Add User") - .data-table.section - %table - %thead - %tr - %td.username << Username - %td.handle << Handle - %td.date << Joined - %td.remove + %fieldset.section + %legend << Users - %tbody - -for user in users + .data-table + %table + %thead %tr - %td.username - =user.username - - %td.handle - =user.handle or "n/a" - - %td.date - =user.created.strftime("%Y-%m-%d") - + %td.username << Username + %td.handle << Handle + %td.date << Joined %td.remove - %a(href="/admin/users/delete/{{user.username}}" title="Remove User") << ✖ + + %tbody + -for user in users + %tr + %td.username + =user.username + + %td.handle + =user.handle or "n/a" + + %td.date + =user.created.strftime("%Y-%m-%d") + + %td.remove + %a(href="/admin/users/delete/{{user.username}}" title="Remove User") << ✖ diff --git a/relay/frontend/page/admin-whitelist.haml b/relay/frontend/page/admin-whitelist.haml index 9126297..e0dffd6 100644 --- a/relay/frontend/page/admin-whitelist.haml +++ b/relay/frontend/page/admin-whitelist.haml @@ -10,7 +10,9 @@ %input(type="submit" value="Add Domain") - .data-table.section + %fieldset.data-table.section + %legend << Whitelist + %table %thead %tr diff --git a/relay/frontend/page/home.haml b/relay/frontend/page/home.haml index b59e5b5..f9618fc 100644 --- a/relay/frontend/page/home.haml +++ b/relay/frontend/page/home.haml @@ -14,27 +14,34 @@ %a(href="https://{{domain}}/actor") << https://{{domain}}/actor -if config.approval_required - %p.section.message + %fieldset.section.message + %legend << Require Approval + Follow requests require approval. You will need to wait for an admin to accept or deny your request. -elif config.whitelist_enabled - %p.section.message + %fieldset.section.message + %legend << Whitelist Enabled + The whitelist is enabled on this instance. Ask the admin to add your instance before joining. - .data-table.section - %table - %thead - %tr - %td.instance << Instance - %td.date << Joined + %fieldset.section + %legend << Instances - %tbody - -for instance in instances + .data-table + %table + %thead %tr - %td.instance -> %a(href="https://{{instance.domain}}/" target="_new") - =instance.domain + %td.instance << Instance + %td.date << Joined - %td.date - =instance.created.strftime("%Y-%m-%d") + %tbody + -for instance in instances + %tr + %td.instance -> %a(href="https://{{instance.domain}}/" target="_new") + =instance.domain + + %td.date + =instance.created.strftime("%Y-%m-%d") diff --git a/relay/frontend/page/login.haml b/relay/frontend/page/login.haml index 1e08185..ab177b6 100644 --- a/relay/frontend/page/login.haml +++ b/relay/frontend/page/login.haml @@ -1,12 +1,15 @@ -extends "base.haml" -set page="Login" -block content - %form.section(action="/login" method="POST") - .grid-2col - %label(for="username") << Username - %input(id="username" name="username" placeholder="Username" value="{{username or ''}}") + %fieldset.section + %legend << Login - %label(for="password") << Password - %input(id="password" name="password" placeholder="Password" type="password") + %form(action="/login" method="POST") + .grid-2col + %label(for="username") << Username + %input(id="username" name="username" placeholder="Username" value="{{username or ''}}") - %input(type="submit" value="Login") + %label(for="password") << Password + %input(id="password" name="password" placeholder="Password" type="password") + + %input(type="submit" value="Login") diff --git a/relay/frontend/static/api.js b/relay/frontend/static/api.js new file mode 100644 index 0000000..a2d3fbb --- /dev/null +++ b/relay/frontend/static/api.js @@ -0,0 +1,90 @@ +function get_cookie(name) { + const regex = new RegExp(`(^| )` + name + `=([^;]+)`); + const match = document.cookie.match(regex); + + if (match) { + return match[2] + } + + return null; +} + + +function get_date_string(date) { + var year = date.getFullYear().toString(); + var month = date.getMonth().toString(); + var day = date.getDay().toString(); + + if (month.length === 1) { + month = "0" + month; + } + + if (day.length === 1) { + day = "0" + day + } + + return `${year}-${month}-${day}`; +} + + +class Client { + constructor() { + this.token = get_cookie("user-token"); + } + + + async request(method, path, body = null) { + var headers = { + "Accept": "application/json" + } + + if (body !== null) { + headers["Content-Type"] = "application/json" + body = JSON.stringify(body) + } + + if (this.token !== null) { + headers["Authorization"] = "Bearer " + this.token; + } + + const response = await fetch("/api/" + path, { + method: method, + mode: "cors", + cache: "no-store", + redirect: "follow", + body: body, + headers: headers + }); + + const message = await response.json(); + + if (Object.hasOwn(message, "error")) { + throw new Error(message.error); + } + + if (Object.hasOwn(message, "created")) { + message.created = new Date(message.created); + } + + return message; + } + + async ban(domain, reason, note) { + const params = { + "domain": domain, + "reason": reason, + "note": note + } + + return await this.request("POST", "v1/domain_ban", params); + } + + + async unban(domain) { + const params = {"domain": domain} + return await this.request("DELETE", "v1/domain_ban", params); + } +} + + +client = new Client(); diff --git a/relay/frontend/static/domain_ban.js b/relay/frontend/static/domain_ban.js new file mode 100644 index 0000000..82434af --- /dev/null +++ b/relay/frontend/static/domain_ban.js @@ -0,0 +1,85 @@ +function create_ban_object(domain, reason, note) { + var text = '
\n'; + text += `${domain}\n`; + text += '
\n'; + text += `\n`; + text += `\n`; + text += `\n`; + text += `\n`; + text += ``; + text += '
'; + + return text; +} + + +async function ban() { + var table = document.getElementById("table"); + var row = table.insertRow(-1); + + var elems = { + domain: document.getElementById("new-domain"), + reason: document.getElementById("new-reason"), + note: document.getElementById("new-note") + } + + var values = { + domain: elems.domain.value.trim(), + reason: elems.reason.value, + note: elems.note.value + } + + if (values.domain === "") { + alert("Domain is required"); + return; + } + + try { + var ban = await client.ban(values.domain, values.reason, values.note); + + } catch (err) { + alert(err); + return + } + + row.id = ban.domain; + var new_domain = row.insertCell(0); + var new_date = row.insertCell(1); + var new_remove = row.insertCell(2); + + new_domain.className = "domain"; + new_date.className = "date"; + new_remove.className = "remove"; + + new_domain.innerHTML = create_ban_object(ban.domain, ban.reason, ban.note); + new_date.innerHTML = get_date_string(ban.created); + new_remove.innerHTML = ``; + + elems.domain.value = null; + elems.reason.value = null; + elems.note.value = null; + + document.querySelectorAll("details.section").forEach((elem) => { + elem.open = false; + }); +} + + +async function update_ban(domain) { + var row = document.getElementById(domain); +} + + +async function unban(domain) { + console.log(domain); + + try { + await client.unban(domain); + + } catch (err) { + alert(err); + return; + } + + document.getElementById(domain).remove(); +} diff --git a/relay/frontend/static/menu.js b/relay/frontend/static/menu.js new file mode 100644 index 0000000..ebd494f --- /dev/null +++ b/relay/frontend/static/menu.js @@ -0,0 +1,21 @@ +const body = document.getElementById("container") +const menu = document.getElementById("menu"); +const menu_open = document.getElementById("menu-open"); +const menu_close = document.getElementById("menu-close"); + +menu_open.addEventListener("click", (event) => { + var new_value = menu.attributes.visible.nodeValue === "true" ? "false" : "true"; + menu.attributes.visible.nodeValue = new_value; +}); + +menu_close.addEventListener("click", (event) => { + menu.attributes.visible.nodeValue = "false" +}); + +body.addEventListener("click", (event) => { + if (event.target === menu_open) { + return; + } + + menu.attributes.visible.nodeValue = "false"; +}); diff --git a/relay/frontend/style.css b/relay/frontend/static/style.css similarity index 96% rename from relay/frontend/style.css rename to relay/frontend/static/style.css index 1e7b90e..e1ac3eb 100644 --- a/relay/frontend/style.css +++ b/relay/frontend/static/style.css @@ -23,17 +23,27 @@ details summary { cursor: pointer; } +fieldset { + margin-left: 0px; + margin-right: 0px; +} + +fieldset > *:nth-child(2) { + margin-top: 0px !important; +} + form input[type="submit"] { display: block; margin: 0 auto; } legend { - background-color: var(--section-background); + background-color: var(--table-background); padding: 5px; border: 1px solid var(--border); border-radius: 5px; font-size: 10pt; + font-weight: bold; } p { diff --git a/relay/views/api.py b/relay/views/api.py index 0435555..4b9dc6c 100644 --- a/relay/views/api.py +++ b/relay/views/api.py @@ -274,54 +274,6 @@ class DomainBan(View): return Response.new(bans, ctype = 'json') - async def post(self, request: Request) -> Response: - data = await self.get_api_data(['domain'], ['note', 'reason']) - - if isinstance(data, Response): - return data - - with self.database.session() as conn: - if conn.get_domain_ban(data['domain']): - return Response.new_error(400, 'Domain already banned', 'json') - - ban = conn.put_domain_ban(**data) - - return Response.new(ban, ctype = 'json') - - - async def patch(self, request: Request) -> Response: - with self.database.session() as conn: - data = await self.get_api_data(['domain'], ['note', 'reason']) - - if isinstance(data, Response): - return data - - if not conn.get_domain_ban(data['domain']): - return Response.new_error(404, 'Domain not banned', 'json') - - if not any([data.get('note'), data.get('reason')]): - return Response.new_error(400, 'Must include note and/or reason parameters', 'json') - - ban = conn.update_domain_ban(data['domain'], **data) - - return Response.new(ban, ctype = 'json') - - - async def delete(self, request: Request) -> Response: - with self.database.session() as conn: - data = await self.get_api_data(['domain'], []) - - if isinstance(data, Response): - return data - - if not conn.get_domain_ban(data['domain']): - return Response.new_error(404, 'Domain not banned', 'json') - - conn.del_domain_ban(data['domain']) - - return Response.new({'message': 'Unbanned domain'}, ctype = 'json') - - @register_route('/api/v1/software_ban') class SoftwareBan(View): async def get(self, request: Request) -> Response: diff --git a/relay/views/base.py b/relay/views/base.py index d293f62..2e035f6 100644 --- a/relay/views/base.py +++ b/relay/views/base.py @@ -2,9 +2,11 @@ from __future__ import annotations import typing +from Crypto.Random import get_random_bytes from aiohttp.abc import AbstractView from aiohttp.hdrs import METH_ALL as METHODS from aiohttp.web import HTTPMethodNotAllowed +from base64 import b64encode from functools import cached_property from json.decoder import JSONDecodeError @@ -56,6 +58,7 @@ class View(AbstractView): async def _run_handler(self, handler: Callable[..., Any], **kwargs: Any) -> Response: + self.request['hash'] = b64encode(get_random_bytes(16)).decode('ascii') return await handler(self.request, **self.request.match_info, **kwargs) diff --git a/relay/views/frontend.py b/relay/views/frontend.py index 4e10e83..448b834 100644 --- a/relay/views/frontend.py +++ b/relay/views/frontend.py @@ -26,21 +26,31 @@ UNAUTH_ROUTES = { @web.middleware async def handle_frontend_path(request: web.Request, handler: Callable) -> Response: + app = get_app() + if request.path in UNAUTH_ROUTES or request.path.startswith('/admin'): request['token'] = request.cookies.get('user-token') request['user'] = None if request['token']: - with get_app().database.session(False) as conn: + with app.database.session(False) as conn: request['user'] = conn.get_user_by_token(request['token']) if request['user'] and request.path == '/login': return Response.new('', 302, {'Location': '/'}) if not request['user'] and request.path.startswith('/admin'): - return Response.new('', 302, {'Location': f'/login?redir={request.path}'}) + response = Response.new('', 302, {'Location': f'/login?redir={request.path}'}) + response.del_cookie('user-token') + return response - return await handler(request) + response = await handler(request) + + if not request['user'] and request['token']: + print("del token") + response.del_cookie('user-token') + + return response @register_route('/') @@ -96,8 +106,8 @@ class Login(View): domain = self.config.domain, path = '/', secure = True, - httponly = True, - samesite = 'Strict' + httponly = False, + samesite = 'lax' ) return resp @@ -271,7 +281,7 @@ class AdminWhitelist(View): with self.database.session(True) as conn: if conn.get_domain_whitelist(data['domain']): - return await self.get(request, message = "Domain already in whitelist") + return await self.get(request, error = "Domain already in whitelist") conn.put_domain_whitelist(data['domain']) @@ -284,7 +294,7 @@ class AdminWhitlistDelete(View): with self.database.session() as conn: if not conn.get_domain_whitelist(domain): msg = 'Whitelisted domain not found' - return await AdminWhitelist.run("GET", request, message = msg) + return await AdminWhitelist.run("GET", request, error = msg) conn.del_domain_whitelist(domain) @@ -342,7 +352,7 @@ class AdminDomainBansDelete(View): async def get(self, request: Request, domain: str) -> Response: with self.database.session() as conn: if not conn.get_domain_ban(domain): - return await AdminDomainBans.run("GET", request, message = 'Domain ban not found') + return await AdminDomainBans.run("GET", request, error = 'Domain ban not found') conn.del_domain_ban(domain) @@ -400,7 +410,7 @@ class AdminSoftwareBansDelete(View): async def get(self, request: Request, name: str) -> Response: with self.database.session() as conn: if not conn.get_software_ban(name): - return await AdminSoftwareBans.run("GET", request, message = 'Software ban not found') + return await AdminSoftwareBans.run("GET", request, error = 'Software ban not found') conn.del_software_ban(name) @@ -441,7 +451,7 @@ class AdminUsers(View): with self.database.session(True) as conn: if conn.get_user(data['username']): - return await self.get(request, message = "User already exists") + return await self.get(request, error = "User already exists") conn.put_user(data['username'], data['password'], data['handle']) @@ -453,7 +463,7 @@ class AdminUsersDelete(View): async def get(self, request: Request, name: str) -> Response: with self.database.session() as conn: if not conn.get_user(name): - return await AdminUsers.run("GET", request, message = 'User not found') + return await AdminUsers.run("GET", request, error = 'User not found') conn.del_user(name)