Browse Source

Encrypt master password everywhere

b5
Guillaume Vincent 3 years ago
parent
commit
8c5a5aa481
16 changed files with 268 additions and 235 deletions
  1. +1
    -1
      packages/lesspass-pure/src/components/Avatar.vue
  2. +0
    -1
      packages/lesspass-pure/src/components/InputSite.vue
  3. +11
    -42
      packages/lesspass-pure/src/components/MasterPassword.vue
  4. +1
    -1
      packages/lesspass-pure/src/components/Menu.vue
  5. +1
    -1
      packages/lesspass-pure/src/components/Message.vue
  6. +1
    -1
      packages/lesspass-pure/src/components/Options.vue
  7. +1
    -1
      packages/lesspass-pure/src/components/PasswordProfile.vue
  8. +11
    -0
      packages/lesspass-pure/src/services/encryption.js
  9. +60
    -59
      packages/lesspass-pure/src/views/Login.vue
  10. +36
    -18
      packages/lesspass-pure/src/views/MyAccount.vue
  11. +4
    -5
      packages/lesspass-pure/src/views/PasswordGenerator.vue
  12. +10
    -11
      packages/lesspass-pure/src/views/PasswordReset.vue
  13. +45
    -37
      packages/lesspass-pure/src/views/PasswordResetConfirm.vue
  14. +1
    -1
      packages/lesspass-pure/src/views/Passwords.vue
  15. +71
    -52
      packages/lesspass-pure/src/views/Register.vue
  16. +14
    -4
      packages/lesspass-pure/src/views/Settings.vue

+ 1
- 1
packages/lesspass-pure/src/components/Avatar.vue View File

@@ -14,7 +14,7 @@
<template>
<div v-bind:style="avatarStyle" class="passwordProfile__avatar">{{firstLetter}}</div>
</template>
<script type="text/ecmascript-6">
<script>
export default {
name: "avatar",
props: {


+ 0
- 1
packages/lesspass-pure/src/components/InputSite.vue View File

@@ -86,7 +86,6 @@ export default {
const suggestions = getSuggestions(newValue).map(suggestion => {
return { site: suggestion, suggestion: true, login: "" };
});
console.log(this.passwords)
this.awesomplete.list = this.passwords.concat(suggestions);
}
},


+ 11
- 42
packages/lesspass-pure/src/components/MasterPassword.vue View File

@@ -23,7 +23,7 @@
id="passwordField"
name="passwordField"
ref="passwordField"
type="password"
v-bind:type="passwordType"
class="form-control"
tabindex="0"
autocorrect="off"
@@ -59,48 +59,28 @@
</button>
</span>
</div>
<small>
<div class="form-check form-switch" v-if="showEncryptButton">
<input
class="form-check-input"
type="checkbox"
id="checkPlainPassword"
v-model="checkPlainPassword"
/>
<label class="form-check-label" for="checkPlainPassword">{{
$t("Use plain password")
}}</label>
</div>
</small>
</div>
</template>
<script>
import LessPass from "lesspass";
import { debounce } from "lodash";
import defaultPasswordProfile from "../store/defaultPassword";

export default {
name: "masterPassword",
props: {
value: String,
label: String,
email: String,
showEncryptButton: {
type: Boolean,
default: false
},
EncryptButtonText: String
label: String
},
data() {
return {
passwordType: "password",
fingerprint: null,
icon1: "",
icon2: "",
icon3: "",
color1: "",
color2: "",
color3: "",
checkPlainPassword: false
color3: ""
};
},
methods: {
@@ -114,15 +94,15 @@ export default {
this.$emit("input", newPassword);
},
togglePasswordType() {
const element = this.$refs.passwordField;
if (element.type === "password") {
element.type = "text";
if (this.passwordType === "password") {
this.passwordType = "text";
} else {
element.type = "password";
this.passwordType = "password";
}
},
hidePassword() {
this.$refs.passwordField.type = "password";
hide() {
this.passwordType = "password";
this.fingerprint = null;
},
getColor(color) {
var colors = [
@@ -212,18 +192,7 @@ export default {
},
showRealFingerprint: debounce(function(password) {
this.setFingerprint(password);
}, 500),
encryptMasterPassword() {
const password = this.$refs.passwordField.value;
return LessPass.generatePassword(
"lesspass.com",
this.email,
password,
defaultPasswordProfile
).then(generatedPassword => {
this.updateValue(generatedPassword);
});
}
}, 500)
}
};
</script>

+ 1
- 1
packages/lesspass-pure/src/components/Menu.vue View File

@@ -46,7 +46,7 @@
</div>
</div>
</template>
<script type="text/ecmascript-6">
<script>
import {mapGetters} from 'vuex';

export default {


+ 1
- 1
packages/lesspass-pure/src/components/Message.vue View File

@@ -49,7 +49,7 @@
</transition>
</div>
</template>
<script type="text/ecmascript-6">
<script>
import { mapState } from "vuex";
import message from "../services/message";



+ 1
- 1
packages/lesspass-pure/src/components/Options.vue View File

@@ -181,7 +181,7 @@
</div>
</template>

<script type="text/ecmascript-6">
<script>
import { increment, decrement } from "../services/form-validator";

export default {


+ 1
- 1
packages/lesspass-pure/src/components/PasswordProfile.vue View File

@@ -37,7 +37,7 @@
</div>
</div>
</template>
<script type="text/ecmascript-6">
<script>
import Avatar from "./Avatar.vue";

export default {


+ 11
- 0
packages/lesspass-pure/src/services/encryption.js View File

@@ -0,0 +1,11 @@
import LessPass from "lesspass";
import defaultPasswordProfile from "../store/defaultPassword";

export function encryptPassword(email, password) {
return LessPass.generatePassword(
"lesspass.com",
email,
password,
defaultPasswordProfile
);
}

+ 60
- 59
packages/lesspass-pure/src/views/Login.vue View File

@@ -13,32 +13,40 @@
/>
</div>
</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"
autocapitalize="none"
v-bind:placeholder="$t('Email')"
required
v-model="email"
/>
</div>
<div class="form-group">
<div class="inner-addon left-addon">
<i class="fa fa-user"></i>
<input
id="email"
class="form-control"
name="username"
type="email"
autocapitalize="none"
v-bind:placeholder="$t('Email')"
required
v-model="email"
/>
</div>
</div>
<div class="form-group mb-2">
<div class="form-group mb-1">
<master-password
v-model="password"
v-bind:label="$t('Master Password')"
v-bind:email="email"
v-bind:showEncryptButton="true"
v-bind:EncryptButtonText="$t('Encrypt my master password')"
></master-password>
</div>
<div class="form-check form-switch mb-3">
<input
id="encryptMasterPassword"
class="form-check-input"
type="checkbox"
v-model="encryptMasterPassword"
/>
<label class="form-check-label" for="encryptMasterPassword">
<small>
{{ $t("Encrypt my master password") }}
</small>
</label>
</div>
<div class="form-group">
<button id="signInButton" class="btn btn-primary btn-block">
{{ $t("Sign In") }}
@@ -71,29 +79,19 @@
</div>
</form>
</template>
<script type="text/ecmascript-6">
<script>
import User from "../api/user";
import { defaultbaseURL } from "../api/default";
import MasterPassword from "../components/MasterPassword.vue";
import message from "../services/message";
import LessPass from "lesspass";
import defaultPasswordProfile from "../store/defaultPassword";

function encryptPassword(email, password) {
return LessPass.generatePassword(
"lesspass.com",
email,
password,
defaultPasswordProfile
);
return res;
}
import { encryptPassword } from "../services/encryption";

export default {
data() {
return {
email: "",
password: "",
encryptMasterPassword: true,
baseURL: localStorage.getItem("baseURL") || defaultbaseURL
};
},
@@ -116,34 +114,37 @@ export default {
signIn() {
if (this.formIsValid()) {
const baseURL = this.baseURL;
let checkPlainPassword = this.$children[0].checkPlainPassword
this.$store.dispatch("setBaseURL", { baseURL });
encryptPassword(this.email, this.password)
.then(encryptedPassword => {
let pass = checkPlainPassword ? this.password : encryptedPassword;
User.login({ email: this.email, password: pass })
.then(response => {
this.$store.dispatch("login", response.data);
this.$store.dispatch("cleanMessage");
this.$router.push({ name: "home" });
})
.catch(err => {
if (err.response === undefined && baseURL !== defaultbaseURL) {
message.error(
this.$t("DBNotRunning", "Your LessPass Database is not running")
);
} else if (err.response && err.response.status === 401) {
message.error(
this.$t(
"LoginIncorrectError",
"The email and password you entered did not match our records. Please double-check and try again."
)
);
} else {
message.displayGenericError();
}
});
});
encryptPassword(this.email, this.password).then(encryptedPassword => {
const password = this.encryptMasterPassword
? encryptedPassword
: this.password;
User.login({ email: this.email, password })
.then(response => {
this.$store.dispatch("login", response.data);
this.$store.dispatch("cleanMessage");
this.$router.push({ name: "home" });
})
.catch(err => {
if (err.response === undefined && baseURL !== defaultbaseURL) {
message.error(
this.$t(
"DBNotRunning",
"Your LessPass Database is not running"
)
);
} else if (err.response && err.response.status === 401) {
message.error(
this.$t(
"LoginIncorrectError",
"The email and password you entered did not match our records. Please double-check and try again."
)
);
} else {
message.displayGenericError();
}
});
});
}
}
}


+ 36
- 18
packages/lesspass-pure/src/views/MyAccount.vue View File

@@ -1,6 +1,6 @@
<template>
<div>
<legend>{{ $t("Change my password") }}</legend>
<h5>{{ $t("Change my password") }}</h5>
<form v-on:submit.prevent="changePassword">
<div class="form-group row">
<div class="col-12">
@@ -22,9 +22,6 @@
<master-password
v-model="current_password"
v-bind:label="$t('Current Master Password')"
v-bind:email="email"
v-bind:showEncryptButton="true"
v-bind:EncryptButtonText="$t('Encrypt my master password')"
></master-password>
</div>
</div>
@@ -33,9 +30,6 @@
<master-password
v-model="new_password"
v-bind:label="$t('New Master Password')"
v-bind:email="email"
v-bind:showEncryptButton="true"
v-bind:EncryptButtonText="$t('Encrypt my master password')"
></master-password>
</div>
</div>
@@ -48,16 +42,21 @@
</div>
</form>
<hr />
<button id="signOutButton" class="btn btn-success btn-block" type="button" v-on:click="logout">
<button
id="signOutButton"
class="btn btn-success btn-block"
type="button"
v-on:click="logout"
>
{{ $t("Sign out") }}
</button>
</div>
</template>
<script type="text/ecmascript-6">
<script>
import User from "../api/user";
import message from "../services/message";
import MasterPassword from "../components/MasterPassword.vue";
import { mapState } from "vuex";
import { encryptPassword } from "../services/encryption";

export default {
components: {
@@ -75,18 +74,37 @@ export default {
this.$store.dispatch("logout");
this.$router.push({ name: "home" }).catch(() => {});
},
changePassword() {
if (!this.current_password) {
message.error(this.$t("PasswordRequired", "A password is required"));
changePassword: async function() {
if (!this.email) {
message.error(this.$t("EmailRequiredError", "Email is required"));
return;
}
if (!this.new_password) {
message.error(this.$t("PasswordRequired", "A password is required"));
if (!this.current_password || !this.new_password) {
message.error(
this.$t(
"MasterPasswordsRequired",
"Old master password and new master password are required."
)
);
return;
}
if (this.current_password === this.new_password) {
message.error(
this.$t(
"MasterPasswordsEqualsNoNeedToChange",
"Old master password and new master password are the same. No need to change it!"
)
);
return;
}
const current_password = await encryptPassword(
this.email,
this.current_password
);
const new_password = await encryptPassword(this.email, this.new_password);
User.changePassword({
current_password: this.current_password,
new_password: this.new_password
current_password,
new_password
})
.then(() => {
message.success(
@@ -95,7 +113,7 @@ export default {
"Your password was changed successfully."
)
);
User.login({ email: this.email, password: this.new_password })
User.login({ email: this.email, password: new_password })
.then(response => {
this.$store.dispatch("login", response.data);
this.$router.push({ name: "home" });


+ 4
- 5
packages/lesspass-pure/src/views/PasswordGenerator.vue View File

@@ -51,7 +51,6 @@ div.awesomplete > ul {
<master-password
ref="masterPassword"
v-model="masterPassword"
v-on:generatePassword="generatePassword"
v-bind:label="$t('Master Password')"
></master-password>
</div>
@@ -112,7 +111,7 @@ div.awesomplete > ul {
</div>
</form>
</template>
<script type="text/ecmascript-6">
<script>
import LessPass from "lesspass";
import { mapGetters, mapState } from "vuex";
import copy from "copy-text-to-clipboard";
@@ -134,7 +133,7 @@ export default {
},
computed: {
...mapState(["password", "passwords"]),
...mapGetters(["passwordURL"]),
...mapGetters(["passwordURL"])
},
beforeMount() {
this.$store.dispatch("getPasswords").then(() => {
@@ -181,14 +180,14 @@ export default {
cleanErrors() {
clearTimeout(this.cleanTimeout);
this.passwordGenerated = "";
this.$refs.masterPassword.hidePassword();
this.$refs.masterPassword.hide();
},
cleanFormIn30Seconds() {
const thirtySecondsInMillisecond = 30 * 1000;
this.cleanTimeout = setTimeout(() => {
this.masterPassword = "";
this.passwordGenerated = "";
this.$refs.masterPassword.hidePassword();
this.$refs.masterPassword.hide();
}, thirtySecondsInMillisecond);
},
generatePassword() {


+ 10
- 11
packages/lesspass-pure/src/views/PasswordReset.vue View File

@@ -34,30 +34,29 @@
</div>
</form>
</template>
<script type="text/ecmascript-6">
import User from '../api/user';
import {mapState} from 'vuex';
import message from '../services/message';
<script>
import User from "../api/user";
import message from "../services/message";

export default {
data() {
return {
email: '',
email: ""
};
},
methods: {
resetPassword() {
if (!this.email) {
message.error(this.$t('EmailRequiredError', 'We need an email to find your account.'));
message.error(this.$t("EmailRequiredError", "Email is required"));
return;
}

User.resetPassword({email: this.email})
User.resetPassword({ email: this.email })
.then(() => {
const successMessage = this.$t(
'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.',
{email: this.email}
"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.",
{ email: this.email }
);
message.success(successMessage);
})
@@ -66,5 +65,5 @@ export default {
});
}
}
}
};
</script>

+ 45
- 37
packages/lesspass-pure/src/views/PasswordResetConfirm.vue View File

@@ -20,9 +20,6 @@
<master-password
v-model="password"
v-bind:label="$t('Master Password')"
v-bind:email="email"
v-bind:showEncryptButton="true"
v-bind:EncryptButtonText="$t('Encrypt my master password')"
></master-password>
</div>
</div>
@@ -35,11 +32,11 @@
</div>
</form>
</template>
<script type="text/ecmascript-6">
import User from '../api/user';
import message from '../services/message';
import MasterPassword from '../components/MasterPassword.vue';
import { mapState } from "vuex";
<script>
import User from "../api/user";
import message from "../services/message";
import MasterPassword from "../components/MasterPassword.vue";
import { encryptPassword } from "../services/encryption";

export default {
components: {
@@ -47,42 +44,53 @@ export default {
},
data() {
return {
email: '',
password: ''
email: "",
password: ""
};
},
methods: {
resetPasswordConfirm(){
resetPasswordConfirm() {
if (!this.password) {
message.error(this.$t('PasswordResetRequired', 'A password is required'));
message.error(
this.$t("PasswordResetRequired", "A password is required")
);
return;
}
User
.confirmResetPassword(
{
uid: this.$route.params.uid,
token: this.$route.params.token,
password: this.password
}
)
.then(() => {
message.success(this.$t('PasswordResetSuccessful', 'Your password was reset successfully.'));
User
.login({ email: this.email, password: this.password })
.then(response => {
this.$store.dispatch("login", response.data);
this.$router.push({ name: "home" });
})
.catch(err => message.displayGenericError());

encryptPassword(this.email, this.password).then(encryptedPassword => {
User.confirmResetPassword({
uid: this.$route.params.uid,
token: this.$route.params.token,
password: encryptedPassword
})
.catch(err => {
if (err.response.status === 400) {
message.error(this.$t('ResetLinkExpired', 'This password reset link has expired.'));
} else {
message.displayGenericError();
}
});
.then(() => {
message.success(
this.$t(
"PasswordResetSuccessful",
"Your password was reset successfully."
)
);
User.login({ email: this.email, password: encryptedPassword })
.then(response => {
this.$store.dispatch("login", response.data);
this.$router.push({ name: "home" });
})
.catch(err => message.displayGenericError());
})
.catch(err => {
if (err.response.status === 400) {
message.error(
this.$t(
"ResetLinkExpired",
"This password reset link has expired."
)
);
} else {
message.displayGenericError();
}
});
});
}
}
}
};
</script>

+ 1
- 1
packages/lesspass-pure/src/views/Passwords.vue View File

@@ -81,7 +81,7 @@
</div>
</div>
</template>
<script type="text/ecmascript-6">
<script>
import PasswordProfile from "../components/PasswordProfile.vue";
import { mapState } from "vuex";
import Paginate from "vuejs-paginate";


+ 71
- 52
packages/lesspass-pure/src/views/Register.vue View File

@@ -30,13 +30,10 @@
</div>
</div>
</div>
<div class="form-group mb-2">
<div class="form-group">
<master-password
v-model="password"
v-bind:label="$t('Master Password')"
v-bind:email="email"
v-bind:showEncryptButton="true"
v-bind:EncryptButtonText="$t('Encrypt my master password')"
></master-password>
</div>
<div class="form-group">
@@ -63,11 +60,12 @@
</div>
</form>
</template>
<script type="text/ecmascript-6">
<script>
import User from "../api/user";
import { defaultbaseURL } from "../api/default";
import MasterPassword from "../components/MasterPassword.vue";
import message from "../services/message";
import { encryptPassword } from "../services/encryption";

export default {
data() {
@@ -97,59 +95,80 @@ export default {
if (this.formIsValid()) {
const baseURL = this.baseURL;
this.$store.dispatch("setBaseURL", { baseURL });
User.register(
{ email: this.email, password: this.password },
)
.then(() => {
message.success(
this.$t(
"WelcomeRegister",
"Welcome {email}, thank you for signing up.",
{ email: this.email }
)
);
User
.login({ email: this.email, password: this.password })
.then(response => {
this.$store.dispatch("login", response.data);
this.$router.push({ name: "home" });

encryptPassword(this.email, this.password).then(encryptedPassword => {
User.register({ email: this.email, password: encryptedPassword })
.then(() => {
message.success(
this.$t(
"WelcomeRegister",
"Welcome {email}, thank you for signing up.",
{ email: this.email }
)
);
return User.login({
email: this.email,
password: encryptedPassword
})
.then(response => {
this.$store.dispatch("login", response.data);
this.$router.push({ name: "home" });
})
.catch(() => message.displayGenericError());
})
.catch(err => message.displayGenericError());
})
.catch(err => {
if ( err.response === undefined && baseURL !== defaultbaseURL) {
message.error(this.$t("DBNotRunning", "Your LessPass Database is not running"));
}else if (
err.response && err.response.data &&
typeof err.response.data.email !== "undefined"
) {
if (err.response.data.email[0].indexOf("already exists") !== -1) {
.catch(err => {
if (err.response === undefined && baseURL !== defaultbaseURL) {
message.error(
this.$t(
"EmailAlreadyExist",
"This email is already registered. Want to login or recover your password?"
"DBNotRunning",
"Your LessPass Database is not running"
)
);
} else if (
err.response &&
err.response.data &&
typeof err.response.data.email !== "undefined"
) {
if (
err.response.data.email[0].indexOf("already exists") !== -1
) {
message.error(
this.$t(
"EmailAlreadyExist",
"This email is already registered. Want to login or recover your password?"
)
);
}
if (err.response.data.email[0].indexOf("valid email") !== -1) {
message.error(
this.$t("EmailInvalid", "Please enter a valid email")
);
}
} else if (
err.response &&
err.response.data &&
typeof err.response.data.password !== "undefined"
) {
if (err.response.data.password[0].indexOf("too short") !== -1) {
message.error(
this.$t(
"PasswordTooShort",
"This password is too short. It must contain at least 8 characters."
)
);
}
if (
err.response.data.password[0].indexOf("too common") !== -1
) {
message.error(
this.$t("PasswordTooCommon", "This password is too common.")
);
}
} else {
message.displayGenericError();
}
if (err.response.data.email[0].indexOf("valid email") !== -1) {
message.error(
this.$t("EmailInvalid", "Please enter a valid email")
);
}
} else if (
err.response && err.response.data &&
typeof err.response.data.password !== "undefined"
) {
if (err.response.data.password[0].indexOf("too short") !== -1) {
message.error(this.$t("PasswordTooShort", "This password is too short. It must contain at least 8 characters."));
}
if (err.response.data.password[0].indexOf("too common") !== -1) {
message.error(this.$t("PasswordTooCommon", "This password is too common."));
}
} else {
message.displayGenericError();
}
});
});
});
}
}
}


+ 14
- 4
packages/lesspass-pure/src/views/Settings.vue View File

@@ -1,9 +1,13 @@
<template>
<div>
<h5>{{$t('Options by default')}}</h5>
<form id="lesspass-options-form" novalidate v-on:submit.prevent="saveAndExit">
<h5>{{ $t("Options by default") }}</h5>
<form
id="lesspass-options-form"
novalidate
v-on:submit.prevent="saveAndExit"
>
<div class="form-group">
<label for="login">{{ $t('Username') }}</label>
<label for="login">{{ $t("Username") }}</label>
<div class="inner-addon left-addon">
<i class="fa fa-user"></i>
<input
@@ -21,7 +25,13 @@
</div>
</div>
<options v-bind:options="defaultPassword"></options>
<button type="submit" id="btn-submit-settings" class="btn btn-primary btn-block mt-4">{{$t('Save')}}</button>
<button
type="submit"
id="btn-submit-settings"
class="btn btn-primary btn-block mt-4"
>
{{ $t("Save") }}
</button>
</form>
</div>
</template>


Loading…
Cancel
Save