Local modifications to ClusterLabs/Anvil by Alteeve
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

237 lines
5.6 KiB

import session, {
SessionData,
Store as BaseSessionStore,
} from 'express-session';
import {
awrite,
dbQuery,
getLocalHostUUID,
timestamp,
} from './lib/accessModule';
import { getSessionSecret } from './lib/getSessionSecret';
import { isObject } from './lib/isObject';
import { stderr, stdout, stdoutVar, uuidgen } from './lib/shell';
const DEFAULT_COOKIE_ORIGINAL_MAX_AGE = 1000 * 60 * 60;
const getWriteCode = (obj: object) => {
let result: number | undefined;
if ('write_code' in obj) {
({ write_code: result } = obj as { write_code: number });
}
return result;
};
export class SessionStore extends BaseSessionStore {
constructor(options = {}) {
super(options);
}
public destroy(
sid: string,
done?: ((err?: unknown) => void) | undefined,
): void {
stdout(`Destroy session ${sid}`);
try {
awrite(`DELETE FROM sessions WHERE session_uuid = '${sid}';`, {
onClose({ stdout: s1 }) {
const wcode = getWriteCode(isObject(s1).obj);
if (wcode !== 0) {
stderr(
`SQL script failed during destroy session ${sid}; code: ${wcode}`,
);
}
},
onError(error) {
stderr(
`Failed to complete DB write in destroy session ${sid}; CAUSE: ${error}`,
);
},
});
} catch (error) {
return done?.call(null, error);
}
return done?.call(null);
}
public get(
sid: string,
done: (err: unknown, session?: SessionData | null | undefined) => void,
): void {
stdout(`Get session ${sid}`);
let rows: [
sessionUuid: string,
userUuid: string,
sessionModifiedDate: string,
][];
try {
rows = dbQuery(
`SELECT
s.session_uuid,
u.user_uuid,
s.modified_date
FROM sessions AS s
JOIN users AS u
ON s.session_user_uuid = u.user_uuid
WHERE s.session_uuid = '${sid}';`,
).stdout;
} catch (queryError) {
return done(queryError);
}
if (!rows.length) {
return done(null);
}
const {
0: [, userUuid, sessionModifiedDate],
} = rows;
const cookieMaxAge =
SessionStore.calculateCookieMaxAge(sessionModifiedDate);
const data: SessionData = {
cookie: {
maxAge: cookieMaxAge,
originalMaxAge: DEFAULT_COOKIE_ORIGINAL_MAX_AGE,
},
passport: {
user: userUuid,
},
};
return done(null, data);
}
public set(
sid: string,
session: SessionData,
done?: ((err?: unknown) => void) | undefined,
): void {
stdout(`Set session ${sid}`);
const {
passport: { user: userUuid },
} = session;
try {
const localHostUuid = getLocalHostUUID();
const modifiedDate = timestamp();
awrite(
`INSERT INTO
sessions (
session_uuid,
session_host_uuid,
session_user_uuid,
session_salt,
modified_date
)
VALUES
(
'${sid}',
'${localHostUuid}',
'${userUuid}',
'',
'${modifiedDate}'
)
ON CONFLICT (session_uuid)
DO UPDATE SET session_host_uuid = '${localHostUuid}',
modified_date = '${modifiedDate}';`,
{
onClose: ({ stdout: s1 }) => {
const wcode = getWriteCode(isObject(s1).obj);
if (wcode !== 0) {
stderr(
`SQL script failed during set session ${sid}; code: ${wcode}`,
);
}
},
onError: (error) => {
stderr(
`Failed to complete DB write in set session ${sid}; CAUSE: ${error}`,
);
},
},
);
} catch (error) {
return done?.call(null, error);
}
return done?.call(null);
}
public touch(
sid: string,
session: SessionData,
done?: ((err?: unknown) => void) | undefined,
): void {
stdout(`Touch session ${sid}`);
try {
awrite(
`UPDATE sessions SET modified_date = '${timestamp()}' WHERE session_uuid = '${sid}';`,
{
onClose: ({ stdout: s1 }) => {
const wcode = getWriteCode(isObject(s1).obj);
if (wcode !== 0) {
stderr(
`SQL script failed during touch session ${sid}; code: ${wcode}`,
);
}
},
onError: (error) => {
stderr(
`Failed to complete DB write in touch session ${sid}; CAUSE: ${error}`,
);
},
},
);
} catch (error) {
return done?.call(null, error);
}
return done?.call(null);
}
public static calculateCookieMaxAge(
sessionModifiedDate: string,
cookieOriginalMaxAge: number = DEFAULT_COOKIE_ORIGINAL_MAX_AGE,
) {
const sessionModifiedEpoch = Date.parse(sessionModifiedDate);
const sessionDeadlineEpoch = sessionModifiedEpoch + cookieOriginalMaxAge;
const cookieMaxAge = sessionDeadlineEpoch - Date.now();
stdoutVar({ sessionModifiedDate, sessionDeadlineEpoch, cookieMaxAge });
return cookieMaxAge;
}
}
const sessionHandler = session({
cookie: { maxAge: DEFAULT_COOKIE_ORIGINAL_MAX_AGE },
genid: ({ path }) => {
const sid = uuidgen('--random').trim();
stdout(`Generated session identifier ${sid}; request.path=${path}`);
return sid;
},
resave: false,
saveUninitialized: false,
secret: getSessionSecret(),
store: new SessionStore(),
});
export default sessionHandler;