def get(self, user_id): require_permission_or_owner('list_users', user_id) user = get_object_or_404(models.User.get_by_id_and_org, user_id, self.current_org) self.record_event({ 'action': 'view', 'object_id': user_id, 'object_type': 'user', }) return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def post(self, user_id): require_admin_or_owner(user_id) user = models.User.get_by_id_and_org(user_id, self.current_org) req = request.get_json(True) params = project( req, ('email', 'name', 'password', 'old_password', 'groups')) if 'password' in params and 'old_password' not in params: abort(403, message="Must provide current password to update password.") if 'old_password' in params and not user.verify_password( params['old_password']): abort(403, message="Incorrect current password.") if 'password' in params: user.hash_password(params.pop('password')) params.pop('old_password') if 'groups' in params and not self.current_user.has_permission( 'admin'): abort(403, message="Must be admin to change groups membership.") if 'email' in params: _, domain = params['email'].split('@', 1) if domain.lower() in blacklist or domain.lower() == 'qq.com': abort(400, message='Bad email address.') try: self.update_model(user, params) models.db.session.commit() # The user has updated their email or password. This should invalidate all _other_ sessions, # forcing them to log in again. Since we don't want to force _this_ session to have to go # through login again, we call `login_user` in order to update the session with the new identity details. if current_user.id == user.id: login_user(user, remember=True) except IntegrityError as e: if "email" in e.message: message = "Email already taken." else: message = "Error updating record" abort(400, message=message) self.record_event({ 'action': 'edit', 'object_id': user.id, 'object_type': 'user', 'updated_fields': params.keys() }) return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def post(self, user_id): user = models.User.get_by_id_and_org(user_id, self.current_org) # admin cannot disable self; current user is an admin (`@require_admin`) # so just check user id if user.id == current_user.id: abort(403, message="You cannot disable your own account. " "Please ask another admin to do this for you.") user.disable() models.db.session.commit() return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def post(self, user_id): user = models.User.get_by_id_and_org(user_id, self.current_org) # admin cannot disable self; current user is an admin (`@require_admin`) # so just check user id if user.id == current_user.id: abort(403, message="您不能禁用自己的帐户。 " "请要求其他管理员为您执行此操作。") user.disable() models.db.session.commit() return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def get(self, user_id): require_permission_or_owner("list_users", user_id) user = get_object_or_404( models.User.get_by_id_and_org, user_id, self.current_org ) self.record_event( {"action": "view", "object_id": user_id, "object_type": "user"} ) return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def post(self, query_id): """ Execute a saved query. :param number query_id: The ID of the query whose results should be fetched. :param object parameters: The parameter values to apply to the query. :qparam number max_age: If query results less than `max_age` seconds old are available, return them, otherwise execute the query; if omitted or -1, returns any cached result, or executes if not available. Set to zero to always execute. """ params = request.get_json(force=True) parameter_values = params.get('parameters') max_age = params.get('max_age', -1) # max_age might have the value of None, in which case calling int(None) will fail if max_age is None: max_age = -1 max_age = int(max_age) query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org) # allow_executing_with_view_only_permissions = query.parameterized.is_safe # TODO: Workaround, always allow APIKEY user if self.current_user.is_api_user: return run_query(query.parameterized, parameter_values, query.data_source, query_id, max_age) elif not is_admin_or_owner(query.user_id): if models.QueryGroup.get_by_query_groups( query, query.query_groups).first() is None: return { 'job': { 'status': 4, 'error': 'You do not have permission to run queries with this data source.' } }, 403 else: logger.debug("ok to run query...") return run_query(query.parameterized, parameter_values, query.data_source, query_id, max_age) else: return { 'job': { 'status': 4, 'error': 'You do not have permission to run queries with this data source.' } }, 403
def post(self, query_id): """ Modify a query. :param query_id: ID of query to update :<json number data_source_id: The ID of the data source this query will run on :<json string query: Query text :<json string name: :<json string description: :<json string schedule: Schedule interval, in seconds, for repeated execution of this query :<json object options: Query options Responds with the updated :ref:`query <query-response-label>` object. """ query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org) if not is_admin_or_owner(query.user_id): if models.QueryGroup.get_by_query_groups( query, query.query_groups).first() is None: abort(404) query_def = request.get_json(force=True) #require_object_modify_permission(query, self.current_user) require_access_to_dropdown_queries(self.current_user, query_def) for field in [ 'id', 'created_at', 'api_key', 'visualizations', 'latest_query_data', 'user', 'last_modified_by', 'org' ]: query_def.pop(field, None) if 'query' in query_def: query_def['query_text'] = query_def.pop('query') query_def['last_modified_by'] = self.current_user query_def['changed_by'] = self.current_user # SQLAlchemy handles the case where a concurrent transaction beats us # to the update. But we still have to make sure that we're not starting # out behind. if 'version' in query_def and query_def['version'] != query.version: abort(409) try: self.update_model(query, query_def) models.db.session.commit() except StaleDataError: abort(409) return QuerySerializer(query, with_visualizations=True).serialize()
def post(self, user_id): require_admin_or_owner(user_id) user = models.User.get_by_id_and_org(user_id, self.current_org) req = request.get_json(True) params = project(req, ('email', 'name', 'password', 'old_password', 'groups')) if 'password' in params and 'old_password' not in params: abort(403, message="Must provide current password to update password.") if 'old_password' in params and not user.verify_password(params['old_password']): abort(403, message="Incorrect current password.") if 'password' in params: user.hash_password(params.pop('password')) params.pop('old_password') if 'groups' in params and not self.current_user.has_permission('admin'): abort(403, message="Must be admin to change groups membership.") if 'email' in params: _, domain = params['email'].split('@', 1) if domain.lower() in blacklist or domain.lower() == 'qq.com': abort(400, message='Bad email address.') try: self.update_model(user, params) models.db.session.commit() # The user has updated their email or password. This should invalidate all _other_ sessions, # forcing them to log in again. Since we don't want to force _this_ session to have to go # through login again, we call `login_user` in order to update the session with the new identity details. login_user(user, remember=True) except IntegrityError as e: if "email" in e.message: message = "Email already taken." else: message = "Error updating record" abort(400, message=message) self.record_event({ 'action': 'edit', 'object_id': user.id, 'object_type': 'user', 'updated_fields': params.keys() }) return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def delete(self, user_id): user = models.User.get_by_id_and_org(user_id, self.current_org) # admin cannot delete self; current user is an admin (`@require_admin`) # so just check user id if user.id == current_user.id: abort(403, message="您不能删除自己的帐户。 " "请要求其他管理员为您执行此操作。") elif not user.is_invitation_pending: abort(403, message="您无法删除已激活的用户。 " "请禁用用户。") models.db.session.delete(user) models.db.session.commit() return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def delete(self, query_id): """ Archives a query. :param query_id: ID of query to archive """ query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org) if not is_admin_or_owner(query.user_id): if models.QueryGroup.get_by_query_groups( query, query.query_groups).first() is None: abort(404) query.archive(self.current_user) models.db.session.commit()
def get(self, user_id): require_permission_or_owner('list_users', user_id) user = models.User.get_by_id(user_id) if user is None: abort(404, "User not found") self.record_event({ 'action': 'view', 'object_id': user_id, 'object_type': 'user', }) return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def delete(self, user_id): user = models.User.get_by_id_and_org(user_id, self.current_org) # admin cannot delete self; current user is an admin (`@require_admin`) # so just check user id if user.id == current_user.id: abort(403, message="You cannot delete your own account. " "Please ask another admin to do this for you.") elif not user.is_invitation_pending: abort(403, message="You cannot delete activated users. " "Please disable the user instead.") models.db.session.delete(user) models.db.session.commit() return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def post(self, user_id): require_admin_or_owner(user_id) user = models.User.get_by_id_and_org(user_id, self.current_org) req = request.get_json(True) params = project( req, ('email', 'name', 'password', 'old_password', 'groups')) if 'password' in params and 'old_password' not in params: abort(403, message="Must provide current password to update password.") if 'old_password' in params and not user.verify_password( params['old_password']): abort(403, message="Incorrect current password.") if 'password' in params: user.hash_password(params.pop('password')) params.pop('old_password') if 'groups' in params and not self.current_user.has_permission( 'admin'): abort(403, message="Must be admin to change groups membership.") if 'email' in params: _, domain = params['email'].split('@', 1) if domain.lower() in blacklist or domain.lower() == 'qq.com': abort(400, message='Bad email address.') try: self.update_model(user, params) models.db.session.commit() except IntegrityError as e: if "email" in e.message: message = "Email already taken." else: message = "Error updating record" abort(400, message=message) self.record_event({ 'action': 'edit', 'object_id': user.id, 'object_type': 'user', 'updated_fields': params.keys() }) return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def post(self, user_id): user = models.User.get_by_id_and_org(user_id, self.current_org) if user.is_disabled: abort(404, message="Not found") if not is_admin_or_owner(user_id): abort(403) user.regenerate_api_key() models.db.session.commit() self.record_event( {"action": "regnerate_api_key", "object_id": user.id, "object_type": "user"} ) return user.to_dict(with_api_key=True)
def post(self, user_id): user = models.User.get_by_id_and_org(user_id, self.current_org) if user.is_disabled: abort(404, message='Not found') if not is_admin_or_owner(user_id): abort(403) user.regenerate_api_key() models.db.session.commit() self.record_event({ 'action': 'regnerate_api_key', 'object_id': user.id, 'object_type': 'user' }) return user.to_dict(with_api_key=True)
def post(self, user_id): require_admin_or_owner(user_id) user = models.User.get_by_id_and_org(user_id, self.current_org) req = request.get_json(True) params = project(req, ('email', 'name', 'password', 'old_password', 'groups')) if 'password' in params and 'old_password' not in params: abort(403, message="Must provide current password to update password.") if 'old_password' in params and not user.verify_password(params['old_password']): abort(403, message="Incorrect current password.") if 'password' in params: user.hash_password(params.pop('password')) params.pop('old_password') if 'groups' in params and not self.current_user.has_permission('admin'): abort(403, message="Must be admin to change groups membership.") if 'email' in params: _, domain = params['email'].split('@', 1) if domain.lower() in blacklist or domain.lower() == 'qq.com': abort(400, message='Bad email address.') try: self.update_model(user, params) models.db.session.commit() except IntegrityError as e: if "email" in e.message: message = "Email already taken." else: message = "Error updating record" abort(400, message=message) self.record_event({ 'action': 'edit', 'object_id': user.id, 'object_type': 'user', 'updated_fields': params.keys() }) return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def post(self, user_id): require_admin_or_owner(user_id) user = models.User.get_by_id_and_org(user_id, self.current_org) req = request.get_json(True) params = project( req, ('email', 'name', 'password', 'old_password', 'groups')) if 'password' in params and 'old_password' not in params: abort(403, message="Must provide current password to update password.") if 'old_password' in params and not user.verify_password( params['old_password']): abort(403, message="Incorrect current password.") if 'password' in params: user.hash_password(params.pop('password')) params.pop('old_password') if 'groups' in params and not self.current_user.has_permission( 'admin'): abort(403, message="Must be admin to change groups membership.") try: self.update_model(user, params) # TODO: this won't be triggered at this point. Need to call db.session.commit? except IntegrityError as e: if "email" in e.message: message = "Email already taken." else: message = "Error updating record" abort(400, message=message) self.record_event({ 'action': 'edit', 'timestamp': int(time.time()), 'object_id': user.id, 'object_type': 'user', 'updated_fields': params.keys() }) return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def post(self, user_id): require_admin_or_owner(user_id) user = models.User.get_by_id(user_id) req = request.get_json(True) params = project(req, ("email", "name", "password", "old_password", "groups")) if "password" in params and "old_password" not in params: abort(403, message="Must provide current password to update password.") if "old_password" in params and not user.verify_password(params["old_password"]): abort(403, message="Incorrect current password.") if "password" in params: user.hash_password(params.pop("password")) params.pop("old_password") if "groups" in params and not self.current_user.has_permission("admin"): abort(403, message="Must be admin to change groups membership.") try: user.update_instance(**params) except IntegrityError as e: if "email" in e.message: message = "Email already taken." else: message = "Error updating record" abort(400, message=message) record_event.delay( { "user_id": self.current_user.id, "action": "edit", "timestamp": int(time.time()), "object_id": user.id, "object_type": "user", "updated_fields": params.keys(), } ) return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def post(self, user_id): require_admin_or_owner(user_id) user = models.User.get_by_id_and_org(user_id, self.current_org) req = request.get_json(True) params = project(req, ('email', 'name', 'password', 'old_password', 'groups')) if 'password' in params and 'old_password' not in params: abort(403, message="Must provide current password to update password.") if 'old_password' in params and not user.verify_password(params['old_password']): abort(403, message="Incorrect current password.") if 'password' in params: user.hash_password(params.pop('password')) params.pop('old_password') if 'groups' in params and not self.current_user.has_permission('admin'): abort(403, message="Must be admin to change groups membership.") try: user.update_instance(**params) except IntegrityError as e: if "email" in e.message: message = "Email already taken." else: message = "Error updating record" abort(400, message=message) self.record_event({ 'action': 'edit', 'timestamp': int(time.time()), 'object_id': user.id, 'object_type': 'user', 'updated_fields': params.keys() }) return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def get(self, query_id): """ Retrieve a query. :param query_id: ID of query to fetch Responds with the :ref:`query <query-response-label>` contents. """ query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org) if not is_admin_or_owner(query.user_id): if models.QueryGroup.get_by_query_groups( query, query.query_groups).first() is None: abort(404) api_key_hash = {} for vis in query.visualizations: api_key = models.ApiKey.get_by_object(vis) if api_key: api_key_hash[vis.id] = api_key.api_key result = QuerySerializer(query, with_visualizations=True).serialize() for vis_result in result['visualizations']: if vis_result['id'] in api_key_hash: vis_result['api_key'] = api_key_hash[vis_result['id']] # result['can_edit'] = can_modify(q, self.current_user) self.record_event({ 'action': 'view', 'object_id': query_id, 'object_type': 'query', }) return result
def post(self, user_id): require_admin_or_owner(user_id) user = models.User.get_by_id_and_org(user_id, self.current_org) req = request.get_json(True) params = project(req, ('email', 'name', 'password', 'old_password', 'group_ids')) if 'password' in params and 'old_password' not in params: abort(403, message="必须提供当前密码才能更新密码。") if 'old_password' in params and not user.verify_password(params['old_password']): abort(403, message="当前密码错误。") if 'password' in params: user.hash_password(params.pop('password')) params.pop('old_password') if 'group_ids' in params: if not self.current_user.has_permission('admin'): abort(403, message="必须是管理员才能更改角色成员身份。") for group_id in params['group_ids']: try: models.Group.get_by_id_and_org(group_id, self.current_org) except NoResultFound: abort(400, message="Group id {} is invalid.".format(group_id)) if len(params['group_ids']) == 0: params.pop('group_ids') if 'email' in params: _, domain = params['email'].split('@', 1) if domain.lower() in blacklist or domain.lower() == 'qq.com': abort(400, message='电子邮件地址错误。') email_address_changed = 'email' in params and params['email'] != user.email needs_to_verify_email = email_address_changed and settings.email_server_is_configured() if needs_to_verify_email: user.is_email_verified = False try: self.update_model(user, params) models.db.session.commit() if needs_to_verify_email: send_verify_email(user, self.current_org) # The user has updated their email or password. This should invalidate all _other_ sessions, # forcing them to log in again. Since we don't want to force _this_ session to have to go # through login again, we call `login_user` in order to update the session with the new identity details. if current_user.id == user.id: login_user(user, remember=True) except IntegrityError as e: if "email" in e.message: message = "电子邮件已被占用。" else: message = "更新时出错" abort(400, message=message) self.record_event({ 'action': 'edit', 'object_id': user.id, 'object_type': 'user', 'updated_fields': params.keys() }) return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def get(self, user_id): require_permission_or_owner('list_users', user_id) user = models.User.get_by_id(user_id) return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def post(self, user_id): require_admin_or_owner(user_id) user = models.User.get_by_id_and_org(user_id, self.current_org) req = request.get_json(True) params = project( req, ("email", "name", "password", "old_password", "group_ids") ) if "password" in params and "old_password" not in params: abort(403, message="Must provide current password to update password.") if "old_password" in params and not user.verify_password( params["old_password"] ): abort(403, message="Incorrect current password.") if "password" in params: user.hash_password(params.pop("password")) params.pop("old_password") if "group_ids" in params: if not self.current_user.has_permission("admin"): abort(403, message="Must be admin to change groups membership.") for group_id in params["group_ids"]: try: models.Group.get_by_id_and_org(group_id, self.current_org) except NoResultFound: abort(400, message="Group id {} is invalid.".format(group_id)) if len(params["group_ids"]) == 0: params.pop("group_ids") if "email" in params: require_allowed_email(params["email"]) email_address_changed = "email" in params and params["email"] != user.email needs_to_verify_email = ( email_address_changed and settings.email_server_is_configured() ) if needs_to_verify_email: user.is_email_verified = False try: self.update_model(user, params) models.db.session.commit() if needs_to_verify_email: send_verify_email(user, self.current_org) # The user has updated their email or password. This should invalidate all _other_ sessions, # forcing them to log in again. Since we don't want to force _this_ session to have to go # through login again, we call `login_user` in order to update the session with the new identity details. if current_user.id == user.id: login_user(user, remember=True) except IntegrityError as e: if "email" in str(e): message = "Email already taken." else: message = "Error updating record" abort(400, message=message) self.record_event( { "action": "edit", "object_id": user.id, "object_type": "user", "updated_fields": list(params.keys()), } ) return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def get(self, user_id): require_permission_or_owner('list_users', user_id) user = get_object_or_404(models.User.get_by_id_and_org, user_id, self.current_org) return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def get(self, query_id=None, query_result_id=None, filetype='json'): """ Retrieve query results. :param number query_id: The ID of the query whose results should be fetched :param number query_result_id: the ID of the query result to fetch :param string filetype: Format to return. One of 'json', 'xlsx', or 'csv'. Defaults to 'json'. :<json number id: Query result ID :<json string query: Query that produced this result :<json string query_hash: Hash code for query text :<json object data: Query output :<json number data_source_id: ID of data source that produced this result :<json number runtime: Length of execution time in seconds :<json string retrieved_at: Query retrieval date/time, in ISO format """ # TODO: # This method handles two cases: retrieving result by id & retrieving result by query id. # They need to be split, as they have different logic (for example, retrieving by query id # should check for query parameters and shouldn't cache the result). should_cache = query_result_id is not None parameter_values = collect_parameters_from_request(request.args) max_age = int(request.args.get('maxAge', 0)) query_result = None query = None if query_result_id: query_result = get_object_or_404( models.QueryResult.get_by_id_and_org, query_result_id, self.current_org) if query_id is not None: query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org) if query_result is None and query is not None and query.latest_query_data_id is not None: query_result = get_object_or_404( models.QueryResult.get_by_id_and_org, query.latest_query_data_id, self.current_org) if query is not None and query_result is not None and self.current_user.is_api_user( ): if query.query_hash != query_result.query_hash: abort(404, message='No cached result found for this query.') if query_result: # require_access(query_result.data_source, self.current_user, view_only) if query is not None: if not is_admin_or_owner(query.user_id): if models.QueryGroup.get_by_query_groups( query, query.query_groups).first() is None: abort(403) if isinstance(self.current_user, models.ApiUser): event = { 'user_id': None, 'org_id': self.current_org.id, 'action': 'api_get', 'api_key': self.current_user.name, 'file_type': filetype, 'user_agent': request.user_agent.string, 'ip': request.remote_addr } if query_id: event['object_type'] = 'query' event['object_id'] = query_id else: event['object_type'] = 'query_result' event['object_id'] = query_result_id self.record_event(event) if filetype == 'json': response = self.make_json_response(query_result) elif filetype == 'xlsx': response = self.make_excel_response(query_result) else: response = self.make_csv_response(query_result) if should_cache: response.headers.add_header('Cache-Control', 'private,max-age=%d' % ONE_YEAR) filename = get_download_filename(query_result, query, filetype) response.headers.add_header( "Content-Disposition", 'attachment; filename="{}"'.format(filename.encode("utf-8"))) return response else: abort(404, message='No cached result found for this query.')
def delete(self, user_id): user = models.User.get_by_id_and_org(user_id, self.current_org) user.enable() models.db.session.commit() return user.to_dict(with_api_key=is_admin_or_owner(user_id))
def get(self, user_id): require_permission_or_owner("list_users", user_id) user = models.User.get_by_id(user_id) return user.to_dict(with_api_key=is_admin_or_owner(user_id))