mirror of https://github.com/status-im/consul.git
ui: Model Layer for SSO Support (#7771)
* ui: Adds model layer required for SSO 1. oidc-provider ember-data triplet plus repo, plus addition of torii addon 2. Make blocking queries support a Cache-Control: no-cache header 3. Tweaks to the token model layer in preparation for SSO work * Fix up meta related Cache-Control tests * Add tests adapter tests for URL shapes * Reset Cache-Control to the original value, return something from logout
This commit is contained in:
parent
ed2444c0b5
commit
6d7a95f82d
|
@ -124,6 +124,10 @@ export default Adapter.extend({
|
|||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
// TODO: This comes originates from ember-data
|
||||
// This can be confusing if you need to use this with Promise.reject
|
||||
// Consider changing this to return the error and then
|
||||
// throw from the call site instead
|
||||
throw error;
|
||||
},
|
||||
query: function(store, type, query) {
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
import Adapter from './application';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
import { env } from 'consul-ui/env';
|
||||
import nonEmptySet from 'consul-ui/utils/non-empty-set';
|
||||
|
||||
let Namespace;
|
||||
if (env('CONSUL_NSPACES_ENABLED')) {
|
||||
Namespace = nonEmptySet('Namespace');
|
||||
} else {
|
||||
Namespace = () => ({});
|
||||
}
|
||||
export default Adapter.extend({
|
||||
env: service('env'),
|
||||
requestForQuery: function(request, { dc, ns, index }) {
|
||||
return request`
|
||||
GET /v1/internal/ui/oidc-auth-methods?${{ dc }}
|
||||
|
||||
${{
|
||||
index,
|
||||
...this.formatNspace(ns),
|
||||
}}
|
||||
`;
|
||||
},
|
||||
requestForQueryRecord: function(request, { dc, ns, id }) {
|
||||
if (typeof id === 'undefined') {
|
||||
throw new Error('You must specify an id');
|
||||
}
|
||||
return request`
|
||||
POST /v1/acl/oidc/auth-url?${{ dc }}
|
||||
Cache-Control: no-store
|
||||
|
||||
${{
|
||||
...Namespace(ns),
|
||||
AuthMethod: id,
|
||||
RedirectURI: `${this.env.var('CONSUL_BASE_UI_URL')}/torii/redirect.html`,
|
||||
}}
|
||||
`;
|
||||
},
|
||||
requestForAuthorize: function(request, { dc, ns, id, code, state }) {
|
||||
if (typeof id === 'undefined') {
|
||||
throw new Error('You must specify an id');
|
||||
}
|
||||
if (typeof code === 'undefined') {
|
||||
throw new Error('You must specify an code');
|
||||
}
|
||||
if (typeof state === 'undefined') {
|
||||
throw new Error('You must specify an state');
|
||||
}
|
||||
return request`
|
||||
POST /v1/acl/oidc/callback?${{ dc }}
|
||||
Cache-Control: no-store
|
||||
|
||||
${{
|
||||
...Namespace(ns),
|
||||
AuthMethod: id,
|
||||
Code: code,
|
||||
State: state,
|
||||
}}
|
||||
`;
|
||||
},
|
||||
requestForLogout: function(request, { id }) {
|
||||
if (typeof id === 'undefined') {
|
||||
throw new Error('You must specify an id');
|
||||
}
|
||||
return request`
|
||||
POST /v1/acl/logout
|
||||
Cache-Control: no-store
|
||||
X-Consul-Token: ${id}
|
||||
`;
|
||||
},
|
||||
authorize: function(store, type, id, snapshot) {
|
||||
return this.request(
|
||||
function(adapter, request, serialized, unserialized) {
|
||||
return adapter.requestForAuthorize(request, serialized, unserialized);
|
||||
},
|
||||
function(serializer, respond, serialized, unserialized) {
|
||||
return serializer.respondForAuthorize(respond, serialized, unserialized);
|
||||
},
|
||||
snapshot,
|
||||
type.modelName
|
||||
);
|
||||
},
|
||||
logout: function(store, type, id, snapshot) {
|
||||
return this.request(
|
||||
function(adapter, request, serialized, unserialized) {
|
||||
return adapter.requestForLogout(request, serialized, unserialized);
|
||||
},
|
||||
function(serializer, respond, serialized, unserialized) {
|
||||
// its ok to return nothing here for the moment at least
|
||||
return {};
|
||||
},
|
||||
snapshot,
|
||||
type.modelName
|
||||
);
|
||||
},
|
||||
});
|
|
@ -104,6 +104,7 @@ export default Adapter.extend({
|
|||
return request`
|
||||
GET /v1/acl/token/self?${{ dc }}
|
||||
X-Consul-Token: ${secret}
|
||||
Cache-Control: no-store
|
||||
|
||||
${{ index }}
|
||||
`;
|
||||
|
@ -132,7 +133,7 @@ export default Adapter.extend({
|
|||
return adapter.requestForSelf(request, serialized, data);
|
||||
},
|
||||
function(serializer, respond, serialized, data) {
|
||||
return serializer.respondForQueryRecord(respond, serialized, data);
|
||||
return serializer.respondForSelf(respond, serialized, data);
|
||||
},
|
||||
unserialized,
|
||||
type.modelName
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import Oauth2CodeProvider from 'torii/providers/oauth2-code';
|
||||
const NAME = 'oidc-with-url';
|
||||
const Provider = Oauth2CodeProvider.extend({
|
||||
name: NAME,
|
||||
buildUrl: function() {
|
||||
return this.baseUrl;
|
||||
},
|
||||
open: function(options) {
|
||||
const name = this.get('name'),
|
||||
url = this.buildUrl(),
|
||||
responseParams = ['state', 'code'],
|
||||
responseType = 'code';
|
||||
return this.get('popup')
|
||||
.open(url, responseParams, options)
|
||||
.then(function(authData) {
|
||||
// the same as the parent class but with an authorizationState added
|
||||
return {
|
||||
authorizationState: authData.state,
|
||||
authorizationCode: decodeURIComponent(authData[responseType]),
|
||||
provider: name,
|
||||
};
|
||||
});
|
||||
},
|
||||
close: function() {
|
||||
const popup = this.get('popup.remote') || {};
|
||||
if (typeof popup.close === 'function') {
|
||||
return popup.close();
|
||||
}
|
||||
},
|
||||
});
|
||||
export function initialize(application) {
|
||||
application.register(`torii-provider:${NAME}`, Provider);
|
||||
}
|
||||
|
||||
export default {
|
||||
initialize,
|
||||
};
|
|
@ -0,0 +1,15 @@
|
|||
import Model from 'ember-data/model';
|
||||
import attr from 'ember-data/attr';
|
||||
|
||||
export const PRIMARY_KEY = 'uid';
|
||||
export const SLUG_KEY = 'Name';
|
||||
export default Model.extend({
|
||||
[PRIMARY_KEY]: attr('string'),
|
||||
[SLUG_KEY]: attr('string'),
|
||||
meta: attr(),
|
||||
Datacenter: attr('string'),
|
||||
DisplayName: attr('string'),
|
||||
Kind: attr('string'),
|
||||
Namespace: attr('string'),
|
||||
AuthURL: attr('string'),
|
||||
});
|
|
@ -20,6 +20,7 @@ export default Model.extend({
|
|||
Description: attr('string', {
|
||||
defaultValue: '',
|
||||
}),
|
||||
meta: attr(),
|
||||
Datacenter: attr('string'),
|
||||
Namespace: attr('string'),
|
||||
Local: attr('boolean'),
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
HEADERS_DATACENTER as HTTP_HEADERS_DATACENTER,
|
||||
HEADERS_NAMESPACE as HTTP_HEADERS_NAMESPACE,
|
||||
} from 'consul-ui/utils/http/consul';
|
||||
import { CACHE_CONTROL as HTTP_HEADERS_CACHE_CONTROL } from 'consul-ui/utils/http/headers';
|
||||
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
|
||||
import { NSPACE_KEY } from 'consul-ui/models/nspace';
|
||||
import createFingerprinter from 'consul-ui/utils/create-fingerprinter';
|
||||
|
@ -101,7 +102,7 @@ export default Serializer.extend({
|
|||
// this could get confusing if you tried to override
|
||||
// say `normalizeQueryResponse`
|
||||
// TODO: consider creating a method for each one of the `normalize...Response` family
|
||||
normalizeResponse: function(store, primaryModelClass, payload, id, requestType) {
|
||||
normalizeResponse: function(store, modelClass, payload, id, requestType) {
|
||||
// Pick the meta/headers back off the payload and cleanup
|
||||
// before we go through serializing
|
||||
const headers = payload[HTTP_HEADERS_SYMBOL] || {};
|
||||
|
@ -114,34 +115,39 @@ export default Serializer.extend({
|
|||
// (which was the reason for the Symbol-like property earlier)
|
||||
// use a method modelled on ember-data methods so we have the opportunity to
|
||||
// do this on a per-model level
|
||||
const meta = this.normalizeMeta(
|
||||
store,
|
||||
primaryModelClass,
|
||||
headers,
|
||||
normalizedPayload,
|
||||
id,
|
||||
requestType
|
||||
);
|
||||
if (requestType === 'queryRecord') {
|
||||
const meta = this.normalizeMeta(store, modelClass, headers, normalizedPayload, id, requestType);
|
||||
if (requestType !== 'query') {
|
||||
normalizedPayload.meta = meta;
|
||||
}
|
||||
return this._super(
|
||||
const res = this._super(
|
||||
store,
|
||||
primaryModelClass,
|
||||
modelClass,
|
||||
{
|
||||
meta: meta,
|
||||
[primaryModelClass.modelName]: normalizedPayload,
|
||||
[modelClass.modelName]: normalizedPayload,
|
||||
},
|
||||
id,
|
||||
requestType
|
||||
);
|
||||
// If the result of the super normalizeResponse is undefined
|
||||
// its because the JSONSerializer (which REST inherits from)
|
||||
// doesn't recognise the requestType, in this case its likely to be an 'action'
|
||||
// request rather than a specific 'load me some data' one.
|
||||
// Therefore its ok to bypass the store here for the moment
|
||||
// we currently use this for self, but it also would affect any custom
|
||||
// methods that use a serializer in our custom service/store
|
||||
if (typeof res === 'undefined') {
|
||||
return payload;
|
||||
}
|
||||
return res;
|
||||
},
|
||||
timestamp: function() {
|
||||
return new Date().getTime();
|
||||
},
|
||||
normalizeMeta: function(store, primaryModelClass, headers, payload, id, requestType) {
|
||||
normalizeMeta: function(store, modelClass, headers, payload, id, requestType) {
|
||||
const meta = {
|
||||
cursor: headers[HTTP_HEADERS_INDEX],
|
||||
cacheControl: headers[HTTP_HEADERS_CACHE_CONTROL.toLowerCase()],
|
||||
cursor: headers[HTTP_HEADERS_INDEX.toLowerCase()],
|
||||
dc: headers[HTTP_HEADERS_DATACENTER.toLowerCase()],
|
||||
nspace: headers[HTTP_HEADERS_NAMESPACE.toLowerCase()],
|
||||
};
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import Serializer from './application';
|
||||
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/oidc-provider';
|
||||
|
||||
export default Serializer.extend({
|
||||
primaryKey: PRIMARY_KEY,
|
||||
slugKey: SLUG_KEY,
|
||||
respondForAuthorize: function(respond, serialized, data) {
|
||||
// we avoid the parent serializer here as it tries to create a
|
||||
// fingerprint for an 'action' request
|
||||
// but we still need to pass the headers through
|
||||
return respond((headers, body) => {
|
||||
return this.attachHeaders(headers, body, data);
|
||||
});
|
||||
},
|
||||
respondForQueryRecord: function(respond, query) {
|
||||
// add the name and nspace here so we can merge this
|
||||
// TODO: Look to see if we always want the merging functionality
|
||||
return this._super(
|
||||
cb =>
|
||||
respond((headers, body) =>
|
||||
cb(headers, {
|
||||
Name: query.id,
|
||||
Namespace: query.ns,
|
||||
...body,
|
||||
})
|
||||
),
|
||||
query
|
||||
);
|
||||
},
|
||||
});
|
|
@ -31,6 +31,9 @@ export default Serializer.extend(WithPolicies, WithRoles, {
|
|||
}
|
||||
return data;
|
||||
},
|
||||
respondForSelf: function(respond, query) {
|
||||
return this.respondForQueryRecord(respond, query);
|
||||
},
|
||||
respondForUpdateRecord: function(respond, serialized, data) {
|
||||
return this._super(
|
||||
cb =>
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
import Service, { inject as service } from '@ember/service';
|
||||
import { get, set } from '@ember/object';
|
||||
|
||||
import { CACHE_CONTROL, CONTENT_TYPE } from 'consul-ui/utils/http/headers';
|
||||
|
||||
import { HEADERS_TOKEN as CONSUL_TOKEN } from 'consul-ui/utils/http/consul';
|
||||
|
||||
import { env } from 'consul-ui/env';
|
||||
import getObjectPool from 'consul-ui/utils/get-object-pool';
|
||||
import Request from 'consul-ui/utils/http/request';
|
||||
|
@ -29,7 +33,7 @@ class HTTPError extends Error {
|
|||
}
|
||||
}
|
||||
const dispose = function(request) {
|
||||
if (request.headers()['content-type'] === 'text/event-stream') {
|
||||
if (request.headers()[CONTENT_TYPE.toLowerCase()] === 'text/event-stream') {
|
||||
const xhr = request.connection();
|
||||
// unsent and opened get aborted
|
||||
// headers and loading means wait for it
|
||||
|
@ -127,30 +131,40 @@ export default Service.extend({
|
|||
const [url, ...headerParts] = urlParts.join(' ').split('\n');
|
||||
|
||||
return client.settings.findBySlug('token').then(function(token) {
|
||||
const requestHeaders = createHeaders(headerParts);
|
||||
const headers = {
|
||||
// default to application/json
|
||||
...{
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
[CONTENT_TYPE]: 'application/json; charset=utf-8',
|
||||
},
|
||||
// add any application level headers
|
||||
...{
|
||||
'X-Consul-Token': typeof token.SecretID === 'undefined' ? '' : token.SecretID,
|
||||
[CONSUL_TOKEN]: typeof token.SecretID === 'undefined' ? '' : token.SecretID,
|
||||
},
|
||||
// but overwrite or add to those from anything in the specific request
|
||||
...createHeaders(headerParts),
|
||||
...requestHeaders,
|
||||
};
|
||||
// We use cache-control in the response
|
||||
// but we don't want to send it, but we artificially
|
||||
// tag it onto the response below if it is set on the request
|
||||
delete headers[CACHE_CONTROL];
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
const options = {
|
||||
url: url.trim(),
|
||||
method: method,
|
||||
contentType: headers['Content-Type'],
|
||||
contentType: headers[CONTENT_TYPE],
|
||||
// type: 'json',
|
||||
complete: function(xhr, textStatus) {
|
||||
client.complete(this.id);
|
||||
},
|
||||
success: function(response, status, xhr) {
|
||||
const headers = createHeaders(xhr.getAllResponseHeaders().split('\n'));
|
||||
if (typeof requestHeaders[CACHE_CONTROL] !== 'undefined') {
|
||||
// if cache-control was on the request, artificially tag
|
||||
// it back onto the response, also see comment above
|
||||
headers[CACHE_CONTROL] = requestHeaders[CACHE_CONTROL];
|
||||
}
|
||||
const respond = function(cb) {
|
||||
return cb(headers, response);
|
||||
};
|
||||
|
@ -191,7 +205,7 @@ export default Service.extend({
|
|||
// for write-like actions
|
||||
// potentially we should change things so you _have_ to do that
|
||||
// as doing it this way is a little magical
|
||||
if (method !== 'GET' && headers['Content-Type'].indexOf('json') !== -1) {
|
||||
if (method !== 'GET' && headers[CONTENT_TYPE].indexOf('json') !== -1) {
|
||||
options.data = JSON.stringify(body);
|
||||
} else {
|
||||
// TODO: Does this need urlencoding? Assuming jQuery does this
|
||||
|
@ -204,7 +218,7 @@ export default Service.extend({
|
|||
// also see adapters/kv content-types in requestForCreate/UpdateRecord
|
||||
// also see https://github.com/hashicorp/consul/issues/3804
|
||||
options.contentType = 'application/json; charset=utf-8';
|
||||
headers['Content-Type'] = options.contentType;
|
||||
headers[CONTENT_TYPE] = options.contentType;
|
||||
//
|
||||
options.beforeSend = function(xhr) {
|
||||
if (headers) {
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
import RepositoryService from 'consul-ui/services/repository';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { getOwner } from '@ember/application';
|
||||
import { set } from '@ember/object';
|
||||
|
||||
const modelName = 'oidc-provider';
|
||||
const OAUTH_PROVIDER_NAME = 'oidc-with-url';
|
||||
export default RepositoryService.extend({
|
||||
manager: service('torii'),
|
||||
init: function() {
|
||||
this._super(...arguments);
|
||||
this.provider = getOwner(this).lookup(`torii-provider:${OAUTH_PROVIDER_NAME}`);
|
||||
},
|
||||
getModelName: function() {
|
||||
return modelName;
|
||||
},
|
||||
authorize: function(id, code, state, dc, nspace, configuration = {}) {
|
||||
const query = {
|
||||
id: id,
|
||||
code: code,
|
||||
state: state,
|
||||
dc: dc,
|
||||
ns: nspace,
|
||||
};
|
||||
return this.store.authorize(this.getModelName(), query);
|
||||
},
|
||||
logout: function(id, code, state, dc, nspace, configuration = {}) {
|
||||
// TODO: Temporarily call this secret, as we alreayd do that with
|
||||
// self in the `store` look to see whether we should just call it id like
|
||||
// the rest
|
||||
const query = {
|
||||
id: id,
|
||||
};
|
||||
return this.store.logout(this.getModelName(), query);
|
||||
},
|
||||
close: function() {
|
||||
this.manager.close(OAUTH_PROVIDER_NAME);
|
||||
},
|
||||
findCodeByURL: function(src) {
|
||||
// TODO: Maybe move this to the provider itself
|
||||
set(this.provider, 'baseUrl', src);
|
||||
return this.manager.open(OAUTH_PROVIDER_NAME, {}).catch(e => {
|
||||
let err;
|
||||
switch (true) {
|
||||
case e.message.startsWith('remote was closed'):
|
||||
err = new Error('Remote was closed');
|
||||
err.statusCode = 499;
|
||||
break;
|
||||
default:
|
||||
err = new Error(e.message);
|
||||
err.statusCode = 500;
|
||||
}
|
||||
this.store.adapterFor(this.getModelName()).error(err);
|
||||
});
|
||||
},
|
||||
});
|
|
@ -21,7 +21,16 @@ export default Store.extend({
|
|||
self: function(modelName, token) {
|
||||
// TODO: no normalization, type it properly for the moment
|
||||
const adapter = this.adapterFor(modelName);
|
||||
return adapter.self(this, { modelName: modelName }, token.secret, token);
|
||||
const serializer = this.serializerFor(modelName);
|
||||
const modelClass = { modelName: modelName };
|
||||
// self is the only custom store method that goes through the serializer for the moment
|
||||
// this means it will have its meta data set correctly
|
||||
// if other methods need meta adding, then this should be carried over to
|
||||
// other methods. Ideally this would have been done from the outset
|
||||
// TODO: Carry this over to the other methods ^
|
||||
return adapter
|
||||
.self(this, modelClass, token.secret, token)
|
||||
.then(payload => serializer.normalizeResponse(this, modelClass, payload, token, 'self'));
|
||||
},
|
||||
//
|
||||
// TODO: This one is only for nodes, should fail nicely if you call it
|
||||
|
@ -31,10 +40,21 @@ export default Store.extend({
|
|||
const adapter = this.adapterFor(modelName);
|
||||
return adapter.queryLeader(this, { modelName: modelName }, null, query);
|
||||
},
|
||||
// TODO: This one is only for ACL, should fail nicely if you call it
|
||||
// for anything other than ACLs for good DX
|
||||
// TODO: This one is only for nspaces and OIDC, should fail nicely if you call it
|
||||
// for anything other than nspaces/OIDC for good DX
|
||||
authorize: function(modelName, query = {}) {
|
||||
// TODO: no normalization, type it properly for the moment
|
||||
return this.adapterFor(modelName).authorize(this, { modelName: modelName }, null, query);
|
||||
const adapter = this.adapterFor(modelName);
|
||||
const serializer = this.serializerFor(modelName);
|
||||
const modelClass = { modelName: modelName };
|
||||
return adapter
|
||||
.authorize(this, modelClass, null, query)
|
||||
.then(payload =>
|
||||
serializer.normalizeResponse(this, modelClass, payload, undefined, 'authorize')
|
||||
);
|
||||
},
|
||||
logout: function(modelName, query = {}) {
|
||||
const adapter = this.adapterFor(modelName);
|
||||
const modelClass = { modelName: modelName };
|
||||
return adapter.logout(this, modelClass, query.id, query);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -96,10 +96,13 @@ export default function(EventSource, backoff = create5xxBackoff()) {
|
|||
// pick off the `cursor` from the meta and add it to configuration
|
||||
// along with cursor validation
|
||||
configuration.cursor = validateCursor(meta.cursor, configuration.cursor);
|
||||
configuration.cacheControl = meta.cacheControl;
|
||||
}
|
||||
this.currentEvent = event;
|
||||
this.dispatchEvent(this.currentEvent);
|
||||
const throttledResolve = throttle(configuration, this.currentEvent, this.previousEvent);
|
||||
if ((configuration.cacheControl || '').indexOf('no-store') === -1) {
|
||||
this.currentEvent = event;
|
||||
}
|
||||
this.dispatchEvent(event);
|
||||
const throttledResolve = throttle(configuration, event, this.previousEvent);
|
||||
this.previousEvent = this.currentEvent;
|
||||
return throttledResolve(result);
|
||||
});
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
// TODO: Need to make all these headers capital case
|
||||
export const HEADERS_NAMESPACE = 'X-Consul-Namespace';
|
||||
export const HEADERS_DATACENTER = 'x-consul-datacenter';
|
||||
export const HEADERS_INDEX = 'x-consul-index';
|
||||
export const HEADERS_DIGEST = 'x-consul-contenthash';
|
||||
export const HEADERS_DATACENTER = 'X-Consul-Datacenter';
|
||||
export const HEADERS_INDEX = 'X-Consul-Index';
|
||||
export const HEADERS_TOKEN = 'X-Consul-Token';
|
||||
export const HEADERS_DIGEST = 'X-Consul-ContentHash';
|
||||
//
|
||||
export const HEADERS_SYMBOL = '__consul_ui_http_headers__';
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
export const CACHE_CONTROL = 'Cache-Control';
|
||||
export const CONTENT_TYPE = 'Content-Type';
|
|
@ -123,7 +123,8 @@
|
|||
"prettier": "^1.10.2",
|
||||
"qunit-dom": "^1.0.0",
|
||||
"tape": "^4.13.0",
|
||||
"text-encoding": "^0.7.0"
|
||||
"text-encoding": "^0.7.0",
|
||||
"torii": "^0.10.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "10.* || >= 12"
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
import { env } from '../../../env';
|
||||
const shouldHaveNspace = function(nspace) {
|
||||
return typeof nspace !== 'undefined' && env('CONSUL_NSPACES_ENABLED');
|
||||
};
|
||||
module('Integration | Adapter | oidc-provider', function(hooks) {
|
||||
setupTest(hooks);
|
||||
const dc = 'dc-1';
|
||||
const id = 'slug';
|
||||
const undefinedNspace = 'default';
|
||||
[undefinedNspace, 'team-1', undefined].forEach(nspace => {
|
||||
test('requestForQuery returns the correct url/method', function(assert) {
|
||||
const adapter = this.owner.lookup('adapter:oidc-provider');
|
||||
const client = this.owner.lookup('service:client/http');
|
||||
const expected = `GET /v1/internal/ui/oidc-auth-methods?dc=${dc}`;
|
||||
let actual = adapter.requestForQuery(client.url, {
|
||||
dc: dc,
|
||||
ns: nspace,
|
||||
});
|
||||
actual = actual.split('\n');
|
||||
assert.equal(actual.shift().trim(), expected);
|
||||
actual = actual.join('\n').trim();
|
||||
assert.equal(actual, `${shouldHaveNspace(nspace) ? `ns=${nspace}` : ``}`);
|
||||
});
|
||||
test('requestForQueryRecord returns the correct url/method', function(assert) {
|
||||
const adapter = this.owner.lookup('adapter:oidc-provider');
|
||||
const client = this.owner.lookup('service:client/http');
|
||||
const expected = `POST /v1/acl/oidc/auth-url?dc=${dc}`;
|
||||
const actual = adapter
|
||||
.requestForQueryRecord(client.url, {
|
||||
dc: dc,
|
||||
id: id,
|
||||
ns: nspace,
|
||||
})
|
||||
.split('\n')
|
||||
.shift();
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
test("requestForQueryRecord throws if you don't specify an id", function(assert) {
|
||||
const adapter = this.owner.lookup('adapter:oidc-provider');
|
||||
const client = this.owner.lookup('service:client/http');
|
||||
assert.throws(function() {
|
||||
adapter.requestForQueryRecord(client.url, {
|
||||
dc: dc,
|
||||
});
|
||||
});
|
||||
});
|
||||
test('requestForAuthorize returns the correct url/method', function(assert) {
|
||||
const adapter = this.owner.lookup('adapter:oidc-provider');
|
||||
const client = this.owner.lookup('service:client/http');
|
||||
const expected = `POST /v1/acl/oidc/callback?dc=${dc}`;
|
||||
const actual = adapter
|
||||
.requestForAuthorize(client.url, {
|
||||
dc: dc,
|
||||
id: id,
|
||||
code: 'code',
|
||||
state: 'state',
|
||||
ns: nspace,
|
||||
})
|
||||
.split('\n')
|
||||
.shift();
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
test('requestForLogout returns the correct url/method', function(assert) {
|
||||
const adapter = this.owner.lookup('adapter:oidc-provider');
|
||||
const client = this.owner.lookup('service:client/http');
|
||||
const expected = `POST /v1/acl/logout`;
|
||||
const actual = adapter
|
||||
.requestForLogout(client.url, {
|
||||
id: id,
|
||||
})
|
||||
.split('\n')
|
||||
.shift();
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -28,6 +28,7 @@ test('findBySlug returns the correct data for item endpoint', function(assert) {
|
|||
Datacenter: dc,
|
||||
uid: `["default","${dc}","${id}"]`,
|
||||
meta: {
|
||||
cacheControl: undefined,
|
||||
cursor: undefined,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -63,6 +63,7 @@ test('findBySlug returns the correct data for item endpoint', function(assert) {
|
|||
Datacenter: dc,
|
||||
uid: `["${nspace}","${dc}","${item.ID}"]`,
|
||||
meta: {
|
||||
cacheControl: undefined,
|
||||
cursor: undefined,
|
||||
dc: dc,
|
||||
nspace: nspace,
|
||||
|
|
|
@ -71,6 +71,7 @@ const undefinedNspace = 'default';
|
|||
Namespace: item.Namespace || undefinedNspace,
|
||||
uid: `["${item.Namespace || undefinedNspace}","${dc}","${item.ID}"]`,
|
||||
meta: {
|
||||
cacheControl: undefined,
|
||||
cursor: undefined,
|
||||
dc: dc,
|
||||
nspace: item.Namespace || undefinedNspace,
|
||||
|
|
|
@ -118,6 +118,7 @@ const undefinedNspace = 'default';
|
|||
service.Tags = [...new Set(payload.Nodes[0].Service.Tags)];
|
||||
service.Namespace = payload.Namespace;
|
||||
service.meta = {
|
||||
cacheControl: undefined,
|
||||
cursor: undefined,
|
||||
dc: dc,
|
||||
nspace: payload.Namespace,
|
||||
|
|
|
@ -69,6 +69,12 @@ const undefinedNspace = 'default';
|
|||
CreateTime: new Date(item.CreateTime),
|
||||
Namespace: item.Namespace || undefinedNspace,
|
||||
uid: `["${item.Namespace || undefinedNspace}","${dc}","${item.AccessorID}"]`,
|
||||
meta: {
|
||||
cacheControl: undefined,
|
||||
cursor: undefined,
|
||||
dc: dc,
|
||||
nspace: item.Namespace || undefinedNspace,
|
||||
},
|
||||
Policies: createPolicies(item),
|
||||
});
|
||||
})
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Adapter | oidc-provider', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let adapter = this.owner.lookup('adapter:oidc-provider');
|
||||
assert.ok(adapter);
|
||||
});
|
||||
});
|
|
@ -12420,6 +12420,13 @@ toidentifier@1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
|
||||
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
|
||||
|
||||
torii@^0.10.1:
|
||||
version "0.10.1"
|
||||
resolved "https://registry.yarnpkg.com/torii/-/torii-0.10.1.tgz#caad0a81e82189fc0483b65e68ee28041ad3590f"
|
||||
integrity sha512-csUz/coeSumt9FjyIXLpRj0ii7TfH3fUm3x9rdf+XXnJ0tVTKqwCRynwY0HKuNkGzACyR84hog3B9a8BQefBHA==
|
||||
dependencies:
|
||||
ember-cli-babel "^6.11.0"
|
||||
|
||||
tough-cookie@^2.3.3, tough-cookie@^2.4.3, tough-cookie@~2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
|
||||
|
|
Loading…
Reference in New Issue