remove transform (ENT)
This commit is contained in:
parent
ffc930c08a
commit
a0f77417d5
@ -671,21 +671,6 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) map[string]cli.Co
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"transform": func() (cli.Command, error) {
|
||||
return &TransformCommand{
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"transform import": func() (cli.Command, error) {
|
||||
return &TransformImportCommand{
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"transform import-version": func() (cli.Command, error) {
|
||||
return &TransformImportVersionCommand{
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"transit": func() (cli.Command, error) {
|
||||
return &TransitCommand{
|
||||
BaseCommand: getBaseCommand(),
|
||||
|
@ -1,44 +0,0 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
||||
var _ cli.Command = (*TransformCommand)(nil)
|
||||
|
||||
type TransformCommand struct {
|
||||
*BaseCommand
|
||||
}
|
||||
|
||||
func (c *TransformCommand) Synopsis() string {
|
||||
return "Interact with Vault's Transform Secrets Engine"
|
||||
}
|
||||
|
||||
func (c *TransformCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: vault transform <subcommand> [options] [args]
|
||||
|
||||
This command has subcommands for interacting with Vault's Transform Secrets
|
||||
Engine. Here are some simple examples, and more detailed examples are
|
||||
available in the subcommands or the documentation.
|
||||
|
||||
To import a key into a new FPE transformation:
|
||||
|
||||
$ vault transform import transform/transformations/fpe/new-transformation @path/to/key \
|
||||
template=identifier \
|
||||
allowed_roles=physical-access
|
||||
|
||||
Please see the individual subcommand help for detailed usage information.
|
||||
`
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (c *TransformCommand) Run(args []string) int {
|
||||
return cli.RunResultHelp
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
var (
|
||||
_ cli.Command = (*TransformImportCommand)(nil)
|
||||
_ cli.CommandAutocomplete = (*TransformImportCommand)(nil)
|
||||
transformKeyPath = regexp.MustCompile("^(.*)/transformations/(fpe|tokenization)/([^/]*)$")
|
||||
)
|
||||
|
||||
type TransformImportCommand struct {
|
||||
*BaseCommand
|
||||
}
|
||||
|
||||
func (c *TransformImportCommand) Synopsis() string {
|
||||
return "Import a key into the Transform secrets engines."
|
||||
}
|
||||
|
||||
func (c *TransformImportCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: vault transform import PATH KEY [options...]
|
||||
|
||||
Using the Transform key wrapping system, imports key material from
|
||||
the base64 encoded KEY (either directly on the CLI or via @path notation),
|
||||
into a new FPE or tokenization transformation whose API path is PATH.
|
||||
|
||||
To import a new key version into an existing tokenization transformation,
|
||||
use import_version.
|
||||
|
||||
The remaining options after KEY (key=value style) are passed on to
|
||||
Create/Update FPE Transformation or Create/Update Tokenization Transformation
|
||||
API endpoints.
|
||||
|
||||
For example:
|
||||
$ vault transform import transform/transformations/tokenization/application-form @path/to/key \
|
||||
allowed_roles=legacy-system
|
||||
` + c.Flags().Help()
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (c *TransformImportCommand) Flags() *FlagSets {
|
||||
return c.flagSet(FlagSetHTTP)
|
||||
}
|
||||
|
||||
func (c *TransformImportCommand) AutocompleteArgs() complete.Predictor {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *TransformImportCommand) AutocompleteFlags() complete.Flags {
|
||||
return c.Flags().Completions()
|
||||
}
|
||||
|
||||
func (c *TransformImportCommand) Run(args []string) int {
|
||||
return ImportKey(c.BaseCommand, "import", transformImportKeyPath, c.Flags(), args)
|
||||
}
|
||||
|
||||
func transformImportKeyPath(s string, operation string) (path string, apiPath string, err error) {
|
||||
parts := transformKeyPath.FindStringSubmatch(s)
|
||||
if len(parts) != 4 {
|
||||
return "", "", errors.New("expected transform path and key name in the form :path:/transformations/fpe|tokenization/:name:")
|
||||
}
|
||||
path = parts[1]
|
||||
transformation := parts[2]
|
||||
keyName := parts[3]
|
||||
apiPath = path + "/transformations/" + transformation + "/" + keyName + "/" + operation
|
||||
|
||||
return path, apiPath, nil
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
var (
|
||||
_ cli.Command = (*TransformImportVersionCommand)(nil)
|
||||
_ cli.CommandAutocomplete = (*TransformImportVersionCommand)(nil)
|
||||
)
|
||||
|
||||
type TransformImportVersionCommand struct {
|
||||
*BaseCommand
|
||||
}
|
||||
|
||||
func (c *TransformImportVersionCommand) Synopsis() string {
|
||||
return "Import key material into a new key version in the Transform secrets engines."
|
||||
}
|
||||
|
||||
func (c *TransformImportVersionCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: vault transform import-version PATH KEY [...]
|
||||
|
||||
Using the Transform key wrapping system, imports new key material from
|
||||
the base64 encoded KEY (either directly on the CLI or via @path notation),
|
||||
into an existing tokenization transformation whose API path is PATH.
|
||||
|
||||
The remaining options after KEY (key=value style) are passed on to
|
||||
Create/Update Tokenization Transformation API endpoint.
|
||||
|
||||
For example:
|
||||
$ vault transform import-version transform/transformations/tokenization/application-form @path/to/new_version \
|
||||
allowed_roles=legacy-system
|
||||
` + c.Flags().Help()
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (c *TransformImportVersionCommand) Flags() *FlagSets {
|
||||
return c.flagSet(FlagSetHTTP)
|
||||
}
|
||||
|
||||
func (c *TransformImportVersionCommand) AutocompleteArgs() complete.Predictor {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *TransformImportVersionCommand) AutocompleteFlags() complete.Flags {
|
||||
return c.Flags().Completions()
|
||||
}
|
||||
|
||||
func (c *TransformImportVersionCommand) Run(args []string) int {
|
||||
return ImportKey(c.BaseCommand, "import_version", transformImportKeyPath, c.Flags(), args)
|
||||
}
|
@ -76,7 +76,6 @@ vault secrets enable "transit"
|
||||
# Enable enterprise features
|
||||
if [[ -n "${VAULT_LICENSE:-}" ]]; then
|
||||
vault secrets enable "keymgmt"
|
||||
vault secrets enable "transform"
|
||||
fi
|
||||
|
||||
# Output OpenAPI, optionally formatted
|
||||
|
@ -1,102 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { assign } from '@ember/polyfills';
|
||||
import { allSettled } from 'rsvp';
|
||||
import ApplicationAdapter from './application';
|
||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||
|
||||
export default ApplicationAdapter.extend({
|
||||
namespace: 'v1',
|
||||
|
||||
createOrUpdate(store, type, snapshot) {
|
||||
const { backend, name } = snapshot.record;
|
||||
const serializer = store.serializerFor(type.modelName);
|
||||
const data = serializer.serialize(snapshot);
|
||||
const url = this.urlForTransformations(backend, name);
|
||||
|
||||
return this.ajax(url, 'POST', { data }).then((resp) => {
|
||||
const response = resp || {};
|
||||
response.id = name;
|
||||
return response;
|
||||
});
|
||||
},
|
||||
|
||||
createRecord() {
|
||||
return this.createOrUpdate(...arguments);
|
||||
},
|
||||
|
||||
updateRecord() {
|
||||
return this.createOrUpdate(...arguments, 'update');
|
||||
},
|
||||
|
||||
deleteRecord(store, type, snapshot) {
|
||||
const { id } = snapshot;
|
||||
return this.ajax(this.urlForTransformations(snapshot.record.get('backend'), id), 'DELETE');
|
||||
},
|
||||
|
||||
pathForType() {
|
||||
return 'transform';
|
||||
},
|
||||
|
||||
urlForTransformations(backend, id) {
|
||||
let url = `${this.buildURL()}/${encodePath(backend)}/transformation`;
|
||||
if (id) {
|
||||
url = url + '/' + encodePath(id);
|
||||
}
|
||||
return url;
|
||||
},
|
||||
|
||||
optionsForQuery(id) {
|
||||
const data = {};
|
||||
if (!id) {
|
||||
data['list'] = true;
|
||||
}
|
||||
return { data };
|
||||
},
|
||||
|
||||
fetchByQuery(store, query) {
|
||||
const { id, backend } = query;
|
||||
const queryAjax = this.ajax(this.urlForTransformations(backend, id), 'GET', this.optionsForQuery(id));
|
||||
|
||||
return allSettled([queryAjax]).then((results) => {
|
||||
// query result 404d, so throw the adapterError
|
||||
if (!results[0].value) {
|
||||
throw results[0].reason;
|
||||
}
|
||||
const resp = {
|
||||
id,
|
||||
name: id,
|
||||
backend,
|
||||
data: {},
|
||||
};
|
||||
|
||||
results.forEach((result) => {
|
||||
if (result.value) {
|
||||
let d = result.value.data;
|
||||
if (d.templates) {
|
||||
// In Transformations data goes up as "template", but comes down as "templates"
|
||||
// To keep the keys consistent we're translating here
|
||||
d = {
|
||||
...d,
|
||||
template: d.templates,
|
||||
};
|
||||
delete d.templates;
|
||||
}
|
||||
resp.data = assign({}, resp.data, d);
|
||||
}
|
||||
});
|
||||
return resp;
|
||||
});
|
||||
},
|
||||
|
||||
query(store, type, query) {
|
||||
return this.fetchByQuery(store, query);
|
||||
},
|
||||
|
||||
queryRecord(store, type, query) {
|
||||
return this.fetchByQuery(store, query);
|
||||
},
|
||||
});
|
@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import BaseAdapter from './base';
|
||||
|
||||
export default BaseAdapter.extend({
|
||||
pathForType() {
|
||||
return 'alphabet';
|
||||
},
|
||||
});
|
@ -1,77 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import ApplicationAdapter from '../application';
|
||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||
|
||||
export default ApplicationAdapter.extend({
|
||||
namespace: 'v1',
|
||||
|
||||
pathForType(type) {
|
||||
return type.replace('transform/', '');
|
||||
},
|
||||
|
||||
createOrUpdate(store, type, snapshot) {
|
||||
const { backend, name } = snapshot.record;
|
||||
const serializer = store.serializerFor(type.modelName);
|
||||
const data = serializer.serialize(snapshot);
|
||||
const url = this.url(backend, type.modelName, name);
|
||||
return this.ajax(url, 'POST', { data }).then((resp) => {
|
||||
// Ember data doesn't like 204 responses except for DELETE method
|
||||
const response = resp || { data: {} };
|
||||
response.data.name = name;
|
||||
return response;
|
||||
});
|
||||
},
|
||||
|
||||
createRecord() {
|
||||
return this.createOrUpdate(...arguments);
|
||||
},
|
||||
|
||||
updateRecord() {
|
||||
return this.createOrUpdate(...arguments, 'update');
|
||||
},
|
||||
|
||||
deleteRecord(store, type, snapshot) {
|
||||
const { id } = snapshot;
|
||||
return this.ajax(this.url(snapshot.record.get('backend'), type.modelName, id), 'DELETE');
|
||||
},
|
||||
|
||||
url(backend, modelType, id) {
|
||||
const type = this.pathForType(modelType);
|
||||
const url = `/${this.namespace}/${encodePath(backend)}/${encodePath(type)}`;
|
||||
if (id) {
|
||||
return `${url}/${encodePath(id)}`;
|
||||
}
|
||||
return url + '?list=true';
|
||||
},
|
||||
|
||||
fetchByQuery(query) {
|
||||
const { backend, modelName, id } = query;
|
||||
return this.ajax(this.url(backend, modelName, id), 'GET').then((resp) => {
|
||||
// The API response doesn't explicitly include the name/id, so add it here
|
||||
return {
|
||||
...resp,
|
||||
backend,
|
||||
id,
|
||||
name: id,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
query(store, type, query) {
|
||||
return this.fetchByQuery(query);
|
||||
},
|
||||
|
||||
queryRecord(store, type, query) {
|
||||
return this.ajax(this.url(query.backend, type.modelName, query.id), 'GET').then((result) => {
|
||||
return {
|
||||
id: query.id,
|
||||
name: query.id,
|
||||
...result,
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import BaseAdapter from './base';
|
||||
|
||||
export default BaseAdapter.extend({
|
||||
pathForType() {
|
||||
return 'role';
|
||||
},
|
||||
});
|
@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import BaseAdapter from './base';
|
||||
|
||||
export default BaseAdapter.extend({
|
||||
pathForType() {
|
||||
return 'template';
|
||||
},
|
||||
});
|
@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import BaseAdapter from './base';
|
||||
|
||||
export default BaseAdapter.extend({
|
||||
// custom stuff for transformation
|
||||
});
|
@ -1,8 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import TransformBase from './transform-edit-base';
|
||||
|
||||
export default TransformBase.extend({});
|
@ -18,7 +18,7 @@
|
||||
@onRadioChange={{mut this.selection}}
|
||||
@disabled={{if type.requiredFeature (not (has-feature type.requiredFeature)) false}}
|
||||
@tooltipMessage={{if
|
||||
(or (eq type.type "transform") (eq type.type "keymgmt"))
|
||||
(eq type.type "keymgmt")
|
||||
(concat
|
||||
type.displayName
|
||||
" is part of the Advanced Data Protection module, which is not included in your enterprise license."
|
||||
|
@ -1,37 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module TransformListItem
|
||||
* TransformListItem components are used for the list items for the Transform Secret Engines for all but Transformations.
|
||||
* This component automatically handles read-only list items if capabilities are not granted or the item is internal only.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* <TransformListItem @item={item} @itemPath="role/my-item" @itemType="role" />
|
||||
* ```
|
||||
* @param {object} item - item refers to the model item used on the list item partial
|
||||
* @param {string} itemPath - usually the id of the item, but can be prefixed with the model type (see transform/role)
|
||||
* @param {string} [itemType] - itemType is used to calculate whether an item is readable or
|
||||
*/
|
||||
|
||||
import { computed } from '@ember/object';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
item: null,
|
||||
itemPath: '',
|
||||
itemType: '',
|
||||
|
||||
isBuiltin: computed('item', 'itemType', function () {
|
||||
const item = this.item;
|
||||
if (this.itemType === 'alphabet' || this.itemType === 'template') {
|
||||
return item.get('id').startsWith('builtin/');
|
||||
}
|
||||
return false;
|
||||
}),
|
||||
|
||||
backendType: 'transform',
|
||||
});
|
@ -1,42 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action, set } from '@ember/object';
|
||||
|
||||
/**
|
||||
* @module TransformAdvancedTemplating
|
||||
* TransformAdvancedTemplating components are used to modify encode/decode formats of transform templates
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* <TransformAdvancedTemplating @model={{this.model}} />
|
||||
* ```
|
||||
* @param {Object} model - transform template model
|
||||
*/
|
||||
|
||||
export default class TransformAdvancedTemplating extends Component {
|
||||
@tracked inputOptions = [];
|
||||
|
||||
@action
|
||||
setInputOptions(testValue, captureGroups) {
|
||||
if (captureGroups && captureGroups.length) {
|
||||
this.inputOptions = captureGroups.map(({ position, value }) => {
|
||||
return {
|
||||
label: `${position}: ${value}`,
|
||||
value: position,
|
||||
};
|
||||
});
|
||||
} else {
|
||||
this.inputOptions = [];
|
||||
}
|
||||
}
|
||||
@action
|
||||
decodeFormatValueChange(kvObject, kvData, value) {
|
||||
set(kvObject, 'value', value);
|
||||
this.args.model.decodeFormats = kvData.toJSON();
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import TransformationEdit from './transformation-edit';
|
||||
|
||||
export default TransformationEdit.extend({});
|
@ -1,133 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { inject as service } from '@ember/service';
|
||||
import { or } from '@ember/object/computed';
|
||||
import { isBlank } from '@ember/utils';
|
||||
import Component from '@ember/component';
|
||||
import { set } from '@ember/object';
|
||||
import FocusOnInsertMixin from 'vault/mixins/focus-on-insert';
|
||||
|
||||
const LIST_ROOT_ROUTE = 'vault.cluster.secrets.backend.list-root';
|
||||
const SHOW_ROUTE = 'vault.cluster.secrets.backend.show';
|
||||
|
||||
export const addToList = (list, itemToAdd) => {
|
||||
if (!list || !Array.isArray(list)) return list;
|
||||
list.push(itemToAdd);
|
||||
return list.uniq();
|
||||
};
|
||||
|
||||
export const removeFromList = (list, itemToRemove) => {
|
||||
if (!list) return list;
|
||||
const index = list.indexOf(itemToRemove);
|
||||
if (index < 0) return list;
|
||||
const newList = list.removeAt(index, 1);
|
||||
return newList.uniq();
|
||||
};
|
||||
|
||||
export default Component.extend(FocusOnInsertMixin, {
|
||||
store: service(),
|
||||
flashMessages: service(),
|
||||
router: service(),
|
||||
|
||||
mode: null,
|
||||
onDataChange() {},
|
||||
onRefresh() {},
|
||||
model: null,
|
||||
requestInFlight: or('model.isLoading', 'model.isReloading', 'model.isSaving'),
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.set('backendType', 'transform');
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
if (this.model && this.model.isError) {
|
||||
this.model.rollbackAttributes();
|
||||
}
|
||||
},
|
||||
|
||||
transitionToRoute() {
|
||||
this.router.transitionTo(...arguments);
|
||||
},
|
||||
|
||||
modelPrefixFromType(modelType) {
|
||||
let modelPrefix = '';
|
||||
if (modelType && modelType.startsWith('transform/')) {
|
||||
modelPrefix = `${modelType.replace('transform/', '')}/`;
|
||||
}
|
||||
return modelPrefix;
|
||||
},
|
||||
|
||||
listTabFromType(modelType) {
|
||||
let tab;
|
||||
if (modelType && modelType.startsWith('transform/')) {
|
||||
tab = `${modelType.replace('transform/', '')}`;
|
||||
}
|
||||
return tab;
|
||||
},
|
||||
|
||||
persist(method, successCallback) {
|
||||
const model = this.model;
|
||||
return model[method]()
|
||||
.then(() => {
|
||||
successCallback(model);
|
||||
})
|
||||
.catch((e) => {
|
||||
model.set('displayErrors', e.errors);
|
||||
throw e;
|
||||
});
|
||||
},
|
||||
|
||||
applyDelete(callback = () => {}) {
|
||||
const tab = this.listTabFromType(this.model.constructor.modelName);
|
||||
this.persist('destroyRecord', () => {
|
||||
this.hasDataChanges();
|
||||
callback();
|
||||
this.transitionToRoute(LIST_ROOT_ROUTE, { queryParams: { tab } });
|
||||
});
|
||||
},
|
||||
|
||||
applyChanges(type, callback = () => {}) {
|
||||
const modelId = this.model.id || this.model.name; // transform comes in as model.name
|
||||
const modelPrefix = this.modelPrefixFromType(this.model.constructor.modelName);
|
||||
// prevent from submitting if there's no key
|
||||
// maybe do something fancier later
|
||||
if (type === 'create' && isBlank(modelId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.persist('save', () => {
|
||||
this.hasDataChanges();
|
||||
callback();
|
||||
this.transitionToRoute(SHOW_ROUTE, `${modelPrefix}${modelId}`);
|
||||
});
|
||||
},
|
||||
|
||||
hasDataChanges() {
|
||||
this.onDataChange(this.model?.hasDirtyAttributes);
|
||||
},
|
||||
|
||||
actions: {
|
||||
createOrUpdate(type, event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.applyChanges(type);
|
||||
},
|
||||
|
||||
setValue(key, event) {
|
||||
set(this.model, key, event.target.checked);
|
||||
},
|
||||
|
||||
refresh() {
|
||||
this.onRefresh();
|
||||
},
|
||||
|
||||
delete() {
|
||||
this.applyDelete();
|
||||
},
|
||||
},
|
||||
});
|
@ -1,8 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import TransformationEdit from './transformation-edit';
|
||||
|
||||
export default TransformationEdit.extend({});
|
@ -1,128 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import TransformBase, { addToList, removeFromList } from './transform-edit-base';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default TransformBase.extend({
|
||||
flashMessages: service(),
|
||||
store: service(),
|
||||
initialTransformations: null,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.set('initialTransformations', this.model.transformations);
|
||||
},
|
||||
|
||||
handleUpdateTransformations(updateTransformations, roleId, type = 'update') {
|
||||
if (!updateTransformations) return;
|
||||
const backend = this.model.backend;
|
||||
const promises = updateTransformations.map((transform) => {
|
||||
return this.store
|
||||
.queryRecord('transform', {
|
||||
backend,
|
||||
id: transform.id,
|
||||
})
|
||||
.then(function (transformation) {
|
||||
let roles = transformation.allowed_roles;
|
||||
if (transform.action === 'ADD') {
|
||||
roles = addToList(roles, roleId);
|
||||
} else if (transform.action === 'REMOVE') {
|
||||
roles = removeFromList(roles, roleId);
|
||||
}
|
||||
|
||||
transformation.setProperties({
|
||||
backend,
|
||||
allowed_roles: roles,
|
||||
});
|
||||
|
||||
return transformation.save().catch((e) => {
|
||||
return { errorStatus: e.httpStatus, ...transform };
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Promise.all(promises).then((res) => {
|
||||
const hasError = res.find((r) => !!r.errorStatus);
|
||||
if (hasError) {
|
||||
const errorAdding = res.find((r) => r.errorStatus === 403 && r.action === 'ADD');
|
||||
const errorRemoving = res.find((r) => r.errorStatus === 403 && r.action === 'REMOVE');
|
||||
|
||||
let message =
|
||||
'The edits to this role were successful, but allowed_roles for its transformations was not edited due to a lack of permissions.';
|
||||
if (type === 'create') {
|
||||
message =
|
||||
'Transformations have been attached to this role, but the role was not added to those transformations’ allowed_roles due to a lack of permissions.';
|
||||
} else if (errorAdding && errorRemoving) {
|
||||
message =
|
||||
'This role was edited to both add and remove transformations; however, this role was not added or removed from those transformations’ allowed_roles due to a lack of permissions.';
|
||||
} else if (errorAdding) {
|
||||
message =
|
||||
'This role was edited to include new transformations, but this role was not added to those transformations’ allowed_roles due to a lack of permissions.';
|
||||
} else if (errorRemoving) {
|
||||
message =
|
||||
'This role was edited to remove transformations, but this role was not removed from those transformations’ allowed_roles due to a lack of permissions.';
|
||||
}
|
||||
this.flashMessages.info(message, {
|
||||
sticky: true,
|
||||
priority: 300,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
createOrUpdate(type, event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.applyChanges('save', () => {
|
||||
const roleId = this.model.id;
|
||||
const newModelTransformations = this.model.transformations;
|
||||
|
||||
if (!this.initialTransformations) {
|
||||
this.handleUpdateTransformations(
|
||||
newModelTransformations.map((t) => ({
|
||||
id: t,
|
||||
action: 'ADD',
|
||||
})),
|
||||
roleId,
|
||||
type
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const updateTransformations = [...newModelTransformations, ...this.initialTransformations]
|
||||
.map((t) => {
|
||||
if (this.initialTransformations.indexOf(t) < 0) {
|
||||
return {
|
||||
id: t,
|
||||
action: 'ADD',
|
||||
};
|
||||
}
|
||||
if (newModelTransformations.indexOf(t) < 0) {
|
||||
return {
|
||||
id: t,
|
||||
action: 'REMOVE',
|
||||
};
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter((t) => !!t);
|
||||
this.handleUpdateTransformations(updateTransformations, roleId);
|
||||
});
|
||||
},
|
||||
|
||||
delete() {
|
||||
const roleId = this.model?.id;
|
||||
const roleTransformations = this.model?.transformations || [];
|
||||
const updateTransformations = roleTransformations.map((t) => ({
|
||||
id: t,
|
||||
action: 'REMOVE',
|
||||
}));
|
||||
this.handleUpdateTransformations(updateTransformations, roleId);
|
||||
this.applyDelete();
|
||||
},
|
||||
},
|
||||
});
|
@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import TransformBase from './transform-edit-base';
|
||||
import { computed } from '@ember/object';
|
||||
|
||||
export default TransformBase.extend({
|
||||
cliCommand: computed('model.{allowed_roles,type,tweak_source}', function () {
|
||||
if (!this.model) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { type, allowed_roles, tweak_source, name } = this.model;
|
||||
const wildCardRole = allowed_roles.find((role) => role.includes('*'));
|
||||
|
||||
// values to be returned
|
||||
let role = '<choose a role>';
|
||||
const value = 'value=<enter your value here>';
|
||||
let tweak = '';
|
||||
|
||||
// determine the role
|
||||
if (allowed_roles.length === 1 && !wildCardRole) {
|
||||
role = allowed_roles[0];
|
||||
}
|
||||
// determine the tweak_source
|
||||
if (type === 'fpe' && tweak_source === 'supplied') {
|
||||
tweak = 'tweak=<enter your tweak>';
|
||||
}
|
||||
|
||||
return `${role} ${value} ${tweak} transformation=${name}`;
|
||||
}),
|
||||
});
|
@ -1,8 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import TransformBase from './transform-edit-base';
|
||||
|
||||
export default TransformBase.extend({});
|
@ -1,134 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import TransformBase, { addToList, removeFromList } from './transform-edit-base';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default TransformBase.extend({
|
||||
flashMessages: service(),
|
||||
store: service(),
|
||||
initialRoles: null,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
if (!this.model) return;
|
||||
this.set('initialRoles', this.model.allowed_roles);
|
||||
},
|
||||
|
||||
updateOrCreateRole(role, transformationId, backend) {
|
||||
return this.store
|
||||
.queryRecord('transform/role', {
|
||||
backend,
|
||||
id: role.id,
|
||||
})
|
||||
.then((roleStore) => {
|
||||
let transformations = roleStore.transformations;
|
||||
if (role.action === 'ADD') {
|
||||
transformations = addToList(transformations, transformationId);
|
||||
} else if (role.action === 'REMOVE') {
|
||||
transformations = removeFromList(transformations, transformationId);
|
||||
}
|
||||
roleStore.setProperties({
|
||||
backend,
|
||||
transformations,
|
||||
});
|
||||
return roleStore.save().catch((e) => {
|
||||
return {
|
||||
errorStatus: e.httpStatus,
|
||||
...role,
|
||||
};
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.httpStatus !== 403 && role.action === 'ADD') {
|
||||
// If role doesn't yet exist, create it with this transformation attached
|
||||
var newRole = this.store.createRecord('transform/role', {
|
||||
id: role.id,
|
||||
name: role.id,
|
||||
transformations: [transformationId],
|
||||
backend,
|
||||
});
|
||||
return newRole.save().catch((e) => {
|
||||
return {
|
||||
errorStatus: e.httpStatus,
|
||||
...role,
|
||||
action: 'CREATE',
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
...role,
|
||||
errorStatus: e.httpStatus,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
handleUpdateRoles(updateRoles, transformationId) {
|
||||
if (!updateRoles) return;
|
||||
const backend = this.model.backend;
|
||||
const promises = updateRoles.map((r) => this.updateOrCreateRole(r, transformationId, backend));
|
||||
|
||||
Promise.all(promises).then((results) => {
|
||||
const hasError = results.find((role) => !!role.errorStatus);
|
||||
|
||||
if (hasError) {
|
||||
let message =
|
||||
'The edits to this transformation were successful, but transformations for its roles was not edited due to a lack of permissions.';
|
||||
if (results.find((e) => !!e.errorStatus && e.errorStatus !== 403)) {
|
||||
// if the errors weren't all due to permissions show generic message
|
||||
// eg. trying to update a role with empty array as transformations
|
||||
message = `You've edited the allowed_roles for this transformation. However, the corresponding edits to some roles' transformations were not made`;
|
||||
}
|
||||
this.flashMessages.info(message, {
|
||||
sticky: true,
|
||||
priority: 300,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
isWildcard(role) {
|
||||
if (typeof role === 'string') {
|
||||
return role.indexOf('*') >= 0;
|
||||
}
|
||||
if (role && role.id) {
|
||||
return role.id.indexOf('*') >= 0;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
actions: {
|
||||
createOrUpdate(type, event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.applyChanges('save', () => {
|
||||
const transformationId = this.model.id || this.model.name;
|
||||
const newModelRoles = this.model.allowed_roles || [];
|
||||
const initialRoles = this.initialRoles || [];
|
||||
|
||||
const updateRoles = [...newModelRoles, ...initialRoles]
|
||||
.filter((r) => !this.isWildcard(r)) // CBS TODO: expand wildcards into included roles instead
|
||||
.map((role) => {
|
||||
if (initialRoles.indexOf(role) < 0) {
|
||||
return {
|
||||
id: role,
|
||||
action: 'ADD',
|
||||
};
|
||||
}
|
||||
if (newModelRoles.indexOf(role) < 0) {
|
||||
return {
|
||||
id: role,
|
||||
action: 'REMOVE',
|
||||
};
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter((r) => !!r);
|
||||
this.handleUpdateRoles(updateRoles, transformationId);
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
@ -16,7 +16,6 @@ const ALL_FEATURES = [
|
||||
'Performance Standby',
|
||||
'Namespaces',
|
||||
'Entropy Augmentation',
|
||||
'Transform Secrets Engine',
|
||||
];
|
||||
|
||||
export function allFeatures() {
|
||||
|
@ -6,12 +6,6 @@
|
||||
import { helper as buildHelper } from '@ember/component/helper';
|
||||
|
||||
const ENTERPRISE_SECRET_ENGINES = [
|
||||
{
|
||||
displayName: 'Transform',
|
||||
type: 'transform',
|
||||
category: 'generic',
|
||||
requiredFeature: 'Transform Secrets Engine',
|
||||
},
|
||||
{
|
||||
displayName: 'Key Management',
|
||||
type: 'keymgmt',
|
||||
|
@ -13,7 +13,6 @@ const SUPPORTED_SECRET_BACKENDS = [
|
||||
'pki',
|
||||
'ssh',
|
||||
'transit',
|
||||
'transform',
|
||||
'keymgmt',
|
||||
'kubernetes',
|
||||
];
|
||||
|
@ -1,104 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Model, { attr } from '@ember-data/model';
|
||||
import { computed } from '@ember/object';
|
||||
import { apiPath } from 'vault/macros/lazy-capabilities';
|
||||
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
|
||||
import attachCapabilities from 'vault/lib/attach-capabilities';
|
||||
|
||||
// these arrays define the order in which the fields will be displayed
|
||||
// see
|
||||
//https://www.vaultproject.io/api-docs/secret/transform#create-update-transformation
|
||||
const TYPES = [
|
||||
{
|
||||
value: 'fpe',
|
||||
displayName: 'Format Preserving Encryption (FPE)',
|
||||
},
|
||||
{
|
||||
value: 'masking',
|
||||
displayName: 'Masking',
|
||||
},
|
||||
];
|
||||
|
||||
const TWEAK_SOURCE = [
|
||||
{
|
||||
value: 'supplied',
|
||||
displayName: 'supplied',
|
||||
},
|
||||
{
|
||||
value: 'generated',
|
||||
displayName: 'generated',
|
||||
},
|
||||
{
|
||||
value: 'internal',
|
||||
displayName: 'internal',
|
||||
},
|
||||
];
|
||||
|
||||
const ModelExport = Model.extend({
|
||||
name: attr('string', {
|
||||
// CBS TODO: make this required for making a transformation
|
||||
label: 'Name',
|
||||
readOnly: true,
|
||||
subText: 'The name for your transformation. This cannot be edited later.',
|
||||
}),
|
||||
type: attr('string', {
|
||||
defaultValue: 'fpe',
|
||||
label: 'Type',
|
||||
possibleValues: TYPES,
|
||||
subText:
|
||||
'Vault provides two types of transformations: Format Preserving Encryption (FPE) is reversible, while Masking is not. This cannot be edited later.',
|
||||
}),
|
||||
tweak_source: attr('string', {
|
||||
defaultValue: 'supplied',
|
||||
label: 'Tweak source',
|
||||
possibleValues: TWEAK_SOURCE,
|
||||
subText: `A tweak value is used when performing FPE transformations. This can be supplied, generated, or internal.`, // CBS TODO: I do not include the link here. Need to figure out the best way to approach this.
|
||||
}),
|
||||
masking_character: attr('string', {
|
||||
characterLimit: 1,
|
||||
defaultValue: '*',
|
||||
label: 'Masking character',
|
||||
subText: 'Specify which character you’d like to mask your data.',
|
||||
}),
|
||||
template: attr('array', {
|
||||
editType: 'searchSelect',
|
||||
isSectionHeader: true,
|
||||
fallbackComponent: 'string-list',
|
||||
label: 'Template', // CBS TODO: make this required for making a transformation
|
||||
models: ['transform/template'],
|
||||
selectLimit: 1,
|
||||
onlyAllowExisting: true,
|
||||
subText:
|
||||
'Templates allow Vault to determine what and how to capture the value to be transformed. Type to use an existing template or create a new one.',
|
||||
}),
|
||||
allowed_roles: attr('array', {
|
||||
editType: 'searchSelect',
|
||||
isSectionHeader: true,
|
||||
label: 'Allowed roles',
|
||||
fallbackComponent: 'string-list',
|
||||
models: ['transform/role'],
|
||||
subText: 'Search for an existing role, type a new role to create it, or use a wildcard (*).',
|
||||
wildcardLabel: 'role',
|
||||
}),
|
||||
transformAttrs: computed('type', function () {
|
||||
if (this.type === 'masking') {
|
||||
return ['name', 'type', 'masking_character', 'template', 'allowed_roles'];
|
||||
}
|
||||
return ['name', 'type', 'tweak_source', 'template', 'allowed_roles'];
|
||||
}),
|
||||
transformFieldAttrs: computed('transformAttrs', function () {
|
||||
return expandAttributeMeta(this, this.transformAttrs);
|
||||
}),
|
||||
|
||||
backend: attr('string', {
|
||||
readOnly: true,
|
||||
}),
|
||||
});
|
||||
|
||||
export default attachCapabilities(ModelExport, {
|
||||
updatePath: apiPath`${'backend'}/transformation/${'id'}`,
|
||||
});
|
@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Model, { attr } from '@ember-data/model';
|
||||
import { computed } from '@ember/object';
|
||||
import { apiPath } from 'vault/macros/lazy-capabilities';
|
||||
import attachCapabilities from 'vault/lib/attach-capabilities';
|
||||
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
|
||||
|
||||
const M = Model.extend({
|
||||
idPrefix: 'alphabet/',
|
||||
idForNav: computed('id', 'idPrefix', function () {
|
||||
const modelId = this.id || '';
|
||||
return `${this.idPrefix}${modelId}`;
|
||||
}),
|
||||
|
||||
name: attr('string', {
|
||||
readOnly: true,
|
||||
subText: 'The alphabet name. Keep in mind that spaces are not allowed and this cannot be edited later.',
|
||||
}),
|
||||
alphabet: attr('string', {
|
||||
label: 'Alphabet',
|
||||
subText:
|
||||
'Provide the set of valid UTF-8 characters contained within both the input and transformed value. Read more.',
|
||||
}),
|
||||
|
||||
attrs: computed(function () {
|
||||
const keys = ['name', 'alphabet'];
|
||||
return expandAttributeMeta(this, keys);
|
||||
}),
|
||||
|
||||
backend: attr('string', { readOnly: true }),
|
||||
});
|
||||
|
||||
export default attachCapabilities(M, {
|
||||
updatePath: apiPath`${'backend'}/alphabet/${'id'}`,
|
||||
});
|
@ -1,47 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Model, { attr } from '@ember-data/model';
|
||||
import { computed } from '@ember/object';
|
||||
import { apiPath } from 'vault/macros/lazy-capabilities';
|
||||
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
|
||||
import attachCapabilities from 'vault/lib/attach-capabilities';
|
||||
|
||||
const ModelExport = Model.extend({
|
||||
// used for getting appropriate options for backend
|
||||
idPrefix: 'role/',
|
||||
// the id prefixed with `role/` so we can use it as the *secret param for the secret show route
|
||||
idForNav: computed('id', 'idPrefix', function () {
|
||||
const modelId = this.id || '';
|
||||
return `${this.idPrefix}${modelId}`;
|
||||
}),
|
||||
|
||||
name: attr('string', {
|
||||
// TODO: make this required for making a transformation
|
||||
label: 'Name',
|
||||
readOnly: true,
|
||||
subText: 'The name for your role. This cannot be edited later.',
|
||||
}),
|
||||
transformations: attr('array', {
|
||||
editType: 'searchSelect',
|
||||
isSectionHeader: true,
|
||||
fallbackComponent: 'string-list',
|
||||
label: 'Transformations',
|
||||
models: ['transform'],
|
||||
onlyAllowExisting: true,
|
||||
subText: 'Select which transformations this role will have access to. It must already exist.',
|
||||
}),
|
||||
|
||||
attrs: computed('transformations', function () {
|
||||
const keys = ['name', 'transformations'];
|
||||
return expandAttributeMeta(this, keys);
|
||||
}),
|
||||
|
||||
backend: attr('string', { readOnly: true }),
|
||||
});
|
||||
|
||||
export default attachCapabilities(ModelExport, {
|
||||
updatePath: apiPath`${'backend'}/role/${'id'}`,
|
||||
});
|
@ -1,54 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Model, { attr } from '@ember-data/model';
|
||||
import { computed } from '@ember/object';
|
||||
import { apiPath } from 'vault/macros/lazy-capabilities';
|
||||
import attachCapabilities from 'vault/lib/attach-capabilities';
|
||||
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
|
||||
|
||||
const M = Model.extend({
|
||||
idPrefix: 'template/',
|
||||
idForNav: computed('id', 'idPrefix', function () {
|
||||
const modelId = this.id || '';
|
||||
return `${this.idPrefix}${modelId}`;
|
||||
}),
|
||||
|
||||
name: attr('string', {
|
||||
readOnly: true,
|
||||
subText:
|
||||
'Templates allow Vault to determine what and how to capture the value to be transformed. This cannot be edited later.',
|
||||
}),
|
||||
type: attr('string', { defaultValue: 'regex' }),
|
||||
pattern: attr('string', {
|
||||
editType: 'regex',
|
||||
subText: 'The template’s pattern defines the data format. Expressed in regex.',
|
||||
}),
|
||||
alphabet: attr('array', {
|
||||
subText:
|
||||
'Alphabet defines a set of characters (UTF-8) that is used for FPE to determine the validity of plaintext and ciphertext values. You can choose a built-in one, or create your own.',
|
||||
editType: 'searchSelect',
|
||||
isSectionHeader: true,
|
||||
fallbackComponent: 'string-list',
|
||||
label: 'Alphabet',
|
||||
models: ['transform/alphabet'],
|
||||
selectLimit: 1,
|
||||
}),
|
||||
encodeFormat: attr('string'),
|
||||
decodeFormats: attr(),
|
||||
backend: attr('string', { readOnly: true }),
|
||||
|
||||
readAttrs: computed(function () {
|
||||
const keys = ['name', 'pattern', 'encodeFormat', 'decodeFormats', 'alphabet'];
|
||||
return expandAttributeMeta(this, keys);
|
||||
}),
|
||||
writeAttrs: computed(function () {
|
||||
return expandAttributeMeta(this, ['name', 'pattern', 'alphabet']);
|
||||
}),
|
||||
});
|
||||
|
||||
export default attachCapabilities(M, {
|
||||
updatePath: apiPath`${'backend'}/template/${'id'}`,
|
||||
});
|
@ -25,13 +25,6 @@ const secretModel = (store, backend, key) => {
|
||||
return secret;
|
||||
};
|
||||
|
||||
const transformModel = (queryParams) => {
|
||||
const modelType = 'transform';
|
||||
if (!queryParams || !queryParams.itemType) return modelType;
|
||||
|
||||
return `${modelType}/${queryParams.itemType}`;
|
||||
};
|
||||
|
||||
export default EditBase.extend({
|
||||
store: service(),
|
||||
|
||||
@ -41,9 +34,6 @@ export default EditBase.extend({
|
||||
if (modelType === 'role-ssh') {
|
||||
return this.store.createRecord(modelType, { keyType: 'ca' });
|
||||
}
|
||||
if (modelType === 'transform') {
|
||||
modelType = transformModel(transition.to.queryParams);
|
||||
}
|
||||
if (modelType === 'database/connection' && transition.to?.queryParams?.itemType === 'role') {
|
||||
modelType = 'database/role';
|
||||
}
|
||||
|
@ -35,25 +35,6 @@ export default Route.extend({
|
||||
},
|
||||
},
|
||||
|
||||
modelTypeForTransform(tab) {
|
||||
let modelType;
|
||||
switch (tab) {
|
||||
case 'role':
|
||||
modelType = 'transform/role';
|
||||
break;
|
||||
case 'template':
|
||||
modelType = 'transform/template';
|
||||
break;
|
||||
case 'alphabet':
|
||||
modelType = 'transform/alphabet';
|
||||
break;
|
||||
default: // CBS TODO: transform/transformation
|
||||
modelType = 'transform';
|
||||
break;
|
||||
}
|
||||
return modelType;
|
||||
},
|
||||
|
||||
secretParam() {
|
||||
const { secret } = this.paramsFor(this.routeName);
|
||||
return secret ? normalizePath(secret) : '';
|
||||
@ -95,7 +76,6 @@ export default Route.extend({
|
||||
database: tab === 'role' ? 'database/role' : 'database/connection',
|
||||
transit: 'transit-key',
|
||||
ssh: 'role-ssh',
|
||||
transform: this.modelTypeForTransform(tab),
|
||||
pki: `pki/${tab || 'pki-role'}`,
|
||||
// secret or secret-v2
|
||||
cubbyhole: 'secret',
|
||||
|
@ -38,38 +38,12 @@ export default Route.extend(UnloadModelRoute, {
|
||||
path = backend + '/keys/' + secret;
|
||||
} else if (backendType === 'ssh') {
|
||||
path = backend + '/roles/' + secret;
|
||||
} else if (modelType.startsWith('transform/')) {
|
||||
path = this.buildTransformPath(backend, secret, modelType);
|
||||
} else {
|
||||
path = backend + '/' + secret;
|
||||
}
|
||||
return this.store.findRecord('capabilities', path);
|
||||
},
|
||||
|
||||
buildTransformPath(backend, secret, modelType) {
|
||||
const noun = modelType.split('/')[1];
|
||||
return `${backend}/${noun}/${secret}`;
|
||||
},
|
||||
|
||||
modelTypeForTransform(secretName) {
|
||||
if (!secretName) return 'transform';
|
||||
if (secretName.startsWith('role/')) {
|
||||
return 'transform/role';
|
||||
}
|
||||
if (secretName.startsWith('template/')) {
|
||||
return 'transform/template';
|
||||
}
|
||||
if (secretName.startsWith('alphabet/')) {
|
||||
return 'transform/alphabet';
|
||||
}
|
||||
return 'transform'; // TODO: transform/transformation;
|
||||
},
|
||||
|
||||
transformSecretName(secret, modelType) {
|
||||
const noun = modelType.split('/')[1];
|
||||
return secret.replace(`${noun}/`, '');
|
||||
},
|
||||
|
||||
backendType() {
|
||||
return this.modelFor('vault.cluster.secrets.backend').get('engineType');
|
||||
},
|
||||
@ -107,7 +81,6 @@ export default Route.extend(UnloadModelRoute, {
|
||||
database: secret && secret.startsWith('role/') ? 'database/role' : 'database/connection',
|
||||
transit: 'transit-key',
|
||||
ssh: 'role-ssh',
|
||||
transform: this.modelTypeForTransform(secret),
|
||||
pki: secret && secret.startsWith('cert/') ? 'pki/cert' : 'pki/pki-role',
|
||||
cubbyhole: 'secret',
|
||||
kv: backendModel.modelTypeForKV,
|
||||
@ -241,9 +214,6 @@ export default Route.extend(UnloadModelRoute, {
|
||||
if (modelType === 'pki/cert') {
|
||||
secret = secret.replace('cert/', '');
|
||||
}
|
||||
if (modelType.startsWith('transform/')) {
|
||||
secret = this.transformSecretName(secret, modelType);
|
||||
}
|
||||
if (modelType === 'database/role') {
|
||||
secret = secret.replace('role/', '');
|
||||
}
|
||||
|
@ -1,37 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import ApplicationSerializer from './application';
|
||||
|
||||
export default ApplicationSerializer.extend({
|
||||
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
|
||||
if (payload.data?.masking_character) {
|
||||
payload.data.masking_character = String.fromCharCode(payload.data.masking_character);
|
||||
}
|
||||
return this._super(store, primaryModelClass, payload, id, requestType);
|
||||
},
|
||||
|
||||
serialize() {
|
||||
const json = this._super(...arguments);
|
||||
if (json.template && Array.isArray(json.template)) {
|
||||
// Transformations should only ever have one template
|
||||
json.template = json.template[0];
|
||||
}
|
||||
return json;
|
||||
},
|
||||
|
||||
extractLazyPaginatedData(payload) {
|
||||
return payload.data.keys.map((key) => {
|
||||
const model = {
|
||||
id: key,
|
||||
name: key,
|
||||
};
|
||||
if (payload.backend) {
|
||||
model.backend = payload.backend;
|
||||
}
|
||||
return model;
|
||||
});
|
||||
},
|
||||
});
|
@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import ApplicationSerializer from '../application';
|
||||
|
||||
export default ApplicationSerializer.extend({
|
||||
primaryKey: 'name',
|
||||
|
||||
extractLazyPaginatedData(payload) {
|
||||
return payload.data.keys.map((key) => {
|
||||
const model = {
|
||||
id: key,
|
||||
name: key,
|
||||
};
|
||||
if (payload.backend) {
|
||||
model.backend = payload.backend;
|
||||
}
|
||||
return model;
|
||||
});
|
||||
},
|
||||
});
|
@ -1,22 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import ApplicationSerializer from '../application';
|
||||
|
||||
export default ApplicationSerializer.extend({
|
||||
primaryKey: 'name',
|
||||
extractLazyPaginatedData(payload) {
|
||||
return payload.data.keys.map((key) => {
|
||||
const model = {
|
||||
id: key,
|
||||
name: key,
|
||||
};
|
||||
if (payload.backend) {
|
||||
model.backend = payload.backend;
|
||||
}
|
||||
return model;
|
||||
});
|
||||
},
|
||||
});
|
@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import ApplicationSerializer from '../application';
|
||||
|
||||
export default ApplicationSerializer.extend({
|
||||
primaryKey: 'name',
|
||||
|
||||
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
|
||||
if (payload.data?.alphabet) {
|
||||
payload.data.alphabet = [payload.data.alphabet];
|
||||
}
|
||||
// strip out P character from any named capture groups
|
||||
if (payload.data?.pattern) {
|
||||
this._formatNamedCaptureGroups(payload.data, '?P', '?');
|
||||
}
|
||||
return this._super(store, primaryModelClass, payload, id, requestType);
|
||||
},
|
||||
|
||||
serialize() {
|
||||
const json = this._super(...arguments);
|
||||
if (json.alphabet && Array.isArray(json.alphabet)) {
|
||||
// Templates should only ever have one alphabet
|
||||
json.alphabet = json.alphabet[0];
|
||||
}
|
||||
// add P character to any named capture groups
|
||||
if (json.pattern) {
|
||||
this._formatNamedCaptureGroups(json, '?', '?P');
|
||||
}
|
||||
return json;
|
||||
},
|
||||
|
||||
_formatNamedCaptureGroups(json, replace, replaceWith) {
|
||||
// named capture groups are handled differently between Go and js
|
||||
// first look for named capture groups in pattern string
|
||||
const regex = new RegExp(/\?P?(<(.+?)>)/, 'g');
|
||||
const namedGroups = json.pattern.match(regex);
|
||||
if (namedGroups) {
|
||||
namedGroups.forEach((group) => {
|
||||
// add or remove P depending on destination
|
||||
json.pattern = json.pattern.replace(group, group.replace(replace, replaceWith));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
extractLazyPaginatedData(payload) {
|
||||
return payload.data.keys.map((key) => {
|
||||
const model = {
|
||||
id: key,
|
||||
name: key,
|
||||
};
|
||||
if (payload.backend) {
|
||||
model.backend = payload.backend;
|
||||
}
|
||||
return model;
|
||||
});
|
||||
},
|
||||
});
|
@ -1,19 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
.copy-text {
|
||||
background: $ui-gray-010;
|
||||
|
||||
& > code {
|
||||
color: $ui-gray-800;
|
||||
padding: 14px;
|
||||
}
|
||||
}
|
||||
.transform-pattern-text div:not(:first-child) {
|
||||
font-family: $family-monospace;
|
||||
}
|
||||
.transform-decode-formats:not(:last-child) {
|
||||
margin-bottom: $spacing-s;
|
||||
}
|
@ -118,7 +118,6 @@
|
||||
@import './components/token-expire-warning';
|
||||
@import './components/toolbar';
|
||||
@import './components/tool-tip';
|
||||
@import './components/transform-edit';
|
||||
@import './components/transit-card';
|
||||
@import './components/ttl-picker';
|
||||
@import './components/unseal-warning';
|
||||
|
@ -1,133 +0,0 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
<PageHeader as |p|>
|
||||
<p.top>
|
||||
<KeyValueHeader
|
||||
@baseKey={{hash display=this.model.id id=this.model.idForNav}}
|
||||
@path="vault.cluster.secrets.backend.list"
|
||||
@mode={{this.mode}}
|
||||
@root={{this.root}}
|
||||
@showCurrent={{true}}
|
||||
/>
|
||||
</p.top>
|
||||
<p.levelLeft>
|
||||
<h1 class="title is-3" data-test-secret-header="true">
|
||||
{{#if (eq this.mode "create")}}
|
||||
Create Alphabet
|
||||
{{else if (eq this.mode "edit")}}
|
||||
Edit Alphabet
|
||||
{{else}}
|
||||
Alphabet
|
||||
<code>{{this.model.id}}</code>
|
||||
{{/if}}
|
||||
</h1>
|
||||
</p.levelLeft>
|
||||
</PageHeader>
|
||||
|
||||
{{#if (eq this.mode "show")}}
|
||||
<Toolbar>
|
||||
<ToolbarActions>
|
||||
{{#if this.capabilities.canDelete}}
|
||||
<button type="button" class="toolbar-link" onclick={{action "delete"}} data-test-transformation-alphabet-delete>
|
||||
Delete alphabet
|
||||
</button>
|
||||
<div class="toolbar-separator"></div>
|
||||
{{/if}}
|
||||
{{#if this.capabilities.canUpdate}}
|
||||
<ToolbarSecretLink
|
||||
@secret={{concat this.model.idPrefix this.model.id}}
|
||||
@mode="edit"
|
||||
data-test-edit-link={{true}}
|
||||
@replace={{true}}
|
||||
>
|
||||
Edit alphabet
|
||||
</ToolbarSecretLink>
|
||||
{{/if}}
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
{{/if}}
|
||||
|
||||
{{#if (or (eq this.mode "edit") (eq this.mode "create"))}}
|
||||
<form onsubmit={{action "createOrUpdate" this.mode}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<MessageError @model={{this.model}} />
|
||||
<NamespaceReminder @mode={{this.mode}} @noun="transform alphabet" />
|
||||
{{#each this.model.attrs as |attr|}}
|
||||
{{#if (and (eq attr.name "name") (eq this.mode "edit"))}}
|
||||
<label for={{attr.name}} class="is-label">
|
||||
{{attr.options.label}}
|
||||
</label>
|
||||
{{#if attr.options.subText}}
|
||||
<p class="sub-text">{{attr.options.subText}}</p>
|
||||
{{/if}}
|
||||
<input
|
||||
data-test-input={{attr.name}}
|
||||
id={{attr.name}}
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
value={{or (get this.model attr.name) this.model.id}}
|
||||
readonly
|
||||
class="field input is-readOnly"
|
||||
type={{attr.type}}
|
||||
/>
|
||||
{{else}}
|
||||
<FormField data-test-field @attr={{attr}} @model={{this.model}} />
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="field is-grouped-split box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={{this.buttonDisabled}}
|
||||
class="button is-primary"
|
||||
data-test-alphabet-transform-create={{true}}
|
||||
>
|
||||
{{#if (eq this.mode "create")}}
|
||||
Create alphabet
|
||||
{{else if (eq this.mode "edit")}}
|
||||
Save
|
||||
{{/if}}
|
||||
</button>
|
||||
<SecretLink
|
||||
@mode={{if (eq this.mode "create") "list" "show"}}
|
||||
class="button"
|
||||
@secret={{concat this.model.idPrefix this.model.id}}
|
||||
>
|
||||
Cancel
|
||||
</SecretLink>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{{else}}
|
||||
{{#if this.model.displayErrors}}
|
||||
<div class="has-top-margin-s">
|
||||
<MessageError @model={{this.model}} />
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
|
||||
{{#each this.model.attrs as |attr|}}
|
||||
{{#if (eq attr.type "object")}}
|
||||
<InfoTableRow
|
||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||
@value={{stringify (get this.model attr.name)}}
|
||||
/>
|
||||
{{else if (eq attr.type "array")}}
|
||||
<InfoTableRow
|
||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||
@value={{get this.model attr.name}}
|
||||
@type={{attr.type}}
|
||||
@isLink={{eq attr.name "transformations"}}
|
||||
/>
|
||||
{{else}}
|
||||
<InfoTableRow
|
||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||
@value={{get this.model attr.name}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
@ -1,75 +0,0 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
{{#if (and @item.updatePath.canRead (not this.isBuiltin))}}
|
||||
<LinkedBlock
|
||||
@params={{array "vault.cluster.secrets.backend.show" @itemPath}}
|
||||
class="list-item-row"
|
||||
data-test-secret-link={{@itemPath}}
|
||||
@encode={{true}}
|
||||
@queryParams={{secret-query-params @backendType}}
|
||||
>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-10">
|
||||
<SecretLink
|
||||
@mode="show"
|
||||
@secret={{@itemPath}}
|
||||
@queryParams={{hash type=@modelType}}
|
||||
class="has-text-black has-text-weight-semibold"
|
||||
>
|
||||
<Icon @name="file" class="has-text-grey-light" />
|
||||
{{if (eq @item.id " ") "(self)" (or @item.keyWithoutParent @item.id)}}
|
||||
</SecretLink>
|
||||
</div>
|
||||
<div class="column has-text-right">
|
||||
{{#if (or @item.updatePath.canRead @item.updatePath.canUpdate)}}
|
||||
<PopupMenu name="secret-menu">
|
||||
<nav class="menu">
|
||||
<ul class="menu-list">
|
||||
{{#if @item.updatePath.canRead}}
|
||||
<li class="action">
|
||||
<SecretLink @mode="show" @secret={{@itemPath}} class="has-text-black has-text-weight-semibold">
|
||||
Details
|
||||
</SecretLink>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if @item.updatePath.canUpdate}}
|
||||
<li class="action">
|
||||
<SecretLink @mode="edit" @secret={{@itemPath}} class="has-text-black has-text-weight-semibold">
|
||||
Edit
|
||||
</SecretLink>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
</nav>
|
||||
</PopupMenu>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</LinkedBlock>
|
||||
{{else}}
|
||||
<div class="list-item-row" data-test-view-only-list-item>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-12 has-text-grey has-text-weight-semibold">
|
||||
<Icon @name="file" class="has-text-grey-light" />
|
||||
{{#if this.isBuiltin}}
|
||||
<ToolTip @verticalPosition="above" @horizontalPosition="left" as |T|>
|
||||
<T.Trigger @tabindex={{false}}>
|
||||
{{@item.id}}
|
||||
</T.Trigger>
|
||||
<T.Content @defaultClass="tool-tip">
|
||||
<div class="box">
|
||||
This is a built-in HashiCorp
|
||||
{{@itemType}}. It can't be viewed or edited.
|
||||
</div>
|
||||
</T.Content>
|
||||
</ToolTip>
|
||||
{{else}}
|
||||
{{@item.id}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
@ -1,70 +0,0 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
{{! CBS TODO do not let click if !canRead }}
|
||||
{{#if (eq @options.item "transformation")}}
|
||||
<LinkedBlock
|
||||
@params={{array "vault.cluster.secrets.backend.show" @item.id}}
|
||||
class="list-item-row"
|
||||
data-test-secret-link={{@item.id}}
|
||||
@encode={{true}}
|
||||
@queryParams={{secret-query-params @backendModel.type}}
|
||||
>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-10">
|
||||
<SecretLink
|
||||
@mode="show"
|
||||
@secret={{@item.id}}
|
||||
@queryParams={{if (eq @backendModel.type "transform") (hash tab="actions") ""}}
|
||||
class="has-text-black has-text-weight-semibold"
|
||||
>
|
||||
<Icon @name="file" class="has-text-grey-light" />
|
||||
{{if (eq @item.id " ") "(self)" (or @item.keyWithoutParent @item.id)}}
|
||||
</SecretLink>
|
||||
</div>
|
||||
<div class="column has-text-right">
|
||||
{{#if (or @item.updatePath.canRead @item.updatePath.canUpdate)}}
|
||||
<PopupMenu name="secret-menu">
|
||||
<nav class="menu">
|
||||
<ul class="menu-list">
|
||||
{{#if (or @item.versionPath.isLoading @item.secretPath.isLoading)}}
|
||||
<li class="action">
|
||||
<button disabled type="button" class="link button is-loading is-transparent">
|
||||
loading
|
||||
</button>
|
||||
</li>
|
||||
{{else}}
|
||||
{{#if @item.updatePath.canRead}}
|
||||
<li class="action">
|
||||
<SecretLink @mode="show" @secret={{@item.id}} class="has-text-black has-text-weight-semibold">
|
||||
Details
|
||||
</SecretLink>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if @item.updatePath.canUpdate}}
|
||||
<li class="action">
|
||||
<SecretLink @mode="edit" @secret={{@item.id}} class="has-text-black has-text-weight-semibold">
|
||||
Edit
|
||||
</SecretLink>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</ul>
|
||||
</nav>
|
||||
</PopupMenu>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</LinkedBlock>
|
||||
{{else}}
|
||||
<div class="list-item-row">
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-12 has-text-grey has-text-weight-semibold">
|
||||
<Icon @name="file" class="has-text-grey-light" />
|
||||
{{if (eq @item.id " ") "(self)" (or @item.keyWithoutParent @item.id)}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
@ -1,63 +0,0 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
<ToggleButton
|
||||
@isOpen={{this.showForm}}
|
||||
@openLabel="Advanced templating"
|
||||
@closedLabel="Advanced templating"
|
||||
@onClick={{fn (mut this.showForm)}}
|
||||
data-test-toggle-advanced={{true}}
|
||||
/>
|
||||
|
||||
{{#if this.showForm}}
|
||||
<div class="box has-container is-fullwidth">
|
||||
<h4 class="title is-5">Advanced templating</h4>
|
||||
<p>
|
||||
Using your template's regex as a starting point, you can specify which parts of your input to encode and decode. For
|
||||
example, you may want to handle input formatting or only decode part of an input. For more information, see
|
||||
<DocLink @path="/vault/tutorials/adp/transform#advanced-handling">
|
||||
our documentation.
|
||||
</DocLink>
|
||||
</p>
|
||||
<div class="has-top-margin-l">
|
||||
<RegexValidator
|
||||
@value={{@model.pattern}}
|
||||
@testInputLabel="Sample input"
|
||||
@testInputSubText="Enter a sample input to match against your regex and identify capture groups. Optional."
|
||||
@showGroups={{true}}
|
||||
@onValidate={{this.setInputOptions}}
|
||||
/>
|
||||
</div>
|
||||
<AutocompleteInput
|
||||
@label="Encode format"
|
||||
@subText="Use the groups above to define how the input will be encoded. Refer to each group with $N. This is optional; if not specified, pattern will be used."
|
||||
@value={{@model.encodeFormat}}
|
||||
@optionsTrigger="$"
|
||||
@options={{this.inputOptions}}
|
||||
@onChange={{fn (mut @model.encodeFormat)}}
|
||||
class="has-top-margin-l"
|
||||
data-test-encode-format
|
||||
/>
|
||||
<KvObjectEditor
|
||||
@value={{@model.decodeFormats}}
|
||||
@onChange={{fn (mut @model.decodeFormats)}}
|
||||
@label="Decode formats"
|
||||
@subText="Using the groups above, define how this data will be decoded. Multiple decode_formats can be used. Optional. If not specified, pattern will be used."
|
||||
@keyPlaceholder="name"
|
||||
class="has-top-margin-l"
|
||||
data-test-kv-object-editor
|
||||
as |kvObject kvData|
|
||||
>
|
||||
<AutocompleteInput
|
||||
@value={{kvObject.value}}
|
||||
@placeholder="format"
|
||||
@optionsTrigger="$"
|
||||
@options={{this.inputOptions}}
|
||||
@onChange={{fn this.decodeFormatValueChange kvObject kvData}}
|
||||
data-test-decode-format
|
||||
/>
|
||||
</KvObjectEditor>
|
||||
</div>
|
||||
{{/if}}
|
@ -1,28 +0,0 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
<form onsubmit={{action "createOrUpdate" "create"}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<MessageError @model={{this.model}} />
|
||||
<NamespaceReminder @mode={{this.mode}} @noun="transformation" />
|
||||
{{#each this.model.transformFieldAttrs as |attr|}}
|
||||
<FormField data-test-field @attr={{attr}} @model={{this.model}} />
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="field is-grouped-split box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button type="submit" disabled={{this.buttonDisabled}} class="button is-primary" data-test-transform-create={{true}}>
|
||||
{{#if (eq this.mode "create")}}
|
||||
Create transformation
|
||||
{{else if (eq this.mode "edit")}}
|
||||
Save
|
||||
{{/if}}
|
||||
</button>
|
||||
<SecretLink @mode={{if (eq this.mode "create") "list" "show"}} class="button" @secret={{this.model.id}}>
|
||||
Cancel
|
||||
</SecretLink>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@ -1,64 +0,0 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
<form onsubmit={{action "createOrUpdate" "create"}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<MessageError @model={{this.model}} />
|
||||
<NamespaceReminder @mode={{this.mode}} @noun="transformation" />
|
||||
{{#each this.model.transformFieldAttrs as |attr|}}
|
||||
{{#if (or (eq attr.name "name") (eq attr.name "type"))}}
|
||||
<label for={{attr.name}} class="is-label">
|
||||
{{attr.options.label}}
|
||||
</label>
|
||||
{{#if attr.options.subText}}
|
||||
<p class="sub-text">{{attr.options.subText}}</p>
|
||||
{{/if}}
|
||||
{{#if attr.options.possibleValues}}
|
||||
<div class="control is-expanded field is-readOnly">
|
||||
<div class="select is-fullwidth">
|
||||
<select name={{attr.name}} id={{attr.name}} disabled data-test-input={{attr.name}}>
|
||||
<option selected={{get this.model attr.name}} value={{get this.model attr.name}}>
|
||||
{{get this.model attr.name}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<input
|
||||
data-test-input={{attr.name}}
|
||||
id={{attr.name}}
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
value={{or (get this.model attr.name) attr.options.defaultValue}}
|
||||
readonly
|
||||
class="field input is-readOnly"
|
||||
type={{attr.type}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<FormField data-test-field @attr={{attr}} @model={{this.model}} />
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="field is-grouped-split box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={{this.buttonDisabled}}
|
||||
class="button is-primary"
|
||||
data-test-transformation-save-button={{true}}
|
||||
>
|
||||
{{#if (eq this.mode "create")}}
|
||||
Create transformation
|
||||
{{else if (eq this.mode "edit")}}
|
||||
Save
|
||||
{{/if}}
|
||||
</button>
|
||||
<SecretLink @mode={{if (eq this.mode "create") "list" "show"}} class="button" @secret={{this.model.id}}>
|
||||
Cancel
|
||||
</SecretLink>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@ -1,122 +0,0 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
<PageHeader as |p|>
|
||||
<p.top>
|
||||
<KeyValueHeader
|
||||
@baseKey={{hash display=this.model.id id=this.model.idForNav}}
|
||||
@path="vault.cluster.secrets.backend.list"
|
||||
@mode={{this.mode}}
|
||||
@root={{this.root}}
|
||||
@showCurrent={{true}}
|
||||
/>
|
||||
</p.top>
|
||||
<p.levelLeft>
|
||||
<h1 class="title is-3" data-test-secret-header="true">
|
||||
{{#if (eq this.mode "create")}}
|
||||
Create Role
|
||||
{{else if (eq this.mode "edit")}}
|
||||
Edit Role
|
||||
{{else}}
|
||||
Role
|
||||
<code>{{this.model.id}}</code>
|
||||
{{/if}}
|
||||
</h1>
|
||||
</p.levelLeft>
|
||||
</PageHeader>
|
||||
|
||||
{{#if (eq this.mode "show")}}
|
||||
<Toolbar>
|
||||
<ToolbarActions>
|
||||
{{#if this.capabilities.canDelete}}
|
||||
<ConfirmAction
|
||||
@buttonClasses="toolbar-link"
|
||||
@onConfirmAction={{action "delete"}}
|
||||
@confirmTitle="Are you sure?"
|
||||
@confirmMessage="Deleting this role means that you’ll need to recreate it and reassign any existing transformations to use it again."
|
||||
@confirmButtonText="Delete"
|
||||
data-test-transformation-role-delete
|
||||
>
|
||||
Delete role
|
||||
</ConfirmAction>
|
||||
<div class="toolbar-separator"></div>
|
||||
{{/if}}
|
||||
{{#if this.capabilities.canUpdate}}
|
||||
<ToolbarSecretLink
|
||||
@secret={{concat this.model.idPrefix this.model.id}}
|
||||
@mode="edit"
|
||||
data-test-edit-link={{true}}
|
||||
@replace={{true}}
|
||||
>
|
||||
Edit role
|
||||
</ToolbarSecretLink>
|
||||
{{/if}}
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
{{/if}}
|
||||
|
||||
{{#if (or (eq this.mode "edit") (eq this.mode "create"))}}
|
||||
<form onsubmit={{action "createOrUpdate" this.mode}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<MessageError @model={{this.model}} />
|
||||
<NamespaceReminder @mode={{this.mode}} @noun="Transform role" />
|
||||
{{#each this.model.attrs as |attr|}}
|
||||
{{#if (and (eq this.mode "edit") attr.options.readOnly)}}
|
||||
<ReadonlyFormField @attr={{attr}} @value={{get this.model attr.name}} />
|
||||
{{else}}
|
||||
<FormField data-test-field @attr={{attr}} @model={{this.model}} />
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="field is-grouped-split box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={{this.buttonDisabled}}
|
||||
class="button is-primary"
|
||||
data-test-role-transform-create={{true}}
|
||||
>
|
||||
{{#if (eq this.mode "create")}}
|
||||
Create role
|
||||
{{else if (eq this.mode "edit")}}
|
||||
Save
|
||||
{{/if}}
|
||||
</button>
|
||||
{{#if (eq this.mode "create")}}
|
||||
<LinkTo @route={{"vault.cluster.secrets.backend.list-root"}} @query={{hash tab="role"}} class="button">
|
||||
Cancel
|
||||
</LinkTo>
|
||||
{{else if (eq this.mode "edit")}}
|
||||
<LinkTo @route="vault.cluster.secrets.backend.show" @model={{concat "role/" this.model.id}} class="button">
|
||||
Cancel
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{{else}}
|
||||
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
|
||||
{{#each this.model.attrs as |attr|}}
|
||||
{{#if (eq attr.type "object")}}
|
||||
<InfoTableRow
|
||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||
@value={{stringify (get this.model attr.name)}}
|
||||
/>
|
||||
{{else if (eq attr.type "array")}}
|
||||
<InfoTableRow
|
||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||
@value={{get this.model attr.name}}
|
||||
@type={{attr.type}}
|
||||
@isLink={{eq attr.name "transformations"}}
|
||||
/>
|
||||
{{else}}
|
||||
<InfoTableRow
|
||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||
@value={{get this.model attr.name}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
@ -1,79 +0,0 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
|
||||
{{#each this.model.transformFieldAttrs as |attr|}}
|
||||
{{#if (eq attr.type "object")}}
|
||||
<InfoTableRow
|
||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||
@value={{stringify (get this.model attr.name)}}
|
||||
/>
|
||||
{{else if (eq attr.type "array")}}
|
||||
<InfoTableRow
|
||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||
@value={{get this.model attr.name}}
|
||||
@type={{attr.type}}
|
||||
@isLink={{eq attr.name "allowed_roles"}}
|
||||
@queryParam="role"
|
||||
@modelType="transform/role"
|
||||
@wildcardLabel={{attr.options.wildcardLabel}}
|
||||
@backend={{this.model.backend}}
|
||||
/>
|
||||
{{else}}
|
||||
<InfoTableRow
|
||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||
@value={{get this.model attr.name}}
|
||||
@type={{attr.type}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
<div class="has-top-margin-xl has-bottom-margin-s">
|
||||
<label class="title has-border-bottom-light page-header">CLI Commands</label>
|
||||
<div class="has-bottom-margin-s">
|
||||
<h2 class="title is-6">Encode</h2>
|
||||
<div class="has-bottom-margin-s">
|
||||
<span class="helper-text has-text-grey">
|
||||
To test the encoding capability of your transformation, use the following command. It will output an encoded_value.
|
||||
</span>
|
||||
</div>
|
||||
<div class="copy-text level">
|
||||
{{#let (concat "vault write " this.model.backend "/encode/" this.cliCommand) as |copyEncodeCommand|}}
|
||||
<code>vault write {{this.model.backend}}/encode/{{this.cliCommand}}</code>
|
||||
<CopyButton
|
||||
class="button is-transparent level-right"
|
||||
@clipboardText={{copyEncodeCommand}}
|
||||
@buttonType="button"
|
||||
@success={{action (set-flash-message "Command copied!")}}
|
||||
>
|
||||
<Icon @name="clipboard-copy" aria-label="Copy" />
|
||||
</CopyButton>
|
||||
{{/let}}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="title is-6">Decode</h2>
|
||||
<div class="has-bottom-margin-s">
|
||||
<span class="helper-text has-text-grey">
|
||||
To test decoding capability of your transformation, use the encoded_value in the following command. It should return
|
||||
your original input.
|
||||
</span>
|
||||
</div>
|
||||
<div class="copy-text level">
|
||||
{{#let (concat "vault write " this.model.backend "/decode/" this.cliCommand) as |copyDecodeCommand|}}
|
||||
<code>vault write {{this.model.backend}}/decode/{{this.cliCommand}}</code>
|
||||
<CopyButton
|
||||
class="button is-transparent level-right"
|
||||
@clipboardText={{copyDecodeCommand}}
|
||||
@buttonType="button"
|
||||
@success={{action (set-flash-message "Command copied!")}}
|
||||
>
|
||||
<Icon @name="clipboard-copy" aria-label="Copy" />
|
||||
</CopyButton>
|
||||
{{/let}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,140 +0,0 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
<PageHeader as |p|>
|
||||
<p.top>
|
||||
<KeyValueHeader
|
||||
@baseKey={{hash display=this.model.id id=this.model.idForNav}}
|
||||
@path="vault.cluster.secrets.backend.list"
|
||||
@mode={{this.mode}}
|
||||
@root={{this.root}}
|
||||
@showCurrent={{true}}
|
||||
/>
|
||||
</p.top>
|
||||
<p.levelLeft>
|
||||
<h1 class="title is-3" data-test-secret-header="true">
|
||||
{{#if (eq this.mode "create")}}
|
||||
Create Template
|
||||
{{else if (eq this.mode "edit")}}
|
||||
Edit Template
|
||||
{{else}}
|
||||
Template
|
||||
<code>{{this.model.id}}</code>
|
||||
{{/if}}
|
||||
</h1>
|
||||
</p.levelLeft>
|
||||
</PageHeader>
|
||||
|
||||
{{#if (eq this.mode "show")}}
|
||||
<Toolbar>
|
||||
<ToolbarActions>
|
||||
{{#if this.capabilities.canDelete}}
|
||||
<button type="button" class="toolbar-link" onclick={{action "delete"}} data-test-transformation-template-delete>
|
||||
Delete template
|
||||
</button>
|
||||
<div class="toolbar-separator"></div>
|
||||
{{/if}}
|
||||
{{#if this.capabilities.canUpdate}}
|
||||
<ToolbarSecretLink
|
||||
@secret={{concat this.model.idPrefix this.model.id}}
|
||||
@mode="edit"
|
||||
data-test-edit-link={{true}}
|
||||
@replace={{true}}
|
||||
>
|
||||
Edit template
|
||||
</ToolbarSecretLink>
|
||||
{{/if}}
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
{{/if}}
|
||||
|
||||
{{#if (or (eq this.mode "edit") (eq this.mode "create"))}}
|
||||
<form onsubmit={{action "createOrUpdate" this.mode}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<MessageError @model={{this.model}} />
|
||||
<NamespaceReminder @mode={{this.mode}} @noun="transform template" />
|
||||
{{#each this.model.writeAttrs as |attr|}}
|
||||
{{#if (and (eq attr.name "name") (eq this.mode "edit"))}}
|
||||
<label for={{attr.name}} class="is-label">
|
||||
{{attr.options.label}}
|
||||
</label>
|
||||
{{#if attr.options.subText}}
|
||||
<p class="sub-text">{{attr.options.subText}}</p>
|
||||
{{/if}}
|
||||
<input
|
||||
data-test-input={{attr.name}}
|
||||
id={{attr.name}}
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
value={{or (get this.model attr.name) attr.options.defaultValue}}
|
||||
readonly={{true}}
|
||||
class="field input is-readOnly"
|
||||
type={{attr.type}}
|
||||
/>
|
||||
{{else}}
|
||||
{{#if (eq attr.name "alphabet")}}
|
||||
<TransformAdvancedTemplating @model={{this.model}} />
|
||||
{{/if}}
|
||||
<FormField data-test-field @attr={{attr}} @model={{this.model}} />
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="field is-grouped-split box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={{this.buttonDisabled}}
|
||||
class="button is-primary"
|
||||
data-test-template-transform-create={{true}}
|
||||
>
|
||||
{{#if (eq this.mode "create")}}
|
||||
Create template
|
||||
{{else if (eq this.mode "edit")}}
|
||||
Save
|
||||
{{/if}}
|
||||
</button>
|
||||
<SecretLink
|
||||
@mode={{if (eq this.mode "create") "list" "show"}}
|
||||
class="button"
|
||||
@secret={{concat this.model.idPrefix this.model.id}}
|
||||
>
|
||||
Cancel
|
||||
</SecretLink>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{{else}}
|
||||
{{#if this.model.displayErrors}}
|
||||
<div class="has-top-margin-s">
|
||||
<MessageError @model={{this.model}} />
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
|
||||
{{#each this.model.readAttrs as |attr|}}
|
||||
{{#let (capitalize (or attr.options.label (humanize (dasherize attr.name)))) as |label|}}
|
||||
{{#if (eq attr.name "decodeFormats")}}
|
||||
{{#if (not (is-empty-value this.model.decodeFormats))}}
|
||||
<InfoTableRow @label={{label}}>
|
||||
<div>
|
||||
{{#each-in this.model.decodeFormats as |key value|}}
|
||||
<div class="transform-decode-formats">
|
||||
<p class="is-label has-text-grey-400">{{key}}</p>
|
||||
<p>{{value}}</p>
|
||||
</div>
|
||||
{{/each-in}}
|
||||
</div>
|
||||
</InfoTableRow>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<InfoTableRow
|
||||
@label={{label}}
|
||||
@value={{get this.model attr.name}}
|
||||
class={{if (eq attr.name "pattern") "transform-pattern-text"}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
@ -1,125 +0,0 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
<PageHeader as |p|>
|
||||
<p.top>
|
||||
<KeyValueHeader
|
||||
@baseKey={{this.model}}
|
||||
@path="vault.cluster.secrets.backend.list"
|
||||
@mode={{this.mode}}
|
||||
@root={{this.root}}
|
||||
@showCurrent={{true}}
|
||||
/>
|
||||
</p.top>
|
||||
<p.levelLeft>
|
||||
<h1 class="title is-3" data-test-secret-header="true">
|
||||
{{#if (eq this.mode "create")}}
|
||||
Create transformation
|
||||
{{else if (eq this.mode "edit")}}
|
||||
Edit transformation
|
||||
{{else}}
|
||||
Transformation
|
||||
<code>{{this.model.id}}</code>
|
||||
{{/if}}
|
||||
</h1>
|
||||
</p.levelLeft>
|
||||
</PageHeader>
|
||||
|
||||
{{#if (eq this.mode "show")}}
|
||||
<Toolbar>
|
||||
<ToolbarActions>
|
||||
{{#if this.capabilities.canDelete}}
|
||||
{{#if (gt this.model.allowed_roles.length 0)}}
|
||||
<ToolTip @verticalPosition="above" @horizontalPosition="center" as |T|>
|
||||
<T.Trigger @tabindex="-1">
|
||||
<button class="toolbar-link" aria-disabled="true" type="button" disabled>
|
||||
Delete transformation
|
||||
</button>
|
||||
</T.Trigger>
|
||||
<T.Content @defaultClass="tool-tip">
|
||||
<div class="box">
|
||||
This transformation is in use by a role and can’t be deleted.
|
||||
</div>
|
||||
</T.Content>
|
||||
</ToolTip>
|
||||
{{else}}
|
||||
<button class="toolbar-link" onclick={{action (mut this.isDeleteModalActive) true}} type="button">
|
||||
Delete transformation
|
||||
</button>
|
||||
{{/if}}
|
||||
<div class="toolbar-separator"></div>
|
||||
{{/if}}
|
||||
{{#if this.capabilities.canUpdate}}
|
||||
{{#if (gt this.model.allowed_roles.length 0)}}
|
||||
<button
|
||||
class="toolbar-link"
|
||||
onclick={{action (mut this.isEditModalActive) true}}
|
||||
type="button"
|
||||
data-test-edit-link
|
||||
>
|
||||
Edit transformation
|
||||
</button>
|
||||
{{else}}
|
||||
<ToolbarSecretLink @secret={{this.model.id}} @mode="edit" data-test-edit-link={{true}} @replace={{true}}>
|
||||
Edit transformation
|
||||
</ToolbarSecretLink>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq this.mode "edit")}}
|
||||
<TransformEditForm @mode={{this.mode}} @model={{this.model}} />
|
||||
{{else if (eq this.mode "create")}}
|
||||
<TransformCreateForm @mode={{this.mode}} @model={{this.model}} />
|
||||
{{else}}
|
||||
<TransformShowTransformation @model={{this.model}} />
|
||||
{{/if}}
|
||||
|
||||
<ConfirmationModal
|
||||
@title="Delete transformation"
|
||||
@onClose={{action (mut this.isDeleteModalActive) false}}
|
||||
@isActive={{this.isDeleteModalActive}}
|
||||
@confirmText={{this.model.name}}
|
||||
@toConfirmMsg="deleting the transformation."
|
||||
@onConfirm={{action "delete"}}
|
||||
>
|
||||
<p class="has-bottom-margin-m">
|
||||
Deleting the
|
||||
<strong>{{this.model.name}}</strong>
|
||||
transformation means that the underlying keys are lost and the data encoded by the transformation are unrecoverable and
|
||||
cannot be decoded.
|
||||
</p>
|
||||
<MessageError @model={{this.model}} @errorMessage={{this.error}} />
|
||||
</ConfirmationModal>
|
||||
|
||||
<Modal
|
||||
@title="Edit transformation"
|
||||
@onClose={{action (mut this.isEditModalActive) false}}
|
||||
@isActive={{this.isEditModalActive}}
|
||||
@type="warning"
|
||||
@showCloseButton={{true}}
|
||||
>
|
||||
<section class="modal-card-body">
|
||||
<p>
|
||||
You’re editing a transformation that is in use by at least one role. Editing it may mean that encode and decode
|
||||
operations stop working. Are you sure?
|
||||
</p>
|
||||
</section>
|
||||
<footer class="modal-card-foot modal-card-foot-outlined">
|
||||
<LinkTo
|
||||
@route="vault.cluster.secrets.backend.edit"
|
||||
@model={{this.model.id}}
|
||||
class="button is-primary"
|
||||
data-test-edit-confirm-button={{true}}
|
||||
>
|
||||
Confirm
|
||||
</LinkTo>
|
||||
<button type="button" class="button is-secondary" onclick={{action (mut this.isEditModalActive) false}}>
|
||||
Cancel
|
||||
</button>
|
||||
</footer>
|
||||
</Modal>
|
@ -18,7 +18,6 @@ const POSSIBLE_FEATURES = [
|
||||
'Seal Wrapping',
|
||||
'Control Groups',
|
||||
'Namespaces',
|
||||
'Transform Secrets Engine',
|
||||
'Key Management Secrets Engine',
|
||||
];
|
||||
|
||||
|
@ -115,53 +115,6 @@ const SECRET_BACKENDS = {
|
||||
},
|
||||
],
|
||||
},
|
||||
transform: {
|
||||
displayName: 'Transformation',
|
||||
navigateTree: false,
|
||||
listItemPartial: 'secret-list/transform-list-item',
|
||||
firstStep: `To use transform, you'll need to create a transformation and a role.`,
|
||||
tabs: [
|
||||
{
|
||||
name: 'transformations',
|
||||
label: 'Transformations',
|
||||
searchPlaceholder: 'Filter transformations',
|
||||
item: 'transformation',
|
||||
create: 'Create transformation',
|
||||
editComponent: 'transformation-edit',
|
||||
listItemPartial: 'secret-list/transform-transformation-item',
|
||||
},
|
||||
{
|
||||
name: 'role',
|
||||
modelPrefix: 'role/',
|
||||
label: 'Roles',
|
||||
searchPlaceholder: 'Filter roles',
|
||||
item: 'role',
|
||||
create: 'Create role',
|
||||
tab: 'role',
|
||||
editComponent: 'transform-role-edit',
|
||||
},
|
||||
{
|
||||
name: 'template',
|
||||
modelPrefix: 'template/',
|
||||
label: 'Templates',
|
||||
searchPlaceholder: 'Filter templates',
|
||||
item: 'template',
|
||||
create: 'Create template',
|
||||
tab: 'template',
|
||||
editComponent: 'transform-template-edit',
|
||||
},
|
||||
{
|
||||
name: 'alphabet',
|
||||
modelPrefix: 'alphabet/',
|
||||
label: 'Alphabets',
|
||||
searchPlaceholder: 'Filter alphabets',
|
||||
item: 'alphabet',
|
||||
create: 'Create alphabet',
|
||||
tab: 'alphabet',
|
||||
editComponent: 'alphabet-edit',
|
||||
},
|
||||
],
|
||||
},
|
||||
transit: {
|
||||
searchPlaceholder: 'Filter keys',
|
||||
item: 'key',
|
||||
|
@ -1,16 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="1" y="1" width="10" height="10" rx="1" fill="white" stroke="#BAC1CC" stroke-width="2"/>
|
||||
<rect x="13" y="13" width="10" height="10" rx="1" fill="white" stroke="#BAC1CC" stroke-width="2"/>
|
||||
<rect x="19" y="6" width="2" height="2" fill="#BAC1CC"/>
|
||||
<rect x="19" y="9" width="2" height="2" fill="#BAC1CC"/>
|
||||
<rect x="11" y="21" width="2" height="2" transform="rotate(-180 11 21)" fill="#BAC1CC"/>
|
||||
<rect x="8" y="21" width="2" height="2" transform="rotate(-180 8 21)" fill="#BAC1CC"/>
|
||||
<rect x="5" y="21" width="2" height="2" transform="rotate(-180 5 21)" fill="#BAC1CC"/>
|
||||
<rect x="5" y="18" width="2" height="2" transform="rotate(-180 5 18)" fill="#BAC1CC"/>
|
||||
<rect x="5" y="15" width="2" height="2" transform="rotate(-180 5 15)" fill="#BAC1CC"/>
|
||||
<path d="M7.02393 9H8.78174L6.98438 2.65869H5.01123L3.21826 9H4.81787L5.12109 7.60693H6.72949L7.02393 9ZM5.89453 4.08691H5.97803L6.479 6.44678H5.37598L5.89453 4.08691Z" fill="#BAC1CC"/>
|
||||
<path d="M15.4248 17.3833L16.9761 18.1743L15.4248 18.9653L16.0269 20.0068L17.4902 19.062L17.3979 20.8022H18.6021L18.5098 19.0576L19.9731 20.0112L20.5752 18.9653L19.0195 18.1743L20.5752 17.3833L19.9731 16.3418L18.5142 17.291L18.6021 15.5464H17.3979L17.4858 17.291L16.0269 16.3374L15.4248 17.3833Z" fill="#BAC1CC"/>
|
||||
<rect x="13" y="3" width="2" height="2" fill="#BAC1CC"/>
|
||||
<rect x="16" y="3" width="2" height="2" fill="#BAC1CC"/>
|
||||
<rect x="19" y="3" width="2" height="2" fill="#BAC1CC"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.5 KiB |
Loading…
Reference in New Issue
Block a user