Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removed is_teacher and is_admin, changed to role enum #127

Merged
merged 10 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions backend/db_construct.sql
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
CREATE TYPE role AS ENUM ('STUDENT', 'TEACHER', 'ADMIN');

CREATE TABLE users (
uid VARCHAR(255),
is_teacher BOOLEAN,
is_admin BOOLEAN,
role role NOT NULL,
PRIMARY KEY(uid)
);

Expand Down
36 changes: 12 additions & 24 deletions backend/project/endpoints/index/OpenAPI_Object.json
Original file line number Diff line number Diff line change
Expand Up @@ -1374,14 +1374,11 @@
"uid": {
"type": "string"
},
"is_teacher": {
"type": "boolean"
},
"is_admin": {
"type": "boolean"
"role": {
"type": "enum"
}
},
"required": ["uid", "is_teacher", "is_admin"]
"required": ["uid", "role"]
}
}
}
Expand All @@ -1399,14 +1396,11 @@
"uid": {
"type": "string"
},
"is_teacher": {
"type": "boolean"
},
"is_admin": {
"type": "boolean"
"role": {
"type": "enum"
}
},
"required": ["uid", "is_teacher", "is_admin"]
"required": ["uid", "role"]
}
}
}
Expand Down Expand Up @@ -1451,14 +1445,11 @@
"uid": {
"type": "string"
},
"is_teacher": {
"type": "boolean"
},
"is_admin": {
"type": "boolean"
"role": {
"type": "enum"
}
},
"required": ["uid", "is_teacher", "is_admin"]
"required": ["uid", "role"]
}
}
}
Expand Down Expand Up @@ -1487,14 +1478,11 @@
"schema": {
"type": "object",
"properties": {
"is_teacher": {
"type": "boolean"
},
"is_admin": {
"type": "boolean"
"role": {
"type": "role"
}
},
"required": ["is_teacher", "is_admin"]
"required": ["role"]
}
}
}
Expand Down
31 changes: 11 additions & 20 deletions backend/project/endpoints/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,10 @@ def get(self):
"""
try:
query = userModel.query
is_teacher = request.args.get('is_teacher')
is_admin = request.args.get('is_admin')
role = request.args.get("role")

if is_teacher is not None:
query = query.filter(userModel.is_teacher == (is_teacher.lower() == 'true'))

if is_admin is not None:
query = query.filter(userModel.is_admin == (is_admin.lower() == 'true'))
if role is not None:
query = query.filter(userModel.role == role.lower())

users = query.all()

Expand All @@ -54,25 +50,23 @@ def post(self):
It should create a new user and return a success message.
"""
uid = request.json.get('uid')
is_teacher = request.json.get('is_teacher')
is_admin = request.json.get('is_admin')
role = request.args.get("role")

if is_teacher is None or is_admin is None or uid is None:
if role is None or uid is None:
return {
"message": "Invalid request data!",
"correct_format": {
"uid": "User ID (string)",
"is_teacher": "Teacher status (boolean)",
"is_admin": "Admin status (boolean)"
"role": "User role (string)"
},"url": f"{API_URL}/users"
}, 400
try:
user = db.session.get(userModel, uid)
if user is not None:
# bad request, error code could be 409 but is rarely used
return {"message": f"User {uid} already exists"}, 400
# Code to create a new user in the database using the uid, is_teacher, and is_admin
new_user = userModel(uid=uid, is_teacher=is_teacher, is_admin=is_admin)
# Code to create a new user in the database using the uid and role
new_user = userModel(uid=uid, role=role)
db.session.add(new_user)
db.session.commit()
return jsonify({"message": "User created successfully!",
Expand Down Expand Up @@ -114,17 +108,14 @@ def patch(self, user_id):
dict: A dictionary containing the message indicating the success
or failure of the update.
"""
is_teacher = request.json.get('is_teacher')
is_admin = request.json.get('is_admin')
role = request.args.get("role")
try:
user = db.session.get(userModel, user_id)
if user is None:
return {"message": "User not found!","url": f"{API_URL}/users"}, 404

if is_teacher is not None:
user.is_teacher = is_teacher
if is_admin is not None:
user.is_admin = is_admin
if role is not None:
user.role = role

# Save the changes to the database
db.session.commit()
Expand Down
15 changes: 10 additions & 5 deletions backend/project/models/user.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
"""User model"""

from enum import Enum
from dataclasses import dataclass
from sqlalchemy import Boolean, Column, String
from sqlalchemy import Column, String, Enum as EnumField
from project.db_in import db

class Role(Enum):
"""This class defines the roles of a user"""
STUDENT = 0
TEACHER = 1
ADMIN = 2

@dataclass
class User(db.Model):
"""This class defines the users table,
a user has a uid,
is_teacher and is_admin booleans because a user
a user has a uid and a role, a user
can be either a student,admin or teacher"""

__tablename__ = "users"
uid: str = Column(String(255), primary_key=True)
is_teacher: bool = Column(Boolean)
is_admin: bool = Column(Boolean)
role: Role = Column(EnumField(Role), nullable=False)
12 changes: 7 additions & 5 deletions backend/project/utils/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from project import db

from project.models.user import User
from project.models.user import User,Role
from project.models.course import Course
from project.models.project import Project
from project.models.submission import Submission
Expand Down Expand Up @@ -62,13 +62,15 @@ def return_authenticated_user_id():

if user:
return auth_user_id
is_teacher = False

# Use the Enum here
role = Role.STUDENT
if user_info["jobTitle"] != None:
is_teacher = True
role = Role.TEACHER

# add user if not yet in database
try:
new_user = User(uid=auth_user_id, is_teacher=is_teacher, is_admin=False)
new_user = User(uid=auth_user_id, role=role)
db.session.add(new_user)
db.session.commit()
except SQLAlchemyError:
Expand All @@ -89,7 +91,7 @@ def is_teacher(auth_user_id):
"url": f"{API_URL}/users"}, 500
if not user: # should realistically never happen
abort(500, "A database error occured")
if user.is_teacher:
if user.role == Role.TEACHER:
return True
return False

Expand Down
10 changes: 5 additions & 5 deletions backend/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from project.sessionmaker import engine, Session
from project.db_in import db
from project.models.course import Course
from project.models.user import User
from project.models.user import User,Role
from project.models.project import Project
from project.models.course_relation import CourseStudent,CourseAdmin
from project.models.submission import Submission
Expand Down Expand Up @@ -34,10 +34,10 @@ def db_session():
def users():
"""Return a list of users to populate the database"""
return [
User(uid="brinkmann", is_admin=True, is_teacher=True),
User(uid="laermans", is_admin=True, is_teacher=True),
User(uid="student01", is_admin=False, is_teacher=False),
User(uid="student02", is_admin=False, is_teacher=False)
User(uid="brinkmann", role=Role.ADMIN),
User(uid="laermans", role=Role.ADMIN),
User(uid="student01", role=Role.STUDENT),
User(uid="student02", role=Role.STUDENT)
]

def courses():
Expand Down
18 changes: 9 additions & 9 deletions backend/tests/endpoints/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from zoneinfo import ZoneInfo
import pytest
from sqlalchemy import create_engine
from project.models.user import User
from project.models.user import User,Role
from project.models.course import Course
from project.models.course_share_code import CourseShareCode
from project import create_app_with_db
Expand Down Expand Up @@ -46,7 +46,7 @@ def valid_user():
"""
return {
"uid": "w_student",
"is_teacher": False
"role": Role.STUDENT
}

@pytest.fixture
Expand All @@ -73,10 +73,10 @@ def valid_user_entries(session):
Returns a list of users that are in the database
"""
users = [
User(uid="del", is_admin=False, is_teacher=True),
User(uid="pat", is_admin=False, is_teacher=True),
User(uid="u_get", is_admin=False, is_teacher=True),
User(uid="query_user", is_admin=True, is_teacher=False)]
User(uid="del", role=Role.TEACHER),
User(uid="pat", role=Role.TEACHER),
User(uid="u_get", role=Role.TEACHER),
User(uid="query_user", role=Role.ADMIN)]

session.add_all(users)
session.commit()
Expand Down Expand Up @@ -127,7 +127,7 @@ def app():
@pytest.fixture
def course_teacher_ad():
"""A user that's a teacher for testing"""
ad_teacher = User(uid="Gunnar", is_teacher=True, is_admin=True)
ad_teacher = User(uid="Gunnar", role=Role.TEACHER)
return ad_teacher

@pytest.fixture
Expand Down Expand Up @@ -177,7 +177,7 @@ def client(app):
@pytest.fixture
def valid_teacher_entry(session):
"""A valid teacher for testing that's already in the db"""
teacher = User(uid="Bart", is_teacher=True)
teacher = User(uid="Bart", role=Role.TEACHER)
session.add(teacher)
session.commit()
return teacher
Expand All @@ -204,7 +204,7 @@ def valid_course_entry(session, valid_course):
def valid_students_entries(session):
"""Valid students for testing that are already in the db"""
students = [
User(uid=f"student_sel2_{i}", is_teacher=False)
User(uid=f"student_sel2_{i}", role=Role.STUDENT)
for i in range(3)
]
session.add_all(students)
Expand Down
37 changes: 21 additions & 16 deletions backend/tests/endpoints/user_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import pytest
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from project.models.user import User
from project.models.user import User,Role
from project.db_in import db
from tests import db_url

Expand All @@ -24,12 +24,12 @@ def user_db_session():
db.metadata.create_all(engine)
session = Session()
session.add_all(
[User(uid="del", is_admin=False, is_teacher=True),
User(uid="pat", is_admin=False, is_teacher=True),
User(uid="u_get", is_admin=False, is_teacher=True),
User(uid="query_user", is_admin=True, is_teacher=False)
]
)
[User(uid="del", role=Role.TEACHER),
User(uid="pat", role=Role.TEACHER),
User(uid="u_get", role=Role.TEACHER),
User(uid="query_user", role=Role.ADMIN)
]
)
session.commit()
yield session
session.rollback()
Expand Down Expand Up @@ -117,37 +117,42 @@ def test_get_one_user_wrong_authentication(self, client, valid_user_entry):
def test_patch_user(self, client, valid_user_entry):
"""Test updating a user."""

new_is_teacher = not valid_user_entry.is_teacher
if valid_user_entry.role == Role.TEACHER:
new_role = Role.ADMIN
if valid_user_entry.role == Role.ADMIN:
new_role = Role.STUDENT
else:
new_role = Role.TEACHER

response = client.patch(f"/users/{valid_user_entry.uid}", json={
'is_teacher': new_is_teacher,
'is_admin': not valid_user_entry.is_admin
'role': new_role
})
assert response.status_code == 403 # Patching a user is never necessary and thus not allowed

def test_patch_non_existent(self, client):
"""Test updating a non-existent user."""
response = client.patch("/users/-20", json={
'is_teacher': False,
'is_admin': True
'role': Role.TEACHER
})
assert response.status_code == 403 # Patching is not allowed

def test_patch_non_json(self, client, valid_user_entry):
"""Test sending a non-JSON patch request."""
valid_user_form = asdict(valid_user_entry)
valid_user_form["is_teacher"] = not valid_user_form["is_teacher"]
if valid_user_form["role"] == Role.TEACHER:
valid_user_form["role"] = Role.STUDENT
else:
valid_user_form["role"] = Role.TEACHER
response = client.patch(f"/users/{valid_user_form['uid']}", data=valid_user_form)
assert response.status_code == 403 # Patching is not allowed

def test_get_users_with_query(self, client, valid_user_entries):
"""Test getting users with a query."""
# Send a GET request with query parameters, this is a nonsense entry but good for testing
response = client.get("/users?is_admin=true&is_teacher=false", headers={"Authorization":"teacher1"})
response = client.get("/users?role=admin", headers={"Authorization":"teacher1"})
assert response.status_code == 200

# Check that the response contains only the user that matches the query
users = response.json["data"]
for user in users:
assert user["is_admin"] is True
assert user["is_teacher"] is False
assert user["role"] == Role.ADMIN
Loading
Loading