def edit(slug: str): try: category = queries.get_category(slug=slug) except exceptions.DoesNotExist: flask.abort(status=404) flask_bouncer.ensure(action=flask_bouncer.EDIT, subject=category) form = forms.Edit(obj=category) if form.validate_on_submit(): try: operations.update_category( category=category, title=form.title.data, description=form.description.data, ) except exceptions.UnableToUpdate: flask.flash(message="Unable to update category.", category="error") return flask.redirect(location=flask.url_for( endpoint="categories.edit", slug=category.slug)) else: flask.flash(message="Category updated.", category="success") return flask.redirect(location=flask.url_for( endpoint="categories.display", slug=category.slug)) context = {"title": f"Edit {category.title}", "form": form} return flask.render_template(template_name_or_list="categories/edit.html", **context)
def edit(slug: str): try: blog = queries.get_blog(slug=slug) except exceptions.DoesNotExist: flask.abort(status=404) flask_bouncer.ensure(action=flask_bouncer.EDIT, subject=blog) form = forms.Edit(obj=blog) form.categories.query = category_models.Category.query.all() if form.validate_on_submit(): try: operations.update_blog( blog=blog, title=form.title.data, description=form.description.data, body=bleach.clean(text=form.body.data, **constants.BLEACH_KWARGS), categories=form.categories.data, published=form.published.data, comment=form.comment.data, ) except exceptions.UnableToUpdate: flask.flash(message="Unable to update blog.", category="error") return flask.redirect( location=flask.url_for(endpoint="blogs.edit", slug=blog.slug)) else: flask.flash(message="Blog updated.", category="success") return flask.redirect(location=flask.url_for( endpoint="blogs.display", slug=blog.slug)) context = {"title": f"Edit {blog.title}", "form": form} return flask.render_template(template_name_or_list="blogs/edit.html", **context)
def post(self): flask_bouncer.ensure(action=flask_bouncer.CREATE, subject=models.Blog) payload = request.get_json(force=True) data = self.serializer.load(data=payload) data["author"] = flask_login.current_user blog = operations.create_blog(**data) return self.serializer.dump(obj=blog), http.HTTPStatus.CREATED
def edit(username: str): try: account = queries.get_account(username=username) except exceptions.DoesNotExist: flask.abort(status=404) flask_bouncer.ensure(action=flask_bouncer.EDIT, subject=account) form = forms.Edit(obj=account) if form.validate_on_submit(): try: operations.update_account( account=account, display=form.display.data, about=form.about.data, ) except exceptions.UnableToUpdate: flask.flash(message="Unable to edit account.", category="error") return flask.redirect(location=flask.url_for( endpoint="accounts.edit", username=account.username)) else: flask.flash( message="Account updated.", category="success", ) return flask.redirect(location=flask.url_for( endpoint="accounts.display", username=account.username)) context = {"title": f"Edit {account.display}", "form": form} return flask.render_template(template_name_or_list="accounts/edit.html", **context)
def put(self, id: int): account = queries.get_account(id=id) flask_bouncer.ensure(action=flask_bouncer.EDIT, subject=account) payload = request.get_json(force=True) data = self.serializer.load(data=payload) operations.update_account(account=account, **data) return self.serializer.dump(obj=account), http.HTTPStatus.ACCEPTED
def authorize(act_as_user_id): try: ensure(impersonation.IMPERSONATE, load_user(act_as_user_id)) return True except Unauthorized as e: abort(403, title="Impersonation Failed", message="Sorry, your role in the system does not allow you see a student's view.") return False # normally won't reach here
def display(slug: str): try: blog = queries.get_blog(slug=slug) except exceptions.DoesNotExist: flask.abort(status=404) flask_bouncer.ensure(action=flask_bouncer.READ, subject=blog) context = {"title": blog.title, "blog": blog} return flask.render_template(template_name_or_list="blogs/display.html", **context)
def edit_post_with_ensure(post_id): # Find an article form a db -- faking for testing mary = User(name='mary', admin=False) article = Article(author_id=mary.id) # bounce them out if they do not have access ensure(EDIT, article) # edit the post return "successfully edited post"
def edit_post(post_id): # Find an article form a db -- faking for testing jonathan = User(name='jonathan', admin=False, id=1) article = Article(author_id=jonathan.id) # bounce them out if they do not have access ensure(EDIT, article) # edit the post return "successfully edited post"
def allow(operation, target): """ This duplicates bouncer's can() operation since flask-bouncer doesn't implement it. Named allow() to avoid namespace confusion with bouncer. """ try: ensure(operation, target) return True except Unauthorized: return False
def require(operation, target): """ This is basically Flask-Bouncer's ensure except it throws a 403 instead of a 401 if the permission check fails. A 403 is more accurate since authentication would not help and it would prevent the login box from showing up. Named require() to avoid confusion with Flask-Bouncer :param operation: same as Flask-Bouncer's ensure :param target: same as Flask-Bouncer's ensure :return:same as Flask-Bouncer's ensure """ try: ensure(operation, target) except Unauthorized as e: raise Forbidden(e.get_description())
def authorize(act_as_user_id): try: ensure(impersonation.IMPERSONATE, load_user(act_as_user_id)) return True except Unauthorized as e: abort( 403, title="Impersonation Failed", message= "Sorry, your role in the system does not allow you see a student's view." ) return False # normally won't reach here
def post( self, blog_id: Optional[int] = None, parent_id: Optional[int] = None ): flask_bouncer.ensure( action=flask_bouncer.CREATE, subject=models.Comment ) payload = request.get_json(force=True) data = self.serializer.load(data=payload) data["author"] = flask_login.current_user if blog_id: data["blog"] = blog_queries.get_blog(id=blog_id) elif parent_id: data["parent"] = queries.get_comment(id=parent_id) comment = operations.create_comment(**data) return self.serializer.dump(obj=comment), http.HTTPStatus.CREATED
def require(operation, target, title=None, message=None): """ This is basically Flask-Bouncer's ensure except it also takes an optional error title and message that'll be passed to the user if the permission check failed. Named require() to avoid confusion with Flask-Bouncer :param operation: same as Flask-Bouncer's ensure :param target: same as Flask-Bouncer's ensure :return:same as Flask-Bouncer's ensure """ try: ensure(operation, target) except Forbidden as e: if not title: title = "Forbidden" if not message: message = e.description abort(403, title=title, message=message)
def require(operation, target, title=None, message=None): """ This is basically Flask-Bouncer's ensure except it throws a 403 instead of a 401 if the permission check fails. A 403 is more accurate since authentication would not help and it would prevent the login box from showing up. Named require() to avoid confusion with Flask-Bouncer :param operation: same as Flask-Bouncer's ensure :param target: same as Flask-Bouncer's ensure :return:same as Flask-Bouncer's ensure """ try: ensure(operation, target) except Unauthorized as e: if not title: title = "Forbidden" if not message: message = e.description abort(403, title=title, message=message)
def _verify_permissions(self, user_id, permissions): user = User.query.get(user_id) with self.app.app_context(): # can't figure out how to get into logged in app context, so just force a login here login_user(user, force=True) admin = user.system_role == SystemRole.sys_admin for model_name, operations in permissions.items(): for operation, permission in operations.items(): expected = True try: ensure(operation, model_name) except Unauthorized: expected = False expected = expected or admin self.assertEqual( permission['global'], expected, "Expected permission " + operation + " on " + model_name + " to be " + str(expected)) # undo the forced login earlier logout_user()
def delete(slug: str): try: blog = queries.get_blog(slug=slug) except exceptions.DoesNotExist: flask.abort(status=404) flask_bouncer.ensure(action=flask_bouncer.DELETE, subject=blog) form = forms.Delete() if form.validate_on_submit(): try: operations.delete_blog(blog=blog) except exceptions.UnableToDelete: flask.flash(message="Unable to delete blog.", category="error") return flask.redirect(location=flask.url_for( endpoint="blogs.display", slug=blog.slug)) else: flask.flash(message="Blog deleted.", category="success") return flask.redirect(location=flask.url_for( endpoint="main.landing")) context = {"title": f"Delete {blog.title}", "form": form} return flask.render_template(template_name_or_list="blogs/delete.html", **context)
def delete(username: str): try: account = queries.get_account(username=username) except exceptions.DoesNotExist: flask.abort(status=404) flask_bouncer.ensure(action=flask_bouncer.DELETE, subject=account) form = forms.Delete() if form.validate_on_submit(): try: operations.delete_account(account=account, delete_blogs=form.delete_blogs.data) except exceptions.UnableToDelete: flask.flash(message="Unable to delete account.", category="error") return flask.redirect(location=flask.url_for( endpoint="accounts.display", username=account.username)) else: flask.flash(message="Account deleted.", category="success") if flask_login.current_user == account: flask_login.logout_user() return flask.redirect(location=flask.url_for( endpoint="main.landing")) context = {"title": f"Delete {account.display}", "form": form} return flask.render_template(template_name_or_list="accounts/delete.html", **context)
def get_logged_in_user_permissions(): user = User.query.get(current_user.id) require(READ, user) courses = UserCourse.query \ .filter_by(user_id=current_user.id) \ .filter(UserCourse.course_role != CourseRole.dropped) \ .all() admin = user.system_role == SystemRole.sys_admin permissions = {} models = { User.__name__: user, } operations = { MANAGE, READ, EDIT, CREATE, DELETE } # global models for model_name, model in models.items(): # create entry if not already exists permissions.setdefault(model_name, {}) # if not model_name in permissions: # permissions[model_name] = {} # obtain permission values for each operation for operation in operations: permissions[model_name][operation] = {'global': True} try: ensure(operation, model) except Unauthorized: permissions[model_name][operation]['global'] = False # course model # model_name / operation / courseId OR global permissions['Course'] = {CREATE: {'global': allow(CREATE, Course)}} mod_operations = {MANAGE, READ, EDIT, DELETE} for operation in mod_operations: permissions['Course'].setdefault(operation, {}) permissions['Course'][operation]['global'] = admin for course in courses: course_uuid = course.course_uuid try: ensure(operation, Course(id=course.course_id)) permissions['Course'][operation][course_uuid] = True permissions['Course'][operation]['global'] = True except Unauthorized: permissions['Course'][operation][course_uuid] = False # assignment model # model_name / operation / courseId OR global permissions['Assignment'] = {} mod_operations = {MANAGE, READ, EDIT, CREATE, DELETE} for operation in mod_operations: permissions['Assignment'].setdefault(operation, {}) permissions['Assignment'][operation]['global'] = admin for course in courses: course_uuid = course.course_uuid try: ensure(operation, Assignment(course_id=course.course_id)) permissions['Assignment'][operation][course_uuid] = True permissions['Assignment'][operation]['global'] = True except Unauthorized: permissions['Assignment'][operation][course_uuid] = False return permissions
def vuln_review(vcdb_id, vuln_id): vulnerability_details = _get_vulnerability_details(vcdb_id, simplify_id=False) view = vulnerability_details.vulnerability_view vuln = vulnerability_details.get_or_create_vulnerability() proposal_vulnerability_details = _get_vulnerability_details( None, vuln_id=vuln_id, simplify_id=False) proposal_view = proposal_vulnerability_details.vulnerability_view proposal_vuln = proposal_vulnerability_details.get_or_create_vulnerability( ) form_reject = VulnerabilityProposalReject() form_approve = VulnerabilityProposalApprove() form_assign = VulnerabilityProposalAssign() form_unassign = VulnerabilityProposalUnassign() form_publish = VulnerabilityProposalPublish() if request.method == 'POST': if request.form[ "review_response"] == "assign" and form_assign.validate_on_submit( ): ensure(ASSIGN, proposal_vuln) if proposal_vuln.is_reviewable(): proposal_vuln.accept_review(g.user) db.session.add(proposal_vuln) db.session.commit() flash("The review was successfully assigned to you.", "success") return redirect(request.url) else: flash_error("This entry is not in a reviewable state.") if request.form[ "review_response"] == "unassign" and form_unassign.validate_on_submit( ): ensure(ASSIGN, proposal_vuln) if proposal_vuln.is_reviewer(g.user): proposal_vuln.deny_review() db.session.add(proposal_vuln) db.session.commit() flash("You successfully unassigned yourself from this review.", "success") return redirect(request.url) else: flash_error("This entry is not assigned to you.") if request.form[ "review_response"] == "approve" and form_approve.validate_on_submit( ): ensure(APPROVE, proposal_vuln) proposal_vuln.accept_change() db.session.add(proposal_vuln) db.session.commit() flash( "You approved the proposal. Waiting for the entry to be published by an admin.", "success") return redirect(request.url) if request.form[ "review_response"] == "reject" and form_reject.validate_on_submit( ): ensure(REJECT, proposal_vuln) proposal_vuln.deny_change(g.user, form_reject.data["review_feedback"]) db.session.add(proposal_vuln) db.session.commit() flash("Waiting for the author to address your feedback.", "success") return redirect(request.url) if request.form[ "review_response"] == "publish" and form_publish.validate_on_submit( ): ensure('PUBLISH', proposal_vuln) proposal_vuln.publish_change() db.session.add(proposal_vuln) db.session.commit() # This might be the first entry of its kind so no archiving is necessary. if vuln.state: vuln.archive_entry() db.session.add(vuln) db.session.commit() flash("Entry was successfully published.", "success") return redirect(request.url) # Published entries can't be reviewed. # if view.state == VulnerabilityState.PUBLISHED: # raise RequestRedirect("/" + str(vcdb_id)) return render_template( "vulnerability/review/review.html", proposal_vulnerability_details=proposal_vulnerability_details, vulnerability_details=vulnerability_details, form_assign=form_assign, form_unassign=form_unassign, form_reject=form_reject, form_approve=form_approve, form_publish=form_publish)
def read(id): user = User.query.get_or_404(id) ensure(READ, user) return APIResult(user.export_data())
def update(id): user = User.query.get_or_404(id) ensure(UPDATE, user) user.import_data(request.method, request.get_json()) user.save() return APIResult({'self_url': user.get_url()})
def delete(id): user = User.query.get_or_404(id) ensure(DELETE, user) user.remove() return APIResult({}, 204)
def delete(self, id: int): blog = queries.get_blog(id=id) flask_bouncer.ensure(action=flask_bouncer.DELETE, subject=blog) blog.delete() return dict(), http.HTTPStatus.NO_CONTENT
def test(self): ensure(self.action, self.subject)
def delete(self, id: int): comment = queries.get_comment(id=id) flask_bouncer.ensure(action=flask_bouncer.DELETE, subject=comment) comment.delete() return dict(), http.HTTPStatus.NO_CONTENT
def get_logged_in_user_permissions(): user = Users.query.get(current_user.id) require(READ, user) dropped_id = UserTypesForCourse.query.filter_by(name=UserTypesForCourse.TYPE_DROPPED).first().id courses = CoursesAndUsers.query.filter_by(users_id=current_user.id) \ .filter(CoursesAndUsers.usertypesforcourse_id != dropped_id).all() admin = user.usertypeforsystem.name == "System Administrator" permissions = {} models = { Users.__name__: Users, } post_based_models = { PostsForQuestions.__name__: PostsForQuestions() } operations = { MANAGE, READ, EDIT, CREATE, DELETE } # global models for model_name, model in models.items(): # create entry if not already exists permissions.setdefault(model_name, {}) # if not model_name in permissions: # permissions[model_name] = {} # obtain permission values for each operation for operation in operations: permissions[model_name][operation] = {'global': True} try: ensure(operation, model) except Unauthorized: permissions[model_name][operation]['global'] = False # course model # model_name / operation / courseId OR global permissions['Courses'] = {CREATE: {'global': allow(CREATE, Courses)}} mod_operations = {MANAGE, READ, EDIT, DELETE} for operation in mod_operations: permissions['Courses'].setdefault(operation, {}) permissions['Courses'][operation]['global'] = admin for course in courses: course_id = str(course.courses_id) try: ensure(operation, Courses(id=course.courses_id)) permissions['Courses'][operation][course_id] = True permissions['Courses'][operation]['global'] = True except Unauthorized: permissions['Courses'][operation][course_id] = False # post-based models for model_name, model in post_based_models.items(): permissions.setdefault(model_name, {}) for operation in operations: permissions[model_name].setdefault(operation, {}) permissions[model_name][operation]['global'] = admin for course in courses: course_id = str(course.courses_id) try: m = model p = Posts(courses_id=course.courses_id) setattr(m, 'post', p) ensure(operation, m) permissions[model_name][operation][course_id] = True permissions[model_name][operation]['global'] = True except Unauthorized: permissions[model_name][operation][course_id] = False return permissions