From cbac8d2d5998f43ddbfc12f763f478b042394f22 Mon Sep 17 00:00:00 2001 From: zinyando Date: Tue, 28 Jun 2016 22:15:03 +0200 Subject: [PATCH] Refactor login to use ember-simple-auth --- app/app/authenticators/documize.js | 33 ++++ app/app/components/folder/folder-toolbar.js | 6 +- app/app/components/layout/zone-navigation.js | 29 +-- app/app/pods/auth/login/controller.js | 25 +-- app/app/routes/application.js | 10 +- app/app/services/ajax.js | 20 +- app/app/services/app-meta.js | 2 +- app/app/services/folder.js | 10 +- app/app/services/session.js | 188 +++---------------- app/config/environment.js | 154 +++++++-------- 10 files changed, 196 insertions(+), 281 deletions(-) create mode 100644 app/app/authenticators/documize.js diff --git a/app/app/authenticators/documize.js b/app/app/authenticators/documize.js new file mode 100644 index 00000000..1039bb6c --- /dev/null +++ b/app/app/authenticators/documize.js @@ -0,0 +1,33 @@ +import Ember from 'ember'; +import Base from 'ember-simple-auth/authenticators/base'; +import encodingUtil from '../utils/encoding'; +import netUtil from '../utils/net'; +import models from '../utils/model'; + +const { + isPresent +} = Ember; + +export default Base.extend({ + serverTokenEndpoint: `public/authenticate`, + + ajax: Ember.inject.service(), + + authenticate({password, email}) { + let domain = netUtil.getSubdomain(); + + if (!isPresent(password) || !isPresent(email)) { + return Ember.RSVP.reject("invalid"); + } + + var encoded = encodingUtil.Base64.encode(`${domain}:${email}:${password}`); + + var headers = { + 'Authorization': 'Basic ' + encoded + }; + + return this.get('ajax').post('public/authenticate', { + headers + }); + } +}); diff --git a/app/app/components/folder/folder-toolbar.js b/app/app/components/folder/folder-toolbar.js index 5ece1015..b523bba0 100644 --- a/app/app/components/folder/folder-toolbar.js +++ b/app/app/components/folder/folder-toolbar.js @@ -13,6 +13,10 @@ import Ember from 'ember'; import NotifierMixin from '../../mixins/notifier'; import TooltipMixin from '../../mixins/tooltip'; +const { + computed +} = Ember; + export default Ember.Component.extend(NotifierMixin, TooltipMixin, { documentService: Ember.inject.service('document'), templateService: Ember.inject.service('template'), @@ -22,7 +26,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, { busy: false, importedDocuments: [], savedTemplates: [], - isFolderOwner: false, + isFolderOwner: computed.equal('folder.userId', 'session.user.id'), moveFolderId: "", didReceiveAttrs() { diff --git a/app/app/components/layout/zone-navigation.js b/app/app/components/layout/zone-navigation.js index 0c7acc0e..cf52fc08 100644 --- a/app/app/components/layout/zone-navigation.js +++ b/app/app/components/layout/zone-navigation.js @@ -15,15 +15,16 @@ import netUtil from '../../utils/net'; export default Ember.Component.extend({ folderService: Ember.inject.service('folder'), folder: null, + appMeta: Ember.inject.service(), - didInitAttrs() { - let self = this; - if (this.session.authenticated) { - this.session.user.accounts.forEach(function(account) { - account.active = account.orgId === self.session.appMeta.orgId; - }); - } - }, + didInitAttrs() { + if (this.get("session.authenticated")) { + this.get("session.user.accounts").forEach((account)=>{ + // TODO: do not mutate account.active here + account.active = account.orgId === this.get("appMeta.orgId"); + }); + } + }, didReceiveAttrs() { if (this.get('folder') === null) { @@ -31,10 +32,10 @@ export default Ember.Component.extend({ } }, - actions: { - switchAccount(domain) { - this.audit.record('switched-account'); - window.location.href = netUtil.getAppUrl(domain); - } - } + actions: { + switchAccount(domain) { + this.audit.record('switched-account'); + window.location.href = netUtil.getAppUrl(domain); + } + } }); diff --git a/app/app/pods/auth/login/controller.js b/app/app/pods/auth/login/controller.js index 3eefa5d5..a66fcf53 100644 --- a/app/app/pods/auth/login/controller.js +++ b/app/app/pods/auth/login/controller.js @@ -4,6 +4,8 @@ export default Ember.Controller.extend({ email: "", password: "", invalidCredentials: false, + session: Ember.inject.service('session'), + audit: Ember.inject.service('audit'), reset() { this.setProperties({ @@ -20,24 +22,13 @@ export default Ember.Controller.extend({ actions: { login() { - let self = this; let creds = this.getProperties('email', 'password'); - this.session.login(creds).then(function() { - self.set('invalidCredentials', false); - self.audit.record("logged-in"); - - var previousTransition = self.session.get('previousTransition'); - - if (previousTransition) { - previousTransition.retry(); - self.session.set('previousTransition', null); - } else { - self.transitionToRoute('folders.folder'); - } - }, function() { - self.set('invalidCredentials', true); - }); + this.get('session').authenticate('authenticator:documize', creds) + .then((response) => { + this.get('audit').record("logged-in"); + return response; + }); } } -}); \ No newline at end of file +}); diff --git a/app/app/routes/application.js b/app/app/routes/application.js index 872ca6e5..82d778f5 100644 --- a/app/app/routes/application.js +++ b/app/app/routes/application.js @@ -11,26 +11,28 @@ import Ember from 'ember'; import netUtil from '../utils/net'; +import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin'; const { inject: { service } } = Ember; -export default Ember.Route.extend({ +export default Ember.Route.extend(ApplicationRouteMixin, { userService: service('user'), - sessionService: service('session'), + session: service('session'), appMeta: service(), transitioning: false, beforeModel: function(transition) { let self = this; - let session = this.get('sessionService'); + let session = this.get('session'); + let appMeta = this.get('appMeta'); // Session ready? return this.get('appMeta').boot().then(function() { // Need to authenticate? - if (!session.get("appMeta.allowAnonymousAccess") && !session.get("authenticated") && + if (!appMeta.get("allowAnonymousAccess") && !session.get("isAuthenticated") && is.not.startWith(transition.targetName, 'auth.')) { if (!self.transitioning) { session.set('previousTransition', transition); diff --git a/app/app/services/ajax.js b/app/app/services/ajax.js index 0f44201d..d83a4533 100644 --- a/app/app/services/ajax.js +++ b/app/app/services/ajax.js @@ -1,7 +1,25 @@ import AjaxService from 'ember-ajax/services/ajax'; import config from '../config/environment'; +const { + computed, + inject: { service } +} = Ember; + export default AjaxService.extend({ + session: service(), host: config.apiHost, - namespace: config.apiNamespace + namespace: config.apiNamespace, + + headers: Ember.computed('session.session.content.authenticated.token', { + get() { + let headers = {}; + const token = this.get('session.session.content.authenticated.token'); + if (token) { + headers['authorization'] = token; + } + + return headers; + } + }) }); diff --git a/app/app/services/app-meta.js b/app/app/services/app-meta.js index b93b127d..ca0f64f5 100644 --- a/app/app/services/app-meta.js +++ b/app/app/services/app-meta.js @@ -15,7 +15,7 @@ export default Ember.Service.extend({ title: '', version: '', message: '', - allowAnonymousAccess: false, + allowAnonymousAccess: null, boot() { let dbhash; diff --git a/app/app/services/folder.js b/app/app/services/folder.js index 4ab9e2a0..d4574009 100644 --- a/app/app/services/folder.js +++ b/app/app/services/folder.js @@ -13,6 +13,10 @@ import Ember from 'ember'; import models from '../utils/model'; import BaseService from '../services/base'; +const { + get +} = Ember; + export default BaseService.extend({ sessionService: Ember.inject.service('session'), ajax: Ember.inject.service(), @@ -155,10 +159,10 @@ export default BaseService.extend({ } this.set('currentFolder', folder); - this.get('sessionService').storeSessionItem("folder", folder.get('id')); + this.get('sessionService').storeSessionItem("folder", get(folder, 'id')); this.set('canEditCurrentFolder', false); - let userId = this.get('sessionService').user.get('id'); + let userId = this.get('sessionService.user.id'); if (userId === "") { userId = "0"; } @@ -194,7 +198,7 @@ export default BaseService.extend({ } }); Ember.run(() => { - this.set('canEditCurrentFolder', canEdit && this.get('sessionService').authenticated); + this.set('canEditCurrentFolder', canEdit && this.get('sessionService.authenticated')); }); }); }, diff --git a/app/app/services/session.js b/app/app/services/session.js index 64cbdd0a..05052cdb 100644 --- a/app/app/services/session.js +++ b/app/app/services/session.js @@ -13,18 +13,31 @@ import Ember from 'ember'; import encodingUtil from '../utils/encoding'; import netUtil from '../utils/net'; import models from '../utils/model'; +import SimpleAuthSession from 'ember-simple-auth/services/session'; -export default Ember.Service.extend({ - ready: false, - appMeta: null, - isMac: false, - isMobile: false, - previousTransition: null, - user: null, - authenticated: false, +const { + inject: { service }, + computed: { oneWay } +} = Ember; + +export default SimpleAuthSession.extend({ + ajax: service(), + appMeta: service(), + + authenticated: oneWay('isAuthenticated'), + user: oneWay('session.content.authenticated.user'), folderPermissions: null, currentFolder: null, - ajax: Ember.inject.service(), + + authenticate() { + return this._super(...arguments) + .then(function({token, user}){ + return { + token, + user: models.User.create(user) + }; + }); + }, isAdmin: function() { if (this.authenticated && is.not.null(this.user) && this.user.id !== "") { @@ -40,86 +53,8 @@ export default Ember.Service.extend({ return false; }.property('user'), - // Boot up - init: function() { - this.set('user', models.UserModel.create()); - this.appMeta = models.AppMeta.create(); - - this.set('isMac', is.mac()); - this.set('isMobile', is.mobile()); - }, - - // Authentication - login: function(credentials) { - let domain = netUtil.getSubdomain(); - - this.clearSession(); - - if (is.empty(credentials.email) || is.empty(credentials.password)) { - return Ember.RSVP.reject("invalid"); - } - - var encoded = encodingUtil.Base64.encode(domain + ":" + credentials.email + ":" + credentials.password); - var headers = { - 'Authorization': 'Basic ' + encoded - }; - - return this.get('ajax').post('public/authenticate', { - headers - }).then((response)=>{ - this.setSession(response.token, models.UserModel.create(response.user)); - this.get('ready', true); - return response; - }); - }, - - // SSO in the form of 'domain:email:password' - sso: function(credentials) { - this.clearSession(); - - if (is.empty(credentials.email) || is.empty(credentials.password)) { - return Ember.RSVP.reject("invalid"); - } - - var headers = { - 'Authorization': 'Basic ' + credentials - }; - - return this.get('ajax').post('public/authenticate', { - headers - }).then((response)=>{ - this.setSession(response.token, models.UserModel.create(response.user)); - this.get('ready', true); - return response; - }); - }, - - // Goodbye - logout: function() { - this.clearSession(); - }, - - // Session management - setSession: function(token, user) { - this.set('user', user); - this.set('authenticated', true); - - this.storeSessionItem('token', token); - this.storeSessionItem('user', JSON.stringify(user)); - - let self = this; - - $.ajaxPrefilter(function(options, originalOptions, jqXHR) { - // We only tack on auth header for Documize API calls - if (is.startWith(options.url, self.get('appMeta.url'))) { - jqXHR.setRequestHeader('Authorization', 'Bearer ' + token); - } - }); - }, - clearSession: function() { - this.set('user', null); - this.set('authenticated', false); + // TODO: clear session properly with ESA localStorage.clear(); }, @@ -133,82 +68,5 @@ export default Ember.Service.extend({ clearSessionItem: function(key) { delete localStorage[key]; - }, - - // Application boot process - boot() { - let token = this.getSessionItem('token'); - - // TODO: the rest should be done through ESA - if (is.not.undefined(token)) { - // We now validate current token - - return this.get('ajax').request(`public/validate?token=${token}`, { - method: 'GET', - contentType: 'json' - }).then((user) => { - this.setSession(token, models.UserModel.create(user)); - this.set('ready', true); - }).catch((reason) => { - if (netUtil.isAjaxAccessError(reason)) { - localStorage.clear(); - window.location.href = "/auth/login"; - } - }); - } - - let self = this; - let dbhash = ""; - - if (is.not.null(document.head.querySelector("[property=dbhash]"))) { - dbhash = document.head.querySelector("[property=dbhash]").content; - } - - if (dbhash.length > 0 && dbhash !== "{{.DBhash}}") { - self.get('appMeta').set('orgId', "response.orgId"); - self.get('appMeta').setSafe('title', "Documize Setup"); - self.get('appMeta').set('version', "response.version"); - self.get('appMeta').setSafe('message', "response.message"); - self.get('appMeta').set('allowAnonymousAccess', false); - self.set('ready', true); - return new Ember.RSVP.Promise(function(resolve) { - resolve(); - }); - } - - if (this.get('ready')) { - return new Ember.RSVP.Promise(function(resolve) { - resolve(); - }); - } - - return this.get('ajax').request("public/meta") - .then((response) => { - this.get('appMeta').set('orgId', response.orgId); - this.get('appMeta').setSafe('title', response.title); - this.get('appMeta').set('version', response.version); - this.get('appMeta').setSafe('message', response.message); - this.get('appMeta').set('allowAnonymousAccess', response.allowAnonymousAccess); - - let token = this.getSessionItem('token'); - - if (is.not.undefined(token)) { - // We now validate current token - let tokenCheckUrl = `public/validate?token=${token}`; - - return this.get('ajax').request(tokenCheckUrl, { - method: 'GET', - contentType: 'json' - }).then((user) => { - this.setSession(token, models.UserModel.create(user)); - this.set('ready', true); - }).catch((reason) => { - if (netUtil.isAjaxAccessError(reason)) { - localStorage.clear(); - window.location.href = "/auth/login"; - } - }); - } - }); } }); diff --git a/app/config/environment.js b/app/config/environment.js index 79c01dda..2d90e9e9 100644 --- a/app/config/environment.js +++ b/app/config/environment.js @@ -13,83 +13,87 @@ module.exports = function(environment) { - var ENV = { - modulePrefix: 'documize', - podModulePrefix: 'documize/pods', - locationType: 'auto', - environment: environment, - baseURL: '/', - apiHost: '', - apiNamespace: '', - contentSecurityPolicyHeader: 'Content-Security-Policy-Report-Only', + var ENV = { + modulePrefix: 'documize', + podModulePrefix: 'documize/pods', + locationType: 'auto', + environment: environment, + baseURL: '/', + apiHost: '', + apiNamespace: '', + contentSecurityPolicyHeader: 'Content-Security-Policy-Report-Only', - EmberENV: { - FEATURES: {} - }, - "ember-cli-mirage": { - enabled: false - }, - APP: { - // Allows to disable audit service in tests - auditEnabled: true, - intercomKey: "" - } + EmberENV: { + FEATURES: {} + }, + "ember-cli-mirage": { + enabled: false + }, + 'ember-simple-auth': { + authenticationRoute: 'auth.login', + routeAfterAuthentication: 'folders.folder' + }, + APP: { + // Allows to disable audit service in tests + auditEnabled: true, + intercomKey: "" + } + }; + + if (environment === 'development') { + ENV.APP.LOG_TRANSITIONS = true; + ENV.APP.LOG_TRANSITIONS_INTERNAL = true; + ENV['ember-cli-mirage'] = { + enabled: false }; - if (environment === 'development') { - ENV.APP.LOG_TRANSITIONS = true; - ENV.APP.LOG_TRANSITIONS_INTERNAL = true; - ENV['ember-cli-mirage'] = { - enabled: false - }; - - ENV.apiHost = "https://localhost:5001"; - ENV.apiNamespace = "api"; - } - - if (environment === 'test') { - ENV.APP.LOG_RESOLVER = false; - ENV.APP.LOG_ACTIVE_GENERATION = false; - ENV.APP.LOG_VIEW_LOOKUPS = false; - // ENV.APP.LOG_TRANSITIONS = false; - // ENV.APP.LOG_TRANSITIONS_INTERNAL = false; - - ENV.baseURL = '/'; - ENV.locationType = 'none'; - ENV.APP.rootElement = '#ember-testing'; - ENV['ember-cli-mirage'] = { - enabled: true - }; - ENV.APP.auditEnabled = false; - - ENV.apiHost = "https://localhost:5001"; - } - - if (environment === 'production') { - ENV.APP.LOG_RESOLVER = false; - ENV.APP.LOG_ACTIVE_GENERATION = false; - ENV.APP.LOG_VIEW_LOOKUPS = false; - ENV.APP.LOG_TRANSITIONS = false; - ENV.APP.LOG_TRANSITIONS_INTERNAL = false; - - ENV.apiHost = ""; - } - - process.argv.forEach(function(element) { - if (element !== undefined) { - if (element.startsWith("intercom=")) { - element = element.replace("intercom=", ""); - ENV.APP.intercomKey = element; - } - if (element.startsWith("apiHost=")) { - element = element.replace("apiHost=", ""); - ENV.apiHost = element; - } - } - }); - + ENV.apiHost = "https://localhost:5001"; ENV.apiNamespace = "api"; - ENV.contentSecurityPolicy = null; + } - return ENV; -}; \ No newline at end of file + if (environment === 'test') { + ENV.APP.LOG_RESOLVER = false; + ENV.APP.LOG_ACTIVE_GENERATION = false; + ENV.APP.LOG_VIEW_LOOKUPS = false; + // ENV.APP.LOG_TRANSITIONS = false; + // ENV.APP.LOG_TRANSITIONS_INTERNAL = false; + + ENV.baseURL = '/'; + ENV.locationType = 'none'; + ENV.APP.rootElement = '#ember-testing'; + ENV['ember-cli-mirage'] = { + enabled: true + }; + ENV.APP.auditEnabled = false; + + ENV.apiHost = "https://localhost:5001"; + } + + if (environment === 'production') { + ENV.APP.LOG_RESOLVER = false; + ENV.APP.LOG_ACTIVE_GENERATION = false; + ENV.APP.LOG_VIEW_LOOKUPS = false; + ENV.APP.LOG_TRANSITIONS = false; + ENV.APP.LOG_TRANSITIONS_INTERNAL = false; + + ENV.apiHost = ""; + } + + process.argv.forEach(function(element) { + if (element !== undefined) { + if (element.startsWith("intercom=")) { + element = element.replace("intercom=", ""); + ENV.APP.intercomKey = element; + } + if (element.startsWith("apiHost=")) { + element = element.replace("apiHost=", ""); + ENV.apiHost = element; + } + } + }); + + ENV.apiNamespace = "api"; + ENV.contentSecurityPolicy = null; + + return ENV; +};