Adding sessions API.
This commit is contained in:
parent
278d984006
commit
cc67e2caa6
|
@ -68,7 +68,7 @@ module.exports = (noAuthPatterns = [], opts) => {
|
||||||
clearCookie(ctx, Cookies.Auth)
|
clearCookie(ctx, Cookies.Auth)
|
||||||
} else {
|
} else {
|
||||||
// make sure we denote that the session is still in use
|
// make sure we denote that the session is still in use
|
||||||
await updateSessionTTL(userId, sessionId)
|
await updateSessionTTL(session)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const apiKey = ctx.request.headers["x-budibase-api-key"]
|
const apiKey = ctx.request.headers["x-budibase-api-key"]
|
||||||
|
|
|
@ -62,14 +62,12 @@ async function authenticate(token, tokenSecret, profile, done) {
|
||||||
|
|
||||||
// authenticate
|
// authenticate
|
||||||
const sessionId = newid()
|
const sessionId = newid()
|
||||||
const payload = {
|
await createASession(dbUser._id, sessionId)
|
||||||
|
|
||||||
|
dbUser.token = jwt.sign({
|
||||||
userId: dbUser._id,
|
userId: dbUser._id,
|
||||||
sessionId,
|
sessionId,
|
||||||
}
|
}, env.JWT_SECRET)
|
||||||
await createASession(dbUser._id, sessionId, payload)
|
|
||||||
dbUser.sessionId = sessionId
|
|
||||||
|
|
||||||
dbUser.token = jwt.sign(payload, env.JWT_SECRET)
|
|
||||||
|
|
||||||
return done(null, dbUser)
|
return done(null, dbUser)
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,14 +34,12 @@ exports.authenticate = async function (email, password, done) {
|
||||||
// authenticate
|
// authenticate
|
||||||
if (await compare(password, dbUser.password)) {
|
if (await compare(password, dbUser.password)) {
|
||||||
const sessionId = newid()
|
const sessionId = newid()
|
||||||
const payload = {
|
await createASession(dbUser._id, sessionId)
|
||||||
|
|
||||||
|
dbUser.token = jwt.sign({
|
||||||
userId: dbUser._id,
|
userId: dbUser._id,
|
||||||
sessionId,
|
sessionId,
|
||||||
}
|
}, env.JWT_SECRET)
|
||||||
await createASession(dbUser._id, sessionId, payload)
|
|
||||||
dbUser.sessionId = sessionId
|
|
||||||
|
|
||||||
dbUser.token = jwt.sign(payload, env.JWT_SECRET)
|
|
||||||
// Remove users password in payload
|
// Remove users password in payload
|
||||||
delete dbUser.password
|
delete dbUser.password
|
||||||
|
|
||||||
|
|
|
@ -4,16 +4,23 @@ const EXPIRY_SECONDS = 86400
|
||||||
|
|
||||||
async function getSessionsForUser(userId) {
|
async function getSessionsForUser(userId) {
|
||||||
const client = await redis.getSessionClient()
|
const client = await redis.getSessionClient()
|
||||||
return client.scan(userId)
|
const sessions = await client.scan(userId)
|
||||||
|
return sessions.map(session => session.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeSessionID(userId, sessionId) {
|
function makeSessionID(userId, sessionId) {
|
||||||
return `${userId}/${sessionId}`
|
return `${userId}/${sessionId}`
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.createASession = async (userId, sessionId, token) => {
|
exports.createASession = async (userId, sessionId) => {
|
||||||
const client = await redis.getSessionClient()
|
const client = await redis.getSessionClient()
|
||||||
await client.store(makeSessionID(userId, sessionId), token, EXPIRY_SECONDS)
|
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) => {
|
exports.invalidateSessions = async (userId, sessionId = null) => {
|
||||||
|
@ -31,9 +38,11 @@ exports.invalidateSessions = async (userId, sessionId = null) => {
|
||||||
await Promise.all(promises)
|
await Promise.all(promises)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.updateSessionTTL = async (userId, sessionId) => {
|
exports.updateSessionTTL = async session => {
|
||||||
const client = await redis.getSessionClient()
|
const client = await redis.getSessionClient()
|
||||||
await client.setExpiry(makeSessionID(userId, sessionId), EXPIRY_SECONDS)
|
const key = makeSessionID(session.userId, session.sessionId)
|
||||||
|
session.lastAccessedAt = (new Date()).toISOString()
|
||||||
|
await client.store(key, session, EXPIRY_SECONDS)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.endSession = async (userId, sessionId) => {
|
exports.endSession = async (userId, sessionId) => {
|
||||||
|
@ -41,6 +50,8 @@ exports.endSession = async (userId, sessionId) => {
|
||||||
await client.delete(makeSessionID(userId, sessionId))
|
await client.delete(makeSessionID(userId, sessionId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.getUserSessions = getSessionsForUser
|
||||||
|
|
||||||
exports.getSession = async (userId, sessionId) => {
|
exports.getSession = async (userId, sessionId) => {
|
||||||
try {
|
try {
|
||||||
const client = await redis.getSessionClient()
|
const client = await redis.getSessionClient()
|
||||||
|
@ -53,5 +64,6 @@ exports.getSession = async (userId, sessionId) => {
|
||||||
|
|
||||||
exports.getAllSessions = async () => {
|
exports.getAllSessions = async () => {
|
||||||
const client = await redis.getSessionClient()
|
const client = await redis.getSessionClient()
|
||||||
return client.scan()
|
const sessions = await client.scan()
|
||||||
|
return sessions.map(session => session.value)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
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."
|
||||||
|
}
|
||||||
|
}
|
|
@ -122,13 +122,16 @@ exports.removeAppRole = async ctx => {
|
||||||
const db = new CouchDB(GLOBAL_DB)
|
const db = new CouchDB(GLOBAL_DB)
|
||||||
const users = await allUsers()
|
const users = await allUsers()
|
||||||
const bulk = []
|
const bulk = []
|
||||||
|
const cacheInvalidations = []
|
||||||
for (let user of users) {
|
for (let user of users) {
|
||||||
if (user.roles[appId]) {
|
if (user.roles[appId]) {
|
||||||
|
cacheInvalidations.push(userCache.invalidateUser(user._id))
|
||||||
delete user.roles[appId]
|
delete user.roles[appId]
|
||||||
bulk.push(user)
|
bulk.push(user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await db.bulkDocs(bulk)
|
await db.bulkDocs(bulk)
|
||||||
|
await Promise.all(cacheInvalidations)
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
message: "App role removed from all users",
|
message: "App role removed from all users",
|
||||||
}
|
}
|
||||||
|
@ -158,6 +161,7 @@ exports.updateSelf = async ctx => {
|
||||||
...user,
|
...user,
|
||||||
...ctx.request.body,
|
...ctx.request.body,
|
||||||
})
|
})
|
||||||
|
await userCache.invalidateUser(user._id)
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
_id: response.id,
|
_id: response.id,
|
||||||
_rev: response.rev,
|
_rev: response.rev,
|
||||||
|
|
|
@ -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
|
|
@ -5,6 +5,7 @@ const templateRoutes = require("./admin/templates")
|
||||||
const emailRoutes = require("./admin/email")
|
const emailRoutes = require("./admin/email")
|
||||||
const authRoutes = require("./admin/auth")
|
const authRoutes = require("./admin/auth")
|
||||||
const roleRoutes = require("./admin/roles")
|
const roleRoutes = require("./admin/roles")
|
||||||
|
const sessionRoutes = require("./admin/sessions")
|
||||||
const appRoutes = require("./app")
|
const appRoutes = require("./app")
|
||||||
|
|
||||||
exports.routes = [
|
exports.routes = [
|
||||||
|
@ -15,5 +16,6 @@ exports.routes = [
|
||||||
appRoutes,
|
appRoutes,
|
||||||
templateRoutes,
|
templateRoutes,
|
||||||
emailRoutes,
|
emailRoutes,
|
||||||
|
sessionRoutes,
|
||||||
roleRoutes,
|
roleRoutes,
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue