forked from tsai/budibase
committed by
GitHub
24 changed files with 312 additions and 72 deletions
@ -0,0 +1,3 @@ |
|||||
|
module.exports = { |
||||
|
user: require("./src/cache/user"), |
||||
|
} |
||||
@ -0,0 +1 @@ |
|||||
|
module.exports = require("./src/security/sessions") |
||||
@ -0,0 +1,21 @@ |
|||||
|
const { getDB } = require("../db") |
||||
|
const { StaticDatabases } = require("../db/utils") |
||||
|
const redis = require("../redis/authRedis") |
||||
|
|
||||
|
const EXPIRY_SECONDS = 3600 |
||||
|
|
||||
|
exports.getUser = async userId => { |
||||
|
const client = await redis.getUserClient() |
||||
|
// try cache
|
||||
|
let user = await client.get(userId) |
||||
|
if (!user) { |
||||
|
user = await getDB(StaticDatabases.GLOBAL.name).get(userId) |
||||
|
client.store(userId, user, EXPIRY_SECONDS) |
||||
|
} |
||||
|
return user |
||||
|
} |
||||
|
|
||||
|
exports.invalidateUser = async userId => { |
||||
|
const client = await redis.getUserClient() |
||||
|
await client.delete(userId) |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
const Client = require("./index") |
||||
|
const utils = require("./utils") |
||||
|
|
||||
|
let userClient, sessionClient |
||||
|
|
||||
|
async function init() { |
||||
|
userClient = await new Client(utils.Databases.USER_CACHE).init() |
||||
|
sessionClient = await new Client(utils.Databases.SESSIONS).init() |
||||
|
} |
||||
|
|
||||
|
process.on("exit", async () => { |
||||
|
if (userClient) await userClient.finish() |
||||
|
if (sessionClient) await sessionClient.finish() |
||||
|
}) |
||||
|
|
||||
|
module.exports = { |
||||
|
getUserClient: async () => { |
||||
|
if (!userClient) { |
||||
|
await init() |
||||
|
} |
||||
|
return userClient |
||||
|
}, |
||||
|
getSessionClient: async () => { |
||||
|
if (!sessionClient) { |
||||
|
await init() |
||||
|
} |
||||
|
return sessionClient |
||||
|
}, |
||||
|
} |
||||
@ -0,0 +1,69 @@ |
|||||
|
const redis = require("../redis/authRedis") |
||||
|
|
||||
|
const EXPIRY_SECONDS = 86400 |
||||
|
|
||||
|
async function getSessionsForUser(userId) { |
||||
|
const client = await redis.getSessionClient() |
||||
|
const sessions = await client.scan(userId) |
||||
|
return sessions.map(session => session.value) |
||||
|
} |
||||
|
|
||||
|
function makeSessionID(userId, sessionId) { |
||||
|
return `${userId}/${sessionId}` |
||||
|
} |
||||
|
|
||||
|
exports.createASession = async (userId, sessionId) => { |
||||
|
const client = await redis.getSessionClient() |
||||
|
const session = { |
||||
|
createdAt: new Date().toISOString(), |
||||
|
lastAccessedAt: new Date().toISOString(), |
||||
|
sessionId, |
||||
|
userId, |
||||
|
} |
||||
|
await client.store(makeSessionID(userId, sessionId), session, EXPIRY_SECONDS) |
||||
|
} |
||||
|
|
||||
|
exports.invalidateSessions = async (userId, sessionId = null) => { |
||||
|
let sessions = [] |
||||
|
if (sessionId) { |
||||
|
sessions.push({ key: makeSessionID(userId, sessionId) }) |
||||
|
} else { |
||||
|
sessions = await getSessionsForUser(userId) |
||||
|
} |
||||
|
const client = await redis.getSessionClient() |
||||
|
const promises = [] |
||||
|
for (let session of sessions) { |
||||
|
promises.push(client.delete(session.key)) |
||||
|
} |
||||
|
await Promise.all(promises) |
||||
|
} |
||||
|
|
||||
|
exports.updateSessionTTL = async session => { |
||||
|
const client = await redis.getSessionClient() |
||||
|
const key = makeSessionID(session.userId, session.sessionId) |
||||
|
session.lastAccessedAt = new Date().toISOString() |
||||
|
await client.store(key, session, EXPIRY_SECONDS) |
||||
|
} |
||||
|
|
||||
|
exports.endSession = async (userId, sessionId) => { |
||||
|
const client = await redis.getSessionClient() |
||||
|
await client.delete(makeSessionID(userId, sessionId)) |
||||
|
} |
||||
|
|
||||
|
exports.getUserSessions = getSessionsForUser |
||||
|
|
||||
|
exports.getSession = async (userId, sessionId) => { |
||||
|
try { |
||||
|
const client = await redis.getSessionClient() |
||||
|
return client.get(makeSessionID(userId, sessionId)) |
||||
|
} catch (err) { |
||||
|
// if can't get session don't error, just don't return anything
|
||||
|
return null |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exports.getAllSessions = async () => { |
||||
|
const client = await redis.getSessionClient() |
||||
|
const sessions = await client.scan() |
||||
|
return sessions.map(session => session.value) |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
const { |
||||
|
getAllSessions, |
||||
|
getUserSessions, |
||||
|
invalidateSessions, |
||||
|
} = require("@budibase/auth/sessions") |
||||
|
|
||||
|
exports.fetch = async ctx => { |
||||
|
ctx.body = await getAllSessions() |
||||
|
} |
||||
|
|
||||
|
exports.find = async ctx => { |
||||
|
const { userId } = ctx.params |
||||
|
const sessions = await getUserSessions(userId) |
||||
|
ctx.body = sessions.map(session => session.value) |
||||
|
} |
||||
|
|
||||
|
exports.invalidateUser = async ctx => { |
||||
|
const { userId } = ctx.params |
||||
|
await invalidateSessions(userId) |
||||
|
ctx.body = { |
||||
|
message: "User sessions invalidated", |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exports.selfSessions = async ctx => { |
||||
|
const userId = ctx.user._id |
||||
|
ctx.body = await getUserSessions(userId) |
||||
|
} |
||||
|
|
||||
|
exports.invalidateSession = async ctx => { |
||||
|
const userId = ctx.user._id |
||||
|
const { sessionId } = ctx.params |
||||
|
await invalidateSessions(userId, sessionId) |
||||
|
ctx.body = { |
||||
|
message: "Session invalidated successfully.", |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
const Router = require("@koa/router") |
||||
|
const controller = require("../../controllers/admin/sessions") |
||||
|
const adminOnly = require("../../../middleware/adminOnly") |
||||
|
|
||||
|
const router = Router() |
||||
|
|
||||
|
router |
||||
|
.get("/api/admin/sessions", adminOnly, controller.fetch) |
||||
|
.get("/api/admin/sessions/self", controller.selfSessions) |
||||
|
.get("/api/admin/sessions/:userId", adminOnly, controller.find) |
||||
|
.delete("/api/admin/sessions/:userId", adminOnly, controller.invalidateUser) |
||||
|
.delete("/api/admin/sessions/self/:sessionId", controller.invalidateSession) |
||||
|
|
||||
|
module.exports = router |
||||
Loading…
Reference in new issue