mirror of https://github.com/logos-co/open-law.git
permissions models
This commit is contained in:
parent
cfd05b8cd9
commit
102a7f3577
|
@ -11,6 +11,7 @@
|
|||
"**/.venv/*/**": true
|
||||
},
|
||||
"cSpell.words": [
|
||||
"backref",
|
||||
"bookname",
|
||||
"Btns",
|
||||
"flowbite",
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
from app import models as m
|
||||
from app.logger import log
|
||||
|
||||
|
||||
def get_or_create_permission(access: int, entity: m.Permission.Entity):
|
||||
def get_or_create_permission(access: int, entity_type: m.Permission.Entity):
|
||||
permission: m.Permission = m.Permission.query.filter_by(
|
||||
access=access, entity=entity
|
||||
access=access, entity_type=entity_type
|
||||
).first()
|
||||
if not permission:
|
||||
permission: m.Permission = m.Permission(access=access, entity=entity).save()
|
||||
log(log.INFO, "Create permission [%d] for entity [%s]", access, entity_type)
|
||||
permission: m.Permission = m.Permission(
|
||||
access=access, entity_type=entity_type
|
||||
).save()
|
||||
return permission
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
from app import models as m
|
||||
from app.logger import log
|
||||
from .get_or_create_permission import get_or_create_permission
|
||||
|
||||
|
||||
def create_moderator_group():
|
||||
log(log.INFO, "Create moderator access group")
|
||||
group: m.AccessGroup = m.AccessGroup(name="moderator").save()
|
||||
|
||||
interpretation_DA = get_or_create_permission(
|
||||
access=m.Permission.Access.D | m.Permission.Access.A,
|
||||
entity_type=m.Permission.Entity.INTERPRETATION,
|
||||
)
|
||||
comment_DA = get_or_create_permission(
|
||||
access=m.Permission.Access.D | m.Permission.Access.A,
|
||||
entity_type=m.Permission.Entity.COMMENT,
|
||||
)
|
||||
|
||||
log(log.INFO, "Add permission [%d] to group[%d]", interpretation_DA.id, group.id)
|
||||
m.PermissionAccessGroups(
|
||||
permission_id=interpretation_DA.id, access_group_id=group.id
|
||||
).save()
|
||||
|
||||
log(log.INFO, "Add permission [%d] to group[%d]", comment_DA.id, group.id)
|
||||
m.PermissionAccessGroups(
|
||||
permission_id=comment_DA.id, access_group_id=group.id
|
||||
).save()
|
||||
|
||||
return group
|
|
@ -18,4 +18,8 @@ from .permission import (
|
|||
AccessGroup,
|
||||
UserAccessGroups,
|
||||
PermissionAccessGroups,
|
||||
BookAccessGroups,
|
||||
CollectionAccessGroups,
|
||||
SectionAccessGroups,
|
||||
InterpretationAccessGroups,
|
||||
)
|
||||
|
|
|
@ -19,6 +19,13 @@ class Book(BaseModel):
|
|||
stars = db.relationship("User", secondary="books_stars", back_populates="stars")
|
||||
contributors = db.relationship("BookContributor")
|
||||
versions = db.relationship("BookVersion", order_by="asc(BookVersion.id)")
|
||||
list_access_groups = db.relationship(
|
||||
"AccessGroup"
|
||||
) # all access_groups in current book(in nested entities)
|
||||
access_groups = db.relationship(
|
||||
"AccessGroup",
|
||||
secondary="books_access_groups",
|
||||
) # access_groups related to current entity
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.id}: {self.label}>"
|
||||
|
|
|
@ -25,6 +25,10 @@ class Collection(BaseModel):
|
|||
order_by="asc(Collection.id)",
|
||||
)
|
||||
sections = db.relationship("Section")
|
||||
access_groups = db.relationship(
|
||||
"AccessGroup",
|
||||
secondary="collections_access_groups",
|
||||
) # access_groups related to current entity
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.id}: {self.label}>"
|
||||
|
|
|
@ -25,6 +25,10 @@ class Interpretation(BaseModel):
|
|||
secondary="interpretation_tags",
|
||||
back_populates="interpretations",
|
||||
)
|
||||
access_groups = db.relationship(
|
||||
"AccessGroup",
|
||||
secondary="interpretations_access_groups",
|
||||
) # access_groups related to current entity
|
||||
|
||||
@property
|
||||
def vote_count(self):
|
||||
|
|
|
@ -3,6 +3,10 @@ from .access_group import AccessGroup
|
|||
from .permission import Permission
|
||||
from .user_access_groups import UserAccessGroups
|
||||
from .permission_access_groups import PermissionAccessGroups
|
||||
from .book_access_groups import BookAccessGroups
|
||||
from .collection_access_groups import CollectionAccessGroups
|
||||
from .section_access_groups import SectionAccessGroups
|
||||
from .interpretation_access_groups import InterpretationAccessGroups
|
||||
|
||||
# access groups
|
||||
# moderators(by default empty) -> root collection -> CRUD Interpretation, Comment
|
||||
|
@ -26,6 +30,7 @@ from .permission_access_groups import PermissionAccessGroups
|
|||
|
||||
|
||||
# Book
|
||||
# Version:
|
||||
# Root Collection
|
||||
# Collection A
|
||||
# Section
|
||||
|
@ -40,3 +45,13 @@ from .permission_access_groups import PermissionAccessGroups
|
|||
|
||||
# If the user has CRUD access to Collection B it means that
|
||||
# it has access to all nested entities(SubCollection B.1/B.2, Sections)
|
||||
|
||||
|
||||
# 1) Create moderator_AG and editor_AG on book create
|
||||
# 2) Inherit parent's access groups
|
||||
|
||||
# TODO many to many
|
||||
# book -> access_group
|
||||
# collections -> access_group
|
||||
# section -> access_group
|
||||
# interpretation -> access_group
|
||||
|
|
|
@ -5,9 +5,13 @@ from app.models.utils import BaseModel
|
|||
class AccessGroup(BaseModel):
|
||||
__tablename__ = "access_groups"
|
||||
|
||||
name = db.Column(db.String(32), unique=True, nullable=False)
|
||||
name = db.Column(db.String(32), nullable=False)
|
||||
|
||||
# Foreign Keys
|
||||
book_id = db.Column(db.Integer, db.ForeignKey("books.id"))
|
||||
|
||||
# Relationships
|
||||
book = db.relationship("Book", viewonly=True)
|
||||
permissions = db.relationship(
|
||||
"Permission",
|
||||
secondary="permissions_access_groups",
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
from app import db
|
||||
from app.models.utils import BaseModel
|
||||
|
||||
|
||||
class BookAccessGroups(BaseModel):
|
||||
__tablename__ = "books_access_groups"
|
||||
|
||||
# Foreign keys
|
||||
book_id = db.Column(db.Integer, db.ForeignKey("books.id"))
|
||||
access_group_id = db.Column(db.Integer, db.ForeignKey("access_groups.id"))
|
||||
|
||||
def __repr__(self):
|
||||
return f"<b:{self.book_id} to a_g:{self.access_group_id}"
|
|
@ -0,0 +1,13 @@
|
|||
from app import db
|
||||
from app.models.utils import BaseModel
|
||||
|
||||
|
||||
class CollectionAccessGroups(BaseModel):
|
||||
__tablename__ = "collections_access_groups"
|
||||
|
||||
# Foreign keys
|
||||
collection_id = db.Column(db.Integer, db.ForeignKey("collections.id"))
|
||||
access_group_id = db.Column(db.Integer, db.ForeignKey("access_groups.id"))
|
||||
|
||||
def __repr__(self):
|
||||
return f"<c:{self.collection_id} to a_g:{self.access_group_id}"
|
|
@ -0,0 +1,13 @@
|
|||
from app import db
|
||||
from app.models.utils import BaseModel
|
||||
|
||||
|
||||
class InterpretationAccessGroups(BaseModel):
|
||||
__tablename__ = "interpretations_access_groups"
|
||||
|
||||
# Foreign keys
|
||||
interpretation_id = db.Column(db.Integer, db.ForeignKey("interpretations.id"))
|
||||
access_group_id = db.Column(db.Integer, db.ForeignKey("access_groups.id"))
|
||||
|
||||
def __repr__(self):
|
||||
return f"<c:{self.interpretation_id} to a_g:{self.access_group_id}"
|
|
@ -8,10 +8,10 @@ class Permission(BaseModel):
|
|||
__tablename__ = "permissions"
|
||||
|
||||
class Access(IntEnum):
|
||||
C = 1 # 0b0001
|
||||
R = 2 # 0b0010
|
||||
U = 4 # 0b0100
|
||||
D = 8 # 0b1000
|
||||
C = 1 # 0b0001 - Create
|
||||
U = 2 # 0b0010 - Update
|
||||
D = 4 # 0b0100 - Delete
|
||||
A = 8 # 0b1000 - Approve
|
||||
# sum = 0b1111
|
||||
|
||||
class Entity(IntEnum):
|
||||
|
@ -22,8 +22,8 @@ class Permission(BaseModel):
|
|||
INTERPRETATION = 4
|
||||
COMMENT = 5
|
||||
|
||||
access = db.Column(db.Integer(), default=Access.C | Access.R | Access.U | Access.D)
|
||||
entity = db.Column(db.Enum(Entity), default=Entity.UNKNOWN)
|
||||
access = db.Column(db.Integer(), default=Access.C | Access.U | Access.D | Access.A)
|
||||
entity_type = db.Column(db.Enum(Entity), default=Entity.UNKNOWN)
|
||||
|
||||
# Relationships
|
||||
access_groups = db.relationship(
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
from app import db
|
||||
from app.models.utils import BaseModel
|
||||
|
||||
|
||||
class SectionAccessGroups(BaseModel):
|
||||
__tablename__ = "sections_access_groups"
|
||||
|
||||
# Foreign keys
|
||||
section_id = db.Column(db.Integer, db.ForeignKey("sections.id"))
|
||||
access_group_id = db.Column(db.Integer, db.ForeignKey("access_groups.id"))
|
||||
|
||||
def __repr__(self):
|
||||
return f"<s:{self.section_id} to a_g:{self.access_group_id}"
|
|
@ -26,6 +26,10 @@ class Section(BaseModel):
|
|||
interpretations = db.relationship(
|
||||
"Interpretation", viewonly=True, order_by="desc(Interpretation.id)"
|
||||
)
|
||||
access_groups = db.relationship(
|
||||
"AccessGroup",
|
||||
secondary="sections_access_groups",
|
||||
) # access_groups related to current entity
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
|
|
|
@ -1137,10 +1137,6 @@ input:checked + .toggle-bg {
|
|||
top: 2.5rem;
|
||||
}
|
||||
|
||||
.top-3 {
|
||||
top: 0.75rem;
|
||||
}
|
||||
|
||||
.top-32 {
|
||||
top: 8rem;
|
||||
}
|
||||
|
@ -1149,8 +1145,8 @@ input:checked + .toggle-bg {
|
|||
top: 11rem;
|
||||
}
|
||||
|
||||
.-z-10 {
|
||||
z-index: -10;
|
||||
.top-3 {
|
||||
top: 0.75rem;
|
||||
}
|
||||
|
||||
.z-0 {
|
||||
|
@ -1189,6 +1185,10 @@ input:checked + .toggle-bg {
|
|||
z-index: 55;
|
||||
}
|
||||
|
||||
.-z-10 {
|
||||
z-index: -10;
|
||||
}
|
||||
|
||||
.col-span-6 {
|
||||
grid-column: span 6 / span 6;
|
||||
}
|
||||
|
@ -1438,6 +1438,10 @@ input:checked + .toggle-bg {
|
|||
height: 1rem;
|
||||
}
|
||||
|
||||
.h-40 {
|
||||
height: 10rem;
|
||||
}
|
||||
|
||||
.h-5 {
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
@ -1446,18 +1450,10 @@ input:checked + .toggle-bg {
|
|||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.h-64 {
|
||||
height: 16rem;
|
||||
}
|
||||
|
||||
.h-8 {
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.h-80 {
|
||||
height: 20rem;
|
||||
}
|
||||
|
||||
.h-9 {
|
||||
height: 2.25rem;
|
||||
}
|
||||
|
@ -1483,6 +1479,14 @@ input:checked + .toggle-bg {
|
|||
height: 100vh;
|
||||
}
|
||||
|
||||
.h-64 {
|
||||
height: 16rem;
|
||||
}
|
||||
|
||||
.h-80 {
|
||||
height: 20rem;
|
||||
}
|
||||
|
||||
.max-h-40 {
|
||||
max-height: 10rem;
|
||||
}
|
||||
|
@ -1591,14 +1595,6 @@ input:checked + .toggle-bg {
|
|||
max-width: 72rem;
|
||||
}
|
||||
|
||||
.max-w-\[230\] {
|
||||
max-width: 230;
|
||||
}
|
||||
|
||||
.max-w-\[280\] {
|
||||
max-width: 280;
|
||||
}
|
||||
|
||||
.max-w-full {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
@ -1607,6 +1603,14 @@ input:checked + .toggle-bg {
|
|||
max-width: 20rem;
|
||||
}
|
||||
|
||||
.max-w-\[230\] {
|
||||
max-width: 230;
|
||||
}
|
||||
|
||||
.max-w-\[280\] {
|
||||
max-width: 280;
|
||||
}
|
||||
|
||||
.flex-1 {
|
||||
flex: 1 1 0%;
|
||||
}
|
||||
|
@ -1632,11 +1636,6 @@ input:checked + .toggle-bg {
|
|||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
}
|
||||
|
||||
.-translate-y-6 {
|
||||
--tw-translate-y: -1.5rem;
|
||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
}
|
||||
|
||||
.-translate-y-full {
|
||||
--tw-translate-y: -100%;
|
||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
|
@ -1662,6 +1661,11 @@ input:checked + .toggle-bg {
|
|||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
}
|
||||
|
||||
.-translate-y-6 {
|
||||
--tw-translate-y: -1.5rem;
|
||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
}
|
||||
|
||||
.rotate-180 {
|
||||
--tw-rotate: 180deg;
|
||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
|
@ -1773,6 +1777,10 @@ input:checked + .toggle-bg {
|
|||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.gap-1 {
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
@ -1841,12 +1849,6 @@ input:checked + .toggle-bg {
|
|||
margin-bottom: calc(2rem * var(--tw-space-y-reverse));
|
||||
}
|
||||
|
||||
.space-x-4 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-x-reverse: 0;
|
||||
margin-right: calc(1rem * var(--tw-space-x-reverse));
|
||||
margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
|
||||
}
|
||||
|
||||
.divide-y > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-divide-y-reverse: 0;
|
||||
border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
|
||||
|
@ -2119,6 +2121,11 @@ input:checked + .toggle-bg {
|
|||
background-color: rgb(200 30 30 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-sky-300 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(125 211 252 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-slate-300 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(203 213 225 / var(--tw-bg-opacity));
|
||||
|
@ -2185,6 +2192,10 @@ input:checked + .toggle-bg {
|
|||
stroke: #F05252;
|
||||
}
|
||||
|
||||
.\!p-0 {
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.p-0 {
|
||||
padding: 0px;
|
||||
}
|
||||
|
@ -2226,11 +2237,6 @@ input:checked + .toggle-bg {
|
|||
padding-right: 0px !important;
|
||||
}
|
||||
|
||||
.px-0 {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.px-2 {
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
|
@ -2286,6 +2292,11 @@ input:checked + .toggle-bg {
|
|||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.px-0 {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.pb-3 {
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
|
@ -2466,6 +2477,11 @@ input:checked + .toggle-bg {
|
|||
color: rgb(14 159 110 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-orange-500 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(255 90 31 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-red-500 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(240 82 82 / var(--tw-text-opacity));
|
||||
|
@ -2489,6 +2505,10 @@ input:checked + .toggle-bg {
|
|||
text-decoration-line: line-through;
|
||||
}
|
||||
|
||||
.\!no-underline {
|
||||
text-decoration-line: none !important;
|
||||
}
|
||||
|
||||
.opacity-0 {
|
||||
opacity: 0;
|
||||
}
|
||||
|
@ -2626,6 +2646,11 @@ input:checked + .toggle-bg {
|
|||
background-color: rgb(155 28 28 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-sky-400:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(56 189 248 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-white:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
|
@ -2680,12 +2705,6 @@ input:checked + .toggle-bg {
|
|||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.focus\:ring-0:focus {
|
||||
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
||||
}
|
||||
|
||||
.focus\:ring-2:focus {
|
||||
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||
|
@ -2698,6 +2717,12 @@ input:checked + .toggle-bg {
|
|||
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
||||
}
|
||||
|
||||
.focus\:ring-0:focus {
|
||||
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
||||
}
|
||||
|
||||
.focus\:ring-blue-100:focus {
|
||||
--tw-ring-opacity: 1;
|
||||
--tw-ring-color: rgb(225 239 254 / var(--tw-ring-opacity));
|
||||
|
|
|
@ -4,27 +4,29 @@ from app.controllers.get_or_create_permission import get_or_create_permission
|
|||
|
||||
def test_get_or_create_permission(client):
|
||||
access = m.Permission.Access
|
||||
entity = m.Permission.Entity
|
||||
entity_type = m.Permission.Entity
|
||||
|
||||
book_u: m.Permission = m.Permission.query.filter_by(
|
||||
access=access.U, entity=entity.BOOK
|
||||
access=access.U, entity_type=entity_type.BOOK
|
||||
).first()
|
||||
assert not book_u
|
||||
|
||||
assert not m.Permission.query.count()
|
||||
|
||||
book_u: m.Permission = get_or_create_permission(access=access.U, entity=entity.BOOK)
|
||||
book_u: m.Permission = get_or_create_permission(
|
||||
access=access.U, entity_type=entity_type.BOOK
|
||||
)
|
||||
assert book_u
|
||||
assert book_u.access == access.U
|
||||
assert book_u.entity == entity.BOOK
|
||||
assert book_u.entity_type == entity_type.BOOK
|
||||
assert m.Permission.query.count() == 1
|
||||
|
||||
book_u: m.Permission = m.Permission.query.filter_by(
|
||||
access=access.U, entity=entity.BOOK
|
||||
access=access.U, entity_type=entity_type.BOOK
|
||||
).first()
|
||||
assert book_u
|
||||
assert book_u.access == access.U
|
||||
assert book_u.entity == entity.BOOK
|
||||
assert book_u.entity_type == entity_type.BOOK
|
||||
|
||||
get_or_create_permission(access=access.U, entity=entity.BOOK)
|
||||
get_or_create_permission(access=access.U, entity_type=entity_type.BOOK)
|
||||
assert m.Permission.query.count() == 1
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
from app.controllers.init_access_groups import create_moderator_group
|
||||
from app import models as m
|
||||
|
||||
|
||||
def test_init_moderator_group(client):
|
||||
create_moderator_group()
|
||||
|
||||
group: m.AccessGroup = m.AccessGroup.query.filter_by(name="moderator").first()
|
||||
assert group
|
||||
assert not group.users
|
||||
assert group.permissions
|
||||
|
||||
permissions = group.permissions
|
||||
|
||||
access = m.Permission.Access
|
||||
|
||||
interpretation_DA: m.Permission = m.Permission.query.filter_by(
|
||||
access=access.D | access.A, entity_type=m.Permission.Entity.INTERPRETATION
|
||||
).first()
|
||||
assert interpretation_DA
|
||||
assert interpretation_DA in permissions
|
||||
|
||||
comment_DA: m.Permission = m.Permission.query.filter_by(
|
||||
access=access.D | access.A, entity_type=m.Permission.Entity.COMMENT
|
||||
).first()
|
||||
assert comment_DA
|
||||
assert comment_DA in permissions
|
||||
|
||||
create_moderator_group()
|
||||
groups: list[m.AccessGroup] = m.AccessGroup.query.filter_by(name="moderator").all()
|
||||
assert len(groups) == 2
|
Loading…
Reference in New Issue