1 parent
23844ae301
commit
d25b5dbe6d
268 files changed
+3684
-8861
No files matched your search
@@ -45,33 +45,24 @@ export const requireAuth: MiddlewareHandler<AppEnv> = async (c, next) => {
|
||||
* 与旧 `BasePermissionDecorator._permission_error` 的两分支一致 —— 前端 `utils/api.ts`
|
||||
* 的拦截器就是按这两个 code 分别弹登录框和弹提示的。禁用账号走第三个码,见 denied()。
|
||||
*/
|
||||
function requireRole(
|
||||
allowed: (user: AuthUser) => boolean,
|
||||
): MiddlewareHandler<AppEnv> {
|
||||
function requireRole(allowed: (user: AuthUser) => boolean): MiddlewareHandler<AppEnv> {
|
||||
return async (c, next) => {
|
||||
const session = await resolveSession(c)
|
||||
if (!session.user) return denied(c, session.reason)
|
||||
if (!allowed(session.user))
|
||||
return failure(c, 403, "permission-denied", "权限不足")
|
||||
if (!allowed(session.user)) return failure(c, 403, "permission-denied", "权限不足")
|
||||
c.set("user", session.user)
|
||||
await next()
|
||||
}
|
||||
}
|
||||
|
||||
/** 旧 `@admin_role_required` */
|
||||
export const requireAdmin = requireRole((user) =>
|
||||
ADMIN_ROLES.includes(user.adminType),
|
||||
)
|
||||
export const requireAdmin = requireRole((user) => ADMIN_ROLES.includes(user.adminType))
|
||||
|
||||
/** 旧 `@teacher_admin_required` */
|
||||
export const requireTeacher = requireRole((user) =>
|
||||
TEACHER_ROLES.includes(user.adminType),
|
||||
)
|
||||
export const requireTeacher = requireRole((user) => TEACHER_ROLES.includes(user.adminType))
|
||||
|
||||
/** 旧 `@super_admin_required` */
|
||||
export const requireSuperAdmin = requireRole(
|
||||
(user) => user.adminType === "Super Admin",
|
||||
)
|
||||
export const requireSuperAdmin = requireRole((user) => user.adminType === "Super Admin")
|
||||
|
||||
/**
|
||||
* 旧 `@problem_permission_required`:先要是管理员,再要 problem_permission 不为 None。
|
||||
@@ -79,6 +70,5 @@ export const requireSuperAdmin = requireRole(
|
||||
* created_by 过滤 —— 旧后端也是这么分工的,别把两件事混在一起。
|
||||
*/
|
||||
export const requireProblemPermission = requireRole(
|
||||
(user) =>
|
||||
ADMIN_ROLES.includes(user.adminType) && user.problemPermission !== "None",
|
||||
(user) => ADMIN_ROLES.includes(user.adminType) && user.problemPermission !== "None",
|
||||
)
|
||||
@@ -5,32 +5,17 @@ const pbkdf2Async = promisify(pbkdf2)
|
||||
|
||||
async function verifyDjangoPbkdf2(password: string, encoded: string) {
|
||||
const [algorithm, iterationsText, salt, digestText] = encoded.split("$")
|
||||
if (
|
||||
algorithm !== "pbkdf2_sha256" ||
|
||||
!iterationsText ||
|
||||
!salt ||
|
||||
!digestText
|
||||
) {
|
||||
if (algorithm !== "pbkdf2_sha256" || !iterationsText || !salt || !digestText) {
|
||||
return false
|
||||
}
|
||||
|
||||
const iterations = Number(iterationsText)
|
||||
const expected = Buffer.from(digestText, "base64")
|
||||
if (
|
||||
!Number.isSafeInteger(iterations) ||
|
||||
iterations <= 0 ||
|
||||
expected.length === 0
|
||||
) {
|
||||
if (!Number.isSafeInteger(iterations) || iterations <= 0 || expected.length === 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
const actual = await pbkdf2Async(
|
||||
password,
|
||||
salt,
|
||||
iterations,
|
||||
expected.length,
|
||||
"sha256",
|
||||
)
|
||||
const actual = await pbkdf2Async(password, salt, iterations, expected.length, "sha256")
|
||||
return timingSafeEqual(actual, expected)
|
||||
}
|
||||
|
||||
|
||||
@@ -77,12 +77,7 @@ export async function createSession(
|
||||
// 全压在登录这一下上
|
||||
const pipeline = redis
|
||||
.pipeline()
|
||||
.set(
|
||||
sessionKey(token),
|
||||
JSON.stringify(value),
|
||||
"EX",
|
||||
config.sessionTtlSeconds,
|
||||
)
|
||||
.set(sessionKey(token), JSON.stringify(value), "EX", config.sessionTtlSeconds)
|
||||
.sadd(userSessionsKey(userId), token)
|
||||
.expire(userSessionsKey(userId), config.sessionTtlSeconds)
|
||||
markOnline(pipeline, userId)
|
||||
@@ -131,10 +126,7 @@ async function sessionUserId(token: string) {
|
||||
* 对已经被别人登着的账号毫无作用 —— 而学生密码是明文存着给老师查的,
|
||||
* 改密码正是发现密码泄露之后唯一的补救手段。
|
||||
*/
|
||||
export async function revokeUserSessions(
|
||||
userId: number,
|
||||
reason: SessionRevokedReason,
|
||||
) {
|
||||
export async function revokeUserSessions(userId: number, reason: SessionRevokedReason) {
|
||||
const tokens = await redis.smembers(userSessionsKey(userId))
|
||||
if (tokens.length) await redis.del(...tokens.map(sessionKey))
|
||||
await redis.del(userSessionsKey(userId))
|
||||
@@ -161,9 +153,7 @@ export type SessionResult =
|
||||
| { user: AuthUser; reason?: undefined }
|
||||
| { user: null; reason: "anonymous" | "disabled" }
|
||||
|
||||
async function authenticateToken(
|
||||
token: string | undefined,
|
||||
): Promise<SessionResult> {
|
||||
async function authenticateToken(token: string | undefined): Promise<SessionResult> {
|
||||
if (!token) return { user: null, reason: "anonymous" }
|
||||
|
||||
const raw = await redis.get(sessionKey(token))
|
||||
@@ -237,8 +227,7 @@ export async function getSessionUser(c: Context) {
|
||||
}
|
||||
|
||||
export async function getRequestSessionUser(request: Request) {
|
||||
return (await authenticateToken(readCookie(request, config.sessionCookie)))
|
||||
.user
|
||||
return (await authenticateToken(readCookie(request, config.sessionCookie))).user
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,11 +282,7 @@ async function getStoredSession(c: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function setContestPassword(
|
||||
c: Context,
|
||||
contestId: number,
|
||||
password: string,
|
||||
) {
|
||||
export async function setContestPassword(c: Context, contestId: number, password: string) {
|
||||
const session = await getStoredSession(c)
|
||||
if (!session) return false
|
||||
session.value.contestPasswords[String(contestId)] = password
|
||||
|
||||
Reference in new issue
Block a user