Fully Encrypted notes Beta
* Encrypts all notes going to the database * Creates encrypted snippets for loading note title cards * Creates an encrypted search index when note is changed * Migrates users to encrypted notes on login * Creates new encrypted master keys for newly logged in users
This commit is contained in:
@@ -30,6 +30,7 @@ export default {
|
||||
//Puts token into state on page load
|
||||
let token = localStorage.getItem('loginToken')
|
||||
let username = localStorage.getItem('username')
|
||||
let masterKey = localStorage.getItem('masterKey')
|
||||
|
||||
// const socket = io({ path:'/socket' });
|
||||
const socket = this.$io
|
||||
@@ -50,7 +51,7 @@ export default {
|
||||
|
||||
//Put user data into global store on load
|
||||
if(token){
|
||||
this.$store.commit('setLoginToken', {token, username})
|
||||
this.$store.commit('setLoginToken', {token, username, masterKey})
|
||||
}
|
||||
|
||||
},
|
||||
|
@@ -86,17 +86,6 @@
|
||||
|
||||
<div class="edit-divide"></div>
|
||||
|
||||
<!-- protect -->
|
||||
<div class="edit-button" v-if="!isEncrypted"
|
||||
v-on:click="$router.push(`/notes/open/${noteid}/menu/passwordprotect`)" data-tooltip="Password Protect" data-position="bottom center" data-inverted>
|
||||
<i class="shield alternate icon"></i>
|
||||
</div>
|
||||
|
||||
<!-- data-tooltip="Remove Password Protection" -->
|
||||
<div class="edit-button" v-if="isEncrypted && isDecrypted" v-on:click="disableEncryption()" data-tooltip="Close" data-position="bottom center" data-inverted>
|
||||
<i class="unlock icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="edit-button" v-on:click="onToggleArchived()" :data-tooltip="archived == 1?'Move to main list':'Move to Archive'" data-position="bottom center" data-inverted>
|
||||
<span v-if="archived == 1"><i class="green archive icon"></i></span>
|
||||
<span v-if="archived != 1"><i class="archive icon"></i></span>
|
||||
@@ -125,8 +114,9 @@
|
||||
|
||||
<!-- Loading indicator -->
|
||||
<div v-if="loading" class="loading-note">
|
||||
<div class="ui active dimmer">
|
||||
<div class="ui text loader">{{loadingMessage}}</div>
|
||||
<div class="loading-text">
|
||||
Decrypting Note &
|
||||
{{loadingMessage}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -267,55 +257,6 @@
|
||||
</div>
|
||||
</side-slide-menu>
|
||||
|
||||
<side-slide-menu v-show="passwordprotect" v-on:close="passwordprotect = false" :fullShadow="true" name="encrypt note">
|
||||
<div class="ui basic segment" v-if="isDecrypted && isEncrypted">
|
||||
<p>Note Decrypted</p>
|
||||
<div class="ui green button" v-on:click="lockNote">Lock Note</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!isEncrypted" class="ui basic segment">
|
||||
|
||||
<div class="ui top attached segment">
|
||||
|
||||
<h2><i class="green lock alternate icon"></i>Password protect this Note</h2>
|
||||
<p>Password protection will prevent anyone from reading the text of this note, unless they enter the correct password.</p>
|
||||
<p><b>Only the note text is protected. Title, tags, and files are not encrypted and remain visible without a password.</b></p>
|
||||
<p>The password you select will only be used for this note. You can use the same password on multiple notes. The note will be encrypted using the password entered. A longer password will be more secure.</p>
|
||||
<h4><i class="red icon exclamation triangle"></i> Warning. There is no way to recover a lost password.</h4>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="ui bottom attached segment">
|
||||
<div class="ui form">
|
||||
<div class="field">
|
||||
<label>New Password to lock this note</label>
|
||||
<input :name="`randomThing-${noteid}`" :id="`yupper-${noteid}`"type="password" v-model="password" placeholder="Note Password">
|
||||
</div>
|
||||
<div class="field" v-if="password.length > 3">
|
||||
<label>Confirm Password</label>
|
||||
<input :name="`randomStuff-${noteid}`" :id="`heckye-${noteid}`"type="password" v-model="passwordConfirm" placeholder="Confirm Password">
|
||||
</div>
|
||||
<div class="field" v-if="password.length > 3">
|
||||
<label>Password Hint - visible when unlocking note</label>
|
||||
<input :name="`randomStuzz-${noteid}`" :id="`heckyo-${noteid}`"type="text" v-model="passwordHint" placeholder="Optional Password Hint" v-on:keyup.enter="enableEncryption">
|
||||
</div>
|
||||
|
||||
<div class="field" v-if="passwordConfirm.length > 3 && password != passwordConfirm">
|
||||
<div v-on:click="enableEncryption" class="ui disabled green button">
|
||||
Passwords do not match
|
||||
</div>
|
||||
</div>
|
||||
<div class="field" v-if="passwordConfirm.length > 3 && password == passwordConfirm">
|
||||
<div v-on:click="enableEncryption" class="ui green button">
|
||||
Protect!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</side-slide-menu>
|
||||
|
||||
<!-- Show side shades if user is on desktop only -->
|
||||
<div class="full-focus-shade shade1"
|
||||
:class="{ 'slide-out-left':(sizeDown == true) }"
|
||||
@@ -1206,6 +1147,10 @@
|
||||
updated: this.updated
|
||||
}
|
||||
|
||||
console.log('Focus regained with note open.')
|
||||
console.log('Attempting to fix diff text. fix this. Search spleen')
|
||||
return
|
||||
|
||||
axios.post('/api/note/difftext', postData)
|
||||
.then( ({data}) => {
|
||||
|
||||
@@ -1255,6 +1200,11 @@
|
||||
|
||||
this.save().then( result => {
|
||||
|
||||
//If note was modified, trigger reindex on close
|
||||
if(this.modified){
|
||||
axios.post('/api/note/reindex')
|
||||
}
|
||||
|
||||
this.sizeDown = true
|
||||
//This timeout allows animation to play before closing
|
||||
setTimeout(() => {
|
||||
@@ -1488,11 +1438,23 @@
|
||||
}
|
||||
.loading-note {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 20%;
|
||||
left: 20%;
|
||||
right: 20%;
|
||||
bottom: 20%;
|
||||
background: transparent;
|
||||
color: #5e6268;;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
.loading-text {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-right: -50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
/* One note open, in the middle of the screen */
|
||||
.master-note-edit.position-0 {
|
||||
left: 50%;
|
||||
|
@@ -68,7 +68,7 @@
|
||||
<div class="tool-bar" @click.self="cardClicked">
|
||||
<div class="icon-bar">
|
||||
|
||||
<!-- <span v-if="note.pinned == 1" data-position="top left" data-tooltip="Pinned" data-inverted>
|
||||
<!-- <span v-if="note.pinned == 1" data-position="top left" data-tooltip="Pinned" data-inverted>
|
||||
<i class="green pin icon"></i>
|
||||
</span>
|
||||
<span v-if="note.archived == 1" data-position="top left" data-tooltip="Archived" data-inverted>
|
||||
@@ -80,7 +80,7 @@
|
||||
<br>
|
||||
</span>
|
||||
|
||||
<span data-tooltip="Edited" class="time-ago-display" :class="{ 'hover-hide':(!$store.getters.getIsUserOnMobile) }">
|
||||
<span class="time-ago-display" :class="{ 'hover-hide':(!$store.getters.getIsUserOnMobile) }">
|
||||
{{$helpers.timeAgo(note.updated)}}
|
||||
</span>
|
||||
|
||||
|
@@ -13,7 +13,7 @@
|
||||
<div class="ui form" v-if="!$store.getters.getIsUserOnMobile">
|
||||
<!-- normal search menu -->
|
||||
<div class="ui left icon fluid input">
|
||||
<input v-model="searchTerm" @keyup="searchKeyUp" @keyup.enter="search" placeholder="Search Notes and Files" ref="searchInput"/>
|
||||
<input v-model="searchTerm" @keyup.enter="search" placeholder="Search Notes and Files" ref="searchInput"/>
|
||||
<i class="search icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -80,12 +80,14 @@
|
||||
|
||||
const token = response.data.token
|
||||
const username = response.data.username
|
||||
const masterKey = response.data.masterKey
|
||||
|
||||
vm.$store.commit('setLoginToken', {token, username})
|
||||
vm.$store.commit('setLoginToken', {token, username, masterKey})
|
||||
|
||||
//Redirect user to notes section after login
|
||||
vm.$router.push('/notes')
|
||||
} else {
|
||||
// this.password = ''
|
||||
this.$bus.$emit('notification', 'Incorrect Username or Password')
|
||||
vm.$store.commit('destroyLoginToken')
|
||||
}
|
||||
|
@@ -64,7 +64,7 @@
|
||||
<h2 v-if="fastFilters['withTags'] == 1">Notes with Tags</h2>
|
||||
<h2 v-if="fastFilters['onlyArchived'] == 1">Archived Notes</h2>
|
||||
<h2 v-if="fastFilters['onlyShowSharedNotes'] == 1">Shared Notes</h2>
|
||||
<h2 v-if="fastFilters['onlyShowEncrypted'] == 1">Password Protected Notes</h2>
|
||||
<h2 v-if="fastFilters['onlyShowEncrypted'] == 1">Password Protected - No longer supported</h2>
|
||||
|
||||
<!-- Note title card display -->
|
||||
<div class="sixteen wide column">
|
||||
@@ -154,14 +154,15 @@
|
||||
highlights: [],
|
||||
searchDebounce: null,
|
||||
fastFilters: {},
|
||||
working: false,
|
||||
|
||||
//Load up notes in batches
|
||||
firstLoadBatchSize: 30, //First set of rapidly loaded notes
|
||||
batchSize: 100, //Size of batch loaded when user scrolls through current batch
|
||||
firstLoadBatchSize: 10, //First set of rapidly loaded notes
|
||||
batchSize: 25, //Size of batch loaded when user scrolls through current batch
|
||||
batchOffset: 0, //Tracks the current batch that has been loaded
|
||||
loadingBatchTimeout: null, //Limit how quickly batches can be loaded
|
||||
loadingInProgress: false,
|
||||
fetchTags: false,
|
||||
scrollLoadEnabled: true,
|
||||
|
||||
//Clear button is not visible
|
||||
showClear: false,
|
||||
@@ -237,9 +238,9 @@
|
||||
return
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
this.$bus.$on('update_fast_filters', newFilter => {
|
||||
this.fastFilters = newFilter
|
||||
//Fast filters always return all the results and tags
|
||||
@@ -254,7 +255,8 @@
|
||||
this.search(true, this.batchSize)
|
||||
.then( () => {
|
||||
|
||||
this.searchAttachments()
|
||||
console.log('Search attachments disabled for now')
|
||||
// this.searchAttachments()
|
||||
|
||||
return this.fetchUserTags()
|
||||
})
|
||||
@@ -275,6 +277,7 @@
|
||||
const id = this.$route.params.id
|
||||
this.openNote(id)
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', this.onScroll)
|
||||
|
||||
//Close notes when back button is pressed
|
||||
@@ -411,7 +414,7 @@
|
||||
const percentageDown = Math.round( (bottomOfWindow/offsetHeight)*100 )
|
||||
|
||||
//If greater than 80 of the way down the page, load the next batch
|
||||
if(percentageDown >= 80){
|
||||
if(percentageDown >= 65 && this.scrollLoadEnabled){
|
||||
|
||||
this.search(false, this.batchSize, true)
|
||||
}
|
||||
@@ -455,7 +458,7 @@
|
||||
},
|
||||
visibiltyChangeAction(event){
|
||||
|
||||
//@TODO - set a timeout on this like 2 minutes or just dont do shit and update it via socket.io
|
||||
//@TODO - phase this out, update it via socket.io
|
||||
//If user leaves page then returns to page, reload the first batch
|
||||
if(this.lastVisibilityState == 'hidden' && document.visibilityState == 'visible'){
|
||||
//Load initial batch, then tags, then other batch
|
||||
@@ -589,12 +592,18 @@
|
||||
|
||||
//Perform search - or die
|
||||
this.loadingInProgress = true
|
||||
console.time('Fetch TitleCard Batch '+notesInNextLoad)
|
||||
axios.post('/api/note/search', postData)
|
||||
.then(response => {
|
||||
|
||||
console.timeEnd('Fetch TitleCard Batch '+notesInNextLoad)
|
||||
|
||||
//Save the number of notes just loaded
|
||||
this.batchOffset += response.data.notes.length
|
||||
|
||||
//Enable or disable scroll loading
|
||||
this.scrollLoadEnabled = response.data.notes.length > 0
|
||||
|
||||
//Mush the two new sets of data together (set will be empty is reset is on)
|
||||
if(response.data.tags.length > 0){
|
||||
this.commonTags = response.data.tags
|
||||
@@ -666,6 +675,7 @@
|
||||
},
|
||||
reset(){
|
||||
this.showClear = false
|
||||
this.scrollLoadEnabled = true
|
||||
this.searchTerm = ''
|
||||
this.searchTags = []
|
||||
this.fastFilters = {}
|
||||
|
@@ -87,6 +87,7 @@ export default new Vuex.Store({
|
||||
})(navigator.userAgent||navigator.vendor||window.opera, state);
|
||||
},
|
||||
toggleNoteSettingsPane(state){
|
||||
|
||||
state.isNoteSettingsOpen = !state.isNoteSettingsOpen
|
||||
},
|
||||
setSocketIoSocket(state, socket){
|
||||
@@ -103,7 +104,6 @@ export default new Vuex.Store({
|
||||
// console.log(key + ' -- ' + totalsObject[key])
|
||||
// })
|
||||
}
|
||||
|
||||
},
|
||||
getters: {
|
||||
getUsername: state => {
|
||||
|
Reference in New Issue
Block a user