local access groups

This commit is contained in:
SvyatoslavArtymovych 2023-06-06 10:42:28 +03:00
parent 039d9b8fea
commit ba467e9f73
8 changed files with 148635 additions and 21 deletions

View File

@ -14,10 +14,11 @@
"backref", "backref",
"bookname", "bookname",
"Btns", "Btns",
"CUDA",
"CLEANR", "CLEANR",
"CUDA",
"Divs", "Divs",
"flowbite", "flowbite",
"indeterminated",
"jsonify", "jsonify",
"pydantic", "pydantic",
"pytest", "pytest",

View File

@ -69,5 +69,4 @@ def create_editor_group(book_id: int):
m.PermissionAccessGroups( m.PermissionAccessGroups(
permission_id=permission.id, access_group_id=group.id permission_id=permission.id, access_group_id=group.id
).save() ).save()
return group return group

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -59,8 +59,8 @@
<button name="submit" type="submit"></button> <button name="submit" type="submit"></button>
</form> </form>
</button> </button>
</div> </div>
<svg id="dropdownCollectionContextButton{{collection.id}}" data-dropdown-toggle="dropdown" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 0 0" stroke-width="1.5" stroke="none" class="w-0 h-0"></svg> <svg id="dropdownCollectionContextButton{{collection.id}}" data-dropdown-toggle="dropdown" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 0 0" stroke-width="1.5" stroke="none" class="w-0 h-0"></svg>
</div> </div>
<div data="collection-context-menu-{{collection.id}}" id="dropdown" class="z-10 hidden bg-white divide-y divide-gray-800 border border-gray-800 dark:border-none dark:divide-gray-100 rounded-lg shadow w-44 dark:bg-gray-700"> <div data="collection-context-menu-{{collection.id}}" id="dropdown" class="z-10 hidden bg-white divide-y divide-gray-800 border border-gray-800 dark:border-none dark:divide-gray-100 rounded-lg shadow w-44 dark:bg-gray-700">
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}

View File

@ -83,10 +83,6 @@
</div> </div>
<input data-tooltip-target="a-tooltip-collection-{{collection.id}}" type="checkbox" data-permission="A" data-access-to="collection" data-access-to-id="{{ collection.id }}" class="w-4 h-4 text-green-600 bg-green-100 border-green-400 rounded focus:ring-green-500 dark:focus:ring-green-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-green-300 dark:border-green-500" /> <input data-tooltip-target="a-tooltip-collection-{{collection.id}}" type="checkbox" data-permission="A" data-access-to="collection" data-access-to-id="{{ collection.id }}" class="w-4 h-4 text-green-600 bg-green-100 border-green-400 rounded focus:ring-green-500 dark:focus:ring-green-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-green-300 dark:border-green-500" />
{#
<input type="checkbox" data-access-to="collection" data-access-to-id="{{ collection.id }}" class="w-4 h-4 text-blue-600 bg-gray-300 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-400 dark:border-gray-600" />
#}
<span class="text-center dark:text-gray-300">{{ collection.label }}</span> <span class="text-center dark:text-gray-300">{{ collection.label }}</span>
</div> </div>
@ -148,12 +144,6 @@
<div class="tooltip-arrow" data-popper-arrow></div> <div class="tooltip-arrow" data-popper-arrow></div>
</div> </div>
<input data-tooltip-target="a-tooltip-section-{{section.id}}" type="checkbox" data-permission="A" data-access-to="section" data-access-to-id="{{ section.id }}" class="w-4 h-4 text-green-600 bg-green-100 border-green-400 rounded focus:ring-green-500 dark:focus:ring-green-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-green-300 dark:border-green-500" /> <input data-tooltip-target="a-tooltip-section-{{section.id}}" type="checkbox" data-permission="A" data-access-to="section" data-access-to-id="{{ section.id }}" class="w-4 h-4 text-green-600 bg-green-100 border-green-400 rounded focus:ring-green-500 dark:focus:ring-green-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-green-300 dark:border-green-500" />
{#
<input type="checkbox" data-permission="A" data-access-to="section" data-access-to-id="{{ section.id }}" class="w-4 h-4 text-blue-600 bg-gray-300 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-400 dark:border-gray-600" />
#}
<span class="text-center dark:text-gray-300">{{ section.label }}</span> <span class="text-center dark:text-gray-300">{{ section.label }}</span>
</div> </div>
</li> </li>

View File

@ -1,9 +1,14 @@
import json
from flask import redirect, url_for, Blueprint, flash, request from flask import redirect, url_for, Blueprint, flash, request
from flask_login import current_user from flask_login import current_user
from sqlalchemy import or_
from app import forms as f, models as m, db from app import forms as f, models as m, db
from app.logger import log from app.logger import log
from app.controllers.create_access_groups import (
create_editor_group,
create_moderator_group,
)
bp = Blueprint("permission", __name__, url_prefix="/permission") bp = Blueprint("permission", __name__, url_prefix="/permission")
@ -34,10 +39,70 @@ def set_permissions():
flash("User are not contributor of this book!", "danger") flash("User are not contributor of this book!", "danger")
return redirect(url_for("book.my_library")) return redirect(url_for("book.my_library"))
# TODO process data from checkbox tree user: m.User = contributor.user
# permissions = json.loads(form.permissions.data) users_access_groups: list[m.AccessGroup] = list(
set(book.list_access_groups).intersection(user.access_groups)
)
if len(users_access_groups) > 1:
log(
log.WARNING,
"User: [%s] has more than 1 access group in book [%s]",
user,
book,
)
return {"status": "ok"} for users_access in users_access_groups:
users_access: m.AccessGroup
users_access.users.remove(user)
new_access_group = None
match contributor.role:
case m.BookContributor.Roles.EDITOR:
new_access_group = create_editor_group(book.id)
case m.BookContributor.Roles.MODERATOR:
new_access_group = create_moderator_group(book.id)
case _:
log(
log.CRITICAL,
"Unknown contributor's [%s] role: [%s]",
contributor,
contributor.role,
)
flash("Unknown contributor's role", "danger")
return redirect(url_for("book.settings", book_id=book_id))
m.UserAccessGroups(user_id=user.id, access_group_id=new_access_group.id).save(
False
)
permissions_json = json.loads(form.permissions.data)
book_ids = permissions_json.get("book", [])
for book_id in book_ids:
m.BookAccessGroups(
book_id=book_id, access_group_id=new_access_group.id
).save(False)
collection_ids = permissions_json.get("collection", [])
for collection_id in collection_ids:
m.CollectionAccessGroups(
collection_id=collection_id, access_group_id=new_access_group.id
).save(False)
section_ids = permissions_json.get("section", [])
for section_id in section_ids:
m.SectionAccessGroups(
section_id=section_id, access_group_id=new_access_group.id
).save(False)
db.session.commit()
flash("Success!", "success")
return redirect(url_for("book.settings", book_id=book_id))
log(log.ERROR, "Errors edit contributor access level: [%s]", form.errors)
for field, errors in form.errors.items():
field_label = form._fields[field].label.text
for error in errors:
flash(error.replace("Field", field_label), "danger")
return redirect(url_for("book.settings", book_id=book_id))
@bp.route("/access_tree", methods=["GET"]) @bp.route("/access_tree", methods=["GET"])

View File

@ -1,3 +1,5 @@
import json
from flask import current_app as Response from flask import current_app as Response
from app import models as m from app import models as m
@ -287,3 +289,44 @@ def test_editor_access_tree_entire_book(client):
assert collections_ids assert collections_ids
assert collection_1.id in collections_ids assert collection_1.id in collections_ids
assert collection_2.id in collections_ids assert collection_2.id in collections_ids
def test_set_access_level(client):
login(client)
book = create_book(client)
collection_1, _ = create_collection(client, book.id)
collection_2, _ = create_collection(client, book.id)
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
assert len(book.list_access_groups) == 2
json_string = json.dumps({"collection": [collection_1.id]})
response: Response = client.post(
"/permission/set",
data=dict(
book_id=book.id,
user_id=editor.id,
permissions=json_string,
),
follow_redirects=True,
)
assert response.status_code == 200
assert len(book.list_access_groups) == 3
response: Response = client.post(
"/permission/set",
data=dict(
book_id=book.id,
user_id=editor.id,
),
follow_redirects=True,
)
assert response.status_code == 200
assert b"Success!" not in response.data