From 588acff9ae1627b3829065db25a79927fa553595 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Thu, 10 Dec 2020 14:30:14 -0600 Subject: [PATCH 01/17] Redirect to url with namespace param if user logged into root namespace without permission --- ui/app/routes/vault/cluster.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ui/app/routes/vault/cluster.js b/ui/app/routes/vault/cluster.js index 96db4d842f66..c5b2f1e632bc 100644 --- a/ui/app/routes/vault/cluster.js +++ b/ui/app/routes/vault/cluster.js @@ -4,6 +4,7 @@ import { reject } from 'rsvp'; import Route from '@ember/routing/route'; import { task, timeout } from 'ember-concurrency'; import Ember from 'ember'; +import getStorage from '../../lib/token-storage'; import ClusterRoute from 'vault/mixins/cluster-route'; import ModelBoundaryRoute from 'vault/mixins/model-boundary-route'; @@ -34,7 +35,19 @@ export default Route.extend(ModelBoundaryRoute, ClusterRoute, { async beforeModel() { const params = this.paramsFor(this.routeName); - this.namespaceService.setNamespace(params.namespaceQueryParam); + let namespace = params.namespaceQueryParam; + const currentTokenName = this.auth.get('currentTokenName'); + // if no namespace queryParam and user authenticated, + // use user's root namespace to redirect to properly param'd url + if (!namespace && currentTokenName) { + const storage = getStorage().getItem(currentTokenName); + namespace = storage.userRootNamespace; + // only redirect if something other than nothing + if (namespace) { + this.transitionTo({ queryParams: { namespace } }); + } + } + this.namespaceService.setNamespace(namespace); const id = this.getClusterId(params); if (id) { this.auth.setCluster(id); From e75a591adfcebbd5d079fbebea3ddf0f878b9704 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Mon, 14 Dec 2020 14:12:19 -0600 Subject: [PATCH 02/17] Managed toolbar (always on) handles input with regards to managed namespace root --- ui/app/controllers/vault/cluster/auth.js | 20 ++++++++++++++ ui/app/styles/components/auth-form.scss | 1 + ui/app/templates/vault/cluster/auth.hbs | 34 +++++++++++++++++++++++- 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/ui/app/controllers/vault/cluster/auth.js b/ui/app/controllers/vault/cluster/auth.js index 03907ac29cf9..51b0b6b64204 100644 --- a/ui/app/controllers/vault/cluster/auth.js +++ b/ui/app/controllers/vault/cluster/auth.js @@ -12,6 +12,26 @@ export default Controller.extend({ wrappedToken: alias('vaultController.wrappedToken'), authMethod: '', redirectTo: alias('vaultController.redirectTo'), + managedNamespaceRoot: 'admin', // TODO: get this from API + + get managedNamespaceChild() { + let fullParam = this.namespaceQueryParam; + let split = fullParam.split('/'); + if (split.length > 1) { + split.shift(); + console.log(split); + return `/${split.join('/')}`; + } + return ''; + }, + + updateManagedNamespace: task(function*(value) { + yield timeout(500); + // TODO: Move this to shared fn + const newNamespace = `${this.managedNamespaceRoot}${value}`; + this.namespaceService.setNamespace(newNamespace, true); + this.set('namespaceQueryParam', newNamespace); + }).restartable(), updateNamespace: task(function*(value) { // debounce diff --git a/ui/app/styles/components/auth-form.scss b/ui/app/styles/components/auth-form.scss index cd7752493d7c..5bba1043107f 100644 --- a/ui/app/styles/components/auth-form.scss +++ b/ui/app/styles/components/auth-form.scss @@ -28,6 +28,7 @@ .field-label { margin-right: $spacing-s; + align-self: center; } .is-label { diff --git a/ui/app/templates/vault/cluster/auth.hbs b/ui/app/templates/vault/cluster/auth.hbs index 1cb533ee0a04..fbf7c8bc3da1 100644 --- a/ui/app/templates/vault/cluster/auth.hbs +++ b/ui/app/templates/vault/cluster/auth.hbs @@ -4,7 +4,39 @@ Sign in to Vault - {{#if (has-feature "Namespaces")}} + {{#if managedNamespaceRoot}} + + +
+
+
+ +
+
+ /{{managedNamespaceRoot}} +
+
+
+
+ +
+
+
+
+
+
+
+ {{else if (has-feature "Namespaces")}}
From 3448f0b31a1836bf6fbb623a0458848f948504ed Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 15 Dec 2020 11:37:13 -0600 Subject: [PATCH 03/17] Update mirage with vault-config mock route --- ui/mirage/config.js | 20 ++++++++++++++++++++ ui/mirage/factories/user.js | 9 --------- 2 files changed, 20 insertions(+), 9 deletions(-) delete mode 100644 ui/mirage/factories/user.js diff --git a/ui/mirage/config.js b/ui/mirage/config.js index 4c145a75db50..8c17fc85d1e1 100644 --- a/ui/mirage/config.js +++ b/ui/mirage/config.js @@ -1,4 +1,23 @@ +import Response from 'ember-cli-mirage/response'; + export default function() { + this.get( + 'http://localhost:4200/vault-config', + function() { + console.log('getting vault config'); + return new Response( + 201, + { + 'Content-Type': 'application/json', + }, + { + managedNamespaceRoot: 'admin', + } + ); + }, + { timing: 3000 } + ); + this.namespace = 'v1'; this.get('sys/internal/counters/activity', function(db) { @@ -19,5 +38,6 @@ export default function() { data: db['metrics/configs'].first(), }; }); + this.passthrough(); } diff --git a/ui/mirage/factories/user.js b/ui/mirage/factories/user.js deleted file mode 100644 index fa39d34cc7be..000000000000 --- a/ui/mirage/factories/user.js +++ /dev/null @@ -1,9 +0,0 @@ -import Mirage from 'ember-cli-mirage'; - -export default Mirage.Factory.extend({ - name(i) { - return `Person ${i}`; - }, - age: 28, - admin: false, -}); From 54d1254de172c6a89efcc91094aa151dee37bcb5 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 15 Dec 2020 11:56:47 -0600 Subject: [PATCH 04/17] Fix tests by skipping storage read when testing --- ui/app/routes/vault/cluster.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/routes/vault/cluster.js b/ui/app/routes/vault/cluster.js index c5b2f1e632bc..182a9909696f 100644 --- a/ui/app/routes/vault/cluster.js +++ b/ui/app/routes/vault/cluster.js @@ -39,7 +39,7 @@ export default Route.extend(ModelBoundaryRoute, ClusterRoute, { const currentTokenName = this.auth.get('currentTokenName'); // if no namespace queryParam and user authenticated, // use user's root namespace to redirect to properly param'd url - if (!namespace && currentTokenName) { + if (!namespace && currentTokenName && !Ember.testing) { const storage = getStorage().getItem(currentTokenName); namespace = storage.userRootNamespace; // only redirect if something other than nothing From 3dc42c4bad278ef558f546629127ca9929ba247a Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Wed, 16 Dec 2020 11:04:58 -0600 Subject: [PATCH 05/17] New config service which gets updated on beforeModel hook of application call to vault-config endpoint (mocked) --- ui/app/controllers/vault/cluster/auth.js | 3 ++- ui/app/routes/application.js | 11 ++++++++++ ui/app/services/config.js | 9 ++++++++ ui/mirage/config.js | 28 ++++++++---------------- 4 files changed, 31 insertions(+), 20 deletions(-) create mode 100644 ui/app/services/config.js diff --git a/ui/app/controllers/vault/cluster/auth.js b/ui/app/controllers/vault/cluster/auth.js index 51b0b6b64204..6d7f684d978f 100644 --- a/ui/app/controllers/vault/cluster/auth.js +++ b/ui/app/controllers/vault/cluster/auth.js @@ -7,12 +7,13 @@ export default Controller.extend({ vaultController: controller('vault'), clusterController: controller('vault.cluster'), namespaceService: service('namespace'), + configService: service('config'), namespaceQueryParam: alias('clusterController.namespaceQueryParam'), queryParams: [{ authMethod: 'with' }], wrappedToken: alias('vaultController.wrappedToken'), authMethod: '', redirectTo: alias('vaultController.redirectTo'), - managedNamespaceRoot: 'admin', // TODO: get this from API + managedNamespaceRoot: alias('configService.managedNamespaceRoot'), get managedNamespaceChild() { let fullParam = this.namespaceQueryParam; diff --git a/ui/app/routes/application.js b/ui/app/routes/application.js index 7fd0fb582020..3b8a55d4f8b5 100644 --- a/ui/app/routes/application.js +++ b/ui/app/routes/application.js @@ -8,6 +8,7 @@ export default Route.extend({ routing: service('router'), wizard: service(), namespaceService: service('namespace'), + config: service(), actions: { willTransition() { @@ -81,4 +82,14 @@ export default Route.extend({ return true; }, }, + + async beforeModel() { + const result = await fetch('/v1/vault-config', { + method: 'GET', + }); + if (result.status === 200) { + const body = await result.json(); + this.config.setManagedNamespaceRoot(body.managedNamespaceRoot); + } + }, }); diff --git a/ui/app/services/config.js b/ui/app/services/config.js new file mode 100644 index 000000000000..fec0db1a5a6e --- /dev/null +++ b/ui/app/services/config.js @@ -0,0 +1,9 @@ +import Service from '@ember/service'; + +export default Service.extend({ + managedNamespaceRoot: null, + + setManagedNamespaceRoot(path) { + this.set('managedNamespaceRoot', path); + }, +}); diff --git a/ui/mirage/config.js b/ui/mirage/config.js index 8c17fc85d1e1..4f15a54d0d94 100644 --- a/ui/mirage/config.js +++ b/ui/mirage/config.js @@ -1,23 +1,4 @@ -import Response from 'ember-cli-mirage/response'; - export default function() { - this.get( - 'http://localhost:4200/vault-config', - function() { - console.log('getting vault config'); - return new Response( - 201, - { - 'Content-Type': 'application/json', - }, - { - managedNamespaceRoot: 'admin', - } - ); - }, - { timing: 3000 } - ); - this.namespace = 'v1'; this.get('sys/internal/counters/activity', function(db) { @@ -39,5 +20,14 @@ export default function() { }; }); + this.get('/vault-config', (schema, request) => { + if (request.params.false) { + return {}; + } + return { + managedNamespaceRoot: 'admin', + }; + }); + this.passthrough(); } From 03b5f086e9e4905c6035fbee7ad90ef741a7c3f6 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Wed, 16 Dec 2020 11:26:29 -0600 Subject: [PATCH 06/17] Redirect with namespace query param if no current namespace param AND managed root namespace set --- ui/app/routes/vault/cluster.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/app/routes/vault/cluster.js b/ui/app/routes/vault/cluster.js index 182a9909696f..ab81c4a09237 100644 --- a/ui/app/routes/vault/cluster.js +++ b/ui/app/routes/vault/cluster.js @@ -16,6 +16,7 @@ export default Route.extend(ModelBoundaryRoute, ClusterRoute, { permissions: service(), store: service(), auth: service(), + config: service(), currentCluster: service(), modelTypes: computed(function() { return ['node', 'secret', 'secret-engine']; @@ -46,6 +47,8 @@ export default Route.extend(ModelBoundaryRoute, ClusterRoute, { if (namespace) { this.transitionTo({ queryParams: { namespace } }); } + } else if (!namespace && !!this.config.managedNamespaceRoot) { + this.transitionTo({ queryParams: { namespace: this.config.managedNamespaceRoot } }); } this.namespaceService.setNamespace(namespace); const id = this.getClusterId(params); From 4007b14071b0df4cd5b483bd5b591ea953c4cdc0 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Mon, 21 Dec 2020 14:07:25 -0600 Subject: [PATCH 07/17] Update config path and return value to array of string feature flags --- ui/app/routes/application.js | 4 ++-- ui/app/services/config.js | 12 +++++++++--- ui/mirage/config.js | 9 ++++----- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/ui/app/routes/application.js b/ui/app/routes/application.js index 3b8a55d4f8b5..dca176aa0bef 100644 --- a/ui/app/routes/application.js +++ b/ui/app/routes/application.js @@ -84,12 +84,12 @@ export default Route.extend({ }, async beforeModel() { - const result = await fetch('/v1/vault-config', { + const result = await fetch('/v1/sys/internal/vault-config', { method: 'GET', }); if (result.status === 200) { const body = await result.json(); - this.config.setManagedNamespaceRoot(body.managedNamespaceRoot); + this.config.setFeatureFlags(body.data.feature_flags); } }, }); diff --git a/ui/app/services/config.js b/ui/app/services/config.js index fec0db1a5a6e..09ca3bc94d65 100644 --- a/ui/app/services/config.js +++ b/ui/app/services/config.js @@ -1,9 +1,15 @@ import Service from '@ember/service'; export default Service.extend({ - managedNamespaceRoot: null, + featureFlags: null, + setFeatureFlags(flags) { + this.set('featureFlags', flags); + }, - setManagedNamespaceRoot(path) { - this.set('managedNamespaceRoot', path); + get managedNamespaceRoot() { + if (this.featureFlags && this.featureFlags.includes('MANAGED_NAMESPACE')) { + return 'admin'; + } + return null; }, }); diff --git a/ui/mirage/config.js b/ui/mirage/config.js index 4f15a54d0d94..9cbba0aa0233 100644 --- a/ui/mirage/config.js +++ b/ui/mirage/config.js @@ -20,12 +20,11 @@ export default function() { }; }); - this.get('/vault-config', (schema, request) => { - if (request.params.false) { - return {}; - } + this.get('/sys/internal/vault-config', (db, request) => { return { - managedNamespaceRoot: 'admin', + data: { + feature_flags: request.queryParams.empty ? null : ['MANAGED_NAMESPACE'], + }, }; }); From 08f90ffc7d77d90eb204dcdc2409fea45fc6fdd2 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 22 Dec 2020 10:01:01 -0600 Subject: [PATCH 08/17] Update mirage response and config service to match mini-RFC --- ui/app/controllers/vault/cluster/auth.js | 2 +- ui/app/services/config.js | 6 +++++- ui/mirage/config.js | 5 +++-- ui/mirage/factories/feature.js | 7 +++++++ ui/mirage/models/feature.js | 3 +++ 5 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 ui/mirage/factories/feature.js create mode 100644 ui/mirage/models/feature.js diff --git a/ui/app/controllers/vault/cluster/auth.js b/ui/app/controllers/vault/cluster/auth.js index 6d7f684d978f..f02fa6384753 100644 --- a/ui/app/controllers/vault/cluster/auth.js +++ b/ui/app/controllers/vault/cluster/auth.js @@ -20,13 +20,13 @@ export default Controller.extend({ let split = fullParam.split('/'); if (split.length > 1) { split.shift(); - console.log(split); return `/${split.join('/')}`; } return ''; }, updateManagedNamespace: task(function*(value) { + // debounce yield timeout(500); // TODO: Move this to shared fn const newNamespace = `${this.managedNamespaceRoot}${value}`; diff --git a/ui/app/services/config.js b/ui/app/services/config.js index 09ca3bc94d65..fb1c85a0ecbc 100644 --- a/ui/app/services/config.js +++ b/ui/app/services/config.js @@ -1,5 +1,9 @@ import Service from '@ember/service'; +const FLAGS = { + vaultCloudNamespace: 'VAULT_CLOUD_ADMIN_NAMESPACE', +}; + export default Service.extend({ featureFlags: null, setFeatureFlags(flags) { @@ -7,7 +11,7 @@ export default Service.extend({ }, get managedNamespaceRoot() { - if (this.featureFlags && this.featureFlags.includes('MANAGED_NAMESPACE')) { + if (this.featureFlags && this.featureFlags.includes(FLAGS.vaultCloudNamespace)) { return 'admin'; } return null; diff --git a/ui/mirage/config.js b/ui/mirage/config.js index 9cbba0aa0233..30d4ab7dc7bc 100644 --- a/ui/mirage/config.js +++ b/ui/mirage/config.js @@ -20,10 +20,11 @@ export default function() { }; }); - this.get('/sys/internal/vault-config', (db, request) => { + this.get('/sys/internal/vault-config', db => { + const featuresResponse = db.features.first(); return { data: { - feature_flags: request.queryParams.empty ? null : ['MANAGED_NAMESPACE'], + feature_flags: featuresResponse ? featuresResponse.feature_flags : null, }, }; }); diff --git a/ui/mirage/factories/feature.js b/ui/mirage/factories/feature.js new file mode 100644 index 000000000000..832aee5ed152 --- /dev/null +++ b/ui/mirage/factories/feature.js @@ -0,0 +1,7 @@ +import { Factory } from 'ember-cli-mirage'; + +export default Factory.extend({ + feature_flags() { + return []; // VAULT_CLOUD_ADMIN_NAMESPACE + }, +}); diff --git a/ui/mirage/models/feature.js b/ui/mirage/models/feature.js new file mode 100644 index 000000000000..770b50936d3c --- /dev/null +++ b/ui/mirage/models/feature.js @@ -0,0 +1,3 @@ +import { Model } from 'ember-cli-mirage'; + +export default Model.extend({}); From 3f92b6f26f5f7cfb969b487a2682cd336d9ddc6e Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 22 Dec 2020 14:53:41 -0600 Subject: [PATCH 09/17] Rename config service to feature-flag service --- ui/app/controllers/vault/cluster/auth.js | 4 ++-- ui/app/routes/application.js | 6 +++--- ui/app/routes/vault/cluster.js | 6 +++--- ui/app/services/{config.js => feature-flag.js} | 0 ui/mirage/config.js | 4 +++- ui/tests/unit/services/feature-flag-test.js | 12 ++++++++++++ 6 files changed, 23 insertions(+), 9 deletions(-) rename ui/app/services/{config.js => feature-flag.js} (100%) create mode 100644 ui/tests/unit/services/feature-flag-test.js diff --git a/ui/app/controllers/vault/cluster/auth.js b/ui/app/controllers/vault/cluster/auth.js index f02fa6384753..d4478bae4cd4 100644 --- a/ui/app/controllers/vault/cluster/auth.js +++ b/ui/app/controllers/vault/cluster/auth.js @@ -7,13 +7,13 @@ export default Controller.extend({ vaultController: controller('vault'), clusterController: controller('vault.cluster'), namespaceService: service('namespace'), - configService: service('config'), + featureFlagService: service('featureFlag'), namespaceQueryParam: alias('clusterController.namespaceQueryParam'), queryParams: [{ authMethod: 'with' }], wrappedToken: alias('vaultController.wrappedToken'), authMethod: '', redirectTo: alias('vaultController.redirectTo'), - managedNamespaceRoot: alias('configService.managedNamespaceRoot'), + managedNamespaceRoot: alias('featureFlagService.managedNamespaceRoot'), get managedNamespaceChild() { let fullParam = this.namespaceQueryParam; diff --git a/ui/app/routes/application.js b/ui/app/routes/application.js index dca176aa0bef..a1e7cda30937 100644 --- a/ui/app/routes/application.js +++ b/ui/app/routes/application.js @@ -8,7 +8,7 @@ export default Route.extend({ routing: service('router'), wizard: service(), namespaceService: service('namespace'), - config: service(), + featureFlagService: service('featureFlag'), actions: { willTransition() { @@ -84,12 +84,12 @@ export default Route.extend({ }, async beforeModel() { - const result = await fetch('/v1/sys/internal/vault-config', { + const result = await fetch('/v1/sys/internal/ui/feature-flags', { method: 'GET', }); if (result.status === 200) { const body = await result.json(); - this.config.setFeatureFlags(body.data.feature_flags); + this.featureFlagService.setFeatureFlags(body.data.feature_flags); } }, }); diff --git a/ui/app/routes/vault/cluster.js b/ui/app/routes/vault/cluster.js index ab81c4a09237..f489a9d3a2ae 100644 --- a/ui/app/routes/vault/cluster.js +++ b/ui/app/routes/vault/cluster.js @@ -16,7 +16,7 @@ export default Route.extend(ModelBoundaryRoute, ClusterRoute, { permissions: service(), store: service(), auth: service(), - config: service(), + featureFlagService: service('featureFlag'), currentCluster: service(), modelTypes: computed(function() { return ['node', 'secret', 'secret-engine']; @@ -47,8 +47,8 @@ export default Route.extend(ModelBoundaryRoute, ClusterRoute, { if (namespace) { this.transitionTo({ queryParams: { namespace } }); } - } else if (!namespace && !!this.config.managedNamespaceRoot) { - this.transitionTo({ queryParams: { namespace: this.config.managedNamespaceRoot } }); + } else if (!namespace && !!this.featureFlagService.managedNamespaceRoot) { + this.transitionTo({ queryParams: { namespace: this.featureFlagService.managedNamespaceRoot } }); } this.namespaceService.setNamespace(namespace); const id = this.getClusterId(params); diff --git a/ui/app/services/config.js b/ui/app/services/feature-flag.js similarity index 100% rename from ui/app/services/config.js rename to ui/app/services/feature-flag.js diff --git a/ui/mirage/config.js b/ui/mirage/config.js index 30d4ab7dc7bc..1e41116537ff 100644 --- a/ui/mirage/config.js +++ b/ui/mirage/config.js @@ -2,6 +2,7 @@ export default function() { this.namespace = 'v1'; this.get('sys/internal/counters/activity', function(db) { + console.log('getting sys/internal/counters/activity'); let data = {}; const firstRecord = db['metrics/activities'].first(); if (firstRecord) { @@ -14,13 +15,14 @@ export default function() { }); this.get('sys/internal/counters/config', function(db) { + console.log('getting sys/internal/counters/config'); return { request_id: '00001', data: db['metrics/configs'].first(), }; }); - this.get('/sys/internal/vault-config', db => { + this.get('/sys/internal/ui/feature-flags', db => { const featuresResponse = db.features.first(); return { data: { diff --git a/ui/tests/unit/services/feature-flag-test.js b/ui/tests/unit/services/feature-flag-test.js new file mode 100644 index 000000000000..b702f515858f --- /dev/null +++ b/ui/tests/unit/services/feature-flag-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Service | feature-flag', function(hooks) { + setupTest(hooks); + + // TODO: Replace this with your real tests. + test('it exists', function(assert) { + let service = this.owner.lookup('service:feature-flag'); + assert.ok(service); + }); +}); From bc58e705438987089746efab699b38440483dc2c Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 22 Dec 2020 14:57:55 -0600 Subject: [PATCH 10/17] Add changelog --- changelog/10588.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog/10588.txt diff --git a/changelog/10588.txt b/changelog/10588.txt new file mode 100644 index 000000000000..0e363b4b3700 --- /dev/null +++ b/changelog/10588.txt @@ -0,0 +1,3 @@ +```release-note:feature +ui: Adds check for feature flag on application, and updates namespace toolbar on login if present +``` From a5d593a38456b3d6f6036f42a76bfa9d313d8099 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 22 Dec 2020 14:59:22 -0600 Subject: [PATCH 11/17] Update mirage for feature namespace testing --- ui/mirage/models/feature.js | 4 ++- ui/mirage/scenarios/default.js | 1 + ui/tests/acceptance/managed-namespace-test.js | 34 +++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 ui/tests/acceptance/managed-namespace-test.js diff --git a/ui/mirage/models/feature.js b/ui/mirage/models/feature.js index 770b50936d3c..4f84f3bd71dc 100644 --- a/ui/mirage/models/feature.js +++ b/ui/mirage/models/feature.js @@ -1,3 +1,5 @@ import { Model } from 'ember-cli-mirage'; -export default Model.extend({}); +export default Model.extend({ + feature_flags: null, +}); diff --git a/ui/mirage/scenarios/default.js b/ui/mirage/scenarios/default.js index 216d7fcb2ff2..5e65448276cf 100644 --- a/ui/mirage/scenarios/default.js +++ b/ui/mirage/scenarios/default.js @@ -1,3 +1,4 @@ export default function(server) { server.create('metrics/config'); + server.create('feature', { feature_flags: ['SOME_FLAG', 'VAULT_CLOUD_ADMIN_NAMESPACE'] }); } diff --git a/ui/tests/acceptance/managed-namespace-test.js b/ui/tests/acceptance/managed-namespace-test.js new file mode 100644 index 000000000000..5bdccd7566ca --- /dev/null +++ b/ui/tests/acceptance/managed-namespace-test.js @@ -0,0 +1,34 @@ +import { module, test } from 'qunit'; +import { visit, currentURL, settled } from '@ember/test-helpers'; +import { setupApplicationTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import logout from 'vault/tests/pages/logout'; + +module('Acceptance | Enterprise | Managed namespace root', function(hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + hooks.beforeEach(function() { + return logout.visit(); + }); + + test('it shows the regular namespace toolbar when not managed', async function(assert) { + assert.dom('[data-test-namespace-toolbar]').exists('Namespace toolbar exists'); + assert.dom('input#namespace').hasAttribute('placeholder', '/ (Root)'); + assert.equal(currentURL(), '/vault/auth?with=token', 'Does not redirect'); + }); + + test('it shows the managed namespace toolbar when feature flag exists', async function(assert) { + await server.create('feature', { feature_flags: ['SOME_FLAG', 'VAULT_CLOUD_ADMIN_NAMESPACE'] }); + // await settled(); + await visit('/vault/auth'); + await settled(); + console.log(currentURL()); + assert.equal(1, 1); + // await this.pauseTest(); + + // assert.equal(currentURL(), '/vault/auth?namespace=admin&with=token', 'Redirects to root namespace'); + // assert.dom('[data-test-managed-namespace-toolbar]').exists('Managed namespace toolbar exists'); + // await this.pauseTest(); + }); +}); From ebc2c6598e792d66deee11fac0320fa9c29058ec Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Wed, 23 Dec 2020 13:12:55 -0600 Subject: [PATCH 12/17] Test coverage for managed namespace changes --- ui/app/templates/vault/cluster/auth.hbs | 6 +-- ui/config/environment.js | 6 +++ .../acceptance/enterprise-namespaces-test.js | 20 +++++++++- ui/tests/acceptance/managed-namespace-test.js | 40 ++++++++++--------- 4 files changed, 49 insertions(+), 23 deletions(-) diff --git a/ui/app/templates/vault/cluster/auth.hbs b/ui/app/templates/vault/cluster/auth.hbs index fbf7c8bc3da1..648658e99e6f 100644 --- a/ui/app/templates/vault/cluster/auth.hbs +++ b/ui/app/templates/vault/cluster/auth.hbs @@ -7,13 +7,13 @@ {{#if managedNamespaceRoot}} -
+
- /{{managedNamespaceRoot}} + /{{managedNamespaceRoot}}
@@ -39,7 +39,7 @@ {{else if (has-feature "Namespaces")}} -
+
diff --git a/ui/config/environment.js b/ui/config/environment.js index 401413bc767f..0ed300b59842 100644 --- a/ui/config/environment.js +++ b/ui/config/environment.js @@ -45,6 +45,9 @@ module.exports = function(environment) { ENV.APP.LOG_TRANSITIONS = true; // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; // ENV.APP.LOG_VIEW_LOOKUPS = true; + ENV['ember-cli-mirage'] = { + enabled: true, + }; } if (environment === 'test') { @@ -56,6 +59,9 @@ module.exports = function(environment) { ENV.APP.rootElement = '#ember-testing'; ENV.APP.autoboot = false; ENV.flashMessageDefaults.timeout = 50; + ENV['ember-cli-mirage'] = { + enabled: true, + }; } if (environment !== 'production') { ENV.APP.DEFAULT_PAGE_SIZE = 15; diff --git a/ui/tests/acceptance/enterprise-namespaces-test.js b/ui/tests/acceptance/enterprise-namespaces-test.js index 06e10dd22122..5d585f08961d 100644 --- a/ui/tests/acceptance/enterprise-namespaces-test.js +++ b/ui/tests/acceptance/enterprise-namespaces-test.js @@ -1,4 +1,4 @@ -import { click, settled } from '@ember/test-helpers'; +import { click, settled, fillIn, currentURL } from '@ember/test-helpers'; import { module, test } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; import { create } from 'ember-cli-page-object'; @@ -66,4 +66,22 @@ module('Acceptance | Enterprise | namespaces', function(hooks) { .dom('[data-test-namespace-link="beep/boop/bop"]') .exists('renders the link to the nested namespace'); }); + + test('it shows the regular namespace toolbar when not managed', async function(assert) { + // This test is the opposite of the test in managed-namespace-test + await logout.visit(); + assert.equal(currentURL(), '/vault/auth?with=token', 'Does not redirect'); + assert.dom('[data-test-namespace-toolbar]').exists('Normal namespace toolbar exists'); + assert + .dom('[data-test-managed-namespace-toolbar]') + .doesNotExist('Managed namespace toolbar does not exist'); + assert.dom('input#namespace').hasAttribute('placeholder', '/ (Root)'); + await fillIn('input#namespace', '/foo'); + let encodedNamespace = encodeURIComponent('/foo'); + assert.equal( + currentURL(), + `/vault/auth?namespace=${encodedNamespace}&with=token`, + 'Does not prepend root to namespace' + ); + }); }); diff --git a/ui/tests/acceptance/managed-namespace-test.js b/ui/tests/acceptance/managed-namespace-test.js index 5bdccd7566ca..5f99d41e4852 100644 --- a/ui/tests/acceptance/managed-namespace-test.js +++ b/ui/tests/acceptance/managed-namespace-test.js @@ -1,34 +1,36 @@ import { module, test } from 'qunit'; -import { visit, currentURL, settled } from '@ember/test-helpers'; +import { currentURL, visit, fillIn } from '@ember/test-helpers'; import { setupApplicationTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; -import logout from 'vault/tests/pages/logout'; module('Acceptance | Enterprise | Managed namespace root', function(hooks) { setupApplicationTest(hooks); setupMirage(hooks); - hooks.beforeEach(function() { - return logout.visit(); - }); - - test('it shows the regular namespace toolbar when not managed', async function(assert) { - assert.dom('[data-test-namespace-toolbar]').exists('Namespace toolbar exists'); - assert.dom('input#namespace').hasAttribute('placeholder', '/ (Root)'); - assert.equal(currentURL(), '/vault/auth?with=token', 'Does not redirect'); + hooks.beforeEach(async function() { + server.logging = true; + /** + * Since the features are fetched on the application load, + * we have to populate them on the beforeEach hook because + * the fetch won't trigger again within the tests + */ + server.create('feature', { feature_flags: ['VAULT_CLOUD_ADMIN_NAMESPACE'] }); }); test('it shows the managed namespace toolbar when feature flag exists', async function(assert) { - await server.create('feature', { feature_flags: ['SOME_FLAG', 'VAULT_CLOUD_ADMIN_NAMESPACE'] }); - // await settled(); await visit('/vault/auth'); - await settled(); - console.log(currentURL()); - assert.equal(1, 1); - // await this.pauseTest(); + assert.equal(currentURL(), '/vault/auth?namespace=admin&with=token', 'Redirected to base namespace'); - // assert.equal(currentURL(), '/vault/auth?namespace=admin&with=token', 'Redirects to root namespace'); - // assert.dom('[data-test-managed-namespace-toolbar]').exists('Managed namespace toolbar exists'); - // await this.pauseTest(); + assert.dom('[data-test-namespace-toolbar]').doesNotExist('Normal namespace toolbar does not exist'); + assert.dom('[data-test-managed-namespace-toolbar]').exists('Managed namespace toolbar exists'); + assert.dom('[data-test-managed-namespace-root]').hasText('/admin', 'Shows /admin namespace prefix'); + assert.dom('input#namespace').hasAttribute('placeholder', '/ (Default)'); + await fillIn('input#namespace', '/foo'); + let encodedNamespace = encodeURIComponent('admin/foo'); + assert.equal( + currentURL(), + `/vault/auth?namespace=${encodedNamespace}&with=token`, + 'Correctly prepends root to namespace' + ); }); }); From dc4ae06e30cc9c3d5d334cd1a7b08e9b4551d686 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Wed, 23 Dec 2020 13:26:56 -0600 Subject: [PATCH 13/17] Remove mirage:true in dev --- ui/config/environment.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/config/environment.js b/ui/config/environment.js index 0ed300b59842..6eae53c4cbc5 100644 --- a/ui/config/environment.js +++ b/ui/config/environment.js @@ -45,9 +45,9 @@ module.exports = function(environment) { ENV.APP.LOG_TRANSITIONS = true; // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; // ENV.APP.LOG_VIEW_LOOKUPS = true; - ENV['ember-cli-mirage'] = { - enabled: true, - }; + // ENV['ember-cli-mirage'] = { + // enabled: true, + // }; } if (environment === 'test') { From 382e0fa065da24db0112b63e4e607b69952feeb3 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 5 Jan 2021 10:40:26 -0600 Subject: [PATCH 14/17] Handle null body case on feature-flag response, add pretender route for feature-flags on shamir test --- ui/app/routes/application.js | 3 ++- ui/tests/acceptance/init-test.js | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/app/routes/application.js b/ui/app/routes/application.js index a1e7cda30937..7c36d1c5431d 100644 --- a/ui/app/routes/application.js +++ b/ui/app/routes/application.js @@ -89,7 +89,8 @@ export default Route.extend({ }); if (result.status === 200) { const body = await result.json(); - this.featureFlagService.setFeatureFlags(body.data.feature_flags); + const flags = body.data?.feature_flags || []; + this.featureFlagService.setFeatureFlags(flags); } }, }); diff --git a/ui/tests/acceptance/init-test.js b/ui/tests/acceptance/init-test.js index bb2fdc543c71..9fbd0e548197 100644 --- a/ui/tests/acceptance/init-test.js +++ b/ui/tests/acceptance/init-test.js @@ -77,6 +77,9 @@ module('Acceptance | init', function(hooks) { this.server.get('/v1/sys/health', () => { return [200, { 'Content-Type': 'application/json' }, JSON.stringify(HEALTH_RESPONSE)]; }); + this.server.get('/v1/sys/internal/ui/feature-flags', () => { + return [200, { 'Content-Type': 'application/json' }, JSON.stringify({ body: { feature_flags: [] } })]; + }); }); hooks.afterEach(function() { From 8ba0d10dd95a3f9fb3662600cabf98c5fb5a0b05 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Wed, 6 Jan 2021 14:35:29 -0600 Subject: [PATCH 15/17] disable mirage in test env, Update managed namespace test to use pretender --- ui/config/environment.js | 5 +--- ui/tests/acceptance/init-test.js | 4 +-- ui/tests/acceptance/managed-namespace-test.js | 25 +++++++++++++++---- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/ui/config/environment.js b/ui/config/environment.js index 6eae53c4cbc5..4896b1ad8f77 100644 --- a/ui/config/environment.js +++ b/ui/config/environment.js @@ -45,9 +45,6 @@ module.exports = function(environment) { ENV.APP.LOG_TRANSITIONS = true; // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; // ENV.APP.LOG_VIEW_LOOKUPS = true; - // ENV['ember-cli-mirage'] = { - // enabled: true, - // }; } if (environment === 'test') { @@ -60,7 +57,7 @@ module.exports = function(environment) { ENV.APP.autoboot = false; ENV.flashMessageDefaults.timeout = 50; ENV['ember-cli-mirage'] = { - enabled: true, + enabled: false, }; } if (environment !== 'production') { diff --git a/ui/tests/acceptance/init-test.js b/ui/tests/acceptance/init-test.js index 9fbd0e548197..8c257906f3ba 100644 --- a/ui/tests/acceptance/init-test.js +++ b/ui/tests/acceptance/init-test.js @@ -77,9 +77,7 @@ module('Acceptance | init', function(hooks) { this.server.get('/v1/sys/health', () => { return [200, { 'Content-Type': 'application/json' }, JSON.stringify(HEALTH_RESPONSE)]; }); - this.server.get('/v1/sys/internal/ui/feature-flags', () => { - return [200, { 'Content-Type': 'application/json' }, JSON.stringify({ body: { feature_flags: [] } })]; - }); + this.server.get('/v1/sys/internal/ui/feature-flags', this.server.passthrough); }); hooks.afterEach(function() { diff --git a/ui/tests/acceptance/managed-namespace-test.js b/ui/tests/acceptance/managed-namespace-test.js index 5f99d41e4852..0437c8dc8a0c 100644 --- a/ui/tests/acceptance/managed-namespace-test.js +++ b/ui/tests/acceptance/managed-namespace-test.js @@ -1,20 +1,35 @@ import { module, test } from 'qunit'; import { currentURL, visit, fillIn } from '@ember/test-helpers'; import { setupApplicationTest } from 'ember-qunit'; -import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import Pretender from 'pretender'; + +const FEATURE_FLAGS_RESPONSE = { + data: { + feature_flags: ['VAULT_CLOUD_ADMIN_NAMESPACE'], + }, +}; module('Acceptance | Enterprise | Managed namespace root', function(hooks) { setupApplicationTest(hooks); - setupMirage(hooks); - hooks.beforeEach(async function() { - server.logging = true; + hooks.beforeEach(function() { /** * Since the features are fetched on the application load, * we have to populate them on the beforeEach hook because * the fetch won't trigger again within the tests */ - server.create('feature', { feature_flags: ['VAULT_CLOUD_ADMIN_NAMESPACE'] }); + this.server = new Pretender(function() { + this.get('/v1/sys/internal/ui/feature-flags', () => { + return [200, { 'Content-Type': 'application/json' }, JSON.stringify(FEATURE_FLAGS_RESPONSE)]; + }); + this.get('/v1/sys/health', this.passthrough); + this.get('/v1/sys/seal-status', this.passthrough); + this.get('/v1/sys/license/features', this.passthrough); + }); + }); + + hooks.afterEach(function() { + this.server.shutdown(); }); test('it shows the managed namespace toolbar when feature flag exists', async function(assert) { From 39e75cd0a1660a8a1cfed35a907c9d6abce1ab94 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Wed, 6 Jan 2021 15:47:35 -0600 Subject: [PATCH 16/17] Feature flag service tests --- ui/tests/unit/services/feature-flag-test.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/ui/tests/unit/services/feature-flag-test.js b/ui/tests/unit/services/feature-flag-test.js index b702f515858f..d9da255774e2 100644 --- a/ui/tests/unit/services/feature-flag-test.js +++ b/ui/tests/unit/services/feature-flag-test.js @@ -4,9 +4,21 @@ import { setupTest } from 'ember-qunit'; module('Unit | Service | feature-flag', function(hooks) { setupTest(hooks); - // TODO: Replace this with your real tests. test('it exists', function(assert) { let service = this.owner.lookup('service:feature-flag'); assert.ok(service); }); + + test('it returns the namespace root when flag is present', function(assert) { + let service = this.owner.lookup('service:feature-flag'); + assert.equal(service.managedNamespaceRoot, null, 'Managed namespace root is null by default'); + service.setFeatureFlags(['VAULT_CLOUD_ADMIN_NAMESPACE']); + assert.equal(service.managedNamespaceRoot, 'admin', 'Managed namespace is admin when flag present'); + service.setFeatureFlags(['SOMETHING_ELSE']); + assert.equal( + service.managedNamespaceRoot, + null, + 'Flags were overwritten and root namespace is null again' + ); + }); }); From 2e5f739488fb413d40cb66b49edfca2321fce8d8 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Thu, 7 Jan 2021 13:52:01 -0600 Subject: [PATCH 17/17] remove logs --- ui/mirage/config.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/mirage/config.js b/ui/mirage/config.js index 1e41116537ff..5fb7e4a01e4d 100644 --- a/ui/mirage/config.js +++ b/ui/mirage/config.js @@ -2,7 +2,6 @@ export default function() { this.namespace = 'v1'; this.get('sys/internal/counters/activity', function(db) { - console.log('getting sys/internal/counters/activity'); let data = {}; const firstRecord = db['metrics/activities'].first(); if (firstRecord) { @@ -15,7 +14,6 @@ export default function() { }); this.get('sys/internal/counters/config', function(db) { - console.log('getting sys/internal/counters/config'); return { request_id: '00001', data: db['metrics/configs'].first(),