@@ -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." | |||
} |
@@ -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." | |||
} |
@@ -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." | |||
} |
@@ -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." | |||
} |
@@ -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 的电子邮件,里面提供有重置密码的操作说明。" | |||
} |
@@ -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的电子邮件,并提供如何重置密码的说明。" | |||
} |
@@ -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" | |||
@@ -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; | |||
} |
@@ -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'); | |||
@@ -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); | |||
}); | |||
} | |||
} | |||
@@ -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() { | |||
@@ -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> | |||
@@ -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> |
@@ -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; | |||
} | |||
@@ -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." | |||
} |
@@ -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." | |||
} |
@@ -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." | |||
} |
@@ -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." | |||
} |
@@ -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 的电子邮件,里面提供有重置密码的操作说明。" | |||
} |
@@ -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的电子邮件,并提供如何重置密码的说明。" | |||
} |
@@ -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 }) => { | |||
@@ -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 | |||
@@ -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"; | |||
@@ -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; | |||
}, | |||
@@ -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) { | |||
@@ -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')) | |||
@@ -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() { | |||
@@ -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 | |||
}, | |||
@@ -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(); | |||
} | |||
}; |
@@ -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: { | |||
@@ -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 => { | |||