enable password on invite or reset

This commit is contained in:
Julian Krauser 2025-05-06 08:37:56 +02:00
parent ddb460f8d0
commit 0ea12eaafc
8 changed files with 61 additions and 32 deletions

View file

@ -7,6 +7,7 @@ export interface CreateUserCommand {
lastname: string; lastname: string;
secret: string; secret: string;
isOwner: boolean; isOwner: boolean;
routine: LoginRoutineEnum;
} }
export interface UpdateUserCommand { export interface UpdateUserCommand {

View file

@ -31,6 +31,7 @@ export default abstract class UserCommandHandler {
lastname: createUser.lastname, lastname: createUser.lastname,
secret: createUser.secret, secret: createUser.secret,
isOwner: createUser.isOwner, isOwner: createUser.isOwner,
routine: createUser.routine,
}) })
.execute() .execute()
.then((result) => { .then((result) => {

View file

@ -41,6 +41,8 @@ export async function login(req: Request, res: Response): Promise<any> {
let { id } = await UserService.getByUsername(username); let { id } = await UserService.getByUsername(username);
let { secret, routine } = await UserService.getUserSecretAndRoutine(id); let { secret, routine } = await UserService.getUserSecretAndRoutine(id);
console.log(secret, passedSecret);
let valid = false; let valid = false;
if (routine == LoginRoutineEnum.totp) { if (routine == LoginRoutineEnum.totp) {
valid = speakeasy.totp.verify({ valid = speakeasy.totp.verify({
@ -50,7 +52,6 @@ export async function login(req: Request, res: Response): Promise<any> {
window: 2, window: 2,
}); });
} else { } else {
console.log(passedSecret, secret, passedSecret == secret);
valid = passedSecret == secret; valid = passedSecret == secret;
} }

View file

@ -16,6 +16,7 @@ import UserService from "../service/management/userService";
import CustomRequestException from "../exceptions/customRequestException"; import CustomRequestException from "../exceptions/customRequestException";
import InviteFactory from "../factory/admin/management/invite"; import InviteFactory from "../factory/admin/management/invite";
import SettingHelper from "../helpers/settingsHelper"; import SettingHelper from "../helpers/settingsHelper";
import { LoginRoutineEnum } from "../enums/loginRoutineEnum";
/** /**
* @description get all invites * @description get all invites
@ -112,20 +113,26 @@ export async function verifyInvite(req: Request, res: Response): Promise<any> {
*/ */
export async function finishInvite(req: Request, res: Response, grantAdmin: boolean = false): Promise<any> { export async function finishInvite(req: Request, res: Response, grantAdmin: boolean = false): Promise<any> {
let mail = req.body.mail; let mail = req.body.mail;
let routine = req.body.routine;
let token = req.body.token; let token = req.body.token;
let totp = req.body.totp; let passedSecret = req.body.secret;
let { secret, username, firstname, lastname } = await InviteService.getByMailAndToken(mail, token); let { secret, username, firstname, lastname } = await InviteService.getByMailAndToken(mail, token);
let valid = speakeasy.totp.verify({ let valid = false;
secret: secret, if (routine == LoginRoutineEnum.totp) {
encoding: "base32", valid = speakeasy.totp.verify({
token: totp, secret: secret,
window: 2, encoding: "base32",
}); token: passedSecret,
window: 2,
});
} else {
valid = passedSecret != "";
}
if (!valid) { if (!valid) {
throw new UnauthorizedRequestException("Token not valid or expired"); throw new UnauthorizedRequestException("Credentials not valid or expired");
} }
let createUser: CreateUserCommand = { let createUser: CreateUserCommand = {
@ -133,8 +140,9 @@ export async function finishInvite(req: Request, res: Response, grantAdmin: bool
firstname: firstname, firstname: firstname,
lastname: lastname, lastname: lastname,
mail: mail, mail: mail,
secret: secret, secret: routine == LoginRoutineEnum.totp ? secret : passedSecret,
isOwner: grantAdmin, isOwner: grantAdmin,
routine,
}; };
let id = await UserCommandHandler.create(createUser); let id = await UserCommandHandler.create(createUser);

View file

@ -81,28 +81,34 @@ export async function verifyReset(req: Request, res: Response): Promise<any> {
*/ */
export async function finishReset(req: Request, res: Response): Promise<any> { export async function finishReset(req: Request, res: Response): Promise<any> {
let mail = req.body.mail; let mail = req.body.mail;
let routine = req.body.routine;
let token = req.body.token; let token = req.body.token;
let totp = req.body.totp; let passedSecret = req.body.secret;
let { secret, username } = await ResetService.getByMailAndToken(mail, token); let { secret, username } = await ResetService.getByMailAndToken(mail, token);
let valid = speakeasy.totp.verify({ let valid = false;
secret: secret, if (routine == LoginRoutineEnum.totp) {
encoding: "base32", valid = speakeasy.totp.verify({
token: totp, secret: secret,
window: 2, encoding: "base32",
}); token: passedSecret,
window: 2,
});
} else {
valid = passedSecret != "";
}
if (!valid) { if (!valid) {
throw new UnauthorizedRequestException("Token not valid or expired"); throw new UnauthorizedRequestException("Credentials not valid or expired");
} }
let { id } = await UserService.getByUsername(username); let { id } = await UserService.getByUsername(username);
let updateUserSecret: UpdateUserSecretCommand = { let updateUserSecret: UpdateUserSecretCommand = {
id, id,
secret, secret: routine == LoginRoutineEnum.totp ? secret : passedSecret,
routine: LoginRoutineEnum.totp, routine,
}; };
await UserCommandHandler.updateSecret(updateUserSecret); await UserCommandHandler.updateSecret(updateUserSecret);

View file

@ -56,12 +56,17 @@ export default abstract class SettingHelper {
return rawValue as unknown as SettingValueMapping[K]; return rawValue as unknown as SettingValueMapping[K];
} }
let processedValue = rawValue;
if (typeof settingType.type === "string" && settingType.type.includes("/crypt")) {
processedValue = CodingHelper.decrypt(APPLICATION_SECRET, processedValue);
}
const baseType = const baseType =
typeof settingType.type === "string" typeof settingType.type === "string"
? (settingType.type.split("/")[0] as SettingTypeAtom) ? (settingType.type.split("/")[0] as SettingTypeAtom)
: (settingType.type as SettingTypeAtom); : (settingType.type as SettingTypeAtom);
return this.converters[baseType].fromString(rawValue) as unknown as SettingValueMapping[K]; return this.converters[baseType].fromString(processedValue) as unknown as SettingValueMapping[K];
} }
/** /**
@ -81,11 +86,11 @@ export default abstract class SettingHelper {
const settingType = settingsType[key]; const settingType = settingsType[key];
this.validateSetting(key, stringValue); this.validateSetting(key, stringValue);
const oldValue = this.getSetting(key); const oldValue = cloneDeep(this.settings[key]);
let finalValue = stringValue; let newValue = stringValue;
if (typeof settingType.type === "string" && settingType.type.includes("/crypt")) { if (typeof settingType.type === "string" && settingType.type.includes("/crypt")) {
finalValue = CodingHelper.encrypt(APPLICATION_SECRET, stringValue); newValue = CodingHelper.encrypt(APPLICATION_SECRET, stringValue);
} }
this.settings[key] = stringValue; this.settings[key] = stringValue;
@ -94,10 +99,9 @@ export default abstract class SettingHelper {
await SettingCommandHandler.create({ await SettingCommandHandler.create({
topic, topic,
key: settingKey, key: settingKey,
value: finalValue, value: newValue,
}); });
const newValue = this.getSetting(key);
this.notifyListeners(key, newValue, oldValue); this.notifyListeners(key, newValue, oldValue);
} }

View file

@ -8,8 +8,12 @@ router.post("/verify", ParamaterPassCheckHelper.requiredIncludedMiddleware(["mai
await verifyInvite(req, res); await verifyInvite(req, res);
}); });
router.put("/", ParamaterPassCheckHelper.requiredIncludedMiddleware(["mail", "token", "totp"]), async (req, res) => { router.put(
await finishInvite(req, res); "/",
}); ParamaterPassCheckHelper.requiredIncludedMiddleware(["mail", "token", "secret", "routine "]),
async (req, res) => {
await finishInvite(req, res);
}
);
export default router; export default router;

View file

@ -12,8 +12,12 @@ router.post("/", ParamaterPassCheckHelper.requiredIncludedMiddleware(["username"
await startReset(req, res); await startReset(req, res);
}); });
router.put("/", ParamaterPassCheckHelper.requiredIncludedMiddleware(["mail", "token", "totp"]), async (req, res) => { router.put(
await finishReset(req, res); "/",
}); ParamaterPassCheckHelper.requiredIncludedMiddleware(["mail", "token", "secret", "routine"]),
async (req, res) => {
await finishReset(req, res);
}
);
export default router; export default router;