Merge branch 'v1.2'

* v1.2: (25 commits)
  Bump version to 1.2.0-beta.3
  Update reset button label to always show ellipsis
  Release v1.2.0-beta.2
  Create application menu on macOS including about page, quit and basic 'Edit' menu; Fixes #60
  Add option to reset app from lock screen
  Send platform as header with each request
  Release v1.2.0-beta.1
  Remove announcements.json (was used for testing)
  Update electron-builder and electron-auto-updater dependencies
  Upgrade to latest electron-builder; Add option to build for linux
  Fix paths in test runner page
  Fix modulo bias in padlock.rand.randomString(); add test to ensure unique strings, even character distribution
  When resetting app, also reset all settings
  Don't save version in settings; reorganise version-related code
  Add option to build electron app for windows
  Add support for electron auto update
  Update cordova build hook to use new build function
  Get app version though various platform apis instead of hardcoding it; Display in settings
  Update manifest file with current version, description and author fields before writing to build dir
  Clean up dependencies
  ...
This commit is contained in:
Martin Kleinschrodt 2017-06-13 09:29:55 +02:00
commit be5041dc72
117 changed files with 641 additions and 308 deletions

View File

@ -1,8 +1,13 @@
{
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": "2015"
},
"env": {
"node": true,
"es6": true,
"browser": true
},
"extends": "eslint:recommended",
"plugins": [
"html"
],

9
.gitignore vendored
View File

@ -1,6 +1,7 @@
bower_components
node_modules
deploy
dist
# files/directories generated by cordova
cordova/platforms
@ -8,10 +9,10 @@ cordova/plugins
cordova/www
# stylus-generated css files
src/components/**/*.css
src/styles/mixins.css
src/styles/config.css
app/src/components/**/*.css
app/src/styles/mixins.css
app/src/styles/config.css
# generated html modules
src/**/*-styles.html
app/src/**/*-styles.html

View File

@ -1,18 +1,14 @@
{
"name": "Padlock",
"version": "1.0.0",
"name": "padlock",
"version": "1.2.0",
"homepage": "http://padlock.io",
"authors": [
"Martin Kleinschrodt <martin@maklesoft.com>"
],
"main": "index.html",
"license": "GPL",
"license": "GPL-3.0",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
"bower_components"
],
"private": true,
"dependencies": {
@ -20,6 +16,7 @@
"polymer": "polymer/polymer#~1.2.1",
"zxcvbn": "~3.1.0",
"autosize": "~3.0.13",
"papaparse": "~4.1.2"
"papaparse": "~4.1.2",
"sjcl": "^1.0.6"
}
}

View File

View File

@ -3,19 +3,39 @@
<head>
<title>Padlock</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<link rel="shortcut icon" type="image/png" href="assets/icon16.png"/>
<!-- Fonts -->
<link href="assets/fonts/fonts.css" rel="stylesheet" type="text/css">
<script>
/* global require, electron */
(function() {
"use strict";
if (typeof require !== "undefined") {
delete window.module;
window.electron = require("electron");
electron.ipcRenderer.on("auto-update", function(_, type) {
const evt = new CustomEvent(type, { detail: Array.prototype.slice.call(arguments, 3) });
window.dispatchEvent(evt);
});
}
})();
</script>
<!-- Load blue background right from the start to avoid white 'flash' -->
<style>
html { background: #59c6ff; }
</style>
<!-- Fonts -->
<link href="src/styles/fonts.css" rel="stylesheet" type="text/css">
<!-- Overrides for platform-specific styling -->
<link href="overrides.css" rel="stylesheet" type="text/css">
<script src="bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<!-- Load the cordova api if available -->
<script src="cordova.js"></script>
<link rel="import" href="src/padlock.html">
<link rel="import" href="src/components/app/app.html">
@ -26,9 +46,7 @@
var source = new padlock.LocalSource(),
store = new padlock.Store(source),
settings = new padlock.Settings(store, {
// "sync_require_subscription": true
}),
settings = new padlock.Settings(store),
collection = new padlock.Collection("default", store),
announcements = new padlock.Announcements("https://padlock.io/announcements.json", store);
@ -37,8 +55,6 @@
});
</script>
<!-- Load the cordova api if available -->
<script src="cordova.js"></script>
<script>
/* global cordova */
document.addEventListener("deviceready", function() {

View File

@ -72,6 +72,7 @@ padlock.CloudSource = (function(Source) {
}
req.setRequestHeader("X-Client-Version", padlock.version);
req.setRequestHeader("X-Client-Platform", padlock.platform.getPlatformName());
return req;
} catch(e) {

View File

@ -41,7 +41,8 @@ padlock.Settings = (function(util, LocalSource) {
"obfuscate_fields": false,
"showed_backup_reminder": 0,
"sync_require_subscription": false,
"sync_id": ""
"sync_id": "",
"version": ""
},
parse: function(data) {
try {
@ -82,6 +83,11 @@ padlock.Settings = (function(util, LocalSource) {
//* Saves the existing settings
save: function(opts) {
this.store.save(storeKey, this.toString(), opts);
},
reset: function() {
for (var prop in this.properties) {
this[prop] = this.properties[prop];
}
}
};

View File

@ -29,7 +29,7 @@
<padlock-header id="header" view="{{ _currentView }}" filter-string="{{ _filterString }}" on-filter-enter="_selectMarkedRecord"></padlock-header>
<padlock-start-view id="startView" class="view" on-newpwd="_newPwd" on-restore="_restored" collection="{{ collection }}" settings="{{ settings }}"></padlock-start-view>
<padlock-lock-view id="lockView" class="view" on-pwdenter="_pwdEnter"></padlock-lock-view>
<padlock-lock-view id="lockView" class="view" on-pwdenter="_pwdEnter" on-reset-app="_resetData"></padlock-lock-view>
<!-- LIST VIEW -->
<padlock-list-view id="listView" class="view" records="{{ _records }}" filter-string="{{ _filterString }}"
selected="{{ _selected }}" on-select="_recordSelected" on-add="_addRecord" on-back="_listViewBack"
@ -39,7 +39,7 @@
on-delete="_deleteRecord" on-categories="_openCategories" settings="{{ settings }}"></padlock-record-view>
<!-- Settings VIEW -->
<padlock-settings-view id="settingsView" class="view" on-back="_settingsBack" on-open-cloud-view="_openCloudView"
on-import="_openImportView" on-export="_openExportView" on-reset="_openStartView" on-change-password="_changePwd"
on-import="_openImportView" on-export="_openExportView" on-reset="_resetData" on-change-password="_changePwd"
collection="{{ collection }}" settings="{{ settings }}"></padlock-settings-view>
<!-- IMPORT VIEW -->
<padlock-import-view id="importView" class="view" on-imported="_imported" collection="{{ collection }}"

View File

@ -102,6 +102,10 @@ padlock.App = (function(Polymer, platform) {
// Init view when app resumes
document.addEventListener("resume", this._resume.bind(this, true), false);
window.addEventListener("update-downloaded", function(e) {
this._updateDownloaded.apply(this, e.detail);
}.bind(this));
},
_cancelAutoLock: function() {
this._pausedAt = null;
@ -165,6 +169,7 @@ padlock.App = (function(Polymer, platform) {
_newPwd: function(event, detail) {
// Update master password
this.collection.setPassword(detail.password);
this.settings.save();
// Navigate to list view
this._popinOpen(this.$.listView);
},
@ -187,6 +192,7 @@ padlock.App = (function(Polymer, platform) {
success: function() {
// Fetch settings from persistent storage
this.settings.fetch({success: function() {
// Write version to settings
this._notifySettings();
this._unlockSuccess();
}.bind(this), fail: function() {
@ -924,6 +930,19 @@ padlock.App = (function(Polymer, platform) {
this._openForm(els, a.text, dismissed, dismissed, true);
}
},
_updateDownloaded: function() {
this._alert("New update available! Restart the app to install!");
},
_resetData: function() {
this.settings.reset();
this._notifySettings();
this.collection.clear();
this.collection.destroy();
this._openStartView();
this.async(function() {
this._alert("Data deleted and app reset succesfully!");
}, 1000);
}
});

View File

@ -32,6 +32,8 @@
<padlock-lock></padlock-lock>
<button class="reset-button ghost" on-tap="_openOptions" type="button">...</button>
</template>
<script src="lock-view.js"></script>

View File

@ -0,0 +1,66 @@
/* global Polymer, padlock */
(function(Polymer, ViewBehavior, platform) {
"use strict";
Polymer({
is: "padlock-lock-view",
behaviors: [ViewBehavior],
hide: function() {
this.$$("padlock-lock").unlocked = true;
var args = arguments;
this.async(function() {
ViewBehavior.hide.apply(this, args);
}, 500);
},
show: function() {
this._clear();
this.$$("padlock-lock").unlocked = false;
ViewBehavior.show.apply(this, arguments);
if (!platform.isTouch()) {
this.async(function() {
this.$.pwdInput.focus();
}, 500);
}
},
enter: function() {
this.$.pwdInput.blur();
this.fire("pwdenter", {password: this.$.pwdInput.value});
},
getAnimationElement: function() {
return this.$$("padlock-lock");
},
_clear: function() {
this.$.pwdInput.value = "";
},
_openOptions: function() {
this.fire("open-form", {
components: [
{element: "button", label: "Reset App", submit: true}
],
submit: this._confirmResetApp.bind(this, false)
});
},
_confirmResetApp: function(failed) {
var title = failed ? "Failed to Confirm. Make sure to type 'RESET' in the text field below." :
"Are you sure you want to delete all your data and reset the app? " +
"This action can not be undone! Type 'RESET' to Confirm.";
this.fire("open-form", {
title: title,
components: [
{element: "input", name: "confirm", placeholder: "Type 'RESET' to Confirm"},
{element: "button", label: "Reset App", submit: true}
],
submit: function(data) {
if (data.confirm.toLowerCase() == "reset") {
this.fire("reset-app");
} else {
this._confirmResetApp(true);
}
}.bind(this)
});
}
});
})(Polymer, padlock.ViewBehavior, padlock.platform);

View File

@ -51,6 +51,16 @@
text-shadow: rgba(0,0,0,0.2) 1px 1px;
}
button.reset-button {
width: 50px;
height: 50px;
position: absolute;
bottom: 0;
right: 0;
animation reveal 0.5s ease 0.6s both;
font-size: 115%;
}
padlock-lock {
align-self: center;
animation: reveal 0.5s ease 0.8s both;

View File

@ -47,7 +47,10 @@
<section>
<button on-tap="_openWebsite">Website</button>
<button on-tap="_sendMail">Support</button>
<div class="info" on-tap="_openHomepage">Developed and maintained by <strong>MaKleSoft</strong></div>
<div class="info" on-tap="_openHomepage">
<strong>Padlock {{ _version() }}</strong><br>
Developed and maintained by <strong>MaKleSoft</strong>
</div>
</section>
<!-- PROGRESS INDICATOR -->

View File

@ -71,11 +71,6 @@
"This action can not be undone! Please enter your master password to confirm.",
submit: function(data) {
if (data.password == this.collection.defaultPassword) {
this.set("settings.sync_connected", false);
this.set("settings.sync_key", "");
this.set("settings.sync_email", "");
this.collection.clear();
this.collection.destroy();
this.fire("reset");
} else {
this.fire("notify", {message: "Wrong password!", type: "error", duration: 2000});
@ -85,6 +80,9 @@
},
_openCloudView: function() {
this.fire("open-cloud-view");
},
_version: function() {
return padlock.version;
}
});

View File

@ -34,8 +34,4 @@ button, .button {
font-size: 15px;
text-align: center;
cursor: pointer;
strong {
color: $color-primary;
}
}

View File

@ -278,7 +278,7 @@
// We're in a web worker! Let's create an interface for calling some of the modules methods
// Load the sjcl dependency.
importScripts("../lib/sjcl.js");
importScripts("../bower_components/sjcl/sjcl.js");
// Create the module (Inject the dependy manually)
var crypto = modFunc(sjcl);

View File

@ -1,16 +1,17 @@
<script>
/* global window, padlock */
window.padlock = {
version: "1.1.0"
version: ""
};
</script>
<script src="../lib/sjcl.js"></script>
<script src="../bower_components/sjcl/sjcl.js"></script>
<script src="../bower_components/zxcvbn/lib/zxcvbn.js"></script>
<script src="../bower_components/papaparse/papaparse.js"></script>
<script src="util.js"></script>
<script src="rand.js"></script>
<script src="crypto.js"></script>
<script src="platform.js"></script>
<script src="version.js"></script>
<script src="Source.js"></script>
<script src="LocalStorageSource.js"></script>
<script src="ChromeStorageSource.js"></script>

View File

@ -1,5 +1,5 @@
/* jshint browser: true */
/* global padlock, chrome, cordova */
/* global padlock, chrome, cordova, electron */
/**
* Module containing various platform-specific utilities
@ -180,6 +180,32 @@ padlock.platform = (function() {
}
};
var getAppVersion = function(cb) {
if (typeof electron !== "undefined") {
cb(electron.remote.app.getVersion());
} else if (typeof cordova !== "undefined" && cordova.getAppVersion) {
cordova.getAppVersion.getVersionNumber(cb);
} else if (isChromeApp()) {
cb(chrome.runtime.getManifest().version);
} else {
cb("");
}
};
var getPlatformName = function() {
if (typeof require !== "undefined" && require("os")) {
return require("os").platform();
} else if (isIOS()) {
return "ios";
} else if (isAndroid()) {
return "android";
} else if (isChromeApp()) {
return "chrome";
} else {
return "";
}
};
return {
getVendorPrefix: getVendorPrefix,
getTransitionEndEventName: getTransitionEndEventName,
@ -193,6 +219,8 @@ padlock.platform = (function() {
setClipboard: setClipboard,
getClipboard: getClipboard,
keyboardDisableScroll: keyboardDisableScroll,
getAppStoreLink: getAppStoreLink
getAppStoreLink: getAppStoreLink,
getAppVersion: getAppVersion,
getPlatformName: getPlatformName
};
})();

View File

@ -25,17 +25,22 @@ padlock.rand = (function() {
function randomString(length, charSet) {
length = length || 32;
charSet = charSet || charSets.full;
var rnd = new Uint8Array(length);
var rnd = new Uint8Array(1);
var str = "";
window.crypto.getRandomValues(rnd);
for (var i=0; i<length; i++) {
str += charSet[Math.floor(rnd[i] * charSet.length / 256)];
while (str.length < length) {
window.crypto.getRandomValues(rnd);
// Prevent modulo bias by rejecting values larger than the highest muliple of `charSet.length`
if (rnd[0] > 255 - 256 % charSet.length) {
continue;
}
str += charSet[rnd[0] % charSet.length];
}
return str;
}
return {
randomString: randomString,
chars: chars
chars: chars,
charSets: charSets
};
})();

View File

@ -21,4 +21,4 @@
font-style: normal;
font-weight: 700;
src: local('Clear Sans Bold'), local('ClearSans-Bold'), url(../../assets/fonts/ClearSans-Bold.woff) format('woff');
}
}

15
app/src/version.js Normal file
View File

@ -0,0 +1,15 @@
/* global padlock */
(function() {
"use strict";
function fetchVersion() {
padlock.platform.getAppVersion(function(version) {
padlock.version = version;
});
}
fetchVersion();
// Try to get app version again after deviceready event since the getAppVersion
// plugin is not ready before
document.addEventListener("deviceready", fetchVersion);
})();

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="192px" height="64px" viewBox="0 0 192 64" enable-background="new 0 0 192 64" xml:space="preserve">
<g>
<polygon fill="#59C6FF" points="40.75,11.578 42.164,10.164 32,0 21.836,10.164 23.25,11.578 31,3.828 31,35.486 33,35.486
33,3.828 "/>
<polygon fill="#59C6FF" points="37,19 37,21 50,21 50,55 14,55 14,21 27,21 27,19 12,19 12,57 52,57 52,19 "/>
</g>
<polygon fill="#333333" points="102.165,42.75 103.579,44.164 113.743,34 103.579,23.836 102.165,25.25 109.915,33 78.257,33
78.257,35 109.915,35 "/>
<g>
<path fill="#59C6FF" d="M160,9c-13.809,0-25,11.191-25,25s11.191,25,25,25s25-11.191,25-25S173.809,9,160,9z M160,57
c-12.682,0-23-10.318-23-23s10.318-23,23-23s23,10.318,23,23S172.682,57,160,57z"/>
<polygon fill="#59C6FF" points="161,19 159,19 159,33 145.066,33 145.066,35 159,35 159,48.934 161,48.934 161,35 175,35 175,33
161,33 "/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="1000px" height="1000px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<rect x="-225" y="-145" display="none" fill="#F5F5F5" width="1442" height="1342"/>
<g>
<rect x="103" y="632" fill="#59C6FF" width="794" height="625"/>
<path fill="none" stroke="#59C6FF" stroke-width="110" stroke-miterlimit="10" d="M778,688V533.991
C778,380.461,653.531,256,500,256c-153.53,0-278,124.461-278,277.991V728"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 826 B

View File

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 488 B

After

Width:  |  Height:  |  Size: 488 B

View File

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Some files were not shown because too many files have changed in this diff Show More