Skip to content

Commit 2543f9d

Browse files
committed
Include the character option statuses even when an empty string is inputted to validatePassword (#7471)
* Include the character option statuses even when an empty string is inputted
1 parent 72e6fce commit 2543f9d

File tree

2 files changed

+99
-16
lines changed

2 files changed

+99
-16
lines changed

packages/auth/src/core/auth/password_policy_impl.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ describe('core/auth/password_policy_impl', () => {
6060
allowedNonAlphanumericCharacters: TEST_ALLOWED_NON_ALPHANUMERIC_CHARS,
6161
schemaVersion: TEST_SCHEMA_VERSION
6262
};
63+
const PASSWORD_POLICY_RESPONSE_REQUIRE_NUMERIC: GetPasswordPolicyResponse = {
64+
customStrengthOptions: {
65+
minPasswordLength: TEST_MIN_PASSWORD_LENGTH,
66+
maxPasswordLength: TEST_MAX_PASSWORD_LENGTH,
67+
containsNumericCharacter: TEST_CONTAINS_NUMERIC
68+
},
69+
allowedNonAlphanumericCharacters: TEST_ALLOWED_NON_ALPHANUMERIC_CHARS,
70+
schemaVersion: TEST_SCHEMA_VERSION
71+
};
6372
const PASSWORD_POLICY_REQUIRE_ALL: PasswordPolicy = {
6473
customStrengthOptions: {
6574
minPasswordLength: TEST_MIN_PASSWORD_LENGTH,
@@ -78,6 +87,7 @@ describe('core/auth/password_policy_impl', () => {
7887
},
7988
allowedNonAlphanumericCharacters: TEST_ALLOWED_NON_ALPHANUMERIC_STRING
8089
};
90+
const TEST_EMPTY_PASSWORD = '';
8191

8292
context('#PasswordPolicyImpl', () => {
8393
it('can construct the password policy from the backend response', () => {
@@ -126,6 +136,9 @@ describe('core/auth/password_policy_impl', () => {
126136
const PASSWORD_POLICY_IMPL_REQUIRE_LENGTH = new PasswordPolicyImpl(
127137
PASSWORD_POLICY_RESPONSE_REQUIRE_LENGTH
128138
);
139+
const PASSWORD_POLICY_IMPL_REQUIRE_NUMERIC = new PasswordPolicyImpl(
140+
PASSWORD_POLICY_RESPONSE_REQUIRE_NUMERIC
141+
);
129142

130143
it('password that is too short is considered invalid', async () => {
131144
const policy = PASSWORD_POLICY_IMPL_REQUIRE_ALL;
@@ -294,6 +307,38 @@ describe('core/auth/password_policy_impl', () => {
294307
expect(status.containsNumericCharacter).to.be.undefined;
295308
expect(status.containsNonAlphanumericCharacter).to.be.undefined;
296309
});
310+
311+
it('should include statuses for requirements included in the policy when the password is an empty string', async () => {
312+
let policy = PASSWORD_POLICY_IMPL_REQUIRE_ALL;
313+
let expectedValidationStatus: PasswordValidationStatus = {
314+
isValid: false,
315+
meetsMinPasswordLength: false,
316+
meetsMaxPasswordLength: true,
317+
containsLowercaseLetter: false,
318+
containsUppercaseLetter: false,
319+
containsNumericCharacter: false,
320+
containsNonAlphanumericCharacter: false,
321+
passwordPolicy: policy
322+
};
323+
324+
let status = policy.validatePassword(TEST_EMPTY_PASSWORD);
325+
expect(status).to.eql(expectedValidationStatus);
326+
327+
policy = PASSWORD_POLICY_IMPL_REQUIRE_NUMERIC;
328+
expectedValidationStatus = {
329+
isValid: false,
330+
meetsMinPasswordLength: false,
331+
meetsMaxPasswordLength: true,
332+
containsNumericCharacter: false,
333+
passwordPolicy: policy
334+
};
335+
336+
status = policy.validatePassword(TEST_EMPTY_PASSWORD);
337+
expect(status).to.eql(expectedValidationStatus);
338+
expect(status.containsLowercaseLetter).to.be.undefined;
339+
expect(status.containsUppercaseLetter).to.be.undefined;
340+
expect(status.containsNonAlphanumericCharacter).to.be.undefined;
341+
});
297342
});
298343
});
299344
});

packages/auth/src/core/auth/password_policy_impl.ts

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -118,25 +118,63 @@ export class PasswordPolicyImpl implements PasswordPolicyInternal {
118118
password: string,
119119
status: PasswordValidationStatusInternal
120120
): void {
121+
// Assign statuses for requirements even if the password is an empty string.
122+
this.updatePasswordCharacterOptionsStasuses(
123+
status,
124+
/* containsLowercaseCharacter= */ false,
125+
/* containsUppercaseCharacter= */ false,
126+
/* containsNumericCharacter= */ false,
127+
/* containsNonAlphanumericCharacter= */ false
128+
);
129+
121130
let passwordChar;
122131
for (let i = 0; i < password.length; i++) {
123132
passwordChar = password.charAt(i);
124-
if (this.customStrengthOptions.containsLowercaseLetter) {
125-
status.containsLowercaseLetter ||=
126-
passwordChar >= 'a' && passwordChar <= 'z';
127-
}
128-
if (this.customStrengthOptions.containsUppercaseLetter) {
129-
status.containsUppercaseLetter ||=
130-
passwordChar >= 'A' && passwordChar <= 'Z';
131-
}
132-
if (this.customStrengthOptions.containsNumericCharacter) {
133-
status.containsNumericCharacter ||=
134-
passwordChar >= '0' && passwordChar <= '9';
135-
}
136-
if (this.customStrengthOptions.containsNonAlphanumericCharacter) {
137-
status.containsNonAlphanumericCharacter ||=
138-
this.allowedNonAlphanumericCharacters.includes(passwordChar);
139-
}
133+
this.updatePasswordCharacterOptionsStasuses(
134+
status,
135+
/* containsLowercaseCharacter= */ passwordChar >= 'a' &&
136+
passwordChar <= 'z',
137+
/* containsUppercaseCharacter= */ passwordChar >= 'A' &&
138+
passwordChar <= 'Z',
139+
/* containsNumericCharacter= */ passwordChar >= '0' &&
140+
passwordChar <= '9',
141+
/* containsNonAlphanumericCharacter= */ this.allowedNonAlphanumericCharacters.includes(
142+
passwordChar
143+
)
144+
);
145+
}
146+
}
147+
148+
/**
149+
* Updates the running validation status with the statuses for the character options.
150+
* Expected to be called each time a character is processed to update each option status
151+
* based on the current character.
152+
*
153+
* @param status Validation status.
154+
* @param containsLowercaseCharacter Whether the character is a lowercase letter.
155+
* @param containsUppercaseCharacter Whether the character is an uppercase letter.
156+
* @param containsNumericCharacter Whether the character is a numeric character.
157+
* @param containsNonAlphanumericCharacter Whether the character is a non-alphanumeric character.
158+
*/
159+
private updatePasswordCharacterOptionsStasuses(
160+
status: PasswordValidationStatusInternal,
161+
containsLowercaseCharacter: boolean,
162+
containsUppercaseCharacter: boolean,
163+
containsNumericCharacter: boolean,
164+
containsNonAlphanumericCharacter: boolean
165+
): void {
166+
if (this.customStrengthOptions.containsLowercaseLetter) {
167+
status.containsLowercaseLetter ||= containsLowercaseCharacter;
168+
}
169+
if (this.customStrengthOptions.containsUppercaseLetter) {
170+
status.containsUppercaseLetter ||= containsUppercaseCharacter;
171+
}
172+
if (this.customStrengthOptions.containsNumericCharacter) {
173+
status.containsNumericCharacter ||= containsNumericCharacter;
174+
}
175+
if (this.customStrengthOptions.containsNonAlphanumericCharacter) {
176+
status.containsNonAlphanumericCharacter ||=
177+
containsNonAlphanumericCharacter;
140178
}
141179
}
142180
}

0 commit comments

Comments
 (0)