From ff4f6d918eb9ba2af24cf1da953cdc9bd9cf99c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Robinaldo=20Ramos=20Da=20Silva=20Junior?= Date: Tue, 13 Sep 2022 21:40:42 -0300 Subject: [PATCH] atividade criptografia-e-user-roles --- .../criptografia-e-user-roles/package.json | 33 ++++ .../criptografia-e-user-roles/requests.rest | 37 +++++ .../criptografia-e-user-roles/src/.DS_Store | Bin 0 -> 6148 bytes modulo5/criptografia-e-user-roles/src/app.ts | 13 ++ .../src/data/BaseDatabase.ts | 20 +++ .../src/data/UserDatabase.ts | 61 ++++++++ .../src/endpoints/User.ts | 145 ++++++++++++++++++ .../src/endpoints/createUser.ts | 41 +++++ .../src/endpoints/editUser.ts | 30 ++++ .../src/error/BaseError.ts | 8 + .../src/error/EmailExist.ts | 7 + .../src/error/IncorrectPassword.ts | 7 + .../src/error/MissingFields.ts | 7 + .../criptografia-e-user-roles/src/index.ts | 15 ++ .../src/model/User.ts | 39 +++++ .../src/services/Authenticator.ts | 39 +++++ .../src/services/GenerateId.ts | 13 ++ .../src/services/HashManager.ts | 20 +++ .../criptografia-e-user-roles/src/types.ts | 13 ++ modulo5/criptografia-e-user-roles/tables.sql | 29 ++++ .../criptografia-e-user-roles/tsconfig.json | 14 ++ 21 files changed, 591 insertions(+) create mode 100644 modulo5/criptografia-e-user-roles/package.json create mode 100644 modulo5/criptografia-e-user-roles/requests.rest create mode 100644 modulo5/criptografia-e-user-roles/src/.DS_Store create mode 100644 modulo5/criptografia-e-user-roles/src/app.ts create mode 100644 modulo5/criptografia-e-user-roles/src/data/BaseDatabase.ts create mode 100644 modulo5/criptografia-e-user-roles/src/data/UserDatabase.ts create mode 100644 modulo5/criptografia-e-user-roles/src/endpoints/User.ts create mode 100644 modulo5/criptografia-e-user-roles/src/endpoints/createUser.ts create mode 100644 modulo5/criptografia-e-user-roles/src/endpoints/editUser.ts create mode 100644 modulo5/criptografia-e-user-roles/src/error/BaseError.ts create mode 100644 modulo5/criptografia-e-user-roles/src/error/EmailExist.ts create mode 100644 modulo5/criptografia-e-user-roles/src/error/IncorrectPassword.ts create mode 100644 modulo5/criptografia-e-user-roles/src/error/MissingFields.ts create mode 100644 modulo5/criptografia-e-user-roles/src/index.ts create mode 100644 modulo5/criptografia-e-user-roles/src/model/User.ts create mode 100644 modulo5/criptografia-e-user-roles/src/services/Authenticator.ts create mode 100644 modulo5/criptografia-e-user-roles/src/services/GenerateId.ts create mode 100644 modulo5/criptografia-e-user-roles/src/services/HashManager.ts create mode 100644 modulo5/criptografia-e-user-roles/src/types.ts create mode 100644 modulo5/criptografia-e-user-roles/tables.sql create mode 100644 modulo5/criptografia-e-user-roles/tsconfig.json diff --git a/modulo5/criptografia-e-user-roles/package.json b/modulo5/criptografia-e-user-roles/package.json new file mode 100644 index 0000000..870e63a --- /dev/null +++ b/modulo5/criptografia-e-user-roles/package.json @@ -0,0 +1,33 @@ +{ + "name": "to-do-list", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "start": "tsc && node --inspect ./build/index.js", + "dev": "ts-node-dev --transpile-only --ignore-watch node_modules ./src/index.ts", + "test": "ts-node-dev ./src/services/authenticator.ts" + }, + "author": "Labenu", + "license": "ISC", + "dependencies": { + "bcryptjs": "^2.4.3", + "cors": "^2.8.5", + "dotenv": "^8.2.0", + "express": "^4.17.1", + "jsonwebtoken": "^8.5.1", + "knex": "^0.21.5", + "mysql": "^2.18.1", + "ts-node-dev": "^2.0.0", + "uuid": "^9.0.0" + }, + "devDependencies": { + "@types/bcryptjs": "^2.4.2", + "@types/cors": "^2.8.8", + "@types/express": "^4.17.8", + "@types/jsonwebtoken": "^8.5.9", + "@types/knex": "^0.16.1", + "@types/node": "^14.11.2", + "@types/uuid": "^8.3.4", + "typescript": "^4.0.3" + } +} diff --git a/modulo5/criptografia-e-user-roles/requests.rest b/modulo5/criptografia-e-user-roles/requests.rest new file mode 100644 index 0000000..96f2625 --- /dev/null +++ b/modulo5/criptografia-e-user-roles/requests.rest @@ -0,0 +1,37 @@ +# @name signup + +POST http://localhost:3003/user/signup +Content-Type: application/json + +{ + "name": "Norman Osbourne", + "nickname": "Green Goblin", + "email": "osbourne6@oscorp.com" , + "password": "ihatepeter", + "role": "ADMIN" +} + +### + +@id = {{signup.response.body.newUser.id}} + +PUT http://localhost:3003/user/edit/{{id}} +Content-Type: application/json + +{ + "name": "Harry Osbourne" +} + +### Login +POST http://localhost:3003/user/login +Content-Type: application/json + +{ + "email": "osbourne6@oscorp.com", + "password": "ihatepeter" +} + +### Get Users +GET http://localhost:3003/user/all +Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Ijk3Y2IyZDY5LWRhMDctNGU5Ni1hOWI0LWNhNjFkNTk1ODVmOSIsInJvbGUiOiJBRE1JTiIsImlhdCI6MTY2MzA4MDE3NSwiZXhwIjoxNjYzMDgzNzc1fQ._bpg5NuS0qWYovoRdurkLSyAgGzFRfauEVVZjb6RKAo + diff --git a/modulo5/criptografia-e-user-roles/src/.DS_Store b/modulo5/criptografia-e-user-roles/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..bfe824b7ef69553f5eeafae89a5d4952dfef57df GIT binary patch literal 6148 zcmeHKI|>3Z5S>wr1shAtT)`Uz(Gz$9@lyzb;G*?bp39^8=2IxEoi_3YCNG)HOUNsB zc0@$ym)%TcA|egkP;NH#&GyZEHpqwq;W*{ + console.log('Servidor rodando na porta 3003') +}) + +export default app \ No newline at end of file diff --git a/modulo5/criptografia-e-user-roles/src/data/BaseDatabase.ts b/modulo5/criptografia-e-user-roles/src/data/BaseDatabase.ts new file mode 100644 index 0000000..e3f2325 --- /dev/null +++ b/modulo5/criptografia-e-user-roles/src/data/BaseDatabase.ts @@ -0,0 +1,20 @@ +import Knex from "knex" +import dotenv from "dotenv" +dotenv.config() + +export class BaseDatabase { + private static connection = Knex({ + client: 'mysql', + connection: { + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_SCHEMA, + port: 3306, + multipleStatements: true + } + }) + protected getConnection() { + return BaseDatabase.connection + } +} \ No newline at end of file diff --git a/modulo5/criptografia-e-user-roles/src/data/UserDatabase.ts b/modulo5/criptografia-e-user-roles/src/data/UserDatabase.ts new file mode 100644 index 0000000..fe071ba --- /dev/null +++ b/modulo5/criptografia-e-user-roles/src/data/UserDatabase.ts @@ -0,0 +1,61 @@ +import User from "../model/User"; +import { IUserDB } from "../types"; +import { BaseDatabase } from "./BaseDatabase"; + +// Utiliza sempre que precisar salvar, editar, deletar ou consultar recursos + +export class UserDatabase extends BaseDatabase { + + public async createUser(user: User) { + + const userDB: IUserDB = { + id: user.getId(), + name: user.getName(), + nickname: user.getNickName(), + email: user.getEmail(), + password: user.getPassword(), + role: user.getRole() + } + + await this.getConnection().insert(userDB).into("to_do_list_users") + } + + public async edit(id: string, nickname: string) { + await this.getConnection().update({ nickname }).into("to_do_list_users").where({ id }) + } + + public async getUserByEmail(email: string): Promise { + const result: IUserDB[] = await this.getConnection() + .select("*") + .from("to_do_list_users") + .where({ email }) + + return result[0] + } + + public async getUserByPassword(senha: string) { + const result: IUserDB[] = await this.getConnection() + .select("*") + .from("to_do_list_users") + .where({ password: senha }) + + return result + } + + public async getUserById(id: string) { + const result: IUserDB[] = await this.getConnection() + .select("*") + .from("to_do_list_users") + .where({ id }) + + return result[0] + } + + public async getUsers() { + const result: IUserDB[] = await this.getConnection() + .select("*") + .from("to_do_list_users") + + return result + } +} diff --git a/modulo5/criptografia-e-user-roles/src/endpoints/User.ts b/modulo5/criptografia-e-user-roles/src/endpoints/User.ts new file mode 100644 index 0000000..a45d9e2 --- /dev/null +++ b/modulo5/criptografia-e-user-roles/src/endpoints/User.ts @@ -0,0 +1,145 @@ +import { Request, Response } from "express"; +import { UserDatabase } from "../data/UserDatabase"; +import { EmailExist } from "../error/EmailExist"; +import { InvalidCredencial } from "../error/IncorrectPassword"; +import { MissingFields } from "../error/MissingFields"; +import User from "../model/User"; +import Authenticator, { ITokenPayload } from "../services/Authenticator"; +import GenerateId from "../services/GenerateId"; +import { HashManager } from "../services/HashManager"; +import { USER_ROLES } from "../types"; + +class UserEndpoint { + + public async createUser(req: Request, res: Response) { + try { + const { name, nickname, email, password, role } = req.body + + if (!name || !nickname || !email || !password) { + throw new MissingFields() + } + + const userDataBase = new UserDatabase() + + const userDB = await userDataBase.getUserByEmail(email) + + if (userDB) { + throw new EmailExist() + } + + const id = new GenerateId().createId(); + + // const user = new User(id, name, nickname, email, password) + + const hashManager = new HashManager() + const hash = await hashManager.hash(password) + + const user = new User(id, name, nickname, email, hash, role) + + await userDataBase.createUser(user) + + const payload: ITokenPayload = { + id, + role + } + + const token = new Authenticator().generateToken(payload) + + res.status(201).send({ message: "Usuario cadastrado com sucesso", token }) + + } catch (error: any) { + res.status(error.statusCode || 500).send({ message: error.message }) + } + } + + async login(req: Request, res: Response) { + try { + + const { email, password } = req.body + + if (!email || !password) { + throw new MissingFields() + } + + const userData = new UserDatabase() + + const userDB = await userData.getUserByEmail(email) + + if (!userDB) { + throw new InvalidCredencial(); + } + + // if (userDB.password !== password) { + // throw new InvalidCredencial(); + // } + + const hashManager = new HashManager() + const isPasswordCorrect = await hashManager.compare(password, userDB.password) + + if (!isPasswordCorrect) { + throw new InvalidCredencial(); + } + + const payload: ITokenPayload = { + id: userDB.id, + role: userDB.role + } + + const token = new Authenticator().generateToken(payload) + + res.status(200).send({ token }) + + } catch (error: any) { + res.status(error.statusCode || 500).send({ message: error.message }) + } + } + + async editUser(req: Request, res: Response) { + try { + // const token = req.headers.authorization as string + const token = req.headers.authorization! + const { nickname } = req.body + + const payload = new Authenticator().verifyToken(token) + + const userData = new UserDatabase() + + await userData.edit(payload.id,nickname) + + res.status(200).send("Atualizado com sucesso!") + + } catch (error: any) { + res.status(error.statusCode || 500).send({ message: error.message }) + } + } + + async getUsers(req: Request, res: Response) { + try { + const token = req.headers.authorization + + if (!token) { + throw new InvalidCredencial(); + } + + const authenticator = new Authenticator() + const payload = authenticator.verifyToken(token) + + if (payload.role !== USER_ROLES.ADMIN) { + throw new Error("Autorização insuficiente") + } + + const userData = new UserDatabase() + const usersDB = await userData.getUsers() + + res.status(200).send({ users: usersDB }) + + } catch (error: any) { + res.status(error.statusCode || 500).send({ message: error.message }) + } + } +} + +export default UserEndpoint + + + diff --git a/modulo5/criptografia-e-user-roles/src/endpoints/createUser.ts b/modulo5/criptografia-e-user-roles/src/endpoints/createUser.ts new file mode 100644 index 0000000..f36ac6a --- /dev/null +++ b/modulo5/criptografia-e-user-roles/src/endpoints/createUser.ts @@ -0,0 +1,41 @@ +import { Request, Response } from "express"; +import { UserDatabase } from "../data/UserDatabase"; +import { user } from "../types"; + +export default async function createUser( + req: Request, + res: Response +): Promise { + try { + + const { name, nickname, email, password } = req.body + const userDB = new UserDatabase() + + if (!name || !nickname || !email || !password) { + res.statusCode = 422 + throw new Error("Preencha os campos 'name','nickname', 'password' e 'email'") + } + + const user = await userDB.getByEmail(email) + console.log(user) + if (user) { + res.statusCode = 409 + throw new Error('Email já cadastrado') + } + + const id: string = Date.now().toString() + + const newUser: user = { id, name, nickname, email, password } + + await userDB.create(newUser) + + res.status(201).send({ newUser }) + + } catch (error: any) { + if (res.statusCode === 200) { + res.status(500).send({ message: "Internal server error" }) + } else { + res.send({ message: error.message }) + } + } +} \ No newline at end of file diff --git a/modulo5/criptografia-e-user-roles/src/endpoints/editUser.ts b/modulo5/criptografia-e-user-roles/src/endpoints/editUser.ts new file mode 100644 index 0000000..f619e17 --- /dev/null +++ b/modulo5/criptografia-e-user-roles/src/endpoints/editUser.ts @@ -0,0 +1,30 @@ +import { Request, Response } from "express"; +import { UserDatabase } from "../data/UserDatabase"; + +export default async function createUser( + req: Request, + res: Response +): Promise { + try { + + const { name, nickname } = req.body + + if (!name && !nickname) { + res.statusCode = 422 + res.statusMessage = "Informe o(s) novo(s) 'name' ou 'nickname'" + throw new Error() + } + + new UserDatabase().edit(req.params.id, {name, nickname}) + + res.end() + + } catch (error) { + + if (res.statusCode === 200) { + res.status(500).end() + } + + res.end() + } +} \ No newline at end of file diff --git a/modulo5/criptografia-e-user-roles/src/error/BaseError.ts b/modulo5/criptografia-e-user-roles/src/error/BaseError.ts new file mode 100644 index 0000000..4a38d3c --- /dev/null +++ b/modulo5/criptografia-e-user-roles/src/error/BaseError.ts @@ -0,0 +1,8 @@ +export class BaseError extends Error{ + public statusCode:number + + constructor(message:string,statusCode:number){ + super(message) + this.statusCode = statusCode + } +} diff --git a/modulo5/criptografia-e-user-roles/src/error/EmailExist.ts b/modulo5/criptografia-e-user-roles/src/error/EmailExist.ts new file mode 100644 index 0000000..51461a0 --- /dev/null +++ b/modulo5/criptografia-e-user-roles/src/error/EmailExist.ts @@ -0,0 +1,7 @@ +import { BaseError } from "./BaseError"; + +export class EmailExist extends BaseError{ + constructor(){ + super("Email ja existe na aplicação",401) + } +} \ No newline at end of file diff --git a/modulo5/criptografia-e-user-roles/src/error/IncorrectPassword.ts b/modulo5/criptografia-e-user-roles/src/error/IncorrectPassword.ts new file mode 100644 index 0000000..20d5ee7 --- /dev/null +++ b/modulo5/criptografia-e-user-roles/src/error/IncorrectPassword.ts @@ -0,0 +1,7 @@ +import { BaseError } from "./BaseError"; + +export class InvalidCredencial extends BaseError{ + constructor(){ + super("As credenciais estao incorretas",401) + } +} \ No newline at end of file diff --git a/modulo5/criptografia-e-user-roles/src/error/MissingFields.ts b/modulo5/criptografia-e-user-roles/src/error/MissingFields.ts new file mode 100644 index 0000000..12ff302 --- /dev/null +++ b/modulo5/criptografia-e-user-roles/src/error/MissingFields.ts @@ -0,0 +1,7 @@ +import { BaseError } from "./BaseError"; + +export class MissingFields extends BaseError{ + constructor(){ + super("Valores devem ser passados",404) + } +} \ No newline at end of file diff --git a/modulo5/criptografia-e-user-roles/src/index.ts b/modulo5/criptografia-e-user-roles/src/index.ts new file mode 100644 index 0000000..4b53ca8 --- /dev/null +++ b/modulo5/criptografia-e-user-roles/src/index.ts @@ -0,0 +1,15 @@ +import app from "./app" +import UserEndpoint from "./endpoints/User" + +const user = new UserEndpoint() + + +app.post('/user/signup', user.createUser) +app.post("/user/login",user.login) +app.put('/user/edit', user.editUser) +app.get('/user/all', user.getUsers) + + + + + diff --git a/modulo5/criptografia-e-user-roles/src/model/User.ts b/modulo5/criptografia-e-user-roles/src/model/User.ts new file mode 100644 index 0000000..e316d7a --- /dev/null +++ b/modulo5/criptografia-e-user-roles/src/model/User.ts @@ -0,0 +1,39 @@ +import { USER_ROLES } from "../types" + +class User{ + + constructor( + private id:string, + private name:string, + private nickname:string, + private email:string, + private password:string, + private role: USER_ROLES = USER_ROLES.NORMAL + ){} + + public getId(){ + return this.id + } + + public getName(){ + return this.name + } + + public getNickName(){ + return this.nickname + } + + public getEmail(){ + return this.email + } + + public getPassword(){ + return this.password + } + + public getRole = () => { + return this.role + } +} + +export default User \ No newline at end of file diff --git a/modulo5/criptografia-e-user-roles/src/services/Authenticator.ts b/modulo5/criptografia-e-user-roles/src/services/Authenticator.ts new file mode 100644 index 0000000..39c6d2f --- /dev/null +++ b/modulo5/criptografia-e-user-roles/src/services/Authenticator.ts @@ -0,0 +1,39 @@ +import jwt from "jsonwebtoken" +import dotenv from "dotenv" +import { USER_ROLES } from "../types"; + +dotenv.config(); + +// Gera tokens e gera payloads = encriptar/decriptar +// Utiliza nos endpoints protegidos e signup/login + +// Caso de uso que não utiliza esse arquivo: endpoints públicos + +export interface ITokenPayload { + id: string, + role: USER_ROLES +} + +class Authenticator { + + generateToken(payload: ITokenPayload) { + const token = jwt.sign( + payload, + process.env.JWT_KEY as string, + { + expiresIn: process.env.EXPIRES_IN + } + ); + + return token + } + + verifyToken(token: string) { + + const payload: ITokenPayload = jwt.verify(token, process.env.JWT_KEY as string) as any + + return payload + } +} + +export default Authenticator \ No newline at end of file diff --git a/modulo5/criptografia-e-user-roles/src/services/GenerateId.ts b/modulo5/criptografia-e-user-roles/src/services/GenerateId.ts new file mode 100644 index 0000000..2463795 --- /dev/null +++ b/modulo5/criptografia-e-user-roles/src/services/GenerateId.ts @@ -0,0 +1,13 @@ +import { v4 } from "uuid" + +// Utiliza no signup e endpoints de criação de recursos + +class GenerateId { + + createId(): string { + return v4(); + } +} + +export default GenerateId + diff --git a/modulo5/criptografia-e-user-roles/src/services/HashManager.ts b/modulo5/criptografia-e-user-roles/src/services/HashManager.ts new file mode 100644 index 0000000..8ce5f8a --- /dev/null +++ b/modulo5/criptografia-e-user-roles/src/services/HashManager.ts @@ -0,0 +1,20 @@ +import bcrypt from 'bcryptjs' +import dotenv from 'dotenv' + +dotenv.config() + +// Utiliza sempre que precisar lidar com senhas (signup/login) + +export class HashManager { + public hash = async (plaintext: string) => { + const rounds = Number(process.env.BCRYPT_COST) + const salt = await bcrypt.genSalt(rounds) + const hash = await bcrypt.hash(plaintext, salt) + + return hash + } + + public compare = async (plaintext: string, hash: string) => { + return bcrypt.compare(plaintext, hash) + } +} diff --git a/modulo5/criptografia-e-user-roles/src/types.ts b/modulo5/criptografia-e-user-roles/src/types.ts new file mode 100644 index 0000000..4292023 --- /dev/null +++ b/modulo5/criptografia-e-user-roles/src/types.ts @@ -0,0 +1,13 @@ +export enum USER_ROLES { + NORMAL = "NORMAL", + ADMIN = "ADMIN" +} + +export interface IUserDB { + id: string + email: string + password: string + name: string + nickname: string, + role: USER_ROLES +} \ No newline at end of file diff --git a/modulo5/criptografia-e-user-roles/tables.sql b/modulo5/criptografia-e-user-roles/tables.sql new file mode 100644 index 0000000..201e480 --- /dev/null +++ b/modulo5/criptografia-e-user-roles/tables.sql @@ -0,0 +1,29 @@ +-- Active: 1656426490262@@35.226.146.116@3306@teacher-gabriel-mina +CREATE TABLE IF NOT EXISTS to_do_list_users ( + id VARCHAR(64) PRIMARY KEY, + name VARCHAR(64) NOT NULL, + nickname VARCHAR(64) NOT NULL, + email VARCHAR(64) NOT NULL, + password VARCHAR(64) NOT NULL, + role ENUM("NORMAL", "ADMIN") DEFAULT "NORMAL" NOT NULL +); + +CREATE TABLE IF NOT EXISTS to_do_list_tasks ( + id VARCHAR(64) PRIMARY KEY, + title VARCHAR(64) NOT NULL, + description VARCHAR(1024) DEFAULT "No description provided", + deadline DATE, + status ENUM("TO_DO", "DOING", "DONE") DEFAULT "TO_DO", + author_id VARCHAR(64), + FOREIGN KEY (author_id) REFERENCES to_do_list_users(id) +); + +CREATE TABLE IF NOT EXISTS to_do_list_assignees ( + task_id VARCHAR(64), + assignee_id VARCHAR(64), + PRIMARY KEY (task_id, assignee_id), + FOREIGN KEY (task_id) REFERENCES to_do_list_tasks(id), + FOREIGN KEY (assignee_id) REFERENCES to_do_list_users(id) +); + +-- ALTER TABLE nome_da_tabela ADD COLUMN role ENUM("NORMAL", "ADMIN") DEFAULT "NORMAL" NOT NULL; \ No newline at end of file diff --git a/modulo5/criptografia-e-user-roles/tsconfig.json b/modulo5/criptografia-e-user-roles/tsconfig.json new file mode 100644 index 0000000..8c020cc --- /dev/null +++ b/modulo5/criptografia-e-user-roles/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "outDir": "./build", + "rootDir": "./src", + "removeComments": true, + "strict": true, + "noImplicitAny": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + } +} \ No newline at end of file