diff --git a/CHANGELOG.md b/CHANGELOG.md index d93b13d23..bedcb179c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,3 +37,11 @@ Please add a _short_ line describing the PR you make, if the PR implements a spe - Fix format of self deletion email ([#984](https://github.com/ScilifelabDataCentre/dds_web/pull/984)) - Add a full zero-conf development environment ([#993](https://github.com/ScilifelabDataCentre/dds_web/pull/993)) - Include frontend build in the backend production target ([#1011](https://github.com/ScilifelabDataCentre/dds_web/pull/1011)) +- Correct response about project being created when email validation fails for users ([#1014](https://github.com/ScilifelabDataCentre/dds_web/pull/1014)) +- Introduced an additional validator `dds_web.utils.contains_disallowed_characters` to fix issue [#1007](https://github.com/scilifelabdatacentre/dds_web/issues/1007) ([#1021](https://github.com/ScilifelabDataCentre/dds_web/pull/1021)). +- Fix regex for listing and deleting files [#1029](https://github.com/scilifelabdatacentre/dds_web/issues/1029) +- Hides the "Size" and "total_size" variables according to the role and project status ([#1032](https://github.com/ScilifelabDataCentre/dds_web/pull/1032)). + +## Sprint (2022-03-09 - 2022-03-23) + +- Introduce a separate error message if someone tried to add an unit user to projects individually. ([#1039](https://github.com/ScilifelabDataCentre/dds_web/pull/1039)) diff --git a/dds_web/api/api_s3_connector.py b/dds_web/api/api_s3_connector.py index c4579135c..324aec81e 100644 --- a/dds_web/api/api_s3_connector.py +++ b/dds_web/api/api_s3_connector.py @@ -9,6 +9,7 @@ import traceback import pathlib import json +import gc # Installed import botocore @@ -84,11 +85,18 @@ def get_s3_info(self): ) @bucket_must_exists - def remove_all(self, *args, **kwargs): + def remove_bucket(self, *args, **kwargs): """Removes all contents from the project specific s3 bucket.""" + # Get bucket object bucket = self.resource.Bucket(self.project.bucket) + + # Delete objects first bucket.objects.all().delete() + # Delete bucket + bucket.delete() + bucket = None + @bucket_must_exists def remove_multiple(self, items, *args, **kwargs): """Removes all with prefix.""" diff --git a/dds_web/api/files.py b/dds_web/api/files.py index 963d500d8..6b0d4d7c4 100644 --- a/dds_web/api/files.py +++ b/dds_web/api/files.py @@ -6,6 +6,7 @@ # Standard library import os +import re # Installed import flask_restful @@ -310,6 +311,8 @@ def items_in_subpath(project, folder="."): # Files have subpath "." and folders do not have child folders # Get everything in folder: # Files have subpath == folder and folders have child folders (regexp) + if folder[-1] == "/": + folder = folder[:-1] try: # All files in project files = models.File.query.filter( @@ -340,8 +343,9 @@ def items_in_subpath(project, folder="."): else: # Get distinct sub folders in specific folder with regex # Match / x number of times + re_folder = re.escape(folder) distinct_folders = ( - files.filter(models.File.subpath.regexp_match(rf"^{folder}(/[^/]+)+$")) + files.filter(models.File.subpath.regexp_match(rf"^{re_folder}(/[^/]+)+$")) .with_entities(models.File.subpath) .distinct() .all() @@ -518,6 +522,9 @@ def delete_folder(self, project, folder): """Delete all items in folder""" exists = False names_in_bucket = [] + if folder[-1] == "/": + folder = folder[:-1] + re_folder = re.escape(folder) try: # File names in root files = ( @@ -527,11 +534,12 @@ def delete_folder(self, project, folder): .filter( sqlalchemy.or_( models.File.subpath == sqlalchemy.func.binary(folder), - models.File.subpath.regexp_match(rf"^{folder}(/[^/]+)*$"), + models.File.subpath.regexp_match(rf"^{re_folder}(/[^/]+)*$"), ) ) .all() ) + except sqlalchemy.exc.SQLAlchemyError as err: raise DatabaseError(message=str(err)) diff --git a/dds_web/api/project.py b/dds_web/api/project.py index 09ec6d2ae..d0d3a41d1 100644 --- a/dds_web/api/project.py +++ b/dds_web/api/project.py @@ -5,6 +5,7 @@ #################################################################################################### # Standard Library +import http # Installed import flask_restful @@ -309,13 +310,15 @@ def get(self): "PI": p.pi, "Status": p.current_status, "Last updated": p.date_updated if p.date_updated else p.date_created, - "Size": p.size, } - # Get proj size and update total size - proj_size = p.size - total_size += proj_size - project_info["Size"] = proj_size + if ( + current_user.role == "Researcher" and p.current_status == "Available" + ) or current_user.role != "Researcher": + # Get proj size and update total size + proj_size = p.size + total_size += proj_size + project_info["Size"] = proj_size if usage: proj_bhours, proj_cost = self.project_usage(project=p) @@ -333,9 +336,11 @@ def get(self): "usage": total_bhours_db, "cost": total_cost_db, }, - "total_size": total_size, } + if total_size or current_user.role in ["Unit Admin", "Unit Personnel"]: + return_info["total_size"] = total_size + return return_info @staticmethod @@ -397,7 +402,7 @@ def delete_project_contents(project): # Delete from cloud with ApiS3Connector(project=project) as s3conn: try: - s3conn.remove_all() + s3conn.remove_bucket() except botocore.client.ClientError as err: raise DeletionError(message=str(err), project=project.public_id) @@ -426,7 +431,7 @@ def delete_project_contents(project): class CreateProject(flask_restful.Resource): - @auth.login_required(role=["Super Admin", "Unit Admin", "Unit Personnel"]) + @auth.login_required(role=["Unit Admin", "Unit Personnel"]) @logging_bind_request @json_required @handle_validation_errors @@ -459,7 +464,7 @@ def post(self): db.session.rollback() raise DatabaseError(message="Server Error: Project was not created") from err except ( - marshmallow.ValidationError, + marshmallow.exceptions.ValidationError, DDSArgumentError, AccessDeniedError, ) as err: @@ -470,10 +475,17 @@ def post(self): flask.current_app.logger.debug( f"Project {new_project.public_id} created by user {auth.current_user().username}." ) + user_addition_statuses = [] if "users_to_add" in p_info: for user in p_info["users_to_add"]: - existing_user = user_schemas.UserSchema().load(user) + try: + existing_user = user_schemas.UserSchema().load(user) + except marshmallow.exceptions.ValidationError as err: + addition_status = f"Error for {user.get('email')}: {err}" + user_addition_statuses.append(addition_status) + continue + if not existing_user: # Send invite if the user doesn't exist invite_user_result = AddUser.invite_user( @@ -481,7 +493,8 @@ def post(self): new_user_role=user.get("role"), project=new_project, ) - if invite_user_result["status"] == 200: + + if invite_user_result["status"] == http.HTTPStatus.OK: invite_msg = ( f"Invitation sent to {user['email']}. " "The user should have a valid account to be added to a project" @@ -505,7 +518,7 @@ def post(self): user_addition_statuses.append(addition_status) return { - "status": 200, + "status": http.HTTPStatus.OK, "message": f"Added new project '{new_project.title}'", "project_id": new_project.public_id, "user_addition_statuses": user_addition_statuses, diff --git a/dds_web/api/schemas/project_schemas.py b/dds_web/api/schemas/project_schemas.py index f2b927eb4..ecc38020d 100644 --- a/dds_web/api/schemas/project_schemas.py +++ b/dds_web/api/schemas/project_schemas.py @@ -66,7 +66,9 @@ class Meta: title = marshmallow.fields.String( required=True, allow_none=False, - validate=marshmallow.validate.Length(min=1), + validate=marshmallow.validate.And( + marshmallow.validate.Length(min=1), dds_web.utils.contains_disallowed_characters + ), error_messages={ "required": {"message": "Title is required."}, "null": {"message": "Title is required."}, @@ -75,7 +77,9 @@ class Meta: description = marshmallow.fields.String( required=True, allow_none=False, - validate=marshmallow.validate.Length(min=1), + validate=marshmallow.validate.And( + marshmallow.validate.Length(min=1), dds_web.utils.contains_disallowed_characters + ), error_messages={ "required": {"message": "A project description is required."}, "null": {"message": "A project description is required."}, @@ -84,7 +88,10 @@ class Meta: pi = marshmallow.fields.String( required=True, allow_none=False, - validate=marshmallow.validate.Length(min=1, max=255), + validate=marshmallow.validate.And( + marshmallow.validate.Length(min=1, max=255), + dds_web.utils.contains_disallowed_characters, + ), error_messages={ "required": {"message": "A principal investigator is required."}, "null": {"message": "A principal investigator is required."}, diff --git a/dds_web/api/user.py b/dds_web/api/user.py index 1bbf2e48b..e807ae7b3 100644 --- a/dds_web/api/user.py +++ b/dds_web/api/user.py @@ -198,7 +198,7 @@ def invite_user(email, new_user_role, project=None, unit=None): # Append invite to unit if applicable if new_invite.role in ["Unit Admin", "Unit Personnel"]: - # TODO Change / move this later. This is just so that we can add an initial unit admin. + # TODO Change / move this later. This is just so that we can add an initial Unit Admin. if auth.current_user().role == "Super Admin": if unit: unit_row = models.Unit.query.filter_by(public_id=unit).one_or_none() @@ -267,7 +267,7 @@ def add_to_project(whom, project, role, send_email=True): allowed_roles = ["Project Owner", "Researcher"] - if role not in allowed_roles or whom.role not in allowed_roles: + if role not in allowed_roles: return { "status": ddserr.AccessDeniedError.code.value, "message": ( @@ -276,6 +276,14 @@ def add_to_project(whom, project, role, send_email=True): ), } + if whom.role not in allowed_roles: + return { + "status": ddserr.AccessDeniedError.code.value, + "message": ( + "Users affiliated with units can not be added to projects individually." + ), + } + is_owner = role == "Project Owner" ownership_change = False @@ -539,7 +547,8 @@ def delete(self): class UserActivation(flask_restful.Resource): """Endpoint to reactivate/deactivate users in the system - Unit admins can reactivate/deactivate unitusers. Super admins can reactivate/deactivate any user.""" + Unit Admins can reactivate/deactivate unitusers. Super admins can reactivate/deactivate any user. + """ @auth.login_required(role=["Super Admin", "Unit Admin"]) @logging_bind_request @@ -558,7 +567,7 @@ def post(self): # Verify that the action is specified -- reactivate or deactivate action = json_input.get("action") - if action is None or action == "": + if not action: raise ddserr.DDSArgumentError( message="Please provide an action 'deactivate' or 'reactivate' for this request." ) @@ -567,11 +576,20 @@ def post(self): current_user = auth.current_user() if current_user.role == "Unit Admin": - if user.role not in ["Unit Admin", "Unit Personnel"] or current_user.unit != user.unit: + # Unit Admin can only activate/deactivate Unit Admins and personnel + if user.role not in ["Unit Admin", "Unit Personnel"]: raise ddserr.AccessDeniedError( message=( - f"You are not allowed to {action} this user. As a unit admin, " - f"you're only allowed to {action} users in your unit." + "You can only activate/deactivate users with " + "the role Unit Admin or Unit Personnel." + ) + ) + + if current_user.unit != user.unit: + raise ddserr.AccessDeniedError( + message=( + "As a Unit Admin, you can only activate/deactivate other Unit Admins or " + "Unit Personnel within your specific unit." ) ) @@ -581,7 +599,7 @@ def post(self): if (action == "reactivate" and user.is_active) or ( action == "deactivate" and not user.is_active ): - raise ddserr.DDSArgumentError(message="User is already in desired state!") + raise ddserr.DDSArgumentError(message=f"User is already {action}d!") # TODO: Check if user has lost access to any projects and if so, grant access again. if action == "reactivate": @@ -631,7 +649,7 @@ def post(self): class DeleteUser(flask_restful.Resource): """Endpoint to remove users from the system - Unit admins can delete unitusers. Super admins can delete any user.""" + Unit Admins can delete Unit Admins and Unit Personnel. Super admins can delete any user.""" @auth.login_required(role=["Super Admin", "Unit Admin"]) @logging_bind_request @@ -651,11 +669,15 @@ def delete(self): current_user = auth.current_user() if current_user.role == "Unit Admin": - if user.role not in ["Unit Admin", "Unit Personnel"] or current_user.unit != user.unit: + if user.role not in ["Unit Admin", "Unit Personnel"]: + raise ddserr.UserDeletionError( + message="You can only delete users with the role Unit Admin or Unit Personnel." + ) + if current_user.unit != user.unit: raise ddserr.UserDeletionError( message=( - "You are not allowed to delete this user. As a unit admin, " - "you're only allowed to delete users in your unit." + "As a Unit Admin, you're can only delete Unit Admins " + "and Unit Personnel within your specific unit." ) ) diff --git a/dds_web/forms.py b/dds_web/forms.py index 5b4438f0a..41f81e6b6 100644 --- a/dds_web/forms.py +++ b/dds_web/forms.py @@ -75,7 +75,7 @@ class LoginForm(flask_wtf.FlaskForm): validators=[wtforms.validators.InputRequired(), wtforms.validators.Length(1, 64)], ) password = wtforms.PasswordField("Password", validators=[wtforms.validators.InputRequired()]) - submit = wtforms.SubmitField("Login") + submit = wtforms.SubmitField("Log in") class LogoutForm(flask_wtf.FlaskForm): diff --git a/dds_web/static/js/dds.js b/dds_web/static/js/dds.js index c6b8577b9..a9643ca49 100644 --- a/dds_web/static/js/dds.js +++ b/dds_web/static/js/dds.js @@ -23,22 +23,57 @@ }); })(); -$(function () { - // Initialise Datatables - $(".datatable").DataTable(); +// +// Dark mode switch +// - // Function to switch CSS theme file - $(".theme-switcher").click(function (e) { - var theme = $("#theme-switcher-check").prop("checked") ? "dark" : "light"; - - // Switch the stylesheet - var newlink = "/static/css/dds_" + theme + ".css"; - $("#dds-stylesheet").attr("href", newlink); +// OS set to dark mode +if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) { + // Only continue if we have no cookie set + if (document.cookie.indexOf("ddstheme") == -1) { + // Set the dark mode switch to checked + document.getElementById("theme-switcher-check").checked = true; + document.getElementById("theme-switcher-sun").classList.add("d-none"); + document.getElementById("theme-switcher-moon").classList.remove("d-none"); + } +} +// OS theme changes (unlikely, but nice to support!) +window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (e) => { + const newTheme = e.matches ? "dark" : "light"; + // Only continue if we have no cookie set + if (document.cookie.indexOf("ddstheme") == -1) { + // Set the dark mode switch + if (newTheme == "dark") { + document.getElementById("theme-switcher-check").checked = true; + document.getElementById("theme-switcher-sun").classList.add("d-none"); + document.getElementById("theme-switcher-moon").classList.remove("d-none"); + } else { + document.getElementById("theme-switcher-check").checked = false; + document.getElementById("theme-switcher-sun").classList.remove("d-none"); + document.getElementById("theme-switcher-moon").classList.add("d-none"); + } + } +}); - // Toggle the button - $(".theme-switcher label i, .theme-switcher label svg").toggleClass("d-none"); +// Manually set dark / light mode +document.querySelector(".theme-switcher").addEventListener("click", (e) => { + const theme = document.getElementById("theme-switcher-check").checked ? "dark" : "light"; + // Change the CSS for the page + var newlink = "/static/css/dds_" + theme + ".css"; + document.getElementById("dds-stylesheet").setAttribute("href", newlink); + // Toggle the button + document.getElementById("theme-switcher-check").checked = theme == "dark" ? true : false; + document.getElementById("theme-switcher-sun").classList.toggle("d-none"); + document.getElementById("theme-switcher-moon").classList.toggle("d-none"); + // Set a cookie + document.cookie = "ddstheme=" + theme + "; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/"; + console.log(document.cookie); +}); - // Set a cookie to remember - document.cookie = "ddstheme=" + theme + "; expires=Thu, 2 Dec 2032 12:00:00 UTC; path=/"; - }); +// +// Legacy jQuery code +// +$(function () { + // Initialise Datatables + $(".datatable").DataTable(); }); diff --git a/dds_web/static/scss/dds_dark.scss b/dds_web/static/scss/dds_dark.scss index 5552f0a7e..840a52df5 100644 --- a/dds_web/static/scss/dds_dark.scss +++ b/dds_web/static/scss/dds_dark.scss @@ -70,3 +70,11 @@ // @import "custom"; @import "homepage"; + +// Dark-mode specific overwrites + +// Custom opacity, not set in cookies-dark-mode, not sure why +.bg-light { + --bs-bg-opacity: 0.2; + background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important; +} diff --git a/dds_web/templates/base.html b/dds_web/templates/base.html index 8f0eea065..0ff98b303 100644 --- a/dds_web/templates/base.html +++ b/dds_web/templates/base.html @@ -47,10 +47,12 @@ diff --git a/dds_web/templates/components/login_form.html b/dds_web/templates/components/login_form.html index 7f5ade9e7..c2ba0c732 100644 --- a/dds_web/templates/components/login_form.html +++ b/dds_web/templates/components/login_form.html @@ -1,20 +1,16 @@ -{% if login_error_message %} - -{% endif %} -
-

or

-
+ + {{ form.csrf_token }}
- - + {{ form.username(class="form-control" + (" is-invalid" if form.username.errors else "") + " rounded-0 shadow-none", **{"placeholder": form.username.label.text, "autocomplete": "off"}) }} + {{ form.username.label }} + {% if form.username.errors %} + {% for error in form.username.errors %} +
{{ error }}
+ {% endfor %} + {% endif %}
@@ -22,8 +18,13 @@
- - + {{ form.password(class="form-control" + (" is-invalid" if form.password.errors else "") + " rounded-0 shadow-none", **{"placeholder": form.password.label.text, "autocomplete": "off"}) }} + {{ form.password.label }} + {% if form.password.errors %} + {% for error in form.password.errors %} +
{{ error }}
+ {% endfor %} + {% endif %}
@@ -32,7 +33,7 @@ {% endif %} -

Forgot your password?

+

Forgot your password?

diff --git a/dds_web/templates/home.html b/dds_web/templates/home.html index 4c99477b3..82264d938 100644 --- a/dds_web/templates/home.html +++ b/dds_web/templates/home.html @@ -1,6 +1,16 @@ {% extends 'base.html' %} {% block body %} +{# + ######################################################### + ##### TODO: REMOVE THIS ONCE TESTING PERIOD IS OVER ##### + ######################################################### +#} +
+ For information on how to use the Data Delivery System, as well as to see a testing protocol, see + here. +
+

Welcome to the SciLifeLab Data Delivery System

diff --git a/dds_web/templates/user/login.html b/dds_web/templates/user/login.html index fcf0c1be5..a7ae9c0dd 100644 --- a/dds_web/templates/user/login.html +++ b/dds_web/templates/user/login.html @@ -6,50 +6,14 @@ {% block body %} - -

- For information on how to use the Data Delivery System, as well as - see a test protocol, go here. -

- -
- {{ form.csrf_token }} - - - {{ form.username.label }} - {{ form.username }} -
    - {% for error in form.username.errors %} -
  • - {{ error }} -
  • - {% endfor %} -
- - - {{ form.password.label }} - {{ form.password }} -
    - {% for error in form.password.errors %} -
  • - {{ error }} -
  • - {% endfor %} -
- - - - {{ form.submit }} - - - - Forgot Password? - - - -
- +

Please use the form below to log in to the SciLifeLab Data Delivery System.

+
+
+ {% include "components/login_form.html" %} +
+
+

In order to get a DDS account you need an invitation from one of the SciLifeLab units that use the DDS for data delivery. + Once you have received the invitation, you can register an account and log in to the system.

+
+
{% endblock %} diff --git a/dds_web/utils.py b/dds_web/utils.py index fc33f9dd3..9783aa453 100644 --- a/dds_web/utils.py +++ b/dds_web/utils.py @@ -60,6 +60,17 @@ def contains_digit_or_specialchar(input): ) +def contains_disallowed_characters(input): + """Inputs like <9f><98><80> cause issues in Project names etc.""" + disallowed = re.findall(r"[^\w\s]+", input) + if disallowed: + disallowed = set(disallowed) # unique values + chars = "characters" + raise marshmallow.ValidationError( + f"The {chars if len(disallowed) > 1 else chars[:-1]} '{' '.join(disallowed)}' within '[italic]{input}[/italic]' {'are' if len(disallowed) > 1 else 'is'} not allowed." + ) + + def email_not_taken(input): """Validator - verify that email is not taken. diff --git a/dds_web/web/root.py b/dds_web/web/root.py index 25b9ea4cc..b3a221042 100644 --- a/dds_web/web/root.py +++ b/dds_web/web/root.py @@ -5,6 +5,7 @@ """ from flask import Blueprint, render_template, jsonify from flask import current_app as app +from dds_web import forms pages = Blueprint("pages", __name__) @@ -13,7 +14,8 @@ @pages.route("/", methods=["GET"]) def home(): """Home page.""" - return render_template("home.html") + form = forms.LoginForm() + return render_template("home.html", form=form) @pages.route("/status") diff --git a/tests/test_flow_invite_to_access.py b/tests/test_flow_invite_to_access.py index f3d3d08ab..3153d445d 100644 --- a/tests/test_flow_invite_to_access.py +++ b/tests/test_flow_invite_to_access.py @@ -335,7 +335,7 @@ def test_invite_to_register_researcher_with_project_as_owner_by_project_owner(cl assert user.project_user_keys[0].project_id == project.id -# Unituser invited by unit admin ################################# Unituser invited by unit admin # +# Unituser invited by Unit Admin ################################# Unituser invited by Unit Admin # def test_invite_to_register_unituser_by_unitadmin(client): diff --git a/tests/test_project_access.py b/tests/test_project_access.py index 92a46860d..f66b2354e 100644 --- a/tests/test_project_access.py +++ b/tests/test_project_access.py @@ -119,7 +119,7 @@ def test_fix_access_projectowner_valid_email_invalid_otheruser(client): def test_fix_access_unituser_valid_email_invalid_otheruser(client): - """Unit user giving access to unit admin - no permissions.""" + """Unit user giving access to Unit Admin - no permissions.""" token = tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client) response = client.post( tests.DDSEndpoint.PROJECT_ACCESS, @@ -132,7 +132,7 @@ def test_fix_access_unituser_valid_email_invalid_otheruser(client): def test_fix_access_user_trying_themselves(client): - """Unit user giving access to unit admin - no permissions.""" + """Unit user giving access to Unit Admin - no permissions.""" token = tests.UserAuth(tests.USER_CREDENTIALS["projectowner"]).token(client) response = client.post( tests.DDSEndpoint.PROJECT_ACCESS, @@ -309,7 +309,7 @@ def test_fix_access_unitpersonnel_valid_email_unitpersonnel(client): def test_fix_access_unitadmin_valid_email_researcher(client): - """Unit admin giving access to researcher - ok.""" + """Unit Admin giving access to researcher - ok.""" project = models.Project.query.filter_by(public_id="public_project_id").one_or_none() assert project @@ -345,7 +345,7 @@ def test_fix_access_unitadmin_valid_email_researcher(client): def test_fix_access_unitadmin_valid_email_projectowner(client): - """Unit admin giving access to project owner - ok.""" + """Unit Admin giving access to project owner - ok.""" project = models.Project.query.filter_by(public_id="public_project_id").one_or_none() assert project @@ -381,7 +381,7 @@ def test_fix_access_unitadmin_valid_email_projectowner(client): def test_fix_access_unitadmin_valid_email_unituser(client): - """Unit admin giving access to unituser - ok.""" + """Unit Admin giving access to unituser - ok.""" project = models.Project.query.filter_by(public_id="public_project_id").one_or_none() assert project diff --git a/tests/test_project_user_keys.py b/tests/test_project_user_keys.py index 45438dfcc..81d2e748d 100644 --- a/tests/test_project_user_keys.py +++ b/tests/test_project_user_keys.py @@ -254,7 +254,7 @@ def test_project_key_sharing(client): def test_delete_user_deletes_project_user_keys(client): - """Unit admin deletes unit user""" + """Unit Admin deletes unit user""" email_to_delete = "unituser2@mailtrap.io" diff --git a/tests/test_user_activation.py b/tests/test_user_activation.py index 3823f1b7c..ee80f1d1e 100644 --- a/tests/test_user_activation.py +++ b/tests/test_user_activation.py @@ -71,7 +71,7 @@ def test_deactivate_deactivated_user_as_superadmin(module_client): json={**user, "action": "deactivate"}, ) assert response.status_code == http.HTTPStatus.BAD_REQUEST - assert "User is already in desired state!" in response.json["message"] + assert "User is already deactivated!" in response.json["message"] def test_reactivate_user_as_superadmin(module_client): @@ -104,7 +104,7 @@ def test_reactivate_user_as_superadmin(module_client): def test_deactivate_user_as_unitadmin(module_client): - """Deactivate researchuser as unit admin""" + """Deactivate researchuser as Unit Admin""" # Try to get token as user that is to be deactivated response = module_client.get( tests.DDSEndpoint.ENCRYPTED_TOKEN, @@ -120,13 +120,13 @@ def test_deactivate_user_as_unitadmin(module_client): ) assert response.status_code == http.HTTPStatus.FORBIDDEN assert ( - "You are not allowed to deactivate this user. As a unit admin, you're only allowed to deactivate users in your unit." + "You can only activate/deactivate users with the role Unit Admin or Unit Personnel" in response.json["message"] ) def test_deactivate_unituser_as_unitadmin(module_client): - """Deactivate unit user as unit admin""" + """Deactivate unit user as Unit Admin""" # Try to get token as user that is to be deactivated response = module_client.get( tests.DDSEndpoint.ENCRYPTED_TOKEN, @@ -167,7 +167,7 @@ def test_deactivate_unituser_as_unitadmin(module_client): def test_reactivate_unituser_as_unitadmin(module_client): - """Reactivate unituser as unit admin""" + """Reactivate unituser as Unit Admin""" # Try to get token as user that is to be deactivated response = module_client.get( tests.DDSEndpoint.ENCRYPTED_TOKEN, diff --git a/tests/test_user_delete.py b/tests/test_user_delete.py index b04dbdb02..fa7e35ba2 100644 --- a/tests/test_user_delete.py +++ b/tests/test_user_delete.py @@ -193,7 +193,7 @@ def test_del_request_others_unprivileged(client): def test_del_request_others_researcher(client): - """Unit admin tries to delete research user""" + """Unit Admin tries to delete research user""" email_to_delete = "researchuser@mailtrap.io" @@ -213,7 +213,7 @@ def test_del_request_others_researcher(client): def test_del_request_others_researcher(client): - """Unit admin tries to delete unit user from different unit""" + """Unit Admin tries to delete unit user from different unit""" email_to_delete = "unituser1@mailtrap.io" @@ -233,7 +233,7 @@ def test_del_request_others_researcher(client): def test_del_request_others_self(client): - """Unit admin tries to instantly self-delete via this endpoint""" + """Unit Admin tries to instantly self-delete via this endpoint""" email_to_delete = "delete_me_unitadmin@mailtrap.io" @@ -253,7 +253,7 @@ def test_del_request_others_self(client): def test_del_request_others_success(client): - """Unit admin deletes unit user""" + """Unit Admin deletes unit user""" email_to_delete = "delete_me_unituser@mailtrap.io" @@ -271,7 +271,7 @@ def test_del_request_others_success(client): def test_del_request_others_superaction(client): - """Super admin deletes unit admin""" + """Super admin deletes Unit Admin""" email_to_delete = "delete_me_unitadmin@mailtrap.io" diff --git a/tests/test_user_info.py b/tests/test_user_info.py index 15e4fa39b..d5e8b8bbf 100644 --- a/tests/test_user_info.py +++ b/tests/test_user_info.py @@ -8,7 +8,7 @@ def test_get_info_unit_user(client): - """Get info for unit user/unit admin""" + """Get info for unit user/Unit Admin""" response = client.get( tests.DDSEndpoint.USER_INFO,