UNPKG

@eclipse-scout/core

Version:
154 lines (134 loc) 5.85 kB
/* * Copyright (c) 2010, 2025 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 */ import { ajax, AjaxCall, AjaxError, BaseDoEntity, ErrorHandler, EventHandler, InitModelOf, ObjectModel, Permission, PermissionCollection, PermissionCollectionModel, PermissionCollectionType, PermissionLevel, PermissionModel, PropertyEventEmitter, scout, SomeRequired, typeName, UiNotificationEvent, uiNotifications } from '../index'; import $ from 'jquery'; export class AccessControl extends PropertyEventEmitter implements AccessControlModel { declare self: AccessControl; declare model: AccessControlModel; declare initModel: SomeRequired<this['model'], 'permissionsUrl'>; permissionsUrl: string; protected _permissionCollection: PermissionCollection; protected _permissionUpdateEventHandler: EventHandler<UiNotificationEvent>; protected _reloadTimeoutId: number; protected _call: AjaxCall; constructor() { super(); this.permissionsUrl = null; this._call = null; this._reloadTimeoutId = -1; this._permissionCollection = PermissionCollection.ensure({type: PermissionCollectionType.NONE}); this._permissionUpdateEventHandler = this._onPermissionUpdateNotify.bind(this); } override init(model: InitModelOf<this>) { this.permissionsUrl = scout.assertParameter('permissionsUrl', model.permissionsUrl); } bootstrap(): JQuery.Promise<void> { return this._subscribeForNotifications() .then(() => this._load()); } destroy() { this._unsubscribeFromNotifications(); } protected _subscribeForNotifications(): JQuery.Promise<string> { return uiNotifications.subscribe('permissionsUpdate', this._permissionUpdateEventHandler); } protected _unsubscribeFromNotifications() { uiNotifications.unsubscribe('permissionsUpdate', this._permissionUpdateEventHandler); } protected _onPermissionUpdateNotify(event: UiNotificationEvent) { const message = event.message as PermissionUpdateMessageDo; const reloadDelay = uiNotifications.computeReloadDelay(message.reloadDelayWindow); $.log.info(`About to refresh permission cache with a delay of ${reloadDelay}ms.`); if (this._reloadTimeoutId > 0) { // cancel current update and schedule a new one to ensure the newest changes from the backend are fetched in case the fetch has already started. clearTimeout(this._reloadTimeoutId); } this._reloadTimeoutId = setTimeout(() => { this._reloadTimeoutId = -1; this._load() .catch((error: AjaxError) => { // Log error scout.create(ErrorHandler, {displayError: false}).handle(error); }); }, reloadDelay); } protected _load(): JQuery.Promise<void> { return this._loadPermissionCollection() .always(() => { this._call = null; // call ended. Not necessary anymore }) .then(model => { // update permission collection this._permissionCollection = PermissionCollection.ensure(model); }); } protected _loadPermissionCollection(): JQuery.Promise<PermissionCollectionDo, AjaxError> { this._call?.abort(); // abort in case there is already a call running this._call = ajax.createCallDataObject({ url: this.permissionsUrl, method: 'GET', cache: true }, null, { maxRetries: -1, // unlimited retries retryIntervals: [300, 500, 1000, 5000] }); return this._call.call(); } /** * Check `permission` against the current {@link PermissionCollection}. * Quick check is executed synchronously while non-quick check is executed asynchronously. */ check(permission: Permission, quick: true): boolean; check(permission: Permission, quick?: false): JQuery.Promise<boolean>; check(permission: Permission, quick?: boolean): boolean | JQuery.Promise<boolean>; check(permission: Permission, quick?: boolean): boolean | JQuery.Promise<boolean> { if (!this._permissionCollection) { return quick ? false : $.resolvedPromise(false); } return this._permissionCollection.implies(permission, quick); } /** * Returns the granted {@link PermissionLevel} for a given permission instance `permission`. * - {@link Permission.Level.UNDEFINED} if `permission` is `null` or in general 'not an {@link Permission}' * - {@link Permission.Level.NONE} if no level at all is granted to `permission` * - {@link PermissionLevel} if the level can be determined exactly. * - {@link Permission.Level.UNDEFINED} if there are multiple granted permission levels possible and there is not enough data in the `permission` contained to determine the result closer. */ getGrantedPermissionLevel(permission: Permission): PermissionLevel { if (!this._permissionCollection) { return Permission.Level.UNDEFINED; } return this._permissionCollection.getGrantedPermissionLevel(permission); } } export interface AccessControlModel extends ObjectModel<AccessControl> { /** * URL pointing to a json resource that provides information about permissions (see {@link PermissionCollectionModel}). */ permissionsUrl?: string; } @typeName('scout.PermissionUpdateMessage') export class PermissionUpdateMessageDo extends BaseDoEntity { reloadDelayWindow: number; } @typeName('scout.Permission') export class PermissionDo extends BaseDoEntity implements PermissionModel { id?: string; objectType?: string; level?: PermissionLevel; } @typeName('scout.PermissionCollection') export class PermissionCollectionDo extends BaseDoEntity implements PermissionCollectionModel { permissions?: Record<string, PermissionDo[]>; type?: PermissionCollectionType; }