Add audit log

main
Lea 2024-01-18 16:53:17 +01:00
parent 9c1f7d5739
commit 3964346db0
Signed by: Lea
GPG Key ID: 1BAFFE8347019C42
3 changed files with 47 additions and 5 deletions

1
.env
View File

@ -1,3 +1,4 @@
NEXTAUTH_SECRET=changeme
CREDENTIALS_DB_PATH=/home/lea/git/maddy-admin/data/credentials.db
ALIASES_DB_PATH=/home/lea/git/maddy-admin/data/aliases.db
AUDIT_FILE_PATH=/home/lea/git/maddy-admin/data/panel.log

View File

@ -3,6 +3,7 @@
import { getServerSession } from "next-auth";
import { AliasEntry, approveAliasEntry, createAliasEntry, createUserEntry, database, deleteAliasEntry, getAlias, getAllAliases, getUserAliases, setUserPassword } from "./db";
import { aliasesNeedApproval, isAdmin } from "./util";
import { auditLog } from "./audit";
export async function fetchAllUsers(): Promise<string[]> {
return new Promise(async (resolve, reject) => {
@ -23,6 +24,7 @@ export async function changeOwnPassword(newPass: string) {
const session = await getServerSession();
if (!session?.user?.email) throw new Error("Unauthenticated");
await setUserPassword(session.user.email, newPass);
auditLog("changeOwnPassword");
}
export async function fetchOwnAliases() {
@ -75,13 +77,15 @@ export async function createAlias(user: string, alias: string): Promise<AliasEnt
if (!await aliasAvailable(alias)) throw new Error("Alias unavailable");
const id = await createAliasEntry(user, alias.toLowerCase(), false);
return {
const res = {
id: id,
address: session.user.email,
alias: alias,
pending: false
pending: false,
};
auditLog('createAlias', res);
return res;
}
export async function createAliasSelf(alias: string): Promise<AliasEntry> {
@ -90,14 +94,17 @@ export async function createAliasSelf(alias: string): Promise<AliasEntry> {
const pending = aliasesNeedApproval(session);
if (!await aliasAvailable(alias)) throw new Error("Alias unavailable");
const id = await createAliasEntry(session.user.email, alias.toLowerCase(), pending);
return {
const id = await createAliasEntry(session.user.email, alias.toLowerCase(), pending);
const res = {
id: id,
address: session.user.email,
alias: alias,
pending: pending
};
auditLog('requestAlias', res);
return res;
}
export async function deleteAlias(alias: string) {
@ -109,6 +116,7 @@ export async function deleteAlias(alias: string) {
}
await deleteAliasEntry(alias);
auditLog('deleteAlias', alias);
}
export async function approveAlias(alias: string) {
@ -117,6 +125,7 @@ export async function approveAlias(alias: string) {
if (!isAdmin(session)) throw new Error("Unauthorized");
await approveAliasEntry(alias);
auditLog('approveAlias', alias);
}
export async function createUser(email: string, password: string) {
@ -133,4 +142,5 @@ export async function createUser(email: string, password: string) {
}
await createUserEntry(email, password);
auditLog('createUser', email);
}

31
src/lib/audit.ts Normal file
View File

@ -0,0 +1,31 @@
import { Session, getServerSession } from "next-auth";
import fs from "fs/promises";
export type AuditLogAction = 'login' | 'changeOwnPassword' | 'requestAlias' | 'deleteAlias' // Unprivileged
| 'createUser' | 'createAlias' | 'approveAlias' | 'deleteAlias'; // Privileged
export function auditLog(action: AuditLogAction, data?: any) {
(async () => {
try {
const session = await getServerSession();
const log = {
user: session?.user?.email,
ts: Date.now(),
action: action,
data: data,
};
console.log("Audit event:", log);
if (process.env.AUDIT_FILE_PATH) {
await fs.appendFile(
process.env.AUDIT_FILE_PATH,
JSON.stringify(log) + "\n",
);
}
} catch (e) {
console.error("Failed to write to log file:", e);
}
})();
}