@eclipse-scout/core
Version:
Eclipse Scout runtime
154 lines (134 loc) • 5.85 kB
text/typescript
/*
* 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;
}
export class PermissionUpdateMessageDo extends BaseDoEntity {
reloadDelayWindow: number;
}
export class PermissionDo extends BaseDoEntity implements PermissionModel {
id?: string;
objectType?: string;
level?: PermissionLevel;
}
export class PermissionCollectionDo extends BaseDoEntity implements PermissionCollectionModel {
permissions?: Record<string, PermissionDo[]>;
type?: PermissionCollectionType;
}