Skip to content

Commit

Permalink
allow users to suggest new words to add to the list
Browse files Browse the repository at this point in the history
  • Loading branch information
dwrensha committed Sep 10, 2024
1 parent a24eb18 commit 9b5a198
Show file tree
Hide file tree
Showing 6 changed files with 372 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,6 @@ dist

# wrangler project

# .dev.vars
.dev.vars

.wrangler
29 changes: 29 additions & 0 deletions migrations/0002_add_suggestions_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-- Migration number: 0002 2024-09-10T16:48:10.681Z

-- User-provided suggestions for words to be added to the main list.
CREATE TABLE suggestions (
-- randomly-generated identifier
id TEXT PRIMARY KEY,

-- The word.
word TEXT,

-- The definition.
def TEXT,

-- The user who submitted the suggestion.
author TEXT,

-- When definition was submitted, as a 64-bit integer number of milliseconds
-- since the unix epoch.
timestamp INT,

-- 0 means pending
-- 1 means accepted
-- -1 means rejected
status INT,

-- When a moderator accepts or rejects a suggestion, they may choose
-- to leave a message explaining their choice.
moderator_note Text
);
76 changes: 76 additions & 0 deletions src/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copied from https://developers.cloudflare.com/workers/examples/basic-auth/

import { Buffer } from "node:buffer";

const encoder = new TextEncoder();

/**
* Protect against timing attacks by safely comparing values using `timingSafeEqual`.
* Refer to https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#timingsafeequal for more details
* @param {string} a
* @param {string} b
* @returns {boolean}
*/
function timingSafeEqual(a, b) {
const aBytes = encoder.encode(a);
const bBytes = encoder.encode(b);

if (aBytes.byteLength !== bBytes.byteLength) {
// Strings must be the same length in order to compare
// with crypto.subtle.timingSafeEqual
return false;
}

return crypto.subtle.timingSafeEqual(aBytes, bBytes);
}

export function bounce_if_not_authed(env, request) {
if (!env.ADMIN_PASSWORD) {
return new Response("Admin password is not configured.", {
status: 403,
});
}

const authorization = request.headers.get("Authorization");
if (!authorization) {
return new Response("You need to login.", {
status: 401,
headers: {
// Prompts the user for credentials.
"WWW-Authenticate": 'Basic realm="my scope", charset="UTF-8"',
},
});
}
const [scheme, encoded] = authorization.split(" ");

// The Authorization header must start with Basic, followed by a space.
if (!encoded || scheme !== "Basic") {
return new Response("Malformed authorization header.", {
status: 400,
});
}

const credentials = Buffer.from(encoded, "base64").toString();

// The username & password are split by the first colon.
//=> example: "username:password"
const index = credentials.indexOf(":");
const user = credentials.substring(0, index);
const pass = credentials.substring(index + 1);

if (
!timingSafeEqual("admin", user) ||
!timingSafeEqual(env.ADMIN_PASSWORD, pass)
) {
return new Response("You need to login.", {
status: 401,
headers: {
// Prompts the user for credentials.
"WWW-Authenticate": 'Basic realm="my scope", charset="UTF-8"',
},
});
}

// returning null means auth succeeded.
return null;
}
Loading

0 comments on commit 9b5a198

Please sign in to comment.