refactor: replace custom CRM with Monica fork
Some checks failed
Build & Push Monica Image to Gitea Registry / build-and-push (push) Failing after 9s
Some checks failed
Build & Push Monica Image to Gitea Registry / build-and-push (push) Failing after 9s
This commit is contained in:
402
resources/js/components/settings/WebauthnConnector.vue
Normal file
402
resources/js/components/settings/WebauthnConnector.vue
Normal file
@@ -0,0 +1,402 @@
|
||||
<style scoped>
|
||||
.time {
|
||||
color: gray;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div class="form-group">
|
||||
<notifications group="webauthn" position="top middle" :duration="5000" width="400" />
|
||||
|
||||
<div v-if="method === 'register-modal'">
|
||||
<h3>{{ $t('settings.webauthn_title') }}</h3>
|
||||
|
||||
<div v-if="currentkeys !== null">
|
||||
<ul class="table">
|
||||
<li v-for="key in currentkeys"
|
||||
:key="key.id"
|
||||
class="table-row"
|
||||
>
|
||||
<div class="table-cell w-30">
|
||||
<strong>{{ key.name }}</strong>
|
||||
</div>
|
||||
<div class="table-cell time w-50">
|
||||
<template v-if="key.counter > 0">
|
||||
{{ $t('settings.webauthn_last_use', {timestamp: formatTime(key.updated_at)}) }}
|
||||
</template>
|
||||
</div>
|
||||
<div class="table-cell actions">
|
||||
<a class="pointer" href="" @click.prevent="showDeleteModal(key.id)">
|
||||
{{ $t('app.delete') }}
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<slot></slot>
|
||||
|
||||
<a v-if="isSupported" class="btn btn-primary" href="" @click.prevent="showRegisterModal">
|
||||
{{ $t('settings.webauthn_enable_description') }}
|
||||
</a>
|
||||
<small v-else>
|
||||
{{ notSupportedMessage() }}
|
||||
</small>
|
||||
|
||||
|
||||
<sweet-modal
|
||||
id="registerModal"
|
||||
ref="registerModal"
|
||||
overlay-theme="dark"
|
||||
:title="$t('settings.webauthn_title')"
|
||||
>
|
||||
<div v-if="registerTab === '1'">
|
||||
<p>
|
||||
{{ $t('settings.webauthn_key_name_help') }}
|
||||
</p>
|
||||
<form-input
|
||||
:id="'keyName'"
|
||||
v-model="keyName"
|
||||
:title="$t('settings.webauthn_key_name')"
|
||||
:value="keyName"
|
||||
:input-type="'text'"
|
||||
:width="150"
|
||||
:required="true"
|
||||
@keyup.enter="showRegisterModalTab('2');startRegister();"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="registerTab === '2'">
|
||||
<div v-if="errorMessage !== ''" class="form-error-message mb3">
|
||||
<div class="pa2">
|
||||
<p class="mb0">
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
<p>
|
||||
<a href="" @click.prevent="startRegister()">
|
||||
{{ $t('app.retry') }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="infoMessage !== ''" class="form-information-message mb3">
|
||||
<div class="pa2">
|
||||
<p class="mb0">
|
||||
{{ infoMessage }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="errorMessage === ''" class="tc">
|
||||
<img src="https://ssl.gstatic.com/accounts/strongauth/Challenge_2SV-Gnubby_graphic.png"
|
||||
:alt="$t('settings.webauthn_insertKey')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="errorMessage === ''" class="pa2">
|
||||
<p>
|
||||
{{ $t('settings.webauthn_insertKey') }}
|
||||
</p>
|
||||
<p>
|
||||
{{ $t('settings.webauthn_buttonAdvise') }}
|
||||
<br />
|
||||
{{ $t('settings.webauthn_noButtonAdvise') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="button">
|
||||
<a v-if="registerTab === '1'" class="btn" href="" @click.prevent="showRegisterModalTab('2');startRegister();">
|
||||
{{ $t('pagination.next') }}
|
||||
</a>
|
||||
<a v-else class="btn" href="" @click.prevent="showRegisterModalTab('1')">
|
||||
{{ $t('pagination.previous') }}
|
||||
</a>
|
||||
<a class="btn" href="" @click.prevent="closeRegisterModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-if="errorMessage !== ''" class="form-error-message mb3">
|
||||
<div class="pa2">
|
||||
<p class="mb0">
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
<p>
|
||||
<a href="" @click.prevent="start()">
|
||||
{{ $t('app.retry') }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="infoMessage !== ''" class="form-information-message mb3">
|
||||
<div class="pa2">
|
||||
<p class="mb0">
|
||||
{{ infoMessage }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tc">
|
||||
<img src="https://ssl.gstatic.com/accounts/strongauth/Challenge_2SV-Gnubby_graphic.png"
|
||||
:alt="$t('settings.webauthn_insertKey')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="pa2">
|
||||
<p>
|
||||
{{ $t('settings.webauthn_insertKey') }}
|
||||
</p>
|
||||
<p>
|
||||
{{ $t('settings.webauthn_buttonAdvise') }}
|
||||
<br />
|
||||
{{ $t('settings.webauthn_noButtonAdvise') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<sweet-modal ref="delete" overlay-theme="dark" title="Remove a key">
|
||||
<form>
|
||||
<div class="mb4">
|
||||
{{ $t('settings.webauthn_delete_confirmation') }}
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeDeleteModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a class="btn" href="" @click.prevent="webauthnRemove(keyToTrash)">
|
||||
{{ $t('app.delete') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { SweetModal } from 'sweet-modal-vue';
|
||||
import * as WebAuthn from '../../../../vendor/asbiin/laravel-webauthn/resources/js/webauthn.js';
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
SweetModal
|
||||
},
|
||||
|
||||
props: {
|
||||
keys: {
|
||||
type: Array,
|
||||
default: function () {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
publicKey: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
method: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
timezone: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
script: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isSupported: true,
|
||||
errorMessage: '',
|
||||
infoMessage: '',
|
||||
success: false,
|
||||
currentkeys: [],
|
||||
keyToTrash: '',
|
||||
keyName: '',
|
||||
registerTab: '',
|
||||
data: null,
|
||||
webauthn: null,
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.prepareComponent();
|
||||
this.start();
|
||||
},
|
||||
|
||||
methods: {
|
||||
prepareComponent() {
|
||||
this.currentkeys = this.keys;
|
||||
this.data = this.registerdata;
|
||||
|
||||
this.webauthn = new WebAuthn((name, message) => {
|
||||
this.errorMessage = this._errorMessage(name, message);
|
||||
});
|
||||
},
|
||||
|
||||
_errorMessage(name, message) {
|
||||
switch (name) {
|
||||
case 'InvalidStateError':
|
||||
return this.$t('settings.webauthn_error_already_used');
|
||||
case 'NotAllowedError':
|
||||
return this.$t('settings.webauthn_error_not_allowed');
|
||||
default:
|
||||
return message;
|
||||
}
|
||||
},
|
||||
|
||||
notSupportedMessage() {
|
||||
return this.$t('settings.webauthn_'+this.webauthn.notSupportedMessage());
|
||||
},
|
||||
|
||||
start() {
|
||||
var self = this;
|
||||
this.errorMessage = '';
|
||||
|
||||
if (! this.webauthn.webAuthnSupport()) {
|
||||
this.isSupported = false;
|
||||
this.errorMessage = this.notSupportedMessage();
|
||||
}
|
||||
|
||||
switch(this.method) {
|
||||
case 'register':
|
||||
setTimeout(function () {
|
||||
self.webauthn.register(
|
||||
self.publicKey,
|
||||
function (datas) { self.webauthnRegisterCallback(datas, true); }
|
||||
);
|
||||
}, 10);
|
||||
break;
|
||||
case 'login':
|
||||
this.webauthn.sign(
|
||||
this.publicKey,
|
||||
function (data) { self.webauthnLoginCallback(data); }
|
||||
);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
showRegisterModal() {
|
||||
this.errorMessage = '';
|
||||
this.infoMessage = '';
|
||||
this.keyName = '';
|
||||
this.success = false;
|
||||
this.showRegisterModalTab('1');
|
||||
this.$refs.registerModal.open();
|
||||
},
|
||||
|
||||
showRegisterModalTab(tab) {
|
||||
this.registerTab = tab;
|
||||
},
|
||||
|
||||
startRegister() {
|
||||
var self = this;
|
||||
this.errorMessage = '';
|
||||
axios.post('webauthn/keys/options')
|
||||
.then(response => {
|
||||
if (self.registerTab === '2') {
|
||||
var data = response.data.publicKey;
|
||||
setTimeout(function () {
|
||||
self.webauthn.register(
|
||||
data,
|
||||
function (datas) { self.webauthnRegisterCallback(datas, false); }
|
||||
);
|
||||
}, 10);
|
||||
}
|
||||
}).catch(error => {
|
||||
self.notify(error.response.data.message, false);
|
||||
});
|
||||
},
|
||||
|
||||
closeRegisterModal() {
|
||||
this.$refs.registerModal.close();
|
||||
this.showRegisterModalTab('');
|
||||
},
|
||||
|
||||
webauthnRegisterCallback(data, redirect) {
|
||||
var self = this;
|
||||
axios.post('webauthn/keys', {
|
||||
...data,
|
||||
name: self.keyName,
|
||||
}).then(response => {
|
||||
self.success = true;
|
||||
self.notify(self.$t('settings.webauthn_success'), true);
|
||||
self.currentkeys.push({
|
||||
id: response.result.id,
|
||||
name: response.result.name,
|
||||
});
|
||||
}).then(response => {
|
||||
if (redirect) {
|
||||
setTimeout(function () {
|
||||
window.location = response.data.callback;
|
||||
}, 100);
|
||||
} else {
|
||||
self.closeRegisterModal();
|
||||
}
|
||||
}).catch(error => {
|
||||
self.errorMessage = error.message ? error.message : error.response.data.message;
|
||||
});
|
||||
},
|
||||
|
||||
webauthnLoginCallback(data) {
|
||||
var self = this;
|
||||
axios.post('webauthn/auth', {
|
||||
...data
|
||||
}).then(response => {
|
||||
self.success = true;
|
||||
self.notify(self.$t('settings.webauthn_success'), true);
|
||||
|
||||
window.location = response.data.callback;
|
||||
}).catch(error => {
|
||||
self.errorMessage = error.message ? error.message : error.response.data.message;
|
||||
});
|
||||
},
|
||||
|
||||
webauthnRemove(id) {
|
||||
var self = this;
|
||||
axios.delete('webauthn/keys/'+id)
|
||||
.then(response => {
|
||||
self.currentkeys.splice(self.currentkeys.indexOf(self.currentkeys.find(item => item.id === response.data.id)), 1);
|
||||
self.success = true;
|
||||
self.notify(self.$t('settings.webauthn_delete_success'), true);
|
||||
self.closeDeleteModal();
|
||||
}).catch(error => {
|
||||
self.errorMessage = error.response.data.message;
|
||||
});
|
||||
},
|
||||
|
||||
showDeleteModal(id) {
|
||||
this.keyToTrash = id;
|
||||
this.$refs.delete.open();
|
||||
},
|
||||
|
||||
closeDeleteModal() {
|
||||
this.$refs.delete.close();
|
||||
},
|
||||
|
||||
formatTime(value) {
|
||||
var moment = require('moment-timezone');
|
||||
moment.locale(this._i18n.locale);
|
||||
moment.tz.setDefault('UTC');
|
||||
|
||||
var t = moment(value);
|
||||
var date = moment.tz(t, this.timezone);
|
||||
|
||||
return date.format('LLLL');
|
||||
},
|
||||
|
||||
notify(text, success) {
|
||||
this.$notify({
|
||||
group: 'webauthn',
|
||||
title: text,
|
||||
text: '',
|
||||
type: success ? 'success' : 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user