From 5abd1faa80ab4459b65590e954d8966ef5da81a7 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Sun, 18 Aug 2024 17:15:43 +0800 Subject: [PATCH 1/4] implemented permission based restrictions --- addon/components/webhook/details.hbs | 2 +- addon/components/webhook/details.js | 16 + addon/controllers/api-keys/index.js | 261 +- addon/controllers/sockets/index.js | 35 - addon/controllers/webhooks/index.js | 76 +- addon/engine.js | 14 + addon/routes/api-keys/index.js | 11 + addon/routes/events/index.js | 11 + addon/routes/events/view.js | 11 + addon/routes/logs/index.js | 11 + addon/routes/logs/view.js | 11 + addon/routes/sockets/index.js | 11 + addon/routes/sockets/view.js | 13 + addon/routes/webhooks/index.js | 16 +- addon/routes/webhooks/view.js | 11 + addon/templates/api-keys/index.hbs | 8 +- addon/templates/application.hbs | 2 +- addon/templates/events/index.hbs | 2 +- addon/templates/logs/index.hbs | 2 +- addon/templates/sockets/index.hbs | 2 +- addon/templates/webhooks/index.hbs | 4 +- addon/templates/webhooks/view.hbs | 8 +- package.json | 15 +- pnpm-lock.yaml | 21480 ++++++++++++++----------- 24 files changed, 12728 insertions(+), 9305 deletions(-) diff --git a/addon/components/webhook/details.hbs b/addon/components/webhook/details.hbs index 59714dd..9de67f7 100644 --- a/addon/components/webhook/details.hbs +++ b/addon/components/webhook/details.hbs @@ -7,7 +7,7 @@
-
diff --git a/addon/components/webhook/details.js b/addon/components/webhook/details.js index 345e456..cdb5777 100644 --- a/addon/components/webhook/details.js +++ b/addon/components/webhook/details.js @@ -1,8 +1,24 @@ import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { inject as service } from '@ember/service'; import { action } from '@ember/object'; export default class WebhookDetailsComponent extends Component { + @service abilities; + @tracked permission; + @tracked doesntHavePermission = false; + + constructor(owner, { permission }) { + super(...arguments); + this.permission = permission; + this.doesntHavePermission = permission && this.abilities.cannot(permission); + } + @action onClickUpdateWebhook() { + if (this.doesntHavePermission) { + return; + } + const { webhook, onClickUpdateWebhook } = this.args; if (typeof onClickUpdateWebhook === 'function') { diff --git a/addon/controllers/api-keys/index.js b/addon/controllers/api-keys/index.js index a1c0826..3bf9f12 100644 --- a/addon/controllers/api-keys/index.js +++ b/addon/controllers/api-keys/index.js @@ -11,74 +11,15 @@ import { format as formatDate } from 'date-fns'; import getWithDefault from '@fleetbase/ember-core/utils/get-with-default'; export default class ApiKeysIndexController extends Controller { - /** - * Inject the `currentUser` service - * - * @var {Service} - */ @service currentUser; - - /** - * Inject the `intl` service - * - * @var {Service} - */ @service intl; - - /** - * Inject the `modalsManager` service - * - * @var {Service} - */ @service modalsManager; - - /** - * Inject the `notifications` service - * - * @var {Service} - */ @service notifications; - - /** - * Inject the `store` service - * - * @var {Service} - */ @service store; - - /** - * Inject the `crud` service - * - * @var {Service} - */ @service crud; - - /** - * Inject the `fetch` service - * - * @var {Service} - */ @service fetch; - - /** - * Inject the `theme` service - * - * @var {Service} - */ @service theme; - - /** - * Inject the `hostRouter` service - * - * @var {Service} - */ @service hostRouter; - - /** - * Inject the `universe` service - * - * @var {Service} - */ @service universe; /** @@ -142,7 +83,7 @@ export default class ApiKeysIndexController extends Controller { * * @memberof ApiKeysIndexController */ - @computed('testMode') get isTestMode() { + @computed('testMode') get isTestMode () { return this.testMode === true; } @@ -151,7 +92,7 @@ export default class ApiKeysIndexController extends Controller { * * @memberof ApiKeysIndexController */ - @computed('currentUser.options.testKey') get testKey() { + @computed('currentUser.options.testKey') get testKey () { return this.currentUser.getOption('testKey'); } @@ -165,6 +106,7 @@ export default class ApiKeysIndexController extends Controller { label: this.intl.t('developers.common.name'), valuePath: 'name', cellComponent: 'table/cell/anchor', + permission: 'developers view api-key', action: this.editApiKey, resizable: true, width: '10%', @@ -236,13 +178,26 @@ export default class ApiKeysIndexController extends Controller { width: '10%', align: 'right', actions: [ - { label: this.intl.t('developers.api-keys.index.edit-key'), fn: this.editApiKey }, - { label: this.intl.t('developers.api-keys.index.roll-key'), fn: this.rollApiKey }, - { label: this.intl.t('developers.api-keys.index.view-logs'), fn: this.viewRequestLogs }, + { + label: this.intl.t('developers.api-keys.index.edit-key'), + fn: this.editApiKey, + permission: 'developers view api-key', + }, + { + label: this.intl.t('developers.api-keys.index.roll-key'), + fn: this.rollApiKey, + permission: 'developers roll api-key', + }, + { + label: this.intl.t('developers.api-keys.index.view-logs'), + fn: this.viewRequestLogs, + permission: 'developers view log', + }, { label: this.intl.t('developers.api-keys.index.delete-key'), fn: this.deleteApiKey, className: 'text-red-700 hover:text-red-800', + permission: 'developers delete api-key', }, ], }, @@ -253,7 +208,7 @@ export default class ApiKeysIndexController extends Controller { * * @void */ - @task({ restartable: true }) *search({ target: { value } }) { + @task({ restartable: true }) *search ({ target: { value } }) { // if no query don't search if (isBlank(value)) { this.query = null; @@ -277,7 +232,7 @@ export default class ApiKeysIndexController extends Controller { * * @void */ - @action toggleTestMode(testMode = false) { + @action toggleTestMode (testMode = false) { this.currentUser.setOption('sandbox', testMode); this.testMode = testMode; this.theme.setEnvironment(); @@ -292,7 +247,7 @@ export default class ApiKeysIndexController extends Controller { * * @void */ - @action toggleTestKey({ target: { value } }) { + @action toggleTestKey ({ target: { value } }) { if (isBlank(value)) { this.currentUser.setOption('testKey', null); return; @@ -306,7 +261,8 @@ export default class ApiKeysIndexController extends Controller { * * @void */ - @action createApiKey() { + @action createApiKey () { + const formPermission = 'developers create api-key'; const apiKey = this.store.createRecord('api-credential', { test_mode: this.testMode, }); @@ -315,8 +271,27 @@ export default class ApiKeysIndexController extends Controller { title: this.intl.t('developers.api-keys.index.new-api-key-title'), acceptButtonIcon: 'check', acceptButtonIconPrefix: 'fas', + acceptButtonDisabled: this.abilities.cannot(formPermission), + acceptButtonHelpText: this.abilities.cannot(formPermission) ? this.intl.t('common.unauthorized') : null, successMessage: this.intl.t('developers.api-keys.index.new-api-key-message'), + formPermission, apiKey, + confirm: async modal => { + modal.startLoading(); + + if (this.abilities.cannot(formPermission)) { + return this.notifications.warning(this.intl.t('common.permissions-required-for-changes')); + } + + try { + await apiKey.save(); + this.notifications.success(modal.getOption('successMessage')); + return this.hostRouter.refresh(); + } catch (error) { + this.notifications.serverError(error); + modal.stopLoading(); + } + }, }); } @@ -325,32 +300,36 @@ export default class ApiKeysIndexController extends Controller { * * @void */ - @action editApiKey(apiKey, options = {}) { + @action editApiKey (apiKey, options = {}) { + const formPermission = 'developers update api-key'; this.modalsManager.show('modals/api-key-form', { title: this.intl.t('developers.api-keys.index.edit-api-key-title'), acceptButtonIcon: 'save', + acceptButtonDisabled: this.abilities.cannot(formPermission), + acceptButtonHelpText: this.abilities.cannot(formPermission) ? this.intl.t('common.unauthorized') : null, successMessage: this.intl.t('developers.api-keys.index.edit-api-key-message'), expirationOptions: this.expirationOptions, testMode: this.currentUser.getOption('sandbox') || false, apiKey, + formPermission, setExpiration: ({ target }) => { apiKey.expires_at = target.value || null; }, - confirm: (modal, done) => { + confirm: async modal => { modal.startLoading(); - apiKey - .save() - .then(() => { - this.notifications.success(modal.getOption('successMessage')); - return this.hostRouter.refresh().finally(() => { - done(); - }); - }) - .catch((error) => { - this.notifications.serverError(error); - modal.stopLoading(); - }); + if (this.abilities.cannot(formPermission)) { + return this.notifications.warning(this.intl.t('common.permissions-required-for-changes')); + } + + try { + await apiKey.save(); + this.notifications.success(modal.getOption('successMessage')); + return this.hostRouter.refresh(); + } catch (error) { + this.notifications.serverError(error); + modal.stopLoading(); + } }, ...options, }); @@ -361,25 +340,25 @@ export default class ApiKeysIndexController extends Controller { * * @void */ - @action renameApiKey(apiKey) { + @action renameApiKey (apiKey) { + const formPermission = 'developers update api-key'; const apiKeyName = getWithDefault(apiKey, 'name', this.intl.t('developers.api-keys.index.untitled')); this.modalsManager.show('modals/rename-api-key-form', { title: this.intl.t('developers.api-keys.index.rename-api-key-title', { apiKeyName }), apiKey, - confirm: (modal, done) => { + formPermission, + confirm: async modal => { modal.startLoading(); - apiKey - .save() - .then(() => { - this.notifications.success(this.intl.t('developers.api-keys.index.rename-api-key-success-message', { apiKeyName })); - return done(); - }) - .catch((error) => { - this.notifications.serverError(error); - modal.stopLoading(); - }); + try { + await apiKey.save(); + this.notifications.success(this.intl.t('developers.api-keys.index.rename-api-key-success-message', { apiKeyName })); + modal.done(); + } catch (error) { + this.notifications.serverError(error); + modal.stopLoading(); + } }, }); } @@ -389,26 +368,22 @@ export default class ApiKeysIndexController extends Controller { * * @void */ - @action deleteApiKey(apiKey) { + @action deleteApiKey (apiKey) { const apiKeyName = getWithDefault(apiKey, 'name', this.intl.t('developers.api-keys.index.untitled')); this.modalsManager.confirm({ title: this.intl.t('developers.api-keys.index.delete-api-key-title', { apiKeyName }), body: this.intl.t('developers.api-keys.index.delete-api-key-body'), - confirm: (modal, done) => { + confirm: async modal => { modal.startLoading(); - apiKey - .destroyRecord() - .then(() => { - this.notifications.success(this.intl.t('developers.api-keys.index.delete-api-key-title', { apiKeyName })); - return this.hostRouter.refresh().finally(() => { - done(); - }); - }) - .catch((error) => { - this.notifications.serverError(error); - modal.stopLoading(); - }); + try { + await apiKey.destroyRecord(); + this.notifications.success(this.intl.t('developers.api-keys.index.delete-api-key-title', { apiKeyName })); + return this.hostRouter.refresh(); + } catch (error) { + this.notifications.serverError(error); + modal.stopLoading(); + } }, }); } @@ -419,7 +394,7 @@ export default class ApiKeysIndexController extends Controller { * @param {Array} selected an array of selected models * @void */ - @action bulkDeleteApiCredentials() { + @action bulkDeleteApiCredentials () { const selected = this.table.selectedRows; this.crud.bulkDelete(selected, { @@ -435,7 +410,7 @@ export default class ApiKeysIndexController extends Controller { * * @void */ - @action rollApiKey(apiKey) { + @action rollApiKey (apiKey) { const apiKeyName = getWithDefault(apiKey, 'name', this.intl.t('developers.api-keys.index.untitled')); this.modalsManager.show('modals/roll-api-key-form', { @@ -450,25 +425,21 @@ export default class ApiKeysIndexController extends Controller { viewRequestLogs: this.viewRequestLogs, password: null, apiKey, - confirm: (modal, done) => { + confirm: async modal => { modal.startLoading(); - this.fetch - .patch( + + try { + await this.fetch.patch( `api-credentials/roll/${apiKey.id}`, - { - password: modal.getOption('password'), - expiration: apiKey.get('expires_at'), - }, + { password: modal.getOption('password'), expiration: apiKey.get('expires_at') }, { normalizeToEmberData: true } - ) - .then(() => { - this.notifications.success(this.intl.t('developers.api-keys.index.roll-api-key-success-message', { apiKeyName })); - return done(); - }) - .catch((error) => { - modal.stopLoading(); - this.notifications.serverError(error, this.intl.t('developers.api-keys.index.roll-api-key-error-message')); - }); + ); + this.notifications.success(this.intl.t('developers.api-keys.index.roll-api-key-success-message', { apiKeyName })); + modal.done(); + } catch (error) { + this.notifications.serverError(error, this.intl.t('developers.api-keys.index.roll-api-key-error-message')); + modal.stopLoading(); + } }, }); } @@ -478,7 +449,7 @@ export default class ApiKeysIndexController extends Controller { * * @void */ - @action viewRequestLogs(apiKey) { + @action viewRequestLogs (apiKey) { return this.universe.transitionToEngineRoute('@fleetbase/dev-engine', 'logs.index', { queryParams: { key: apiKey.id }, }); @@ -489,7 +460,7 @@ export default class ApiKeysIndexController extends Controller { * * @void */ - @action exportApiKeys() { + @action exportApiKeys () { this.modalsManager.show('modals/export-form', { title: this.intl.t('developers.api-keys.index.export-api'), acceptButtonText: this.intl.t('developers.api-keys.index.export-api-accept-button-text'), @@ -498,13 +469,13 @@ export default class ApiKeysIndexController extends Controller { setFormat: ({ target }) => { this.modalsManager.setOption('format', target.value || null); }, - confirm: (modal, done) => { + confirm: async modal => { modal.startLoading(); const format = modal.getOption('format', 'xlsx'); - this.fetch - .download( + try { + await this.fetch.download( `api-credentials/export`, { format, @@ -512,20 +483,18 @@ export default class ApiKeysIndexController extends Controller { { fileName: `api-credentials-${formatDate(new Date(), 'yyyy-MM-dd-HH:mm')}.${format}`, } - ) - .then(() => { - later( - this, - () => { - return done(); - }, - 600 - ); - }) - .catch((error) => { - modal.stopLoading(); - this.notifications.serverError(error, this.intl.t('developers.api-keys.index.export-api-error-message')); - }); + ); + later( + this, + () => { + return model.done(); + }, + 600 + ); + } catch (error) { + this.notifications.serverError(error, this.intl.t('developers.api-keys.index.export-api-error-message')); + modal.stopLoading(); + } }, }); } @@ -533,7 +502,7 @@ export default class ApiKeysIndexController extends Controller { /** * Reload data. */ - @action reload() { + @action reload () { return this.hostRouter.refresh(); } } diff --git a/addon/controllers/sockets/index.js b/addon/controllers/sockets/index.js index 90af448..d554676 100644 --- a/addon/controllers/sockets/index.js +++ b/addon/controllers/sockets/index.js @@ -5,46 +5,11 @@ import { action, computed } from '@ember/object'; import fromStore from '@fleetbase/ember-core/decorators/from-store'; export default class SocketsIndexController extends BaseController { - /** - * Inject the `modalsManager` service - * - * @var {Service} - */ @service modalsManager; - - /** - * Inject the `intl` service - * - * @var {Service} - */ @service intl; - - /** - * Inject the `notifications` service - * - * @var {Service} - */ @service notifications; - - /** - * Inject the `currentUser` service - * - * @var {Service} - */ @service currentUser; - - /** - * Inject the `store` service - * - * @var {Service} - */ @service store; - - /** - * Inject the `hostRouter` service - * - * @var {Service} - */ @service hostRouter; /** diff --git a/addon/controllers/webhooks/index.js b/addon/controllers/webhooks/index.js index a21b9f2..5ab629b 100644 --- a/addon/controllers/webhooks/index.js +++ b/addon/controllers/webhooks/index.js @@ -64,7 +64,7 @@ export default class WebhooksIndexController extends BaseController { * * @var {Object} */ - @computed('webhookEvents.[]') get groupedApiEvents() { + @computed('webhookEvents.[]') get groupedApiEvents () { return groupApiEvents(this.webhookEvents); } @@ -138,6 +138,7 @@ export default class WebhooksIndexController extends BaseController { width: '40%', sortable: false, cellComponent: 'table/cell/link-to', + permission: 'developers view webhook', route: 'webhooks.view', cellClassNames: 'no-underline', }, @@ -172,14 +173,17 @@ export default class WebhooksIndexController extends BaseController { { label: this.intl.t('developers.webhooks.index.view-logs'), fn: this.viewWebhook, + permission: 'developers view webhook', }, { label: this.intl.t('developers.webhooks.index.edit-webhook'), fn: this.editWebhook, + permission: 'developers update webhook', }, { label: this.intl.t('developers.webhooks.index.delete-webhook'), fn: this.deleteWebhook, + permission: 'developers delete webhook', }, ], sortable: false, @@ -194,7 +198,7 @@ export default class WebhooksIndexController extends BaseController { * * @void */ - @task({ restartable: true }) *search({ target: { value } }) { + @task({ restartable: true }) *search ({ target: { value } }) { // if no query don't search if (isBlank(value)) { this.query = null; @@ -218,7 +222,8 @@ export default class WebhooksIndexController extends BaseController { * * @void */ - @action createWebhook() { + @action createWebhook () { + const formPermission = 'developers create webhook'; const webhook = this.store.createRecord('webhook-endpoint', { events: [], mode: this.currentUser.getOption('sandbox') ? 'test' : 'live', @@ -229,7 +234,26 @@ export default class WebhooksIndexController extends BaseController { acceptButtonText: this.intl.t('developers.webhooks.index.add-webhook-button-text'), acceptButtonIcon: 'check', acceptButtonIconPrefix: 'fas', + acceptButtonDisabled: this.abilities.cannot(formPermission), + acceptButtonHelpText: this.abilities.cannot(formPermission) ? this.intl.t('common.unauthorized') : null, + formPermission, webhook, + confirm: async modal => { + modal.startLoading(); + + if (this.abilities.cannot(formPermission)) { + return this.notifications.warning(this.intl.t('common.permissions-required-for-changes')); + } + + try { + await webhook.save(); + this.notifications.success(this.intl.t('developers.webhooks.index.new-webhook-success-message')); + return this.hostRouter.refresh(); + } catch (error) { + this.notifications.serverError(error); + modal.stopLoading(); + } + }, }); } @@ -240,11 +264,15 @@ export default class WebhooksIndexController extends BaseController { * @param {Object} options * @void */ - @action editWebhook(webhook, options = {}) { + @action editWebhook (webhook, options = {}) { + const formPermission = 'developers update webhook'; this.modalsManager.show('modals/webhook-form', { title: this.intl.t('developers.webhooks.index.edit-webhook-endpoint'), acceptButtonText: this.intl.t('developers.webhooks.index.edit-webhook-endpoint-button-text'), acceptButtonIcon: 'save', + acceptButtonDisabled: this.abilities.cannot(formPermission), + acceptButtonHelpText: this.abilities.cannot(formPermission) ? this.intl.t('common.unauthorized') : null, + formPermission, declineButtonIcon: 'times', declineButtonIconPrefix: 'fas', eventOptions: this.groupedApiEvents, @@ -257,14 +285,14 @@ export default class WebhooksIndexController extends BaseController { setApiCredential: ({ target }) => { webhook.api_credential_uuid = target.value || null; }, - searchEvents: (query) => { + searchEvents: query => { if (typeof query !== 'string') { return; } const resources = Object.keys(this.groupedApiEvents); const filteredEvents = {}; - resources.forEach((eventResource) => { - filteredEvents[eventResource] = this.groupedApiEvents[eventResource].filter((event) => { + resources.forEach(eventResource => { + filteredEvents[eventResource] = this.groupedApiEvents[eventResource].filter(event => { return event.toLowerCase().includes(query.toLowerCase()); }); // if 0 events remove from filter @@ -274,14 +302,14 @@ export default class WebhooksIndexController extends BaseController { }); this.modalsManager.setOption('eventOptions', filteredEvents); }, - addEvent: (event) => { + addEvent: event => { if (webhook.events.includes(event)) { return; } webhook.events.pushObject(event); }, - removeEvent: (event) => { + removeEvent: event => { webhook.events.removeObject(event); }, clearEvents: () => { @@ -290,13 +318,21 @@ export default class WebhooksIndexController extends BaseController { receiveAllEvents: () => { webhook.events.pushObjects(this.webhookEvents); }, - confirm: (modal) => { + confirm: async modal => { modal.startLoading(); - return webhook.save().then(() => { + if (this.abilities.cannot(formPermission)) { + return this.notifications.warning(this.intl.t('common.permissions-required-for-changes')); + } + + try { + await webhook.save(); this.notifications.success(this.intl.t('developers.webhooks.index.new-webhook-success-message')); return this.hostRouter.refresh(); - }); + } catch (error) { + this.notifications.serverError(error); + modal.stopLoading(); + } }, ...options, }); @@ -309,17 +345,21 @@ export default class WebhooksIndexController extends BaseController { * @param {Object} options * @void */ - @action deleteWebhook(webhook, options = {}) { + @action deleteWebhook (webhook, options = {}) { this.modalsManager.confirm({ title: this.intl.t('developers.webhooks.index.delete-webhook-endpoint'), body: this.intl.t('developers.webhooks.index.delete-webhook-endpoint-body'), - confirm: (modal) => { + confirm: async modal => { modal.startLoading(); - return webhook.destroyRecord().then(() => { + try { + await webhook.destroyRecord(); this.notifications.success(this.intl.t('developers.webhooks.index.delete-webhook-success-message')); return this.hostRouter.refresh(); - }); + } catch (error) { + this.notifications.serverError(error); + modal.stopLoading(); + } }, ...options, }); @@ -332,14 +372,14 @@ export default class WebhooksIndexController extends BaseController { * @return {Transition} * @memberof WebhooksIndexController */ - @action viewWebhook(webhook) { + @action viewWebhook (webhook) { return this.transitionToRoute('webhooks.view', webhook); } /** * Reload data. */ - @action reload() { + @action reload () { return this.hostRouter.refresh(); } } diff --git a/addon/engine.js b/addon/engine.js index 369e4dd..33bccd2 100644 --- a/addon/engine.js +++ b/addon/engine.js @@ -3,6 +3,7 @@ import loadInitializers from 'ember-load-initializers'; import Resolver from 'ember-resolver'; import config from './config/environment'; import services from '@fleetbase/ember-core/exports/services'; +import WidgetApiMetricsComponent from './components/widget/api-metrics'; const { modulePrefix } = config; const externalRoutes = ['console', 'extensions']; @@ -17,6 +18,19 @@ export default class DevEngine extends Engine { setupExtension = function (app, engine, universe) { // register menu item in header universe.registerHeaderMenuItem('Developers', 'console.developers', { icon: 'code', priority: 2 }); + // register metrics widget + const ApiMetricsWidgetDefinition = { + widgetId: 'dev-api-metrics-widget', + name: 'Developer API Metrics', + description: 'Key metrics from API Usage.', + icon: 'code', + component: WidgetApiMetricsComponent, + grid_options: { w: 12, h: 12, minW: 8, minH: 12 }, + options: { + title: 'API Metrics', + }, + }; + universe.registerDashboardWidgets([ApiMetricsWidgetDefinition]); }; } diff --git a/addon/routes/api-keys/index.js b/addon/routes/api-keys/index.js index c058825..9269106 100644 --- a/addon/routes/api-keys/index.js +++ b/addon/routes/api-keys/index.js @@ -6,6 +6,10 @@ export default class ApiKeysIndexRoute extends Route { @service store; @service loader; @service currentUser; + @service abilities; + @service notifications; + @service hostRouter; + @service intl; queryParams = { page: { @@ -26,6 +30,13 @@ export default class ApiKeysIndexRoute extends Route { this.loader.showOnInitialTransition(transition, 'section.next-view-section', { loadingMessage: 'Loading api keys...' }); } + beforeModel () { + if (this.abilities.cannot('developers list api-key')) { + this.notifications.warning(this.intl.t('common.unauthorized-access')); + return this.hostRouter.transitionTo('console.developers.home'); + } + } + model(params) { return this.store.query('api-credential', { ...params }); } diff --git a/addon/routes/events/index.js b/addon/routes/events/index.js index 384c6a9..c5f6427 100644 --- a/addon/routes/events/index.js +++ b/addon/routes/events/index.js @@ -5,6 +5,10 @@ import { action } from '@ember/object'; export default class EventsIndexRoute extends Route { @service store; @service loader; + @service abilities; + @service notifications; + @service hostRouter; + @service intl; queryParams = { query: { @@ -31,6 +35,13 @@ export default class EventsIndexRoute extends Route { this.loader.showOnInitialTransition(transition, 'section.next-view-section', { loadingMessage: 'Loading events...' }); } + beforeModel () { + if (this.abilities.cannot('developers list event')) { + this.notifications.warning(this.intl.t('common.unauthorized-access')); + return this.hostRouter.transitionTo('console.developers.home'); + } + } + model(params) { return this.store.query('api-event', params); } diff --git a/addon/routes/events/view.js b/addon/routes/events/view.js index 9ce118f..4b2fa85 100644 --- a/addon/routes/events/view.js +++ b/addon/routes/events/view.js @@ -3,6 +3,17 @@ import { inject as service } from '@ember/service'; export default class EventsViewRoute extends Route { @service store; + @service abilities; + @service notifications; + @service hostRouter; + @service intl; + + beforeModel () { + if (this.abilities.cannot('developers view event')) { + this.notifications.warning(this.intl.t('common.unauthorized-access')); + return this.hostRouter.transitionTo('console.developers.events.index'); + } + } model({ public_id }) { return this.store.findRecord('api-event', public_id); diff --git a/addon/routes/logs/index.js b/addon/routes/logs/index.js index 70e1eb3..970a866 100644 --- a/addon/routes/logs/index.js +++ b/addon/routes/logs/index.js @@ -5,6 +5,10 @@ import { action } from '@ember/object'; export default class LogsIndexRoute extends Route { @service store; @service loader; + @service abilities; + @service notifications; + @service hostRouter; + @service intl; queryParams = { query: { @@ -37,6 +41,13 @@ export default class LogsIndexRoute extends Route { this.loader.showOnInitialTransition(transition, 'section.next-view-section', { loadingMessage: 'Loading logs...' }); } + beforeModel () { + if (this.abilities.cannot('developers list log')) { + this.notifications.warning(this.intl.t('common.unauthorized-access')); + return this.hostRouter.transitionTo('console.developers.home'); + } + } + model(params) { return this.store.query('api-request-log', params); } diff --git a/addon/routes/logs/view.js b/addon/routes/logs/view.js index d704297..b001719 100644 --- a/addon/routes/logs/view.js +++ b/addon/routes/logs/view.js @@ -3,6 +3,17 @@ import { inject as service } from '@ember/service'; export default class LogsViewRoute extends Route { @service store; + @service abilities; + @service notifications; + @service hostRouter; + @service intl; + + beforeModel () { + if (this.abilities.cannot('developers view log')) { + this.notifications.warning(this.intl.t('common.unauthorized-access')); + return this.hostRouter.transitionTo('console.developers.logs.index'); + } + } model({ public_id }) { return this.store.findRecord('api-request-log', public_id); diff --git a/addon/routes/sockets/index.js b/addon/routes/sockets/index.js index 84e0fcf..b2ae4b2 100644 --- a/addon/routes/sockets/index.js +++ b/addon/routes/sockets/index.js @@ -4,8 +4,19 @@ import { action } from '@ember/object'; export default class SocketsIndexRoute extends Route { @service loader; + @service abilities; + @service notifications; + @service hostRouter; + @service intl; @action loading(transition) { this.loader.showOnInitialTransition(transition, 'section.next-view-section', { loadingMessage: 'Loading websockets...' }); } + + beforeModel () { + if (this.abilities.cannot('developers list socket')) { + this.notifications.warning(this.intl.t('common.unauthorized-access')); + return this.hostRouter.transitionTo('console.developers.home'); + } + } } diff --git a/addon/routes/sockets/view.js b/addon/routes/sockets/view.js index 8990f70..e3cb09f 100644 --- a/addon/routes/sockets/view.js +++ b/addon/routes/sockets/view.js @@ -1,6 +1,19 @@ import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; export default class SocketsViewRoute extends Route { + @service abilities; + @service notifications; + @service hostRouter; + @service intl; + + beforeModel () { + if (this.abilities.cannot('developers view socket')) { + this.notifications.warning(this.intl.t('common.unauthorized-access')); + return this.hostRouter.transitionTo('console.developers.sockets.index'); + } + } + model({ name }) { return { name }; } diff --git a/addon/routes/webhooks/index.js b/addon/routes/webhooks/index.js index 37873a5..e6c7071 100644 --- a/addon/routes/webhooks/index.js +++ b/addon/routes/webhooks/index.js @@ -4,8 +4,11 @@ import { action } from '@ember/object'; export default class WebhooksIndexRoute extends Route { @service store; - @service fetch; @service loader; + @service abilities; + @service notifications; + @service hostRouter; + @service intl; queryParams = { page: { @@ -19,11 +22,18 @@ export default class WebhooksIndexRoute extends Route { }, }; - @action loading(transition) { + @action loading (transition) { this.loader.showOnInitialTransition(transition, 'section.next-view-section', { loadingMessage: 'Loading webhooks...' }); } - model(params) { + beforeModel () { + if (this.abilities.cannot('developers list webhook')) { + this.notifications.warning(this.intl.t('common.unauthorized-access')); + return this.hostRouter.transitionTo('console.developers.home'); + } + } + + model (params) { return this.store.query('webhook-endpoint', params); } } diff --git a/addon/routes/webhooks/view.js b/addon/routes/webhooks/view.js index 48d01e4..03f78fe 100644 --- a/addon/routes/webhooks/view.js +++ b/addon/routes/webhooks/view.js @@ -3,6 +3,17 @@ import { inject as service } from '@ember/service'; export default class WebhooksViewRoute extends Route { @service store; + @service abilities; + @service notifications; + @service hostRouter; + @service intl; + + beforeModel () { + if (this.abilities.cannot('developers view webhook')) { + this.notifications.warning(this.intl.t('common.unauthorized-access')); + return this.hostRouter.transitionTo('console.developers.webhooks.index'); + } + } model({ id }) { return this.store.findRecord('webhook-endpoint', id); diff --git a/addon/templates/api-keys/index.hbs b/addon/templates/api-keys/index.hbs index a5d9058..7b6ed13 100644 --- a/addon/templates/api-keys/index.hbs +++ b/addon/templates/api-keys/index.hbs @@ -1,4 +1,4 @@ - +