Icu 6794 refactor attributes using the newer is nested attribute annotation (#1481)

This commit is contained in:
Zhihe Li authored and GitHub committed 2022-11-23 14:40:07 -05:00
1 parent ff6e2bb4cf
commit ff7a1fedea
15 files changed
+325 -259

No files matched your search

@@ -49,4 +49,26 @@ export default class GeneratedAuthMethodModel extends BaseModel {
description: 'Current version number of this resource.',
})
version;
@attr('string', { readOnly: true, isNestedAttribute: true }) state;
@attr('string', { isNestedAttribute: true }) issuer;
@attr('string', { isNestedAttribute: true }) client_id;
@attr('string', { isNestedAttribute: true }) client_secret;
@attr('string', { readOnly: true, isNestedAttribute: true })
client_secret_hmac;
@attr('number', { isNestedAttribute: true }) max_age;
@attr('string', { isNestedAttribute: true }) api_url_prefix;
@attr('string', { readOnly: true, isNestedAttribute: true }) callback_url;
@attr('boolean', { isNestedAttribute: true })
disable_discovered_config_validation;
@attr('boolean', { isNestedAttribute: true }) dry_run;
}
+33 -30
View File
@@ -1,6 +1,5 @@
import GeneratedAuthMethodModel from '../generated/models/auth-method';
import { attr } from '@ember-data/model';
import { fragment } from 'ember-data-model-fragments/attributes';
import { fragmentArray } from 'ember-data-model-fragments/attributes';
import { equal } from '@ember/object/computed';
/**
@@ -28,33 +27,37 @@ export const options = {
};
export default class AuthMethodModel extends GeneratedAuthMethodModel {
// =error attributes
// These attributes exist solely to capture errors on nested fields.
// See the application adapter's error normalization method for
// more information.
@attr('string', { readOnly: true }) attributes_state;
@attr('string', { readOnly: true }) attributes_issuer;
@attr('string', { readOnly: true }) attributes_client_id;
@attr('string', { readOnly: true }) attributes_client_secret;
@attr('string', { readOnly: true }) attributes_max_age;
@attr('string', { readOnly: true }) attributes_api_url_prefix;
@attr('string', { readOnly: true })
attributes_disable_discovered_config_validation;
@attr('string', { readOnly: true }) attributes_dry_run;
@attr('string', { readOnly: true }) attributes_account_claim_maps;
@attr('string', { readOnly: true }) attributes_claims_scopes;
@attr('string', { readOnly: true }) attributes_signing_algorithms;
@attr('string', { readOnly: true }) attributes_allowed_audiences;
@attr('string', { readOnly: true }) attributes_idp_ca_certs;
// =attributes
/**
* Attributes of this resource, if any, represented as a JSON fragment.
* @type {FragmentAuthMethodAttributesModel}
*/
@fragment('fragment-auth-method-attributes', { defaultValue: {} }) attributes;
@fragmentArray('fragment-auth-method-attributes-account-claim-map', {
emptyArrayIfMissing: true,
isNestedAttribute: true,
})
account_claim_maps;
@fragmentArray('fragment-string', {
emptyArrayIfMissing: true,
isNestedAttribute: true,
})
claims_scopes;
@fragmentArray('fragment-string', {
emptyArrayIfMissing: true,
isNestedAttribute: true,
})
signing_algorithms;
@fragmentArray('fragment-string', {
emptyArrayIfMissing: true,
isNestedAttribute: true,
})
allowed_audiences;
@fragmentArray('fragment-string', {
emptyArrayIfMissing: true,
isNestedAttribute: true,
})
idp_ca_certs;
/**
* @type {boolean}
@@ -81,21 +84,21 @@ export default class AuthMethodModel extends GeneratedAuthMethodModel {
* @type {boolean}
*/
get isInactive() {
return this.attributes.state === 'inactive';
return this.state === 'inactive';
}
/**
* @type {boolean}
*/
get isPrivate() {
return this.attributes.state === 'active-private';
return this.state === 'active-private';
}
/**
* @type {boolean}
*/
get isPublic() {
return this.attributes.state === 'active-public';
return this.state === 'active-public';
}
// =methods
@@ -1,40 +0,0 @@
import Fragment from 'ember-data-model-fragments/fragment';
import { attr } from '@ember-data/model';
import { fragmentArray } from 'ember-data-model-fragments/attributes';
export default class FragmentAuthMethodAttributesModel extends Fragment {
// =attributes (OIDC)
@attr('string', { readOnly: true }) state;
@attr('string') issuer;
@attr('string') client_id;
@attr('string') client_secret;
@attr('string', { readOnly: true }) client_secret_hmac;
@attr('number') max_age;
@attr('string') api_url_prefix;
@attr('string', { readOnly: true }) callback_url;
@attr('boolean') disable_discovered_config_validation;
@attr('boolean') dry_run;
@fragmentArray('fragment-auth-method-attributes-account-claim-map', {
emptyArrayIfMissing: true,
})
account_claim_maps;
@fragmentArray('fragment-string', {
emptyArrayIfMissing: true,
})
claims_scopes;
@fragmentArray('fragment-string', {
emptyArrayIfMissing: true,
})
signing_algorithms;
@fragmentArray('fragment-string', {
emptyArrayIfMissing: true,
})
allowed_audiences;
@fragmentArray('fragment-string', { emptyArrayIfMissing: true }) idp_ca_certs;
}
@@ -1,26 +0,0 @@
import JSONSerializer from '@ember-data/serializer/json';
export default class FragmentAuthMethodAttributesSerializer extends JSONSerializer {
/**
* If an attribute is annotated as readOnly in the model, don't serialize it.
* Otherwise delegate to default attribute serializer.
*
* @override
* @method serializeAttribute
* @param {Snapshot} snapshot
* @param {Object} json
* @param {String} key
* @param {Object} attribute
*/
serializeAttribute(snapshot, json, key, attribute) {
const { type, options } = attribute;
let value = super.serializeAttribute(...arguments);
// Convert empty strings to null.
if (type === 'string' && json[key] === '') json[key] = null;
// Do not serialize read-only attributes.
if (options.readOnly) delete json[key];
// Client secret cannot be unset. If it's falsy, it must be removed.
if (key === 'client_secret' && !json[key]) delete json[key];
return value;
}
}
@@ -1 +0,0 @@
export { default } from 'api/models/fragment-auth-method-attributes';
@@ -1 +0,0 @@
export { default } from 'api/serializers/fragment-auth-method-attributes';
@@ -1,12 +0,0 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Model | fragment auth method attributes', function (hooks) {
setupTest(hooks);
test('it exists', function (assert) {
let store = this.owner.lookup('service:store');
let model = store.createRecord('fragment-auth-method-attributes', {});
assert.ok(model);
});
});
@@ -33,27 +33,25 @@ module('Unit | Serializer | auth method', function (hooks) {
let record = store.createRecord('auth-method', {
type: 'oidc',
name: 'OIDC Auth Method',
attributes: {
state: 'foo',
account_claim_maps: [{ from: 'foo', to: 'bar' }],
claims_scopes: [{ value: 'profile' }, { value: 'email' }],
signing_algorithms: [{ value: 'RS256' }, { value: 'RS384' }],
allowed_audiences: [
{ value: 'www.alice.com' },
{ value: 'www.alice.com/admin' },
],
idp_ca_certs: [
{ value: 'certificate-1234' },
{ value: 'certificate-5678' },
],
api_url_prefix: 'protocol://host:port/foo',
client_id: 'id123',
client_secret: 'secret456',
disable_discovered_config_validation: true,
dry_run: true,
issuer: 'http://www.example.net',
max_age: 500,
},
state: 'foo',
account_claim_maps: [{ from: 'foo', to: 'bar' }],
claims_scopes: [{ value: 'profile' }, { value: 'email' }],
signing_algorithms: [{ value: 'RS256' }, { value: 'RS384' }],
allowed_audiences: [
{ value: 'www.alice.com' },
{ value: 'www.alice.com/admin' },
],
idp_ca_certs: [
{ value: 'certificate-1234' },
{ value: 'certificate-5678' },
],
api_url_prefix: 'protocol://host:port/foo',
client_id: 'id123',
client_secret: 'secret456',
disable_discovered_config_validation: true,
dry_run: true,
issuer: 'http://www.example.net',
max_age: 500,
});
let serializedRecord = record.serialize();
@@ -148,19 +146,56 @@ module('Unit | Serializer | auth method', function (hooks) {
test('it normalizes OIDC records', async function (assert) {
const store = this.owner.lookup('service:store');
const apiUrlPrefix = 'protocol://host:port/foo';
const clientId = 'id123';
const clientSecret = 'secret456';
const disableDiscoveredConfigValidation = true;
const dryRun = true;
const issuer = 'http://www.example.net';
const maxAge = 500;
this.server.get('/v1/auth-methods/oidc123', () => ({
attributes: {
account_claim_maps: ['from=to', 'foo=bar'],
claims_scopes: [{ value: 'profile' }, { value: 'email' }],
signing_algorithms: [{ value: 'RS256' }, { value: 'RS384' }],
allowed_audiences: [
{ value: 'www.alice.com' },
{ value: 'www.alice.com/admin' },
],
idp_ca_certs: [
{ value: 'certificate-1234' },
{ value: 'certificate-5678' },
],
api_url_prefix: apiUrlPrefix,
client_id: clientId,
client_secret: clientSecret,
disable_discovered_config_validation: disableDiscoveredConfigValidation,
dry_run: dryRun,
issuer: issuer,
max_age: maxAge,
},
version: 1,
type: 'oidc',
id: 'oidc123',
}));
const record = await store.findRecord('auth-method', 'oidc123');
const { account_claim_maps } = record.attributes;
const { account_claim_maps } = record;
assert.strictEqual(account_claim_maps.firstObject.from, 'from');
assert.strictEqual(account_claim_maps.firstObject.to, 'to');
assert.strictEqual(account_claim_maps.lastObject.from, 'foo');
assert.strictEqual(account_claim_maps.lastObject.to, 'bar');
assert.strictEqual(record.api_url_prefix, apiUrlPrefix);
assert.strictEqual(record.client_id, clientId);
assert.strictEqual(record.client_secret, clientSecret);
assert.strictEqual(
record.disable_discovered_config_validation,
disableDiscoveredConfigValidation
);
assert.strictEqual(record.dry_run, dryRun);
assert.strictEqual(record.issuer, issuer);
assert.strictEqual(record.max_age, maxAge);
});
});
@@ -1,37 +0,0 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Serializer | fragment auth method attributes', function (hooks) {
setupTest(hooks);
test('it serializes empty strings to null', function (assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
const record = store.createRecord('user', {
name: '',
description: '',
version: null,
});
let serializedRecord = record.serialize();
assert.deepEqual(serializedRecord, {
name: null,
description: null,
});
});
test('it does not serialize client_secret if present but falsy', function (assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
const record = store.createRecord('user', {
name: '',
description: '',
version: null,
client_secret: false,
});
let serializedRecord = record.serialize();
assert.deepEqual(serializedRecord, {
name: null,
description: null,
});
});
});
@@ -1,6 +1,6 @@
{{#if @model.isOIDC}}
<Rose::Dropdown
@text={{t (concat 'states.' @model.attributes.state)}}
@text={{t (concat 'states.' @model.state)}}
@icon={{if
@model.isInactive
'flight-icons/svg/x-circle-16'
@@ -17,7 +17,7 @@
<Rose::Form as |form|>
<form.radioGroup
@name='theme'
@selectedValue={{@model.attributes.state}}
@selectedValue={{@model.state}}
@changed={{route-action 'changeState'}}
as |radioGroup|
>
@@ -59,16 +59,16 @@
<form.input
@name='issuer'
@type='text'
@value={{@model.attributes.issuer}}
@value={{@model.issuer}}
@label={{t 'form.issuer.label'}}
@helperText={{t 'form.issuer.help'}}
@error={{@model.errors.attributes_issuer}}
@error={{@model.errors.issuer}}
readonly={{false}}
as |field|
>
{{#if @model.errors.attributes_issuer}}
{{#if @model.errors.issuer}}
<field.errors as |errors|>
{{#each @model.errors.attributes_issuer as |error|}}
{{#each @model.errors.issuer as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
@@ -78,26 +78,26 @@
<form.checkbox
@label={{t 'form.disable_discovered_config_validation.label'}}
@helperText={{t 'form.disable_discovered_config_validation.help'}}
@checked={{@model.attributes.disable_discovered_config_validation}}
@checked={{@model.disable_discovered_config_validation}}
@onChange={{fn
(mut @model.attributes.disable_discovered_config_validation)
(if @model.attributes.disable_discovered_config_validation false true)
(mut @model.disable_discovered_config_validation)
(if @model.disable_discovered_config_validation false true)
}}
/>
<form.input
@name='client_id'
@type='text'
@value={{@model.attributes.client_id}}
@value={{@model.client_id}}
@label={{t 'form.client_id.label'}}
@helperText={{t 'form.client_id.help'}}
@error={{@model.errors.attributes_client_id}}
@error={{@model.errors.client_id}}
readonly={{false}}
as |field|
>
{{#if @model.errors.attributes_client_id}}
{{#if @model.errors.client_id}}
<field.errors as |errors|>
{{#each @model.errors.attributes_client_id as |error|}}
{{#each @model.errors.client_id as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
@@ -107,16 +107,16 @@
<form.input
@name='client_secret'
@type='password'
@value={{@model.attributes.client_secret}}
@value={{@model.client_secret}}
@label={{t 'form.client_secret.label'}}
@helperText={{t 'form.client_secret.help'}}
@error={{@model.errors.attributes_client_secret}}
@error={{@model.errors.client_secret}}
readonly={{false}}
as |field|
>
{{#if @model.errors.attributes_client_secret}}
{{#if @model.errors.client_secret}}
<field.errors as |errors|>
{{#each @model.errors.attributes_client_secret as |error|}}
{{#each @model.errors.client_secret as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
@@ -125,7 +125,7 @@
{{#unless @model.isNew}}
<form.input
@value={{@model.attributes.client_secret_hmac}}
@value={{@model.client_secret_hmac}}
@label={{t 'form.client_secret_hmac.label'}}
readonly={{true}}
@disabled={{true}}
@@ -137,9 +137,9 @@
<:title>{{t 'form.signing_algorithms.label'}}</:title>
<:description>{{t 'form.signing_algorithms.help'}}</:description>
<:body>
{{#if @model.errors.attributes_signing_algorithms}}
{{#if @model.errors.signing_algorithms}}
<Rose::Form::Errors as |errors|>
{{#each @model.errors.attributes_signing_algorithms as |error|}}
{{#each @model.errors.signing_algorithms as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</Rose::Form::Errors>
@@ -152,13 +152,13 @@
</header.row>
</table.header>
<table.body as |body|>
{{#each @model.attributes.signing_algorithms as |algorithm i|}}
{{#each @model.signing_algorithms as |algorithm i|}}
<body.row as |row|>
<row.cell>
<Hds::Form::Select::Field
@value={{algorithm.value}}
@isInvalid={{@model.errors.attributes_signing_algorithms}}
@isInvalid={{@model.errors.signing_algorithms}}
disabled={{form.disabled}}
{{on 'change' (set-from-event algorithm 'value')}}
as |F|
@@ -187,10 +187,7 @@
false
(if form.isEditable false true)
}}
{{on
'click'
(fn @removeItemByIndex 'attributes.signing_algorithms' i)
}}
{{on 'click' (fn @removeItemByIndex 'signing_algorithms' i)}}
>
{{t 'actions.remove'}}
</Rose::Button>
@@ -233,9 +230,7 @@
{{on
'click'
(fn
@addStringItem
'attributes.signing_algorithms'
this.newSigningAlgorithm
@addStringItem 'signing_algorithms' this.newSigningAlgorithm
)
}}
>
@@ -253,9 +248,9 @@
<:title>{{t 'form.allowed_audiences.label'}}</:title>
<:description>{{t 'form.allowed_audiences.help'}}</:description>
<:body>
{{#if @model.errors.attributes_allowed_audiences}}
{{#if @model.errors.allowed_audiences}}
<Rose::Form::Errors as |errors|>
{{#each @model.errors.attributes_allowed_audiences as |error|}}
{{#each @model.errors.allowed_audiences as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</Rose::Form::Errors>
@@ -268,13 +263,14 @@
</header.row>
</table.header>
<table.body as |body|>
{{#each @model.attributes.allowed_audiences as |audience i|}}
{{#each @model.allowed_audiences as |audience i|}}
<body.row as |row|>
<row.cell>
<form.input
@type='text'
@name='allowed_audiences'
@value={{audience.value}}
@error={{@model.errors.attributes_allowed_audiences}}
@error={{@model.errors.allowed_audiences}}
@contextual={{true}}
as |field|
>
@@ -292,10 +288,7 @@
false
(if form.isEditable false true)
}}
{{on
'click'
(fn @removeItemByIndex 'attributes.allowed_audiences' i)
}}
{{on 'click' (fn @removeItemByIndex 'allowed_audiences' i)}}
>
{{t 'actions.remove'}}
</Rose::Button>
@@ -307,6 +300,7 @@
<row.cell>
<form.input
@type='text'
@name='allowed_audiences'
@value={{this.newAllowedAudience}}
@label={{t 'resources.auth-method.titles.new-allowed-audience'}}
/>
@@ -323,9 +317,7 @@
{{on
'click'
(fn
@addStringItem
'attributes.allowed_audiences'
this.newAllowedAudience
@addStringItem 'allowed_audiences' this.newAllowedAudience
)
}}
>
@@ -343,9 +335,9 @@
<:title>{{t 'form.claims_scopes.label'}}</:title>
<:description>{{t 'form.claims_scopes.help'}}</:description>
<:body>
{{#if @model.errors.attributes_claims_scopes}}
{{#if @model.errors.claims_scopes}}
<Rose::Form::Errors as |errors|>
{{#each @model.errors.attributes_claims_scopes as |error|}}
{{#each @model.errors.claims_scopes as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</Rose::Form::Errors>
@@ -358,13 +350,14 @@
</header.row>
</table.header>
<table.body as |body|>
{{#each @model.attributes.claims_scopes as |claimsScope i|}}
{{#each @model.claims_scopes as |claimsScope i|}}
<body.row as |row|>
<row.cell>
<form.input
@type='text'
@name='claims_scopes'
@value={{claimsScope.value}}
@error={{@model.errors.attributes_claims_scopes}}
@error={{@model.errors.claims_scopes}}
@contextual={{true}}
as |field|
>
@@ -382,10 +375,7 @@
false
(if form.isEditable false true)
}}
{{on
'click'
(fn @removeItemByIndex 'attributes.claims_scopes' i)
}}
{{on 'click' (fn @removeItemByIndex 'claims_scopes' i)}}
>
{{t 'actions.remove'}}
</Rose::Button>
@@ -397,6 +387,7 @@
<row.cell>
<form.input
@type='text'
@name='claims_scopes'
@value={{this.newClaimsScope}}
@label={{t 'resources.auth-method.titles.new-claims-scope'}}
/>
@@ -412,11 +403,7 @@
}}
{{on
'click'
(fn
@addStringItem
'attributes.claims_scopes'
this.newClaimsScope
)
(fn @addStringItem 'claims_scopes' this.newClaimsScope)
}}
>
{{t 'actions.add'}}
@@ -433,9 +420,9 @@
<:title>{{t 'form.account_claim_maps.label'}}</:title>
<:description>{{t 'form.account_claim_maps.help'}}</:description>
<:body>
{{#if @model.errors.attributes_account_claim_maps}}
{{#if @model.errors.account_claim_maps}}
<Rose::Form::Errors as |errors|>
{{#each @model.errors.attributes_account_claim_maps as |error|}}
{{#each @model.errors.account_claim_maps as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</Rose::Form::Errors>
@@ -449,13 +436,14 @@
</header.row>
</table.header>
<table.body as |body|>
{{#each @model.attributes.account_claim_maps as |claimMap i|}}
{{#each @model.account_claim_maps as |claimMap i|}}
<body.row as |row|>
<row.cell>
<form.input
@type='text'
@name='from_claim'
@value={{claimMap.from}}
@error={{@model.errors.attributes_account_claim_maps}}
@error={{@model.errors.account_claim_maps}}
@contextual={{true}}
as |field|
>
@@ -467,7 +455,7 @@
<Hds::Form::Select::Field
@value={{claimMap.to}}
@isInvalid={{@model.errors.attributes_account_claim_maps}}
@isInvalid={{@model.errors.account_claim_maps}}
disabled={{form.disabled}}
{{on 'change' (set-from-event claimMap 'to')}}
as |F|
@@ -494,10 +482,7 @@
false
(if form.isEditable false true)
}}
{{on
'click'
(fn @removeItemByIndex 'attributes.account_claim_maps' i)
}}
{{on 'click' (fn @removeItemByIndex 'account_claim_maps' i)}}
>
{{t 'actions.remove'}}
</Rose::Button>
@@ -509,6 +494,7 @@
<row.cell>
<form.input
@type='text'
@name='from_claim'
@value={{this.newFromClaim}}
@label={{t 'resources.auth-method.titles.new-from-claim'}}
/>
@@ -563,9 +549,9 @@
<:title>{{t 'form.idp_ca_certs.label'}}</:title>
<:description>{{t 'form.idp_ca_certs.help'}}</:description>
<:body>
{{#if @model.errors.attributes_idp_ca_certs}}
{{#if @model.errors.idp_ca_certs}}
<Rose::Form::Errors as |errors|>
{{#each @model.errors.attributes_idp_ca_certs as |error|}}
{{#each @model.errors.idp_ca_certs as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</Rose::Form::Errors>
@@ -578,12 +564,12 @@
</header.row>
</table.header>
<table.body as |body|>
{{#each @model.attributes.idp_ca_certs as |cert i|}}
{{#each @model.idp_ca_certs as |cert i|}}
<body.row as |row|>
<row.cell>
<form.textarea
@value={{cert.value}}
@error={{@model.errors.attributes_idp_ca_certs}}
@error={{@model.errors.idp_ca_certs}}
@contextual={{true}}
as |field|
>
@@ -601,10 +587,7 @@
false
(if form.isEditable false true)
}}
{{on
'click'
(fn @removeItemByIndex 'attributes.idp_ca_certs' i)
}}
{{on 'click' (fn @removeItemByIndex 'idp_ca_certs' i)}}
>
{{t 'actions.remove'}}
</Rose::Button>
@@ -630,9 +613,7 @@
}}
{{on
'click'
(fn
@addStringItem 'attributes.idp_ca_certs' this.newCertificate
)
(fn @addStringItem 'idp_ca_certs' this.newCertificate)
}}
>
{{t 'actions.add'}}
@@ -647,18 +628,18 @@
<form.input
@name='max_age'
@type='number'
@value={{@model.attributes.max_age}}
@value={{@model.max_age}}
@label={{t 'form.max_age.label'}}
@helperText={{t 'form.max_age.help'}}
@error={{@model.errors.attributes_max_age}}
@error={{@model.errors.max_age}}
readonly={{false}}
step='1'
min='0'
as |field|
>
{{#if @model.errors.attributes_max_age}}
{{#if @model.errors.max_age}}
<field.errors as |errors|>
{{#each @model.errors.attributes_max_age as |error|}}
{{#each @model.errors.max_age as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
@@ -668,16 +649,16 @@
<form.input
@name='api_url_prefix'
@type='test'
@value={{@model.attributes.api_url_prefix}}
@value={{@model.api_url_prefix}}
@label={{t 'form.api_url_prefix.label'}}
@helperText={{t 'form.api_url_prefix.help'}}
@error={{@model.errors.attributes_api_url_prefix}}
@error={{@model.errors.api_url_prefix}}
readonly={{false}}
as |field|
>
{{#if @model.errors.attributes_api_url_prefix}}
{{#if @model.errors.api_url_prefix}}
<field.errors as |errors|>
{{#each @model.errors.attributes_api_url_prefix as |error|}}
{{#each @model.errors.api_url_prefix as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
@@ -686,7 +667,7 @@
{{#unless @model.isNew}}
<form.input
@value={{@model.attributes.callback_url}}
@value={{@model.callback_url}}
@label={{t 'form.callback_url.label'}}
@helperText={{t 'form.callback_url.help'}}
readonly={{true}}
@@ -9,7 +9,7 @@ export default class ScopesScopeAuthenticateMethodOidcController extends Control
// =attributes
/**
* Authetnication URL for the pending OIDC flow, if any.
* Authentication URL for the pending OIDC flow, if any.
* @type {?string}
*/
get authURL() {
@@ -191,7 +191,7 @@ export default class ScopesScopeAuthMethodsRoute extends Route {
*/
@action
async addAccountClaimMapItem(authMethod, from, to) {
const array = authMethod.attributes.account_claim_maps;
const array = authMethod.account_claim_maps;
const value = { from, to };
array.addObject(value);
}
@@ -1,5 +1,12 @@
import { module, test } from 'qunit';
import { visit, currentURL, click, find, fillIn } from '@ember/test-helpers';
import {
visit,
currentURL,
click,
find,
fillIn,
select,
} from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { Response } from 'miragejs';
@@ -14,6 +21,7 @@ module('Acceptance | auth-methods | create ', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
let getAuthMethodsCount;
const instances = {
scopes: {
global: null,
@@ -56,16 +64,65 @@ module('Acceptance | auth-methods | create ', function (hooks) {
urls.authMethods = `${urls.orgScope}/auth-methods`;
urls.newAuthMethod = `${urls.authMethods}/new?type=password`;
urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`;
getAuthMethodsCount = () =>
this.server.schema.authMethods.all().models.length;
});
test('Users can create new auth method', async function (assert) {
assert.expect(1);
const authMethodsCount = this.server.db.authMethods.length;
const count = getAuthMethodsCount();
await visit(urls.newAuthMethod);
await fillIn('[name="name"]', 'AuthMethod name');
await fillIn('[name="description"]', 'description');
await click('form [type="submit"]:not(:disabled)');
assert.strictEqual(this.server.db.authMethods.length, authMethodsCount + 1);
assert.strictEqual(getAuthMethodsCount(), count + 1);
});
test('Users can create new oidc auth method', async function (assert) {
assert.expect(13);
const count = getAuthMethodsCount();
await visit(`${urls.authMethods}/new?type=oidc`);
const name = 'oidc name';
await fillIn('[name="name"]', name);
await fillIn('[name="description"]', 'description');
await fillIn('[name="issuer"]', 'issuer');
await fillIn('[name="client_id"]', 'client_id');
await fillIn('[name="client_secret"]', 'client_secret');
await select('form fieldset:nth-of-type(1) select', 'RS384');
await click('form fieldset:nth-of-type(1) [title="Add"]');
await fillIn('[name="allowed_audiences"]', 'allowed_audiences');
await click('form fieldset:nth-of-type(2) [title="Add"]');
await fillIn('[name="claims_scopes"]', 'claims_scopes');
await click('form fieldset:nth-of-type(3) [title="Add"]');
await fillIn('[name="from_claim"]', 'from_claim');
await select('form fieldset:nth-of-type(4) select', 'email');
await click('form fieldset:nth-of-type(4) [title="Add"]');
await fillIn('form fieldset:nth-of-type(5) textarea', 'certificates');
await click('form fieldset:nth-of-type(5) [title="Add"]');
await fillIn('[name="max_age"]', '5');
await fillIn('[name="api_url_prefix"]', 'api_url_prefix');
await click('form [type="submit"]:not(:disabled)');
assert.strictEqual(getAuthMethodsCount(), count + 1);
const authMethod = this.server.schema.authMethods.findBy({ name });
assert.strictEqual(authMethod.name, name);
assert.strictEqual(authMethod.description, 'description');
assert.strictEqual(authMethod.attributes.issuer, 'issuer');
assert.strictEqual(authMethod.attributes.client_id, 'client_id');
assert.strictEqual(authMethod.attributes.client_secret, 'client_secret');
assert.deepEqual(authMethod.attributes.signing_algorithms, ['RS384']);
assert.deepEqual(authMethod.attributes.allowed_audiences, [
'allowed_audiences',
]);
assert.deepEqual(authMethod.attributes.claims_scopes, ['claims_scopes']);
assert.deepEqual(authMethod.attributes.account_claim_maps, [
'from_claim=email',
]);
assert.deepEqual(authMethod.attributes.idp_ca_certs, ['certificates']);
assert.strictEqual(authMethod.attributes.max_age, 5);
assert.strictEqual(authMethod.attributes.api_url_prefix, 'api_url_prefix');
});
test('Users can navigate to new auth-methods route with proper authorization', async function (assert) {
@@ -97,12 +154,12 @@ module('Acceptance | auth-methods | create ', function (hooks) {
test('can cancel new auth method creation', async function (assert) {
assert.expect(2);
const authMethodsCount = this.server.db.authMethods.length;
const count = getAuthMethodsCount();
await visit(urls.newAuthMethod);
await fillIn('[name="name"]', 'AuthMethod name');
await fillIn('[name="description"]', 'description');
await click('form button:not([type="submit"])');
assert.strictEqual(this.server.db.authMethods.length, authMethodsCount);
assert.strictEqual(getAuthMethodsCount(), count);
assert.strictEqual(currentURL(), urls.authMethods);
});
@@ -1,5 +1,12 @@
import { module, test } from 'qunit';
import { visit, click, find, fillIn } from '@ember/test-helpers';
import {
visit,
click,
find,
fillIn,
select,
findAll,
} from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import a11yAudit from 'ember-a11y-testing/test-support/audit';
@@ -57,6 +64,84 @@ module('Acceptance | auth methods | update', function (hooks) {
assert.strictEqual(this.server.db.authMethods[0].name, 'update name');
});
test('can update an oidc auth method and save changes', async function (assert) {
assert.expect(12);
instances.authMethod = this.server.create('auth-method', {
scope: instances.scopes.org,
type: 'oidc',
});
await visit(`${urls.authMethods}/${instances.authMethod.id}`);
await click('form [type="button"]:not(:disabled)', 'Activate edit mode');
const name = 'oidc name';
await fillIn('[name="name"]', name);
await fillIn('[name="description"]', 'description');
await fillIn('[name="issuer"]', 'issuer');
await fillIn('[name="client_id"]', 'client_id');
await fillIn('[name="client_secret"]', 'client_secret');
// Remove all signing algorithms
await Promise.all(
findAll('form fieldset:nth-of-type(1) [title="Remove"]').map((element) =>
click(element)
)
);
await select('form fieldset:nth-of-type(1) select', 'RS384');
await click('form fieldset:nth-of-type(1) [title="Add"]');
// Remove all allowed audiences
await Promise.all(
findAll('form fieldset:nth-of-type(2) [title="Remove"]').map((element) =>
click(element)
)
);
await fillIn('[name="allowed_audiences"]', 'allowed_audiences');
await click('form fieldset:nth-of-type(2) [title="Add"]');
// Remove all claims scopes
await Promise.all(
findAll('form fieldset:nth-of-type(3) [title="Remove"]').map((element) =>
click(element)
)
);
await fillIn('[name="claims_scopes"]', 'claims_scopes');
await click('form fieldset:nth-of-type(3) [title="Add"]');
// Remove all claim maps
await Promise.all(
findAll('form fieldset:nth-of-type(4) [title="Remove"]').map((element) =>
click(element)
)
);
await fillIn('[name="from_claim"]', 'from_claim');
await select('form fieldset:nth-of-type(4) select', 'email');
await click('form fieldset:nth-of-type(4) [title="Add"]');
// Remove all certificates
await Promise.all(
findAll('form fieldset:nth-of-type(5) [title="Remove"]').map((element) =>
click(element)
)
);
await fillIn('form fieldset:nth-of-type(5) textarea', 'certificates');
await click('form fieldset:nth-of-type(5) [title="Add"]');
await fillIn('[name="max_age"]', '5');
await fillIn('[name="api_url_prefix"]', 'api_url_prefix');
await click('form [type="submit"]:not(:disabled)');
const authMethod = this.server.schema.authMethods.findBy({ name });
assert.strictEqual(authMethod.name, name);
assert.strictEqual(authMethod.description, 'description');
assert.strictEqual(authMethod.attributes.issuer, 'issuer');
assert.strictEqual(authMethod.attributes.client_id, 'client_id');
assert.strictEqual(authMethod.attributes.client_secret, 'client_secret');
assert.deepEqual(authMethod.attributes.signing_algorithms, ['RS384']);
assert.deepEqual(authMethod.attributes.allowed_audiences, [
'allowed_audiences',
]);
assert.deepEqual(authMethod.attributes.claims_scopes, ['claims_scopes']);
assert.deepEqual(authMethod.attributes.account_claim_maps, [
'from_claim=email',
]);
assert.deepEqual(authMethod.attributes.idp_ca_certs, ['certificates']);
assert.strictEqual(authMethod.attributes.max_age, 5);
assert.strictEqual(authMethod.attributes.api_url_prefix, 'api_url_prefix');
});
test('can update an auth method and cancel changes', async function (assert) {
assert.expect(1);
await visit(urls.authMethod);