From bb2833b2de8d9310cc128c5523b22ab9bcbdeffb Mon Sep 17 00:00:00 2001 From: vince8x Date: Thu, 10 Nov 2022 11:46:53 +0700 Subject: [PATCH] Support lixilotus --- .gitattributes | 1 + apps/authorization-server/src/main.ts | 85 +++--- .../aggregates/signup/signup.service.ts | 255 ++++++++++-------- .../src/user-management/commands/index.ts | 88 +++--- .../signup-via-email-no-verified.command.ts | 6 + .../signup-via-email-no-verified.handler.ts | 23 ++ .../controllers/signup/signup.controller.ts | 63 +++-- .../src/user-management/events/index.ts | 84 +++--- ...r-signed-up-via-email-no-verified.event.ts | 10 + ...signed-up-via-email-no-verified.handler.ts | 28 ++ .../src/user-management/policies/index.ts | 39 +-- .../signup-via-email-no-verified.dto.ts | 11 + docker/lixi-backend.yaml | 155 +++++++++++ docker/lixi-frontend.yaml | 41 +++ frontends/admin-client/Dockerfile | 2 +- frontends/authorization-client/Dockerfile | 2 +- frontends/identity-client/Dockerfile | 2 +- 17 files changed, 616 insertions(+), 279 deletions(-) create mode 100644 .gitattributes create mode 100644 apps/authorization-server/src/user-management/commands/signup-via-email-no-verified/signup-via-email-no-verified.command.ts create mode 100644 apps/authorization-server/src/user-management/commands/signup-via-email-no-verified/signup-via-email-no-verified.handler.ts create mode 100644 apps/authorization-server/src/user-management/events/user-signed-up-via-email-no-verified/user-signed-up-via-email-no-verified.event.ts create mode 100644 apps/authorization-server/src/user-management/events/user-signed-up-via-email-no-verified/user-signed-up-via-email-no-verified.handler.ts create mode 100644 apps/authorization-server/src/user-management/policies/signup-via-email-no-verified/signup-via-email-no-verified.dto.ts create mode 100644 docker/lixi-backend.yaml create mode 100644 docker/lixi-frontend.yaml diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..176a458f --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/apps/authorization-server/src/main.ts b/apps/authorization-server/src/main.ts index 5159523a..24351191 100644 --- a/apps/authorization-server/src/main.ts +++ b/apps/authorization-server/src/main.ts @@ -1,36 +1,49 @@ -import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; -import { ExpressAdapter } from '@nestjs/platform-express'; -import { ExpressServer } from './express-server'; -import { ConfigService } from './config/config.service'; -import { setupEvents } from './events-server'; - -async function bootstrap() { - const authServer = new ExpressServer(new ConfigService()); - authServer.setupSecurity(); - authServer.setupI18n(); - - const app = await NestFactory.create( - AppModule, - new ExpressAdapter(authServer.server), - ); - - // Enable CORS - app.enableCors(); - - // Setup Swagger - ExpressServer.setupSwagger(app); - - // Handlebars View engine - ExpressServer.setupViewEngine(app); - - // Setup Session - authServer.setupSession(app); - - // Setup Events - setupEvents(app); - - await app.listen(3000); -} - -bootstrap(); +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; +import { ExpressAdapter } from '@nestjs/platform-express'; +import { ExpressServer } from './express-server'; +import { ConfigService } from './config/config.service'; +import { setupEvents } from './events-server'; +const allowedOrigins = ['https://lixilotus.test','http://admin.localhost:4220','http://accounts.localhost:4210','http://myaccount.localhost:4420']; + +async function bootstrap() { + const authServer = new ExpressServer(new ConfigService()); + authServer.setupSecurity(); + authServer.setupI18n(); + + const app = await NestFactory.create( + AppModule, + new ExpressAdapter(authServer.server), + ); + + // Enable CORS + app.enableCors({ + // eslint-disable-next-line object-shorthand + origin: function (origin, callback) { + if (!origin) return callback(null, true); + if (allowedOrigins.indexOf(origin) === -1) { + const msg = 'The CORS policy for this site does not allow access from the specified Origin.'; + return callback(new Error(msg), false); + } + return callback(null, true); + }, + credentials: true, + exposedHeaders: ["set-cookie"], + }); + + // Setup Swagger + ExpressServer.setupSwagger(app); + + // Handlebars View engine + ExpressServer.setupViewEngine(app); + + // Setup Session + authServer.setupSession(app); + + // Setup Events + setupEvents(app); + + await app.listen(3000); +} + +bootstrap(); diff --git a/apps/authorization-server/src/user-management/aggregates/signup/signup.service.ts b/apps/authorization-server/src/user-management/aggregates/signup/signup.service.ts index 4cab5e77..143e4527 100644 --- a/apps/authorization-server/src/user-management/aggregates/signup/signup.service.ts +++ b/apps/authorization-server/src/user-management/aggregates/signup/signup.service.ts @@ -1,109 +1,146 @@ -import { Injectable, BadRequestException } from '@nestjs/common'; -import { randomBytes } from 'crypto'; -import { AggregateRoot } from '@nestjs/cqrs'; -import { v4 as uuidv4 } from 'uuid'; - -import { UserService } from '../../entities/user/user.service'; -import { User } from '../../entities/user/user.interface'; -import { ServerSettingsService } from '../../../system-settings/entities/server-settings/server-settings.service'; -import { i18n } from '../../../i18n/i18n.config'; -import { SignupViaEmailDto } from '../../policies'; -import { - AuthData, - AuthDataType, -} from '../../entities/auth-data/auth-data.interface'; -import { - EmailForVerificationNotFound, - invalidUserException, - userAlreadyExistsException, -} from '../../../common/filters/exceptions'; -import { USER } from '../../entities/user/user.schema'; -import { UserSignedUpViaEmailEvent } from '../../events/user-signed-up-via-email/user-signed-up-via-email.event'; -import { UnverifiedEmailVerificationCodeSentEvent } from '../../events'; -import { AuthDataService } from '../../entities/auth-data/auth-data.service'; - -@Injectable() -export class SignupService extends AggregateRoot { - constructor( - private readonly user: UserService, - private readonly authData: AuthDataService, - private readonly serverSettingsService: ServerSettingsService, - ) { - super(); - } - - async initSignupViaEmail(payload: SignupViaEmailDto) { - await this.validateSignupEnabled(); - - payload.email = payload.email.trim().toLowerCase(); - const user = await this.user.findOne({ email: payload.email }); - if (user) { - throw userAlreadyExistsException; - } - - const unverifiedUser = {} as User; - unverifiedUser.uuid = uuidv4(); - unverifiedUser.name = payload.name; - unverifiedUser.email = payload.email; - unverifiedUser.disabled = true; - unverifiedUser.isEmailVerified = false; - - const verificationCode = {} as AuthData; - verificationCode.password = randomBytes(32).toString('hex'); - if (payload.redirect) { - verificationCode.metaData = { redirect: payload.redirect }; - } - verificationCode.entity = USER; - verificationCode.entityUuid = unverifiedUser.uuid; - const expiry = new Date(); - expiry.setDate(expiry.getDate() + 1); - verificationCode.expiry = new Date(); - verificationCode.authDataType = AuthDataType.VerificationCode; - - this.apply(new UserSignedUpViaEmailEvent(unverifiedUser, verificationCode)); - } - - async validateSignupEnabled() { - const settings = await this.serverSettingsService.find(); - if (settings.disableSignup) { - throw new BadRequestException({ - message: i18n.__('Signup Disabled'), - }); - } - } - - async sendUnverifiedEmailVerificationCode(userUuid: string) { - const user = await this.user.findOne({ uuid: userUuid }); - if (!user) { - throw invalidUserException; - } - if (user.isEmailVerified) { - throw new EmailForVerificationNotFound(); - } - - const verificationCode = - (await this.authData.findOne({ - entity: USER, - entityUuid: user.uuid, - authDataType: AuthDataType.VerificationCode, - })) || ({} as AuthData); - - verificationCode.entity = USER; - verificationCode.entityUuid = user.uuid; - verificationCode.authDataType = AuthDataType.VerificationCode; - - if (!verificationCode.password) { - verificationCode.password = randomBytes(32).toString('hex'); - } - - if (!verificationCode.expiry) { - const expiry = new Date(); - expiry.setDate(expiry.getDate() + 1); - verificationCode.expiry = new Date(); - } - - this.apply( - new UnverifiedEmailVerificationCodeSentEvent(user, verificationCode), - ); - } -} +import { Injectable, BadRequestException } from '@nestjs/common'; +import { randomBytes } from 'crypto'; +import { AggregateRoot } from '@nestjs/cqrs'; +import { v4 as uuidv4 } from 'uuid'; + +import { UserService } from '../../entities/user/user.service'; +import { User } from '../../entities/user/user.interface'; +import { ServerSettingsService } from '../../../system-settings/entities/server-settings/server-settings.service'; +import { i18n } from '../../../i18n/i18n.config'; +import { SignupViaEmailDto, SignupViaEmailNoVerifiedDto } from '../../policies'; +import { + AuthData, + AuthDataType, +} from '../../entities/auth-data/auth-data.interface'; +import { + EmailForVerificationNotFound, + invalidUserException, + userAlreadyExistsException, +} from '../../../common/filters/exceptions'; +import { USER } from '../../entities/user/user.schema'; +import { UserSignedUpViaEmailEvent } from '../../events/user-signed-up-via-email/user-signed-up-via-email.event'; +import { UserSignedUpViaEmailNoVerifiedEvent } from '../../events/user-signed-up-via-email-no-verified/user-signed-up-via-email-no-verified.event'; +import { UnverifiedEmailVerificationCodeSentEvent } from '../../events'; +import { AuthDataService } from '../../entities/auth-data/auth-data.service'; +import { PasswordPolicyService } from '../../policies/password-policy/password-policy.service'; +import { CryptographerService } from '../../../common/services/cryptographer/cryptographer.service'; + +@Injectable() +export class SignupService extends AggregateRoot { + constructor( + private readonly user: UserService, + private readonly authData: AuthDataService, + private readonly serverSettingsService: ServerSettingsService, + private readonly passwordPolicy: PasswordPolicyService, + private readonly crypto: CryptographerService, + ) { + super(); + } + + async initSignupViaEmail(payload: SignupViaEmailDto) { + await this.validateSignupEnabled(); + + payload.email = payload.email.trim().toLowerCase(); + const user = await this.user.findOne({ email: payload.email }); + if (user) { + throw userAlreadyExistsException; + } + + const unverifiedUser = {} as User; + unverifiedUser.uuid = uuidv4(); + unverifiedUser.name = payload.name; + unverifiedUser.email = payload.email; + unverifiedUser.disabled = true; + unverifiedUser.isEmailVerified = false; + + const verificationCode = {} as AuthData; + verificationCode.password = randomBytes(32).toString('hex'); + if (payload.redirect) { + verificationCode.metaData = { redirect: payload.redirect }; + } + verificationCode.entity = USER; + verificationCode.entityUuid = unverifiedUser.uuid; + const expiry = new Date(); + expiry.setDate(expiry.getDate() + 1); + verificationCode.expiry = new Date(); + verificationCode.authDataType = AuthDataType.VerificationCode; + + this.apply(new UserSignedUpViaEmailEvent(unverifiedUser, verificationCode)); + } + + async validateSignupEnabled() { + const settings = await this.serverSettingsService.find(); + if (settings.disableSignup) { + throw new BadRequestException({ + message: i18n.__('Signup Disabled'), + }); + } + } + + async sendUnverifiedEmailVerificationCode(userUuid: string) { + const user = await this.user.findOne({ uuid: userUuid }); + if (!user) { + throw invalidUserException; + } + if (user.isEmailVerified) { + throw new EmailForVerificationNotFound(); + } + + const verificationCode = + (await this.authData.findOne({ + entity: USER, + entityUuid: user.uuid, + authDataType: AuthDataType.VerificationCode, + })) || ({} as AuthData); + + verificationCode.entity = USER; + verificationCode.entityUuid = user.uuid; + verificationCode.authDataType = AuthDataType.VerificationCode; + + if (!verificationCode.password) { + verificationCode.password = randomBytes(32).toString('hex'); + } + + if (!verificationCode.expiry) { + const expiry = new Date(); + expiry.setDate(expiry.getDate() + 1); + verificationCode.expiry = new Date(); + } + + this.apply( + new UnverifiedEmailVerificationCodeSentEvent(user, verificationCode), + ); + } + + async initSignupViaEmailNoVerified(payload: SignupViaEmailNoVerifiedDto) { + await this.validateSignupEnabled(); + + payload.email = payload.email.trim().toLowerCase(); + const user = await this.user.findOne({ email: payload.email }); + if (user) { + throw userAlreadyExistsException; + } + const result = this.passwordPolicy.validatePassword(payload.password); + if (result.errors.length > 0) { + throw new BadRequestException(result.errors); + } + + const verifiedUser = {} as User; + verifiedUser.uuid = uuidv4(); + verifiedUser.name = payload.name; + verifiedUser.email = payload.email; + verifiedUser.disabled = false; + verifiedUser.isEmailVerified = true; + + const userPassword = {} as AuthData & { _id: any }; + userPassword.authDataType = AuthDataType.Password; + userPassword.uuid = uuidv4(); + userPassword.entity = USER; + userPassword.entityUuid = verifiedUser.uuid; + userPassword.password = this.crypto.hashPassword(payload.password); + + verifiedUser.password = userPassword.uuid; + + this.apply(new UserSignedUpViaEmailNoVerifiedEvent(verifiedUser, userPassword)); + } +} diff --git a/apps/authorization-server/src/user-management/commands/index.ts b/apps/authorization-server/src/user-management/commands/index.ts index 2662dfd8..90d29e3d 100644 --- a/apps/authorization-server/src/user-management/commands/index.ts +++ b/apps/authorization-server/src/user-management/commands/index.ts @@ -1,43 +1,45 @@ -import { RemoveUserAccountHandler } from './remove-user-account/remove-user-account.handler'; -import { RemoveUserRoleHandler } from './remove-user-role/remove-user-role.handler'; -import { GenerateForgottenPasswordHandler } from './generate-forgotten-password/generate-forgotten-password.handler'; -import { ChangePasswordHandler } from './change-password/change-password.handler'; -import { VerifyEmailAndSetPasswordHandler } from './verify-email-and-set-password/verify-email-and-set-password.handler'; -import { AddUserAccountHandler } from './add-user-account/add-user-account.handler'; -import { ModifyUserAccountHandler } from './modify-user-account/modify-user-account.handler'; -import { TogglePasswordLessLoginHandler } from './toggle-password-less-login/toggle-password-less-login.handler'; -import { Initialize2FAHandler } from './initialize-2fa/initialize-2fa.handler'; -import { Verify2FAHandler } from './verify-2fa/verify-2fa.handler'; -import { Disable2FAHandler } from './disable-2fa/disable-2fa.handler'; -import { AddUserRoleHandler } from './add-user-role/add-user-role.handler'; -import { ModifyUserRoleHandler } from './modify-user-role/modify-user-role.handler'; -import { UpdateUserFullNameHandler } from './update-user-full-name/update-user-full-name.handler'; -import { AddUserClaimHandler } from './add-user-claim/add-user-claim.handler'; -import { UpdateUserClaimHandler } from './update-user-claim/update-user-claim.handler'; -import { RemoveUserClaimHandler } from './remove-user-claim/remove-user-claim.handler'; -import { SignupViaPhoneHandler } from './signup-via-phone/signup-via-phone.handler'; -import { SignupViaEmailHandler } from './signup-via-email/signup-via-email.handler'; -import { RemoveUnverifiedUserCommandHandler } from './remove-unverified-user/remove-unverified-user.handler'; - -export const UserManagementCommandHandlers = [ - RemoveUserAccountHandler, - RemoveUserRoleHandler, - GenerateForgottenPasswordHandler, - ChangePasswordHandler, - VerifyEmailAndSetPasswordHandler, - AddUserAccountHandler, - ModifyUserAccountHandler, - TogglePasswordLessLoginHandler, - Initialize2FAHandler, - Verify2FAHandler, - Disable2FAHandler, - AddUserRoleHandler, - ModifyUserRoleHandler, - UpdateUserFullNameHandler, - AddUserClaimHandler, - UpdateUserClaimHandler, - RemoveUserClaimHandler, - SignupViaPhoneHandler, - SignupViaEmailHandler, - RemoveUnverifiedUserCommandHandler, -]; +import { RemoveUserAccountHandler } from './remove-user-account/remove-user-account.handler'; +import { RemoveUserRoleHandler } from './remove-user-role/remove-user-role.handler'; +import { GenerateForgottenPasswordHandler } from './generate-forgotten-password/generate-forgotten-password.handler'; +import { ChangePasswordHandler } from './change-password/change-password.handler'; +import { VerifyEmailAndSetPasswordHandler } from './verify-email-and-set-password/verify-email-and-set-password.handler'; +import { AddUserAccountHandler } from './add-user-account/add-user-account.handler'; +import { ModifyUserAccountHandler } from './modify-user-account/modify-user-account.handler'; +import { TogglePasswordLessLoginHandler } from './toggle-password-less-login/toggle-password-less-login.handler'; +import { Initialize2FAHandler } from './initialize-2fa/initialize-2fa.handler'; +import { Verify2FAHandler } from './verify-2fa/verify-2fa.handler'; +import { Disable2FAHandler } from './disable-2fa/disable-2fa.handler'; +import { AddUserRoleHandler } from './add-user-role/add-user-role.handler'; +import { ModifyUserRoleHandler } from './modify-user-role/modify-user-role.handler'; +import { UpdateUserFullNameHandler } from './update-user-full-name/update-user-full-name.handler'; +import { AddUserClaimHandler } from './add-user-claim/add-user-claim.handler'; +import { UpdateUserClaimHandler } from './update-user-claim/update-user-claim.handler'; +import { RemoveUserClaimHandler } from './remove-user-claim/remove-user-claim.handler'; +import { SignupViaPhoneHandler } from './signup-via-phone/signup-via-phone.handler'; +import { SignupViaEmailHandler } from './signup-via-email/signup-via-email.handler'; +import { RemoveUnverifiedUserCommandHandler } from './remove-unverified-user/remove-unverified-user.handler'; +import { SignupViaEmailNoVerifiedHandler } from './signup-via-email-no-verified/signup-via-email-no-verified.handler'; + +export const UserManagementCommandHandlers = [ + RemoveUserAccountHandler, + RemoveUserRoleHandler, + GenerateForgottenPasswordHandler, + ChangePasswordHandler, + VerifyEmailAndSetPasswordHandler, + AddUserAccountHandler, + ModifyUserAccountHandler, + TogglePasswordLessLoginHandler, + Initialize2FAHandler, + Verify2FAHandler, + Disable2FAHandler, + AddUserRoleHandler, + ModifyUserRoleHandler, + UpdateUserFullNameHandler, + AddUserClaimHandler, + UpdateUserClaimHandler, + RemoveUserClaimHandler, + SignupViaPhoneHandler, + SignupViaEmailHandler, + RemoveUnverifiedUserCommandHandler, + SignupViaEmailNoVerifiedHandler +]; diff --git a/apps/authorization-server/src/user-management/commands/signup-via-email-no-verified/signup-via-email-no-verified.command.ts b/apps/authorization-server/src/user-management/commands/signup-via-email-no-verified/signup-via-email-no-verified.command.ts new file mode 100644 index 00000000..c6a6a4b3 --- /dev/null +++ b/apps/authorization-server/src/user-management/commands/signup-via-email-no-verified/signup-via-email-no-verified.command.ts @@ -0,0 +1,6 @@ +import { ICommand } from '@nestjs/cqrs'; +import { SignupViaEmailNoVerifiedDto } from '../../policies'; + +export class SignupViaEmailNoVerifiedCommand implements ICommand { + constructor(public readonly payload: SignupViaEmailNoVerifiedDto) {} +} diff --git a/apps/authorization-server/src/user-management/commands/signup-via-email-no-verified/signup-via-email-no-verified.handler.ts b/apps/authorization-server/src/user-management/commands/signup-via-email-no-verified/signup-via-email-no-verified.handler.ts new file mode 100644 index 00000000..0130efec --- /dev/null +++ b/apps/authorization-server/src/user-management/commands/signup-via-email-no-verified/signup-via-email-no-verified.handler.ts @@ -0,0 +1,23 @@ +import { ICommandHandler, CommandHandler, EventPublisher } from '@nestjs/cqrs'; + +import { SignupViaEmailNoVerifiedCommand } from './signup-via-email-no-verified.command'; +import { SignupService } from '../../aggregates/signup/signup.service'; + +@CommandHandler(SignupViaEmailNoVerifiedCommand) +export class SignupViaEmailNoVerifiedHandler + implements ICommandHandler +{ + constructor( + private readonly manager: SignupService, + private readonly eventPublisher: EventPublisher, + ) {} + + async execute(command: SignupViaEmailNoVerifiedCommand) { + const { payload } = command; + const aggregate = this.eventPublisher.mergeObjectContext(this.manager); + + const user = await aggregate.initSignupViaEmailNoVerified(payload); + aggregate.commit(); + return user; + } +} diff --git a/apps/authorization-server/src/user-management/controllers/signup/signup.controller.ts b/apps/authorization-server/src/user-management/controllers/signup/signup.controller.ts index b784cb3c..37de3ad1 100644 --- a/apps/authorization-server/src/user-management/controllers/signup/signup.controller.ts +++ b/apps/authorization-server/src/user-management/controllers/signup/signup.controller.ts @@ -1,28 +1,35 @@ -import { - Controller, - Body, - Post, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { CommandBus } from '@nestjs/cqrs'; -import { SignupViaPhoneCommand } from '../../commands/signup-via-phone/signup-via-phone.command'; -import { SignupViaEmailDto, SignupViaPhoneDto } from '../../policies'; -import { SignupViaEmailCommand } from '../../commands/signup-via-email/signup-via-email.command'; - -@Controller('user_signup') -export class SignupController { - constructor(private readonly commandBus: CommandBus) {} - - @Post('v1/email') - @UsePipes(new ValidationPipe({ whitelist: true })) - async signupViaEmail(@Body() payload: SignupViaEmailDto) { - return await this.commandBus.execute(new SignupViaEmailCommand(payload)); - } - - @Post('v1/phone') - @UsePipes(new ValidationPipe({ whitelist: true })) - async signupViaPhone(@Body() payload: SignupViaPhoneDto) { - return await this.commandBus.execute(new SignupViaPhoneCommand(payload)); - } -} +import { + Controller, + Body, + Post, + UsePipes, + ValidationPipe, +} from '@nestjs/common'; +import { CommandBus } from '@nestjs/cqrs'; +import { SignupViaPhoneCommand } from '../../commands/signup-via-phone/signup-via-phone.command'; +import { SignupViaEmailDto, SignupViaPhoneDto, SignupViaEmailNoVerifiedDto } from '../../policies'; +import { SignupViaEmailCommand } from '../../commands/signup-via-email/signup-via-email.command'; +import { SignupViaEmailNoVerifiedCommand } from '../../commands/signup-via-email-no-verified/signup-via-email-no-verified.command'; + +@Controller('user_signup') +export class SignupController { + constructor(private readonly commandBus: CommandBus) {} + + @Post('v1/email') + @UsePipes(new ValidationPipe({ whitelist: true })) + async signupViaEmail(@Body() payload: SignupViaEmailDto) { + return await this.commandBus.execute(new SignupViaEmailCommand(payload)); + } + + @Post('v1/email_no_verified') + @UsePipes(new ValidationPipe({ whitelist: true })) + async signupViaEmailNoVerified(@Body() payload: SignupViaEmailNoVerifiedDto) { + return await this.commandBus.execute(new SignupViaEmailNoVerifiedCommand(payload)); + } + + @Post('v1/phone') + @UsePipes(new ValidationPipe({ whitelist: true })) + async signupViaPhone(@Body() payload: SignupViaPhoneDto) { + return await this.commandBus.execute(new SignupViaPhoneCommand(payload)); + } +} diff --git a/apps/authorization-server/src/user-management/events/index.ts b/apps/authorization-server/src/user-management/events/index.ts index c3233c44..b163effb 100644 --- a/apps/authorization-server/src/user-management/events/index.ts +++ b/apps/authorization-server/src/user-management/events/index.ts @@ -1,41 +1,43 @@ -import { UserAccountRemovedHandler } from './user-account-removed/user-account-removed.handler'; -import { UserRoleRemovedHandler } from './user-role-removed/user-role-removed.handler'; -import { ForgottenPasswordGeneratedHandler } from './forgotten-password-generated/forgotten-password-generated.handler'; -import { PasswordChangedHandler } from './password-changed/password-changed.handler'; -import { UserAccountAddedHandler } from './user-account-added/user-account-added.handler'; -import { UserAccountModifiedHandler } from './user-account-modified/user-account-modified.handler'; -import { EmailVerifiedAndPasswordSetHandler } from './email-verified-and-password-set/email-verified-and-password-set.handler'; -import { AuthDataRemovedHandler } from './auth-data-removed/auth-data-removed.handler'; -import { UserRoleAddedHandler } from './user-role-added/user-role-added.handler'; -import { UserRoleModifiedHandler } from './user-role-modified/user-role-modified.handler'; -import { UserClaimRemovedHandler } from './user-claim-removed/user-claim-removed.handler'; -import { UserClaimAddedHandler } from './user-claim-added/user-claim-added.handler'; -import { UserClaimUpdatedHandler } from './user-claim-updated/user-claim-updated.handler'; -import { UserSignedUpViaEmailHandler } from './user-signed-up-via-email/user-signed-up-via-email.handler'; -import { EmailVerificationCodeHandler } from '../commands/email-verification-code/email-verification-code.handler'; - -/* eslint-disable */ -import { UnverifiedEmailVerificationCodeSentHandler } from './unverified-email-verification-code-sent/unverified-email-verification-code-sent.handler'; -import { EmailVerifiedAndUpdatedHandler } from './email-verified-and-updated/email-verified-and-updated.handler'; -export { UnverifiedEmailVerificationCodeSentEvent } from './unverified-email-verification-code-sent/unverified-email-verification-code-sent.event'; -/* eslint-enable */ - -export const UserManagementEventHandlers = [ - UserAccountRemovedHandler, - UserRoleRemovedHandler, - ForgottenPasswordGeneratedHandler, - PasswordChangedHandler, - UserAccountAddedHandler, - UserAccountModifiedHandler, - EmailVerifiedAndPasswordSetHandler, - AuthDataRemovedHandler, - UserRoleAddedHandler, - UserRoleModifiedHandler, - UserClaimAddedHandler, - UserClaimUpdatedHandler, - UserClaimRemovedHandler, - UserSignedUpViaEmailHandler, - EmailVerificationCodeHandler, - UnverifiedEmailVerificationCodeSentHandler, - EmailVerifiedAndUpdatedHandler, -]; +import { UserAccountRemovedHandler } from './user-account-removed/user-account-removed.handler'; +import { UserRoleRemovedHandler } from './user-role-removed/user-role-removed.handler'; +import { ForgottenPasswordGeneratedHandler } from './forgotten-password-generated/forgotten-password-generated.handler'; +import { PasswordChangedHandler } from './password-changed/password-changed.handler'; +import { UserAccountAddedHandler } from './user-account-added/user-account-added.handler'; +import { UserAccountModifiedHandler } from './user-account-modified/user-account-modified.handler'; +import { EmailVerifiedAndPasswordSetHandler } from './email-verified-and-password-set/email-verified-and-password-set.handler'; +import { AuthDataRemovedHandler } from './auth-data-removed/auth-data-removed.handler'; +import { UserRoleAddedHandler } from './user-role-added/user-role-added.handler'; +import { UserRoleModifiedHandler } from './user-role-modified/user-role-modified.handler'; +import { UserClaimRemovedHandler } from './user-claim-removed/user-claim-removed.handler'; +import { UserClaimAddedHandler } from './user-claim-added/user-claim-added.handler'; +import { UserClaimUpdatedHandler } from './user-claim-updated/user-claim-updated.handler'; +import { UserSignedUpViaEmailHandler } from './user-signed-up-via-email/user-signed-up-via-email.handler'; +import { EmailVerificationCodeHandler } from '../commands/email-verification-code/email-verification-code.handler'; +import { UserSignedUpViaEmailNoVerifiedHandler } from './user-signed-up-via-email-no-verified/user-signed-up-via-email-no-verified.handler' + +/* eslint-disable */ +import { UnverifiedEmailVerificationCodeSentHandler } from './unverified-email-verification-code-sent/unverified-email-verification-code-sent.handler'; +import { EmailVerifiedAndUpdatedHandler } from './email-verified-and-updated/email-verified-and-updated.handler'; +export { UnverifiedEmailVerificationCodeSentEvent } from './unverified-email-verification-code-sent/unverified-email-verification-code-sent.event'; +/* eslint-enable */ + +export const UserManagementEventHandlers = [ + UserAccountRemovedHandler, + UserRoleRemovedHandler, + ForgottenPasswordGeneratedHandler, + PasswordChangedHandler, + UserAccountAddedHandler, + UserAccountModifiedHandler, + EmailVerifiedAndPasswordSetHandler, + AuthDataRemovedHandler, + UserRoleAddedHandler, + UserRoleModifiedHandler, + UserClaimAddedHandler, + UserClaimUpdatedHandler, + UserClaimRemovedHandler, + UserSignedUpViaEmailHandler, + EmailVerificationCodeHandler, + UnverifiedEmailVerificationCodeSentHandler, + EmailVerifiedAndUpdatedHandler, + UserSignedUpViaEmailNoVerifiedHandler +]; diff --git a/apps/authorization-server/src/user-management/events/user-signed-up-via-email-no-verified/user-signed-up-via-email-no-verified.event.ts b/apps/authorization-server/src/user-management/events/user-signed-up-via-email-no-verified/user-signed-up-via-email-no-verified.event.ts new file mode 100644 index 00000000..82c64309 --- /dev/null +++ b/apps/authorization-server/src/user-management/events/user-signed-up-via-email-no-verified/user-signed-up-via-email-no-verified.event.ts @@ -0,0 +1,10 @@ +import { IEvent } from '@nestjs/cqrs'; +import { AuthData } from '../../entities/auth-data/auth-data.interface'; +import { User } from '../../entities/user/user.interface'; + +export class UserSignedUpViaEmailNoVerifiedEvent implements IEvent { + constructor( + public readonly unverifiedUser: User, + public readonly userPassword: AuthData, + ) {} +} diff --git a/apps/authorization-server/src/user-management/events/user-signed-up-via-email-no-verified/user-signed-up-via-email-no-verified.handler.ts b/apps/authorization-server/src/user-management/events/user-signed-up-via-email-no-verified/user-signed-up-via-email-no-verified.handler.ts new file mode 100644 index 00000000..f41f6be8 --- /dev/null +++ b/apps/authorization-server/src/user-management/events/user-signed-up-via-email-no-verified/user-signed-up-via-email-no-verified.handler.ts @@ -0,0 +1,28 @@ +import { IEventHandler, EventsHandler } from '@nestjs/cqrs'; +import { forkJoin, from } from 'rxjs'; + +import { UserSignedUpViaEmailNoVerifiedEvent } from './user-signed-up-via-email-no-verified.event' +import { UserService } from '../../entities/user/user.service'; +import { AuthDataService } from '../../entities/auth-data/auth-data.service'; + +@EventsHandler(UserSignedUpViaEmailNoVerifiedEvent) +export class UserSignedUpViaEmailNoVerifiedHandler + implements IEventHandler +{ + constructor( + private readonly user: UserService, + private readonly authData: AuthDataService, + ) {} + + handle(event: UserSignedUpViaEmailNoVerifiedEvent) { + const { unverifiedUser, userPassword } = event; + + forkJoin({ + user: from(this.user.save(unverifiedUser)), + code: from(this.authData.save(userPassword)), + }).subscribe({ + next: success => {}, + error: error => {}, + }); + } +} diff --git a/apps/authorization-server/src/user-management/policies/index.ts b/apps/authorization-server/src/user-management/policies/index.ts index 068f451f..fa787d78 100644 --- a/apps/authorization-server/src/user-management/policies/index.ts +++ b/apps/authorization-server/src/user-management/policies/index.ts @@ -1,19 +1,20 @@ -import { PasswordPolicyService } from './password-policy/password-policy.service'; -import { RoleValidationPolicyService } from './role-validation-policy/role-validation-policy.service'; - -export { CreateScopeDto } from './create-scope/create-scope.dto'; -export { UserAccountDto } from './user-account/user-account.dto'; -export { SignupViaEmailDto } from './signup-via-email/signup-via-email.dto'; -export { VerifyEmailDto } from './verify-email/verify-email.dto'; -export { ChangePasswordDto } from './change-password/change-password.dto'; -export { SignupViaPhoneDto } from './signup-via-phone/signup-via-phone.dto'; -export { VerifyPhoneDto } from './verify-phone/verify-phone.dto'; -export { VerifySignupViaPhoneDto } from './verify-signup-via-phone/verify-signup-via-phone.dto'; -export { UnverifiedPhoneDto } from './unverified-phone/unverified-phone.dto'; -export { UnverifiedEmailDto } from './unverified-email/unverified-email.dto'; -export { VerifyUpdatedEmailDto } from './verify-updated-email/verify-updated-email.dto'; - -export const UserManagementPolicies = [ - PasswordPolicyService, - RoleValidationPolicyService, -]; +import { PasswordPolicyService } from './password-policy/password-policy.service'; +import { RoleValidationPolicyService } from './role-validation-policy/role-validation-policy.service'; + +export { CreateScopeDto } from './create-scope/create-scope.dto'; +export { UserAccountDto } from './user-account/user-account.dto'; +export { SignupViaEmailDto } from './signup-via-email/signup-via-email.dto'; +export { VerifyEmailDto } from './verify-email/verify-email.dto'; +export { ChangePasswordDto } from './change-password/change-password.dto'; +export { SignupViaPhoneDto } from './signup-via-phone/signup-via-phone.dto'; +export { VerifyPhoneDto } from './verify-phone/verify-phone.dto'; +export { VerifySignupViaPhoneDto } from './verify-signup-via-phone/verify-signup-via-phone.dto'; +export { UnverifiedPhoneDto } from './unverified-phone/unverified-phone.dto'; +export { UnverifiedEmailDto } from './unverified-email/unverified-email.dto'; +export { VerifyUpdatedEmailDto } from './verify-updated-email/verify-updated-email.dto'; +export { SignupViaEmailNoVerifiedDto } from './signup-via-email-no-verified/signup-via-email-no-verified.dto' + +export const UserManagementPolicies = [ + PasswordPolicyService, + RoleValidationPolicyService, +]; diff --git a/apps/authorization-server/src/user-management/policies/signup-via-email-no-verified/signup-via-email-no-verified.dto.ts b/apps/authorization-server/src/user-management/policies/signup-via-email-no-verified/signup-via-email-no-verified.dto.ts new file mode 100644 index 00000000..b71fc9f2 --- /dev/null +++ b/apps/authorization-server/src/user-management/policies/signup-via-email-no-verified/signup-via-email-no-verified.dto.ts @@ -0,0 +1,11 @@ +import { IsEmail, IsNotEmpty, IsString } from 'class-validator'; + +export class SignupViaEmailNoVerifiedDto { + @IsNotEmpty() + name: string; + @IsEmail() + email: string; + @IsString() + @IsNotEmpty() + password: string; +} diff --git a/docker/lixi-backend.yaml b/docker/lixi-backend.yaml new file mode 100644 index 00000000..4064867c --- /dev/null +++ b/docker/lixi-backend.yaml @@ -0,0 +1,155 @@ +version: "3" + +services: + # Backend + authorization-server: + # image: registry.gitlab.com/castlecraft/building-blocks/authorization-server:latest + build: + context: ../apps/authorization-server + dockerfile: Dockerfile + extra_hosts: + admin.localhost: 172.17.0.1 + connect.localhost: 172.17.0.1 + myaccount.localhost: 172.17.0.1 + environment: + - NODE_ENV=development + - SESSION_SECRET=secret + - COOKIE_MAX_AGE=7.884e+9 + - SESSION_NAME=AS_SESSION + - TOKEN_VALIDITY=3600 + - DB_HOST=mongo + - DB_NAME=authorization-server + - DB_USER=authorization-server + - DB_PASSWORD=admin + - EVENTS_HOST=event-bus + - EVENTS_USER=user + - EVENTS_PASSWORD=admin123 + - EVENTS_PORT=1883 + - EVENTS_PROTO=mqtt + ports: + - 3000:3000 + + communication-server: + # image: registry.gitlab.com/castlecraft/building-blocks/communication-server:latest + build: + context: ../apps/communication-server + dockerfile: Dockerfile + extra_hosts: + accounts.localhost: 172.17.0.1 + admin.localhost: 172.17.0.1 + myaccount.localhost: 172.17.0.1 + environment: + - NODE_ENV=development + - DB_HOST=mongo + - DB_NAME=communication-server + - DB_USER=communication-server + - DB_PASSWORD=admin + - EVENTS_HOST=event-bus + - EVENTS_USER=user + - EVENTS_PASSWORD=admin123 + - EVENTS_PORT=1883 + - EVENTS_PROTO=mqtt + ports: + - 4100:4100 + + identity-provider: + # image: registry.gitlab.com/castlecraft/building-blocks/identity-provider:latest + build: + context: ../apps/identity-provider + dockerfile: Dockerfile + extra_hosts: + accounts.localhost: 172.17.0.1 + admin.localhost: 172.17.0.1 + connect.localhost: 172.17.0.1 + environment: + - NODE_ENV=development + - DB_HOST=mongo + - DB_NAME=identity-provider + - DB_USER=identity-provider + - DB_PASSWORD=admin + - EVENTS_HOST=event-bus + - EVENTS_USER=user + - EVENTS_PASSWORD=admin123 + - EVENTS_PORT=1883 + - EVENTS_PROTO=mqtt + ports: + - 3200:3200 + + infrastructure-console: + build: + context: ../apps/infrastructure-console + dockerfile: Dockerfile + extra_hosts: + accounts.localhost: 172.17.0.1 + connect.localhost: 172.17.0.1 + myaccount.localhost: 172.17.0.1 + # image: registry.gitlab.com/castlecraft/building-blocks/infrastructure-console:latest + environment: + - NODE_ENV=development + - DB_HOST=mongo + - DB_NAME=infrastructure-console + - DB_USER=infrastructure-console + - DB_PASSWORD=admin + - EVENTS_HOST=event-bus + - EVENTS_USER=user + - EVENTS_PASSWORD=admin123 + - EVENTS_PORT=1883 + - EVENTS_PROTO=mqtt + ports: + - 5000:5000 + + event-bus: + image: emqx/emqx:latest + environment: + - EMQX_ALLOW_ANONYMOUS=true + - EMQX_LOADED_PLUGINS=emqx_management,emqx_auth_mnesia,emqx_recon,emqx_retainer,emqx_dashboard + ports: + - 1883:1883 + - 18083:18083 + - 8081:8081 + + mongo: + image: bitnami/mongodb:latest + environment: + - "MONGODB_ROOT_PASSWORD=admin" + - "MONGODB_DATABASE=authorization-server" + - "MONGODB_USERNAME=authorization-server" + - "MONGODB_PASSWORD=admin" + volumes: + - mongodb-vol:/bitnami/mongodb + ports: + - 27017:27017 + + mongo-configuration: + image: bitnami/mongodb:latest + command: + - bash + - -c + - > + sleep 10; + mongosh identity-provider \ + --host mongo \ + --port 27017 \ + -u root \ + -p admin \ + --authenticationDatabase admin \ + --eval "db.createUser({user: 'identity-provider', pwd: 'admin', roles:[{role:'dbOwner', db: 'identity-provider'}]});"; + + mongosh infrastructure-console \ + --host mongo \ + --port 27017 \ + -u root \ + -p admin \ + --authenticationDatabase admin \ + --eval "db.createUser({user: 'infrastructure-console', pwd: 'admin', roles:[{role:'dbOwner', db: 'infrastructure-console'}]});"; + + mongosh communication-server \ + --host mongo \ + --port 27017 \ + -u root \ + -p admin \ + --authenticationDatabase admin \ + --eval "db.createUser({user: 'communication-server', pwd: 'admin', roles:[{role:'dbOwner', db: 'communication-server'}]});"; + +volumes: + mongodb-vol: \ No newline at end of file diff --git a/docker/lixi-frontend.yaml b/docker/lixi-frontend.yaml new file mode 100644 index 00000000..4f3d1a3a --- /dev/null +++ b/docker/lixi-frontend.yaml @@ -0,0 +1,41 @@ +version: "3" + +services: + # Frontend + authorization-client: + # image: registry.gitlab.com/castlecraft/building-blocks/authorization-client:latest + build: + context: ../frontends/authorization-client + dockerfile: Dockerfile + environment: + - API_HOST=authorization-server + - API_PORT=3000 + ports: + - 4210:8080 + + admin-client: + # image: registry.gitlab.com/castlecraft/building-blocks/admin-client:latest + build: + context: ../frontends/admin-client + dockerfile: Dockerfile + environment: + - API_HOST=infrastructure-console + - API_PORT=5000 + ports: + - 4220:8080 + + identity-client: + # image: registry.gitlab.com/castlecraft/building-blocks/identity-client:latest + build: + context: ../frontends/identity-client + dockerfile: Dockerfile + environment: + - API_HOST=identity-provider + - API_PORT=3200 + ports: + - 4420:8080 + +networks: + default: + name: lixi-backend_default + external: true \ No newline at end of file diff --git a/frontends/admin-client/Dockerfile b/frontends/admin-client/Dockerfile index e6891e05..9a204ee4 100644 --- a/frontends/admin-client/Dockerfile +++ b/frontends/admin-client/Dockerfile @@ -1,6 +1,6 @@ FROM node:latest -ARG BUILD_ARGS="--configuration production" +ARG BUILD_ARGS="--configuration development" # Copy and build app COPY . /home/craft/admin-client WORKDIR /home/craft diff --git a/frontends/authorization-client/Dockerfile b/frontends/authorization-client/Dockerfile index 21039afc..786e2818 100644 --- a/frontends/authorization-client/Dockerfile +++ b/frontends/authorization-client/Dockerfile @@ -1,6 +1,6 @@ FROM node:latest -ARG BUILD_ARGS="--configuration production" +ARG BUILD_ARGS="--configuration development" # Copy and build app COPY . /home/craft/authorization-client WORKDIR /home/craft diff --git a/frontends/identity-client/Dockerfile b/frontends/identity-client/Dockerfile index d28e3ec5..62baa026 100644 --- a/frontends/identity-client/Dockerfile +++ b/frontends/identity-client/Dockerfile @@ -1,6 +1,6 @@ FROM node:latest -ARG BUILD_ARGS="--configuration production" +ARG BUILD_ARGS="--configuration development" # Copy and build app COPY . /home/craft/identity-client WORKDIR /home/craft