mirror of
https://github.com/status-im/consul.git
synced 2025-01-10 13:55:55 +00:00
UI: Catch 500 error on token endpoint and revert to legacy tokens (#4874)
In some circumstances a consul 1.4 client could be running in an un-upgraded 1.3 or lower cluster. Currently this gives a 500 error on the new ACL token endpoint. Here we catch this specific 500 error/message and set the users AccessorID to null. Elsewhere in the frontend we use this fact (AccessorID being null) to decide whether to present the legacy or the new ACL UI to the user. Also: - Re-adds in most of the old style ACL acceptance tests, now that we are keeping the old style UI - Restricts code editors to HCL only mode for all `Rules` editing (legacy/'half legacy'/new style) - Adds a [Stop using] button to the old style ACL rows so its possible to logout. - Updates copy and documentation links for the upgrade notices
This commit is contained in:
parent
3981c5d48c
commit
f65f001675
@ -18,6 +18,13 @@ export default Component.extend(SlotsMixin, {
|
|||||||
$html.classList.remove(...templatize(['loading']));
|
$html.classList.remove(...templatize(['loading']));
|
||||||
}
|
}
|
||||||
if (cls) {
|
if (cls) {
|
||||||
|
// its possible for 'layout' templates to change after insert
|
||||||
|
// check for these specific layouts and clear them out
|
||||||
|
[...$html.classList].forEach(function(item, i) {
|
||||||
|
if (templatize(['edit', 'show', 'list']).indexOf(item) !== -1) {
|
||||||
|
$html.classList.remove(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
$html.classList.add(...templatize(cls.split(' ')));
|
$html.classList.add(...templatize(cls.split(' ')));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,9 +1,19 @@
|
|||||||
import { helper } from '@ember/component/helper';
|
import { helper } from '@ember/component/helper';
|
||||||
import { get } from '@ember/object';
|
import { get } from '@ember/object';
|
||||||
|
|
||||||
|
const _isLegacy = function(token) {
|
||||||
|
const rules = get(token, 'Rules');
|
||||||
|
return get(token, 'Legacy') || (rules != null && rules.trim() != '');
|
||||||
|
};
|
||||||
export function isLegacy(params, hash) {
|
export function isLegacy(params, hash) {
|
||||||
const token = params[0];
|
const token = params[0];
|
||||||
return get(token, 'Legacy') || typeof get(token, 'Rules') !== 'undefined';
|
// is array like (RecordManager isn't an array)
|
||||||
|
if (typeof token.length !== 'undefined') {
|
||||||
|
return token.find(function(item) {
|
||||||
|
return _isLegacy(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return _isLegacy(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default helper(isLegacy);
|
export default helper(isLegacy);
|
||||||
|
@ -8,13 +8,34 @@ export default Mixin.create(WithBlockingActions, {
|
|||||||
actions: {
|
actions: {
|
||||||
use: function(item) {
|
use: function(item) {
|
||||||
return get(this, 'feedback').execute(() => {
|
return get(this, 'feedback').execute(() => {
|
||||||
|
// old style legacy ACLs don't have AccessorIDs
|
||||||
|
// therefore set it to null, this way the frontend knows
|
||||||
|
// to use legacy ACLs
|
||||||
return get(this, 'settings')
|
return get(this, 'settings')
|
||||||
.persist({ token: get(item, 'ID') })
|
.persist({
|
||||||
|
token: {
|
||||||
|
AccessorID: null,
|
||||||
|
SecretID: get(item, 'ID'),
|
||||||
|
},
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return this.transitionTo('dc.services');
|
return this.transitionTo('dc.services');
|
||||||
});
|
});
|
||||||
}, 'use');
|
}, 'use');
|
||||||
},
|
},
|
||||||
|
// TODO: This is also used in tokens, probably an opportunity to dry this out
|
||||||
|
logout: function(item) {
|
||||||
|
return get(this, 'feedback').execute(() => {
|
||||||
|
return get(this, 'settings')
|
||||||
|
.delete('token')
|
||||||
|
.then(() => {
|
||||||
|
// in this case we don't do the same as delete as we want to go to the new
|
||||||
|
// dc.acls.tokens page. If we get there via the dc.acls redirect/rewrite
|
||||||
|
// then we lose the flash message
|
||||||
|
return this.transitionTo('dc.acls.tokens');
|
||||||
|
});
|
||||||
|
}, 'logout');
|
||||||
|
},
|
||||||
clone: function(item) {
|
clone: function(item) {
|
||||||
return get(this, 'feedback').execute(() => {
|
return get(this, 'feedback').execute(() => {
|
||||||
return get(this, 'repo')
|
return get(this, 'repo')
|
||||||
|
@ -12,6 +12,7 @@ export default Route.extend({
|
|||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
},
|
},
|
||||||
repo: service('repository/dc'),
|
repo: service('repository/dc'),
|
||||||
|
settings: service('settings'),
|
||||||
actions: {
|
actions: {
|
||||||
loading: function(transition, originRoute) {
|
loading: function(transition, originRoute) {
|
||||||
let dc = null;
|
let dc = null;
|
||||||
@ -58,13 +59,17 @@ export default Route.extend({
|
|||||||
// 403 page
|
// 403 page
|
||||||
// To note: Consul only gives you back a 403 if a non-existent token has been sent in the header
|
// To note: Consul only gives you back a 403 if a non-existent token has been sent in the header
|
||||||
// if a token has not been sent at all, it just gives you a 200 with an empty dataset
|
// if a token has not been sent at all, it just gives you a 200 with an empty dataset
|
||||||
|
const model = this.modelFor('dc');
|
||||||
if (error.status === '403') {
|
if (error.status === '403') {
|
||||||
return this.transitionTo('dc.acls.tokens');
|
return get(this, 'settings')
|
||||||
|
.delete('token')
|
||||||
|
.then(() => {
|
||||||
|
return this.transitionTo('dc.acls.tokens', model.dc.Name);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (error.status === '') {
|
if (error.status === '') {
|
||||||
error.message = 'Error';
|
error.message = 'Error';
|
||||||
}
|
}
|
||||||
const model = this.modelFor('dc');
|
|
||||||
hash({
|
hash({
|
||||||
error: error,
|
error: error,
|
||||||
dc:
|
dc:
|
||||||
|
@ -2,7 +2,6 @@ import Route from '@ember/routing/route';
|
|||||||
import { get } from '@ember/object';
|
import { get } from '@ember/object';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
||||||
|
|
||||||
export default Route.extend(WithBlockingActions, {
|
export default Route.extend(WithBlockingActions, {
|
||||||
settings: service('settings'),
|
settings: service('settings'),
|
||||||
feedback: service('feedback'),
|
feedback: service('feedback'),
|
||||||
@ -21,8 +20,15 @@ export default Route.extend(WithBlockingActions, {
|
|||||||
SecretID: secret,
|
SecretID: secret,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(item => {
|
||||||
|
// a null AccessorID means we are in legacy mode
|
||||||
|
// take the user to the legacy acls
|
||||||
|
// otherwise just refresh the page
|
||||||
|
if (get(item, 'token.AccessorID') === null) {
|
||||||
|
return this.transitionTo('dc.acls');
|
||||||
|
} else {
|
||||||
this.refresh();
|
this.refresh();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, 'authorize');
|
}, 'authorize');
|
||||||
|
@ -7,6 +7,7 @@ import WithAclActions from 'consul-ui/mixins/acl/with-actions';
|
|||||||
|
|
||||||
export default Route.extend(WithAclActions, {
|
export default Route.extend(WithAclActions, {
|
||||||
repo: service('repository/acl'),
|
repo: service('repository/acl'),
|
||||||
|
settings: service('settings'),
|
||||||
queryParams: {
|
queryParams: {
|
||||||
s: {
|
s: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
@ -14,12 +15,24 @@ export default Route.extend(WithAclActions, {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
beforeModel: function(transition) {
|
beforeModel: function(transition) {
|
||||||
return this.replaceWith('dc.acls.tokens');
|
return get(this, 'settings')
|
||||||
|
.findBySlug('token')
|
||||||
|
.then(token => {
|
||||||
|
// If you don't have a token set or you have a
|
||||||
|
// token set with AccessorID set to not null (new ACL mode)
|
||||||
|
// then rewrite to the new acls
|
||||||
|
if (!token || get(token, 'AccessorID') !== null) {
|
||||||
|
// If you return here, you get a TransitionAborted error in the tests only
|
||||||
|
// everything works fine either way checking things manually
|
||||||
|
this.replaceWith('dc.acls.tokens');
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
model: function(params) {
|
model: function(params) {
|
||||||
return hash({
|
return hash({
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
items: get(this, 'repo').findAllByDatacenter(this.modelFor('dc').dc.Name),
|
items: get(this, 'repo').findAllByDatacenter(this.modelFor('dc').dc.Name),
|
||||||
|
token: get(this, 'settings').findBySlug('token'),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setupController: function(controller, model) {
|
setupController: function(controller, model) {
|
||||||
|
@ -12,6 +12,19 @@ export default Route.extend(WithTokenActions, {
|
|||||||
replace: true,
|
replace: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
beforeModel: function(transition) {
|
||||||
|
return get(this, 'settings')
|
||||||
|
.findBySlug('token')
|
||||||
|
.then(token => {
|
||||||
|
// If you have a token set with AccessorID set to null (legacy mode)
|
||||||
|
// then rewrite to the old acls
|
||||||
|
if (token && get(token, 'AccessorID') === null) {
|
||||||
|
// If you return here, you get a TransitionAborted error in the tests only
|
||||||
|
// everything works fine either way checking things manually
|
||||||
|
this.replaceWith('dc.acls');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
model: function(params) {
|
model: function(params) {
|
||||||
const repo = get(this, 'repo');
|
const repo = get(this, 'repo');
|
||||||
return hash({
|
return hash({
|
||||||
|
@ -4,7 +4,9 @@ import { typeOf } from '@ember/utils';
|
|||||||
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/policy';
|
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/policy';
|
||||||
import { Promise } from 'rsvp';
|
import { Promise } from 'rsvp';
|
||||||
import statusFactory from 'consul-ui/utils/acls-status';
|
import statusFactory from 'consul-ui/utils/acls-status';
|
||||||
const status = statusFactory(Promise);
|
import isValidServerErrorFactory from 'consul-ui/utils/http/acl/is-valid-server-error';
|
||||||
|
const isValidServerError = isValidServerErrorFactory();
|
||||||
|
const status = statusFactory(isValidServerError, Promise);
|
||||||
const MODEL_NAME = 'policy';
|
const MODEL_NAME = 'policy';
|
||||||
export default Service.extend({
|
export default Service.extend({
|
||||||
getModelName: function() {
|
getModelName: function() {
|
||||||
|
@ -4,7 +4,9 @@ import { typeOf } from '@ember/utils';
|
|||||||
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/token';
|
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/token';
|
||||||
import { Promise } from 'rsvp';
|
import { Promise } from 'rsvp';
|
||||||
import statusFactory from 'consul-ui/utils/acls-status';
|
import statusFactory from 'consul-ui/utils/acls-status';
|
||||||
const status = statusFactory(Promise);
|
import isValidServerErrorFactory from 'consul-ui/utils/http/acl/is-valid-server-error';
|
||||||
|
const isValidServerError = isValidServerErrorFactory();
|
||||||
|
const status = statusFactory(isValidServerError, Promise);
|
||||||
const MODEL_NAME = 'token';
|
const MODEL_NAME = 'token';
|
||||||
export default Service.extend({
|
export default Service.extend({
|
||||||
getModelName: function() {
|
getModelName: function() {
|
||||||
@ -20,9 +22,21 @@ export default Service.extend({
|
|||||||
return status(obj);
|
return status(obj);
|
||||||
},
|
},
|
||||||
self: function(secret, dc) {
|
self: function(secret, dc) {
|
||||||
return get(this, 'store').self(this.getModelName(), {
|
return get(this, 'store')
|
||||||
|
.self(this.getModelName(), {
|
||||||
secret: secret,
|
secret: secret,
|
||||||
dc: dc,
|
dc: dc,
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
// If we get this 500 RPC error, it means we are a legacy ACL cluster
|
||||||
|
// set AccessorID to null - which for the frontend means legacy mode
|
||||||
|
if (isValidServerError(e)) {
|
||||||
|
return {
|
||||||
|
AccessorID: null,
|
||||||
|
SecretID: secret,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return Promise.reject(e);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
clone: function(item) {
|
clone: function(item) {
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<label class="type-text">
|
<label class="type-text">
|
||||||
<span>Policy <a href="{{env 'CONSUL_DOCUMENTATION_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a></span>
|
<span>Policy <a href="{{env 'CONSUL_DOCUMENTATION_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a></span>
|
||||||
{{code-editor class=(if item.error.Rules 'error') name='Rules' value=item.Rules onkeyup=(action 'change')}}
|
{{code-editor class=(if item.error.Rules 'error') name='Rules' value=item.Rules syntax="hcl" onkeyup=(action 'change')}}
|
||||||
</label>
|
</label>
|
||||||
{{#if create }}
|
{{#if create }}
|
||||||
<label class="type-text">
|
<label class="type-text">
|
||||||
|
@ -16,6 +16,12 @@
|
|||||||
{{else}}
|
{{else}}
|
||||||
There was an error deleting your ACL token.
|
There was an error deleting your ACL token.
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{ else if (eq type 'logout')}}
|
||||||
|
{{#if (eq status 'success') }}
|
||||||
|
You are now logged out.
|
||||||
|
{{else}}
|
||||||
|
There was an error logging out.
|
||||||
|
{{/if}}
|
||||||
{{ else if (eq type 'use')}}
|
{{ else if (eq type 'use')}}
|
||||||
{{#if (eq status 'success') }}
|
{{#if (eq status 'success') }}
|
||||||
Now using new ACL token.
|
Now using new ACL token.
|
||||||
|
@ -45,9 +45,16 @@
|
|||||||
<li>
|
<li>
|
||||||
<a data-test-edit href={{href-to 'dc.acls.edit' item.ID}}>Edit</a>
|
<a data-test-edit href={{href-to 'dc.acls.edit' item.ID}}>Edit</a>
|
||||||
</li>
|
</li>
|
||||||
|
{{#if (eq item.ID token.SecretID) }}
|
||||||
|
<li>
|
||||||
|
<a data-test-logout onclick={{queue (action confirm 'logout' item) (action change)}}>Stop using</a>
|
||||||
|
</li>
|
||||||
|
{{else}}
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<a data-test-use onclick={{queue (action confirm 'use' item) (action change)}}>Use</a>
|
<a data-test-use onclick={{queue (action confirm 'use' item) (action change)}}>Use</a>
|
||||||
</li>
|
</li>
|
||||||
|
{{/if}}
|
||||||
<li>
|
<li>
|
||||||
<a data-test-clone onclick={{action 'sendClone' item}}>Clone</a>
|
<a data-test-clone onclick={{action 'sendClone' item}}>Clone</a>
|
||||||
</li>
|
</li>
|
||||||
@ -63,6 +70,8 @@
|
|||||||
<p>
|
<p>
|
||||||
{{#if (eq name 'delete')}}
|
{{#if (eq name 'delete')}}
|
||||||
Are you sure you want to delete this ACL token?
|
Are you sure you want to delete this ACL token?
|
||||||
|
{{else if (eq name 'logout')}}
|
||||||
|
Are you sure you want to stop using this ACL token? This will log you out.
|
||||||
{{ else if (eq name 'use')}}
|
{{ else if (eq name 'use')}}
|
||||||
Are you sure you want to use this ACL token?
|
Are you sure you want to use this ACL token?
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@ -70,6 +79,8 @@
|
|||||||
<button type="button" class="type-delete" {{action execute}}>
|
<button type="button" class="type-delete" {{action execute}}>
|
||||||
{{#if (eq name 'delete')}}
|
{{#if (eq name 'delete')}}
|
||||||
Confirm Delete
|
Confirm Delete
|
||||||
|
{{else if (eq name 'logout')}}
|
||||||
|
Confirm Logout
|
||||||
{{ else if (eq name 'use')}}
|
{{ else if (eq name 'use')}}
|
||||||
Confirm Use
|
Confirm Use
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<p class="notice policy-management"><strong>Management</strong> This global-management token is built into Consul's policy system. You can apply this special policy to Tokens for full access. This policy is not editable or removeable, but can be ignored by not applying it to any tokens. Learn more in our <a href="{{env 'CONSUL_DOCUMENTATION_URL'}}/guides/acl.html" target="_blank" rel="noopener noreferrer">documentation</a>.</p>
|
<p class="notice policy-management"><strong>Management</strong> This global-management token is built into Consul's policy system. You can apply this special policy to tokens for full access. This policy is not editable or removeable, but can be ignored by not applying it to any tokens. Learn more in our <a href="{{env 'CONSUL_DOCUMENTATION_URL'}}/guides/acl.html#builtin-policies" target="_blank" rel="noopener noreferrer">documentation</a>.</p>
|
||||||
<div>
|
<div>
|
||||||
<dl>
|
<dl>
|
||||||
<dt>Name</dt>
|
<dt>Name</dt>
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
<label class="type-text">
|
<label class="type-text">
|
||||||
<span>Rules <a href="{{env 'CONSUL_DOCUMENTATION_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a></span>
|
<span>Rules <a href="{{env 'CONSUL_DOCUMENTATION_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a></span>
|
||||||
{{code-editor class=(if item.error.Rules 'error') name='Rules' value=item.Rules onkeyup=(action 'change' 'Rules')}}
|
{{code-editor class=(if item.error.Rules 'error') name='Rules' syntax='hcl' value=item.Rules onkeyup=(action 'change' 'Rules')}}
|
||||||
</label>
|
</label>
|
||||||
{{#if create }}
|
{{#if create }}
|
||||||
<label class="type-text">
|
<label class="type-text">
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{#block-slot 'content'}}
|
{{#block-slot 'content'}}
|
||||||
{{#if (token/is-legacy item)}}
|
{{#if (token/is-legacy item)}}
|
||||||
<p class="notice info"><strong>Update.</strong> We've upgraded our ACL system by allowing you to create reusable policies which you can then apply to tokens. Don't worry, even though this token was written in the old style, it is still valid. However, we do recommend upgrading your old Tokens to the new style. Learn how in our <a href="{{env 'CONSUL_DOCUMENTATION_URL'}}/guides/acl.html" target="_blank" rel="noopener noreferrer">documentation</a>.</p>
|
<p class="notice info"><strong>Update.</strong> We have upgraded our ACL system by allowing you to create reusable policies which you can then apply to tokens. Don't worry, even though this token was written in the old style, it is still valid. However, we do recommend upgrading your old tokens to the new style. Learn how in our <a href="{{env 'CONSUL_DOCUMENTATION_URL'}}/guide/acl-migrate-tokens.html" target="_blank" rel="noopener noreferrer">documentation</a>.</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (not create) }}
|
{{#if (not create) }}
|
||||||
<div>
|
<div>
|
||||||
|
@ -25,8 +25,8 @@
|
|||||||
{{freetext-filter onchange=(action 'filter') value=filter.s placeholder="Search"}}
|
{{freetext-filter onchange=(action 'filter') value=filter.s placeholder="Search"}}
|
||||||
</form>
|
</form>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (find-by 'legacy' true items)}}
|
{{#if (token/is-legacy items)}}
|
||||||
<p class="notice info"><strong>Update.</strong> We've upgraded our ACL system by allowing you to create reusable Policies, which you can then apply to Tokens. Read more about the change and learn how to upgrade your legacy Tokens in our <a href="{{env 'CONSUL_DOCUMENTATION_URL'}}/guide/acl.html" target="_blank" rel="noopener noreferrer">documentation</a>.</p>
|
<p data-test-notification-update class="notice info"><strong>Update.</strong> We have upgraded our ACL System to allow the creation of reusable policies that can be applied to tokens. Read more about the changes and how to upgrade legacy tokens in our <a href="{{env 'CONSUL_DOCUMENTATION_URL'}}/guide/acl-migrate-tokens.html" target="_blank" rel="noopener noreferrer">documentation</a>.</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (gt filtered.length 0)}}
|
{{#if (gt filtered.length 0)}}
|
||||||
{{#tabular-collection
|
{{#tabular-collection
|
||||||
|
@ -3,7 +3,8 @@
|
|||||||
// has a valid token
|
// has a valid token
|
||||||
// Right now this is very acl specific, but is likely to be
|
// Right now this is very acl specific, but is likely to be
|
||||||
// made a bit more less specific
|
// made a bit more less specific
|
||||||
export default function(P = Promise) {
|
|
||||||
|
export default function(isValidServerError, P = Promise) {
|
||||||
return function(obj) {
|
return function(obj) {
|
||||||
const propName = Object.keys(obj)[0];
|
const propName = Object.keys(obj)[0];
|
||||||
const p = obj[propName];
|
const p = obj[propName];
|
||||||
@ -23,6 +24,12 @@ export default function(P = Promise) {
|
|||||||
[propName]: p
|
[propName]: p
|
||||||
.catch(function(e) {
|
.catch(function(e) {
|
||||||
switch (e.errors[0].status) {
|
switch (e.errors[0].status) {
|
||||||
|
case '500':
|
||||||
|
if (isValidServerError(e)) {
|
||||||
|
enable(true);
|
||||||
|
authorize(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case '403':
|
case '403':
|
||||||
enable(true);
|
enable(true);
|
||||||
authorize(false);
|
authorize(false);
|
||||||
|
12
ui-v2/app/utils/http/acl/is-valid-server-error.js
Normal file
12
ui-v2/app/utils/http/acl/is-valid-server-error.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// very specific error check just for one specific ACL case
|
||||||
|
// likely to be reused at a later date, so lets use the specific
|
||||||
|
// case we need right now as default
|
||||||
|
const UNKNOWN_METHOD_ERROR = "rpc error making call: rpc: can't find method ACL";
|
||||||
|
export default function(response = UNKNOWN_METHOD_ERROR) {
|
||||||
|
return function(e) {
|
||||||
|
if (e && e.errors && e.errors[0] && e.errors[0].detail) {
|
||||||
|
return e.errors[0].detail.indexOf(response) !== -1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
@setupApplicationTest
|
@setupApplicationTest
|
||||||
@ignore
|
|
||||||
Feature: components / acl filter: Acl Filter
|
Feature: components / acl filter: Acl Filter
|
||||||
In order to find the acl token I'm looking for easier
|
In order to find the acl token I'm looking for easier
|
||||||
As a user
|
As a user
|
||||||
@ -7,6 +6,7 @@ Feature: components / acl filter: Acl Filter
|
|||||||
Scenario: Filtering [Model]
|
Scenario: Filtering [Model]
|
||||||
Given 1 datacenter model with the value "dc-1"
|
Given 1 datacenter model with the value "dc-1"
|
||||||
And 2 [Model] models
|
And 2 [Model] models
|
||||||
|
And I'm using a legacy token
|
||||||
When I visit the [Page] page for yaml
|
When I visit the [Page] page for yaml
|
||||||
---
|
---
|
||||||
dc: dc-1
|
dc: dc-1
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
@setupApplicationTest
|
@setupApplicationTest
|
||||||
@ignore
|
|
||||||
Feature: dc / acls / list-order
|
Feature: dc / acls / list-order
|
||||||
In order to be able to find ACL tokens easier
|
In order to be able to find ACL tokens easier
|
||||||
As a user
|
As a user
|
||||||
@ -7,6 +6,7 @@ Feature: dc / acls / list-order
|
|||||||
|
|
||||||
Scenario: I have 10 randomly sorted tokens
|
Scenario: I have 10 randomly sorted tokens
|
||||||
Given 1 datacenter model with the value "datacenter"
|
Given 1 datacenter model with the value "datacenter"
|
||||||
|
And I'm using a legacy token
|
||||||
And 10 acl model from yaml
|
And 10 acl model from yaml
|
||||||
---
|
---
|
||||||
- Name: zz
|
- Name: zz
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
@setupApplicationTest
|
@setupApplicationTest
|
||||||
Feature: dc / acls / tokens / index: ACL Token List
|
Feature: dc / acls / tokens / index: ACL Token List
|
||||||
|
|
||||||
Scenario:
|
Scenario: I see the tokens
|
||||||
Given 1 datacenter model with the value "dc-1"
|
Given 1 datacenter model with the value "dc-1"
|
||||||
And 3 token models
|
And 3 token models
|
||||||
When I visit the tokens page for yaml
|
When I visit the tokens page for yaml
|
||||||
@ -9,5 +9,34 @@ Feature: dc / acls / tokens / index: ACL Token List
|
|||||||
dc: dc-1
|
dc: dc-1
|
||||||
---
|
---
|
||||||
Then the url should be /dc-1/acls/tokens
|
Then the url should be /dc-1/acls/tokens
|
||||||
And I click actions on the tokens
|
|
||||||
Then I see 3 token models
|
Then I see 3 token models
|
||||||
|
Scenario: I see the legacy message if I have one legacy token
|
||||||
|
Given 1 datacenter model with the value "dc-1"
|
||||||
|
And 3 token models from yaml
|
||||||
|
---
|
||||||
|
- Legacy: true
|
||||||
|
- Legacy: false
|
||||||
|
- Legacy: false
|
||||||
|
---
|
||||||
|
When I visit the tokens page for yaml
|
||||||
|
---
|
||||||
|
dc: dc-1
|
||||||
|
---
|
||||||
|
Then the url should be /dc-1/acls/tokens
|
||||||
|
And I see update
|
||||||
|
And I see 3 token models
|
||||||
|
Scenario: I don't see the legacy message if I have no legacy tokens
|
||||||
|
Given 1 datacenter model with the value "dc-1"
|
||||||
|
And 3 token models from yaml
|
||||||
|
---
|
||||||
|
- Legacy: false
|
||||||
|
- Legacy: false
|
||||||
|
- Legacy: false
|
||||||
|
---
|
||||||
|
When I visit the tokens page for yaml
|
||||||
|
---
|
||||||
|
dc: dc-1
|
||||||
|
---
|
||||||
|
Then the url should be /dc-1/acls/tokens
|
||||||
|
And I don't see update
|
||||||
|
And I see 3 token models
|
||||||
|
@ -6,7 +6,7 @@ Feature: dc / acls / tokens / legacy / update: ACL Token Update
|
|||||||
---
|
---
|
||||||
AccessorID: key
|
AccessorID: key
|
||||||
SecretID: secret
|
SecretID: secret
|
||||||
Rules: ''
|
Rules: 'key {}'
|
||||||
Type: client
|
Type: client
|
||||||
Policies: ~
|
Policies: ~
|
||||||
---
|
---
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
@setupApplicationTest
|
@setupApplicationTest
|
||||||
@ignore
|
|
||||||
Feature: dc / acls / update: ACL Update
|
Feature: dc / acls / update: ACL Update
|
||||||
Background:
|
Background:
|
||||||
Given 1 datacenter model with the value "datacenter"
|
Given 1 datacenter model with the value "datacenter"
|
||||||
|
And I'm using a legacy token
|
||||||
And 1 acl model from yaml
|
And 1 acl model from yaml
|
||||||
---
|
---
|
||||||
ID: key
|
ID: key
|
||||||
@ -45,6 +45,3 @@ Feature: dc / acls / update: ACL Update
|
|||||||
# @ignore
|
# @ignore
|
||||||
# Scenario: Rules can be edited/updated
|
# Scenario: Rules can be edited/updated
|
||||||
# Then ok
|
# Then ok
|
||||||
# @ignore
|
|
||||||
# Scenario: The feedback dialog says success or failure
|
|
||||||
# Then ok
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
@setupApplicationTest
|
@setupApplicationTest
|
||||||
@ignore
|
|
||||||
Feature: dc / acls / use: Using an ACL token
|
Feature: dc / acls / use: Using an ACL token
|
||||||
Background:
|
Background:
|
||||||
Given 1 datacenter model with the value "datacenter"
|
Given 1 datacenter model with the value "datacenter"
|
||||||
|
And I'm using a legacy token
|
||||||
And 1 acl model from yaml
|
And 1 acl model from yaml
|
||||||
---
|
---
|
||||||
ID: token
|
ID: token
|
||||||
@ -14,7 +14,7 @@ Feature: dc / acls / use: Using an ACL token
|
|||||||
---
|
---
|
||||||
Then I have settings like yaml
|
Then I have settings like yaml
|
||||||
---
|
---
|
||||||
token: ~
|
consul:token: '{"AccessorID":null,"SecretID":"id"}'
|
||||||
---
|
---
|
||||||
And I click actions on the acls
|
And I click actions on the acls
|
||||||
And I click use on the acls
|
And I click use on the acls
|
||||||
@ -23,7 +23,7 @@ Feature: dc / acls / use: Using an ACL token
|
|||||||
And "[data-notification]" has the "success" class
|
And "[data-notification]" has the "success" class
|
||||||
Then I have settings like yaml
|
Then I have settings like yaml
|
||||||
---
|
---
|
||||||
token: token
|
consul:token: '{"AccessorID":null,"SecretID":"token"}'
|
||||||
---
|
---
|
||||||
Scenario: Using an ACL token from the detail page
|
Scenario: Using an ACL token from the detail page
|
||||||
When I visit the acl page for yaml
|
When I visit the acl page for yaml
|
||||||
@ -33,7 +33,7 @@ Feature: dc / acls / use: Using an ACL token
|
|||||||
---
|
---
|
||||||
Then I have settings like yaml
|
Then I have settings like yaml
|
||||||
---
|
---
|
||||||
token: ~
|
consul:token: '{"AccessorID":null,"SecretID":"id"}'
|
||||||
---
|
---
|
||||||
And I click use
|
And I click use
|
||||||
And I click confirmUse
|
And I click confirmUse
|
||||||
@ -41,5 +41,5 @@ Feature: dc / acls / use: Using an ACL token
|
|||||||
And "[data-notification]" has the "success" class
|
And "[data-notification]" has the "success" class
|
||||||
Then I have settings like yaml
|
Then I have settings like yaml
|
||||||
---
|
---
|
||||||
token: token
|
consul:token: '{"AccessorID":null,"SecretID":"token"}'
|
||||||
---
|
---
|
||||||
|
@ -47,7 +47,7 @@ Feature: Page Navigation
|
|||||||
| kv | kvs | /dc-1/kv/necessitatibus-0/edit | /v1/session/info/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=dc-1 | /dc-1/kv |
|
| kv | kvs | /dc-1/kv/necessitatibus-0/edit | /v1/session/info/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=dc-1 | /dc-1/kv |
|
||||||
# | acl | acls | /dc-1/acls/anonymous | /v1/acl/info/anonymous?dc=dc-1 | /dc-1/acls |
|
# | acl | acls | /dc-1/acls/anonymous | /v1/acl/info/anonymous?dc=dc-1 | /dc-1/acls |
|
||||||
| intention | intentions | /dc-1/intentions/ee52203d-989f-4f7a-ab5a-2bef004164ca | /v1/internal/ui/services?dc=dc-1 | /dc-1/intentions |
|
| intention | intentions | /dc-1/intentions/ee52203d-989f-4f7a-ab5a-2bef004164ca | /v1/internal/ui/services?dc=dc-1 | /dc-1/intentions |
|
||||||
| token | tokens | /dc-1/acls/tokens/7ca5cd4d-4a7e-459d-b812-bb4078cecbd4 | /v1/acl/policies?dc=dc-1 | /dc-1/acls/tokens |
|
| token | tokens | /dc-1/acls/tokens/ee52203d-989f-4f7a-ab5a-2bef004164ca | /v1/acl/policies?dc=dc-1 | /dc-1/acls/tokens |
|
||||||
| policy | policies | /dc-1/acls/policies/ee52203d-989f-4f7a-ab5a-2bef004164ca | /v1/acl/tokens?policy=ee52203d-989f-4f7a-ab5a-2bef004164ca&dc=dc-1 | /dc-1/acls/policies |
|
| policy | policies | /dc-1/acls/policies/ee52203d-989f-4f7a-ab5a-2bef004164ca | /v1/acl/tokens?policy=ee52203d-989f-4f7a-ab5a-2bef004164ca&dc=dc-1 | /dc-1/acls/policies |
|
||||||
# | token | tokens | /dc-1/acls/tokens/00000000-0000-0000-0000-000000000000 | /v1/acl/token/00000000-0000-0000-0000-000000000000?dc=dc-1 | /dc-1/acls/tokens |
|
# | token | tokens | /dc-1/acls/tokens/00000000-0000-0000-0000-000000000000 | /v1/acl/token/00000000-0000-0000-0000-000000000000?dc=dc-1 | /dc-1/acls/tokens |
|
||||||
# | policy | policies | /dc-1/acls/policies/ee52203d-989f-4f7a-ab5a-2bef004164ca | /v1/acl/policy/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=dc-1 | /dc-1/acls/policies |
|
# | policy | policies | /dc-1/acls/policies/ee52203d-989f-4f7a-ab5a-2bef004164ca | /v1/acl/policy/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=dc-1 | /dc-1/acls/policies |
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
@setupApplicationTest
|
@setupApplicationTest
|
||||||
@ignore
|
|
||||||
Feature: token headers
|
Feature: token headers
|
||||||
In order to authenticate with tokens
|
In order to authenticate with tokens
|
||||||
As a user
|
As a user
|
||||||
@ -15,11 +14,15 @@ Feature: token headers
|
|||||||
---
|
---
|
||||||
Scenario: Set the token to [Token] and then navigate to the index page
|
Scenario: Set the token to [Token] and then navigate to the index page
|
||||||
Given 1 datacenter model with the value "datacenter"
|
Given 1 datacenter model with the value "datacenter"
|
||||||
When I visit the settings page
|
And the url "/v1/acl/tokens" responds with a 403 status
|
||||||
Then the url should be /settings
|
When I visit the tokens page for yaml
|
||||||
|
---
|
||||||
|
dc: datacenter
|
||||||
|
---
|
||||||
|
Then the url should be /datacenter/acls/tokens
|
||||||
Then I fill in with yaml
|
Then I fill in with yaml
|
||||||
---
|
---
|
||||||
token: [Token]
|
secret: [Token]
|
||||||
---
|
---
|
||||||
And I submit
|
And I submit
|
||||||
When I visit the index page
|
When I visit the index page
|
||||||
|
@ -54,7 +54,17 @@ export default {
|
|||||||
policy(visitable, submitable, deletable, cancelable, clickable, attribute, collection)
|
policy(visitable, submitable, deletable, cancelable, clickable, attribute, collection)
|
||||||
),
|
),
|
||||||
tokens: create(
|
tokens: create(
|
||||||
tokens(visitable, deletable, creatable, clickable, attribute, collection, freetextFilter)
|
tokens(
|
||||||
|
visitable,
|
||||||
|
submitable,
|
||||||
|
deletable,
|
||||||
|
creatable,
|
||||||
|
clickable,
|
||||||
|
attribute,
|
||||||
|
collection,
|
||||||
|
text,
|
||||||
|
freetextFilter
|
||||||
|
)
|
||||||
),
|
),
|
||||||
token: create(
|
token: create(
|
||||||
token(visitable, submitable, deletable, cancelable, clickable, attribute, collection)
|
token(visitable, submitable, deletable, cancelable, clickable, attribute, collection)
|
||||||
|
@ -1,6 +1,18 @@
|
|||||||
export default function(visitable, deletable, creatable, clickable, attribute, collection, filter) {
|
export default function(
|
||||||
return creatable({
|
visitable,
|
||||||
|
submitable,
|
||||||
|
deletable,
|
||||||
|
creatable,
|
||||||
|
clickable,
|
||||||
|
attribute,
|
||||||
|
collection,
|
||||||
|
text,
|
||||||
|
filter
|
||||||
|
) {
|
||||||
|
return submitable(
|
||||||
|
creatable({
|
||||||
visit: visitable('/:dc/acls/tokens'),
|
visit: visitable('/:dc/acls/tokens'),
|
||||||
|
update: text('[data-test-notification-update]'),
|
||||||
tokens: collection(
|
tokens: collection(
|
||||||
'[data-test-tabular-row]',
|
'[data-test-tabular-row]',
|
||||||
deletable({
|
deletable({
|
||||||
@ -12,5 +24,6 @@ export default function(visitable, deletable, creatable, clickable, attribute, c
|
|||||||
})
|
})
|
||||||
),
|
),
|
||||||
filter: filter,
|
filter: filter,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,9 @@ export default function(assert) {
|
|||||||
return create(number, model, data);
|
return create(number, model, data);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
.given(["I'm using a legacy token"], function(number, model, data) {
|
||||||
|
window.localStorage['consul:token'] = JSON.stringify({ AccessorID: null, SecretID: 'id' });
|
||||||
|
})
|
||||||
// TODO: Abstract this away from HTTP
|
// TODO: Abstract this away from HTTP
|
||||||
.given(['the url "$url" responds with a $status status'], function(url, status) {
|
.given(['the url "$url" responds with a $status status'], function(url, status) {
|
||||||
return api.server.respondWithStatus(url.split('?')[0], parseInt(status));
|
return api.server.respondWithStatus(url.split('?')[0], parseInt(status));
|
||||||
|
46
ui-v2/tests/unit/helpers/token/is-legacy-test.js
Normal file
46
ui-v2/tests/unit/helpers/token/is-legacy-test.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { isLegacy } from 'consul-ui/helpers/token/is-legacy';
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
|
||||||
|
module('Unit | Helper | token/is-legacy');
|
||||||
|
|
||||||
|
test('it returns true if the token has a Legacy=true', function(assert) {
|
||||||
|
const actual = isLegacy([{ Legacy: true }]);
|
||||||
|
assert.ok(actual);
|
||||||
|
});
|
||||||
|
test('it returns false if the token has a Legacy=false', function(assert) {
|
||||||
|
const actual = isLegacy([{ Legacy: false }]);
|
||||||
|
assert.notOk(actual);
|
||||||
|
});
|
||||||
|
test('it returns true if the token has Rules', function(assert) {
|
||||||
|
const actual = isLegacy([{ Rules: 'some rules' }]);
|
||||||
|
assert.ok(actual);
|
||||||
|
});
|
||||||
|
test('it returns false if the token has Rules but those rules are empty', function(assert) {
|
||||||
|
const actual = isLegacy([{ Rules: '' }]);
|
||||||
|
assert.notOk(actual);
|
||||||
|
});
|
||||||
|
test('it returns false if the token has Rules but those rules are empty', function(assert) {
|
||||||
|
const actual = isLegacy([{ Rules: null }]);
|
||||||
|
assert.notOk(actual);
|
||||||
|
});
|
||||||
|
// passing arrays
|
||||||
|
test("it returns false if things don't have Legacy or Rules", function(assert) {
|
||||||
|
const actual = isLegacy([[{}, {}]]);
|
||||||
|
assert.notOk(actual);
|
||||||
|
});
|
||||||
|
test('it returns true if the token has a Legacy=true', function(assert) {
|
||||||
|
const actual = isLegacy([[{}, { Legacy: true }]]);
|
||||||
|
assert.ok(actual);
|
||||||
|
});
|
||||||
|
test('it returns false if the token has a Legacy=false', function(assert) {
|
||||||
|
const actual = isLegacy([[{}, { Legacy: false }]]);
|
||||||
|
assert.notOk(actual);
|
||||||
|
});
|
||||||
|
test('it returns true if one token has Rules', function(assert) {
|
||||||
|
const actual = isLegacy([[{}, { Rules: 'some rules' }]]);
|
||||||
|
assert.ok(actual);
|
||||||
|
});
|
||||||
|
test('it returns false if tokens have no Rules, or has Rules but those rules are empty', function(assert) {
|
||||||
|
const actual = isLegacy([[{}, { Rules: '' }]]);
|
||||||
|
assert.notOk(actual);
|
||||||
|
});
|
@ -40,11 +40,12 @@ test('use persists the token and calls transitionTo correctly', function(assert)
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
const item = { ID: 'id' };
|
const item = { ID: 'id' };
|
||||||
|
const expectedToken = { AccessorID: null, SecretID: item.ID };
|
||||||
this.register(
|
this.register(
|
||||||
'service:settings',
|
'service:settings',
|
||||||
Service.extend({
|
Service.extend({
|
||||||
persist: function(actual) {
|
persist: function(actual) {
|
||||||
assert.equal(actual.token, item.ID);
|
assert.deepEqual(actual.token, expectedToken);
|
||||||
return Promise.resolve(actual);
|
return Promise.resolve(actual);
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -2,7 +2,7 @@ import { moduleFor, test } from 'ember-qunit';
|
|||||||
|
|
||||||
moduleFor('route:application', 'Unit | Route | application', {
|
moduleFor('route:application', 'Unit | Route | application', {
|
||||||
// Specify the other units that are required for this test.
|
// Specify the other units that are required for this test.
|
||||||
needs: ['service:repository/dc'],
|
needs: ['service:repository/dc', 'service:settings'],
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it exists', function(assert) {
|
test('it exists', function(assert) {
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
import createIsValidServerError from 'consul-ui/utils/http/acl/is-valid-server-error';
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
|
||||||
|
module('Unit | Utility | http/acl/is valid server error');
|
||||||
|
const createEmberDataError = function(response) {
|
||||||
|
return {
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
detail: response,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
test('it returns a function', function(assert) {
|
||||||
|
const isValidServerError = createIsValidServerError();
|
||||||
|
assert.ok(typeof isValidServerError === 'function');
|
||||||
|
});
|
||||||
|
test("it returns false if there is no 'correctly' formatted error", function(assert) {
|
||||||
|
const isValidServerError = createIsValidServerError();
|
||||||
|
assert.notOk(isValidServerError());
|
||||||
|
assert.notOk(isValidServerError({}));
|
||||||
|
assert.notOk(isValidServerError({ errors: {} }));
|
||||||
|
assert.notOk(isValidServerError({ errors: [{}] }));
|
||||||
|
assert.notOk(isValidServerError({ errors: [{ notDetail: '' }] }));
|
||||||
|
});
|
||||||
|
// don't go too crazy with these, just enough for sanity check, we are essentially testing indexOf
|
||||||
|
test("it returns false if the response doesn't contain the exact error response", function(assert) {
|
||||||
|
const isValidServerError = createIsValidServerError();
|
||||||
|
[
|
||||||
|
"pc error making call: rpc: can't find method ACL",
|
||||||
|
"rpc error making call: rpc: can't find method",
|
||||||
|
"rpc rror making call: rpc: can't find method ACL",
|
||||||
|
].forEach(function(response) {
|
||||||
|
const e = createEmberDataError(response);
|
||||||
|
assert.notOk(isValidServerError(e));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('it returns true if the response contains the exact error response', function(assert) {
|
||||||
|
const isValidServerError = createIsValidServerError();
|
||||||
|
[
|
||||||
|
"rpc error making call: rpc: can't find method ACL",
|
||||||
|
" rpc error making call: rpc: can't find method ACL",
|
||||||
|
"rpc error making call: rpc: rpc error making call: rpc: rpc error making call: rpc: can't find method ACL",
|
||||||
|
].forEach(function(response) {
|
||||||
|
const e = createEmberDataError(response);
|
||||||
|
assert.ok(isValidServerError(e));
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user