diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b39a4655c..a22e13198 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,18 @@ Changelog ========== +.. _2.6.4: + +2.6.4 - 2023-04-10 +~~~~~~~~~~~~~~~~~~~~~~~ + +- New features: + - Fix the files endpoint acording to openAPI standar . + - Added email contact to troubleshouting page +- Dependencies: + - `jwcrypto` from `1.5.1` to `1.5.6` +- Update base image for the docker containers from `python:3.10-alpine` to `python:3.11-alpine` + .. _2.6.3: 2.6.3 - 2023-02-27 diff --git a/Dockerfiles/backend.Dockerfile b/Dockerfiles/backend.Dockerfile index d8027f638..5f013b78e 100644 --- a/Dockerfiles/backend.Dockerfile +++ b/Dockerfiles/backend.Dockerfile @@ -3,7 +3,7 @@ ############################# # Set official image -- parent image -FROM python:3.10-alpine as base +FROM python:3.11-alpine as base ARG USERNAME=dds-user ARG USER_UID=1001 diff --git a/Dockerfiles/cli.Dockerfile b/Dockerfiles/cli.Dockerfile index c43a04c8b..195b7ead6 100644 --- a/Dockerfiles/cli.Dockerfile +++ b/Dockerfiles/cli.Dockerfile @@ -1,5 +1,5 @@ # Set official image -FROM python:3.10-alpine as base +FROM python:3.11-alpine as base # Update and upgrade RUN apk update && apk upgrade diff --git a/SPRINTLOG.md b/SPRINTLOG.md index ee960953a..38230474f 100644 --- a/SPRINTLOG.md +++ b/SPRINTLOG.md @@ -344,6 +344,8 @@ _Nothing merged in CLI during this sprint_ - Minor update jinja2 package to address cve ([#1503](https://github.com/ScilifelabDataCentre/dds_web/pull/1503)) - Minor update jwcrypto package to address cve ([#1504](https://github.com/ScilifelabDataCentre/dds_web/pull/1504)) +# 2023-01-15 - 2024-01-25 + # 2024-01-15 - 2024-01-26 - Document Superadmin endpoints ([#1507](https://github.com/ScilifelabDataCentre/dds_web/pull/1507)) @@ -367,3 +369,16 @@ _Nothing merged in CLI during this sprint_ - Add link in footer for new User Agreement and Privacy Policy ([#1516](https://github.com/ScilifelabDataCentre/dds_web/pull/1516)) - New extra release, outside maintenance window, version 2.6.3 ([#1518](https://github.com/ScilifelabDataCentre/dds_web/pull/1518)) + +# 2024-03-11 - 2024-03-22 + +- Fix the files endpoints according to the openAPI standards, providing new endpoint version that co-exists with the current one ([#1505](https://github.com/ScilifelabDataCentre/dds_web/pull/1505)) +- Added email to troubleshouting webpage, with obfuscation ([#1520](https://github.com/ScilifelabDataCentre/dds_web/pull/1520)) + +# 2024-03-25 - 2024-04-5 + +- Update base image and packages to address cve in docker containers ([#1523](https://github.com/ScilifelabDataCentre/dds_web/pull/1523)) + +# 2024-04-8 - 2024-04-19 + +- New version: 2.6.4 ([#1526](https://github.com/ScilifelabDataCentre/dds_web/pull/1526)) diff --git a/dds_web/__init__.py b/dds_web/__init__.py index 56cf43f10..cb63b3faa 100644 --- a/dds_web/__init__.py +++ b/dds_web/__init__.py @@ -194,6 +194,10 @@ def prepare(): # Verify cli version compatible if "api/v1" in flask.request.path: verify_cli_version(version_cli=flask.request.headers.get("X-Cli-Version")) + elif "api/v3" in flask.request.path: # experimental v3 version + # If version api is not provided, it gets the data from the __version__ file + # When v3 is finallized, this should be removed and the __version__ file should be updated + pass # Get message of the day flask.g.motd = get_active_motds() @@ -309,28 +313,44 @@ def dds_version_filter(_): import dds_web.security.auth # Register blueprints - from dds_web.api import api_blueprint + from dds_web.api import api_blueprint, api_blueprint_v3 from dds_web.web.root import pages from dds_web.web.user import auth_blueprint from flask_swagger_ui import get_swaggerui_blueprint - # url for the documentation - SWAGGER_URL = "/documentation" - # path where the swagger file is localted in the repository - API_URL = "/static/swagger.yaml" + # base url for the api documentation + SWAGGER_URL_1 = "/api/documentation/v1" + SWAGGER_URL_3 = "/api/documentation/v3" - # register blueprint for the documentation - swagger_ui_blueprint = get_swaggerui_blueprint( - SWAGGER_URL, - API_URL, + # path where the swagger file(s) are localted in the repository + API_URL_V1 = "/static/swagger.yaml" + API_URL_V3 = "/static/swaggerv3.yaml" + + # register blueprint(s) for the documentation + swagger_ui_blueprint_v1 = get_swaggerui_blueprint( + SWAGGER_URL_1, + API_URL_V1, + config={ + "app_name": "DDS API Documentation", + "defaultModelsExpandDepth": -1, + "layout": "BaseLayout", + }, + ) + swagger_ui_blueprint_v3 = get_swaggerui_blueprint( + SWAGGER_URL_3, + API_URL_V3, config={ "app_name": "DDS API Documentation", "defaultModelsExpandDepth": -1, "layout": "BaseLayout", }, ) - app.register_blueprint(swagger_ui_blueprint, url_prefix=SWAGGER_URL) + + # two documentation and api versions, v3 will contain the new endpoints fixed + app.register_blueprint(swagger_ui_blueprint_v1, url_prefix=SWAGGER_URL_1, name="v1") + app.register_blueprint(swagger_ui_blueprint_v3, url_prefix=SWAGGER_URL_3, name="v3") app.register_blueprint(api_blueprint, url_prefix="/api/v1") + app.register_blueprint(api_blueprint_v3, url_prefix="/api/v3") app.register_blueprint(pages, url_prefix="") app.register_blueprint(auth_blueprint, url_prefix="") diff --git a/dds_web/api/__init__.py b/dds_web/api/__init__.py index afdd0a467..fd5a6796e 100644 --- a/dds_web/api/__init__.py +++ b/dds_web/api/__init__.py @@ -22,8 +22,12 @@ api_blueprint = flask.Blueprint("api_blueprint", __name__) api = flask_restful.Api(api_blueprint) +api_blueprint_v3 = flask.Blueprint("api_blueprint_v3", __name__) +api_v3 = flask_restful.Api(api_blueprint_v3) + @api.representation("application/json") +@api_v3.representation("application/json") def output_json(data, code, headers=None): resp = flask.make_response(flask.json.dumps(data), code) resp.headers.extend(headers or {}) @@ -33,66 +37,74 @@ def output_json(data, code, headers=None): #################################################################################################### # RESOURCES ############################################################################ RESOURCES # #################################################################################################### - -# Login/access ###################################################################### Login/access # -api.add_resource(user.EncryptedToken, "/user/encrypted_token", endpoint="encrypted_token") -api.add_resource(user.SecondFactor, "/user/second_factor", endpoint="second_factor") - -# S3 ########################################################################################## S3 # -api.add_resource(s3.S3Info, "/s3/proj", endpoint="proj_s3_info") - -# Files #################################################################################### Files # -api.add_resource(files.NewFile, "/file/new", endpoint="new_file") -api.add_resource(files.MatchFiles, "/file/match", endpoint="match_files") -api.add_resource(files.ListFiles, "/files/list", endpoint="list_files") -api.add_resource(files.RemoveFile, "/file/rm", endpoint="remove_file") -api.add_resource(files.RemoveDir, "/file/rmdir", endpoint="remove_dir") -api.add_resource(files.FileInfo, "/file/info", endpoint="file_info") -api.add_resource(files.FileInfoAll, "/file/all/info", endpoint="all_file_info") -api.add_resource(files.UpdateFile, "/file/update", endpoint="update_file") -api.add_resource(files.AddFailedFiles, "/file/failed/add", endpoint="add_failed_files") - -# Projects ############################################################################## Projects # -api.add_resource(project.UserProjects, "/proj/list", endpoint="list_projects") -api.add_resource(project.RemoveContents, "/proj/rm", endpoint="remove_contents") -api.add_resource(project.GetPublic, "/proj/public", endpoint="public_key") -api.add_resource(project.GetPrivate, "/proj/private", endpoint="private_key") -api.add_resource(project.CreateProject, "/proj/create", endpoint="create_project") -api.add_resource(project.ProjectUsers, "/proj/users", endpoint="list_project_users") -api.add_resource(project.ProjectStatus, "/proj/status", endpoint="project_status") -api.add_resource(project.ProjectAccess, "/proj/access", endpoint="project_access") -api.add_resource(project.ProjectBusy, "/proj/busy", endpoint="project_busy") -api.add_resource(project.ProjectInfo, "/proj/info", endpoint="project_info") - -# User management ################################################################ User management # -api.add_resource(user.RetrieveUserInfo, "/user/info", endpoint="user_info") -api.add_resource(user.AddUser, "/user/add", endpoint="add_user") -api.add_resource(user.DeleteUser, "/user/delete", endpoint="delete_user") -api.add_resource(user.DeleteUserSelf, "/user/delete_self", endpoint="delete_user_self") -api.add_resource(user.RemoveUserAssociation, "/user/access/revoke", endpoint="revoke_from_project") -api.add_resource(user.UserActivation, "/user/activation", endpoint="user_activation") -api.add_resource( - user.RequestHOTPActivation, "/user/hotp/activate", endpoint="request_hotp_activation" -) -api.add_resource( - user.RequestTOTPActivation, "/user/totp/activate", endpoint="request_totp_activation" -) -api.add_resource(user.Users, "/users", endpoint="users") -api.add_resource(user.InvitedUsers, "/user/invites", endpoint="list_invites") - -# Super Admins ###################################################################### Super Admins # - -api.add_resource(superadmin_only.MaintenanceMode, "/maintenance", endpoint="maintenance") -api.add_resource(superadmin_only.AllUnits, "/unit/info/all", endpoint="all_units") -api.add_resource(superadmin_only.MOTD, "/motd", endpoint="motd") -api.add_resource(superadmin_only.SendMOTD, "/motd/send", endpoint="send_motd") -api.add_resource(superadmin_only.FindUser, "/user/find", endpoint="find_user") -api.add_resource( - superadmin_only.ResetTwoFactor, "/user/totp/deactivate", endpoint="reset_user_hotp" -) -api.add_resource(superadmin_only.AnyProjectsBusy, "/proj/busy/any", endpoint="projects_busy_any") -api.add_resource(superadmin_only.Statistics, "/stats", endpoint="stats") -api.add_resource(superadmin_only.UnitUserEmails, "/user/emails", endpoint="user_emails") - -# Invoicing ############################################################################ Invoicing # -api.add_resource(user.ShowUsage, "/usage", endpoint="usage") +def add_resources(api): + # Login/access ###################################################################### Login/access # + api.add_resource(user.EncryptedToken, "/user/encrypted_token", endpoint="encrypted_token") + api.add_resource(user.SecondFactor, "/user/second_factor", endpoint="second_factor") + + # S3 ########################################################################################## S3 # + api.add_resource(s3.S3Info, "/s3/proj", endpoint="proj_s3_info") + + # Files #################################################################################### Files # + api.add_resource(files.NewFile, "/file/new", endpoint="new_file") + api.add_resource(files.MatchFiles, "/file/match", endpoint="match_files") + api.add_resource(files.ListFiles, "/files/list", endpoint="list_files") + api.add_resource(files.RemoveFile, "/file/rm", endpoint="remove_file") + api.add_resource(files.RemoveDir, "/file/rmdir", endpoint="remove_dir") + api.add_resource(files.FileInfo, "/file/info", endpoint="file_info") + api.add_resource(files.FileInfoAll, "/file/all/info", endpoint="all_file_info") + api.add_resource(files.UpdateFile, "/file/update", endpoint="update_file") + api.add_resource(files.AddFailedFiles, "/file/failed/add", endpoint="add_failed_files") + + # Projects ############################################################################## Projects # + api.add_resource(project.UserProjects, "/proj/list", endpoint="list_projects") + api.add_resource(project.RemoveContents, "/proj/rm", endpoint="remove_contents") + api.add_resource(project.GetPublic, "/proj/public", endpoint="public_key") + api.add_resource(project.GetPrivate, "/proj/private", endpoint="private_key") + api.add_resource(project.CreateProject, "/proj/create", endpoint="create_project") + api.add_resource(project.ProjectUsers, "/proj/users", endpoint="list_project_users") + api.add_resource(project.ProjectStatus, "/proj/status", endpoint="project_status") + api.add_resource(project.ProjectAccess, "/proj/access", endpoint="project_access") + api.add_resource(project.ProjectBusy, "/proj/busy", endpoint="project_busy") + api.add_resource(project.ProjectInfo, "/proj/info", endpoint="project_info") + + # User management ################################################################ User management # + api.add_resource(user.RetrieveUserInfo, "/user/info", endpoint="user_info") + api.add_resource(user.AddUser, "/user/add", endpoint="add_user") + api.add_resource(user.DeleteUser, "/user/delete", endpoint="delete_user") + api.add_resource(user.DeleteUserSelf, "/user/delete_self", endpoint="delete_user_self") + api.add_resource( + user.RemoveUserAssociation, "/user/access/revoke", endpoint="revoke_from_project" + ) + api.add_resource(user.UserActivation, "/user/activation", endpoint="user_activation") + api.add_resource( + user.RequestHOTPActivation, "/user/hotp/activate", endpoint="request_hotp_activation" + ) + api.add_resource( + user.RequestTOTPActivation, "/user/totp/activate", endpoint="request_totp_activation" + ) + api.add_resource(user.Users, "/users", endpoint="users") + api.add_resource(user.InvitedUsers, "/user/invites", endpoint="list_invites") + + # Super Admins ###################################################################### Super Admins # + + api.add_resource(superadmin_only.MaintenanceMode, "/maintenance", endpoint="maintenance") + api.add_resource(superadmin_only.AllUnits, "/unit/info/all", endpoint="all_units") + api.add_resource(superadmin_only.MOTD, "/motd", endpoint="motd") + api.add_resource(superadmin_only.SendMOTD, "/motd/send", endpoint="send_motd") + api.add_resource(superadmin_only.FindUser, "/user/find", endpoint="find_user") + api.add_resource( + superadmin_only.ResetTwoFactor, "/user/totp/deactivate", endpoint="reset_user_hotp" + ) + api.add_resource( + superadmin_only.AnyProjectsBusy, "/proj/busy/any", endpoint="projects_busy_any" + ) + api.add_resource(superadmin_only.Statistics, "/stats", endpoint="stats") + api.add_resource(superadmin_only.UnitUserEmails, "/user/emails", endpoint="user_emails") + + # Invoicing ############################################################################ Invoicing # + api.add_resource(user.ShowUsage, "/usage", endpoint="usage") + + +add_resources(api) +add_resources(api_v3) diff --git a/dds_web/api/files.py b/dds_web/api/files.py index 2a5dddb68..cb190cedd 100644 --- a/dds_web/api/files.py +++ b/dds_web/api/files.py @@ -204,10 +204,52 @@ class MatchFiles(flask_restful.Resource): @auth.login_required(role=["Unit Admin", "Unit Personnel"]) @logging_bind_request - @json_required @handle_validation_errors def get(self): """Get name in bucket for all files specified.""" + + if "api/v1" in flask.request.path: + # requests comming from api/v1 should be handled as before + return self.old_get() + + elif "api/v3" in flask.request.path: + # Verify project ID and access + project = project_schemas.ProjectRequiredSchema().load(flask.request.args) + + # Verify project has correct status for upload + check_eligibility_for_upload(status=project.current_status) + + # Get files from request + files = flask.request.args.getlist("files") + if not files: + raise DDSArgumentError("No files specified.") + + try: + matching_files = ( + models.File.query.filter(models.File.name.in_(files)) + .filter(models.File.project_id == sqlalchemy.func.binary(project.id)) + .all() + ) + except (sqlalchemy.exc.SQLAlchemyError, sqlalchemy.exc.OperationalError) as err: + raise DatabaseError( + message=str(err), + alt_message=f"Failed to get matching files in db" + + ( + ": Database malfunction." + if isinstance(err, sqlalchemy.exc.OperationalError) + else "." + ), + ) from err + + # The files checked are not in the db + if not matching_files or matching_files is None: + return {"files": None} + + return {"files": {x.name: x.name_in_bucket for x in matching_files}} + + @json_required + def old_get(self): + """Implementation of old get method. Should be removed when api/v1 is removed.""" # Verify project ID and access project = project_schemas.ProjectRequiredSchema().load(flask.request.args) @@ -247,6 +289,65 @@ class ListFiles(flask_restful.Resource): @handle_validation_errors def get(self): """Get a list of files within the specified folder.""" + + if "api/v1" in flask.request.path: + # requests comming from api/v1 should be handled as before + return self.old_get() + + elif "api/v3" in flask.request.path: + # Verify project ID and access + project = project_schemas.ProjectRequiredSchema().load(flask.request.args) + + if auth.current_user().role == "Researcher" and project.current_status == "In Progress": + raise AccessDeniedError( + message="The project status must be 'Available'. Please wait for unit to release the data." + ) + + # Check if to return file size + show_size = flask.request.args.get("show_size") + + # Check if to get from root or folder + subpath = "." + if flask.request.args.get("subpath"): + subpath = flask.request.args.get("subpath").rstrip(os.sep) + + files_folders = list() + + # Check project not empty + if project.num_files == 0: + return {"num_items": 0, "message": f"The project {project.public_id} is empty."} + + # Get files and folders + distinct_files, distinct_folders = self.items_in_subpath( + project=project, folder=subpath + ) + + # Collect file and folder info to return to CLI + if distinct_files: + for x in distinct_files: + info = { + "name": x[0] if subpath == "." else x[0].split(os.sep)[-1], + "folder": False, + } + if show_size: + info.update({"size": float(x[1])}) + files_folders.append(info) + if distinct_folders: + for x in distinct_folders: + info = { + "name": x if subpath == "." else x.split(os.sep)[-1], + "folder": True, + } + + if show_size: + folder_size = self.get_folder_size(project=project, folder_name=x) + info.update({"size": float(folder_size)}) + files_folders.append(info) + + return {"files_folders": files_folders} + + def old_get(self): + """Implementation of old get method. Should be removed when api/v1 is removed.""" # Verify project ID and access project = project_schemas.ProjectRequiredSchema().load(flask.request.args) @@ -406,10 +507,35 @@ class RemoveFile(flask_restful.Resource): @auth.login_required(role=["Unit Admin", "Unit Personnel"]) @logging_bind_request - @json_required @handle_validation_errors def delete(self): """Delete file(s).""" + + if "api/v1" in flask.request.path: + # requests comming from api/v1 should be handled as before + return self.old_delete() + + elif "api/v3" in flask.request.path: + # Verify project ID and access + project = project_schemas.ProjectRequiredSchema().load(flask.request.args) + + # Verify project status ok for deletion + check_eligibility_for_deletion( + status=project.current_status, has_been_available=project.has_been_available + ) + + # Delete file(s) from db and cloud + not_removed_dict, not_exist_list = self.delete_multiple( + project=project, files=flask.request.args.getlist("files") + ) + + # Return deleted and not deleted files + return {"not_removed": not_removed_dict, "not_exists": not_exist_list} + + @json_required + def old_delete(self): + """Implementation of old get method. Should be removed when api/v1 is removed.""" + # Verify project ID and access project = project_schemas.ProjectRequiredSchema().load(flask.request.args) @@ -522,10 +648,79 @@ class RemoveDir(flask_restful.Resource): @auth.login_required(role=["Unit Admin", "Unit Personnel"]) @logging_bind_request - @json_required @handle_validation_errors def delete(self): """Delete folder(s).""" + + if "api/v1" in flask.request.path: + # requests comming from api/v1 should be handled as before + return self.old_delete() + + elif "api/v3" in flask.request.path: + # Verify project ID and access + project = project_schemas.ProjectRequiredSchema().load(flask.request.args) + # Verify project status ok for deletion + check_eligibility_for_deletion( + status=project.current_status, has_been_available=project.has_been_available + ) + + # Remove folder(s) + not_removed, not_exist = ({}, []) + fail_type = None + with ApiS3Connector(project=project) as s3conn: + for folder_name in flask.request.args.getlist("folders"): + # Get all files in the folder + files = self.get_files_for_deletion(project=project, folder=folder_name) + if not files: + not_exist.append(folder_name) + continue + + # S3 can only delete 1000 files per request + # The deletion will thus be divided into batches of at most 1000 files + batch_size: int = 1000 + for i in range(0, len(files), batch_size): + # Delete from s3 + bucket_names = tuple( + entry.name_in_bucket for entry in files[i : i + batch_size] + ) + try: + s3conn.remove_multiple(items=bucket_names, batch_size=batch_size) + except botocore.client.ClientError as err: + not_removed[folder_name] = str(err) + fail_type = "s3" + break + + # Commit to db if no error so far + try: + self.queue_file_entry_deletion(files[i : i + batch_size]) + project.date_updated = dds_web.utils.current_time() + db.session.commit() + except ( + sqlalchemy.exc.SQLAlchemyError, + sqlalchemy.exc.OperationalError, + ) as err: + db.session.rollback() + flask.current_app.logger.error( + "Files deleted in S3 but not in db. The entries must be synchronised! " + f"Error: {str(err)}" + ) + not_removed[folder_name] = "Could not remove files in folder" + ( + ": Database malfunction." + if isinstance(err, sqlalchemy.exc.OperationalError) + else "." + ) + fail_type = "db" + break + + return { + "not_removed": not_removed, + "fail_type": fail_type, + "not_exists": not_exist, + "nr_deleted": len(files) if not not_removed else i, + } + + @json_required + def old_delete(self): # Verify project ID and access project = project_schemas.ProjectRequiredSchema().load(flask.request.args) # Verify project status ok for deletion @@ -636,10 +831,41 @@ class FileInfo(flask_restful.Resource): @auth.login_required(role=["Unit Admin", "Unit Personnel", "Project Owner", "Researcher"]) @logging_bind_request - @json_required @handle_validation_errors def get(self): """Checks which files can be downloaded, and get their info.""" + + if "api/v1" in flask.request.path: + # requests comming from api/v1 should be handled as before + return self.old_get() + + elif "api/v3" in flask.request.path: + # Verify project ID and access + project = project_schemas.ProjectRequiredSchema().load(flask.request.args) + + # Verify project status ok for download + user_role = auth.current_user().role + check_eligibility_for_download(status=project.current_status, user_role=user_role) + + # Get project contents + input_ = { + "project": project.public_id, + **{"requested_items": flask.request.args.getlist("files"), "url": True}, + } + ( + found_files, + found_folder_contents, + not_found, + ) = project_schemas.ProjectContentSchema().dump(input_) + + return { + "files": found_files, + "folder_contents": found_folder_contents, + "not_found": not_found, + } + + @json_required + def old_get(self): # Verify project ID and access project = project_schemas.ProjectRequiredSchema().load(flask.request.args) diff --git a/dds_web/static/swagger.yaml b/dds_web/static/swagger.yaml index f77ae651e..f5856e15b 100644 --- a/dds_web/static/swagger.yaml +++ b/dds_web/static/swagger.yaml @@ -2,22 +2,44 @@ # yamllint disable rule:braces openapi: 3.0.0 info: - description: | - Documentation for the API of the Data Delivery System (DDS) in SciLifeLab - version: "2.6.0" + description: | # html syntax is allowed + Documentation for the API of the Data Delivery System (DDS) in SciLifeLab Data Centre.
+ This documentation is mainly intended for internal use. But developers and researchers + can use it at their own risk. + However, DC recomends to always use the client and tools provided to units and researchers.
+

+ WARNING: Some endpoints are not according to the OpenAPI standard, and therefore + cannot be documented properly. These endpoints are marked with CHECK METHOD and as deprecated.
+ These endpoints will be changed in the future, and will be documented properly in the url hosted in + api/documentation/v3
+
+ For interaction with the API resources:
+ 1. Change the server in the list below depending on the instance you are using, + and click on the "Authorize" button to enter your user and password.
+ 2. Use the endpoint /user/encrypted_token to generate an encrypted token for the user.
+ 3. Add this returned token in the second section, secondFactorToken (http, Bearer) + of the same "Authorize" form used before.
+ 4. Call the endpoint /user/second_factor to authenticate with MFA. This will return a new token.
+ 5. Lastly, go back to the "Authorize" form, click "Logout" at the secondFactorToken (http, Bearer) part, + and use the second token from the previous step to login again.
+ + Now you can start using the API!
+
+ NOTE: The encrypted token is valid for 7 days.
+ version: "version 2" title: Data Delivery System # Apply the encrypted token to all operations security: - - seconFactorToken: [] + - secondFactorToken: [] servers: # Added by API Auto Mocking Plugin - url: https://delivery.scilifelab.se/api/v1 - description: Endpoint for the production enviroment + description: Endpoint for the production server - url: https://dds-dev.dckube3.scilifelab.se/api/v1 - description: Endpoint for the development enviroment + description: Endpoint for the test server # Uncomment when local testing - - url: http://localhost:5000/api/v1 - description: Local enviroment +# - url: http://localhost:5000/api/v1 +# description: Local enviroment tags: - name: authentication description: Authorization and authentication operations @@ -37,7 +59,10 @@ paths: post: tags: - files - summary: Add a new file to the database + summary: Add the information of a new file to the database. + description: Adds the metadata of a file to the DB.
+ This endpoints needs to be called AFTER uploading the actual file to the bucket.
+ To do that you need to get the s3 keys in the /s3/proj endpoint operationId: newFile parameters: - $ref: "#/components/parameters/defaultHeader" @@ -62,6 +87,8 @@ paths: tags: - files summary: Update an existing file + description: Updates the metadata of a file in the DB. The name will be used + to find the file. The rest of the fields will be updated. operationId: updateFile parameters: - $ref: "#/components/parameters/defaultHeader" @@ -162,11 +189,15 @@ paths: delete: tags: - files - summary: Deletes a file CHECK METHOD - description: This method requires the data + summary: Deletes a file or several files metadata from the database AND the s3 bucket. CHECK METHOD + description: Deletes the file from the DB and bucket.
+ In contrast with the /file/new endpoint there is no extra operation with the s3 keys needed. +
+ This method requires the data to be passed in the request body instead of the query. Since this does not comply with the openAPI standards, swagger cannot document it properly, - therefore we need to change/remove it in the future. + therefore we need to change/remove it in the future.

+ Deletes the file from the DB and bucket, there is no extra operation with the s3 keys needed. deprecated: true operationId: removeFile parameters: @@ -188,7 +219,9 @@ paths: "500": $ref: "#/components/responses/InternalServerlError" "200": - description: successful operation + description: Successful operation, this means that the backend didn't encounted + any errors. But the file may not have been deleted from the database. If this happens, it will + be indicated in the response content: application/json: schema: @@ -203,11 +236,15 @@ paths: delete: tags: - files - summary: Deletes folder(s) CHECK METHOD - description: This method requires the data + summary: Removes one or more full directories metadata from the database AND the s3 bucket CHECK METHOD + description: Deletes the directory from the DB and bucket.
+ In contrast with the /file/new endpoint there is no extra operation with the s3 keys needed. +
+ This method requires the data to be passed in the request body instead of the query. Since this does not comply with the openAPI standards, swagger cannot document it properly, - therefore we need to change/remove it in the future. + therefore we need to change/remove it in the future.

+ Deletes the file from the DB and bucket, there is no extra operation with the s3 keys needed. deprecated: true operationId: removeDir parameters: @@ -229,7 +266,9 @@ paths: "500": $ref: "#/components/responses/InternalServerlError" "200": - description: successful operation + description: Successful operation, this means that the backend didn't encounted + any errors. But the file may not have been deleted from the database. If this happens, it will + be indicated in the response content: application/json: schema: @@ -247,7 +286,9 @@ paths: tags: - files summary: Get file info on files to download CHECK METHOD - description: This method requires the data + description: Checks which files can be downloaded, and get their info. +
+ This method requires the data to be passed in the request body instead of the query. Since this does not comply with the openAPI standards, swagger cannot document it properly, therefore we need to change/remove it in the future. @@ -279,19 +320,16 @@ paths: type: object properties: files: - type: array - items: - $ref: "#/components/schemas/Files" + $ref: "#/components/schemas/FilesInfo" + folder_contents: + example: {} + not_found: + example: {} /file/all/info: get: tags: - files - summary: Get info on all project files CHECK METHOD - description: This method requires the data - to be passed in the request body instead of the query. - Since this does not comply with the openAPI standards, swagger cannot document it properly, - therefore we need to change/remove it in the future. - deprecated: true + summary: Get info on all project files operationId: fileInfoAll parameters: - $ref: "#/components/parameters/defaultHeader" @@ -311,18 +349,17 @@ paths: type: object properties: files: - type: array - items: - $ref: "#/components/schemas/Files" + $ref: "#/components/schemas/FilesInfo" /file/update: put: tags: - files - summary: Update file info after download + summary: Get info on all project files + description: After downloading a file, that event has to be registered using this endpoint parameters: - $ref: "#/components/parameters/defaultHeader" - $ref: "#/components/parameters/projectID" - operationId: updateFile2 + operationId: updateFile responses: "405": description: Invalid input @@ -347,6 +384,8 @@ paths: tags: - files summary: Get files from log file and save to database + description: If adding database entries failed for some files, the information from the log is + passed to this endpoint to allow for a new database update attempt. parameters: - $ref: "#/components/parameters/defaultHeader" - $ref: "#/components/parameters/projectID" @@ -375,7 +414,7 @@ paths: tags: - authentication summary: Generates encrypted token for the user - operationId: token + operationId: getEncryptedToken parameters: - $ref: "#/components/parameters/defaultHeader" security: @@ -414,7 +453,7 @@ paths: Since this does not comply with the openAPI standards, swagger cannot document it properly, therefore we need to change/remove it in the future. deprecated: true - operationId: secondFactorGet + operationId: GetSecondFactor parameters: - $ref: "#/components/parameters/defaultHeader" - name: TOTP @@ -445,7 +484,7 @@ paths: an authenticated user with basic credentials description: Send either the HOTP or TOTP code depending on the two factor method used - operationId: secondFactorPost + operationId: PostSecondFactor parameters: - $ref: "#/components/parameters/defaultHeader" responses: @@ -875,7 +914,7 @@ paths: tags: - project summary: Gets the private key beloning to the current project - operationId: getPublic + operationId: getPrivateKey parameters: - $ref: "#/components/parameters/defaultHeader" - $ref: "#/components/parameters/projectID" @@ -1319,7 +1358,7 @@ paths: tags: - superadmin summary: Return list of all active MOTDs to super admin. - operationId: allUnits + operationId: getMOTDs parameters: - $ref: "#/components/parameters/defaultHeader" responses: @@ -1592,7 +1631,7 @@ paths: tags: - s3 summary: Get the project S3 keys - operationId: retrieveUserInfo + operationId: retrieveProjectKeys parameters: - $ref: "#/components/parameters/defaultHeader" - $ref: "#/components/parameters/projectID" @@ -1637,7 +1676,10 @@ components: required: true schema: type: string - example: "2.6.0" + example: "2.6.2" + description: The oficial CLI provided by the DDS team always uses the + latest stable version. When implementing your own client(s) make sure to set the + correct version header in every request. projectID: name: project in: query @@ -1809,6 +1851,40 @@ components: checksum: type: string example: cheksum + FilesInfo: + type: object + properties: + filename.txt: + type: object + properties: + checksum: + type: string + example: checksum + compressed: + type: boolean + example: true + name_in_bucket: + type: string + example: filebucketname1 + public_key: + type: string + example: public_key + salt: + type: string + example: salt + size_original: + type: integer + format: int64 + example: 1000 + size_stored: + type: integer + format: int64 + example: 500 + subpath: + type: string + example: subpath + url: + type: string User: type: object required: @@ -2056,7 +2132,7 @@ components: encryptedToken: # user and pass type: http scheme: basic - seconFactorToken: # encrypted token + secondFactorToken: # encrypted token type: http scheme: bearer bearerFormat: JWT diff --git a/dds_web/static/swaggerv3.yaml b/dds_web/static/swaggerv3.yaml new file mode 100644 index 000000000..6fde368c6 --- /dev/null +++ b/dds_web/static/swaggerv3.yaml @@ -0,0 +1,2130 @@ +# yammllint and prettier conflict with some rules, disable them +# yamllint disable rule:braces +openapi: 3.0.0 +info: + description: | # html syntax is allowed + Documentation for the API of the Data Delivery System (DDS) in SciLifeLab Data Centre.
+ This documentation is mainly intended for internal use. But developers and researchers + can use it at their own risk. + However, DC recomends to always use the client and tools provided to units and researchers.
+

+ WARNING: This version is NOT stable and is subject to change + without notice. Use at your own risk. + We recomended to still use, for now, the version located in the + the url api/documentation/v1
+
+ For interaction with the API resources:
+ 1. Change the server in the list below depending on the instance you are using, + and click on the "Authorize" button to enter your user and password.
+ 2. Use the endpoint /user/encrypted_token to generate an encrypted token for the user.
+ 3. Add this returned token in the second section, secondFactorToken (http, Bearer) + of the same "Authorize" form used before.
+ 4. Call the endpoint /user/second_factor to authenticate with MFA. This will return a new token.
+ 5. Lastly, go back to the "Authorize" form, click "Logout" at the secondFactorToken (http, Bearer) part, + and use the second token from the previous step to login again.
+ + Now you can start using the API!
+
+ NOTE: The encrypted token is valid for 7 days.
+ version: "version 3" + title: Data Delivery System +# Apply the encrypted token to all operations +security: + - secondFactorToken: [] +servers: + - url: https://delivery.scilifelab.se/api/v3 + description: Endpoint for the production server + - url: https://dds-dev.dckube3.scilifelab.se/api/v3 + description: Endpoint for the test server + # Uncomment when local testing +# - url: http://localhost:5000/api/v3 +# description: Local enviroment +tags: + - name: authentication + description: Authorization and authentication operations + - name: files + description: Manage files operations + - name: user + description: Operations about user + - name: project + description: Operations about projects + - name: s3 + description: s3 bucket + - name: superadmin + description: Superadmin operations +paths: + ### FILES OPERATIONS ######### + /file/new: + post: + tags: + - files + summary: Add the information of a new file to the database + description: Adds the metadata of a file to the DB.
+ This endpoints needs to be called AFTER uploading the actual file to the bucket.
+ To do that you need to get the s3 keys in the /s3/proj endpoint + operationId: newFile + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: "File data_seq.txt added to db." + requestBody: + $ref: "#/components/requestBodies/Files" + put: + tags: + - files + summary: Update an existing file + description: Updates the metadata of a file in the DB. The name will be used + to find the file. The rest of the fields will be updated. + operationId: updateFile + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: "File data_seq.txt updated in the db." + requestBody: + $ref: "#/components/requestBodies/Files" + /file/match: + get: + tags: + - files + summary: Get name in bucket for all files specified + operationId: matchFiles + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + - $ref: "#/components/parameters/filesToQuery" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: successful operation + content: + application/json: + schema: + type: object + properties: + files: + type: array + items: + type: string + example: [name_in_bucket, name_in_bucket_2] + /files/list: + get: + tags: + - files + summary: Get a list of files and folders within an specific folder + operationId: listFiles + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + - name: show_size + in: query + schema: + type: boolean + description: Return the size of files/folders + - name: subpath + in: query + schema: + type: string + description: Check from which folder to get, empty parameters equals check from root + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: successful operation + content: + application/json: + schema: + type: object + properties: + files_folder: + type: array + items: + type: object + properties: + folder: + type: boolean + name: + type: string + example: + [ + { "folder": true, "name": "filename1" }, + { "folder": true, "name": "filename2" }, + { "folder": true, "name": "sub" }, + ] + /file/rm: + delete: + tags: + - files + summary: Deletes a file or several files metadata from the database AND the s3 bucket. + description: Deletes the file from the DB and bucket.
+ In contrast with the /file/new endpoint there is no extra operation with the s3 keys needed. + operationId: removeFile + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + - $ref: "#/components/parameters/filesToQuery" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: Successful operation, this means that the backend didn't encounted + any errors. But the file may not have been deleted from the database. If this happens, it will + be indicated in the response + content: + application/json: + schema: + type: object + properties: + not_removed: + type: boolean + not_exists: + type: boolean + example: { "not_removed": false, "not_exists": false } + /file/rmdir: + delete: + tags: + - files + summary: Removes one or more full directories metadata from the database AND the s3 bucket. + description: Deletes the directory from the DB and bucket.
+ In contrast with the /file/new endpoint there is no extra operation with the s3 keys needed. + operationId: removeDir + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + - $ref: "#/components/parameters/foldersToQuery" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: Successful operation, this means that the backend didn't encounted + any errors. But the file may not have been deleted from the database. If this happens, it will + be indicated in the response + content: + application/json: + schema: + type: object + properties: + not_removed: + type: boolean + fail_type: + type: string + nr_deleted: + type: integer + example: { "not_removed": false, "fail_type": "none", "nr_deleted": 10 } # yamllint disable-line + /file/info: + get: + tags: + - files + summary: Get file info on files to download + description: Checks which files can be downloaded, and get their info. + operationId: fileInfo + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + - $ref: "#/components/parameters/filesToQuery" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: successful operation + content: + application/json: + schema: + type: object + properties: + files: + $ref: "#/components/schemas/FilesInfo" + folder_contents: + example: {} + not_found: + example: {} + /file/all/info: + get: + tags: + - files + summary: Get info on all project files + operationId: fileInfoAll + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: successful operation + content: + application/json: + schema: + type: object + properties: + files: + $ref: "#/components/schemas/FilesInfo" + /file/update: + put: + tags: + - files + summary: Update file info after download + description: After downloading a file, that event has to be registered using this endpoint + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + operationId: updateFile + responses: + "405": + description: Invalid input + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: "File info updated" + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + example: filename + /file/failed/add: + put: + tags: + - files + summary: Get files from log file and save to database + description: If adding database entries failed for some files, the information from the log is + passed to this endpoint to allow for a new database update attempt. + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + operationId: addFailed + responses: + "405": + description: Invalid input + "200": + description: successful operation + content: + application/json: + schema: + type: object + properties: + files_added: + type: array + items: + type: string + example: [filefailed] + requestBody: + $ref: "#/components/requestBodies/FailedFiles" + + # LOGIN OPERATIONS + /user/encrypted_token: + get: + tags: + - authentication + summary: Generates encrypted token for the user + operationId: getEncryptedToken + parameters: + - $ref: "#/components/parameters/defaultHeader" + security: + - encryptedToken: [] + responses: + "401": + $ref: "#/components/responses/UnauthorizedTwofactor" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: Please take this token to /user/second_factor + to authenticate with MFA! + token: + type: string + secondfactor_method: + type: string + example: secondfactor_method + /user/second_factor: + post: + tags: + - authentication + summary: Take in and verify an authentication one-time code entered by + an authenticated user with basic credentials + description: Send either the HOTP or TOTP code + depending on the two factor method used + operationId: GetSecondFactor + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + token: + type: string + requestBody: + $ref: "#/components/requestBodies/Token" + + ### USER OPERATIONS ######### + /user/info: + get: + tags: + - user + summary: Return own info when required + operationId: retrieveUserInfo + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + $ref: "#/components/schemas/UserInfo" + /user/add: + post: + tags: + - user + summary: Associate existing users or unanswered invites with projects or create invites + operationId: addUser + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + email: + type: string + example: researcher1@mailtrap.io + errors: + type: object + message: + type: string + example: Pending invite was successful + status: + type: integer + example: 200 + requestBody: + $ref: "#/components/requestBodies/User" + /user/delete: + delete: + tags: + - user + summary: Delete user or invite in the DDS. Unit Admins can delete Unit Admins and Unit Personnel. + Super admins can delete any user CHECK METHOD + description: This method requires the data + to be passed in the request body instead of the query. + Since this does not comply with the openAPI standards, swagger cannot document it properly, + therefore we need to change/remove it in the future. + deprecated: true + operationId: deleteUser + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/email" + - $ref: "#/components/parameters/is_invite" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: successful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: You successfully deleted the account. + /user/delete_self: + delete: + tags: + - user + summary: Endpoint to initiate user self removal from the system. + Every user can self-delete the own account with an e-mail confirmation + operationId: deleteUserSelf + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: successful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: Requested account deletion initiated. + An e-mail with a confirmation link has been sent to your address + /user/access/revoke: + post: + tags: + - user + summary: Remove an user from a project + operationId: removeUserAssociation + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: successful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: User with email email@example.com no longer + associated with exmple_project. + requestBody: + content: + application/json: + schema: + type: object + properties: + email: + type: string + example: researcher1@mailtrap.io + /user/activation: + post: + tags: + - user + summary: Unit Admins can reactivate/deactivate unitusers. Super admins can reactivate/deactivate any user. + operationId: userActivation + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: successful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: You successfully reactivated the account. + requestBody: + content: + application/json: + schema: + type: object + properties: + email: + type: string + example: researcher1@mailtrap.io + action: + type: string + enum: + - reactivate + - deactivate + /user/hotp/activate: + post: + tags: + - user + summary: Request to switch from TOTP to HOTP for second factor authentication. + operationId: requestHOTPActivation + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: successful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: Please check your email and follow the attached link to activate two-factor with email # yamllint disable-line + security: + - encryptedToken: [] + /user/totp/activate: + post: + tags: + - user + summary: Request to switch from HOTP to TOTP for second factor authentication. + operationId: requestTOTPActivation + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: successful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: Please check your email and follow the attached link to activate two-factor with autenticator app # yamllint disable-line + /users: + get: + tags: + - user + summary: List unit users within the unit the current user is connected to, or the one defined by a superadmin. + CHECK METHOD FOR SUPERADMINS + description: TODO - for superadmin, it can be passed a unit, currently in body, needs to be in query. + deprecated: true + operationId: getUsers + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/UserList" + /user/invites: + get: + tags: + - user + summary: Provides a list of invited users. + Superadmins see all invites, unitusers the invites to their projects and units. + description: The Unit field is only returned when authenticated as a superadmin. + operationId: getInvites + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: successful operation + content: + application/json: + schema: + type: object + properties: + invites: + type: array + items: + $ref: "#/components/schemas/InvitesList" + keys: + type: array + items: + type: string + example: [Email, Role, Projects, Created, Unit] + /usage: + get: + tags: + - user + - project + summary: Calculate and display the amount of GB hours and the total cost. + Only unit accounts can get invoicing information + operationId: showUsage + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: successful operation + content: + application/json: + schema: + type: object + properties: + project_usage: + type: object + properties: + project_1: + type: object + properties: + gbhours: + type: number + example: 0.00000000000000000000 + cost: + type: number + example: 0.00000000000000000000 + total_usage: + type: object + properties: + gbhours: + type: number + example: 0.00000000000000000000 + cost: + type: number + example: 0.00000000000000000000 + + ### PROJECT OPERATIONS ######### + /proj/list: + get: + tags: + - project + summary: Get info regarding all projects which user is involved in + operationId: userProjects + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + $ref: "#/components/schemas/ProjListInfo" + /proj/rm: + delete: + tags: + - project + summary: Remove project contents + operationId: removeContents + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: string + example: "{removed: true}" + /proj/public: + get: + tags: + - project + summary: Gets the public key beloning to the current project + operationId: getPublic + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + public: + type: string + example: public_key + /proj/private: + get: + tags: + - project + summary: Gets the private key beloning to the current project + operationId: getPrivateKey + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + private: + type: string + example: private + /proj/create: + post: + tags: + - project + summary: Create a new project + operationId: createProject + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + status: + type: integer + example: 200 + message: + type: string + example: Added new project project_title + project_id: + type: string + example: project_id + user_addition_statuses: + type: array + items: + type: object + properties: + status: + type: string + example: User added + requestBody: + content: + application/json: + schema: + type: object + required: + - title + - description + - pi + properties: + forced: + type: boolean + example: false + description: Verify enough number of Unit Admins + title: + type: string + example: project_title + description: + type: string + example: project_description + pi: + type: string + example: pi@example.com + non_sensitive: + type: boolean + example: False + description: If has non-sensitive data + users_to_add: + type: array + items: + type: object + properties: + email: + type: string + example: researcheruser@mailtrap.io + role: + type: string + example: Researcher + description: List of users (outside the unit) to add to the project. Can be empty. + /proj/users: + get: + tags: + - project + summary: Get all users in a project + operationId: projectUsers + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + research_users: + type: array + items: + type: object + properties: + Primary email: + type: string + User name: + type: string + Role: + type: string + /proj/status: + get: + tags: + - project + summary: Get current project status and optionally entire status history CHECK METHOD + description: This method requires some data + to be passed in the request body instead of the query. + Since this does not comply with the openAPI standards, swagger cannot document it properly, + therefore we need to change/remove it in the future. + deprecated: true + operationId: projectStatusGet + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + - name: history + in: query + required: false + schema: + type: boolean + example: true + description: If true, return entire status history + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + current_status: + type: string + example: In progress + post: + tags: + - project + summary: Update project status + operationId: projectStatusPost + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + requestBody: + content: + application/json: + schema: + type: object + properties: + new_status: + type: string + example: Available + send_email: + type: boolean + example: true + deadline: + type: integer + example: 30 + is_aborted: + type: boolean + example: false + /proj/access: + post: + tags: + - project + summary: Give access to user + operationId: projectAccess + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: Project Access updated + for user user_primeary_email. + requestBody: + content: + application/json: + schema: + type: object + properties: + email: + type: string + /proj/info: + get: + tags: + - project + summary: Display Project information + operationId: projectInfo + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + $ref: "#/components/schemas/ProjInfo" + put: + tags: + - project + summary: Update Project information + operationId: projectInfoUpdate + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: Project_id info was successfuly updated + title: + type: string + example: new_title + description: + type: string + example: new_description + pi: + type: string + example: new_pi@example.org + requestBody: + content: + application/json: + schema: + type: object + properties: + title: + type: string + new_description: + type: string + new_pi: + type: string + + ### SUPERADMIN OPERATIONS ######### + /maintenance: + put: + tags: + - superadmin + summary: Change the maintenance mode of the system + operationId: maintenanceMode + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: "Maintenance mode is set to: ON" + requestBody: + content: + application/json: + schema: + type: object + properties: + state: + type: string + enum: [on, off] + get: + tags: + - superadmin + summary: Return current Maintenance mode + operationId: maintenanceModeGet + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + state: + type: string + example: "Maintenance mode is set to: ON" + /unit/info/all: + get: + tags: + - superadmin + summary: Get units info. + operationId: allUnits + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + units: + type: array + items: + $ref: "#/components/schemas/UnitsInfo" + keys: + type: array + items: + type: string + example: + [ + Name, + Contact Email, + Days In Available, + Days In Expired, + External Display Name, + Public ID, + Safespring Endpoint, + Size, + ] + /motd: + post: + tags: + - superadmin + summary: Add a Message of the Day. + operationId: addMOTD + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: The MOTD was successfuly + added to the database. + requestBody: + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: "This is a message of the day" + get: + tags: + - superadmin + summary: Return list of all active MOTDs to super admin. + operationId: getMOTDs + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + motds: + type: array + items: + $ref: "#/components/schemas/MotdInfo" + keys: + type: array + items: + type: string + example: [MOTD ID, Message, Created] + put: + tags: + - superadmin + summary: Deactivate MOTD. + operationId: deactivateMOTD + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: The MOTD was successfuly + deactivated in the database. + requestBody: + content: + application/json: + schema: + type: object + properties: + motd_id: + type: integer + example: 1 + /motd/send: + post: + tags: + - superadmin + summary: Send MOTD as email to users. + operationId: sendMOTD + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: MOTD 1 has been sent to the users. + requestBody: + content: + application/json: + schema: + type: object + properties: + motd_id: + type: integer + example: 1 + /user/find: + get: + tags: + - superadmin + summary: Get all users or check if there is a specific user in the database CHECK METHOD + description: This method requires the data + to be passed in the request body instead of the query. + Since this does not comply with the openAPI standards, swagger cannot document it properly, + therefore we need to change/remove it in the future. + deprecated: true + operationId: findUser + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + Exists: + type: boolean + example: true + /user/totp/deactivate: + put: + tags: + - superadmin + summary: Deactivate TOTP and activate HOTP for other user, e.g. if phone lost + operationId: resetTwoFactor + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: TOTP has been deactivated for user unituser1. + They can now use 2FA via email during authentication + requestBody: + content: + application/json: + schema: + type: object + properties: + username: + type: string + example: unituser1 + /proj/busy/any: + get: + tags: + - superadmin + summary: Check if any project are busy CHECK METHOD + description: This method requires the data + to be passed in the request body instead of the query. + Since this does not comply with the openAPI standards, swagger cannot document it properly, + therefore we need to change/remove it in the future. + deprecated: true + operationId: anyProjectBusy + parameters: + - $ref: "#/components/parameters/defaultHeader" + - in: query + name: list + schema: + type: boolean + required: false + description: Check if user is listing the busy projects. + Otherwise just returns the number of busy projects. + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + num: + type: integer + example: 0 + /stats: + get: + tags: + - superadmin + summary: Collect rows from reporting table and return them. + operationId: stats + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + columns: + type: array + items: + type: string + example: + [ + Active projects, + Data Now (TB), + Data Uploaded (TB), + Date, + Inactive projects, + Project Owners, + Researchers, + Super Admins, + TBHours Last Month, + TBHours Total, + Total projects, + Total users, + Unit Admins, + Unit Personnel, + Units, + ] + stats: + type: array + items: + $ref: "#/components/schemas/Reporting" + /user/emails: + get: + tags: + - superadmin + summary: Get emails for Unit Admins and Unit Personnel. + operationId: unitUsersEmails + parameters: + - $ref: "#/components/parameters/defaultHeader" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + emails: + type: array + items: + type: string + example: + [ + unitadmin1@mailtrap.io, + unitadmin2@mailtrap.io, + unitadmin3@mailtrap.io, + unituser1@mailtrap.io, + unituser2@mailtrap.io, + ] + + ### S3 OPERATIONS ######### + /s3/proj: + get: + tags: + - s3 + summary: Get the project S3 keys + operationId: retrieveProjectKeys + parameters: + - $ref: "#/components/parameters/defaultHeader" + - $ref: "#/components/parameters/projectID" + responses: + "401": + $ref: "#/components/responses/UnauthorizedToken" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerlError" + "200": + description: succesful operation + content: + application/json: + schema: + type: object + properties: + safespring_project: + type: string + example: sfsp_proj + url: + type: string + example: url + keys: + type: object + properties: + access_key: + type: string + example: access_key + secret_key: + type: string + example: secret_key + bucket: + type: string + example: bucketname +components: + # define reusable parameters: + parameters: + defaultHeader: + name: X-CLI-Version + in: header + required: true + schema: + type: string + example: "3.0.1" + description: The oficial CLI provided by the DDS team always uses the + latest stable version. When implementing your own client(s) make sure to set the + correct version header in every request. + projectID: + name: project + in: query + schema: + type: string + description: project id to query + email: + name: email + in: query + schema: + type: string + description: email of the user/invite to query + is_invite: + name: is_invite + in: query + schema: + type: string + description: flag to mark if the user to query is an invite + filesToQuery: + name: files + in: query + schema: + type: array + items: + type: string + description: Files to query + foldersToQuery: + name: folders + in: query + schema: + type: array + items: + type: string + description: Folders to query + # Define common request bodies: + requestBodies: + Files: + content: + application/json: + schema: + $ref: "#/components/schemas/Files" + FailedFiles: + content: + application/json: + schema: + type: object + properties: + filename: + $ref: "#/components/schemas/FailedFiles" + User: + content: + application/json: + schema: + $ref: "#/components/schemas/User" + Token: + content: + application/json: + schema: + $ref: "#/components/schemas/Token" + # define reusable responses: + responses: + BadRequest: + description: The requests is missing important information in the body + or have invalidad data, such as invalid project specified + content: + application/json: + schema: + type: object + properties: + message: + type: string + UnauthorizedTwofactor: + description: Username and password not provided or is invalid + content: + application/json: + schema: + type: object + properties: + message: + type: string + UnauthorizedToken: + description: Encrypted Token is not provided or is invalid + content: + application/json: + schema: + type: object + properties: + message: + type: string + InternalServerlError: + description: Something wrong in the server side + content: + application/json: + schema: + type: object + properties: + message: + type: string + # define schemas for objects + schemas: + Files: + type: object + required: + - name + - name_in_bucket + - subpath + - size + - size_processed + - compressed + - public_key + - salt + - checksum + properties: + name: + type: string + example: adn_secuence.txt + name_in_bucket: + type: string + example: filebucketname1 + subpath: + type: string + example: subpath + size: + type: integer + format: int64 + example: 1000 + size_processed: + type: integer + format: int64 + example: 500 + compressed: + type: boolean + example: true + public_key: + type: string + example: pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp # yamllint disable-line + salt: + type: string + example: ssssssssssssssssssssssssssssssss + checksum: + type: string + example: cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc # yamllint disable-line + FailedFiles: + type: object + required: + - status + - path_remote + - subpath + - size_raw + - size_processed + - compressed + - public_key + - salt + - checksum + properties: + status: + type: object + properties: + failed_op: + type: string + example: add_file_db + path_remote: + type: string + example: path/to/file1.txt + subpath: + type: string + example: subpath + size_raw: + type: integer + format: int64 + example: 100 + size_processed: + type: integer + format: int64 + example: 200 + compressed: + type: boolean + example: false + public_key: + type: string + example: public_key + salt: + type: string + example: salt + checksum: + type: string + example: cheksum + FilesInfo: + type: object + properties: + filename.txt: + type: object + properties: + checksum: + type: string + example: checksum + compressed: + type: boolean + example: true + name_in_bucket: + type: string + example: filebucketname1 + public_key: + type: string + example: public_key + salt: + type: string + example: salt + size_original: + type: integer + format: int64 + example: 1000 + size_stored: + type: integer + format: int64 + example: 500 + subpath: + type: string + example: subpath + url: + type: string + User: + type: object + required: + - role + - unit + - email + properties: + role: + type: string + example: Researcher + unit: + type: string + example: UnitA + email: + type: string + example: researcher1@mailtrap.io + send_email: + type: boolean + example: true + UserInfo: + type: object + properties: + info: + type: object + properties: + email_primary: + type: string + example: unitadmin1@mailtrap.io + emails_all: + type: array + items: + type: string + is_admin: + type: boolean + example: true + name: + type: string + example: Unit Admin 1 + role: + type: string + example: Unit Admin + username: + type: string + example: unitadmin_1 + UserList: + type: object + properties: + active: + type: boolean + example: true + name: + type: string + example: Unit Admin 1 + role: + type: string + example: Unit Admin + username: + type: string + example: unitadmin_1 + email: + type: string + example: unitadmin1@mailtrap.io + InvitesList: + type: object + required: + - Created + - Email + - Role + properties: + Created: + type: string + format: date-time + Email: + type: string + example: researcher1@mailtrap.io + Role: + type: string + example: Researcher + Projects: + type: array + items: + type: string + example: [project1] + Unit: + type: string + example: "" + ProjListInfo: + type: object + properties: + always_show: + type: boolean + example: true + project_info: + type: array + items: + type: object + properties: + Access: + type: boolean + example: true + Created By: + type: string + example: user + Last Updated: + type: string + example: 2020-01-01 00:00:00 + PI: + type: string + example: support@example.org + Project ID: + type: string + example: project_id + Size: + type: integer + example: 1000 + Status: + type: string + example: In progress + Title: + type: string + example: project_title + total_size: + type: integer + example: 1000 + tota_usage: + type: object + properties: + cost: + type: integer + example: 0 + usage: + type: integer + example: 1000 + ProjInfo: + type: object + properties: + project_info: + type: object + properties: + Created By: + type: string + example: user + Last Updated: + type: string + example: 2020-01-01 00:00:00 + PI: + type: string + example: support@example.org + Project ID: + type: string + example: project_id + Status: + type: string + example: In progress + Size: + type: integer + example: 1000 + Title: + type: string + example: project_title + Description: + type: string + example: project_description + UnitsInfo: + type: object + properties: + Name: + type: string + example: Unit 1 + Contact Email: + type: string + example: support@example.com + Days In Available: + type: integer + example: 90 + Days In Expired: + type: integer + example: 30 + External Display Name: + type: string + example: Unit 1 + Public ID: + type: string + example: unit_1 + Safespring Endpoint: + type: string + Size: + type: integer + format: int64 + example: 1000 + MotdInfo: + type: object + properties: + MOTD ID: + type: integer + example: 1 + Message: + type: string + example: This is a message of the day + Created: + type: string + example: 2021-01-01 00:00:00 + Reporting: + type: object + properties: + Active projects: + type: integer + Data Now (TB): + type: integer + Data Uploaded (TB): + type: integer + Date: + type: string + Inactive projects: + type: integer + Project Owners: + type: integer + Researchers: + type: integer + Super Admins: + type: integer + TBHours Last Month: + type: integer + TBHours Total: + type: integer + Total projects: + type: integer + Total users: + type: integer + Unit Admins: + type: integer + Unit Personnel: + type: integer + Units: + type: integer + Token: + type: object + properties: + HOTP: + type: string + TOTP: + type: string + # Securit schemes: + securitySchemes: + encryptedToken: # user and pass + type: http + scheme: basic + secondFactorToken: # encrypted token + type: http + scheme: bearer + bearerFormat: JWT diff --git a/dds_web/templates/troubleshooting.html b/dds_web/templates/troubleshooting.html index 97a2ded13..9f8d60351 100644 --- a/dds_web/templates/troubleshooting.html +++ b/dds_web/templates/troubleshooting.html @@ -17,8 +17,32 @@

Did the troubleshooting document not help?


Does the issue persist and / or the provided information not help?

-

If you're a data producer, using the DDS for data deliveries to your users: contact the Data Centre.

-

If you're a data recipient, using the DDS to download your data from a data producing SciLifeLab unit: contact the SciLifeLab unit responsible for delivering the data to you.

+

If you're a data producer , using the DDS for data deliveries to your users: contact the + + +

+

If you're a data recipient , using the DDS to download your data from a data producing SciLifeLab unit: contact the SciLifeLab unit responsible for delivering the data to you.

Please provide the following information: