mirror of https://github.com/status-im/consul.git
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']));
|
||||
}
|
||||
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(' ')));
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,9 +1,19 @@
|
|||
import { helper } from '@ember/component/helper';
|
||||
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) {
|
||||
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);
|
||||
|
|
|
@ -8,13 +8,34 @@ export default Mixin.create(WithBlockingActions, {
|
|||
actions: {
|
||||
use: function(item) {
|
||||
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')
|
||||
.persist({ token: get(item, 'ID') })
|
||||
.persist({
|
||||
token: {
|
||||
AccessorID: null,
|
||||
SecretID: get(item, 'ID'),
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
return this.transitionTo('dc.services');
|
||||
});
|
||||
}, '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) {
|
||||
return get(this, 'feedback').execute(() => {
|
||||
return get(this, 'repo')
|
||||
|
|
|
@ -12,6 +12,7 @@ export default Route.extend({
|
|||
this._super(...arguments);
|
||||
},
|
||||
repo: service('repository/dc'),
|
||||
settings: service('settings'),
|
||||
actions: {
|
||||
loading: function(transition, originRoute) {
|
||||
let dc = null;
|
||||
|
@ -58,13 +59,17 @@ export default Route.extend({
|
|||
// 403 page
|
||||
// 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
|
||||
const model = this.modelFor('dc');
|
||||
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 === '') {
|
||||
error.message = 'Error';
|
||||
}
|
||||
const model = this.modelFor('dc');
|
||||
hash({
|
||||
error: error,
|
||||
dc:
|
||||
|
|
|
@ -2,7 +2,6 @@ import Route from '@ember/routing/route';
|
|||
import { get } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
||||
|
||||
export default Route.extend(WithBlockingActions, {
|
||||
settings: service('settings'),
|
||||
feedback: service('feedback'),
|
||||
|
@ -21,8 +20,15 @@ export default Route.extend(WithBlockingActions, {
|
|||
SecretID: secret,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.refresh();
|
||||
.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();
|
||||
}
|
||||
});
|
||||
});
|
||||
}, 'authorize');
|
||||
|
|
|
@ -7,6 +7,7 @@ import WithAclActions from 'consul-ui/mixins/acl/with-actions';
|
|||
|
||||
export default Route.extend(WithAclActions, {
|
||||
repo: service('repository/acl'),
|
||||
settings: service('settings'),
|
||||
queryParams: {
|
||||
s: {
|
||||
as: 'filter',
|
||||
|
@ -14,12 +15,24 @@ export default Route.extend(WithAclActions, {
|
|||
},
|
||||
},
|
||||
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) {
|
||||
return hash({
|
||||
isLoading: false,
|
||||
items: get(this, 'repo').findAllByDatacenter(this.modelFor('dc').dc.Name),
|
||||
token: get(this, 'settings').findBySlug('token'),
|
||||
});
|
||||
},
|
||||
setupController: function(controller, model) {
|
||||
|
|
|
@ -12,6 +12,19 @@ export default Route.extend(WithTokenActions, {
|
|||
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) {
|
||||
const repo = get(this, 'repo');
|
||||
return hash({
|
||||
|
|
|
@ -4,7 +4,9 @@ import { typeOf } from '@ember/utils';
|
|||
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/policy';
|
||||
import { Promise } from 'rsvp';
|
||||
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';
|
||||
export default Service.extend({
|
||||
getModelName: function() {
|
||||
|
|
|
@ -4,7 +4,9 @@ import { typeOf } from '@ember/utils';
|
|||
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/token';
|
||||
import { Promise } from 'rsvp';
|
||||
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';
|
||||
export default Service.extend({
|
||||
getModelName: function() {
|
||||
|
@ -20,10 +22,22 @@ export default Service.extend({
|
|||
return status(obj);
|
||||
},
|
||||
self: function(secret, dc) {
|
||||
return get(this, 'store').self(this.getModelName(), {
|
||||
secret: secret,
|
||||
dc: dc,
|
||||
});
|
||||
return get(this, 'store')
|
||||
.self(this.getModelName(), {
|
||||
secret: secret,
|
||||
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) {
|
||||
return get(this, 'store').clone(this.getModelName(), get(item, PRIMARY_KEY));
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
</div>
|
||||
<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>
|
||||
{{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>
|
||||
{{#if create }}
|
||||
<label class="type-text">
|
||||
|
|
|
@ -16,6 +16,12 @@
|
|||
{{else}}
|
||||
There was an error deleting your ACL token.
|
||||
{{/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')}}
|
||||
{{#if (eq status 'success') }}
|
||||
Now using new ACL token.
|
||||
|
|
|
@ -45,9 +45,16 @@
|
|||
<li>
|
||||
<a data-test-edit href={{href-to 'dc.acls.edit' item.ID}}>Edit</a>
|
||||
</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>
|
||||
<a data-test-use onclick={{queue (action confirm 'use' item) (action change)}}>Use</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
<li>
|
||||
<a data-test-clone onclick={{action 'sendClone' item}}>Clone</a>
|
||||
</li>
|
||||
|
@ -63,6 +70,8 @@
|
|||
<p>
|
||||
{{#if (eq name 'delete')}}
|
||||
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')}}
|
||||
Are you sure you want to use this ACL token?
|
||||
{{/if}}
|
||||
|
@ -70,6 +79,8 @@
|
|||
<button type="button" class="type-delete" {{action execute}}>
|
||||
{{#if (eq name 'delete')}}
|
||||
Confirm Delete
|
||||
{{else if (eq name 'logout')}}
|
||||
Confirm Logout
|
||||
{{ else if (eq name 'use')}}
|
||||
Confirm Use
|
||||
{{/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>
|
||||
<dl>
|
||||
<dt>Name</dt>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
{{/if}}
|
||||
<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>
|
||||
{{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>
|
||||
{{#if create }}
|
||||
<label class="type-text">
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
{{/block-slot}}
|
||||
{{#block-slot 'content'}}
|
||||
{{#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 (not create) }}
|
||||
<div>
|
||||
|
|
|
@ -25,8 +25,8 @@
|
|||
{{freetext-filter onchange=(action 'filter') value=filter.s placeholder="Search"}}
|
||||
</form>
|
||||
{{/if}}
|
||||
{{#if (find-by 'legacy' true 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>
|
||||
{{#if (token/is-legacy items)}}
|
||||
<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 (gt filtered.length 0)}}
|
||||
{{#tabular-collection
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
// has a valid token
|
||||
// Right now this is very acl specific, but is likely to be
|
||||
// made a bit more less specific
|
||||
export default function(P = Promise) {
|
||||
|
||||
export default function(isValidServerError, P = Promise) {
|
||||
return function(obj) {
|
||||
const propName = Object.keys(obj)[0];
|
||||
const p = obj[propName];
|
||||
|
@ -23,6 +24,12 @@ export default function(P = Promise) {
|
|||
[propName]: p
|
||||
.catch(function(e) {
|
||||
switch (e.errors[0].status) {
|
||||
case '500':
|
||||
if (isValidServerError(e)) {
|
||||
enable(true);
|
||||
authorize(false);
|
||||
}
|
||||
break;
|
||||
case '403':
|
||||
enable(true);
|
||||
authorize(false);
|
||||
|
|
|
@ -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
|
||||
@ignore
|
||||
Feature: components / acl filter: Acl Filter
|
||||
In order to find the acl token I'm looking for easier
|
||||
As a user
|
||||
|
@ -7,6 +6,7 @@ Feature: components / acl filter: Acl Filter
|
|||
Scenario: Filtering [Model]
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 2 [Model] models
|
||||
And I'm using a legacy token
|
||||
When I visit the [Page] page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
@setupApplicationTest
|
||||
@ignore
|
||||
Feature: dc / acls / list-order
|
||||
In order to be able to find ACL tokens easier
|
||||
As a user
|
||||
|
@ -7,6 +6,7 @@ Feature: dc / acls / list-order
|
|||
|
||||
Scenario: I have 10 randomly sorted tokens
|
||||
Given 1 datacenter model with the value "datacenter"
|
||||
And I'm using a legacy token
|
||||
And 10 acl model from yaml
|
||||
---
|
||||
- Name: zz
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@setupApplicationTest
|
||||
Feature: dc / acls / tokens / index: ACL Token List
|
||||
|
||||
Scenario:
|
||||
Scenario: I see the tokens
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 3 token models
|
||||
When I visit the tokens page for yaml
|
||||
|
@ -9,5 +9,34 @@ Feature: dc / acls / tokens / index: ACL Token List
|
|||
dc: dc-1
|
||||
---
|
||||
Then the url should be /dc-1/acls/tokens
|
||||
And I click actions on the tokens
|
||||
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
|
||||
SecretID: secret
|
||||
Rules: ''
|
||||
Rules: 'key {}'
|
||||
Type: client
|
||||
Policies: ~
|
||||
---
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
@setupApplicationTest
|
||||
@ignore
|
||||
Feature: dc / acls / update: ACL Update
|
||||
Background:
|
||||
Given 1 datacenter model with the value "datacenter"
|
||||
And I'm using a legacy token
|
||||
And 1 acl model from yaml
|
||||
---
|
||||
ID: key
|
||||
|
@ -45,6 +45,3 @@ Feature: dc / acls / update: ACL Update
|
|||
# @ignore
|
||||
# Scenario: Rules can be edited/updated
|
||||
# Then ok
|
||||
# @ignore
|
||||
# Scenario: The feedback dialog says success or failure
|
||||
# Then ok
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
@setupApplicationTest
|
||||
@ignore
|
||||
Feature: dc / acls / use: Using an ACL token
|
||||
Background:
|
||||
Given 1 datacenter model with the value "datacenter"
|
||||
And I'm using a legacy token
|
||||
And 1 acl model from yaml
|
||||
---
|
||||
ID: token
|
||||
|
@ -14,7 +14,7 @@ Feature: dc / acls / use: Using an ACL token
|
|||
---
|
||||
Then I have settings like yaml
|
||||
---
|
||||
token: ~
|
||||
consul:token: '{"AccessorID":null,"SecretID":"id"}'
|
||||
---
|
||||
And I click actions 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
|
||||
Then I have settings like yaml
|
||||
---
|
||||
token: token
|
||||
consul:token: '{"AccessorID":null,"SecretID":"token"}'
|
||||
---
|
||||
Scenario: Using an ACL token from the detail page
|
||||
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
|
||||
---
|
||||
token: ~
|
||||
consul:token: '{"AccessorID":null,"SecretID":"id"}'
|
||||
---
|
||||
And I click use
|
||||
And I click confirmUse
|
||||
|
@ -41,5 +41,5 @@ Feature: dc / acls / use: Using an ACL token
|
|||
And "[data-notification]" has the "success" class
|
||||
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 |
|
||||
# | 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 |
|
||||
| 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 |
|
||||
# | 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 |
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
@setupApplicationTest
|
||||
@ignore
|
||||
Feature: token headers
|
||||
In order to authenticate with tokens
|
||||
As a user
|
||||
|
@ -15,11 +14,15 @@ Feature: token headers
|
|||
---
|
||||
Scenario: Set the token to [Token] and then navigate to the index page
|
||||
Given 1 datacenter model with the value "datacenter"
|
||||
When I visit the settings page
|
||||
Then the url should be /settings
|
||||
And the url "/v1/acl/tokens" responds with a 403 status
|
||||
When I visit the tokens page for yaml
|
||||
---
|
||||
dc: datacenter
|
||||
---
|
||||
Then the url should be /datacenter/acls/tokens
|
||||
Then I fill in with yaml
|
||||
---
|
||||
token: [Token]
|
||||
secret: [Token]
|
||||
---
|
||||
And I submit
|
||||
When I visit the index page
|
||||
|
|
|
@ -54,7 +54,17 @@ export default {
|
|||
policy(visitable, submitable, deletable, cancelable, clickable, attribute, collection)
|
||||
),
|
||||
tokens: create(
|
||||
tokens(visitable, deletable, creatable, clickable, attribute, collection, freetextFilter)
|
||||
tokens(
|
||||
visitable,
|
||||
submitable,
|
||||
deletable,
|
||||
creatable,
|
||||
clickable,
|
||||
attribute,
|
||||
collection,
|
||||
text,
|
||||
freetextFilter
|
||||
)
|
||||
),
|
||||
token: create(
|
||||
token(visitable, submitable, deletable, cancelable, clickable, attribute, collection)
|
||||
|
|
|
@ -1,16 +1,29 @@
|
|||
export default function(visitable, deletable, creatable, clickable, attribute, collection, filter) {
|
||||
return creatable({
|
||||
visit: visitable('/:dc/acls/tokens'),
|
||||
tokens: collection(
|
||||
'[data-test-tabular-row]',
|
||||
deletable({
|
||||
id: attribute('data-test-token', '[data-test-token]'),
|
||||
token: clickable('a'),
|
||||
actions: clickable('label'),
|
||||
use: clickable('[data-test-use]'),
|
||||
confirmUse: clickable('button.type-delete'),
|
||||
})
|
||||
),
|
||||
filter: filter,
|
||||
});
|
||||
export default function(
|
||||
visitable,
|
||||
submitable,
|
||||
deletable,
|
||||
creatable,
|
||||
clickable,
|
||||
attribute,
|
||||
collection,
|
||||
text,
|
||||
filter
|
||||
) {
|
||||
return submitable(
|
||||
creatable({
|
||||
visit: visitable('/:dc/acls/tokens'),
|
||||
update: text('[data-test-notification-update]'),
|
||||
tokens: collection(
|
||||
'[data-test-tabular-row]',
|
||||
deletable({
|
||||
id: attribute('data-test-token', '[data-test-token]'),
|
||||
token: clickable('a'),
|
||||
actions: clickable('label'),
|
||||
use: clickable('[data-test-use]'),
|
||||
confirmUse: clickable('button.type-delete'),
|
||||
})
|
||||
),
|
||||
filter: filter,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -77,6 +77,9 @@ export default function(assert) {
|
|||
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
|
||||
.given(['the url "$url" responds with a $status status'], function(url, status) {
|
||||
return api.server.respondWithStatus(url.split('?')[0], parseInt(status));
|
||||
|
|
|
@ -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 expectedToken = { AccessorID: null, SecretID: item.ID };
|
||||
this.register(
|
||||
'service:settings',
|
||||
Service.extend({
|
||||
persist: function(actual) {
|
||||
assert.equal(actual.token, item.ID);
|
||||
assert.deepEqual(actual.token, expectedToken);
|
||||
return Promise.resolve(actual);
|
||||
},
|
||||
})
|
||||
|
|
|
@ -2,7 +2,7 @@ import { moduleFor, test } from 'ember-qunit';
|
|||
|
||||
moduleFor('route:application', 'Unit | Route | application', {
|
||||
// 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) {
|
||||
|
|
|
@ -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…
Reference in New Issue