Skip to content

Commit

Permalink
refactor aws secret ui (#5193)
Browse files Browse the repository at this point in the history
Update UI for AWS secret backend refactor

* Support empty AWS policy documents
* Try to make ARN input multiple
* move aws-role serializer to use the application serializer as the base
* support editing strings as JSON in the form field component
* update model, form and show to use form-component component, and swap fields based on credential type
* fix tests
* unify credential generation for aws and remove the STS specific action in the UI
* add label to the new json string form field
  • Loading branch information
meirish committed Aug 28, 2018
1 parent 4377a6e commit 3437e82
Show file tree
Hide file tree
Showing 16 changed files with 220 additions and 321 deletions.
25 changes: 25 additions & 0 deletions ui/app/adapters/aws-credential.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import ApplicationAdapter from './application';

export default ApplicationAdapter.extend({
createRecord(store, type, snapshot) {
let ttl = snapshot.attr('ttl');
let roleArn = snapshot.attr('roleArn');
let data = {};
if (ttl) {
data.ttl = ttl;
}
if (roleArn) {
data.role_arn = roleArn;
}
let method = ttl || roleArn ? 'POST' : 'GET';
let options = ttl || roleArn ? { data } : {};
let role = snapshot.attr('role');
let url = `/v1/${role.backend}/creds/${role.name}`;

return this.ajax(url, method, options).then(response => {
response.id = snapshot.id;
response.modelName = type.modelName;
store.pushPayload(type.modelName, response);
});
},
});
26 changes: 0 additions & 26 deletions ui/app/adapters/iam-credential.js

This file was deleted.

7 changes: 4 additions & 3 deletions ui/app/components/form-field.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,14 @@ export default Ember.Component.extend({
this.send('setAndBroadcast', path, valueToSet);
},

codemirrorUpdated(path, value, codemirror) {
codemirrorUpdated(path, isString, value, codemirror) {
codemirror.performLint();
const hasErrors = codemirror.state.lint.marked.length > 0;
let valToSet = isString ? value : JSON.parse(value);

if (!hasErrors) {
this.get('model').set(path, JSON.parse(value));
this.get('onChange')(path, JSON.parse(value));
this.get('model').set(path, valToSet);
this.get('onChange')(path, valToSet);
}
},
},
Expand Down
47 changes: 10 additions & 37 deletions ui/app/components/generate-credentials.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Ember from 'ember';

const { get, computed } = Ember;
const { get, set, computed, Component, inject } = Ember;

const MODEL_TYPES = {
'ssh-sign': {
Expand All @@ -9,34 +9,25 @@ const MODEL_TYPES = {
'ssh-creds': {
model: 'ssh-otp-credential',
title: 'Generate SSH Credentials',
generatedAttr: 'key',
},
'aws-creds': {
model: 'iam-credential',
title: 'Generate IAM Credentials',
generateWithoutInput: true,
model: 'aws-credential',
title: 'Generate AWS Credentials',
backIsListLink: true,
},
'aws-sts': {
model: 'iam-credential',
title: 'Generate IAM Credentials with STS',
generatedAttr: 'accessKey',
},
'pki-issue': {
model: 'pki-certificate',
title: 'Issue Certificate',
generatedAttr: 'certificate',
},
'pki-sign': {
model: 'pki-certificate-sign',
title: 'Sign Certificate',
generatedAttr: 'certificate',
},
};

export default Ember.Component.extend({
store: Ember.inject.service(),
routing: Ember.inject.service('-routing'),
export default Component.extend({
store: inject.service(),
routing: inject.service('-routing'),
// set on the component
backend: null,
action: null,
Expand Down Expand Up @@ -64,7 +55,6 @@ export default Ember.Component.extend({
init() {
this._super(...arguments);
this.createOrReplaceModel();
this.maybeGenerate();
},

willDestroy() {
Expand All @@ -86,33 +76,16 @@ export default Ember.Component.extend({
role: roleModel,
id: `${get(roleModel, 'backend')}-${get(roleModel, 'name')}`,
};
if (this.get('action') === 'sts') {
attrs.withSTS = true;
}
const newModel = this.get('store').createRecord(modelType, attrs);
this.set('model', newModel);
},

/*
*
* @function maybeGenerate
*
* This method is called on `init`. If there is no input requried (as is the case for AWS IAM creds)
* then the `create` action is triggered right away.
*
*/
maybeGenerate() {
if (this.get('backend.type') !== 'aws' || this.get('action') === 'sts') {
return;
}
// for normal IAM creds - there's no input, so just generate right away
this.send('create');
},

actions: {
create() {
let model = this.get('model');
this.set('loading', true);
this.model.save().finally(() => {
model.save().finally(() => {
model.set('hasGenerated', true);
this.set('loading', false);
});
},
Expand All @@ -122,7 +95,7 @@ export default Ember.Component.extend({
const hasErrors = codemirror.state.lint.marked.length > 0;

if (!hasErrors) {
Ember.set(this.get('model'), attr, JSON.parse(val));
set(this.get('model'), attr, JSON.parse(val));
}
},

Expand Down
26 changes: 16 additions & 10 deletions ui/app/components/role-aws-edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,8 @@ const { get, set } = Ember;
const SHOW_ROUTE = 'vault.cluster.secrets.backend.show';

export default RoleEdit.extend({
useARN: false,
init() {
this._super(...arguments);
const arn = get(this, 'model.arn');
if (arn) {
set(this, 'useARN', true);
}
},

actions: {
Expand All @@ -24,11 +19,22 @@ export default RoleEdit.extend({
if (type === 'create' && Ember.isBlank(modelId)) {
return;
}
// clear the policy or arn before save depending on "useARN"
if (get(this, 'useARN')) {
set(this, 'model.policy', '');
} else {
set(this, 'model.arn', '');

var credential_type = get(this, 'model.credential_type');
if (credential_type == "iam_user") {
set(this, 'model.role_arns', []);
}
if (credential_type == "assumed_role") {
set(this, 'model.policy_arns', []);
}
if (credential_type == "federation_token") {
set(this, 'model.role_arns', []);
set(this, 'model.policy_arns', []);
}

var policy_document = get(this, 'model.policy_document');
if (policy_document == '{}') {
set(this, 'model.policy_document', '');
}

this.persist('save', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,26 @@ import Ember from 'ember';
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
const { attr } = DS;
const { computed } = Ember;
const CREATE_FIELDS = ['ttl'];
const CREATE_FIELDS = ['ttl', 'roleArn'];

const DISPLAY_FIELDS = ['accessKey', 'secretKey', 'securityToken', 'leaseId', 'renewable', 'leaseDuration'];
export default DS.Model.extend({
helpText:
'For Vault roles that have a credential type of iam_user, these attributes are optional and you may simply submit the form.',
role: attr('object', {
readOnly: true,
}),

withSTS: attr('boolean', {
readOnly: true,
roleArn: attr('string', {
label: 'Role ARN',
helpText:
'The ARN of the role to assume if credential_type on the Vault role is assumed_role. Optional if the role has a single role ARN; required otherwise.',
}),

ttl: attr({
editType: 'ttl',
defaultValue: '1h',
label: 'TTL',
helpText:
'Specifies the TTL for the use of the STS token. Valid only when credential_type is assumed_role or federation_token.',
}),
leaseId: attr('string'),
renewable: attr('boolean'),
Expand All @@ -26,8 +31,9 @@ export default DS.Model.extend({
secretKey: attr('string'),
securityToken: attr('string'),

attrs: computed('accessKey', function() {
let keys = this.get('accessKey') ? DISPLAY_FIELDS.slice(0) : CREATE_FIELDS.slice(0);
attrs: computed('accessKey', 'securityToken', function() {
let keys =
this.get('accessKey') || this.get('securityToken') ? DISPLAY_FIELDS.slice(0) : CREATE_FIELDS.slice(0);
return expandAttributeMeta(this, keys);
}),

Expand Down
54 changes: 43 additions & 11 deletions ui/app/models/role-aws.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,20 @@ import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
const { attr } = DS;
const { computed } = Ember;

const CREATE_FIELDS = ['name', 'policy', 'arn'];
const CREDENTIAL_TYPES = [
{
value: 'iam_user',
displayName: 'IAM User',
},
{
value: 'assumed_role',
displayName: 'Assumed Role',
},
{
value: 'federation_token',
displayName: 'Federation Token',
},
];
export default DS.Model.extend({
backend: attr('string', {
readOnly: true,
Expand All @@ -16,15 +29,37 @@ export default DS.Model.extend({
fieldValue: 'id',
readOnly: true,
}),
arn: attr('string', {
helpText: '',
// credentialTypes are for backwards compatibility.
// we use this to populate "credentialType" in
// the serializer. if there is more than one, the
// show and edit pages will show a warning
credentialTypes: attr('array', {
readOnly: true,
}),
credentialType: attr('string', {
defaultValue: 'iam_user',
possibleValues: CREDENTIAL_TYPES,
}),
roleArns: attr({
editType: 'stringArray',
label: 'Role ARNs',
}),
policy: attr('string', {
helpText: '',
widget: 'json',
policyArns: attr({
editType: 'stringArray',
label: 'Policy ARNs',
}),
attrs: computed(function() {
let keys = CREATE_FIELDS.slice(0);
policyDocument: attr('string', {
editType: 'json',
}),
fields: computed('credentialType', function() {
let keys;
let credentialType = this.get('credentialType');
let keysForType = {
iam_user: ['name', 'credentialType', 'policyArns', 'policyDocument'],
assumed_role: ['name', 'credentialType', 'roleArns', 'policyDocument'],
federation_token: ['name', 'credentialType', 'policyDocument'],
};
keys = keysForType[credentialType];
return expandAttributeMeta(this, keys);
}),

Expand All @@ -35,7 +70,4 @@ export default DS.Model.extend({

generatePath: lazyCapabilities(apiPath`${'backend'}/creds/${'id'}`, 'backend', 'id'),
canGenerate: computed.alias('generatePath.canUpdate'),

stsPath: lazyCapabilities(apiPath`${'backend'}/sts/${'id'}`, 'backend', 'id'),
canGenerateSTS: computed.alias('stsPath.canUpdate'),
});
3 changes: 3 additions & 0 deletions ui/app/serializers/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export default DS.JSONSerializer.extend({
normalizeItems(payload) {
if (payload.data && payload.data.keys && Array.isArray(payload.data.keys)) {
let models = payload.data.keys.map(key => {
if (typeof key !== 'string') {
return key;
}
let pk = this.get('primaryKey') || 'id';
return { [pk]: key };
});
Expand Down
34 changes: 0 additions & 34 deletions ui/app/serializers/iam-credential.js

This file was deleted.

Loading

0 comments on commit 3437e82

Please sign in to comment.