diff --git a/README.md b/README.md index d257a32..b583423 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,7 @@ ## TODO - [x] sign up -- [x] check for logged in user session -- [x] log in +- [ ] log in - [ ] chat rooms - [ ] chat messages - [ ] bulletin board instead of exchange/work? diff --git a/deno.json b/deno.json index 0c12e83..89e4bcd 100644 --- a/deno.json +++ b/deno.json @@ -7,8 +7,7 @@ "tasks": { "lint": "deno lint", "fmt": "deno fmt", - "serve": "FSDB_ROOT=$PWD/.fsdb SERVERUS_TYPESCRIPT_IMPORT_LOGGING=1 deno --allow-env --allow-read --allow-write --allow-net jsr:@andyburke/serverus --root ./public", - "test": "DENO_ENV=test FSDB_ROOT=$PWD/tests/data/$(date --iso-8601=seconds) SERVERUS_TYPESCRIPT_IMPORT_LOGGING=1 SERVERUS_ROOT=$PWD/public deno test --allow-env --allow-read --allow-write --allow-net --trace-leaks --fail-fast tests/" + "serve": "FSDB_ROOT=$PWD/.fsdb deno --allow-env --allow-read --allow-write --allow-net jsr:@andyburke/serverus --root ./public" }, "test": { "exclude": ["tests/data/"] @@ -34,10 +33,8 @@ "imports": { "@andyburke/fsdb": "jsr:@andyburke/fsdb@^0.4.0", "@andyburke/lurid": "jsr:@andyburke/lurid@^0.2.0", - "@andyburke/serverus": "jsr:@andyburke/serverus@^0.6.0", - "@std/assert": "jsr:@std/assert@^1.0.13", + "@andyburke/serverus": "jsr:@andyburke/serverus@^0.0.12", "@std/encoding": "jsr:@std/encoding@^1.0.10", - "@std/http": "jsr:@std/http@^1.0.18", "@std/path": "jsr:@std/path@^1.1.0", "@stdext/crypto": "jsr:@stdext/crypto@^0.1.0" } diff --git a/deno.lock b/deno.lock index f3037bd..4246f0c 100644 --- a/deno.lock +++ b/deno.lock @@ -1,34 +1,21 @@ { "version": "5", "specifiers": { - "jsr:@andyburke/fsdb@*": "0.4.0", "jsr:@andyburke/fsdb@0.4": "0.4.0", - "jsr:@andyburke/lurid@*": "0.2.0", "jsr:@andyburke/lurid@0.2": "0.2.0", - "jsr:@andyburke/serverus@0.6": "0.6.0", - "jsr:@std/assert@*": "1.0.13", - "jsr:@std/assert@^1.0.13": "1.0.13", + "jsr:@andyburke/serverus@^0.0.12": "0.0.12", "jsr:@std/async@^1.0.13": "1.0.13", "jsr:@std/cli@^1.0.19": "1.0.20", "jsr:@std/cli@^1.0.20": "1.0.20", - "jsr:@std/encoding@*": "1.0.10", "jsr:@std/encoding@1": "1.0.10", "jsr:@std/encoding@^1.0.10": "1.0.10", "jsr:@std/fmt@^1.0.6": "1.0.8", - "jsr:@std/fmt@^1.0.8": "1.0.8", "jsr:@std/fs@^1.0.14": "1.0.18", "jsr:@std/fs@^1.0.18": "1.0.18", - "jsr:@std/html@^1.0.4": "1.0.4", - "jsr:@std/http@*": "1.0.18", - "jsr:@std/http@^1.0.13": "1.0.18", - "jsr:@std/http@^1.0.18": "1.0.18", - "jsr:@std/internal@^1.0.6": "1.0.8", + "jsr:@std/http@^1.0.13": "1.0.17", "jsr:@std/media-types@^1.1.0": "1.1.0", - "jsr:@std/net@^1.0.4": "1.0.4", "jsr:@std/path@^1.0.8": "1.1.0", "jsr:@std/path@^1.1.0": "1.1.0", - "jsr:@std/streams@^1.0.10": "1.0.10", - "jsr:@stdext/crypto@*": "0.1.0", "jsr:@stdext/crypto@0.1": "0.1.0" }, "jsr": { @@ -46,24 +33,18 @@ "jsr:@std/cli@^1.0.19" ] }, - "@andyburke/serverus@0.6.0": { - "integrity": "89f013c1d77e3d5d2c4e0908b29cc4a1acd19ebf22fa2890a6c5aa777e7b0de3", + "@andyburke/serverus@0.0.12": { + "integrity": "051cbffd30577e39ca604e009c3870c4b32b7e4118f2da58fc18ec05afa5b5bb", "dependencies": [ "jsr:@std/async", "jsr:@std/cli@^1.0.19", - "jsr:@std/fmt@^1.0.6", + "jsr:@std/fmt", "jsr:@std/fs@^1.0.14", - "jsr:@std/http@^1.0.13", + "jsr:@std/http", "jsr:@std/media-types", "jsr:@std/path@^1.0.8" ] }, - "@std/assert@1.0.13": { - "integrity": "ae0d31e41919b12c656c742b22522c32fb26ed0cba32975cb0de2a273cb68b29", - "dependencies": [ - "jsr:@std/internal" - ] - }, "@std/async@1.0.13": { "integrity": "1d76ca5d324aef249908f7f7fe0d39aaf53198e5420604a59ab5c035adc97c96" }, @@ -82,41 +63,15 @@ "jsr:@std/path@^1.1.0" ] }, - "@std/html@1.0.4": { - "integrity": "eff3497c08164e6ada49b7f81a28b5108087033823153d065e3f89467dd3d50e" - }, "@std/http@1.0.17": { "integrity": "98aec8ab4080d95c21f731e3008f69c29c5012d12f1b4e553f85935db601569f" }, - "@std/http@1.0.18": { - "integrity": "8d9546aa532c52a0cf318c74616db0638b4c1073405355d1b14f9e1591dccf20", - "dependencies": [ - "jsr:@std/cli@^1.0.20", - "jsr:@std/encoding@^1.0.10", - "jsr:@std/fmt@^1.0.8", - "jsr:@std/fs@^1.0.18", - "jsr:@std/html", - "jsr:@std/media-types", - "jsr:@std/net", - "jsr:@std/path@^1.1.0", - "jsr:@std/streams" - ] - }, - "@std/internal@1.0.8": { - "integrity": "fc66e846d8d38a47cffd274d80d2ca3f0de71040f855783724bb6b87f60891f5" - }, "@std/media-types@1.1.0": { "integrity": "c9d093f0c05c3512932b330e3cc1fe1d627b301db33a4c2c2185c02471d6eaa4" }, - "@std/net@1.0.4": { - "integrity": "2f403b455ebbccf83d8a027d29c5a9e3a2452fea39bb2da7f2c04af09c8bc852" - }, "@std/path@1.1.0": { "integrity": "ddc94f8e3c275627281cbc23341df6b8bcc874d70374f75fec2533521e3d6886" }, - "@std/streams@1.0.10": { - "integrity": "75c0b1431873cd0d8b3d679015220204d36d3c7420d93b60acfc379eb0dc30af" - }, "@stdext/crypto@0.1.0": { "integrity": "05dc9e754c2529574d8bf98bd40c7dc468a02dcb2fa5e8644fff6813ceab66a4", "dependencies": [ @@ -124,54 +79,12 @@ ] } }, - "remote": { - "https://deno.land/std@0.167.0/_util/asserts.ts": "d0844e9b62510f89ce1f9878b046f6a57bf88f208a10304aab50efcb48365272", - "https://deno.land/std@0.167.0/_util/os.ts": "8a33345f74990e627b9dfe2de9b040004b08ea5146c7c9e8fe9a29070d193934", - "https://deno.land/std@0.167.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", - "https://deno.land/std@0.167.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", - "https://deno.land/std@0.167.0/path/_util.ts": "d16be2a16e1204b65f9d0dfc54a9bc472cafe5f4a190b3c8471ec2016ccd1677", - "https://deno.land/std@0.167.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", - "https://deno.land/std@0.167.0/path/glob.ts": "81cc6c72be002cd546c7a22d1f263f82f63f37fe0035d9726aa96fc8f6e4afa1", - "https://deno.land/std@0.167.0/path/mod.ts": "cf7cec7ac11b7048bb66af8ae03513e66595c279c65cfa12bfc07d9599608b78", - "https://deno.land/std@0.167.0/path/posix.ts": "b859684bc4d80edfd4cad0a82371b50c716330bed51143d6dcdbe59e6278b30c", - "https://deno.land/std@0.167.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", - "https://deno.land/std@0.167.0/path/win32.ts": "7cebd2bda6657371adc00061a1d23fdd87bcdf64b4843bb148b0b24c11b40f69", - "https://deno.land/std@0.184.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", - "https://deno.land/std@0.184.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", - "https://deno.land/std@0.184.0/async/abortable.ts": "fd682fa46f3b7b16b4606a5ab52a7ce309434b76f820d3221bdfb862719a15d7", - "https://deno.land/std@0.184.0/async/deadline.ts": "c5facb0b404eede83e38bd2717ea8ab34faa2ffb20ef87fd261fcba32ba307aa", - "https://deno.land/std@0.184.0/async/debounce.ts": "adab11d04ca38d699444ac8a9d9856b4155e8dda2afd07ce78276c01ea5a4332", - "https://deno.land/std@0.184.0/async/deferred.ts": "42790112f36a75a57db4a96d33974a936deb7b04d25c6084a9fa8a49f135def8", - "https://deno.land/std@0.184.0/async/delay.ts": "73aa04cec034c84fc748c7be49bb15cac3dd43a57174bfdb7a4aec22c248f0dd", - "https://deno.land/std@0.184.0/async/mod.ts": "f04344fa21738e5ad6bea37a6bfffd57c617c2d372bb9f9dcfd118a1b622e576", - "https://deno.land/std@0.184.0/async/mux_async_iterator.ts": "70c7f2ee4e9466161350473ad61cac0b9f115cff4c552eaa7ef9d50c4cbb4cc9", - "https://deno.land/std@0.184.0/async/pool.ts": "fd082bd4aaf26445909889435a5c74334c017847842ec035739b4ae637ae8260", - "https://deno.land/std@0.184.0/async/retry.ts": "dd19d93033d8eaddbfcb7654c0366e9d3b0a21448bdb06eba4a7d8a8cf936a92", - "https://deno.land/std@0.184.0/async/tee.ts": "47e42d35f622650b02234d43803d0383a89eb4387e1b83b5a40106d18ae36757", - "https://deno.land/std@0.184.0/fmt/colors.ts": "d67e3cd9f472535241a8e410d33423980bec45047e343577554d3356e1f0ef4e", - "https://deno.land/std@0.184.0/fs/_util.ts": "579038bebc3bd35c43a6a7766f7d91fbacdf44bc03468e9d3134297bb99ed4f9", - "https://deno.land/std@0.184.0/fs/ensure_dir.ts": "dc64c4c75c64721d4e3fb681f1382f803ff3d2868f08563ff923fdd20d071c40", - "https://deno.land/std@0.184.0/fs/walk.ts": "920be35a7376db6c0b5b1caf1486fb962925e38c9825f90367f8f26b5e5d0897", - "https://deno.land/std@0.184.0/http/server.ts": "cbb17b594651215ba95c01a395700684e569c165a567e4e04bba327f41197433", - "https://deno.land/std@0.184.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", - "https://deno.land/std@0.184.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", - "https://deno.land/std@0.184.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0", - "https://deno.land/std@0.184.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", - "https://deno.land/std@0.184.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", - "https://deno.land/std@0.184.0/path/mod.ts": "bf718f19a4fdd545aee1b06409ca0805bd1b68ecf876605ce632e932fe54510c", - "https://deno.land/std@0.184.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d", - "https://deno.land/std@0.184.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", - "https://deno.land/std@0.184.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba", - "https://deno.land/x/ulid@v0.3.0/mod.ts": "f7ff065b66abd485051fc68af23becef6ccc7e81f7774d7fcfd894a4b2da1984" - }, "workspace": { "dependencies": [ "jsr:@andyburke/fsdb@0.4", "jsr:@andyburke/lurid@0.2", - "jsr:@andyburke/serverus@0.6", - "jsr:@std/assert@^1.0.13", + "jsr:@andyburke/serverus@^0.0.12", "jsr:@std/encoding@^1.0.10", - "jsr:@std/http@^1.0.18", "jsr:@std/path@^1.1.0", "jsr:@stdext/crypto@0.1" ] diff --git a/models/session.ts b/models/session.ts index 75903de..b88241f 100644 --- a/models/session.ts +++ b/models/session.ts @@ -19,7 +19,6 @@ export const SESSIONS = new FSDB_COLLECTION({ user_id: new FSDB_INDEXER_SYMLINKS({ name: 'user_id', field: 'user_id', - to_many: true, organize: by_lurid }) } diff --git a/public/api/auth/index.ts b/public/api/auth/index.ts index 3a8743d..aa749dc 100644 --- a/public/api/auth/index.ts +++ b/public/api/auth/index.ts @@ -9,15 +9,15 @@ import { TOTP_ENTRIES } from '../../../models/totp_entry.ts'; import { verifyTotp } from 'jsr:@stdext/crypto/totp'; import { encodeBase64 } from 'jsr:@std/encoding/base64'; import parse_body from '../../../utils/bodyparser.ts'; -import { SESSION_ID_TOKEN, SESSION_SECRET_TOKEN } from '../../../utils/prechecks.ts'; -const DEFAULT_SESSION_TIME: number = 60 * 60 * 1_000; // 1 Hour +const DEFAULT_SESSION_TIME: number = 60 * 60; // 1 Hour // POST /api/auth - Authenticate export async function POST(req: Request, meta: Record): Promise { try { const body = await parse_body(req); + const email: string = body.email?.toLowerCase().trim() ?? ''; const username: string = body.username?.toLowerCase() ?? ''; const password: string | undefined = body.password; const password_hash: string | undefined = body.password_hash ?? (typeof password === 'string' @@ -27,11 +27,11 @@ export async function POST(req: Request, meta: Record): Promise): Promise): Promise): Promise { +export async function get_session(session_settings: SESSION_INFO): Promise { const now = new Date().toISOString(); const expires: string = session_settings.expires ?? new Date(new Date(now).valueOf() + DEFAULT_SESSION_TIME).toISOString(); @@ -177,13 +183,7 @@ export async function create_new_session(session_settings: SESSION_INFO): Promis await SESSIONS.create(session); const headers = new Headers(); - - headers.append('Set-Cookie', `${SESSION_ID_TOKEN}=${session.id}; Path=/; Expires=${expires}`); - headers.append(`x-${SESSION_ID_TOKEN}`, session.id); - - // TODO: this wasn't really intended to be persisted in a cookie, but we are using it to - // generate the TOTP for the call to /api/users/me - headers.append('Set-Cookie', `${SESSION_SECRET_TOKEN}=${session.secret}; Path=/; Expires=${expires}`); + headers.append('Set-Cookie', `checklist_observer_session_id=${session.id}; Path=/; Expires=${expires}`); return { session, diff --git a/public/api/users/:id/index.ts b/public/api/users/:id/index.ts index 0e45e93..a7de0ba 100644 --- a/public/api/users/:id/index.ts +++ b/public/api/users/:id/index.ts @@ -1,24 +1,19 @@ -import { get_session, get_user, PRECHECK_TABLE, require_user } from '../../../../utils/prechecks.ts'; import { PASSWORD_ENTRIES, PASSWORD_ENTRY } from '../../../../models/password_entry.ts'; import { SESSIONS } from '../../../../models/session.ts'; import { USER, USERS } from '../../../../models/user.ts'; import { PERMISSIONS_STORE, USER_PERMISSIONS } from '../../../../models/user_permissions.ts'; import parse_body from '../../../../utils/bodyparser.ts'; -import { CANNED_RESPONSES } from '../../../../utils/canned_responses.ts'; -export const PRECHECKS: PRECHECK_TABLE = {}; +export const PERMISSIONS: Record) => Promise> = {}; // GET /api/users/:id - Get single user -PRECHECKS.GET = [get_session, get_user, require_user, (_req: Request, meta: Record): Response | undefined => { +PERMISSIONS.GET = (_req: Request, meta: Record): Promise => { const user_is_self = !!meta.user && !!meta.params && meta.user.id === meta.params.id; const can_read_self = meta.user_permissions?.permissions.includes('self.read'); const can_read_others = meta.user_permissions?.permissions?.includes('users.read'); - const has_permission = can_read_others || (can_read_self && user_is_self); - if (!has_permission) { - return CANNED_RESPONSES.permission_denied(); - } -}]; + return can_read_others || (can_read_self && user_is_self); +}; export async function GET(_req: Request, meta: Record): Promise { const user_id: string = meta.params?.id?.toLowerCase().trim() ?? ''; const user: USER | null = user_id.length === 49 ? await USERS.get(user_id) : null; // lurid is 49 chars as we use them, eg: "also-play-flow-want-form-wide-thus-work-burn-same" @@ -34,22 +29,34 @@ export async function GET(_req: Request, meta: Record): Promise): Response | undefined => { +PERMISSIONS.PUT = (_req: Request, meta: Record): Promise => { const user_is_self = !!meta.user && !!meta.params && meta.user.id === meta.params.id; const can_write_self = meta.user_permissions?.permissions.includes('self.write'); const can_write_others = meta.user_permissions?.permissions?.includes('users.write'); - const has_permission = can_write_others || (can_write_self && user_is_self); - if (!has_permission) { - return CANNED_RESPONSES.permission_denied(); - } -}]; + return can_write_others || (can_write_self && user_is_self); +}; export async function PUT(req: Request, meta: { params: Record }): Promise { const now = new Date().toISOString(); const id: string = meta.params.id ?? ''; @@ -94,16 +101,13 @@ export async function PUT(req: Request, meta: { params: Record }): } // DELETE /api/users/:id - Delete user -PRECHECKS.DELETE = [get_session, get_user, require_user, (_req: Request, meta: Record): Response | undefined => { +PERMISSIONS.DELETE = (_req: Request, meta: Record): Promise => { const user_is_self = !!meta.user && !!meta.params && meta.user.id === meta.params.id; const can_write_self = meta.user_permissions?.permissions.includes('self.write'); const can_write_others = meta.user_permissions?.permissions?.includes('users.write'); - const has_permission = can_write_others || (can_write_self && user_is_self); - if (!has_permission) { - return CANNED_RESPONSES.permission_denied(); - } -}]; + return can_write_others || (can_write_self && user_is_self); +}; export async function DELETE(_req: Request, meta: { params: Record }): Promise { const user_id: string = meta.params.id ?? ''; diff --git a/public/api/users/index.ts b/public/api/users/index.ts index 558dfe3..68e4705 100644 --- a/public/api/users/index.ts +++ b/public/api/users/index.ts @@ -6,27 +6,28 @@ import { hash } from 'jsr:@stdext/crypto/hash'; import lurid from 'jsr:@andyburke/lurid'; import { encodeBase64 } from 'jsr:@std/encoding'; import parse_body from '../../../utils/bodyparser.ts'; -import { create_new_session, SESSION_RESULT } from '../auth/index.ts'; -import { PRECHECKS } from './me/index.ts'; -import { get_session, get_user, require_user } from '../../../utils/prechecks.ts'; -import { CANNED_RESPONSES } from '../../../utils/canned_responses.ts'; +import { get_session, SESSION_RESULT } from '../auth/index.ts'; // TODO: figure out a better solution for doling out permissions const DEFAULT_USER_PERMISSIONS: string[] = [ 'self.read', - 'self.write' + 'self.write', + 'checklists.read', + 'checklists.write', + 'checklists.events.read', + 'checklists.events.write' ]; +export const PERMISSIONS: Record) => Promise> = {}; + // GET /api/users - get users // query parameters: // partial_id: the partial id subset you would like to match (remember, lurids are lexigraphically sorted) -PRECHECKS.GET = [get_session, get_user, require_user, (_req: Request, meta: Record): Response | undefined => { +PERMISSIONS.GET = (_req: Request, meta: Record): Promise => { const can_read_others = meta.user_permissions?.permissions?.includes('users.read'); - if (!can_read_others) { - return CANNED_RESPONSES.permission_denied(); - } -}]; + return can_read_others; +}; export async function GET(_req: Request, meta: Record): Promise { const query: URLSearchParams = meta.query; const partial_id: string | undefined = query.get('partial_id')?.toLowerCase().trim(); @@ -131,7 +132,7 @@ export async function POST(req: Request, meta: Record): Promise) => Promise> = {}; -export const PRECHECKS: PRECHECK_TABLE = {}; - -// GET /api/users/me - Get the current user -PRECHECKS.GET = [get_session, get_user, require_user, (_req: Request, meta: Record): Response | undefined => { - const can_read_self = meta.user_permissions?.permissions.includes('self.read'); - - const has_permission = can_read_self; - console.dir({ - meta, - can_read_self, - has_permission - }); - if (!has_permission) { - return CANNED_RESPONSES.permission_denied(); - } -}]; -export function GET(_req: Request, meta: Record): Response { - return Response.json(meta.user, { - status: 200 - }); -} diff --git a/public/index.html b/public/index.html index a6180cf..3342ab7 100644 --- a/public/index.html +++ b/public/index.html @@ -468,54 +468,6 @@ top: 8px; } - -
@@ -626,13 +578,6 @@ action="/api/auth" onreply="(user)=>{ document.body.dataset.user = user; }" > -