diff --git a/backend/python/app/models/user.py b/backend/python/app/models/user.py index 825399c8..4c2c4480 100644 --- a/backend/python/app/models/user.py +++ b/backend/python/app/models/user.py @@ -3,8 +3,6 @@ from . import db -roles_enum = db.Enum("User", "Admin", name="roles") - class User(db.Model): __tablename__ = "users" @@ -13,7 +11,9 @@ class User(db.Model): first_name = db.Column(db.String, nullable=False) last_name = db.Column(db.String, nullable=False) auth_id = db.Column(db.String, nullable=False) - role = db.Column(roles_enum) + role = db.Column( + db.Enum("Admin", "Regular Staff", "Relief Staff", name="roles"), nullable=False + ) def to_dict(self, include_relationships=False): # define the entities table diff --git a/backend/python/app/rest/auth_routes.py b/backend/python/app/rest/auth_routes.py index 49c08f31..f30bdb68 100644 --- a/backend/python/app/rest/auth_routes.py +++ b/backend/python/app/rest/auth_routes.py @@ -77,7 +77,7 @@ def register(): Returns access token and user info in response body and sets refreshToken as an httpOnly cookie """ try: - request.json["role"] = "User" + request.json["role"] = "Relief Staff" user = CreateUserDTO(**request.json) user_service.create_user(user) auth_dto = auth_service.generate_token( diff --git a/backend/python/app/rest/entity_routes.py b/backend/python/app/rest/entity_routes.py index 0694d996..39c1ded5 100644 --- a/backend/python/app/rest/entity_routes.py +++ b/backend/python/app/rest/entity_routes.py @@ -25,9 +25,10 @@ # defines a shared URL prefix for all routes blueprint = Blueprint("entity", __name__, url_prefix="/entities") + # defines GET endpoint for retrieving all entities @blueprint.route("/", methods=["GET"], strict_slashes=False) -@require_authorization_by_role({"User", "Admin"}) +@require_authorization_by_role({"Relief Staff", "Regular Staff", "Admin"}) def get_entities(): result = entity_service.get_entities() content_type = request.mimetype @@ -40,7 +41,7 @@ def get_entities(): # defines GET endpoint for retrieving a single entity based on a provided id @blueprint.route("/", methods=["GET"], strict_slashes=False) -@require_authorization_by_role({"User", "Admin"}) +@require_authorization_by_role({"Relief Staff", "Regular Staff", "Admin"}) def get_entity(id): try: result = entity_service.get_entity(id) @@ -54,7 +55,7 @@ def get_entity(id): # define POST endpoint for creating an entity @blueprint.route("/", methods=["POST"], strict_slashes=False) -@require_authorization_by_role({"User", "Admin"}) +@require_authorization_by_role({"Relief Staff", "Regular Staff", "Admin"}) @validate_request("EntityDTO") def create_entity(): try: @@ -77,7 +78,7 @@ def create_entity(): # defines PUT endpoint for updating the entity with the provided id @blueprint.route("/", methods=["PUT"], strict_slashes=False) -@require_authorization_by_role({"User", "Admin"}) +@require_authorization_by_role({"Relief Staff", "Regular Staff", "Admin"}) @validate_request("EntityDTO") def update_entity(id): try: @@ -102,7 +103,7 @@ def update_entity(id): # defines DELETE endpoint for deleting the entity with the provided id @blueprint.route("/", methods=["DELETE"], strict_slashes=False) -@require_authorization_by_role({"User", "Admin"}) +@require_authorization_by_role({"Relief Staff", "Regular Staff", "Admin"}) def delete_entity(id): try: result = entity_service.delete_entity(id) @@ -115,7 +116,7 @@ def delete_entity(id): # defines GET endpoint for a URL to the entity's file with the provided uuid @blueprint.route("/files/", methods=["GET"], strict_slashes=False) -@require_authorization_by_role({"User", "Admin"}) +@require_authorization_by_role({"Relief Staff", "Regular Staff", "Admin"}) def get_file(id): try: file_url = file_storage_service.get_file(id) diff --git a/backend/python/app/rest/user_routes.py b/backend/python/app/rest/user_routes.py index 0ec79d5c..e69f93fb 100644 --- a/backend/python/app/rest/user_routes.py +++ b/backend/python/app/rest/user_routes.py @@ -35,7 +35,7 @@ @blueprint.route("/", methods=["GET"], strict_slashes=False) -@require_authorization_by_role({"User", "Admin"}) +@require_authorization_by_role({"Relief Staff", "Regular Staff", "Admin"}) def get_users(): """ Get all users, optionally filter by a user_id or email query parameter to retrieve a single user @@ -96,7 +96,7 @@ def get_users(): @blueprint.route("/", methods=["POST"], strict_slashes=False) -@require_authorization_by_role({"User", "Admin"}) +@require_authorization_by_role({"Admin"}) @validate_request("CreateUserDTO") def create_user(): """ @@ -113,7 +113,7 @@ def create_user(): @blueprint.route("/", methods=["PUT"], strict_slashes=False) -@require_authorization_by_role({"User", "Admin"}) +@require_authorization_by_role({"Relief Staff", "Regular Staff", "Admin"}) @validate_request("UpdateUserDTO") def update_user(user_id): """ @@ -129,7 +129,7 @@ def update_user(user_id): @blueprint.route("/", methods=["DELETE"], strict_slashes=False) -@require_authorization_by_role({"User", "Admin"}) +@require_authorization_by_role({"Admin"}) def delete_user(): """ Delete a user by user_id or email, specified through a query parameter diff --git a/backend/python/app/services/implementations/auth_service.py b/backend/python/app/services/implementations/auth_service.py index a44dde91..c0d0ac95 100644 --- a/backend/python/app/services/implementations/auth_service.py +++ b/backend/python/app/services/implementations/auth_service.py @@ -60,7 +60,7 @@ def generate_token_for_oauth(self, id_token): first_name=google_user["firstName"], last_name=google_user["lastName"], email=google_user["email"], - role="User", + role="Relief Staff", password="", ), auth_id=google_user["localId"], diff --git a/backend/python/app/static/openapi.json b/backend/python/app/static/openapi.json index 89634832..8f6103cc 100644 --- a/backend/python/app/static/openapi.json +++ b/backend/python/app/static/openapi.json @@ -727,7 +727,8 @@ "role": { "type": "string", "enum": [ - "User", + "Relief Staff", + "Regular Staff", "Admin" ] }, @@ -863,7 +864,8 @@ "role": { "type": "string", "enum": [ - "User", + "Relief Staff", + "Regular Staff", "Admin" ] }, diff --git a/backend/python/migrations/versions/56b71c6b4977_add_defined_roles_for_users.py b/backend/python/migrations/versions/56b71c6b4977_add_defined_roles_for_users.py new file mode 100644 index 00000000..6c2bff40 --- /dev/null +++ b/backend/python/migrations/versions/56b71c6b4977_add_defined_roles_for_users.py @@ -0,0 +1,40 @@ +"""add defined roles for users + +Revision ID: 56b71c6b4977 +Revises: 797bfedc3a06 +Create Date: 2023-02-05 20:15:49.674896 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = "56b71c6b4977" +down_revision = "797bfedc3a06" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column( + "users", + "role", + existing_type=postgresql.ENUM( + "Admin", "Regular Staff", "Relief Staff", name="roles" + ), + nullable=False, + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column( + "users", + "role", + existing_type=postgresql.ENUM("User", "Admin", name="roles"), + nullable=True, + ) + # ### end Alembic commands ### diff --git a/backend/python/migrations/versions/dedc5f7be10c_initial_migration.py b/backend/python/migrations/versions/dedc5f7be10c_initial_migration.py index 0e0239d4..f9eed98f 100644 --- a/backend/python/migrations/versions/dedc5f7be10c_initial_migration.py +++ b/backend/python/migrations/versions/dedc5f7be10c_initial_migration.py @@ -36,7 +36,11 @@ def upgrade(): sa.Column("first_name", sa.String(), nullable=False), sa.Column("last_name", sa.String(), nullable=False), sa.Column("auth_id", sa.String(), nullable=False), - sa.Column("role", sa.Enum("User", "Admin", name="roles"), nullable=True), + sa.Column( + "role", + sa.Enum("Admin", "Regular Staff", "Relief Staff", name="roles"), + nullable=True, + ), sa.PrimaryKeyConstraint("id"), ) # ### end Alembic commands ### diff --git a/backend/python/tests/functional/test_user_routes.py b/backend/python/tests/functional/test_user_routes.py index f48bee80..8cdc3f3c 100644 --- a/backend/python/tests/functional/test_user_routes.py +++ b/backend/python/tests/functional/test_user_routes.py @@ -23,7 +23,7 @@ "auth_id": "B", "first_name": "Hello", "last_name": "World", - "role": "User", + "role": "Relief Staff", }, ] diff --git a/backend/python/tests/functional/test_user_service.py b/backend/python/tests/functional/test_user_service.py index 563e4b02..b81a07e1 100644 --- a/backend/python/tests/functional/test_user_service.py +++ b/backend/python/tests/functional/test_user_service.py @@ -40,7 +40,7 @@ def user_service(): "auth_id": "B", "first_name": "Hello", "last_name": "World", - "role": "User", + "role": "Relief Staff", }, ) diff --git a/e2e-tests/test_auth.py b/e2e-tests/test_auth.py index 9f3b6445..e4cc4ffe 100644 --- a/e2e-tests/test_auth.py +++ b/e2e-tests/test_auth.py @@ -9,7 +9,7 @@ def register_user(backend_url, body, access_token_field): assert response.status_code == 200 data = response.json() assert "role" in data - assert data["role"] == "User" + assert data["role"] == "Relief Staff" assert "id" in data assert access_token_field in data expected = {k: v for k, v in body.items() if k != "password"} diff --git a/e2e-tests/test_user.py b/e2e-tests/test_user.py index 942b42ba..683d9f60 100644 --- a/e2e-tests/test_user.py +++ b/e2e-tests/test_user.py @@ -84,21 +84,30 @@ def test_users(backend_url, auth_header, lang, api, new_user_email): body1 = { "firstName": "Test", "lastName": "Script", - "role": "User", + "role": "Relief Staff", "email": new_user_email, "password": "password", } body2 = { - "firstName": "Test2", - "lastName": "Script2", - "role": "User", + "firstName": "Test", + "lastName": "Script", + "email": new_user_email, + "role": "Regular Staff", + } + body3 = { + "firstName": "Test", + "lastName": "Script", "email": new_user_email, + "role": "Admin", } if lang != "ts": body1 = {inflection.underscore(k): v for k, v in body1.items()} body2 = {inflection.underscore(k): v for k, v in body2.items()} + body3 = {inflection.underscore(k): v for k, v in body3.items()} user = create_user(backend_url, auth_header, body1) + + # update to Regular Staff updated_user = update_user(backend_url, auth_header, user["id"], body2) retrieved_user_by_id = get_user_by_id(backend_url, auth_header, user["id"], lang) assert updated_user == retrieved_user_by_id @@ -106,5 +115,15 @@ def test_users(backend_url, auth_header, lang, api, new_user_email): backend_url, auth_header, updated_user["email"] ) assert updated_user == retrieved_user_by_email + + # update to Admin + updated_user = update_user(backend_url, auth_header, user["id"], body3) + retrieved_user_by_id = get_user_by_id(backend_url, auth_header, user["id"], lang) + assert updated_user == retrieved_user_by_id + retrieved_user_by_email = get_user_by_email( + backend_url, auth_header, updated_user["email"] + ) + assert updated_user == retrieved_user_by_email + assert get_users(backend_url, auth_header) delete_user(backend_url, auth_header, user["id"], lang)