Skip to content

Commit

Permalink
Merge pull request #143 from conceptadev/feature/password-refactor
Browse files Browse the repository at this point in the history
feat(password): more password module improvements
  • Loading branch information
MrMaz authored Dec 7, 2023
2 parents 5e9b78e + 378df55 commit 7c1f996
Show file tree
Hide file tree
Showing 25 changed files with 457 additions and 157 deletions.
1 change: 1 addition & 0 deletions packages/nestjs-auth-github/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"devDependencies": {
"@concepta/nestjs-crud": "^4.0.0-alpha.35",
"@concepta/nestjs-jwt": "^4.0.0-alpha.35",
"@concepta/nestjs-password": "^4.0.0-alpha.35",
"@concepta/nestjs-typeorm-ext": "^4.0.0-alpha.35",
"@concepta/nestjs-user": "^4.0.0-alpha.35",
"@nestjs/testing": "^9.0.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/nestjs-auth-github/src/auth-github.module.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
UserLookupService,
UserMutateService,
} from '@concepta/nestjs-user';
import { PasswordModule } from '@concepta/nestjs-password';
import { FederatedModule } from '@concepta/nestjs-federated';
import { AuthGithubController } from './auth-github.controller';
import { AuthGithubModule } from './auth-github.module';
Expand Down Expand Up @@ -44,6 +45,7 @@ describe(AuthGithubModule, () => {
},
}),
CrudModule.forRoot({}),
PasswordModule.forRoot({}),
UserModule.forRoot({
entities: {
user: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ export class AuthLocalValidateUserService
}

// validate password
const isValid = await this.passwordValidationService.validateObject({
passwordPlain: dto.password,
object: user,
});
const isValid = await this.passwordValidationService.validateObject(
dto.password,
user,
);

// password is valid?
if (!isValid) {
Expand Down
1 change: 1 addition & 0 deletions packages/nestjs-auth-recovery/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@concepta/nestjs-crud": "^4.0.0-alpha.35",
"@concepta/nestjs-email": "^4.0.0-alpha.35",
"@concepta/nestjs-otp": "^4.0.0-alpha.35",
"@concepta/nestjs-password": "^4.0.0-alpha.35",
"@concepta/nestjs-typeorm-ext": "^4.0.0-alpha.35",
"@concepta/nestjs-user": "^4.0.0-alpha.35",
"@concepta/typeorm-seeding": "^4.0.0-beta.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { UserEntityFixture } from './user/entities/user-entity.fixture';

import { default as ormConfig } from './ormconfig.fixture';
import { MailerServiceFixture } from './email/mailer.service.fixture';
import { PasswordModule } from '@concepta/nestjs-password';

@Module({
imports: [
Expand All @@ -42,6 +43,7 @@ import { MailerServiceFixture } from './email/mailer.service.fixture';
},
},
}),
PasswordModule.forRoot({}),
UserModule.forRoot({
entities: {
user: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import { default as ormConfig } from './ormconfig.fixture';
@Module({
imports: [
EventModule.forRoot({}),
PasswordModule.forRoot({}),
TypeOrmExtModule.forRoot(ormConfig),
CrudModule.forRoot({}),
MailerModule.forRoot({ transport: { host: '' } }),
Expand Down Expand Up @@ -58,6 +57,7 @@ import { default as ormConfig } from './ormconfig.fixture';
},
},
}),
PasswordModule.forRoot({}),
UserModule.forRoot({
settings: {
invitationRequestEvent: InvitationAcceptedEventAsync,
Expand Down
1 change: 1 addition & 0 deletions packages/nestjs-org/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
},
"devDependencies": {
"@concepta/nestjs-invitation": "^4.0.0-alpha.35",
"@concepta/nestjs-password": "^4.0.0-alpha.35",
"@concepta/nestjs-user": "^4.0.0-alpha.35",
"@concepta/typeorm-seeding": "^4.0.0-beta.0",
"@faker-js/faker": "^6.0.0-alpha.6",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { InvitationEntityFixture } from '../__fixtures__/invitation.entity.fixtu
import { OrgFactory } from '../seeding/org.factory';
import { OrgEntityInterface } from '../interfaces/org-entity.interface';
import { OwnerFactoryFixture } from '../__fixtures__/owner-factory.fixture';
import { PasswordModule } from '@concepta/nestjs-password';

describe(InvitationAcceptedListener, () => {
const category = INVITATION_MODULE_CATEGORY_ORG_KEY;
Expand Down Expand Up @@ -51,6 +52,7 @@ describe(InvitationAcceptedListener, () => {
InvitationEntityFixture,
],
}),
PasswordModule.forRoot({}),
UserModule.forRoot({
entities: {
user: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe('password configuration', () => {

expect(config).toMatchObject({
maxPasswordAttempts: 3,
minPasswordStrength: 8,
minPasswordStrength: 0,
});
});

Expand All @@ -39,7 +39,7 @@ describe('password configuration', () => {
const config = await passwordDefaultConfig();

expect(config.maxPasswordAttempts).toBe(3);
expect(config.minPasswordStrength).toBe(8);
expect(config.minPasswordStrength).toBe(0);
});

it('configProcessNotNull', async () => {
Expand Down Expand Up @@ -92,7 +92,7 @@ describe('password configuration', () => {

expect(config).toMatchObject({
maxPasswordAttempts: 3,
minPasswordStrength: 8,
minPasswordStrength: 0,
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export const passwordDefaultConfig = registerAs(

minPasswordStrength: process.env.PASSWORD_MIN_PASSWORD_STRENGTH
? Number.parseInt(process.env.PASSWORD_MIN_PASSWORD_STRENGTH)
: 8,
: process.env?.NODE_ENV === 'production'
? 8
: 0,
}),
);
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
import { PasswordPlainInterface } from '@concepta/ts-common';
import { PasswordStorageInterface } from './password-storage.interface';

/**
* Password Creation Service Interface
*/
export interface PasswordCreationServiceInterface {
/**
* Check if password is strong
* @param password
* @returns
* Create password for an object (optionally).
*
* @param object An object containing the new password to hash.
* @param options.salt Optional salt. If not provided, one will be generated.
* @param options.required Set to true if password is required.
* @param options.currentPassword Optional current password object to validate.
* @returns A new object with the password hashed, with salt added.
*/
isStrong(password: string): boolean;
createObject<T extends PasswordPlainInterface>(
object: T,
options?: {
salt?: string;
required?: boolean;
currentPassword?: {
password: string;
object: PasswordStorageInterface;
};
},
): Promise<
Omit<T, 'password'> | (Omit<T, 'password'> & PasswordStorageInterface)
>;

/**
* Check if attempt is valid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,30 @@ export interface PasswordStorageServiceInterface {
* Hash a password using a salt, if no
* was passed, then generate one automatically.
*
* @param password Password to be hashed
* @param salt Optional salt. If not provided, one will be generated.
* @param options.password Password to be hashed
* @param options.salt Optional salt. If not provided, one will be generated.
*/
hash(password: string, salt?: string): Promise<PasswordStorageInterface>;
hash(
password: string,
options?: {
salt?: string;
},
): Promise<PasswordStorageInterface>;

/**
* Hash password for an object.
*
* @param object An object containing the new password to hash.
* @param salt Optional salt. If not provided, one will be generated.
* @param options.salt Optional salt. If not provided, one will be generated.
* @param options.required Set to true if password is required.
* @returns A new object with the password hashed, with salt added.
*/
hashObject<T extends PasswordPlainInterface>(
object: T,
salt?: string,
): Promise<Omit<T, 'password'> & PasswordStorageInterface>;

/**
* Hash password for an object if password property exists.
*
* @param object An object containing the new password to hash.
* @param salt Optional salt. If not provided, one will be generated.
* @returns A new object with the password hashed, with salt added.
*/
hashObjectOptional<T extends Partial<PasswordPlainInterface>>(
object: T,
salt?: string,
options?: {
salt?: string;
required?: boolean;
},
): Promise<
Omit<T, 'password'> | (Omit<T, 'password'> & PasswordStorageInterface)
>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ export interface PasswordValidationServiceInterface {
/**
* Validate if password matches and its valid.
*
* @param options.passwordPlain Plain text password
* @param options.password Plain text password
* @param options.passwordHash Password hashed
* @param options.passwordSalt salt to be used on plain password to see it match
*/
validate(options: {
passwordPlain: string;
password: string;
passwordHash: string;
passwordSalt: string;
}): Promise<boolean>;
Expand All @@ -23,8 +23,9 @@ export interface PasswordValidationServiceInterface {
* @param options.passwordPlain Plain text password
* @param options.object The object on which the password and salt are stored
*/
validateObject<T extends PasswordStorageInterface>(options: {
passwordPlain: string;
object: T;
}): Promise<boolean>;
validateObject<T extends PasswordStorageInterface>(
passwordPlain: string,

object: T,
): Promise<boolean>;
}
Loading

0 comments on commit 7c1f996

Please sign in to comment.