add rotating keys
All checks were successful
Build and push image for doorman / docker (push) Successful in 1m49s
Build and push image for doorman / deploy-portainer (push) Successful in 26s

This commit is contained in:
Martin Dimitrov 2024-02-27 22:48:39 -08:00
parent f0ab000415
commit d92305e504
8 changed files with 59 additions and 4 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -10,6 +10,8 @@
"typescript": "^5.0.0"
},
"dependencies": {
"express-fingerprint": "^1.2.2"
"crypto": "^1.0.1",
"express-fingerprint": "^1.2.2",
"node-fetch": "^3.3.2"
}
}

View File

@ -1,8 +1,11 @@
import { Request, RequestHandler } from "express";
import { getRedisClient } from "../clients/db/RedisDbProvider";
import { getAuthModes, getDoorSettingString } from "../util/EnvConfigUtil";
import { getAllDoorNames, getAuthModes, getDoorSettingString } from "../util/EnvConfigUtil";
import { IAuthMode } from "../types/IAuthMode";
import { IDoorConfig } from "../types/IDoorConfig";
import { doorRotatingKey } from "../types/RedisKeys";
import crypto from "crypto";
import fetch from "node-fetch";
const client = await getRedisClient();
@ -14,12 +17,15 @@ export const HandleAuthMode: RequestHandler = async (req, res, next) => {
return;
}
const isAuthorized = authModes.some((mode) => {
const checkAuth = async (mode: IAuthMode): Promise<boolean> => {
switch(mode) {
case IAuthMode.FIXED_PIN: return handleFixedPinAuth(req)
case IAuthMode.RANDOM_ROTATING_KEY: return await handleRandomRotatingKeyAuth(req)
default: return false;
}
});
}
const isAuthorized = (await Promise.all(authModes.map((mode) => checkAuth(mode)))).some(b => b);
if (!isAuthorized) {
res.status(401).json({ msg: 'Unauthorized' });
@ -32,4 +38,28 @@ export const HandleAuthMode: RequestHandler = async (req, res, next) => {
const handleFixedPinAuth = (req: Request): boolean => {
const fixedPin = getDoorSettingString(req.params.id, IDoorConfig.FIXED_PIN);
return fixedPin !== undefined && req.query['key'] === fixedPin;
}
const handleRandomRotatingKeyAuth = async (req: Request): Promise<boolean> => {
const currentPin = await client.get(doorRotatingKey(req.params.id));
if (currentPin === req.query['rotatingKey']) {
await replaceDoorRandomKey(req.params.id);
return true;
}
return false;
}
export const initializeRandomDoorPins = () => {
const doors = getAllDoorNames();
doors.forEach(replaceDoorRandomKey);
}
export const replaceDoorRandomKey = async (door: string) => {
const newKey = crypto.randomBytes(20).toString('hex');
await client.put(doorRotatingKey(door), newKey);
const message = `New key for door ${door}! Unlock link: ${Bun.env.BASE_DOMAIN}/api/door/${door}/auth?rotatingKey=${newKey}`;
await fetch(Bun.env.ROTATING_KEY_NTFY, { method: "POST", body: message });
}

View File

@ -1,7 +1,11 @@
import express from "express";
import DoorRouter from "./routers/DoorRouter";
import { getAllDoorNames } from "./util/EnvConfigUtil";
import { getRedisClient } from "./clients/db/RedisDbProvider";
import { initializeRandomDoorPins } from "./middlewares/DoorAuthModes";
const Fingerprint = require('express-fingerprint');
const client = getRedisClient();
const app = express();
@ -25,4 +29,5 @@ app.use('/api/door', DoorRouter);
app.listen(5000, async () => {
console.log("listening on port 5000");
initializeRandomDoorPins();
});

View File

@ -5,5 +5,6 @@ declare module "bun" {
REDIS_CONNECT_URL: string; // `redis[s]://[[username][:password]@][host][:port][/db-number]`
DOOR_OPEN_TIMEOUT: number;
DOOR_FIXED_PIN: string;
ROTATING_KEY_NTFY: string;
}
}

View File

@ -1,3 +1,4 @@
export enum IAuthMode {
FIXED_PIN = "FIXED_PIN",
RANDOM_ROTATING_KEY = "RANDOM_ROTATING_KEY",
}

View File

@ -7,6 +7,10 @@ export function doorStatusKey(id: string) {
return concatKeys(RedisKeys.DOORS, id, 'open');
}
export function doorRotatingKey(id: string) {
return concatKeys(RedisKeys.DOORS, id, 'rotatingKey');
}
export function concatKeys(...keys: String[]) {
return keys.join(':');
}

View File

@ -36,4 +36,16 @@ export const getDoorSettingTimeLock = (door: string): number[] => {
// never locked (always -1 < hr < 25)
return [25, -1];
}
export const getAllDoorNames = (): string[] => {
const names: string[] = [];
Object.keys(Bun.env).forEach(key => {
if (key.startsWith(IDoorConfig.AUTH_MODES)) {
names.push(key.replace(IDoorConfig.AUTH_MODES + "_", "").toLowerCase());
}
});
return names;
}