Browse Source

Change MasterPassword so that it does not depend on services

pull/342/head
Guillaume Vincent 7 years ago
parent
commit
9cb82c6348
6 changed files with 137 additions and 167 deletions
  1. +0
    -52
      src/components/EncryptMasterPassword.vue
  2. +0
    -75
      src/components/Fingerprint.vue
  3. +116
    -32
      src/components/MasterPassword.vue
  4. +9
    -3
      src/views/Login.vue
  5. +3
    -2
      src/views/PasswordGenerator.vue
  6. +9
    -3
      src/views/PasswordResetConfirm.vue

+ 0
- 52
src/components/EncryptMasterPassword.vue View File

@@ -1,52 +0,0 @@
<template>
<div id="encryptMasterPassword">
<master-password
v-model="password"
v-on:input="$emit('input', password)"></master-password>
<button type="button"
class="btn btn-link btn-sm hint--top hint--medium p-0"
v-on:click="encryptMasterPassword()"
v-bind:data-hint="$t('EncryptMasterPassword', 'Click me to encrypt this password before sending it to lesspass.com')">
<small>{{$t('Encrypt my master password')}}</small>
</button>
</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>

+ 0
- 75
src/components/Fingerprint.vue View File

@@ -1,75 +0,0 @@
<style>
#fingerprint {
min-width: 90px;
text-align: center;
background-color: transparent;
color: white;
}

#fingerprint i {
color: black;
position: relative;
padding: 0;
text-shadow: 1px 1px 0 white;
font-size: 1.3em;
}
</style>
<template>
<span class="input-group-btn" v-if="fingerprint">
<button id="fingerprint" class="btn" type="button" tabindex="-1">
<small class="hint--left">
<i class="fa fa-fw" v-bind:class="[icon1]" v-bind:style="{ color: color1 }"></i>
<i class="fa fa-fw" v-bind:class="[icon2]" v-bind:style="{ color: color2 }"></i>
<i class="fa fa-fw" v-bind:class="[icon3]" v-bind:style="{ color: color3 }"></i>
</small>
</button>
</span>
</template>

<script type="text/ecmascript-6">
import LessPass from 'lesspass';

export default {
data(){
return {
icon1: '',
icon2: '',
icon3: '',
color1: '',
color2: '',
color3: ''
}
},
props: ['fingerprint'],
watch: {
fingerprint(newFingerprint) {
if (!newFingerprint) {
return;
}
LessPass.createFingerprint(newFingerprint).then(sha256 => {
const hash1 = sha256.substring(0, 6);
const hash2 = sha256.substring(6, 12);
const hash3 = sha256.substring(12, 18);
this.icon1 = this.getIcon(hash1);
this.icon2 = this.getIcon(hash2);
this.icon3 = this.getIcon(hash3);
this.color1 = this.getColor(hash1);
this.color2 = this.getColor(hash2);
this.color3 = this.getColor(hash3);
});
}
},
methods: {
getColor(color) {
var colors = ['#000000', '#074750', '#009191', '#FF6CB6', '#FFB5DA', '#490092', '#006CDB', '#B66DFF', '#6DB5FE', '#B5DAFE', '#920000', '#924900', '#DB6D00', '#24FE23'];
var index = parseInt(color, 16) % colors.length;
return colors[index];
},
getIcon(hash) {
var icons = ['fa-hashtag', 'fa-heart', 'fa-hotel', 'fa-university', 'fa-plug', 'fa-ambulance', 'fa-bus', 'fa-car', 'fa-plane', 'fa-rocket', 'fa-ship', 'fa-subway', 'fa-truck', 'fa-jpy', 'fa-eur', 'fa-btc', 'fa-usd', 'fa-gbp', 'fa-archive', 'fa-area-chart', 'fa-bed', 'fa-beer', 'fa-bell', 'fa-binoculars', 'fa-birthday-cake', 'fa-bomb', 'fa-briefcase', 'fa-bug', 'fa-camera', 'fa-cart-plus', 'fa-certificate', 'fa-coffee', 'fa-cloud', 'fa-coffee', 'fa-comment', 'fa-cube', 'fa-cutlery', 'fa-database', 'fa-diamond', 'fa-exclamation-circle', 'fa-eye', 'fa-flag', 'fa-flask', 'fa-futbol-o', 'fa-gamepad', 'fa-graduation-cap'];
var index = parseInt(hash, 16) % icons.length;
return icons[index];
}
}
}
</script>

+ 116
- 32
src/components/MasterPassword.vue View File

@@ -1,53 +1,96 @@
<style>
#fingerprint {
min-width: 90px;
text-align: center;
background-color: transparent;
color: white;
}

#fingerprint i {
color: black;
position: relative;
padding: 0;
text-shadow: 1px 1px 0 white;
font-size: 1.3em;
}
</style>
<template>
<div id="masterPassword" class="input-group inner-addon left-addon">
<label for="password" class="sr-only">{{ $t('Master Password') }}</label>
<div class="masterPassword">
<div class="input-group inner-addon left-addon">
<label for="passwordField" class="sr-only">
{{ label }}
</label>
<i class="fa fa-lock"></i>
<input id="password"
name="password"
ref="password"
<input id="passwordField"
name="passwordField"
ref="passwordField"
type="password"
class="form-control"
autocorrect="off"
autocapitalize="off"
v-model="password"
v-bind:placeholder="$t('Master Password')"
v-on:input="updatePassword($event.target.value)"
v-on:keyup.enter="triggerEnterMethod"
v-on:blur="hidePassword($refs.password)">
<fingerprint v-bind:fingerprint="fingerprint" v-on:click.native="togglePasswordType($refs.password)">
</fingerprint>
v-bind:placeholder="label"
v-on:blur="hidePassword($refs.passwordField)">
<span class="input-group-btn"
v-if="fingerprint"
v-on:click="togglePasswordType($refs.passwordField)">
<button id="fingerprint" class="btn" type="button" tabindex="-1">
<small class="hint--left">
<i class="fa fa-fw" v-bind:class="[icon1]" v-bind:style="{ color: color1 }"></i>
<i class="fa fa-fw" v-bind:class="[icon2]" v-bind:style="{ color: color2 }"></i>
<i class="fa fa-fw" v-bind:class="[icon3]" v-bind:style="{ color: color3 }"></i>
</small>
</button>
</span>
</div>
<button type="button"
class="btn btn-link btn-sm p-0"
v-if="showEncryptButton"
v-on:click="encryptMasterPassword($refs.passwordField.value)"
v-bind:data-hint="EncryptButtonHelp"
v-bind:class="{'disabled': email === '', 'hint--top hint--medium': email !== ''}">
<small>{{ EncryptButtonText }}</small>
</button>
</div>
</template>
<script type="text/ecmascript-6">
<script>
import LessPass from 'lesspass';
import debounce from 'lodash.debounce';
import Fingerprint from './Fingerprint.vue';

export default {
components: {
Fingerprint
name: 'masterPassword',
props: {
value: String,
label: String,
email: String,
showEncryptButton: {
type: Boolean,
default: false
},
EncryptButtonHelp: String,
EncryptButtonText: String
},
props: ['value', 'keyupEnter'],
data(){
return {
password: this.value,
fingerprint: '',
password: this.value
icon1: '',
icon2: '',
icon3: '',
color1: '',
color2: '',
color3: ''
}
},
watch: {
'value': function(password) {
this.password = password;
this.updatePassword(password);
password(newPassword){
const fakePassword = Math.random().toString(36).substring(7);
this.setFingerprint(fakePassword);
this.showRealFingerprint(newPassword);
this.$emit('input', newPassword);
}
},
methods: {
updatePassword: function(password) {
this.fingerprint = Math.random().toString(36).substring(7);
this.showRealFingerprint(password);
this.$emit('input', password)
},
showRealFingerprint: debounce(function(password) {
this.fingerprint = password;
}, 500),
togglePasswordType(element){
if (element.type === 'password') {
element.type = 'text';
@@ -58,10 +101,51 @@
hidePassword(element){
element.type = 'password';
},
triggerEnterMethod(){
if (typeof this.keyupEnter !== 'undefined' && this.password) {
this.keyupEnter()
}
getColor(color) {
var colors = ['#000000', '#074750', '#009191', '#FF6CB6', '#FFB5DA', '#490092', '#006CDB', '#B66DFF', '#6DB5FE', '#B5DAFE', '#920000', '#924900', '#DB6D00', '#24FE23'];
var index = parseInt(color, 16) % colors.length;
return colors[index];
},
getIcon(hash) {
var icons = ['fa-hashtag', 'fa-heart', 'fa-hotel', 'fa-university', 'fa-plug', 'fa-ambulance', 'fa-bus', 'fa-car', 'fa-plane', 'fa-rocket', 'fa-ship', 'fa-subway', 'fa-truck', 'fa-jpy', 'fa-eur', 'fa-btc', 'fa-usd', 'fa-gbp', 'fa-archive', 'fa-area-chart', 'fa-bed', 'fa-beer', 'fa-bell', 'fa-binoculars', 'fa-birthday-cake', 'fa-bomb', 'fa-briefcase', 'fa-bug', 'fa-camera', 'fa-cart-plus', 'fa-certificate', 'fa-coffee', 'fa-cloud', 'fa-coffee', 'fa-comment', 'fa-cube', 'fa-cutlery', 'fa-database', 'fa-diamond', 'fa-exclamation-circle', 'fa-eye', 'fa-flag', 'fa-flask', 'fa-futbol-o', 'fa-gamepad', 'fa-graduation-cap'];
var index = parseInt(hash, 16) % icons.length;
return icons[index];
},
setFingerprint(password){
LessPass.createFingerprint(password).then(fingerprint => {
this.fingerprint = fingerprint;

const hash1 = fingerprint.substring(0, 6);
this.icon1 = this.getIcon(hash1);
this.color1 = this.getColor(hash1);

const hash2 = fingerprint.substring(6, 12);
this.icon2 = this.getIcon(hash2);
this.color2 = this.getColor(hash2);

const hash3 = fingerprint.substring(12, 18);
this.icon3 = this.getIcon(hash3);
this.color3 = this.getColor(hash3);
});
},
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,
};
return LessPass
.generatePassword('lesspass.com', this.email, password, defaultPasswordProfile)
.then(generatedPassword => {
this.password = generatedPassword;
});
}
}
}


+ 9
- 3
src/views/Login.vue View File

@@ -34,7 +34,13 @@
</div>
</div>
<div class="form-group mb-2">
<encrypt-master-password v-model="password" v-bind:email="email"></encrypt-master-password>
<master-password
v-model="password"
v-bind:label="$t('Master Password')"
v-bind:email="email"
v-bind:showEncryptButton="true"
v-bind:EncryptButtonHelp="$t('EncryptMasterPassword', 'Click me to encrypt this password before sending it to lesspass.com')"
v-bind:EncryptButtonText="$t('Encrypt my master password')"></master-password>
</div>
<div class="form-group row no-gutters mb-0">
<div class="col">
@@ -61,7 +67,7 @@
<script type="text/ecmascript-6">
import User from '../api/user';
import {mapGetters} from 'vuex';
import EncryptMasterPassword from '../components/EncryptMasterPassword.vue';
import MasterPassword from '../components/MasterPassword.vue';
import message from '../services/message';

export default {
@@ -73,7 +79,7 @@
};
},
components: {
EncryptMasterPassword
MasterPassword
},
computed: {
...mapGetters(['version'])


+ 3
- 2
src/views/PasswordGenerator.vue View File

@@ -47,7 +47,8 @@
</div>
<div class="form-group">
<master-password ref="masterPassword" v-model="masterPassword"
:keyupEnter="generatePassword"></master-password>
:keyupEnter="generatePassword"
v-bind:label="$t('Master Password')"></master-password>
</div>
<div class="form-group"
v-bind:class="{ 'mb-0': !showOptions }">
@@ -237,7 +238,7 @@
focusBestInputField(){
const site = this.$refs.site;
const login = this.$refs.login;
const masterPassword = this.$refs.masterPassword.$refs.password;
const masterPassword = this.$refs.masterPassword.$refs.passwordField;
site.value ? (login.value ? masterPassword.focus() : login.focus()) : site.focus();
},
copyPassword(){


+ 9
- 3
src/views/PasswordResetConfirm.vue View File

@@ -15,7 +15,13 @@
</div>
<div class="form-group row">
<div class="col-12">
<encrypt-master-password v-model="newPassword" v-bind:email="email"></encrypt-master-password>
<master-password
v-model="password"
v-bind:label="$t('Master Password')"
v-bind:email="email"
v-bind:showEncryptButton="true"
v-bind:EncryptButtonHelp="$t('EncryptMasterPassword', 'Click me to encrypt this password before sending it to lesspass.com')"
v-bind:EncryptButtonText="$t('Encrypt my master password')"></master-password>
</div>
</div>
<div class="form-group row">
@@ -32,14 +38,14 @@
import User from '../api/user';
import {mapActions, mapGetters} from 'vuex';
import message from '../services/message';
import EncryptMasterPassword from '../components/EncryptMasterPassword.vue';
import MasterPassword from '../components/MasterPassword.vue';

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


Loading…
Cancel
Save