gfsd
This commit is contained in:
+74
-6
@@ -86,14 +86,57 @@ export function verifySignature(sshPubkey, message, signature) {
|
||||
}
|
||||
}
|
||||
|
||||
import security from './security.mjs';
|
||||
import { getHashUserIps } from './settings.mjs';
|
||||
|
||||
/**
|
||||
* Get IP for audit/logging, hashed if hash_user_ips is enabled in config.
|
||||
* @param {object} req
|
||||
* @returns {string}
|
||||
*/
|
||||
export function resolveAuditIP(req) {
|
||||
if (!req) return 'unknown';
|
||||
const rawIp = security.getRealIP(req);
|
||||
return getHashUserIps() ? security.hashIP(rawIp) : rawIp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log activity for an anonymous user (or session).
|
||||
* @param {object} req
|
||||
* @param {{ action: string, targetId?: number|string, details?: object, hwFingerprint?: string }} params
|
||||
*/
|
||||
export async function logAnonActivity(req, { action, targetId = null, details = null, hwFingerprint = null } = {}) {
|
||||
try {
|
||||
const rawIp = security.getRealIP(req);
|
||||
const ip = getHashUserIps() ? security.hashIP(rawIp) : rawIp;
|
||||
const userId = req?.session?.id || null;
|
||||
if (!userId) return;
|
||||
const fingerprint = req?.session?.fingerprint || req?.session?.anon_fingerprint || null;
|
||||
const hwFp = hwFingerprint || req?.session?.hw_fingerprint || null;
|
||||
const numTargetId = targetId ? parseInt(targetId, 10) : null;
|
||||
|
||||
await db`
|
||||
INSERT INTO anon_activity_log (user_id, fingerprint, hw_fingerprint, ip, action, target_id, details)
|
||||
VALUES (${userId}, ${fingerprint}, ${hwFp}, ${ip}, ${action}, ${!isNaN(numTargetId) ? numTargetId : null}, ${details ? JSON.stringify(details) : null})
|
||||
`;
|
||||
|
||||
await security.logUserIP(userId, rawIp);
|
||||
} catch (err) {
|
||||
console.error('[ANON_ACTIVITY_LOG] Failed to log activity:', err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find or create a shadow user in the database for an anonymous SSH identity.
|
||||
* @param {string} pubkey
|
||||
* @param {string} fingerprint
|
||||
* @param {object} [req]
|
||||
* @param {string} [hwFingerprint]
|
||||
* @returns {Promise<{ userId: number, isNew: boolean }>}
|
||||
*/
|
||||
export async function getOrCreateAnonUser(pubkey, fingerprint) {
|
||||
export async function getOrCreateAnonUser(pubkey, fingerprint, req = null, hwFingerprint = null) {
|
||||
const normPubkey = pubkey.trim();
|
||||
const auditIp = req ? resolveAuditIP(req) : null;
|
||||
const existing = await db`
|
||||
SELECT user_id FROM anon_identities
|
||||
WHERE pubkey = ${normPubkey}
|
||||
@@ -101,7 +144,13 @@ export async function getOrCreateAnonUser(pubkey, fingerprint) {
|
||||
`;
|
||||
|
||||
if (existing.length > 0) {
|
||||
await db`UPDATE anon_identities SET last_seen = NOW() WHERE pubkey = ${normPubkey}`;
|
||||
await db`
|
||||
UPDATE anon_identities
|
||||
SET last_seen = NOW()
|
||||
${auditIp ? db`, last_ip = ${auditIp}` : db``}
|
||||
${hwFingerprint ? db`, hw_fingerprint = ${hwFingerprint}` : db``}
|
||||
WHERE pubkey = ${normPubkey}
|
||||
`;
|
||||
return { userId: existing[0].user_id, isNew: false };
|
||||
}
|
||||
|
||||
@@ -131,9 +180,12 @@ export async function getOrCreateAnonUser(pubkey, fingerprint) {
|
||||
`;
|
||||
|
||||
await db`
|
||||
INSERT INTO anon_identities (user_id, pubkey, fingerprint)
|
||||
VALUES (${userId}, ${normPubkey}, ${fingerprint})
|
||||
ON CONFLICT (pubkey) DO NOTHING
|
||||
INSERT INTO anon_identities (user_id, pubkey, fingerprint, created_ip, last_ip, hw_fingerprint)
|
||||
VALUES (${userId}, ${normPubkey}, ${fingerprint}, ${auditIp}, ${auditIp}, ${hwFingerprint})
|
||||
ON CONFLICT (pubkey) DO UPDATE
|
||||
SET last_seen = NOW()
|
||||
${auditIp ? db`, last_ip = ${auditIp}` : db``}
|
||||
${hwFingerprint ? db`, hw_fingerprint = ${hwFingerprint}` : db``}
|
||||
`;
|
||||
|
||||
return { userId, isNew: true };
|
||||
@@ -143,11 +195,24 @@ export async function getOrCreateAnonUser(pubkey, fingerprint) {
|
||||
* Create a valid session in user_sessions for this anonymous user.
|
||||
* @param {number} userId
|
||||
* @param {object} req
|
||||
* @param {string} [hwFingerprint]
|
||||
* @returns {Promise<{ session: string, csrf_token: string }>}
|
||||
*/
|
||||
export async function createAnonSession(userId, req) {
|
||||
export async function createAnonSession(userId, req, hwFingerprint = null) {
|
||||
const auditIp = resolveAuditIP(req);
|
||||
|
||||
// Update anon_identities last_ip, created_ip, and hw_fingerprint
|
||||
await db`
|
||||
UPDATE anon_identities
|
||||
SET last_ip = ${auditIp},
|
||||
created_ip = COALESCE(created_ip, ${auditIp})
|
||||
${hwFingerprint ? db`, hw_fingerprint = COALESCE(${hwFingerprint}, hw_fingerprint)` : db``}
|
||||
WHERE user_id = ${userId}
|
||||
`.catch(() => {});
|
||||
|
||||
// 1. If req.session is already active for this exact userId, reuse it!
|
||||
if (req?.session && req.session.id === userId && req.session.csrf_token && req.cookies?.session) {
|
||||
await logAnonActivity(req, { action: 'handshake', hwFingerprint });
|
||||
return { session: req.cookies.session, csrf_token: req.session.csrf_token };
|
||||
}
|
||||
|
||||
@@ -161,6 +226,7 @@ export async function createAnonSession(userId, req) {
|
||||
`;
|
||||
if (existing.length > 0) {
|
||||
await db`UPDATE user_sessions SET last_used = ${~~(Date.now() / 1e3)} WHERE session = ${existingHash}`;
|
||||
await logAnonActivity({ ...req, session: { id: userId, is_anon: true } }, { action: 'handshake', hwFingerprint });
|
||||
return { session: req.cookies.session, csrf_token: existing[0].csrf_token };
|
||||
}
|
||||
}
|
||||
@@ -188,5 +254,7 @@ export async function createAnonSession(userId, req) {
|
||||
INSERT INTO "user_sessions" ${db(sessRecord, 'user_id', 'session', 'csrf_token', 'browser', 'created_at', 'last_used', 'last_action', 'kmsi', 'ip')}
|
||||
`;
|
||||
|
||||
await logAnonActivity({ ...req, session: { id: userId, is_anon: true } }, { action: 'handshake', hwFingerprint });
|
||||
|
||||
return { session, csrf_token: csrfToken };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user