superdesk/superdesk-client-core

View on GitHub
scripts/core/directives/PasswordStrengthDirective.ts

Summary

Maintainability
C
1 day
Test Coverage
import {gettext} from 'core/utils';

// config holds the default configuration for the password strength calculator.
var config = {
    MIN_LENGTH: 8,
    ONE_LOWER: /^(?=.*[a-z])/g,
    ONE_UPPER: /^(?=.*[A-Z])/g,
    ONE_NUMBER: /^(?=.*[0-9])/g,
    ONE_OTHER: /^(?=.*[^0-9^a-z^A-Z])/g,
    MIN_STRENGTH: 3,
};

/**
 * @ngdoc directive
 * @module superdesk.core.directives
 * @name sdPasswordStrength
 *
 * @param {Object} ngModel - model that the input is bound to
 *
 * @description Appends a strength indicator to the input that it is
 * added to. Strength is computed in the following way:
 *
 *   - If the length of the password is less than 8, strength will be set
 *     to 'Short'.
 *
 *   - Strength is increased whenever one of the following is found:
 *        - a lower-case letter
 *        - an upper-case letter
 *        - a number
 *        - another character
 */
PasswordStrength.$inject = [];
function PasswordStrength() {
    // styles holds each of the strength labels by index along with the class
    // to be added to the indicator.
    var styles = [
        {txt: gettext('Short'), cls: 'red'},
        {txt: gettext('Weak'), cls: 'red'},
        {txt: gettext('Better'), cls: 'yellow'},
        {txt: gettext('OK'), cls: 'green'},
        {txt: gettext('Strong'), cls: 'green'},
    ];

    // helpText holds the text that will be shown when the user hovers over the
    // informational icon.
    var helpText = gettext(
        'Must be {{MIN_LENGTH}} characters long and contain {{MIN_STRENGTH}} out of 4 of the following:',
        {MIN_LENGTH: config.MIN_LENGTH, MIN_STRENGTH: config.MIN_STRENGTH},
    ) +
        '<ul>' +
            '<li>' + gettext('a lower case letter (a-z)') + '</li>' +
            '<li>' + gettext('an upper case letter (A-Z)') + '</li>' +
            '<li>' + gettext('a number (0-9)') + '</li>' +
            '<li>' + gettext('a special character (!@#$%^&...)') + '</li>' +
        '</ul>';

    return {
        require: 'ngModel',
        scope: {
            password: '=ngModel',
        },
        link: function($scope, el, attr, ngModel) {
            var indicator = angular.element(
                '<div class="password-strength">' +
                    gettext('Strength') + ': <span class="label"></span>' +
                    '<div class="icon-question-sign"></div>' +
                '</div>',
            );

            indicator.find('.icon-question-sign').tooltip({
                title: helpText,
                html: true,
            });

            ngModel.$error.weakPass = gettext('Password is too weak.');

            /*
             * @description updateStrength updates the indicator's state to
             * reflect the strength of the given password.
             * @param {string} pass the password to compute the strength of
             */
            var updateStrength = function(pass) {
                var strength = 0;

                if (typeof pass === 'string') {
                    strength = pass.length >= config.MIN_LENGTH ?
                        (config.ONE_LOWER.test(pass) ? 1 : 0) +
                    (config.ONE_UPPER.test(pass) ? 1 : 0) +
                    (config.ONE_NUMBER.test(pass) ? 1 : 0) +
                    (config.ONE_OTHER.test(pass) ? 1 : 0) : 0;
                }

                indicator.find('.label')
                    .text(styles[strength].txt)
                    .removeClass('red yellow green')
                    .addClass(styles[strength].cls);

                ngModel.$setValidity('weakPass', strength >= config.MIN_STRENGTH);
            };

            updateStrength(ngModel.$modelValue || '');
            $scope.$watch('password', updateStrength);
            indicator.insertAfter(el);
        },
    };
}

export default angular
    .module('superdesk.core.directives.passwordStrength', [])
    .directive('sdPasswordStrength', PasswordStrength);