Skip to content

Commit

Permalink
Create User & Complete Password Processes (#16)
Browse files Browse the repository at this point in the history
* #3 Complete password implemented.  Still needs testing.

* #3 Complete password tested.  MFA no longer is two steps to activate.

* #17 Starting to create new user.  Need to add role select box.

* #17 Create user is implemented but error handling is still required.  #15 Current user and sign out is improved.
  • Loading branch information
nadnoslen authored Jan 28, 2019
1 parent fafdbf6 commit ec6003a
Show file tree
Hide file tree
Showing 36 changed files with 669 additions and 101 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ yarn add --dev @fortawesome/free-solid-svg-icons
yarn add --dev @fortawesome/free-regular-svg-icons
yarn add --dev @fortawesome/free-brands-svg-icons

# Ember Select Boxes - this addon is great
ember install @zestia/ember-select-box

# For custom actions that aren't normal REST end points; e.g. `current-user` needs a `sign-out` endpoint.
ember install ember-api-actions

# For importing libraries (replaces ember-browserify); needed for: `import Auth from '@aws-amplify/Auth'
ember install ember-auto-import

Expand Down
8 changes: 8 additions & 0 deletions app/adapters/current-user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import ApplicationAdapter from './application';

export default ApplicationAdapter.extend({
_buildURL(/*modelName, id*/) {
const url = this._super(...arguments);
return url.replace('current-users', 'current-user');
}
});
8 changes: 7 additions & 1 deletion app/controllers/forgot-password.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import Controller from '@ember/controller';

export default Controller.extend({});
export default Controller.extend({
email: '',

queryParams: [
{ email: { type: 'string' } }
]
});
32 changes: 32 additions & 0 deletions app/models/current-user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { collectionAction } from 'ember-api-actions';
import BaseModel from 'ember-cybertooth-base-model/models/-base';
import DS from 'ember-data';

export default BaseModel.extend({
/* Attributes
* ---------------------------------------------------------------------------------------------------------------- */

email: DS.attr('string'),

/* Relationships
* ---------------------------------------------------------------------------------------------------------------- */

roles: DS.hasMany('role'),
sessions: DS.hasMany('session'),

/* Instance Methods
* ---------------------------------------------------------------------------------------------------------------- */

signOut() {
return new Promise((resolve, reject) => {
this._signOut()
.then(() => resolve(this))
.catch(response => reject(response))
});
},

/* Custom API end points
* ---------------------------------------------------------------------------------------------------------------- */

_signOut: collectionAction({ path: 'sign-out', type: 'PATCH' })
});
3 changes: 3 additions & 0 deletions app/models/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ export default BaseModel.extend({
browser: DS.attr('string'),
browserVersion: DS.attr('string'),
device: DS.attr('string'),
invalidatedAt: DS.attr('date'),
ipAddress: DS.attr('string'),
platform: DS.attr('string'),
platformVersion: DS.attr('string'),

/** Relationships
* ---------------------------------------------------------------------------------------------------------------- */

invalidatedBy: DS.belongsTo('user'),

user: DS.belongsTo('user')
});
15 changes: 12 additions & 3 deletions app/models/user.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
import { not, readOnly } from '@ember/object/computed';
import BaseModel from 'ember-cybertooth-base-model/models/-base';
import DS from 'ember-data';

export default BaseModel.extend({

/** Attributes
/* Attributes
* ---------------------------------------------------------------------------------------------------------------- */

email: DS.attr('string'),
inCognito: DS.attr('boolean'),

/** Relationships
/* Relationships
* ---------------------------------------------------------------------------------------------------------------- */

roles: DS.hasMany('role'),
sessions: DS.hasMany('session')
sessions: DS.hasMany('session', { inverse: 'user' }),

/* Computed
* ---------------------------------------------------------------------------------------------------------------- */

'inCognito?': readOnly('inCognito'),

'notInCognito?': not('inCognito?')
});
2 changes: 2 additions & 0 deletions app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Router.map(function () {
this.route('sessions', function () {
});
});
this.route('new', function () {
});
});
});
this.route('profile', function () {
Expand Down
7 changes: 6 additions & 1 deletion app/routes/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ export default Route.extend(ApplicationRouteMixin, {
},

logout(session) {
session.invalidate();
this.get('store')
.peekAll('current-user')
.get('firstObject')
.signOut()
.finally(() => this.get('store').unloadAll('current-user'))
.finally(() => session.invalidate())
}
},

Expand Down
9 changes: 7 additions & 2 deletions app/routes/forgot-password.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { hash } from 'rsvp';
import { getWithDefault } from '@ember/object';
import Route from '@ember/routing/route';

export default Route.extend({
Expand All @@ -17,9 +18,13 @@ export default Route.extend({
}
},

model() {
model(params) {
return hash({
username: ''
username: getWithDefault(params, 'email', '')
});
},

queryParams: {
email: { refreshModel: true }
}
});
16 changes: 15 additions & 1 deletion app/routes/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ import UnauthenticatedRouteMixin from 'ember-simple-auth/mixins/unauthenticated-

export default Route.extend(UnauthenticatedRouteMixin, {
actions: {
completePassword(authenticationState, newPassword, additionalAttributes = {}/*, submitEvent*/) {
this.get('session')
.completePassword(authenticationState, newPassword, additionalAttributes)
.then(authenticationState => {
if (authenticationState.get('mfaRequired?')) {
set(this, 'controller.model.authenticationState', authenticationState);
}
})
.catch(response => this.get('notify').error(response.message));
return false;
},

confirmSignIn(authenticationState, code) {
this.get('session')
.confirmSignIn(authenticationState, code)
Expand All @@ -18,7 +30,7 @@ export default Route.extend(UnauthenticatedRouteMixin, {
this.get('session')
.signIn(username, password)
.then(authenticationState => {
if (authenticationState.get('mfaRequired?')) {
if (authenticationState.get('mfaRequired?') || authenticationState.get('newPasswordRequired?')) {
set(this, 'controller.model.authenticationState', authenticationState);
}
})
Expand All @@ -32,8 +44,10 @@ export default Route.extend(UnauthenticatedRouteMixin, {

model(params) {
return {
additionalAttributes: {},
authenticationState: {}, // will be set during sign in if MFA is enabled
mfaCode: '',
newPassword: '',
password: '',
username: isNone(get(params, 'username')) ? '' : get(params, 'username')
}
Expand Down
15 changes: 14 additions & 1 deletion app/routes/protected.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
import Route from '@ember/routing/route';

export default Route.extend(AuthenticatedRouteMixin, {});
export default Route.extend(AuthenticatedRouteMixin, {
/**
* Loading the `current-user` into the store; you can peek to get access to it.
* @return {*|Promise} the `current-user` instance.
*/
model() {
return this.get('store').queryRecord('current-user', {
include: '' +
'roles' +
',sessions' +
''
});
}
});
10 changes: 10 additions & 0 deletions app/routes/protected/configuration/users/new.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { hash } from 'rsvp';
import Route from '@ember/routing/route';

export default Route.extend({
model() {
return hash({
roles: this.get('store').query('role', { sort: 'name' })
})
}
});
37 changes: 37 additions & 0 deletions app/routes/protected/configuration/users/new/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { isBlank } from '@ember/utils';
import { get } from '@ember/object';
import { hash } from 'rsvp';
import Route from '@ember/routing/route';

export default Route.extend({
actions: {
chooseRole(user, chosenRole) {
if (isBlank(chosenRole)) {
return true;
}

user.get('roles').pushObject(chosenRole);
return true;
},

save(user) {
user
.save()
.then((/*response*/) => {
this.get('notify').success('Created.');
})
.catch((errors) => {
console.error('???', errors);
this.get('notify').error('Check for errors.');
});
return false;
}
},

model() {
return hash({
roles: get(this.modelFor('protected.configuration.users.new'), 'roles'),
user: this.get('store').createRecord('user')
});
}
});
4 changes: 2 additions & 2 deletions app/routes/protected/profile/change-password.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ export default Route.extend({
session
.changePassword(currentPassword, newPassword)
.then(() => {
this.transitionTo('protected.index')
.then(newRoute => newRoute.get('notify').success('Your password was updated successfully.'));
this.refresh();
this.get('notify').success('Your password was updated successfully.');
})
.catch(response => this.get('notify').error(response.message));
return false;
Expand Down
11 changes: 2 additions & 9 deletions app/routes/protected/profile/configure-mfa.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,15 @@ export default Route.extend({
return false;
},

finalize(session, mfaActivationState) {
verify(session, mfaActivationState) {
session
.finalizeTotp(mfaActivationState)
.verifyTotpPasscode(mfaActivationState)
.then(() => {
set(this, 'controller.model.mfaActivationState', {});
this.get('notify').success('Multi-factor authentication is now enabled.')
})
.catch((response) => this.get('notify').error(response.message));
return false;
},

verify(session, mfaActivationState) {
session
.verifyTotpPasscode(mfaActivationState)
.catch((response) => this.get('notify').error(response.message));
return false;
}
},

Expand Down
8 changes: 8 additions & 0 deletions app/serializers/application.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import DS from 'ember-data';

export default DS.JSONAPISerializer.extend({
attrs: {
createdAt: { serialize: false },
updatedAt: { serialize: false }
}
});
2 changes: 1 addition & 1 deletion app/templates/forgot-password.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<legend>Reset My Password</legend>
<form class="form" onsubmit={{action (route-action "forgotPassword" session model.username)}}>
<div class="form-group">
<label for="js-username" class="control-label">Email Address</label>
<label for="js-username">Email Address</label>
{{input-text
aria-describedby="js-username-help"
autocomplete="email"
Expand Down
Loading

0 comments on commit ec6003a

Please sign in to comment.