-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/password by role #347
base: main
Are you sure you want to change the base?
Conversation
* main: feat: move event manager options to contructor feat: fix tsdoc type feat: implement event manager on dependent modules feat: add event manager
packages/nestjs-common/src/domain/role/interfaces/role-ownable.interface.ts
Outdated
Show resolved
Hide resolved
packages/nestjs-common/src/domain/user/interfaces/user-roles.interface.ts
Outdated
Show resolved
Hide resolved
packages/nestjs-password/src/interfaces/password-strength-service.interface.ts
Outdated
Show resolved
Hide resolved
Just a couple of high level things before deeper review of the latest commits.
|
import { RoleInterface } from './role.interface'; | ||
|
||
export interface RoleOwnableInterface { | ||
roleId?: ReferenceId; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
roleId property should be required
roleId?: ReferenceId; | |
roleId: ReferenceId; |
import { RoleOwnableInterface } from '../../role/interfaces/role-ownable.interface'; | ||
|
||
export interface PasswordStrengthTransformOptionsInterface { | ||
roles: RoleOwnableInterface[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
roles propert should be optional
roles: RoleOwnableInterface[]; | |
roles?: RoleOwnableInterface[]; |
* Optional password strength requirement. If provided, will validate | ||
* that password meets minimum strength requirements. | ||
*/ | ||
passwordStrength?: PasswordStrengthEnum | null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
undefined is better
passwordStrength?: PasswordStrengthEnum | null; | |
passwordStrength?: PasswordStrengthEnum | undefined; |
* Password Strength Options Interface | ||
*/ | ||
export interface PasswordStrengthOptionsInterface { | ||
passwordStrength?: PasswordStrengthEnum | null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
undefined is better
passwordStrength?: PasswordStrengthEnum | null; | |
passwordStrength?: PasswordStrengthEnum | undefined; |
!this.passwordStrengthService.isStrong(password, { | ||
passwordStrength: options?.passwordStrength, | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we have a try/catch here?
): boolean { | ||
const { passwordStrength } = options || {}; | ||
|
||
// TODO: Should we allow overriding the minimum password strength even if the provided strength is lower than the configured minimum? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did you align with Gabriel on this point?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, what ever user define with the roles should overwrite
passwordStrength || | ||
this.settings?.minPasswordStrength || | ||
PasswordStrengthEnum.None; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
coalesce is safer i think in case strength is explicitly zero? recommend test coverage to be sure.
passwordStrength || | |
this.settings?.minPasswordStrength || | |
PasswordStrengthEnum.None; | |
passwordStrength ?? | |
this.settings?.minPasswordStrength ?? | |
PasswordStrengthEnum.None; |
*/ | ||
getUserRoles( | ||
userDto: UserRolesInterface, | ||
userToUpdateId?: ReferenceId, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think this is redundant, it would always be the id in the UserDto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not always, on create user for example we wont have a userId inside dto, however now that i review maybe we can remove the dto from this function, and keep function only to get roles based on user id, like
then i can verify before i call this function if i actually need it
this.userRoleService.resolvePasswordStrength
* @returns Array of role names, or empty array if no roles found | ||
*/ | ||
getUserRoles( | ||
userDto: UserRolesInterface, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should require the user id on the object?
userDto: UserRolesInterface, | |
userDto: ReferenceIdInterface & UserRolesInterface, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we dont even need that object anymore, it was only being used to get the userRoles of it
*/ | ||
resolvePasswordStrength( | ||
roles?: RoleOwnableInterface[], | ||
): PasswordStrengthEnum | null | undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think null doesn't make sense
): PasswordStrengthEnum | null | undefined; | |
): PasswordStrengthEnum | undefined; |
@@ -45,7 +46,7 @@ export class UserMutateService | |||
} | |||
|
|||
protected async transform<T extends DeepPartial<UserEntityInterface>>( | |||
user: T | (T & PasswordPlainInterface), | |||
user: T | (T & PasswordPlainInterface & UserRolesInterface), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
adding roles interface like this is slightly weird because the only property is optional, we need to review this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need this to pass to getPasswordStrength to be able to get strength based on roles
@Optional() | ||
@Inject(UserRoleService) | ||
private userRoleService?: UserRoleServiceInterface, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be last parameter for better back compat
this.userRoleService.getUserRoles && | ||
this.userRoleService.resolvePasswordStrength |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you need to check that these methods exist, is a weird smell
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
true, i had injected wrong service, thats why lol,
const roles = await this.userRoleService.getUserRoles( | ||
userDto, | ||
userToUpdateId, | ||
); | ||
passwordStrength = await this.userRoleService.resolvePasswordStrength( | ||
roles, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should only be making one call to the service, resolving the roles here is not necessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lets discuss this one
const user: (ReferenceIdInterface<string> & UserRolesInterface) | null = | ||
await this.userLookupService.byId(userToUpdateId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needs a try/catch?
if (userToUpdateId) { | ||
const user: (ReferenceIdInterface<string> & UserRolesInterface) | null = | ||
await this.userLookupService.byId(userToUpdateId); | ||
if (user && user.userRoles) return user.userRoles; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use brackets
} | ||
|
||
// get roles from payload | ||
if (userDto.userRoles && userDto.userRoles.length > 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use brackets
return ( | ||
roles && | ||
this.userSettings.passwordStrength?.passwordStrengthTransform && | ||
this.userSettings.passwordStrength?.passwordStrengthTransform({ roles }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
must have a try/catch on this
export enum UserResource { | ||
'One' = 'user', | ||
'Many' = 'user-list', | ||
} | ||
|
||
export type PasswordStrengthTransform = ( | ||
options: PasswordStrengthTransformOptionsInterface, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
options parameter should be optional
options: PasswordStrengthTransformOptionsInterface, | |
options?: PasswordStrengthTransformOptionsInterface, |
|
||
export type PasswordStrengthTransform = ( | ||
options: PasswordStrengthTransformOptionsInterface, | ||
) => PasswordStrengthEnum | null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
undefined is better than null
) => PasswordStrengthEnum | null; | |
) => PasswordStrengthEnum | undefined; |
|
||
export const defaultPasswordStrengthTransform: PasswordStrengthTransform = ( | ||
options: PasswordStrengthTransformOptionsInterface, | ||
): PasswordStrengthEnum | null => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
undefined is better than null
): PasswordStrengthEnum | null => { | |
): PasswordStrengthEnum | undefined => { |
import { PasswordStrengthTransformOptionsInterface } from '@concepta/nestjs-common'; | ||
|
||
export const defaultPasswordStrengthTransform: PasswordStrengthTransform = ( | ||
options: PasswordStrengthTransformOptionsInterface, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
options parameter should be optional
options: PasswordStrengthTransformOptionsInterface, | |
options?: PasswordStrengthTransformOptionsInterface, |
): PasswordStrengthEnum | null => { | ||
const { roles } = options; | ||
|
||
if (roles.some((role) => role.role?.name === 'admin')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
role param in lambda needs to be typed
if (roles.some((role) => role.role?.name === 'admin')) { | ||
return PasswordStrengthEnum.VeryStrong; | ||
} | ||
if (roles.some((role) => role.role?.name === 'user')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
role param in lambda needs to be typed
Defining password strength based on role