Commit c139c6db authored by Gergely Ujvari's avatar Gergely Ujvari

Introduce auth service

- Bridge the for identity service
- Handles login, logout, and exposes the current logged in user
- permits function for authorizing actions
parent 87f2b942
class AccountController class AccountController
@inject = [ '$scope', '$filter', @inject = [ '$scope', '$filter',
'flash', 'formHelpers', 'identity', 'session', 'user'] 'flash', 'formHelpers', 'identity', 'session', 'auth']
constructor: ($scope, $filter, constructor: ($scope, $filter,
flash, formHelpers, identity, session, user ) -> flash, formHelpers, identity, session, auth ) ->
persona_filter = $filter('persona') persona_filter = $filter('persona')
$scope.subscriptionDescription = $scope.subscriptionDescription =
reply: 'Receive notification emails when: - Someone replies to one of my annotations' reply: 'Receive notification emails when: - Someone replies to one of my annotations'
...@@ -33,7 +33,7 @@ class AccountController ...@@ -33,7 +33,7 @@ class AccountController
$scope.$broadcast 'formState', form.$name, '' # Update status btn $scope.$broadcast 'formState', form.$name, '' # Update status btn
$scope.tab = 'Account' $scope.tab = 'Account'
session.profile({user_id: user.getPersona()}).$promise session.profile({user_id: auth.user}).$promise
.then (result) => .then (result) =>
$scope.subscriptions = result.subscriptions $scope.subscriptions = result.subscriptions
...@@ -47,7 +47,7 @@ class AccountController ...@@ -47,7 +47,7 @@ class AccountController
# The extension is then removed from the page. # The extension is then removed from the page.
# Confirmation of success is given. # Confirmation of success is given.
return unless form.$valid return unless form.$valid
username = persona_filter user.getPersona() username = persona_filter auth.user
packet = packet =
username: username username: username
pwd: form.pwd.$modelValue pwd: form.pwd.$modelValue
...@@ -62,7 +62,7 @@ class AccountController ...@@ -62,7 +62,7 @@ class AccountController
formHelpers.applyValidationErrors(form) formHelpers.applyValidationErrors(form)
return unless form.$valid return unless form.$valid
username = persona_filter user.getPersona() username = persona_filter auth.user
packet = packet =
username: username username: username
pwd: form.pwd.$modelValue pwd: form.pwd.$modelValue
...@@ -77,7 +77,7 @@ class AccountController ...@@ -77,7 +77,7 @@ class AccountController
$scope.updated = (index, form) -> $scope.updated = (index, form) ->
packet = packet =
username: user.getPersona() username: auth.user
subscriptions: JSON.stringify $scope.subscriptions[index] subscriptions: JSON.stringify $scope.subscriptions[index]
successHandler = angular.bind(null, onSuccess, form) successHandler = angular.bind(null, onSuccess, form)
......
...@@ -2,17 +2,15 @@ class AppController ...@@ -2,17 +2,15 @@ class AppController
this.$inject = [ this.$inject = [
'$location', '$route', '$scope', '$timeout', '$location', '$route', '$scope', '$timeout',
'annotator', 'flash', 'identity', 'streamer', 'streamfilter', 'annotator', 'flash', 'identity', 'streamer', 'streamfilter',
'documentHelpers', 'drafts', 'user' 'documentHelpers', 'drafts', 'auth'
] ]
constructor: ( constructor: (
$location, $route, $scope, $timeout, $location, $route, $scope, $timeout,
annotator, flash, identity, streamer, streamfilter, annotator, flash, identity, streamer, streamfilter,
documentHelpers, drafts, user documentHelpers, drafts, auth
) -> ) ->
{plugins, host, providers} = annotator {plugins, host, providers} = annotator
isFirstRun = $location.search().hasOwnProperty('firstrun')
applyUpdates = (action, data) -> applyUpdates = (action, data) ->
"""Update the application with new data from the websocket.""" """Update the application with new data from the websocket."""
return unless data?.length return unless data?.length
...@@ -54,7 +52,7 @@ class AppController ...@@ -54,7 +52,7 @@ class AppController
Store = plugins.Store Store = plugins.Store
delete plugins.Store delete plugins.Store
if user.getPersona() or annotator.socialView.name is 'none' if auth.user or annotator.socialView.name is 'none'
annotator.addPlugin 'Store', annotator.options.Store annotator.addPlugin 'Store', annotator.options.Store
$scope.store = plugins.Store $scope.store = plugins.Store
...@@ -79,12 +77,12 @@ class AppController ...@@ -79,12 +77,12 @@ class AppController
Store.updateAnnotation = angular.noop Store.updateAnnotation = angular.noop
# Sort out which annotations should remain in place. # Sort out which annotations should remain in place.
persona = user.getPersona() persona = auth.user
view = annotator.socialView.name view = annotator.socialView.name
cull = (acc, annotation) -> cull = (acc, annotation) ->
if view is 'single-player' and annotation.user != persona if view is 'single-player' and annotation.user != persona
acc.drop.push annotation acc.drop.push annotation
else if authorizeAction 'read', annotation, persona else if auth.permits 'read', annotation, persona
acc.keep.push annotation acc.keep.push annotation
else else
acc.drop.push annotation acc.drop.push annotation
...@@ -127,9 +125,11 @@ class AppController ...@@ -127,9 +125,11 @@ class AppController
$scope.dialog.visible = false $scope.dialog.visible = false
reset = -> reset = ->
$scope.persona = user.getPersona() $scope.persona = auth.user
$scope.dialog.visible = false $scope.dialog.visible = false
firstRun = false
# Update any edits in progress. # Update any edits in progress.
for draft in drafts.all() for draft in drafts.all()
annotator.publish 'beforeAnnotationCreated', draft annotator.publish 'beforeAnnotationCreated', draft
...@@ -139,12 +139,10 @@ class AppController ...@@ -139,12 +139,10 @@ class AppController
streamer.close() streamer.close()
streamer.open() streamer.open()
identity.watch {onlogin, onlogout, onready}
$scope.$watch 'socialView.name', (newValue, oldValue) -> $scope.$watch 'socialView.name', (newValue, oldValue) ->
return if newValue is oldValue return if newValue is oldValue
initStore() initStore()
if newValue is 'single-player' and not user.getPersona() if newValue is 'single-player' and not auth.user
annotator.show() annotator.show()
flash 'info', flash 'info',
'You will need to sign in for your highlights to be saved.' 'You will need to sign in for your highlights to be saved.'
...@@ -158,7 +156,7 @@ class AppController ...@@ -158,7 +156,7 @@ class AppController
$scope.sort = {name, predicate} $scope.sort = {name, predicate}
$scope.$watch 'store.entities', (entities, oldEntities) -> $scope.$watch 'store.entities', (entities, oldEntities) ->
return if entities is oldEntities return if entities is oldEntities or not entities
if entities.length if entities.length
streamfilter streamfilter
...@@ -169,12 +167,12 @@ class AppController ...@@ -169,12 +167,12 @@ class AppController
$scope.login = -> $scope.login = ->
$scope.dialog.visible = true $scope.dialog.visible = true
identity.request {oncancel} auth.login().then(reset, oncancel)
$scope.logout = -> $scope.logout = ->
return unless drafts.discard() return unless drafts.discard()
$scope.dialog.visible = false $scope.dialog.visible = false
identity.logout() auth.logout().then(reset)
$scope.loadMore = (number) -> $scope.loadMore = (number) ->
unless streamfilter.getPastData().hits then return unless streamfilter.getPastData().hits then return
...@@ -203,6 +201,11 @@ class AppController ...@@ -203,6 +201,11 @@ class AppController
$scope.sort = name: 'Location' $scope.sort = name: 'Location'
$scope.threading = plugins.Threading $scope.threading = plugins.Threading
auth.getInitialUser().then(reset, ->
reset()
$scope.login()
)
class AnnotationViewerController class AnnotationViewerController
this.$inject = [ this.$inject = [
......
...@@ -38,9 +38,9 @@ validate = (value) -> ...@@ -38,9 +38,9 @@ validate = (value) ->
### ###
AnnotationController = [ AnnotationController = [
'$scope', '$timeout', '$scope', '$timeout',
'annotator', 'drafts', 'flash', 'documentHelpers', 'timeHelpers', 'user' 'annotator', 'drafts', 'flash', 'documentHelpers', 'timeHelpers', 'auth'
($scope, $timeout, ($scope, $timeout,
annotator, drafts, flash, documentHelpers, timeHelpers, user annotator, drafts, flash, documentHelpers, timeHelpers, auth
) -> ) ->
@annotation = {} @annotation = {}
@action = 'view' @action = 'view'
...@@ -182,17 +182,16 @@ AnnotationController = [ ...@@ -182,17 +182,16 @@ AnnotationController = [
reply = {references, uri} reply = {references, uri}
annotator.publish 'beforeAnnotationCreated', reply annotator.publish 'beforeAnnotationCreated', reply
persona = user.getPersona() if auth.user?
if persona? reply.permissions.update = [auth.user]
reply.permissions.update = [persona] reply.permissions.delete = [auth.user]
reply.permissions.delete = [persona] reply.permissions.admin = [auth.user]
reply.permissions.admin = [persona]
# If replying to a public annotation make the response public. # If replying to a public annotation make the response public.
if 'group:__world__' in (model.permissions.read or []) if 'group:__world__' in (model.permissions.read or [])
reply.permissions.read = ['group:__world__'] reply.permissions.read = ['group:__world__']
else else
reply.permissions.read = [persona] reply.permissions.read = [auth.user]
###* ###*
# @ngdoc method # @ngdoc method
......
LOGIN_REQUEST = 'login'
LOGOUT_REQUEST = 'logout'
INITIAL_REQUEST = 'initial'
pendingRequests =
login: []
logout: []
initial: []
resolvePendingRequests = (requestType, user) ->
for request in pendingRequests[requestType]
request.resolve user if request.resolve?
pendingRequests[requestType] = []
rejectPendingRequests = (requestType, reason) ->
for request in pendingRequests[requestType]
request.reject reason if request.reject?
pendingRequests[requestType] = []
class Auth
_checkingToken: false
login: null
logout: null
getInitialUser: null
user: null
this.$inject = ['$location', '$q',
'annotator', 'documentHelpers', 'identity']
constructor: ( $location, $q,
annotator, documentHelpers, identity) ->
{plugins} = annotator
@login = ->
oncancel = ->
rejectPendingRequests LOGIN_REQUEST, null
deferred = $q.defer()
pendingRequests[LOGIN_REQUEST].push deferred
identity.request({oncancel})
deferred.promise
@logout = ->
deferred = $q.defer()
identity.logout()
pendingRequests[LOGOUT_REQUEST].push deferred
deferred.promise
@getInitialUser = ->
deferred = $q.defer()
pendingRequests[INITIAL_REQUEST].push deferred
deferred.promise
onlogin = (assertion) =>
@_checkingToken = true
# Configure the Auth plugin with the issued assertion as refresh token.
annotator.addPlugin 'Auth',
tokenUrl: documentHelpers.absoluteURI(
"/api/token?assertion=#{assertion}")
# Set the user from the token.
plugins.Auth.withToken (token) =>
@_checkingToken = false
annotator.addPlugin 'Permissions',
user: token.userId
userAuthorize: @permits
@user = token.userId
resolvePendingRequests INITIAL_REQUEST, @user
resolvePendingRequests LOGIN_REQUEST, @user
onlogout = =>
plugins.Auth?.element.removeData('annotator:headers')
plugins.Auth?.destroy()
delete plugins.Auth
plugins.Permissions?.setUser(null)
plugins.Permissions?.destroy()
delete plugins.Permissions
@user = null
@_checkingToken = false
resolvePendingRequests LOGOUT_REQUEST, @user
onready = =>
if not @_checkingToken
rejectPendingRequests INITIAL_REQUEST, null
identity.watch {onlogin, onlogout, onready}
# User authorization function for the Permissions plugin.
permits: (action, annotation, user) ->
if annotation.permissions
tokens = annotation.permissions[action] || []
if tokens.length == 0
# Empty or missing tokens array: only admin can perform action.
return false
for token in tokens
if user == token
return true
if token == 'group:__world__'
return true
# No tokens matched: action should not be performed.
return false
# Coarse-grained authorization
else if annotation.user
return user and user == annotation.user
# No authorization info on annotation: free-for-all!
true
angular.module('h')
.service('auth', Auth)
# User authorization function for the Permissions plugin.
authorizeAction = (action, annotation, user) ->
if annotation.permissions
tokens = annotation.permissions[action] || []
if tokens.length == 0
# Empty or missing tokens array: only admin can perform action.
return false
for token in tokens
if user == token
return true
if token == 'group:__world__'
return true
# No tokens matched: action should not be performed.
return false
# Coarse-grained authorization
else if annotation.user
return user and user == annotation.user
# No authorization info on annotation: free-for-all!
true
class User
_persona: undefined
_checkingToken: false
login: undefined
logout: undefined
this.$inject = ['annotator', 'documentHelpers']
constructor: ( annotator, documentHelpers) ->
{plugins} = annotator
@login = (assertion, callbackFn) ->
@_checkingToken = true
# Configure the Auth plugin with the issued assertion as refresh token.
annotator.addPlugin 'Auth',
tokenUrl: documentHelpers.absoluteURI(
"/api/token?assertion=#{assertion}")
# Set the user from the token.
plugins.Auth.withToken (token) =>
@_checkingToken = false
annotator.addPlugin 'Permissions',
user: token.userId
userAuthorize: authorizeAction
@_persona = token.userId
callbackFn()
@logout = ->
plugins.Auth?.element.removeData('annotator:headers')
plugins.Auth?.destroy()
delete plugins.Auth
plugins.Permissions?.setUser(null)
plugins.Permissions?.destroy()
delete plugins.Permissions
@_persona = null
@_checkingToken = false
checkingInProgress: -> @_checkingToken
getPersona: -> @_persona
noPersona: -> @_persona = null
angular.module('h')
.service('user', User)
\ No newline at end of file
...@@ -8,7 +8,7 @@ describe 'h.account.AccountController', -> ...@@ -8,7 +8,7 @@ describe 'h.account.AccountController', ->
fakeSession = null fakeSession = null
fakeIdentity = null fakeIdentity = null
fakeFormHelpers = null fakeFormHelpers = null
fakeUser = null fakeAuth = null
editProfilePromise = null editProfilePromise = null
disableUserPromise = null disableUserPromise = null
profilePromise = null profilePromise = null
...@@ -23,7 +23,7 @@ describe 'h.account.AccountController', -> ...@@ -23,7 +23,7 @@ describe 'h.account.AccountController', ->
logout: sandbox.spy() logout: sandbox.spy()
fakeFormHelpers = fakeFormHelpers =
applyValidationErrors: sandbox.spy() applyValidationErrors: sandbox.spy()
fakeUser = fakeAuth =
getPersona: (-> 'egon@columbia.edu') getPersona: (-> 'egon@columbia.edu')
$filterProvider.register 'persona', -> $filterProvider.register 'persona', ->
...@@ -33,7 +33,7 @@ describe 'h.account.AccountController', -> ...@@ -33,7 +33,7 @@ describe 'h.account.AccountController', ->
$provide.value 'flash', fakeFlash $provide.value 'flash', fakeFlash
$provide.value 'identity', fakeIdentity $provide.value 'identity', fakeIdentity
$provide.value 'formHelpers', fakeFormHelpers $provide.value 'formHelpers', fakeFormHelpers
$provide.value 'user', fakeUser $provide.value 'auth', fakeAuth
return return
beforeEach inject ($rootScope, $q, $controller) -> beforeEach inject ($rootScope, $q, $controller) ->
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment