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 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..b58364b 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..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, @@ -909,9 +925,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 @@ -991,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 @@ -1197,7 +1223,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()