diff options
| -rw-r--r-- | backend/helpers.py | 15 | ||||
| -rw-r--r-- | backend/legacy/library.js | 8 | ||||
| -rw-r--r-- | backend/main.py | 11 | ||||
| -rw-r--r-- | frontend/src/components/store/Store.tsx | 28 | ||||
| -rw-r--r-- | frontend/src/index.tsx | 37 | ||||
| -rw-r--r-- | frontend/src/plugin-loader.tsx | 15 | ||||
| -rw-r--r-- | frontend/src/updater.ts | 2 |
7 files changed, 95 insertions, 21 deletions
diff --git a/backend/helpers.py b/backend/helpers.py index a75f1075..e8c2ce5b 100644 --- a/backend/helpers.py +++ b/backend/helpers.py @@ -1,7 +1,20 @@ +from aiohttp.web import middleware, Response import ssl import certifi +import uuid ssl_ctx = ssl.create_default_context(cafile=certifi.where()) +csrf_token = str(uuid.uuid4()) + def get_ssl_context(): - return ssl_ctx
\ No newline at end of file + return ssl_ctx + +def get_csrf_token(): + return csrf_token + +@middleware +async def csrf_middleware(request, handler): + if str(request.method) == "OPTIONS" or request.headers.get('Authentication') == csrf_token or str(request.rel_url) == "/auth/token" or str(request.rel_url).startswith("/plugins/load_main/") or str(request.rel_url).startswith("/static/") or str(request.rel_url).startswith("/legacy/") or str(request.rel_url).startswith("/steam_resource/"): + return await handler(request) + return Response(text='Forbidden', status='403')
\ No newline at end of file diff --git a/backend/legacy/library.js b/backend/legacy/library.js index f9dfe699..17f4e46f 100644 --- a/backend/legacy/library.js +++ b/backend/legacy/library.js @@ -8,10 +8,13 @@ window.addEventListener("message", function(evt) { }, false); async function call_server_method(method_name, arg_object={}) { + const token = await fetch("http://127.0.0.1:1337/auth/token").then(r => r.text()); const response = await fetch(`http://127.0.0.1:1337/methods/${method_name}`, { method: 'POST', + credentials: "include", headers: { 'Content-Type': 'application/json', + Authentication: token }, body: JSON.stringify(arg_object), }); @@ -40,10 +43,13 @@ async function fetch_nocors(url, request={}) { async function call_plugin_method(method_name, arg_object={}) { if (plugin_name == undefined) throw new Error("Plugin methods can only be called from inside plugins (duh)"); + const token = await fetch("http://127.0.0.1:1337/auth/token").then(r => r.text()); const response = await fetch(`http://127.0.0.1:1337/plugins/${plugin_name}/methods/${method_name}`, { method: 'POST', + credentials: "include", headers: { - 'Content-Type': 'application/json', + 'Content-Type': 'application/json', + Authentication: token }, body: JSON.stringify({ args: arg_object, diff --git a/backend/main.py b/backend/main.py index b52180ab..02cc0d56 100644 --- a/backend/main.py +++ b/backend/main.py @@ -20,12 +20,13 @@ from os import path from subprocess import call import aiohttp_cors -from aiohttp.web import Application, run_app, static +from aiohttp.web import Application, run_app, static, get, Response from aiohttp_jinja2 import setup as jinja_setup from browser import PluginBrowser from injector import inject_to_tab, tab_has_global_var from loader import Loader +from helpers import csrf_middleware, get_csrf_token from utilities import Utilities from updater import Updater @@ -41,9 +42,10 @@ class PluginManager: def __init__(self) -> None: self.loop = get_event_loop() self.web_app = Application() + self.web_app.middlewares.append(csrf_middleware) self.cors = aiohttp_cors.setup(self.web_app, defaults={ "https://steamloopback.host": aiohttp_cors.ResourceOptions(expose_headers="*", - allow_headers="*") + allow_headers="*", allow_credentials=True) }) self.plugin_loader = Loader(self.web_app, CONFIG["plugin_path"], self.loop, CONFIG["live_reload"]) self.plugin_browser = PluginBrowser(CONFIG["plugin_path"], self.web_app, self.plugin_loader.plugins) @@ -57,6 +59,8 @@ class PluginManager: self.loop.create_task(self.loader_reinjector()) self.loop.create_task(self.load_plugins()) self.loop.set_exception_handler(self.exception_handler) + self.web_app.add_routes([get("/auth/token", self.get_auth_token)]) + for route in list(self.web_app.router.routes()): self.cors.add(route) self.web_app.add_routes([static("/static", path.join(path.dirname(__file__), 'static'))]) @@ -67,6 +71,9 @@ class PluginManager: return loop.default_exception_handler(context) + async def get_auth_token(self, request): + return Response(text=get_csrf_token()) + async def wait_for_server(self): async with ClientSession() as web: while True: diff --git a/frontend/src/components/store/Store.tsx b/frontend/src/components/store/Store.tsx index fc95fcd5..86318fd5 100644 --- a/frontend/src/components/store/Store.tsx +++ b/frontend/src/components/store/Store.tsx @@ -35,6 +35,10 @@ export async function installFromURL(url: string) { await fetch('http://localhost:1337/browser/install_plugin', { method: 'POST', body: formData, + credentials: 'include', + headers: { + Authentication: window.deckyAuthToken, + }, }); } @@ -50,6 +54,10 @@ export function requestLegacyPluginInstall(plugin: LegacyStorePlugin, selectedVe fetch('http://localhost:1337/browser/install_plugin', { method: 'POST', body: formData, + credentials: 'include', + headers: { + Authentication: window.deckyAuthToken, + }, }); }} onCancel={() => { @@ -75,6 +83,10 @@ export async function requestPluginInstall(plugin: StorePlugin, selectedVer: Sto await fetch('http://localhost:1337/browser/install_plugin', { method: 'POST', body: formData, + credentials: 'include', + headers: { + Authentication: window.deckyAuthToken, + }, }); } @@ -84,12 +96,24 @@ const StorePage: FC<{}> = () => { useEffect(() => { (async () => { - const res = await fetch('https://beta.deckbrew.xyz/plugins', { method: 'GET' }).then((r) => r.json()); + const res = await fetch('https://beta.deckbrew.xyz/plugins', { + method: 'GET', + credentials: 'include', + headers: { + Authentication: window.deckyAuthToken, + }, + }).then((r) => r.json()); console.log(res); setData(res.filter((x: StorePlugin) => x.name !== 'Example Plugin')); })(); (async () => { - const res = await fetch('https://plugins.deckbrew.xyz/get_plugins', { method: 'GET' }).then((r) => r.json()); + const res = await fetch('https://plugins.deckbrew.xyz/get_plugins', { + method: 'GET', + credentials: 'include', + headers: { + Authentication: window.deckyAuthToken, + }, + }).then((r) => r.json()); console.log(res); setLegacyData(res); })(); diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 4045751f..20f71766 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -8,24 +8,33 @@ declare global { importDeckyPlugin: Function; syncDeckyPlugins: Function; deckyHasLoaded: boolean; + deckyAuthToken: string; } } +(async () => { + window.deckyAuthToken = await fetch('http://127.0.0.1:1337/auth/token').then((r) => r.text()); -window.DeckyPluginLoader?.dismountAll(); -window.DeckyPluginLoader?.deinit(); + window.DeckyPluginLoader?.dismountAll(); + window.DeckyPluginLoader?.deinit(); -window.DeckyPluginLoader = new PluginLoader(); -window.importDeckyPlugin = function (name: string) { - window.DeckyPluginLoader?.importPlugin(name); -}; + window.DeckyPluginLoader = new PluginLoader(); + window.importDeckyPlugin = function (name: string) { + window.DeckyPluginLoader?.importPlugin(name); + }; -window.syncDeckyPlugins = async function () { - const plugins = await (await fetch('http://127.0.0.1:1337/plugins')).json(); - for (const plugin of plugins) { - if (!window.DeckyPluginLoader.hasPlugin(plugin)) window.DeckyPluginLoader?.importPlugin(plugin); - } -}; + window.syncDeckyPlugins = async function () { + const plugins = await ( + await fetch('http://127.0.0.1:1337/plugins', { + credentials: 'include', + headers: { Authentication: window.deckyAuthToken }, + }) + ).json(); + for (const plugin of plugins) { + if (!window.DeckyPluginLoader.hasPlugin(plugin)) window.DeckyPluginLoader?.importPlugin(plugin); + } + }; -setTimeout(() => window.syncDeckyPlugins(), 5000); + setTimeout(() => window.syncDeckyPlugins(), 5000); -window.deckyHasLoaded = true; + window.deckyHasLoaded = true; +})(); diff --git a/frontend/src/plugin-loader.tsx b/frontend/src/plugin-loader.tsx index 98cb3c06..29ca326f 100644 --- a/frontend/src/plugin-loader.tsx +++ b/frontend/src/plugin-loader.tsx @@ -75,6 +75,10 @@ class PluginLoader extends Logger { await fetch('http://localhost:1337/browser/uninstall_plugin', { method: 'POST', body: formData, + credentials: 'include', + headers: { + Authentication: window.deckyAuthToken, + }, }); }} onCancel={() => { @@ -144,7 +148,12 @@ class PluginLoader extends Logger { } private async importReactPlugin(name: string) { - let res = await fetch(`http://127.0.0.1:1337/plugins/${name}/frontend_bundle`); + let res = await fetch(`http://127.0.0.1:1337/plugins/${name}/frontend_bundle`, { + credentials: 'include', + headers: { + Authentication: window.deckyAuthToken, + }, + }); if (res.ok) { let plugin = await eval(await res.text())(this.createPluginAPI(name)); this.plugins.push({ @@ -166,8 +175,10 @@ class PluginLoader extends Logger { async callServerMethod(methodName: string, args = {}) { const response = await fetch(`http://127.0.0.1:1337/methods/${methodName}`, { method: 'POST', + credentials: 'include', headers: { 'Content-Type': 'application/json', + Authentication: window.deckyAuthToken, }, body: JSON.stringify(args), }); @@ -182,8 +193,10 @@ class PluginLoader extends Logger { async callPluginMethod(methodName: string, args = {}) { const response = await fetch(`http://127.0.0.1:1337/plugins/${pluginName}/methods/${methodName}`, { method: 'POST', + credentials: 'include', headers: { 'Content-Type': 'application/json', + Authentication: window.deckyAuthToken, }, body: JSON.stringify({ args, diff --git a/frontend/src/updater.ts b/frontend/src/updater.ts index 692a7a70..f499d030 100644 --- a/frontend/src/updater.ts +++ b/frontend/src/updater.ts @@ -14,8 +14,10 @@ export interface DeckyUpdater { export async function callUpdaterMethod(methodName: string, args = {}) { const response = await fetch(`http://127.0.0.1:1337/updater/${methodName}`, { method: 'POST', + credentials: 'include', headers: { 'Content-Type': 'application/json', + Authentication: window.deckyAuthToken, }, body: JSON.stringify(args), }); |
