add controllers for auth

This commit is contained in:
OxSaitama 2023-06-21 15:08:00 +02:00
parent 219ebe5a83
commit acbbba023c
19 changed files with 285 additions and 100 deletions

View File

@ -1,32 +1,94 @@
import { Response, Request } from "express";
import { Controller,Post } from "@ControllerPattern/index";
import ApiController from "@Common/system/controller-pattern/ApiController";
import { Service } from "typedi";
import { Controller, Post } from "@ControllerPattern/index";
import ApiController from "@Common/system/controller-pattern/ApiController";
import { Service } from "typedi";
import AuthService from "@Services/private-services/AuthService/AuthService";
import UsersService from "@Services/super-admin/UsersService/UsersService";
import User from "le-coffre-resources/dist/SuperAdmin";
import { JwtPayload } from "jsonwebtoken";
import { validateOrReject } from "class-validator";
//import User from "le-coffre-resources/dist/Notary";
@Controller()
@Service()
export default class UserInfoController extends ApiController {
constructor(private authService: AuthService) {
super();
}
/**
* @description Get user created from IdNot authentification
* @returns User
*/
@Post("/api/v1/idnot-user/:code")
protected async getUserInfosFromIdnot(req: Request, response: Response) {
try {
const code = req.params["code"];
const user = await this.authService.getUserFromIdNotTokens(code!);
//success
@Controller()
@Service()
export default class UserInfoController extends ApiController {
constructor(private authService: AuthService, private userService: UsersService) {
super();
}
/**
* @description Get user created from IdNot authentification
* @returns User
*/
@Post("/api/v1/idnot-user/:code")
protected async getUserInfosFromIdnot(req: Request, response: Response) {
try {
const code = req.params["code"];
const user = await this.authService.getUserFromIdNotTokens(code!);
//success
this.httpSuccess(response, user);
} catch (error) {
this.httpInternalError(response);
return;
}
}
}
}
@Post("/api/v1/login/:idnot")
protected async login(req: Request, response: Response) {
try {
const id = req.params["idnot"];
if (!id) throw new Error("idnot is required");
const users = await this.userService.get({ where: { idNot: id } , include: {contact: true, role: true, office_membership: true}});
const user = User.hydrate<User>(users[0]!, { strategy: "excludeAll" });
await validateOrReject(user, { groups: ["auth"] })
console.log(user)
const accessToken = this.authService.generateAccessToken(user);
const refreshToken = this.authService.generateRefreshToken(user);
//success
this.httpSuccess(response, { accessToken, refreshToken });
} catch (error) {
console.log(error);
this.httpInternalError(response);
return;
}
}
@Post("/api/v1/refresh-token")
protected async refreshToken(req: Request, response: Response) {
try {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1];
if (!token) {
this.httpBadRequest(response);
return;
}
let accessToken;
this.authService.verifyRefreshToken(token, (err, userPayload) => {
if (err) {
this.httpUnauthorized(response);
return;
}
const user = userPayload as JwtPayload;
console.log(userPayload)
// const user = User.hydrate<User>(userPayload!, { strategy: "excludeAll" });
// const user = await this.userService.getByUid(userPayload!.uid);
// const users = await this.userService.getByUid(userPayload!.uid);
delete user.iat;
delete user!.exp;
accessToken = this.authService.generateAccessToken(user);
});
//success
this.httpSuccess(response, accessToken);
} catch (error) {
console.log(error)
this.httpInternalError(response);
return;
}
}
}

View File

@ -1,16 +1,16 @@
import { Response, Request } from "express";
import { Controller, Get, Post, Put } from "@ControllerPattern/index";
import ApiController from "@Common/system/controller-pattern/ApiController";
import RulesService from "@Services/super-admin/RulesService/RulesService";
import RolesService from "@Services/super-admin/RolesService/RolesService";
import { Service } from "typedi";
import { validateOrReject } from "class-validator";
import { Rule } from "le-coffre-resources/dist/Notary";
import { Rules } from "@prisma/client";
import { Role } from "le-coffre-resources/dist/Notary";
import { Roles } from "@prisma/client";
@Controller()
@Service()
export default class RulesController extends ApiController {
constructor(private rolesService: RulesService) {
export default class RolesController extends ApiController {
constructor(private rolesService: RolesService) {
super();
}
@ -27,7 +27,7 @@ export default class RulesController extends ApiController {
const rolesEntities = await this.rolesService.get(query);
//Hydrate ressource with prisma entity
const roles = Rule.hydrateArray<Rule>(rolesEntities, { strategy: "excludeAll" });
const roles = Role.hydrateArray<Role>(rolesEntities, { strategy: "excludeAll" });
//success
this.httpSuccess(response, roles);
@ -43,17 +43,17 @@ export default class RulesController extends ApiController {
@Post("/api/v1/super-admin/roles")
protected async getAddresses(req: Request, response: Response) {
try {
//init IRule resource with request body values
const roleEntity = Rule.hydrate<Rule>(req.body);
//init IRole resource with request body values
const roleEntity = Role.hydrate<Role>(req.body);
//validate role
await validateOrReject(roleEntity, { groups: ["createRule"] });
await validateOrReject(roleEntity, { groups: ["createRole"] });
//call service to get prisma entity
const roleEntityCreated = await this.rolesService.create(roleEntity);
//Hydrate ressource with prisma entity
const role = Rule.hydrate<Rule>(roleEntityCreated, {
const role = Role.hydrate<Role>(roleEntityCreated, {
strategy: "excludeAll",
});
@ -84,17 +84,17 @@ export default class RulesController extends ApiController {
return;
}
//init IRule resource with request body values
const roleEntity = Rule.hydrate<Rule>(req.body);
//init IRole resource with request body values
const roleEntity = Role.hydrate<Role>(req.body);
//validate role
await validateOrReject(roleEntity, { groups: ["update"] });
await validateOrReject(roleEntity, { groups: ["updateRole"] });
//call service to get prisma entity
const roleEntityUpdated = await this.rolesService.update(roleEntity);
//Hydrate ressource with prisma entity
const role = Rule.hydrate<Rule>(roleEntityUpdated, {
const role = Role.hydrate<Role>(roleEntityUpdated, {
strategy: "excludeAll",
});
@ -117,7 +117,7 @@ export default class RulesController extends ApiController {
this.httpBadRequest(response, "No uid provided");
return;
}
let roleEntity: Rules | null;
let roleEntity: Roles | null;
//get query
if (req.query["q"]) {
const query = JSON.parse(req.query["q"] as string);
@ -133,7 +133,7 @@ export default class RulesController extends ApiController {
}
//Hydrate ressource with prisma entity
const role = Rule.hydrate<Rule>(roleEntity, { strategy: "excludeAll" });
const role = Role.hydrate<Role>(roleEntity, { strategy: "excludeAll" });
//success
this.httpSuccess(response, role);

View File

@ -88,7 +88,7 @@ export default class RulesController extends ApiController {
const ruleEntity = Rule.hydrate<Rule>(req.body);
//validate rule
await validateOrReject(ruleEntity, { groups: ["update"] });
await validateOrReject(ruleEntity, { groups: ["updateRule"] });
//call service to get prisma entity
const ruleEntityUpdated = await this.rulesService.update(ruleEntity);

View File

@ -11,6 +11,8 @@ import DocumentTypesController from "./api/super-admin/DocumentTypesController";
import IdNotUserInfoController from "./api/idnot-user/UserInfoController";
import DocumentsControllerCustomer from "./api/customer/DocumentsController";
import FilesController from "./api/super-admin/FilesController";
import RulesController from "./api/super-admin/RolesController";
import RolesController from "./api/super-admin/RolesController";
/**
@ -30,5 +32,7 @@ export default {
Container.get(IdNotUserInfoController);
Container.get(FilesController);
Container.get(DocumentsControllerCustomer);
Container.get(RulesController);
Container.get(RolesController);
},
};

View File

@ -54,6 +54,12 @@ export class BackendVariables {
@IsNotEmpty()
public readonly PINATA_GATEWAY!: string;
@IsNotEmpty()
public readonly ACCESS_TOKEN_SECRET!: string;
@IsNotEmpty()
public readonly REFRESH_TOKEN_SECRET!: string;
public constructor() {
dotenv.config();
this.DATABASE_PORT = process.env["DATABASE_PORT"]!;
@ -72,9 +78,21 @@ export class BackendVariables {
this.PINATA_API_KEY = process.env["PINATA_API_KEY"]!;
this.PINATA_API_SECRET = process.env["PINATA_API_SECRET"]!;
this.PINATA_GATEWAY = process.env["PINATA_GATEWAY"]!;
this.ACCESS_TOKEN_SECRET = process.env["ACCESS_TOKEN_SECRET"]!;
this.REFRESH_TOKEN_SECRET = process.env["REFRESH_TOKEN_SECRET"]!;
}
public async validate() {
await validateOrReject(this);
public async validate(groups?: string[]) {
const validationOptions = groups ? { groups } : undefined;
try {
await validateOrReject(this, validationOptions);
}
catch(error) {
if(process.env["NODE_ENV"] === "development") {
throw error;
}
throw new Error("Some env variables are required!");
}
return this;
}
}

View File

@ -1,8 +0,0 @@
/*
Warnings:
- Added the required column `iv` to the `files` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "files" ADD COLUMN "iv" VARCHAR(255) NOT NULL;

View File

@ -1,8 +0,0 @@
/*
Warnings:
- Added the required column `file_name` to the `files` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "files" ADD COLUMN "file_name" VARCHAR(255) NOT NULL;

View File

@ -1,9 +0,0 @@
/*
Warnings:
- You are about to drop the column `iv` on the `files` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "files" DROP COLUMN "iv",
ADD COLUMN "key" VARCHAR(255);

View File

@ -1,2 +0,0 @@
-- AlterTable
ALTER TABLE "files" ADD COLUMN "archived_at" TIMESTAMP(3);

View File

@ -1,10 +0,0 @@
/*
Warnings:
- Added the required column `mimetype` to the `files` table without a default value. This is not possible if the table is not empty.
- Added the required column `size` to the `files` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "files" ADD COLUMN "mimetype" VARCHAR(255) NOT NULL,
ADD COLUMN "size" INTEGER NOT NULL;

View File

@ -1,10 +0,0 @@
/*
Warnings:
- Added the required column `mimetype` to the `files` table without a default value. This is not possible if the table is not empty.
- Added the required column `size` to the `files` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "files" ADD COLUMN "mimetype" VARCHAR(255) NOT NULL,
ADD COLUMN "size" INTEGER NOT NULL;

View File

@ -35,9 +35,9 @@ CREATE TABLE "contacts" (
"last_name" VARCHAR(255) NOT NULL,
"email" VARCHAR(255) NOT NULL,
"phone_number" VARCHAR(50),
"cell_phone_number" VARCHAR(50),
"cell_phone_number" VARCHAR(50) NOT NULL,
"civility" "ECivility" NOT NULL DEFAULT 'MALE',
"address_uid" VARCHAR(255) NOT NULL,
"address_uid" VARCHAR(255),
"birthdate" TIMESTAMP(3),
"created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3),
@ -50,6 +50,7 @@ CREATE TABLE "users" (
"uid" TEXT NOT NULL,
"idNot" VARCHAR(255) NOT NULL,
"contact_uid" VARCHAR(255) NOT NULL,
"roles_uid" TEXT NOT NULL,
"created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3),
"office_uid" VARCHAR(255) NOT NULL,
@ -174,6 +175,11 @@ CREATE TABLE "files" (
"uid" TEXT NOT NULL,
"document_uid" VARCHAR(255) NOT NULL,
"file_path" VARCHAR(255) NOT NULL,
"file_name" VARCHAR(255) NOT NULL,
"mimetype" VARCHAR(255) NOT NULL,
"size" INTEGER NOT NULL,
"archived_at" TIMESTAMP(3),
"key" VARCHAR(255),
"created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3),
@ -249,6 +255,50 @@ CREATE TABLE "deed_type_has_document_types" (
CONSTRAINT "deed_type_has_document_types_pkey" PRIMARY KEY ("uid")
);
-- CreateTable
CREATE TABLE "roles" (
"uid" TEXT NOT NULL,
"name" VARCHAR(255) NOT NULL,
"created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3),
CONSTRAINT "roles_pkey" PRIMARY KEY ("uid")
);
-- CreateTable
CREATE TABLE "rules" (
"uid" TEXT NOT NULL,
"name" VARCHAR(255) NOT NULL,
"created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3),
"role_has_rules_uid" TEXT,
"office_role_has_rules_uid" TEXT,
CONSTRAINT "rules_pkey" PRIMARY KEY ("uid")
);
-- CreateTable
CREATE TABLE "role_has_rules" (
"uid" TEXT NOT NULL,
"role_uid" VARCHAR(255) NOT NULL,
"rule_uid" VARCHAR(255) NOT NULL,
"created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3),
CONSTRAINT "role_has_rules_pkey" PRIMARY KEY ("uid")
);
-- CreateTable
CREATE TABLE "office_role_has_rules" (
"uid" TEXT NOT NULL,
"role_uid" VARCHAR(255) NOT NULL,
"rule_uid" VARCHAR(255) NOT NULL,
"created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3),
CONSTRAINT "office_role_has_rules_pkey" PRIMARY KEY ("uid")
);
-- CreateIndex
CREATE UNIQUE INDEX "addresses_uid_key" ON "addresses"("uid");
@ -366,12 +416,27 @@ CREATE UNIQUE INDEX "deed_type_has_document_types_uid_key" ON "deed_type_has_doc
-- CreateIndex
CREATE UNIQUE INDEX "deed_type_has_document_types_deed_type_uid_document_type_ui_key" ON "deed_type_has_document_types"("deed_type_uid", "document_type_uid");
-- CreateIndex
CREATE UNIQUE INDEX "roles_uid_key" ON "roles"("uid");
-- CreateIndex
CREATE UNIQUE INDEX "rules_uid_key" ON "rules"("uid");
-- CreateIndex
CREATE UNIQUE INDEX "role_has_rules_uid_key" ON "role_has_rules"("uid");
-- CreateIndex
CREATE UNIQUE INDEX "office_role_has_rules_uid_key" ON "office_role_has_rules"("uid");
-- AddForeignKey
ALTER TABLE "contacts" ADD CONSTRAINT "contacts_address_uid_fkey" FOREIGN KEY ("address_uid") REFERENCES "addresses"("uid") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "users" ADD CONSTRAINT "users_contact_uid_fkey" FOREIGN KEY ("contact_uid") REFERENCES "contacts"("uid") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "users" ADD CONSTRAINT "users_roles_uid_fkey" FOREIGN KEY ("roles_uid") REFERENCES "roles"("uid") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "users" ADD CONSTRAINT "users_office_uid_fkey" FOREIGN KEY ("office_uid") REFERENCES "offices"("uid") ON DELETE CASCADE ON UPDATE CASCADE;
@ -444,6 +509,14 @@ ALTER TABLE "deed_type_has_document_types" ADD CONSTRAINT "deed_type_has_documen
-- AddForeignKey
ALTER TABLE "deed_type_has_document_types" ADD CONSTRAINT "deed_type_has_document_types_deed_type_uid_fkey" FOREIGN KEY ("deed_type_uid") REFERENCES "deed_types"("uid") ON DELETE CASCADE ON UPDATE CASCADE;
-- AlterTable
ALTER TABLE "contacts" ALTER COLUMN "cell_phone_number" SET NOT NULL,
ALTER COLUMN "address_uid" DROP NOT NULL;
-- AddForeignKey
ALTER TABLE "rules" ADD CONSTRAINT "rules_role_has_rules_uid_fkey" FOREIGN KEY ("role_has_rules_uid") REFERENCES "role_has_rules"("uid") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "rules" ADD CONSTRAINT "rules_office_role_has_rules_uid_fkey" FOREIGN KEY ("office_role_has_rules_uid") REFERENCES "office_role_has_rules"("uid") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "role_has_rules" ADD CONSTRAINT "role_has_rules_role_uid_fkey" FOREIGN KEY ("role_uid") REFERENCES "roles"("uid") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "office_role_has_rules" ADD CONSTRAINT "office_role_has_rules_role_uid_fkey" FOREIGN KEY ("role_uid") REFERENCES "roles"("uid") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -0,0 +1,14 @@
/*
Warnings:
- You are about to alter the column `roles_uid` on the `users` table. The data in that column could be lost. The data in that column will be cast from `Text` to `VarChar(255)`.
*/
-- DropForeignKey
ALTER TABLE "users" DROP CONSTRAINT "users_roles_uid_fkey";
-- AlterTable
ALTER TABLE "users" ALTER COLUMN "roles_uid" SET DATA TYPE VARCHAR(255);
-- AddForeignKey
ALTER TABLE "users" ADD CONSTRAINT "users_roles_uid_fkey" FOREIGN KEY ("roles_uid") REFERENCES "roles"("uid") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -54,8 +54,8 @@ model Users {
idNot String @unique @db.VarChar(255)
contact Contacts @relation(fields: [contact_uid], references: [uid], onDelete: Cascade)
contact_uid String @unique @db.VarChar(255)
role Roles @relation(fields: [roles_uid], references: [uid])
roles_uid String
role Roles @relation(fields: [roles_uid], references: [uid], onDelete: Cascade)
roles_uid String @db.VarChar(255)
created_at DateTime? @default(now())
updated_at DateTime? @updatedAt
office_membership Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade)

View File

@ -19,6 +19,7 @@ import {
ECivility,
ECustomerStatus,
PrismaClient,
Roles,
} from "@prisma/client";
(async () => {
@ -226,6 +227,21 @@ import {
},
];
const roles: Roles[] = [
{
uid: uidRole1,
name: 'super-admin',
created_at: new Date(),
updated_at: new Date(),
},
{
uid: uidRole2,
name: 'admin',
created_at: new Date(),
updated_at: new Date(),
}
];
const users: Users[] = [
{
uid: uidUser1,
@ -532,6 +548,10 @@ import {
await prisma.offices.create({ data: office });
}
for (const role of roles) {
await prisma.roles.create({ data: role });
}
for (const user of users) {
await prisma.users.create({ data: user });
}

View File

@ -17,6 +17,7 @@ import {
ECivility,
ECustomerStatus,
PrismaClient,
Roles,
} from "@prisma/client";
(async () => {
@ -788,6 +789,21 @@ import {
}
];
const roles: Roles[] = [
{
uid: uidRole1,
name: 'super-admin',
created_at: new Date(),
updated_at: new Date(),
},
{
uid: uidRole2,
name: 'admin',
created_at: new Date(),
updated_at: new Date(),
}
];
const users: Users[] = [
{
uid: uidUser1,
@ -1848,6 +1864,10 @@ import {
await prisma.offices.create({ data: office });
}
for (const role of roles) {
await prisma.roles.create({ data: role });
}
for (const user of users) {
await prisma.users.create({ data: user });
}

View File

@ -32,6 +32,10 @@ export default abstract class BaseController {
return this.httpResponse(response, HttpCodes.INTERNAL_ERROR, responseData);
}
protected httpUnauthorized(response: Response, responseData: IResponseData = "http Unauthorized Request") {
return this.httpResponse(response, HttpCodes.UNAUTHORIZED, responseData);
}
protected httpNotImplemented(response: Response, responseData: IResponseData = "Not implemented") {
return this.httpResponse(response, HttpCodes.NOT_IMPLEMENTED, responseData);
}

View File

@ -7,5 +7,6 @@ enum HttpCodes {
UNKNOWN_ERROR = 520,
NOT_IMPLEMENTED = 501,
NOT_FOUND = 404,
UNAUTHORIZED = 401,
}
export default HttpCodes;

View File

@ -1,8 +1,9 @@
import jwt from "jsonwebtoken";
import jwt, { VerifyCallback } from "jsonwebtoken";
import BaseService from "@Services/BaseService";
import "reflect-metadata";
import { BackendVariables } from "@Common/config/variables/Variables";
import Container, { Service } from "typedi";
import { Service } from "typedi";
//import User from "le-coffre-resources/dist/Notary";
type IdNotTokens = {
access_token: string;
@ -11,8 +12,7 @@ type IdNotTokens = {
@Service()
export default class AuthService extends BaseService {
protected readonly variables = Container.get(BackendVariables);
private constructor() {
private constructor(protected variables: BackendVariables) {
super();
}
@ -50,4 +50,20 @@ export default class AuthService extends BaseService {
throw new Error();
}
}
public generateAccessToken(user: any) {
return jwt.sign({...user}, this.variables.ACCESS_TOKEN_SECRET, { expiresIn: "15m" });
}
public generateRefreshToken(user: any) {
return jwt.sign({...user}, this.variables.REFRESH_TOKEN_SECRET, { expiresIn: "1h" });
}
public verifyAccessToken(token: string, callback?: VerifyCallback) {
return jwt.verify(token, this.variables.ACCESS_TOKEN_SECRET, callback);
}
public verifyRefreshToken(token: string, callback?: VerifyCallback) {
return jwt.verify(token, this.variables.REFRESH_TOKEN_SECRET, callback);
}
}