Преглед на файлове

Remove v1 and update bootstrap v4

pull/342/head
Guillaume Vincent преди 7 години
родител
ревизия
aea154026e
променени са 34 файла, в които са добавени 1296 реда и са изтрити 948 реда
  1. +1
    -5
      dist/i18n/de.json
  2. +1
    -5
      dist/i18n/en.json
  3. +1
    -5
      dist/i18n/es.json
  4. +1
    -5
      dist/i18n/fr.json
  5. +1
    -5
      dist/i18n/zh-CN.json
  6. +1
    -5
      dist/i18n/zh.json
  7. +1
    -1
      dist/lesspass.min.css
  8. +8
    -8
      dist/lesspass.min.js
  9. +1180
    -617
      package-lock.json
  10. +3
    -3
      package.json
  11. +6
    -8
      src/LessPass.scss
  12. +1
    -3
      src/LessPass.vue
  13. +12
    -20
      src/components/MasterPassword.vue
  14. +4
    -4
      src/components/Menu.vue
  15. +1
    -1
      src/components/Message.vue
  16. +25
    -86
      src/components/Options.vue
  17. +1
    -1
      src/components/PasswordProfile.vue
  18. +1
    -5
      src/i18n/de.json
  19. +1
    -5
      src/i18n/en.json
  20. +1
    -5
      src/i18n/es.json
  21. +1
    -5
      src/i18n/fr.json
  22. +1
    -5
      src/i18n/zh-CN.json
  23. +1
    -5
      src/i18n/zh.json
  24. +3
    -2
      src/store/actions.js
  25. +0
    -7
      src/store/getters.js
  26. +1
    -1
      src/store/mutation-types.js
  27. +2
    -5
      src/store/mutations.js
  28. +1
    -4
      src/views/Login.vue
  29. +13
    -33
      src/views/PasswordGenerator.vue
  30. +3
    -5
      src/views/PasswordReset.vue
  31. +1
    -6
      src/views/PasswordResetConfirm.vue
  32. +0
    -20
      test/e2e/specs/defaultOptions.js
  33. +0
    -18
      test/unit/store.getters.js
  34. +18
    -35
      test/unit/store.mutations.js

+ 1
- 5
dist/i18n/de.json Целия файл

@@ -37,11 +37,7 @@
"SorryCopy": "Es tut uns leid, dass die Kopie nur auf modernen Browsern funktioniert",
"UNDO": "STORNIEREN",
"UpdateYourSearch": "Bitte erweitern Sie Ihre Suche.",
"Version": "Version",
"WarningV1Deprecated": "Version 1 ist veraltet und wird bald gelöscht werden. Wir empfehlen Ihnen dringend, Ihre Passwörter auf die Version 2 zu migrieren.",
"WelcomeRegister": "Willkommen {email}, danke für die Anmeldung.",
"Your options have been saved successfully": "Ihre Optionen wurden erfolgreich gespeichert",
"resetPasswordSuccess": "Wenn die E-Mail-Adresse {email} mit einem LessPass-Konto verknüpft ist, erhalten Sie in Kürze eine E-Mail von LessPass mit Anweisungen zum Zurücksetzen Ihres Passworts.",
"version": "Version",
"versionShortcut": "V"
"resetPasswordSuccess": "Wenn die E-Mail-Adresse {email} mit einem LessPass-Konto verknüpft ist, erhalten Sie in Kürze eine E-Mail von LessPass mit Anweisungen zum Zurücksetzen Ihres Passworts."
}

+ 1
- 5
dist/i18n/en.json Целия файл

@@ -37,11 +37,7 @@
"SorryCopy": "We are sorry the copy only works on modern browsers",
"UNDO": "UNDO",
"UpdateYourSearch": "Please try broadening your search.",
"Version": "Version",
"WarningV1Deprecated": "Version 1 is deprecated and will be deleted soon. We strongly advise you to migrate your passwords to version 2.",
"WelcomeRegister": "Welcome {email}, thank you for signing up.",
"Your options have been saved successfully": "Your options have been saved successfully",
"resetPasswordSuccess": "If the email address {email} is associated with a LessPass account, you will shortly receive an email from LessPass with instructions on how to reset your password.",
"version": "version",
"versionShortcut": "v"
"resetPasswordSuccess": "If the email address {email} is associated with a LessPass account, you will shortly receive an email from LessPass with instructions on how to reset your password."
}

+ 1
- 5
dist/i18n/es.json Целия файл

@@ -37,11 +37,7 @@
"SorryCopy": "Lamentamos que la copia sólo funcione en navegadores modernos",
"UNDO": "DESHACER",
"UpdateYourSearch": "Trate de ampliar su búsqueda.",
"Version": "Versión",
"WarningV1Deprecated": "La versión 1 está obsoleta y será eliminada pronto. Le recomendamos enérgicamente migrar sus contraseñas a la versión 2.",
"WelcomeRegister": "Bienvenido o bienvenida {email}, gracias por registrarse.",
"Your options have been saved successfully": "Sus opciones se han guardado correctamente",
"resetPasswordSuccess": "Si la dirección de correo electrónico {email} está asociada a una cuenta LessPass, recibirá un correo electrónico de LessPass con instrucciones sobre cómo restablecer su contraseña.",
"version": "versión",
"versionShortcut": "v"
"resetPasswordSuccess": "Si la dirección de correo electrónico {email} está asociada a una cuenta LessPass, recibirá un correo electrónico de LessPass con instrucciones sobre cómo restablecer su contraseña."
}

+ 1
- 5
dist/i18n/fr.json Целия файл

@@ -37,11 +37,7 @@
"SorryCopy": "Nous sommes désolés, la copie ne fonctionne que sur les navigateurs modernes",
"UNDO": "ANNULER",
"UpdateYourSearch": "Merci de modifier votre recherche.",
"Version": "Version",
"WarningV1Deprecated": "La version 1 est déconseillée et sera supprimée bientôt. Nous vous conseillons fortement de migrer vos mots de passe vers la version 2.",
"WelcomeRegister": "Bienvenue {email}, merci pour vous être enregistré.",
"Your options have been saved successfully": "Vos options ont été enregistrées avec succès",
"resetPasswordSuccess": "Si l'adresse email {email} est associée avec un compte LessPass, vous allez recevoir un email de la part de LessPass avec les instructions pour changer votre mot de passe.",
"version": "version",
"versionShortcut": "v"
"resetPasswordSuccess": "Si l'adresse email {email} est associée avec un compte LessPass, vous allez recevoir un email de la part de LessPass avec les instructions pour changer votre mot de passe."
}

+ 1
- 5
dist/i18n/zh-CN.json Целия файл

@@ -37,11 +37,7 @@
"SorryCopy": "很抱歉,但复制功能仅适用于现代浏览器",
"UNDO": "解开",
"UpdateYourSearch": "请尝试放宽您的搜索条件。",
"Version": "版本",
"WarningV1Deprecated": "版本 1 已不再支持,不久后将被删除。我们强烈建议您将密码迁移至版本 2。",
"WelcomeRegister": "你好 {email},欢迎您的注册。",
"Your options have been saved successfully": "您的选项已成功保存",
"resetPasswordSuccess": "如果电子邮件地址 {email} 与一个 LessPass 账户相关联,您将很快收到 LessPass 的电子邮件,里面提供有重置密码的操作说明。",
"version": "版本",
"versionShortcut": "v"
"resetPasswordSuccess": "如果电子邮件地址 {email} 与一个 LessPass 账户相关联,您将很快收到 LessPass 的电子邮件,里面提供有重置密码的操作说明。"
}

+ 1
- 5
dist/i18n/zh.json Целия файл

@@ -37,11 +37,7 @@
"SorryCopy": "我們很抱歉,該副本僅適用於現代瀏覽器",
"UNDO": "解開",
"UpdateYourSearch": "請試著放寬您的搜尋條件。",
"Version": "版本",
"WarningV1Deprecated": "版本 1 已不支援,不久將被刪除。 我們強烈得建議您將密碼換至版本 2。",
"WelcomeRegister": "歡迎 {email},謝謝您的註冊。",
"Your options have been saved successfully": "您的選項已成功保存",
"resetPasswordSuccess": "如果电子邮件地址 {email} 与LessPass帐户相关联,您将很快收到LessPass的电子邮件,并提供如何重置密码的说明。",
"version": "版本",
"versionShortcut": "v"
"resetPasswordSuccess": "如果电子邮件地址 {email} 与LessPass帐户相关联,您将很快收到LessPass的电子邮件,并提供如何重置密码的说明。"
}

+ 1
- 1
dist/lesspass.min.css
Файловите разлики са ограничени, защото са твърде много
Целия файл


+ 8
- 8
dist/lesspass.min.js
Файловите разлики са ограничени, защото са твърде много
Целия файл


+ 1180
- 617
package-lock.json
Файловите разлики са ограничени, защото са твърде много
Целия файл


+ 3
- 3
package.json Целия файл

@@ -26,14 +26,14 @@
"dependencies": {
"awesomplete": "^1.1.2",
"axios": "^0.16.2",
"bootstrap": "4.0.0-alpha.6",
"bootstrap": "^4.0.0-beta",
"copy-text-to-clipboard": "^1.0.2",
"font-awesome": "^4.7.0",
"hint.css": "^2.5.0",
"jwt-decode": "^2.2.0",
"lesspass": "^6.0.0",
"lodash.debounce": "^4.0.8",
"vue": "^2.4.2",
"vue": "^2.4.4",
"vue-polyglot": "^0.2.1",
"vue-router": "^2.7.0",
"vuejs-paginate": "^1.2.0",
@@ -67,7 +67,7 @@
"url-loader": "^0.5.9",
"vue-loader": "^13.0.4",
"vue-polyglot-utils": "^0.1.1",
"vue-template-compiler": "^2.4.2",
"vue-template-compiler": "^2.4.4",
"walk": "^2.3.9",
"webpack": "^3.5.5",
"webpack-dev-server": "^2.7.1"


+ 6
- 8
src/LessPass.scss Целия файл

@@ -1,10 +1,7 @@
@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";
@import "~bootstrap/scss/mixins";
@import "~bootstrap/scss/custom";
// Reset and dependencies
@import "~bootstrap/scss/normalize";
//@import "~bootstrap/scss/print";
// Core CSS
@import "~bootstrap/scss/reboot";
@import "~bootstrap/scss/type";
@import "~bootstrap/scss/images";
@@ -13,7 +10,6 @@
@import "~bootstrap/scss/tables";
@import "~bootstrap/scss/forms";
@import "~bootstrap/scss/buttons";
// Components
@import "~bootstrap/scss/transitions";
@import "~bootstrap/scss/dropdown";
@import "~bootstrap/scss/button-group";
@@ -30,15 +26,13 @@
//@import "~bootstrap/scss/progress";
@import "~bootstrap/scss/media";
@import "~bootstrap/scss/list-group";
@import "~bootstrap/scss/responsive-embed";
@import "~bootstrap/scss/close";
// Components w/ JavaScript
//@import "~bootstrap/scss/modal";
//@import "~bootstrap/scss/tooltip";
//@import "~bootstrap/scss/popover";
//@import "~bootstrap/scss/carousel";
// Utility classes
@import "~bootstrap/scss/utilities";

@import '~font-awesome/css/font-awesome.css';
@import '~hint.css/hint.css';
@import '~awesomplete/awesomplete.css';
@@ -100,3 +94,7 @@ button, .pointer {
.right-addon input {
padding-right: 30px;
}

.border-blue{
border-color: $blue !important;
}

+ 1
- 3
src/LessPass.vue Целия файл

@@ -2,7 +2,7 @@
<div id="lesspass" class="card">
<lesspass-menu></lesspass-menu>
<lesspass-message></lesspass-message>
<div class="lesspass__inner-box card-block">
<div class="lesspass__inner-box card-body">
<router-view></router-view>
</div>
</div>
@@ -11,7 +11,6 @@
import './LessPass.scss';
import Menu from './components/Menu.vue';
import Message from './components/Message.vue';
import {mapGetters} from 'vuex';

export default {
name: 'LessPass',
@@ -19,7 +18,6 @@
'lesspass-menu': Menu,
'lesspass-message': Message
},
computed: mapGetters(['version']),
created(){
this.$store.dispatch('cleanMessage');
this.$store.dispatch('refreshToken');


+ 12
- 20
src/components/MasterPassword.vue Целия файл

@@ -28,12 +28,13 @@
class="form-control"
autocorrect="off"
autocapitalize="off"
v-model="password"
v-bind:value="value"
v-bind:placeholder="label"
v-on:input="updateValue($event.target.value)"
v-on:keyup.enter="$emit('keyupEnter')"
v-on:blur="hidePassword($refs.passwordField)">
<span class="input-group-btn"
v-if="fingerprint && password"
v-if="fingerprint && value"
v-on:click="togglePasswordType($refs.passwordField)">
<button id="fingerprint" class="btn" type="button" tabindex="-1">
<small class="hint--left">
@@ -48,7 +49,7 @@
type="button"
class="btn btn-link btn-sm p-0"
v-if="showEncryptButton"
v-on:click="encryptMasterPassword($refs.passwordField.value)"
v-on:click="encryptMasterPassword()"
v-bind:class="{'disabled': email === '', 'hint--top hint--medium': email !== ''}">
<small>{{ EncryptButtonText }}</small>
</button>
@@ -57,6 +58,7 @@
<script>
import LessPass from 'lesspass';
import debounce from 'lodash.debounce';
import defaultPasswordProfile from '../store/defaultPassword';

export default {
name: 'masterPassword',
@@ -72,7 +74,6 @@
},
data(){
return {
password: this.value,
fingerprint: '',
icon1: '',
icon2: '',
@@ -82,15 +83,14 @@
color3: ''
}
},
watch: {
password(newPassword){
methods: {
updateValue(newPassword){
const fakePassword = Math.random().toString(36).substring(7);
this.setFingerprint(fakePassword);
this.showRealFingerprint(newPassword);
this.$refs.passwordField.value = newPassword;
this.$emit('input', newPassword);
}
},
methods: {
},
togglePasswordType(element){
if (element.type === 'password') {
element.type = 'text';
@@ -131,20 +131,12 @@
showRealFingerprint: debounce(function(password) {
this.setFingerprint(password);
}, 500),
encryptMasterPassword(password){
const defaultPasswordProfile = {
lowercase: true,
uppercase: true,
numbers: true,
symbols: true,
length: 16,
counter: 1,
version: 2,
};
encryptMasterPassword(){
const password = this.$refs.passwordField.value;
return LessPass
.generatePassword('lesspass.com', this.email, password, defaultPasswordProfile)
.then(generatedPassword => {
this.password = generatedPassword;
this.updateValue(generatedPassword);
});
}
}


+ 4
- 4
src/components/Menu.vue Целия файл

@@ -15,12 +15,12 @@
</style>
<template>
<div id="menu">
<div class="card-header" v-bind:class="{ 'card-inverse': isGuest}">
<div class="card-header" v-bind:class="{ 'text-white bg-dark': isGuest}">
<div class="row">
<div class="col-3">
<div class="col-4">
<span id="title" v-on:click="fullReload()" class="white-link pointer">LessPass</span>
</div>
<div class="col-9 text-right">
<div class="col-8 text-right">
<span v-if="saved && isAuthenticated">
<small><i class="fa fa-lg fa-check pl-3"></i> saved</small>
</span>
@@ -55,7 +55,7 @@
},
methods: {
fullReload() {
this.$store.dispatch('savePassword', {password: this.$store.state.defaultPassword});
this.$store.dispatch('resetPassword');
this.$router.push({name: 'home'});
},
logout() {


+ 1
- 1
src/components/Message.vue Целия файл

@@ -32,7 +32,7 @@
<transition name="fade">
<div v-if="message.text">
<div class="card-header text-white"
v-bind:class="{ 'card-warning': message.status==='warning', 'card-danger': message.status==='error', 'card-success': message.status==='success' }">
v-bind:class="{ 'bg-warning': message.status==='warning', 'bg-danger': message.status==='error', 'bg-success': message.status==='success' }">
<div class="row">
<div class="col-12">
<small>{{message.text}}</small>


+ 25
- 86
src/components/Options.vue Целия файл

@@ -22,44 +22,44 @@
<div class="col-3">
<button id="lowercase__btn"
type="button" class="btn btn-block btn-sm px-0"
v-bind:class="{'btn-primary':options.lowercase===true && options.version===2,'btn-warning':options.lowercase===true && options.version===1,'btn-secondary':options.lowercase===false}"
v-on:click="options.lowercase=!options.lowercase">
v-bind:class="{'btn-primary':password.lowercase===true, 'btn-light':password.lowercase===false}"
v-on:click="password.lowercase=!password.lowercase">
a-z
</button>
</div>
<div class="col-3">
<button id="uppercase__btn"
type="button" class="btn btn-block btn-sm px-0"
v-bind:class="{'btn-primary':options.uppercase===true && options.version===2,'btn-warning':options.uppercase===true && options.version===1,'btn-secondary':options.uppercase===false}"
v-on:click="options.uppercase=!options.uppercase">
v-bind:class="{'btn-primary':password.uppercase===true, 'btn-light':password.uppercase===false}"
v-on:click="password.uppercase=!password.uppercase">
A-Z
</button>
</div>
<div class="col-3">
<button id="numbers__btn"
type="button" class="btn btn-block btn-sm px-0"
v-bind:class="{'btn-primary':options.numbers===true && options.version===2,'btn-warning':options.numbers===true && options.version===1,'btn-secondary':options.numbers===false}"
v-on:click="options.numbers=!options.numbers">
v-bind:class="{'btn-primary':password.numbers===true,'btn-light':password.numbers===false}"
v-on:click="password.numbers=!password.numbers">
0-9
</button>
</div>
<div class="col-3">
<button id="symbols__btn"
type="button" class="btn btn-block btn-sm px-0"
v-bind:class="{'btn-primary':options.symbols===true && options.version===2,'btn-warning':options.symbols===true && options.version===1,'btn-secondary':options.symbols===false}"
v-on:click="options.symbols=!options.symbols">
v-bind:class="{'btn-primary':password.symbols===true,'btn-light':password.symbols===false}"
v-on:click="password.symbols=!password.symbols">
%!@
</button>
</div>
</div>
</div>
</div>
<div class="form-group row">
<div class="col">
<div class="form-group row mb-0">
<div class="col-6 col-sm-4">
<label for="passwordLength">{{ $t('Length') }}</label>
<div class="input-group input-group-sm">
<span class="input-group-btn" v-on:click="options.length=decrement(options.length, {min: 5, max: 35})">
<button id="decreaseLength__btn" class="btn btn-secondary p-1" type="button">
<span class="input-group-btn" v-on:click="password.length=decrement(password.length, {min: 5, max: 35})">
<button id="decreaseLength__btn" class="btn btn-primary border-blue px-2" type="button">
<i class="fa fa-minus"></i>
</button>
</span>
@@ -68,16 +68,16 @@
type="number"
min="5"
max="35"
v-model.number="options.length">
v-model.number="password.length">
<span class="input-group-btn"
v-on:click="options.length=increment(options.length, {min: 5, max: 35})">
<button id="increaseLength__btn" class="btn btn-secondary p-1" type="button">
v-on:click="password.length=increment(password.length, {min: 5, max: 35})">
<button id="increaseLength__btn" class="btn btn-primary border-blue px-2" type="button">
<i class="fa fa-plus"></i>
</button>
</span>
</div>
</div>
<div class="col">
<div class="col-6 col-sm-4">
<label for="passwordCounter"
class="hint--top hint--medium"
v-bind:aria-label="$t('CounterFieldHelp','Increment this value to change the generated password without changing your master options.')">
@@ -85,8 +85,8 @@
</label>
<div class="input-group input-group-sm">
<span id="decreaseCounter__btn" class="input-group-btn"
v-on:click="options.counter=decrement(options.counter, {min: 1})">
<button class="btn btn-secondary p-1" type="button">
v-on:click="password.counter=decrement(password.counter, {min: 1})">
<button class="btn btn-primary border-blue px-2" type="button">
<i class="fa fa-minus"></i>
</button>
</span>
@@ -94,52 +94,15 @@
class="form-control form-control-sm"
type="number"
min="1"
v-model.number="options.counter">
v-model.number="password.counter">
<span id="increaseCounter__btn" class="input-group-btn"
v-on:click="options.counter=increment(options.counter, {min: 1})">
<button class="btn btn-secondary p-1" type="button">
v-on:click="password.counter=increment(password.counter, {min: 1})">
<button class="btn btn-primary border-blue px-2" type="button">
<i class="fa fa-plus"></i>
</button>
</span>
</div>
</div>
<div class="col">
<div class="row">
<div class="col">
<label>{{ $t('Version') }}</label>
</div>
</div>
<div class="row no-gutters">
<div class="col">
<button type="button" class="btn btn-block btn-sm border-right-0"
v-bind:class="{'btn-primary':options.version===2,'btn-secondary':options.version!==2}"
v-on:click="setVersion(2)">
<span class="hidden-xs-up">{{$t('version')}} </span>
<span class="hidden-xs-down">{{$t('versionShortcut', 'v')}}</span>2
</button>
</div>
<div class="col">
<button type="button"
class="btn btn-block btn-sm border-left-0"
v-bind:class="{'btn-warning':options.version===1,'btn-secondary':options.version!==1}"
v-on:click="setVersion(1)">
<span class="hidden-xs-up">{{$t('version')}} </span>
<span class="hidden-xs-down">{{$t('versionShortcut', 'v')}}</span>1
</button>
</div>
</div>
</div>
</div>
<div class="form-group row mb-0">
<div class="col">
<button id="saveOptions__btn"
type="button" class="btn btn-sm hint--top-right hint--medium"
v-bind:aria-label="$t('DefaultOptionLocalStorage', 'We use local storage to save default options locally. Each time you open the app, these options will be loaded by default.')"
v-bind:class="{'btn-outline-warning':options.version===1,'btn-outline-primary':options.version!==1}"
v-on:click="saveDefaultOptions()">
<i class="fa fa-floppy-o"></i> {{$t('Save options')}}
</button>
</div>
</div>
</div>
</template>
@@ -147,40 +110,16 @@
<script type="text/ecmascript-6">
import message from '../services/message';
import {increment, decrement} from "../services/form-validator";
import { mapState} from 'vuex';

export default {
name: 'options',
props: {
options: Object
},
watch: {
'options': {
handler: function(options) {
this.$emit('update:options', options)
},
deep: true
}
computed: {
...mapState(['password']),
},
methods: {
decrement,
increment,
setVersion(value) {
if (value === 1) {
message.error(this.$t(
"WarningV1Deprecated",
"Version 1 is deprecated and will be deleted soon. We strongly advise you to migrate your passwords to version 2."
));
}
this.options = Object.assign({}, this.options, {
length: value === 1 ? 12 : 16,
version: value
});
this.$store.dispatch('saveVersion', {version: value});
},
saveDefaultOptions() {
this.$store.dispatch('saveDefaultOptions', {options: this.options});
message.success(this.$t('Your options have been saved successfully'));
},
increment
}
}
</script>

+ 1
- 1
src/components/PasswordProfile.vue Целия файл

@@ -8,7 +8,7 @@
font-weight: 600;
}

.passwordProfile__site, .passwordProfile__login, .passwordProfile__version {
.passwordProfile__site, .passwordProfile__login {
font-size: 0.8rem;
line-height: 0.8rem;
}


+ 1
- 5
src/i18n/de.json Целия файл

@@ -37,11 +37,7 @@
"SorryCopy": "Es tut uns leid, dass die Kopie nur auf modernen Browsern funktioniert",
"UNDO": "STORNIEREN",
"UpdateYourSearch": "Bitte erweitern Sie Ihre Suche.",
"Version": "Version",
"WarningV1Deprecated": "Version 1 ist veraltet und wird bald gelöscht werden. Wir empfehlen Ihnen dringend, Ihre Passwörter auf die Version 2 zu migrieren.",
"WelcomeRegister": "Willkommen {email}, danke für die Anmeldung.",
"Your options have been saved successfully": "Ihre Optionen wurden erfolgreich gespeichert",
"resetPasswordSuccess": "Wenn die E-Mail-Adresse {email} mit einem LessPass-Konto verknüpft ist, erhalten Sie in Kürze eine E-Mail von LessPass mit Anweisungen zum Zurücksetzen Ihres Passworts.",
"version": "Version",
"versionShortcut": "V"
"resetPasswordSuccess": "Wenn die E-Mail-Adresse {email} mit einem LessPass-Konto verknüpft ist, erhalten Sie in Kürze eine E-Mail von LessPass mit Anweisungen zum Zurücksetzen Ihres Passworts."
}

+ 1
- 5
src/i18n/en.json Целия файл

@@ -37,11 +37,7 @@
"SorryCopy": "We are sorry the copy only works on modern browsers",
"UNDO": "UNDO",
"UpdateYourSearch": "Please try broadening your search.",
"Version": "Version",
"WarningV1Deprecated": "Version 1 is deprecated and will be deleted soon. We strongly advise you to migrate your passwords to version 2.",
"WelcomeRegister": "Welcome {email}, thank you for signing up.",
"Your options have been saved successfully": "Your options have been saved successfully",
"resetPasswordSuccess": "If the email address {email} is associated with a LessPass account, you will shortly receive an email from LessPass with instructions on how to reset your password.",
"version": "version",
"versionShortcut": "v"
"resetPasswordSuccess": "If the email address {email} is associated with a LessPass account, you will shortly receive an email from LessPass with instructions on how to reset your password."
}

+ 1
- 5
src/i18n/es.json Целия файл

@@ -37,11 +37,7 @@
"SorryCopy": "Lamentamos que la copia sólo funcione en navegadores modernos",
"UNDO": "DESHACER",
"UpdateYourSearch": "Trate de ampliar su búsqueda.",
"Version": "Versión",
"WarningV1Deprecated": "La versión 1 está obsoleta y será eliminada pronto. Le recomendamos enérgicamente migrar sus contraseñas a la versión 2.",
"WelcomeRegister": "Bienvenido o bienvenida {email}, gracias por registrarse.",
"Your options have been saved successfully": "Sus opciones se han guardado correctamente",
"resetPasswordSuccess": "Si la dirección de correo electrónico {email} está asociada a una cuenta LessPass, recibirá un correo electrónico de LessPass con instrucciones sobre cómo restablecer su contraseña.",
"version": "versión",
"versionShortcut": "v"
"resetPasswordSuccess": "Si la dirección de correo electrónico {email} está asociada a una cuenta LessPass, recibirá un correo electrónico de LessPass con instrucciones sobre cómo restablecer su contraseña."
}

+ 1
- 5
src/i18n/fr.json Целия файл

@@ -37,11 +37,7 @@
"SorryCopy": "Nous sommes désolés, la copie ne fonctionne que sur les navigateurs modernes",
"UNDO": "ANNULER",
"UpdateYourSearch": "Merci de modifier votre recherche.",
"Version": "Version",
"WarningV1Deprecated": "La version 1 est déconseillée et sera supprimée bientôt. Nous vous conseillons fortement de migrer vos mots de passe vers la version 2.",
"WelcomeRegister": "Bienvenue {email}, merci pour vous être enregistré.",
"Your options have been saved successfully": "Vos options ont été enregistrées avec succès",
"resetPasswordSuccess": "Si l'adresse email {email} est associée avec un compte LessPass, vous allez recevoir un email de la part de LessPass avec les instructions pour changer votre mot de passe.",
"version": "version",
"versionShortcut": "v"
"resetPasswordSuccess": "Si l'adresse email {email} est associée avec un compte LessPass, vous allez recevoir un email de la part de LessPass avec les instructions pour changer votre mot de passe."
}

+ 1
- 5
src/i18n/zh-CN.json Целия файл

@@ -37,11 +37,7 @@
"SorryCopy": "很抱歉,但复制功能仅适用于现代浏览器",
"UNDO": "解开",
"UpdateYourSearch": "请尝试放宽您的搜索条件。",
"Version": "版本",
"WarningV1Deprecated": "版本 1 已不再支持,不久后将被删除。我们强烈建议您将密码迁移至版本 2。",
"WelcomeRegister": "你好 {email},欢迎您的注册。",
"Your options have been saved successfully": "您的选项已成功保存",
"resetPasswordSuccess": "如果电子邮件地址 {email} 与一个 LessPass 账户相关联,您将很快收到 LessPass 的电子邮件,里面提供有重置密码的操作说明。",
"version": "版本",
"versionShortcut": "v"
"resetPasswordSuccess": "如果电子邮件地址 {email} 与一个 LessPass 账户相关联,您将很快收到 LessPass 的电子邮件,里面提供有重置密码的操作说明。"
}

+ 1
- 5
src/i18n/zh.json Целия файл

@@ -37,11 +37,7 @@
"SorryCopy": "我們很抱歉,該副本僅適用於現代瀏覽器",
"UNDO": "解開",
"UpdateYourSearch": "請試著放寬您的搜尋條件。",
"Version": "版本",
"WarningV1Deprecated": "版本 1 已不支援,不久將被刪除。 我們強烈得建議您將密碼換至版本 2。",
"WelcomeRegister": "歡迎 {email},謝謝您的註冊。",
"Your options have been saved successfully": "您的選項已成功保存",
"resetPasswordSuccess": "如果电子邮件地址 {email} 与LessPass帐户相关联,您将很快收到LessPass的电子邮件,并提供如何重置密码的说明。",
"version": "版本",
"versionShortcut": "v"
"resetPasswordSuccess": "如果电子邮件地址 {email} 与LessPass帐户相关联,您将很快收到LessPass的电子邮件,并提供如何重置密码的说明。"
}

+ 3
- 2
src/store/actions.js Целия файл

@@ -33,8 +33,8 @@ export const savePassword = ({ commit }, payload) => {
commit(types.SET_PASSWORD, payload);
};

export const saveVersion = ({ commit }, payload) => {
commit(types.SET_VERSION, payload);
export const resetPassword = ({ commit, state }) => {
commit(types.RESET_PASSWORD);
};

export const getSite = ({ commit }) => {
@@ -53,6 +53,7 @@ export const login = ({ commit }, payload) => {

export const logout = ({ commit }) => {
commit(types.LOGOUT);
commit(types.RESET_PASSWORD);
};

export const getPasswords = ({ commit, state }) => {


+ 0
- 7
src/store/getters.js Целия файл

@@ -4,13 +4,6 @@ export const isAuthenticated = state => state.authenticated;

export const isGuest = state => !state.authenticated;

export const version = state => {
if (state.password === null) {
return state.defaultPassword.version;
}
return state.password.version;
};

export const passwordURL = state => {
return `${state.baseURL}/#/?login=${state.password.login}&site=${state
.password.site}&uppercase=${state.password.uppercase}&lowercase=${state


+ 1
- 1
src/store/mutation-types.js Целия файл

@@ -6,7 +6,7 @@ export const SET_MESSAGE = "SET_MESSAGE";
export const SET_PASSWORD = "SET_PASSWORD";
export const SET_PASSWORDS = "SET_PASSWORDS";
export const SET_TOKEN = "SET_TOKEN";
export const SET_VERSION = "SET_VERSION";
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";


+ 2
- 5
src/store/mutations.js Целия файл

@@ -11,6 +11,8 @@ export default {
state.authenticated = false;
state.token = null;
state.passwords = [];
},
[types.RESET_PASSWORD](state) {
state.password = { ...state.defaultPassword };
},
[types.SET_PASSWORD](state, { password }) {
@@ -33,11 +35,6 @@ export default {
[types.SET_BASE_URL](state, { baseURL }) {
state.baseURL = baseURL;
},
[types.SET_VERSION](state, { version }) {
const length = version === 1 ? 12 : 16;
state.password.version = version;
state.password.length = length;
},
[types.SET_SITE](state, { site }) {
state.password.site = site;
},


+ 1
- 4
src/views/Login.vue Целия файл

@@ -44,8 +44,7 @@
</div>
<div class="form-group row no-gutters mb-0">
<div class="col">
<button id="signInButton" class="btn btn-block"
v-bind:class="{ 'btn-warning': version===1, 'btn-primary': version===2 }">
<button id="signInButton" class="btn btn-primary btn-block">
{{$t('Sign In')}}
</button>
</div>
@@ -67,7 +66,6 @@
</template>
<script type="text/ecmascript-6">
import User from '../api/user';
import {mapGetters} from 'vuex';
import MasterPassword from '../components/MasterPassword.vue';
import message from '../services/message';

@@ -82,7 +80,6 @@
components: {
MasterPassword
},
computed: mapGetters(['version']),
methods: {
formIsValid(){
if (!this.email || !this.password || !this.baseURL) {


+ 13
- 33
src/views/PasswordGenerator.vue Целия файл

@@ -1,6 +1,6 @@
<style>
#generated-password {
font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, sans-serif;
font-family: Consolas, Menlo, Monaco, Courier New, monospace, sans-serif;
}

div.awesomplete {
@@ -44,7 +44,8 @@
</div>
</div>
<div class="form-group">
<master-password ref="masterPassword" v-model="masterPassword"
<master-password ref="masterPassword"
v-model="masterPassword"
v-on:keyupEnter="generatePassword"
v-bind:label="$t('Master Password')"></master-password>
</div>
@@ -53,13 +54,12 @@
<div v-if="!passwordGenerated">
<button id="generatePassword__btn"
type="button"
class="btn"
v-on:click="generatePassword"
v-bind:class="{ 'btn-warning': password.version===1, 'btn-primary': password.version===2 }">
class="btn btn-primary border-blue"
v-on:click="generatePassword">
{{ $t('Generate') }}
</button>
<button type="button"
class="btn btn-secondary pull-right showOptions__btn"
class="btn btn-light pull-right showOptions__btn"
v-show="!passwordGenerated"
v-on:click="showOptions =! showOptions">
<i class="fa fa-sliders"></i>
@@ -69,10 +69,9 @@
<div class="input-group">
<span class="input-group-btn">
<button id="copyPasswordButton"
class="btn"
class="btn btn-primary border-blue"
type="button"
v-on:click="copyPassword()"
v-bind:class="{ 'btn-warning': password.version===1, 'btn-primary': password.version===2 }">
v-on:click="copyPassword()">
<i class="fa fa-clipboard"></i>
</button>
</span>
@@ -85,7 +84,7 @@
<span class="input-group-btn">
<button id="revealGeneratedPassword"
type="button"
class="btn btn-secondary"
class="btn btn-light"
v-on:click="togglePasswordType($refs.passwordGenerated)">
<i class="fa fa-eye"></i>
</button>
@@ -93,14 +92,14 @@
<span class="input-group-btn">
<button id="sharePasswordProfileButton"
type="button"
class="btn btn-secondary"
class="btn btn-light"
v-on:click="sharePasswordProfile()">
<i class="fa fa-share-alt pointer"></i>
</button>
</span>
<span class="input-group-btn">
<button type="button"
class="btn btn-secondary showOptions__btn"
class="btn btn-light showOptions__btn"
v-on:click="showOptions =! showOptions">
<i class="fa fa-sliders"></i>
</button>
@@ -108,7 +107,7 @@
</div>
</div>
</div>
<options :options.sync="password" v-if="showOptions || !isDefaultProfile"></options>
<options v-if="showOptions || !isDefaultProfile"></options>
</form>
</template>
<script type="text/ecmascript-6">
@@ -151,7 +150,6 @@
return {
showOptions: false,
masterPassword: '',
fingerprint: '',
passwordGenerated: '',
cleanTimeout: null
}
@@ -178,13 +176,6 @@
this.cleanErrors();
},
deep: true
},
'passwordGenerated': function() {
this.cleanFormInSeconds(30);
},
'masterPassword': function() {
this.cleanErrors();
this.cleanFormInSeconds(30);
}
},
methods: {
@@ -200,26 +191,20 @@
this.passwordGenerated = '';
},
cleanFormInSeconds(seconds = 15) {
clearTimeout(this.cleanTimeout);
this.cleanTimeout = setTimeout(() => {
this.masterPassword = '';
this.passwordGenerated = '';
this.fingerprint = '';
}, 1000 * seconds);
},
generatePassword() {
const site = this.password.site;
const login = this.password.login;
const masterPassword = this.masterPassword;

if (!site && !login || !masterPassword) {
message.error(this.$t('SiteLoginMasterPasswordMandatory', 'Site, login, and master password fields are mandatory.'));
return;
}

this.cleanErrors();
this.fingerprint = this.masterPassword;

const passwordProfile = {
lowercase: this.password.lowercase,
uppercase: this.password.uppercase,
@@ -231,6 +216,7 @@
};
return LessPass.generatePassword(site, login, masterPassword, passwordProfile).then(passwordGenerated => {
this.passwordGenerated = passwordGenerated;
this.cleanFormInSeconds(30);
});
},
focusBestInputField() {
@@ -249,9 +235,6 @@
const copied = copy(this.passwordGenerated);
if (copied) {
showTooltip(document.getElementById('copyPasswordButton'), this.$t('Copied', 'copied !'));
setTimeout(() => {
this.cleanFormInSeconds();
}, 2000);
} else {
message.warning(this.$t('SorryCopy', 'We are sorry the copy only works on modern browsers'))
}
@@ -265,9 +248,6 @@
copySuccessMessage,
'hint--top-left'
);
setTimeout(() => {
this.cleanFormInSeconds();
}, 2000);
}
else {
message.warning(this.$t('SorryCopy', 'We are sorry the copy only works on modern browsers'))


+ 3
- 5
src/views/PasswordReset.vue Целия файл

@@ -16,8 +16,7 @@
<div class="form-group row">
<div class="col-12">
<button id="password-reset__reset-password-btn"
class="btn"
v-bind:class="{ 'btn-warning': version===1, 'btn-primary': version===2 }">
class="btn btn-primary">
{{$t('Reset my password')}}
</button>
</div>
@@ -26,7 +25,7 @@
</template>
<script type="text/ecmascript-6">
import User from '../api/user';
import {mapState, mapGetters} from 'vuex';
import {mapState} from 'vuex';
import message from '../services/message';

export default {
@@ -36,8 +35,7 @@
};
},
computed: {
...mapState(['baseURL']),
...mapGetters(['version'])
...mapState(['baseURL'])
},
methods: {
resetPassword() {


+ 1
- 6
src/views/PasswordResetConfirm.vue Целия файл

@@ -25,8 +25,7 @@
</div>
<div class="form-group row">
<div class="col-12">
<button id="loginButton" class="btn"
v-bind:class="{ 'btn-warning': version===1, 'btn-primary': version===2 }">
<button id="loginButton" class="btn btn-primary">
{{$t('Reset my password')}}
</button>
</div>
@@ -35,14 +34,10 @@
</template>
<script type="text/ecmascript-6">
import User from '../api/user';
import {mapActions, mapGetters} from 'vuex';
import message from '../services/message';
import MasterPassword from '../components/MasterPassword.vue';

export default {
computed: {
...mapGetters(['version'])
},
components: {
MasterPassword
},


+ 0
- 20
test/e2e/specs/defaultOptions.js Целия файл

@@ -1,20 +0,0 @@
var assert = require("assert");

module.exports = {
"Set default options": function(browser) {
browser
.url(browser.launch_url)
.waitForElementVisible("#site")
.setValue("#site", "lesspass.com")
.setValue("#login", "test@lesspass.com")
.click(".showOptions__btn")
.waitForElementVisible("#saveOptions__btn")
.click("#saveOptions__btn")
.click("#title")
.refresh()
.assert.value("#site", "lesspass.com")
.assert.value("#login", "test@lesspass.com");

browser.end();
}
};

+ 0
- 18
test/unit/store.getters.js Целия файл

@@ -1,24 +1,6 @@
import test from "ava";
import * as getters from "../../src/store/getters";

test("version", t => {
const state = {
password: { version: 1 },
defaultPassword: { version: 2 }
};
const version = getters.version(state);
t.is(version, 1);
});

test("version no password return default password version", t => {
const state = {
password: null,
defaultPassword: { version: 2 }
};
const version = getters.version(state);
t.is(version, 2);
});

test("passwordURL", t => {
const state = {
password: {


+ 18
- 35
test/unit/store.mutations.js Целия файл

@@ -13,6 +13,16 @@ test("LOGOUT", t => {
t.false(state.authenticated);
});

test("RESET_PASSWORD set default password", t => {
const RESET_PASSWORD = mutations[types.RESET_PASSWORD];
const state = {
password: { counter: 2 },
defaultPassword: { counter: 1 }
};
RESET_PASSWORD(state);
t.is(state.password.counter, 1);
});

test("LOGOUT clean user personal info", t => {
const LOGOUT = mutations[types.LOGOUT];
const state = {
@@ -24,7 +34,7 @@ test("LOGOUT clean user personal info", t => {
LOGOUT(state);
t.true(state.token === null);
t.is(state.passwords.length, 0);
t.is(state.password.counter, 1);
t.is(state.password.counter, 2);
});

test("LOGIN", t => {
@@ -45,18 +55,18 @@ test("SET_TOKEN", t => {
test("SET_PASSWORD", t => {
const SET_PASSWORD = mutations[types.SET_PASSWORD];
const state = { password: null };
SET_PASSWORD(state, { password: { uppercase: true, version: 2 } });
t.is(state.password.version, 2);
SET_PASSWORD(state, { password: { uppercase: true, counter: 2 } });
t.is(state.password.counter, 2);
t.true(state.password.uppercase);
});

test("SET_PASSWORD immutable", t => {
const SET_PASSWORD = mutations[types.SET_PASSWORD];
const state = {};
const password = { version: 2 };
const password = { counter: 2 };
SET_PASSWORD(state, { password });
password.version = 1;
t.is(state.password.version, 2);
password.counter = 1;
t.is(state.password.counter, 2);
});

test("SET_DEFAULT_OPTIONS", t => {
@@ -120,33 +130,6 @@ test("SET_BASE_URL", t => {
t.is(state.baseURL, baseURL);
});

test("SET_VERSION", t => {
const SET_VERSION = mutations[types.SET_VERSION];
const state = {
password: { version: 2 }
};
SET_VERSION(state, { version: 1 });
t.is(state.password.version, 1);
});

test("SET_VERSION 1 should modify length to 12", t => {
const SET_VERSION = mutations[types.SET_VERSION];
const state = {
password: { length: 16, version: 2 }
};
SET_VERSION(state, { version: 1 });
t.is(state.password.length, 12);
});

test("SET_VERSION 2 should modify length to 16", t => {
const SET_VERSION = mutations[types.SET_VERSION];
const state = {
password: { length: 12, version: 1 }
};
SET_VERSION(state, { version: 2 });
t.is(state.password.length, 16);
});

test("LOAD_PASSWORD_PROFILE", t => {
const state = {
password: {
@@ -253,7 +236,7 @@ test("LOAD_PASSWORD_PROFILE with no site keep password profile", t => {
site: "example.org",
login: "contact@example.org",
length: 8,
version: 1
version: 2
},
defaultPassword: {
login: "",
@@ -265,7 +248,7 @@ test("LOAD_PASSWORD_PROFILE with no site keep password profile", t => {
t.is(state.password.site, "example.org");
t.is(state.password.login, "contact@example.org");
t.is(state.password.length, 8);
t.is(state.password.version, 1);
t.is(state.password.version, 2);
});

test("LOAD_PASSWORD_PROFILE no passwords", t => {


Зареждане…
Отказ
Запис