diff --git a/a.out b/a.out new file mode 100644 index 0000000..e69de29 diff --git a/app/assets/app/auth/MFADisable.component.js b/app/assets/app/auth/MFADisable.component.js index 483e19a..6aadbb2 100644 --- a/app/assets/app/auth/MFADisable.component.js +++ b/app/assets/app/auth/MFADisable.component.js @@ -40,7 +40,6 @@ export default class MFADisableComponent extends Component { vue_on_create() { this.app_name = session.get('app.name') - console.log({session}) } async back_click() { diff --git a/app/assets/app/cobalt/Form.component.js b/app/assets/app/cobalt/Form.component.js index 1cee255..90caeb0 100644 --- a/app/assets/app/cobalt/Form.component.js +++ b/app/assets/app/cobalt/Form.component.js @@ -5,52 +5,111 @@ import { resource_service } from '../service/Resource.service.js' import { action_service } from '../service/Action.service.js' const template = ` -
-

- {{ title }} -

-
-
- - - - {{ field.error }} - - - - - {{ field.error }} - +
+ +
+
+
{{ access_msg }}
+
- -
- {{ error_message }} - {{ other_message }} - -
+
+ +

+ {{ title }} +

+
+
+

Loading...

+
+
+
+
+ + + + + {{ field.error }} + + + + + {{ field.error }} + + + + + {{ field.error }} + + + + + + {{ field.error }} + +
+
+
+ {{ error_message }} + {{ other_message }} + +
+
` @@ -80,6 +139,9 @@ export default class FormComponent extends Component { error_message = '' other_message = '' + access_msg = '' + can_access = false + is_ready = false mode = '' id = '' @@ -99,47 +161,60 @@ export default class FormComponent extends Component { this.mode = this.initial_mode this.id = this.form_id this.resource_class = await resource_service.get(this.resource) + + if ( await this.resource_class.can(this.mode) ) { + this.can_access = true + this.access_msg = true + } else { + this.can_access = false + this.access_msg = 'Sorry, you do not have permission to ' + this.mode + ' this resource.' + return + } + } else { this.reset() } this.uuid = utility.uuid() - await this.load() await this.init() + await this.load() } async init() { + this.definition = this.resource_class.form_definition for ( const field of this.definition.fields ) { if ( field.type.startsWith('select.dynamic') ) { field._options = field._options || field.options const rsc = await resource_service.get(field._options.resource) + const other_params = field._options.other_params || {} - field.options = (await rsc.list()).map(item => { + field.options = (await rsc.list(other_params)).map(item => { return { display: typeof field._options.display === 'function' ? field._options.display(item) : item[field._options.display || 'display'], value: typeof field._options.value === 'function' ? field._options.value(item) : item[field._options.value || 'display'], } }) } - - if ( field.type.endsWith('.multiple') ) { - this.data[field.field] = [] - } } - - this.is_ready = true - this.$nextTick(() => { - if ( this.mode !== 'view' ) this.$refs.input[0].focus() - }) } async load() { - this.definition = this.resource_class.form_definition if (this.mode !== 'insert') { this.data = await this.resource_class.get(this.id) } + for ( const field of this.definition.fields ) { + if ( field.type.endsWith('.multiple') && !this.data[field.field] ) { + this.data[field.field] = [] + } + } + this.title = title_map[this.mode] + ' ' + this.resource_class.item + + this.is_ready = true + this.$nextTick(() => { + if ( this.mode !== 'view' ) this.$refs.input[0].focus() + }) } async on_create() { @@ -171,9 +246,12 @@ export default class FormComponent extends Component { validate() { let valid = true for ( const field of this.definition.fields ) { - if ( field.required && (!(field.field in this.data) || !this.data[field.field]) ) { + if ( (Array.isArray(field.required) ? field.required.includes(this.mode) : field.required) && (!(field.field in this.data) || !this.data[field.field]) ) { field.error = 'This field is required.' valid = false + } else if ( field.type === 'password' && this.data[field.field] !== this.data[field.field + '-confirm'] ) { + field.error = field.name + ' confirmation does not match.' + valid = false } else { field.error = '' } diff --git a/app/assets/app/cobalt/Listing.component.js b/app/assets/app/cobalt/Listing.component.js index 460ebc9..5bcb203 100644 --- a/app/assets/app/cobalt/Listing.component.js +++ b/app/assets/app/cobalt/Listing.component.js @@ -1,49 +1,62 @@ import { Component } from '../../lib/vues6/vues6.js' import { action_service } from '../service/Action.service.js' import { message_service } from '../service/Message.service.js' +import { resource_service } from '../service/Resource.service.js' const template = `
-
-

{{ resource_class.plural }}

-
- + +
+
+

{{ access_msg }}

+
-
- - - - - - - - - - - - - - - -
#{{ col.name }}
{{ index + 1 }} - {{ col.renderer(row[col.field]) }} - {{ row[col.field] ? 'Yes' : 'No' }} - {{ col.field in row ? row[col.field] : '-' }} - - -
+ + +
+

{{ resource_class.plural }}

+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + +
#{{ col.name }}
{{ index + 1 }} + {{ col.renderer(row[col.field]) }} + {{ row[col.field] ? 'Yes' : 'No' }} + {{ col.field in row ? row[col.field] : '-' }} + + +
+
` @@ -56,17 +69,23 @@ export default class ListingComponent extends Component { data = [] resource_class = {} + access_msg = '' + can_access = false + async vue_on_create() { // Load the resource - const resource_mod = await import(`../resource/${this.resource}.resource.js`) - if ( !resource_mod ) - throw new Error('Unable to load Cobalt listing resource.') + this.resource_class = await resource_service.get(this.resource) - const rsc_name = this.resource.toLowerCase().replace(/\//g, '_') - if ( !resource_mod[rsc_name] ) - throw new Error('Unable to extract resource object from module.') + // Make sure we have permission + if ( !(await this.resource_class.can('list')) ) { + this.access_msg = 'Sorry, you do not have permission to view this resource.' + this.can_access = false + return + } else { + this.access_msg = '' + this.can_access = true + } - this.resource_class = resource_mod[rsc_name] await this.load() } diff --git a/app/assets/app/dash/NavBar.component.js b/app/assets/app/dash/NavBar.component.js index 2650db9..375e204 100644 --- a/app/assets/app/dash/NavBar.component.js +++ b/app/assets/app/dash/NavBar.component.js @@ -37,6 +37,7 @@ const template = ` > My Profile + API Tokens Sign-Out of {{ app_name }}
@@ -51,6 +52,8 @@ export default class NavBarComponent extends Component { static get template() { return template } static get props() { return [] } + can = {} + constructor() { super() this.toggle_event = event_bus.event('sidebar.toggle') @@ -59,6 +62,10 @@ export default class NavBarComponent extends Component { this.app_name = session.get('app.name') } + async vue_on_create() { + this.can.api_tokens = await session.check_permissions('v1:reflect:tokens:list') + } + toggle_sidebar() { this.toggle_event.fire() } diff --git a/app/assets/app/dash/SideBar.component.js b/app/assets/app/dash/SideBar.component.js index 374e876..1713e19 100644 --- a/app/assets/app/dash/SideBar.component.js +++ b/app/assets/app/dash/SideBar.component.js @@ -1,6 +1,8 @@ import { Component } from '../../lib/vues6/vues6.js' import { event_bus } from '../service/EventBus.service.js' import { action_service } from '../service/Action.service.js' +import { resource_service } from '../service/Resource.service.js' +import { session } from '../service/Session.service.js' const template = `