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 %}
-
-
- Oops! Something went wrong.
-
-
{{ login_error_message }}
-
-{% endif %}
-
-
or
-
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.
+
- For information on how to use the Data Delivery System, as well as
- see a test protocol, go here.
-
-
-
-
+
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,