Files
threshold-ui/addons/api/addon/models/session.js
T

238 lines
5.9 KiB
JavaScript
Raw Normal View History

/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import GeneratedSessionModel from '../generated/models/session';
2021-02-05 16:57:06 -05:00
import { equal } from '@ember/object/computed';
2021-06-04 11:31:29 -07:00
import { tracked } from '@glimmer/tracking';
import { A } from '@ember/array';
import { service } from '@ember/service';
import { flattenObject } from '../utils/flatten-nested-object';
import { TYPES_CREDENTIAL_LIBRARY } from 'api/models/credential-library';
import { TYPE_CREDENTIAL_JSON } from 'api/models/credential';
export const STATUS_SESSION_ACTIVE = 'active';
export const STATUS_SESSION_PENDING = 'pending';
export const STATUS_SESSION_CANCELING = 'canceling';
export const STATUS_SESSION_TERMINATED = 'terminated';
export const statusTypes = [
STATUS_SESSION_ACTIVE,
STATUS_SESSION_PENDING,
STATUS_SESSION_CANCELING,
STATUS_SESSION_TERMINATED,
];
2021-06-24 16:26:51 -04:00
/**
*
*/
export class SessionCredential {
2021-06-24 16:26:51 -04:00
// =classes
/**
*
*/
2022-06-20 09:45:50 -07:00
static Source = class Source {
2021-06-24 16:26:51 -04:00
id;
name;
description;
type;
credentialtype;
2021-06-24 16:26:51 -04:00
constructor(id, name, description, type, credentialtype) {
2021-06-24 16:26:51 -04:00
this.id = id;
this.name = name;
this.description = description;
this.type = type;
this.credentialType = credentialtype;
2021-06-24 16:26:51 -04:00
}
};
/**
*
*/
static SecretItem = class SecretItem {
key;
value;
constructor(key, value) {
this.key = key;
this.value = value;
}
};
// =attributes
rawCredential;
2022-06-20 09:45:50 -07:00
source;
#payloadSecret;
2021-06-24 16:26:51 -04:00
/**
* The incoming payload may contain a JSON object under the `decoded` key only
* if it can be JSON-decoded. Some credential stores may not provide JSON,
* however, in which case we may have only the raw base64-encoded secret
* string. In the former case, we convert the key/value pairs of the JSON to
* an array of `SecretItem` instances. In the latter case, we return an
* array containing a single `SecretItem` instance with key `secret` and a
* string value equal to the base64-decoded raw secret.
2021-06-24 16:26:51 -04:00
* The credential secret as an array of key/value objects.
* @type {SessionCredential.SecretItem[]}
*/
get secrets() {
if (this.#payloadSecret?.decoded) {
const secretJSON = this.#payloadSecret.decoded;
return this.extractSecrets(
secretJSON,
this.source.type,
this.source.credentialType,
);
2021-06-24 16:26:51 -04:00
} else {
// decode from base64
const decodedString = atob(this.#payloadSecret.raw);
return [new SessionCredential.SecretItem('secret', decodedString)];
2021-06-24 16:26:51 -04:00
}
}
/**
* Extracts secrets from the payload secret JSON object.
* We only need to flatten the object if the type is vault or if its a json type credential.
* @param {object} secretJSON - The payload secret JSON object.
* @param {string} type - The credential source type.
* @param {string} credentialType - The credential type.
* @returns {SessionCredential.SecretItem[]} - The array of secret items.
*/
extractSecrets(secretJSON, type, credentialType) {
let source;
if (credentialType === TYPE_CREDENTIAL_JSON) {
source = flattenObject(secretJSON);
} else if (TYPES_CREDENTIAL_LIBRARY.includes(type) && secretJSON?.data) {
source = flattenObject(secretJSON.data);
} else {
source = secretJSON;
}
return Object.entries(source).map(
([key, value]) => new SessionCredential.SecretItem(key, value),
);
}
// =methods
constructor(cred) {
const { id, name, description, type, credential_type } =
cred.credential_source;
this.source = new SessionCredential.Source(
id,
name,
description,
type,
credential_type,
);
this.#payloadSecret = cred.secret;
this.rawCredential = cred;
}
}
2020-09-18 10:16:40 -04:00
export default class SessionModel extends GeneratedSessionModel {
// =services
@service store;
// =attributes
// These transient attributes are used for values that come from the CLI
// `connect` command as triggered by Desktop. These attributes represent
// transient read-only values.
2021-06-04 11:31:29 -07:00
@tracked acknowledged;
@tracked started_desktop_client;
@tracked proxy_address;
@tracked proxy_port;
@tracked host;
credentials = A();
@equal('status', STATUS_SESSION_ACTIVE) isActive;
@equal('status', STATUS_SESSION_PENDING) isPending;
2021-02-05 16:57:06 -05:00
/**
* True if status is an unknown string.
* @type {boolean}
*/
get isUnknownStatus() {
return !statusTypes.includes(this.status);
}
/**
* @type {boolean}
*/
2023-11-01 11:22:18 -05:00
get isAvailable() {
2021-02-05 16:57:06 -05:00
return this.isActive || this.isPending;
}
/**
* The full proxy address and port if address exists, otherwise null.
* @type {?string}
*/
get proxy() {
return this.proxy_address
? `${this.proxy_address}:${this.proxy_port}`
: null;
}
/**
* The target associated with this session (if loaded).
* @type {TargetModel}
*/
get target() {
if (!this.target_id) {
return null;
}
return this.store.peekRecord('target', this.target_id);
}
/**
* The user associated with this session (if loaded).
* @type {UserModel}
*/
get user() {
if (!this.user_id) {
return null;
}
return this.store.peekRecord('user', this.user_id);
}
// =methods
2021-06-24 16:26:51 -04:00
/**
*
*/
addCredential(cred) {
this.credentials.pushObject(new SessionCredential(cred));
}
/**
* Adds associated host to session if host present
* @param {HostModel} host
*/
addHost(host) {
this.host = host;
}
/**
* Cancels the session via the `cancel` method.
* See serializer and adapter for more information.
* @param {object} options
* @param {object} options.adapterOptions
* @return {Promise}
*/
cancelSession(options = { adapterOptions: {} }) {
const defaultAdapterOptions = {
method: 'cancel',
};
return this.save({
...options,
adapterOptions: {
...defaultAdapterOptions,
...options.adapterOptions,
},
});
}
}