diff --git a/Gruntfile.js b/Gruntfile.js index 6ced3e32b4..e177caa0c0 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -509,11 +509,12 @@ var path = require('path'), 'bower_components/showdown/src/showdown.js', 'bower_components/moment/moment.js', 'bower_components/keymaster/keymaster.js', - 'bower_components/jquery-ui/ui/jquery-ui.js', 'bower_components/jquery-file-upload/js/jquery.fileupload.js', 'bower_components/fastclick/lib/fastclick.js', 'bower_components/nprogress/nprogress.js', + 'bower_components/ember-simple-auth/ember-simple-auth.js', + 'bower_components/ember-simple-auth/ember-simple-auth-oauth2.js', 'core/shared/lib/showdown/extensions/ghostimagepreview.js', 'core/shared/lib/showdown/extensions/ghostgfm.js', diff --git a/bower.json b/bower.json index 0917ac9476..3ee05013ff 100644 --- a/bower.json +++ b/bower.json @@ -22,6 +22,10 @@ "nprogress": "0.1.2", "showdown": "https://github.com/ErisDS/showdown.git#v0.3.2-ghost", "validator-js": "3.4.0", - "loader.js": "stefanpenner/loader.js#1.0.0" + "loader.js": "stefanpenner/loader.js#1.0.0", + "ember-simple-auth": "https://github.com/simplabs/ember-simple-auth-component.git#0.5.3" + }, + "resolutions": { + "ember": "~1.4.0" } } diff --git a/core/client/controllers/application.js b/core/client/controllers/application.js index 280c1df79f..bb214cd6a1 100644 --- a/core/client/controllers/application.js +++ b/core/client/controllers/application.js @@ -1,5 +1,4 @@ var ApplicationController = Ember.Controller.extend({ - isSignedIn: Ember.computed.bool('user.isSignedIn'), hideNav: Ember.computed.match('currentPath', /(signin|signup|setup|forgotten|reset)/), actions: { diff --git a/core/client/controllers/signin.js b/core/client/controllers/signin.js index 048b6e149f..623f3933ef 100644 --- a/core/client/controllers/signin.js +++ b/core/client/controllers/signin.js @@ -1,64 +1,5 @@ -import ajax from 'ghost/utils/ajax'; -import ValidationEngine from 'ghost/mixins/validation-engine'; - -var SigninController = Ember.ObjectController.extend(ValidationEngine, { - needs: 'application', - email: null, - password: null, - submitting: false, - - // ValidationEngine settings - validationType: 'signin', - - actions: { - login: function () { - var self = this, - data = this.getProperties('email', 'password'), - //Data to check if user came in somewhere besides index - appController = this.get('controllers.application'), - loginTransition = appController.get('loginTransition'); - - this.toggleProperty('submitting'); - - // @TODO This should call closePassive() to only close passive notifications - self.notifications.closeAll(); - - this.validate({ format: false }).then(function () { - ajax({ - url: self.get('ghostPaths').adminUrl('signin'), - type: 'POST', - headers: {'X-CSRF-Token': self.get('csrf')}, - data: data - }).then(function (response) { - // once the email and password are pulled from the controller - // they need to be cleared, or they will reappear next time the signin - // page is visited - self.setProperties({ - email: '', - password: '' - }); - - self.store.pushPayload({users: [response.userData]}); - return self.store.find('user', response.userData.id); - }).then(function (user) { - self.send('signedIn', user); - if (loginTransition) { - appController.set('loginTransition', null); - loginTransition.retry(); - } else { - self.transitionToRoute('posts'); - } - }).catch(function (resp) { - self.toggleProperty('submitting'); - self.notifications.showAPIError(resp, 'There was a problem logging in, please try again.'); - }); - }).catch(function (errors) { - self.toggleProperty('submitting'); - self.notifications.showErrors(errors); - }); - } - } - +var SigninController = Ember.Controller.extend(Ember.SimpleAuth.LoginControllerMixin, { + authenticatorFactory: 'ember-simple-auth-authenticator:oauth2-password-grant', }); export default SigninController; diff --git a/core/client/initializers/authentication.js b/core/client/initializers/authentication.js new file mode 100644 index 0000000000..a0578f6765 --- /dev/null +++ b/core/client/initializers/authentication.js @@ -0,0 +1,23 @@ +var AuthenticationInitializer = { + + name: 'authentication', + after: 'registerTrailingLocationHistory', + + initialize: function (container, application) { + Ember.SimpleAuth.Authenticators.OAuth2.reopen({ + serverTokenEndpoint: '/ghost/api/v0.1/authentication/token', + refreshAccessTokens: true, + makeRequest: function (data) { + data.client_id = 'ghost-admin'; + return this._super(data); + } + }); + Ember.SimpleAuth.setup(container, application, { + authenticationRoute: 'signin', + routeAfterAuthentication: 'content', + authorizerFactory: 'ember-simple-auth-authorizer:oauth2-bearer' + }); + } +}; + +export default AuthenticationInitializer; \ No newline at end of file diff --git a/core/client/initializers/notifications.js b/core/client/initializers/notifications.js index e0827b9574..3e0d4fac26 100644 --- a/core/client/initializers/notifications.js +++ b/core/client/initializers/notifications.js @@ -2,6 +2,7 @@ import Notifications from 'ghost/utils/notifications'; var injectNotificationsInitializer = { name: 'injectNotifications', + before: 'authentication', initialize: function (container, application) { application.register('notifications:main', Notifications); diff --git a/core/client/models/user.js b/core/client/models/user.js index 617b1e18fe..f2729b246a 100644 --- a/core/client/models/user.js +++ b/core/client/models/user.js @@ -20,8 +20,6 @@ var User = DS.Model.extend({ updated_at: DS.attr('moment-date'), updated_by: DS.attr('number'), - isSignedIn: Ember.computed.bool('id'), - validationErrors: function () { var validationErrors = []; diff --git a/core/client/routes/application.js b/core/client/routes/application.js index bf2f4fb7e6..5a0de37bd5 100644 --- a/core/client/routes/application.js +++ b/core/client/routes/application.js @@ -1,11 +1,25 @@ import ShortcutsRoute from 'ghost/mixins/shortcuts-route'; import mobileUtils from 'ghost/utils/mobile-utils'; -var ApplicationRoute = Ember.Route.extend(ShortcutsRoute, { +var ApplicationRoute = Ember.Route.extend(Ember.SimpleAuth.ApplicationRouteMixin, ShortcutsRoute, { + shortcuts: { 'esc': 'closePopups' }, + beforeModel: function () { + var self = this; + if (this.get('session').isAuthenticated) { + this.store.find('user', 'me').then(function (user) { + // Update the user on all routes and controllers + self.container.unregister('user:current'); + self.container.register('user:current', user, { instantiate: false }); + self.container.injection('route', 'user', 'user:current'); + self.container.injection('controller', 'user', 'user:current'); + + }); + } + }, mobileInteractions: function () { var responsiveAction = mobileUtils.responsiveAction; diff --git a/core/client/routes/authenticated.js b/core/client/routes/authenticated.js deleted file mode 100644 index 04c3311934..0000000000 --- a/core/client/routes/authenticated.js +++ /dev/null @@ -1,25 +0,0 @@ -var AuthenticatedRoute = Ember.Route.extend({ - beforeModel: function (transition) { - var user = this.container.lookup('user:current'); - - if (!user || !user.get('isSignedIn')) { - this.redirectToSignin(transition); - } - }, - redirectToSignin: function (transition) { - this.notifications.showError('Please sign in'); - if (transition) { - this.controllerFor('application').set('loginTransition', transition); - } - this.transitionTo('signin'); - }, - actions: { - error: function (error) { - if (error.jqXHR && error.jqXHR.status === 401) { - this.redirectToSignin(); - } - } - } -}); - -export default AuthenticatedRoute; \ No newline at end of file diff --git a/core/client/routes/debug.js b/core/client/routes/debug.js index 9cd9fc976a..46e779a90b 100644 --- a/core/client/routes/debug.js +++ b/core/client/routes/debug.js @@ -1,8 +1,7 @@ import styleBody from 'ghost/mixins/style-body'; -import AuthenticatedRoute from 'ghost/routes/authenticated'; import loadingIndicator from 'ghost/mixins/loading-indicator'; -var DebugRoute = AuthenticatedRoute.extend(styleBody, loadingIndicator, { +var DebugRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, styleBody, loadingIndicator, { classNames: ['settings'], model: function () { diff --git a/core/client/routes/editor/edit.js b/core/client/routes/editor/edit.js index e069aaa992..eb20df1b64 100644 --- a/core/client/routes/editor/edit.js +++ b/core/client/routes/editor/edit.js @@ -1,7 +1,6 @@ -import AuthenticatedRoute from 'ghost/routes/authenticated'; import base from 'ghost/mixins/editor-route-base'; -var EditorEditRoute = AuthenticatedRoute.extend(base, { +var EditorEditRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, base, { classNames: ['editor'], model: function (params) { diff --git a/core/client/routes/editor/new.js b/core/client/routes/editor/new.js index 04413f337c..25f7db0eed 100644 --- a/core/client/routes/editor/new.js +++ b/core/client/routes/editor/new.js @@ -1,7 +1,6 @@ -import AuthenticatedRoute from 'ghost/routes/authenticated'; import base from 'ghost/mixins/editor-route-base'; -var EditorNewRoute = AuthenticatedRoute.extend(base, { +var EditorNewRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, base, { classNames: ['editor'], model: function () { diff --git a/core/client/routes/posts.js b/core/client/routes/posts.js index 9657ca702c..179ea9a85c 100644 --- a/core/client/routes/posts.js +++ b/core/client/routes/posts.js @@ -1,4 +1,3 @@ -import AuthenticatedRoute from 'ghost/routes/authenticated'; import styleBody from 'ghost/mixins/style-body'; import ShortcutsRoute from 'ghost/mixins/shortcuts-route'; import loadingIndicator from 'ghost/mixins/loading-indicator'; @@ -10,7 +9,7 @@ var paginationSettings = { page: 1 }; -var PostsRoute = AuthenticatedRoute.extend(ShortcutsRoute, styleBody, loadingIndicator, { +var PostsRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, ShortcutsRoute, styleBody, loadingIndicator, { classNames: ['manage'], model: function () { diff --git a/core/client/routes/posts/index.js b/core/client/routes/posts/index.js index 4a01a81231..996297d931 100644 --- a/core/client/routes/posts/index.js +++ b/core/client/routes/posts/index.js @@ -1,7 +1,6 @@ -import AuthenticatedRoute from 'ghost/routes/authenticated'; import loadingIndicator from 'ghost/mixins/loading-indicator'; -var PostsIndexRoute = AuthenticatedRoute.extend(loadingIndicator, { +var PostsIndexRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, loadingIndicator, { // redirect to first post subroute unless no posts exist beforeModel: function () { var self = this; diff --git a/core/client/routes/posts/post.js b/core/client/routes/posts/post.js index 32ed5cc5b0..a4546f8b4a 100644 --- a/core/client/routes/posts/post.js +++ b/core/client/routes/posts/post.js @@ -1,8 +1,7 @@ -import AuthenticatedRoute from 'ghost/routes/authenticated'; import loadingIndicator from 'ghost/mixins/loading-indicator'; import ShortcutsRoute from 'ghost/mixins/shortcuts-route'; -var PostsPostRoute = AuthenticatedRoute.extend(loadingIndicator, ShortcutsRoute, { +var PostsPostRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, loadingIndicator, ShortcutsRoute, { model: function (params) { var self = this, post, diff --git a/core/client/routes/settings.js b/core/client/routes/settings.js index ee382dc879..1703d63a7d 100644 --- a/core/client/routes/settings.js +++ b/core/client/routes/settings.js @@ -1,8 +1,7 @@ import styleBody from 'ghost/mixins/style-body'; -import AuthenticatedRoute from 'ghost/routes/authenticated'; import loadingIndicator from 'ghost/mixins/loading-indicator'; -var SettingsRoute = AuthenticatedRoute.extend(styleBody, loadingIndicator, { +var SettingsRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, styleBody, loadingIndicator, { classNames: ['settings'] }); diff --git a/core/client/routes/settings/apps.js b/core/client/routes/settings/apps.js index 91e3fa90c9..f0bda6b86b 100644 --- a/core/client/routes/settings/apps.js +++ b/core/client/routes/settings/apps.js @@ -1,6 +1,4 @@ -import AuthenticatedRoute from 'ghost/routes/authenticated'; - -var AppsRoute = AuthenticatedRoute.extend({ +var AppsRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, { beforeModel: function () { if (!this.get('config.apps')) { this.transitionTo('settings.general'); diff --git a/core/client/routes/settings/general.js b/core/client/routes/settings/general.js index 8162322fca..c1fd5f830f 100644 --- a/core/client/routes/settings/general.js +++ b/core/client/routes/settings/general.js @@ -1,7 +1,6 @@ -import AuthenticatedRoute from 'ghost/routes/authenticated'; import loadingIndicator from 'ghost/mixins/loading-indicator'; -var SettingsGeneralRoute = AuthenticatedRoute.extend(loadingIndicator, { +var SettingsGeneralRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, loadingIndicator, { model: function () { return this.store.find('setting', { type: 'blog,theme' }).then(function (records) { return records.get('firstObject'); diff --git a/core/client/routes/settings/index.js b/core/client/routes/settings/index.js index a2c4bb5567..3f86e36842 100644 --- a/core/client/routes/settings/index.js +++ b/core/client/routes/settings/index.js @@ -1,6 +1,6 @@ -import AuthenticatedRoute from 'ghost/routes/authenticated'; -var SettingsIndexRoute = AuthenticatedRoute.extend({ +var SettingsIndexRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, { + // redirect to general tab redirect: function () { this.transitionTo('settings.general'); } diff --git a/core/client/routes/signin.js b/core/client/routes/signin.js index db0c94fa4b..535927e638 100644 --- a/core/client/routes/signin.js +++ b/core/client/routes/signin.js @@ -2,7 +2,34 @@ import styleBody from 'ghost/mixins/style-body'; import loadingIndicator from 'ghost/mixins/loading-indicator'; var SigninRoute = Ember.Route.extend(styleBody, loadingIndicator, { - classNames: ['ghost-login'] + classNames: ['ghost-login'], + actions: { + sessionAuthenticationFailed: function (error) { + this.notifications.showError(error.message); + }, + sessionAuthenticationSucceeded: function () { + var self = this; + this.store.find('user', 'me').then(function (user) { + self.send('signedIn', user); + var attemptedTransition = self.get('session').get('attemptedTransition'); + if (attemptedTransition) { + attemptedTransition.retry(); + self.get('session').set('attemptedTransition', null); + } else { + self.transitionTo(Ember.SimpleAuth.routeAfterAuthentication); + } + }); + }, + sessionInvalidationFailed: function (error) { + this.notifications.showError(error.message); + }, + sessionInvalidationSucceeded: function () { + this.notifications.showSuccess('You were successfully signed out.', true); + this.send('signedOut'); + } + } + + }); export default SigninRoute; \ No newline at end of file diff --git a/core/client/routes/signout.js b/core/client/routes/signout.js index 9b20f95446..33f255c8ed 100644 --- a/core/client/routes/signout.js +++ b/core/client/routes/signout.js @@ -1,27 +1,17 @@ -import ajax from 'ghost/utils/ajax'; import styleBody from 'ghost/mixins/style-body'; -import AuthenticatedRoute from 'ghost/routes/authenticated'; import loadingIndicator from 'ghost/mixins/loading-indicator'; -var SignoutRoute = AuthenticatedRoute.extend(styleBody, loadingIndicator, { +var SignoutRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, styleBody, loadingIndicator, { classNames: ['ghost-signout'], - beforeModel: function () { - var self = this; - - ajax({ - url: this.get('ghostPaths').adminUrl('signout'), - type: 'POST', - headers: { - 'X-CSRF-Token': this.get('csrf') - } - }).then(function () { - self.transitionTo('signin'); - self.notifications.showSuccess('You were successfully signed out.', true); - }, function (resp) { - self.notifications.showAPIError(resp, 'There was a problem logging out, please try again.', true); - self.transitionTo('posts'); - }); + afterModel: function (resolvedModel, transition) { + if (Ember.canInvoke(transition, 'send')) { + transition.abort(); + transition.send('invalidateSession'); + this.transitionTo('signin'); + } else { + this.send('invalidateSession'); + } } }); diff --git a/core/client/templates/-navbar.hbs b/core/client/templates/-navbar.hbs index 28fd63d721..5c97b83bcb 100644 --- a/core/client/templates/-navbar.hbs +++ b/core/client/templates/-navbar.hbs @@ -22,7 +22,7 @@