diff --git a/builtin/logical/ssh/path_roles.go b/builtin/logical/ssh/path_roles.go
index ce3fb24756..450d768447 100644
--- a/builtin/logical/ssh/path_roles.go
+++ b/builtin/logical/ssh/path_roles.go
@@ -218,7 +218,7 @@ func pathRoles(b *backend) *framework.Path {
The maximum allowed lease duration
`,
DisplayAttrs: &framework.DisplayAttributes{
- Value: "Max TTL",
+ Name: "Max TTL",
},
},
"allowed_critical_options": &framework.FieldSchema{
diff --git a/ui/app/adapters/kmip/base.js b/ui/app/adapters/kmip/base.js
new file mode 100644
index 0000000000..174249e534
--- /dev/null
+++ b/ui/app/adapters/kmip/base.js
@@ -0,0 +1,59 @@
+import ApplicationAdapter from '../application';
+import { encodePath } from 'vault/utils/path-encoding-helpers';
+
+export default ApplicationAdapter.extend({
+ namespace: 'v1',
+ pathForType(type) {
+ return type.replace('kmip/', '');
+ },
+
+ _url(modelType, meta = {}, id) {
+ let { backend, scope, role } = meta;
+ let type = this.pathForType(modelType);
+ let base;
+ switch (type) {
+ case 'scope':
+ base = `${encodePath(backend)}/scope`;
+ break;
+ case 'role':
+ base = `${encodePath(backend)}/scope/${encodePath(scope)}/role`;
+ break;
+ case 'credential':
+ base = `${encodePath(backend)}/scope/${encodePath(scope)}/role/${encodePath(role)}/credential`;
+ break;
+ }
+ if (id && type === 'credential') {
+ return `/v1/${base}/lookup?serial_number=${encodePath(id)}`;
+ }
+
+ if (id) {
+ return `/v1/${base}/${encodePath(id)}`;
+ }
+ return `/v1/${base}`;
+ },
+
+ urlForQuery(query, modelType) {
+ let base = this._url(modelType, query);
+ return base + '?list=true';
+ },
+
+ query(store, type, query) {
+ return this.ajax(this.urlForQuery(query, type.modelName), 'GET');
+ },
+
+ queryRecord(store, type, query) {
+ let id = query.id;
+ delete query.id;
+ return this.ajax(this._url(type.modelName, query, id), 'GET').then(resp => {
+ resp.id = id;
+ resp = { ...resp, ...query };
+ return resp;
+ });
+ },
+ buildURL(modelName, id, snapshot, requestType, query) {
+ if (requestType === 'createRecord') {
+ return this._super(...arguments);
+ }
+ return this._super(`${modelName}`, id, snapshot, requestType, query);
+ },
+});
diff --git a/ui/app/adapters/kmip/config.js b/ui/app/adapters/kmip/config.js
new file mode 100644
index 0000000000..32f27803f8
--- /dev/null
+++ b/ui/app/adapters/kmip/config.js
@@ -0,0 +1,19 @@
+import BaseAdapter from './base';
+
+export default BaseAdapter.extend({
+ _url(id, modelName, snapshot) {
+ let name = this.pathForType(modelName);
+ // id here will be the mount path,
+ // modelName will be config so we want to transpose the first two call args
+ return this.buildURL(id, name, snapshot);
+ },
+ urlForFindRecord() {
+ return this._url(...arguments);
+ },
+ urlForCreateRecord(modelName, snapshot) {
+ return this._url(snapshot.id, modelName, snapshot);
+ },
+ urlForUpdateRecord() {
+ return this._url(...arguments);
+ },
+});
diff --git a/ui/app/adapters/kmip/credential.js b/ui/app/adapters/kmip/credential.js
new file mode 100644
index 0000000000..b485d1b26c
--- /dev/null
+++ b/ui/app/adapters/kmip/credential.js
@@ -0,0 +1,16 @@
+import BaseAdapter from './base';
+
+export default BaseAdapter.extend({
+ createRecord(store, type, snapshot) {
+ let url = this._url(type.modelName, {
+ backend: snapshot.record.backend,
+ scope: snapshot.record.scope,
+ role: snapshot.record.role,
+ });
+ url = `${url}/generate`;
+ return this.ajax(url, 'POST', { data: snapshot.serialize() }).then(model => {
+ model.data.id = model.data.serial_number;
+ return model;
+ });
+ },
+});
diff --git a/ui/app/adapters/kmip/role.js b/ui/app/adapters/kmip/role.js
new file mode 100644
index 0000000000..0777e85518
--- /dev/null
+++ b/ui/app/adapters/kmip/role.js
@@ -0,0 +1,25 @@
+import BaseAdapter from './base';
+
+export default BaseAdapter.extend({
+ createRecord(store, type, snapshot) {
+ let name = snapshot.id || snapshot.attr('name');
+ let url = this._url(
+ type.modelName,
+ {
+ backend: snapshot.record.backend,
+ scope: snapshot.record.scope,
+ },
+ name
+ );
+ return this.ajax(url, 'POST', { data: snapshot.serialize() }).then(() => {
+ return {
+ id: name,
+ name,
+ };
+ });
+ },
+
+ updateRecord() {
+ return this.createRecord(...arguments);
+ },
+});
diff --git a/ui/app/adapters/kmip/scope.js b/ui/app/adapters/kmip/scope.js
new file mode 100644
index 0000000000..db3f27374b
--- /dev/null
+++ b/ui/app/adapters/kmip/scope.js
@@ -0,0 +1,15 @@
+import BaseAdapter from './base';
+
+export default BaseAdapter.extend({
+ createRecord(store, type, snapshot) {
+ let name = snapshot.attr('name');
+ return this.ajax(this._url(type.modelName, { backend: snapshot.record.backend }, name), 'POST').then(
+ () => {
+ return {
+ id: name,
+ name,
+ };
+ }
+ );
+ },
+});
diff --git a/ui/app/app.js b/ui/app/app.js
index a215b634c9..b8675a7383 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -28,6 +28,24 @@ App = Application.extend({
],
},
},
+ kmip: {
+ dependencies: {
+ services: [
+ 'auth',
+ 'flash-messages',
+ 'namespace',
+ 'path-help',
+ 'router',
+ 'store',
+ 'version',
+ 'wizard',
+ 'secret-mount-path',
+ ],
+ externalRoutes: {
+ secrets: 'vault.cluster.secrets.backends',
+ },
+ },
+ },
},
});
diff --git a/ui/app/components/empty-action-namespaces.js b/ui/app/components/empty-action-namespaces.js
deleted file mode 100644
index 96167992d7..0000000000
--- a/ui/app/components/empty-action-namespaces.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import OuterHTML from './outer-html';
-export default OuterHTML.extend();
diff --git a/ui/app/components/field-group-show.js b/ui/app/components/field-group-show.js
deleted file mode 100644
index f045077d06..0000000000
--- a/ui/app/components/field-group-show.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * @module FieldGroupShow
- * FieldGroupShow components loop through a Model's fieldGroups
- * to display their attributes
- *
- * @example
- * ```js
- *
+ ENTER to go to {{this.filter}} roles
+
+ TAB to complete {{firstPartialMatch.id}}
+
{{chain}}
+ {{/each}}
+
+ ENTER to go to {{this.filter}} roles
+
+ TAB to complete {{firstPartialMatch.id}}
+
+ ENTER to go to {{this.filter}}
+
+ TAB to complete {{firstPartialMatch.id}}
+