Skip to content

Commit

Permalink
check auth
Browse files Browse the repository at this point in the history
hzrd149 committed Feb 19, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 266d349 commit fff38e4
Showing 6 changed files with 108 additions and 12 deletions.
1 change: 1 addition & 0 deletions config.yml
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@ cache:

upload:
enabled: true
requireAuth: true
rules:
- type: "*"
expiration: 1d
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -18,6 +18,8 @@
"public"
],
"dependencies": {
"@koa/cors": "^5.0.0",
"@koa/router": "^12.0.1",
"@nostr-dev-kit/ndk": "^2.4.0",
"dayjs": "^1.11.10",
"debug": "^4.3.4",
@@ -38,6 +40,8 @@
"@types/debug": "^4.1.12",
"@types/koa": "^2.14.0",
"@types/koa-static": "^4.0.4",
"@types/koa__cors": "^5.0.0",
"@types/koa__router": "^12.0.4",
"@types/node": "^20.11.19",
"nodemon": "^3.0.3",
"prettier": "^3.2.5",
4 changes: 2 additions & 2 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ <h2>List:</h2>
<code>GET /list?pubkey=266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5</code>

<h2>Upload:</h2>
<code>PUT /item</code>
<code>PUT /upload</code>
<br />
<br />
<input type="file" id="file" />
@@ -36,7 +36,7 @@ <h2>Upload:</h2>
if (!file) return;

button.textContent = "Uploading...";
await fetch("/item", { method: "PUT", body: file }).then(async (res) => {
await fetch("/upload", { method: "PUT", body: file }).then(async (res) => {
if (res.ok) {
const body = await res.json();

1 change: 1 addition & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ export type Config = {
};
upload: {
enabled: boolean;
requireAuth: boolean;
rules: Rule[];
};
tor: {
50 changes: 41 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -8,6 +8,9 @@ import { PassThrough } from "node:stream";
import { URLSearchParams } from "node:url";
import mime from "mime";
import pfs from "node:fs/promises";
import { NostrEvent } from "@nostr-dev-kit/ndk";
import cors from "@koa/cors";
import Router from "@koa/router";

import { config } from "./config.js";
import { BlobPointer, BlobSearch } from "./types.js";
@@ -16,11 +19,12 @@ import * as cdnDiscovery from "./discover/cdn.js";
import * as nostrDiscovery from "./discover/nostr.js";
import * as httpTransport from "./transport/http.js";
import * as uploadModule from "./storage/upload.js";
import { db, setBlobExpiration, setBlobMimetype, setBlobSize } from "./db.js";
import { addPubkeyToBlob, db, setBlobExpiration, setBlobMimetype, setBlobSize } from "./db.js";
import { getExpirationTime, getFileRule } from "./storage/rules.js";

const log = debug("cdn");
const app = new Koa();
const router = new Router();

async function handlePointers(ctx: Koa.ParameterizedContext, pointers: BlobPointer[]) {
for (const pointer of pointers) {
@@ -46,9 +50,18 @@ function getBlobURL(hash: string) {
return new URL(hash + (ext ? "." + ext : ""), config.publicDomain).toString();
}

// set CORS headers
app.use(
cors({
origin: "*",
allowMethods: "*",
allowHeaders: "Authorization,*",
exposeHeaders: "*",
}),
);

// fetch blobs
app.use(async (ctx, next) => {
if (ctx.method !== "GET") return next();
router.get("/:hash", async (ctx, next) => {
const match = ctx.path.match(/([0-9a-f]{64})(\.[a-z]+)?/);
if (!match) return next();

@@ -95,10 +108,10 @@ app.use(async (ctx, next) => {
ctx.status = 404;
}
});
``;

// upload blobs
app.use(async (ctx, next) => {
if (ctx.path !== "/item" && ctx.method !== "PUT") return next();
router.put("/upload", async (ctx) => {
if (!config.upload.enabled) {
ctx.status = 403;
ctx.body = "Uploads disabled";
@@ -107,8 +120,17 @@ app.use(async (ctx, next) => {

// handle upload
try {
const auth = ctx.query.auth as string | undefined;
const contentType = ctx.header["content-type"];
const auth = (ctx.headers["authorization"] || ctx.query.auth) as string | undefined;
const authEvent = auth ? (JSON.parse(auth) as NostrEvent) : undefined;
if (config.upload.requireAuth && !authEvent) {
ctx.status = 403;
ctx.body = "Missing Authorization header";
return;
}

const pubkey = authEvent?.pubkey;
const authSize = authEvent ? parseInt(authEvent.tags.find((t) => t[0] === "size")?.[1] || "NaN") : undefined;

const rule = getFileRule(
{
@@ -125,6 +147,13 @@ app.use(async (ctx, next) => {
const metadata = await uploadModule.uploadWriteStream(ctx.req);
const mimeType = contentType || metadata.mimeType;

if (config.upload.requireAuth && metadata.size !== authSize) {
ctx.status = 403;
ctx.body = "Incorrect upload size";
await pfs.rm(metadata.tempFile);
return;
}

// save the file if its not already there
if (!db.data.blobs[metadata.hash]) {
setBlobSize(metadata.hash, metadata.size);
@@ -136,11 +165,15 @@ app.use(async (ctx, next) => {
await pfs.rm(metadata.tempFile);
}

if (pubkey) addPubkeyToBlob(metadata.hash, pubkey);

ctx.status = 200;
ctx.body = {
url: getBlobURL(metadata.hash),
created: db.data.blobs[metadata.hash].created,
sha256: metadata.hash,
type: mimeType,
size: metadata.size,
};
} catch (e) {
ctx.status = 403;
@@ -149,9 +182,7 @@ app.use(async (ctx, next) => {
});

// list blobs
app.use(async (ctx, next) => {
if (ctx.method !== "GET" || ctx.path !== "/list") return next();

router.get("/list", async (ctx) => {
const filter = ctx.query as { pubkey?: string };

ctx.status = 200;
@@ -166,6 +197,7 @@ app.use(async (ctx, next) => {
}));
});

app.use(router.routes()).use(router.allowedMethods());
app.use(serve(path.join(process.cwd(), "public")));

app.listen(3000);
60 changes: 59 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
@@ -230,6 +230,24 @@
human-id "^1.0.2"
prettier "^2.7.1"

"@koa/cors@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@koa/cors/-/cors-5.0.0.tgz#0029b5f057fa0d0ae0e37dd2c89ece315a0daffd"
integrity sha512-x/iUDjcS90W69PryLDIMgFyV21YLTnG9zOpPXS7Bkt2b8AsY3zZsIpOLBkYr9fBcF3HbkKaER5hOBZLfpLgYNw==
dependencies:
vary "^1.1.2"

"@koa/router@^12.0.1":
version "12.0.1"
resolved "https://registry.yarnpkg.com/@koa/router/-/router-12.0.1.tgz#1a66f92a630c02832cf5bbf0db06c9e53e423468"
integrity sha512-ribfPYfHb+Uw3b27Eiw6NPqjhIhTpVFzEWLwyc/1Xp+DCdwRRyIlAUODX+9bPARF6aQtUu1+/PHzdNvRzcs/+Q==
dependencies:
debug "^4.3.4"
http-errors "^2.0.0"
koa-compose "^4.1.0"
methods "^1.1.2"
path-to-regexp "^6.2.1"

"@manypkg/find-root@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@manypkg/find-root/-/find-root-1.1.0.tgz#a62d8ed1cd7e7d4c11d9d52a8397460b5d4ad29f"
@@ -476,6 +494,20 @@
"@types/koa-compose" "*"
"@types/node" "*"

"@types/koa__cors@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@types/koa__cors/-/koa__cors-5.0.0.tgz#74567a045b599266e2cd3940cef96cedecc2ef1f"
integrity sha512-LCk/n25Obq5qlernGOK/2LUwa/2YJb2lxHUkkvYFDOpLXlVI6tKcdfCHRBQnOY4LwH6el5WOLs6PD/a8Uzau6g==
dependencies:
"@types/koa" "*"

"@types/koa__router@^12.0.4":
version "12.0.4"
resolved "https://registry.yarnpkg.com/@types/koa__router/-/koa__router-12.0.4.tgz#a1f9afec9dc7e7d9fa1252d1938c44b403e19a28"
integrity sha512-Y7YBbSmfXZpa/m5UGGzb7XadJIRBRnwNY9cdAojZGp65Cpe5MAP3mOZE7e3bImt8dfKS4UFcR16SLH8L/z7PBw==
dependencies:
"@types/koa" "*"

"@types/mime@*":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45"
@@ -970,7 +1002,7 @@ delegates@^1.0.0:
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==

depd@^2.0.0, depd@~2.0.0:
depd@2.0.0, depd@^2.0.0, depd@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
@@ -1449,6 +1481,17 @@ http-errors@^1.6.3, http-errors@^1.7.3, http-errors@~1.8.0:
statuses ">= 1.5.0 < 2"
toidentifier "1.0.1"

http-errors@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
dependencies:
depd "2.0.0"
inherits "2.0.4"
setprototypeof "1.2.0"
statuses "2.0.1"
toidentifier "1.0.1"

http-errors@~1.6.2:
version "1.6.3"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
@@ -1899,6 +1942,11 @@ merge2@^1.3.0, merge2@^1.4.1:
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==

methods@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==

micromatch@^4.0.2, micromatch@^4.0.4:
version "4.0.5"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
@@ -2185,6 +2233,11 @@ path-parse@^1.0.7:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==

path-to-regexp@^6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5"
integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==

path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
@@ -2565,6 +2618,11 @@ sprintf-js@~1.0.2:
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==

statuses@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==

"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"

0 comments on commit fff38e4

Please sign in to comment.