basic acl checks

This commit is contained in:
Davide Alberani 2016-06-02 16:06:12 +02:00
parent aa7250656f
commit 82432eaa04
4 changed files with 31 additions and 19 deletions

View file

@ -30,22 +30,24 @@
<thead> <thead>
<tr> <tr>
<th><strong>{{'Event' | translate}}</strong></th> <th><strong>{{'Event' | translate}}</strong></th>
<th class="hcenter"><strong>{{'Attendees / Registered' | translate}}</strong></th> <th ng-if="hasPermission('event|update')" class="hcenter"><strong>{{'Attendees / Registered' | translate}}</strong></th>
<th><strong>{{'Delete' | translate}}</strong></th> <th ng-if="hasPermission('event|update')"><strong>{{'Actions' | translate}}</strong></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="event in events | splittedFilter:query | orderBy:eventsOrderProp"> <tr ng-repeat="event in events | splittedFilter:query | orderBy:eventsOrderProp">
<td> <td>
<span><strong><a ui-sref="event.info({id: event._id})">{{event.title}}</a></strong></span> <span><strong>{{event.title}}</strong></span>
<p>{{'Begins:' | translate}} {{event['begin-date'] | date:'fullDate'}} {{event['begin-time'] | date:'HH:mm'}}<br/> <p>{{'Begins:' | translate}} {{event['begin-date'] | date:'fullDate'}} {{event['begin-time'] | date:'HH:mm'}}<br/>
{{'Ends:' | translate}} {{event['end-date'] | date:'fullDate' }} {{event['end-time'] | date:'HH:mm'}}</p> {{'Ends:' | translate}} {{event['end-date'] | date:'fullDate' }} {{event['end-time'] | date:'HH:mm'}}</p>
</td> </td>
<td class="hcenter"> <td ng-if="hasPermission('persons|read')" class="hcenter">
<p><span ng-init="attendeesNr = (event.persons | attendeesFilter).length">{{attendeesNr}}</span> / {{event.persons.length || 0}} ({{((attendeesNr / (event.persons.length || 0) * 100) || 0).toFixed()}}%)</p> <p><span ng-init="attendeesNr = (event.persons | attendeesFilter).length">{{attendeesNr}}</span> / {{event.persons.length || 0}} ({{((attendeesNr / (event.persons.length || 0) * 100) || 0).toFixed()}}%)</p>
</td> </td>
<td> <td ng-if="hasPermission('event|update')">
<button ng-click="remove(event._id)" type="button" class="btn btn-link fa fa-trash fa-lg"></button> <button ng-if="hasPermission('ticket|create')" ng-click="$state.go('ticket.new', {id: event._id})" class="btn btn-link fa fa-user-plus" type="button" title="{{'Register' | translate}}"></button>
<button ng-if="hasPermission('persons|update')" ng-click="$state.go('event.info', {id: event._id})" class="btn btn-link fa fa-list" type="button" title="{{'Manage attendees' | translate}}"></button>
<button ng-if="hasPermission('event|delete')" ng-click="remove(event._id)" type="button" class="btn btn-link fa fa-trash fa-lg" title="{{'Delete' | translate}}"></button>
</td> </td>
</tr> </tr>
</tbody> </tbody>

View file

@ -52,8 +52,7 @@
<div ng-if="logo.imgURL" class="navbar-brand"><a ng-if="logo.link" href="{{logo.link}}" target="_blank"><img src="{{logo.imgURL}}" /></a></div> <div ng-if="logo.imgURL" class="navbar-brand"><a ng-if="logo.link" href="{{logo.link}}" target="_blank"><img src="{{logo.imgURL}}" /></a></div>
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li ng-class="{active: isActive('/events') || isActive('/event')}"><a ui-sref="events">{{'Events' | translate}}</a></li> <li ng-class="{active: isActive('/events') || isActive('/event')}"><a ui-sref="events">{{'Events' | translate}}</a></li>
<!-- li ng-if="hasPermission('persons|read')" ng-class="{active: isActive('/persons') || isActive('/person') || isActive('/import/persons')}"><a ui-sref="persons">{{'Persons' | translate}}</a></li --> <li ng-if="hasPermission('persons|read')" ng-class="{active: isActive('/persons') || isActive('/person') || isActive('/import/persons')}"><a ui-sref="persons">{{'Persons' | translate}}</a></li>
<li ng-class="{active: isActive('/persons') || isActive('/person') || isActive('/import/persons')}"><a ui-sref="persons">{{'Persons' | translate}}</a></li>
</ul> </ul>
</div> </div>
<div class="collapse navbar-collapse"> <div class="collapse navbar-collapse">

View file

@ -73,7 +73,6 @@ eventManApp.run(['$rootScope', '$state', '$stateParams', '$log', 'Info',
granted = true; granted = true;
return; return;
} }
} }
); );
return granted; return granted;

View file

@ -141,9 +141,7 @@ class BaseHandler(tornado.web.RequestHandler):
:returns: True if the user is allowed to perform the action or False :returns: True if the user is allowed to perform the action or False
:rtype: bool :rtype: bool
""" """
user_info = self.current_user_info user_info = self.current_user_info or {}
if not user_info:
return False
user_permissions = user_info.get('permissions') or [] user_permissions = user_info.get('permissions') or []
global_permission = '%s|all' % permission.split('|')[0] global_permission = '%s|all' % permission.split('|')[0]
if 'admin|all' in user_permissions or global_permission in user_permissions or permission in user_permissions: if 'admin|all' in user_permissions or global_permission in user_permissions or permission in user_permissions:
@ -263,11 +261,11 @@ class CollectionHandler(BaseHandler):
@gen.coroutine @gen.coroutine
@authenticated @authenticated
def get(self, id_=None, resource=None, resource_id=None, **kwargs): def get(self, id_=None, resource=None, resource_id=None, acl=True, **kwargs):
if resource: if resource:
# Handle access to sub-resources. # Handle access to sub-resources.
permission = '%s:%s|read' % (self.document, resource) permission = '%s:%s|read' % (self.document, resource)
if not self.has_permission(permission): if acl and not self.has_permission(permission):
return self.build_error(status=401, message='insufficient permissions: %s' % permission) return self.build_error(status=401, message='insufficient permissions: %s' % permission)
method = getattr(self, 'handle_get_%s' % resource, None) method = getattr(self, 'handle_get_%s' % resource, None)
if method and callable(method): if method and callable(method):
@ -276,7 +274,7 @@ class CollectionHandler(BaseHandler):
if id_ is not None: if id_ is not None:
# read a single document # read a single document
permission = '%s|read' % self.document permission = '%s|read' % self.document
if not self.has_permission(permission): if acl and not self.has_permission(permission):
return self.build_error(status=401, message='insufficient permissions: %s' % permission) return self.build_error(status=401, message='insufficient permissions: %s' % permission)
self.write(self.db.get(self.collection, id_)) self.write(self.db.get(self.collection, id_))
else: else:
@ -285,9 +283,9 @@ class CollectionHandler(BaseHandler):
# Please, never return JSON lists that are not encapsulated into an object, # Please, never return JSON lists that are not encapsulated into an object,
# to avoid XSS vulnerabilities. # to avoid XSS vulnerabilities.
permission = '%s|read' % self.collection permission = '%s|read' % self.collection
if not self.has_permission(permission): if acl and not self.has_permission(permission):
return self.build_error(status=401, message='insufficient permissions: %s' % permission) return self.build_error(status=401, message='insufficient permissions: %s' % permission)
self.write({self.collection: self.db.query(self.collection)}) self.write({self.collection: self.db.query(self.collection, self.arguments)})
@gen.coroutine @gen.coroutine
@authenticated @authenticated
@ -423,6 +421,7 @@ class PersonsHandler(CollectionHandler):
document = 'person' document = 'person'
collection = 'persons' collection = 'persons'
object_id = 'person_id' object_id = 'person_id'
permissions = {}
def handle_get_events(self, id_, resource_id=None, **kwargs): def handle_get_events(self, id_, resource_id=None, **kwargs):
# Get a list of events attended by this person. # Get a list of events attended by this person.
@ -563,6 +562,16 @@ class EventsHandler(CollectionHandler):
return ret return ret
class TicketsHandler(CollectionHandler):
"""Handle requests for Tickets."""
document = 'ticket'
collection = 'tickets'
object_id = 'ticket_id'
permissions = {
'ticket|read': True
}
class EbCSVImportPersonsHandler(BaseHandler): class EbCSVImportPersonsHandler(BaseHandler):
"""Importer for CSV files exported from eventbrite.""" """Importer for CSV files exported from eventbrite."""
csvRemap = { csvRemap = {
@ -689,7 +698,7 @@ class WebSocketEventUpdatesHandler(tornado.websocket.WebSocketHandler):
try: try:
if self in _ws_clients.get(self._clean_url(self.request.uri), []): if self in _ws_clients.get(self._clean_url(self.request.uri), []):
_ws_clients[self._clean_url(self.request.uri)].remove(self) _ws_clients[self._clean_url(self.request.uri)].remove(self)
except Exception, e: except Exception as e:
logging.warn('WebSocketEventUpdatesHandler.on_close error closing websocket: %s', str(e)) logging.warn('WebSocketEventUpdatesHandler.on_close error closing websocket: %s', str(e))
@ -810,11 +819,14 @@ def run():
_ws_handler = (r"/ws/+event/+(?P<event_id>\w+)/+updates/?", WebSocketEventUpdatesHandler) _ws_handler = (r"/ws/+event/+(?P<event_id>\w+)/+updates/?", WebSocketEventUpdatesHandler)
_persons_path = r"/persons/?(?P<id_>\w+)?/?(?P<resource>\w+)?/?(?P<resource_id>\w+)?" _persons_path = r"/persons/?(?P<id_>\w+)?/?(?P<resource>\w+)?/?(?P<resource_id>\w+)?"
_events_path = r"/events/?(?P<id_>\w+)?/?(?P<resource>\w+)?/?(?P<resource_id>\w+)?" _events_path = r"/events/?(?P<id_>\w+)?/?(?P<resource>\w+)?/?(?P<resource_id>\w+)?"
_tickets_path = r"/tickets/?(?P<id_>\w+)?/?(?P<resource>\w+)?/?(?P<resource_id>\w+)?"
application = tornado.web.Application([ application = tornado.web.Application([
(_persons_path, PersonsHandler, init_params), (_persons_path, PersonsHandler, init_params),
(r'/v%s%s' % (API_VERSION, _persons_path), PersonsHandler, init_params), (r'/v%s%s' % (API_VERSION, _persons_path), PersonsHandler, init_params),
(_events_path, EventsHandler, init_params), (_events_path, EventsHandler, init_params),
(r'/v%s%s' % (API_VERSION, _events_path), EventsHandler, init_params), (r'/v%s%s' % (API_VERSION, _events_path), EventsHandler, init_params),
(_tickets_path, TicketsHandler, init_params),
(r'/v%s%s' % (API_VERSION, _tickets_path), TicketsHandler, init_params),
(r"/(?:index.html)?", RootHandler, init_params), (r"/(?:index.html)?", RootHandler, init_params),
(r"/ebcsvpersons", EbCSVImportPersonsHandler, init_params), (r"/ebcsvpersons", EbCSVImportPersonsHandler, init_params),
(r"/settings", SettingsHandler, init_params), (r"/settings", SettingsHandler, init_params),
@ -841,7 +853,7 @@ def run():
http_server.listen(options.port, options.address) http_server.listen(options.port, options.address)
# Also listen on options.port+1 for our local ws connection. # Also listen on options.port+1 for our local ws connection.
ws_application = tornado.web.Application([_ws_handler,], debug=options.debug) ws_application = tornado.web.Application([_ws_handler], debug=options.debug)
ws_http_server = tornado.httpserver.HTTPServer(ws_application) ws_http_server = tornado.httpserver.HTTPServer(ws_application)
ws_http_server.listen(options.port+1, address='127.0.0.1') ws_http_server.listen(options.port+1, address='127.0.0.1')
logger.debug('Starting WebSocket on ws://127.0.0.1:%d', options.port+1) logger.debug('Starting WebSocket on ws://127.0.0.1:%d', options.port+1)