authentication
parent
7ede8eda45
commit
5c26804cc4
|
@ -31,7 +31,7 @@ import Feedback from "../models/feedback";
|
|||
|
||||
export async function SignUp(req: Request, res: Response) {
|
||||
try {
|
||||
let { username, accountName, password, language } = req.body;
|
||||
let { username, accountName, password, language, rememberMe } = req.body;
|
||||
|
||||
// validate request
|
||||
|
||||
|
@ -42,7 +42,8 @@ export async function SignUp(req: Request, res: Response) {
|
|||
!language ||
|
||||
!isUsernameValid(username) ||
|
||||
!isAccountNameValid(accountName) ||
|
||||
!isLanguageCodeValid(language)
|
||||
!isLanguageCodeValid(language) ||
|
||||
rememberMe === undefined
|
||||
) {
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
@ -58,10 +59,6 @@ export async function SignUp(req: Request, res: Response) {
|
|||
});
|
||||
|
||||
if (existingUser) {
|
||||
logger.debug(
|
||||
"User already exists with this accountName: %s",
|
||||
accountName
|
||||
);
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
|
@ -70,7 +67,6 @@ export async function SignUp(req: Request, res: Response) {
|
|||
const decodedPassword = decodeBase64(password);
|
||||
|
||||
if (!isPasswordValid(decodedPassword)) {
|
||||
logger.debug("Password is not valid");
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
|
@ -91,12 +87,6 @@ export async function SignUp(req: Request, res: Response) {
|
|||
calendar_max_service_duration: CALENDAR_MAX_SERVICE_DURATION,
|
||||
})
|
||||
.then(async (store) => {
|
||||
logger.debug(
|
||||
"Store created with storeId: %s storeName: %s",
|
||||
store.store_id,
|
||||
store.name
|
||||
);
|
||||
|
||||
// create user
|
||||
|
||||
await User.create({
|
||||
|
@ -108,16 +98,11 @@ export async function SignUp(req: Request, res: Response) {
|
|||
password: hashedPassword,
|
||||
language: language,
|
||||
analytics_enabled: USER_ANALYTICS_ENABLED_DEFAULT,
|
||||
state: ACCOUNT_STATE.ACTIVE,
|
||||
})
|
||||
.then((user) => {
|
||||
logger.debug(
|
||||
"User created with accountName: %s username: %s",
|
||||
user.account_name,
|
||||
user.username
|
||||
);
|
||||
|
||||
// create session
|
||||
saveSession(req, res, user.user_id, user.username);
|
||||
saveSession(req, res, user.user_id, user.username, rememberMe);
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error(err);
|
||||
|
@ -136,11 +121,11 @@ export async function SignUp(req: Request, res: Response) {
|
|||
|
||||
export async function Login(req: Request, res: Response) {
|
||||
try {
|
||||
let { accountName, password } = req.body;
|
||||
let { accountName, password, rememberMe } = req.body;
|
||||
|
||||
// validate request
|
||||
|
||||
if (!accountName || !password) {
|
||||
if (!accountName) {
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
|
@ -155,6 +140,7 @@ export async function Login(req: Request, res: Response) {
|
|||
where: {
|
||||
account_name: accountName,
|
||||
},
|
||||
attributes: ["user_id", "password", "state"],
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
|
@ -165,6 +151,15 @@ export async function Login(req: Request, res: Response) {
|
|||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
// if password not provided, then send user state
|
||||
// user is on the login page on the first step of the login process
|
||||
// and only needs to enter their account name to get the user state to know what to do next
|
||||
|
||||
if (password === undefined) {
|
||||
console.debug("Password not provided");
|
||||
return res.status(200).send({ state: user.state });
|
||||
}
|
||||
|
||||
// decode password
|
||||
|
||||
const decodedPassword = decodeBase64(password);
|
||||
|
@ -185,7 +180,7 @@ export async function Login(req: Request, res: Response) {
|
|||
|
||||
// check user state
|
||||
|
||||
if (user.state === ACCOUNT_STATE.DELETED) {
|
||||
if (user.state === ACCOUNT_STATE.PENDING_DELETION) {
|
||||
// update user state back to active
|
||||
|
||||
User.update(
|
||||
|
@ -201,7 +196,7 @@ export async function Login(req: Request, res: Response) {
|
|||
}
|
||||
|
||||
// create session
|
||||
saveSession(req, res, user.user_id, user.username);
|
||||
saveSession(req, res, user.user_id, user.username, rememberMe);
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
res.status(500).send({ err: "invalid request" });
|
||||
|
@ -337,10 +332,6 @@ export async function IsAccountNameAvailable(req: Request, res: Response) {
|
|||
});
|
||||
|
||||
if (user) {
|
||||
logger.debug(
|
||||
"User already exists with this accountName: %s",
|
||||
accountName
|
||||
);
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
|
@ -607,11 +598,11 @@ export async function DeleteUserProfile(req: Request, res: Response) {
|
|||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
// set user state to deleted
|
||||
// set user state to pending deletion
|
||||
|
||||
await User.update(
|
||||
{
|
||||
state: ACCOUNT_STATE.DELETED,
|
||||
state: ACCOUNT_STATE.PENDING_DELETION,
|
||||
},
|
||||
{
|
||||
where: {
|
||||
|
|
|
@ -14,7 +14,11 @@ import {
|
|||
newUserId,
|
||||
} from "../utils/utils";
|
||||
import User from "../models/user";
|
||||
import { Roles, USER_ANALYTICS_ENABLED_DEFAULT } from "../utils/constants";
|
||||
import {
|
||||
ACCOUNT_STATE,
|
||||
Roles,
|
||||
USER_ANALYTICS_ENABLED_DEFAULT,
|
||||
} from "../utils/constants";
|
||||
import Store from "../models/store";
|
||||
|
||||
export async function AddEmployee(req: Request, res: Response) {
|
||||
|
@ -27,6 +31,7 @@ export async function AddEmployee(req: Request, res: Response) {
|
|||
calendarMaxFutureBookingDays,
|
||||
calendarMinEarliestBookingTime,
|
||||
language,
|
||||
passwordSetOnInitLogging,
|
||||
} = req.body;
|
||||
|
||||
// validate request
|
||||
|
@ -35,19 +40,22 @@ export async function AddEmployee(req: Request, res: Response) {
|
|||
!storeId ||
|
||||
!username ||
|
||||
!accountName ||
|
||||
!password ||
|
||||
passwordSetOnInitLogging === undefined ||
|
||||
(!password && passwordSetOnInitLogging === false) ||
|
||||
!language ||
|
||||
!isLanguageCodeValid(language)
|
||||
) {
|
||||
logger.debug("Invalid request");
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
logger.debug("Request is valid %s %s", passwordSetOnInitLogging, password);
|
||||
|
||||
// verify if requester is a store master
|
||||
|
||||
const requesterSession = await getUserSession(req);
|
||||
|
||||
if (!requesterSession) {
|
||||
logger.debug("Requester session not found");
|
||||
return res.status(401).send({ err: "unauthorized" });
|
||||
}
|
||||
|
||||
|
@ -58,12 +66,10 @@ export async function AddEmployee(req: Request, res: Response) {
|
|||
});
|
||||
|
||||
if (!store) {
|
||||
logger.debug("Store not found");
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
if (store.owner_user_id !== requesterSession.user_id) {
|
||||
logger.debug("Requester is not the store owner");
|
||||
return res.status(401).send({ err: "unauthorized" });
|
||||
}
|
||||
|
||||
|
@ -87,42 +93,60 @@ export async function AddEmployee(req: Request, res: Response) {
|
|||
});
|
||||
|
||||
if (existingUser) {
|
||||
logger.debug(
|
||||
"User already exists with this accountName: %s",
|
||||
accountName
|
||||
);
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
// decode password
|
||||
|
||||
const decodedPassword = decodeBase64(password);
|
||||
|
||||
if (!isPasswordValid(decodedPassword)) {
|
||||
logger.debug("Password is not valid");
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
// hash password
|
||||
|
||||
const hashedPassword = await hashPassword(decodedPassword);
|
||||
|
||||
// create user
|
||||
|
||||
const userId = newUserId();
|
||||
|
||||
await User.create({
|
||||
let newUser = {
|
||||
user_id: userId,
|
||||
store_id: storeId,
|
||||
role: Roles.Worker,
|
||||
account_name: accountName,
|
||||
username: username,
|
||||
password: hashedPassword,
|
||||
calendar_max_future_booking_days: calendarMaxFutureBookingDays,
|
||||
calendar_min_earliest_booking_time: calendarMinEarliestBookingTime,
|
||||
language: language,
|
||||
analytics_enabled: USER_ANALYTICS_ENABLED_DEFAULT,
|
||||
})
|
||||
state: passwordSetOnInitLogging
|
||||
? ACCOUNT_STATE.INIT_LOGIN
|
||||
: ACCOUNT_STATE.ACTIVE,
|
||||
};
|
||||
|
||||
if (!passwordSetOnInitLogging) {
|
||||
// decode password
|
||||
|
||||
const decodedPassword = decodeBase64(password);
|
||||
|
||||
if (!isPasswordValid(decodedPassword)) {
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
// hash password
|
||||
|
||||
const hashedPassword = await hashPassword(decodedPassword);
|
||||
|
||||
newUser = {
|
||||
...newUser,
|
||||
password: hashedPassword,
|
||||
} as {
|
||||
user_id: string;
|
||||
store_id: any;
|
||||
role: string;
|
||||
account_name: any;
|
||||
username: any;
|
||||
calendar_max_future_booking_days: any;
|
||||
calendar_min_earliest_booking_time: any;
|
||||
language: any;
|
||||
analytics_enabled: boolean;
|
||||
password: string;
|
||||
state: number;
|
||||
};
|
||||
}
|
||||
|
||||
await User.create(newUser)
|
||||
.then(() => res.status(200).send({ msg: "success" }))
|
||||
.catch((err) => {
|
||||
logger.error(err);
|
||||
|
|
|
@ -8,5 +8,11 @@ export async function sessionProtection(req: Request, res: any, next: any) {
|
|||
return res.status(401).send({ err: "unauthorized" });
|
||||
}
|
||||
|
||||
// check if session is expired
|
||||
|
||||
if (session.expires < new Date()) {
|
||||
return res.status(401).send({ err: "unauthorized" });
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
|
|
|
@ -8,12 +8,12 @@ interface UserAttributes {
|
|||
role: string;
|
||||
account_name: string;
|
||||
username: string;
|
||||
password: string;
|
||||
password?: string; // can be null if user is created by store owner - password is set by user when they first login
|
||||
calendar_max_future_booking_days?: number;
|
||||
calendar_min_earliest_booking_time?: number;
|
||||
calendar_using_primary_calendar?: boolean;
|
||||
language: string;
|
||||
state?: number; // like active, deleted, etc
|
||||
state: number; // like active, deleted, etc
|
||||
analytics_enabled: boolean;
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ User.init(
|
|||
},
|
||||
password: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
// allowNull defaults to true
|
||||
},
|
||||
calendar_max_future_booking_days: {
|
||||
type: DataTypes.INTEGER,
|
||||
|
@ -78,7 +78,7 @@ User.init(
|
|||
},
|
||||
state: {
|
||||
type: DataTypes.INTEGER,
|
||||
// allowNull defaults to true
|
||||
allowNull: false,
|
||||
},
|
||||
analytics_enabled: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export const DEFAULT_SESSION_EXPIRY = 365 * 24 * 60 * 60 * 1000; // 365 days
|
||||
export const SESSION_EXPIRY_NOT_REMEMBER_ME = 60 * 60 * 1000; // 1 hour
|
||||
export const USER_SESSION_LENGTH = 32;
|
||||
|
||||
export const USERNAME_MIN_LENGTH = 3;
|
||||
|
@ -44,7 +45,9 @@ export const VALID_LANGUAGE_CODES = ["en", "de"];
|
|||
|
||||
export enum ACCOUNT_STATE {
|
||||
ACTIVE = 0, // everything is fine
|
||||
DELETED = 1, // account still exists but is marked as deleted, can be restored or will be deleted after a certain time
|
||||
PENDING_DELETION = 1, // account still exists but is marked as deleted, can be restored or will be deleted after a certain time
|
||||
INIT_LOGIN = 2, // account is created but password is not set yet
|
||||
BANNED = 3, // account is banned, cannot login
|
||||
}
|
||||
|
||||
export const FEEDBACK_MIN_LENGTH = 3;
|
||||
|
|
|
@ -6,6 +6,7 @@ import Session from "../models/session";
|
|||
import {
|
||||
DEFAULT_SESSION_EXPIRY,
|
||||
HEADER_X_AUTHORIZATION,
|
||||
SESSION_EXPIRY_NOT_REMEMBER_ME,
|
||||
USER_SESSION_LENGTH,
|
||||
} from "./constants";
|
||||
|
||||
|
@ -53,7 +54,8 @@ export async function saveSession(
|
|||
req: Request,
|
||||
res: Response,
|
||||
userId: string,
|
||||
username: string
|
||||
username: string,
|
||||
rememberMe: boolean
|
||||
) {
|
||||
try {
|
||||
const userSession = newUserSession();
|
||||
|
@ -65,7 +67,10 @@ export async function saveSession(
|
|||
browser: req.useragent?.browser as string,
|
||||
os: req.useragent?.os as string,
|
||||
last_used: new Date(),
|
||||
expires: new Date(Date.now() + DEFAULT_SESSION_EXPIRY),
|
||||
expires: new Date(
|
||||
Date.now() +
|
||||
(rememberMe ? DEFAULT_SESSION_EXPIRY : SESSION_EXPIRY_NOT_REMEMBER_ME)
|
||||
),
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import logger from "../logger/logger";
|
||||
import {
|
||||
USERNAME_MIN_LENGTH,
|
||||
USERNAME_MAX_LENGTH,
|
||||
|
|
Loading…
Reference in New Issue