Merge branch 'ICU-580-set-a-new-password' into ICU-580-remove-password-attribute-from-account-model
This commit is contained in:
20 files changed
+162
-57
No files matched your search
@@ -14,14 +14,15 @@ export default class AccountModel extends GeneratedAccountModel {
|
||||
/**
|
||||
* Save account password via the `set-password` method.
|
||||
* See serializer and adapter for more information.
|
||||
* @param {string} password
|
||||
* @param {object} options
|
||||
* @param {object} options.adapterOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
savePassword(options={ adapterOptions: {} }) {
|
||||
setPassword(password, options={ adapterOptions: {} }) {
|
||||
const defaultAdapterOptions = {
|
||||
method: 'set-password',
|
||||
serializePassword: true
|
||||
password
|
||||
};
|
||||
return this.save({
|
||||
...options,
|
||||
|
||||
@@ -7,7 +7,4 @@ export default class FragmentAccountAttributesModel extends Fragment {
|
||||
description: 'The account login name'
|
||||
}) login_name;
|
||||
|
||||
@attr('string', {
|
||||
description: 'The account password'
|
||||
}) password;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import Fragment from 'ember-data-model-fragments/fragment';
|
||||
import { attr } from '@ember-data/model';
|
||||
|
||||
export default class FragmentTargetAttributesModel extends Fragment {
|
||||
|
||||
@attr('number', {
|
||||
description: 'The default port a target should use if present.'
|
||||
}) default_port;
|
||||
|
||||
}
|
||||
@@ -1,11 +1,17 @@
|
||||
import GeneratedTargetModel from '../generated/models/target';
|
||||
import { fragmentArray } from 'ember-data-model-fragments/attributes';
|
||||
import { fragment, fragmentArray } from 'ember-data-model-fragments/attributes';
|
||||
import { computed } from '@ember/object';
|
||||
|
||||
export default class TargetModel extends GeneratedTargetModel {
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* Attributes of this resource, if any, represented as a JSON fragment.
|
||||
* @type {FragmentTargetAttributesModel}
|
||||
*/
|
||||
@fragment('fragment-target-attributes', { defaultValue: {} }) attributes;
|
||||
|
||||
/**
|
||||
* @type {[FragmentHostSetModel]}
|
||||
*/
|
||||
|
||||
@@ -1,33 +1,38 @@
|
||||
import ApplicationSerializer from './application';
|
||||
import { get } from '@ember/object';
|
||||
|
||||
export default class AccountSerializer extends ApplicationSerializer {
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* If `adapterOptions.serializePassword` is true, the serialization should
|
||||
* include **password** and the version.
|
||||
* If `adapterOptions.method` is `set-password`, the serialization should
|
||||
* include only **password** and the version.
|
||||
* If `adapterOptions.password` is set, the serialization should
|
||||
* include **password** in it's `attributes`.
|
||||
* @override
|
||||
* @param {Snapshot} snapshot
|
||||
* @return {object}
|
||||
*/
|
||||
serialize(snapshot) {
|
||||
const serializePassword = get(snapshot, 'adapterOptions.serializePassword');
|
||||
const password = snapshot?.adapterOptions?.password;
|
||||
let serialized = super.serialize(...arguments);
|
||||
if (serializePassword) serialized = this.serializeWithPassword(snapshot);
|
||||
if (password && snapshot?.record?.isNew) serialized.attributes.password = password;
|
||||
if (snapshot?.adapterOptions?.method === 'set-password') {
|
||||
serialized = this.serializeForSetPassword(snapshot, password);
|
||||
}
|
||||
return serialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a payload containing only password.
|
||||
* Returns a payload containing only new password.
|
||||
* @param {Snapshot} snapshot
|
||||
* @param {string} password
|
||||
* @return {object}
|
||||
*/
|
||||
serializeWithPassword(snapshot) {
|
||||
serializeForSetPassword(snapshot, password) {
|
||||
return {
|
||||
version: snapshot.attr('version'),
|
||||
password: snapshot.attr('attributes').attr('password'),
|
||||
password,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/models/fragment-target-attributes';
|
||||
@@ -4,21 +4,10 @@ import { setupTest } from 'ember-qunit';
|
||||
module('Unit | Model | account', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let model = store.createRecord('account', {});
|
||||
assert.ok(model);
|
||||
});
|
||||
|
||||
test('it contains attributes', function(assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let model = store.createRecord('account', {
|
||||
type: 'password',
|
||||
attributes: {
|
||||
login_name: 'loginname'
|
||||
}
|
||||
});
|
||||
assert.equal(model.attributes.login_name, 'loginname');
|
||||
});
|
||||
});
|
||||
@@ -4,16 +4,72 @@ import { setupTest } from 'ember-qunit';
|
||||
module('Unit | Serializer | account', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it serializes records', function(assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let record = store.createRecord('account', {});
|
||||
|
||||
let serializedRecord = record.serialize();
|
||||
|
||||
assert.ok(serializedRecord);
|
||||
test('it serializes normally', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('account');
|
||||
const record = store.createRecord('account', {
|
||||
type: 'password',
|
||||
name: 'Name',
|
||||
auth_method_id: '1',
|
||||
description: 'Description',
|
||||
attributes: {
|
||||
login_name: 'Login Name',
|
||||
},
|
||||
version: 1
|
||||
});
|
||||
const snapshot = record._createSnapshot();
|
||||
snapshot.adapterOptions = {};
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
assert.deepEqual(serializedRecord, {
|
||||
type: 'password',
|
||||
name: 'Name',
|
||||
auth_method_id: '1',
|
||||
description: 'Description',
|
||||
attributes: {
|
||||
login_name: 'Login Name'
|
||||
},
|
||||
version: 1
|
||||
});
|
||||
});
|
||||
|
||||
test('it serializes only password when `adapterOptions.serializePassword` is true', function (assert) {
|
||||
test('it serializes password attribute when `adapterOptions.password` is true', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('account');
|
||||
const record = store.createRecord('account', {
|
||||
type: 'password',
|
||||
name: 'Name',
|
||||
auth_method_id: '1',
|
||||
description: 'Description',
|
||||
attributes: {
|
||||
login_name: 'Login Name',
|
||||
},
|
||||
version: 1
|
||||
});
|
||||
const snapshot = record._createSnapshot();
|
||||
snapshot.adapterOptions = {
|
||||
password: 'Password'
|
||||
};
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
assert.deepEqual(serializedRecord, {
|
||||
type: 'password',
|
||||
name: 'Name',
|
||||
auth_method_id: '1',
|
||||
description: 'Description',
|
||||
attributes: {
|
||||
login_name: 'Login Name',
|
||||
password: 'Password'
|
||||
},
|
||||
version: 1
|
||||
});
|
||||
});
|
||||
|
||||
test('it does not serialize password for non-new records', function (assert) {
|
||||
assert.expect(0);
|
||||
});
|
||||
|
||||
test('it serializes only password when `adapterOptions.method` is set to `set-password`', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('account');
|
||||
@@ -22,14 +78,15 @@ module('Unit | Serializer | account', function(hooks) {
|
||||
name: 'Name',
|
||||
description: 'Description',
|
||||
attributes: {
|
||||
login_name: 'Login Name',
|
||||
password: 'Password'
|
||||
login_name: 'Login Name'
|
||||
},
|
||||
auth_method_id: '1',
|
||||
version: 1
|
||||
});
|
||||
const snapshot = record._createSnapshot();
|
||||
snapshot.adapterOptions = {
|
||||
serializePassword: true
|
||||
method: 'set-password',
|
||||
password: 'Password'
|
||||
};
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
assert.deepEqual(serializedRecord, {
|
||||
|
||||
@@ -30,7 +30,10 @@ module('Unit | Serializer | target', function(hooks) {
|
||||
description: 'Description',
|
||||
version: 1,
|
||||
type: 'tcp',
|
||||
scope_id: 'org_1'
|
||||
scope_id: 'org_1',
|
||||
attributes: {
|
||||
default_port: null
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -14,11 +14,10 @@
|
||||
|
||||
<form.input
|
||||
@name="name"
|
||||
@type="name"
|
||||
@type="text"
|
||||
@value={{@model.name}}
|
||||
@label={{t "form.label.name"}}
|
||||
@error={{@model.errors.name}}
|
||||
readonly={{false}}
|
||||
as |field|>
|
||||
{{#if @model.errors.name}}
|
||||
<field.errors as |errors|>
|
||||
@@ -31,12 +30,10 @@
|
||||
|
||||
<form.textarea
|
||||
@name="description"
|
||||
@type="description"
|
||||
@type="text"
|
||||
@value={{@model.description}}
|
||||
@label={{t "form.label.description"}}
|
||||
@error={{@model.errors.description}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|>
|
||||
{{#if @model.errors.description}}
|
||||
<field.errors as |errors|>
|
||||
@@ -47,6 +44,26 @@
|
||||
{{/if}}
|
||||
</form.textarea>
|
||||
|
||||
{{!-- TODO this is a type-specific field --}}
|
||||
<form.input
|
||||
@name="default_port"
|
||||
@type="number"
|
||||
@value={{@model.attributes.default_port}}
|
||||
@label={{t "form.label.default_port"}}
|
||||
@error={{@model.errors.default_port}}
|
||||
min="0"
|
||||
step="1"
|
||||
as |field|>
|
||||
{{!-- TODO need to figure out how to reference `attributes.errors` here --}}
|
||||
{{#if @model.errors.default_port}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.default_port as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.input>
|
||||
|
||||
<form.actions
|
||||
@disabled={{if @model.cannotSave @model.cannotSave}}
|
||||
@submitText={{t "actions.save"}}
|
||||
|
||||
@@ -4,7 +4,7 @@ export default class ScopesScopeAuthMethodsAuthMethodRoute extends Route {
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Load a single auth-method in current scope.
|
||||
* Load an auth method by ID.
|
||||
* @param {object} params
|
||||
* @param {string} params.auth_method_id
|
||||
* @return {authMethodModel}
|
||||
|
||||
@@ -12,15 +12,17 @@ export default class ScopesScopeAuthMethodsAuthMethodAccountsRoute extends Route
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Returns accounts in current auth method.
|
||||
* Returns accounts for the current auth method.
|
||||
* @return {Promise{[AccountModel]}}
|
||||
*/
|
||||
model() {
|
||||
const { id: auth_method_id } = this.modelFor('scopes.scope.auth-methods.auth-method');
|
||||
const { id: auth_method_id } =
|
||||
this.modelFor('scopes.scope.auth-methods.auth-method');
|
||||
return this.store.query('account', { auth_method_id });
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Rollback changes on an account.
|
||||
* @param {AccountModel} account
|
||||
@@ -43,11 +45,11 @@ export default class ScopesScopeAuthMethodsAuthMethodAccountsRoute extends Route
|
||||
if(isNew) { adapterOptions.password = password; }
|
||||
try {
|
||||
await account.save({ adapterOptions });
|
||||
await this.transitionTo('scopes.scope.auth-methods.auth-method.accounts.account', account);
|
||||
this.refresh();
|
||||
this.notify.success(
|
||||
this.intl.t(isNew ? 'notify.create-success' : 'notify.save-success')
|
||||
);
|
||||
this.transitionTo('scopes.scope.auth-methods.auth-method.accounts.account', account);
|
||||
} catch (error) {
|
||||
// TODO: replace with translated strings
|
||||
this.notify.error(error.message, { closeAfter: null });
|
||||
@@ -62,9 +64,9 @@ export default class ScopesScopeAuthMethodsAuthMethodAccountsRoute extends Route
|
||||
async delete(account) {
|
||||
try {
|
||||
await account.destroyRecord();
|
||||
await this.transitionTo('scopes.scope.auth-methods.auth-method.accounts');
|
||||
this.refresh();
|
||||
this.notify.success(this.intl.t('notify.delete-success'));
|
||||
this.transitionTo('scopes.scope.auth-methods.auth-method.accounts');
|
||||
} catch (error) {
|
||||
//TODO: replace with translated strings
|
||||
this.notify.error(error.message, { closeAfter: null });
|
||||
|
||||
@@ -5,7 +5,7 @@ export default class ScopesScopeAuthMethodsAuthMethodAccountsAccountRoute extend
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Load an account in current scope.
|
||||
* Load an account by ID.
|
||||
* @param {object} params
|
||||
* @param {string} params.account_id
|
||||
* @return {AccountModel}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<h2>{{t "resources.auth-method"}}</h2>
|
||||
{{!--
|
||||
<Rose::Badge>
|
||||
{{t (concat 'auth.method-types.' @model.type)}}
|
||||
</Rose::Badge>
|
||||
</Rose::Badge>
|
||||
--}}
|
||||
@@ -30,9 +30,9 @@
|
||||
{{else}}
|
||||
|
||||
<Rose::Layout::Centered>
|
||||
<Rose::Message @title={{t "titles.auth-method-accounts-welcome"}} as |message|>
|
||||
<Rose::Message @title={{t "titles.accounts-welcome"}} as |message|>
|
||||
<message.description>
|
||||
{{t "descriptions.auth-method-accounts-welcome"}}
|
||||
{{t "descriptions.accounts-welcome"}}
|
||||
</message.description>
|
||||
</Rose::Message>
|
||||
</Rose::Layout::Centered>
|
||||
|
||||
@@ -64,7 +64,7 @@ export default function() {
|
||||
const account = accounts.find(id);
|
||||
const updatedAttrs = {
|
||||
version: attrs.version,
|
||||
attributes: {}
|
||||
attributes: account.attributes
|
||||
};
|
||||
// Set new password
|
||||
if (method === 'set-password') {
|
||||
|
||||
@@ -1,11 +1,25 @@
|
||||
import factory from '../generated/factories/target';
|
||||
import { trait } from 'ember-cli-mirage';
|
||||
import { random } from 'faker';
|
||||
|
||||
const randomBoolean = (chance = 0.5) => Math.random() < chance;
|
||||
const hostSetChance = 0.3;
|
||||
|
||||
export default factory.extend({
|
||||
|
||||
/**
|
||||
* Generates attributes fields by type.
|
||||
*/
|
||||
afterCreate(target) {
|
||||
if (target.type === 'tcp') {
|
||||
target.update({
|
||||
attributes: {
|
||||
default_port: random.number()
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Randomly selects existing host sets to assign to target.
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Model, belongsTo, hasMany} from 'ember-cli-mirage';
|
||||
import { Model, belongsTo } from 'ember-cli-mirage';
|
||||
|
||||
export default Model.extend({
|
||||
|
||||
scope: belongsTo(),
|
||||
accounts: hasMany('account')
|
||||
scope: belongsTo()
|
||||
|
||||
});
|
||||
@@ -6,6 +6,7 @@ export default ApplicationSerializer.extend({
|
||||
_hashForModel(model) {
|
||||
const json = ApplicationSerializer.prototype._hashForModel.apply(this, arguments);
|
||||
json.auth_method_id = model.authMethodId;
|
||||
delete json.attributes?.password;
|
||||
return json;
|
||||
}
|
||||
});
|
||||
@@ -111,7 +111,7 @@ titles:
|
||||
no-available-users: No Users Available
|
||||
accounts: Accounts
|
||||
account: Account
|
||||
auth-method-accounts-welcome: Welcome to Accounts
|
||||
accounts-welcome: Welcome to Accounts
|
||||
descriptions:
|
||||
host-catalogs: Overview of collections of hosts and host sets.
|
||||
empty-set: There are no items to display yet. You may be able to add items or try back later.
|
||||
@@ -132,8 +132,8 @@ descriptions:
|
||||
no-principals-available: Principals are not available for assignment.
|
||||
group-members-welcome: Users belonging to this group appear here.
|
||||
no-available-users: No users are available to add to this group.
|
||||
auth-method-accounts-welcome: No accounts are available in this auth method.
|
||||
account-settings-overview: Set the password for current account.
|
||||
accounts-welcome: No accounts are available in this auth method.
|
||||
states:
|
||||
info: Info
|
||||
success: Success
|
||||
@@ -152,6 +152,7 @@ form:
|
||||
address: Address
|
||||
protocol: Protocol
|
||||
actions: Actions
|
||||
default_port: Default Port
|
||||
errors:
|
||||
titles:
|
||||
unauthenticated-error: You are not signed in
|
||||
|
||||
Reference in new issue
Block a user