From 868faf6fc4e58000ea9ba9479fd6da01f25d5678 Mon Sep 17 00:00:00 2001 From: Camden Moors Date: Tue, 12 Jan 2021 14:55:14 -0500 Subject: [PATCH 01/34] Add backend for authenticating with LDAP --- README.md | 6 +- apps/backend/.env-example | 14 ++++ apps/backend/package.json | 1 + apps/backend/src/authn/authn.controller.ts | 7 ++ apps/backend/src/authn/authn.module.ts | 3 +- apps/backend/src/authn/authn.service.ts | 43 ++++++++++++ apps/backend/src/authn/ldap.strategy.ts | 46 +++++++++++++ yarn.lock | 79 +++++++++++++++++++++- 8 files changed, 192 insertions(+), 7 deletions(-) create mode 100644 apps/backend/src/authn/ldap.strategy.ts diff --git a/README.md b/README.md index 9e4bb52aeb..ce656d1648 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ To run Heimdall Server, Postgresql must be installed and a user account must exi # Start the Postgres terminal psql postgres - + # Create the user CREATE USER with encrypted password ''; ALTER USER CREATEDB; @@ -180,9 +180,7 @@ Then, the following one-time steps must be performed: cp apps/backend/.env-example apps/backend/.env # Edit /apps/backend/.env to reflect the appropriate configuration for your system - yarn backend sequelize-cli db:create - yarn backend sequelize-cli db:migrate - yarn backend sequelize-cli db:seed:all + Once the above steps are completed it is possible to start heimdall-server using the following command diff --git a/apps/backend/.env-example b/apps/backend/.env-example index 6f2824b1c5..02c584f8a1 100644 --- a/apps/backend/.env-example +++ b/apps/backend/.env-example @@ -10,3 +10,17 @@ JWT_EXPIRE_TIME= HEIMDALL_HEADLESS_TESTS= ADMIN_PASSWORD= + +# LDAP Configuration +LDAP_HOST= +LDAP_PORT= +LDAP_BINDDN= +LDAP_PASSWORD= +# Here you set your LDAP searchbase, for more info see https://docs.oracle.com/cd/E19693-01/819-0997/auto45/index.html +# If you're using Active Directory, you probably want "OU=Users, DC=, DC=local" +LDAP_SEARCHBASE="" +# Here you set your LDAP search filter, for more info see https://confluence.atlassian.com/kb/how-to-write-ldap-search-filters-792496933.html +# If you are using Active Directory Users, you probably want "(sAMAccountName={{username}}" +LDAP_SEARCHFILTER="" +LDAP_NAMEFIELD="" +LDAP_MAILFIELD="" \ No newline at end of file diff --git a/apps/backend/package.json b/apps/backend/package.json index 0f9a292d51..b5a23c0cd7 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -67,6 +67,7 @@ "js-levenshtein": "^1.1.6", "passport": "^0.4.1", "passport-jwt": "^4.0.0", + "passport-ldapauth": "^3.0.1", "passport-local": "^1.0.0", "pg": "^8.2.1", "reflect-metadata": "^0.1.13", diff --git a/apps/backend/src/authn/authn.controller.ts b/apps/backend/src/authn/authn.controller.ts index 5e866c9aa9..acd571a5e0 100644 --- a/apps/backend/src/authn/authn.controller.ts +++ b/apps/backend/src/authn/authn.controller.ts @@ -1,4 +1,5 @@ import {Controller, Post, Req, UseGuards} from '@nestjs/common'; +import {AuthGuard} from '@nestjs/passport'; import {Request} from 'express'; import {LocalAuthGuard} from '../guards/local-auth.guard'; import {User} from '../users/user.model'; @@ -13,4 +14,10 @@ export class AuthnController { async login(@Req() req: Request): Promise { return this.authnService.login(req.user as User); } + + @UseGuards(AuthGuard('ldap')) + @Post('login/ldap') + async loginToLDAP(@Req() req: Request): Promise { + return this.authnService.login(req.user as User); + } } diff --git a/apps/backend/src/authn/authn.module.ts b/apps/backend/src/authn/authn.module.ts index b5f6b8dfad..6e3f361ae0 100644 --- a/apps/backend/src/authn/authn.module.ts +++ b/apps/backend/src/authn/authn.module.ts @@ -6,11 +6,12 @@ import {UsersModule} from '../users/users.module'; import {AuthnController} from './authn.controller'; import {AuthnService} from './authn.service'; import {JwtStrategy} from './jwt.strategy'; +import {LDAPStrategy} from './ldap.strategy'; import {LocalStrategy} from './local.strategy'; @Module({ imports: [UsersModule, PassportModule, TokenModule, ConfigModule], - providers: [AuthnService, LocalStrategy, JwtStrategy], + providers: [AuthnService, LocalStrategy, JwtStrategy, LDAPStrategy], controllers: [AuthnController] }) export class AuthnModule {} diff --git a/apps/backend/src/authn/authn.service.ts b/apps/backend/src/authn/authn.service.ts index 2078decefa..ea9fbff03c 100644 --- a/apps/backend/src/authn/authn.service.ts +++ b/apps/backend/src/authn/authn.service.ts @@ -1,6 +1,9 @@ import {Injectable, UnauthorizedException} from '@nestjs/common'; import {JwtService} from '@nestjs/jwt'; import {compare} from 'bcrypt'; +import * as crypto from 'crypto'; +import {ConfigService} from '../config/config.service'; +import {CreateUserDto} from '../users/dto/create-user.dto'; import {User} from '../users/user.model'; import {UsersService} from '../users/users.service'; @@ -8,6 +11,7 @@ import {UsersService} from '../users/users.service'; export class AuthnService { constructor( private usersService: UsersService, + private configService: ConfigService, private jwtService: JwtService ) {} @@ -26,6 +30,37 @@ export class AuthnService { } } + async validateOrCreateUser( + email: string, + firstName: string, + lastName: string + ): Promise { + let user: User; + try { + user = await this.usersService.findByEmail(email); + } catch { + const randomPass = crypto.randomBytes(128).toString('hex'); + const createUser: CreateUserDto = { + email: email, + password: randomPass, + passwordConfirmation: randomPass, + firstName: firstName, + lastName: lastName, + organization: '', + title: '', + role: '' + }; + await this.usersService.create(createUser); + user = await this.usersService.findByEmail(email); + } + + if (user) { + this.usersService.updateLoginMetadata(user); + } + + return user; + } + async login(user: { id: string; email: string; @@ -51,4 +86,12 @@ export class AuthnService { }; } } + + splitName(fullName: string): {firstName: string; lastName: string} { + const nameArray = fullName.split(' '); + return { + firstName: nameArray.slice(0, -1).join(' '), + lastName: nameArray[nameArray.length - 1] + }; + } } diff --git a/apps/backend/src/authn/ldap.strategy.ts b/apps/backend/src/authn/ldap.strategy.ts new file mode 100644 index 0000000000..a4692ba9a9 --- /dev/null +++ b/apps/backend/src/authn/ldap.strategy.ts @@ -0,0 +1,46 @@ +import {Injectable} from '@nestjs/common'; +import {PassportStrategy} from '@nestjs/passport'; +import {Request} from 'express'; +import Strategy from 'passport-ldapauth'; +import {ConfigService} from '../config/config.service'; +import {AuthnService} from './authn.service'; + +@Injectable() +export class LDAPStrategy extends PassportStrategy(Strategy, 'ldap') { + constructor( + private authnService: AuthnService, + private configService: ConfigService + ) { + super( + { + passReqToCallback: true, + server: { + url: `ldap://${configService.get('LDAP_HOST')}:${ + configService.get('LDAP_PORT') || 389 + }`, + bindDN: configService.get('LDAP_BINDDN'), + bindCredentials: configService.get('LDAP_PASSWORD'), + searchBase: configService.get('LDAP_SEARCHBASE') || 'disabled', + searchFilter: + configService.get('LDAP_SEARCHFILTER') || + '(sAMAccountName={{username}})', + passReqToCallback: true + } + }, + async (req: Request, user: any, done: any) => { + const {firstName, lastName} = this.authnService.splitName( + user[configService.get('LDAP_NAMEFIELD') || 'name'] + ); + const email: string = + user[configService.get('LDAP_MAILFIELD') || 'mail']; + + req.user = this.authnService.validateOrCreateUser( + email, + firstName, + lastName + ); + return done(null, req.user); + } + ); + } +} diff --git a/yarn.lock b/yarn.lock index c3b1c84f47..8338757b67 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3471,6 +3471,13 @@ "@types/koa-compose" "*" "@types/node" "*" +"@types/ldapjs@^1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@types/ldapjs/-/ldapjs-1.0.9.tgz#1224192d14cc5ab5218fcea72ebb04489c52cb95" + integrity sha512-3PvY7Drp1zoLbcGlothCAkoc5o6Jp9KvUPwHadlHyKp3yPvyeIh7w2zQc9UXMzgDRkoeGXUEODtbEs5XCh9ZyA== + dependencies: + "@types/node" "*" + "@types/lodash@*", "@types/lodash@^4.14.161": version "4.14.167" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.167.tgz#ce7d78553e3c886d4ea643c37ec7edc20f16765e" @@ -4556,6 +4563,11 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abstract-logging@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/abstract-logging/-/abstract-logging-2.0.1.tgz#6b0c371df212db7129b57d2e7fcf282b8bf1c839" + integrity sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA== + accepts@^1.3.5, accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -5383,7 +5395,7 @@ asn1.js@^5.2.0: minimalistic-assert "^1.0.0" safer-buffer "^2.1.0" -asn1@~0.2.3: +asn1@^0.2.4, asn1@~0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== @@ -5783,6 +5795,13 @@ backo2@^1.0.2: resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= +backoff@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" + integrity sha1-9hbtqdPktmuMp/ynn2lXIsX44m8= + dependencies: + precond "0.2" + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -5826,6 +5845,11 @@ bcrypt@^5.0.0: node-addon-api "^3.0.0" node-pre-gyp "0.15.0" +bcryptjs@^2.4.0: + version "2.4.3" + resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" + integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms= + before-after-hook@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" @@ -13398,6 +13422,37 @@ lazy-ass@^1.6.0: resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= +ldap-filter@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/ldap-filter/-/ldap-filter-0.3.3.tgz#2b14c68a2a9d4104dbdbc910a1ca85fd189e9797" + integrity sha1-KxTGiiqdQQTb28kQocqF/Riel5c= + dependencies: + assert-plus "^1.0.0" + +ldapauth-fork@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ldapauth-fork/-/ldapauth-fork-5.0.1.tgz#18779a9c30371c5bbea02e3b6aaadb60819ad29c" + integrity sha512-EdELQz8zgPruqV2y88PAuAiZCgTaMjex/kEA2PIcOlPYFt75C9QFt5HGZKVQo8Sf/3Mwnr1AtiThHKcq+pRtEg== + dependencies: + "@types/ldapjs" "^1.0.9" + bcryptjs "^2.4.0" + ldapjs "^2.2.1" + lru-cache "^6.0.0" + +ldapjs@^2.2.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/ldapjs/-/ldapjs-2.2.3.tgz#7ae42c601911c2809f126355a2595ee1d1e21edf" + integrity sha512-143MayI+cSo1PEngge0HMVj3Fb0TneX4Qp9yl9bKs45qND3G64B75GMSxtZCfNuVsvg833aOp1UWG8peFu1LrQ== + dependencies: + abstract-logging "^2.0.0" + asn1 "^0.2.4" + assert-plus "^1.0.0" + backoff "^2.5.0" + ldap-filter "^0.3.3" + once "^1.4.0" + vasync "^2.2.0" + verror "^1.8.1" + left-pad@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" @@ -15614,6 +15669,14 @@ passport-jwt@^4.0.0: jsonwebtoken "^8.2.0" passport-strategy "^1.0.0" +passport-ldapauth@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/passport-ldapauth/-/passport-ldapauth-3.0.1.tgz#1432e8469de18bd46b5b39a46a866b416c1ddded" + integrity sha512-TRRx3BHi8GC8MfCT9wmghjde/EGeKjll7zqHRRfGRxXbLcaDce2OftbQrFG7/AWaeFhR6zpZHtBQ/IkINdLVjQ== + dependencies: + ldapauth-fork "^5.0.1" + passport-strategy "^1.0.0" + passport-local@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/passport-local/-/passport-local-1.0.0.tgz#1fe63268c92e75606626437e3b906662c15ba6ee" @@ -16316,6 +16379,11 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" +precond@0.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" + integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw= + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -19794,12 +19862,19 @@ vary@^1, vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= +vasync@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/vasync/-/vasync-2.2.0.tgz#cfde751860a15822db3b132bc59b116a4adaf01b" + integrity sha1-z951GGChWCLbOxMrxZsRakra8Bs= + dependencies: + verror "1.10.0" + vendors@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== -verror@1.10.0: +verror@1.10.0, verror@^1.8.1: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= From 247cdef8cb01c645c7ae2551936e1db93ec1ee56 Mon Sep 17 00:00:00 2001 From: Camden Moors Date: Thu, 21 Jan 2021 11:18:26 -0500 Subject: [PATCH 02/34] Add frontend for LDAP login --- apps/backend/.env-example | 1 + apps/backend/src/config/config.service.ts | 5 +- .../src/config/dto/startup-settings.dto.ts | 2 + apps/frontend/src/store/server.ts | 12 ++ apps/frontend/src/views/Login.vue | 203 +++++++++++++----- .../config/startup-settings.interface.ts | 1 + 6 files changed, 174 insertions(+), 50 deletions(-) diff --git a/apps/backend/.env-example b/apps/backend/.env-example index 02c584f8a1..73b87c7949 100644 --- a/apps/backend/.env-example +++ b/apps/backend/.env-example @@ -12,6 +12,7 @@ HEIMDALL_HEADLESS_TESTS= # LDAP Configuration +LDAP_ENABLED= LDAP_HOST= LDAP_PORT= LDAP_BINDDN= diff --git a/apps/backend/src/config/config.service.ts b/apps/backend/src/config/config.service.ts index c9fb637dd6..2fdb30ed11 100644 --- a/apps/backend/src/config/config.service.ts +++ b/apps/backend/src/config/config.service.ts @@ -31,7 +31,10 @@ export class ConfigService { } frontendStartupSettings(): StartupSettingsDto { - return new StartupSettingsDto({banner: this.get('WARNING_BANNER') || ''}); + return new StartupSettingsDto({ + banner: this.get('WARNING_BANNER') || '', + ldap: this.get('LDAP_ENABLED')?.toLocaleLowerCase() == 'true' || false + }); } private parseDatabaseUrl(): boolean { diff --git a/apps/backend/src/config/dto/startup-settings.dto.ts b/apps/backend/src/config/dto/startup-settings.dto.ts index 274c576dbb..07ed3572d6 100644 --- a/apps/backend/src/config/dto/startup-settings.dto.ts +++ b/apps/backend/src/config/dto/startup-settings.dto.ts @@ -2,8 +2,10 @@ import {IStartupSettings} from '@heimdall/interfaces'; export class StartupSettingsDto implements IStartupSettings { readonly banner: string; + readonly ldap: boolean; constructor(settings: IStartupSettings) { this.banner = settings.banner; + this.ldap = settings.ldap; } } diff --git a/apps/frontend/src/store/server.ts b/apps/frontend/src/store/server.ts index 70f3c3d887..71b2b3c2af 100644 --- a/apps/frontend/src/store/server.ts +++ b/apps/frontend/src/store/server.ts @@ -19,6 +19,7 @@ export interface IServerState { loading: boolean; token: string; banner: string; + ldap: boolean; userInfo: IUser; } @@ -30,6 +31,7 @@ export interface IServerState { }) class Server extends VuexModule implements IServerState { banner = ''; + ldap = false; serverUrl = ''; serverMode = false; loading = true; @@ -66,6 +68,7 @@ class Server extends VuexModule implements IServerState { @Mutation SET_STARTUP_SETTINGS(settings: IStartupSettings) { this.banner = settings.banner; + this.ldap = settings.ldap; } @Mutation @@ -146,6 +149,15 @@ class Server extends VuexModule implements IServerState { }); } + @Action({rawError: true}) + public async LoginLDAP(userInfo: {username: string; password: string}) { + return axios.post('/authn/login/ldap', userInfo).then(({data}) => { + this.context.commit('SET_TOKEN', data.accessToken); + this.context.commit('SET_USERID', data.userID); + this.GetUserInfo(); + }); + } + @Action({rawError: true}) public async Register(userInfo: { email: string; diff --git a/apps/frontend/src/views/Login.vue b/apps/frontend/src/views/Login.vue index d6a6760850..f28cec57fb 100644 --- a/apps/frontend/src/views/Login.vue +++ b/apps/frontend/src/views/Login.vue @@ -4,61 +4,126 @@ - + Login to Heimdall Server - - - - - - Login - - - - - -
- - Sign Up - -
-
+ + Heimdall Login + Organization Login + + + + + + + + + Login + + + + + +
+ + Sign Up + +
+
+
+
+ + + + + + + Login + + + + +
@@ -71,14 +136,22 @@ import Vue from 'vue'; import Component from 'vue-class-component'; import UserValidatorMixin from '@/mixins/UserValidatorMixin'; import {required, email} from 'vuelidate/lib/validators'; +import {LocalStorageVal} from '@/utilities/helper_util'; import {ServerModule} from '@/store/server'; import {SnackbarModule} from '@/store/snackbar'; +const login_tab = new LocalStorageVal('login_curr_tab'); + export interface LoginHash { email: string; password: string; } +export interface LDAPLoginHash { + username: string; + password: string; +} + @Component({ mixins: [UserValidatorMixin], validations: { @@ -86,6 +159,9 @@ export interface LoginHash { required, email }, + username: { + required + }, password: { required } @@ -93,9 +169,14 @@ export interface LoginHash { }) export default class Login extends Vue { email: string = ''; + username: string = ''; password: string = ''; + active_tab: string = login_tab.get_default('logintab-standard') mounted() { + if(!ServerModule.ldap){ + document.querySelectorAll('[role="tablist"]')[0].remove() + } this.checkLoggedIn(); } @@ -123,5 +204,29 @@ export default class Login extends Vue { SnackbarModule.notify(error.response.data.message); }); } + + ldapLogin() { + let creds: LDAPLoginHash = { + username: this.username, + password: this.password + }; + ServerModule.LoginLDAP(creds) + .then(() => { + this.$router.push('/'); + SnackbarModule.notify('You have successfully signed in.'); + }) + .catch((error) => { + SnackbarModule.notify(error.response.data.message); + }); + } + + get ldapenabled() { + return ServerModule.ldap + } } + diff --git a/libs/interfaces/config/startup-settings.interface.ts b/libs/interfaces/config/startup-settings.interface.ts index b6ef797d6b..30ae54dd17 100644 --- a/libs/interfaces/config/startup-settings.interface.ts +++ b/libs/interfaces/config/startup-settings.interface.ts @@ -1,3 +1,4 @@ export interface IStartupSettings { readonly banner: string; + readonly ldap: boolean; } From f11288fd70de74fc5f490ba7ad9a444e84194a34 Mon Sep 17 00:00:00 2001 From: Camden Moors Date: Thu, 21 Jan 2021 11:19:48 -0500 Subject: [PATCH 03/34] Undo changes to README --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ce656d1648..9e4bb52aeb 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ To run Heimdall Server, Postgresql must be installed and a user account must exi # Start the Postgres terminal psql postgres - + # Create the user CREATE USER with encrypted password ''; ALTER USER CREATEDB; @@ -180,7 +180,9 @@ Then, the following one-time steps must be performed: cp apps/backend/.env-example apps/backend/.env # Edit /apps/backend/.env to reflect the appropriate configuration for your system - + yarn backend sequelize-cli db:create + yarn backend sequelize-cli db:migrate + yarn backend sequelize-cli db:seed:all Once the above steps are completed it is possible to start heimdall-server using the following command From 1626c99992e7c0e59dc5089d787b093991f94014 Mon Sep 17 00:00:00 2001 From: Camden Moors Date: Thu, 21 Jan 2021 12:15:37 -0500 Subject: [PATCH 04/34] Remove username requirement (interferes with regular login validation, may not fit everyone) --- apps/frontend/src/views/Login.vue | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/frontend/src/views/Login.vue b/apps/frontend/src/views/Login.vue index f28cec57fb..b8c518d8e9 100644 --- a/apps/frontend/src/views/Login.vue +++ b/apps/frontend/src/views/Login.vue @@ -88,7 +88,6 @@ Date: Thu, 21 Jan 2021 12:44:45 -0500 Subject: [PATCH 05/34] Fix sonarcloud recommendations --- apps/backend/src/authn/authn.service.ts | 2 +- apps/backend/src/authn/ldap.strategy.ts | 4 ++-- apps/backend/src/config/config.service.ts | 2 +- apps/frontend/src/store/server.ts | 19 +++++++++++-------- apps/frontend/src/views/Login.vue | 6 +++--- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/apps/backend/src/authn/authn.service.ts b/apps/backend/src/authn/authn.service.ts index ea9fbff03c..180740c6c6 100644 --- a/apps/backend/src/authn/authn.service.ts +++ b/apps/backend/src/authn/authn.service.ts @@ -11,7 +11,7 @@ import {UsersService} from '../users/users.service'; export class AuthnService { constructor( private usersService: UsersService, - private configService: ConfigService, + private readonly configService: ConfigService, private jwtService: JwtService ) {} diff --git a/apps/backend/src/authn/ldap.strategy.ts b/apps/backend/src/authn/ldap.strategy.ts index a4692ba9a9..428d429bda 100644 --- a/apps/backend/src/authn/ldap.strategy.ts +++ b/apps/backend/src/authn/ldap.strategy.ts @@ -8,8 +8,8 @@ import {AuthnService} from './authn.service'; @Injectable() export class LDAPStrategy extends PassportStrategy(Strategy, 'ldap') { constructor( - private authnService: AuthnService, - private configService: ConfigService + private readonly authnService: AuthnService, + private readonly configService: ConfigService ) { super( { diff --git a/apps/backend/src/config/config.service.ts b/apps/backend/src/config/config.service.ts index 2fdb30ed11..8288408820 100644 --- a/apps/backend/src/config/config.service.ts +++ b/apps/backend/src/config/config.service.ts @@ -33,7 +33,7 @@ export class ConfigService { frontendStartupSettings(): StartupSettingsDto { return new StartupSettingsDto({ banner: this.get('WARNING_BANNER') || '', - ldap: this.get('LDAP_ENABLED')?.toLocaleLowerCase() == 'true' || false + ldap: this.get('LDAP_ENABLED')?.toLocaleLowerCase() === 'true' || false }); } diff --git a/apps/frontend/src/store/server.ts b/apps/frontend/src/store/server.ts index 71b2b3c2af..d15a540178 100644 --- a/apps/frontend/src/store/server.ts +++ b/apps/frontend/src/store/server.ts @@ -140,21 +140,24 @@ class Server extends VuexModule implements IServerState { }); } + @Action + public async handleLogin(data: {userID: string; accessToken: string}) { + this.context.commit('SET_USERID', data.userID); + this.context.commit('SET_TOKEN', data.accessToken); + this.GetUserInfo(); + } + @Action({rawError: true}) public async Login(userInfo: {email: string; password: string}) { - return axios.post('/authn/login', userInfo).then(({data}) => { - this.context.commit('SET_TOKEN', data.accessToken); - this.context.commit('SET_USERID', data.userID); - this.GetUserInfo(); + return axios.post('/authn/login', userInfo).then((response) => { + this.handleLogin(response.data); }); } @Action({rawError: true}) public async LoginLDAP(userInfo: {username: string; password: string}) { - return axios.post('/authn/login/ldap', userInfo).then(({data}) => { - this.context.commit('SET_TOKEN', data.accessToken); - this.context.commit('SET_USERID', data.userID); - this.GetUserInfo(); + return axios.post('/authn/login/ldap', userInfo).then((response) => { + this.handleLogin(response.data); }); } diff --git a/apps/frontend/src/views/Login.vue b/apps/frontend/src/views/Login.vue index b8c518d8e9..0a0919883a 100644 --- a/apps/frontend/src/views/Login.vue +++ b/apps/frontend/src/views/Login.vue @@ -13,7 +13,7 @@ ('login_curr_tab'); +const lastLoginTab = new LocalStorageVal('login_curr_tab'); export interface LoginHash { email: string; @@ -167,7 +167,7 @@ export default class Login extends Vue { email: string = ''; username: string = ''; password: string = ''; - active_tab: string = login_tab.get_default('logintab-standard') + activeTab: string = lastLoginTab.get_default('logintab-standard') mounted() { if(!ServerModule.ldap){ From cb9ff3188a40216951bdec33cbc1c8b716e6ac20 Mon Sep 17 00:00:00 2001 From: Camden Moors Date: Thu, 21 Jan 2021 13:00:14 -0500 Subject: [PATCH 06/34] constify ldap login creds --- apps/frontend/src/views/Login.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/views/Login.vue b/apps/frontend/src/views/Login.vue index 0a0919883a..503515f549 100644 --- a/apps/frontend/src/views/Login.vue +++ b/apps/frontend/src/views/Login.vue @@ -202,7 +202,7 @@ export default class Login extends Vue { } ldapLogin() { - let creds: LDAPLoginHash = { + const creds: LDAPLoginHash = { username: this.username, password: this.password }; From d0794685c30cf1dc268f9fb31ca9efd51e0f05e6 Mon Sep 17 00:00:00 2001 From: Camden Moors Date: Thu, 21 Jan 2021 13:54:24 -0500 Subject: [PATCH 07/34] Split Local Login and LDAPLogin into two components --- .../src/components/global/login/LDAPLogin.vue | 84 +++++++++ .../components/global/login/LocalLogin.vue | 98 ++++++++++ apps/frontend/src/views/Login.vue | 170 ++---------------- 3 files changed, 193 insertions(+), 159 deletions(-) create mode 100644 apps/frontend/src/components/global/login/LDAPLogin.vue create mode 100644 apps/frontend/src/components/global/login/LocalLogin.vue diff --git a/apps/frontend/src/components/global/login/LDAPLogin.vue b/apps/frontend/src/components/global/login/LDAPLogin.vue new file mode 100644 index 0000000000..b7ee1d176d --- /dev/null +++ b/apps/frontend/src/components/global/login/LDAPLogin.vue @@ -0,0 +1,84 @@ + + + diff --git a/apps/frontend/src/components/global/login/LocalLogin.vue b/apps/frontend/src/components/global/login/LocalLogin.vue new file mode 100644 index 0000000000..d71387b48e --- /dev/null +++ b/apps/frontend/src/components/global/login/LocalLogin.vue @@ -0,0 +1,98 @@ + + diff --git a/apps/frontend/src/views/Login.vue b/apps/frontend/src/views/Login.vue index 503515f549..aad81ae1a6 100644 --- a/apps/frontend/src/views/Login.vue +++ b/apps/frontend/src/views/Login.vue @@ -21,106 +21,18 @@ Heimdall Login - Organization Login - - - - - - - Login - - - - - -
- - Sign Up - -
-
-
+
- - - - - - Login - - - +
@@ -134,45 +46,24 @@ import Vue from 'vue'; import Component from 'vue-class-component'; import UserValidatorMixin from '@/mixins/UserValidatorMixin'; -import {required, email} from 'vuelidate/lib/validators'; import {LocalStorageVal} from '@/utilities/helper_util'; import {ServerModule} from '@/store/server'; -import {SnackbarModule} from '@/store/snackbar'; +import LocalLogin from '@/components/global/login/LocalLogin.vue' +import LDAPLogin from '@/components/global/login/LDAPLogin.vue' const lastLoginTab = new LocalStorageVal('login_curr_tab'); -export interface LoginHash { - email: string; - password: string; -} - -export interface LDAPLoginHash { - username: string; - password: string; -} - @Component({ - mixins: [UserValidatorMixin], - validations: { - email: { - required, - email - }, - password: { - required - } + components: { + LocalLogin, + LDAPLogin } }) export default class Login extends Vue { - email: string = ''; username: string = ''; - password: string = ''; activeTab: string = lastLoginTab.get_default('logintab-standard') mounted() { - if(!ServerModule.ldap){ - document.querySelectorAll('[role="tablist"]')[0].remove() - } this.checkLoggedIn(); } @@ -182,47 +73,8 @@ export default class Login extends Vue { } } - signup() { - this.$router.push('/signup'); - } - - login() { - let creds: LoginHash = { - email: this.email, - password: this.password - }; - ServerModule.Login(creds) - .then(() => { - this.$router.push('/'); - SnackbarModule.notify('You have successfully signed in.'); - }) - .catch((error) => { - SnackbarModule.notify(error.response.data.message); - }); - } - - ldapLogin() { - const creds: LDAPLoginHash = { - username: this.username, - password: this.password - }; - ServerModule.LoginLDAP(creds) - .then(() => { - this.$router.push('/'); - SnackbarModule.notify('You have successfully signed in.'); - }) - .catch((error) => { - SnackbarModule.notify(error.response.data.message); - }); - } - get ldapenabled() { return ServerModule.ldap } } - From 3d691450ad9c97236d702b3e83fd8e3e6448d957 Mon Sep 17 00:00:00 2001 From: Camden Moors Date: Thu, 21 Jan 2021 14:02:04 -0500 Subject: [PATCH 08/34] Remove unused UserValidatorMixin --- apps/frontend/src/views/Login.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/frontend/src/views/Login.vue b/apps/frontend/src/views/Login.vue index aad81ae1a6..194e8e3246 100644 --- a/apps/frontend/src/views/Login.vue +++ b/apps/frontend/src/views/Login.vue @@ -45,7 +45,6 @@