diff --git a/.gitignore b/.gitignore index b74bc5a..639a628 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ data/ -.fsdb +.fsdb* public/files/* -.vscode/* \ No newline at end of file +.vscode/* diff --git a/public/api/topics/:topic_id/events/index.ts b/public/api/topics/:topic_id/events/index.ts index 95f947d..fe99165 100644 --- a/public/api/topics/:topic_id/events/index.ts +++ b/public/api/topics/:topic_id/events/index.ts @@ -204,10 +204,6 @@ export async function POST(req: Request, meta: Record): Promise[A-Za-z\-]+)\/?(?\w+)?/gm; const UPDATE_TOPICS_FREQUENCY = 60_000; const APP = { + user: undefined, user_servers: [], user_watches: [], @@ -56,16 +57,19 @@ const APP = { groups: {}, }; + /* console.dir({ url: window.location.href, hash: window.location.hash, topic_id, view, }); + */ if (!document.body.dataset.topic || document.body.dataset.topic !== topic_id) { const previous = document.body.dataset.topic; + /* console.dir({ topic_changed: { detail: { @@ -74,6 +78,7 @@ const APP = { }, }, }); + */ document.body.dataset.topic = topic_id; @@ -94,6 +99,7 @@ const APP = { const previous = document.body.dataset.view; document.body.dataset.view = view; + /* console.dir({ view_changed: { detail: { @@ -102,6 +108,7 @@ const APP = { }, }, }); + */ this._emit( 'view_changed', { previous, @@ -149,8 +156,9 @@ const APP = { this._emit( 'load', this ); }, - update_user: async function( updated_user ) { - const user = this.user = updated_user; + update_user: async function( user ) { + this.user = user; + document.body.dataset.user = JSON.stringify(user); document.body.dataset.perms = user.permissions.join(":"); diff --git a/public/sidebar/sidebar.html b/public/sidebar/sidebar.html index 556e4fe..d118b03 100644 --- a/public/sidebar/sidebar.html +++ b/public/sidebar/sidebar.html @@ -809,9 +809,10 @@ form.on_reply = (new_topic) => { const topic_list = document.getElementById("topic-list"); + topic_list.querySelectorAll( 'li' ).forEach( (li) => li.classList.remove( 'active' ) ); topic_list.insertAdjacentHTML( "beforeend", - `
  • ${new_topic.name}
  • `, + `
  • ${new_topic.name}
  • `, ); new_topic_name_input.value = ""; diff --git a/public/signup_login_wall.html b/public/signup_login_wall.html index e252cdc..f4b2834 100644 --- a/public/signup_login_wall.html +++ b/public/signup_login_wall.html @@ -105,12 +105,7 @@ const form = document.currentScript.closest("form"); form.on_reply = (response) => { const user = response.user; - document.body.dataset.user = JSON.stringify(user); - document.body.dataset.perms = user.permissions.join(":"); - - document.dispatchEvent( - new CustomEvent("user_logged_in", { detail: { user } }), - ); + APP.login( user ); }; } diff --git a/tests/03_login.test.ts b/tests/03_login.test.ts index 25cc4d4..3ed1c88 100644 --- a/tests/03_login.test.ts +++ b/tests/03_login.test.ts @@ -44,7 +44,7 @@ Deno.test({ } }); - const authed_user: USER | undefined = auth_response.user; + const _authed_user: USER | undefined = auth_response.user; const authed_session: Record | undefined = auth_response.session; cookies.push({ diff --git a/tests/04_create_topic.test.ts b/tests/04_create_topic.test.ts index 6fee81e..5b248e9 100644 --- a/tests/04_create_topic.test.ts +++ b/tests/04_create_topic.test.ts @@ -22,14 +22,34 @@ Deno.test({ port: test_server_info.port }); - const info = await get_new_user(client); + const root_user_info = await get_new_user(client); + + try { + const root_user_topic = await client.fetch('/topics', { + method: 'POST', + headers: { + 'x-session_id': root_user_info.session.id, + 'x-totp': await generateTotp(root_user_info.session.secret) + }, + json: { + name: 'this is the root user topic' + } + }); + + asserts.assert(root_user_topic); + } catch (error) { + const reason: string = (error as Error).cause as string ?? (error as Error).toString(); + asserts.fail(reason); + } + + const regular_user_info = await get_new_user(client, {}, root_user_info); try { const _permission_denied_topic = await client.fetch('/topics', { method: 'POST', headers: { - 'x-session_id': info.session.id, - 'x-totp': await generateTotp(info.session.secret) + 'x-session_id': regular_user_info.session.id, + 'x-totp': await generateTotp(regular_user_info.session.secret) }, json: { name: 'this should not be allowed' @@ -41,14 +61,14 @@ Deno.test({ asserts.assertEquals((error as Error).cause, 'permission_denied'); } - await set_user_permissions(client, info.user, info.session, [...info.user.permissions, 'topics.create']); + await set_user_permissions(client, regular_user_info.user, regular_user_info.session, [...regular_user_info.user.permissions, 'topics.create']); try { const _too_long_name_topic = await client.fetch('/topics', { method: 'POST', headers: { - 'x-session_id': info.session.id, - 'x-totp': await generateTotp(info.session.secret) + 'x-session_id': regular_user_info.session.id, + 'x-totp': await generateTotp(regular_user_info.session.secret) }, json: { name: 'X'.repeat(1024) @@ -63,8 +83,8 @@ Deno.test({ const new_topic = await client.fetch('/topics', { method: 'POST', headers: { - 'x-session_id': info.session.id, - 'x-totp': await generateTotp(info.session.secret) + 'x-session_id': regular_user_info.session.id, + 'x-totp': await generateTotp(regular_user_info.session.secret) }, json: { name: 'test topic' @@ -73,7 +93,8 @@ Deno.test({ asserts.assert(new_topic); - await delete_user(client, info); + await delete_user(client, regular_user_info); + await delete_user(client, root_user_info); } finally { clear_topic_events_cache(); if (test_server_info) { diff --git a/tests/11_file_uploads.test.ts b/tests/11_file_uploads.test.ts index 413cd8d..e2d1c5e 100644 --- a/tests/11_file_uploads.test.ts +++ b/tests/11_file_uploads.test.ts @@ -1,8 +1,7 @@ import { api, API_CLIENT } from '../utils/api.ts'; import * as asserts from '@std/assert'; -import { USER } from '../models/user.ts'; import { delete_user, EPHEMERAL_SERVER, get_ephemeral_listen_server, get_new_user, random_username, set_user_permissions } from './helpers.ts'; -import { Cookie, getSetCookies } from '@std/http/cookie'; +import { Cookie } from '@std/http/cookie'; import { generateTotp } from '../utils/totp.ts'; import * as fs from '@std/fs'; import * as path from '@std/path'; @@ -136,55 +135,11 @@ Deno.test({ port: test_server_info.port }); - const username = random_username(); - const password = 'password'; + const root_user_info = await get_new_user(client); + asserts.assert(root_user_info); - const user_creation_response: Record = await client.fetch('/users', { - method: 'POST', - json: { - username, - password - } - }); - - asserts.assert(user_creation_response?.user); - asserts.assert(user_creation_response?.session); - - let cookies: Cookie[] = []; - const auth_response: any = await client.fetch('/auth', { - method: 'POST', - json: { - username, - password: 'password' - }, - done: (response) => { - cookies = getSetCookies(response.headers); - } - }); - - const user: USER | undefined = auth_response.user; - asserts.assert(user); - asserts.assert(user.id); - - const session: Record | undefined = auth_response.session; - asserts.assert(session); - - cookies.push({ - name: 'totp', - value: await generateTotp(session?.secret ?? ''), - maxAge: 30, - expires: Date.now() + 30_000, - path: '/' - }); - - const headers_for_upload_request = new Headers(); - for (const cookie of cookies) { - headers_for_upload_request.append(`x-${cookie.name}`, cookie.value); - } - headers_for_upload_request.append( - 'cookie', - cookies.map((cookie) => `${cookie.name}=${cookie.value}`).join('; ') - ); + const regular_user_info = await get_new_user(client, {}, root_user_info); + asserts.assert(regular_user_info); const upload_body = new FormData(); upload_body.append( @@ -196,7 +151,10 @@ Deno.test({ `http://${test_server_info.hostname}:${test_server_info.port}/files/test_uploading_to_root_dir.txt`, { method: 'PUT', - headers: headers_for_upload_request, + headers: { + 'x-session_id': regular_user_info.session.id, + 'x-totp': await generateTotp(regular_user_info.session.secret) + }, body: upload_body } ); @@ -204,13 +162,16 @@ Deno.test({ asserts.assert(!disallowed_upload_response.ok); await disallowed_upload_response.text(); - await set_user_permissions(client, user, session, [...user.permissions, 'files.write.all']); + await set_user_permissions(client, regular_user_info.user, regular_user_info.session, [...regular_user_info.user.permissions, 'files.write.all']); const allowed_upload_response = await fetch( `http://${test_server_info.hostname}:${test_server_info.port}/files/test_uploading_to_root_dir.txt`, { method: 'PUT', - headers: headers_for_upload_request, + headers: { + 'x-session_id': regular_user_info.session.id, + 'x-totp': await generateTotp(regular_user_info.session.secret) + }, body: upload_body } ); diff --git a/utils/totp.ts b/utils/totp.ts index 8c913be..43460e2 100644 --- a/utils/totp.ts +++ b/utils/totp.ts @@ -7,9 +7,10 @@ import { decodeBase32 } from '@std/encoding'; * * @ignore */ -export function counterToBuffer(counter: number): DataView { - const buffer = new DataView(new ArrayBuffer(8)); - buffer.setBigUint64(0, BigInt(counter), false); +export function counterToBuffer(counter: number): ArrayBuffer { + const buffer = new ArrayBuffer(8); + const view = new DataView(buffer); + view.setBigUint64(0, BigInt(counter), false); return buffer; } @@ -23,7 +24,7 @@ export async function generateHmacSha1( ): Promise { const importedKey = await crypto.subtle.importKey( 'raw', - key, + new Uint8Array(key), { name: 'HMAC', hash: 'SHA-1' }, false, ['sign']