diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..0d9e9fb --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +version: 2 + +updates: + - package-ecosystem: 'npm' + directory: '/' + schedule: + interval: 'weekly' + day: 'monday' + time: '08:00' + timezone: 'Etc/GMT' + target-branch: 'workflow/dependency-update' + labels: + - 'workflow' + milestone: 1 diff --git a/.github/workflows/cron-dependency-checker-workflow.yml b/.github/workflows/cron-dependency-checker-workflow.yml new file mode 100644 index 0000000..70522fd --- /dev/null +++ b/.github/workflows/cron-dependency-checker-workflow.yml @@ -0,0 +1,34 @@ +name: Cron Dependency Checker Workflow + +on: + schedule: + - cron: '0 4 * * 1' + +jobs: + cron-dependency-checker: + name: 'Cron Dependency Checker' + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@v2 + with: + token: ${{ secrets.MASTER_BRANCH_ACCESS_TOKEN }} + + - name: Install Node + uses: actions/setup-node@v1 + with: + node-version: 12 + + - name: NPM Install + run: npm i + - name: Npm Outdated + run: npm run outdated + - name: Build + run: npm run buildProd + + - name: Git Commit and Push + run: | + git config --global user.email "furknyavuz@gmail.com" + git config --global user.name "Furkan Yavuz" + git commit -am "Workflow/dependency check" + git push diff --git a/.github/workflows/issue-assigned-workflows.yml b/.github/workflows/issue-assigned-workflows.yml index 65d4417..8bc46b6 100644 --- a/.github/workflows/issue-assigned-workflows.yml +++ b/.github/workflows/issue-assigned-workflows.yml @@ -6,7 +6,7 @@ on: jobs: automate-project-columns: - name: "Automate Project Columns" + name: 'Automate Project Columns' runs-on: ubuntu-latest steps: diff --git a/.github/workflows/on-push-tags.yml b/.github/workflows/on-push-tags.yml index 32c3d16..73f8127 100644 --- a/.github/workflows/on-push-tags.yml +++ b/.github/workflows/on-push-tags.yml @@ -7,12 +7,12 @@ on: jobs: tagged-release: - name: "Tagged Release" - runs-on: "ubuntu-latest" + name: 'Tagged Release' + runs-on: 'ubuntu-latest' steps: - name: Generate Release From Tag - uses: "marvinpinto/action-automatic-releases@latest" + uses: 'marvinpinto/action-automatic-releases@latest' with: - repo_token: "${{ secrets.MASTER_BRANCH_ACCESS_TOKEN }}" + repo_token: '${{ secrets.MASTER_BRANCH_ACCESS_TOKEN }}' prerelease: false diff --git a/.github/workflows/on-push-to-master.yml b/.github/workflows/on-push-to-master.yml deleted file mode 100644 index 65f1097..0000000 --- a/.github/workflows/on-push-to-master.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: On Push to Master Workflows - -on: - push: - branches: - - "master" - -jobs: - continuous-integration: - name: "Npm Continuous Integration" - runs-on: ubuntu-latest - - steps: - - name: Checkout Repo - uses: actions/checkout@v2 - - - name: Install Node - uses: actions/setup-node@v1 - with: - node-version: 12 - - - name: NPM Install - run: npm install - - name: NPM Continous Integration - run: npm ci diff --git a/.github/workflows/on-version-update.yml b/.github/workflows/on-version-update.yml index 288865e..113154c 100644 --- a/.github/workflows/on-version-update.yml +++ b/.github/workflows/on-version-update.yml @@ -5,11 +5,11 @@ on: paths-ignore: - 'package.json' branches: - - "master" + - 'master' jobs: bump-version: - name: "Update Version" + name: 'Update Version' runs-on: ubuntu-latest steps: @@ -30,7 +30,7 @@ jobs: token: ${{ secrets.MASTER_BRANCH_ACCESS_TOKEN }} create-version-update-pr: - name: "Create Version Update PR" + name: 'Create Version Update PR' runs-on: ubuntu-latest needs: bump-version diff --git a/.github/workflows/pr-labeled-at-develop-workflows.yml b/.github/workflows/pr-labeled-at-develop-workflows.yml index e023a17..9e95304 100644 --- a/.github/workflows/pr-labeled-at-develop-workflows.yml +++ b/.github/workflows/pr-labeled-at-develop-workflows.yml @@ -9,14 +9,14 @@ on: jobs: auto-merge: - name: "Auto Merge" + name: 'Auto Merge' runs-on: ubuntu-latest steps: - name: automerge - uses: "pascalgn/automerge-action@v0.14.3" + uses: 'pascalgn/automerge-action@v0.14.3' env: - GITHUB_TOKEN: "${{ secrets.MASTER_BRANCH_ACCESS_TOKEN }}" - MERGE_LABELS: "workflow" - MERGE_COMMIT_MESSAGE: "Auto merge for PR with workflow label" - MERGE_FORKS: "false" - MERGE_RETRY_SLEEP: "60000" + GITHUB_TOKEN: '${{ secrets.MASTER_BRANCH_ACCESS_TOKEN }}' + MERGE_LABELS: 'workflow' + MERGE_COMMIT_MESSAGE: 'Auto merge for PR with workflow label' + MERGE_FORKS: 'false' + MERGE_RETRY_SLEEP: '60000' diff --git a/.github/workflows/pr-open-to-demos.yml b/.github/workflows/pr-open-to-demos.yml index 7fee8bd..13703f0 100644 --- a/.github/workflows/pr-open-to-demos.yml +++ b/.github/workflows/pr-open-to-demos.yml @@ -8,7 +8,7 @@ on: jobs: reset-demo-from-develop: - name: "Reset Demo From Develop" + name: 'Reset Demo From Develop' runs-on: ubuntu-latest steps: - name: Checkout Repo @@ -19,5 +19,5 @@ jobs: - name: Hard Reset Demo From Develop run: | git fetch origin develop:develop - git reset --hard develop + git reset --hard origin/develop git push -f diff --git a/.github/workflows/pr-open-workflows.yml b/.github/workflows/pr-open-workflows.yml index 26f7082..ec17d3d 100644 --- a/.github/workflows/pr-open-workflows.yml +++ b/.github/workflows/pr-open-workflows.yml @@ -3,10 +3,11 @@ name: PR Open Workflows on: pull_request: types: [ opened ] + branches-ignore: [ 'workflow/dependency-update' ] jobs: pr-labeler: - name: "Add Label to PR" + name: 'Add Label to PR' runs-on: ubuntu-latest steps: @@ -16,7 +17,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.MASTER_BRANCH_ACCESS_TOKEN }} automate-project-columns: - name: "Automate Project Columns" + name: 'Automate Project Columns' runs-on: ubuntu-latest steps: @@ -28,7 +29,7 @@ jobs: repo-token: ${{ secrets.MASTER_BRANCH_ACCESS_TOKEN }} milestone-binder: - name: "Milestone Binder" + name: 'Milestone Binder' runs-on: ubuntu-latest steps: diff --git a/.run/env.run.xml b/.run/env.run.xml new file mode 100644 index 0000000..8960e75 --- /dev/null +++ b/.run/env.run.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.run/outdated.run.xml b/.run/outdated.run.xml new file mode 100644 index 0000000..1172442 --- /dev/null +++ b/.run/outdated.run.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.run/update.run.xml b/.run/update.run.xml new file mode 100644 index 0000000..7a1d372 --- /dev/null +++ b/.run/update.run.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 3d95150..0b603ae 100644 --- a/README.md +++ b/README.md @@ -57,39 +57,39 @@ Check project's current **nodejs** and **npm** version from **[package.json](pac If you don't give **RESPONSE_ENCRYPTION_SECRET**, response encryption mechanism will be disabled automatically. ```applescript -PORT={Port} +PORT=4001 -ACCESS_TOKEN_EXPIRE=1hour -ACCESS_TOKEN_SECRET={Access Token Secret} +PROJECT=OTH +MODULE=AuthServer +ENVIRONMENT=Local -CLIENT_RESET_PASSWORD_URL=/reset-password CLIENT_URL=http://localhost:4200 -CLIENT_VERIFICATION_SUCCESS_URL=/verify-account +CLIENT_RESET_PASSWORD_URL=http://localhost:4200/reset-password +CLIENT_VERIFICATION_SUCCESS_URL=http://localhost:4200/verify-account DATABASE_URL={Database Connection Url} +POSTGRESQL_CONNECTION_LIMIT={Postgresql Connection Limit} -MAIL_HOST={SMTP Host} -MAIL_PASSWORD={Mail Password} -MAIL_PORT={SMTP Port} -MAIL_USERNAME={Mail Address} +CLOUDAMQP_APIKEY={MQ Api Key} +CLOUDAMQP_URL={MQ Connection Url} -REFRESH_TOKEN_EXPIRE=30days -REFRESH_TOKEN_SECRET={Refresh Token Secret} +AUTH_SERVER_QUEUE_CHANNEL=oth_auth_queue +ORCHESTRATION_SERVER_QUEUE_CHANNEL=oth_orchestration_queue -RESET_PASSWORD_TOKEN_EXPIRE=1day -RESET_PASSWORD_TOKEN_SECRET={Reset Password Token Secret} - -RESPONSE_ENCRYPTION_SECRET={Response Encryption Secret} +AUTO_VERIFY=false -VERIFICATION_TOKEN_SECRET={Verification Token Secret} +ACCESS_TOKEN_EXPIRE=1hour +ACCESS_TOKEN_SECRET={Access Token Secret} -AUTO_VERIFY={Set true If Auto Verify On SignUp} +REFRESH_TOKEN_EXPIRE=30days +REFRESH_TOKEN_SECRET={Refresh Token Secret} -POSTGRESQL_CONNECTION_LIMIT={PostgreSQL Connection Limit In Pool} +RESET_PASSWORD_TOKEN_EXPIRE=1day +RESET_PASSWORD_TOKEN_SECRET={Reset Token Secret} -MAIL_SERVER_DISABLED={Set true If Mail server disabled} +VERIFICATION_TOKEN_SECRET={Verification Token Secret -ORCHESTRATION_SERVER_QUEUE_CHANNEL= {Orchestration Server MQ Channel Name} +RESPONSE_ENCRYPTION_SECRET={Response Encryption Secret} ``` ## Social Login Configurations diff --git a/app/consumer/auth-queue.consumer.ts b/app/consumer/auth-queue.consumer.ts new file mode 100644 index 0000000..9ef7340 --- /dev/null +++ b/app/consumer/auth-queue.consumer.ts @@ -0,0 +1,56 @@ +import { AuthActionType } from '@open-template-hub/common'; + +export class AuthQueueConsumer { + constructor(private channel: any) {} + + onMessage = async (msg: any) => { + if (msg !== null) { + const msgStr = msg.content.toString(); + const msgObj = JSON.parse(msgStr); + + const message: AuthActionType = msgObj.message; + + // Decide requeue in the error handling + let requeue = false; + + if (message.example) { + var exampleHook = async () => { + console.log('Auth server example'); + }; + + await this.operate(msg, msgObj, requeue, exampleHook); + } else { + console.log('Message will be rejected: ', msgObj); + this.channel.reject(msg, false); + } + } + }; + + private operate = async ( + msg: any, + msgObj: any, + requeue: boolean, + hook: Function + ) => { + try { + console.log( + 'Message Received with deliveryTag: ' + msg.fields.deliveryTag, + msgObj + ); + await hook(); + await this.channel.ack(msg); + console.log( + 'Message Processed with deliveryTag: ' + msg.fields.deliveryTag, + msgObj + ); + } catch (e) { + console.log( + 'Error with processing deliveryTag: ' + msg.fields.deliveryTag, + msgObj, + e + ); + + this.channel.nack(msg, false, requeue); + } + }; +} diff --git a/app/controller/auth.controller.ts b/app/controller/auth.controller.ts index 20b1fdc..b74ee6a 100644 --- a/app/controller/auth.controller.ts +++ b/app/controller/auth.controller.ts @@ -2,7 +2,19 @@ * @description holds auth controller */ -import { HttpError, MailUtil, PostgreSqlProvider, ResponseCode, TokenUtil, User } from '@open-template-hub/common'; +import { + AccountVerificationMailActionParams, + ForgetPasswordMailActionParams, + HttpError, + MailActionType, + MessageQueueChannelType, + MessageQueueProvider, + PostgreSqlProvider, + QueueMessage, + ResponseCode, + TokenUtil, + User, +} from '@open-template-hub/common'; import bcrypt from 'bcrypt'; import { Environment } from '../../environment'; import { TokenRepository } from '../repository/token.repository'; @@ -10,44 +22,67 @@ import { UserRepository } from '../repository/user.repository'; export class AuthController { constructor( - private environment = new Environment(), - private mailUtil: MailUtil = new MailUtil( environment.args() ), - private tokenUtil: TokenUtil = new TokenUtil( environment.args() ) - ) { - } + private environment = new Environment(), + private tokenUtil: TokenUtil = new TokenUtil(environment.args()) + ) {} /** * sign up user * @param db database * @param user user */ - signup = async ( db: PostgreSqlProvider, user: User ) => { - if ( !user.password || !user.username || !user.email ) { - let e = new Error( 'username, password and email required' ) as HttpError; + signup = async ( + db: PostgreSqlProvider, + message_queue_provider: MessageQueueProvider, + user: User + ) => { + if (!user.password || !user.username || !user.email) { + let e = new Error('username, password and email required') as HttpError; e.responseCode = ResponseCode.BAD_REQUEST; - console.error( e ); + console.error(e); throw e; } - const hashedPassword = await bcrypt.hash( user.password, 10 ); - const userRepository = new UserRepository( db ); + const hashedPassword = await bcrypt.hash(user.password, 10); + const userRepository = new UserRepository(db); - await userRepository.insertUser( { + await userRepository.insertUser({ username: user.username, password: hashedPassword, email: user.email, - } as User ); + } as User); - const tokenUtil = new TokenUtil( this.environment.args() ); - const verificationToken = tokenUtil.generateVerificationToken( user ); + const tokenUtil = new TokenUtil(this.environment.args()); + const verificationToken = tokenUtil.generateVerificationToken(user); const isAutoVerify = process.env.AUTO_VERIFY === 'true'; - if ( isAutoVerify ) { - await this.verify( db, verificationToken ); - return this.login( db, user ); + if (isAutoVerify) { + await this.verify(db, verificationToken); + return this.login(db, user); } else { - await this.mailUtil.sendAccountVerificationMail( user, verificationToken ); + var orchestrationChannelTag = + this.environment.args().mqArgs?.orchestrationServerMessageQueueChannel; + var verificationParams = { + user: user.username, + email: user.email, + accountVerificationToken: verificationToken, + clientVerificationSuccessUrl: + this.environment.args().extentedArgs?.clientVerificationSuccessUrl, + } as AccountVerificationMailActionParams; + var message = { + sender: MessageQueueChannelType.AUTH, + receiver: MessageQueueChannelType.MAIL, + message: { + verifyAccount: { + params: verificationParams, + }, + } as MailActionType, + } as QueueMessage; + await message_queue_provider.publish( + message, + orchestrationChannelTag as string + ); return { email: user.email }; } }; @@ -57,40 +92,39 @@ export class AuthController { * @param db database * @param user user */ - login = async ( db: PostgreSqlProvider, user: User ) => { - - if ( !( user.username || user.email ) ) { - let e = new Error( 'username or email required' ) as HttpError; + login = async (db: PostgreSqlProvider, user: User) => { + if (!(user.username || user.email)) { + let e = new Error('username or email required') as HttpError; e.responseCode = ResponseCode.BAD_REQUEST; throw e; } - if ( !user.password ) { - let e = new Error( 'password required' ) as HttpError; + if (!user.password) { + let e = new Error('password required') as HttpError; e.responseCode = ResponseCode.BAD_REQUEST; throw e; } - const userRepository = new UserRepository( db ); + const userRepository = new UserRepository(db); const username = user.username || user.email; - let dbUser = await userRepository.findUserByUsernameOrEmail( username ); + let dbUser = await userRepository.findUserByUsernameOrEmail(username); - if ( !( await bcrypt.compare( user.password, dbUser.password ) ) ) { - let e = new Error( 'Bad credentials' ) as HttpError; + if (!(await bcrypt.compare(user.password, dbUser.password))) { + let e = new Error('Bad credentials') as HttpError; e.responseCode = ResponseCode.FORBIDDEN; throw e; } - if ( !dbUser.verified ) { - let e = new Error( 'Account not verified' ) as HttpError; + if (!dbUser.verified) { + let e = new Error('Account not verified') as HttpError; e.responseCode = ResponseCode.FORBIDDEN; throw e; } - const tokenRepository = new TokenRepository( db ); - return tokenRepository.generateTokens( dbUser ); + const tokenRepository = new TokenRepository(db); + return tokenRepository.generateTokens(dbUser); }; /** @@ -98,9 +132,9 @@ export class AuthController { * @param db database * @param token token */ - logout = async ( db: PostgreSqlProvider, token: string ) => { - const tokenRepository = new TokenRepository( db ); - await tokenRepository.deleteToken( token ); + logout = async (db: PostgreSqlProvider, token: string) => { + const tokenRepository = new TokenRepository(db); + await tokenRepository.deleteToken(token); }; /** @@ -108,12 +142,11 @@ export class AuthController { * @param db database * @param token token */ - token = async ( db: PostgreSqlProvider, token: string ) => { - - const tokenRepository = new TokenRepository( db ); - await tokenRepository.findToken( token ); - const user: any = this.tokenUtil.verifyRefreshToken( token ); - return this.tokenUtil.generateAccessToken( user ); + token = async (db: PostgreSqlProvider, token: string) => { + const tokenRepository = new TokenRepository(db); + await tokenRepository.findToken(token); + const user: any = this.tokenUtil.verifyRefreshToken(token); + return this.tokenUtil.generateAccessToken(user); }; /** @@ -121,11 +154,11 @@ export class AuthController { * @param db database * @param token token */ - verify = async ( db: PostgreSqlProvider, token: string ) => { - const user: any = this.tokenUtil.verifyVerificationToken( token ); + verify = async (db: PostgreSqlProvider, token: string) => { + const user: any = this.tokenUtil.verifyVerificationToken(token); - const userRepository = new UserRepository( db ); - await userRepository.verifyUser( user.username ); + const userRepository = new UserRepository(db); + await userRepository.verifyUser(user.username); }; /** @@ -134,13 +167,39 @@ export class AuthController { * @param username username * @param sendEmail don't send email if false */ - forgetPassword = async ( db: PostgreSqlProvider, username: string, sendEmail: boolean = true ) => { - const userRepository = new UserRepository( db ); - const user = await userRepository.findEmailAndPasswordByUsername( username ); - const passwordResetToken = this.tokenUtil.generatePasswordResetToken( user ); - - if ( sendEmail ) { - await this.mailUtil.sendPasswordResetMail( user, passwordResetToken ); + forgetPassword = async ( + db: PostgreSqlProvider, + message_queue_provider: MessageQueueProvider, + username: string, + sendEmail: boolean = true + ) => { + const userRepository = new UserRepository(db); + const user = await userRepository.findEmailAndPasswordByUsername(username); + const passwordResetToken = this.tokenUtil.generatePasswordResetToken(user); + + if (sendEmail) { + var orchestrationChannelTag = + this.environment.args().mqArgs?.orchestrationServerMessageQueueChannel; + var forgetPasswordParams = { + user: user.username, + email: user.email, + passwordResetToken, + clientResetPasswordUrl: + this.environment.args().extentedArgs?.clientResetPasswordUrl, + } as ForgetPasswordMailActionParams; + var message = { + sender: MessageQueueChannelType.AUTH, + receiver: MessageQueueChannelType.MAIL, + message: { + forgetPassword: { + params: forgetPasswordParams, + }, + } as MailActionType, + } as QueueMessage; + await message_queue_provider.publish( + message, + orchestrationChannelTag as string + ); } return passwordResetToken; @@ -152,22 +211,22 @@ export class AuthController { * @param user user * @param token token */ - resetPassword = async ( db: PostgreSqlProvider, user: User, token: string ) => { - if ( !user.password || !user.username ) { - let e = new Error( 'username and password required' ) as HttpError; + resetPassword = async (db: PostgreSqlProvider, user: User, token: string) => { + if (!user.password || !user.username) { + let e = new Error('username and password required') as HttpError; e.responseCode = ResponseCode.BAD_REQUEST; throw e; } - user.password = await bcrypt.hash( user.password, 10 ); + user.password = await bcrypt.hash(user.password, 10); - const userRepository = new UserRepository( db ); + const userRepository = new UserRepository(db); const dbUser = await userRepository.findEmailAndPasswordByUsername( - user.username + user.username ); - this.tokenUtil.verifyPasswordResetToken( token, dbUser.password ); - await userRepository.updateByUsername( user ); + this.tokenUtil.verifyPasswordResetToken(token, dbUser.password); + await userRepository.updateByUsername(user); }; /** @@ -176,19 +235,23 @@ export class AuthController { * @param currentUser current user * @param user user */ - deleteUser = async ( db: PostgreSqlProvider, currentUser: string, user: User ) => { - if ( !user.username ) { - let e = new Error( 'username required' ) as HttpError; + deleteUser = async ( + db: PostgreSqlProvider, + currentUser: string, + user: User + ) => { + if (!user.username) { + let e = new Error('username required') as HttpError; e.responseCode = ResponseCode.BAD_REQUEST; throw e; - } else if ( currentUser === user.username ) { - let e = new Error( 'you cannot delete yourself' ) as HttpError; + } else if (currentUser === user.username) { + let e = new Error('you cannot delete yourself') as HttpError; e.responseCode = ResponseCode.BAD_REQUEST; throw e; } - const userRepository = new UserRepository( db ); + const userRepository = new UserRepository(db); - await userRepository.deleteUserByUsername( user.username ); + await userRepository.deleteUserByUsername(user.username); }; } diff --git a/app/route/auth.route.ts b/app/route/auth.route.ts index fb1fb1d..33356c7 100644 --- a/app/route/auth.route.ts +++ b/app/route/auth.route.ts @@ -17,7 +17,7 @@ const subRoutes = { forgetPassword: '/forget-password', resetPassword: '/reset-password', resetPasswordToken: '/reset-password-token', - user: '/user' + user: '/user', }; export const publicRoutes = [ @@ -30,123 +30,126 @@ export const publicRoutes = [ subRoutes.resetPassword, ]; -export const adminRoutes = [ - subRoutes.resetPasswordToken, - subRoutes.user -]; +export const adminRoutes = [subRoutes.resetPasswordToken, subRoutes.user]; export const router = Router(); -router.post( subRoutes.signup, async ( req: Request, res: Response ) => { +router.post(subRoutes.signup, async (req: Request, res: Response) => { // sign up const authController = new AuthController(); const context = res.locals.ctx; - const response = await authController.signup( context.postgresql_provider, { - username: req.body.username, - password: req.body.password, - email: req.body.email, - } as User ); - res.status( ResponseCode.CREATED ).json( response ); -} ); + const response = await authController.signup( + context.postgresql_provider, + context.message_queue_provider, + { + username: req.body.username, + password: req.body.password, + email: req.body.email, + } as User + ); + res.status(ResponseCode.CREATED).json(response); +}); -router.post( subRoutes.login, async ( req: Request, res: Response ) => { +router.post(subRoutes.login, async (req: Request, res: Response) => { // login const authController = new AuthController(); const context = res.locals.ctx; - const response = await authController.login( context.postgresql_provider, { + const response = await authController.login(context.postgresql_provider, { username: req.body.username, password: req.body.password, email: req.body.email, - } as User ); - res.status( ResponseCode.OK ).json( { + } as User); + res.status(ResponseCode.OK).json({ accessToken: response.accessToken, refreshToken: response.refreshToken, - } ); -} ); + }); +}); -router.post( subRoutes.logout, async ( req: Request, res: Response ) => { +router.post(subRoutes.logout, async (req: Request, res: Response) => { // logout const authController = new AuthController(); const context = res.locals.ctx; - await authController.logout( context.postgresql_provider, req.body.token ); - res.status( ResponseCode.NO_CONTENT ).json( {} ); -} ); + await authController.logout(context.postgresql_provider, req.body.token); + res.status(ResponseCode.NO_CONTENT).json({}); +}); -router.post( subRoutes.token, async ( req: Request, res: Response ) => { +router.post(subRoutes.token, async (req: Request, res: Response) => { // get token const authController = new AuthController(); const context = res.locals.ctx; const accessToken = await authController.token( - context.postgresql_provider, - req.body.token + context.postgresql_provider, + req.body.token ); res - .status( ResponseCode.OK ) - .json( { accessToken: accessToken, refreshToken: req.body.token } ); -} ); + .status(ResponseCode.OK) + .json({ accessToken: accessToken, refreshToken: req.body.token }); +}); -router.get( subRoutes.verify, async ( req: Request, res: Response ) => { +router.get(subRoutes.verify, async (req: Request, res: Response) => { // verify token const authController = new AuthController(); const context = res.locals.ctx; await authController.verify( - context.postgresql_provider, - req.query.token as string + context.postgresql_provider, + req.query.token as string ); - res.status( ResponseCode.NO_CONTENT ).json( {} ); -} ); + res.status(ResponseCode.NO_CONTENT).json({}); +}); -router.post( subRoutes.forgetPassword, async ( req: Request, res: Response ) => { +router.post(subRoutes.forgetPassword, async (req: Request, res: Response) => { // forget password const authController = new AuthController(); const context = res.locals.ctx; await authController.forgetPassword( - context.postgresql_provider, - req.body.username + context.postgresql_provider, + context.message_queue_provider, + req.body.username ); - res.status( ResponseCode.NO_CONTENT ).json( {} ); -} ); + res.status(ResponseCode.NO_CONTENT).json({}); +}); router.get( - subRoutes.resetPasswordToken, - async ( req: Request, res: Response ) => { - // generate reset password token - const authController = new AuthController(); - const context = res.locals.ctx; - const resetPasswordToken = await authController.forgetPassword( - context.postgresql_provider, - req.query.username as string, - false - ); - res.status( ResponseCode.OK ).json( { resetPasswordToken } ); - } + subRoutes.resetPasswordToken, + async (req: Request, res: Response) => { + // generate reset password token + const authController = new AuthController(); + const context = res.locals.ctx; + const resetPasswordToken = await authController.forgetPassword( + context.postgresql_provider, + context.message_queue_provider, + req.query.username as string, + true + ); + res.status(ResponseCode.OK).json({ resetPasswordToken }); + } ); -router.post( subRoutes.resetPassword, async ( req: Request, res: Response ) => { +router.post(subRoutes.resetPassword, async (req: Request, res: Response) => { // reset password const authController = new AuthController(); const context = res.locals.ctx; await authController.resetPassword( - context.postgresql_provider, - { - username: req.body.username, - password: req.body.password, - } as User, - req.body.token + context.postgresql_provider, + { + username: req.body.username, + password: req.body.password, + } as User, + req.body.token ); - res.status( ResponseCode.NO_CONTENT ).json( {} ); -} ); + res.status(ResponseCode.NO_CONTENT).json({}); +}); -router.delete( subRoutes.user, async ( req: Request, res: Response ) => { +router.delete(subRoutes.user, async (req: Request, res: Response) => { // delete user const authController = new AuthController(); const context = res.locals.ctx; await authController.deleteUser( - context.postgresql_provider, - context.username, - { - username: req.query.username, - } as User + context.postgresql_provider, + context.username, + { + username: req.query.username, + } as User ); - res.status( ResponseCode.NO_CONTENT ).json( {} ); -} ); + res.status(ResponseCode.NO_CONTENT).json({}); +}); diff --git a/app/route/index.route.ts b/app/route/index.route.ts index 8da1698..c620b2a 100644 --- a/app/route/index.route.ts +++ b/app/route/index.route.ts @@ -2,13 +2,31 @@ * @description holds index routes */ -import { context, EncryptionUtil, ErrorHandlerUtil, PostgreSqlProvider, PreloadUtil, } from '@open-template-hub/common'; +import { + context, + EncryptionUtil, + ErrorHandlerUtil, + MessageQueueProvider, + PostgreSqlProvider, + PreloadUtil, +} from '@open-template-hub/common'; import { NextFunction, Request, Response } from 'express'; import { Environment } from '../../environment'; -import { adminRoutes as authAdminRoutes, publicRoutes as authPublicRoutes, router as authRouter, } from './auth.route'; +import { AuthQueueConsumer } from '../consumer/auth-queue.consumer'; +import { + adminRoutes as authAdminRoutes, + publicRoutes as authPublicRoutes, + router as authRouter, +} from './auth.route'; import { router as infoRouter } from './info.route'; -import { publicRoutes as monitorPublicRoutes, router as monitorRouter, } from './monitor.route'; -import { publicRoutes as socialLoginPublicRoutes, router as socialLoginRouter, } from './social-login.route'; +import { + publicRoutes as monitorPublicRoutes, + router as monitorRouter, +} from './monitor.route'; +import { + publicRoutes as socialLoginPublicRoutes, + router as socialLoginRouter, +} from './social-login.route'; const subRoutes = { root: '/', @@ -20,102 +38,116 @@ const subRoutes = { export namespace Routes { var environment: Environment; + var message_queue_provider: MessageQueueProvider; var postgresql_provider: PostgreSqlProvider; const errorHandlerUtil = new ErrorHandlerUtil(); var publicRoutes: string[] = []; var adminRoutes: string[] = []; - function populateRoutes( mainRoute: string, routes: Array ) { + function populateRoutes(mainRoute: string, routes: Array) { var populated = Array(); - for ( const s of routes ) { - populated.push( mainRoute + ( s === '/' ? '' : s ) ); + for (const s of routes) { + populated.push(mainRoute + (s === '/' ? '' : s)); } return populated; } - export function mount( app: any ) { - const preloadUtil = new PreloadUtil(); + export function mount(app: any) { environment = new Environment(); + message_queue_provider = new MessageQueueProvider(environment.args()); + + const channelTag = new Environment().args().mqArgs + ?.authServerMessageQueueChannel as string; + message_queue_provider.getChannel(channelTag).then((channel: any) => { + const authQueueConsumer = new AuthQueueConsumer(channel); + message_queue_provider.consume( + channel, + channelTag, + authQueueConsumer.onMessage, + 1 + ); + }); + const preloadUtil = new PreloadUtil(); + postgresql_provider = new PostgreSqlProvider( - environment.args(), - 'AuthServer' + environment.args(), + 'AuthServer' ); preloadUtil - .preload( undefined, postgresql_provider ) - .then( () => console.log( 'DB preloads are completed.' ) ); + .preload(undefined, postgresql_provider) + .then(() => console.log('DB preloads are completed.')); publicRoutes = [ - ...populateRoutes( subRoutes.monitor, monitorPublicRoutes ), - ...populateRoutes( subRoutes.auth, authPublicRoutes ), - ...populateRoutes( subRoutes.social, socialLoginPublicRoutes ), + ...populateRoutes(subRoutes.monitor, monitorPublicRoutes), + ...populateRoutes(subRoutes.auth, authPublicRoutes), + ...populateRoutes(subRoutes.social, socialLoginPublicRoutes), ]; - console.log( 'Public Routes: ', publicRoutes ); + console.log('Public Routes: ', publicRoutes); - adminRoutes = [ - ...populateRoutes( subRoutes.auth, authAdminRoutes ) - ]; - console.log( 'Admin Routes: ', adminRoutes ); + adminRoutes = [...populateRoutes(subRoutes.auth, authAdminRoutes)]; + console.log('Admin Routes: ', adminRoutes); const responseInterceptor = ( - req: Request, - res: Response, - next: NextFunction + req: Request, + res: Response, + next: NextFunction ) => { var originalSend = res.send; - const encryptionUtil = new EncryptionUtil( environment.args() ); + const encryptionUtil = new EncryptionUtil(environment.args()); res.send = function () { - console.log( 'Starting Encryption: ', new Date() ); - let encrypted_arguments = encryptionUtil.encrypt( arguments ); - console.log( 'Encryption Completed: ', new Date() ); + console.log('Starting Encryption: ', new Date()); + let encrypted_arguments = encryptionUtil.encrypt(arguments); + console.log('Encryption Completed: ', new Date()); - originalSend.apply( res, encrypted_arguments as any ); + originalSend.apply(res, encrypted_arguments as any); } as any; next(); }; // Use this interceptor before routes - app.use( responseInterceptor ); + app.use(responseInterceptor); // INFO: Keep this method at top at all times - app.all( '/*', async ( req: Request, res: Response, next: NextFunction ) => { + app.all('/*', async (req: Request, res: Response, next: NextFunction) => { try { // create context res.locals.ctx = await context( - req, - environment.args(), - publicRoutes, - adminRoutes, - undefined, - postgresql_provider + req, + environment.args(), + publicRoutes, + adminRoutes, + undefined, + postgresql_provider, + message_queue_provider ); next(); - } catch ( err ) { - let error = errorHandlerUtil.handle( err ); - res.status( error.code ).json( { message: error.message } ); + } catch (err) { + let error = errorHandlerUtil.handle(err); + res.status(error.code).json({ message: error.message }); } - } ); + }); // INFO: Add your routes here - app.use( subRoutes.monitor, monitorRouter ); - app.use( subRoutes.auth, authRouter ); - app.use( subRoutes.social, socialLoginRouter ); - app.use( subRoutes.info, infoRouter ); + app.use(subRoutes.monitor, monitorRouter); + app.use(subRoutes.auth, authRouter); + app.use(subRoutes.social, socialLoginRouter); + app.use(subRoutes.info, infoRouter); // Use for error handling - app.use( function ( - err: Error, - req: Request, - res: Response, - next: NextFunction + app.use(function ( + err: Error, + req: Request, + res: Response, + next: NextFunction ) { - let error = errorHandlerUtil.handle( err ); - res.status( error.code ).json( { message: error.message } ); - } ); + let error = errorHandlerUtil.handle(err); + res.status(error.code).json({ message: error.message }); + }); } } diff --git a/assets/mail-templates/forget-password-mail-template.html b/assets/mail-templates/forget-password-mail-template.html deleted file mode 100644 index 176712c..0000000 --- a/assets/mail-templates/forget-password-mail-template.html +++ /dev/null @@ -1,157 +0,0 @@ - - - - Open Template Hub - - -
- - - - - - - - - - - - -
-
-
- - - - - - - - - - - - - -
- logo -
- - - - - - - - - - - - - - - - -
-
-

- You have requested to reset your password -

-

- Hi ${username}, -

-

- We cannot simply send you your old password. A unique link to reset your password has been - generated for you. To reset your password, click the following link and follow the - instructions. -

-
-
- - - - - - - - - - -
- - - - - - - - - - -
- -
-
-
-
-
-
- - - - - - - - - - -
-

- Open Template Hub -

-

- logo logo -

-
-
-
-
-
- - diff --git a/assets/mail-templates/verify-account-mail-template.html b/assets/mail-templates/verify-account-mail-template.html deleted file mode 100644 index 31ad586..0000000 --- a/assets/mail-templates/verify-account-mail-template.html +++ /dev/null @@ -1,159 +0,0 @@ - - - - Open Template Hub - - -
- - - - - - - - - - - - -
-
-
- - - - - - - - - - - - - -
- logo -
- - - - - - - - - - - - - - - - -
-
-

- Hi ${username}, -

-

- Thanks for registering this customizable UI Template! Click - this link to access our open source microservices that are integrated with this UI. -

-

- Enjoy Your Customization,
- The Open Template Hub Team -

-
-
- - - - - - - - - - -
- - - - - - - - - - -
- -
-
-
-
-
-
-
- - - - - - - - - - -
-

- Open Template Hub -

-

- logo logo -

-
-
-
-
-
- - diff --git a/env.sh b/env.sh new file mode 100644 index 0000000..f01a1bb --- /dev/null +++ b/env.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +if [ ! -f .env ]; then + echo "Generating .env file.." + touch .env + { + echo "PORT=4001" + + echo "PROJECT=OTH" + echo "MODULE=AuthServer" + echo "ENVIRONMENT=Local" + + echo "CLIENT_URL=http://localhost:4200" + echo "CLIENT_RESET_PASSWORD_URL=http://localhost:4200/reset-password" + echo "CLIENT_VERIFICATION_SUCCESS_URL=http://localhost:4200/verify-account" + + echo "DATABASE_URL={Database Connection Url}" + echo "POSTGRESQL_CONNECTION_LIMIT={Postgresql Connection Limit}" + + echo "CLOUDAMQP_APIKEY={MQ Api Key}" + echo "CLOUDAMQP_URL={MQ Connection Url}" + + echo "AUTH_SERVER_QUEUE_CHANNEL=oth_auth_queue" + echo "ORCHESTRATION_SERVER_QUEUE_CHANNEL=oth_orchestration_queue" + + echo "AUTO_VERIFY=false" + + echo "ACCESS_TOKEN_EXPIRE=1hour" + echo "ACCESS_TOKEN_SECRET={Access Token Secret}" + + echo "REFRESH_TOKEN_EXPIRE=30days" + echo "REFRESH_TOKEN_SECRET={Refresh Token Secret}" + + echo "RESET_PASSWORD_TOKEN_EXPIRE=1day" + echo "RESET_PASSWORD_TOKEN_SECRET={Reset Token Secret}" + + echo "VERIFICATION_TOKEN_SECRET={Verification Token Secret" + + echo "RESPONSE_ENCRYPTION_SECRET={Response Encryption Secret}" + } >>.env +else + echo ".env file already exists. Nothing to do..." +fi diff --git a/environment.ts b/environment.ts index d54cfed..b993e19 100644 --- a/environment.ts +++ b/environment.ts @@ -1,34 +1,16 @@ -import { EnvArgs } from '@open-template-hub/common'; -import * as path from 'path'; +import { + DbArgs, + EnvArgs, + ExtendedArgs, + MailArgs, + TokenArgs, +} from '@open-template-hub/common'; export class Environment { - constructor( private _args: EnvArgs = {} as EnvArgs ) { - const verifyAccountMailTemplatePath = path.join( - __dirname, - '/assets/mail-templates/verify-account-mail-template.html' - ); - - const resetPasswordMailTemplatePath = path.join( - __dirname, - '/assets/mail-templates/forget-password-mail-template.html' - ); - - this._args = { + constructor(private _args: EnvArgs = {} as EnvArgs) { + var tokenArgs = { accessTokenExpire: process.env.ACCESS_TOKEN_EXPIRE, accessTokenSecret: process.env.ACCESS_TOKEN_SECRET, - - clientResetPasswordUrl: process.env.CLIENT_RESET_PASSWORD_URL, - clientUrl: process.env.CLIENT_URL, - clientVerificationSuccessUrl: process.env.CLIENT_VERIFICATION_SUCCESS_URL, - - postgreSqlUri: process.env.DATABASE_URL, - postgreSqlConnectionLimit: process.env.POSTGRESQL_CONNECTION_LIMIT, - - mailHost: process.env.MAIL_HOST, - mailPassword: process.env.MAIL_PASSWORD, - mailPort: process.env.MAIL_PORT, - mailUsername: process.env.MAIL_USERNAME, - refreshTokenExpire: process.env.REFRESH_TOKEN_EXPIRE, refreshTokenSecret: process.env.REFRESH_TOKEN_SECRET, @@ -38,11 +20,31 @@ export class Environment { responseEncryptionSecret: process.env.RESPONSE_ENCRYPTION_SECRET, verificationTokenSecret: process.env.VERIFICATION_TOKEN_SECRET, + } as TokenArgs; + + var dbArgs = { + postgreSqlUri: process.env.DATABASE_URL, + postgreSqlConnectionLimit: process.env.POSTGRESQL_CONNECTION_LIMIT, + } as DbArgs; - mailServerDisabled: process.env.MAIL_SERVER_DISABLED === 'true', + var extentedArgs = { + clientResetPasswordUrl: process.env.CLIENT_RESET_PASSWORD_URL, + clientUrl: process.env.CLIENT_URL, + clientVerificationSuccessUrl: process.env.CLIENT_VERIFICATION_SUCCESS_URL, + } as ExtendedArgs; - resetPasswordMailTemplatePath, - verifyAccountMailTemplatePath, + var mqArgs = { + messageQueueConnectionUrl: process.env.CLOUDAMQP_URL, + authServerMessageQueueChannel: process.env.AUTH_SERVER_QUEUE_CHANNEL, + orchestrationServerMessageQueueChannel: + process.env.ORCHESTRATION_SERVER_QUEUE_CHANNEL, + }; + + this._args = { + tokenArgs, + dbArgs, + mqArgs, + extentedArgs, } as EnvArgs; } diff --git a/package.json b/package.json index bfd9d88..8a0edf1 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "postmanDevelop": "mkdir -p -- ./assets/test-results && touch ./assets/test-results/postman-dark.html && touch ./assets/test-results/postman.html && newman run assets/tests/regression-tests/postman/regression-test.postman_collection.json -e assets/tests/regression-tests/postman/regression-test.postman_environment_develop.json -r htmlextra --reporter-htmlextra-export ./assets/test-results/postman.html --reporter-htmlextra-darkTheme > ./assets/test-results/postman-dark.html --env-var $npm_config_adminAuthToken --env-var $npm_config_responseEncryptionSecret" }, "dependencies": { - "@open-template-hub/common": "^2.1.12", + "@open-template-hub/common": "^2.1.15", "@types/bcrypt": "^3.0.0", "@types/capitalize": "^2.0.0", "@types/cors": "^2.8.9", @@ -29,7 +29,7 @@ "oauth-1.0a": "^2.2.6", "ts-node": "^8.1.0", "typescript": "^3.4.5", - "uuid": "^7.0.3" + "uuid": "^8.3.2" }, "devDependencies": { "@types/bcrypt": "^3.0.0", diff --git a/version.ts b/version.ts index 88715d8..0ef594e 100644 --- a/version.ts +++ b/version.ts @@ -1 +1 @@ -export const version = '1.0.0'; +export const version = '2.3.5';