* remove checkbox and replace with a button * remove inner-addon to clean the interface * add encrypt master password feature in reset password page fix https://github.com/lesspass/lesspass/issues/227 fix https://github.com/lesspass/lesspass/issues/110pull/342/head
@@ -0,0 +1,48 @@ | |||
<template> | |||
<div id="encryptMasterPassword"> | |||
<master-password | |||
v-model="password" | |||
showEncryptMasterPassword="true" | |||
v-on:input="$emit('input', password)" | |||
v-on:encryptMasterPassword="encryptMasterPassword"></master-password> | |||
</div> | |||
</template> | |||
<script type="text/ecmascript-6"> | |||
import LessPass from 'lesspass'; | |||
import MasterPassword from './MasterPassword.vue'; | |||
import message from '../services/message'; | |||
export default { | |||
components: { | |||
MasterPassword | |||
}, | |||
props: ['value', 'email'], | |||
data(){ | |||
return { | |||
password: this.value | |||
} | |||
}, | |||
methods: { | |||
encryptMasterPassword(){ | |||
if (!this.email) { | |||
message.error(this.$t('EmailRequired', 'An email is required')); | |||
return; | |||
} | |||
const defaultPasswordProfile = { | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: true, | |||
length: 16, | |||
counter: 1, | |||
version: 2, | |||
}; | |||
return LessPass | |||
.generatePassword('lesspass.com', this.email, this.password, defaultPasswordProfile) | |||
.then(generatedPassword => { | |||
this.password = generatedPassword; | |||
}); | |||
} | |||
} | |||
} | |||
</script> |
@@ -1,7 +1,15 @@ | |||
<template> | |||
<div id="masterPassword" class="inner-addon left-addon input-group"> | |||
<div id="masterPassword" class="input-group"> | |||
<span class="input-group-btn" v-if="showEncryptMasterPassword"> | |||
<button type="button" | |||
tabindex="-1" | |||
class="btn btn-secondary hint--right hint--medium" | |||
v-on:click="encryptMasterPassword()" | |||
v-bind:data-hint="$t('EncryptMasterPassword', 'Click me to encrypt this password before sending it to lesspass.com')"> | |||
<i class="fa fa-shield"></i> | |||
</button> | |||
</span> | |||
<label for="password" class="sr-only">{{ $t('Master Password') }}</label> | |||
<i class="fa fa-lock"></i> | |||
<input id="password" | |||
name="password" | |||
ref="password" | |||
@@ -26,7 +34,13 @@ | |||
components: { | |||
Fingerprint | |||
}, | |||
props: ['value', 'keyupEnter'], | |||
props: { | |||
value: '', | |||
keyupEnter: '', | |||
showEncryptMasterPassword: { | |||
'default': false | |||
} | |||
}, | |||
data(){ | |||
return { | |||
fingerprint: '', | |||
@@ -62,6 +76,9 @@ | |||
if (typeof this.keyupEnter !== 'undefined' && this.password) { | |||
this.keyupEnter() | |||
} | |||
}, | |||
encryptMasterPassword(){ | |||
this.$emit('encryptMasterPassword') | |||
} | |||
} | |||
} | |||
@@ -10,39 +10,25 @@ | |||
<template> | |||
<form v-on:submit.prevent="signIn"> | |||
<div class="form-group"> | |||
<div class="inner-addon left-addon"> | |||
<i class="fa fa-globe"></i> | |||
<input id="baseURL" | |||
class="form-control" | |||
type="text" | |||
placeholder="https://lesspass.com" | |||
v-model="baseURL"> | |||
</div> | |||
<input id="baseURL" | |||
class="form-control" | |||
type="text" | |||
v-bind:placeholder="$t('LessPass Database Url')" | |||
v-model="baseURL"> | |||
</div> | |||
<div class="form-group row"> | |||
<div class="col-12"> | |||
<div class="inner-addon left-addon"> | |||
<i class="fa fa-user"></i> | |||
<input id="email" | |||
class="form-control" | |||
name="username" | |||
type="email" | |||
v-bind:placeholder="$t('Email')" | |||
required | |||
v-model="email"> | |||
</div> | |||
<input id="email" | |||
class="form-control" | |||
name="username" | |||
type="email" | |||
v-bind:placeholder="$t('Email')" | |||
required | |||
v-model="email"> | |||
</div> | |||
</div> | |||
<div class="form-group mb-2"> | |||
<master-password v-model="password"></master-password> | |||
<label class="custom-control custom-checkbox hint--top hint--medium mb-0" | |||
v-bind:data-hint="$t('EncryptMasterPassword', 'Click me to encrypt this password before sending it to lesspass.com')"> | |||
<input type="checkbox" class="custom-control-input" v-model="transformMasterPassword"> | |||
<span class="custom-control-indicator"></span> | |||
<span class="custom-control-description text-muted"> | |||
{{$t('Encrypt my master password')}} | |||
</span> | |||
</label> | |||
<div class="form-group"> | |||
<encrypt-master-password v-model="password" v-bind:email="email"></encrypt-master-password> | |||
</div> | |||
<div class="form-group row no-gutters mb-0"> | |||
<div class="col"> | |||
@@ -65,10 +51,9 @@ | |||
</form> | |||
</template> | |||
<script type="text/ecmascript-6"> | |||
import LessPass from 'lesspass'; | |||
import User from '../api/user'; | |||
import {mapGetters} from 'vuex'; | |||
import MasterPassword from '../components/MasterPassword.vue'; | |||
import EncryptMasterPassword from '../components/EncryptMasterPassword.vue'; | |||
import message from '../services/message'; | |||
export default { | |||
@@ -76,38 +61,15 @@ | |||
return { | |||
email: '', | |||
password: '', | |||
baseURL: 'https://lesspass.com', | |||
transformMasterPassword: false, | |||
baseURL: 'https://lesspass.com' | |||
}; | |||
}, | |||
components: { | |||
MasterPassword | |||
EncryptMasterPassword | |||
}, | |||
computed: { | |||
...mapGetters(['version']) | |||
}, | |||
watch: { | |||
password: function() { | |||
this.transformMasterPassword = false; | |||
}, | |||
transformMasterPassword: function(transformPassword) { | |||
if (!transformPassword) { | |||
return; | |||
} | |||
const defaultPasswordProfile = { | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: true, | |||
length: 16, | |||
counter: 1, | |||
version: 2, | |||
}; | |||
return LessPass.generatePassword('lesspass.com', this.email, this.password, defaultPasswordProfile).then(generatedPassword => { | |||
this.password = generatedPassword; | |||
}); | |||
} | |||
}, | |||
methods: { | |||
formIsValid(){ | |||
if (!this.email || !this.password || !this.baseURL) { | |||
@@ -3,33 +3,6 @@ | |||
font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, sans-serif; | |||
} | |||
.inner-addon i { | |||
position: absolute; | |||
padding: 10px; | |||
pointer-events: none; | |||
z-index: 10; | |||
} | |||
.inner-addon { | |||
position: relative; | |||
} | |||
.left-addon i { | |||
left: 0; | |||
} | |||
.right-addon i { | |||
right: 0; | |||
} | |||
.left-addon input { | |||
padding-left: 30px; | |||
} | |||
.right-addon input { | |||
padding-right: 30px; | |||
} | |||
div.awesomplete { | |||
display: block; | |||
} | |||
@@ -41,36 +14,30 @@ | |||
<template> | |||
<form id="password-generator"> | |||
<div class="form-group"> | |||
<div class="inner-addon left-addon"> | |||
<label for="site" class="sr-only">{{ $t('Site') }}</label> | |||
<i class="fa fa-globe"></i> | |||
<input id="site" | |||
name="site" | |||
type="text" | |||
ref="site" | |||
class="form-control awesomplete" | |||
autocorrect="off" | |||
autocapitalize="none" | |||
v-bind:placeholder="$t('Site')" | |||
v-model="password.site"> | |||
</div> | |||
<label for="site" class="sr-only">{{ $t('Site') }}</label> | |||
<input id="site" | |||
name="site" | |||
type="text" | |||
ref="site" | |||
class="form-control awesomplete" | |||
autocorrect="off" | |||
autocapitalize="none" | |||
v-bind:placeholder="$t('Site')" | |||
v-model="password.site"> | |||
</div> | |||
<remove-auto-complete></remove-auto-complete> | |||
<div class="form-group"> | |||
<div class="inner-addon left-addon"> | |||
<label for="login" class="sr-only">{{ $t('Login') }}</label> | |||
<i class="fa fa-user"></i> | |||
<input id="login" | |||
name="login" | |||
type="text" | |||
ref="login" | |||
class="form-control" | |||
autocomplete="off" | |||
autocorrect="off" | |||
autocapitalize="none" | |||
v-bind:placeholder="$t('Login')" | |||
v-model="password.login"> | |||
</div> | |||
<label for="login" class="sr-only">{{ $t('Login') }}</label> | |||
<input id="login" | |||
name="login" | |||
type="text" | |||
ref="login" | |||
class="form-control" | |||
autocomplete="off" | |||
autocorrect="off" | |||
autocapitalize="none" | |||
v-bind:placeholder="$t('Login')" | |||
v-model="password.login"> | |||
</div> | |||
<div class="form-group"> | |||
<master-password ref="masterPassword" v-model="masterPassword" | |||
@@ -2,15 +2,12 @@ | |||
<form v-on:submit.prevent="resetPassword"> | |||
<div class="form-group row"> | |||
<div class="col-12"> | |||
<div class="inner-addon left-addon"> | |||
<i class="fa fa-user"></i> | |||
<input id="email" | |||
class="form-control" | |||
name="email" | |||
type="email" | |||
placeholder="Email" | |||
v-model="email"> | |||
</div> | |||
<input id="email" | |||
class="form-control" | |||
name="email" | |||
type="email" | |||
placeholder="Email" | |||
v-model="email"> | |||
</div> | |||
</div> | |||
<div class="form-group row"> | |||
@@ -2,16 +2,17 @@ | |||
<form v-on:submit.prevent="resetPasswordConfirm"> | |||
<div class="form-group row"> | |||
<div class="col-12"> | |||
<div class="inner-addon left-addon"> | |||
<i class="fa fa-lock"></i> | |||
<input id="new-password" | |||
class="form-control" | |||
name="new-password" | |||
type="password" | |||
autocomplete="new-password" | |||
v-bind:placeholder="$t('New Password')" | |||
v-model="new_password"> | |||
</div> | |||
<input id="email" | |||
class="form-control" | |||
name="email" | |||
type="email" | |||
placeholder="Email" | |||
v-model="email"> | |||
</div> | |||
</div> | |||
<div class="form-group row"> | |||
<div class="col-12"> | |||
<encrypt-master-password v-model="newPassword" v-bind:email="email"></encrypt-master-password> | |||
</div> | |||
</div> | |||
<div class="form-group row"> | |||
@@ -28,39 +29,44 @@ | |||
import User from '../api/user'; | |||
import {mapActions, mapGetters} from 'vuex'; | |||
import message from '../services/message'; | |||
import EncryptMasterPassword from '../components/EncryptMasterPassword.vue'; | |||
export default { | |||
computed: { | |||
...mapGetters(['version']) | |||
}, | |||
components: { | |||
EncryptMasterPassword | |||
}, | |||
data() { | |||
return { | |||
new_password: '' | |||
email: '', | |||
newPassword: '' | |||
}; | |||
}, | |||
methods: { | |||
resetPasswordConfirm(){ | |||
if (!this.new_password) { | |||
message.success($t('PasswordResetRequired', 'A password is required')); | |||
if (!this.newPassword) { | |||
message.error(this.$t('PasswordResetRequired', 'A password is required')); | |||
return; | |||
} | |||
User | |||
.confirmResetPassword({ | |||
uid: this.$route.params.uid, | |||
token: this.$route.params.token, | |||
new_password: this.new_password | |||
newPassword: this.newPassword | |||
}) | |||
.then(() => { | |||
message.success($t('PasswordResetSuccessful', 'Your password was reset successfully.')); | |||
message.success(this.$t('PasswordResetSuccessful', 'Your password was reset successfully.')); | |||
}) | |||
.catch(err => { | |||
if (err.response.status === 400) { | |||
message.error($t('ResetLinkExpired', 'This password reset link has expired.')); | |||
message.error(this.$t('ResetLinkExpired', 'This password reset link has expired.')); | |||
} else { | |||
message.displayGenericError(); | |||
} | |||
}); | |||
} | |||
}, | |||
computed: { | |||
...mapGetters(['version']) | |||
}, | |||
} | |||
} | |||
</script> |