Commit 2825888b authored by Barbora Kompišová's avatar Barbora Kompišová
Browse files

Permissions skeleton

parent 196cffb0
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -64,6 +64,10 @@ class User(db.Model, EntityBase):
                result.append(role.course)
        return result

    def get_roles_in_course(self, course):
        return Role.query.filter_by(course=course) \
               .join(Role.users).filter(User.id == self.id).all()

    def __init__(self, uco, email, username, is_admin=False) -> None:
        self.uco = uco
        self.email = email
+3 −2
Original line number Diff line number Diff line
@@ -57,10 +57,11 @@ class UnauthorizedError(PortalAPIError):

class ForbiddenError(PortalAPIError):
    # could use a resource identification (like 404)
    def __init__(self, uid, note=None):
    def __init__(self, uid=None, note=None):
        user_message = f"Forbidden for user: {uid}!" if uid else "Forbidden action."
        message = dict(
            uid=uid,
            message=f"Forbidden for user: {uid}!",
            message=user_message,
        )
        if note:
            message['note'] = note
+85 −0
Original line number Diff line number Diff line
from portal.database.models import User, Course, Component, Role
from portal.service.errors import ForbiddenError


def require_user(user: User, course: Course, permissions: list):
    if not check_user(user, course=course, permissions=permissions):
        note = f"Course: {course.id}[{course.codename}]; Permissions: {permissions}"
        raise ForbiddenError(uid=user.id, note=note)


def require_sysadmin(user: User):
    if not check_sysadmin(user):
        note = f"User is not sysadmin!"
        raise ForbiddenError(uid=user.id, note=note)


def check_component(component: Component, course: Course, permissions: list) -> bool:
    """Checks component permissions for a course

    Args:
        component(Component): Component instance
        course(Course): Course instance
        permissions(list): List of permissions required

    Returns(bool): True if checks has passed

    """
    # TODO
    return True


def check_user(user: User, course: Course, permissions: list) -> bool:
    """Checks a user's permissions based on his roles in the course

    Args:
        user(User): User instance
        course(Course): Course instance
        permissions(list): Permissions list

    Returns(bool): True if checks pass

    """
    if check_sysadmin(user):
        return True
    roles = user.get_roles_in_course(course=course)

    return all(__evaluate_permission_for_roles(roles, perm) for perm in permissions)


def check_sysadmin(user: User) -> bool:
    """Checks whether the user is sysadmin

    Args:
        user(User): User instance

    Returns(bool): True if checks pass
    """
    return user.is_admin


def __evaluate_permission_for_role(role: Role, permission: str, default=False) -> bool:
    """Evaluates permission for a role

    If the permission doesn't exist, False is returned.
    Args:
        role(Role): the role to check
        permission(str): Permission name
        default(bool): Default behaviour when the permission does not exist

    Returns(bool): True if permission is True in the role

    """
    return getattr(role, permission, default=default)


def __evaluate_permission_for_roles(roles: list, permission: str) -> bool:
    """Evaluates permissions for all a list of roles
    Args:
        roles(list): Role list
        permission(str): Permission name

    Returns(bool): True if any of the roles allows the permission

    """
    return any(__evaluate_permission_for_role(role, permission) for role in roles)
+4 −3
Original line number Diff line number Diff line
@@ -49,11 +49,12 @@ def create_groups(db):


def create_submissions(db):
    user = User.query.filter_by(username="xfoo").first()
    sub = Submission(user=user, project=Project.query.filter_by(name="p1").first(), parameters="")
    xfoo = User.query.filter_by(username="xfoo").first()
    xbar = User.query.filter_by(username="xbar").first()
    sub = Submission(user=xfoo, project=Project.query.filter_by(name="p1").first(), parameters="")
    sub.state = SubmissionState.IN_PROGRESS
    db.session.add(sub)
    db.session.add(Submission(user=user, project=Project.query.filter_by(name="p1").first(), parameters=""))
    db.session.add(Submission(user=xbar, project=Project.query.filter_by(name="p1").first(), parameters=""))
    db.session.commit()


+30 −2
Original line number Diff line number Diff line
@@ -845,10 +845,38 @@ def test_user_submission_review(session):
    assert submission in user.submissions
    review = Review(submission=submission)

    ri1 = ReviewItem(user, review, "foo", 10)
    ri2 = ReviewItem(user, review, "bar", 1)
    ri1 = ReviewItem(user, review, "foo", 10, content="something")
    ri2 = ReviewItem(user, review, "bar", 1, content="something else")
    session.flush()

    assert ri1 in user.review_items
    assert ri2 in user.review_items


def test_user_roles_for_course(session):
    user = User(uco=123, email='foo', username='xfoo')
    course = Course(name="C++", codename="PB161")
    c_java = Course(name="Java", codename="PB162")

    teacher_cpp = Role(course=course, name='teacher')
    lect_cpp = Role(course=course, name='lecturer')
    student_java = Role(course=c_java, name='student')

    user.roles.append(teacher_cpp)
    user.roles.append(lect_cpp)
    user.roles.append(student_java)

    session.add(user)
    session.flush()

    assert len(user.roles) == 3

    cpp_roles = user.get_roles_in_course(course=course)
    assert len(cpp_roles) == 2
    assert teacher_cpp in cpp_roles
    assert lect_cpp in cpp_roles

    java_roles = user.get_roles_in_course(course=c_java)
    assert len(java_roles) == 1
    assert student_java in java_roles
Loading