@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
201 lines • 27.2 kB
JavaScript
import { Injectable } from '@angular/core';
import { InventoryService, UserService } from '@c8y/client';
import { concat, firstValueFrom, from, Subject } from 'rxjs';
import { filter, first, map, switchMap } from 'rxjs/operators';
import { AppStateService } from '../ui-state.service';
import { UserPreferencesStorageLocal } from './user-preferences-storage-local';
import { UserPreferencesStorageInventory } from './user-preferences-store-inventory';
import { Permissions } from '../permissions.service';
import { AlertService } from '../../alert/alert.service';
import { UserPreferencesStorageCurrentUser } from './user-preferences-store-current-user';
import * as i0 from "@angular/core";
import * as i1 from "@c8y/client";
import * as i2 from "../ui-state.service";
import * as i3 from "../../alert/alert.service";
export class UserPreferencesService {
constructor(user, inventory, appState, alert) {
this.user = user;
this.inventory = inventory;
this.appState = appState;
this.alert = alert;
this.preferenceChanges$ = new Subject();
this.storage = {
local: new UserPreferencesStorageLocal(),
inventory: new UserPreferencesStorageInventory(this.inventory),
currentUser: new UserPreferencesStorageCurrentUser(this.user, this.appState)
};
this.currentUser = this.appState.currentUser.pipe(filter(currentUser => currentUser !== null));
}
/**
* Returns an observable of a user preference with given key.
* Emits its initial value first and then updated values when set by user.
* @param key The storage key for searched value.
* @returns An Observable of a user preference.
*/
observe(key) {
return concat(this.get(key), this.preferenceChanges$.pipe(filter(change => change.key === key), map(change => change.value)));
}
/**
* Get an Observable value for searched key for current user.
* @param key The storage key for searched value.
* @returns An Observable with the value of preference.
*/
get(key) {
return this.currentUser.pipe(first(), switchMap(user => this.getForCurrentUser(key, user)));
}
/**
* Sets a value in storage for current user.
* @param key The storage key for the value to be set.
* @param value The storage value to be set.
* @returns A promise with saved value.
*/
set(key, value) {
this.preferenceChanges$.next({ key, value });
return this.setForCurrentUser(key, value);
}
/**
* Get an Observable value of searched key for a specific user.
* @param key The storage key for searched value.
* @param user The user for whom the search is done.
* @returns An Observable with the value of preference.
*
* @deprecated Uses depracted inventory approach. Use get instead.
*/
getForUser(key, user) {
const rawKey = this.getTransformedRawKey(key, user);
const storage = this.getStorage(user);
return from(storage.get(rawKey));
}
/**
* Sets a value in storage for a specific user.
* @param key The storage key for the value to be set.
* @param value The storage value to be set.
* @returns A promise with saved value.
*
* @deprecated Uses deprecated inventory approach. Use set instead.
*/
setForUser(key, value, user) {
const rawKey = this.getTransformedRawKey(key, user);
const storage = this.getStorage(user);
return Promise.resolve(storage.set(rawKey, value));
}
/**
* Get value of searched key for current user.
* If preference is not found in user's customProperties, it will try to get it from inventory or local storage and
* update user's customProperties with the value and return that value.
* @param key The preference key for searched value.
* @param user The user for whom the search is done.
* @returns A Promise with the value of preference.
*/
async getForCurrentUser(key, user) {
const currentUserStorage = await this.getCurrentUserStorage();
const customPropertiesKey = this.getCustomPropertiesKey(key);
const currentUserHasKeyProperty = currentUserStorage && (await currentUserStorage.hasKey(customPropertiesKey));
if (currentUserHasKeyProperty) {
return await currentUserStorage.get(customPropertiesKey);
}
else {
const localOrInventoryStorage = this.getStorage(user);
const transformedRawKey = this.getTransformedRawKey(key, user);
const rawKey = this.getRawKey(key, user);
const valueFromStorage = (await localOrInventoryStorage.get(transformedRawKey)) ||
(await localOrInventoryStorage.get(rawKey));
if (valueFromStorage === undefined) {
return undefined;
}
try {
await currentUserStorage?.set(customPropertiesKey, valueFromStorage);
}
catch (e) {
// do nothing
}
return valueFromStorage;
}
}
/**
* Sets a value for current user.
* @param key The preference key for the value to be set.
* @param value The preference value to be set.
*/
async setForCurrentUser(key, value) {
const currentUserStorage = await this.getCurrentUserStorage();
const userPreferencesKey = this.getCustomPropertiesKey(key);
if (currentUserStorage) {
try {
await currentUserStorage.set(userPreferencesKey, value);
}
catch (e) {
this.alert.addServerFailure(e);
}
}
else {
const user = await firstValueFrom(this.currentUser);
const rawKey = this.getRawKey(key, user);
this.storage.local.set(rawKey, value);
}
}
/**
* Get a string of key concatenated with username.
* Used by deprecated invetory storage approach and for leftovers in local storage.
* It was used to store preferences for specific users and it as replacing "." with "__"
* because of MongoDB restrictions.
* @param key The storage key for searched value.
* @param user The user for whom the search is done.
* @returns A string of key concatenated with username.
*/
getTransformedRawKey(key, user) {
const username = user.userName.replace(/\./g, '__');
return `${key}${username}`;
}
/**
* Get a string of key concatenated with username.
* Used by local storage.
* @param key The storage key for searched value.
* @param user The user for whom the search is done.
* @returns A string of key concatenated with username.
*/
getRawKey(key, user) {
return `${key}${user.userName}`;
}
/**
* Get a key for user preferences. Used current user customProperties.
* @param key The storage key for searched value.
* @returns A key for user preferences.
*/
getCustomPropertiesKey(key) {
return `c8y_UserPreference-${key}`;
}
/**
* Gets a proper storage depending on the user roles.
* @param user The user for whom the role check is done.
* @returns A proper storage.
*/
getStorage(user) {
return this.user.hasAllRoles(user, [
Permissions.ROLE_INVENTORY_READ,
Permissions.ROLE_INVENTORY_ADMIN
]) ||
this.user.hasAllRoles(user, [
Permissions.ROLE_MANAGED_OBJECT_ADMIN,
Permissions.ROLE_MANAGED_OBJECT_READ
])
? this.storage.inventory
: this.storage.local;
}
async getCurrentUserStorage() {
const currentUser = await firstValueFrom(this.currentUser);
const hasRoleToEditCurrentUser = this.user.hasRole(currentUser, Permissions.ROLE_USER_MANAGEMENT_OWN_ADMIN);
const isExternalUser = currentUser.customProperties?.userOrigin === 'OAUTH2';
if (!hasRoleToEditCurrentUser || isExternalUser) {
return null;
}
return this.storage.currentUser;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserPreferencesService, deps: [{ token: i1.UserService }, { token: i1.InventoryService }, { token: i2.AppStateService }, { token: i3.AlertService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserPreferencesService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserPreferencesService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: i1.UserService }, { type: i1.InventoryService }, { type: i2.AppStateService }, { type: i3.AlertService }] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXNlci1wcmVmZXJlbmNlcy5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vY29yZS9jb21tb24vdXNlci1wcmVmZXJlbmNlcy91c2VyLXByZWZlcmVuY2VzLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMzQyxPQUFPLEVBQUUsZ0JBQWdCLEVBQVMsV0FBVyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ25FLE9BQU8sRUFBRSxNQUFNLEVBQUUsY0FBYyxFQUFFLElBQUksRUFBYyxPQUFPLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDekUsT0FBTyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsR0FBRyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQy9ELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN0RCxPQUFPLEVBQUUsMkJBQTJCLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUMvRSxPQUFPLEVBQUUsK0JBQStCLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUVyRixPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDckQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ3pELE9BQU8sRUFBRSxpQ0FBaUMsRUFBRSxNQUFNLHVDQUF1QyxDQUFDOzs7OztBQUcxRixNQUFNLE9BQU8sc0JBQXNCO0lBU2pDLFlBQ1UsSUFBaUIsRUFDakIsU0FBMkIsRUFDM0IsUUFBeUIsRUFDekIsS0FBbUI7UUFIbkIsU0FBSSxHQUFKLElBQUksQ0FBYTtRQUNqQixjQUFTLEdBQVQsU0FBUyxDQUFrQjtRQUMzQixhQUFRLEdBQVIsUUFBUSxDQUFpQjtRQUN6QixVQUFLLEdBQUwsS0FBSyxDQUFjO1FBWDdCLHVCQUFrQixHQUE0QixJQUFJLE9BQU8sRUFBRSxDQUFD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Qyx5QkFBeUI7Z0JBQ3JDLFdBQVcsQ0FBQyx3QkFBd0I7YUFDckMsQ0FBQztZQUNGLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVM7WUFDeEIsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDO0lBQ3pCLENBQUM7SUFFTyxLQUFLLENBQUMscUJBQXFCO1FBQ2pDLE1BQU0sV0FBVyxHQUFHLE1BQU0sY0FBYyxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUMzRCxNQUFNLHdCQUF3QixHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUNoRCxXQUFXLEVBQ1gsV0FBVyxDQUFDLDhCQUE4QixDQUMzQyxDQUFDO1FBQ0YsTUFBTSxjQUFjLEdBQUcsV0FBVyxDQUFDLGdCQUFnQixFQUFFLFVBQVUsS0FBSyxRQUFRLENBQUM7UUFDN0UsSUFBSSxDQUFDLHdCQUF3QixJQUFJLGNBQWMsRUFBRSxDQUFDO1lBQ2hELE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUNELE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUM7SUFDbEMsQ0FBQzsrR0FqTlUsc0JBQXNCO21IQUF0QixzQkFBc0IsY0FEVCxNQUFNOzs0RkFDbkIsc0JBQXNCO2tCQURsQyxVQUFVO21CQUFDLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEludmVudG9yeVNlcnZpY2UsIElVc2VyLCBVc2VyU2VydmljZSB9IGZyb20gJ0BjOHkvY2xpZW50JztcbmltcG9ydCB7IGNvbmNhdCwgZmlyc3RWYWx1ZUZyb20sIGZyb20sIE9ic2VydmFibGUsIFN1YmplY3QgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IGZpbHRlciwgZmlyc3QsIG1hcCwgc3dpdGNoTWFwIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgQXBwU3RhdGVTZXJ2aWNlIH0gZnJvbSAnLi4vdWktc3RhdGUuc2VydmljZSc7XG5pbXBvcnQgeyBVc2VyUHJlZmVyZW5jZXNTdG9yYWdlTG9jYWwgfSBmcm9tICcuL3VzZXItcHJlZmVyZW5jZXMtc3RvcmFnZS1sb2NhbCc7XG5pbXBvcnQgeyBVc2VyUHJlZmVyZW5jZXNTdG9yYWdlSW52ZW50b3J5IH0gZnJvbSAnLi91c2VyLXByZWZlcmVuY2VzLXN0b3JlLWludmVudG9yeSc7XG5pbXBvcnQgeyBVc2VyUHJlZmVyZW5jZSB9IGZyb20gJy4vdXNlci1wcmVmZXJlbmNlLm1vZGVsJztcbmltcG9ydCB7IFBlcm1pc3Npb25zIH0gZnJvbSAnLi4vcGVybWlzc2lvbnMuc2VydmljZSc7XG5pbXBvcnQgeyBBbGVydFNlcnZpY2UgfSBmcm9tICcuLi8uLi9hbGVydC9hbGVydC5zZXJ2aWNlJztcbmltcG9ydCB7IFVzZXJQcmVmZXJlbmNlc1N0b3JhZ2VDdXJyZW50VXNlciB9IGZyb20gJy4vdXNlci1wcmVmZXJlbmNlcy1zdG9yZS1jdXJyZW50LXVzZXInO1xuXG5ASW5qZWN0YWJsZSh7IHByb3ZpZGVkSW46ICdyb290JyB9KVxuZXhwb3J0IGNsYXNzIFVzZXJQcmVmZXJlbmNlc1NlcnZpY2Uge1xuICBjdXJyZW50VXNlcjogT2JzZXJ2YWJsZTxJVXNlcj47XG4gIHByZWZlcmVuY2VDaGFuZ2VzJDogU3ViamVjdDxVc2VyUHJlZmVyZW5jZT4gPSBuZXcgU3ViamVjdCgpO1xuICBwcml2YXRlIHN0b3JhZ2U6IHtcbiAgICBsb2NhbDogVXNlclByZWZlcmVuY2VzU3RvcmFnZUxvY2FsO1xuICAgIGludmVudG9yeTogVXNlclByZWZlcmVuY2VzU3RvcmFnZUludmVudG9yeTtcbiAgICBjdXJyZW50VXNlcjogVXNlclByZWZlcmVuY2VzU3RvcmFnZUN1cnJlbnRVc2VyO1xuICB9O1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgdXNlcjogVXNlclNlcnZpY2UsXG4gICAgcHJpdmF0ZSBpbnZlbnRvcnk6IEludmVudG9yeVNlcnZpY2UsXG4gICAgcHJpdmF0ZSBhcHBTdGF0ZTogQXBwU3RhdGVTZXJ2aWNlLFxuICAgIHByaXZhdGUgYWxlcnQ6IEFsZXJ0U2VydmljZVxuICApIHtcbiAgICB0aGlzLnN0b3JhZ2UgPSB7XG4gICAgICBsb2NhbDogbmV3IFVzZXJQcmVmZXJlbmNlc1N0b3JhZ2VMb2NhbCgpLFxuICAgICAgaW52ZW50b3J5OiBuZXcgVXNlclByZWZlcmVuY2VzU3RvcmFnZUludmVudG9yeSh0aGlzLmludmVudG9yeSksXG4gICAgICBjdXJyZW50VXNlcjogbmV3IFVzZXJQcmVmZXJlbmNlc1N0b3JhZ2VDdXJyZW50VXNlcih0aGlzLnVzZXIsIHRoaXMuYXBwU3RhdGUpXG4gICAgfTtcbiAgICB0aGlzLmN1cnJlbnRVc2VyID0gdGhpcy5hcHBTdGF0ZS5jdXJyZW50VXNlci5waXBlKGZpbHRlcihjdXJyZW50VXNlciA9PiBjdXJyZW50VXNlciAhPT0gbnVsbCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYW4gb2JzZXJ2YWJsZSBvZiBhIHVzZXIgcHJlZmVyZW5jZSB3aXRoIGdpdmVuIGtleS5cbiAgICogRW1pdHMgaXRzIGluaXRpYWwgdmFsdWUgZmlyc3QgYW5kIHRoZW4gdXBkYXRlZCB2YWx1ZXMgd2hlbiBzZXQgYnkgdXNlci5cbiAgICogQHBhcmFtIGtleSBUaGUgc3RvcmFnZSBrZXkgZm9yIHNlYXJjaGVkIHZhbHVlLlxuICAgKiBAcmV0dXJucyBBbiBPYnNlcnZhYmxlIG9mIGEgdXNlciBwcmVmZXJlbmNlLlxuICAgKi9cbiAgb2JzZXJ2ZTxUPihrZXk6IHN0cmluZyk6IE9ic2VydmFibGU8VD4ge1xuICAgIHJldHVybiBjb25jYXQoXG4gICAgICB0aGlzLmdldChrZXkpLFxuICAgICAgdGhpcy5wcmVmZXJlbmNlQ2hhbmdlcyQucGlwZShcbiAgICAgICAgZmlsdGVyKGNoYW5nZSA9PiBjaGFuZ2Uua2V5ID09PSBrZXkpLFxuICAgICAgICBtYXAoY2hhbmdlID0+IGNoYW5nZS52YWx1ZSlcbiAgICAgIClcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhbiBPYnNlcnZhYmxlIHZhbHVlIGZvciBzZWFyY2hlZCBrZXkgZm9yIGN1cnJlbnQgdXNlci5cbiAgICogQHBhcmFtIGtleSBUaGUgc3RvcmFnZSBrZXkgZm9yIHNlYXJjaGVkIHZhbHVlLlxuICAgKiBAcmV0dXJucyBBbiBPYnNlcnZhYmxlIHdpdGggdGhlIHZhbHVlIG9mIHByZWZlcmVuY2UuXG4gICAqL1xuICBnZXQoa2V5OiBzdHJpbmcpOiBPYnNlcnZhYmxlPGFueT4ge1xuICAgIHJldHVybiB0aGlzLmN1cnJlbnRVc2VyLnBpcGUoXG4gICAgICBmaXJzdCgpLFxuICAgICAgc3dpdGNoTWFwKHVzZXIgPT4gdGhpcy5nZXRGb3JDdXJyZW50VXNlcihrZXksIHVzZXIpKVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogU2V0cyBhIHZhbHVlIGluIHN0b3JhZ2UgZm9yIGN1cnJlbnQgdXNlci5cbiAgICogQHBhcmFtIGtleSBUaGUgc3RvcmFnZSBrZXkgZm9yIHRoZSB2YWx1ZSB0byBiZSBzZXQuXG4gICAqIEBwYXJhbSB2YWx1ZSBUaGUgc3RvcmFnZSB2YWx1ZSB0byBiZSBzZXQuXG4gICAqIEByZXR1cm5zIEEgcHJvbWlzZSB3aXRoIHNhdmVkIHZhbHVlLlxuICAgKi9cbiAgc2V0KGtleTogc3RyaW5nLCB2YWx1ZTogYW55KTogUHJvbWlzZTxhbnk+IHtcbiAgICB0aGlzLnByZWZlcmVuY2VDaGFuZ2VzJC5uZXh0KHsga2V5LCB2YWx1ZSB9KTtcbiAgICByZXR1cm4gdGhpcy5zZXRGb3JDdXJyZW50VXNlcihrZXksIHZhbHVlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgYW4gT2JzZXJ2YWJsZSB2YWx1ZSBvZiBzZWFyY2hlZCBrZXkgZm9yIGEgc3BlY2lmaWMgdXNlci5cbiAgICogQHBhcmFtIGtleSBUaGUgc3RvcmFnZSBrZXkgZm9yIHNlYXJjaGVkIHZhbHVlLlxuICAgKiBAcGFyYW0gdXNlciBUaGUgdXNlciBmb3Igd2hvbSB0aGUgc2VhcmNoIGlzIGRvbmUuXG4gICAqIEByZXR1cm5zIEFuIE9ic2VydmFibGUgd2l0aCB0aGUgdmFsdWUgb2YgcHJlZmVyZW5jZS5cbiAgICpcbiAgICogQGRlcHJlY2F0ZWQgVXNlcyBkZXByYWN0ZWQgaW52ZW50b3J5IGFwcHJvYWNoLiBVc2UgZ2V0IGluc3RlYWQuXG4gICAqL1xuICBnZXRGb3JVc2VyKGtleTogc3RyaW5nLCB1c2VyOiBJVXNlcik6IE9ic2VydmFibGU8YW55PiB7XG4gICAgY29uc3QgcmF3S2V5ID0gdGhpcy5nZXRUcmFuc2Zvcm1lZFJhd0tleShrZXksIHVzZXIpO1xuICAgIGNvbnN0IHN0b3JhZ2UgPSB0aGlzLmdldFN0b3JhZ2UodXNlcik7XG4gICAgcmV0dXJuIGZyb20oc3RvcmFnZS5nZXQocmF3S2V5KSk7XG4gIH1cblxuICAvKipcbiAgICogU2V0cyBhIHZhbHVlIGluIHN0b3JhZ2UgZm9yIGEgc3BlY2lmaWMgdXNlci5cbiAgICogQHBhcmFtIGtleSBUaGUgc3RvcmFnZSBrZXkgZm9yIHRoZSB2YWx1ZSB0byBiZSBzZXQuXG4gICAqIEBwYXJhbSB2YWx1ZSBUaGUgc3RvcmFnZSB2YWx1ZSB0byBiZSBzZXQuXG4gICAqIEByZXR1cm5zIEEgcHJvbWlzZSB3aXRoIHNhdmVkIHZhbHVlLlxuICAgKlxuICAgKiBAZGVwcmVjYXRlZCBVc2VzIGRlcHJlY2F0ZWQgaW52ZW50b3J5IGFwcHJvYWNoLiBVc2Ugc2V0IGluc3RlYWQuXG4gICAqL1xuICBzZXRGb3JVc2VyKGtleTogc3RyaW5nLCB2YWx1ZTogYW55LCB1c2VyOiBJVXNlcik6IFByb21pc2U8YW55PiB7XG4gICAgY29uc3QgcmF3S2V5ID0gdGhpcy5nZXRUcmFuc2Zvcm1lZFJhd0tleShrZXksIHVzZXIpO1xuICAgIGNvbnN0IHN0b3JhZ2UgPSB0aGlzLmdldFN0b3JhZ2UodXNlcik7XG4gICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShzdG9yYWdlLnNldChyYXdLZXksIHZhbHVlKSk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHZhbHVlIG9mIHNlYXJjaGVkIGtleSBmb3IgY3VycmVudCB1c2VyLlxuICAgKiBJZiBwcmVmZXJlbmNlIGlzIG5vdCBmb3VuZCBpbiB1c2VyJ3MgY3VzdG9tUHJvcGVydGllcywgaXQgd2lsbCB0cnkgdG8gZ2V0IGl0IGZyb20gaW52ZW50b3J5IG9yIGxvY2FsIHN0b3JhZ2UgYW5kXG4gICAqIHVwZGF0ZSB1c2VyJ3MgY3VzdG9tUHJvcGVydGllcyB3aXRoIHRoZSB2YWx1ZSBhbmQgcmV0dXJuIHRoYXQgdmFsdWUuXG4gICAqIEBwYXJhbSBrZXkgVGhlIHByZWZlcmVuY2Uga2V5IGZvciBzZWFyY2hlZCB2YWx1ZS5cbiAgICogQHBhcmFtIHVzZXIgVGhlIHVzZXIgZm9yIHdob20gdGhlIHNlYXJjaCBpcyBkb25lLlxuICAgKiBAcmV0dXJucyBBIFByb21pc2Ugd2l0aCB0aGUgdmFsdWUgb2YgcHJlZmVyZW5jZS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZ2V0Rm9yQ3VycmVudFVzZXIoa2V5OiBzdHJpbmcsIHVzZXI6IElVc2VyKTogUHJvbWlzZTxhbnk+IHtcbiAgICBjb25zdCBjdXJyZW50VXNlclN0b3JhZ2UgPSBhd2FpdCB0aGlzLmdldEN1cnJlbnRVc2VyU3RvcmFnZSgpO1xuICAgIGNvbnN0IGN1c3RvbVByb3BlcnRpZXNLZXkgPSB0aGlzLmdldEN1c3RvbVByb3BlcnRpZXNLZXkoa2V5KTtcbiAgICBjb25zdCBjdXJyZW50VXNlckhhc0tleVByb3BlcnR5ID1cbiAgICAgIGN1cnJlbnRVc2VyU3RvcmFnZSAmJiAoYXdhaXQgY3VycmVudFVzZXJTdG9yYWdlLmhhc0tleShjdXN0b21Qcm9wZXJ0aWVzS2V5KSk7XG4gICAgaWYgKGN1cnJlbnRVc2VySGFzS2V5UHJvcGVydHkpIHtcbiAgICAgIHJldHVybiBhd2FpdCBjdXJyZW50VXNlclN0b3JhZ2UuZ2V0KGN1c3RvbVByb3BlcnRpZXNLZXkpO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBsb2NhbE9ySW52ZW50b3J5U3RvcmFnZSA9IHRoaXMuZ2V0U3RvcmFnZSh1c2VyKTtcbiAgICAgIGNvbnN0IHRyYW5zZm9ybWVkUmF3S2V5ID0gdGhpcy5nZXRUcmFuc2Zvcm1lZFJhd0tleShrZXksIHVzZXIpO1xuICAgICAgY29uc3QgcmF3S2V5ID0gdGhpcy5nZXRSYXdLZXkoa2V5LCB1c2VyKTtcblxuICAgICAgY29uc3QgdmFsdWVGcm9tU3RvcmFnZSA9XG4gICAgICAgIChhd2FpdCBsb2NhbE9ySW52ZW50b3J5U3RvcmFnZS5nZXQodHJhbnNmb3JtZWRSYXdLZXkpKSB8fFxuICAgICAgICAoYXdhaXQgbG9jYWxPckludmVudG9yeVN0b3JhZ2UuZ2V0KHJhd0tleSkpO1xuICAgICAgaWYgKHZhbHVlRnJvbVN0b3JhZ2UgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgICAgfVxuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgY3VycmVudFVzZXJTdG9yYWdlPy5zZXQoY3VzdG9tUHJvcGVydGllc0tleSwgdmFsdWVGcm9tU3RvcmFnZSk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIC8vIGRvIG5vdGhpbmdcbiAgICAgIH1cbiAgICAgIHJldHVybiB2YWx1ZUZyb21TdG9yYWdlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBTZXRzIGEgdmFsdWUgZm9yIGN1cnJlbnQgdXNlci5cbiAgICogQHBhcmFtIGtleSBUaGUgcHJlZmVyZW5jZSBrZXkgZm9yIHRoZSB2YWx1ZSB0byBiZSBzZXQuXG4gICAqIEBwYXJhbSB2YWx1ZSBUaGUgcHJlZmVyZW5jZSB2YWx1ZSB0byBiZSBzZXQuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHNldEZvckN1cnJlbnRVc2VyKGtleTogc3RyaW5nLCB2YWx1ZTogYW55KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgY3VycmVudFVzZXJTdG9yYWdlID0gYXdhaXQgdGhpcy5nZXRDdXJyZW50VXNlclN0b3JhZ2UoKTtcbiAgICBjb25zdCB1c2VyUHJlZmVyZW5jZXNLZXkgPSB0aGlzLmdldEN1c3RvbVByb3BlcnRpZXNLZXkoa2V5KTtcbiAgICBpZiAoY3VycmVudFVzZXJTdG9yYWdlKSB7XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCBjdXJyZW50VXNlclN0b3JhZ2Uuc2V0KHVzZXJQcmVmZXJlbmNlc0tleSwgdmFsdWUpO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICB0aGlzLmFsZXJ0LmFkZFNlcnZlckZhaWx1cmUoZSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IHVzZXIgPSBhd2FpdCBmaXJzdFZhbHVlRnJvbSh0aGlzLmN1cnJlbnRVc2VyKTtcbiAgICAgIGNvbnN0IHJhd0tleSA9IHRoaXMuZ2V0UmF3S2V5KGtleSwgdXNlcik7XG4gICAgICB0aGlzLnN0b3JhZ2UubG9jYWwuc2V0KHJhd0tleSwgdmFsdWUpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgYSBzdHJpbmcgb2Yga2V5IGNvbmNhdGVuYXRlZCB3aXRoIHVzZXJuYW1lLlxuICAgKiBVc2VkIGJ5IGRlcHJlY2F0ZWQgaW52ZXRvcnkgc3RvcmFnZSBhcHByb2FjaCBhbmQgZm9yIGxlZnRvdmVycyBpbiBsb2NhbCBzdG9yYWdlLlxuICAgKiBJdCB3YXMgdXNlZCB0byBzdG9yZSBwcmVmZXJlbmNlcyBmb3Igc3BlY2lmaWMgdXNlcnMgYW5kIGl0IGFzIHJlcGxhY2luZyBcIi5cIiB3aXRoIFwiX19cIlxuICAgKiBiZWNhdXNlIG9mIE1vbmdvREIgcmVzdHJpY3Rpb25zLlxuICAgKiBAcGFyYW0ga2V5IFRoZSBzdG9yYWdlIGtleSBmb3Igc2VhcmNoZWQgdmFsdWUuXG4gICAqIEBwYXJhbSB1c2VyIFRoZSB1c2VyIGZvciB3aG9tIHRoZSBzZWFyY2ggaXMgZG9uZS5cbiAgICogQHJldHVybnMgQSBzdHJpbmcgb2Yga2V5IGNvbmNhdGVuYXRlZCB3aXRoIHVzZXJuYW1lLlxuICAgKi9cbiAgcHJpdmF0ZSBnZXRUcmFuc2Zvcm1lZFJhd0tleShrZXk6IHN0cmluZywgdXNlcjogSVVzZXIpOiBzdHJpbmcge1xuICAgIGNvbnN0IHVzZXJuYW1lID0gdXNlci51c2VyTmFtZS5yZXBsYWNlKC9cXC4vZywgJ19fJyk7XG4gICAgcmV0dXJuIGAke2tleX0ke3VzZXJuYW1lfWA7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGEgc3RyaW5nIG9mIGtleSBjb25jYXRlbmF0ZWQgd2l0aCB1c2VybmFtZS5cbiAgICogVXNlZCBieSBsb2NhbCBzdG9yYWdlLlxuICAgKiBAcGFyYW0ga2V5IFRoZSBzdG9yYWdlIGtleSBmb3Igc2VhcmNoZWQgdmFsdWUuXG4gICAqIEBwYXJhbSB1c2VyIFRoZSB1c2VyIGZvciB3aG9tIHRoZSBzZWFyY2ggaXMgZG9uZS5cbiAgICogQHJldHVybnMgQSBzdHJpbmcgb2Yga2V5IGNvbmNhdGVuYXRlZCB3aXRoIHVzZXJuYW1lLlxuICAgKi9cbiAgcHJpdmF0ZSBnZXRSYXdLZXkoa2V5OiBzdHJpbmcsIHVzZXI6IElVc2VyKTogc3RyaW5nIHtcbiAgICByZXR1cm4gYCR7a2V5fSR7dXNlci51c2VyTmFtZX1gO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhIGtleSBmb3IgdXNlciBwcmVmZXJlbmNlcy4gVXNlZCBjdXJyZW50IHVzZXIgY3VzdG9tUHJvcGVydGllcy5cbiAgICogQHBhcmFtIGtleSBUaGUgc3RvcmFnZSBrZXkgZm9yIHNlYXJjaGVkIHZhbHVlLlxuICAgKiBAcmV0dXJucyBBIGtleSBmb3IgdXNlciBwcmVmZXJlbmNlcy5cbiAgICovXG4gIHByaXZhdGUgZ2V0Q3VzdG9tUHJvcGVydGllc0tleShrZXk6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGBjOHlfVXNlclByZWZlcmVuY2UtJHtrZXl9YDtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXRzIGEgcHJvcGVyIHN0b3JhZ2UgZGVwZW5kaW5nIG9uIHRoZSB1c2VyIHJvbGVzLlxuICAgKiBAcGFyYW0gdXNlciBUaGUgdXNlciBmb3Igd2hvbSB0aGUgcm9sZSBjaGVjayBpcyBkb25lLlxuICAgKiBAcmV0dXJucyBBIHByb3BlciBzdG9yYWdlLlxuICAgKi9cbiAgcHJpdmF0ZSBnZXRTdG9yYWdlKHVzZXI6IElVc2VyKTogVXNlclByZWZlcmVuY2VzU3RvcmFnZUludmVudG9yeSB8IFVzZXJQcmVmZXJlbmNlc1N0b3JhZ2VMb2NhbCB7XG4gICAgcmV0dXJuIHRoaXMudXNlci5oYXNBbGxSb2xlcyh1c2VyLCBbXG4gICAgICBQZXJtaXNzaW9ucy5ST0xFX0lOVkVOVE9SWV9SRUFELFxuICAgICAgUGVybWlzc2lvbnMuUk9MRV9JTlZFTlRPUllfQURNSU5cbiAgICBdKSB8fFxuICAgICAgdGhpcy51c2VyLmhhc0FsbFJvbGVzKHVzZXIsIFtcbiAgICAgICAgUGVybWlzc2lvbnMuUk9MRV9NQU5BR0VEX09CSkVDVF9BRE1JTixcbiAgICAgICAgUGVybWlzc2lvbnMuUk9MRV9NQU5BR0VEX09CSkVDVF9SRUFEXG4gICAgICBdKVxuICAgICAgPyB0aGlzLnN0b3JhZ2UuaW52ZW50b3J5XG4gICAgICA6IHRoaXMuc3RvcmFnZS5sb2NhbDtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZ2V0Q3VycmVudFVzZXJTdG9yYWdlKCk6IFByb21pc2U8VXNlclByZWZlcmVuY2VzU3RvcmFnZUN1cnJlbnRVc2VyPiB7XG4gICAgY29uc3QgY3VycmVudFVzZXIgPSBhd2FpdCBmaXJzdFZhbHVlRnJvbSh0aGlzLmN1cnJlbnRVc2VyKTtcbiAgICBjb25zdCBoYXNSb2xlVG9FZGl0Q3VycmVudFVzZXIgPSB0aGlzLnVzZXIuaGFzUm9sZShcbiAgICAgIGN1cnJlbnRVc2VyLFxuICAgICAgUGVybWlzc2lvbnMuUk9MRV9VU0VSX01BTkFHRU1FTlRfT1dOX0FETUlOXG4gICAgKTtcbiAgICBjb25zdCBpc0V4dGVybmFsVXNlciA9IGN1cnJlbnRVc2VyLmN1c3RvbVByb3BlcnRpZXM/LnVzZXJPcmlnaW4gPT09ICdPQVVUSDInO1xuICAgIGlmICghaGFzUm9sZVRvRWRpdEN1cnJlbnRVc2VyIHx8IGlzRXh0ZXJuYWxVc2VyKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuc3RvcmFnZS5jdXJyZW50VXNlcjtcbiAgfVxufVxuIl19