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.
111 lines
3.4 KiB
111 lines
3.4 KiB
import type { H3Event } from 'h3'
|
|
import { eq } from 'drizzle-orm'
|
|
import { users } from '../database/schema'
|
|
import { getUserApprovedRoleCodes } from './roles'
|
|
|
|
/**
|
|
* Role validation cache TTL (Time To Live)
|
|
* Roles are re-validated against DB after this period
|
|
*/
|
|
const ROLE_VALIDATION_TTL = 5 * 60 * 1000 // 5 minutes in milliseconds
|
|
|
|
/**
|
|
* Get user's active role with automatic validation
|
|
*
|
|
* Features:
|
|
* - Returns cached role from session if TTL hasn't expired
|
|
* - Re-validates against DB after TTL expires (every 5 minutes)
|
|
* - Auto-fallback if active role was revoked by admin
|
|
* - Updates session automatically with validated data
|
|
*
|
|
* @param event - H3 Event from API handler
|
|
* @returns Active role code (e.g., 'private', 'educator', 'company')
|
|
* @throws 401 if user is not authenticated
|
|
*/
|
|
export async function getUserActiveRole(event: H3Event): Promise<string> {
|
|
const session = await getUserSession(event)
|
|
|
|
if (!session?.user) {
|
|
throw createError({
|
|
statusCode: 401,
|
|
message: 'Not authenticated',
|
|
})
|
|
}
|
|
|
|
const now = Date.now()
|
|
const lastValidated = session.rolesLastValidated
|
|
? new Date(session.rolesLastValidated).getTime()
|
|
: 0
|
|
const needsRevalidation = now - lastValidated > ROLE_VALIDATION_TTL
|
|
|
|
if (needsRevalidation) {
|
|
// TTL expired → Re-validate against database
|
|
const currentRoles = await getUserApprovedRoleCodes(session.user.id)
|
|
const activeRole = session.activeRoleCode || 'private'
|
|
|
|
// Check: Was active role revoked by admin?
|
|
if (!currentRoles.includes(activeRole)) {
|
|
// Auto-fallback to first available role
|
|
const newActiveRole = currentRoles[0] || 'private'
|
|
|
|
await replaceUserSession(event, {
|
|
activeRoleCode: newActiveRole,
|
|
userApprovedRoles: currentRoles,
|
|
rolesLastValidated: new Date().toISOString(),
|
|
roleChangedByAdmin: true, // Flag for client notification
|
|
})
|
|
|
|
return newActiveRole
|
|
}
|
|
|
|
// Role still valid, just update cache + timestamp
|
|
await replaceUserSession(event, {
|
|
userApprovedRoles: currentRoles,
|
|
rolesLastValidated: new Date().toISOString(),
|
|
})
|
|
}
|
|
|
|
return session.activeRoleCode || 'private'
|
|
}
|
|
|
|
/**
|
|
* Set user's active role with validation
|
|
*
|
|
* Validates that user has the requested role before switching.
|
|
* Updates both session (immediate) and database (preference for next login).
|
|
*
|
|
* @param event - H3 Event from API handler
|
|
* @param roleCode - Role to switch to (must be in user's approved roles)
|
|
* @throws 401 if not authenticated
|
|
* @throws 403 if user doesn't have the requested role
|
|
*/
|
|
export async function setUserActiveRole(
|
|
event: H3Event,
|
|
roleCode: string
|
|
): Promise<void> {
|
|
const session = await requireUserSession(event)
|
|
|
|
// Validate that user has this role
|
|
const approvedRoles = await getUserApprovedRoleCodes(session.user.id)
|
|
|
|
if (!approvedRoles.includes(roleCode)) {
|
|
throw createError({
|
|
statusCode: 403,
|
|
message: 'Du hast diese Rolle nicht',
|
|
})
|
|
}
|
|
|
|
// Update session with new active role
|
|
await replaceUserSession(event, {
|
|
activeRoleCode: roleCode,
|
|
roleChangedByAdmin: false, // User switched manually
|
|
rolesLastValidated: new Date().toISOString(), // Reset TTL
|
|
})
|
|
|
|
// Update database: Save as user preference for next login
|
|
const db = useDatabase()
|
|
await db
|
|
.update(users)
|
|
.set({ lastActiveRoleCode: roleCode })
|
|
.where(eq(users.id, session.user.id))
|
|
}
|
|
|