def patch_change_ownership(self, manager_id: bson.ObjectId, patch: dict): """Shares or un-shares the Manager with a user.""" man_man = current_flamenco.manager_manager if not man_man.user_is_owner(mngr_doc_id=manager_id): log.warning('User %s uses PATCH to (un)share manager %s, ' 'but user is not owner of that Manager. Request denied.', current_user_id(), manager_id) raise wz_exceptions.Forbidden() action = patch.get('action', '') try: action = man_man.ShareAction[action] except KeyError: raise wz_exceptions.BadRequest(f'Unknown action {action!r}') subject_uid = str2id(patch.get('user', '')) if action == man_man.ShareAction.share and subject_uid == current_user_id(): log.warning('%s tries to %s Manager %s with itself', current_user_id(), action, manager_id) raise wz_exceptions.BadRequest(f'Cannot share a Manager with yourself') if action == man_man.ShareAction.share and \ not current_flamenco.auth.user_is_flamenco_user(subject_uid): log.warning('%s Manager %s on behalf of user %s, but subject user %s ' 'is not Flamenco user', action, manager_id, current_user_id(), subject_uid) raise wz_exceptions.Forbidden(f'User {subject_uid} is not allowed to use Flamenco') try: man_man.share_unshare_manager(manager_id, action, subject_uid) except ValueError as ex: raise wz_exceptions.BadRequest(str(ex))
def _check_related_permissions(self, obj): """Check permissions before deleting related Evidence or Document""" if not permissions.is_allowed_delete( obj.type, obj.id, obj.context_id) \ and not permissions.has_conditions("delete", obj.type): raise wzg_exceptions.Forbidden() if not permissions.is_allowed_delete_for(obj): raise wzg_exceptions.Forbidden()
def check_mapping_permissions(obj1, obj2): """Check is mapping allowed between objects""" error_message = "Mapping of this objects is not allowed" if isinstance(obj1, WithCustomRestrictions): if obj1.is_mapping_restricted(obj2): raise exceptions.Forbidden(description=error_message) if isinstance(obj2, WithCustomRestrictions): if obj2.is_mapping_restricted(obj1): raise exceptions.Forbidden(description=error_message)
def _authorize_github(self): """ :rtype: None :raises werkzeug.exceptions.Forbidden: If cannot login to GitHub using `auth` credentials """ self.github_api = GitHubApi(**self._auth) try: self.current_user = self.github_api.me() except gh_exc.AuthenticationFailed: raise exceptions.Forbidden() if self.current_user is None: raise exceptions.Forbidden()
def check_put_access(request, lookup): """Only allow PUT to the current user, or all users if admin.""" user = pillar.auth.get_current_user() if user.has_cap('admin'): return if user.is_anonymous: raise wz_exceptions.Forbidden() if str(lookup['_id']) != str(user.user_id): raise wz_exceptions.Forbidden()
def _assign_or_remove_project(self, manager_id: bson.ObjectId, patch: dict, action: str): """Assigns a manager to a project or removes it. The calling user must be owner of the manager (always) and member of the project (if assigning). """ from pillar.api.projects.utils import user_rights_in_project from flamenco import current_flamenco try: project_strid = patch['project'] except KeyError: log.warning('User %s sent invalid PATCH %r for manager %s.', current_user_id(), patch, manager_id) raise wz_exceptions.BadRequest('Missing key "project"') project_id = str2id(project_strid) if not current_flamenco.manager_manager.user_is_owner( mngr_doc_id=manager_id): log.warning( 'User %s uses PATCH to %s manager %s to/from project %s, ' 'but user is not owner of that Manager. Request denied.', current_user_id(), action, manager_id, project_id) raise wz_exceptions.Forbidden() # Removing from a project doesn't require project membership. if action != 'remove': methods = user_rights_in_project(project_id) if 'PUT' not in methods: log.warning( 'User %s uses PATCH to %s manager %s to/from project %s, ' 'but only has %s rights on project. Request denied.', current_user_id(), action, manager_id, project_id, ', '.join(methods)) raise wz_exceptions.Forbidden() log.info('User %s uses PATCH to %s manager %s to/from project %s', current_user_id(), action, manager_id, project_id) ok = current_flamenco.manager_manager.api_assign_to_project( manager_id, project_id, action) if not ok: # Manager Manager will have already logged the cause. raise wz_exceptions.InternalServerError()
def setup_for_flamenco(project: pillarsdk.Project): from pillar.api.utils import str2id import flamenco.setup project_id = project._id if not project.has_method('PUT'): log.warning( 'User %s tries to set up project %s for Flamenco, but has no PUT rights.', current_user, project_id) raise wz_exceptions.Forbidden() if not current_flamenco.auth.current_user_is_flamenco_user(): log.warning( 'User %s tries to set up project %s for Flamenco, but is not flamenco-user.', current_user, project_id) raise wz_exceptions.Forbidden() log.info('User %s sets up project %s for Flamenco', current_user, project_id) flamenco.setup.setup_for_flamenco(project.url) # Find the Managers available to this user, so we can auto-assign if there is exactly one. man_man = current_flamenco.manager_manager managers = man_man.owned_managers( [bson.ObjectId(gid) for gid in current_user.groups]) manager_count = managers.count() project_oid = str2id(project_id) user_id = current_user_id() if manager_count == 0: _, mngr_doc, _ = man_man.create_new_manager('My Manager', '', user_id) assign_man_oid = mngr_doc['_id'] log.info( 'Created and auto-assigning Manager %s to project %s upon setup for Flamenco.', assign_man_oid, project_oid) man_man.api_assign_to_project(assign_man_oid, project_oid, 'assign') elif manager_count == 1: assign_manager = managers.next() assign_man_oid = str2id(assign_manager['_id']) log.info( 'Auto-assigning Manager %s to project %s upon setup for Flamenco.', assign_man_oid, project_oid) man_man.api_assign_to_project(assign_man_oid, project_oid, 'assign') return '', 204
def assert_is_valid_patch(node_id, patch): """Raises an exception when the patch isn't valid.""" try: op = patch['op'] except KeyError: raise wz_exceptions.BadRequest("PATCH should have a key 'op' indicating the operation.") if op not in VALID_COMMENT_OPERATIONS: raise wz_exceptions.BadRequest('Operation should be one of %s', ', '.join(VALID_COMMENT_OPERATIONS)) if op not in COMMENT_VOTING_OPS: # We can't check here, we need the node owner for that. return # See whether the user is allowed to patch if authorization.user_matches_roles(current_app.config['ROLES_FOR_COMMENT_VOTING']): log.debug('User is allowed to upvote/downvote comment') return # Access denied. log.info('User %s wants to PATCH comment node %s, but is not allowed.', authentication.current_user_id(), node_id) raise wz_exceptions.Forbidden()
def proxy_search(request, url): found = False for allowed_host in g.db.query(ParcelSearchSource).filter_by( active=True).all(): if url.startswith(allowed_host.url): found = True if not found: raise exceptions.Forbidden() headers = end_to_end_headers(request.headers) content_length = request.headers.get('content-length') if not content_length: data = None else: data = LimitedStream(request.stream) try: resp = requests.request(request.method, url, data=data, headers=headers, params=request.args, stream=True) chunked_response = resp.headers.get('Transfer-Encoding') == 'chunked' line_based = resp.headers.get('Content-type', '').startswith( ('text/plain', 'application/json')) except requests.exceptions.RequestException, ex: raise exceptions.BadGateway('source returned: %s' % ex)
def oauth_callback(provider): if current_user.is_authenticated: return redirect(url_for('main.homepage')) oauth = OAuthSignIn.get_provider(provider) try: oauth_user = oauth.callback() except OAuthCodeNotProvided as e: log.error(e) raise wz_exceptions.Forbidden() if oauth_user.id is None: log.debug('Authentication failed for user with {}'.format(provider)) return redirect(url_for('main.homepage')) # Find or create user user_info = {'id': oauth_user.id, 'email': oauth_user.email, 'full_name': ''} db_user = find_user_in_db(user_info, provider=provider) db_id, status = upsert_user(db_user) token = generate_and_store_token(db_id) # Login user pillar.auth.login_user(token['token'], load_from_db=True) if provider == 'blender-id' and current_user.is_authenticated: # Check with Blender ID to update certain user roles. update_subscription() next_after_login = session.pop('next_after_login', None) if next_after_login: log.debug('Redirecting user to %s', next_after_login) return redirect(next_after_login) return redirect(url_for('main.homepage'))
def patch_requeue(self, task_id: bson.ObjectId, patch: dict): """Re-queue a task and its successors.""" from flamenco import current_flamenco from pillar.api.utils.authentication import current_user_id tasks_coll = current_flamenco.db('tasks') task = tasks_coll.find_one({'_id': task_id}, projection={ 'job': 1, 'manager': 1 }) if not current_flamenco.manager_manager.user_may_use( mngr_doc_id=task['manager']): log.warning( 'patch_set_task_status(%s, %r): User %s is not allowed to use manager %s!', task_id, patch, current_user_id(), task['manager']) raise wz_exceptions.Forbidden() current_flamenco.task_manager.api_requeue_task_and_successors(task_id) # Also inspect other tasks of the same job, and possibly update the job status as well. current_flamenco.job_manager.update_job_after_task_status_change( task['job'], task_id, 'queued')
def handle_export_post(**kwargs): """Handle export post""" check_import_export_headers() request_json = request.json objects = request_json.get("objects") exportable_objects = request_json.get("exportable_objects", []) current_time = request.json.get("current_time") user = login.get_current_user() if user.system_wide_role == 'No Access': raise wzg_exceptions.Forbidden() if not objects or not current_time: raise wzg_exceptions.BadRequest( app_errors.INCORRECT_REQUEST_DATA.format(job_type="Export")) try: filename = import_helper.get_export_filename(objects, current_time, exportable_objects) ie = import_export.create_import_export_entry( job_type="Export", status="In Progress", title=filename, start_at=datetime.utcnow(), ) run_background_export(ie.id, objects, exportable_objects) return make_import_export_response(ie.log_json()) except Exception as e: logger.exception(e.message) raise wzg_exceptions.BadRequest( app_errors.INCORRECT_REQUEST_DATA.format(job_type="Export"))
def remove_file(full_path: str = ''): abspath = os.path.join(HOME_PATH, full_path) if not (os.path.exists(abspath)): raise wexs.BadRequest() try: if (os.path.isfile(abspath)): absdir = os.path.dirname(full_path) os.remove(abspath) return redirect(url_for('index', varargs=absdir)) elif (os.path.isdir(abspath)): abspath = abspath.replace('/', os.sep) cmd = f'rmdir /Q /S "{abspath}"' code = os.system(cmd) assert code == 0, f'Process returned with code {code}. Failed to remove folder' parent = os.path.join(full_path, os.pardir) parent = os.path.normpath(parent) return redirect(url_for('index', varargs=parent)) else: raise OSError('Path is neither a file nor directory') except OSError as e: raise wexs.Forbidden(e) except AssertionError as e: raise wexs.BadRequest(e) raise wexs.BadRequest('Unknown error!')
def check_job_permission_fetch_resource(response): from functools import lru_cache if current_flamenco.auth.current_user_is_flamenco_admin(): return if not current_flamenco.manager_manager.user_is_manager(): # Subscribers can read Flamenco jobs. if current_user.has_cap('flamenco-view'): return raise wz_exceptions.Forbidden() @lru_cache(32) def user_managers(mngr_doc_id): return current_flamenco.manager_manager.user_manages(mngr_doc_id=mngr_doc_id) items = response['_items'] to_remove = [] for idx, job_doc in enumerate(items): if not user_managers(job_doc.get('manager')): to_remove.append(idx) for idx in reversed(to_remove): del items[idx] response['_meta']['total'] -= len(items)
def wrapper(project_url, *args, **kwargs): if isinstance(project_url, pillarsdk.Resource): # This is already a resource, so this call probably is from one # view to another. Assume the caller knows what he's doing and # just pass everything along. return wrapped(project_url, *args, **kwargs) api = pillar_api() project = pillarsdk.Project.find_by_url( project_url, {'projection': projections}, api=api) is_flamenco = current_flamenco.is_flamenco_project(project) if not is_flamenco: return error_project_not_setup_for_flamenco(project) session['flamenco_last_project'] = project.to_dict() project_id = bson.ObjectId(project['_id']) auth = current_flamenco.auth if not auth.current_user_may(action, project_id): if current_user.is_anonymous: raise wz_exceptions.Forbidden( 'Login required for this URL') log.info('Denying user %s access %s to Flamenco on project %s', flask_login.current_user, action, project_id) return error_project_not_available() if extension_props: pprops = project.extension_props.flamenco return wrapped(project, pprops, *args, **kwargs) return wrapped(project, *args, **kwargs)
def post(self): args = parser.parse(self.post_args, request) username, password = args['username'], args['password'] user = db.session.query(User) \ .filter(User.username == username) \ .filter(User.deleted.isnot(True)) \ .options(joinedload(User.roles)) \ .one_or_none() if user is None: raise exc.NotFound('User with this username does not exist') is_password_valid = User.check_password(user.password_md5, password) if not is_password_valid: current_app.logger.warning(f'user_email={args["username"]} ' f'has failed to log in') raise exc.Forbidden('Invalid password') current_app.logger.debug(f'user_email={args["username"]} has logged in') token = generate_refresh_token(user) refresh_token = RefreshToken(token=token, user_id=user.id) db.session.add(refresh_token) db.session.commit() user_serializer = UserAuthSerializer() user.refresh_token = token user_data = user_serializer.dump(user) return jsonify(user_data.data)
def related_objects(self, id): """Get data for snapshot related_objects page.""" # id name is used as a kw argument and can't be changed here # pylint: disable=invalid-name,redefined-builtin from ggrc import models from ggrc.rbac import permissions snapshot = models.Snapshot.query.get(id) if snapshot is None: return self.not_found_response() if not permissions.is_allowed_read_for(snapshot): raise exceptions.Forbidden() data = defaultdict(list) for obj in snapshot.related_objects(): obj_data = _stub(obj) if obj.type == "Snapshot": child = referenced_objects.get(obj.child_type, obj.child_id) obj_data.update({ "child": _stub(child), "revision:": { "content": { "title": obj.revision.content.get("title", ""), "updated_at": obj.revision.content.get("updated_at", "") } } }) data[obj.type].append(obj_data) return self.json_success_response(data, )
def view_job(project, flamenco_props, job_id): if not request.is_xhr: return for_project(project, job_id=job_id) # Job list is public, job details are not. if not current_user.has_cap('flamenco-view'): raise wz_exceptions.Forbidden() from .sdk import Job from ..managers.sdk import Manager api = pillar_api() job = Job.find(job_id, api=api) try: manager = Manager.find(job.manager, api=api) except pillarsdk.ForbiddenAccess: # It's very possible that the user doesn't have access to this Manager. manager = None except pillarsdk.ResourceNotFound: log.warning('Flamenco job %s has a non-existant manager %s', job_id, job.manager) manager = None from . import (CANCELABLE_JOB_STATES, REQUEABLE_JOB_STATES, RECREATABLE_JOB_STATES, ARCHIVE_JOB_STATES, ARCHIVEABLE_JOB_STATES, FAILED_TASKS_REQUEABLE_JOB_STATES) auth = current_flamenco.auth write_access = auth.current_user_may(auth.Actions.USE, bson.ObjectId(project['_id'])) status = job['status'] is_archived = status in ARCHIVE_JOB_STATES archive_available = is_archived and job.archive_blob_name # Sort job settings so we can iterate over them in a deterministic way. job_settings = collections.OrderedDict( (key, job.settings[key]) for key in sorted(job.settings.to_dict().keys())) return render_template( 'flamenco/jobs/view_job_embed.html', job=job, manager=manager, project=project, flamenco_props=flamenco_props.to_dict(), flamenco_context=request.args.get('context'), can_cancel_job=write_access and status in CANCELABLE_JOB_STATES, can_requeue_job=write_access and status in REQUEABLE_JOB_STATES, can_recreate_job=write_access and status in RECREATABLE_JOB_STATES, can_archive_job=write_access and status in ARCHIVEABLE_JOB_STATES, # TODO(Sybren): check that there are actually failed tasks before setting to True: can_requeue_failed_tasks=write_access and status in FAILED_TASKS_REQUEABLE_JOB_STATES, is_archived=is_archived, write_access=write_access, archive_available=archive_available, job_settings=job_settings, )
def render_category(category='', template=None): """ Render a category page. Arguments: category -- The category to render template -- The template to render it with """ # pylint:disable=too-many-return-statements # See if this is an aliased path redir = get_redirect() if redir: return redir # Forbidden template types if template and template.startswith('_'): raise http_error.Forbidden("Template is private") if template in ['entry', 'error']: raise http_error.BadRequest("Invalid view requested") if category: # See if there's any entries for the view... if not orm.select(e for e in model.Entry if e.category == category or e.category.startswith(category + '/')): raise http_error.NotFound("No such category") if not template: template = Category(category).get('Index-Template') or 'index' tmpl = map_template(category, template) if not tmpl: # this might actually be a malformed category URL test_path = '/'.join((category, template)) if category else template LOGGER.debug("Checking for malformed category %s", test_path) record = orm.select( e for e in model.Entry if e.category == test_path).exists() if record: return redirect(url_for('category', category=test_path, **request.args)) # nope, we just don't know what this is raise http_error.NotFound( "No such view '{template}'".format(template=template)) view_spec = view.parse_view_spec(request.args) view_spec['category'] = category view_obj = view.View(view_spec) rendered, etag = render_publ_template( tmpl, _url_root=request.url_root, category=Category(category), view=view_obj) if request.if_none_match.contains(etag): return 'Not modified', 304 return rendered, {'Content-Type': mime_type(tmpl), 'ETag': etag}
def portfolio_download(human_id, filename): logger.debug('GET ' + request.url) try: # Find the portfolio folio = data_engine.get_portfolio(human_id=human_id) if not folio: raise DoesNotExistError('Portfolio \'%s\' does not exist' % human_id) # Ensure that the user has permission to download the portfolio user = get_session_user() permissions_engine.ensure_portfolio_permitted( folio, FolioPermission.ACCESS_DOWNLOAD, user) # Check that the filename is valid (note: assumes folio.downloads is eager loaded) if not filename: raise DoesNotExistError('No filename specified') folio_exports = [ dl for dl in folio.downloads if dl.filename == filename ] if not folio_exports: raise DoesNotExistError('Download \'%s\' is not available' % filename) folio_export = folio_exports[0] # The physical file should always exist when the data+filename exists # This also checks that the file path lies inside IMAGES_BASE_DIR zip_path = get_portfolio_export_file_path(folio_export) ensure_path_exists(zip_path, require_file=True) # Prepare to serve the file response = send_file( get_abs_path(zip_path), mimetype='application/zip', as_attachment=True, conditional=True, cache_timeout=31536000 # zips never change once created ) # Lastly write an audit record data_engine.add_portfolio_history(folio, user, FolioHistory.ACTION_DOWNLOADED, folio_export.filename) return response except httpexc.HTTPException: # Pass through HTTP 4xx and 5xx raise except SecurityError as e: if app.config['DEBUG']: raise log_security_error(e, request) raise httpexc.Forbidden() except DoesNotExistError as e: logger.warning('404 Not found: ' + str(e)) raise httpexc.NotFound(safe_error_str(e)) except Exception as e: if app.config['DEBUG']: raise logger.error('500 Error for ' + request.url + '\n' + str(e)) raise httpexc.InternalServerError(safe_error_str(e))
def view_task(project, flamenco_props, task_id): from flamenco.tasks.sdk import Task api = pillar_api() if not request.is_xhr: # Render page that'll perform the XHR. from flamenco.jobs import routes as job_routes task = Task.find(task_id, {'projection': {'job': 1}}, api=api) return job_routes.for_project(project, job_id=task['job'], task_id=task_id) # Task list is public, task details are not. if not current_user.has_cap('flamenco-view'): raise wz_exceptions.Forbidden() task = Task.find(task_id, api=api) from . import REQUEABLE_TASK_STATES project_id = bson.ObjectId(project['_id']) write_access = current_flamenco.auth.current_user_may( Actions.USE, project_id) can_requeue_task = write_access and task['status'] in REQUEABLE_TASK_STATES return render_template('flamenco/tasks/view_task_embed.html', task=task, project=project, flamenco_props=flamenco_props.to_dict(), flamenco_context=request.args.get('context'), can_view_log=write_access, can_requeue_task=can_requeue_task)
def by_bank(self, project): if not http.request.env.user.sudo(http.request.env.user). \ user_has_groups( 'bestja_project_hierarchy.managers_level0,bestja_project_hierarchy.managers_level1' ): return exceptions.Forbidden() http.request.env.cr.execute( """ SELECT responsible.id, responsible.name, SUM(entry.total_cities_nr) as cities_nr, SUM(entry.tonnage) as sum_tonnage FROM bestja_report_entry as entry JOIN organization as responsible ON (entry.responsible_organization = responsible.id) JOIN bestja_detailed_report as report ON (entry.detailed_report = report.id) WHERE entry.top_project = %s AND report.state = 'accepted' GROUP BY responsible.id, responsible.name ORDER BY sum_tonnage DESC, responsible.name ASC """, [ project, ]) bank_sums = http.request.env.cr.fetchall() return http.request.render( 'bestja_detailed_reports.by_bank', { 'project': http.request.env['bestja.project'].sudo().browse([ project, ]), 'bank_sums': bank_sums, })
def patch_set_task_status(self, task_id: bson.ObjectId, patch: dict): """Updates a task's status in the database.""" from flamenco import current_flamenco from pillar.api.utils.authentication import current_user_id tasks_coll = current_flamenco.db('tasks') task = tasks_coll.find_one({'_id': task_id}, projection={ 'job': 1, 'manager': 1, 'status': 1 }) if not current_flamenco.manager_manager.user_may_use( mngr_doc_id=task['manager']): log.warning( 'patch_set_task_status(%s, %r): User %s is not allowed to use manager %s!', task_id, patch, current_user_id(), task['manager']) raise wz_exceptions.Forbidden() new_status = patch['status'] try: current_flamenco.task_manager.api_set_task_status(task, new_status) except ValueError: raise wz_exceptions.UnprocessableEntity('Invalid status')
def projects_list(self): user_bank_id = None if not http.request.env.user.sudo(http.request.env.user). \ user_has_groups( 'bestja_project_hierarchy.managers_level0,bestja_project_hierarchy.managers_level1' ): user_organization = http.request.env['organization'].sudo().search( [ ('level', '=', 2), '|', # noqa ('coordinator', '=', http.request.env.user.id), ('projects.manager', '=', http.request.env.user.id) ]) if not user_organization: return exceptions.Forbidden() user_bank_id = user_organization.parent.id projects = http.request.env['bestja.project'].sudo().search( [ ('organization_level', '=', 0), ('use_detailed_reports', '=', True), ('date_start', '<=', Date.today()), ], order='date_start desc') return http.request.render('bestja_detailed_reports.projects_list', { 'projects': projects, 'user_bank_id': user_bank_id, })
def authenticate_account(request, number): """ Authenticates the account and raises error :param request: contains input parameter :param number: number belonging to some account :return: Error in case authentication fails """ error = None username = request.get('username', None) auth_id = request.get('password', None) # authenticate the request account_obj = Account.query.filter_by(auth_id=auth_id, username=username).first() if not account_obj: raise exceptions.Forbidden() # check if number belongs to the particular account number_object = phone_number.query.filter_by( number=number, account_id=account_obj.id).first() if not number_object: error = "%s parameter not found" return error
def view_task(project, flamenco_props, task_id): if not request.is_xhr: return for_project(project, task_id=task_id) # Task list is public, task details are not. if not flask_login.current_user.has_role(*ROLES_REQUIRED_TO_VIEW_ITEMS): raise wz_exceptions.Forbidden() api = pillar_api() task = pillarsdk.Node.find(task_id, api=api) node_type = project.get_node_type(node_type_task['name']) # Fetch project users so that we can assign them tasks if 'PUT' in task.allowed_methods: users = project.get_users(api=api) project.users = users['_items'] else: task.properties.assigned_to.users = [ pillar.web.subquery.get_user_info(uid) for uid in task.properties.assigned_to.users ] return render_template('flamenco/tasks/view_task_embed.html', task=task, project=project, task_node_type=node_type, flamenco_props=flamenco_props.to_dict(), flamenco_context=request.args.get('context'))
def revoke_auth_token(manager_id): """Revokes the Manager's existing authentication tokens. Only allowed by owners of the Manager. """ manager_oid = str2id(manager_id) csrf = request.form.get('csrf', '') if not flask_wtf.csrf.validate_csrf(csrf): log.warning( 'User %s tried to generate authentication token for Manager %s without ' 'valid CSRF token!', current_user.user_id, manager_oid) raise wz_exceptions.PreconditionFailed() if not current_flamenco.manager_manager.user_is_owner( mngr_doc_id=manager_oid): log.warning( 'User %s wants to generate authentication token of manager %s, ' 'but user is not owner of that Manager. Request denied.', current_user.user_id, manager_oid) raise wz_exceptions.Forbidden() current_flamenco.manager_manager.revoke_auth_token(manager_oid) return '', 204
def ValidateCSRFTokenOrRaise(request): """Decorator for WSGI handler that checks CSRF cookie against the request.""" # CSRF check doesn't make sense for GET/HEAD methods, because they can # (and are) used when downloading files through <a href> links - and # there's no way to set X-CSRFToken header in this case. if request.method in ("GET", "HEAD"): return # In the ideal world only JavaScript can be used to add a custom header, and # only within its origin. By default, browsers don't allow JavaScript to # make cross origin requests. # # Unfortunately, in the real world due to bugs in browsers plugins, it can't # be guaranteed that a page won't set an HTTP request with a custom header # set. That's why we also check the contents of a header via an HMAC check # with a server-stored secret. # # See for more details: # https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet # (Protecting REST Services: Use of Custom Request Headers). csrf_token = utils.SmartStr(request.headers.get("X-CSRFToken", "")) if not csrf_token: logging.info("Did not find headers CSRF token for: %s", request.path) raise werkzeug_exceptions.Forbidden("CSRF token is missing") try: decoded = base64.urlsafe_b64decode(csrf_token + "==") digest, token_time = decoded.rsplit(CSRF_DELIMITER, 1) token_time = long(token_time) except (TypeError, ValueError): logging.info("Malformed CSRF token for: %s", request.path) raise werkzeug_exceptions.Forbidden("Malformed CSRF token") if len(digest) != hashlib.sha256().digest_size: logging.info("Invalid digest size for: %s", request.path) raise werkzeug_exceptions.Forbidden("Malformed CSRF token digest") expected = GenerateCSRFToken(request.user, token_time) if not constant_time.bytes_eq(csrf_token, expected): logging.info("Non-matching CSRF token for: %s", request.path) raise werkzeug_exceptions.Forbidden("Non-matching CSRF token") current_time = rdfvalue.RDFDatetime.Now().AsMicrosecondsSinceEpoch() if current_time - token_time > CSRF_TOKEN_DURATION.microseconds: logging.info("Expired CSRF token for: %s", request.path) raise werkzeug_exceptions.Forbidden("Expired CSRF token")
def _HandleHelp(self, request): """Handles help requests.""" help_path = request.path.split("/", 2)[-1] if not help_path: raise werkzeug_exceptions.Forbidden("Error: Invalid help path.") # Proxy remote documentation. return self._RedirectToRemoteHelp(help_path)
def present_notifications(): """Present proposal notifications.""" if not rbac.permissions.is_admin(): raise exceptions.Forbidden() proposals = get_email_proposal_list() generator = ("<h1> email to {}</h1>\n {}".format(addressee.email, body) for addressee, body in addressee_body_generator(proposals)) return "".join(generator)