diff --git a/package-lock.json b/package-lock.json index 56e4ccb..e297152 100644 --- a/package-lock.json +++ b/package-lock.json @@ -159,6 +159,16 @@ } } }, + "@oslab/atob": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@oslab/atob/-/atob-0.1.0.tgz", + "integrity": "sha512-WCyYRySdkc2OWNdsx5s7wPzUFdbOe0ivSWtkDbQE/klj4gCCcVPyjsFB9V9rarTAP9zc1pxC2Y3ACOGudqKw3g==" + }, + "@oslab/btoa": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@oslab/btoa/-/btoa-0.1.0.tgz", + "integrity": "sha512-6f5wwOkOuka5brUjKi5NUENvBp0LesYNNhBBugbVxZcqAgLTzea+6L1dl3ZKIhDE0fi86AuqWDtkb93+fLlSsg==" + }, "abab": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", diff --git a/package.json b/package.json index ebb47c5..d1b3c56 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ ] }, "dependencies": { + "@oslab/btoa": "^0.1.0", + "@oslab/atob": "^0.1.0", "awesomplete": "^1.1.2", "axios": "^0.18.0", "bootstrap": "^4.0.0", diff --git a/src/services/url-parser.js b/src/services/url-parser.js index a253ed6..bbe815e 100644 --- a/src/services/url-parser.js +++ b/src/services/url-parser.js @@ -1,5 +1,7 @@ "use strict"; +import atob from "@oslab/atob"; + export function cleanUrl(url) { if (!url) { return ""; @@ -49,7 +51,7 @@ export function getSite() { }); } -export function getPasswordFromUrlQuery(query) { +function passwordProfileFromRawQuery(query) { const password = {}; ["uppercase", "lowercase", "numbers", "symbols"].forEach(booleanishQuery => { if (booleanishQuery in query) { @@ -70,3 +72,16 @@ export function getPasswordFromUrlQuery(query) { }); return password; } + +function decodeBase64PasswordProfile(b64) { + return JSON.parse(atob(b64)); +} + +export function getPasswordFromUrlQuery(queryParameters) { + if ("passwordProfileEncoded" in queryParameters) { + return decodeBase64PasswordProfile( + queryParameters["passwordProfileEncoded"] + ); + } + return passwordProfileFromRawQuery(queryParameters); +} diff --git a/src/store/actions.js b/src/store/actions.js index 79b5e33..f84b627 100644 --- a/src/store/actions.js +++ b/src/store/actions.js @@ -21,8 +21,8 @@ export const loadPasswordProfile = ({ commit }, { site }) => { }; export const getPasswordFromUrlQuery = ({ commit }, { query }) => { - if (Object.keys(query).length >= 9) { - const password = urlParser.getPasswordFromUrlQuery(query); + const password = urlParser.getPasswordFromUrlQuery(query); + if (Object.keys(password).length === 9) { commit(types.SET_PASSWORD, { password }); } }; diff --git a/src/store/getters.js b/src/store/getters.js index e723b02..7e867ee 100644 --- a/src/store/getters.js +++ b/src/store/getters.js @@ -1,3 +1,4 @@ +import btoa from "@oslab/btoa"; import { defaultOptions } from "./defaultPassword"; export const isAuthenticated = state => state.authenticated; @@ -5,11 +6,9 @@ export const isAuthenticated = state => state.authenticated; export const isGuest = state => !state.authenticated; export const passwordURL = state => { - return `${state.baseURL}/#/?login=${state.password.login}&site=${state - .password.site}&uppercase=${state.password.uppercase}&lowercase=${state - .password.lowercase}&numbers=${state.password.numbers}&symbols=${state - .password.symbols}&length=${state.password.length}&counter=${state.password - .counter}&version=${state.password.version}`; + const base64PasswordProfile = btoa(JSON.stringify(state.password)); + const encodedPasswordProfile = encodeURIComponent(base64PasswordProfile); + return `${state.baseURL}/#/?passwordProfileEncoded=${encodedPasswordProfile}`; }; export const isDefaultProfile = state => { diff --git a/test/unit/store.getters.js b/test/unit/store.getters.js index 3af7707..27ab8a5 100644 --- a/test/unit/store.getters.js +++ b/test/unit/store.getters.js @@ -19,7 +19,21 @@ test("passwordURL", t => { t.is( getters.passwordURL(state), - "https://lesspass.com/#/?login=test@example.org&site=example.org&uppercase=true&lowercase=true&numbers=true&symbols=false&length=16&counter=1&version=2" + "https://lesspass.com/#/?passwordProfileEncoded=eyJsb2dpbiI6InRlc3RAZXhhbXBsZS5vcmciLCJzaXRlIjoiZXhhbXBsZS5vcmciLCJ1cHBlcmNhc2UiOnRydWUsImxvd2VyY2FzZSI6dHJ1ZSwibnVtYmVycyI6dHJ1ZSwic3ltYm9scyI6ZmFsc2UsImxlbmd0aCI6MTYsImNvdW50ZXIiOjEsInZlcnNpb24iOjJ9" + ); +}); + +test("passwordURL encode uri component", t => { + const state = { + password: { + login: "contact@lesspass.com" + }, + baseURL: "https://lesspass.com" + }; + + t.is( + getters.passwordURL(state), + "https://lesspass.com/#/?passwordProfileEncoded=eyJsb2dpbiI6ImNvbnRhY3RAbGVzc3Bhc3MuY29tIn0%3D" ); }); diff --git a/test/unit/url-parser.js b/test/unit/url-parser.js index 31f6173..0880b59 100644 --- a/test/unit/url-parser.js +++ b/test/unit/url-parser.js @@ -96,6 +96,25 @@ test("getPasswordFromUrlQuery", t => { t.deepEqual(urlParser.getPasswordFromUrlQuery(query), expectedPassword); }); +test("getPasswordFromUrlQuery with base 64 encoded password profile", t => { + const query = { + passwordProfileEncoded: + "eyJsb2dpbiI6InRlc3RAZXhhbXBsZS5vcmciLCJzaXRlIjoiZXhhbXBsZS5vcmciLCJ1cHBlcmNhc2UiOnRydWUsImxvd2VyY2FzZSI6dHJ1ZSwibnVtYmVycyI6dHJ1ZSwic3ltYm9scyI6ZmFsc2UsImxlbmd0aCI6MTYsImNvdW50ZXIiOjEsInZlcnNpb24iOjJ9" + }; + const expectedPassword = { + login: "test@example.org", + site: "example.org", + uppercase: true, + lowercase: true, + numbers: true, + symbols: false, + length: 16, + counter: 1, + version: 2 + }; + t.deepEqual(urlParser.getPasswordFromUrlQuery(query), expectedPassword); +}); + test("getPasswordFromUrlQuery booleanish", t => { const query = { uppercase: "true",