backend: remove default user, update user interface to include password hash and a list of roles, and refactor associated code
This commit is contained in:
parent
7d128ed699
commit
a118b5cf92
@ -112,5 +112,7 @@ export const getProfile = (req: Request, res: Response) => {
|
||||
return rejectRequestFromMeetError(res, error);
|
||||
}
|
||||
|
||||
return res.status(200).json(user);
|
||||
const userService = container.get(UserService);
|
||||
const userDTO = userService.convertToDTO(user);
|
||||
return res.status(200).json(userDTO);
|
||||
};
|
||||
|
||||
@ -23,8 +23,6 @@ export const {
|
||||
|
||||
// Authentication configuration
|
||||
MEET_API_KEY = 'meet-api-key',
|
||||
MEET_USER = 'user',
|
||||
MEET_SECRET = 'user',
|
||||
MEET_ADMIN_USER = 'admin',
|
||||
MEET_ADMIN_SECRET = 'admin',
|
||||
|
||||
|
||||
@ -82,7 +82,7 @@ export const tokenAndRoleValidator = (role: UserRole) => {
|
||||
throw errorWithControl(errorInvalidTokenSubject(), true);
|
||||
}
|
||||
|
||||
if (user.role !== role) {
|
||||
if (!user.roles.includes(role)) {
|
||||
throw errorWithControl(errorInsufficientPermissions(), false);
|
||||
}
|
||||
|
||||
@ -134,10 +134,8 @@ export const apiKeyValidator = async (req: Request) => {
|
||||
throw errorWithControl(errorInvalidApiKey(), true);
|
||||
}
|
||||
|
||||
const apiUser = {
|
||||
username: INTERNAL_CONFIG.API_USER,
|
||||
role: UserRole.APP
|
||||
};
|
||||
const userService = container.get(UserService);
|
||||
const apiUser = userService.getApiUser();
|
||||
|
||||
req.session = req.session || {};
|
||||
req.session.user = apiUser;
|
||||
@ -152,7 +150,8 @@ export const allowAnonymous = async (req: Request) => {
|
||||
};
|
||||
|
||||
// Return the authenticated user if available, otherwise return an anonymous user
|
||||
const getAuthenticatedUserOrAnonymous = async (req: Request) => {
|
||||
const getAuthenticatedUserOrAnonymous = async (req: Request): Promise<User> => {
|
||||
const userService = container.get(UserService);
|
||||
let user: User | null = null;
|
||||
|
||||
// Check if there is a user already authenticated
|
||||
@ -163,7 +162,6 @@ const getAuthenticatedUserOrAnonymous = async (req: Request) => {
|
||||
const tokenService = container.get(TokenService);
|
||||
const payload = await tokenService.verifyToken(token);
|
||||
const username = payload.sub;
|
||||
const userService = container.get(UserService);
|
||||
user = username ? await userService.getUser(username) : null;
|
||||
} catch (error) {
|
||||
const logger = container.get(LoggerService);
|
||||
@ -172,10 +170,7 @@ const getAuthenticatedUserOrAnonymous = async (req: Request) => {
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
user = {
|
||||
username: INTERNAL_CONFIG.ANONYMOUS_USER,
|
||||
role: UserRole.USER
|
||||
};
|
||||
user = userService.getAnonymousUser();
|
||||
}
|
||||
|
||||
return user;
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { User } from '@typings-ce';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { MEET_ADMIN_SECRET, MEET_ADMIN_USER } from '../environment.js';
|
||||
import { PasswordHelper } from '../helpers/index.js';
|
||||
import { LoggerService, MeetStorageService, UserService } from './index.js';
|
||||
|
||||
@ -13,28 +12,12 @@ export class AuthService {
|
||||
) {}
|
||||
|
||||
async authenticate(username: string, password: string): Promise<User | null> {
|
||||
const isAdmin = this.authenticateAdmin(username, password);
|
||||
const isUser = await this.authenticateUser(username, password);
|
||||
const user = await this.userService.getUser(username);
|
||||
|
||||
if (isAdmin || isUser) {
|
||||
return this.userService.getUser(username);
|
||||
if (!user || !(await PasswordHelper.verifyPassword(password, user.passwordHash))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private authenticateAdmin(username: string, password: string): boolean {
|
||||
return username === MEET_ADMIN_USER && password === MEET_ADMIN_SECRET;
|
||||
}
|
||||
|
||||
private async authenticateUser(username: string, password: string): Promise<boolean> {
|
||||
const userCredentials = await this.userService.getStoredUserCredentials();
|
||||
|
||||
if (!userCredentials) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isPasswordValid = await PasswordHelper.verifyPassword(password, userCredentials.passwordHash);
|
||||
return username === userCredentials.username && isPasswordValid;
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ export class TokenService {
|
||||
identity: user.username,
|
||||
ttl: MEET_ACCESS_TOKEN_EXPIRATION,
|
||||
metadata: JSON.stringify({
|
||||
role: user.role
|
||||
roles: user.roles
|
||||
})
|
||||
};
|
||||
return await this.generateJwtToken(tokenOptions);
|
||||
@ -32,7 +32,7 @@ export class TokenService {
|
||||
identity: user.username,
|
||||
ttl: MEET_REFRESH_TOKEN_EXPIRATION,
|
||||
metadata: JSON.stringify({
|
||||
role: user.role
|
||||
roles: user.roles
|
||||
})
|
||||
};
|
||||
return await this.generateJwtToken(tokenOptions);
|
||||
|
||||
@ -1,43 +1,35 @@
|
||||
import { SingleUserAuth, SingleUserCredentials, User, UserRole } from '@typings-ce';
|
||||
import { User, UserDTO, UserRole } from '@typings-ce';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { MEET_ADMIN_USER } from '../environment.js';
|
||||
import { LoggerService, MeetStorageService } from './index.js';
|
||||
import INTERNAL_CONFIG from '../config/internal-config.js';
|
||||
import { MeetStorageService } from './index.js';
|
||||
|
||||
@injectable()
|
||||
export class UserService {
|
||||
constructor(
|
||||
@inject(LoggerService) protected logger: LoggerService,
|
||||
@inject(MeetStorageService) protected globalPrefService: MeetStorageService
|
||||
) {}
|
||||
constructor(@inject(MeetStorageService) protected storageService: MeetStorageService) {}
|
||||
|
||||
async getUser(username: string): Promise<User | null> {
|
||||
if (username === MEET_ADMIN_USER) {
|
||||
return {
|
||||
username: MEET_ADMIN_USER,
|
||||
role: UserRole.ADMIN
|
||||
};
|
||||
}
|
||||
|
||||
const userCredentials = await this.getStoredUserCredentials();
|
||||
|
||||
if (userCredentials && username === userCredentials.username) {
|
||||
return {
|
||||
username,
|
||||
role: UserRole.USER
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
return this.storageService.getUser(username);
|
||||
}
|
||||
|
||||
async getStoredUserCredentials(): Promise<SingleUserCredentials | null> {
|
||||
try {
|
||||
const { securityPreferences } = await this.globalPrefService.getGlobalPreferences();
|
||||
const { method: authMethod } = securityPreferences.authentication;
|
||||
return (authMethod as SingleUserAuth).credentials;
|
||||
} catch (error) {
|
||||
this.logger.error('Error getting stored user credentials:' + error);
|
||||
return null;
|
||||
}
|
||||
getAnonymousUser(): User {
|
||||
return {
|
||||
username: INTERNAL_CONFIG.ANONYMOUS_USER,
|
||||
passwordHash: '',
|
||||
roles: [UserRole.USER]
|
||||
};
|
||||
}
|
||||
|
||||
getApiUser(): User {
|
||||
return {
|
||||
username: INTERNAL_CONFIG.API_USER,
|
||||
passwordHash: '',
|
||||
roles: [UserRole.APP]
|
||||
};
|
||||
}
|
||||
|
||||
// Convert user to UserDTO to remove sensitive information
|
||||
convertToDTO(user: User): UserDTO {
|
||||
const { passwordHash, ...userDTO } = user;
|
||||
return userDTO;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
export interface User {
|
||||
username: string;
|
||||
role: UserRole;
|
||||
passwordHash: string;
|
||||
roles: UserRole[];
|
||||
}
|
||||
|
||||
export const enum UserRole {
|
||||
@ -8,3 +9,5 @@ export const enum UserRole {
|
||||
USER = 'user',
|
||||
APP = 'app'
|
||||
}
|
||||
|
||||
export type UserDTO = Omit<User, 'passwordHash'>;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user