mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
125 lines
2.9 KiB
TypeScript
125 lines
2.9 KiB
TypeScript
import becca from "../becca/becca.js";
|
|
import utils from "./utils.js";
|
|
import BEtapiToken from "../becca/entities/betapi_token.js";
|
|
import crypto from "crypto";
|
|
|
|
function getTokens() {
|
|
return becca.getEtapiTokens();
|
|
}
|
|
|
|
function getTokenHash(token: crypto.BinaryLike) {
|
|
return crypto.createHash('sha256').update(token).digest('base64');
|
|
}
|
|
|
|
function createToken(tokenName: string) {
|
|
const token = utils.randomSecureToken(32);
|
|
const tokenHash = getTokenHash(token);
|
|
|
|
const etapiToken = new BEtapiToken({
|
|
name: tokenName,
|
|
tokenHash
|
|
}).save();
|
|
|
|
return {
|
|
authToken: `${etapiToken.etapiTokenId}_${token}`
|
|
};
|
|
}
|
|
|
|
function parseAuthToken(auth: string | undefined) {
|
|
if (!auth) {
|
|
return null;
|
|
}
|
|
|
|
if (auth.startsWith("Basic ")) {
|
|
// allow also basic auth format for systems which allow this type of authentication
|
|
// expect ETAPI token in the password field, require "etapi" username
|
|
// https://github.com/zadam/trilium/issues/3181
|
|
const basicAuthStr = utils.fromBase64(auth.substring(6)).toString("utf-8");
|
|
const basicAuthChunks = basicAuthStr.split(":");
|
|
|
|
if (basicAuthChunks.length !== 2) {
|
|
return null;
|
|
}
|
|
|
|
if (basicAuthChunks[0] !== "etapi") {
|
|
return null;
|
|
}
|
|
|
|
auth = basicAuthChunks[1];
|
|
}
|
|
|
|
const chunks = auth.split("_");
|
|
|
|
if (chunks.length === 1) {
|
|
return { token: auth }; // legacy format without etapiTokenId
|
|
}
|
|
else if (chunks.length === 2) {
|
|
return {
|
|
etapiTokenId: chunks[0],
|
|
token: chunks[1]
|
|
}
|
|
}
|
|
else {
|
|
return null; // wrong format
|
|
}
|
|
}
|
|
|
|
function isValidAuthHeader(auth: string | undefined) {
|
|
const parsed = parseAuthToken(auth);
|
|
|
|
if (!parsed) {
|
|
return false;
|
|
}
|
|
|
|
const authTokenHash = getTokenHash(parsed.token);
|
|
|
|
if (parsed.etapiTokenId) {
|
|
const etapiToken = becca.getEtapiToken(parsed.etapiTokenId);
|
|
|
|
if (!etapiToken) {
|
|
return false;
|
|
}
|
|
|
|
return etapiToken.tokenHash === authTokenHash;
|
|
}
|
|
else {
|
|
for (const etapiToken of becca.getEtapiTokens()) {
|
|
if (etapiToken.tokenHash === authTokenHash) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function renameToken(etapiTokenId: string, newName: string) {
|
|
const etapiToken = becca.getEtapiToken(etapiTokenId);
|
|
|
|
if (!etapiToken) {
|
|
throw new Error(`Token '${etapiTokenId}' does not exist`);
|
|
}
|
|
|
|
etapiToken.name = newName;
|
|
etapiToken.save();
|
|
}
|
|
|
|
function deleteToken(etapiTokenId: string) {
|
|
const etapiToken = becca.getEtapiToken(etapiTokenId);
|
|
|
|
if (!etapiToken) {
|
|
return; // ok, already deleted
|
|
}
|
|
|
|
etapiToken.markAsDeletedSimple();
|
|
}
|
|
|
|
export = {
|
|
getTokens,
|
|
createToken,
|
|
renameToken,
|
|
deleteToken,
|
|
parseAuthToken,
|
|
isValidAuthHeader
|
|
};
|