diff --git a/backend/migrations/0029_serviceuser_agreed_privacy_policy.py b/backend/migrations/0029_serviceuser_agreed_privacy_policy.py new file mode 100644 index 00000000..d09c0570 --- /dev/null +++ b/backend/migrations/0029_serviceuser_agreed_privacy_policy.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.15 on 2023-02-22 10:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('backend', '0028_project_uuid'), + ] + + operations = [ + migrations.AddField( + model_name='serviceuser', + name='agreed_privacy_policy', + field=models.BooleanField(default=False), + ), + ] diff --git a/backend/models.py b/backend/models.py index ee452c42..cbcc7749 100644 --- a/backend/models.py +++ b/backend/models.py @@ -53,6 +53,7 @@ class ServiceUser(AbstractUser): receive_mail_notifications = models.BooleanField(default=True) doc_format_pref = models.IntegerField(choices=UserDocumentFormatPreference.USER_DOC_FORMAT_PREF, default=UserDocumentFormatPreference.JSON) + agreed_privacy_policy = models.BooleanField(default=False) @property def has_active_project(self): diff --git a/backend/rpc.py b/backend/rpc.py index d4981312..22e8bd26 100644 --- a/backend/rpc.py +++ b/backend/rpc.py @@ -3,6 +3,7 @@ import datetime import json +import os from urllib.parse import urljoin from django.conf import settings from django.contrib.auth import authenticate, get_user_model, login as djlogin, logout as djlogout @@ -26,7 +27,7 @@ from backend.rpcserver import rpc_method, rpc_method_auth, rpc_method_manager, rpc_method_admin from backend.models import Project, Document, DocumentType, Annotation, AnnotatorProject, AnnotationChangeHistory, \ UserDocumentFormatPreference -from backend.utils.misc import get_value_from_key_path, insert_value_to_key_path +from backend.utils.misc import get_value_from_key_path, insert_value_to_key_path, read_custom_document from backend.utils.serialize import ModelSerializer log = logging.getLogger(__name__) @@ -98,9 +99,10 @@ def register(request, payload): username = payload.get("username") password = payload.get("password") email = payload.get("email") + agreed_privacy_policy = True if not get_user_model().objects.filter(username=username).exists(): - user = get_user_model().objects.create_user(username=username, password=password, email=email) + user = get_user_model().objects.create_user(username=username, password=password, email=email, agreed_privacy_policy=agreed_privacy_policy) _generate_user_activation(user) djlogin(request, user) context["username"] = payload["username"] @@ -941,6 +943,31 @@ def admin_update_user_password(request, username, password): user.save() +################################## +### Privacy Policy/T&C Methods ### +################################## + +@rpc_method +def get_privacy_policy_details(request): + + details = settings.PRIVACY_POLICY + + custom_docs = { + 'CUSTOM_PP_DOCUMENT': read_custom_document(settings.CUSTOM_PP_DOCUMENT_PATH) if os.path.isfile(settings.CUSTOM_PP_DOCUMENT_PATH) else None, + 'CUSTOM_TC_DOCUMENT': read_custom_document(settings.CUSTOM_TC_DOCUMENT_PATH) if os.path.isfile(settings.CUSTOM_TC_DOCUMENT_PATH) else None + } + + details.update(custom_docs) + + url = { + 'URL': request.headers['Host'] + } + + details.update(url) + + return details + + ############################### ### Utility Methods ### ############################### diff --git a/backend/tests/test_models.py b/backend/tests/test_models.py index f895a463..016989fa 100644 --- a/backend/tests/test_models.py +++ b/backend/tests/test_models.py @@ -23,6 +23,10 @@ def check_model_fields(self, model_class, field_name_types_dict): class TestUserModel(TestCase): + def test_agree_privacy_policy(self): + user = get_user_model().objects.create(username="test1", agreed_privacy_policy=True) + self.assertTrue(user.agreed_privacy_policy) + def test_document_association_check(self): user = get_user_model().objects.create(username="test1") user2 = get_user_model().objects.create(username="test2") diff --git a/backend/utils/misc.py b/backend/utils/misc.py index 7cd91153..d50c686b 100644 --- a/backend/utils/misc.py +++ b/backend/utils/misc.py @@ -39,3 +39,13 @@ def insert_value_to_key_path(obj_dict, key_path, value, delimiter="."): return True return False + + +def read_custom_document(path): + """ + Reads in a text file and returns as a string. + Primarily used for reading in custom privacy policy and/or terms & conditions documents. + """ + with open(path) as file: + doc_str = file.read() + return doc_str \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 746a030e..9b3a896c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,6 +15,14 @@ services: - SUPERUSER_USERNAME - SUPERUSER_PASSWORD - SUPERUSER_EMAIL + - PP_HOST_NAME + - PP_HOST_ADDRESS + - PP_HOST_CONTACT + - PP_ADMIN_NAME + - PP_ADMIN_ADDRESS + - PP_ADMIN_CONTACT + volumes: + - ./custom-policies:/app/custom-policies/ depends_on: - db diff --git a/docs/docs/developerguide/README.md b/docs/docs/developerguide/README.md index 60aaa6da..d0853340 100644 --- a/docs/docs/developerguide/README.md +++ b/docs/docs/developerguide/README.md @@ -120,7 +120,7 @@ To run separately: ``` ## Deployment using Docker -Deployment is via [docker-compose](https://docs.docker.com/compose/), using [NGINX](https://www.nginx.com/) to serve static content, a separate [postgreSQL](https://hub.docker.com/_/postgres) service containing the database and a database backup service (see `docker-compose.yml` for details). +Teamware can be deployed via [docker-compose](https://docs.docker.com/compose/), using [NGINX](https://www.nginx.com/) to serve static content, a separate [postgreSQL](https://hub.docker.com/_/postgres) service containing the database and a database backup service (see `docker-compose.yml` for details). 1. Run `./generate-docker-env.sh` to create a `.env` file containing randomly generated secrets which are mounted as environment variables into the container. See [below](#env-config) for details. @@ -218,6 +218,20 @@ email: # You will also need to set user and passwordSecret if your # mail server requires authentication +privacyPolicy: +# Contact details of the host and administrator of the teamware instance, if no admin defined, defaults to the host values. + host: + # Name of the host + name: "Service Host" + # Host's physical address + address: "123 Example Street, City. Country." + # A method of contacting the host, field supports HTML for e.g. linking to a form + contact: "Email" + admin: + name: "Dr. Service Admin" + address: "Department of Example Studies, University of Example, City. Country." + contact: "Email" + backend: # Name of the random secret you created above djangoSecret: django-secret @@ -304,3 +318,46 @@ This package includes the script linked in the documentation above, which simpli DJANGO_GMAIL_API_CLIENT_SECRET='google_assigned_secret' DJANGO_GMAIL_API_REFRESH_TOKEN='google_assigned_token' ``` + + +#### Teamware Privacy Policy and Terms & Conditions + +Teamware includes a default privacy policy and terms & conditions, which are required for running the application. + +The default privacy policy is intended to be compliant with UK GDPR regulations, which may comply with the rights of users of your deployment, however it is your responsibility to ensure that this is the case. + +If the default privacy policy covers your use case, then you will need to include configuration for a few contact details. + +Contact details are required for the **host** and the **administrator**: the **host** is the organisation or individual responsible for managing the deployment of the teamware instance and the **administrator** is the organisation or individual responsible for managing users, projects and data on the instance. In many cases these roles will be filled by the same organisation or individual, so in this case specifying just the **host** details is sufficient. + +For deployment from source, set the following environment variables: + +* `PP_HOST_NAME` +* `PP_HOST_ADDRESS` +* `PP_HOST_CONTACT` +* `PP_ADMIN_NAME` +* `PP_ADMIN_ADDRESS` +* `PP_ADMIN_CONTACT` + +For deployment using docker-compose, set these values in `.env`. + +If the host and administrator are the same, you can just set the `PP_HOST_*` variables above which will be used for both. + +##### Including a custom Privacy Policy and/or Terms & Conditions + +If the default privacy policy or terms & conditions do not cover your use case, you can easily replace these with your own documents. + +If deploying from source, include markdown (`.md`) files in a `custom-policies` directory in the project root with the exact names `custom-policies/privacy-policy.md` and/or `custom-policies/terms-and-conditions.md` which will be rendered at the corresponding pages on the running web app. If you are not familiar with the Markdown language there are a number of free WYSIWYG-style editor tools available including [StackEdit](https://stackedit.io/app) (browser based) and [Zettlr](https://www.zettlr.com) (desktop app). + +If deploying with docker compose, place the `custom-policies` directory at the same location as the `docker-compose.yml` file before running `./deploy.sh` as above. + +An example custom privacy policy file contents might look like: + +```md +# Organisation X Teamware Privacy Policy +... +... +## Definitions of Roles and Terminology +... +... +``` \ No newline at end of file diff --git a/frontend/src/AnnotationApp.vue b/frontend/src/AnnotationApp.vue index fa13225d..2f0f0cf9 100644 --- a/frontend/src/AnnotationApp.vue +++ b/frontend/src/AnnotationApp.vue @@ -2,14 +2,16 @@
Last updated: {{ lastUpdated }}
+ ++ For the purposes of this privacy policy the following roles are defined. Note that in some cases, the Host and Administrator (and/or the Project Manager) may constitute the same individual or organisation. + +
+
+ ++ We collect the following information: +
+ Under the UK General Data Protection Regulation (UK GDPR), the lawful bases we rely on for processing this information are: +
+ ++ When you register for a {{ productName }} account, {{ productName }} collects your username, email address and password which are used to: + allow you to log in and use {{ productName }} securely; verify your identity before using the service; contact you if required regarding + your data, account or updates to the terms of service. + + When project managers and/or administrators upload documents for annotation and annotations are made, + data is stored for the purposes of managing annotation tasks and providing {{ productName }}’s functionality. +
+ ++ When you use {{ productName }} the host stores your IP address in the form of request logs, which are used to monitor and diagnose any issues with the web service. + Such logs are not shared except if necessary to comply with a legal obligation, and are deleted once they are no longer required, typically within a few weeks of their creation. + + When {{ productName }} projects are either completed or deleted, aggregated, anonymous data including the number of documents, + number of annotators, number of annotations are sent to the developers of {{ productName }}, GATE. {{ productName }} is provided as free, + open source software and collecting usage statistics allows GATE to demonstrate its usage to funders, + allowing us to support its development and maintenance. +
+ ++ + Your information is securely stored. + + Data is kept within a {{ productName }} instance as long as the instance is running; or until it is deleted. + Once an instance is no longer running, data will be kept for a duration according to the agreement between the host and administrator. +
+ +
+ Under data protection law, you have rights including:
+
+ Your right of access - You have the right to ask the host or administrator(s) for copies of your personal information.
+
+ Your right to rectification - You have the right to ask the host or administrator(s) to rectify personal information you think is inaccurate.
+ You also have the right to ask the host or administrator(s) to complete information you think is incomplete.
+
+ Your right to erasure - You have the right to ask the host or administrator(s) to erase your personal information in certain circumstances.
+ Please use the facilities within {{ productName }} to delete your account if required, at which point any annotations or documents will remain, but your account will be anonymised.
+
+ Your right to restriction of processing - You have the right to ask us to restrict the processing of your personal information in certain circumstances.
+
+ Your right to object to processing - You have the right to object to the processing of your personal information in certain circumstances.
+
+ Your right to data portability - You have the right to ask that we transfer the personal information you gave us to another organisation, or to you, in certain circumstances.
+
+ You are not required to pay any charge for exercising your rights. If you make a request, we have one month to respond to you.
+
+ Please contact the host and/or administrators at the contact details above if you wish to make a request.
+
+
+ If you have any concerns about the use of your personal information in {{ productName }},
+ you can make a complaint to the host and or administrator at the contact details above.
+ You can also complain to the Information Commissioner’s Office (ICO) if you are unhappy with how your data is used.
+
+ The ICO’s address:
+ Information Commissioner’s Office
+ Wycliffe House
+ Water Lane
+ Wilmslow
+ Cheshire
+ SK9 5AF
+ United Kingdom
+
+ Helpline number: 0303 123 1113
+ ICO website: https://www.ico.org.uk
+
+
By registering to use Teamware, you confirm that you are over 18 years of age and have read and agreed to Teamware's privacy policy and terms & conditions.
+
+ Last updated: {{ lastUpdated }}
+
+ These terms and conditions (the “Terms and Conditions”) govern the use of {{ productName }}. {{ productName }} is owned and operated by the Host on behalf of the Administrator. {{ productName }} is a service.
+
+ By using {{ productName }}, you indicate that you have read and understand these Terms and Conditions and agree to abide by them at all times.
+
+
+
+ As a user of {{ productName }}, you agree to use {{ productName }} legally, not to use {{ productName }} for illegal purposes, and not to:
+
+
+ Users may post the following information on {{ productName }}: +
+ When you create a {{ productName }} account, you agree to the following:
+
+ You are solely responsible for your account and the security and privacy of your account, including passwords or sensitive information attached to that account; and
+ All personal information you provide to use through your account is up to date, accurate, and truthful and that you will update your personal information if it changes.
+
+ The Host reserves the right to suspend or terminate your account if you are using {{ productName }} illegally or if you violate these Terms and Conditions.
+
+
+ Except where prohibited by law, by using {{ productName }} you indemnify and hold harmless the Host and our directors, officers, agents, employees, subsidiaries, and affiliates from any actions, claims, losses, damages, liabilities and expenses including legal fees arising out of your use of {{ productName }} or your violation of these Terms and Conditions. +
++ These Terms and Conditions are governed by the laws of the Country of England. +
++ If at any time any of the provisions set forth in these Terms and Conditions are found to be inconsistent of invalid under applicable laws, those provisions will be deemed void and will be removed from these Terms and Conditions. All other provisions will not be affected by the removal and the rest of these Terms and Conditions will still be considered valid. +
++ These Terms and Conditions may be amended from time to time in order to maintain compliance with the law and to reflect any changes to the way we operate {{ productName }} and the way we expect users to behave using {{ productName }}. We will notify users by email of changes to these Terms and Conditions or post a notice on the site. +
+
+ Please contact us if you have any questions or concerns. Our contact details are as follows:
+
+