connected permissions checks and testing editor access

This commit is contained in:
SvyatoslavArtymovych 2023-05-31 14:18:11 +03:00
parent 9032a3d4bf
commit 8754347ed2
18 changed files with 148561 additions and 87 deletions

View File

@ -28,10 +28,7 @@ def create_app(environment="development"):
permissions_blueprint,
search_blueprint,
)
from app.models import (
User,
AnonymousUser,
)
from app.models import User, AnonymousUser, Permission
# Instantiate app.
app = Flask(__name__)
@ -75,12 +72,16 @@ def create_app(environment="development"):
display_tags,
build_qa_url_using_interpretation,
recursive_render,
has_permission,
)
app.jinja_env.globals["Access"] = Permission.Access
app.jinja_env.globals["form_hidden_tag"] = form_hidden_tag
app.jinja_env.globals["display_tags"] = display_tags
app.jinja_env.globals["build_qa_url"] = build_qa_url_using_interpretation
app.jinja_env.globals["recursive_render"] = recursive_render
app.jinja_env.globals["has_permission"] = has_permission
# Error handlers.
@app.errorhandler(HTTPException)

View File

@ -0,0 +1,21 @@
from app import models as m
BOOK = {
"model": m.Book,
"entity_id_field": "book_id",
}
COLLECTION = {
"model": m.Collection,
"entity_id_field": "collection_id",
}
SECTION = {
"model": m.Section,
"entity_id_field": "section_id",
}
INTERPRETATION = {
"model": m.Interpretation,
"entity_id_field": "interpretation_id",
}

View File

@ -3,6 +3,7 @@ import re
from flask import current_app
from flask_wtf import FlaskForm
from flask import url_for, render_template
from flask_login import current_user
from app import models as m
@ -51,9 +52,35 @@ def build_qa_url_using_interpretation(interpretation: m.Interpretation):
return url
# Using: {{ recursive_render("template.html", collection=collection, book=book) }}
def recursive_render(template: str, collection: m.Collection, book: m.Book):
return render_template(
template,
collection=collection,
book=book,
)
# Using: {{ has_permission(entity=book, required_permissions=[Access.create]) }}
def has_permission(
entity: m.Book | m.Collection | m.Section | m.Interpretation,
required_permissions: m.Permission.Access | list[m.Permission.Access],
) -> bool:
if type(required_permissions) == m.Permission.Access:
required_permissions = [required_permissions]
access_groups: list[m.AccessGroup] = list(
set(entity.access_groups).intersection(current_user.access_groups)
)
if not access_groups:
return False
for access_group in access_groups:
for permission in access_group.permissions:
permission: m.Permission
for required_permission in required_permissions:
if permission.access & required_permission:
return True
return False

View File

@ -3,41 +3,45 @@ from flask import flash, redirect, url_for, request, make_response
import functools
from app import models as m, db
from app.logger import log
def check_permissions(
entity_type: m.Permission.Entity,
access: list[m.Permission.Access],
entities_data: list[dict] | dict,
entities: list[dict],
):
if not current_user.is_authenticated:
flash("You do not have permission", "danger")
return make_response(redirect(url_for("home.get_all")))
request_args = (
{**request.view_args, **request.args} if request.view_args else {**request.args}
)
if type(entities_data) == dict:
entities_data = [entities_data]
entity = None
for entity_data in entities_data:
model = entity_data.get("model")
entity_id_field = entity_data.get("entity_id_field")
if not model or entity_id_field is None:
raise ValueError(
"One of required arguments(model, entity_id_field) is missions"
)
for model in entities:
entity_id_field = (model.__name__ + "_id").lower()
entity_id = request_args.get(entity_id_field)
if entity_id is None:
raise ValueError("entity_id not found")
entity: m.Book | m.Collection | m.Section | m.Interpretation = db.session.get(
model, entity_id
)
if entity is None:
flash("You do not have permission", "danger")
return make_response(redirect(url_for("home.get_all")))
book_id = request_args.get("book_id")
book: m.Book = db.session.get(m.Book, book_id)
if book and book.user_id == current_user.id:
# user has access because he is book owner
return None
if not entity or not entity.access_groups:
flash("You do not have permission", "warning")
return make_response(redirect(url_for("home.get_all")))
# check if user is not owner of book
if entity.access_groups[0].book.user_id == current_user.id:
if not book and entity.access_groups[0].book.user_id == current_user.id:
# user has access because he is book owner
return None
access_group_query = (
@ -72,7 +76,7 @@ def check_permissions(
def require_permission(
entity_type: m.Permission.Entity,
access: list[m.Permission.Access],
entities_data: list[dict] | dict,
entities: list[dict],
):
def decorator(f):
@functools.wraps(f)
@ -80,7 +84,7 @@ def require_permission(
if response := check_permissions(
entity_type=entity_type,
access=access,
entities_data=entities_data,
entities=entities,
):
return response
return f(*args, **kwargs)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -36,6 +36,13 @@
<li>
<button type="button" id="callAddSectionModal" data-modal-target="add-section-modal" data-modal-toggle="add-section-modal" data-collection-id="{{collection.id}}" data-sub-collection-id="{{sub_collection.id}}" class="w-full block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"> New Section </button>
</li>
{% elif not sub_collection.is_leaf and not sub_collection.children %}
<li>
<button type="button" id="callAddSectionModal" data-modal-target="add-section-modal" data-modal-toggle="add-section-modal" data-collection-id="{{collection.id}}" data-sub-collection-id="{{sub_collection.id}}" class="w-full block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"> New Section </button>
</li>
<li>
<button type="button" id="callAddSubCollectionModal" data-modal-target="add-sub-collection-modal" data-modal-toggle="add-sub-collection-modal" data-collection-id="{{sub_collection.id}}" class="w-full block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"> New Subcollection </button>
</li>
{% else %}
<li>
<button type="button" id="callAddSubCollectionModal" data-modal-target="add-sub-collection-modal" data-modal-toggle="add-sub-collection-modal" data-collection-id="{{sub_collection.id}}" class="w-full block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"> New Subcollection </button>

View File

@ -114,7 +114,7 @@
{% endif %}
<!-- TODO check permissions -->
{% if interpretation.book.owner == current_user %}
{% if interpretation.book.owner == current_user or has_permission(interpretation, Access.D) %}
<div class="approve-button select-none approve-btn mt-3 cursor-pointer" data-approve="comment" data-entity-id="{{ comment.id }}">
<!-- outline -->
<svg class="not-approved-icon w-6 h-6 {% if comment.approved %} hidden {% endif %}" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">

View File

@ -138,6 +138,7 @@
</td>
<td class="px-6 py-4">
<!-- prettier-ignore -->
{% if current_user.id != contributor.user_id %}
<form class="mb-0 flex justify-end" action="{{ url_for('book.delete_contributor', book_id=book.id) }}" method="post">
{{ form_hidden_tag() }}
<input type="hidden" name="user_id" id="user_id" value="{{ contributor.user_id }}" />
@ -145,6 +146,7 @@
Delete
</button>
</form>
{% endif %}
</td>
</tr>
{% endfor %}

View File

@ -5,6 +5,7 @@ from flask import (
from flask_login import login_required, current_user
from app import models as m, db
from app.controllers.require_permission import require_permission
from app.logger import log
bp = Blueprint("approve", __name__, url_prefix="/approve")
@ -14,6 +15,11 @@ bp = Blueprint("approve", __name__, url_prefix="/approve")
"/interpretation/<int:interpretation_id>",
methods=["POST"],
)
@require_permission(
entity_type=m.Permission.Entity.INTERPRETATION,
access=[m.Permission.Access.A],
entities=[m.Interpretation],
)
@login_required
def approve_interpretation(interpretation_id: int):
interpretation: m.Interpretation = db.session.get(
@ -23,16 +29,6 @@ def approve_interpretation(interpretation_id: int):
log(log.WARNING, "Interpretation with id [%s] not found", interpretation_id)
return jsonify({"message": "Interpretation not found"}), 404
# TODO check permission
if interpretation.book.owner != current_user:
log(
log.WARNING,
"User [%s] dont have permission to approve [%s]",
current_user,
interpretation,
)
return jsonify({"message": "You dont have permission"}), 404
already_approved_interpretations = (
m.Interpretation.query.filter_by(
approved=True, section_id=interpretation.section_id
@ -68,6 +64,11 @@ def approve_interpretation(interpretation_id: int):
"/comment/<int:interpretation_id>",
methods=["POST"],
)
@require_permission(
entity_type=m.Permission.Entity.COMMENT,
access=[m.Permission.Access.A],
entities=[m.Interpretation],
)
@login_required
def approve_comment(interpretation_id: int):
comment: m.Comment = db.session.get(m.Comment, interpretation_id)
@ -75,16 +76,6 @@ def approve_comment(interpretation_id: int):
log(log.WARNING, "Comment with id [%s] not found", interpretation_id)
return jsonify({"message": "Comment not found"}), 404
# TODO check permission
if comment.interpretation.book.owner != current_user:
log(
log.WARNING,
"User [%s] dont have permission to approve [%s]",
current_user,
comment,
)
return jsonify({"message": "You dont have permission"}), 404
comment.approved = not comment.approved
log(
log.INFO,

View File

@ -116,10 +116,7 @@ def create():
@require_permission(
entity_type=m.Permission.Entity.BOOK,
access=[m.Permission.Access.U],
entity_data={
"model": m.Book,
"entity_id_field": "book_id",
},
entities=[m.Book],
)
@login_required
def edit(book_id: int):
@ -150,12 +147,7 @@ def edit(book_id: int):
@require_permission(
entity_type=m.Permission.Entity.BOOK,
access=[m.Permission.Access.D],
entities_data=[
{
"model": m.Book,
"entity_id_field": "book_id",
}
],
entities=[m.Book],
)
@login_required
def delete(book_id: int):

View File

@ -39,8 +39,7 @@ def collection_view(book_id: int):
@require_permission(
entity_type=m.Permission.Entity.COLLECTION,
access=[m.Permission.Access.C],
model=m.Collection,
entity_id_field="collection_id",
entities=[m.Collection, m.Book],
)
@login_required
def collection_create(book_id: int, collection_id: int | None = None):
@ -119,6 +118,11 @@ def collection_create(book_id: int, collection_id: int | None = None):
@bp.route("/<int:book_id>/<int:collection_id>/edit", methods=["POST"])
@register_book_verify_route(bp.name)
@require_permission(
entity_type=m.Permission.Entity.COLLECTION,
access=[m.Permission.Access.U],
entities=[m.Collection],
)
@login_required
def collection_edit(book_id: int, collection_id: int):
book: m.Book = db.session.get(m.Book, book_id)
@ -174,6 +178,11 @@ def collection_edit(book_id: int, collection_id: int):
@bp.route("/<int:book_id>/<int:collection_id>/delete", methods=["POST"])
@register_book_verify_route(bp.name)
@require_permission(
entity_type=m.Permission.Entity.COLLECTION,
access=[m.Permission.Access.D],
entities=[m.Collection],
)
@login_required
def collection_delete(book_id: int, collection_id: int):
collection: m.Collection = db.session.get(m.Collection, collection_id)

View File

@ -12,6 +12,7 @@ from app.controllers.delete_nested_book_entities import (
delete_nested_interpretation_entities,
)
from app import models as m, db, forms as f
from app.controllers.require_permission import require_permission
from app.controllers.tags import set_interpretation_tags
from app.logger import log
from .bp import bp
@ -111,6 +112,11 @@ def interpretation_create(
"/<int:book_id>/<int:interpretation_id>/edit_interpretation", methods=["POST"]
)
@register_book_verify_route(bp.name)
@require_permission(
entity_type=m.Permission.Entity.INTERPRETATION,
access=[m.Permission.Access.U],
entities=[m.Interpretation],
)
@login_required
def interpretation_edit(
book_id: int,
@ -155,6 +161,11 @@ def interpretation_edit(
"/<int:book_id>/<int:interpretation_id>/delete_interpretation", methods=["POST"]
)
@register_book_verify_route(bp.name)
@require_permission(
entity_type=m.Permission.Entity.INTERPRETATION,
access=[m.Permission.Access.D],
entities=[m.Interpretation],
)
@login_required
def interpretation_delete(book_id: int, interpretation_id: int):
interpretation: m.Interpretation = db.session.get(

View File

@ -8,12 +8,18 @@ from flask_login import login_required
from app.controllers import register_book_verify_route
from app.controllers.delete_nested_book_entities import delete_nested_section_entities
from app import models as m, db, forms as f
from app.controllers.require_permission import require_permission
from app.logger import log
from .bp import bp
@bp.route("/<int:book_id>/<int:collection_id>/create_section", methods=["POST"])
@register_book_verify_route(bp.name)
@require_permission(
entity_type=m.Permission.Entity.SECTION,
access=[m.Permission.Access.C],
entities=[m.Collection],
)
@login_required
def section_create(book_id: int, collection_id: int):
book: m.Book = db.session.get(m.Book, book_id)
@ -58,6 +64,11 @@ def section_create(book_id: int, collection_id: int):
@bp.route("/<int:book_id>/<int:section_id>/edit_section", methods=["POST"])
@register_book_verify_route(bp.name)
@require_permission(
entity_type=m.Permission.Entity.SECTION,
access=[m.Permission.Access.U],
entities=[m.Section],
)
@login_required
def section_edit(book_id: int, section_id: int):
section: m.Section = db.session.get(m.Section, section_id)
@ -86,6 +97,11 @@ def section_edit(book_id: int, section_id: int):
@bp.route("/<int:book_id>/<int:section_id>/delete_section", methods=["POST"])
@register_book_verify_route(bp.name)
@require_permission(
entity_type=m.Permission.Entity.SECTION,
access=[m.Permission.Access.D],
entities=[m.Section],
)
@login_required
def section_delete(
book_id: int,

View File

@ -20,8 +20,7 @@ from .bp import bp
@require_permission(
entity_type=m.Permission.Entity.BOOK,
access=[m.Permission.Access.U],
model=m.Book,
entity_id_field="book_id",
entities=[m.Book],
)
@login_required
def settings(book_id: int):
@ -37,8 +36,7 @@ def settings(book_id: int):
@require_permission(
entity_type=m.Permission.Entity.BOOK,
access=[m.Permission.Access.U],
model=m.Book,
entity_id_field="book_id",
entities=[m.Book],
)
@login_required
def add_contributor(book_id: int):
@ -87,8 +85,7 @@ def add_contributor(book_id: int):
@require_permission(
entity_type=m.Permission.Entity.BOOK,
access=[m.Permission.Access.U],
model=m.Book,
entity_id_field="book_id",
entities=[m.Book],
)
@login_required
def delete_contributor(book_id: int):
@ -146,8 +143,7 @@ def delete_contributor(book_id: int):
@require_permission(
entity_type=m.Permission.Entity.BOOK,
access=[m.Permission.Access.U],
model=m.Book,
entity_id_field="book_id",
entities=[m.Book],
)
@login_required
def edit_contributor_role(book_id: int):

View File

@ -21,8 +21,7 @@ def test_approve_interpretation(client: FlaskClient):
)
assert response
assert response.status_code == 404
assert response.json["message"] == "Interpretation not found"
assert b"You do not have permission" in response.data
interpretation: m.Interpretation = m.Interpretation.query.filter_by(
user_id=dummy_user.id
@ -33,7 +32,7 @@ def test_approve_interpretation(client: FlaskClient):
)
assert response
assert response.json["message"] == "You dont have permission"
assert b"You do not have permission" in response.data
interpretation: m.Interpretation = m.Interpretation.query.filter_by(
user_id=user.id
@ -78,8 +77,7 @@ def test_approve_comment(client: FlaskClient):
)
assert response
assert response.status_code == 404
assert response.json["message"] == "Comment not found"
assert b"You do not have permission" in response.data
comment: m.Comment = m.Comment.query.filter_by(user_id=dummy_user.id).first()
response: Response = client.post(
@ -88,7 +86,7 @@ def test_approve_comment(client: FlaskClient):
)
assert response
assert response.json["message"] == "You dont have permission"
assert b"You do not have permission" in response.data
comment: m.Comment = m.Comment.query.filter_by(user_id=user.id).first()
response: Response = client.post(

View File

@ -3,6 +3,7 @@ from flask import current_app as Response, url_for
from flask.testing import FlaskClient, FlaskCliRunner
from app import models as m, db
from app.controllers.create_access_groups import create_moderator_group
from tests.utils import (
login,
logout,
@ -562,7 +563,7 @@ def test_crud_sections(client: FlaskClient, runner: FlaskCliRunner):
).first()
response: Response = client.post(
f"/book/{book.id}/{sub_collection.id}/create_section",
f"/book/{book.id}/{collection.id}/create_section",
data=dict(
collection_id=collection.id,
label="Test Section",
@ -875,15 +876,23 @@ def test_crud_interpretation(client: FlaskClient):
# edit
m.Interpretation(
i_1 = m.Interpretation(
text="Test", section_id=section_in_collection.id, user_id=user.id
).save()
m.Interpretation(
i_2 = m.Interpretation(
text="Test",
section_id=section_in_subcollection.id,
).save()
group = create_moderator_group(book.id)
m.InterpretationAccessGroups(
interpretation_id=i_1.id, access_group_id=group.id
).save()
m.InterpretationAccessGroups(
interpretation_id=i_2.id, access_group_id=group.id
).save()
interpretation: m.Interpretation = m.Interpretation.query.filter_by(
section_id=section_in_collection.id
).first()
@ -996,6 +1005,10 @@ def test_crud_comment(client: FlaskClient, runner: FlaskCliRunner):
collection_id=sub_collection.id,
version_id=book.last_version.id,
).save()
group = create_moderator_group(book.id)
m.SectionAccessGroups(
section_id=section_in_subcollection.id, access_group_id=group.id
).save()
label_1 = "Test Interpretation #1 Label"
text_1 = "Test Interpretation #1 Text"
@ -1142,6 +1155,13 @@ def test_interpretation_in_home_last_inter_section(
collection_id=sub_collection.id,
version_id=book.last_version.id,
).save()
group = create_moderator_group(book.id)
m.SectionAccessGroups(
section_id=section_in_subcollection.id, access_group_id=group.id
).save()
m.SectionAccessGroups(
section_id=section_in_collection.id, access_group_id=group.id
).save()
label_1 = "Test Interpretation #1 Label"
text_1 = "Test Interpretation #1 Text"

288
tests/test_permissions.py Normal file
View File

@ -0,0 +1,288 @@
from random import randint
from flask import current_app as Response, url_for
from flask.testing import FlaskClient, FlaskCliRunner
from app import models as m, db
from app.controllers.create_access_groups import create_moderator_group
from tests.utils import (
login,
logout,
check_if_nested_book_entities_is_deleted,
check_if_nested_collection_entities_is_deleted,
check_if_nested_section_entities_is_deleted,
check_if_nested_interpretation_entities_is_deleted,
create_test_book,
)
def create_book(client):
random_id = randint(1, 100)
BOOK_NAME = f"TBook {random_id}"
response: Response = client.post(
"/book/create",
data=dict(label=BOOK_NAME),
follow_redirects=True,
)
assert response.status_code == 200
assert b"Book added!" in response.data
book: m.Book = m.Book.query.filter_by(label=BOOK_NAME).first()
assert book
assert book.versions
assert len(book.versions) == 1
assert book.access_groups
assert len(book.access_groups) == 2
root_collection: m.Collection = book.last_version.collections[0]
assert root_collection
assert root_collection.access_groups
assert len(root_collection.access_groups) == 2
return book
def create_collection(client, book_id):
random_id = randint(1, 100)
LABEL = f"TCollection {random_id}"
response: Response = client.post(
f"/book/{book_id}/create_collection",
data=dict(label=LABEL),
follow_redirects=True,
)
assert response.status_code == 200
assert b"Success!" in response.data
collection: m.Collection = m.Collection.query.filter_by(label=LABEL).first()
assert collection
assert collection.access_groups
assert len(collection.access_groups) == 2
for access_group in collection.access_groups:
access_group: m.AccessGroup
assert access_group.book_id == collection.version.book_id
return collection, response
def create_section(client, book_id, collection_id):
random_id = randint(1, 100)
LABEL = f"TSection {random_id}"
response: Response = client.post(
f"/book/{book_id}/{collection_id}/create_section",
data=dict(collection_id=collection_id, label=LABEL),
follow_redirects=True,
)
assert response.status_code == 200
assert b"Success!" in response.data
section: m.Section = m.Section.query.filter_by(
label=LABEL, collection_id=collection_id
).first()
assert section
assert section.collection_id == collection_id
assert not section.interpretations
assert section.access_groups
assert len(section.access_groups) == 2
for access_group in section.access_groups:
access_group: m.AccessGroup
assert access_group.book_id == section.version.book_id
return section, response
def create_interpretation(client, book_id, section_id):
random_id = randint(1, 100)
LABEL = f"TInterpretation {random_id}"
response: Response = client.post(
f"/book/{book_id}/{section_id}/create_interpretation",
data=dict(section_id=section_id, text=LABEL),
follow_redirects=True,
)
assert response.status_code == 200
interpretation: m.Interpretation = m.Interpretation.query.filter_by(
section_id=section_id, text=LABEL
).first()
assert interpretation
assert interpretation.section_id == section_id
assert not interpretation.comments
assert interpretation.access_groups
assert len(interpretation.access_groups) == 2
for access_group in interpretation.access_groups:
access_group: m.AccessGroup
assert access_group.book_id == interpretation.section.version.book_id
return interpretation, response
def create_comment(client, book_id, interpretation_id):
random_id = randint(1, 100)
TEXT = f"TComment {random_id}"
response: Response = client.post(
f"/book/{book_id}/{interpretation_id}/create_comment",
data=dict(
text=TEXT,
interpretation_id=interpretation_id,
),
follow_redirects=True,
)
assert response
assert response.status_code == 200
assert b"Success" in response.data
assert str.encode(TEXT) in response.data
comment: m.Comment = m.Comment.query.filter_by(text=TEXT).first()
assert comment
return comment, response
def test_editor_access_to_entire_book(client):
login(client)
book = create_book(client)
editor = m.User(username="editor", password="editor").save()
response: Response = client.post(
f"/book/{book.id}/add_contributor",
data=dict(user_id=editor.id, role=m.BookContributor.Roles.EDITOR),
follow_redirects=True,
)
assert response.status_code == 200
assert b"Contributor was added!" in response.data
logout(client)
login(client, "editor", "editor")
# access to settings page
response: Response = client.get(f"/book/{book.id}/settings", follow_redirects=True)
assert b"You do not have permission" not in response.data
# access to edit book
response: Response = client.post(
f"/book/{book.id}/edit",
data=dict(book_id=book.id, label="BookEdited"),
follow_redirects=True,
)
assert b"You do not have permission" not in response.data
assert b"Success!" in response.data
# dont have access to delete
response: Response = client.post(
f"/book/{book.id}/delete",
data=dict(book_id=book.id),
follow_redirects=True,
)
assert b"You do not have permission" in response.data
# access to create collection
collection, response = create_collection(client, book.id)
assert b"You do not have permission" not in response.data
assert b"Success!" in response.data
# access to edit collection
response: Response = client.post(
f"/book/{book.id}/{collection.id}/edit",
data=dict(label="NewLabel"),
follow_redirects=True,
)
assert b"You do not have permission" not in response.data
assert b"Success!" in response.data
# access to delete collection
response: Response = client.post(
f"/book/{book.id}/{collection.id}/delete", follow_redirects=True
)
assert b"You do not have permission" not in response.data
assert b"Success!" in response.data
# restore collection
collection.is_deleted = False
collection.save()
# access to create section
section, response = create_section(client, book.id, collection.id)
assert b"You do not have permission" not in response.data
assert b"Success!" in response.data
# access to edit section
response: Response = client.post(
f"/book/{book.id}/{section.id}/edit_section",
data=dict(section_id=section.id, label="NewLabel"),
follow_redirects=True,
)
assert b"You do not have permission" not in response.data
assert b"Success!" in response.data
# access to delete section
response: Response = client.post(
f"/book/{book.id}/{section.id}/delete_section", follow_redirects=True
)
assert b"You do not have permission" not in response.data
assert b"Success!" in response.data
# restore section
section.is_deleted = False
section.save()
# access to create interpretation
interpretation, response = create_interpretation(client, book.id, section.id)
assert b"You do not have permission" not in response.data
assert b"Success!" in response.data
# access to approve interpretation
response: Response = client.post(
f"/approve/interpretation/{interpretation.id}",
follow_redirects=True,
)
assert response
assert response.json["message"] == "success"
assert response.json["approve"]
assert interpretation.approved
# access to delete interpretation
response: Response = client.post(
(f"/book/{book.id}/{interpretation.id}/delete_interpretation"),
follow_redirects=True,
)
assert b"You do not have permission" not in response.data
assert b"Success!" in response.data
# restore interpretation
interpretation.is_deleted = False
interpretation.save()
# access to create comment
comment, response = create_comment(client, book.id, interpretation.id)
assert b"You do not have permission" not in response.data
assert b"Success!" in response.data
# access to approve comment
response: Response = client.post(
f"/approve/comment/{comment.id}",
follow_redirects=True,
)
assert response
assert response.json["message"] == "success"
assert response.json["approve"]
assert interpretation.approved
# access to delete comment
response: Response = client.post(
f"/book/{book.id}/{interpretation.id}/comment_delete",
data=dict(
text=comment.text,
interpretation_id=interpretation.id,
comment_id=comment.id,
),
follow_redirects=True,
)
assert b"You do not have permission" not in response.data
assert b"Success!" in response.data