identity/asset-api/index.js

152 lines
4 KiB
JavaScript
Raw Normal View History

2024-06-16 18:56:41 +00:00
import { readFile } from "node:fs/promises";
2024-06-16 18:39:10 +00:00
import { createWriteStream, readFileSync, writeFileSync } from "node:fs";
import { createSign, generateKeyPairSync, randomUUID } from "node:crypto";
2024-06-15 19:58:23 +00:00
import Fastify from "fastify";
2024-06-16 18:56:41 +00:00
import multipart from "@fastify/multipart";
2024-06-16 10:53:12 +00:00
import { join } from "node:path";
2024-06-16 18:56:41 +00:00
import mime from "mime";
2024-06-16 18:39:10 +00:00
import { promisify } from "node:util";
import { pipeline } from "node:stream";
2024-06-15 19:58:23 +00:00
2024-06-16 18:56:41 +00:00
const M2M_ALGORITHM = "RSA-SHA512";
const { private: M2M_PRIVATE_KEY, public: M2M_PUBLIC_KEY } = loadM2MKeys();
2024-06-16 10:53:12 +00:00
if (M2M_PRIVATE_KEY == null || M2M_PUBLIC_KEY == null) {
2024-06-16 18:56:41 +00:00
console.error("Couldn't load keys");
process.exit(1);
2024-06-16 10:53:12 +00:00
}
2024-06-16 18:56:41 +00:00
const ASSETS_FOLDER = "./.assets/";
const ASSET_API_LANDING_MESSAGE = "asset-api v1.0.0";
2024-06-15 19:58:23 +00:00
2024-06-16 18:56:41 +00:00
const IDENTITY_API_ENDPOINT = "http://localhost:3000";
2024-06-15 19:58:23 +00:00
const fastify = new Fastify({
logger: true,
2024-06-16 18:56:41 +00:00
});
2024-06-15 19:58:23 +00:00
2024-06-16 18:56:41 +00:00
fastify.register(multipart);
2024-06-16 18:39:10 +00:00
2024-06-16 18:56:41 +00:00
fastify.get("/", async () => {
return signString(ASSET_API_LANDING_MESSAGE);
});
2024-06-16 18:56:41 +00:00
fastify.get("/crypto/cert", async () => {
return M2M_PUBLIC_KEY;
});
2024-06-16 18:56:41 +00:00
fastify.get("/crypto/algo", () => {
return M2M_ALGORITHM;
});
2024-06-15 19:58:23 +00:00
2024-06-16 10:53:12 +00:00
fastify.put("/asset", {
2024-06-16 18:56:41 +00:00
async handler(request) {
await userFromSessionKey(request.query.session_key);
let file = await request.file();
2024-06-16 18:39:10 +00:00
2024-06-16 18:56:41 +00:00
let id = randomUUID().toString();
let extension = mime.getExtension(file.mimetype) || ".bin";
let full_id = `${id}.${extension}`;
2024-06-16 18:39:10 +00:00
2024-06-16 18:56:41 +00:00
let url = new URL(IDENTITY_API_ENDPOINT);
url.pathname = "/m2m/asset";
2024-06-16 18:39:10 +00:00
await fetch(url, {
method: "PUT",
body: signObject({
session_key: request.query.session_key,
asset_id: full_id,
2024-06-16 18:56:41 +00:00
}),
});
2024-06-16 10:53:12 +00:00
2024-06-16 18:56:41 +00:00
promisify(pipeline)(file.file, createWriteStream(`.assets/${full_id}`));
2024-06-16 10:53:12 +00:00
},
schema: {
query: {
type: "object",
properties: {
session_key: { type: "string" },
},
required: ["session_key"],
},
},
2024-06-16 18:56:41 +00:00
});
2024-06-16 10:53:12 +00:00
fastify.get("/asset", {
async handler(request, reply) {
2024-06-16 18:56:41 +00:00
let user = await userFromSessionKey(request.query.session_key);
2024-06-16 10:53:12 +00:00
if (user.assets.includes(request.query.asset_id)) {
2024-06-16 18:56:41 +00:00
let path = join(ASSETS_FOLDER, request.query.asset_id);
2024-06-16 18:56:41 +00:00
reply.type(mime.getType(path));
reply.send(await readFile(path));
2024-06-16 10:53:12 +00:00
} else {
2024-06-16 18:56:41 +00:00
return "Not authorized";
2024-06-16 10:53:12 +00:00
}
},
schema: {
query: {
type: "object",
properties: {
2024-06-16 10:53:12 +00:00
asset_id: { type: "string" },
session_key: { type: "string" },
},
2024-06-16 10:53:12 +00:00
required: ["asset_id", "session_key"],
},
},
2024-06-16 18:56:41 +00:00
});
2024-06-16 18:56:41 +00:00
fastify.listen({ port: 3001 });
function loadM2MKeys() {
2024-06-16 18:39:10 +00:00
try {
return {
private: readFileSync("./.keys/m2m.pem").toString("ascii"),
public: readFileSync("./.keys/m2m.pub").toString("ascii"),
2024-06-16 18:56:41 +00:00
};
} catch {
console.warn("Generating M2M key pair!");
2024-06-16 18:39:10 +00:00
let { publicKey, privateKey } = generateKeyPairSync("rsa", {
modulusLength: 4096,
publicKeyEncoding: {
2024-06-16 18:56:41 +00:00
type: "spki",
format: "pem",
2024-06-16 18:39:10 +00:00
},
privateKeyEncoding: {
2024-06-16 18:56:41 +00:00
type: "pkcs8",
format: "pem",
2024-06-16 18:39:10 +00:00
},
2024-06-16 18:56:41 +00:00
});
2024-06-16 18:39:10 +00:00
2024-06-16 18:56:41 +00:00
writeFileSync("./.keys/m2m.pem", privateKey);
writeFileSync("./.keys/m2m.pub", publicKey);
2024-06-16 18:39:10 +00:00
2024-06-16 18:56:41 +00:00
return loadM2MKeys();
}
}
function signString(content) {
2024-06-16 18:56:41 +00:00
let sign = createSign(M2M_ALGORITHM);
sign.update(content);
return `-----BEGIN SIGNED MESSAGE-----\n\n${content}\n\n-----BEGIN SIGNATURE-----\n\n${sign.sign(M2M_PRIVATE_KEY, "base64")}\n-----END SIGNATURE-----`;
2024-06-16 10:53:12 +00:00
}
function signObject(content) {
2024-06-16 18:56:41 +00:00
return signString(JSON.stringify(content));
2024-06-16 10:53:12 +00:00
}
async function userFromSessionKey(session_key) {
2024-06-16 18:56:41 +00:00
let url = new URL(IDENTITY_API_ENDPOINT);
url.pathname = "/m2m/account";
2024-06-16 10:53:12 +00:00
let res1 = await fetch(url, {
method: "POST",
body: signObject({
2024-06-16 18:39:10 +00:00
session_key,
2024-06-16 18:56:41 +00:00
}),
});
2024-06-16 10:53:12 +00:00
2024-06-16 18:56:41 +00:00
return await res1.json();
}