1
0
Fork 0
mirror of synced 2024-09-21 11:53:49 +12:00
budibase/packages/backend-core/src/security/sessions.ts

116 lines
3.4 KiB
TypeScript
Raw Normal View History

const redis = require("../redis/init")
2022-01-26 11:54:50 +13:00
const { v4: uuidv4 } = require("uuid")
const { logWarn } = require("../logging")
const env = require("../environment")
2021-07-07 05:10:04 +12:00
interface Session {
key: string
userId: string
sessionId: string
lastAccessedAt: string
createdAt: string
csrfToken?: string
value: string
}
type SessionKey = { key: string }[]
// a week in seconds
const EXPIRY_SECONDS = 86400 * 7
2021-07-07 05:10:04 +12:00
function makeSessionID(userId: string, sessionId: string) {
return `${userId}/${sessionId}`
2021-07-07 05:10:04 +12:00
}
export async function getSessionsForUser(userId: string) {
const client = await redis.getSessionClient()
const sessions = await client.scan(userId)
return sessions.map((session: Session) => session.value)
2021-07-07 05:10:04 +12:00
}
export async function invalidateSessions(
userId: string,
opts: { sessionIds?: string[]; reason?: string } = {}
) {
2022-05-25 09:57:32 +12:00
try {
const reason = opts?.reason || "unknown"
let sessionIds: string[] = opts.sessionIds || []
let sessions: SessionKey
2022-05-25 09:57:32 +12:00
// If no sessionIds, get all the sessions for the user
if (sessionIds.length === 0) {
2022-05-25 09:57:32 +12:00
sessions = await getSessionsForUser(userId)
sessions.forEach(
(session: any) =>
2022-05-25 09:57:32 +12:00
(session.key = makeSessionID(session.userId, session.sessionId))
)
} else {
// use the passed array of sessionIds
sessionIds = Array.isArray(sessionIds) ? sessionIds : [sessionIds]
sessions = sessionIds.map((sessionId: string) => ({
2022-05-25 09:57:32 +12:00
key: makeSessionID(userId, sessionId),
}))
}
if (sessions && sessions.length > 0) {
const client = await redis.getSessionClient()
const promises = []
for (let session of sessions) {
promises.push(client.delete(session.key))
}
if (!env.isTest()) {
logWarn(
`Invalidating sessions for ${userId} (reason: ${reason}) - ${sessions
.map(session => session.key)
.join(", ")}`
)
}
await Promise.all(promises)
2022-05-25 09:57:32 +12:00
}
} catch (err) {
console.error(`Error invalidating sessions: ${err}`)
2021-07-07 05:10:04 +12:00
}
}
export async function createASession(userId: string, session: Session) {
// invalidate all other sessions
await invalidateSessions(userId, { reason: "creation" })
const client = await redis.getSessionClient()
const sessionId = session.sessionId
if (!session.csrfToken) {
session.csrfToken = uuidv4()
}
session = {
...session,
createdAt: new Date().toISOString(),
lastAccessedAt: new Date().toISOString(),
userId,
}
await client.store(makeSessionID(userId, sessionId), session, EXPIRY_SECONDS)
}
export async function updateSessionTTL(session: Session) {
2021-07-07 05:10:04 +12:00
const client = await redis.getSessionClient()
2021-07-08 10:29:19 +12:00
const key = makeSessionID(session.userId, session.sessionId)
2021-07-08 10:30:14 +12:00
session.lastAccessedAt = new Date().toISOString()
2021-07-08 10:29:19 +12:00
await client.store(key, session, EXPIRY_SECONDS)
2021-07-07 05:10:04 +12:00
}
export async function endSession(userId: string, sessionId: string) {
2021-07-07 05:10:04 +12:00
const client = await redis.getSessionClient()
await client.delete(makeSessionID(userId, sessionId))
}
export async function getSession(userId: string, sessionId: string) {
if (!userId || !sessionId) {
throw new Error(`Invalid session details - ${userId} - ${sessionId}`)
2021-07-07 05:10:04 +12:00
}
const client = await redis.getSessionClient()
const session = await client.get(makeSessionID(userId, sessionId))
if (!session) {
throw new Error(`Session not found - ${userId} - ${sessionId}`)
}
return session
2021-07-07 05:10:04 +12:00
}