From 309a8605e9252e0f3dc3e4ed88f0d8026a09ab71 Mon Sep 17 00:00:00 2001 From: Guillaume Vincent Date: Fri, 2 Apr 2021 20:38:26 +0200 Subject: [PATCH] Load already saved password profile on mount Fixes https://github.com/lesspass/lesspass/issues/611 --- .gitignore | 1 + packages/lesspass-pure/package.json | 2 +- packages/lesspass-pure/src/store/actions.js | 4 +- packages/lesspass-pure/src/store/mutation-types.js | 1 - packages/lesspass-pure/src/store/mutations.js | 46 ++-- packages/lesspass-pure/src/store/mutations.test.js | 305 ++++++++++----------- .../lesspass-pure/src/views/PasswordGenerator.vue | 2 +- 7 files changed, 178 insertions(+), 183 deletions(-) diff --git a/.gitignore b/.gitignore index 17f60fe..98aac7d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ node_modules package-lock.json default.nix .envrc +sandbox/lesspass-passwordrules \ No newline at end of file diff --git a/packages/lesspass-pure/package.json b/packages/lesspass-pure/package.json index bff92a9..09b04a3 100644 --- a/packages/lesspass-pure/package.json +++ b/packages/lesspass-pure/package.json @@ -1,6 +1,6 @@ { "name": "lesspass-pure", - "version": "9.4.2", + "version": "9.4.3", "description": "LessPass web component", "license": "GPL-3.0", "author": "Guillaume Vincent ", diff --git a/packages/lesspass-pure/src/store/actions.js b/packages/lesspass-pure/src/store/actions.js index ad51fe1..3c5022c 100644 --- a/packages/lesspass-pure/src/store/actions.js +++ b/packages/lesspass-pure/src/store/actions.js @@ -3,8 +3,8 @@ import * as urlParser from "../services/url-parser"; import * as types from "./mutation-types"; import defaultPasswordProfile from "./defaultPassword"; -export const loadPasswordProfile = ({ commit }, { site }) => { - commit(types.LOAD_PASSWORD_PROFILE, { site }); +export const setSite = ({ commit }, { site }) => { + commit(types.SET_SITE, { site }); }; export const getPasswordFromUrlQuery = ({ commit }, { query }) => { diff --git a/packages/lesspass-pure/src/store/mutation-types.js b/packages/lesspass-pure/src/store/mutation-types.js index be7c56e..db5fa44 100644 --- a/packages/lesspass-pure/src/store/mutation-types.js +++ b/packages/lesspass-pure/src/store/mutation-types.js @@ -7,6 +7,5 @@ export const SET_PASSWORDS = "SET_PASSWORDS"; export const SET_TOKENS = "SET_TOKENS"; export const RESET_PASSWORD = "RESET_PASSWORD"; export const SET_SITE = "SET_SITE"; -export const LOAD_PASSWORD_PROFILE = "LOAD_PASSWORD_PROFILE"; export const DELETE_PASSWORD = "DELETE_PASSWORD"; export const CLEAN_MESSAGE = "CLEAN_MESSAGE"; diff --git a/packages/lesspass-pure/src/store/mutations.js b/packages/lesspass-pure/src/store/mutations.js index 356913b..97b1fca 100644 --- a/packages/lesspass-pure/src/store/mutations.js +++ b/packages/lesspass-pure/src/store/mutations.js @@ -1,5 +1,21 @@ import * as types from "./mutation-types"; +function loadPasswordProfileMatchingSite(passwordProfiles, site) { + let bestMatch = undefined; + const siteWithoutWWW = site.replace(/^www./g, ""); + for (let i = 0; i < passwordProfiles.length; i++) { + const password = passwordProfiles[i]; + if (site.endsWith(password.site)) { + return password; + } else if (password.site.endsWith(siteWithoutWWW)) { + bestMatch = password; + } + } + if (bestMatch) { + return bestMatch; + } +} + export default { [types.LOGIN](state) { state.isAuthenticated = true; @@ -24,6 +40,17 @@ export default { state.defaultPassword = Object.assign({}, state.defaultPassword, options); }, [types.SET_PASSWORDS](state, { passwords }) { + if (state?.password?.site && !state.password.id) { + const matchingPasswordProfile = loadPasswordProfileMatchingSite( + passwords, + state.password.site + ); + if (matchingPasswordProfile) { + state.password = { + ...matchingPasswordProfile + }; + } + } state.passwords = passwords; }, [types.DELETE_PASSWORD](state, { id }) { @@ -35,23 +62,8 @@ export default { } }, [types.SET_SITE](state, { site }) { - state.password.site = site; - }, - [types.LOAD_PASSWORD_PROFILE](state, { site }) { - if (!site || typeof state.password.id !== "undefined") { - return; - } - state.password = Object.assign({}, state.password, { site }); - const passwords = state.passwords || []; - const siteWithoutWWW = site.replace(/^www./g, ""); - for (let i = 0; i < passwords.length; i++) { - const password = passwords[i]; - if (site.endsWith(password.site)) { - state.password = { ...password }; - break; - } else if (password.site.endsWith(siteWithoutWWW)) { - state.password = { ...password }; - } + if (site && !state?.password?.id) { + state.password.site = site; } }, [types.SET_MESSAGE](state, { message }) { diff --git a/packages/lesspass-pure/src/store/mutations.test.js b/packages/lesspass-pure/src/store/mutations.test.js index f9afff1..c03cfe5 100644 --- a/packages/lesspass-pure/src/store/mutations.test.js +++ b/packages/lesspass-pure/src/store/mutations.test.js @@ -113,206 +113,189 @@ test("DELETE_PASSWORD replace state.password with state.defaultPassword", () => DELETE_PASSWORD(state, { id: "1" }); expect(state.password.length).toBe(16); }); - -test("LOAD_PASSWORD_PROFILE", () => { - const state = { - password: { - login: "", - site: "", - uppercase: true, +describe("SET_PASSWORDS", () => { + const passwords = [ + { + id: "b89fdd8e-8e82-475a-ace4-91bcbd2042dc", + login: "contact@example.org", + site: "subdomaine.example.org", lowercase: true, - numbers: true, + uppercase: true, symbols: true, + numbers: true, + counter: 1, length: 16, + version: 2 + }, + { + id: "7cbadebf-49c8-4136-a579-6ee5beb6de7c", + login: "contact@example.org", + site: "www.example.org", + lowercase: true, + uppercase: false, + symbols: false, + numbers: true, counter: 1, + length: 8, version: 2 }, - passwords: [ - { - id: "b89fdd8e-8e82-475a-ace4-91bcbd2042dc", - login: "contact@example.org", - site: "subdomaine.example.org", - lowercase: true, - uppercase: true, - symbols: true, - numbers: true, - counter: 1, - length: 16, - version: 2 - }, - { - id: "7cbadebf-49c8-4136-a579-6ee5beb6de7c", - login: "contact@example.org", + { + id: "31a50139-4add-4486-b553-5e33b4540640", + login: "contact@example.org", + site: "lesspass.com", + lowercase: true, + uppercase: true, + symbols: true, + numbers: true, + counter: 1, + length: 12, + version: 1 + } + ]; + + test("select good password profile base on site", () => { + const state = { + password: { + login: "", site: "www.example.org", + uppercase: true, lowercase: true, - uppercase: false, - symbols: false, numbers: true, + symbols: true, + length: 16, counter: 1, - length: 8, version: 2 }, - { - id: "31a50139-4add-4486-b553-5e33b4540640", - login: "contact@example.org", - site: "lesspass.com", - lowercase: true, + passwords: [], + defaultPassword: { + login: "", + site: "", uppercase: true, - symbols: true, + lowercase: true, numbers: true, + symbols: true, + length: 16, counter: 1, - length: 12, - version: 1 + version: 2 } - ], - defaultPassword: { - login: "", - site: "", - uppercase: true, - lowercase: true, - numbers: true, - symbols: true, - length: 16, - counter: 1, - version: 2 - }, - lastUse: null - }; - const LOAD_PASSWORD_PROFILE = mutations[types.LOAD_PASSWORD_PROFILE]; - LOAD_PASSWORD_PROFILE(state, { site: "www.example.org" }); - expect(state.password).toEqual(state.passwords[1]); -}); + }; -test("LOAD_PASSWORD_PROFILE do nothing if id not empty", () => { - const state = { - password: { - id: "1", - site: "example.org" - }, - passwords: [] - }; - const LOAD_PASSWORD_PROFILE = mutations[types.LOAD_PASSWORD_PROFILE]; - LOAD_PASSWORD_PROFILE(state, { site: "lesspass.com" }); - expect(state.password.site).toBe("example.org"); -}); + const SET_PASSWORDS = mutations[types.SET_PASSWORDS]; + SET_PASSWORDS(state, { passwords }); + expect(state.password).toEqual(passwords[1]); + }); -test("LOAD_PASSWORD_PROFILE with passwords", () => { - const state = { - password: { - site: "" - }, - passwords: [ - { id: "1", site: "www.example.org" }, - { id: "2", site: "www.google.com" } - ] - }; - const LOAD_PASSWORD_PROFILE = mutations[types.LOAD_PASSWORD_PROFILE]; - LOAD_PASSWORD_PROFILE(state, { site: "www.google.com" }); - expect(state.password.id).toBe("2"); - expect(state.password.site).toBe("www.google.com"); -}); + test("SET_PASSWORDS do nothing if id not empty", () => { + const state = { + password: { + id: "1", + site: "example.org" + }, + passwords: [] + }; + const SET_PASSWORDS = mutations[types.SET_PASSWORDS]; + SET_PASSWORDS(state, { passwords }); + expect(state.password.id).toBe("1"); + }); -test("LOAD_PASSWORD_PROFILE with no site keep password profile", () => { - const state = { - password: { - id: "1", - site: "example.org", - login: "contact@example.org", - length: 8, - version: 2 - }, - passwords: [] - }; - const LOAD_PASSWORD_PROFILE = mutations[types.LOAD_PASSWORD_PROFILE]; - LOAD_PASSWORD_PROFILE(state, { site: "" }); - expect(state.password.id).toBe("1"); - expect(state.password.site).toBe("example.org"); - expect(state.password.login).toBe("contact@example.org"); - expect(state.password.length).toBe(8); - expect(state.password.version).toBe(2); -}); + test("with no site keep password profile", () => { + const state = { + password: defaultPassword, + passwords: [] + }; + const SET_PASSWORDS = mutations[types.SET_PASSWORDS]; + SET_PASSWORDS(state, { passwords }); + expect(state.password).toEqual(defaultPassword); + }); -test("LOAD_PASSWORD_PROFILE no passwords", () => { - const state = { - password: { - site: "" - }, - passwords: [] - }; - const LOAD_PASSWORD_PROFILE = mutations[types.LOAD_PASSWORD_PROFILE]; - LOAD_PASSWORD_PROFILE(state, { site: "account.google.com" }); - expect(state.password.site).toBe("account.google.com"); -}); + test("multiple accounts matching criteria", () => { + const state = { + password: { + site: "example.org" + }, + passwords: [] + }; + const SET_PASSWORDS = mutations[types.SET_PASSWORDS]; + SET_PASSWORDS(state, { passwords }); + expect(state.password.id).toBe("7cbadebf-49c8-4136-a579-6ee5beb6de7c"); + }); -test("LOAD_PASSWORD_PROFILE multiple accounts matching criteria", () => { - const state = { - password: { - site: "" - }, - passwords: [ - { id: "1", site: "www.example.org" }, - { id: "2", site: "www.google.com" }, - { id: "3", site: "account.google.com" } - ] - }; - const LOAD_PASSWORD_PROFILE = mutations[types.LOAD_PASSWORD_PROFILE]; - LOAD_PASSWORD_PROFILE(state, { site: "www.google.com" }); - expect(state.password.id).toBe("2"); - expect(state.password.site).toBe("www.google.com"); + test("ends matching criteria nrt #285", () => { + const state = { + password: { + site: "www.google.com" + }, + passwords: [] + }; + const SET_PASSWORDS = mutations[types.SET_PASSWORDS]; + SET_PASSWORDS(state, { + passwords: [{ id: "1", site: "account.google.com" }] + }); + expect(state.password.id).toBe("1"); + expect(state.password.site).toBe("account.google.com"); + }); + + test("without www", () => { + const state = { + password: { + site: "www.reddit.com" + }, + passwords: [] + }; + const SET_PASSWORDS = mutations[types.SET_PASSWORDS]; + SET_PASSWORDS(state, { passwords: [{ id: "1", site: "reddit.com" }] }); + expect(state.password.id).toBe("1"); + expect(state.password.site).toBe("reddit.com"); + }); + + test("with no matching password profile let password profile intact", () => { + const state = { + password: { + site: "not-in-my-password-profile.org" + }, + passwords: [] + }; + const SET_PASSWORDS = mutations[types.SET_PASSWORDS]; + SET_PASSWORDS(state, { passwords }); + expect(state.password.site).toBe("not-in-my-password-profile.org"); + }); }); -test("LOAD_PASSWORD_PROFILE multiple accounts matching criteria order doesn't matter", () => { +test("SET_SITE default state", () => { const state = { - password: { - site: "" - }, - passwords: [ - { id: "1", site: "www.example.org" }, - { id: "2", site: "account.google.com" }, - { id: "3", site: "www.google.com" } - ] + password: defaultPassword, + passwords: [], + defaultPassword }; - const LOAD_PASSWORD_PROFILE = mutations[types.LOAD_PASSWORD_PROFILE]; - LOAD_PASSWORD_PROFILE(state, { site: "www.google.com" }); - expect(state.password.id).toBe("3"); - expect(state.password.site).toBe("www.google.com"); + const SET_SITE = mutations[types.SET_SITE]; + SET_SITE(state, { site: "www.example.org" }); + expect(state.password.site).toEqual("www.example.org"); }); -test("LOAD_PASSWORD_PROFILE ends matching criteria nrt #285", () => { +test("SET_SITE ignore empty", () => { const state = { password: { - site: "" + "site": "www.example.org" }, - passwords: [{ id: "1", site: "account.google.com" }] + passwords: [], + defaultPassword }; - const LOAD_PASSWORD_PROFILE = mutations[types.LOAD_PASSWORD_PROFILE]; - LOAD_PASSWORD_PROFILE(state, { site: "www.google.com" }); - expect(state.password.id).toBe("1"); - expect(state.password.site).toBe("account.google.com"); + const SET_SITE = mutations[types.SET_SITE]; + SET_SITE(state, { site: "" }); + expect(state.password.site).toEqual("www.example.org"); }); -test("LOAD_PASSWORD_PROFILE without www", () => { +test("SET_SITE ignore if id set", () => { const state = { password: { - site: "" + "id": "1", + "site": "www.example.org" }, - passwords: [{ id: "1", site: "reddit.com" }] - }; - const LOAD_PASSWORD_PROFILE = mutations[types.LOAD_PASSWORD_PROFILE]; - LOAD_PASSWORD_PROFILE(state, { site: "www.reddit.com" }); - expect(state.password.id).toBe("1"); - expect(state.password.site).toBe("reddit.com"); -}); - -test("SET_SITE default state", () => { - const state = { - password: defaultPassword, passwords: [], - defaultPassword: defaultPassword, - lastUse: null + defaultPassword }; const SET_SITE = mutations[types.SET_SITE]; - SET_SITE(state, { site: "www.example.org" }); + SET_SITE(state, { site: "www.lesspass.com" }); expect(state.password.site).toEqual("www.example.org"); }); diff --git a/packages/lesspass-pure/src/views/PasswordGenerator.vue b/packages/lesspass-pure/src/views/PasswordGenerator.vue index aa9ee23..09275d6 100644 --- a/packages/lesspass-pure/src/views/PasswordGenerator.vue +++ b/packages/lesspass-pure/src/views/PasswordGenerator.vue @@ -137,7 +137,7 @@ export default { }, beforeMount() { urlParser.getSite().then(site => { - this.$store.dispatch("loadPasswordProfile", { site }); + this.$store.dispatch("setSite", { site }) }); this.$store.dispatch("getPasswordFromUrlQuery", { query: this.$route.query