mirror of
https://github.com/fosrl/pangolin.git
synced 2026-09-02 01:09:03 +02:00
harden initial server-admin setup against duplicate users and races
This commit is contained in:
@@ -42,54 +42,62 @@ export async function setServerAdmin(
|
|||||||
|
|
||||||
const { email, password, setupToken } = parsedBody.data;
|
const { email, password, setupToken } = parsedBody.data;
|
||||||
|
|
||||||
// Validate setup token
|
|
||||||
const [validToken] = await db
|
|
||||||
.select()
|
|
||||||
.from(setupTokens)
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(setupTokens.token, setupToken),
|
|
||||||
eq(setupTokens.used, false)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!validToken) {
|
|
||||||
return next(
|
|
||||||
createHttpError(
|
|
||||||
HttpCode.BAD_REQUEST,
|
|
||||||
"Invalid or expired setup token"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const [existing] = await db
|
|
||||||
.select()
|
|
||||||
.from(users)
|
|
||||||
.where(eq(users.serverAdmin, true));
|
|
||||||
|
|
||||||
if (existing) {
|
|
||||||
return next(
|
|
||||||
createHttpError(
|
|
||||||
HttpCode.BAD_REQUEST,
|
|
||||||
"Server admin already exists"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const passwordHash = await hashPassword(password);
|
const passwordHash = await hashPassword(password);
|
||||||
const userId = generateId(15);
|
const userId = generateId(15);
|
||||||
|
|
||||||
await db.transaction(async (trx) => {
|
await db.transaction(async (trx) => {
|
||||||
// Mark the token as used
|
const consumed = await trx
|
||||||
await trx
|
|
||||||
.update(setupTokens)
|
.update(setupTokens)
|
||||||
.set({
|
.set({
|
||||||
used: true,
|
used: true,
|
||||||
dateUsed: moment().toISOString()
|
dateUsed: moment().toISOString()
|
||||||
})
|
})
|
||||||
.where(eq(setupTokens.tokenId, validToken.tokenId));
|
.where(
|
||||||
|
and(
|
||||||
|
eq(setupTokens.token, setupToken),
|
||||||
|
eq(setupTokens.used, false)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.returning({ tokenId: setupTokens.tokenId });
|
||||||
|
|
||||||
|
if (!consumed.length) {
|
||||||
|
throw createHttpError(
|
||||||
|
HttpCode.BAD_REQUEST,
|
||||||
|
"Invalid setup token"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [existingAdmin] = await trx
|
||||||
|
.select({ userId: users.userId })
|
||||||
|
.from(users)
|
||||||
|
.where(eq(users.serverAdmin, true))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (existingAdmin) {
|
||||||
|
throw createHttpError(
|
||||||
|
HttpCode.BAD_REQUEST,
|
||||||
|
"Server admin already exists"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [existingUser] = await trx
|
||||||
|
.select({ userId: users.userId })
|
||||||
|
.from(users)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(users.email, email),
|
||||||
|
eq(users.type, UserType.Internal)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (existingUser) {
|
||||||
|
throw createHttpError(
|
||||||
|
HttpCode.BAD_REQUEST,
|
||||||
|
"A user with that email address already exists"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Create the server admin user
|
|
||||||
await trx.insert(users).values({
|
await trx.insert(users).values({
|
||||||
userId: userId,
|
userId: userId,
|
||||||
email: email,
|
email: email,
|
||||||
@@ -111,6 +119,9 @@ export async function setServerAdmin(
|
|||||||
status: HttpCode.OK
|
status: HttpCode.OK
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (createHttpError.isHttpError(e)) {
|
||||||
|
return next(e);
|
||||||
|
}
|
||||||
logger.error(e);
|
logger.error(e);
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export async function validateSetupToken(
|
|||||||
return response<ValidateSetupTokenResponse>(res, {
|
return response<ValidateSetupTokenResponse>(res, {
|
||||||
data: {
|
data: {
|
||||||
valid: false,
|
valid: false,
|
||||||
message: "Invalid or expired setup token"
|
message: "Invalid setup token"
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user