chore: 🤖 Update storage buckets to handle new api changes (#266)
This commit is contained in:
1 parent
1a57e2b96d
commit
55f7406576
14 files changed
+66
-251
No files matched your search
@@ -53,18 +53,4 @@ export default class TargetAbility extends ModelAbility {
|
||||
get canRemoveCredentialSources() {
|
||||
return this.hasAuthorizedAction('remove-credential-sources');
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canSetStorageBucket() {
|
||||
return this.model.isSSH && this.hasAuthorizedAction('set-storage-bucket');
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canRemoveStorageBucket() {
|
||||
return this.hasAuthorizedAction('remove-storage-bucket');
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ export default class SessionRecordingAdapter extends ApplicationAdapter {
|
||||
mime_type: mimeType,
|
||||
});
|
||||
|
||||
const response = await fetch(url);
|
||||
const response = await fetch(url, { headers: this.headers });
|
||||
return response.text();
|
||||
}
|
||||
|
||||
|
||||
@@ -86,6 +86,13 @@ export default class GeneratedTargetModel extends BaseModel {
|
||||
|
||||
@attr('string', {
|
||||
description: 'The storage bucket associated with this target.',
|
||||
isNestedAttribute: true,
|
||||
})
|
||||
storage_bucket_id;
|
||||
|
||||
@attr('boolean', {
|
||||
description: 'Whether session recording is enabled on the target.',
|
||||
isNestedAttribute: true,
|
||||
})
|
||||
enable_session_recording;
|
||||
}
|
||||
@@ -324,44 +324,6 @@ export default class TargetModel extends GeneratedTargetModel {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} options
|
||||
* @param {object} options.adapterOptions
|
||||
* @returns {Promise}
|
||||
*/
|
||||
setStorageBucket(options = { adapterOptions: {} }) {
|
||||
const defaultAdapterOptions = {
|
||||
method: 'set-storage-bucket',
|
||||
};
|
||||
|
||||
return this.save({
|
||||
...options,
|
||||
adapterOptions: {
|
||||
...defaultAdapterOptions,
|
||||
...options.adapterOptions,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} options
|
||||
* @param {object} options.adapterOptions
|
||||
* @returns {Promise}
|
||||
*/
|
||||
removeStorageBucket(options = { adapterOptions: {} }) {
|
||||
const defaultAdapterOptions = {
|
||||
method: 'remove-storage-bucket',
|
||||
};
|
||||
|
||||
return this.save({
|
||||
...options,
|
||||
adapterOptions: {
|
||||
...defaultAdapterOptions,
|
||||
...options.adapterOptions,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the target type is tcp.
|
||||
* @type {boolean}
|
||||
|
||||
@@ -74,7 +74,10 @@ export default factory.extend({
|
||||
);
|
||||
|
||||
if (randomlySelectedStorageBucket && target.type === TYPE_TARGET_SSH) {
|
||||
target.update({ storage_bucket_id: randomlySelectedStorageBucket.id });
|
||||
target.update({
|
||||
storage_bucket_id: randomlySelectedStorageBucket.id,
|
||||
enable_session_recording: true,
|
||||
});
|
||||
}
|
||||
|
||||
target.update({
|
||||
|
||||
@@ -101,13 +101,5 @@ export function targetHandler({ targets }, { params: { idMethod } }) {
|
||||
}
|
||||
}
|
||||
|
||||
if (method === 'set-storage-bucket') {
|
||||
updatedAttrs.storage_bucket_id = attrs.storageBucketId;
|
||||
}
|
||||
|
||||
if (method === 'remove-storage-bucket') {
|
||||
updatedAttrs.storage_bucket_id = null;
|
||||
}
|
||||
|
||||
return target.update(updatedAttrs);
|
||||
}
|
||||
@@ -95,46 +95,4 @@ module('Unit | Abilities | Target', function (hooks) {
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test('it reflects when a given ssh target may set a storage bucket', function (assert) {
|
||||
assert.expect(3);
|
||||
const model = {
|
||||
authorized_actions: ['set-storage-bucket'],
|
||||
isSSH: true,
|
||||
};
|
||||
const modelWithoutAuthorizedActions = {
|
||||
authorized_actions: [],
|
||||
isSSH: true,
|
||||
};
|
||||
const modelWithoutSSHType = {
|
||||
authorized_actions: ['set-storage-bucket'],
|
||||
};
|
||||
|
||||
assert.true(canService.can('setStorageBucket target', model));
|
||||
assert.false(
|
||||
canService.can('setStorageBucket target', modelWithoutAuthorizedActions)
|
||||
);
|
||||
assert.false(
|
||||
canService.can('setStorageBucket target', modelWithoutSSHType)
|
||||
);
|
||||
});
|
||||
|
||||
test('it reflects when a given target may remove a storage bucket', function (assert) {
|
||||
assert.expect(2);
|
||||
const model = {
|
||||
authorized_actions: ['remove-storage-bucket'],
|
||||
};
|
||||
const modelWithoutAuthorizedActions = {
|
||||
authorized_actions: [],
|
||||
isSSH: true,
|
||||
};
|
||||
|
||||
assert.true(canService.can('removeStorageBucket target', model));
|
||||
assert.false(
|
||||
canService.can(
|
||||
'removeStorageBucket target',
|
||||
modelWithoutAuthorizedActions
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -404,71 +404,4 @@ module('Unit | Model | target', function (hooks) {
|
||||
assert.true(modelTCP.isTCP);
|
||||
assert.false(modelTCP.isSSH);
|
||||
});
|
||||
|
||||
test('it has a `setStorageBucket` method that targets a specific POST API endpoint and serialization', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.post(
|
||||
'/v1/targets/123abc:set-storage-bucket',
|
||||
(schema, request) => {
|
||||
const body = JSON.parse(request.requestBody);
|
||||
assert.deepEqual(body, {
|
||||
storage_bucket_id: 'bucketID',
|
||||
version: 1,
|
||||
});
|
||||
return { id: '123abc' };
|
||||
}
|
||||
);
|
||||
const store = this.owner.lookup('service:store');
|
||||
store.push({
|
||||
data: {
|
||||
id: '123abc',
|
||||
type: 'target',
|
||||
attributes: {
|
||||
name: 'Target',
|
||||
description: 'Description',
|
||||
storage_bucket_id: 'bucketID',
|
||||
version: 1,
|
||||
scope: {
|
||||
scope_id: 'o_1',
|
||||
type: 'scope',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const model = store.peekRecord('target', '123abc');
|
||||
await model.setStorageBucket();
|
||||
});
|
||||
|
||||
test('it has a `removeStorageBucket` method that targets a specific POST API endpoint and serialization', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.post(
|
||||
'/v1/targets/123abc:remove-storage-bucket',
|
||||
(schema, request) => {
|
||||
const body = JSON.parse(request.requestBody);
|
||||
assert.deepEqual(body, {
|
||||
version: 1,
|
||||
});
|
||||
return { id: '123abc' };
|
||||
}
|
||||
);
|
||||
const store = this.owner.lookup('service:store');
|
||||
store.push({
|
||||
data: {
|
||||
id: '123abc',
|
||||
type: 'target',
|
||||
attributes: {
|
||||
name: 'Target',
|
||||
description: 'Description',
|
||||
storage_bucket_id: null,
|
||||
version: 1,
|
||||
scope: {
|
||||
scope_id: 'o_1',
|
||||
type: 'scope',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const model = store.peekRecord('target', '123abc');
|
||||
await model.removeStorageBucket();
|
||||
});
|
||||
});
|
||||
@@ -49,9 +49,10 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
worker_filter: null,
|
||||
egress_worker_filter: null,
|
||||
ingress_worker_filter: null,
|
||||
storage_bucket_id: null,
|
||||
attributes: {
|
||||
default_port: 1234,
|
||||
enable_session_recording: false,
|
||||
storage_bucket_id: null,
|
||||
},
|
||||
address: '0.0.0.0',
|
||||
});
|
||||
@@ -128,46 +129,41 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
});
|
||||
});
|
||||
|
||||
test('it serializes only storage_bucket_id and version when an `adapterOptions.method` is passed for set storage bucket', function (assert) {
|
||||
test('it serializes the storage_bucket_id attribute if present', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('target');
|
||||
const record = store.createRecord('target', {
|
||||
name: 'User',
|
||||
description: 'Description',
|
||||
storage_bucket_id: 'bucketID',
|
||||
version: 1,
|
||||
type: TYPE_TARGET_SSH,
|
||||
enable_session_recording: true,
|
||||
storage_bucket_id: 'bucketID',
|
||||
default_port: 1234,
|
||||
scope: {
|
||||
scope_id: 'org_1',
|
||||
},
|
||||
});
|
||||
const snapshot = record._createSnapshot();
|
||||
snapshot.adapterOptions = {
|
||||
method: 'set-storage-bucket',
|
||||
};
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
|
||||
assert.deepEqual(serializedRecord, {
|
||||
storage_bucket_id: 'bucketID',
|
||||
version: 1,
|
||||
});
|
||||
});
|
||||
|
||||
test('it serializes only version when an `adapterOptions.method` is passed for remove storage bucket', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('target');
|
||||
const record = store.createRecord('target', {
|
||||
name: 'User',
|
||||
description: 'Description',
|
||||
storage_bucket_id: 'bucketID',
|
||||
version: 1,
|
||||
});
|
||||
const snapshot = record._createSnapshot();
|
||||
snapshot.adapterOptions = {
|
||||
method: 'remove-storage-bucket',
|
||||
};
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
|
||||
assert.deepEqual(serializedRecord, {
|
||||
version: 1,
|
||||
type: TYPE_TARGET_SSH,
|
||||
scope_id: 'org_1',
|
||||
session_max_seconds: 28800,
|
||||
session_connection_limit: null,
|
||||
worker_filter: null,
|
||||
egress_worker_filter: null,
|
||||
ingress_worker_filter: null,
|
||||
attributes: {
|
||||
default_port: 1234,
|
||||
enable_session_recording: true,
|
||||
storage_bucket_id: 'bucketID',
|
||||
},
|
||||
address: null,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -199,9 +195,10 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
worker_filter: 'worker',
|
||||
egress_worker_filter: null,
|
||||
ingress_worker_filter: null,
|
||||
storage_bucket_id: null,
|
||||
attributes: {
|
||||
default_port: 1234,
|
||||
enable_session_recording: false,
|
||||
storage_bucket_id: null,
|
||||
},
|
||||
address: null,
|
||||
});
|
||||
@@ -235,9 +232,10 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
worker_filter: null,
|
||||
egress_worker_filter: 'egress worker',
|
||||
ingress_worker_filter: null,
|
||||
storage_bucket_id: null,
|
||||
attributes: {
|
||||
default_port: 1234,
|
||||
enable_session_recording: false,
|
||||
storage_bucket_id: null,
|
||||
},
|
||||
address: null,
|
||||
});
|
||||
@@ -271,9 +269,10 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
worker_filter: null,
|
||||
egress_worker_filter: null,
|
||||
ingress_worker_filter: 'ingress worker',
|
||||
storage_bucket_id: null,
|
||||
attributes: {
|
||||
default_port: 1234,
|
||||
enable_session_recording: false,
|
||||
storage_bucket_id: null,
|
||||
},
|
||||
address: null,
|
||||
});
|
||||
@@ -495,6 +494,7 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
id: '1',
|
||||
name: 'Target 1',
|
||||
storage_bucket_id: 'bucketID',
|
||||
enable_session_recording: true,
|
||||
};
|
||||
const normalized = serializer.normalizeSingleResponse(
|
||||
store,
|
||||
@@ -513,6 +513,7 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
brokered_credential_source_ids: [],
|
||||
injected_application_credential_source_ids: [],
|
||||
storage_bucket_id: 'bucketID',
|
||||
enable_session_recording: true,
|
||||
},
|
||||
relationships: {},
|
||||
},
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div class='enable-session-recording-toggle'>
|
||||
<Hds::Form::Toggle::Field
|
||||
name='target-enable-session-recording'
|
||||
checked={{this.toggleEnabled}}
|
||||
checked={{@model.enable_session_recording}}
|
||||
{{on 'change' this.toggleSessionRecording}}
|
||||
as |F|
|
||||
>
|
||||
@@ -14,7 +14,7 @@
|
||||
'resources.target.enable-session-recording.form.filter.label'
|
||||
}}</F.Label>
|
||||
</Hds::Form::Toggle::Field>
|
||||
{{#if this.toggleEnabled}}
|
||||
{{#if @model.enable_session_recording}}
|
||||
<Hds::Form::Select::Field
|
||||
@isRequired={{true}}
|
||||
@isInvalid={{@model.errors.storage_bucket_id}}
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
export default class FormEnableSessionRecordingIndexComponent extends Component {
|
||||
// =attributes
|
||||
@tracked toggleEnabled = true;
|
||||
|
||||
//actions
|
||||
/**
|
||||
* toggle to enable session recording for the target
|
||||
*/
|
||||
@action
|
||||
toggleSessionRecording() {
|
||||
this.toggleEnabled = !this.toggleEnabled;
|
||||
this.args.model.enable_session_recording =
|
||||
!this.args.model.enable_session_recording;
|
||||
}
|
||||
|
||||
@action
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { loading } from 'ember-loading';
|
||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||
import { set } from '@ember/object';
|
||||
|
||||
export default class ScopesScopeTargetsTargetEnableSessionRecordingRoute extends Route {
|
||||
// =services
|
||||
@@ -45,37 +42,15 @@ export default class ScopesScopeTargetsTargetEnableSessionRecordingRoute extends
|
||||
}
|
||||
|
||||
// =actions
|
||||
/**
|
||||
* Add storage bucket to target if the toggle is enabled, else remove it
|
||||
* @param {TargetModel} target
|
||||
* @param {boolean} isToggleEnabled
|
||||
*/
|
||||
@action
|
||||
@loading
|
||||
@notifyError(({ message }) => message, { catch: true })
|
||||
@notifySuccess('notifications.add-success')
|
||||
async save(target, isToggleEnabled) {
|
||||
const { id } = target;
|
||||
if (isToggleEnabled) {
|
||||
await target.setStorageBucket();
|
||||
} else {
|
||||
await target.removeStorageBucket();
|
||||
}
|
||||
this.router.replaceWith('scopes.scope.targets.target', id);
|
||||
}
|
||||
|
||||
/**
|
||||
* reset the selected storage bucket and redirect to target
|
||||
* Reset the selected storage bucket and redirect to target
|
||||
* @param {TargetModel} target
|
||||
*/
|
||||
@action
|
||||
cancel(target) {
|
||||
const { id } = target;
|
||||
const changedAttributes = target.changedAttributes();
|
||||
if (Object.keys(changedAttributes).length) {
|
||||
const existingAttributes = changedAttributes?.['storage_bucket_id']?.[0];
|
||||
set(target, 'storage_bucket_id', existingAttributes);
|
||||
}
|
||||
target.rollbackAttributes();
|
||||
this.router.replaceWith('scopes.scope.targets.target', id);
|
||||
}
|
||||
}
|
||||
@@ -30,19 +30,14 @@
|
||||
@cancel={{route-action 'cancel' @model}}
|
||||
/>
|
||||
</sb.Body>
|
||||
{{#if
|
||||
(and
|
||||
(can 'setStorageBucket target' @model)
|
||||
(feature-flag 'ssh-session-recording')
|
||||
)
|
||||
}}
|
||||
{{#if (and @model.isSSH (feature-flag 'ssh-session-recording'))}}
|
||||
<sb.Sidebar>
|
||||
<div class='target-sidebar'>
|
||||
<h3>{{t 'resources.target.form.sidebar.label'}}</h3>
|
||||
<p class='description'>
|
||||
{{t 'resources.target.form.sidebar.help'}}
|
||||
</p>
|
||||
{{#if @model.storage_bucket_id}}
|
||||
{{#if @model.enable_session_recording}}
|
||||
<LinkListPanel as |P|>
|
||||
<P.Item
|
||||
@icon='cloud-upload'
|
||||
|
||||
@@ -113,6 +113,7 @@ module('Acceptance | targets | enable session recording', function (hooks) {
|
||||
assert.expect(1);
|
||||
instances.target.update({
|
||||
storageBucketId: storageBucketOne.id,
|
||||
enableSessionRecording: true,
|
||||
});
|
||||
await visit(urls.target);
|
||||
await click(SETTINGS_LINK_SELECTOR);
|
||||
@@ -122,20 +123,23 @@ module('Acceptance | targets | enable session recording', function (hooks) {
|
||||
test('users can click on associated storage bucket card on an ssh target', async function (assert) {
|
||||
featuresService.enable('ssh-session-recording');
|
||||
assert.expect(1);
|
||||
instances.target.update({ storageBucketId: storageBucketOne.id });
|
||||
instances.target.update({
|
||||
storageBucketId: storageBucketOne.id,
|
||||
enableSessionRecording: true,
|
||||
});
|
||||
await visit(urls.target);
|
||||
await click(LINK_LIST_SELECTOR);
|
||||
assert.strictEqual(currentURL(), urls.storageBucket);
|
||||
});
|
||||
|
||||
test('toggle should be enabled and storage buckets list should be shown when enable session recording button is clicked', async function (assert) {
|
||||
test('toggle should be disabled and storage buckets list should be shown when enable session recording button is clicked', async function (assert) {
|
||||
featuresService.enable('ssh-session-recording');
|
||||
assert.expect(3);
|
||||
await visit(urls.target);
|
||||
await click(ENABLE_BUTTON_SELECTOR);
|
||||
assert.strictEqual(currentURL(), urls.enableSessionRecording);
|
||||
assert.dom(TOGGLE_SELECTOR).isVisible();
|
||||
assert.dom(DROPDOWN_SELECTOR).isVisible();
|
||||
assert.dom(DROPDOWN_SELECTOR).isNotVisible();
|
||||
});
|
||||
|
||||
test('storage buckets list is hidden when toggle is disabled', async function (assert) {
|
||||
@@ -145,9 +149,9 @@ module('Acceptance | targets | enable session recording', function (hooks) {
|
||||
await click(ENABLE_BUTTON_SELECTOR);
|
||||
assert.strictEqual(currentURL(), urls.enableSessionRecording);
|
||||
assert.dom(TOGGLE_SELECTOR).isVisible();
|
||||
assert.dom(DROPDOWN_SELECTOR).isVisible();
|
||||
await click(TOGGLE_SELECTOR);
|
||||
assert.dom(DROPDOWN_SELECTOR).isNotVisible();
|
||||
await click(TOGGLE_SELECTOR);
|
||||
assert.dom(DROPDOWN_SELECTOR).isVisible();
|
||||
});
|
||||
|
||||
test('link to add new storage bucket should be displayed and redirect to new storage buckets form', async function (assert) {
|
||||
@@ -166,6 +170,7 @@ module('Acceptance | targets | enable session recording', function (hooks) {
|
||||
assert.expect(3);
|
||||
instances.target.update({
|
||||
storageBucketId: storageBucketOne.id,
|
||||
enable_session_recording: true,
|
||||
});
|
||||
await visit(urls.target);
|
||||
await click(SETTINGS_LINK_SELECTOR);
|
||||
@@ -188,12 +193,12 @@ module('Acceptance | targets | enable session recording', function (hooks) {
|
||||
assert.expect(5);
|
||||
await visit(urls.target);
|
||||
await click(ENABLE_BUTTON_SELECTOR);
|
||||
assert.strictEqual(currentURL(), urls.enableSessionRecording);
|
||||
assert.dom(DROPDOWN_SELECTOR).hasNoValue();
|
||||
|
||||
assert.strictEqual(currentURL(), urls.enableSessionRecording);
|
||||
await click(TOGGLE_SELECTOR);
|
||||
assert.dom(DROPDOWN_SELECTOR).hasNoValue();
|
||||
await select(DROPDOWN_SELECTOR, storageBucketTwo.id);
|
||||
assert.dom(DROPDOWN_SELECTOR).hasValue();
|
||||
|
||||
await click(SAVE_BTN_SELECTOR);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.target);
|
||||
@@ -204,11 +209,12 @@ module('Acceptance | targets | enable session recording', function (hooks) {
|
||||
);
|
||||
});
|
||||
|
||||
test('can cancel changes to an exisiting storage bucket selection', async function (assert) {
|
||||
test('can cancel changes to an existing storage bucket selection', async function (assert) {
|
||||
featuresService.enable('ssh-session-recording');
|
||||
assert.expect(3);
|
||||
instances.target.update({
|
||||
storageBucketId: storageBucketOne.id,
|
||||
enable_session_recording: true,
|
||||
});
|
||||
|
||||
await visit(urls.target);
|
||||
|
||||
Reference in new issue
Block a user