Refactor auth code

* don't use __container__
* register it as a factory on container and inject into routes and
  controller
* avoid afterSignIn errors by checking on hooksTarget, ApplicationRoute
  should register itself as a hooksTarget
* keep user info on auth
This commit is contained in:
Piotr Sarnacki 2014-05-21 14:24:44 +02:00
parent b134f82fab
commit 3544d523d7
10 changed files with 76 additions and 84 deletions

View File

@ -1,8 +1,6 @@
unless window.TravisApplication unless window.TravisApplication
window.TravisApplication = Em.Application.extend(Ember.Evented, window.TravisApplication = Em.Application.extend(Ember.Evented,
LOG_TRANSITIONS: true, LOG_TRANSITIONS: true,
authState: Ember.computed.alias('auth.state')
signedIn: (-> @get('authState') == 'signed-in' ).property('authState')
mappings: (-> mappings: (->
broadcasts: Travis.Broadcast broadcasts: Travis.Broadcast
@ -40,8 +38,6 @@ unless window.TravisApplication
@tailing = new Travis.Tailing($(window), '#tail', '#log') @tailing = new Travis.Tailing($(window), '#tail', '#log')
@toTop = new Travis.ToTop($(window), '.to-top', '#log-container') @toTop = new Travis.ToTop($(window), '.to-top', '#log-container')
@set('auth', Travis.Auth.create(app: this, endpoint: Travis.config.api_endpoint))
reset: -> reset: ->
@_super.apply(this, arguments) @_super.apply(this, arguments)
@get('modelClasses').forEach (klass) -> @get('modelClasses').forEach (klass) ->
@ -54,22 +50,6 @@ unless window.TravisApplication
flash: (options) -> flash: (options) ->
Travis.lookup('controller:flash').loadFlashes([options]) Travis.lookup('controller:flash').loadFlashes([options])
storeAfterSignInPath: (path) ->
@get('auth').storeAfterSignInPath(path)
autoSignIn: (path) ->
@get('auth').autoSignIn()
signIn: ->
@get('auth').signIn()
signOut: ->
@get('auth').signOut()
signingIn: (->
Travis.get('authState') == 'signing-in'
).property('authState')
receive: (event, data) -> receive: (event, data) ->
[name, type] = event.split(':') [name, type] = event.split(':')

View File

@ -1,32 +1,32 @@
@Travis.Auth = Ember.Object.extend Auth = Ember.Object.extend
state: "signed-out" state: "signed-out"
receivingEnd: "#{location.protocol}//#{location.host}" receivingEnd: "#{location.protocol}//#{location.host}"
init: -> init: ->
window.addEventListener('message', (e) => @receiveMessage(e)) window.addEventListener('message', (e) => @receiveMessage(e))
endpoint: (->
@container.lookup('application:main').config.api_endpoint
).property(),
signOut: -> signOut: ->
Travis.storage.removeItem('travis.user') Travis.storage.removeItem('travis.user')
Travis.storage.removeItem('travis.token') Travis.storage.removeItem('travis.token')
Travis.sessionStorage.clear() Travis.sessionStorage.clear()
@set('state', 'signed-out') @set('state', 'signed-out')
@set('user', undefined) @set('user', undefined)
if user = Travis.__container__.lookup('controller:currentUser').get('content') if user = @get('currentUser')
user.unload() user.unload()
Travis.__container__.lookup('controller:currentUser').set('content', null) @set('currentUser', null)
if controller = Travis.__container__.lookup('controller:currentUser') if hooksTarget = @get('hooksTarget')
try hooksTarget.afterSignOut()
controller.send('afterSignOut')
catch e
throw e unless e.message =~ /There are no active handlers/
signIn: (data) -> signIn: (data) ->
if data if data
@autoSignIn(data) @autoSignIn(data)
else else
@set('state', 'signing-in') @set('state', 'signing-in')
url = "#{@endpoint}/auth/post_message?origin=#{@receivingEnd}" url = "#{@get('endpoint')}/auth/post_message?origin=#{@receivingEnd}"
$('<iframe id="auth-frame" />').hide().appendTo('body').attr('src', url) $('<iframe id="auth-frame" />').hide().appendTo('body').attr('src', url)
autoSignIn: (data) -> autoSignIn: (data) ->
@ -60,33 +60,36 @@
@storeData(data, Travis.sessionStorage) @storeData(data, Travis.sessionStorage)
@storeData(data, Travis.storage) unless @userDataFrom(Travis.storage) @storeData(data, Travis.storage) unless @userDataFrom(Travis.storage)
user = @loadUser(data.user) user = @loadUser(data.user)
# TODO: we should not use __container__ directly, how to do it better? @set('currentUser', user)
# A good answer seems to do auth in context of controller.
Travis.__container__.lookup('controller:currentUser').set('content', user)
@set('state', 'signed-in') @set('state', 'signed-in')
Travis.trigger('user:signed_in', data.user) Travis.trigger('user:signed_in', data.user)
if controller = Travis.__container__.lookup('controller:currentUser') # TODO: I would like to get rid of this dependency in the future
Ember.run.next => if hooksTarget = @get('hooksTarget')
try hooksTarget.afterSignIn()
controller.send('afterSignIn')
catch e
throw e unless e =~ /There are no active handlers/ || e =~ /Can't trigger action "afterSignIn/
@refreshUserData(data.user)
refreshUserData: (user) -> refreshUserData: (user) ->
Travis.ajax.get "/users/#{user.id}", (data) => Travis.ajax.get "/users/#{user.id}", (data) =>
Travis.loadOrMerge(Travis.User, data.user) Travis.loadOrMerge(Travis.User, data.user)
# if user is still signed in, update saved data # if user is still signed in, update saved data
if @signedIn() if @get('signedIn')
data.user.token = user.token data.user.token = user.token
@storeData(data, Travis.sessionStorage) @storeData(data, Travis.sessionStorage)
@storeData(data, Travis.storage) @storeData(data, Travis.storage)
, (data, status, xhr) => , (data, status, xhr) =>
@signOut() if xhr.status == 401 @signOut() if xhr.status == 401
signedIn: -> signedIn: (->
@get('state') == 'signed-in' @get('state') == 'signed-in'
).property('state')
signedOut: (->
@get('state') == 'signed-out'
).property('state')
signingIn: (->
@get('state') == 'signing-in'
).property('state')
storeData: (data, storage) -> storeData: (data, storage) ->
storage.setItem('travis.token', data.token) if data.token storage.setItem('travis.token', data.token) if data.token
@ -101,10 +104,22 @@
receiveMessage: (event) -> receiveMessage: (event) ->
if event.origin == @expectedOrigin() if event.origin == @expectedOrigin()
if event.data == 'redirect' if event.data == 'redirect'
window.location = "#{@endpoint}/auth/handshake?redirect_uri=#{location}" window.location = "#{@get('endpoint')}/auth/handshake?redirect_uri=#{location}"
else if event.data.user? else if event.data.user?
event.data.user.token = event.data.travis_token if event.data.travis_token event.data.user.token = event.data.travis_token if event.data.travis_token
@setData(event.data) @setData(event.data)
expectedOrigin: -> expectedOrigin: ->
if @endpoint[0] == '/' then @receivingEnd else @endpoint endpoint = @get('endpoint')
if endpoint[0] == '/' then @receivingEnd else endpoint
Ember.onLoad 'Ember.Application', (Application) ->
Application.initializer
name: "auth",
initialize: (container, application) ->
application.register 'auth:main', Auth
application.inject('route', 'auth', 'auth:main')
application.inject('controller', 'auth', 'auth:main')
application.inject('application', 'auth', 'auth:main')

View File

@ -13,18 +13,6 @@ Travis.TopController = Em.Controller.extend
"#{location.protocol}//www.gravatar.com/avatar/#{@get('user.gravatarId')}?s=48&d=mm" "#{location.protocol}//www.gravatar.com/avatar/#{@get('user.gravatarId')}?s=48&d=mm"
).property('user.gravatarId') ).property('user.gravatarId')
signedIn: (->
Travis.get('authState') == 'signed-in'
).property('Travis.authState')
signedOut: (->
Travis.get('authState') == 'signed-out'
).property('Travis.authState')
signingIn: (->
Travis.get('authState') == 'signing-in'
).property('Travis.authState')
Travis.ApplicationController = Em.Controller.extend Travis.ApplicationController = Em.Controller.extend
templateName: 'layouts/home' templateName: 'layouts/home'

View File

@ -8,8 +8,12 @@ Travis.CurrentUserController = Em.ObjectController.extend
sync: -> sync: ->
@get('content').sync() @get('content').sync()
content: (->
@get('auth.currentUser')
).property('auth.currentUser')
syncingDidChange: (-> syncingDidChange: (->
if (user = @get('content')) && user.get('isSyncing') && !user.get('syncedAt') if (user = @get('content')) && user.get('isSyncing') && !user.get('syncedAt')
Ember.run.scheduleOnce 'routerTransitions', this, -> Ember.run.scheduleOnce 'routerTransitions', this, ->
@container.lookup('router:main').send('renderFirstSync') @container.lookup('router:main').send('renderFirstSync')
).observes('isSyncing', 'content') ).observes('isSyncing', 'content')

View File

@ -12,10 +12,10 @@ Travis.Route = Ember.Route.extend
@transitionTo 'first_sync' @transitionTo 'first_sync'
beforeModel: (transition) -> beforeModel: (transition) ->
Travis.autoSignIn() unless @signedIn() @auth.autoSignIn() unless @signedIn()
if !@signedIn() && @get('needsAuth') if !@signedIn() && @get('needsAuth')
Travis.auth.set('afterSignInTransition', transition) @auth.set('afterSignInTransition', transition)
Ember.RSVP.reject("needs-auth") Ember.RSVP.reject("needs-auth")
else else
@_super.apply(this, arguments) @_super.apply(this, arguments)
@ -24,7 +24,7 @@ Travis.Route = Ember.Route.extend
@controllerFor('currentUser').get('content') @controllerFor('currentUser').get('content')
redirect: -> redirect: ->
Travis.autoSignIn() unless @signedIn() @auth.autoSignIn() unless @signedIn()
if @get('needsAuth') if @get('needsAuth')
@authorize(@router.location.getURL()) @authorize(@router.location.getURL())
@ -33,10 +33,23 @@ Travis.Route = Ember.Route.extend
authorize: (path) -> authorize: (path) ->
if !@signedIn() if !@signedIn()
Travis.storeAfterSignInPath(path) @auth.storeAfterSignInPath(path)
@transitionTo('auth') @transitionTo('auth')
Travis.ApplicationRoute = Travis.Route.extend Travis.ApplicationRoute = Travis.Route.extend
init: ->
@_super.apply this, arguments
@auth.set('hooksTarget', this)
afterSignIn: ->
if transition = @auth.get('afterSignInTransition')
@auth.set('afterSignInTransition', null)
transition.retry()
afterSignOut: ->
@transitionTo('index.current')
actions: actions:
redirectToGettingStarted: -> redirectToGettingStarted: ->
# do nothing, we handle it only in index path # do nothing, we handle it only in index path
@ -55,14 +68,6 @@ Travis.ApplicationRoute = Travis.Route.extend
renderFirstSync: -> renderFirstSync: ->
@renderFirstSync() @renderFirstSync()
afterSignIn: ->
if transition = Travis.auth.get('afterSignInTransition')
Travis.auth.set('afterSignInTransition', null)
transition.retry()
afterSignOut: ->
@transitionTo('index.current')
Travis.Router.map -> Travis.Router.map ->
@resource 'index', path: '/', -> @resource 'index', path: '/', ->
@resource 'getting_started' @resource 'getting_started'

View File

@ -6,8 +6,8 @@
<h2>In order to view your repositories, please sign in.</h2> <h2>In order to view your repositories, please sign in.</h2>
<p> <p>
<a href="#" {{action "signIn" target="Travis"}}> <a href="#" {{action "signIn" target="auth"}}>
{{#if Travis.signingIn}} {{#if auth.signingIn}}
Signing in... Signing in...
{{else}} {{else}}
Sign in with GitHub Sign in with GitHub

View File

@ -31,13 +31,13 @@
</li> </li>
<li {{bind-attr class="view.classProfile"}}> <li {{bind-attr class="view.classProfile"}}>
<p class="handle"> <p class="handle">
{{#if signedOut}} {{#if auth.signedOut}}
<a class="signed-out" href="#" {{action "signIn" target="Travis"}}>Sign in with GitHub</a> <a class="signed-out" href="#" {{action "signIn" target="auth"}}>Sign in with GitHub</a>
{{/if}} {{/if}}
{{#if signedIn}} {{#if auth.signedIn}}
{{#link-to "profile" class="signed-in"}}<img {{bind-attr src="gravatarUrl"}}/>{{userName}}{{/link-to}} {{#link-to "profile" class="signed-in"}}<img {{bind-attr src="gravatarUrl"}}/>{{userName}}{{/link-to}}
{{/if}} {{/if}}
{{#if signingIn}} {{#if auth.signingIn}}
<span class="signing-in">Signing In</span> <span class="signing-in">Signing In</span>
{{/if}} {{/if}}
</p> </p>
@ -46,7 +46,7 @@
{{#link-to "profile.index" class="signed-in"}}Accounts{{/link-to}} {{#link-to "profile.index" class="signed-in"}}Accounts{{/link-to}}
</li> </li>
<li> <li>
<a href="/" {{action "signOut" target="Travis"}}>Sign Out</a> <a href="/" {{action "signOut" target="auth"}}>Sign Out</a>
</li> </li>
</ul> </ul>
</li> </li>

View File

@ -1,7 +1,3 @@
@Travis.reopen @Travis.reopen
SigninView: Travis.View.extend SigninView: Travis.View.extend
templateName: 'auth/signin' templateName: 'auth/signin'
signingIn: (->
Travis.get('authState') == 'signing-in'
).property('Travis.authState')

View File

@ -16,9 +16,9 @@
classProfile: (-> classProfile: (->
classes = ['profile menu'] classes = ['profile menu']
classes.push('active') if @get('tab') == 'profile' classes.push('active') if @get('tab') == 'profile'
classes.push(Travis.get('authState') || 'signed-out') classes.push(@get('controller.auth.state') || 'signed-out')
classes.join(' ') classes.join(' ')
).property('tab', 'Travis.authState') ).property('tab', 'controller.auth.state')
showProfile: -> showProfile: ->
$('#top .profile ul').show() $('#top .profile ul').show()

View File

@ -7,7 +7,11 @@ Travis.injectTestHelpers()
oldSetup = Travis.setup oldSetup = Travis.setup
Travis.ready = -> Travis.ready = ->
oldSetup.apply(this, arguments) oldSetup.apply(this, arguments)
Travis.auth.signOut() # TODO: in the future I would like to create a TestAuth class which
# would have similar interface to Travis.Auth, but with the
# auth logic stubbed out. It will give use ability to do things
# like testAuth.shouldSucceedOnSignIn()
Travis.lookup('auth:main').signOut()
window.exists = (selector) -> window.exists = (selector) ->
return !!find(selector).length return !!find(selector).length
@ -61,7 +65,7 @@ window.signInUser = (data) ->
# for now let's just use harcoded data to log in the user, # for now let's just use harcoded data to log in the user,
# we may extend it in the future to pass specific user data # we may extend it in the future to pass specific user data
Travis.auth.signIn Travis.lookup('auth:main').signIn
user: userData user: userData
token: 'abcdef' token: 'abcdef'