From 77b7506854df93ce6751d7d139de0bfa4b3c6e46 Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 11 Sep 2023 10:12:11 +0200 Subject: [PATCH 1/4] Add unit tests --- packages/web-client/src/ocs/capabilities.ts | 1 - .../services/passwordPolicy/passwordPolicy.ts | 13 +++- .../unit/services/passwordPolicy.spec.ts | 65 +++++++++++++++++++ 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 packages/web-pkg/tests/unit/services/passwordPolicy.spec.ts diff --git a/packages/web-client/src/ocs/capabilities.ts b/packages/web-client/src/ocs/capabilities.ts index 410ac89202e..8c43e2d42a7 100644 --- a/packages/web-client/src/ocs/capabilities.ts +++ b/packages/web-client/src/ocs/capabilities.ts @@ -17,7 +17,6 @@ export interface PasswordPolicyCapability { min_upper_case_characters?: number min_digits?: number min_special_characters?: number - special_characters?: string } export interface Capabilities { diff --git a/packages/web-pkg/src/services/passwordPolicy/passwordPolicy.ts b/packages/web-pkg/src/services/passwordPolicy/passwordPolicy.ts index dac86de30c6..0e6c12a4562 100644 --- a/packages/web-pkg/src/services/passwordPolicy/passwordPolicy.ts +++ b/packages/web-pkg/src/services/passwordPolicy/passwordPolicy.ts @@ -36,10 +36,17 @@ export class PasswordPolicyService { } const rules = {} as any + // add default rule + if ( + Object.keys(this.capability).length === 0 || + (Object.keys(this.capability).length === 1 && + Object.keys(this.capability)[0] === 'max_characters') + ) { + rules.mustNotBeEmpty = {} + } + if (this.capability.min_characters) { rules.atLeastCharacters = { minLength: this.capability.min_characters } - } else { - rules.mustNotBeEmpty = {} } if (this.capability.min_upper_case_characters) { @@ -61,7 +68,7 @@ export class PasswordPolicyService { if (this.capability.min_special_characters) { rules.mustContain = { minLength: this.capability.min_special_characters, - characters: this.capability.special_characters + characters: ' "!#\\$%&\'()*+,-./:;<=>?@[\\]^_`{|}~"' } } diff --git a/packages/web-pkg/tests/unit/services/passwordPolicy.spec.ts b/packages/web-pkg/tests/unit/services/passwordPolicy.spec.ts new file mode 100644 index 00000000000..719ed64388c --- /dev/null +++ b/packages/web-pkg/tests/unit/services/passwordPolicy.spec.ts @@ -0,0 +1,65 @@ +import { PasswordPolicyService } from '../../../src/services' +import { createStore, defaultStoreMockOptions } from 'web-test-helpers' +import { Language } from 'vue3-gettext' +import { PasswordPolicyCapability } from 'web-client/src/ocs/capabilities' + +describe('PasswordPolicyService', () => { + describe('policy', () => { + describe('contains the rules according to the capability', () => { + it.each([ + [{} as PasswordPolicyCapability, ['mustNotBeEmpty']], + [{ min_characters: 2 } as PasswordPolicyCapability, ['atLeastCharacters']], + [ + { min_lower_case_characters: 2 } as PasswordPolicyCapability, + ['atLeastLowercaseCharacters'] + ], + [ + { min_upper_case_characters: 2 } as PasswordPolicyCapability, + ['atLeastUppercaseCharacters'] + ], + [{ min_digits: 2 } as PasswordPolicyCapability, ['atLeastDigits']], + [{ min_digits: 2 } as PasswordPolicyCapability, ['atLeastDigits']], + [{ min_special_characters: 2 } as PasswordPolicyCapability, ['mustContain']], + [ + { max_characters: 72 } as PasswordPolicyCapability, + ['mustNotBeEmpty', 'atMostCharacters'] + ], + [ + { + min_characters: 2, + min_lower_case_characters: 2, + min_upper_case_characters: 2, + min_digits: 2, + min_special_characters: 2, + max_characters: 72 + } as PasswordPolicyCapability, + [ + 'atLeastCharacters', + 'atLeastUppercaseCharacters', + 'atLeastLowercaseCharacters', + 'atLeastDigits', + 'mustContain', + 'atMostCharacters' + ] + ] + ])('capability "%s"', (capability: PasswordPolicyCapability, expected: Array) => { + const { passwordPolicyService } = getWrapper(capability) + expect(Object.keys((passwordPolicyService.getPolicy() as any).rules)).toEqual(expected) + }) + }) + }) +}) + +const getWrapper = (capability: PasswordPolicyCapability) => { + const storeOptions = defaultStoreMockOptions + storeOptions.getters.capabilities.mockReturnValue({ + password_policy: capability + }) + const store = createStore(storeOptions) + return { + passwordPolicyService: new PasswordPolicyService({ + store, + language: { current: 'en' } as Language + }) + } +} From 65b632a0b1728fd838d44d65a7e91a52f8e3f769 Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 11 Sep 2023 10:47:17 +0200 Subject: [PATCH 2/4] add more tests --- .../unit/services/passwordPolicy.spec.ts | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/packages/web-pkg/tests/unit/services/passwordPolicy.spec.ts b/packages/web-pkg/tests/unit/services/passwordPolicy.spec.ts index 719ed64388c..84c6f4574a6 100644 --- a/packages/web-pkg/tests/unit/services/passwordPolicy.spec.ts +++ b/packages/web-pkg/tests/unit/services/passwordPolicy.spec.ts @@ -3,7 +3,7 @@ import { createStore, defaultStoreMockOptions } from 'web-test-helpers' import { Language } from 'vue3-gettext' import { PasswordPolicyCapability } from 'web-client/src/ocs/capabilities' -describe('PasswordPolicyService', () => { +describe('passwordPolicy', () => { describe('policy', () => { describe('contains the rules according to the capability', () => { it.each([ @@ -47,6 +47,68 @@ describe('PasswordPolicyService', () => { expect(Object.keys((passwordPolicyService.getPolicy() as any).rules)).toEqual(expected) }) }) + describe('method "check"', () => { + describe('test the password correctly against te defined rules', () => { + it.each([ + [{} as PasswordPolicyCapability, ['', 'o'], [false, true]], + [ + { min_characters: 2 } as PasswordPolicyCapability, + ['', 'o', 'ow', 'ownCloud'], + [false, false, true, true] + ], + [ + { min_lower_case_characters: 2 } as PasswordPolicyCapability, + ['', 'o', 'oWNCLOUD', 'ownCloud'], + [false, false, false, true] + ], + [ + { min_upper_case_characters: 2 } as PasswordPolicyCapability, + ['', 'o', 'ownCloud', 'ownCLoud'], + [false, false, false, true] + ], + [ + { min_digits: 2 } as PasswordPolicyCapability, + ['', '1', 'ownCloud1', 'ownCloud12'], + [false, false, false, true] + ], + [ + { min_special_characters: 2 } as PasswordPolicyCapability, + ['', '!', 'ownCloud!', 'ownCloud!#'], + [false, false, false, true] + ], + [ + { max_characters: 2 } as PasswordPolicyCapability, + ['ownCloud', 'ownC', 'ow', 'o'], + [false, false, true, true] + ], + [ + { + min_characters: 8, + min_lower_case_characters: 2, + min_upper_case_characters: 2, + min_digits: 2, + min_special_characters: 2, + max_characters: 72 + } as PasswordPolicyCapability, + ['öwnCloud', 'öwnCloudää', 'öwnCloudää12', 'öwnCloudäÄ12#!'], + [false, false, false, true] + ] + ])( + 'capability "%s, passwords "%s"', + ( + capability: PasswordPolicyCapability, + passwords: Array, + expected: Array + ) => { + const { passwordPolicyService } = getWrapper(capability) + const policy = passwordPolicyService.getPolicy() + for (let i = 0; i < passwords.length; i++) { + expect((policy as any).check(passwords[i])).toEqual(expected[i]) + } + } + ) + }) + }) }) }) From a77061dcdfd917d5d6b5795a3c87de9b1c1ad618 Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 11 Sep 2023 10:59:39 +0200 Subject: [PATCH 3/4] change capability prop names --- packages/web-client/src/ocs/capabilities.ts | 4 ++-- .../services/passwordPolicy/passwordPolicy.ts | 8 ++++---- .../tests/unit/services/passwordPolicy.spec.ts | 18 +++++++++--------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/web-client/src/ocs/capabilities.ts b/packages/web-client/src/ocs/capabilities.ts index 8c43e2d42a7..4f5fd4680ee 100644 --- a/packages/web-client/src/ocs/capabilities.ts +++ b/packages/web-client/src/ocs/capabilities.ts @@ -13,8 +13,8 @@ export interface AppProviderCapability { export interface PasswordPolicyCapability { min_characters?: number max_characters?: number - min_lower_case_characters?: number - min_upper_case_characters?: number + min_lowercase_characters?: number + min_uppercase_characters?: number min_digits?: number min_special_characters?: number } diff --git a/packages/web-pkg/src/services/passwordPolicy/passwordPolicy.ts b/packages/web-pkg/src/services/passwordPolicy/passwordPolicy.ts index 0e6c12a4562..25c584c1b98 100644 --- a/packages/web-pkg/src/services/passwordPolicy/passwordPolicy.ts +++ b/packages/web-pkg/src/services/passwordPolicy/passwordPolicy.ts @@ -49,15 +49,15 @@ export class PasswordPolicyService { rules.atLeastCharacters = { minLength: this.capability.min_characters } } - if (this.capability.min_upper_case_characters) { + if (this.capability.min_uppercase_characters) { rules.atLeastUppercaseCharacters = { - minLength: this.capability.min_upper_case_characters + minLength: this.capability.min_uppercase_characters } } - if (this.capability.min_lower_case_characters) { + if (this.capability.min_lowercase_characters) { rules.atLeastLowercaseCharacters = { - minLength: this.capability.min_lower_case_characters + minLength: this.capability.min_lowercase_characters } } diff --git a/packages/web-pkg/tests/unit/services/passwordPolicy.spec.ts b/packages/web-pkg/tests/unit/services/passwordPolicy.spec.ts index 84c6f4574a6..efd08270cf6 100644 --- a/packages/web-pkg/tests/unit/services/passwordPolicy.spec.ts +++ b/packages/web-pkg/tests/unit/services/passwordPolicy.spec.ts @@ -3,18 +3,18 @@ import { createStore, defaultStoreMockOptions } from 'web-test-helpers' import { Language } from 'vue3-gettext' import { PasswordPolicyCapability } from 'web-client/src/ocs/capabilities' -describe('passwordPolicy', () => { +describe('PasswordPolicyService', () => { describe('policy', () => { describe('contains the rules according to the capability', () => { it.each([ [{} as PasswordPolicyCapability, ['mustNotBeEmpty']], [{ min_characters: 2 } as PasswordPolicyCapability, ['atLeastCharacters']], [ - { min_lower_case_characters: 2 } as PasswordPolicyCapability, + { min_lowercase_characters: 2 } as PasswordPolicyCapability, ['atLeastLowercaseCharacters'] ], [ - { min_upper_case_characters: 2 } as PasswordPolicyCapability, + { min_uppercase_characters: 2 } as PasswordPolicyCapability, ['atLeastUppercaseCharacters'] ], [{ min_digits: 2 } as PasswordPolicyCapability, ['atLeastDigits']], @@ -27,8 +27,8 @@ describe('passwordPolicy', () => { [ { min_characters: 2, - min_lower_case_characters: 2, - min_upper_case_characters: 2, + min_lowercase_characters: 2, + min_uppercase_characters: 2, min_digits: 2, min_special_characters: 2, max_characters: 72 @@ -57,12 +57,12 @@ describe('passwordPolicy', () => { [false, false, true, true] ], [ - { min_lower_case_characters: 2 } as PasswordPolicyCapability, + { min_lowercase_characters: 2 } as PasswordPolicyCapability, ['', 'o', 'oWNCLOUD', 'ownCloud'], [false, false, false, true] ], [ - { min_upper_case_characters: 2 } as PasswordPolicyCapability, + { min_uppercase_characters: 2 } as PasswordPolicyCapability, ['', 'o', 'ownCloud', 'ownCLoud'], [false, false, false, true] ], @@ -84,8 +84,8 @@ describe('passwordPolicy', () => { [ { min_characters: 8, - min_lower_case_characters: 2, - min_upper_case_characters: 2, + min_lowercase_characters: 2, + min_uppercase_characters: 2, min_digits: 2, min_special_characters: 2, max_characters: 72 From c863a95e404a7f3e0e7a9395ecc65770e97b9b34 Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 11 Sep 2023 11:05:41 +0200 Subject: [PATCH 4/4] Add changelog item --- .../enhancement-add-password-policy-compatibility | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 changelog/unreleased/enhancement-add-password-policy-compatibility diff --git a/changelog/unreleased/enhancement-add-password-policy-compatibility b/changelog/unreleased/enhancement-add-password-policy-compatibility new file mode 100644 index 00000000000..ece0964fe92 --- /dev/null +++ b/changelog/unreleased/enhancement-add-password-policy-compatibility @@ -0,0 +1,9 @@ +Enhancement: Add password policy compatibility + +We consume password policy rules from the server and test public link passwords against those. +Additionally we added a show/hide toggle button to password input field + +https://github.com/owncloud/web/pull/9682 +https://github.com/owncloud/web/pull/9634 +https://github.com/owncloud/web/issues/9638 +https://github.com/owncloud/web/issues/9657