Skip to content

Commit

Permalink
DEV - Delete user
Browse files Browse the repository at this point in the history
  • Loading branch information
juliecoust committed Jan 30, 2024
1 parent ab728a1 commit 4d269b4
Show file tree
Hide file tree
Showing 17 changed files with 121 additions and 13 deletions.
12 changes: 3 additions & 9 deletions src/data/data-sources/sqlite/sqlite-user-data-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class SQLiteUserDataSource implements UserDataSource {
}

init_user_db() {
const sql_create = "CREATE TABLE IF NOT EXISTS 'user' (user_id INTEGER PRIMARY KEY AUTOINCREMENT, first_name TEXT NOT NULL, last_name TEXT NOT NULL, email TEXT NOT NULL UNIQUE, password_hash CHAR(60) NOT NULL, valid_email BOOLEAN CHECK (valid_email IN (0, 1)) DEFAULT 0, confirmation_code TEXT , reset_password_code TEXT ,is_admin BOOLEAN CHECK (is_admin IN (0, 1)) DEFAULT 0, organisation TEXT NOT NULL, country TEXT NOT NULL, user_planned_usage TEXT NOT NULL, user_creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP);"
const sql_create = "CREATE TABLE IF NOT EXISTS 'user' (user_id INTEGER PRIMARY KEY AUTOINCREMENT, first_name TEXT NOT NULL, last_name TEXT NOT NULL, email TEXT NOT NULL UNIQUE, password_hash CHAR(60) NOT NULL, valid_email BOOLEAN CHECK (valid_email IN (0, 1)) DEFAULT 0, confirmation_code TEXT , reset_password_code TEXT ,is_admin BOOLEAN CHECK (is_admin IN (0, 1)) DEFAULT 0, organisation TEXT NOT NULL, country TEXT NOT NULL, user_planned_usage TEXT NOT NULL, user_creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted TIMESTAMP DEFAULT NULL);"
this.db.run(sql_create, [])
}

Expand Down Expand Up @@ -53,20 +53,13 @@ export class SQLiteUserDataSource implements UserDataSource {
country: row.country,
user_planned_usage: row.user_planned_usage,
user_creation_date: row.user_creation_date,
deleted: row.deleted
}));
resolve(result);
}
});
})
}
//TODO
// async deleteOne(user_id: String) {
// await this.db.run(`delete ${DB_TABLE} where user_id = $1`, [user_id])
// }
deleteOne(user_id: string): void {
console.log(user_id)
throw new Error("Method not implemented.");
}

// Returns the number of lines updates
updateOne(user: UserUpdateModel): Promise<number> {
Expand Down Expand Up @@ -131,6 +124,7 @@ export class SQLiteUserDataSource implements UserDataSource {
country: row.country,
user_planned_usage: row.user_planned_usage,
user_creation_date: row.user_creation_date,
deleted: row.deleted
};
resolve(result);
}
Expand Down
1 change: 0 additions & 1 deletion src/data/interfaces/data-sources/user-data-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { AuthUserCredentialsModel } from "../../../domain/entities/auth";
export interface UserDataSource {
create(user: UserRequesCreationtModel): Promise<number>;
getAll(): Promise<UserResponseModel[]>;
deleteOne(user_id: string): void;
updateOne(user: UserUpdateModel): Promise<number>;
getOne(user: UserRequestModel): Promise<UserResponseModel | null>;
getUserLogin(email: string): Promise<AuthUserCredentialsModel | null>;
Expand Down
5 changes: 3 additions & 2 deletions src/domain/entities/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export enum UserStatus {
Anonym = "ANONYM"
}

// the user request model
export interface UserRequesCreationtModel {
password: string;
first_name: string;
Expand All @@ -28,6 +27,7 @@ export interface UserRequestModel {
country?: string;
user_planned_usage?: string;
user_creation_date?: string;
deleted?: string;
}
export interface UserUpdateModel {
[key: string]: any;
Expand All @@ -44,10 +44,11 @@ export interface UserUpdateModel {
country?: string;
user_planned_usage?: string;
user_creation_date?: string;
deleted?: string;
}

// the user response model
export interface UserResponseModel extends PublicUserModel {
deleted?: string;
confirmation_code?: string | null;
reset_password_code?: string | null;
}
Expand Down
2 changes: 2 additions & 0 deletions src/domain/interfaces/repositories/user-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ export interface UserRepository {
verifyResetPasswordToken(reset_password_token: string): DecodedToken | null;
setResetPasswordCode(user: UserUpdateModel): Promise<number>;
toPublicUser(createdUser: PrivateUserModel): PublicUserModel;
deleteUser(user: UserUpdateModel): Promise<number>;
isDeleted(user_id: number): Promise<boolean>;
}
4 changes: 4 additions & 0 deletions src/domain/interfaces/use-cases/user/delete-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { UserUpdateModel } from "../../../entities/user";
export interface DeleteUserUseCase {
execute(current_user: UserUpdateModel, user_to_update: UserUpdateModel): Promise<void>;
}
30 changes: 30 additions & 0 deletions src/domain/repositories/user-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,34 @@ export class UserRepositoryImpl implements UserRepository {
return publicUser
}

async isDeleted(user_id: number): Promise<boolean> {
const user = await this.userDataSource.getOne({ user_id: user_id })
if (!user) return false
return user.deleted ? true : false
}

async deleteUser(user: UserUpdateModel): Promise<number> {
const params = ["user_id", "first_name", "last_name", "email", "valid_email", "confirmation_code", "is_admin", "organisation", "country", "user_planned_usage", "password_hash", "reset_password_code", "deleted"]
const anonymized_user: UserUpdateModel = {
user_id: user.user_id,
first_name: "anonym_" + user.user_id,
last_name: "anonym_" + user.user_id,
email: "anonym_" + user.user_id,
valid_email: false,
confirmation_code: null,
is_admin: false,
organisation: "anonymized",
country: "anonymized",
user_planned_usage: "anonymized",
password_hash: "anonymized",
reset_password_code: null,
// Deleted : current date time
deleted: new Date().toISOString()
}
console.log(anonymized_user)

const nb_of_updated_user = await this.updateUser(anonymized_user, params)
return nb_of_updated_user

}
}
3 changes: 3 additions & 0 deletions src/domain/use-cases/auth/change-password.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export class ChangePassword implements ChangePasswordUseCase {
async execute(current_user: DecodedToken, credentials: ChangeCredentialsModel): Promise<void> {
let nb_of_updated_user: number = 0

// User should not be deleted
if (await this.userRepository.isDeleted(credentials.user_id)) throw new Error("User is deleted");

// admin can update anyone password without old password
if (await this.userRepository.isAdmin(current_user.user_id)) {
nb_of_updated_user = await this.userRepository.changePassword(credentials)
Expand Down
3 changes: 3 additions & 0 deletions src/domain/use-cases/auth/refresh-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ export class RefreshToken implements RefreshTokenUseCase {
}

async execute(userAuth: DecodedToken): Promise<AuthJwtRefreshedResponseModel> {
// User should not be deleted
if (await this.userRepository.isDeleted(userAuth.user_id)) throw new Error("User is deleted");

// Get full user based on decoded token user's email
const full_user = await this.userRepository.getUser({ email: userAuth.email })

Expand Down
3 changes: 3 additions & 0 deletions src/domain/use-cases/auth/reset-password-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export class ResetPasswordRequest implements ResetPasswordRequestUseCase {
const preexistant_user = await this.userRepository.getUser(user)
if (!preexistant_user) throw new Error("User does not exist");

// User should not be deleted
if (await this.userRepository.isDeleted(preexistant_user.user_id)) throw new Error("User is deleted");

// is the user validated ?
if (!preexistant_user.valid_email) throw new Error("User email is not validated");

Expand Down
2 changes: 2 additions & 0 deletions src/domain/use-cases/auth/reset-password.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export class ResetPassword implements ResetPasswordUseCase {
if (credentials.reset_password_token) {
decoded_token = this.userRepository.verifyResetPasswordToken(credentials.reset_password_token)
if (!decoded_token) throw new Error("Token is not valid");
// User should not be deleted
if (await this.userRepository.isDeleted(decoded_token.user_id)) throw new Error("User is deleted");
} else throw new Error("No token provided");

// does the user bind to reset_password_code exist ?
Expand Down
3 changes: 3 additions & 0 deletions src/domain/use-cases/user/create-user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export class CreateUser implements CreateUserUseCase {

// Check if a user with the given email already exists
if (preexistentUser) {
// User should not be deleted
if (await this.userRepository.isDeleted(preexistentUser.user_id)) throw new Error("User is deleted");

// If the user exists but hasn't validated their email
if (!preexistentUser.valid_email) {
// Update the preexisting user with new information
Expand Down
36 changes: 36 additions & 0 deletions src/domain/use-cases/user/delete-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { UserUpdateModel } from "../../entities/user";
import { UserRepository } from "../../interfaces/repositories/user-repository";
import { DeleteUserUseCase } from "../../interfaces/use-cases/user/delete-user";

export class DeleteUser implements DeleteUserUseCase {
userRepository: UserRepository
constructor(userRepository: UserRepository) {
this.userRepository = userRepository
}

async execute(current_user: UserUpdateModel, user_to_update: UserUpdateModel): Promise<void> {
let nb_of_deleted_user: number = 0
//TODO LATER : check if user_to_update have no progects where he is manager or member
// const user_to_update_projects = await this.userRepository.getUserProjects(user_to_update.user_id)
// if (user_to_update_projects.length > 0) throw new Error("User have projects")

// Check that user to upadate exist
const user_to_update_exist = await this.userRepository.getUser({ user_id: user_to_update.user_id })
if (!user_to_update_exist) throw new Error("Can't find user to delete");
// User should not be deleted
if (await this.userRepository.isDeleted(user_to_update_exist.user_id)) throw new Error("User is deleted");

// Check that current_user is admin or is the user to update
if (await this.userRepository.isAdmin(current_user.user_id) || current_user.user_id == user_to_update.user_id) {
nb_of_deleted_user = await this.userRepository.deleteUser(user_to_update)
if (nb_of_deleted_user == 0) throw new Error("Can't delete user");
} else {
throw new Error("Logged user cannot delet this user");
}

const updated_user = await this.userRepository.getUser({ user_id: user_to_update.user_id })
console.log(updated_user)
if (!updated_user) throw new Error("Can't find deleted user");

}
}
4 changes: 4 additions & 0 deletions src/domain/use-cases/user/get-all-users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ export class GetAllUsers implements GetAllUsersUseCase {
}

async execute(): Promise<UserResponseModel[]> {
// TODO
// User should not be deleted
//if (await this.userRepository.isDeleted(userAuth.user_id)) throw new Error("User is deleted");

const result = await this.userRepository.getUsers()
const publicUsers = result.map(user => this.userRepository.toPublicUser(user))
return publicUsers
Expand Down
3 changes: 3 additions & 0 deletions src/domain/use-cases/user/update-user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export class UpdateUser implements UpdateUserUseCase {
async execute(current_user: UserUpdateModel, user_to_update: UserUpdateModel): Promise<UserResponseModel> {
let nb_of_updated_user: number = 0

// User should not be deleted
if (await this.userRepository.isDeleted(user_to_update.user_id)) throw new Error("User is deleted");

// update admin can update anyone
if (await this.userRepository.isAdmin(current_user.user_id)) {
nb_of_updated_user = await this.userRepository.adminUpdateUser(user_to_update)
Expand Down
3 changes: 3 additions & 0 deletions src/domain/use-cases/user/valid-user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ export class ValidUser implements ValidUserUseCase {
if (!decoded_token) throw new Error("Invalid confirmation token");
// Check if user_id in token is the same as user_id in params
if (decoded_token.user_id != user_id) throw new Error("User vallidation forbidden");
// User should not be deleted
if (await this.userRepository.isDeleted(decoded_token.user_id)) throw new Error("User is deleted");


// Find user with confirmation code and user_id
const user_to_update = await this.userRepository.getUser({ user_id: user_id, confirmation_code: decoded_token.confirmation_code })
Expand Down
2 changes: 2 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { ChangePassword } from './domain/use-cases/auth/change-password'
import { ValidUser } from './domain/use-cases/user/valid-user'
import { ResetPasswordRequest } from './domain/use-cases/auth/reset-password-request'
import { ResetPassword } from './domain/use-cases/auth/reset-password'
import { DeleteUser } from './domain/use-cases/user/delete-user'

import { UserRepositoryImpl } from './domain/repositories/user-repository'
import { AuthRepositoryImpl } from './domain/repositories/auth-repository'
Expand Down Expand Up @@ -86,6 +87,7 @@ async function getSQLiteDS() {
new CreateUser(new UserRepositoryImpl(dataSource, bcryptAdapter, jwtAdapter, config.VALIDATION_TOKEN_SECRET, config.RESET_PASSWORD_TOKEN_SECRET), transporter, mailerAdapter),
new UpdateUser(new UserRepositoryImpl(dataSource, bcryptAdapter, jwtAdapter, config.VALIDATION_TOKEN_SECRET, config.RESET_PASSWORD_TOKEN_SECRET)),
new ValidUser(new UserRepositoryImpl(dataSource, bcryptAdapter, jwtAdapter, config.VALIDATION_TOKEN_SECRET, config.RESET_PASSWORD_TOKEN_SECRET)),
new DeleteUser(new UserRepositoryImpl(dataSource, bcryptAdapter, jwtAdapter, config.VALIDATION_TOKEN_SECRET, config.RESET_PASSWORD_TOKEN_SECRET)),
)
const authMiddleWare = AuthRouter(
new MiddlewareAuthCookie(jwtAdapter, config.ACCESS_TOKEN_SECRET, config.REFRESH_TOKEN_SECRET),
Expand Down
18 changes: 17 additions & 1 deletion src/presentation/routers/user-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { CreateUserUseCase } from '../../domain/interfaces/use-cases/user/create
import { GetAllUsersUseCase } from '../../domain/interfaces/use-cases/user/get-all-users'
import { UpdateUserUseCase } from '../../domain/interfaces/use-cases/user/update-user'
import { ValidUserUseCase } from '../../domain/interfaces/use-cases/user/valid-user'
import { DeleteUserUseCase } from '../../domain/interfaces/use-cases/user/delete-user'
import { CustomRequest } from '../../domain/entities/auth'

export default function UsersRouter(
Expand All @@ -15,7 +16,8 @@ export default function UsersRouter(
getAllUsersUseCase: GetAllUsersUseCase,
createUserUseCase: CreateUserUseCase,
updateUserUseCase: UpdateUserUseCase,
validUserUseCase: ValidUserUseCase
validUserUseCase: ValidUserUseCase,
deleteUserUseCase: DeleteUserUseCase
) {
const router = express.Router()

Expand Down Expand Up @@ -73,5 +75,19 @@ export default function UsersRouter(
else res.status(500).send({ errors: ["Can't welcome user"] })
}
})

router.delete('/:user_id/', middlewareAuth.auth, async (req: Request, res: Response) => {
try {
const deleted_user = await deleteUserUseCase.execute((req as CustomRequest).token, { ...req.body, user_id: req.params.user_id })
res.status(200).send(deleted_user)
} catch (err) {
console.log(err)
if (err.message === "Logged user cannot delete this user") res.status(401).send({ errors: [err.message] })
else if (err.message === "Can't find user to delete") res.status(404).send({ errors: [err.message] })
else if (err.message === "Can't find deleted user") res.status(500).send({ errors: [err.message] })
else res.status(500).send({ errors: ["Can't delete user"] })
}
})

return router
}

0 comments on commit 4d269b4

Please sign in to comment.