From 57caac7e435c709eb22c0aeeefffae82b0639cfc Mon Sep 17 00:00:00 2001 From: zwiterrion Date: Wed, 6 Dec 2023 10:39:01 +0100 Subject: [PATCH] add some tests and allow reconnection for the websocket --- .../dist/.gitkeep | 0 .../esbuild.js | 1 - .../index.js | 1 - .../authentication/otoroshi_auth.test.js | 86 ++++++++- ...-module-new-auth-module-1701851199519.json | 60 ++++++ .../route-wasm-manager-1701851138888.json | 176 ++++++++++++++++++ ui/src/Terminal.js | 79 +++++--- 7 files changed, 366 insertions(+), 37 deletions(-) delete mode 100644 server/build/87009577-78cf-4d63-bc56-097c03f7a396/dist/.gitkeep delete mode 100644 server/build/87009577-78cf-4d63-bc56-097c03f7a396/esbuild.js delete mode 100644 server/build/87009577-78cf-4d63-bc56-097c03f7a396/index.js create mode 100644 server/tests/authentication/otoroshi_entities/authentication-module-new-auth-module-1701851199519.json create mode 100644 server/tests/authentication/otoroshi_entities/route-wasm-manager-1701851138888.json diff --git a/server/build/87009577-78cf-4d63-bc56-097c03f7a396/dist/.gitkeep b/server/build/87009577-78cf-4d63-bc56-097c03f7a396/dist/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/server/build/87009577-78cf-4d63-bc56-097c03f7a396/esbuild.js b/server/build/87009577-78cf-4d63-bc56-097c03f7a396/esbuild.js deleted file mode 100644 index 53bfdd0..0000000 --- a/server/build/87009577-78cf-4d63-bc56-097c03f7a396/esbuild.js +++ /dev/null @@ -1 +0,0 @@ -const esbuild = require('esbuild');esbuild.build({entryPoints: ['index.js'], outdir: 'dist', bundle: true, sourcemap: true, minify: false, format: 'cjs', target: ['es2020']}) \ No newline at end of file diff --git a/server/build/87009577-78cf-4d63-bc56-097c03f7a396/index.js b/server/build/87009577-78cf-4d63-bc56-097c03f7a396/index.js deleted file mode 100644 index e697a7a..0000000 --- a/server/build/87009577-78cf-4d63-bc56-097c03f7a396/index.js +++ /dev/null @@ -1 +0,0 @@ -export function execute() { let context = JSON.parse(Host.inputString()); if (context.request.headers["foo"] === "bar") { const out = { result: true }; Host.outputString(JSON.stringify(out)); } else { const error = { result: false, error: { message: "you're not authorized", status: 401 } }; Host.outputString(JSON.stringify(error)); } return 0; } \ No newline at end of file diff --git a/server/tests/authentication/otoroshi_auth.test.js b/server/tests/authentication/otoroshi_auth.test.js index 60c0dca..f545632 100644 --- a/server/tests/authentication/otoroshi_auth.test.js +++ b/server/tests/authentication/otoroshi_auth.test.js @@ -1,7 +1,12 @@ const { GenericContainer, Network } = require("testcontainers"); +const wasmo_route = require('./otoroshi_entities/route-wasm-manager-1701851138888.json'); +const authentication_module = require('./otoroshi_entities/authentication-module-new-auth-module-1701851199519.json'); + let instance; + let container; +let otoroshi; const WASMO_CLIENT_ID = "id"; const WASMO_CLIENT_SECRET = "secret"; @@ -9,7 +14,7 @@ const WASMO_CLIENT_SECRET = "secret"; beforeAll(async () => { const network = await new Network().start(); - await new GenericContainer("maif/otoroshi:16.11.2") + otoroshi = await new GenericContainer("maif/otoroshi:16.11.2") .withNetwork(network) .withExposedPorts(8080) .withEnvironment({ @@ -23,7 +28,7 @@ beforeAll(async () => { .withExposedPorts(5003) .withEnvironment({ MANAGER_PORT: 5003, - AUTH_MODE: "BASIC_AUTH", + AUTH_MODE: "OTOROSHI_AUTH", CHECK_DOMAINS: false, STORAGE: 'test', WASMO_CLIENT_ID, @@ -38,16 +43,79 @@ beforeAll(async () => { }) }, 60000); -test('/health', async () => { - return fetch(`${instance}/health`) - .then(r => expect(r.status).toBe(401)) +test('setup otoroshi', async () => { + const _createdRoute = await fetch(`http://otoroshi-api.oto.tools:${otoroshi.getFirstMappedPort()}/api/routes`, { + method: 'POST', + headers: { + "Content-type": "application/json", + "Authorization": `Basic ${btoa('admin-api-apikey-id:admin-api-apikey-secret')}` + }, + body: JSON.stringify(wasmo_route) + }) + .then(r => expect(r.status).toBe(201)); + + const _createdAuthenticationModule = await fetch(`http://otoroshi-api.oto.tools:${otoroshi.getFirstMappedPort()}/api/auths`, { + method: 'POST', + headers: { + "Content-Type": "application/json", + "Authorization": `Basic ${btoa('admin-api-apikey-id:admin-api-apikey-secret')}` + }, + body: JSON.stringify(authentication_module) + }) + .then(r => expect(r.status).toBe(201)); }); -test('/health', async () => { - return fetch(`${instance}/health`, { +function getGlobalConfiguration() { + return fetch(`http://otoroshi-api.oto.tools:${otoroshi.getFirstMappedPort()}/api/globalconfig`, { headers: { - 'Authorization': `Basic ${btoa(`${WASMO_CLIENT_ID}:${WASMO_CLIENT_SECRET}`)}` + "Accept": "application/json", + "Authorization": `Basic ${btoa('admin-api-apikey-id:admin-api-apikey-secret')}` } }) - .then(r => expect(r.status).toBe(200)) + .then(r => { + expect(r.status).toBe(200) + return r.json(); + }); +} + +test('set wasmo configuration in global configuration', async () => { + const initialConfiguration = await getGlobalConfiguration(); + + await fetch(`http://otoroshi-api.oto.tools:${otoroshi.getFirstMappedPort()}/api/globalconfig`, { + method: 'PUT', + headers: { + "Content-Type": "application/json", + "Authorization": `Basic ${btoa('admin-api-apikey-id:admin-api-apikey-secret')}` + }, + body: JSON.stringify({ + ...initialConfiguration, + wasmoSettings: { + ...initialConfiguration.wasmoSettings, + url: instance, + clientId: WASMO_CLIENT_ID, + clientSecret: WASMO_CLIENT_SECRET, + } + }) + }) + + const wasmoConfiguration = await fetch(`http://otoroshi-api.oto.tools:${otoroshi.getFirstMappedPort()}/api/globalconfig`, { + headers: { + "Accept": "application/json", + "Authorization": `Basic ${btoa('admin-api-apikey-id:admin-api-apikey-secret')}` + } + }) + .then(r => { + expect(r.status).toBe(200) + return r.json(); + }) + .then(configuration => configuration.wasmoSettings); + + expect(wasmoConfiguration.url).toBe(instance); + expect(wasmoConfiguration.clientId).toBe(WASMO_CLIENT_ID); + expect(wasmoConfiguration.clientSecret).toBe(WASMO_CLIENT_SECRET); +}); + +test('/health', async () => { + return fetch(`${instance}/health`) + .then(r => expect(r.status).toBe(401)) }); diff --git a/server/tests/authentication/otoroshi_entities/authentication-module-new-auth-module-1701851199519.json b/server/tests/authentication/otoroshi_entities/authentication-module-new-auth-module-1701851199519.json new file mode 100644 index 0000000..018f7a4 --- /dev/null +++ b/server/tests/authentication/otoroshi_entities/authentication-module-new-auth-module-1701851199519.json @@ -0,0 +1,60 @@ +{ + "_loc": { + "tenant": "default", + "teams": [ + "default" + ] + }, + "type": "basic", + "id": "auth_mod_dev_f7b3446f-dee2-424f-849f-e6c1e372ae38", + "name": "New auth. module", + "desc": "New auth. module", + "basicAuth": false, + "webauthn": false, + "clientSideSessionEnabled": true, + "sessionMaxAge": 86400, + "metadata": {}, + "tags": [], + "users": [ + { + "name": "Walker Bins", + "password": "$2a$10$hy8dKr1F2MjjhXAR2rZb/eSWj8u8E8.R0oIneNYrMPkNLHClv0X3m", + "email": "wasm@oto.tools", + "metadata": {}, + "tags": [], + "webauthn": null, + "rights": [ + { + "tenant": "*:rw", + "teams": [ + "*:rw" + ] + } + ], + "adminEntityValidators": {} + }, + { + "name": "Stanley Nikolaus", + "password": "$2a$10$.pnZk8BEvPkN1/ZPMIhMbuXXIeOX8Zl77lvIpOzxPlFA.dN9b8lmy", + "email": "second@oto.tools", + "metadata": {}, + "tags": [], + "webauthn": null, + "rights": [ + { + "tenant": "*:rw", + "teams": [ + "*:rw" + ] + } + ], + "adminEntityValidators": {} + } + ], + "sessionCookieValues": { + "httpOnly": true, + "secure": false + }, + "userValidators": [], + "kind": "AuthModule" +} \ No newline at end of file diff --git a/server/tests/authentication/otoroshi_entities/route-wasm-manager-1701851138888.json b/server/tests/authentication/otoroshi_entities/route-wasm-manager-1701851138888.json new file mode 100644 index 0000000..afe8632 --- /dev/null +++ b/server/tests/authentication/otoroshi_entities/route-wasm-manager-1701851138888.json @@ -0,0 +1,176 @@ +{ + "_loc": { + "tenant": "default", + "teams": [ + "default" + ] + }, + "id": "wasm-manager", + "name": "wasmo", + "description": "", + "tags": [], + "metadata": {}, + "enabled": false, + "debug_flow": false, + "export_reporting": false, + "capture": false, + "groups": [ + "default" + ], + "frontend": { + "domains": [ + "wasmo.oto.tools" + ], + "strip_path": true, + "exact": false, + "headers": {}, + "query": {}, + "methods": [] + }, + "backend": { + "targets": [ + { + "id": "http://localhost:5001", + "hostname": "localhost", + "port": 5001, + "tls": false, + "weight": 1, + "predicate": { + "type": "AlwaysMatch" + }, + "protocol": "HTTP/1.1", + "ip_address": null, + "tls_config": { + "certs": [], + "trusted_certs": [], + "enabled": false, + "loose": false, + "trust_all": false + } + } + ], + "root": "/", + "rewrite": false, + "load_balancing": { + "type": "RoundRobin" + }, + "client": { + "retries": 1, + "max_errors": 20, + "retry_initial_delay": 50, + "backoff_factor": 2, + "call_timeout": 30000, + "call_and_stream_timeout": 120000, + "connection_timeout": 10000, + "idle_timeout": 60000, + "global_timeout": 30000, + "sample_interval": 2000, + "proxy": {}, + "custom_timeouts": [], + "cache_connection_settings": { + "enabled": false, + "queue_size": 2048 + } + }, + "health_check": { + "enabled": false, + "url": "", + "timeout": 5000, + "healthyStatuses": [], + "unhealthyStatuses": [] + } + }, + "backend_ref": null, + "plugins": [ + { + "enabled": true, + "debug": false, + "plugin": "cp:otoroshi.next.plugins.ApikeyCalls", + "include": [], + "exclude": [], + "config": { + "extractors": { + "basic": { + "enabled": true, + "header_name": null, + "query_name": null + }, + "custom_headers": { + "enabled": true, + "client_id_header_name": null, + "client_secret_header_name": null + }, + "client_id": { + "enabled": true, + "header_name": null, + "query_name": null + }, + "jwt": { + "enabled": true, + "secret_signed": true, + "keypair_signed": true, + "include_request_attrs": false, + "max_jwt_lifespan_sec": null, + "header_name": "", + "query_name": null, + "cookie_name": null + } + }, + "routing": { + "enabled": false + }, + "validate": true, + "mandatory": true, + "pass_with_user": true, + "wipe_backend_request": false, + "update_quotas": true + }, + "plugin_index": { + "validate_access": 0, + "transform_request": 0, + "match_route": 0 + }, + "nodeId": "cp:otoroshi.next.plugins.ApikeyCalls" + }, + { + "enabled": true, + "debug": false, + "plugin": "cp:otoroshi.next.plugins.AuthModule", + "include": [], + "exclude": [], + "config": { + "pass_with_apikey": true, + "auth_module": null, + "module": "auth_mod_dev_f7b3446f-dee2-424f-849f-e6c1e372ae38" + }, + "plugin_index": { + "validate_access": 1 + }, + "nodeId": "cp:otoroshi.next.plugins.AuthModule" + }, + { + "enabled": true, + "debug": false, + "plugin": "cp:otoroshi.next.plugins.OtoroshiInfos", + "include": [], + "exclude": [], + "config": { + "version": "Latest", + "ttl": 30, + "header_name": "Otoroshi-User", + "add_fields": null, + "algo": { + "type": "HSAlgoSettings", + "size": 512, + "secret": "secret", + "base64": false + } + }, + "plugin_index": { + "transform_request": 1 + }, + "nodeId": "cp:otoroshi.next.plugins.OtoroshiInfos" + } + ], + "kind": "Route" +} \ No newline at end of file diff --git a/ui/src/Terminal.js b/ui/src/Terminal.js index 6c24a44..c77f056 100644 --- a/ui/src/Terminal.js +++ b/ui/src/Terminal.js @@ -17,36 +17,63 @@ function Terminal({ sizeTerminal, toggleResizingTerminal, changeTerminalSize, se } }, [loadConfigurationFile]); + const connect = (isDevelopment, attempts) => { + let socket; + let internalAttempts = attempts; + + if (isDevelopment) { + socket = new WebSocket(`ws://${window.location.hostname}:5001/${selectedPlugin.pluginId}`); + } else { + socket = new WebSocket(`ws://${window.location.host}/${selectedPlugin.pluginId}`); + } + + socket.onopen = () => { + internalAttempts = 1; + } + + socket.onmessage = event => { + const text = event.data + if (sizeTerminal === 0) { + changeTerminalSize(0.5); + } + + if (text.includes("You can now use the generated wasm")) { + setLoadConfigurationFile(true) + } + + if (text.includes('Starting build')) { + setContent(text) + } else { + setContent(content => content + text) + if (ref && ref.current) + ref.current.view.scrollDOM.scrollTop = ref.current.view.scrollDOM.scrollHeight; + } + } + + socket.onclose = function (e) { + console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason); + + if (internalAttempts <= 5) { + console.log(1000 * (internalAttempts)) + setTimeout(function () { + connect(isDevelopment, internalAttempts + 1); + }, 1000 * (internalAttempts)); + } else { + console.log('Reconnect attempts have failed. You need to reload the page'); + } + }; + + socket.onerror = function (err) { + console.error('Socket encountered error: ', err.message, 'Closing socket'); + socket.close(); + }; + } + useEffect(() => { if (selectedPlugin) { isDevelopmentMode() .then(isDevelopment => { - let socket; - - if (isDevelopment) { - socket = new WebSocket(`ws://${window.location.hostname}:5001/${selectedPlugin.pluginId}`); - } else { - socket = new WebSocket(`ws://${window.location.host}/${selectedPlugin.pluginId}`); - } - - socket.onmessage = event => { - const text = event.data - if (sizeTerminal === 0) { - changeTerminalSize(0.5); - } - - if (text.includes("You can now use the generated wasm")) { - setLoadConfigurationFile(true) - } - - if (text.includes('Starting build')) { - setContent(text) - } else { - setContent(content => content + text) - if (ref && ref.current) - ref.current.view.scrollDOM.scrollTop = ref.current.view.scrollDOM.scrollHeight; - } - } + connect(isDevelopment, 1) }) } }, [selectedPlugin?.pluginId]);