From 5ce88774b88cb60362c44322f07429d7fc205551 Mon Sep 17 00:00:00 2001
From: Kenia <19161242+kaxcode@users.noreply.github.com>
Date: Fri, 26 Mar 2021 11:47:47 -0400
Subject: [PATCH] ui: Auth Methods - Create Binding Rules tab (#9914)
* Create BindingRule adapter and tests
* Create BindingRule serializer and test
* Create BindingRule model and repository
* Add binding-rules mock data
* Create binding-rules router and call endpoint
* Create Binding rules tab
* Create and use BindingView component
* Create empty state for BindingView
* Remove binding rule requestForQueryRecord endpoint and tests
* Update binding rules selector to be monospaced
* Add bind type tooltip
* Create and Tabular-dl styling component
* Update hr tag global styling
* Rename BindingView to BindingList and refactor
* Add translations for bind types tooltip info
* Remove unused endpoint
* Refactor based on review notes
---
.../consul-ui/app/adapters/binding-rule.js | 14 ++++
.../consul/auth-method/binding-list/index.hbs | 28 ++++++++
.../components/consul/auth-method/index.scss | 65 +++++++------------
.../app/components/tooltip/index.scss | 2 +-
.../consul-ui/app/models/binding-rule.js | 19 ++++++
ui/packages/consul-ui/app/router.js | 3 +
.../app/routes/dc/acls/auth-methods/show.js | 13 +++-
.../acls/auth-methods/show/binding-rules.js | 16 +++++
.../consul-ui/app/serializers/binding-rule.js | 7 ++
.../app/services/repository/binding-rule.js | 32 +++++++++
.../app/styles/base/reset/system.scss | 1 +
.../consul-ui/app/styles/components.scss | 1 +
.../styles/components/tabular-dl/index.scss | 2 +
.../styles/components/tabular-dl/layout.scss | 38 +++++++++++
.../styles/components/tabular-dl/skin.scss | 21 ++++++
.../templates/dc/acls/auth-methods/show.hbs | 1 +
.../acls/auth-methods/show/binding-rules.hbs | 28 ++++++++
.../consul-ui/mock-api/v1/acl/binding-rules | 30 +++++++++
.../integration/adapters/binding-rule-test.js | 38 +++++++++++
.../serializers/binding-rule-test.js | 39 +++++++++++
.../tests/unit/adapters/binding-rule-test.js | 12 ++++
.../unit/serializers/binding-rule-test.js | 23 +++++++
ui/packages/consul-ui/translations/en-us.yaml | 10 +++
23 files changed, 399 insertions(+), 44 deletions(-)
create mode 100644 ui/packages/consul-ui/app/adapters/binding-rule.js
create mode 100644 ui/packages/consul-ui/app/components/consul/auth-method/binding-list/index.hbs
create mode 100644 ui/packages/consul-ui/app/models/binding-rule.js
create mode 100644 ui/packages/consul-ui/app/routes/dc/acls/auth-methods/show/binding-rules.js
create mode 100644 ui/packages/consul-ui/app/serializers/binding-rule.js
create mode 100644 ui/packages/consul-ui/app/services/repository/binding-rule.js
create mode 100644 ui/packages/consul-ui/app/styles/components/tabular-dl/index.scss
create mode 100644 ui/packages/consul-ui/app/styles/components/tabular-dl/layout.scss
create mode 100644 ui/packages/consul-ui/app/styles/components/tabular-dl/skin.scss
create mode 100644 ui/packages/consul-ui/app/templates/dc/acls/auth-methods/show/binding-rules.hbs
create mode 100644 ui/packages/consul-ui/mock-api/v1/acl/binding-rules
create mode 100644 ui/packages/consul-ui/tests/integration/adapters/binding-rule-test.js
create mode 100644 ui/packages/consul-ui/tests/integration/serializers/binding-rule-test.js
create mode 100644 ui/packages/consul-ui/tests/unit/adapters/binding-rule-test.js
create mode 100644 ui/packages/consul-ui/tests/unit/serializers/binding-rule-test.js
diff --git a/ui/packages/consul-ui/app/adapters/binding-rule.js b/ui/packages/consul-ui/app/adapters/binding-rule.js
new file mode 100644
index 000000000..b487f2d6f
--- /dev/null
+++ b/ui/packages/consul-ui/app/adapters/binding-rule.js
@@ -0,0 +1,14 @@
+import Adapter from './application';
+
+export default class BindingRuleAdapter extends Adapter {
+ requestForQuery(request, { dc, ns, authmethod, index, id }) {
+ return request`
+ GET /v1/acl/binding-rules?${{ dc, authmethod }}
+
+ ${{
+ ...this.formatNspace(ns),
+ index,
+ }}
+ `;
+ }
+}
diff --git a/ui/packages/consul-ui/app/components/consul/auth-method/binding-list/index.hbs b/ui/packages/consul-ui/app/components/consul/auth-method/binding-list/index.hbs
new file mode 100644
index 000000000..d0a5189fa
--- /dev/null
+++ b/ui/packages/consul-ui/app/components/consul/auth-method/binding-list/index.hbs
@@ -0,0 +1,28 @@
+
+
{{@item.BindName}}
+
+ - {{t "models.binding-rule.BindType"}}
+ -
+ {{@item.BindType}}
+
+ {{#if (eq @item.BindType 'service')}}
+
+ {{t "components.consul.auth-method.binding-list.bind-type.service"}}
+
+ {{else if (eq @item.BindType 'node')}}
+
+ {{t "components.consul.auth-method.binding-list.bind-type.node"}}
+
+ {{else if (eq @item.BindType 'role')}}
+
+ {{t "components.consul.auth-method.binding-list.bind-type.role"}}
+
+ {{/if}}
+
+
+ - {{t "models.binding-rule.Selector"}}
+ {{@item.Selector}}
+ - {{t "models.binding-rule.Description"}}
+ - {{@item.Description}}
+
+
\ No newline at end of file
diff --git a/ui/packages/consul-ui/app/components/consul/auth-method/index.scss b/ui/packages/consul-ui/app/components/consul/auth-method/index.scss
index 7dcac12a1..de2f4eb28 100644
--- a/ui/packages/consul-ui/app/components/consul/auth-method/index.scss
+++ b/ui/packages/consul-ui/app/components/consul/auth-method/index.scss
@@ -1,14 +1,14 @@
-.consul-consul-auth-method-view-list ul {
+// List
+.consul-auth-method-list ul {
.locality::before {
@extend %with-public-default-mask, %as-pseudo;
margin-right: 4px;
}
}
+
+// View
.consul-auth-method-view {
margin-bottom: 32px;
- > hr {
- background-color: var(--gray-200);
- }
section {
@extend %p1;
width: 100%;
@@ -32,43 +32,26 @@
}
dl,
section dl {
- display: flex;
- flex-wrap: wrap;
- > dt:last-of-type,
- > dd:last-of-type {
- border-bottom: 1px solid var(--gray-300) !important;
- }
- dt, dd {
- padding: 12px 0;
- margin: 0;
- border-top: 1px solid var(--gray-300) !important;
- color: $black !important;
- }
- dt {
- width: 20%;
- font-weight: $typo-weight-bold;
- }
- dd {
- margin-left: auto;
- width: 80%;
- display: flex;
- }
- dd > ul li {
- display: flex;
- }
- dd > ul li:not(:last-of-type) {
- padding-bottom: 12px;
- }
- dd .copy-button button {
- padding: 0 !important;
- margin: 0 4px 0 0 !important;
- }
- dd .copy-button button::before {
- background-color: $black;
- }
- dt.check + dd {
- padding-top: 16px;
- }
+ @extend %tabular-dl;
}
}
+// Binding List
+.consul-auth-method-binding-list {
+ p {
+ margin-bottom: 4px !important;
+ }
+ h2 {
+ @extend %h200;
+ padding-bottom: 12px;
+ }
+ dl {
+ @extend %tabular-dl;
+ }
+ code {
+ background-color: var(--gray-050);
+ padding: 0 12px;
+ }
+}
+
+
diff --git a/ui/packages/consul-ui/app/components/tooltip/index.scss b/ui/packages/consul-ui/app/components/tooltip/index.scss
index d4271ce71..952723e77 100644
--- a/ui/packages/consul-ui/app/components/tooltip/index.scss
+++ b/ui/packages/consul-ui/app/components/tooltip/index.scss
@@ -24,7 +24,7 @@
%tooltip-content {
@extend %p3;
padding: 12px;
- max-width: 192px;
+ max-width: 224px;
position: relative;
z-index: 1;
}
diff --git a/ui/packages/consul-ui/app/models/binding-rule.js b/ui/packages/consul-ui/app/models/binding-rule.js
new file mode 100644
index 000000000..90d7fa3d9
--- /dev/null
+++ b/ui/packages/consul-ui/app/models/binding-rule.js
@@ -0,0 +1,19 @@
+import Model, { attr } from '@ember-data/model';
+
+export const PRIMARY_KEY = 'uid';
+export const SLUG_KEY = 'ID';
+
+export default class BindingRule extends Model {
+ @attr('string') uid;
+ @attr('string') ID;
+
+ @attr('string') Datacenter;
+ @attr('string') Namespace;
+ @attr('string', { defaultValue: () => '' }) Description;
+ @attr('string') AuthMethod;
+ @attr('string', { defaultValue: () => '' }) Selector;
+ @attr('string') BindType;
+ @attr('string') BindName;
+ @attr('number') CreateIndex;
+ @attr('number') ModifyIndex;
+}
diff --git a/ui/packages/consul-ui/app/router.js b/ui/packages/consul-ui/app/router.js
index e7534ccea..14cb6656f 100644
--- a/ui/packages/consul-ui/app/router.js
+++ b/ui/packages/consul-ui/app/router.js
@@ -196,6 +196,9 @@ export const routes = {
'auth-method': {
_options: { path: '/auth-method' },
},
+ 'binding-rules': {
+ _options: { path: '/binding-rules' },
+ },
},
},
},
diff --git a/ui/packages/consul-ui/app/routes/dc/acls/auth-methods/show.js b/ui/packages/consul-ui/app/routes/dc/acls/auth-methods/show.js
index 368ccc2ed..6f543e1ae 100644
--- a/ui/packages/consul-ui/app/routes/dc/acls/auth-methods/show.js
+++ b/ui/packages/consul-ui/app/routes/dc/acls/auth-methods/show.js
@@ -4,16 +4,25 @@ import { hash } from 'rsvp';
export default class ShowRoute extends SingleRoute {
@service('repository/auth-method') repo;
+ @service('repository/binding-rule') bindingRuleRepo;
model(params) {
+ const dc = this.modelFor('dc').dc;
+ const nspace = this.modelFor('nspace').nspace.substr(1);
+
return super.model(...arguments).then(model => {
return hash({
...model,
...{
item: this.repo.findBySlug({
id: params.id,
- dc: this.modelFor('dc').dc.Name,
- ns: this.modelFor('nspace').nspace.substr(1),
+ dc: dc.Name,
+ ns: nspace,
+ }),
+ bindingRules: this.bindingRuleRepo.findAllByDatacenter({
+ ns: nspace,
+ dc: dc.Name,
+ authmethod: params.id,
}),
},
});
diff --git a/ui/packages/consul-ui/app/routes/dc/acls/auth-methods/show/binding-rules.js b/ui/packages/consul-ui/app/routes/dc/acls/auth-methods/show/binding-rules.js
new file mode 100644
index 000000000..14668be38
--- /dev/null
+++ b/ui/packages/consul-ui/app/routes/dc/acls/auth-methods/show/binding-rules.js
@@ -0,0 +1,16 @@
+import Route from 'consul-ui/routing/route';
+
+export default class BindingRulesRoute extends Route {
+ model() {
+ const parent = this.routeName
+ .split('.')
+ .slice(0, -1)
+ .join('.');
+ return this.modelFor(parent);
+ }
+
+ setupController(controller, model) {
+ super.setupController(...arguments);
+ controller.setProperties(model);
+ }
+}
diff --git a/ui/packages/consul-ui/app/serializers/binding-rule.js b/ui/packages/consul-ui/app/serializers/binding-rule.js
new file mode 100644
index 000000000..fce850715
--- /dev/null
+++ b/ui/packages/consul-ui/app/serializers/binding-rule.js
@@ -0,0 +1,7 @@
+import Serializer from './application';
+import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/binding-rule';
+
+export default class BindingRuleSerializer extends Serializer {
+ primaryKey = PRIMARY_KEY;
+ slugKey = SLUG_KEY;
+}
diff --git a/ui/packages/consul-ui/app/services/repository/binding-rule.js b/ui/packages/consul-ui/app/services/repository/binding-rule.js
new file mode 100644
index 000000000..f2b4c58a3
--- /dev/null
+++ b/ui/packages/consul-ui/app/services/repository/binding-rule.js
@@ -0,0 +1,32 @@
+import RepositoryService from 'consul-ui/services/repository';
+import statusFactory from 'consul-ui/utils/acls-status';
+import isValidServerErrorFactory from 'consul-ui/utils/http/acl/is-valid-server-error';
+import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/binding-rule';
+import dataSource from 'consul-ui/decorators/data-source';
+
+const isValidServerError = isValidServerErrorFactory();
+const status = statusFactory(isValidServerError, Promise);
+const MODEL_NAME = 'binding-rule';
+
+export default class BindingRuleService extends RepositoryService {
+ getModelName() {
+ return MODEL_NAME;
+ }
+
+ getPrimaryKey() {
+ return PRIMARY_KEY;
+ }
+
+ getSlugKey() {
+ return SLUG_KEY;
+ }
+
+ @dataSource('/:ns/:dc/binding-rules')
+ async findAllByDatacenter() {
+ return super.findAllByDatacenter(...arguments);
+ }
+
+ status(obj) {
+ return status(obj);
+ }
+}
diff --git a/ui/packages/consul-ui/app/styles/base/reset/system.scss b/ui/packages/consul-ui/app/styles/base/reset/system.scss
index 26c58a311..b44318a6f 100644
--- a/ui/packages/consul-ui/app/styles/base/reset/system.scss
+++ b/ui/packages/consul-ui/app/styles/base/reset/system.scss
@@ -104,4 +104,5 @@ html {
hr {
height: 1px;
margin: 1.5rem 0;
+ background-color: var(--gray-200);
}
diff --git a/ui/packages/consul-ui/app/styles/components.scss b/ui/packages/consul-ui/app/styles/components.scss
index 673cbe77f..ada90c902 100644
--- a/ui/packages/consul-ui/app/styles/components.scss
+++ b/ui/packages/consul-ui/app/styles/components.scss
@@ -33,6 +33,7 @@
@import './components/more-popover-menu';
@import './components/definition-table';
@import './components/radio-card';
+@import './components/tabular-dl';
/**/
diff --git a/ui/packages/consul-ui/app/styles/components/tabular-dl/index.scss b/ui/packages/consul-ui/app/styles/components/tabular-dl/index.scss
new file mode 100644
index 000000000..62cfad5c0
--- /dev/null
+++ b/ui/packages/consul-ui/app/styles/components/tabular-dl/index.scss
@@ -0,0 +1,2 @@
+@import './layout';
+@import './skin';
diff --git a/ui/packages/consul-ui/app/styles/components/tabular-dl/layout.scss b/ui/packages/consul-ui/app/styles/components/tabular-dl/layout.scss
new file mode 100644
index 000000000..edeb482a4
--- /dev/null
+++ b/ui/packages/consul-ui/app/styles/components/tabular-dl/layout.scss
@@ -0,0 +1,38 @@
+%tabular-dl {
+ display: flex;
+ flex-wrap: wrap;
+ > dt:last-of-type,
+ > dd:last-of-type {
+ border-bottom: 1px solid !important;
+ }
+ dt,
+ dd {
+ padding: 12px 0;
+ margin: 0;
+ border-top: 1px solid !important;
+ }
+ dt {
+ width: 20%;
+ }
+ dd {
+ margin-left: auto;
+ width: 80%;
+ display: flex;
+ }
+ dd > ul li {
+ display: flex;
+ }
+ dd > ul li:not(:last-of-type) {
+ padding-bottom: 12px;
+ }
+ dd .copy-button button {
+ padding: 0 !important;
+ margin: 0 4px 0 0 !important;
+ }
+ dt.check + dd {
+ padding-top: 16px;
+ }
+ dt.type + dd span::before {
+ margin-left: 4px;
+ }
+}
diff --git a/ui/packages/consul-ui/app/styles/components/tabular-dl/skin.scss b/ui/packages/consul-ui/app/styles/components/tabular-dl/skin.scss
new file mode 100644
index 000000000..71de9caf5
--- /dev/null
+++ b/ui/packages/consul-ui/app/styles/components/tabular-dl/skin.scss
@@ -0,0 +1,21 @@
+%tabular-dl {
+ > dt:last-of-type,
+ > dd:last-of-type {
+ border-color: var(--gray-300) !important;
+ }
+ dt,
+ dd {
+ border-color: var(--gray-300) !important;
+ color: $black !important;
+ }
+ dt {
+ font-weight: $typo-weight-bold;
+ }
+ dd .copy-button button::before {
+ background-color: $black;
+ }
+ dt.type + dd span::before {
+ @extend %with-info-circle-outline-mask, %as-pseudo;
+ background-color: var(--gray-500);
+ }
+}
diff --git a/ui/packages/consul-ui/app/templates/dc/acls/auth-methods/show.hbs b/ui/packages/consul-ui/app/templates/dc/acls/auth-methods/show.hbs
index 0b5889978..52e86479c 100644
--- a/ui/packages/consul-ui/app/templates/dc/acls/auth-methods/show.hbs
+++ b/ui/packages/consul-ui/app/templates/dc/acls/auth-methods/show.hbs
@@ -27,6 +27,7 @@
compact
(array
(hash label="General info" href=(href-to "dc.acls.auth-methods.show.auth-method") selected=(is-href "dc.acls.auth-methods.show.auth-method"))
+ (hash label="Binding rules" href=(href-to "dc.acls.auth-methods.show.binding-rules") selected=(is-href "dc.acls.auth-methods.show.binding-rules"))
)
}}/>
diff --git a/ui/packages/consul-ui/app/templates/dc/acls/auth-methods/show/binding-rules.hbs b/ui/packages/consul-ui/app/templates/dc/acls/auth-methods/show/binding-rules.hbs
new file mode 100644
index 000000000..84cc12520
--- /dev/null
+++ b/ui/packages/consul-ui/app/templates/dc/acls/auth-methods/show/binding-rules.hbs
@@ -0,0 +1,28 @@
+
+
+{{#if (gt bindingRules.length 0)}}
+
Binding rules allow an operator to express a systematic way of automatically linking roles and service identities to newly created tokens without operator intervention.
+
+
Successful authentication with an auth method returns a set of trusted identity attributes corresponding to the authenticated identity. Those attributes are matched against all configured binding rules for that auth method to determine what privileges to grant the Consul ACL token it will ultimately create.
+
+
+ {{#each bindingRules as |item|}}
+
+
+ {{/each}}
+{{else}}
+
+
+ No binding rules
+
+
+ Binding rules allow an operator to express a systematic way of automatically linking roles and service identities to newly created tokens without operator intervention.
+
+
+
+ Read the documentation
+
+
+
+{{/if}}
+
diff --git a/ui/packages/consul-ui/mock-api/v1/acl/binding-rules b/ui/packages/consul-ui/mock-api/v1/acl/binding-rules
new file mode 100644
index 000000000..c563a280f
--- /dev/null
+++ b/ui/packages/consul-ui/mock-api/v1/acl/binding-rules
@@ -0,0 +1,30 @@
+[
+ ${
+ range(
+ env(
+ 'CONSUL_BINDING_RULE_COUNT',
+ Math.floor(
+ (
+ Math.random() * env('CONSUL_BINDING_RULE_MAX', 10)
+ ) + parseInt(env('CONSUL_BINDING_RULE_MIN', 1))
+ )
+ )
+ ).map(
+ function(item, i) {
+ return `
+ {
+ "ID": "${fake.random.uuid()}",
+ "Description": "${fake.lorem.sentence()}",
+ "AuthMethod": "${fake.hacker.noun()}-${i}",
+ "Selector": "serviceaccount.namespace==${fake.hacker.noun()} and serviceaccount.name!=${fake.hacker.noun()}",
+ "BindType": "${fake.helpers.randomize(['service', 'node', 'role'])}",
+ "BindName": "${fake.hacker.noun()}-${i}",
+ "Namespace": "${location.search.ns}",
+ "CreateIndex": ${fake.random.number()},
+ "ModifyIndex": 10
+ }
+ `
+ }
+ )
+ }
+]
diff --git a/ui/packages/consul-ui/tests/integration/adapters/binding-rule-test.js b/ui/packages/consul-ui/tests/integration/adapters/binding-rule-test.js
new file mode 100644
index 000000000..08f64d207
--- /dev/null
+++ b/ui/packages/consul-ui/tests/integration/adapters/binding-rule-test.js
@@ -0,0 +1,38 @@
+import { module, test } from 'qunit';
+import { setupTest } from 'ember-qunit';
+import getNspaceRunner from 'consul-ui/tests/helpers/get-nspace-runner';
+
+const nspaceRunner = getNspaceRunner('binding-rule');
+module('Integration | Adapter | binding-rule', function(hooks) {
+ setupTest(hooks);
+ const dc = 'dc-1';
+ test('requestForQuery returns the correct url/method', function(assert) {
+ const adapter = this.owner.lookup('adapter:binding-rule');
+ const client = this.owner.lookup('service:client/http');
+ const expected = `GET /v1/acl/binding-rules?dc=${dc}`;
+ const actual = adapter.requestForQuery(client.requestParams.bind(client), {
+ dc: dc,
+ });
+ assert.equal(`${actual.method} ${actual.url}`, expected);
+ });
+ test('requestForQuery returns the correct body', function(assert) {
+ return nspaceRunner(
+ (adapter, serializer, client) => {
+ return adapter.requestForQuery(client.body, {
+ dc: dc,
+ ns: 'team-1',
+ index: 1,
+ });
+ },
+ {
+ index: 1,
+ ns: 'team-1',
+ },
+ {
+ index: 1,
+ },
+ this,
+ assert
+ );
+ });
+});
diff --git a/ui/packages/consul-ui/tests/integration/serializers/binding-rule-test.js b/ui/packages/consul-ui/tests/integration/serializers/binding-rule-test.js
new file mode 100644
index 000000000..f4730ee13
--- /dev/null
+++ b/ui/packages/consul-ui/tests/integration/serializers/binding-rule-test.js
@@ -0,0 +1,39 @@
+import { module, test } from 'qunit';
+import { setupTest } from 'ember-qunit';
+import { get } from 'consul-ui/tests/helpers/api';
+module('Integration | Serializer | binding-rule', function(hooks) {
+ setupTest(hooks);
+ const dc = 'dc-1';
+ const undefinedNspace = 'default';
+ [undefinedNspace, 'team-1', undefined].forEach(nspace => {
+ test(`respondForQuery returns the correct data for list endpoint when nspace is ${nspace}`, function(assert) {
+ const serializer = this.owner.lookup('serializer:binding-rule');
+ const request = {
+ url: `/v1/acl/binding-rules?dc=${dc}${
+ typeof nspace !== 'undefined' ? `&ns=${nspace}` : ``
+ }`,
+ };
+ return get(request.url).then(function(payload) {
+ const expected = payload.map(item =>
+ Object.assign({}, item, {
+ Datacenter: dc,
+ Namespace: item.Namespace || undefinedNspace,
+ uid: `["${item.Namespace || undefinedNspace}","${dc}","${item.ID}"]`,
+ })
+ );
+ const actual = serializer.respondForQuery(
+ function(cb) {
+ const headers = {};
+ const body = payload;
+ return cb(headers, body);
+ },
+ {
+ dc: dc,
+ ns: nspace,
+ }
+ );
+ assert.deepEqual(actual, expected);
+ });
+ });
+ });
+});
diff --git a/ui/packages/consul-ui/tests/unit/adapters/binding-rule-test.js b/ui/packages/consul-ui/tests/unit/adapters/binding-rule-test.js
new file mode 100644
index 000000000..a726f13ae
--- /dev/null
+++ b/ui/packages/consul-ui/tests/unit/adapters/binding-rule-test.js
@@ -0,0 +1,12 @@
+import { module, test } from 'qunit';
+import { setupTest } from 'ember-qunit';
+
+module('Unit | Adapter | binding-rule', function(hooks) {
+ setupTest(hooks);
+
+ // Replace this with your real tests.
+ test('it exists', function(assert) {
+ let adapter = this.owner.lookup('adapter:binding-rule');
+ assert.ok(adapter);
+ });
+});
diff --git a/ui/packages/consul-ui/tests/unit/serializers/binding-rule-test.js b/ui/packages/consul-ui/tests/unit/serializers/binding-rule-test.js
new file mode 100644
index 000000000..a4e0f0ac8
--- /dev/null
+++ b/ui/packages/consul-ui/tests/unit/serializers/binding-rule-test.js
@@ -0,0 +1,23 @@
+import { module, test } from 'qunit';
+import { setupTest } from 'ember-qunit';
+
+module('Unit | Serializer | binding-rule', function(hooks) {
+ setupTest(hooks);
+
+ // Replace this with your real tests.
+ test('it exists', function(assert) {
+ let store = this.owner.lookup('service:store');
+ let serializer = store.serializerFor('binding-rule');
+
+ assert.ok(serializer);
+ });
+
+ test('it serializes records', function(assert) {
+ let store = this.owner.lookup('service:store');
+ let record = store.createRecord('binding-rule', {});
+
+ let serializedRecord = record.serialize();
+
+ assert.ok(serializedRecord);
+ });
+});
diff --git a/ui/packages/consul-ui/translations/en-us.yaml b/ui/packages/consul-ui/translations/en-us.yaml
index 3262c5a31..85a44a0b1 100644
--- a/ui/packages/consul-ui/translations/en-us.yaml
+++ b/ui/packages/consul-ui/translations/en-us.yaml
@@ -143,6 +143,11 @@ components:
options:
local: Creates local tokens
global: Creates global tokens
+ binding-list:
+ bind-type:
+ service: The bind name value is used as an ACLServiceIdentity.ServiceName field in the token that is created.
+ node: The bind name value is used as an ACLNodeIdentity.NodeName field in the token that is created.
+ role: The bind name value is used as an RoleLink.Name field in the token that is created.
kv:
search-bar:
kind:
@@ -209,6 +214,11 @@ models:
VerboseOIDCLogging: Verbose OIDC logging
ClaimMappings: Claim Mappings
ListClaimMappings: List Claim Mappings
+ binding-rule:
+ BindType: Type
+ Description: Description
+ Selector: Selector
+