Skip to content

Commit

Permalink
Use hash instead of encryption for storing passwords refs jhipster#234
Browse files Browse the repository at this point in the history
  • Loading branch information
glutengo committed Jun 1, 2021
1 parent 9ecf354 commit 7c91216
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 19 deletions.
1 change: 1 addition & 0 deletions generators/server/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const serverFiles = {
'src/security/decorators/auth-user.decorator.ts',
'src/security/decorators/roles.decorator.ts',
'src/security/index.ts',
'src/security/hash.transformer.ts',
'src/client/header-util.ts',
'src/client/interceptors/logging.interceptor.ts',
'src/service/auth.service.ts',
Expand Down
8 changes: 5 additions & 3 deletions generators/server/templates/server/e2e/user.e2e-spec.ts.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,12 @@ describe('User', () => {

it('/PUT update user', async () => {
testUserDTO.login = 'TestUserUpdate';
const savedUser: UserDTO = await service.save(testUserDTO);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { password: savedPassword, ...savedUser } = await service.save(testUserDTO);
savedUser.firstName = 'Updated Name';

const updatedUser: UserDTO = (
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { password: updatedPassword, ...updatedUser } = (
await request(app.getHttpServer())
.put('/api/admin/users')
.send(savedUser)
Expand All @@ -80,7 +82,7 @@ describe('User', () => {
expect(updatedUser.firstName).toEqual(savedUser.firstName);
<%_ } _%>

await service.delete(savedUser);
await service.delete(savedUser as UserDTO);
});

it('/GET user with a login name', async () => {
Expand Down
2 changes: 2 additions & 0 deletions generators/server/templates/server/package.json.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<%_ } _%>
"@nestjs/swagger": "3.1.0",
"@nestjs/typeorm": "7.1.4",
"bcrypt": "5.0.1",
"class-transformer": "0.3.1",
"class-validator": "0.13.1",
"cloud-config-client": "1.4.2",
Expand Down Expand Up @@ -79,6 +80,7 @@
"typeorm-encrypted": "0.5.6"
},
"devDependencies": {
"@types/bcrypt": "5.0.0",
"@nestjs/testing": "7.5.1",
"@types/express": "4.17.1",
"@types/express-serve-static-core": "4.17.3",
Expand Down
5 changes: 2 additions & 3 deletions generators/server/templates/server/src/config.ts.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export class Config {
'jhipster.security.authentication.jwt.base64-secret' = '';
'jhipster.security.authentication.jwt.token-validity-in-seconds' = 86400;
'jhipster.security.authentication.jwt.token-validity-in-seconds-for-remember-me' = 2592000;
'jhipster.security.authentication.jwt.hash-salt-or-rounds' = 10;
<%_ } else if (authenticationType === 'oauth2') { _%>
'jhipster.security.session.secret' = '';
'jhipster.security.oauth2.client.provider.oidc.issuer-uri' = '';
Expand Down Expand Up @@ -49,9 +50,7 @@ export class Config {
'cloud.config.uri' = 'http://admin:${jhipster.registry.password}@localhost:8761/config';
'cloud.config.name' = '<%= baseName %>';
'cloud.config.profile' = 'prod';
'loud.config.label' = 'master';
'crypto.key' = '3772c1cdbd27c225735d116d1e4c5421a3aec26c919cc7ab457f21a4d16a1821';
'crypto.iv' = '54f3ad979d9262d3a2dd4489531daf34';
'cloud.config.label' = 'master';

constructor(properties) {
this.addAll(properties);
Expand Down
18 changes: 10 additions & 8 deletions generators/server/templates/server/src/domain/user.entity.ts.ejs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Authority } from './authority.entity';
import { Entity, Column <%_ if (databaseType !== 'mongodb') { _%>, ManyToMany, JoinTable <%_ } _%> } from 'typeorm';
import { Entity, Column, BeforeInsert, BeforeUpdate <%_ if (databaseType !== 'mongodb') { _%>, ManyToMany, JoinTable <%_ } _%> } from 'typeorm';
import { BaseEntity } from './base/base.entity';
import * as bcrypt from 'bcrypt';
import { config } from '../config';
import { EncryptionTransformer } from "typeorm-encrypted";

@Entity('nhi_user')
export class User extends BaseEntity {
Expand Down Expand Up @@ -30,12 +30,6 @@ export class User extends BaseEntity {

@Column({
type: "varchar",
transformer: new EncryptionTransformer({
key: config.get('crypto.key'),
algorithm: 'aes-256-cbc',
ivLength: 16,
iv: config.get('crypto.iv')
}),
select: false
})
password: string;
Expand All @@ -47,4 +41,12 @@ export class User extends BaseEntity {
resetKey?: string;
@Column({ nullable: true })
resetDate?: Date;

@BeforeInsert()
@BeforeUpdate()
async hashPassword?(): Promise<void> {
if (this.password) {
this.password = await bcrypt.hash(this.password, config.get('jhipster.security.authentication.jwt.hash-salt-or-rounds'));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ export class SeedUsersRoles1570200490072 implements MigrationInterface {
this.user3.authorities= [adminRole, userRole];
this.user4.authorities= [userRole];
await userRepository.save([this.user1, this.user2, this.user3, this.user4]);
await userRepository.save([this.user1, this.user2, this.user3, this.user4]
.map(u => userRepository.create(u)));
<%_ } _%>
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ValueTransformer } from 'typeorm';
import * as bcrypt from 'bcrypt';
import { config } from '../config';

export class HashTransformer implements ValueTransformer {
from(value: string): string {
return value;
}

to(value: string): string {
if (value) {
return bcrypt.hashSync(value, config.get('jhipster.security.authentication.jwt.hash-salt-or-rounds'));
} else {
return value;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { AuthorityRepository } from '../repository/authority.repository';
import { UserService } from '../service/user.service';
import { UserDTO } from './dto/user.dto';
import { FindManyOptions } from 'typeorm';
import * as bcrypt from 'bcrypt';

<%_
const userIdType = getPkType(databaseType) === 'Long' ? 'number' : 'string';
Expand All @@ -31,8 +32,9 @@ export class AuthService {
const loginUserName = userLogin.username;
const loginPassword = userLogin.password;
const userFind = await this.userService.findByfields({ where: { login: loginUserName, password: loginPassword } });
if (!userFind) {
const userFind = await this.userService.findByfields({ where: { login: loginUserName }, select: ['id', 'login', 'password', 'activated'] });
const validPassword = !!userFind && await bcrypt.compare(loginPassword, userFind.password);
if (!userFind || !validPassword) {
throw new HttpException('Invalid login name or password!', HttpStatus.BAD_REQUEST);
}
Expand Down Expand Up @@ -77,7 +79,7 @@ export class AuthService {
if (!userFind) {
throw new HttpException('Invalid login name!', HttpStatus.BAD_REQUEST);
}
if (userFind.password !== currentClearTextPassword ) {
if (!(await bcrypt.compare(currentClearTextPassword, userFind.password))) {
throw new HttpException('Invalid password!', HttpStatus.BAD_REQUEST);
}
userFind.password = newPassword;
Expand Down

0 comments on commit 7c91216

Please sign in to comment.