Open Closed

Angular: Need customization in Password validation #544


User avatar
0
ashutosh.tanksale created

Check the docs before asking a question: https://docs.abp.io/en/commercial/latest/ Check the samples, to see the basic tasks: https://docs.abp.io/en/commercial/latest/samples/index The exact solution to your question may have been answered before, please use the search on the homepage.

  • ABP Framework version: v3.2.1
  • UI type: Angular
  • Tiered (MVC) or Identity Server Seperated (Angular): yes
  • Exception message and stack trace:
  • Steps to reproduce the issue:

Hi there, I have a special requirement in which i have to compare some custom validation against the default validation for password. Please have a following attached image 'RolesCreation', In which i customized the Role screen and added the custom validation as i want. They are working as expected, values are storing and retreiving without any issue. If you see below "Public" option there are custom validation settings which i added.

Now when i go to user creation and started entering password application showing validation message as per values we set to the default password settings. Please look attached image 'PasswordValidation '

At this screen i want to access the values of settings i created in Roles screen and i want to validation check the values from custom settings instead of default settings. Please help me on this. How and Where can implement this logic?


1 Answer(s)
  • User Avatar
    0
    bunyamin created

    Hello,

    I will present two different ways to achieve what you are trying to do. However, keep in mind that the second option heavily relies on the internal implementation of the UsersComponent which could change in the future and we may introduce some breaking changes for that component. If you choose to follow the second option, it is your responsibility to ensure that your code is working. If there are some breaking changes, again it is your responsibility to edit your code accordingly.

    First Option: Override UsersComponent

    You could implement your own users page so that you can define any logic you'd like. Please refer to the docs for more detail about replacing components.

    The key for UsersComponent is eIdentityComponents.Users

    Second Option: Override Password field in the form

    You can override password field for New user modal.

    Please refer to the docs

    Firstly, let's create a file and call it identity-create-form-prop-contributors.ts and add the following code block in it.

    export function customAsyncPasswordValidator(data: PropData<IdentityUserDto>) {
       // validator will come here
    }
    
    const passwordField = new FormProp<IdentityUserDto>({
      type: ePropType.Password,
      name: 'password',
      displayName: 'AbpIdentity::Password',
      id: 'password',
      autocomplete: 'new-password',
      validators: _ => [Validators.required],
      asyncValidators: customAsyncPasswordValidator,
    });
    
    export function userCreateFormContributor(formProps: FormPropList<IdentityUserDto>) {
      const index = formProps.indexOf('password', (action, name) => action.name === name);
      formProps.dropByIndex(index);
      formProps.addByIndex(passwordField, index);
    }
    
    export const identityCreateFormContributors: IdentityCreateFormPropContributors = {
      [eIdentityComponents.Users]: [userCreateFormContributor],
    };
    

    Up to this point, we haven't done anything hacky yet. You can use this part confidently. It is the trivial way of overriding a field within an extensible form. However, the implementation of customAsyncPasswordValidator will be error-prone and depend on the internals of UsersComponent. It is up to you to decide whether to use the following implementation. It contains some comment about

    export function customAsyncPasswordValidator(data: PropData<IdentityUserDto>) {
      const roleService = data.getInjected(IdentityRoleService);
      const user = data.getInjected(UsersComponent);
    
      /**
       * Retrieve the existing roles and filter them with name `Super Admin`
       */
      const superAdminRoleDetails = roleService.getAllList().pipe(
        map(roles => roles.items.filter(role => role.name === 'Super Admin')),
        shareReplay(), // this exists so that we will only retrieve this list once.
      );
    
      /**
       * If `Super Admin` is not selected, we should keep the default validators.
       * This function calls every validator with `control` to keep the default behavior.
       */
      const defaultValidator = (control: FormControl) =>
        of(
          getPasswordValidators({ get: data.getInjected }).reduce(
            (retVal, currValidator) => ({ ...retVal, ...currValidator(control) }),
            {},
          ),
        );
      const superAdminPasswordValidator = (control: FormControl) =>
        superAdminRoleDetails.pipe(
          map(details => {
            if (details?.length) {
              const rules = details[0];
              // You can write your custom rule here with rules.extraProperties
              return control.value !== '123456'
                ? {
                    wrongPassword: true,
                  }
                : null;
            }
    
            return null;
          }),
        );
    
      const validator = control => {
        /**
         * Find if a role group exists with name `Super Admin`
         */
        const superAdminGroup = user.roleGroups.find(roleGr => roleGr.contains('Super Admin'));
    
        if (superAdminGroup) {
          const isAdminSelected = superAdminGroup.controls['Super Admin'].value;
          return isAdminSelected ? superAdminPasswordValidator(control) : defaultValidator(control);
        }
        return defaultValidator(control);
      };
    
      return [validator];
    }
    

    Finally, we need to add some imports to app.module and app-routing.module

    Import following into app.module

    @NgModule({
        // ...
        imports: [
          // ...
          NgxValidateCoreModule.forRoot({
             blueprints: {
               wrongPassword: 'Please choose 123456',
            },
          })
        ]
    export class AppModule {}
    

    And, update app-routing.module

    {
        path: 'identity',
        loadChildren: () =>
          import('@volo/abp.ng.identity').then(m =>
            m.IdentityModule.forLazy({
              // this line should be added
              createFormPropContributors: identityCreateFormContributors,
            }),
          ),
      },
    
Made with ❤️ on ABP v9.1.0-preview. Updated on November 11, 2024, 11:11