Procházet zdrojové kódy

add options inside popup

pull/342/head
Guillaume Vincent před 8 roky
rodič
revize
c5ff0ef0d3
15 změnil soubory, kde provedl 596 přidání a 426 odebrání
  1. +1
    -0
      .gitignore
  2. +273
    -151
      extension/dist/popup.min.js
  3. binární
      extension/icons/icon-128x128.png
  4. binární
      extension/icons/logo.png
  5. +6
    -2
      extension/manifest.json
  6. +0
    -70
      extension/options.html
  7. +0
    -54
      extension/options.js
  8. +0
    -69
      extension/popup.css
  9. +168
    -10
      extension/popup.html
  10. +138
    -65
      extension/popup.js
  11. binární
      extension/vendor/img/settings.png
  12. +9
    -4
      package.json
  13. +1
    -1
      readme.md
  14. binární
      screenshot.png
  15. binární
      tile.png

+ 1
- 0
.gitignore Zobrazit soubor

@@ -1 +1,2 @@
node_modules
build

+ 273
- 151
extension/dist/popup.min.js Zobrazit soubor

@@ -9,25 +9,30 @@ var _urlParser = require('./url-parser');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var emailField = document.querySelector('#login-container-email');
var passwordField = document.querySelector('#login-container-password');
var siteField = document.querySelector('#login-container-site');
function getStore(key, callback) {
chrome.storage.local.get(key, function (result) {
callback(result[key]);
});
}

function getLocalStore(storeName) {
return new Promise(function (resolve, reject) {
chrome.storage.local.get(storeName, function (result) {
if (result === null) {
return reject(storeName + ' not found');
}
resolve(result);
});
function saveStore(key, value, callback) {
var newStore = {};
newStore[key] = value;
chrome.storage.local.set(newStore, function () {
callback(value);
});
}

function updateStore(store) {
return new Promise(function (resolve) {
chrome.storage.local.set(store, function () {
resolve(store);
function initStore(callback) {
getStore('lesspassStore', function (store) {
var defaultStore = Object.assign({
login: '',
counter: 1,
password: { length: 12, settings: ['lowercase', 'uppercase', 'numbers', 'symbols'] }
}, store);

saveStore('lesspassStore', defaultStore, function (store) {
callback(store);
});
});
}
@@ -40,80 +45,149 @@ function getCurrentTab() {
});
}

document.getElementById('login-container-btn').addEventListener('click', function () {
var email = emailField.value;
var site = siteField.value;
var masterPassword = passwordField.value;
function fillForm(data) {
document.getElementById('loginField').value = data.login;
document.getElementById('masterPasswordField').value = '';
document.getElementById('siteField').value = data.site;
document.getElementById('passwordCounter').value = data.counter;

if (!email || !masterPassword || !site) {
return;
var passwordInfo = data.password;
document.getElementById('passwordLength').value = passwordInfo.length;

document.getElementById('lowercase').checked = false;
document.getElementById('uppercase').checked = false;
document.getElementById('numbers').checked = false;
document.getElementById('symbols').checked = false;

for (var i = 0; i < passwordInfo.settings.length; i++) {
document.querySelector('#' + passwordInfo.settings[i]).checked = true;
}
}

var storagePromise = getLocalStore('lesspassStore').then(function (store) {
store.lesspassStore.email = email;
return updateStore(store);
});
function selectGoodField() {
var loginField = document.getElementById('loginField');
var passwordField = document.getElementById('masterPasswordField');
if (loginField.value === '') {
loginField.focus();
} else {
passwordField.focus();
}
}

var hashPromise = _lesspass2.default.createMasterPassword(email, masterPassword);
function displayMessage(message) {
var messageField = document.getElementById('errorMessage');
messageField.replaceChild(document.createTextNode(message));
}

var lesspassPromise = Promise.all([hashPromise, storagePromise]).then(function (values) {
var entry = {
site: site,
password: values[1].lesspassStore.password
};
return _lesspass2.default.createPassword(values[0], entry);
function getData() {
var defaultOptions = {
login: document.getElementById('loginField').value,
counter: document.getElementById('passwordCounter').value,
password: {
length: document.getElementById('passwordLength').value,
settings: []
}
};
var options = ['lowercase', 'uppercase', 'numbers', 'symbols'];

for (var i = 0; i < options.length; i++) {
if (document.getElementById(options[i]).checked) {
defaultOptions.password.settings.push(options[i]);
}
}
return defaultOptions;
}

function getFormData() {
var initData = getData();
initData.masterPassword = document.getElementById('masterPasswordField').value;
initData.site = document.getElementById('siteField').value;
return initData;
}

document.getElementById('saveDefaultOptionButton').addEventListener('click', function () {
var options = getData();

getStore('lesspassStore', function (store) {
var newStore = Object.assign(store, options);

saveStore('lesspassStore', newStore, function () {
displayMessage('(saved)');
});
});
});

var tabPromise = getCurrentTab();
document.getElementById('saveDefaultOptionButton').addEventListener('click', function () {
var options = getData();

getStore('lesspassStore', function (store) {
var newStore = Object.assign(store, options);

Promise.all([lesspassPromise, tabPromise]).then(function (values) {
chrome.tabs.sendMessage(values[1].id, { login: email, password: values[0] });
window.close();
saveStore('lesspassStore', newStore, function () {
displayMessage('(saved)');
});
});
});

function setDomainName(domain) {
siteField.value = domain;
document.getElementById('loginField').addEventListener('blur', generatePassword);
document.getElementById('masterPasswordField').addEventListener('blur', generatePassword);
document.getElementById('siteField').addEventListener('blur', generatePassword);

function generatePassword() {
var data = getFormData();
if (!data.login || !data.masterPassword || !data.site) {
return;
}
return _lesspass2.default.generatePassword(data.login, data.masterPassword, data.site, data).then(function (password) {
document.getElementById('generatedPasswordField').value = password;
return password;
});
}

function displayError(message) {
var messageField = document.getElementById('errorMessage');
messageField.replaceChild(document.createTextNode(message));
}

function setEmail(email) {
emailField.value = email;
function copyPassword() {
var generatedPasswordField = document.getElementById('generatedPasswordField');
generatedPasswordField.disabled = false;
generatedPasswordField.select();
document.execCommand('copy');
generatedPasswordField.disabled = true;
}

function initStore(callback) {
chrome.storage.local.get('lesspassStore', function (result) {
var store = {
password: { length: 12, settings: ['lowercase', 'uppercase', 'numbers', 'symbols'], counter: 1 },
email: ''
};
var lesspassStore = result.lesspassStore;
if (typeof lesspassStore !== 'undefined') {
if ('email' in lesspassStore) {
store.email = lesspassStore.email;
}
if ('password' in lesspassStore) {
store.password = lesspassStore.password;
}
document.getElementById('loginButton').addEventListener('click', function (event) {
event.preventDefault();
var data = getFormData();
if (!data.login || !data.masterPassword || !data.site) {
displayError('login, master password and site are required to generate a password');
return;
}

var tabPromise = getCurrentTab();
var passwordPromise = _lesspass2.default.generatePassword(data.login, data.masterPassword, data.site, data);
Promise.all([passwordPromise, tabPromise]).then(function (values) {
if (chrome.tabs && chrome.tabs.sendMessage) {
chrome.tabs.sendMessage(values[1].id, { login: data.login, password: values[0] });
window.close();
} else {
copyPassword();
}
chrome.storage.local.set({ lesspassStore: store }, function () {
callback(store);
});
});
}
});

chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
if (tabs[0]) {
initStore(function (store) {
setEmail(store.email);
store.site = (0, _urlParser.getDomainName)(tabs[0].url);
fillForm(store);
selectGoodField();
});

var currentTab = tabs[0];
setDomainName((0, _urlParser.getDomainName)(currentTab.url));
passwordField.focus();
}
});

},{"./url-parser":2,"lesspass":95}],2:[function(require,module,exports){
},{"./url-parser":2,"lesspass":96}],2:[function(require,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
@@ -14569,7 +14643,7 @@ module.exports={
"_args": [
[
"elliptic@^6.0.0",
"/home/guillaume/workspace/lesspass/extension/node_modules/browserify-sign"
"/home/guillaume/workspace/lesspass/lesspass/webextension/node_modules/browserify-sign"
]
],
"_from": "elliptic@>=6.0.0 <7.0.0",
@@ -14600,7 +14674,7 @@ module.exports={
"_shasum": "18e46d7306b0951275a2d42063270a14b74ebe99",
"_shrinkwrap": null,
"_spec": "elliptic@^6.0.0",
"_where": "/home/guillaume/workspace/lesspass/extension/node_modules/browserify-sign",
"_where": "/home/guillaume/workspace/lesspass/lesspass/webextension/node_modules/browserify-sign",
"author": {
"email": "fedor@indutny.com",
"name": "Fedor Indutny"
@@ -16319,118 +16393,166 @@ module.exports = Array.isArray || function (arr) {
},{}],95:[function(require,module,exports){
'use strict';

var text = require('./text');
var passwordGenerator = require('./password-generator');
var _crypto = require('crypto');

var _crypto2 = _interopRequireDefault(_crypto);

var _render = require('./render');

var _render2 = _interopRequireDefault(_render);

function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}

module.exports = {
createPassword: createPassword,
createMasterPassword: passwordGenerator.createMasterPassword
createPassword: _createPassword,
generatePassword: _generatePassword,
encryptLogin: _encryptLogin,
deriveHash: _deriveHash
};

function createPassword(masterPassword, entry) {
var hash = passwordGenerator._createHash(masterPassword, entry);
var template = passwordGenerator._getTemplate(entry.password.settings);
return text._encode(hash, template);
function _encryptLogin(login, masterPassword) {
return new Promise(function (resolve, reject) {
if (!login || !masterPassword) {
reject('encryptLogin parameter could not be empty');
}
var iterations = 8192;
var keylen = 32;
_crypto2.default.pbkdf2(masterPassword, login, iterations, keylen, 'sha256', function (error, key) {
if (error) {
reject('error in pbkdf2');
} else {
resolve(key.toString('hex'));
}
});
});
}
},{"./password-generator":96,"./text":97}],96:[function(require,module,exports){
'use strict';

var crypto = require('crypto');
function _deriveHash(hash, site) {
var _ref = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];

module.exports = {
createMasterPassword: createMasterPassword,
_getTemplate: _getTemplate,
_createHash: _createHash
};

function createMasterPassword(email, password) {
return new Promise(function (resolve, reject) {
var iterations = 8192;
var keylen = 32;
crypto.pbkdf2(password, email, iterations, keylen, 'sha256', function (error, key) {
if (error) {
reject('error in pbkdf2');
} else {
resolve(key.toString('hex'));
}
});
});
var _ref$password = _ref.password;
var password = _ref$password === undefined ? { length: 12 } : _ref$password;
var _ref$counter = _ref.counter;
var counter = _ref$counter === undefined ? 1 : _ref$counter;

var salt = site + counter.toString();
var derivedHash = _crypto2.default.createHmac('sha256', hash).update(salt).digest('hex');
return derivedHash.substring(0, password.length);
}

function _getTemplate() {
var passwordTypes = arguments.length <= 0 || arguments[0] === undefined ? ['strong'] : arguments[0];

var passwordTypesInfo = {
lowercase: { value: 'vc', order: 1 },
uppercase: { value: 'VC', order: 2 },
numbers: { value: 'n', order: 3 },
symbols: { value: 's', order: 4 },
strong: { value: 'Cvcvns', order: 5 }
};
return passwordTypes.map(function (passwordType) {
return passwordTypesInfo[passwordType];
}).sort(function (passwordType1, passwordType2) {
return passwordType1.order > passwordType2.order;
}).map(function (passwordType) {
return passwordType.value;
}).join('');
function _createPassword(hash, entry) {
// createPassword is deprecated use generatePassword instead
var options = {
counter: entry.password.counter || 1,
password: entry.password
};
var site = entry.site;
var derivedHash = _deriveHash(hash, site, options);
var template = _render2.default.getTemplate(options.password.settings);
return _render2.default.prettyPrint(derivedHash, template);
}

function _createHash(masterPassword, _ref) {
var site = _ref.site;
var _ref$password = _ref.password;
var password = _ref$password === undefined ? { length: 12, counter: 1 } : _ref$password;
function _generatePassword(login, masterPassword, site, options) {
return new Promise(function (resolve, reject) {
if (!login || !masterPassword || !site) {
reject('generatePassword invalid parameter');
}

var salt = site + password.counter.toString();
var hash = crypto.createHmac('sha256', masterPassword).update(salt).digest('hex');
return hash.substring(0, password.length);
_encryptLogin(login, masterPassword).then(function (hash) {
var derivedHash = _deriveHash(hash, site, options);
var template = _render2.default.getTemplate(options.password.settings);
resolve(_render2.default.prettyPrint(derivedHash, template));
});
});
}
},{"crypto":54}],97:[function(require,module,exports){
"use strict";

},{"./render":97,"crypto":54}],96:[function(require,module,exports){
'use strict';

var _encryption = require('./encryption');

var _encryption2 = _interopRequireDefault(_encryption);

function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}

module.exports = {
generatePassword: _encryption2.default.generatePassword,
createPassword: _encryption2.default.createPassword,
createMasterPassword: _encryption2.default.encryptLogin
};

},{"./encryption":95}],97:[function(require,module,exports){
'use strict';

module.exports = {
_getCharType: _getCharType,
_getPasswordChar: _getPasswordChar,
_encode: _encode,
_string2charCodes: _string2charCodes
prettyPrint: _prettyPrint,
getTemplate: _getTemplate,
_getCharType: _getCharType,
_getPasswordChar: _getPasswordChar,
_string2charCodes: _string2charCodes
};

function _getCharType(template, index) {
return template[index % template.length];
return template[index % template.length];
}

function _getPasswordChar(charType, index) {
var passwordsChars = {
V: "AEIOUY",
C: "BCDFGHJKLMNPQRSTVWXZ",
v: "aeiouy",
c: "bcdfghjklmnpqrstvwxz",
A: "AEIOUYBCDFGHJKLMNPQRSTVWXZ",
a: "AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz",
n: "0123456789",
s: "@&%?,=[]_:-+*$#!'^~;()/.",
x: "AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz0123456789@&%?,=[]_:-+*$#!'^~;()/."
};
var passwordChar = passwordsChars[charType];
return passwordChar[index % passwordChar.length];
var passwordsChars = {
V: 'AEIOUY',
C: 'BCDFGHJKLMNPQRSTVWXZ',
v: 'aeiouy',
c: 'bcdfghjklmnpqrstvwxz',
A: 'AEIOUYBCDFGHJKLMNPQRSTVWXZ',
a: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz',
n: '0123456789',
s: '@&%?,=[]_:-+*$#!\'^~;()/.',
x: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz0123456789@&%?,=[]_:-+*$#!\'^~;()/.'
};
var passwordChar = passwordsChars[charType];
return passwordChar[index % passwordChar.length];
}

function _encode(hash, template) {
var password = '';
_string2charCodes(hash).map(function (charCode, index) {
var charType = _getCharType(template, index);
password += _getPasswordChar(charType, charCode);
});
return password;
function _prettyPrint(hash, template) {
var password = '';

_string2charCodes(hash).forEach(function (charCode, index) {
var charType = _getCharType(template, index);
password += _getPasswordChar(charType, charCode);
});
return password;
}

function _string2charCodes(text) {
var charCodes = [];
for (var i = 0; i < text.length; i++) {
charCodes.push(text.charCodeAt(i));
}
return charCodes;
var charCodes = [];
for (var i = 0; i < text.length; i++) {
charCodes.push(text.charCodeAt(i));
}
return charCodes;
}

function _getTemplate() {
var passwordTypes = arguments.length <= 0 || arguments[0] === undefined ? ['strong'] : arguments[0];

var passwordTypesInfo = {
lowercase: { value: 'vc', order: 1 },
uppercase: { value: 'VC', order: 2 },
numbers: { value: 'n', order: 3 },
symbols: { value: 's', order: 4 },
strong: { value: 'Cvcvns', order: 5 }
};
return passwordTypes.map(function (passwordType) {
return passwordTypesInfo[passwordType];
}).sort(function (passwordType1, passwordType2) {
return passwordType1.order > passwordType2.order;
}).map(function (passwordType) {
return passwordType.value;
}).join('');
}

},{}],98:[function(require,module,exports){
var bn = require('bn.js');
var brorand = require('brorand');


binární
extension/icons/icon-128x128.png Zobrazit soubor

Před Za
Šířka: 128  |  Výška: 128  |  Velikost: 4.7 KiB

binární
extension/icons/logo.png Zobrazit soubor

Před Za
Šířka: 356  |  Výška: 80  |  Velikost: 9.1 KiB

+ 6
- 2
extension/manifest.json Zobrazit soubor

@@ -2,7 +2,7 @@
"description": "chrome and firefox web extension for lesspass password manager",
"manifest_version": 2,
"name": "lesspass",
"version": "1.0.0",
"version": "0.0.1",
"homepage_url": "https://github.com/lesspass/webextension",
"icons": {
"64": "icons/logo-64.png"
@@ -23,5 +23,9 @@
"default_title": "LessPass",
"default_popup": "popup.html"
},
"options_page": "options.html"
"applications": {
"gecko": {
"id": "contact@lesspass.com"
}
}
}

+ 0
- 70
extension/options.html Zobrazit soubor

@@ -1,70 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="vendor/css/bootstrap.min.css"/>
</head>
<body>

<div class="container m-t-1">
<div class="row">
<div class="col-sm-6 offset-sm-3">
<form>
<legend>LessPass options <span id="message" style="color: green;"></span></legend>

<fieldset class="form-group">
<label for="passwordLength">Password Length</label>
<input class="form-control" placeholder="Password length" id="passwordLength" type="number"
value="12" min="6" max="64">
<small class="text-muted">default password length</small>
</fieldset>
<fieldset class="form-group">
<label for="passwordCounter">Counter</label>
<input class="form-control" id="passwordCounter" type="number" value="1" min="1">
<small class="text-muted">counter to change password value</small>
</fieldset>
<label>Password Options</label>
<div class="row">
<div class="col-lg-5">
<label class="c-input c-checkbox">
<input type="checkbox" id="lowercase" value="lowercase" checked>
<span class="c-indicator"></span>
lowercase
</label>
</div>
<div class="col-lg-7">
<label class="c-input c-checkbox">
<input type="checkbox" id="uppercase" value="uppercase" checked>
<span class="c-indicator"></span>
uppercase
</label>
</div>
</div>
<div class="row">
<div class="col-lg-5">
<label class="c-input c-checkbox">
<input type="checkbox" id="numbers" value="numbers"
checked>
<span class="c-indicator"></span>
numbers
</label>
</div>
<div class="col-lg-7">
<label class="c-input c-checkbox">
<input type="checkbox" id="symbols" value="symbols"
checked>
<span class="c-indicator"></span>
symbols
</label>
</div>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
</div>
</div>
</div>

<script src="options.js"></script>
</body>
</html>


+ 0
- 54
extension/options.js Zobrazit soubor

@@ -1,54 +0,0 @@
function displayMessage(message) {
document.getElementById('message').innerHTML = message;
setTimeout(() => {
document.getElementById('message').innerHTML = '';
}, 3000);
}

function saveOptions(e) {
e.preventDefault();
const defaultOptions = {
password: {
counter: document.querySelector('#passwordCounter').value,
length: document.querySelector('#passwordLength').value,
settings: []
}
};
const options = ['lowercase', 'uppercase', 'numbers', 'symbols'];

for (let i = 0; i < options.length; i++) {
if (document.querySelector(`#${options[i]}`).checked) {
defaultOptions.password.settings.push(options[i]);
}
}

chrome.storage.local.set({
lesspassStore: {
options: defaultOptions
}
});
displayMessage('(saved)');
}

function restoreOptions() {
chrome.storage.local.get('lesspassStore', value => {
if (value && 'options' in value.lesspassStore) {
const passwordInfo = value.lesspassStore.options.password;
document.querySelector('#passwordCounter').value = passwordInfo.counter;
document.querySelector('#passwordLength').value = passwordInfo.length;

document.querySelector('#lowercase').checked = false;
document.querySelector('#uppercase').checked = false;
document.querySelector('#numbers').checked = false;
document.querySelector('#symbols').checked = false;

for (let i = 0; i < passwordInfo.settings.length; i++) {
document.querySelector(`#${passwordInfo.settings[i]}`).checked = true;
}
}
});
}

document.addEventListener('DOMContentLoaded', restoreOptions);

document.querySelector('form').addEventListener('submit', saveOptions);

+ 0
- 69
extension/popup.css Zobrazit soubor

@@ -1,69 +0,0 @@
html, body {
width: 400px;
margin: 0;
}

body {
font-family: sans-serif;
}

.login {
width: 400px;
margin: 0 auto;
font-size: 16px;
}

.login-header,
.login p {
margin-top: 0;
margin-bottom: 0;
}

.login-header {
padding: 20px 20px 10px;
font-size: 1.4em;
font-weight: normal;
text-align: center;
text-transform: uppercase;
color: #0275d8;
}

.login-container {
padding: 12px;
}

.login p {
padding: 12px;
}

.login input {
box-sizing: border-box;
display: block;
width: 100%;
border: 1px solid;
padding: 16px;
outline: 0;
font-family: inherit;
font-size: 0.95em;
}

.login input {
background: #fff;
border-color: #bbb;
color: #555;
}

.login input {
border-color: #888;
}

.login input[type="submit"] {
background: #0275d8;
border-color: transparent;
color: #fff;
cursor: pointer;
}

.login input[type="submit"]:focus {
border-color: #05a;
}

+ 168
- 10
extension/popup.html Zobrazit soubor

@@ -2,18 +2,176 @@
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="popup.css"/>
<link rel="stylesheet" href="vendor/css/bootstrap.min.css"/>
<style>
body {
min-width: 655px;
padding: 2em;
}

.form-control, .btn, .input-group-addon {
border-radius: 0 !important;
}

#generatedPasswordForm a {
color: inherit;
}
</style>
</head>
<body>
<div class="login">
<h2 class="login-header">LessPass</h2>

<form class="login-container">
<p><input id="login-container-email" type="email" placeholder="Email/Username"></p>
<p><input id="login-container-site" type="text" placeholder="Site"></p>
<p><input id="login-container-password" type="password" placeholder="Master Password"></p>
<p><input id="login-container-btn" type="submit" value="Let's go !"></p>
</form>
<div class="row">
<div class="col-xs-12 m-b-1">
<a href="https://lesspass.com/"><img src="icons/logo.png" alt="lesspass favicon" height="32"></a>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<form id="generatedPasswordForm">
<div class="form-group row">
<div class="col-xs-12">
<span id="errorMessage" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<div class="col-xs-6">
<label for="loginField" class="sr-only">
Login
</label>
<input id="loginField"
class="form-control"
type="text"
placeholder="login"
autofocus
autocomplete="off">

</div>
<div class="col-xs-6">
<label for="masterPasswordField" class="sr-only">
Master Password
</label>
<input id="masterPasswordField"
class="form-control"
type="password"
placeholder="master password"
autocomplete="off">

</div>
</div>
<div class="form-group row">
<div class="col-xs-12">
<label for="siteField" class="sr-only">
Site
</label>
<input id="siteField"
class="form-control"
type="text"
placeholder="site">

</div>
</div>
<div class="form-group row">
<div class="col-xs-12">
<label for="generatedPasswordField" class="sr-only">
Generated password
</label>
<div class="input-group">
<input type="text"
id="generatedPasswordField"
class="form-control"
placeholder="generated password"
disabled>
<span class="input-group-btn">
<button id="loginButton" class="btn btn-primary" type="submit">
login
</button>
</span>
</div>
</div>
</div>
<div class="form-group row">
<div class="col-xs-12">
<img src="vendor/img/settings.png"/> options <span id="message" class="text-success"></span>
</div>
</div>
<div class="form-group row">
<div class="col-xs-5">
<label class="c-input c-checkbox">
<input type="checkbox" id="lowercase" value="lowercase">

<span class="c-indicator"></span>
lowercase
</label>
</div>
<div class="col-xs-7">
<label class="c-input c-checkbox">
<input type="checkbox" id="uppercase" value="uppercase">

<span class="c-indicator"></span>
uppercase
</label>
</div>
</div>
<div class="form-group row">
<div class="col-xs-5">
<label class="c-input c-checkbox">
<input type="checkbox" id="numbers" value="numbers">

<span class="c-indicator"></span>
numbers
</label>
</div>
<div class="col-xs-7">
<label class="c-input c-checkbox">
<input type="checkbox" id="symbols" value="symbols">

<span class="c-indicator"></span>
symbols
</label>
</div>
</div>
<div class="row m-t-1">
<div class="col-xs-5 m-b-1">
<label for="passwordLength" class="sr-only">
length
</label>
<div class="input-group">
<span class="input-group-addon" id="passwordLengthAddon">
length
</span>
<input type="number" class="form-control" id="passwordLength"
aria-describedby="passwordLengthAddon" value="12" min="6" max="64">
</div>
</div>
<div class="col-xs-4 m-b-1">
<label for="passwordCounter" class="sr-only">
counter
</label>
<div class="input-group">
<span class="input-group-addon" id="passwordCounterAddon">
counter
</span>
<input type="number" class="form-control" id="passwordCounter"
aria-describedby="passwordCounterAddon"
value="1" min="1" max="100">
</div>
</div>
</div>
<div class="form-group row">
<div class="col-xs-12">
<button id="saveDefaultOptionButton" class="btn btn-primary" type="button">
save as default
</button>
</div>
</div>
<div class="form-group row">
<div class="col-xs-12">
<small class="text-help">options are stored locally within your browser. options are not
synchronised for now.
</small>
</div>
</div>
</form>
</div>
</div>
<script src="dist/popup.min.js"></script>
</body>


+ 138
- 65
extension/popup.js Zobrazit soubor

@@ -1,25 +1,30 @@
import lesspass from 'lesspass';
import {getDomainName} from './url-parser';

const emailField = document.querySelector('#login-container-email');
const passwordField = document.querySelector('#login-container-password');
const siteField = document.querySelector('#login-container-site');

function getLocalStore(storeName) {
return new Promise((resolve, reject) => {
chrome.storage.local.get(storeName, result => {
if (result === null) {
return reject(`${storeName} not found`);
}
resolve(result);
});
function getStore(key, callback) {
chrome.storage.local.get(key, result => {
callback(result[key]);
});
}

function updateStore(store) {
return new Promise(resolve => {
chrome.storage.local.set(store, () => {
resolve(store);
function saveStore(key, value, callback) {
const newStore = {};
newStore[key] = value;
chrome.storage.local.set(newStore, () => {
callback(value);
});
}

function initStore(callback) {
getStore('lesspassStore', store => {
const defaultStore = Object.assign({
login: '',
counter: 1,
password: {length: 12, settings: ['lowercase', 'uppercase', 'numbers', 'symbols']}
}, store);

saveStore('lesspassStore', defaultStore, store => {
callback(store);
});
});
}
@@ -32,76 +37,144 @@ function getCurrentTab() {
});
}

document.getElementById('login-container-btn').addEventListener('click', () => {
const email = emailField.value;
const site = siteField.value;
const masterPassword = passwordField.value;
function fillForm(data) {
document.getElementById('loginField').value = data.login;
document.getElementById('masterPasswordField').value = '';
document.getElementById('siteField').value = data.site;
document.getElementById('passwordCounter').value = data.counter;

if (!email || !masterPassword || !site) {
return;
const passwordInfo = data.password;
document.getElementById('passwordLength').value = passwordInfo.length;

document.getElementById('lowercase').checked = false;
document.getElementById('uppercase').checked = false;
document.getElementById('numbers').checked = false;
document.getElementById('symbols').checked = false;

for (let i = 0; i < passwordInfo.settings.length; i++) {
document.querySelector(`#${passwordInfo.settings[i]}`).checked = true;
}
}

const storagePromise = getLocalStore('lesspassStore').then(store => {
store.lesspassStore.email = email;
return updateStore(store);
});
function selectGoodField() {
const loginField = document.getElementById('loginField');
const passwordField = document.getElementById('masterPasswordField');
if (loginField.value === '') {
loginField.focus();
} else {
passwordField.focus();
}
}

function displayMessage(message) {
const messageField = document.getElementById('errorMessage');
messageField.replaceChild(document.createTextNode(message));
}

function getData() {
const defaultOptions = {
login: document.getElementById('loginField').value,
counter: document.getElementById('passwordCounter').value,
password: {
length: document.getElementById('passwordLength').value,
settings: []
}
};
const options = ['lowercase', 'uppercase', 'numbers', 'symbols'];

for (let i = 0; i < options.length; i++) {
if (document.getElementById(options[i]).checked) {
defaultOptions.password.settings.push(options[i]);
}
}
return defaultOptions;
}

function getFormData() {
const initData = getData();
initData.masterPassword = document.getElementById('masterPasswordField').value;
initData.site = document.getElementById('siteField').value;
return initData;
}

document.getElementById('saveDefaultOptionButton').addEventListener('click', () => {
const options = getData();

const hashPromise = lesspass.createMasterPassword(email, masterPassword);
getStore('lesspassStore', store => {
const newStore = Object.assign(store, options);

const lesspassPromise = Promise.all([hashPromise, storagePromise])
.then(values => {
const entry = {
site,
password: values[1].lesspassStore.password
};
return lesspass.createPassword(values[0], entry);
saveStore('lesspassStore', newStore, () => {
displayMessage('(saved)');
});
});
});

const tabPromise = getCurrentTab();
document.getElementById('saveDefaultOptionButton').addEventListener('click', () => {
const options = getData();

getStore('lesspassStore', store => {
const newStore = Object.assign(store, options);

Promise.all([lesspassPromise, tabPromise]).then(values => {
chrome.tabs.sendMessage(values[1].id, {login: email, password: values[0]});
window.close();
saveStore('lesspassStore', newStore, () => {
displayMessage('(saved)');
});
});
});

function setDomainName(domain) {
siteField.value = domain;
document.getElementById('loginField').addEventListener('blur', generatePassword);
document.getElementById('masterPasswordField').addEventListener('blur', generatePassword);
document.getElementById('siteField').addEventListener('blur', generatePassword);

function generatePassword() {
const data = getFormData();
if (!data.login || !data.masterPassword || !data.site) {
return;
}
return lesspass.generatePassword(data.login, data.masterPassword, data.site, data).then(password => {
document.getElementById('generatedPasswordField').value = password;
return password;
});
}

function setEmail(email) {
emailField.value = email;
function displayError(message) {
const messageField = document.getElementById('errorMessage');
messageField.replaceChild(document.createTextNode(message));
}

function initStore(callback) {
chrome.storage.local.get('lesspassStore', result => {
const store = {
password: {length: 12, settings: ['lowercase', 'uppercase', 'numbers', 'symbols'], counter: 1},
email: ''
};
const lesspassStore = result.lesspassStore;
if (typeof lesspassStore !== 'undefined') {
if ('email' in lesspassStore) {
store.email = lesspassStore.email;
}
if ('password' in lesspassStore) {
store.password = lesspassStore.password;
}
function copyPassword() {
const generatedPasswordField = document.getElementById('generatedPasswordField');
generatedPasswordField.disabled = false;
generatedPasswordField.select();
document.execCommand('copy');
generatedPasswordField.disabled = true;
}

document.getElementById('loginButton').addEventListener('click', event => {
event.preventDefault();
const data = getFormData();
if (!data.login || !data.masterPassword || !data.site) {
displayError('login, master password and site are required to generate a password');
return;
}

const tabPromise = getCurrentTab();
const passwordPromise = lesspass.generatePassword(data.login, data.masterPassword, data.site, data);
Promise.all([passwordPromise, tabPromise]).then(values => {
if (chrome.tabs && chrome.tabs.sendMessage) {
chrome.tabs.sendMessage(values[1].id, {login: data.login, password: values[0]});
window.close();
} else {
copyPassword();
}
chrome.storage.local.set({lesspassStore: store}, () => {
callback(store);
});
});
}
});

chrome.tabs.query({active: true, currentWindow: true}, tabs => {
if (tabs[0]) {
initStore(store => {
setEmail(store.email);
store.site = getDomainName(tabs[0].url);
fillForm(store);
selectGoodField();
});

const currentTab = tabs[0];
setDomainName(getDomainName(currentTab.url));
passwordField.focus();
}
});

binární
extension/vendor/img/settings.png Zobrazit soubor

Před Za
Šířka: 16  |  Výška: 16  |  Velikost: 382 B

+ 9
- 4
package.json Zobrazit soubor

@@ -1,7 +1,10 @@
{
"scripts": {
"build": "rm -rf extension/dist && mkdir extension/dist && browserify ./extension/content.js -o ./extension/dist/content.min.js && browserify ./extension/popup.js -o ./extension/dist/popup.min.js",
"test": "xo && ava tests --require babel-core/register"
"prebuild": "npm prune && npm install",
"zip": "rm -rf build && mkdir build && cd extension && zip -r extension.zip ./* && cp extension.zip ../build/lesspass.firefox.xpi && mv extension.zip ../build/lesspass.chrome.zip && cd ..",
"browserify": "rm -rf extension/dist && mkdir extension/dist && browserify ./extension/content.js -o ./extension/dist/content.min.js && browserify ./extension/popup.js -o ./extension/dist/popup.min.js",
"build": "npm run browserify && npm run zip",
"test": "ava tests --require babel-core/register && xo"
},
"devDependencies": {
"ava": "*",
@@ -9,6 +12,7 @@
"babel-preset-es2015": "*",
"babelify": "^7.3.0",
"browserify": "^13.0.1",
"jpm": "^1.0.7",
"jsdom": "^9.0.0",
"xo": "*"
},
@@ -17,7 +21,6 @@
"space": true,
"envs": [
"browser",
"jquery",
"webextensions"
],
"ignores": [
@@ -44,7 +47,9 @@
]
},
"dependencies": {
"lesspass": "^1.1.1",
"bootstrap": "^4.0.0-alpha.2",
"jquery": "^2.2.3",
"lesspass": "^2.0.1",
"tldjs": "^1.6.2"
}
}

+ 1
- 1
readme.md Zobrazit soubor

@@ -4,4 +4,4 @@

chrome and firefox web extension for lesspass password manager

[lesspass submodule](https://github.com/lesspass/lesspass)
see [lesspass](https://github.com/lesspass/lesspass) project

binární
screenshot.png Zobrazit soubor

Před Za
Šířka: 640  |  Výška: 400  |  Velikost: 31 KiB

binární
tile.png Zobrazit soubor

Před Za
Šířka: 440  |  Výška: 280  |  Velikost: 21 KiB

Načítá se…
Zrušit
Uložit