Add link share password authentication (#466)

Co-authored-by: kolaente <k@knt.li>
Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/466
Co-authored-by: konrad <konrad@kola-entertainments.de>
Co-committed-by: konrad <konrad@kola-entertainments.de>
This commit is contained in:
konrad 2021-04-11 13:18:19 +00:00
parent fa35f7790a
commit 2d8c43a920
4 changed files with 121 additions and 26 deletions

View file

@ -1,21 +1,33 @@
<template> <template>
<div> <div>
<p class="has-text-weight-bold">Share Links</p> <p class="has-text-weight-bold">
Share Links
<span
class="is-size-7"
v-tooltip="'Share Links allow you to easily share a list with other users who don\'t have an account on Vikunja.'">
What is a share link?
</span>
</p>
<div class="sharables-list"> <div class="sharables-list">
<div class="p-4">
<p>Share with a link:</p> <x-button
<div class="field has-addons"> v-if="!(linkShares.length === 0 || showNewForm)"
<div class="control"> @click="showNewForm = true"
<input icon="plus"
class="input" class="mb-4">
placeholder="Name" Create a new link share
v-tooltip="'All actions done by this link share will show up with the name.'" </x-button>
v-model="name"
/> <div class="p-4" v-if="linkShares.length === 0 || showNewForm">
</div> <div class="field">
<label class="label" for="linkShareRight">
Right
</label>
<div class="control"> <div class="control">
<div class="select"> <div class="select">
<select v-model="selectedRight"> <select v-model="selectedRight" id="linkShareRight">
<option :value="rights.READ">Read only</option> <option :value="rights.READ">Read only</option>
<option :value="rights.READ_WRITE"> <option :value="rights.READ_WRITE">
Read & write Read & write
@ -24,11 +36,39 @@
</select> </select>
</div> </div>
</div> </div>
</div>
<div class="field">
<label class="label" for="linkShareName">
Name (optional)
</label>
<div class="control"> <div class="control">
<x-button @click="add"> Share</x-button> <input
id="linkShareName"
class="input"
placeholder="e.g. Lorem Ipsum"
v-tooltip="'All actions done by this link share will show up with the name.'"
v-model="name"
/>
</div> </div>
</div> </div>
<div class="field">
<label class="label" for="linkSharePassword">
Password (optional)
</label>
<div class="control">
<input
id="linkSharePassword"
type="password"
class="input"
placeholder="e.g. ••••••••••••"
v-tooltip="'When authenticating, the user will be required to enter this password.'"
v-model="password"
/>
</div> </div>
</div>
<x-button @click="add" icon="plus">Share</x-button>
</div>
<table <table
class="table has-actions is-striped is-hoverable is-fullwidth link-share-list" class="table has-actions is-striped is-hoverable is-fullwidth link-share-list"
v-if="linkShares.length > 0" v-if="linkShares.length > 0"
@ -156,8 +196,10 @@ export default {
rights: rights, rights: rights,
selectedRight: rights.READ, selectedRight: rights.READ,
name: '', name: '',
password: '',
showDeleteModal: false, showDeleteModal: false,
linkIdToDelete: 0, linkIdToDelete: 0,
showNewForm: false,
} }
}, },
beforeMount() { beforeMount() {
@ -193,15 +235,19 @@ export default {
}) })
}, },
add() { add() {
let newLinkShare = new LinkShareModel({ const newLinkShare = new LinkShareModel({
right: this.selectedRight, right: this.selectedRight,
listId: this.listId, listId: this.listId,
name: this.name, name: this.name,
password: this.password,
}) })
this.linkShareService this.linkShareService
.create(newLinkShare) .create(newLinkShare)
.then(() => { .then(() => {
this.selectedRight = rights.READ this.selectedRight = rights.READ
this.name = ''
this.password = ''
this.showNewForm = false
this.success( this.success(
{message: 'The link share was successfully created'}, {message: 'The link share was successfully created'},
this this
@ -213,7 +259,7 @@ export default {
}) })
}, },
remove() { remove() {
let linkshare = new LinkShareModel({ const linkshare = new LinkShareModel({
id: this.linkIdToDelete, id: this.linkIdToDelete,
listId: this.listId, listId: this.listId,
}) })

View file

@ -23,6 +23,7 @@ export default class ListModel extends AbstractModel {
sharingType: 0, sharingType: 0,
listId: 0, listId: 0,
name: '', name: '',
password: '',
created: null, created: null,
updated: null, updated: null,

View file

@ -151,10 +151,11 @@ export default {
ctx.commit(LOADING, false, {root: true}) ctx.commit(LOADING, false, {root: true})
}) })
}, },
linkShareAuth(ctx, {hash, password}) {
linkShareAuth(ctx, hash) {
const HTTP = HTTPFactory() const HTTP = HTTPFactory()
return HTTP.post('/shares/' + hash + '/auth') return HTTP.post('/shares/' + hash + '/auth', {
password: password,
})
.then(r => { .then(r => {
localStorage.setItem('token', r.data.token) localStorage.setItem('token', r.data.token)
ctx.dispatch('checkAuth') ctx.dispatch('checkAuth')

View file

@ -1,9 +1,33 @@
<template> <template>
<div class="message is-centered is-info"> <div>
<div class="message-header"> <div class="notification is-info is-light has-text-centered" v-if="loading">
<p class="has-text-centered">
Authenticating... Authenticating...
</div>
<div v-if="authenticateWithPassword" class="box">
<p class="pb-2">
This shared list requires a password. Please enter it below:
</p> </p>
<div class="field">
<div class="control">
<input
id="linkSharePassword"
type="password"
class="input"
placeholder="e.g. ••••••••••••"
v-model="password"
v-focus
@keyup.enter.prevent="auth"
/>
</div>
</div>
<x-button @click="auth" :loading="loading">
Login
</x-button>
<div class="notification is-danger mt-4" v-if="error !== ''">
{{ error }}
</div>
</div> </div>
</div> </div>
</template> </template>
@ -16,7 +40,12 @@ export default {
name: 'LinkSharingAuth', name: 'LinkSharingAuth',
data() { data() {
return { return {
loading: true,
authenticateWithPassword: false,
error: '',
hash: '', hash: '',
password: '',
} }
}, },
created() { created() {
@ -30,17 +59,35 @@ export default {
}), }),
methods: { methods: {
auth() { auth() {
this.error = ''
if (this.authLinkShare) { if (this.authLinkShare) {
return return
} }
this.$store.dispatch('auth/linkShareAuth', this.$route.params.share) this.loading = true
this.$store.dispatch('auth/linkShareAuth', {hash: this.$route.params.share, password: this.password})
.then((r) => { .then((r) => {
console.log('after link share auth')
this.$router.push({name: 'list.list', params: {listId: r.list_id}}) this.$router.push({name: 'list.list', params: {listId: r.list_id}})
}) })
.catch(e => { .catch(e => {
this.error(e, this) if (typeof e.response.data.code !== 'undefined' && e.response.data.code === 13001) {
this.authenticateWithPassword = true
return
}
let error = 'An error occured.'
if (e.response && e.response.data && e.response.data.message) {
error = e.response.data.message
}
if (typeof e.response.data.code !== 'undefined' && e.response.data.code === 13002) {
error = 'The password is invalid.'
}
this.error = error
})
.finally(() => {
this.loading = false
}) })
}, },
}, },