SAML; Dashboard
This commit is contained in:
65
app/assets/app/dash/NavBar.component.js
Normal file
65
app/assets/app/dash/NavBar.component.js
Normal file
@@ -0,0 +1,65 @@
|
||||
import { Component } from '../../lib/vues6/vues6.js'
|
||||
import { event_bus } from '../service/EventBus.service.js'
|
||||
import { session } from '../service/Session.service.js'
|
||||
import { message_service } from '../service/Message.service.js'
|
||||
|
||||
const template = `
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light border-bottom">
|
||||
<button id="menu-toggle" class="btn" @click="toggle_sidebar">
|
||||
<i class="fa fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<button
|
||||
class="navbar-toggler"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target='#navbarSupportedContent'
|
||||
aria-controls='navbarSupportedContent'
|
||||
aria-expanded='false'
|
||||
aria-label='Toggle navigation'
|
||||
>
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div id="navbarSupportedContent" class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav ml-auto mt-2 mt-lg-0">
|
||||
<li class="nav-item dropdown">
|
||||
<a
|
||||
href="#"
|
||||
role='button'
|
||||
data-toggle='dropdown'
|
||||
aria-haspopup='true'
|
||||
aria-expanded='false'
|
||||
id="navbarDropdown"
|
||||
class="nav-link dropdown-toggle"
|
||||
>{{ first_name }} {{ last_name }}</a>
|
||||
<div
|
||||
class="dropdown-menu dropdown-menu-right"
|
||||
aria-labelledby="navbarDropdown"
|
||||
>
|
||||
<h6 class="dropdown-header">Hello, {{ first_name }}.</h6>
|
||||
<a href="/dash/profile" class="dropdown-item">My Profile</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="/auth/logout" class="dropdown-item">Sign-Out of {{ app_name }}</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
`
|
||||
|
||||
export default class NavBarComponent extends Component {
|
||||
static get selector() { return 'coreid-navbar' }
|
||||
static get template() { return template }
|
||||
static get props() { return [] }
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.toggle_event = event_bus.event('sidebar.toggle')
|
||||
this.first_name = session.get('user.first_name')
|
||||
this.last_name = session.get('user.last_name')
|
||||
this.app_name = session.get('app.name')
|
||||
}
|
||||
|
||||
toggle_sidebar() {
|
||||
this.toggle_event.fire()
|
||||
}
|
||||
}
|
||||
75
app/assets/app/dash/SideBar.component.js
Normal file
75
app/assets/app/dash/SideBar.component.js
Normal file
@@ -0,0 +1,75 @@
|
||||
import { Component } from '../../lib/vues6/vues6.js'
|
||||
import { event_bus } from '../service/EventBus.service.js'
|
||||
import { action_service } from '../service/Action.service.js'
|
||||
|
||||
const template = `
|
||||
<div class="bg-light border-right coreid-sidebar-wrapper" id="sidebar-wrapper" v-bind:class="{ collapsed: isCollapsed }">
|
||||
<div class="sidebar-heading">{{ app_name }}</div>
|
||||
<div class="list-group list-group-flush">
|
||||
<a
|
||||
href="#"
|
||||
@click="perform(action)"
|
||||
class="list-group-item list-group-item-action bg-light"
|
||||
v-for="action in actions"
|
||||
>{{ action.text }}</a>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// TODO figure out why this doesn't show up in mobile layouts
|
||||
|
||||
export default class SideBarComponent extends Component {
|
||||
static get selector() { return 'coreid-sidebar' }
|
||||
static get props() { return ['app_name'] }
|
||||
static get template() { return template }
|
||||
|
||||
actions = [
|
||||
{
|
||||
text: 'Profile',
|
||||
action: 'redirect',
|
||||
next: '/dash/profile',
|
||||
},
|
||||
{
|
||||
text: 'Users',
|
||||
action: 'redirect',
|
||||
next: '/dash/users',
|
||||
},
|
||||
{
|
||||
text: 'Groups',
|
||||
action: 'redirect',
|
||||
next: '/dash/groups',
|
||||
},
|
||||
{
|
||||
text: 'LDAP Clients',
|
||||
action: 'redirect',
|
||||
next: '/dash/ldap/clients',
|
||||
},
|
||||
{
|
||||
text: 'SAML Service Providers',
|
||||
action: 'redirect',
|
||||
next: '/dash/saml/service-providers',
|
||||
},
|
||||
{
|
||||
text: 'Settings',
|
||||
action: 'redirect',
|
||||
next: '/dash/settings',
|
||||
},
|
||||
]
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
event_bus.event('sidebar.toggle').subscribe(() => {
|
||||
this.toggle()
|
||||
})
|
||||
}
|
||||
|
||||
isCollapsed = false
|
||||
|
||||
toggle() {
|
||||
this.isCollapsed = !this.isCollapsed
|
||||
}
|
||||
|
||||
perform(action) {
|
||||
return action_service.perform({delay: 0, ...action})
|
||||
}
|
||||
}
|
||||
118
app/assets/app/dash/message/MessageContainer.component.js
Normal file
118
app/assets/app/dash/message/MessageContainer.component.js
Normal file
@@ -0,0 +1,118 @@
|
||||
import { Component } from '../../../lib/vues6/vues6.js'
|
||||
import { event_bus } from '../../service/EventBus.service.js'
|
||||
import { message_service } from '../../service/Message.service.js'
|
||||
|
||||
const template = `
|
||||
<span class="message-container">
|
||||
<span v-for="message of messages">
|
||||
<div class="alert alert-dismissible fade show" role="alert" v-bind:class="[message.type]">
|
||||
{{ message.message }}
|
||||
<button
|
||||
class="close"
|
||||
type="button"
|
||||
aria-label="Close"
|
||||
@click="dismiss_alert($event, message)"
|
||||
>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<span v-for="modal of modals">
|
||||
<div
|
||||
class="modal fade"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
aria-hidden="true"
|
||||
ref="modal"
|
||||
>
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">{{ modal.title }}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{{ modal.message }}
|
||||
</div>
|
||||
<div class="modal-footer" v-if="modal.buttons && modal.buttons.length > 0">
|
||||
<button
|
||||
type="button"
|
||||
:class="button.class || ['btn', 'btn-secondary']"
|
||||
v-for="button of modal.buttons"
|
||||
:data-dismiss="button.type === 'close' ? 'modal' : ''"
|
||||
@click="modal_button_click($event, modal, button)"
|
||||
>{{ button.text }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
`
|
||||
|
||||
export default class MessageContainerComponent extends Component {
|
||||
static get selector() { return 'coreid-message-container' }
|
||||
static get template() { return template }
|
||||
static get props() { return [] }
|
||||
|
||||
messages = []
|
||||
modals = []
|
||||
|
||||
vue_on_create() {
|
||||
this.alert_event = event_bus.event('message.alert')
|
||||
this.alert_event.subscribe(({ message, type = 'info', timeout = 0, on_dismiss = () => {} }) => {
|
||||
this.create_alert(message, type, timeout, on_dismiss)
|
||||
})
|
||||
|
||||
this.modal_event = event_bus.event('message.modal')
|
||||
this.modal_event.subscribe(({ title, message, buttons = [] }) => {
|
||||
this.create_modal(title, message, buttons)
|
||||
})
|
||||
|
||||
message_service.init_listener()
|
||||
}
|
||||
|
||||
dismiss_alert($event, message) {
|
||||
this.messages = this.messages.filter(x => x !== message)
|
||||
message.on_dismiss($event)
|
||||
}
|
||||
|
||||
create_alert(message, type, timeout, on_dismiss = () => {}) {
|
||||
const msg = {
|
||||
message,
|
||||
type: type.startsWith('alert-') ? type : `alert-${type}`,
|
||||
on_dismiss,
|
||||
}
|
||||
|
||||
this.messages.push(msg)
|
||||
|
||||
if ( timeout > 0 ) {
|
||||
setTimeout(() => {
|
||||
this.dismiss_alert(msg)
|
||||
}, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
create_modal(title, message, buttons = []) {
|
||||
const index = this.modals.length
|
||||
const modal = {
|
||||
title,
|
||||
message,
|
||||
buttons
|
||||
}
|
||||
|
||||
this.modals.push(modal)
|
||||
this.$nextTick(() => {
|
||||
$(this.$refs.modal[index]).modal()
|
||||
})
|
||||
}
|
||||
|
||||
modal_button_click($event, modal, button) {
|
||||
if ( typeof button.on_click === 'function' ) {
|
||||
button.on_click($event)
|
||||
}
|
||||
}
|
||||
}
|
||||
288
app/assets/app/dash/profile/EditProfile.component.js
Normal file
288
app/assets/app/dash/profile/EditProfile.component.js
Normal file
@@ -0,0 +1,288 @@
|
||||
import { Component } from '../../../lib/vues6/vues6.js'
|
||||
import { session } from '../../service/Session.service.js'
|
||||
import { password_service } from '../../service/Password.service.js'
|
||||
import { auth_api } from '../../service/AuthApi.service.js'
|
||||
import { location_service } from '../../service/Location.service.js'
|
||||
import { message_service } from '../../service/Message.service.js'
|
||||
import { utility } from '../../service/Utility.service.js'
|
||||
import { profile_service } from '../../service/Profile.service.js'
|
||||
|
||||
const template = `
|
||||
<div class="coreid-profile-container mb-5">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-8 offset-2 col-sm-4 offset-sm-0">
|
||||
<img src="/assets/profile.jpg" alt="Profile Image" class="img-fluid">
|
||||
</div>
|
||||
<div class="col-sm-8 offset-sm-0 col-12 text-sm-left text-center pad-top">
|
||||
<div class="card-title"><h3>{{ profile_first }} {{ profile_last }}</h3></div>
|
||||
<div class="card-subtitle mb-2 text-muted">{{ profile_tagline }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">
|
||||
<h4>Basic Profile</h4>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6 form-group">
|
||||
<label for="coreid-profile-first-input">First Name</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="coreid-profile-first-input"
|
||||
placeholder="John"
|
||||
v-model="profile_first"
|
||||
@keyup="on_key_up($event)"
|
||||
>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 form-group">
|
||||
<label for="coreid-profile-last-input">Last Name</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="coreid-profile-last-input"
|
||||
placeholder="Doe"
|
||||
v-model="profile_last"
|
||||
@keyup="on_key_up($event)"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 form-group">
|
||||
<label for="coreid-profile-email-input">E-Mail Address</label>
|
||||
<input
|
||||
type="email"
|
||||
class="form-control"
|
||||
id="coreid-profile-email-input"
|
||||
placeholder="john.doe@contoso.com"
|
||||
v-model="profile_email"
|
||||
ref="email_input"
|
||||
@keyup="on_key_up($event)"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 form-group">
|
||||
<label for="coreid-profile-tag-input">Tagline</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="coreid-profile-tag-input"
|
||||
v-model="profile_tagline"
|
||||
@keyup="on_key_up($event)"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item text-right font-italic text-muted">
|
||||
{{ form_message }}
|
||||
</li>
|
||||
<li class="list-group-item" v-if="!user_id || user_id === 'me'">
|
||||
<h4>Password</h4>
|
||||
<p class="font-italic" v-if="last_reset">Your password was last changed on {{ last_reset }}.</p>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
@click="change_password"
|
||||
>Change Password</button>
|
||||
</li>
|
||||
<li class="list-group-item" v-if="!has_mfa && (!user_id || user_id === 'me')">
|
||||
<h4>Multi-factor Authentication</h4>
|
||||
<p>MFA is a good-practice security measure that requires you to provide a second factor of identification when you sign in from a service or device that makes use of {{ app_name }}.</p>
|
||||
<p>Once enabled, {{ app_name }} will prompt you to enter a code when you sign-in with the {{ app_name }} web interface from a new device. It will also require you to append the code to your password when signing in to a service that uses {{ app_name }} as a backend.</p>
|
||||
<button class="btn btn-success btn-sm" type="button" @click="enable_mfa">Enable MFA</button>
|
||||
</li>
|
||||
<li class="list-group-item" v-if="has_mfa && (!user_id || user_id === 'me')">
|
||||
<h4>Multi-factor Authentication</h4>
|
||||
<p class="font-italic">MFA was enabled for your account on {{ mfa_enable_date }}.</p>
|
||||
<button
|
||||
class="btn btn-danger btn-sm"
|
||||
type="button"
|
||||
@click="disable_mfa"
|
||||
>Disable MFA</button>
|
||||
|
||||
<h6 class="pad-top">App Passwords</h6>
|
||||
<p>App passwords are specially generated passwords that allow you to sign into legacy services with your {{ app_name }} account.</p>
|
||||
<p>You should only use this to authenticate against a service that needs to repeatedly use your password on your behalf (e.g. e-mail clients).</p>
|
||||
<p>Use these with caution, as they can bypass your multi-factor authentication.</p>
|
||||
|
||||
<p class="font-italic text-muted" v-if="app_passwords.length > 0">You have {{ app_passwords.length }} app {{ app_passwords.length === 1 ? 'password' : 'passwords' }} associated with your account.</p>
|
||||
<ul class="list-group mb-4" v-if="app_passwords.length > 0">
|
||||
<li class="list-group-item" v-for="pw of app_passwords">
|
||||
<div class="row">
|
||||
<div class="col-9">
|
||||
{{ pw.name }}
|
||||
<br><span class="text-muted font-italic">Issued: {{ pw.created }}</span>
|
||||
</div>
|
||||
<div class="col-3 my-auto">
|
||||
<button
|
||||
class="btn btn-sm btn-danger"
|
||||
type="button"
|
||||
@click="deactivate_app_password($event, pw)"
|
||||
>Deactivate</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<button class="btn btn-sm btn-primary" @click="on_click_generate_app_password">Generate New</button>
|
||||
|
||||
<h6 class="pad-top">Recovery Codes</h6>
|
||||
<p>Recovery codes can be used to regain access to your account in the event that you lose access to the device that generates your MFA codes.</p>
|
||||
<p class="font-italic">No recovery codes have been generated for your account.</p>
|
||||
<button class="btn btn-sm btn-success">Generate Recovery Codes</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<coreid-form-app-password
|
||||
v-if="!user_id || user_id === 'me'"
|
||||
ref="app_password_modal"
|
||||
@modal-success="load_app_passwords"
|
||||
></coreid-form-app-password>
|
||||
</div>
|
||||
`
|
||||
|
||||
export default class EditProfileComponent extends Component {
|
||||
static get selector() { return 'coreid-profile-edit' }
|
||||
static get template() { return template }
|
||||
static get props() { return ['user_id'] }
|
||||
|
||||
profile_first = ''
|
||||
profile_last = ''
|
||||
profile_email = ''
|
||||
profile_tagline = ''
|
||||
last_reset = ''
|
||||
mfa_enable_date = ''
|
||||
|
||||
form_message = 'No changes.'
|
||||
|
||||
has_mfa = false
|
||||
ready = false
|
||||
|
||||
app_passwords = []
|
||||
|
||||
on_key_up = ($event) => {}
|
||||
|
||||
vue_on_create() {
|
||||
this.app_name = session.get('app.name')
|
||||
this.load().then(() => {
|
||||
this.ready = true
|
||||
})
|
||||
|
||||
const save = utility.debounce(this.save_form.bind(this))
|
||||
this.on_key_up = () => {
|
||||
this.form_message = 'Saving...'
|
||||
save()
|
||||
}
|
||||
|
||||
console.log('profile form', this)
|
||||
}
|
||||
|
||||
get_submit_data() {
|
||||
return {
|
||||
first_name: this.profile_first,
|
||||
last_name: this.profile_last,
|
||||
email: this.profile_email,
|
||||
tagline: this.profile_tagline,
|
||||
user_id: this.user_id || 'me',
|
||||
}
|
||||
}
|
||||
|
||||
valid_email() {
|
||||
return this.$refs.email_input.validity.valid
|
||||
}
|
||||
|
||||
async save_form($event) {
|
||||
const submit_data = this.get_submit_data()
|
||||
try {
|
||||
if ( !this.valid_email() ) {
|
||||
this.form_message = 'Invalid e-mail address.'
|
||||
} else {
|
||||
await profile_service.update_profile(submit_data)
|
||||
this.form_message = 'All changes saved.'
|
||||
}
|
||||
} catch(e) {
|
||||
this.form_message = 'Unknown error occurred while saving.'
|
||||
}
|
||||
}
|
||||
|
||||
populate_from_session() {
|
||||
this.profile_first = session.get('user.first_name')
|
||||
this.profile_last = session.get('user.last_name')
|
||||
this.profile_email = session.get('user.email')
|
||||
this.profile_tagline = session.get('user.tagline')
|
||||
}
|
||||
|
||||
async load() {
|
||||
const result = await profile_service.get_profile(this.user_id || 'me')
|
||||
if ( !result ) throw new Error('Unable to load profile!')
|
||||
|
||||
this.profile_first = result.first_name
|
||||
this.profile_last = result.last_name
|
||||
this.profile_email = result.email
|
||||
this.profile_tagline = result.tagline
|
||||
|
||||
if ( !this.user_id || this.user_id === 'me' ) {
|
||||
const reset = (await password_service.get_resets()).reverse()[0]
|
||||
if (reset && reset.reset_on) {
|
||||
this.last_reset = (new Date(reset.reset_on)).toLocaleDateString()
|
||||
}
|
||||
|
||||
const mfa = await auth_api.has_mfa()
|
||||
this.has_mfa = mfa && mfa.mfa_enabled
|
||||
if (this.has_mfa) this.mfa_enable_date = (new Date(mfa.mfa_enable_date)).toLocaleDateString()
|
||||
|
||||
await this.load_app_passwords()
|
||||
}
|
||||
}
|
||||
|
||||
async load_app_passwords() {
|
||||
let app_pws = await auth_api.app_passwords()
|
||||
if ( !Array.isArray(app_pws) ) app_pws = []
|
||||
this.app_passwords = app_pws.map(x => {
|
||||
if ( x.expires ) x.expires = (new Date(x.expires)).toLocaleDateString()
|
||||
if ( x.created ) x.created = (new Date(x.created)).toLocaleDateString()
|
||||
return x
|
||||
})
|
||||
}
|
||||
|
||||
disable_mfa() {
|
||||
location_service.redirect('/auth/mfa/disable')
|
||||
}
|
||||
|
||||
enable_mfa() {
|
||||
location_service.redirect('/auth/mfa/setup')
|
||||
}
|
||||
|
||||
change_password() {
|
||||
location_service.redirect('/password/reset')
|
||||
}
|
||||
|
||||
on_click_generate_app_password() {
|
||||
this.$refs.app_password_modal.trigger()
|
||||
}
|
||||
|
||||
async deactivate_app_password($event, pw) {
|
||||
message_service.modal({
|
||||
title: 'Deactivate app password?',
|
||||
message: `You are about to deactivate the app password for ${pw.name}. If you do this, ${pw.name} will no longer be able to sign-in on your behalf. Continue?`,
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
type: 'close',
|
||||
},
|
||||
{
|
||||
text: 'Deactivate',
|
||||
type: 'close',
|
||||
class: ['btn', 'btn-danger'],
|
||||
on_click: async () => {
|
||||
await auth_api.delete_app_password(pw.uuid)
|
||||
await this.load_app_passwords()
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
114
app/assets/app/dash/profile/form/AppPassword.component.js
Normal file
114
app/assets/app/dash/profile/form/AppPassword.component.js
Normal file
@@ -0,0 +1,114 @@
|
||||
import { Component } from '../../../../lib/vues6/vues6.js'
|
||||
import { utility } from '../../../service/Utility.service.js'
|
||||
import { auth_api } from '../../../service/AuthApi.service.js'
|
||||
|
||||
const template = `
|
||||
<div
|
||||
class="modal fade"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
aria-hidden="true"
|
||||
ref="modal"
|
||||
>
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Generate App-Password</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group" v-if="!display_password">
|
||||
<label :for="uuid">App Name</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
:id="uuid"
|
||||
v-model="name"
|
||||
@keyup="on_name_change"
|
||||
placeholder="My really cool e-mail client"
|
||||
:disabled="!enable_form"
|
||||
ref="input"
|
||||
>
|
||||
</div>
|
||||
<div v-if="display_password">
|
||||
The app password for <code>{{ name }}</code> was generated successfully. Copy the password below and use it in <code>{{ name }}</code> to sign in. Note that, once you close this window, you will no longer be able to view this password.
|
||||
</div>
|
||||
<div v-if="display_password" class="text-center pad-top">
|
||||
<pre><code>{{ display_password }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal"
|
||||
@click="$emit('modal-cancel')"
|
||||
v-if="!display_password"
|
||||
>Cancel</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
:disabled="!valid || !enable_form"
|
||||
@click="generate_pw"
|
||||
v-if="!display_password"
|
||||
>Generate</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
v-if="display_password"
|
||||
data-dismiss="modal"
|
||||
@click="$emit('modal-success')"
|
||||
>Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
export default class AppPasswordFormComponent extends Component {
|
||||
static get selector() { return 'coreid-form-app-password' }
|
||||
static get template() { return template }
|
||||
static get props() { return [] }
|
||||
|
||||
name = ''
|
||||
valid = false
|
||||
uuid = ''
|
||||
enable_form = true
|
||||
display_password = ''
|
||||
|
||||
vue_on_create() {
|
||||
this.uuid = utility.uuid()
|
||||
console.log({auth_api})
|
||||
}
|
||||
|
||||
async on_name_change(event) {
|
||||
this.valid = this.name.trim().length > 0
|
||||
|
||||
if ( event.keyCode === 13 ) {
|
||||
// Enter was pressed
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
if ( this.valid && this.enable_form ) await this.generate_pw()
|
||||
}
|
||||
}
|
||||
|
||||
trigger() {
|
||||
this.name = ''
|
||||
this.valid = false
|
||||
this.enable_form = true
|
||||
this.display_password = ''
|
||||
|
||||
this.$nextTick(() => {
|
||||
$(this.$refs.modal).modal()
|
||||
})
|
||||
}
|
||||
|
||||
async generate_pw() {
|
||||
this.enable_form = false
|
||||
const result = await auth_api.create_app_password(this.name)
|
||||
this.display_password = result.password
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user