From 0c231432a2f9f5f4eb64a52c1891c8e666283ba6 Mon Sep 17 00:00:00 2001 From: Davide Alberani Date: Mon, 3 Apr 2017 21:39:26 +0200 Subject: [PATCH 1/4] fixes 169: ability to grant super cow powers to users --- angular_app/js/app.js | 11 +++++++---- angular_app/js/controllers.js | 1 + angular_app/user-edit.html | 5 +++++ eventman_server.py | 17 +++++++++++++---- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/angular_app/js/app.js b/angular_app/js/app.js index 15c034a..e015b68 100644 --- a/angular_app/js/app.js +++ b/angular_app/js/app.js @@ -75,16 +75,19 @@ eventManApp.run(['$rootScope', '$state', '$stateParams', '$log', 'Info', } }; - /* Check GUI privileges. */ - $rootScope.hasPermission = function(permission) { - if (!($rootScope.info && $rootScope.info.user && $rootScope.info.user.permissions)) { + /* Check privileges of the currently logged in user or of the one specified with the second parameter. */ + $rootScope.hasPermission = function(permission, user) { + if (!(user || ($rootScope.info && $rootScope.info.user && $rootScope.info.user.permissions))) { return false; } + if (!user) { + user = $rootScope.info.user; + } var granted = false; var splitted_permission = permission.split('|'); var global_permission = splitted_permission[0] + '|all'; - angular.forEach($rootScope.info.user.permissions || [], + angular.forEach(user.permissions || [], function(value, idx) { if (value === 'admin|all' || value === global_permission || value === permission) { granted = true; diff --git a/angular_app/js/controllers.js b/angular_app/js/controllers.js index 636f989..4f25166 100644 --- a/angular_app/js/controllers.js +++ b/angular_app/js/controllers.js @@ -687,6 +687,7 @@ eventManControllers.controller('UsersCtrl', ['$scope', '$rootScope', '$state', ' if ($state.is('user.edit') && $state.params.id) { $scope.user = User.get({id: $state.params.id}, function() { $scope.updateUserInfo = $scope.user; + $scope.updateUserInfo.isAdmin = $rootScope.hasPermission('admin|all', $scope.updateUserInfo); }); } diff --git a/angular_app/user-edit.html b/angular_app/user-edit.html index 29017dc..3b1b30e 100644 --- a/angular_app/user-edit.html +++ b/angular_app/user-edit.html @@ -20,6 +20,11 @@ {{'New password' | translate}} +
+ +
diff --git a/eventman_server.py b/eventman_server.py index c45f2f2..2685ef6 100755 --- a/eventman_server.py +++ b/eventman_server.py @@ -909,9 +909,18 @@ class UsersHandler(CollectionHandler): del data['_id'] if 'username' in data: del data['username'] - # for the moment, prevent the ability to update permissions via web - if 'permissions' in data: - del data['permissions'] + if not self.has_permission('admin|all'): + if 'permissions' in data: + del data['permissions'] + else: + if 'isAdmin' in data: + if not 'permissions' in data: + data['permissions'] = [] + if 'admin|all' in data['permissions'] and not data['isAdmin']: + data['permissions'].remove('admin|all') + elif 'admin|all' not in data['permissions'] and data['isAdmin']: + data['permissions'].append('admin|all') + del data['isAdmin'] return data @gen.coroutine @@ -1197,7 +1206,7 @@ def run(): ws_application = tornado.web.Application([_ws_handler], debug=options.debug) ws_http_server = tornado.httpserver.HTTPServer(ws_application) ws_http_server.listen(options.port+1, address='127.0.0.1') - logger.debug('Starting WebSocket on %s://127.0.0.1:%d', 'wss' if ssl_options else 'ws', options.port+1) + logger.debug('Starting WebSocket on ws://127.0.0.1:%d', options.port+1) tornado.ioloop.IOLoop.instance().start() From 6648154ba67960956b7414a57775b91f64d62386 Mon Sep 17 00:00:00 2001 From: Davide Alberani Date: Mon, 3 Apr 2017 21:42:46 +0200 Subject: [PATCH 2/4] fixes 169: ability to grant super cow powers to users --- angular_app/user-edit.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/angular_app/user-edit.html b/angular_app/user-edit.html index 3b1b30e..b58364b 100644 --- a/angular_app/user-edit.html +++ b/angular_app/user-edit.html @@ -22,7 +22,7 @@
From 0b2002dab9a3f773f08464d615edd778ad380c6c Mon Sep 17 00:00:00 2001 From: Davide Alberani Date: Mon, 3 Apr 2017 22:14:06 +0200 Subject: [PATCH 3/4] improve collection of modification time/author information --- eventman_server.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/eventman_server.py b/eventman_server.py index 2685ef6..d6ecd4c 100755 --- a/eventman_server.py +++ b/eventman_server.py @@ -191,6 +191,23 @@ class BaseHandler(tornado.web.RequestHandler): self._users_cache[current_user] = user_info return user_info + def add_access_info(self, doc): + """Add created/updated by/at to a document (modified in place and returned). + + :param doc: the doc to be updated + :type doc: dict + :returns: the updated document + :rtype: dict""" + user_id = self.current_user + now = datetime.datetime.utcnow() + if 'created_by' not in doc: + doc['created_by'] = user_id + if 'created_at' not in doc: + doc['created_at'] = now + doc['updated_by'] = user_id + doc['updated_at'] = now + return doc + def has_permission(self, permission): """Check permissions of the current user. @@ -343,7 +360,8 @@ class CollectionHandler(BaseHandler): :type data: dict""" if isinstance(data, dict): for key in list(data.keys()): - if isinstance(key, str) and key.startswith('$'): + if (isinstance(key, str) and key.startswith('$')) or key in ('_id', 'created_by', 'created_at', + 'updated_by', 'updated_at', 'isRegistered'): del data[key] return data @@ -419,17 +437,12 @@ class CollectionHandler(BaseHandler): self._clean_dict(data) method = self.request.method.lower() crud_method = 'create' if method == 'post' else 'update' - now = datetime.datetime.now() user_info = self.current_user_info user_id = user_info.get('_id') env = {} if id_ is not None: env['%s_ID' % self.document.upper()] = id_ - if crud_method == 'create': - data['created_by'] = user_id - data['created_at'] = now - data['updated_by'] = user_id - data['updated_at'] = now + self.add_access_info(data) if resource: permission = '%s:%s%s|%s' % (self.document, resource, '-all' if resource_id is None else '', crud_method) if not self.has_permission(permission): @@ -633,6 +646,7 @@ class EventsHandler(CollectionHandler): if not self.has_permission('event|update'): if 'attended' in data: del data['attended'] + self.add_access_info(data) return data filter_input_put_tickets = filter_input_post_tickets @@ -745,6 +759,7 @@ class EventsHandler(CollectionHandler): data['seq'] = self.get_next_seq('event_%s_tickets' % id_) data['seq_hex'] = '%06X' % data['seq'] data['_id'] = ticket_id = self.gen_id() + self.add_access_info(data) ret = {'action': 'add', 'ticket': data, 'uuid': uuid} merged, doc = self.db.update('events', {'_id': id_}, @@ -790,6 +805,7 @@ class EventsHandler(CollectionHandler): if 'number_of_tickets' in current_event and old_ticket_data.get('cancelled') and not data.get('cancelled'): self._check_number_of_tickets(current_event) + self.add_access_info(data) merged, doc = self.db.update('events', query, data, updateList='tickets', create=False) new_ticket_data = self._get_ticket_data(ticket_query, @@ -1000,6 +1016,7 @@ class EbCSVImportPersonsHandler(BaseHandler): reply['valid'] += 1 person['attended'] = False person['from_file'] = filename + self.add_access_info(person) duplicate_check = '%s_%s_%s' % (person.get('name'), person.get('surname'), person.get('email')) if duplicate_check in all_emails: continue From 7f527319eb3aa8012387f43ddb2942ed07714339 Mon Sep 17 00:00:00 2001 From: Davide Alberani Date: Mon, 3 Apr 2017 22:16:07 +0200 Subject: [PATCH 4/4] update doc --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 944aed1..8195502 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,6 @@ Notes on some special features Some notes about the event registration form: - field names are important (case is not considered). You can use whatever you want, but "name", "surname" and "email" are internally used to show the tickets list, so please add at least one of them -- please notice that the "Email" field type has a very silly regular expression and will create a lot of problems: please use "Text input" and names the field "Email" About the "Group ID" of events and "Unregistered persons" list: - "Group ID" is a random non-guessable identifier associated to an event. You can use whatever you want; if left empty, it will be autogenerated. It's not the same as the Event ID (the \_id key in the db): the Group ID is supposed to be secret