Merge branch 'ICU-580-set-a-new-password' into ICU-580-remove-password-attribute-from-account-model

This commit is contained in:
Susmitha Girumala authored and GitHub committed 2020-09-16 10:56:02 -04:00
commit a4b4fb0677
20 files changed
+162 -57

No files matched your search

+3 -2
View File
@@ -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;
}
+7 -1
View File
@@ -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]}
*/
+13 -8
View File
@@ -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
}
});
});
+22 -5
View File
@@ -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>
+1 -1
View File
@@ -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') {
+14
View File
@@ -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.
*/
+2 -3
View File
@@ -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()
});
+1
View File
@@ -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;
}
});
+3 -2
View File
@@ -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