mirror of
https://github.com/datahub-project/datahub.git
synced 2025-11-01 11:19:05 +00:00
308 lines
9.7 KiB
TypeScript
308 lines
9.7 KiB
TypeScript
import Component from '@ember/component';
|
|
import { get, set, getProperties, computed } from '@ember/object';
|
|
import { task, TaskInstance } from 'ember-concurrency';
|
|
import { action } from '@ember-decorators/object';
|
|
import { isUserInAcl } from 'wherehows-web/constants/dataset-aclaccess';
|
|
import CurrentUser from 'wherehows-web/services/current-user';
|
|
import { IUser } from 'wherehows-web/typings/api/authentication/user';
|
|
import {
|
|
IAccessControlAccessTypeOption,
|
|
IAccessControlEntry,
|
|
IRequestAccessControlEntry
|
|
} from 'wherehows-web/typings/api/datasets/aclaccess';
|
|
import { readDatasetAcls, removeAclAccess, requestAclAccess } from 'wherehows-web/utils/api/datasets/acl-access';
|
|
import {
|
|
AccessControlAccessType,
|
|
getAccessControlTypeOptions,
|
|
getDefaultRequestAccessControlEntry
|
|
} from 'wherehows-web/utils/datasets/acl-access';
|
|
import { hasEnumerableKeys } from 'wherehows-web/utils/object';
|
|
import Notifications, { NotificationEvent } from 'wherehows-web/services/notifications';
|
|
import { notificationDialogActionFactory } from 'wherehows-web/utils/notifications/notifications';
|
|
import { service } from '@ember-decorators/service';
|
|
import { equal } from '@ember-decorators/object/computed';
|
|
|
|
/**
|
|
* Enumerates the string value for an acl access request
|
|
* @type string
|
|
*/
|
|
enum AclRequestType {
|
|
Removal = 'removed',
|
|
Approval = 'approved'
|
|
}
|
|
|
|
export default class DatasetAclAccessContainer extends Component {
|
|
/**
|
|
* The currently logged in user service
|
|
* @type {ComputedProperty<CurrentUser>}
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
@service('current-user')
|
|
currentUser: CurrentUser;
|
|
|
|
/**
|
|
* App notifications service
|
|
* @type {ComputedProperty<Notifications>}
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
@service
|
|
notifications: Notifications;
|
|
|
|
/**
|
|
* The currently logged in user
|
|
* @type {IUser}
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
user: IUser;
|
|
|
|
/**
|
|
* The list of acls
|
|
* @type {Array<IAccessControlEntry>}
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
acls: Array<IAccessControlEntry> = [];
|
|
|
|
/**
|
|
* The user's acl entry if exists
|
|
* @type {Array<IAccessControlEntry>}
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
userAcl: Array<IAccessControlEntry> = [];
|
|
|
|
/**
|
|
* Flag indicating that this dataset platform has JIT ACL access enabled
|
|
* @type {boolean}
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
isJitAclAccessEnabled: boolean;
|
|
|
|
/**
|
|
* Who to contact in case of error
|
|
*/
|
|
jitAclContact: string;
|
|
|
|
/**
|
|
* Request object for the current user requesting access control
|
|
* @type {IRequestAccessControlEntry}
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
userAclRequest: IRequestAccessControlEntry = getDefaultRequestAccessControlEntry();
|
|
|
|
/**
|
|
* Lists dropdown options for acl access type
|
|
* @type {Array<IAccessControlAccessTypeOption>}
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
accessTypeDropDownOptions: Array<IAccessControlAccessTypeOption> = getAccessControlTypeOptions(<
|
|
Array<AccessControlAccessType>
|
|
>Object.values(AccessControlAccessType));
|
|
|
|
/**
|
|
* Checks if there is a acl entry in the userAcl tuple
|
|
* @type {ComputedProperty<boolean>}
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
@equal('userAcl.length', 1)
|
|
userHasAclAccess: boolean;
|
|
|
|
/**
|
|
* Dataset urn
|
|
* @type {string}
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
urn: string;
|
|
|
|
didInsertElement() {
|
|
get(this, 'getContainerDataTask').perform();
|
|
}
|
|
|
|
didUpdateAttrs() {
|
|
get(this, 'getContainerDataTask').perform();
|
|
}
|
|
|
|
constructor() {
|
|
super(...arguments);
|
|
|
|
// Default to false if not set externally
|
|
typeof this.isJitAclAccessEnabled === 'boolean' || set(this, 'isJitAclAccessEnabled', false);
|
|
}
|
|
|
|
/**
|
|
* Checks if the acl request entry object is valid / correctly entered
|
|
* @type {ComputedProperty<boolean>}
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
hasValidAclRequest = computed('userAclRequest.businessJustification', function(
|
|
this: DatasetAclAccessContainer
|
|
): boolean {
|
|
const userAclRequest = get(this, 'userAclRequest');
|
|
return hasEnumerableKeys(userAclRequest) && !!userAclRequest.businessJustification;
|
|
});
|
|
|
|
/**
|
|
* Notifies user of changes to acl access
|
|
* @param {(string | Error)} param notification message string or error object
|
|
* @returns {void}
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
notifyStatus(this: DatasetAclAccessContainer, param: AclRequestType | Error) {
|
|
type AclNotificationMessage = { [key in AclRequestType]: string };
|
|
const { notify } = get(this, 'notifications');
|
|
const isAclRequestType = (value: any): value is AclRequestType => Object.values(AclRequestType).includes(value);
|
|
|
|
if (isAclRequestType(param)) {
|
|
const notificationMessage = (<AclNotificationMessage>{
|
|
[AclRequestType.Approval]: 'Congrats, your request has been approved!',
|
|
[AclRequestType.Removal]: 'Your access has been removed'
|
|
})[param];
|
|
|
|
return param === AclRequestType.Approval
|
|
? this.notifyApproval(notificationMessage)
|
|
: notify(NotificationEvent.success, { content: notificationMessage });
|
|
}
|
|
|
|
notify(NotificationEvent.error, { content: param.message });
|
|
}
|
|
|
|
/**
|
|
* Notifies the user that approval was successful but alc access propagation will
|
|
* take some time
|
|
* @param {string} message
|
|
*/
|
|
notifyApproval(this: DatasetAclAccessContainer, message: string) {
|
|
const { notify } = get(this, 'notifications');
|
|
const { dialogActions } = notificationDialogActionFactory();
|
|
|
|
notify(NotificationEvent.confirm, {
|
|
header: 'Successfully processed ACL request',
|
|
content: `${message} Your access should be enabled within 15 minutes. Contact ${
|
|
this.jitAclContact
|
|
} if you're still having issues after that time.`,
|
|
dialogActions,
|
|
confirmButtonText: false,
|
|
dismissButtonText: 'Dismiss'
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Parent container task to get all data for the container component
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
getContainerDataTask = task(function*(this: DatasetAclAccessContainer): IterableIterator<any> {
|
|
if (get(this, 'isJitAclAccessEnabled')) {
|
|
const { getCurrentUserTask, getDatasetAclsTask, checkUserAccessTask } = getProperties(this, [
|
|
'getCurrentUserTask',
|
|
'getDatasetAclsTask',
|
|
'checkUserAccessTask'
|
|
]);
|
|
const user: DatasetAclAccessContainer['user'] = yield getCurrentUserTask.perform();
|
|
|
|
if (user) {
|
|
yield getDatasetAclsTask.perform();
|
|
yield checkUserAccessTask.perform();
|
|
}
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Fetches the current logged in user
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
getCurrentUserTask = task(function*(this: DatasetAclAccessContainer): IterableIterator<IUser> {
|
|
const { currentUser } = get(this, 'currentUser');
|
|
|
|
return set(this, 'user', currentUser);
|
|
});
|
|
|
|
/**
|
|
* Fetches the list of acls for this dataset
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
getDatasetAclsTask = task(function*(
|
|
this: DatasetAclAccessContainer
|
|
): IterableIterator<Promise<Array<IAccessControlEntry>>> {
|
|
return set(this, 'acls', yield readDatasetAcls(get(this, 'urn')));
|
|
});
|
|
|
|
/**
|
|
* Checks if the current user has access to the acl
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
checkUserAccessTask = task(function*(this: DatasetAclAccessContainer): IterableIterator<Array<IAccessControlEntry>> {
|
|
const {
|
|
user: { userName },
|
|
acls
|
|
} = getProperties(this, ['user', 'acls']);
|
|
const userAcl: Array<IAccessControlEntry> = yield isUserInAcl(userName)(acls);
|
|
|
|
return get(this, 'userAcl').setObjects(userAcl);
|
|
}).restartable();
|
|
|
|
/**
|
|
* Requests the current user be added to the acl
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
requestAccessTask = task(function*(this: DatasetAclAccessContainer): IterableIterator<Promise<void>> {
|
|
if (get(this, 'hasValidAclRequest')) {
|
|
yield requestAclAccess(get(this, 'urn'), get(this, 'userAclRequest'));
|
|
}
|
|
}).drop();
|
|
|
|
/**
|
|
* Requests access for the current user, get the dataset acl
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
requestAccessAndCheckAccessTask = task(function*(this: DatasetAclAccessContainer): IterableIterator<any> {
|
|
try {
|
|
yield get(this, 'requestAccessTask').perform();
|
|
yield get(this, 'getDatasetAclsTask').perform();
|
|
yield get(this, 'checkUserAccessTask').perform();
|
|
|
|
get(this, 'userHasAclAccess') && this.notifyStatus(AclRequestType.Approval);
|
|
} catch (e) {
|
|
this.notifyStatus(e);
|
|
}
|
|
}).drop();
|
|
|
|
/**
|
|
* Requests the current user be removed from the dataset's acl
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
removeAccessTask = task(function*(
|
|
this: DatasetAclAccessContainer
|
|
): IterableIterator<
|
|
Promise<void> | TaskInstance<Array<IAccessControlEntry>> | TaskInstance<Promise<IAccessControlEntry[]>>
|
|
> {
|
|
try {
|
|
yield removeAclAccess(get(this, 'urn'));
|
|
yield get(this, 'getDatasetAclsTask').perform();
|
|
yield get(this, 'checkUserAccessTask').perform();
|
|
|
|
!get(this, 'userHasAclAccess') && this.notifyStatus(AclRequestType.Removal);
|
|
} catch (e) {
|
|
this.notifyStatus(e);
|
|
}
|
|
}).drop();
|
|
|
|
/**
|
|
* Sets the accessType attribute on the user's acl access request object
|
|
* @param {IAccessControlAccessTypeOption} { value }
|
|
* @memberof DatasetAclAccessContainer
|
|
*/
|
|
@action
|
|
accessTypeDidChange({ value }: IAccessControlAccessTypeOption) {
|
|
//@ts-ignore object property access path notation limitation
|
|
set(this, 'userAclRequest.accessType', value);
|
|
}
|
|
|
|
/**
|
|
* Sets the expiresAt attribute timestamp in seconds
|
|
* @param {Date} date the expiration date object
|
|
*/
|
|
@action
|
|
expiresAtDidChange(date: Date) {
|
|
//@ts-ignore object property access path notation limitation
|
|
set(this, 'userAclRequest.expiresAt', date.getTime() / 1000);
|
|
}
|
|
}
|