diff --git a/.pylintrc b/.pylintrc
deleted file mode 100644
index 15b43cdb..00000000
--- a/.pylintrc
+++ /dev/null
@@ -1,447 +0,0 @@
-# This Pylint rcfile contains a best-effort configuration to uphold the
-# best-practices and style described in the Google Python style guide:
-# https://google.github.io/styleguide/pyguide.html
-#
-# Its canonical open-source location is:
-# https://google.github.io/styleguide/pylintrc
-
-[MASTER]
-
-# Add files or directories to the blacklist. They should be base names, not
-# paths.
-ignore=third_party
-
-# Add files or directories matching the regex patterns to the blacklist. The
-# regex matches against base names, not paths.
-ignore-patterns=
-
-# Pickle collected data for later comparisons.
-persistent=no
-
-# List of plugins (as comma separated values of python modules names) to load,
-# usually to register additional checkers.
-load-plugins=
-
-# Use multiple processes to speed up Pylint.
-jobs=4
-
-# Allow loading of arbitrary C extensions. Extensions are imported into the
-# active Python interpreter and may run arbitrary code.
-unsafe-load-any-extension=no
-
-# A comma-separated list of package or module names from where C extensions may
-# be loaded. Extensions are loading into the active Python interpreter and may
-# run arbitrary code
-extension-pkg-whitelist=
-
-
-[MESSAGES CONTROL]
-
-# Only show warnings with the listed confidence levels. Leave empty to show
-# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
-confidence=
-
-# Enable the message, report, category or checker with the given id(s). You can
-# either give multiple identifier separated by comma (,) or put this option
-# multiple time (only on the command line, not in the configuration file where
-# it should appear only once). See also the "--disable" option for examples.
-#enable=
-
-# Disable the message, report, category or checker with the given id(s). You
-# can either give multiple identifiers separated by comma (,) or put this
-# option multiple times (only on the command line, not in the configuration
-# file where it should appear only once).You can also use "--disable=all" to
-# disable everything first and then reenable specific checks. For example, if
-# you want to run only the similarities checker, you can use "--disable=all
-# --enable=similarities". If you want to run only the classes checker, but have
-# no Warning level messages displayed, use"--disable=all --enable=classes
-# --disable=W"
-disable=abstract-method,
- apply-builtin,
- arguments-differ,
- attribute-defined-outside-init,
- backtick,
- bad-option-value,
- basestring-builtin,
- buffer-builtin,
- c-extension-no-member,
- consider-using-enumerate,
- cmp-builtin,
- cmp-method,
- coerce-builtin,
- coerce-method,
- delslice-method,
- div-method,
- duplicate-code,
- eq-without-hash,
- execfile-builtin,
- file-builtin,
- filter-builtin-not-iterating,
- fixme,
- getslice-method,
- global-statement,
- hex-method,
- idiv-method,
- implicit-str-concat-in-sequence,
- import-error,
- import-self,
- import-star-module-level,
- inconsistent-return-statements,
- input-builtin,
- intern-builtin,
- invalid-str-codec,
- locally-disabled,
- long-builtin,
- long-suffix,
- map-builtin-not-iterating,
- misplaced-comparison-constant,
- missing-function-docstring,
- metaclass-assignment,
- next-method-called,
- next-method-defined,
- no-absolute-import,
- no-else-break,
- no-else-continue,
- no-else-raise,
- no-else-return,
- no-init, # added
- no-member,
- no-name-in-module,
- no-self-use,
- nonzero-method,
- oct-method,
- old-division,
- old-ne-operator,
- old-octal-literal,
- old-raise-syntax,
- parameter-unpacking,
- print-statement,
- raising-string,
- range-builtin-not-iterating,
- raw_input-builtin,
- rdiv-method,
- reduce-builtin,
- relative-import,
- reload-builtin,
- round-builtin,
- setslice-method,
- signature-differs,
- standarderror-builtin,
- suppressed-message,
- sys-max-int,
- too-few-public-methods,
- too-many-ancestors,
- too-many-arguments,
- too-many-boolean-expressions,
- too-many-branches,
- too-many-instance-attributes,
- too-many-locals,
- too-many-nested-blocks,
- too-many-public-methods,
- too-many-return-statements,
- too-many-statements,
- trailing-newlines,
- unichr-builtin,
- unicode-builtin,
- unnecessary-pass,
- unpacking-in-except,
- useless-else-on-loop,
- useless-object-inheritance,
- useless-suppression,
- using-cmp-argument,
- wrong-import-order,
- xrange-builtin,
- zip-builtin-not-iterating,
-
-
-[REPORTS]
-
-# Set the output format. Available formats are text, parseable, colorized, msvs
-# (visual studio) and html. You can also give a reporter class, eg
-# mypackage.mymodule.MyReporterClass.
-output-format=text
-
-# Put messages in a separate file for each module / package specified on the
-# command line instead of printing them on stdout. Reports (if any) will be
-# written in a file name "pylint_global.[txt|html]". This option is deprecated
-# and it will be removed in Pylint 2.0.
-files-output=no
-
-# Tells whether to display a full report or only the messages
-reports=no
-
-# Python expression which should return a note less than 10 (10 is the highest
-# note). You have access to the variables errors warning, statement which
-# respectively contain the number of errors / warnings messages and the total
-# number of statements analyzed. This is used by the global evaluation report
-# (RP0004).
-evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
-
-# Template used to display messages. This is a python new-style format string
-# used to format the message information. See doc for all details
-#msg-template=
-
-
-[BASIC]
-
-# Good variable names which should always be accepted, separated by a comma
-good-names=main,_
-
-# Bad variable names which should always be refused, separated by a comma
-bad-names=
-
-# Colon-delimited sets of names that determine each other's naming style when
-# the name regexes allow several styles.
-name-group=
-
-# Include a hint for the correct naming format with invalid-name
-include-naming-hint=no
-
-# List of decorators that produce properties, such as abc.abstractproperty. Add
-# to this list to register other decorators that produce valid properties.
-property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl
-
-# Regular expression matching correct function names
-function-rgx=^(?:(?PsetUp|tearDown|setUpModule|tearDownModule)|(?P_?[A-Z][a-zA-Z0-9]*)|(?P_?[a-z][a-z0-9_]*))$
-
-# Regular expression matching correct variable names
-variable-rgx=^[a-z][a-z0-9_]*$
-
-# Regular expression matching correct constant names
-const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
-
-# Regular expression matching correct attribute names
-attr-rgx=^_{0,2}[a-z][a-z0-9_]*$
-
-# Regular expression matching correct argument names
-argument-rgx=^[a-z][a-z0-9_]*$
-
-# Regular expression matching correct class attribute names
-class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
-
-# Regular expression matching correct inline iteration names
-inlinevar-rgx=^[a-z][a-z0-9_]*$
-
-# Regular expression matching correct class names
-class-rgx=^_?[A-Z][a-zA-Z0-9]*$
-
-# Regular expression matching correct module names
-module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$
-
-# Regular expression matching correct method names
-method-rgx=(?x)^(?:(?P_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P_{0,2}[a-z][a-z0-9_]*))$
-
-# Regular expression which should only match function or class names that do
-# not require a docstring.
-no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$
-
-# Minimum line length for functions/classes that require docstrings, shorter
-# ones are exempt.
-docstring-min-length=10
-
-
-[TYPECHECK]
-
-# List of decorators that produce context managers, such as
-# contextlib.contextmanager. Add to this list to register other decorators that
-# produce valid context managers.
-contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager
-
-# Tells whether missing members accessed in mixin class should be ignored. A
-# mixin class is detected if its name ends with "mixin" (case insensitive).
-ignore-mixin-members=yes
-
-# List of module names for which member attributes should not be checked
-# (useful for modules/projects where namespaces are manipulated during runtime
-# and thus existing member attributes cannot be deduced by static analysis. It
-# supports qualified module names, as well as Unix pattern matching.
-ignored-modules=
-
-# List of class names for which member attributes should not be checked (useful
-# for classes with dynamically set attributes). This supports the use of
-# qualified names.
-ignored-classes=optparse.Values,thread._local,_thread._local
-
-# List of members which are set dynamically and missed by pylint inference
-# system, and so shouldn't trigger E1101 when accessed. Python regular
-# expressions are accepted.
-generated-members=
-
-
-[FORMAT]
-
-# Maximum number of characters on a single line.
-max-line-length=100
-
-# TODO(https://github.com/PyCQA/pylint/issues/3352): Direct pylint to exempt
-# lines made too long by directives to pytype.
-
-# Regexp for a line that is allowed to be longer than the limit.
-ignore-long-lines=(?x)(
- ^\s*(\#\ )??$|
- ^\s*(from\s+\S+\s+)?import\s+.+$)
-
-# Allow the body of an if to be on the same line as the test if there is no
-# else.
-single-line-if-stmt=yes
-
-# List of optional constructs for which whitespace checking is disabled. `dict-
-# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
-# `trailing-comma` allows a space between comma and closing bracket: (a, ).
-# `empty-line` allows space-only lines.
-no-space-check=
-
-# Maximum number of lines in a module
-max-module-lines=99999
-
-# String used as indentation unit. The internal Google style guide mandates 2
-# spaces. Google's externaly-published style guide says 4, consistent with
-# PEP 8. Here, we use 2 spaces, for conformity with many open-sourced Google
-# projects (like TensorFlow).
-indent-string=' '
-
-# Number of spaces of indent required inside a hanging or continued line.
-indent-after-paren=4
-
-# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
-expected-line-ending-format=
-
-
-[MISCELLANEOUS]
-
-# List of note tags to take in consideration, separated by a comma.
-notes=TODO
-
-
-[STRING]
-
-# This flag controls whether inconsistent-quotes generates a warning when the
-# character used as a quote delimiter is used inconsistently within a module.
-check-quote-consistency=yes
-
-
-[VARIABLES]
-
-# Tells whether we should check for unused import in __init__ files.
-init-import=no
-
-# A regular expression matching the name of dummy variables (i.e. expectedly
-# not used).
-dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_)
-
-# List of additional names supposed to be defined in builtins. Remember that
-# you should avoid to define new builtins when possible.
-additional-builtins=
-
-# List of strings which can identify a callback function by name. A callback
-# name must start or end with one of those strings.
-callbacks=cb_,_cb
-
-# List of qualified module names which can have objects that can redefine
-# builtins.
-redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools
-
-
-[LOGGING]
-
-# Logging modules to check that the string format arguments are in logging
-# function parameter format
-logging-modules=logging,absl.logging,tensorflow.io.logging
-
-
-[SIMILARITIES]
-
-# Minimum lines number of a similarity.
-min-similarity-lines=4
-
-# Ignore comments when computing similarities.
-ignore-comments=yes
-
-# Ignore docstrings when computing similarities.
-ignore-docstrings=yes
-
-# Ignore imports when computing similarities.
-ignore-imports=no
-
-
-[SPELLING]
-
-# Spelling dictionary name. Available dictionaries: none. To make it working
-# install python-enchant package.
-spelling-dict=
-
-# List of comma separated words that should not be checked.
-spelling-ignore-words=
-
-# A path to a file that contains private dictionary; one word per line.
-spelling-private-dict-file=
-
-# Tells whether to store unknown words to indicated private dictionary in
-# --spelling-private-dict-file option instead of raising a message.
-spelling-store-unknown-words=no
-
-
-[IMPORTS]
-
-# Deprecated modules which should not be used, separated by a comma
-deprecated-modules=regsub,
- TERMIOS,
- Bastion,
- rexec,
- sets
-
-# Create a graph of every (i.e. internal and external) dependencies in the
-# given file (report RP0402 must not be disabled)
-import-graph=
-
-# Create a graph of external dependencies in the given file (report RP0402 must
-# not be disabled)
-ext-import-graph=
-
-# Create a graph of internal dependencies in the given file (report RP0402 must
-# not be disabled)
-int-import-graph=
-
-# Force import order to recognize a module as part of the standard
-# compatibility libraries.
-known-standard-library=
-
-# Force import order to recognize a module as part of a third party library.
-known-third-party=enchant, absl
-
-# Analyse import fallback blocks. This can be used to support both Python 2 and
-# 3 compatible code, which means that the block might have code that exists
-# only in one or another interpreter, leading to false positives when analysed.
-analyse-fallback-blocks=no
-
-
-[CLASSES]
-
-# List of method names used to declare (i.e. assign) instance attributes.
-defining-attr-methods=__init__,
- __new__,
- setUp
-
-# List of member names, which should be excluded from the protected access
-# warning.
-exclude-protected=_asdict,
- _fields,
- _replace,
- _source,
- _make
-
-# List of valid names for the first argument in a class method.
-valid-classmethod-first-arg=cls,
- class_
-
-# List of valid names for the first argument in a metaclass class method.
-valid-metaclass-classmethod-first-arg=mcs
-
-
-[EXCEPTIONS]
-
-# Exceptions that will emit a warning when being caught. Defaults to
-# "Exception"
-overgeneral-exceptions=StandardError,
- Exception,
- BaseException
diff --git a/Dockerfile-backend b/Dockerfile-backend
index e0b3265c..540ac09d 100644
--- a/Dockerfile-backend
+++ b/Dockerfile-backend
@@ -1,6 +1,6 @@
FROM python:3.8-alpine
-RUN apk add gcc musl-dev python3-dev libffi-dev openssl-dev
+RUN apk add gcc musl-dev python3-dev libffi-dev openssl-dev rust cargo
COPY ./backend/requirements.txt /requirements.txt
diff --git a/Dockerfile-frontend b/Dockerfile-frontend
index 234a88fc..03ec18e6 100644
--- a/Dockerfile-frontend
+++ b/Dockerfile-frontend
@@ -1,4 +1,4 @@
-FROM node:alpine
+FROM node:14-alpine
RUN yarn global add @quasar/cli
diff --git a/README.md b/README.md
index cec84712..257a7b77 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,14 @@ The backend requires a `config.yaml` file to be mounted to `/config.yaml`.
The frontend assumes that the backend is available at `/api`.
+## Code
+
+`backend`: python3, flask
+
+`frontend`: Quasar (Vue)
+
+`docs`: Sphinx
+
[travis-badge]: https://api.travis-ci.com/ScilifelabDataCentre/Data-Tracker.svg?branch=develop
[travis-link]: https://travis-ci.com/ScilifelabDataCentre/Data-Tracker
diff --git a/backend/app.py b/backend/app.py
index 60fbe3a8..93aeb3ba 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -21,21 +21,19 @@
appconf = config.init()
db_management.check_db(appconf)
app.config.update(appconf)
-app.config['PERMANENT_SESSION_LIFETIME'] = datetime.timedelta(days=31)
+if app.config["dev_mode"]["api"]:
+ app.register_blueprint(developer.blueprint, url_prefix="/api/v1/developer")
-if app.config['dev_mode']['api']:
- app.register_blueprint(developer.blueprint, url_prefix='/api/v1/developer')
-
-app.register_blueprint(dataset.blueprint, url_prefix='/api/v1/dataset')
-app.register_blueprint(order.blueprint, url_prefix='/api/v1/order')
-app.register_blueprint(collection.blueprint, url_prefix='/api/v1/collection')
-app.register_blueprint(user.blueprint, url_prefix='/api/v1/user')
+app.register_blueprint(dataset.blueprint, url_prefix="/api/v1/dataset")
+app.register_blueprint(order.blueprint, url_prefix="/api/v1/order")
+app.register_blueprint(collection.blueprint, url_prefix="/api/v1/collection")
+app.register_blueprint(user.blueprint, url_prefix="/api/v1/user")
oauth = OAuth(app)
-for oidc_name in app.config.get('oidc_names'):
- oauth.register(oidc_name, client_kwargs={'scope': 'openid profile email'})
+for oidc_name in app.config.get("oidc_names"):
+ oauth.register(oidc_name, client_kwargs={"scope": "openid profile email"})
@app.before_request
@@ -43,95 +41,97 @@ def prepare():
"""Open the database connection and get the current user."""
flask.g.dbclient = utils.get_dbclient(flask.current_app.config)
flask.g.db = utils.get_db(flask.g.dbclient, flask.current_app.config)
- if apikey := flask.request.headers.get('X-API-Key'):
- if not (apiuser := flask.request.headers.get('X-API-User')): # pylint: disable=superfluous-parens
+ if apikey := flask.request.headers.get("X-API-Key"):
+ if not (
+ apiuser := flask.request.headers.get("X-API-User")
+ ): # pylint: disable=superfluous-parens
flask.abort(status=400)
utils.verify_api_key(apiuser, apikey)
- flask.g.current_user = flask.g.db['users'].find_one({'auth_ids': apiuser})
- flask.g.permissions = flask.g.current_user['permissions']
+ flask.g.current_user = flask.g.db["users"].find_one({"auth_ids": apiuser})
+ flask.g.permissions = flask.g.current_user["permissions"]
else:
- if flask.request.method != 'GET':
+ if flask.request.method != "GET":
utils.verify_csrf_token()
flask.g.current_user = user.get_current_user()
- flask.g.permissions = flask.g.current_user['permissions'] if flask.g.current_user else None
+ flask.g.permissions = (
+ flask.g.current_user["permissions"] if flask.g.current_user else None
+ )
@app.after_request
def finalize(response):
"""Finalize the response and clean up."""
# close db connection
- if hasattr(flask.g, 'dbserver'):
+ if hasattr(flask.g, "dbserver"):
flask.g.dbserver.close()
# set csrf cookie if not set
- if not flask.request.cookies.get('_csrf_token'):
- response.set_cookie('_csrf_token', utils.gen_csrf_token(), samesite='Lax')
+ if not flask.request.cookies.get("_csrf_token"):
+ response.set_cookie("_csrf_token", utils.gen_csrf_token(), samesite="Lax")
# add some headers for protection
- response.headers['X-Frame-Options'] = 'SAMEORIGIN'
- response.headers['X-XSS-Protection'] = '1; mode=block'
+ response.headers["X-Frame-Options"] = "SAMEORIGIN"
+ response.headers["X-XSS-Protection"] = "1; mode=block"
return response
-@app.route('/api/v1/')
+@app.route("/api/v1/")
def api_base():
"""List entities."""
- return flask.jsonify({'entities': ['dataset', 'order', 'collection', 'user', 'login']})
+ return flask.jsonify(
+ {"entities": ["dataset", "order", "collection", "user", "login"]}
+ )
-@app.route('/api/v1/login/')
+@app.route("/api/v1/login/")
def login_types():
"""List login types."""
- return flask.jsonify({'types': ['apikey', 'oidc']})
+ return flask.jsonify({"types": ["apikey", "oidc"]})
-@app.route('/api/v1/login/oidc/')
+@app.route("/api/v1/login/oidc/")
def oidc_types():
"""List OpenID Connect types."""
auth_types = {}
- for auth_name in app.config.get('oidc_names'):
- auth_types[auth_name] = flask.url_for('oidc_login',
- auth_name=auth_name)
+ for auth_name in app.config.get("oidc_names"):
+ auth_types[auth_name] = flask.url_for("oidc_login", auth_name=auth_name)
return flask.jsonify(auth_types)
-@app.route('/api/v1/login/oidc//login/')
+@app.route("/api/v1/login/oidc//login/")
def oidc_login(auth_name):
"""Perform a login using OpenID Connect (e.g. Elixir AAI)."""
client = oauth.create_client(auth_name)
- redirect_uri = flask.url_for('oidc_authorize',
- auth_name=auth_name,
- _external=True)
- flask.session['incoming_url'] = flask.request.args.get('origin') or '/'
+ redirect_uri = flask.url_for("oidc_authorize", auth_name=auth_name, _external=True)
+ flask.session["incoming_url"] = flask.request.args.get("origin") or "/"
return client.authorize_redirect(redirect_uri)
-@app.route('/api/v1/login/oidc//authorize/')
+@app.route("/api/v1/login/oidc//authorize/")
def oidc_authorize(auth_name):
"""Authorize a login using OpenID Connect (e.g. Elixir AAI)."""
- if auth_name not in app.config.get('oidc_names'):
+ if auth_name not in app.config.get("oidc_names"):
flask.abort(status=404)
client = oauth.create_client(auth_name)
token = client.authorize_access_token()
- if 'id_token' in token:
+ if "id_token" in token:
user_info = client.parse_id_token(token)
else:
user_info = client.userinfo()
- if auth_name != 'elixir':
- user_info['auth_id'] = f'{user_info["email"]}::{auth_name}'
+ if auth_name != "elixir":
+ user_info["auth_id"] = f'{user_info["email"]}::{auth_name}'
else:
- user_info['auth_id'] = token['sub']
- if not user.do_login(user_info['auth_id']):
+ user_info["auth_id"] = token["sub"]
+ if not user.do_login(user_info["auth_id"]):
user.add_new_user(user_info)
- user.do_login(user_info['auth_id'])
+ user.do_login(user_info["auth_id"])
- response = flask.redirect(flask.session['incoming_url'])
- del flask.session['incoming_url']
- response.set_cookie('loggedIn', 'true', max_age=datetime.timedelta(days=31))
+ response = flask.redirect(flask.session["incoming_url"])
+ del flask.session["incoming_url"]
return response
# requests
-@app.route('/api/v1/login/apikey/', methods=['POST'])
+@app.route("/api/v1/login/apikey/", methods=["POST"])
def key_login():
"""Log in using an apikey."""
try:
@@ -139,23 +139,21 @@ def key_login():
except json.decoder.JSONDecodeError:
flask.abort(status=400)
- if 'api-user' not in indata or 'api-key' not in indata:
- app.logger.debug('API key login - bad keys: %s', indata)
+ if "api-user" not in indata or "api-key" not in indata:
+ app.logger.debug("API key login - bad keys: %s", indata)
return flask.Response(status=400)
- utils.verify_api_key(indata['api-user'], indata['api-key'])
- user.do_login(auth_id=indata['api-user'])
+ utils.verify_api_key(indata["api-user"], indata["api-key"])
+ user.do_login(auth_id=indata["api-user"])
response = flask.Response(status=200)
- response.set_cookie('loggedIn', 'true', max_age=datetime.timedelta(days=31))
return response
-@app.route('/api/v1/logout/')
+@app.route("/api/v1/logout/")
def logout():
"""Log out the current user."""
flask.session.clear()
response = flask.Response(status=200)
- response.set_cookie('_csrf_token', utils.gen_csrf_token(), 0)
- response.set_cookie('loggedIn', 'false', 0)
+ response.set_cookie("_csrf_token", utils.gen_csrf_token(), 0)
return response
@@ -184,10 +182,10 @@ def error_not_found(_):
# to allow coverage check for testing
-if __name__ == '__main__':
- app.run(host='0.0.0.0', port=5000)
+if __name__ == "__main__":
+ app.run(host="0.0.0.0", port=5000)
else:
- # Assume this means it's handled by gunicorn
- gunicorn_logger = logging.getLogger('gunicorn.error')
- app.logger.handlers = gunicorn_logger.handlers
- app.logger.setLevel(gunicorn_logger.level)
+ gunicorn_logger = logging.getLogger("gunicorn.error")
+ if gunicorn_logger:
+ app.logger.handlers = gunicorn_logger.handlers
+ app.logger.setLevel(gunicorn_logger.level)
diff --git a/backend/collection.py b/backend/collection.py
index 0b99c299..850a486b 100644
--- a/backend/collection.py
+++ b/backend/collection.py
@@ -7,21 +7,22 @@
import user
import utils
-blueprint = flask.Blueprint('collection', __name__) # pylint: disable=invalid-name
+blueprint = flask.Blueprint("collection", __name__) # pylint: disable=invalid-name
-@blueprint.route('/', methods=['GET'])
+@blueprint.route("/", methods=["GET"])
def list_collection():
"""Provide a simplified list of all available collections."""
- results = list(flask.g.db['collections'].find(projection={'title': 1,
- '_id': 1,
- 'tags': 1,
- 'properties': 1}))
- return utils.response_json({'collections': results})
+ results = list(
+ flask.g.db["collections"].find(
+ projection={"title": 1, "_id": 1, "tags": 1, "properties": 1}
+ )
+ )
+ return utils.response_json({"collections": results})
-@blueprint.route('/random/', methods=['GET'])
-@blueprint.route('/random/', methods=['GET'])
+@blueprint.route("/random/", methods=["GET"])
+@blueprint.route("/random/", methods=["GET"])
def get_random(amount: int = 1):
"""
Retrieve random collection(s).
@@ -33,25 +34,28 @@ def get_random(amount: int = 1):
flask.Request: json structure for the collection(s)
"""
- results = list(flask.g.db['collections'].aggregate([{'$sample': {'size': amount}}]))
+ results = list(flask.g.db["collections"].aggregate([{"$sample": {"size": amount}}]))
for result in results:
# only show editors if editor/admin
- if not flask.g.current_user or\
- (not user.has_permission('DATA_MANAGEMENT') or
- flask.g.current_user['_id'] not in result['editors']):
- flask.current_app.logger.debug('Not allowed to access editors field %s',
- flask.g.current_user)
- del result['editors']
+ if not flask.g.current_user or (
+ not user.has_permission("DATA_MANAGEMENT")
+ or flask.g.current_user["_id"] not in result["editors"]
+ ):
+ flask.current_app.logger.debug(
+ "Not allowed to access editors field %s", flask.g.current_user
+ )
+ del result["editors"]
# return {_id, _title} for datasets
- result['datasets'] = [flask.g.db.datasets.find_one({'_id': dataset},
- {'title': 1})
- for dataset in result['datasets']]
- return utils.response_json({'collections': results})
+ result["datasets"] = [
+ flask.g.db.datasets.find_one({"_id": dataset}, {"title": 1})
+ for dataset in result["datasets"]
+ ]
+ return utils.response_json({"collections": results})
-@blueprint.route('//', methods=['GET'])
+@blueprint.route("//", methods=["GET"])
def get_collection(identifier):
"""
Retrieve the collection with uuid .
@@ -68,29 +72,32 @@ def get_collection(identifier):
except ValueError:
flask.abort(status=404)
- result = flask.g.db['collections'].find_one({'_id': uuid})
+ result = flask.g.db["collections"].find_one({"_id": uuid})
if not result:
return flask.Response(status=404)
# only show owner if owner/admin
- if not flask.g.current_user or\
- (not user.has_permission('DATA_MANAGEMENT') and
- flask.g.current_user['_id'] not in result['editors']):
- flask.current_app.logger.debug('Not allowed to access editors field %s',
- flask.g.current_user)
- del result['editors']
+ if not flask.g.current_user or (
+ not user.has_permission("DATA_MANAGEMENT")
+ and flask.g.current_user["_id"] not in result["editors"]
+ ):
+ flask.current_app.logger.debug(
+ "Not allowed to access editors field %s", flask.g.current_user
+ )
+ del result["editors"]
else:
- result['editors'] = utils.user_uuid_data(result['editors'], flask.g.db)
+ result["editors"] = utils.user_uuid_data(result["editors"], flask.g.db)
# return {_id, _title} for datasets
- result['datasets'] = [flask.g.db.datasets.find_one({'_id': dataset},
- {'title': 1})
- for dataset in result['datasets']]
+ result["datasets"] = [
+ flask.g.db.datasets.find_one({"_id": dataset}, {"title": 1})
+ for dataset in result["datasets"]
+ ]
- return utils.response_json({'collection': result})
+ return utils.response_json({"collection": result})
-@blueprint.route('/structure/', methods=['GET'])
+@blueprint.route("/structure/", methods=["GET"])
def get_collection_data_structure():
"""
Get an empty collection entry.
@@ -99,11 +106,11 @@ def get_collection_data_structure():
flask.Response: JSON structure with a list of collections.
"""
empty_collection = structure.collection()
- empty_collection['_id'] = ''
- return utils.response_json({'collection': empty_collection})
+ empty_collection["_id"] = ""
+ return utils.response_json({"collection": empty_collection})
-@blueprint.route('/', methods=['POST'])
+@blueprint.route("/", methods=["POST"])
@user.login_required
def add_collection(): # pylint: disable=too-many-branches
"""
@@ -121,36 +128,32 @@ def add_collection(): # pylint: disable=too-many-branches
flask.abort(status=400)
# indata validation
- validation = utils.basic_check_indata(indata, collection, prohibited=['_id'])
+ validation = utils.basic_check_indata(indata, collection, prohibited=["_id"])
if not validation[0]:
flask.abort(status=validation[1])
- # properties may only be set by users with DATA_MANAGEMENT
- if 'properties' in indata:
- if not user.has_permission('DATA_MANAGEMENT'):
- flask.abort(403)
-
- if 'title' not in indata:
+ if "title" not in indata:
flask.abort(status=400)
- if not indata.get('editors'):
- indata['editors'] = [flask.g.current_user['_id']]
+ if not indata.get("editors"):
+ indata["editors"] = [flask.g.current_user["_id"]]
- if 'datasets' in indata:
- indata['datasets'] = [utils.str_to_uuid(value) for value in indata['datasets']]
+ if "datasets" in indata:
+ indata["datasets"] = [utils.str_to_uuid(value) for value in indata["datasets"]]
collection.update(indata)
+ collection["description"] = utils.secure_description(collection["description"])
# add to db
- result = flask.g.db['collections'].insert_one(collection)
+ result = flask.g.db["collections"].insert_one(collection)
if not result.acknowledged:
- flask.current_app.logger.error('Collection insert failed: %s', collection)
+ flask.current_app.logger.error("Collection insert failed: %s", collection)
else:
- utils.make_log('collection', 'add', 'Collection added', collection)
+ utils.make_log("collection", "add", "Collection added", collection)
- return utils.response_json({'_id': result.inserted_id})
+ return utils.response_json({"_id": result.inserted_id})
-@blueprint.route('//', methods=['DELETE'])
+@blueprint.route("//", methods=["DELETE"])
@user.login_required
def delete_collection(identifier: str):
"""
@@ -165,25 +168,27 @@ def delete_collection(identifier: str):
ds_uuid = utils.str_to_uuid(identifier)
except ValueError:
return flask.abort(status=404)
- collection = flask.g.db['collections'].find_one({'_id': ds_uuid})
+ collection = flask.g.db["collections"].find_one({"_id": ds_uuid})
if not collection:
flask.abort(status=404)
# permission check
- if not user.has_permission('DATA_MANAGEMENT') and \
- flask.g.current_user['_id'] not in collection['editors']:
+ if (
+ not user.has_permission("DATA_MANAGEMENT")
+ and flask.g.current_user["_id"] not in collection["editors"]
+ ):
flask.abort(status=403)
- result = flask.g.db['collections'].delete_one({'_id': ds_uuid})
+ result = flask.g.db["collections"].delete_one({"_id": ds_uuid})
if not result.acknowledged:
- flask.current_app.logger.error('Failed to delete collection %s', ds_uuid)
+ flask.current_app.logger.error("Failed to delete collection %s", ds_uuid)
return flask.Response(status=500)
- utils.make_log('collection', 'delete', 'Deleted collection', data={'_id': ds_uuid})
+ utils.make_log("collection", "delete", "Deleted collection", data={"_id": ds_uuid})
return flask.Response(status=200)
-@blueprint.route('//', methods=['PATCH'])
+@blueprint.route("//", methods=["PATCH"])
@user.login_required
def update_collection(identifier): # pylint: disable=too-many-branches
"""
@@ -199,7 +204,7 @@ def update_collection(identifier): # pylint: disable=too-many-branches
collection_uuid = utils.str_to_uuid(identifier)
except ValueError:
return flask.abort(status=404)
- collection = flask.g.db['collections'].find_one({'_id': collection_uuid})
+ collection = flask.g.db["collections"].find_one({"_id": collection_uuid})
if not collection:
flask.abort(status=404)
@@ -209,29 +214,31 @@ def update_collection(identifier): # pylint: disable=too-many-branches
flask.abort(status=400)
# permission check
- if not user.has_permission('DATA_MANAGEMENT') and \
- flask.g.current_user['_id'] not in collection['editors'] and\
- flask.g.current_user['email'] not in collection['editors']:
- flask.current_app.logger.debug('Unauthorized update attempt (collection %s, user %s)',
- collection_uuid,
- flask.g.current_user['_id'])
+ if (
+ not user.has_permission("DATA_MANAGEMENT")
+ and flask.g.current_user["_id"] not in collection["editors"]
+ and flask.g.current_user["email"] not in collection["editors"]
+ ):
+ flask.current_app.logger.debug(
+ "Unauthorized update attempt (collection %s, user %s)",
+ collection_uuid,
+ flask.g.current_user["_id"],
+ )
flask.abort(status=403)
# indata validation
- validation = utils.basic_check_indata(indata, collection, prohibited=('_id'))
+ validation = utils.basic_check_indata(indata, collection, prohibited=("_id"))
if not validation[0]:
flask.abort(status=validation[1])
- # properties may only be set by users with DATA_MANAGEMENT
- if 'properties' in indata:
- if not user.has_permission('DATA_MANAGEMENT'):
- flask.abort(403)
+ if "datasets" in indata:
+ indata["datasets"] = [utils.str_to_uuid(value) for value in indata["datasets"]]
- if 'datasets' in indata:
- indata['datasets'] = [utils.str_to_uuid(value) for value in indata['datasets']]
+ if "editors" in indata and not indata["editors"]:
+ indata["editors"] = [flask.g.current_user["_id"]]
- if 'editors' in indata and not indata['editors']:
- indata['editors'] = [flask.g.current_user['_id']]
+ if "description" in indata:
+ indata["description"] = utils.secure_description(indata["description"])
is_different = False
for field in indata:
@@ -240,17 +247,19 @@ def update_collection(identifier): # pylint: disable=too-many-branches
break
if indata and is_different:
- result = flask.g.db['collections'].update_one({'_id': collection['_id']}, {'$set': indata})
+ result = flask.g.db["collections"].update_one(
+ {"_id": collection["_id"]}, {"$set": indata}
+ )
if not result.acknowledged:
- flask.current_app.logger.error('Collection update failed: %s', indata)
+ flask.current_app.logger.error("Collection update failed: %s", indata)
else:
collection.update(indata)
- utils.make_log('collection', 'edit', 'Collection updated', collection)
+ utils.make_log("collection", "edit", "Collection updated", collection)
return flask.Response(status=200)
-@blueprint.route('/user/', methods=['GET'])
+@blueprint.route("/user/", methods=["GET"])
@user.login_required
def list_user_collections(): # pylint: disable=too-many-branches
"""
@@ -259,12 +268,13 @@ def list_user_collections(): # pylint: disable=too-many-branches
Returns:
flask.Response: JSON structure.
"""
- results = list(flask.g.db['collections']
- .find({'editors': flask.g.current_user['_id']}))
- return utils.response_json({'collections': results})
+ results = list(
+ flask.g.db["collections"].find({"editors": flask.g.current_user["_id"]})
+ )
+ return utils.response_json({"collections": results})
-@blueprint.route('//log/', methods=['GET'])
+@blueprint.route("//log/", methods=["GET"])
@user.login_required
def get_collection_log(identifier: str = None):
"""
@@ -283,21 +293,28 @@ def get_collection_log(identifier: str = None):
except ValueError:
flask.abort(status=404)
- if not user.has_permission('DATA_MANAGEMENT'):
- collection_data = flask.g.db['collections'].find_one({'_id': collection_uuid})
+ if not user.has_permission("DATA_MANAGEMENT"):
+ collection_data = flask.g.db["collections"].find_one({"_id": collection_uuid})
if not collection_data:
flask.abort(403)
- if flask.g.current_user['_id'] not in collection_data['editors']:
+ if flask.g.current_user["_id"] not in collection_data["editors"]:
flask.abort(403)
- collection_logs = list(flask.g.db['logs'].find({'data_type': 'collection',
- 'data._id': collection_uuid}))
+ collection_logs = list(
+ flask.g.db["logs"].find(
+ {"data_type": "collection", "data._id": collection_uuid}
+ )
+ )
for log in collection_logs:
- del log['data_type']
+ del log["data_type"]
utils.incremental_logs(collection_logs)
- return utils.response_json({'entry_id': collection_uuid,
- 'data_type': 'collection',
- 'logs': collection_logs})
+ return utils.response_json(
+ {
+ "entry_id": collection_uuid,
+ "data_type": "collection",
+ "logs": collection_logs,
+ }
+ )
diff --git a/backend/config.py b/backend/config.py
index bb84268f..09e93f5c 100755
--- a/backend/config.py
+++ b/backend/config.py
@@ -11,7 +11,7 @@
import yaml
-def read_config(path: str = ''):
+def read_config(path: str = ""):
"""
Look for settings.yaml and parse the settings from there.
@@ -27,16 +27,15 @@ def read_config(path: str = ''):
FileNotFoundError: No settings file found
"""
- file_locations = [os.getcwd(),
- os.pardir]
+ file_locations = [os.getcwd(), os.pardir]
if not path:
for location in file_locations:
- fpath = os.path.join(location, 'config.yaml')
+ fpath = os.path.join(location, "config.yaml")
if os.path.exists(fpath):
path = fpath
break
- with open(path, 'r') as in_file:
+ with open(path, "r") as in_file:
return yaml.load(in_file, Loader=yaml.FullLoader)
@@ -47,31 +46,33 @@ def init() -> dict:
Returns:
dict: The config.
"""
- config_file = ''
- arg = '--config_file'
+ config_file = ""
+ arg = "--config_file"
if arg in sys.argv:
try:
- config_file = sys.argv[sys.argv.index(arg)+1]
+ config_file = sys.argv[sys.argv.index(arg) + 1]
except IndexError:
- logging.error('No argument for --config_file')
+ logging.error("No argument for --config_file")
sys.exit(1)
config = read_config(config_file)
- if config['dev_mode']['testing']:
+ if config["dev_mode"]["testing"]:
logging.getLogger().setLevel(logging.DEBUG)
- config['DEBUG'] = True
- config['TESTING'] = True
- config['ENV'] = 'development'
+ config["DEBUG"] = True
+ config["TESTING"] = True
+ config["ENV"] = "development"
- if config.get('oidc'):
- for oidc_entry in config['oidc']:
+ if config.get("oidc"):
+ for oidc_entry in config["oidc"]:
base_name = oidc_entry.upper()
- for conf_part in config['oidc'][oidc_entry]:
- config[f'{base_name}_{conf_part.upper()}'] = config['oidc'][oidc_entry][conf_part]
- config['oidc_names'] = config['oidc'].keys()
- del config['oidc']
-
- config['SESSION_COOKIE_NAME'] = 'dt_session'
- config['SECRET_KEY'] = config['flask']['secret']
- config['SESSION_COOKIE_SAMESITE'] = 'Lax'
+ for conf_part in config["oidc"][oidc_entry]:
+ config[f"{base_name}_{conf_part.upper()}"] = config["oidc"][oidc_entry][
+ conf_part
+ ]
+ config["oidc_names"] = config["oidc"].keys()
+ del config["oidc"]
+
+ config["SESSION_COOKIE_NAME"] = "dt_session"
+ config["SECRET_KEY"] = config["flask"]["secret"]
+ config["SESSION_COOKIE_SAMESITE"] = "Lax"
return config
diff --git a/backend/dataset.py b/backend/dataset.py
index 8469e9ad..fb39e50e 100644
--- a/backend/dataset.py
+++ b/backend/dataset.py
@@ -7,34 +7,37 @@
import utils
import user
-blueprint = flask.Blueprint('dataset', __name__) # pylint: disable=invalid-name
+blueprint = flask.Blueprint("dataset", __name__) # pylint: disable=invalid-name
-@blueprint.route('/', methods=['GET'])
+@blueprint.route("/", methods=["GET"])
def list_datasets():
"""Provide a simplified list of all available datasets."""
- results = list(flask.g.db['datasets'].find(projection={'title': 1,
- '_id': 1,
- 'tags': 1,
- 'properties': 1}))
- return utils.response_json({'datasets': results})
+ results = list(
+ flask.g.db["datasets"].find(
+ projection={"title": 1, "_id": 1, "tags": 1, "properties": 1}
+ )
+ )
+ return utils.response_json({"datasets": results})
-@blueprint.route('/user/', methods=['GET'])
+@blueprint.route("/user/", methods=["GET"])
@user.login_required
def list_user_data():
"""List all datasets belonging to current user."""
- user_orders = list(flask.g.db['orders'].find({'$or': [{'receivers': flask.session['user_id']},
- {'editors': flask.session['user_id']}]},
- {'datasets': 1}))
- uuids = list(ds for entry in user_orders for ds in entry['datasets'])
- user_datasets = list(flask.g.db['datasets'].find({'_id': {'$in': uuids}}))
+ user_orders = list(
+ flask.g.db["orders"].find(
+ {"editors": flask.session["user_id"]}, {"datasets": 1}
+ )
+ )
+ uuids = list(ds for entry in user_orders for ds in entry["datasets"])
+ user_datasets = list(flask.g.db["datasets"].find({"_id": {"$in": uuids}}))
- return utils.response_json({'datasets': user_datasets})
+ return utils.response_json({"datasets": user_datasets})
-@blueprint.route('/random/', methods=['GET'])
-@blueprint.route('/random//', methods=['GET'])
+@blueprint.route("/random/", methods=["GET"])
+@blueprint.route("/random//", methods=["GET"])
def get_random_ds(amount: int = 1):
"""
Retrieve random dataset(s).
@@ -46,14 +49,17 @@ def get_random_ds(amount: int = 1):
flask.Response: json structure for the dataset(s)
"""
- results = list(flask.g.db['datasets'].aggregate([{'$sample': {'size': amount}},
- {'$project': {'_id': 1}}]))
+ results = list(
+ flask.g.db["datasets"].aggregate(
+ [{"$sample": {"size": amount}}, {"$project": {"_id": 1}}]
+ )
+ )
for i, result in enumerate(results):
- results[i] = build_dataset_info(result['_id'].hex)
- return utils.response_json({'datasets': results})
+ results[i] = build_dataset_info(result["_id"].hex)
+ return utils.response_json({"datasets": results})
-@blueprint.route('/structure/', methods=['GET'])
+@blueprint.route("/structure/", methods=["GET"])
def get_dataset_data_structure():
"""
Get an empty dataset entry.
@@ -62,11 +68,11 @@ def get_dataset_data_structure():
flask.Response: JSON structure with a list of datasets.
"""
empty_dataset = structure.dataset()
- empty_dataset['_id'] = ''
- return utils.response_json({'dataset': empty_dataset})
+ empty_dataset["_id"] = ""
+ return utils.response_json({"dataset": empty_dataset})
-@blueprint.route('//', methods=['GET'])
+@blueprint.route("//", methods=["GET"])
def get_dataset(identifier):
"""
Retrieve the dataset with uuid .
@@ -81,9 +87,10 @@ def get_dataset(identifier):
result = build_dataset_info(identifier)
if not result:
return flask.Response(status=404)
- return utils.response_json({'dataset': result})
+ return utils.response_json({"dataset": result})
-@blueprint.route('/', methods=['POST'])
+
+@blueprint.route("/", methods=["POST"])
@user.login_required
def add_dataset(): # pylint: disable=too-many-branches
"""
@@ -97,62 +104,64 @@ def add_dataset(): # pylint: disable=too-many-branches
indata = flask.json.loads(flask.request.data)
except json.decoder.JSONDecodeError:
flask.abort(status=400)
- if not 'order' in indata:
- flask.current_app.logger.debug('Order field missing')
+ if not "order" in indata:
+ flask.current_app.logger.debug("Order field missing")
flask.abort(status=400)
try:
- order_uuid = utils.str_to_uuid(indata['order'])
+ order_uuid = utils.str_to_uuid(indata["order"])
except ValueError:
- flask.current_app.logger.debug('Incorrect order UUID (%s)', indata['order'])
+ flask.current_app.logger.debug("Incorrect order UUID (%s)", indata["order"])
flask.abort(status=400)
- order = flask.g.db['orders'].find_one({'_id': order_uuid})
+ order = flask.g.db["orders"].find_one({"_id": order_uuid})
if not order:
- flask.current_app.logger.debug('Order (%s) not in db', indata['order'])
+ flask.current_app.logger.debug("Order (%s) not in db", indata["order"])
flask.abort(status=400)
- if not (user.has_permission('DATA_MANAGEMENT') or
- flask.g.current_user['_id'] in order['editors']):
+ if not (
+ user.has_permission("DATA_MANAGEMENT")
+ or flask.g.current_user["_id"] in order["editors"]
+ ):
return flask.abort(status=403)
- del indata['order']
-
- # properties may only be set by users with DATA_MANAGEMENT
- if 'properties' in indata:
- if not user.has_permission('DATA_MANAGEMENT'):
- flask.abort(403)
+ del indata["order"]
# create new dataset
dataset = structure.dataset()
- validation = utils.basic_check_indata(indata, dataset, ['_id'])
+ validation = utils.basic_check_indata(indata, dataset, ["_id"])
if not validation.result:
flask.abort(status=validation.status)
dataset.update(indata)
+ dataset["description"] = utils.secure_description(dataset["description"])
+
# add to db
- result_ds = flask.g.db['datasets'].insert_one(dataset)
+ result_ds = flask.g.db["datasets"].insert_one(dataset)
if not result_ds.acknowledged:
- flask.current_app.logger.error('Dataset insert failed: %s', dataset)
+ flask.current_app.logger.error("Dataset insert failed: %s", dataset)
else:
- utils.make_log('dataset',
- 'add',
- f'Dataset added for order {order_uuid}',
- dataset)
+ utils.make_log(
+ "dataset", "add", f"Dataset added for order {order_uuid}", dataset
+ )
- result_o = flask.g.db['orders'].update_one({'_id': order_uuid},
- {'$push': {'datasets': dataset['_id']}})
+ result_o = flask.g.db["orders"].update_one(
+ {"_id": order_uuid}, {"$push": {"datasets": dataset["_id"]}}
+ )
if not result_o.acknowledged:
- flask.current_app.logger.error('Order %s insert failed: ADD dataset %s',
- order_uuid, dataset['_id'])
+ flask.current_app.logger.error(
+ "Order %s insert failed: ADD dataset %s", order_uuid, dataset["_id"]
+ )
else:
- order = flask.g.db['orders'].find_one({'_id': order_uuid})
+ order = flask.g.db["orders"].find_one({"_id": order_uuid})
- utils.make_log('order',
- 'edit',
- f'Dataset {result_ds.inserted_id} added for order',
- order)
+ utils.make_log(
+ "order",
+ "edit",
+ f"Dataset {result_ds.inserted_id} added for order",
+ order,
+ )
- return utils.response_json({'_id': result_ds.inserted_id})
+ return utils.response_json({"_id": result_ds.inserted_id})
-@blueprint.route('//', methods=['DELETE'])
+@blueprint.route("//", methods=["DELETE"])
@user.login_required
def delete_dataset(identifier: str):
"""
@@ -167,46 +176,52 @@ def delete_dataset(identifier: str):
ds_uuid = utils.str_to_uuid(identifier)
except ValueError:
return flask.abort(status=404)
- dataset = flask.g.db['datasets'].find_one({'_id': ds_uuid})
+ dataset = flask.g.db["datasets"].find_one({"_id": ds_uuid})
if not dataset:
flask.abort(status=404)
# permission check
- order = flask.g.db['orders'].find_one({'datasets': ds_uuid})
- if not user.has_permission('DATA_MANAGEMENT') and \
- flask.g.current_user['_id'] not in order['editors']:
+ order = flask.g.db["orders"].find_one({"datasets": ds_uuid})
+ if (
+ not user.has_permission("DATA_MANAGEMENT")
+ and flask.g.current_user["_id"] not in order["editors"]
+ ):
flask.abort(status=403)
- result = flask.g.db['datasets'].delete_one({'_id': ds_uuid})
+ result = flask.g.db["datasets"].delete_one({"_id": ds_uuid})
if not result.acknowledged:
- flask.current_app.logger.error('Failed to delete dataset %s', ds_uuid)
+ flask.current_app.logger.error("Failed to delete dataset %s", ds_uuid)
return flask.Response(status=500)
- utils.make_log('dataset', 'delete', 'Deleted dataset', data={'_id': ds_uuid})
+ utils.make_log("dataset", "delete", "Deleted dataset", data={"_id": ds_uuid})
- for entry in flask.g.db['orders'].find({'datasets': ds_uuid}):
- result = flask.g.db['orders'].update_one({'_id': entry['_id']},
- {'$pull': {'datasets': ds_uuid}})
+ for entry in flask.g.db["orders"].find({"datasets": ds_uuid}):
+ result = flask.g.db["orders"].update_one(
+ {"_id": entry["_id"]}, {"$pull": {"datasets": ds_uuid}}
+ )
if not result.acknowledged:
- flask.current_app.logger.error('Failed to delete dataset %s in order %s',
- ds_uuid, entry['_id'])
+ flask.current_app.logger.error(
+ "Failed to delete dataset %s in order %s", ds_uuid, entry["_id"]
+ )
return flask.Response(status=500)
- new_data = flask.g.db['orders'].find_one({'_id': entry['_id']})
- utils.make_log('order', 'edit', f'Deleted dataset {ds_uuid}', new_data)
+ new_data = flask.g.db["orders"].find_one({"_id": entry["_id"]})
+ utils.make_log("order", "edit", f"Deleted dataset {ds_uuid}", new_data)
- for entry in flask.g.db['collections'].find({'datasets': ds_uuid}):
- flask.g.db['collections'].update_one({'_id': entry['_id']},
- {'$pull': {'datasets': ds_uuid}})
+ for entry in flask.g.db["collections"].find({"datasets": ds_uuid}):
+ flask.g.db["collections"].update_one(
+ {"_id": entry["_id"]}, {"$pull": {"datasets": ds_uuid}}
+ )
if not result.acknowledged:
- flask.current_app.logger.error('Failed to delete dataset %s in project %s',
- ds_uuid, entry['_id'])
+ flask.current_app.logger.error(
+ "Failed to delete dataset %s in project %s", ds_uuid, entry["_id"]
+ )
return flask.Response(status=500)
- new_data = flask.g.db['collections'].find_one({'_id': entry['_id']})
- utils.make_log('collection', 'edit', f'Deleted dataset {ds_uuid}', new_data)
+ new_data = flask.g.db["collections"].find_one({"_id": entry["_id"]})
+ utils.make_log("collection", "edit", f"Deleted dataset {ds_uuid}", new_data)
return flask.Response(status=200)
-@blueprint.route('//', methods=['PATCH'])
+@blueprint.route("//", methods=["PATCH"])
@user.login_required
def update_dataset(identifier):
"""
@@ -217,33 +232,32 @@ def update_dataset(identifier):
Returns:
flask.Response: success: 200, failure: 400
-
"""
try:
ds_uuid = utils.str_to_uuid(identifier)
except ValueError:
return flask.abort(status=404)
- dataset = flask.g.db['datasets'].find_one({'_id': ds_uuid})
+ dataset = flask.g.db["datasets"].find_one({"_id": ds_uuid})
if not dataset:
flask.abort(status=404)
# permissions
- order = flask.g.db['orders'].find_one({'datasets': ds_uuid})
- if not user.has_permission('DATA_MANAGEMENT') and \
- flask.g.current_user['_id'] not in order['editors']:
+ order = flask.g.db["orders"].find_one({"datasets": ds_uuid})
+ if (
+ not user.has_permission("DATA_MANAGEMENT")
+ and flask.g.current_user["_id"] not in order["editors"]
+ ):
flask.abort(status=403)
try:
indata = flask.json.loads(flask.request.data)
except json.decoder.JSONDecodeError:
flask.abort(status=400)
- validation = utils.basic_check_indata(indata, dataset, prohibited=('_id'))
+ validation = utils.basic_check_indata(indata, dataset, prohibited=("_id"))
if not validation[0]:
flask.abort(status=validation[1])
- # properties may only be set by users with DATA_MANAGEMENT
- if 'properties' in indata:
- if not user.has_permission('DATA_MANAGEMENT'):
- flask.abort(403)
+ if "description" in indata:
+ indata["description"] = utils.secure_description(indata["description"])
is_different = False
for field in indata:
@@ -252,18 +266,20 @@ def update_dataset(identifier):
break
if is_different:
- result = flask.g.db['datasets'].update_one({'_id': dataset['_id']}, {'$set': indata})
+ result = flask.g.db["datasets"].update_one(
+ {"_id": dataset["_id"]}, {"$set": indata}
+ )
if not result.acknowledged:
- flask.current_app.logger.error('Dataset update failed: %s', dataset)
+ flask.current_app.logger.error("Dataset update failed: %s", dataset)
flask.abort(status=500)
else:
dataset.update(indata)
- utils.make_log('dataset', 'edit', 'Dataset updated', dataset)
+ utils.make_log("dataset", "edit", "Dataset updated", dataset)
return flask.Response(status=200)
-@blueprint.route('//log/', methods=['GET'])
+@blueprint.route("//log/", methods=["GET"])
@user.login_required
def get_dataset_log(identifier: str = None):
"""
@@ -282,22 +298,24 @@ def get_dataset_log(identifier: str = None):
except ValueError:
flask.abort(status=404)
- if not user.has_permission('DATA_MANAGEMENT'):
- order_data = flask.g.db['orders'].find_one({'datasets': dataset_uuid})
+ if not user.has_permission("DATA_MANAGEMENT"):
+ order_data = flask.g.db["orders"].find_one({"datasets": dataset_uuid})
if not order_data:
flask.abort(403)
- if flask.g.current_user['_id'] not in order_data['editors']:
+ if flask.g.current_user["_id"] not in order_data["editors"]:
flask.abort(403)
- dataset_logs = list(flask.g.db['logs'].find({'data_type': 'dataset', 'data._id': dataset_uuid}))
+ dataset_logs = list(
+ flask.g.db["logs"].find({"data_type": "dataset", "data._id": dataset_uuid})
+ )
for log in dataset_logs:
- del log['data_type']
+ del log["data_type"]
utils.incremental_logs(dataset_logs)
- return utils.response_json({'entry_id': dataset_uuid,
- 'data_type': 'dataset',
- 'logs': dataset_logs})
+ return utils.response_json(
+ {"entry_id": dataset_uuid, "data_type": "dataset", "logs": dataset_logs}
+ )
# helper functions
@@ -315,26 +333,33 @@ def build_dataset_info(identifier: str):
dataset_uuid = utils.str_to_uuid(identifier)
except ValueError:
return None
- dataset = flask.g.db['datasets'].find_one({'_id': dataset_uuid})
+ dataset = flask.g.db["datasets"].find_one({"_id": dataset_uuid})
if not dataset:
return None
- order = flask.g.db['orders'].find_one({'datasets': dataset_uuid})
-
- if (user.has_permission('DATA_MANAGEMENT') or\
- flask.g.db.current_user['id'] in order['editors']):
- dataset['order'] = order['_id']
- dataset['related'] = list(flask.g.db['datasets'].find({'_id': {'$in': order['datasets']}},
- {'title': 1}))
- dataset['related'].remove({'_id': dataset['_id'], 'title': dataset['title']})
- dataset['collections'] = list(flask.g.db['projects'].find({'datasets': dataset_uuid},
- {'title': 1}))
- for field in ('editors', 'generators', 'authors'):
- if field == 'editors' and\
- (not user.has_permission('DATA_MANAGEMENT') and\
- flask.g.db.current_user['id'] not in order[field]):
+ order = flask.g.db["orders"].find_one({"datasets": dataset_uuid})
+
+ if (
+ user.has_permission("DATA_MANAGEMENT")
+ or flask.g.db.current_user["id"] in order["editors"]
+ ):
+ dataset["order"] = order["_id"]
+ dataset["related"] = list(
+ flask.g.db["datasets"].find({"_id": {"$in": order["datasets"]}}, {"title": 1})
+ )
+ dataset["related"].remove({"_id": dataset["_id"], "title": dataset["title"]})
+ dataset["collections"] = list(
+ flask.g.db["projects"].find({"datasets": dataset_uuid}, {"title": 1})
+ )
+ for field in ("editors", "generators", "authors"):
+ if field == "editors" and (
+ not user.has_permission("DATA_MANAGEMENT")
+ and flask.g.db.current_user["id"] not in order[field]
+ ):
continue
dataset[field] = utils.user_uuid_data(order[field], flask.g.db)
- dataset['organisation'] = utils.user_uuid_data(order[field], flask.g.db)
- dataset['organisation'] = dataset['organisation'][0] if dataset['organisation'] else ''
+ dataset["organisation"] = utils.user_uuid_data(order[field], flask.g.db)
+ dataset["organisation"] = (
+ dataset["organisation"][0] if dataset["organisation"] else ""
+ )
return dataset
diff --git a/backend/db_management.py b/backend/db_management.py
index d9efc1e1..5d5c1a6b 100644
--- a/backend/db_management.py
+++ b/backend/db_management.py
@@ -8,6 +8,7 @@
DB_VERSION = 1
+
def check_db(config: dict):
"""
Perform database checks.
@@ -19,7 +20,7 @@ def check_db(config: dict):
config (dict): Configuration for the data tracker
"""
db = utils.get_db(utils.get_dbclient(config), config)
- db_initialised = db['db_status'].find_one({'_id': 'init_db'})
+ db_initialised = db["db_status"].find_one({"_id": "init_db"})
if not db_initialised:
init_db(db)
else:
@@ -33,18 +34,14 @@ def init_db(db):
- create a default user
- set current db_version
"""
- db['db_status'].insert_one({'_id': 'init_db',
- 'started': True,
- 'user_added': False,
- 'finished': False})
+ db["db_status"].insert_one(
+ {"_id": "init_db", "started": True, "user_added": False, "finished": False}
+ )
add_default_user(db)
# Set DB version
- db['db_status'].insert_one({'_id': 'db_version',
- 'version': DB_VERSION})
- db['db_status'].update_one({'_id': 'init_db'},
- {'$set': {'finished': True}})
-
+ db["db_status"].insert_one({"_id": "db_version", "version": DB_VERSION})
+ db["db_status"].update_one({"_id": "init_db"}, {"$set": {"finished": True}})
def add_default_user(db):
@@ -62,21 +59,26 @@ def add_default_user(db):
Api_key: 1234
Auth_id: default::default
"""
- logging.info('Attempting to add default user')
+ logging.info("Attempting to add default user")
new_user = structure.user()
- api_salt = 'fedcba09'
- new_user.update({'name': 'Default User',
- 'email': 'default_user@example.com',
- 'permissions': ['USER_MANAGEMENT'],
- 'api_key': utils.gen_api_key_hash('1234', api_salt),
- 'api_salt': api_salt,
- 'auth_ids': ['default::default']})
+ api_salt = "fedcba09"
+ new_user.update(
+ {
+ "name": "Default User",
+ "email": "default_user@example.com",
+ "permissions": ["USER_MANAGEMENT"],
+ "api_key": utils.gen_api_key_hash("1234", api_salt),
+ "api_salt": api_salt,
+ "auth_ids": ["default::default"],
+ }
+ )
result = db.users.insert_one(new_user)
- print(result)
- db['db_status'].update_one({'_id': 'init_db'},
- {'$set': {'user_added': True}})
- logging.info('Default user added')
+ db["db_status"].update_one({"_id": "init_db"}, {"$set": {"user_added": True}})
+ if result.acknowledged:
+ logging.info("Default user added")
+ else:
+ logging.error("Failed to add default user")
def check_migrations(db):
@@ -86,13 +88,13 @@ def check_migrations(db):
Args:
config (dict): Configuration for the data tracker
"""
- db_version = db['db_status'].find_one({'_id': 'db_version'})
- if db_version['version'] > DB_VERSION:
- logging.critical('The database is newer than the software')
+ db_version = db["db_status"].find_one({"_id": "db_version"})
+ if db_version["version"] > DB_VERSION:
+ logging.critical("The database is newer than the software")
sys.exit(1)
- elif db_version['version'] == DB_VERSION:
- logging.info('The database is up-to-date')
+ elif db_version["version"] == DB_VERSION:
+ logging.info("The database is up-to-date")
- for i in range(db_version['version'], DB_VERSION):
- logging.info('Database migration for version %d to %d starting', i, i+1)
+ for i in range(db_version["version"], DB_VERSION):
+ logging.info("Database migration for version %d to %d starting", i, i + 1)
MIGRATIONS[i](db)
diff --git a/backend/developer.py b/backend/developer.py
index 86014873..3c9d1792 100644
--- a/backend/developer.py
+++ b/backend/developer.py
@@ -5,10 +5,10 @@
import user
-blueprint = flask.Blueprint('developer', __name__) # pylint: disable=invalid-name
+blueprint = flask.Blueprint("developer", __name__) # pylint: disable=invalid-name
-@blueprint.route('/login/')
+@blueprint.route("/login/")
def login(identifier: str):
"""
Log in without password.
@@ -19,25 +19,24 @@ def login(identifier: str):
res = user.do_login(auth_id=identifier)
if res:
response = flask.Response(status=200)
- response.set_cookie('loggedIn', 'true')
return response
return flask.Response(status=500)
-@blueprint.route('/hello')
+@blueprint.route("/hello")
def api_hello():
"""Test request."""
- return flask.jsonify({'test': 'success'})
+ return flask.jsonify({"test": "success"})
-@blueprint.route('/loginhello')
+@blueprint.route("/loginhello")
@user.login_required
def login_hello():
"""Test request requiring login."""
- return flask.jsonify({'test': 'success'})
+ return flask.jsonify({"test": "success"})
-@blueprint.route('/hello/')
+@blueprint.route("/hello/")
def permission_hello(permission: str):
"""
Test request requiring the given permission.
@@ -48,24 +47,25 @@ def permission_hello(permission: str):
if not user.has_permission(permission):
flask.abort(status=403)
- return flask.jsonify({'test': 'success'})
+ return flask.jsonify({"test": "success"})
-@blueprint.route('/csrftest', methods=['POST', 'PATCH', 'POST', 'DELETE'])
+@blueprint.route("/csrftest", methods=["POST", "PATCH", "POST", "DELETE"])
def csrf_test():
"""Test csrf tokens."""
- return flask.jsonify({'test': 'success'})
+ return flask.jsonify({"test": "success"})
-@blueprint.route('/test_datasets')
+@blueprint.route("/test_datasets")
def get_added_ds():
"""Get datasets added during testing."""
- added = list(flask.g.db['datasets'].find({'description': 'Test dataset'},
- {'_id': 1}))
- return flask.jsonify({'datasets': added})
+ added = list(
+ flask.g.db["datasets"].find({"description": "Test dataset"}, {"_id": 1})
+ )
+ return flask.jsonify({"datasets": added})
-@blueprint.route('/session')
+@blueprint.route("/session")
def list_session():
"""List all session variables."""
session = copy.deepcopy(flask.session)
@@ -74,7 +74,7 @@ def list_session():
return flask.jsonify(session)
-@blueprint.route('/user/me')
+@blueprint.route("/user/me")
def list_current_user():
"""List all session variables."""
current_user = flask.g.current_user
@@ -83,7 +83,7 @@ def list_current_user():
return flask.jsonify(current_user)
-@blueprint.route('/config')
+@blueprint.route("/config")
def list_config():
"""List all session variables."""
config = copy.deepcopy(flask.current_app.config)
@@ -92,8 +92,8 @@ def list_config():
return flask.jsonify(config)
-@blueprint.route('/quit')
+@blueprint.route("/quit")
def stop_server():
"""Shutdown the flask server."""
- flask.request.environ.get('werkzeug.server.shutdown')()
+ flask.request.environ.get("werkzeug.server.shutdown")()
return flask.Response(status=200)
diff --git a/backend/exceptions.py b/backend/exceptions.py
index abaa751b..d8b7a4a6 100644
--- a/backend/exceptions.py
+++ b/backend/exceptions.py
@@ -1,4 +1,5 @@
"""Custom exception definitions."""
+
class AuthError(Exception):
"""Raised if a permission check fails."""
diff --git a/backend/order.py b/backend/order.py
index 09cdab24..01e2ecee 100644
--- a/backend/order.py
+++ b/backend/order.py
@@ -14,7 +14,7 @@
import user
import utils
-blueprint = flask.Blueprint('order', __name__) # pylint: disable=invalid-name
+blueprint = flask.Blueprint("order", __name__) # pylint: disable=invalid-name
@blueprint.before_request
@@ -26,11 +26,11 @@ def prepare():
"""
if not flask.g.current_user:
flask.abort(status=401)
- if not user.has_permission('ORDERS'):
+ if not user.has_permission("ORDERS"):
flask.abort(status=403)
-@blueprint.route('/', methods=['GET'])
+@blueprint.route("/", methods=["GET"])
def list_orders():
"""
List all orders visible to the current user.
@@ -38,21 +38,24 @@ def list_orders():
Returns:
flask.Response: JSON structure with a list of orders.
"""
- if user.has_permission('DATA_MANAGEMENT'):
- orders = list(flask.g.db['orders'].find(projection={'_id': 1,
- 'title': 1,
- 'tags': 1,
- 'properties': 1}))
+ if user.has_permission("DATA_MANAGEMENT"):
+ orders = list(
+ flask.g.db["orders"].find(
+ projection={"_id": 1, "title": 1, "tags": 1, "properties": 1}
+ )
+ )
else:
- orders = list(flask.g.db['orders']
- .find({'editors': flask.g.current_user['_id']},
- projection={'_id': 1,
- 'title': 1}))
+ orders = list(
+ flask.g.db["orders"].find(
+ {"editors": flask.g.current_user["_id"]},
+ projection={"_id": 1, "title": 1},
+ )
+ )
- return utils.response_json({'orders': orders})
+ return utils.response_json({"orders": orders})
-@blueprint.route('/structure/', methods=['GET'])
+@blueprint.route("/structure/", methods=["GET"])
def get_order_data_structure():
"""
Get an empty order entry.
@@ -61,12 +64,12 @@ def get_order_data_structure():
flask.Response: JSON structure with a list of orders.
"""
empty_order = structure.order()
- empty_order['_id'] = ''
- return utils.response_json({'order': empty_order})
+ empty_order["_id"] = ""
+ return utils.response_json({"order": empty_order})
-@blueprint.route('/user/', defaults={'user_id': None}, methods=['GET'])
-@blueprint.route('/user//', methods=['GET'])
+@blueprint.route("/user/", defaults={"user_id": None}, methods=["GET"])
+@blueprint.route("/user//", methods=["GET"])
def list_orders_user(user_id: str):
"""
List all orders belonging to the provided user.
@@ -78,24 +81,26 @@ def list_orders_user(user_id: str):
flask.Response: Json structure with a list of orders.
"""
if user_id:
- if not user.has_permission('OWNERS_READ'):
+ if not user.has_permission("OWNERS_READ"):
flask.abort(status=403)
try:
user_uuid = utils.str_to_uuid(user_id)
except ValueError:
return flask.abort(status=404)
- if not flask.g.db['users'].find_one({'_id': user_uuid}):
+ if not flask.g.db["users"].find_one({"_id": user_uuid}):
return flask.abort(status=404)
else: # current user
- user_uuid = flask.session['user_id']
- orders = list(flask.g.db['orders'].find({'editors': user_uuid},
- projection={'_id': 1,
- 'title': 1}))
+ user_uuid = flask.session["user_id"]
+ orders = list(
+ flask.g.db["orders"].find(
+ {"editors": user_uuid}, projection={"_id": 1, "title": 1}
+ )
+ )
- return utils.response_json({'orders': orders})
+ return utils.response_json({"orders": orders})
-@blueprint.route('//', methods=['GET'])
+@blueprint.route("//", methods=["GET"])
def get_order(identifier):
"""
Retrieve the order with the provided uuid.
@@ -112,19 +117,21 @@ def get_order(identifier):
uuid = utils.str_to_uuid(identifier)
except ValueError:
flask.abort(status=404)
- order_data = flask.g.db['orders'].find_one({'_id': uuid})
+ order_data = flask.g.db["orders"].find_one({"_id": uuid})
if not order_data:
flask.abort(status=404)
- if not (user.has_permission('DATA_MANAGEMENT') or
- flask.session['user_id'] in order_data['editors']):
+ if not (
+ user.has_permission("DATA_MANAGEMENT")
+ or flask.session["user_id"] in order_data["editors"]
+ ):
flask.abort(status=403)
prepare_order_response(order_data, flask.g.db)
- return utils.response_json({'order': order_data})
+ return utils.response_json({"order": order_data})
-@blueprint.route('//log/', methods=['GET'])
+@blueprint.route("//log/", methods=["GET"])
def get_order_logs(identifier):
"""
List changes to the dataset.
@@ -144,23 +151,25 @@ def get_order_logs(identifier):
except ValueError:
flask.abort(status=404)
- if not user.has_permission('DATA_MANAGEMENT'):
- order_data = flask.g.db['orders'].find_one({'_id': order_uuid})
- if not order_data or flask.g.current_user['_id'] not in order_data['editors']:
+ if not user.has_permission("DATA_MANAGEMENT"):
+ order_data = flask.g.db["orders"].find_one({"_id": order_uuid})
+ if not order_data or flask.g.current_user["_id"] not in order_data["editors"]:
flask.abort(403)
- order_logs = list(flask.g.db['logs'].find({'data_type': 'order', 'data._id': order_uuid}))
+ order_logs = list(
+ flask.g.db["logs"].find({"data_type": "order", "data._id": order_uuid})
+ )
for log in order_logs:
- del log['data_type']
+ del log["data_type"]
utils.incremental_logs(order_logs)
- return utils.response_json({'entry_id': order_uuid,
- 'data_type': 'order',
- 'logs': order_logs})
+ return utils.response_json(
+ {"entry_id": order_uuid, "data_type": "order", "logs": order_logs}
+ )
-@blueprint.route('/base/', methods=['GET'])
+@blueprint.route("/base/", methods=["GET"])
def get_empty_order():
"""
Provide the basic data structure for an empty order.
@@ -170,12 +179,12 @@ def get_empty_order():
"""
# create new order
order = structure.order()
- order['_id'] = ''
+ order["_id"] = ""
- return utils.response_json({'order': order})
+ return utils.response_json({"order": order})
-@blueprint.route('/', methods=['POST'])
+@blueprint.route("/", methods=["POST"])
def add_order():
"""
Add an order.
@@ -188,42 +197,38 @@ def add_order():
try:
indata = flask.json.loads(flask.request.data)
except json.decoder.JSONDecodeError:
- flask.current_app.logger.debug('Bad json')
+ flask.current_app.logger.debug("Bad json")
flask.abort(status=400)
- validation = utils.basic_check_indata(indata, new_order, ['_id', 'datasets'])
+ validation = utils.basic_check_indata(indata, new_order, ["_id", "datasets"])
if not validation.result:
flask.abort(status=validation.status)
- # properties may only be set by users with DATA_MANAGEMENT
- if 'properties' in indata:
- if not user.has_permission('DATA_MANAGEMENT'):
- flask.abort(403)
-
# convert all incoming uuids to uuid.UUID
- for field in ('editors', 'authors', 'generators'):
+ for field in ("editors", "authors", "generators"):
if field in indata:
indata[field] = [utils.str_to_uuid(entry) for entry in indata[field]]
- if 'organisation' in indata:
- if indata['organisation']:
- indata['organisation'] = utils.str_to_uuid(indata['organisation'])
+ if "organisation" in indata:
+ if indata["organisation"]:
+ indata["organisation"] = utils.str_to_uuid(indata["organisation"])
new_order.update(indata)
+ new_order["description"] = utils.secure_description(new_order["description"])
- if not new_order['editors']:
- new_order['editors'].append(flask.g.current_user['_id'])
+ if not new_order["editors"]:
+ new_order["editors"].append(flask.g.current_user["_id"])
# add to db
- result = flask.g.db['orders'].insert_one(new_order)
+ result = flask.g.db["orders"].insert_one(new_order)
if not result.acknowledged:
- flask.current_app.logger.error('Order insert failed: %s', new_order)
+ flask.current_app.logger.error("Order insert failed: %s", new_order)
else:
- utils.make_log('order', 'add', 'Order added', new_order)
+ utils.make_log("order", "add", "Order added", new_order)
- return utils.response_json({'_id': result.inserted_id})
+ return utils.response_json({"_id": result.inserted_id})
-@blueprint.route('//', methods=['DELETE'])
+@blueprint.route("//", methods=["DELETE"])
def delete_order(identifier: str):
"""
Delete the order with the given identifier.
@@ -235,32 +240,37 @@ def delete_order(identifier: str):
order_uuid = utils.str_to_uuid(identifier)
except ValueError:
flask.abort(status=404)
- order = flask.g.db['orders'].find_one({'_id': order_uuid})
+ order = flask.g.db["orders"].find_one({"_id": order_uuid})
if not order:
flask.abort(status=404)
- if not user.has_permission('DATA_MANAGEMENT') and \
- flask.g.current_user['_id'] not in order['editors']:
+ if (
+ not user.has_permission("DATA_MANAGEMENT")
+ and flask.g.current_user["_id"] not in order["editors"]
+ ):
flask.abort(status=403)
- for dataset_uuid in order['datasets']:
- result = flask.g.db['datasets'].delete_one({'_id': dataset_uuid})
+ for dataset_uuid in order["datasets"]:
+ result = flask.g.db["datasets"].delete_one({"_id": dataset_uuid})
if not result.acknowledged:
- flask.current_app.logger.error('Dataset %s delete failed (order %s deletion):',
- dataset_uuid, order_uuid)
+ flask.current_app.logger.error(
+ "Dataset %s delete failed (order %s deletion):",
+ dataset_uuid,
+ order_uuid,
+ )
flask.abort(status=500)
else:
- utils.make_log('dataset', 'delete', 'Deleting order', {'_id': dataset_uuid})
- result = flask.g.db['orders'].delete_one(order)
+ utils.make_log("dataset", "delete", "Deleting order", {"_id": dataset_uuid})
+ result = flask.g.db["orders"].delete_one(order)
if not result.acknowledged:
- flask.current_app.logger.error('Order deletion failed: %s', order_uuid)
+ flask.current_app.logger.error("Order deletion failed: %s", order_uuid)
flask.abort(status=500)
else:
- utils.make_log('order', 'delete', 'Order deleted', {'_id': order_uuid})
+ utils.make_log("order", "delete", "Order deleted", {"_id": order_uuid})
return flask.Response(status=200)
-@blueprint.route('//', methods=['PATCH'])
+@blueprint.route("//", methods=["PATCH"])
def update_order(identifier: str): # pylint: disable=too-many-branches
"""
Update an existing order.
@@ -276,32 +286,32 @@ def update_order(identifier: str): # pylint: disable=too-many-branches
except ValueError:
return flask.abort(status=404)
- order = flask.g.db['orders'].find_one({'_id': order_uuid})
+ order = flask.g.db["orders"].find_one({"_id": order_uuid})
if not order:
return flask.abort(status=404)
- if not (user.has_permission('DATA_MANAGEMENT') or
- flask.g.current_user['_id'] in order['editors']):
+ if not (
+ user.has_permission("DATA_MANAGEMENT")
+ or flask.g.current_user["_id"] in order["editors"]
+ ):
return flask.abort(status=403)
try:
indata = flask.json.loads(flask.request.data)
except json.decoder.JSONDecodeError:
flask.abort(status=400)
- validation = utils.basic_check_indata(indata, order, ['_id', 'datasets'])
+ validation = utils.basic_check_indata(indata, order, ["_id", "datasets"])
if not validation.result:
flask.abort(status=validation.status)
- # properties may only be set by users with DATA_MANAGEMENT
- if 'properties' in indata:
- if not user.has_permission('DATA_MANAGEMENT'):
- flask.abort(403)
-
- for field in ('editors', 'authors', 'generators'):
+ for field in ("editors", "authors", "generators"):
if field in indata:
indata[field] = [utils.str_to_uuid(entry) for entry in indata[field]]
- if 'organisation' in indata:
- if indata['organisation']:
- indata['organisation'] = utils.str_to_uuid(indata['organisation'])
+ if "organisation" in indata:
+ if indata["organisation"]:
+ indata["organisation"] = utils.str_to_uuid(indata["organisation"])
+
+ if "description" in indata:
+ indata["description"] = utils.secure_description(indata["description"])
is_different = False
for field in indata:
@@ -311,15 +321,15 @@ def update_order(identifier: str): # pylint: disable=too-many-branches
order.update(indata)
- if not order['editors']:
- order['editors'] = [flask.g.current_user['_id']]
+ if not order["editors"]:
+ order["editors"] = [flask.g.current_user["_id"]]
if is_different:
- result = flask.g.db['orders'].update_one({'_id': order['_id']}, {'$set': order})
+ result = flask.g.db["orders"].update_one({"_id": order["_id"]}, {"$set": order})
if not result.acknowledged:
- flask.current_app.logger.error('Order update failed: %s', order)
+ flask.current_app.logger.error("Order update failed: %s", order)
else:
- utils.make_log('order', 'edit', 'Order updated', order)
+ utils.make_log("order", "edit", "Order updated", order)
return flask.Response(status=200)
@@ -334,18 +344,22 @@ def prepare_order_response(order_data: dict, mongodb):
order_data (dict): The order entry from the db.
mongodb: The mongo database to use.
"""
- order_data['authors'] = utils.user_uuid_data(order_data['authors'], mongodb)
- order_data['generators'] = utils.user_uuid_data(order_data['generators'], mongodb)
- order_data['editors'] = utils.user_uuid_data(order_data['editors'], mongodb)
- if order_data['organisation']:
- if org_entry := utils.user_uuid_data(order_data['organisation'], mongodb):
- order_data['organisation'] = org_entry[0]
+ order_data["authors"] = utils.user_uuid_data(order_data["authors"], mongodb)
+ order_data["generators"] = utils.user_uuid_data(order_data["generators"], mongodb)
+ order_data["editors"] = utils.user_uuid_data(order_data["editors"], mongodb)
+ if order_data["organisation"]:
+ if org_entry := utils.user_uuid_data(order_data["organisation"], mongodb):
+ order_data["organisation"] = org_entry[0]
else:
- flask.current_app.logger.error('Reference to non-existing organisation: %s',
- order_data['organisation'])
+ flask.current_app.logger.error(
+ "Reference to non-existing organisation: %s", order_data["organisation"]
+ )
else:
- order_data['organisation'] = {}
+ order_data["organisation"] = {}
# convert dataset list into {title, _id}
- order_data['datasets'] = list(mongodb['datasets'].find({'_id': {'$in': order_data['datasets']}},
- {'_id': 1, 'title': 1}))
+ order_data["datasets"] = list(
+ mongodb["datasets"].find(
+ {"_id": {"$in": order_data["datasets"]}}, {"_id": 1, "title": 1}
+ )
+ )
diff --git a/backend/requirements.txt b/backend/requirements.txt
index 36e00ab5..b89f256f 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -1,10 +1,11 @@
Click==7.1.2
Flask==1.1.2
itsdangerous==1.1.0
-Jinja2==2.11.2
+Jinja2==2.11.3
MarkupSafe==1.1.1
pymongo==3.11.0
-PyYAML==5.3.1
+PyYAML==5.4
Werkzeug==1.0.1
authlib==0.14.3
requests==2.24.0
+argon2-cffi==20.1.0
diff --git a/backend/structure.py b/backend/structure.py
index f7db25e0..240dc154 100644
--- a/backend/structure.py
+++ b/backend/structure.py
@@ -14,12 +14,14 @@ def dataset():
Returns:
dict: The data structure for a dataset.
"""
- return {'_id': utils.new_uuid(),
- 'description': '',
- 'cross_references': [],
- 'title': '',
- 'properties': {},
- 'tags': []}
+ return {
+ "_id": utils.new_uuid(),
+ "description": "",
+ "cross_references": [],
+ "title": "",
+ "properties": {},
+ "tags": [],
+ }
def order():
@@ -29,16 +31,18 @@ def order():
Returns:
dict: The data structure for an order.
"""
- return {'_id': utils.new_uuid(),
- 'title': '',
- 'description': '',
- 'authors': [],
- 'generators': [],
- 'organisation': '',
- 'editors': [],
- 'datasets': [],
- 'properties': {},
- 'tags': []}
+ return {
+ "_id": utils.new_uuid(),
+ "title": "",
+ "description": "",
+ "authors": [],
+ "generators": [],
+ "organisation": "",
+ "editors": [],
+ "datasets": [],
+ "properties": {},
+ "tags": [],
+ }
def collection():
@@ -48,14 +52,16 @@ def collection():
Returns:
dict: The data structure for a project.
"""
- return {'_id': utils.new_uuid(),
- 'cross_references': [],
- 'datasets': [],
- 'description': '',
- 'properties': {},
- 'tags': [],
- 'editors': [],
- 'title': ''}
+ return {
+ "_id": utils.new_uuid(),
+ "cross_references": [],
+ "datasets": [],
+ "description": "",
+ "properties": {},
+ "tags": [],
+ "editors": [],
+ "title": "",
+ }
def user():
@@ -65,17 +71,19 @@ def user():
Returns:
dict: The data structure for a user.
"""
- return {'_id': utils.new_uuid(),
- 'affiliation': '',
- 'api_key': '',
- 'api_salt': '',
- 'auth_ids': [],
- 'email': '',
- 'contact': '',
- 'name': '',
- 'orcid': '',
- 'permissions': [],
- 'url': ''}
+ return {
+ "_id": utils.new_uuid(),
+ "affiliation": "",
+ "api_key": "",
+ "api_salt": "",
+ "auth_ids": [],
+ "email": "",
+ "contact": "",
+ "name": "",
+ "orcid": "",
+ "permissions": [],
+ "url": "",
+ }
def log():
@@ -85,10 +93,12 @@ def log():
Returns:
dict: The data structure for a log.
"""
- return {'_id': utils.new_uuid(),
- 'action': '',
- 'comment': '',
- 'data_type': '',
- 'data': '',
- 'timestamp': utils.make_timestamp(),
- 'user': ''}
+ return {
+ "_id": utils.new_uuid(),
+ "action": "",
+ "comment": "",
+ "data_type": "",
+ "data": "",
+ "timestamp": utils.make_timestamp(),
+ "user": "",
+ }
diff --git a/backend/tests/helpers.py b/backend/tests/helpers.py
index 603db764..1bb71f0c 100644
--- a/backend/tests/helpers.py
+++ b/backend/tests/helpers.py
@@ -17,26 +17,28 @@
CURR_DIR = os.path.realpath(__file__)
-SETTINGS = json.loads(open(f'{os.path.dirname(CURR_DIR)}/settings_tests.json').read())
+SETTINGS = json.loads(open(f"{os.path.dirname(CURR_DIR)}/settings_tests.json").read())
BASE_URL = f'{SETTINGS["host"]}:{SETTINGS["port"]}'
-TEST_LABEL = {'tags': ['testing']}
+TEST_LABEL = {"tags": ["testing"]}
-USERS = {'no-login': None,
- 'base': 'base::testers',
- 'orders': 'orders::testers',
- 'owners': 'owners::testers',
- 'users': 'users::testers',
- 'data': 'data::testers',
- 'root': 'root::testers'}
+USERS = {
+ "no-login": None,
+ "base": "base::testers",
+ "orders": "orders::testers",
+ "owners": "owners::testers",
+ "users": "users::testers",
+ "data": "data::testers",
+ "root": "root::testers",
+}
-Response = collections.namedtuple('Response',
- ['data', 'code', 'role'],
- defaults=[None, None, None])
+Response = collections.namedtuple(
+ "Response", ["data", "code", "role"], defaults=[None, None, None]
+)
-FACILITY_RE = re.compile('facility[0-9]*::local')
-ORGANISATION_RE = re.compile('organisation[0-9]*::local')
-USER_RE = re.compile('.*::elixir')
+FACILITY_RE = re.compile("facility[0-9]*::local")
+ORGANISATION_RE = re.compile("organisation[0-9]*::local")
+USER_RE = re.compile(".*::elixir")
def db_connection():
@@ -66,13 +68,13 @@ def as_user(session: requests.Session, auth_id: str, set_csrf: bool = True) -> i
int: Status code.
"""
if auth_id:
- code = session.get(f'{BASE_URL}/api/v1/developer/login/{auth_id}').status_code
+ code = session.get(f"{BASE_URL}/api/v1/developer/login/{auth_id}").status_code
assert code == 200
else:
- code = session.get(f'{BASE_URL}/api/v1/logout/').status_code
- session.get(f'{BASE_URL}/api/v1/developer/hello') # reset cookies
+ code = session.get(f"{BASE_URL}/api/v1/logout/").status_code
+ session.get(f"{BASE_URL}/api/v1/developer/hello") # reset cookies
if set_csrf:
- session.headers['X-CSRFToken'] = session.cookies.get('_csrf_token')
+ session.headers["X-CSRFToken"] = session.cookies.get("_csrf_token")
return code
@@ -100,34 +102,40 @@ def add_dataset():
mongo_db = db_connection()
# prepare
order_indata = structure.order()
- order_indata.update({'description': 'Added by fixture.',
- 'title': 'Test title from fixture'})
+ order_indata.update(
+ {"description": "Added by fixture.", "title": "Test title from fixture"}
+ )
order_indata.update(TEST_LABEL)
- orders_user = mongo_db['users'].find_one({'auth_ids': USERS['orders']})
- base_user = mongo_db['users'].find_one({'auth_ids': USERS['base']})
- order_indata['authors'] = [orders_user['_id']]
- order_indata['editors'] = [orders_user['_id']]
- order_indata['generators'] = [orders_user['_id']]
- order_indata['organisation'] = orders_user['_id']
- order_indata['receivers'] = [base_user['_id']]
+ orders_user = mongo_db["users"].find_one({"auth_ids": USERS["orders"]})
+ base_user = mongo_db["users"].find_one({"auth_ids": USERS["base"]})
+ order_indata["authors"] = [orders_user["_id"]]
+ order_indata["editors"] = [orders_user["_id"]]
+ order_indata["generators"] = [orders_user["_id"]]
+ order_indata["organisation"] = orders_user["_id"]
+ order_indata["receivers"] = [base_user["_id"]]
dataset_indata = structure.dataset()
- dataset_indata.update({'description': 'Added by fixture.',
- 'title': 'Test title from fixture'})
+ dataset_indata.update(
+ {"description": "Added by fixture.", "title": "Test title from fixture"}
+ )
dataset_indata.update(TEST_LABEL)
collection_indata = structure.collection()
- collection_indata.update({'description': 'Added by fixture.',
- 'title': 'Test title from fixture',
- 'editors': [base_user['_id']]})
+ collection_indata.update(
+ {
+ "description": "Added by fixture.",
+ "title": "Test title from fixture",
+ "editors": [base_user["_id"]],
+ }
+ )
collection_indata.update(TEST_LABEL)
- mongo_db['datasets'].insert_one(dataset_indata)
- order_indata['datasets'].append(dataset_indata['_id'])
- collection_indata['datasets'].append(dataset_indata['_id'])
- mongo_db['orders'].insert_one(order_indata)
- mongo_db['collections'].insert_one(collection_indata)
- return (order_indata['_id'], dataset_indata['_id'], collection_indata['_id'])
+ mongo_db["datasets"].insert_one(dataset_indata)
+ order_indata["datasets"].append(dataset_indata["_id"])
+ collection_indata["datasets"].append(dataset_indata["_id"])
+ mongo_db["orders"].insert_one(order_indata)
+ mongo_db["collections"].insert_one(collection_indata)
+ return (order_indata["_id"], dataset_indata["_id"], collection_indata["_id"])
def delete_dataset(order_uuid, dataset_uuid, project_uuid):
@@ -135,12 +143,14 @@ def delete_dataset(order_uuid, dataset_uuid, project_uuid):
Delete an order and a dataset added by ``add_dataset()``.
"""
mongo_db = db_connection()
- mongo_db['orders'].delete_one({'_id': order_uuid})
- mongo_db['datasets'].delete_one({'_id': dataset_uuid})
- mongo_db['projects'].delete_one({'_id': project_uuid})
+ mongo_db["orders"].delete_one({"_id": order_uuid})
+ mongo_db["datasets"].delete_one({"_id": dataset_uuid})
+ mongo_db["projects"].delete_one({"_id": project_uuid})
-def make_request(session, url: str, data: dict = None, method='GET', ret_json: bool = True) -> dict:
+def make_request(
+ session, url: str, data: dict = None, method="GET", ret_json: bool = True
+) -> dict:
"""
Helper method for using get/post to a url.
Args:
@@ -153,21 +163,18 @@ def make_request(session, url: str, data: dict = None, method='GET', ret_json: b
Returns:
tuple: (data: dict, status_code: int)
"""
- if method == 'GET':
- response = session.get(f'{BASE_URL}{url}')
- elif method == 'POST':
- response = session.post(f'{BASE_URL}{url}',
- data=json.dumps(data))
- elif method == 'PATCH':
- response = session.patch(f'{BASE_URL}{url}',
- data=json.dumps(data))
- elif method == 'PUT':
- response = session.put(f'{BASE_URL}{url}',
- data=json.dumps(data))
- elif method == 'DELETE':
- response = session.delete(f'{BASE_URL}{url}')
+ if method == "GET":
+ response = session.get(f"{BASE_URL}{url}")
+ elif method == "POST":
+ response = session.post(f"{BASE_URL}{url}", data=json.dumps(data))
+ elif method == "PATCH":
+ response = session.patch(f"{BASE_URL}{url}", data=json.dumps(data))
+ elif method == "PUT":
+ response = session.put(f"{BASE_URL}{url}", data=json.dumps(data))
+ elif method == "DELETE":
+ response = session.delete(f"{BASE_URL}{url}")
else:
- raise ValueError(f'Unsupported http method ({method})')
+ raise ValueError(f"Unsupported http method ({method})")
if response.text and ret_json:
data = json.loads(response.text)
@@ -178,8 +185,13 @@ def make_request(session, url: str, data: dict = None, method='GET', ret_json: b
return Response(data=data, code=response.status_code)
-def make_request_all_roles(url: str, method: str = 'GET', data=None,
- set_csrf: bool = True, ret_json: bool = False) -> list:
+def make_request_all_roles(
+ url: str,
+ method: str = "GET",
+ data=None,
+ set_csrf: bool = True,
+ ret_json: bool = False,
+) -> list:
"""
Perform a query for all roles (anonymous, User, Steward, Admin).
@@ -208,18 +220,22 @@ def collection_for_tests():
# prepare
mongo_db = db_connection()
session = requests.Session()
- as_user(session, USERS['data'])
+ as_user(session, USERS["data"])
collection_indata = structure.collection()
- base_user = mongo_db['users'].find_one({'auth_ids': USERS['base']})
- collection_indata.update({'description': 'Added by fixture.',
- 'title': 'Test title from fixture',
- 'editors': [base_user['_id']]})
+ base_user = mongo_db["users"].find_one({"auth_ids": USERS["base"]})
+ collection_indata.update(
+ {
+ "description": "Added by fixture.",
+ "title": "Test title from fixture",
+ "editors": [base_user["_id"]],
+ }
+ )
collection_indata.update(TEST_LABEL)
- mongo_db['collections'].insert_one(collection_indata)
+ mongo_db["collections"].insert_one(collection_indata)
- yield collection_indata['_id']
+ yield collection_indata["_id"]
- mongo_db['collections'].delete_one({'_id': collection_indata['_id']})
+ mongo_db["collections"].delete_one({"_id": collection_indata["_id"]})
def random_string(min_length: int = 1, max_length: int = 150):
@@ -234,9 +250,9 @@ def random_string(min_length: int = 1, max_length: int = 150):
str: a string of random characters
"""
- char_source = string.ascii_letters + string.digits + '-'
+ char_source = string.ascii_letters + string.digits + "-"
length = random.randint(min_length, max_length)
- return ''.join(random.choice(char_source) for _ in range(length))
+ return "".join(random.choice(char_source) for _ in range(length))
def parse_time(datetime_str: str):
@@ -246,5 +262,5 @@ def parse_time(datetime_str: str):
Args:
datetime_str (str): timestamp string (Wed, 22 Jan 2020 21:07:35 GMT)
"""
- str_format = '%a, %d %b %Y %H:%M:%S %Z'
+ str_format = "%a, %d %b %Y %H:%M:%S %Z"
return datetime.datetime.strptime(datetime_str, str_format)
diff --git a/backend/tests/test_collections.py b/backend/tests/test_collections.py
index db3a0f77..b512def2 100644
--- a/backend/tests/test_collections.py
+++ b/backend/tests/test_collections.py
@@ -5,38 +5,54 @@
import utils
# pylint: disable=unused-import
-from helpers import make_request, as_user, make_request_all_roles,\
- USERS, random_string, mdb, TEST_LABEL, collection_for_tests, add_dataset, delete_dataset
+from helpers import (
+ make_request,
+ as_user,
+ make_request_all_roles,
+ USERS,
+ random_string,
+ mdb,
+ TEST_LABEL,
+ collection_for_tests,
+ add_dataset,
+ delete_dataset,
+)
+
# pylint: enable=unused-import
# pylint: disable=redefined-outer-name
+
def test_random_collection():
"""Request a random collection."""
- responses = make_request_all_roles('/api/v1/collection/random', ret_json=True)
+ responses = make_request_all_roles("/api/v1/collection/random", ret_json=True)
for response in responses:
assert response.code == 200
- assert len(response.data['collections']) == 1
+ assert len(response.data["collections"]) == 1
def test_random_collections():
"""Request random collections."""
session = requests.Session()
- as_user(session, USERS['base'])
+ as_user(session, USERS["base"])
for i in (1, 5, 0):
- response = make_request(session, f'/api/v1/collection/random/{i}', ret_json=True)
+ response = make_request(
+ session, f"/api/v1/collection/random/{i}", ret_json=True
+ )
assert response.code == 200
- assert len(response.data['collections']) == i
+ assert len(response.data["collections"]) == i
- response = make_request(session, '/api/v1/collection/random/-1')
+ response = make_request(session, "/api/v1/collection/random/-1")
assert response[1] == 404
assert not response[0]
def test_get_collection_permissions(mdb):
"""Test permissions for requesting a collection."""
- collection = list(mdb['collections'].aggregate([{'$sample': {'size': 1}}]))[0]
+ collection = list(mdb["collections"].aggregate([{"$sample": {"size": 1}}]))[0]
- responses = make_request_all_roles(f'/api/v1/collection/{collection["_id"]}', ret_json=True)
+ responses = make_request_all_roles(
+ f'/api/v1/collection/{collection["_id"]}', ret_json=True
+ )
for response in responses:
assert response.code == 200
@@ -49,48 +65,48 @@ def test_get_collection(mdb):
"""
session = requests.Session()
for _ in range(3):
- collection = list(mdb['collections'].aggregate([{'$sample': {'size': 1}}]))[0]
- collection['_id'] = str(collection['_id'])
- proj_owner = mdb['users'].find_one({'_id': {'$in': collection['editors']}})
- collection['editors'] = [str(entry) for entry in collection['editors']]
- collection['datasets'] = [str(entry) for entry in collection['datasets']]
+ collection = list(mdb["collections"].aggregate([{"$sample": {"size": 1}}]))[0]
+ collection["_id"] = str(collection["_id"])
+ proj_owner = mdb["users"].find_one({"_id": {"$in": collection["editors"]}})
+ collection["editors"] = [str(entry) for entry in collection["editors"]]
+ collection["datasets"] = [str(entry) for entry in collection["datasets"]]
collection = utils.convert_keys_to_camel(collection)
- as_user(session, USERS['base'])
+ as_user(session, USERS["base"])
response = make_request(session, f'/api/v1/collection/{collection["_id"]}')
assert response.code == 200
for field in collection:
- if field == 'datasets':
+ if field == "datasets":
for i, ds_uuid in enumerate(collection[field]):
- assert ds_uuid == response.data['collection'][field][i]['_id']
- elif field == 'editors':
+ assert ds_uuid == response.data["collection"][field][i]["_id"]
+ elif field == "editors":
continue
else:
- assert collection[field] == response.data['collection'][field]
+ assert collection[field] == response.data["collection"][field]
- as_user(session, proj_owner['auth_ids'][0])
+ as_user(session, proj_owner["auth_ids"][0])
response = make_request(session, f'/api/v1/collection/{collection["_id"]}')
assert response.code == 200
print(collection)
for field in collection:
- if field in ('datasets', 'editors'):
- entries = [entry['_id'] for entry in response.data['collection'][field]]
+ if field in ("datasets", "editors"):
+ entries = [entry["_id"] for entry in response.data["collection"][field]]
assert len(collection[field]) == len(entries)
for i, ds_uuid in enumerate(collection[field]):
assert ds_uuid in entries
else:
- assert collection[field] == response.data['collection'][field]
+ assert collection[field] == response.data["collection"][field]
- as_user(session, USERS['root'])
+ as_user(session, USERS["root"])
response = make_request(session, f'/api/v1/collection/{collection["_id"]}')
assert response.code == 200
for field in collection:
- if field in ('datasets', 'editors'):
- entries = [entry['_id'] for entry in response.data['collection'][field]]
+ if field in ("datasets", "editors"):
+ entries = [entry["_id"] for entry in response.data["collection"][field]]
assert len(collection[field]) == len(entries)
for i, ds_uuid in enumerate(collection[field]):
assert ds_uuid in entries
else:
- assert collection[field] == response.data['collection'][field]
+ assert collection[field] == response.data["collection"][field]
def test_get_collection_bad():
@@ -101,12 +117,12 @@ def test_get_collection_bad():
"""
session = requests.Session()
for _ in range(2):
- response = make_request(session, f'/api/v1/collection/{uuid.uuid4().hex}')
+ response = make_request(session, f"/api/v1/collection/{uuid.uuid4().hex}")
assert response.code == 404
assert not response.data
for _ in range(2):
- response = make_request(session, f'/api/v1/collection/{random_string()}')
+ response = make_request(session, f"/api/v1/collection/{random_string()}")
assert response.code == 404
assert not response.data
@@ -117,37 +133,35 @@ def test_add_collection_permissions(mdb):
Test permissions.
"""
- indata = {'title': 'Test title'}
+ indata = {"title": "Test title"}
indata.update(TEST_LABEL)
- responses = make_request_all_roles('/api/v1/collection/',
- method='POST',
- data=indata,
- ret_json=True)
+ responses = make_request_all_roles(
+ "/api/v1/collection/", method="POST", data=indata, ret_json=True
+ )
for response in responses:
- if response.role == 'no-login':
+ if response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 200
- assert '_id' in response.data
- assert len(response.data['_id']) == 36
+ assert "_id" in response.data
+ assert len(response.data["_id"]) == 36
- user_info = mdb['users'].find_one({'auth_ids': USERS['base']})
- indata.update({'editors': [str(user_info['_id'])]})
+ user_info = mdb["users"].find_one({"auth_ids": USERS["base"]})
+ indata.update({"editors": [str(user_info["_id"])]})
- responses = make_request_all_roles('/api/v1/collection/',
- method='POST',
- data=indata,
- ret_json=True)
+ responses = make_request_all_roles(
+ "/api/v1/collection/", method="POST", data=indata, ret_json=True
+ )
for response in responses:
- if response.role == 'no-login':
+ if response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 200
- assert '_id' in response.data
- assert len(response.data['_id']) == 36
+ assert "_id" in response.data
+ assert len(response.data["_id"]) == 36
def test_add_collection(mdb):
@@ -158,62 +172,68 @@ def test_add_collection(mdb):
* fields are set correctly
* logs are created
"""
- dataset_info = next(mdb['datasets'].aggregate([{'$sample': {'size': 1}}]))
- order_info = mdb['orders'].find_one({'datasets': dataset_info['_id']})
+ dataset_info = next(mdb["datasets"].aggregate([{"$sample": {"size": 1}}]))
+ order_info = mdb["orders"].find_one({"datasets": dataset_info["_id"]})
session = requests.Session()
- user_info = mdb['users'].find_one({'_id': {'$in': order_info['editors']}})
+ user_info = mdb["users"].find_one({"_id": {"$in": order_info["editors"]}})
- as_user(session, user_info['auth_ids'][0])
+ as_user(session, user_info["auth_ids"][0])
- indata = {'description': 'Test description',
- 'editors': [str(user_info['_id'])],
- 'title': 'Test title',
- 'datasets': [str(dataset_info['_id'])]}
+ indata = {
+ "description": "Test description",
+ "editors": [str(user_info["_id"])],
+ "title": "Test title",
+ "datasets": [str(dataset_info["_id"])],
+ }
indata.update(TEST_LABEL)
- response = make_request(session,
- '/api/v1/collection/',
- method='POST',
- data=indata,
- ret_json=True)
+ response = make_request(
+ session, "/api/v1/collection/", method="POST", data=indata, ret_json=True
+ )
assert response.code == 200
- assert '_id' in response.data
- assert len(response.data['_id']) == 36
- collection = mdb['collections'].find_one({'_id': uuid.UUID(response.data['_id'])})
- assert collection['description'] == indata['description']
- assert str(collection['editors'][0]) == indata['editors'][0]
- assert collection['title'] == indata['title']
- assert str(collection['datasets'][0]) == indata['datasets'][0]
+ assert "_id" in response.data
+ assert len(response.data["_id"]) == 36
+ collection = mdb["collections"].find_one({"_id": uuid.UUID(response.data["_id"])})
+ assert collection["description"] == indata["description"]
+ assert str(collection["editors"][0]) == indata["editors"][0]
+ assert collection["title"] == indata["title"]
+ assert str(collection["datasets"][0]) == indata["datasets"][0]
# log
- assert mdb['logs'].find_one({'data._id': uuid.UUID(response.data['_id']),
- 'data_type': 'collection',
- 'user': user_info['_id'],
- 'action': 'add'})
-
- as_user(session, USERS['data'])
-
- response = make_request(session,
- '/api/v1/collection/',
- method='POST',
- data=indata,
- ret_json=True)
+ assert mdb["logs"].find_one(
+ {
+ "data._id": uuid.UUID(response.data["_id"]),
+ "data_type": "collection",
+ "user": user_info["_id"],
+ "action": "add",
+ }
+ )
+
+ as_user(session, USERS["data"])
+
+ response = make_request(
+ session, "/api/v1/collection/", method="POST", data=indata, ret_json=True
+ )
assert response.code == 200
- assert '_id' in response.data
- assert len(response.data['_id']) == 36
- collection = mdb['collections'].find_one({'_id': uuid.UUID(response.data['_id'])})
- assert collection['description'] == indata['description']
- assert str(collection['editors'][0]) == indata['editors'][0]
- assert collection['title'] == indata['title']
- assert str(collection['datasets'][0]) == indata['datasets'][0]
+ assert "_id" in response.data
+ assert len(response.data["_id"]) == 36
+ collection = mdb["collections"].find_one({"_id": uuid.UUID(response.data["_id"])})
+ assert collection["description"] == indata["description"]
+ assert str(collection["editors"][0]) == indata["editors"][0]
+ assert collection["title"] == indata["title"]
+ assert str(collection["datasets"][0]) == indata["datasets"][0]
- data_user = mdb['users'].find_one({'auth_ids': USERS['data']})
+ data_user = mdb["users"].find_one({"auth_ids": USERS["data"]})
# log
- assert mdb['logs'].find_one({'data._id': uuid.UUID(response.data['_id']),
- 'data_type': 'collection',
- 'user': data_user['_id'],
- 'action': 'add'})
+ assert mdb["logs"].find_one(
+ {
+ "data._id": uuid.UUID(response.data["_id"]),
+ "data_type": "collection",
+ "user": data_user["_id"],
+ "action": "add",
+ }
+ )
def test_add_collection_bad():
@@ -222,66 +242,61 @@ def test_add_collection_bad():
Bad requests.
"""
- indata = {'title': ''}
+ indata = {"title": ""}
indata.update(TEST_LABEL)
- responses = make_request_all_roles('/api/v1/collection/',
- method='POST',
- data=indata,
- ret_json=True)
+ responses = make_request_all_roles(
+ "/api/v1/collection/", method="POST", data=indata, ret_json=True
+ )
for response in responses:
- if response.role == 'no-login':
+ if response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 400
assert not response.data
-
indata = {}
indata.update(TEST_LABEL)
- responses = make_request_all_roles('/api/v1/collection/',
- method='POST',
- data=indata,
- ret_json=True)
+ responses = make_request_all_roles(
+ "/api/v1/collection/", method="POST", data=indata, ret_json=True
+ )
for response in responses:
- if response.role == 'no-login':
+ if response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 400
assert not response.data
-
- indata = {'bad_tag': 'content',
- 'title': 'title'}
+ indata = {"bad_tag": "content", "title": "title"}
indata.update(TEST_LABEL)
- responses = make_request_all_roles('/api/v1/collection/',
- method='POST',
- data=indata,
- ret_json=True)
+ responses = make_request_all_roles(
+ "/api/v1/collection/", method="POST", data=indata, ret_json=True
+ )
for response in responses:
- if response.role == 'no-login':
+ if response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 400
assert not response.data
- indata = {'description': 'Test description',
- 'owners': [str(uuid.uuid4())],
- 'title': 'Test title'}
+ indata = {
+ "description": "Test description",
+ "owners": [str(uuid.uuid4())],
+ "title": "Test title",
+ }
indata.update(TEST_LABEL)
- responses = make_request_all_roles('/api/v1/collection/',
- method='POST',
- data=indata,
- ret_json=True)
+ responses = make_request_all_roles(
+ "/api/v1/collection/", method="POST", data=indata, ret_json=True
+ )
for response in responses:
- if response.role == 'no-login':
+ if response.role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -289,27 +304,24 @@ def test_add_collection_bad():
assert not response.data
session = requests.Session()
- as_user(session, USERS['data'])
- indata = {'_id': str(uuid.uuid4()),
- 'owners': [str(uuid.uuid4())],
- 'title': 'Test title'}
+ as_user(session, USERS["data"])
+ indata = {
+ "_id": str(uuid.uuid4()),
+ "owners": [str(uuid.uuid4())],
+ "title": "Test title",
+ }
indata.update(TEST_LABEL)
- response = make_request(session,
- '/api/v1/collection/',
- method='POST',
- data=indata,
- ret_json=True)
+ response = make_request(
+ session, "/api/v1/collection/", method="POST", data=indata, ret_json=True
+ )
assert response.code == 403
assert not response.data
- indata = {'datasets': [str(uuid.uuid4())],
- 'title': 'Test title'}
+ indata = {"datasets": [str(uuid.uuid4())], "title": "Test title"}
indata.update(TEST_LABEL)
- response = make_request(session,
- '/api/v1/collection/',
- method='POST',
- data=indata,
- ret_json=True)
+ response = make_request(
+ session, "/api/v1/collection/", method="POST", data=indata, ret_json=True
+ )
assert response.code == 400
@@ -322,22 +334,24 @@ def test_update_collection_permissions(mdb, collection_for_tests):
session = requests.Session()
collection_uuid = collection_for_tests
- print(mdb['collections'].find_one({'_id': collection_uuid}))
+ print(mdb["collections"].find_one({"_id": collection_uuid}))
for role in USERS:
as_user(session, USERS[role])
- indata = {'title': f'Test title - updated by {role}'}
- response = make_request(session,
- f'/api/v1/collection/{collection_uuid}/',
- method='PATCH',
- data=indata,
- ret_json=True)
- if role in ('base', 'data', 'root'):
+ indata = {"title": f"Test title - updated by {role}"}
+ response = make_request(
+ session,
+ f"/api/v1/collection/{collection_uuid}/",
+ method="PATCH",
+ data=indata,
+ ret_json=True,
+ )
+ if role in ("base", "data", "root"):
assert response.code == 200
assert not response.data
- new_collection = mdb['collections'].find_one({'_id': collection_uuid})
- assert new_collection['title'] == f'Test title - updated by {role}'
- elif role == 'no-login':
+ new_collection = mdb["collections"].find_one({"_id": collection_uuid})
+ assert new_collection["title"] == f"Test title - updated by {role}"
+ elif role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -353,62 +367,78 @@ def test_update_collection(mdb):
Confirm that logs are created.
"""
uuids = add_dataset()
- collection_info = mdb['collections'].find_one({'_id': uuids[2]})
- user_info = mdb['users'].find_one({'auth_ids': USERS['base']})
-
- indata = {'description': 'Test description updated',
- 'editors': [str(collection_info['editors'][0])],
- 'title': 'Test title updated',
- 'datasets': [str(uuids[1])]}
+ collection_info = mdb["collections"].find_one({"_id": uuids[2]})
+ user_info = mdb["users"].find_one({"auth_ids": USERS["base"]})
+
+ indata = {
+ "description": "Test description updated",
+ "editors": [str(collection_info["editors"][0])],
+ "title": "Test title updated",
+ "datasets": [str(uuids[1])],
+ }
indata.update(TEST_LABEL)
session = requests.Session()
- as_user(session, USERS['base'])
-
- response = make_request(session,
- f'/api/v1/collection/{collection_info["_id"]}/',
- method='PATCH',
- data=indata,
- ret_json=True)
+ as_user(session, USERS["base"])
+
+ response = make_request(
+ session,
+ f'/api/v1/collection/{collection_info["_id"]}/',
+ method="PATCH",
+ data=indata,
+ ret_json=True,
+ )
assert response.code == 200
- collection = mdb['collections'].find_one({'_id': collection_info['_id']})
- assert collection['description'] == indata['description']
- assert str(collection['editors'][0]) == indata['editors'][0]
- assert collection['title'] == indata['title']
- assert str(collection['datasets'][0]) == indata['datasets'][0]
+ collection = mdb["collections"].find_one({"_id": collection_info["_id"]})
+ assert collection["description"] == indata["description"]
+ assert str(collection["editors"][0]) == indata["editors"][0]
+ assert collection["title"] == indata["title"]
+ assert str(collection["datasets"][0]) == indata["datasets"][0]
# log
- assert mdb['logs'].find_one({'data._id': collection_info['_id'],
- 'data_type': 'collection',
- 'user': user_info['_id'],
- 'action': 'edit'})
-
- as_user(session, USERS['data'])
- user_info = mdb['users'].find_one({'auth_ids': USERS['data']})
-
- indata = {'description': 'Test description updated2',
- 'editors': [str(user_info['_id'])],
- 'title': 'Test title updated',
- 'datasets': [str(uuids[1]), str(uuids[1])]}
+ assert mdb["logs"].find_one(
+ {
+ "data._id": collection_info["_id"],
+ "data_type": "collection",
+ "user": user_info["_id"],
+ "action": "edit",
+ }
+ )
+
+ as_user(session, USERS["data"])
+ user_info = mdb["users"].find_one({"auth_ids": USERS["data"]})
+
+ indata = {
+ "description": "Test description updated2",
+ "editors": [str(user_info["_id"])],
+ "title": "Test title updated",
+ "datasets": [str(uuids[1]), str(uuids[1])],
+ }
indata.update(TEST_LABEL)
- response = make_request(session,
- f'/api/v1/collection/{collection_info["_id"]}/',
- method='PATCH',
- data=indata,
- ret_json=True)
+ response = make_request(
+ session,
+ f'/api/v1/collection/{collection_info["_id"]}/',
+ method="PATCH",
+ data=indata,
+ ret_json=True,
+ )
assert response.code == 200
- collection = mdb['collections'].find_one({'_id': collection_info['_id']})
- assert collection['description'] == indata['description']
- assert str(collection['editors'][0]) == indata['editors'][0]
- assert collection['title'] == indata['title']
- assert str(collection['datasets'][0]) == indata['datasets'][0]
+ collection = mdb["collections"].find_one({"_id": collection_info["_id"]})
+ assert collection["description"] == indata["description"]
+ assert str(collection["editors"][0]) == indata["editors"][0]
+ assert collection["title"] == indata["title"]
+ assert str(collection["datasets"][0]) == indata["datasets"][0]
# log
- assert mdb['logs'].find_one({'data._id': collection_info['_id'],
- 'data_type': 'collection',
- 'user': user_info['_id'],
- 'action': 'edit'})
+ assert mdb["logs"].find_one(
+ {
+ "data._id": collection_info["_id"],
+ "data_type": "collection",
+ "user": user_info["_id"],
+ "action": "edit",
+ }
+ )
delete_dataset(*uuids)
@@ -419,38 +449,44 @@ def test_update_collection_bad(mdb):
Bad requests.
"""
uuids = add_dataset()
- collection_info = mdb['collections'].find_one({'_id': uuids[2]})
+ collection_info = mdb["collections"].find_one({"_id": uuids[2]})
- indata = {'bad_tag': 'value'}
+ indata = {"bad_tag": "value"}
- responses = make_request_all_roles(f'/api/v1/collection/{collection_info["_id"]}/',
- method='PATCH',
- data=indata,
- ret_json=True)
+ responses = make_request_all_roles(
+ f'/api/v1/collection/{collection_info["_id"]}/',
+ method="PATCH",
+ data=indata,
+ ret_json=True,
+ )
for response in responses:
- if response.role in ('base', 'data', 'root'):
+ if response.role in ("base", "data", "root"):
assert response.code == 400
assert not response.data
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 403
assert not response.data
- indata = {'description': 'Test description',
- 'owners': [str(uuid.uuid4())],
- 'title': 'Test title'}
-
- responses = make_request_all_roles(f'/api/v1/collection/{collection_info["_id"]}/',
- method='PATCH',
- data=indata,
- ret_json=True)
+ indata = {
+ "description": "Test description",
+ "owners": [str(uuid.uuid4())],
+ "title": "Test title",
+ }
+
+ responses = make_request_all_roles(
+ f'/api/v1/collection/{collection_info["_id"]}/',
+ method="PATCH",
+ data=indata,
+ ret_json=True,
+ )
for response in responses:
- if response.role in ('base', 'data', 'root'):
+ if response.role in ("base", "data", "root"):
assert response.code == 400
assert not response.data
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -458,26 +494,30 @@ def test_update_collection_bad(mdb):
assert not response.data
for _ in range(2):
- indata = {'title': 'Test title'}
- responses = make_request_all_roles(f'/api/v1/collection/{uuid.uuid4()}/',
- method='PATCH',
- data=indata,
- ret_json=True)
+ indata = {"title": "Test title"}
+ responses = make_request_all_roles(
+ f"/api/v1/collection/{uuid.uuid4()}/",
+ method="PATCH",
+ data=indata,
+ ret_json=True,
+ )
for response in responses:
- if response.role == 'no-login':
+ if response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 404
assert not response.data
- indata = {'title': 'Test title'}
- responses = make_request_all_roles(f'/api/v1/collection/{random_string()}/',
- method='PATCH',
- data=indata,
- ret_json=True)
+ indata = {"title": "Test title"}
+ responses = make_request_all_roles(
+ f"/api/v1/collection/{random_string()}/",
+ method="PATCH",
+ data=indata,
+ ret_json=True,
+ )
for response in responses:
- if response.role == 'no-login':
+ if response.role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -499,36 +539,46 @@ def test_delete_collection(mdb):
session = requests.Session()
# must be updated if TEST_LABEL is modified
- collections = list(mdb['collections'].find({'extra.testing': 'yes'}))
+ collections = list(mdb["collections"].find({"extra.testing": "yes"}))
i = 0
while i < len(collections):
for role in USERS:
as_user(session, USERS[role])
- response = make_request(session,
- f'/api/v1/collection/{collections[i]["_id"]}/',
- method='DELETE')
- if role in ('data', 'root'):
+ response = make_request(
+ session, f'/api/v1/collection/{collections[i]["_id"]}/', method="DELETE"
+ )
+ if role in ("data", "root"):
assert response.code == 200
assert not response.data
- assert not mdb['collections'].find_one({'_id': collections[i]['_id']})
- assert mdb['logs'].find_one({'data._id': collections[i]['_id'],
- 'action': 'delete',
- 'data_type': 'collection'})
+ assert not mdb["collections"].find_one({"_id": collections[i]["_id"]})
+ assert mdb["logs"].find_one(
+ {
+ "data._id": collections[i]["_id"],
+ "action": "delete",
+ "data_type": "collection",
+ }
+ )
i += 1
if i >= len(collections):
break
- elif role == 'no-login':
+ elif role == "no-login":
assert response.code == 401
assert not response.data
else:
- current_user = mdb['users'].find_one({'auth_id': USERS[role]})
- if current_user['_id'] in collections[i]['owners']:
+ current_user = mdb["users"].find_one({"auth_id": USERS[role]})
+ if current_user["_id"] in collections[i]["owners"]:
assert response.code == 200
assert not response.data
- assert not mdb['collections'].find_one({'_id': collections[i]['_id']})
- assert mdb['logs'].find_one({'data._id': collections[i]['_id'],
- 'action': 'delete',
- 'data_type': 'collection'})
+ assert not mdb["collections"].find_one(
+ {"_id": collections[i]["_id"]}
+ )
+ assert mdb["logs"].find_one(
+ {
+ "data._id": collections[i]["_id"],
+ "action": "delete",
+ "data_type": "collection",
+ }
+ )
i += 1
if i >= len(collections):
break
@@ -537,15 +587,14 @@ def test_delete_collection(mdb):
assert response.code == 403
assert not response.data
- as_user(session, USERS['base'])
- response = make_request(session,
- '/api/v1/collection/',
- data={'title': 'tmp'},
- method='POST')
+ as_user(session, USERS["base"])
+ response = make_request(
+ session, "/api/v1/collection/", data={"title": "tmp"}, method="POST"
+ )
assert response.code == 200
- response = make_request(session,
- f'/api/v1/collection/{response.data["_id"]}/',
- method='DELETE')
+ response = make_request(
+ session, f'/api/v1/collection/{response.data["_id"]}/', method="DELETE"
+ )
assert response.code == 200
assert not response.data
@@ -554,18 +603,18 @@ def test_delete_collection_bad():
"""Attempt bad collection delete requests."""
session = requests.Session()
- as_user(session, USERS['data'])
+ as_user(session, USERS["data"])
for _ in range(2):
- response = make_request(session,
- f'/api/v1/collection/{random_string()}/',
- method='DELETE')
+ response = make_request(
+ session, f"/api/v1/collection/{random_string()}/", method="DELETE"
+ )
assert response.code == 404
assert not response.data
for _ in range(2):
- response = make_request(session,
- f'/api/v1/collection/{uuid.uuid4()}/',
- method='DELETE')
+ response = make_request(
+ session, f"/api/v1/collection/{uuid.uuid4()}/", method="DELETE"
+ )
assert response.code == 404
assert not response.data
@@ -576,10 +625,12 @@ def test_list_collections(mdb):
Should also test e.g. pagination once implemented.
"""
- responses = make_request_all_roles('/api/v1/collection/', ret_json=True)
+ responses = make_request_all_roles("/api/v1/collection/", ret_json=True)
for response in responses:
assert response.code == 200
- assert len(response.data['collections']) == mdb['collections'].count_documents({})
+ assert len(response.data["collections"]) == mdb["collections"].count_documents(
+ {}
+ )
def test_get_collection_logs_permissions(mdb):
@@ -588,15 +639,16 @@ def test_get_collection_logs_permissions(mdb):
Assert that DATA_MANAGEMENT or user in owners is required.
"""
- collection_data = mdb['collections'].aggregate([{'$sample': {'size': 1}}]).next()
- user_data = mdb['users'].find_one({'_id': {'$in': collection_data['editors']}})
- responses = make_request_all_roles(f'/api/v1/collection/{collection_data["_id"]}/log/',
- ret_json=True)
+ collection_data = mdb["collections"].aggregate([{"$sample": {"size": 1}}]).next()
+ user_data = mdb["users"].find_one({"_id": {"$in": collection_data["editors"]}})
+ responses = make_request_all_roles(
+ f'/api/v1/collection/{collection_data["_id"]}/log/', ret_json=True
+ )
for response in responses:
- if response.role in ('data', 'root'):
+ if response.role in ("data", "root"):
assert response.code == 200
- assert 'logs' in response.data
- elif response.role == 'no-login':
+ assert "logs" in response.data
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -605,13 +657,13 @@ def test_get_collection_logs_permissions(mdb):
session = requests.Session()
- as_user(session, user_data['auth_ids'][0])
- response = make_request(session,
- f'/api/v1/collection/{collection_data["_id"]}/log/',
- ret_json=True)
+ as_user(session, user_data["auth_ids"][0])
+ response = make_request(
+ session, f'/api/v1/collection/{collection_data["_id"]}/log/', ret_json=True
+ )
assert response.code == 200
- assert 'logs' in response.data
+ assert "logs" in response.data
def test_get_collection_logs(mdb):
@@ -621,12 +673,16 @@ def test_get_collection_logs(mdb):
Confirm that the logs contain only the intended fields.
"""
session = requests.session()
- collections = mdb['collections'].aggregate([{'$sample': {'size': 2}}])
+ collections = mdb["collections"].aggregate([{"$sample": {"size": 2}}])
for collection in collections:
- logs = list(mdb['logs'].find({'data_type': 'collection', 'data._id': collection['_id']}))
- as_user(session, USERS['data'])
- response = make_request(session, f'/api/v1/collection/{collection["_id"]}/log/', ret_json=True)
- assert response.data['dataType'] == 'collection'
- assert response.data['entryId'] == str(collection['_id'])
- assert len(response.data['logs']) == len(logs)
+ logs = list(
+ mdb["logs"].find({"data_type": "collection", "data._id": collection["_id"]})
+ )
+ as_user(session, USERS["data"])
+ response = make_request(
+ session, f'/api/v1/collection/{collection["_id"]}/log/', ret_json=True
+ )
+ assert response.data["dataType"] == "collection"
+ assert response.data["entryId"] == str(collection["_id"])
+ assert len(response.data["logs"]) == len(logs)
assert response.code == 200
diff --git a/backend/tests/test_datasets.py b/backend/tests/test_datasets.py
index 858b82af..30cccf51 100644
--- a/backend/tests/test_datasets.py
+++ b/backend/tests/test_datasets.py
@@ -6,66 +6,136 @@
# avoid pylint errors because of fixtures
# pylint: disable = redefined-outer-name, unused-import
-from helpers import make_request, as_user, make_request_all_roles,\
- dataset_for_tests, USERS, random_string, parse_time, TEST_LABEL, mdb,\
- add_dataset, delete_dataset, USER_RE
+from helpers import (
+ make_request,
+ as_user,
+ make_request_all_roles,
+ dataset_for_tests,
+ USERS,
+ random_string,
+ parse_time,
+ TEST_LABEL,
+ mdb,
+ add_dataset,
+ delete_dataset,
+ USER_RE,
+)
-def test_list_user_datasets(mdb):
+def test_list_datasets(mdb):
+ """
+ Confirm that listing datasets work as intended.
+
+ Tests:
+
+ * Confirm all datasets in the database are listed.
+ * Confirm that the correct fields are included
+ """
+ responses = make_request_all_roles("/api/v1/dataset/", ret_json=True)
+ expected_fields = {"title", "_id", "tags", "properties"}
+ for response in responses:
+ assert response.code == 200
+ assert len(response.data["datasets"]) == mdb["datasets"].count_documents({})
+ assert set(response.data["datasets"][0].keys()) == expected_fields
+
+
+def test_list_user_datasets_permissions():
"""
- Choose a few users.
+ Confirm that users get the correct status code response.
- Compare the ids of datasets from the request to a db query.
+ Tests:
+
+ * Confirm that non-logged in users get 401, logged in users 200
+ """
+ responses = make_request_all_roles("/api/v1/dataset/user/")
+ for response in responses:
+ if response.role == "no-login":
+ assert response.code == 401
+ else:
+ assert response.code == 200
+
+
+def test_list_user_datasets_with_datasets(mdb):
+ """
+ Confirm that users get the correct datasets.
+
+ Tests:
+
+ * Select a few users, confirm that the returned datasets are correct
+ * Confirm that the included fields are the intended ones
"""
session = requests.Session()
- users = mdb['users'].aggregate([{'$sample': {'size': 5}},
- {'$match': {'auth_ids': USER_RE}}])
+ orders = mdb["orders"].aggregate(
+ [{"$match": {"datasets": {"$not": {"$size": 0}}}}, {"$sample": {"size": 2}}]
+ )
+ user_uuids = list(
+ itertools.chain.from_iterable(order["editors"] for order in orders)
+ )
+ users = mdb["users"].find({"_id": {"$in": list(user_uuids)}})
for user in users:
- user_orders = list(mdb['orders'].find({'$or': [{'editors': user['_id']},
- {'receivers': user['_id']}],
- 'datasets': {'$not': {'$size': 0}}},
- {'datasets': 1}))
- user_datasets = list(itertools.chain.from_iterable(order['datasets']
- for order in user_orders))
+ user_orders = list(
+ mdb["orders"].find({"editors": user["_id"]}, {"datasets": 1})
+ )
+ user_datasets = list(
+ itertools.chain.from_iterable(order["datasets"] for order in user_orders)
+ )
user_datasets = [str(uuid) for uuid in user_datasets]
- as_user(session, user['auth_ids'][0])
- response = make_request(session, '/api/v1/dataset/user/')
+ as_user(session, user["auth_ids"][0])
+ response = make_request(session, "/api/v1/dataset/user/")
assert response.code == 200
- assert len(user_datasets) == len(response.data['datasets'])
- for dset in response.data['datasets']:
- assert dset['_id'] in user_datasets
+ assert len(user_datasets) == len(response.data["datasets"])
+ assert set(entry["_id"] for entry in response.data["datasets"]) == set(
+ user_datasets
+ )
+
+
+def test_list_user_datasets_no_datasets():
+ """
+ Confirm that users with no datasets get the correct response.
+
+ Tests:
+
+ * Select a few users, confirm that no datasets are returned as intended
+ """
+ # *::testers should have no datasets
+ responses = make_request_all_roles("/api/v1/dataset/user/", ret_json=True)
+ for response in responses:
+ if response.role != "no-login":
+ assert len(response.data["datasets"]) == 0
def test_random_dataset():
"""Request a random dataset."""
- responses = make_request_all_roles('/api/v1/dataset/random/', ret_json=True)
+ responses = make_request_all_roles("/api/v1/dataset/random/", ret_json=True)
for response in responses:
assert response.code == 200
- assert len(response.data['datasets']) == 1
+ assert len(response.data["datasets"]) == 1
def test_random_datasets():
"""Request random datasets."""
session = requests.Session()
- as_user(session, USERS['base'])
+ as_user(session, USERS["base"])
for i in (1, 5, 0):
- response = make_request(session, f'/api/v1/dataset/random/{i}/')
+ response = make_request(session, f"/api/v1/dataset/random/{i}/")
assert response.code == 200
- assert len(response.data['datasets']) == i
+ assert len(response.data["datasets"]) == i
- response = make_request(session, '/api/v1/dataset/random/-1')
+ response = make_request(session, "/api/v1/dataset/random/-1")
assert response.code == 404
assert not response.data
def test_get_dataset_get_permissions(mdb):
"""Test permissions for requesting a dataset."""
- orders = list(mdb['datasets'].aggregate([{'$sample': {'size': 2}}]))
+ orders = list(mdb["datasets"].aggregate([{"$sample": {"size": 2}}]))
for order in orders:
- responses = make_request_all_roles(f'/api/v1/dataset/{order["_id"]}/', ret_json=True)
+ responses = make_request_all_roles(
+ f'/api/v1/dataset/{order["_id"]}/', ret_json=True
+ )
for response in responses:
- assert response.data['dataset']
+ assert response.data["dataset"]
assert response.code == 200
@@ -77,12 +147,12 @@ def test_get_dataset():
"""
session = requests.Session()
for _ in range(10):
- orig = make_request(session, '/api/v1/dataset/random/')[0]['datasets'][0]
+ orig = make_request(session, "/api/v1/dataset/random/")[0]["datasets"][0]
response = make_request(session, f'/api/v1/dataset/{orig["_id"]}/')
assert response[1] == 200
- requested = response[0]['dataset']
+ requested = response[0]["dataset"]
assert orig == requested
- assert requested['_id'] not in requested['related']
+ assert requested["_id"] not in requested["related"]
def test_get_dataset_bad():
@@ -93,12 +163,12 @@ def test_get_dataset_bad():
"""
session = requests.Session()
for _ in range(5):
- response = make_request(session, f'/api/v1/dataset/{uuid.uuid4().hex}/')
+ response = make_request(session, f"/api/v1/dataset/{uuid.uuid4().hex}/")
assert response.code == 404
assert not response.data
for _ in range(5):
- response = make_request(session, f'/api/v1/dataset/{random_string()}/')
+ response = make_request(session, f"/api/v1/dataset/{random_string()}/")
assert response.code == 404
assert not response.data
@@ -116,42 +186,60 @@ def test_delete_dataset(mdb):
uuids = [add_dataset() for _ in range(5)]
- datasets = list(mdb['datasets'].find(TEST_LABEL))
+ datasets = list(mdb["datasets"].find(TEST_LABEL))
if not datasets:
assert False
i = 0
while i < len(datasets):
for role in USERS:
as_user(session, USERS[role])
- order = mdb['orders'].find_one({'datasets': datasets[i]['_id']})
- collections = list(mdb['collections'].find({'datasets': datasets[i]['_id']}))
- response = make_request(session,
- f'/api/v1/dataset/{datasets[i]["_id"]}/',
- method='DELETE')
- current_user = mdb['users'].find_one({'auth_ids': USERS[role]})
- if role == 'no-login':
+ order = mdb["orders"].find_one({"datasets": datasets[i]["_id"]})
+ collections = list(
+ mdb["collections"].find({"datasets": datasets[i]["_id"]})
+ )
+ response = make_request(
+ session, f'/api/v1/dataset/{datasets[i]["_id"]}/', method="DELETE"
+ )
+ current_user = mdb["users"].find_one({"auth_ids": USERS[role]})
+ if role == "no-login":
assert response.code == 401
assert not response.data
# only data managers or owners may delete datasets
- elif role in ('data', 'root') or current_user['_id'] in order['editors']:
+ elif role in ("data", "root") or current_user["_id"] in order["editors"]:
assert response.code == 200
assert not response.data
# confirm that dataset does not exist in mdb and that a log has been created
- assert not mdb['datasets'].find_one({'_id': datasets[i]['_id']})
- assert mdb['logs'].find_one({'data._id': datasets[i]['_id'],
- 'action': 'delete',
- 'data_type': 'dataset'})
+ assert not mdb["datasets"].find_one({"_id": datasets[i]["_id"]})
+ assert mdb["logs"].find_one(
+ {
+ "data._id": datasets[i]["_id"],
+ "action": "delete",
+ "data_type": "dataset",
+ }
+ )
# confirm that no references to the dataset exist in orders or collection
- assert not list(mdb['orders'].find({'datasets': datasets[i]['_id']}))
- assert not list(mdb['collections'].find({'datasets': datasets[i]['_id']}))
+ assert not list(mdb["orders"].find({"datasets": datasets[i]["_id"]}))
+ assert not list(
+ mdb["collections"].find({"datasets": datasets[i]["_id"]})
+ )
# confirm that the removal of the references are logged.
- assert mdb['logs'].find_one({'data._id': order['_id'],
- 'action': 'edit',
- 'data_type': 'order',
- 'comment': f'Deleted dataset {datasets[i]["_id"]}'})
- p_log = list(mdb['logs'].find({'action': 'edit',
- 'data_type': 'collection',
- 'comment': f'Deleted dataset {datasets[i]["_id"]}'}))
+ assert mdb["logs"].find_one(
+ {
+ "data._id": order["_id"],
+ "action": "edit",
+ "data_type": "order",
+ "comment": f'Deleted dataset {datasets[i]["_id"]}',
+ }
+ )
+ p_log = list(
+ mdb["logs"].find(
+ {
+ "action": "edit",
+ "data_type": "collection",
+ "comment": f'Deleted dataset {datasets[i]["_id"]}',
+ }
+ )
+ )
assert len(p_log) == len(collections)
i += 1
if i >= len(datasets):
@@ -172,18 +260,14 @@ def test_delete_bad():
Should require at least Steward.
"""
session = requests.Session()
- as_user(session, USERS['data'])
+ as_user(session, USERS["data"])
for _ in range(3):
ds_uuid = random_string()
- response = make_request(session,
- f'/api/v1/dataset/{ds_uuid}/',
- method='DELETE')
+ response = make_request(session, f"/api/v1/dataset/{ds_uuid}/", method="DELETE")
assert response.code == 404
assert not response.data
ds_uuid = uuid.uuid4().hex
- response = make_request(session,
- f'/api/v1/dataset/{ds_uuid}/',
- method='DELETE')
+ response = make_request(session, f"/api/v1/dataset/{ds_uuid}/", method="DELETE")
assert response.code == 404
assert not response.data
@@ -195,12 +279,14 @@ def test_dataset_update_permissions(dataset_for_tests):
Should require at least Steward or being the owner of the dataset.
"""
ds_uuid = dataset_for_tests
- indata = {'title': 'Updated title'}
- responses = make_request_all_roles(f'/api/v1/dataset/{ds_uuid}/', method='PATCH', data=indata)
+ indata = {"title": "Updated title"}
+ responses = make_request_all_roles(
+ f"/api/v1/dataset/{ds_uuid}/", method="PATCH", data=indata
+ )
for response in responses:
- if response.role in ('orders', 'data', 'root'):
+ if response.role in ("orders", "data", "root"):
assert response.code == 200
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
else:
assert response.code == 403
@@ -215,11 +301,13 @@ def test_dataset_update_empty(dataset_for_tests):
"""
ds_uuid = dataset_for_tests
indata = {}
- responses = make_request_all_roles(f'/api/v1/dataset/{ds_uuid}/', method='PATCH', data=indata)
+ responses = make_request_all_roles(
+ f"/api/v1/dataset/{ds_uuid}/", method="PATCH", data=indata
+ )
for response in responses:
- if response.role in ('orders', 'data', 'root'):
+ if response.role in ("orders", "data", "root"):
assert response.code == 200
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
else:
assert response.code == 403
@@ -233,23 +321,27 @@ def test_dataset_update(mdb, dataset_for_tests):
Should require at least Steward.
"""
ds_uuid = dataset_for_tests
- indata = {'description': 'Test description - updated',
- 'title': 'Test title - updated'}
+ indata = {
+ "description": "Test description - updated",
+ "title": "Test title - updated",
+ }
indata.update(TEST_LABEL)
session = requests.Session()
- as_user(session, USERS['data'])
+ as_user(session, USERS["data"])
- response = make_request(session, f'/api/v1/dataset/{ds_uuid}/', method='PATCH', data=indata)
+ response = make_request(
+ session, f"/api/v1/dataset/{ds_uuid}/", method="PATCH", data=indata
+ )
assert response.code == 200
assert not response.data
- dataset = mdb['datasets'].find_one({'_id': ds_uuid})
+ dataset = mdb["datasets"].find_one({"_id": ds_uuid})
for field in indata:
assert dataset[field] == indata[field]
- assert mdb['logs'].find_one({'data._id': ds_uuid,
- 'action': 'edit',
- 'data_type': 'dataset'})
+ assert mdb["logs"].find_one(
+ {"data._id": ds_uuid, "action": "edit", "data_type": "dataset"}
+ )
def test_dataset_update_bad(dataset_for_tests):
@@ -259,26 +351,28 @@ def test_dataset_update_bad(dataset_for_tests):
Should require at least Steward.
"""
for _ in range(2):
- indata = {'title': 'Updated title'}
+ indata = {"title": "Updated title"}
ds_uuid = random_string()
- responses = make_request_all_roles(f'/api/v1/dataset/{ds_uuid}/',
- method='PATCH', data=indata)
+ responses = make_request_all_roles(
+ f"/api/v1/dataset/{ds_uuid}/", method="PATCH", data=indata
+ )
for response in responses:
- if response.role in ('base', 'orders', 'data', 'root'):
+ if response.role in ("base", "orders", "data", "root"):
assert response.code == 404
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
else:
assert response.code == 404
assert not response.data
ds_uuid = uuid.uuid4().hex
- responses = make_request_all_roles(f'/api/v1/dataset/{ds_uuid}/',
- method='PATCH', data=indata)
+ responses = make_request_all_roles(
+ f"/api/v1/dataset/{ds_uuid}/", method="PATCH", data=indata
+ )
for response in responses:
- if response.role in ('base', 'orders', 'data', 'root'):
+ if response.role in ("base", "orders", "data", "root"):
assert response.code == 404
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
else:
assert response.code == 404
@@ -286,54 +380,48 @@ def test_dataset_update_bad(dataset_for_tests):
ds_uuid = dataset_for_tests
session = requests.Session()
- as_user(session, USERS['data'])
- indata = {'title': ''}
- response = make_request(session, f'/api/v1/dataset/{ds_uuid}/',
- method='PATCH', data=indata)
+ as_user(session, USERS["data"])
+ indata = {"title": ""}
+ response = make_request(
+ session, f"/api/v1/dataset/{ds_uuid}/", method="PATCH", data=indata
+ )
assert response.code == 400
assert not response.data
- indata = {'extra': 'asd'}
- response = make_request(session, f'/api/v1/dataset/{ds_uuid}/',
- method='PATCH', data=indata)
+ indata = {"extra": "asd"}
+ response = make_request(
+ session, f"/api/v1/dataset/{ds_uuid}/", method="PATCH", data=indata
+ )
assert response.code == 400
assert not response.data
- indata = {'timestamp': 'asd'}
- response = make_request(session, f'/api/v1/dataset/{ds_uuid}/',
- method='PATCH', data=indata)
+ indata = {"timestamp": "asd"}
+ response = make_request(
+ session, f"/api/v1/dataset/{ds_uuid}/", method="PATCH", data=indata
+ )
assert response.code == 400
assert not response.data
-def test_list_datasets(mdb):
- """
- Request a list of all datasets.
-
- Should also test e.g. pagination once implemented.
- """
- responses = make_request_all_roles('/api/v1/dataset/', ret_json=True)
- for response in responses:
- assert response.code == 200
- assert len(response.data['datasets']) == mdb['datasets'].count_documents({})
-
-
def test_get_dataset_logs_permissions(mdb):
"""
Get dataset logs.
Assert that DATA_MANAGEMENT or user in editors is required.
"""
- dataset_data = mdb['datasets'].aggregate([{'$sample': {'size': 1}}]).next()
- order_data = mdb['orders'].find_one({'datasets': dataset_data['_id']})
- user_data = mdb['users'].find_one({'$or': [{'_id': {'$in': order_data['editors']}}]})
- responses = make_request_all_roles(f'/api/v1/dataset/{dataset_data["_id"]}/log/',
- ret_json=True)
+ dataset_data = mdb["datasets"].aggregate([{"$sample": {"size": 1}}]).next()
+ order_data = mdb["orders"].find_one({"datasets": dataset_data["_id"]})
+ user_data = mdb["users"].find_one(
+ {"$or": [{"_id": {"$in": order_data["editors"]}}]}
+ )
+ responses = make_request_all_roles(
+ f'/api/v1/dataset/{dataset_data["_id"]}/log/', ret_json=True
+ )
for response in responses:
- if response.role in ('data', 'root'):
+ if response.role in ("data", "root"):
assert response.code == 200
- assert 'logs' in response.data
- elif response.role == 'no-login':
+ assert "logs" in response.data
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -342,13 +430,13 @@ def test_get_dataset_logs_permissions(mdb):
session = requests.Session()
- as_user(session, user_data['auth_ids'][0])
- response = make_request(session,
- f'/api/v1/dataset/{dataset_data["_id"]}/log/',
- ret_json=True)
+ as_user(session, user_data["auth_ids"][0])
+ response = make_request(
+ session, f'/api/v1/dataset/{dataset_data["_id"]}/log/', ret_json=True
+ )
assert response.code == 200
- assert 'logs' in response.data
+ assert "logs" in response.data
def test_get_dataset_logs(mdb):
@@ -358,14 +446,18 @@ def test_get_dataset_logs(mdb):
Confirm that the logs contain only the intended fields.
"""
session = requests.session()
- datasets = mdb['datasets'].aggregate([{'$sample': {'size': 2}}])
+ datasets = mdb["datasets"].aggregate([{"$sample": {"size": 2}}])
for dataset in datasets:
- logs = list(mdb['logs'].find({'data_type': 'dataset', 'data._id': dataset['_id']}))
- as_user(session, USERS['data'])
- response = make_request(session, f'/api/v1/dataset/{dataset["_id"]}/log/', ret_json=True)
- assert response.data['dataType'] == 'dataset'
- assert response.data['entryId'] == str(dataset['_id'])
- assert len(response.data['logs']) == len(logs)
+ logs = list(
+ mdb["logs"].find({"data_type": "dataset", "data._id": dataset["_id"]})
+ )
+ as_user(session, USERS["data"])
+ response = make_request(
+ session, f'/api/v1/dataset/{dataset["_id"]}/log/', ret_json=True
+ )
+ assert response.data["dataType"] == "dataset"
+ assert response.data["entryId"] == str(dataset["_id"])
+ assert len(response.data["logs"]) == len(logs)
assert response.code == 200
@@ -378,22 +470,20 @@ def test_add_dataset_permissions(mdb):
session = requests.Session()
db = mdb
- orders = db['orders'].aggregate([{'$sample': {'size': 2}}])
+ orders = db["orders"].aggregate([{"$sample": {"size": 2}}])
for order in orders:
- indata = {'title': 'Test title',
- 'order': str(order['_id'])}
+ indata = {"title": "Test title", "order": str(order["_id"])}
indata.update(TEST_LABEL)
- responses = make_request_all_roles(f'/api/v1/dataset/',
- method='POST',
- data=indata,
- ret_json=True)
+ responses = make_request_all_roles(
+ "/api/v1/dataset/", method="POST", data=indata, ret_json=True
+ )
for response in responses:
- if response.role in ('data', 'root'):
+ if response.role in ("data", "root"):
assert response.code == 200
- assert '_id' in response.data
- assert len(response.data['_id']) == 36
- elif response.role == 'no-login':
+ assert "_id" in response.data
+ assert len(response.data["_id"]) == 36
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -401,15 +491,12 @@ def test_add_dataset_permissions(mdb):
assert not response.data
# as order editor
- owner = db['users'].find_one({'_id': order['editors'][0]})
- as_user(session, owner['auth_ids'][0])
- response = make_request(session,
- f'/api/v1/dataset/',
- method='POST',
- data=indata)
+ owner = db["users"].find_one({"_id": order["editors"][0]})
+ as_user(session, owner["auth_ids"][0])
+ response = make_request(session, "/api/v1/dataset/", method="POST", data=indata)
assert response.code == 200
- assert '_id' in response.data
- assert len(response.data['_id']) == 36
+ assert "_id" in response.data
+ assert len(response.data["_id"]) == 36
def test_add_dataset(mdb):
@@ -418,33 +505,33 @@ def test_add_dataset(mdb):
Set values in all available fields.
"""
- order = next(mdb['orders'].aggregate([{'$sample': {'size': 1}}]))
- indata = {'title': 'Test title',
- 'description': 'Test description',
- 'order': str(order['_id']),}
+ order = next(mdb["orders"].aggregate([{"$sample": {"size": 1}}]))
+ indata = {
+ "title": "Test title",
+ "description": "Test description",
+ "order": str(order["_id"]),
+ }
indata.update(TEST_LABEL)
session = requests.session()
- as_user(session, USERS['data'])
+ as_user(session, USERS["data"])
- response = make_request(session,
- f'/api/v1/dataset/',
- method='POST',
- data=indata,
- ret_json=True)
+ response = make_request(
+ session, "/api/v1/dataset/", method="POST", data=indata, ret_json=True
+ )
assert response.code == 200
- assert '_id' in response.data
- assert len(response.data['_id']) == 36
- indata.update({'_id': response.data['_id']})
- mdb_ds = mdb['datasets'].find_one({'_id': uuid.UUID(response.data['_id'])})
- mdb_o = mdb['orders'].find_one({'_id': order['_id']})
- mdb_ds['_id'] = str(mdb_ds['_id'])
- mdb_o['datasets'] = [str(ds_uuid) for ds_uuid in mdb_o['datasets']]
+ assert "_id" in response.data
+ assert len(response.data["_id"]) == 36
+ indata.update({"_id": response.data["_id"]})
+ mdb_ds = mdb["datasets"].find_one({"_id": uuid.UUID(response.data["_id"])})
+ mdb_o = mdb["orders"].find_one({"_id": order["_id"]})
+ mdb_ds["_id"] = str(mdb_ds["_id"])
+ mdb_o["datasets"] = [str(ds_uuid) for ds_uuid in mdb_o["datasets"]]
for field in indata:
- if field == 'order':
+ if field == "order":
continue
assert mdb_ds[field] == indata[field]
- assert response.data['_id'] in mdb_o['datasets']
+ assert response.data["_id"] in mdb_o["datasets"]
def test_add_dataset_log(mdb):
@@ -453,80 +540,67 @@ def test_add_dataset_log(mdb):
Check that both there is both update on order and add on dataset.
"""
- order = next(mdb['orders'].aggregate([{'$sample': {'size': 1}}]))
- indata = {'title': 'Test title',
- 'order': str(order['_id'])}
+ order = next(mdb["orders"].aggregate([{"$sample": {"size": 1}}]))
+ indata = {"title": "Test title", "order": str(order["_id"])}
indata.update(TEST_LABEL)
session = requests.session()
- as_user(session, USERS['data'])
+ as_user(session, USERS["data"])
- order_logs = list(mdb['logs'].find({'data_type': 'order', 'data._id': order['_id']}))
+ order_logs = list(
+ mdb["logs"].find({"data_type": "order", "data._id": order["_id"]})
+ )
- response = make_request(session,
- f'/api/v1/dataset/',
- method='POST',
- data=indata,
- ret_json=True)
+ response = make_request(
+ session, "/api/v1/dataset/", method="POST", data=indata, ret_json=True
+ )
- order_logs_post = list(mdb['logs'].find({'data_type': 'order', 'data._id': order['_id']}))
+ order_logs_post = list(
+ mdb["logs"].find({"data_type": "order", "data._id": order["_id"]})
+ )
print(order_logs_post)
- assert len(order_logs_post) == len(order_logs)+1
- ds_logs_post = list(mdb['logs'].find({'data_type': 'dataset',
- 'data._id': uuid.UUID(response.data['_id'])}))
+ assert len(order_logs_post) == len(order_logs) + 1
+ ds_logs_post = list(
+ mdb["logs"].find(
+ {"data_type": "dataset", "data._id": uuid.UUID(response.data["_id"])}
+ )
+ )
assert len(ds_logs_post) == 1
- assert ds_logs_post[0]['action']
+ assert ds_logs_post[0]["action"]
def test_add_dataset_bad_fields(mdb):
"""Attempt to add datasets with e.g. forbidden fields."""
db = mdb
- order = next(db['orders'].aggregate([{'$sample': {'size': 1}}]))
+ order = next(db["orders"].aggregate([{"$sample": {"size": 1}}]))
session = requests.Session()
- as_user(session, USERS['data'])
-
- indata = {'_id': 'asd',
- 'title': 'test title',
- 'order': str(order["_id"]),}
- response = make_request(session,
- f'/api/v1/dataset/',
- method='POST',
- data=indata)
+ as_user(session, USERS["data"])
+
+ indata = {
+ "_id": "asd",
+ "title": "test title",
+ "order": str(order["_id"]),
+ }
+ response = make_request(session, "/api/v1/dataset/", method="POST", data=indata)
assert response.code == 403
assert not response.data
- indata = {'timestamp': 'asd',
- 'title': 'test title'}
- response = make_request(session,
- f'/api/v1/dataset/',
- method='POST',
- data=indata)
+ indata = {"timestamp": "asd", "title": "test title"}
+ response = make_request(session, "/api/v1/dataset/", method="POST", data=indata)
assert response.code == 400
assert not response.data
- indata = {'extra': [{'asd': 123}],
- 'title': 'test title'}
- response = make_request(session,
- f'/api/v1/dataset/',
- method='POST',
- data=indata)
+ indata = {"extra": [{"asd": 123}], "title": "test title"}
+ response = make_request(session, "/api/v1/dataset/", method="POST", data=indata)
assert response.code == 400
assert not response.data
- indata = {'links': [{'asd': 123}],
- 'title': 'test title'}
- response = make_request(session,
- f'/api/v1/dataset/',
- method='POST',
- data=indata)
+ indata = {"links": [{"asd": 123}], "title": "test title"}
+ response = make_request(session, "/api/v1/dataset/", method="POST", data=indata)
assert response.code == 400
assert not response.data
- indata = {'links': 'Some text',
- 'title': 'test title'}
- response = make_request(session,
- f'/api/v1/dataset/',
- method='POST',
- data=indata)
+ indata = {"links": "Some text", "title": "test title"}
+ response = make_request(session, "/api/v1/dataset/", method="POST", data=indata)
assert response.code == 400
assert not response.data
diff --git a/backend/tests/test_logins.py b/backend/tests/test_logins.py
index 83e71eb2..b15409ee 100644
--- a/backend/tests/test_logins.py
+++ b/backend/tests/test_logins.py
@@ -6,20 +6,29 @@
import helpers
-from helpers import make_request, as_user, make_request_all_roles, USERS, mdb, random_string
+from helpers import (
+ make_request,
+ as_user,
+ make_request_all_roles,
+ USERS,
+ mdb,
+ random_string,
+)
+
# pylint: disable=redefined-outer-name
+
def test_logout():
"""Assure that session is cleared after logging out."""
session = requests.Session()
- as_user(session, USERS['root'])
- response = make_request(session, '/api/v1/user/me/')
- for field in response.data['user']:
- assert response.data['user'][field]
- response = make_request(session, '/api/v1/logout/', ret_json=False)
- response = make_request(session, '/api/v1/user/me/')
- for field in response.data['user']:
- assert not response.data['user'][field]
+ as_user(session, USERS["root"])
+ response = make_request(session, "/api/v1/user/me/")
+ for field in response.data["user"]:
+ assert response.data["user"][field]
+ response = make_request(session, "/api/v1/logout/", ret_json=False)
+ response = make_request(session, "/api/v1/user/me/")
+ for field in response.data["user"]:
+ assert not response.data["user"][field]
def test_key_login():
@@ -27,35 +36,35 @@ def test_key_login():
session = requests.Session()
as_user(session, None)
for i, userid in enumerate(USERS):
- response = make_request(session,
- '/api/v1/login/apikey/',
- data = {'api-user': USERS[userid],
- 'api-key': str(i-1)},
- method='POST')
- if userid == 'no-login':
+ response = make_request(
+ session,
+ "/api/v1/login/apikey/",
+ data={"api-user": USERS[userid], "api-key": str(i - 1)},
+ method="POST",
+ )
+ if userid == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 200
assert not response.data
- response = make_request(session,
- '/api/v1/developer/loginhello')
+ response = make_request(session, "/api/v1/developer/loginhello")
assert response.code == 200
- assert response.data == {'test': 'success'}
+ assert response.data == {"test": "success"}
def test_list_login_types():
"""List possible ways to login"""
- responses = helpers.make_request_all_roles('/api/v1/login/', ret_json=True)
+ responses = helpers.make_request_all_roles("/api/v1/login/", ret_json=True)
for response in responses:
assert response.code == 200
- assert response.data == {'types': ['apikey', 'oidc']}
+ assert response.data == {"types": ["apikey", "oidc"]}
def test_list_oidc_types():
"""List supported oidc logins"""
- responses = helpers.make_request_all_roles('/api/v1/login/oidc/', ret_json=True)
+ responses = helpers.make_request_all_roles("/api/v1/login/oidc/", ret_json=True)
for response in responses:
assert response.code == 200
- assert response.data == {'oidcserver': '/api/v1/login/oidc/oidcserver/login/'}
+ assert response.data == {"entry": "/api/v1/login/oidc/entry/login/"}
diff --git a/backend/tests/test_orders.py b/backend/tests/test_orders.py
index 66281893..26b938ed 100644
--- a/backend/tests/test_orders.py
+++ b/backend/tests/test_orders.py
@@ -7,8 +7,16 @@
import structure
import utils
-from helpers import make_request, as_user, make_request_all_roles,\
- USERS, random_string, TEST_LABEL, mdb, USER_RE
+from helpers import (
+ make_request,
+ as_user,
+ make_request_all_roles,
+ USERS,
+ random_string,
+ TEST_LABEL,
+ mdb,
+ USER_RE,
+)
# avoid pylint errors because of fixtures
# pylint: disable = redefined-outer-name, unused-import
@@ -26,25 +34,30 @@ def test_get_order_permissions(mdb):
session = requests.Session()
db = mdb
- orders = list(db['orders'].aggregate([{'$match': {'auth_ids': USER_RE}},
- {'$sample': {'size': 2}}]))
+ orders = list(
+ db["orders"].aggregate(
+ [{"$match": {"auth_ids": USER_RE}}, {"$sample": {"size": 2}}]
+ )
+ )
for order in orders:
- owner = db['users'].find_one({'_id': order['editors'][0]})
- responses = make_request_all_roles(f'/api/v1/order/{order["_id"]}/', ret_json=True)
+ owner = db["users"].find_one({"_id": order["editors"][0]})
+ responses = make_request_all_roles(
+ f'/api/v1/order/{order["_id"]}/', ret_json=True
+ )
for response in responses:
- if response.role in ('data', 'root'):
+ if response.role in ("data", "root"):
assert response.code == 200
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 403
assert not response.data
- as_user(session, owner['auth_id'])
+ as_user(session, owner["auth_id"])
response = make_request(session, f'/api/v1/order/{order["_id"]}/')
assert response.code == 200
- data = response.data['order']
+ data = response.data["order"]
def test_get_order(mdb):
@@ -54,32 +67,33 @@ def test_get_order(mdb):
Request the order and confirm that it contains the correct data.
"""
session = requests.Session()
- as_user(session, USERS['data'])
+ as_user(session, USERS["data"])
db = mdb
- orders = list(db['orders'].aggregate([{'$sample': {'size': 3}}]))
+ orders = list(db["orders"].aggregate([{"$sample": {"size": 3}}]))
for order in orders:
# to simplify comparison
- order['_id'] = str(order['_id'])
+ order["_id"] = str(order["_id"])
# user entries
- for key in ('authors', 'generators', 'editors'):
+ for key in ("authors", "generators", "editors"):
order[key] = utils.user_uuid_data(order[key], db)
- order['organisation'] = utils.user_uuid_data(order['organisation'], db)[0]
-
- for i, ds in enumerate(order['datasets']):
- order['datasets'][i] = next(db['datasets'].aggregate([{'$match': {'_id': ds}},
- {'$project': {'_id': 1,
- 'title': 1}}]))
- order['datasets'][i]['_id'] = str(order['datasets'][i]['_id'])
+ order["organisation"] = utils.user_uuid_data(order["organisation"], db)[0]
+ for i, ds in enumerate(order["datasets"]):
+ order["datasets"][i] = next(
+ db["datasets"].aggregate(
+ [{"$match": {"_id": ds}}, {"$project": {"_id": 1, "title": 1}}]
+ )
+ )
+ order["datasets"][i]["_id"] = str(order["datasets"][i]["_id"])
response = make_request(session, f'/api/v1/order/{order["_id"]}/')
assert response.code == 200
assert response.code == 200
- data = response.data['order']
+ data = response.data["order"]
assert len(order) == len(data)
for field in order:
- if field == 'datasets':
+ if field == "datasets":
assert len(order[field]) == len(data[field])
for ds in order[field]:
assert ds in data[field]
@@ -87,18 +101,17 @@ def test_get_order(mdb):
assert order[field] == data[field]
-
def test_get_order_structure():
"""Request the order structure and confirm that it matches the official one"""
session = requests.Session()
- as_user(session, USERS['data'])
+ as_user(session, USERS["data"])
reference = structure.order()
- reference['_id'] = ''
+ reference["_id"] = ""
- response = make_request(session, '/api/v1/order/base/')
+ response = make_request(session, "/api/v1/order/base/")
assert response.code == 200
- data = response.data['order']
+ data = response.data["order"]
assert data == reference
@@ -109,22 +122,22 @@ def test_get_order_bad():
All are expected to return 401, 403, or 404 depending on permissions.
"""
for _ in range(2):
- responses = make_request_all_roles(f'/api/v1/order/{uuid.uuid4()}/')
+ responses = make_request_all_roles(f"/api/v1/order/{uuid.uuid4()}/")
for response in responses:
- if response.role in ('orders', 'data', 'root'):
+ if response.role in ("orders", "data", "root"):
assert response.code == 404
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
else:
assert response.code == 403
assert not response.data
for _ in range(2):
- responses = make_request_all_roles(f'/api/v1/order/{random_string()}/')
+ responses = make_request_all_roles(f"/api/v1/order/{random_string()}/")
for response in responses:
- if response.role in ('orders', 'data', 'root'):
+ if response.role in ("orders", "data", "root"):
assert response.code == 404
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
else:
assert response.code == 403
@@ -138,15 +151,16 @@ def test_get_order_logs_permissions(mdb):
Assert that DATA_MANAGEMENT or user in creator is required.
"""
db = mdb
- order_data = db['orders'].aggregate([{'$sample': {'size': 1}}]).next()
- user_data = db['users'].find_one({'_id': {'$in': order_data['editors']}})
- responses = make_request_all_roles(f'/api/v1/order/{order_data["_id"]}/log/',
- ret_json=True)
+ order_data = db["orders"].aggregate([{"$sample": {"size": 1}}]).next()
+ user_data = db["users"].find_one({"_id": {"$in": order_data["editors"]}})
+ responses = make_request_all_roles(
+ f'/api/v1/order/{order_data["_id"]}/log/', ret_json=True
+ )
for response in responses:
- if response.role in ('data', 'root'):
+ if response.role in ("data", "root"):
assert response.code == 200
- assert 'logs' in response.data
- elif response.role == 'no-login':
+ assert "logs" in response.data
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -155,13 +169,13 @@ def test_get_order_logs_permissions(mdb):
session = requests.Session()
- as_user(session, user_data['auth_ids'][0])
- response = make_request(session,
- f'/api/v1/order/{order_data["_id"]}/log/',
- ret_json=True)
+ as_user(session, user_data["auth_ids"][0])
+ response = make_request(
+ session, f'/api/v1/order/{order_data["_id"]}/log/', ret_json=True
+ )
assert response.code == 200
- assert 'logs' in response.data
+ assert "logs" in response.data
def test_get_order_logs(mdb):
@@ -172,14 +186,16 @@ def test_get_order_logs(mdb):
"""
session = requests.session()
db = mdb
- orders = db['orders'].aggregate([{'$sample': {'size': 2}}])
+ orders = db["orders"].aggregate([{"$sample": {"size": 2}}])
for order in orders:
- logs = list(db['logs'].find({'data_type': 'order', 'data._id': order['_id']}))
- as_user(session, USERS['data'])
- response = make_request(session, f'/api/v1/order/{order["_id"]}/log/', ret_json=True)
- assert response.data['dataType'] == 'order'
- assert response.data['entryId'] == str(order['_id'])
- assert len(response.data['logs']) == len(logs)
+ logs = list(db["logs"].find({"data_type": "order", "data._id": order["_id"]}))
+ as_user(session, USERS["data"])
+ response = make_request(
+ session, f'/api/v1/order/{order["_id"]}/log/', ret_json=True
+ )
+ assert response.data["dataType"] == "order"
+ assert response.data["entryId"] == str(order["_id"])
+ assert len(response.data["logs"]) == len(logs)
assert response.code == 200
@@ -191,10 +207,14 @@ def test_get_order_logs_bad():
"""
session = requests.session()
for _ in range(2):
- as_user(session, USERS['data'])
- response = make_request(session, f'/api/v1/order/{uuid.uuid4()}/log/', ret_json=True)
+ as_user(session, USERS["data"])
+ response = make_request(
+ session, f"/api/v1/order/{uuid.uuid4()}/log/", ret_json=True
+ )
assert response.code == 200
- response = make_request(session, f'/api/v1/order/{random_string()}/log/', ret_json=True)
+ response = make_request(
+ session, f"/api/v1/order/{random_string()}/log/", ret_json=True
+ )
assert response.code == 404
@@ -207,47 +227,52 @@ def test_list_user_orders_permissions(mdb):
session = requests.Session()
db = mdb
- users = db['users'].aggregate([{'$match': {'permissions': {'$in': ['ORDERS_SELF',
- 'DATA_MANAGEMENT']}}},
- {'$sample': {'size': 2}}])
+ users = db["users"].aggregate(
+ [
+ {"$match": {"permissions": {"$in": ["ORDERS_SELF", "DATA_MANAGEMENT"]}}},
+ {"$sample": {"size": 2}},
+ ]
+ )
for user in users:
- responses = make_request_all_roles('/api/v1/order/user/', ret_json=True)
+ responses = make_request_all_roles("/api/v1/order/user/", ret_json=True)
for response in responses:
- if response.role in ('orders', 'data', 'root'):
+ if response.role in ("orders", "data", "root"):
assert response.code == 200
- assert len(response.data['orders']) == 0
- elif response.role == 'no-login':
+ assert len(response.data["orders"]) == 0
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 403
assert not response.data
- user_orders = list(db['orders'].find({'editors': user['_id']}))
- responses = make_request_all_roles(f'/api/v1/order/user/{user["_id"]}/', ret_json=True)
+ user_orders = list(db["orders"].find({"editors": user["_id"]}))
+ responses = make_request_all_roles(
+ f'/api/v1/order/user/{user["_id"]}/', ret_json=True
+ )
for response in responses:
- if response.role in ('data', 'root'):
+ if response.role in ("data", "root"):
if user_orders:
assert response.code == 200
assert response.data
else:
assert response.code == 200
- assert len(response.data['orders']) == 0
- elif response.role == 'no-login':
+ assert len(response.data["orders"]) == 0
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 403
assert not response.data
- as_user(session, user['auth_ids'][0])
- response = make_request(session, '/api/v1/order/user/')
+ as_user(session, user["auth_ids"][0])
+ response = make_request(session, "/api/v1/order/user/")
if user_orders:
assert response.code == 200
assert response.data
else:
assert response.code == 200
- assert len(response.data['orders']) == 0
+ assert len(response.data["orders"]) == 0
def test_list_user_orders(mdb):
@@ -259,25 +284,28 @@ def test_list_user_orders(mdb):
session = requests.Session()
db = mdb
- users = db['users'].aggregate([{'$match': {'permissions': {'$in': ['ORDERS_SELF',
- 'DATA_MANAGEMENT']}}},
- {'$sample': {'size': 2}}])
+ users = db["users"].aggregate(
+ [
+ {"$match": {"permissions": {"$in": ["ORDERS_SELF", "DATA_MANAGEMENT"]}}},
+ {"$sample": {"size": 2}},
+ ]
+ )
for user in users:
- user_orders = list(db['orders'].find({'editors': user['_id']}))
- order_uuids = [str(order['_id']) for order in user_orders]
+ user_orders = list(db["orders"].find({"editors": user["_id"]}))
+ order_uuids = [str(order["_id"]) for order in user_orders]
- as_user(session, user['auth_ids'][0])
- response = make_request(session, '/api/v1/order/user/')
+ as_user(session, user["auth_ids"][0])
+ response = make_request(session, "/api/v1/order/user/")
if user_orders:
assert response.code == 200
assert response.data
- assert len(user_orders) == len(response.data['orders'])
- for order in response.data['orders']:
- assert order['_id'] in order_uuids
+ assert len(user_orders) == len(response.data["orders"])
+ for order in response.data["orders"]:
+ assert order["_id"] in order_uuids
else:
assert response.code == 200
- assert len(response.data['orders']) == 0
+ assert len(response.data["orders"]) == 0
def test_list_user_orders_bad():
@@ -288,13 +316,13 @@ def test_list_user_orders_bad():
"""
session = requests.Session()
- as_user(session, USERS['data'])
+ as_user(session, USERS["data"])
for _ in range(2):
- responses = make_request_all_roles(f'/api/v1/order/user/{uuid.uuid4()}/')
+ responses = make_request_all_roles(f"/api/v1/order/user/{uuid.uuid4()}/")
for response in responses:
- if response.role in ('data', 'root'):
+ if response.role in ("data", "root"):
assert response.code == 404
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -302,11 +330,11 @@ def test_list_user_orders_bad():
assert not response.data
for _ in range(2):
- responses = make_request_all_roles(f'/api/v1/order/user/{random_string()}/')
+ responses = make_request_all_roles(f"/api/v1/order/user/{random_string()}/")
for response in responses:
- if response.role in ('data', 'root'):
+ if response.role in ("data", "root"):
assert response.code == 404
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
else:
assert response.code == 403
@@ -319,18 +347,17 @@ def test_add_order_permissions():
Test permissions.
"""
- indata = {'title': 'Test title'}
+ indata = {"title": "Test title"}
indata.update(TEST_LABEL)
- responses = make_request_all_roles('/api/v1/order/',
- method='POST',
- data=indata,
- ret_json=True)
+ responses = make_request_all_roles(
+ "/api/v1/order/", method="POST", data=indata, ret_json=True
+ )
for response in responses:
- if response.role in ('orders', 'data', 'root'):
+ if response.role in ("orders", "data", "root"):
assert response.code == 200
- assert '_id' in response.data
- assert len(response.data['_id']) == 36
- elif response.role == 'no-login':
+ assert "_id" in response.data
+ assert len(response.data["_id"]) == 36
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -346,61 +373,62 @@ def test_add_order(mdb):
"""
db = mdb
- indata = {'description': 'Test description',
- 'title': 'Test title'}
+ indata = {"description": "Test description", "title": "Test title"}
indata.update(TEST_LABEL)
- responses = make_request_all_roles('/api/v1/order/',
- method='POST',
- data=indata,
- ret_json=True)
+ responses = make_request_all_roles(
+ "/api/v1/order/", method="POST", data=indata, ret_json=True
+ )
for response in responses:
- if response.role in ('orders', 'data', 'root'):
+ if response.role in ("orders", "data", "root"):
assert response.code == 200
- assert '_id' in response.data
- assert len(response.data['_id']) == 36
- order = db['orders'].find_one({'_id': uuid.UUID(response.data['_id'])})
- curr_user = db['users'].find_one({'auth_ids': USERS[response.role]})
- assert order['description'] == indata['description']
- assert order['title'] == indata['title']
- assert curr_user['_id'] in order['editors']
- elif response.role == 'no-login':
+ assert "_id" in response.data
+ assert len(response.data["_id"]) == 36
+ order = db["orders"].find_one({"_id": uuid.UUID(response.data["_id"])})
+ curr_user = db["users"].find_one({"auth_ids": USERS[response.role]})
+ assert order["description"] == indata["description"]
+ assert order["title"] == indata["title"]
+ assert curr_user["_id"] in order["editors"]
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 403
assert not response.data
- orders_user = db['users'].find_one({'auth_ids': USERS['orders']})
- indata = {'description': 'Test description',
- 'authors': [str(orders_user['_id'])],
- 'editors': [str(orders_user['_id'])],
- 'generators': [str(orders_user['_id'])],
- 'organisation': str(orders_user['_id']),
- 'title': 'Test title'}
+ orders_user = db["users"].find_one({"auth_ids": USERS["orders"]})
+ indata = {
+ "description": "Test description",
+ "authors": [str(orders_user["_id"])],
+ "editors": [str(orders_user["_id"])],
+ "generators": [str(orders_user["_id"])],
+ "organisation": str(orders_user["_id"]),
+ "title": "Test title",
+ }
indata.update(TEST_LABEL)
- responses = make_request_all_roles('/api/v1/order/',
- method='POST',
- data=indata,
- ret_json=True)
+ responses = make_request_all_roles(
+ "/api/v1/order/", method="POST", data=indata, ret_json=True
+ )
for response in responses:
- if response.role in ('orders', 'data', 'root'):
+ if response.role in ("orders", "data", "root"):
assert response.code == 200
- assert '_id' in response.data
- assert len(response.data['_id']) == 36
- order = db['orders'].find_one({'_id': uuid.UUID(response.data['_id'])})
+ assert "_id" in response.data
+ assert len(response.data["_id"]) == 36
+ order = db["orders"].find_one({"_id": uuid.UUID(response.data["_id"])})
- user_list = [orders_user['_id']]
- for field in ('description', 'title'):
+ user_list = [orders_user["_id"]]
+ for field in ("description", "title"):
assert order[field] == indata[field]
- for field in ('authors', 'generators'):
+ for field in ("authors", "generators"):
assert order[field] == user_list
- curr_user = db['users'].find_one({'auth_ids': USERS[response.role]})
+ curr_user = db["users"].find_one({"auth_ids": USERS[response.role]})
- assert set(order['editors']) == set([uuid.UUID(entry) for entry in indata[field]])
- assert order['organisation'] == uuid.UUID(indata['organisation'])
- elif response.role == 'no-login':
+ assert set(order["editors"]) == set(
+ [uuid.UUID(entry) for entry in indata[field]]
+ )
+ assert order["organisation"] == uuid.UUID(indata["organisation"])
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -416,26 +444,27 @@ def test_add_order_log(mdb):
"""
db = mdb
- indata = {'description': 'Test description',
- 'title': 'Test title'}
+ indata = {"description": "Test description", "title": "Test title"}
indata.update(TEST_LABEL)
- responses = make_request_all_roles('/api/v1/order/',
- method='POST',
- data=indata,
- ret_json=True)
+ responses = make_request_all_roles(
+ "/api/v1/order/", method="POST", data=indata, ret_json=True
+ )
for response in responses:
- if response.role in ('orders', 'data', 'root'):
+ if response.role in ("orders", "data", "root"):
assert response.code == 200
- assert '_id' in response.data
- assert len(response.data['_id']) == 36
- order = db['orders'].find_one({'_id': uuid.UUID(response.data['_id'])})
- logs = list(db['logs'].find({'data_type': 'order',
- 'data._id': uuid.UUID(response.data['_id'])}))
+ assert "_id" in response.data
+ assert len(response.data["_id"]) == 36
+ order = db["orders"].find_one({"_id": uuid.UUID(response.data["_id"])})
+ logs = list(
+ db["logs"].find(
+ {"data_type": "order", "data._id": uuid.UUID(response.data["_id"])}
+ )
+ )
assert len(logs) == 1
- assert logs[0]['data'] == order
- assert logs[0]['action'] == 'add'
- elif response.role == 'no-login':
+ assert logs[0]["data"] == order
+ assert logs[0]["action"] == "add"
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -449,38 +478,40 @@ def test_add_order_bad():
Bad requests.
"""
- indata = {'description': 'Test description',
- 'organisation': 'url@bad.se',
- 'title': 'Test title'}
+ indata = {
+ "description": "Test description",
+ "organisation": "url@bad.se",
+ "title": "Test title",
+ }
indata.update(TEST_LABEL)
- responses = make_request_all_roles('/api/v1/order/',
- method='POST',
- data=indata,
- ret_json=True)
+ responses = make_request_all_roles(
+ "/api/v1/order/", method="POST", data=indata, ret_json=True
+ )
for response in responses:
- if response.role in ('orders', 'data', 'root'):
+ if response.role in ("orders", "data", "root"):
assert response.code == 400
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 403
assert not response.data
- indata = {'description': 'Test description',
- 'authors': [str(uuid.uuid4())],
- 'title': 'Test title'}
+ indata = {
+ "description": "Test description",
+ "authors": [str(uuid.uuid4())],
+ "title": "Test title",
+ }
indata.update(TEST_LABEL)
- responses = make_request_all_roles('/api/v1/order/',
- method='POST',
- data=indata,
- ret_json=True)
+ responses = make_request_all_roles(
+ "/api/v1/order/", method="POST", data=indata, ret_json=True
+ )
for response in responses:
- if response.role in ('orders', 'data', 'root'):
+ if response.role in ("orders", "data", "root"):
assert response.code == 400
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -488,15 +519,12 @@ def test_add_order_bad():
assert not response.data
session = requests.Session()
- as_user(session, USERS['data'])
- indata = {'_id': str(uuid.uuid4()),
- 'title': 'Test title'}
+ as_user(session, USERS["data"])
+ indata = {"_id": str(uuid.uuid4()), "title": "Test title"}
indata.update(TEST_LABEL)
- response = make_request(session,
- '/api/v1/order/',
- method='POST',
- data=indata,
- ret_json=True)
+ response = make_request(
+ session, "/api/v1/order/", method="POST", data=indata, ret_json=True
+ )
assert response.code == 403
assert not response.data
@@ -511,26 +539,31 @@ def test_update_order_permissions(mdb):
db = mdb
- orders_user = db['users'].find_one({'auth_ids': USERS['orders']})
+ orders_user = db["users"].find_one({"auth_ids": USERS["orders"]})
- orders = list(db['orders'].aggregate([{'$match': {'editors': orders_user['_id']}},
- {'$sample': {'size': 3}}]))
+ orders = list(
+ db["orders"].aggregate(
+ [{"$match": {"editors": orders_user["_id"]}}, {"$sample": {"size": 3}}]
+ )
+ )
for order in orders:
for role in USERS:
as_user(session, USERS[role])
- indata = {'title': f'Test title - updated by {role}'}
- response = make_request(session,
- f'/api/v1/order/{order["_id"]}/',
- method='PATCH',
- data=indata,
- ret_json=True)
- if role in ('orders', 'data', 'root'):
+ indata = {"title": f"Test title - updated by {role}"}
+ response = make_request(
+ session,
+ f'/api/v1/order/{order["_id"]}/',
+ method="PATCH",
+ data=indata,
+ ret_json=True,
+ )
+ if role in ("orders", "data", "root"):
assert response.code == 200
assert not response.data
- new_order = db['orders'].find_one({'_id': order['_id']})
- assert new_order['title'] == f'Test title - updated by {role}'
- elif role == 'no-login':
+ new_order = db["orders"].find_one({"_id": order["_id"]})
+ assert new_order["title"] == f"Test title - updated by {role}"
+ elif role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -549,39 +582,50 @@ def test_update_order_data(mdb):
db = mdb
- orders_user = db['users'].find_one({'auth_ids': USERS['orders']})
+ orders_user = db["users"].find_one({"auth_ids": USERS["orders"]})
- orders = list(db['orders'].aggregate([{'$match': {'editors': orders_user['_id']}},
- {'$sample': {'size': 2}}]))
+ orders = list(
+ db["orders"].aggregate(
+ [{"$match": {"editors": orders_user["_id"]}}, {"$sample": {"size": 2}}]
+ )
+ )
assert len(orders) > 0
- as_user(session, USERS['orders'])
+ as_user(session, USERS["orders"])
for order in orders:
- indata = {'title': 'Test title - updated by orders user',
- 'description': 'Test description - updated by orders user'}
+ indata = {
+ "title": "Test title - updated by orders user",
+ "description": "Test description - updated by orders user",
+ }
indata.update(TEST_LABEL)
- response = make_request(session,
- f'/api/v1/order/{order["_id"]}/',
- method='PATCH',
- data=indata,
- ret_json=True)
+ response = make_request(
+ session,
+ f'/api/v1/order/{order["_id"]}/',
+ method="PATCH",
+ data=indata,
+ ret_json=True,
+ )
assert response.code == 200
assert not response.data
- new_order = db['orders'].find_one({'_id': order['_id']})
- new_order['_id'] = str(new_order['_id'])
- new_order['authors'] = [str(entry) for entry in new_order['authors']]
- new_order['generators'] = [str(entry) for entry in new_order['generators']]
- new_order['organisation'] = str(new_order['organisation'])
- new_order['datasets'] = [str(ds_uuid) for ds_uuid in new_order['datasets']]
+ new_order = db["orders"].find_one({"_id": order["_id"]})
+ new_order["_id"] = str(new_order["_id"])
+ new_order["authors"] = [str(entry) for entry in new_order["authors"]]
+ new_order["generators"] = [str(entry) for entry in new_order["generators"]]
+ new_order["organisation"] = str(new_order["organisation"])
+ new_order["datasets"] = [str(ds_uuid) for ds_uuid in new_order["datasets"]]
for field in indata:
assert new_order[field] == indata[field]
- assert db['logs'].find_one({'data._id': order['_id'],
- 'action': 'edit',
- 'data_type': 'order',
- 'user': orders_user['_id']})
+ assert db["logs"].find_one(
+ {
+ "data._id": order["_id"],
+ "action": "edit",
+ "data_type": "order",
+ "user": orders_user["_id"],
+ }
+ )
def test_update_order_bad(mdb):
@@ -592,90 +636,96 @@ def test_update_order_bad(mdb):
"""
db = mdb
- orders_user = db['users'].find_one({'auth_ids': USERS['orders']})
- orders = list(db['orders'].aggregate([{'$match': {'editors': orders_user['_id']}},
- {'$sample': {'size': 2}}]))
+ orders_user = db["users"].find_one({"auth_ids": USERS["orders"]})
+ orders = list(
+ db["orders"].aggregate(
+ [{"$match": {"editors": orders_user["_id"]}}, {"$sample": {"size": 2}}]
+ )
+ )
assert len(orders) > 0
for order in orders:
- indata = {'description': 'Test description',
- 'authors': str(uuid.uuid4()),
- 'title': 'Test title'}
- responses = make_request_all_roles(f'/api/v1/order/{order["_id"]}/',
- method='PATCH',
- data=indata,
- ret_json=True)
+ indata = {
+ "description": "Test description",
+ "authors": str(uuid.uuid4()),
+ "title": "Test title",
+ }
+ responses = make_request_all_roles(
+ f'/api/v1/order/{order["_id"]}/', method="PATCH", data=indata, ret_json=True
+ )
for response in responses:
- if response.role in ('orders', 'data', 'root'):
+ if response.role in ("orders", "data", "root"):
assert response.code == 400
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 403
assert not response.data
- indata = {'description': 'Test description',
- 'editors': str(orders_user['_id']),
- 'title': 'Test title'}
- responses = make_request_all_roles(f'/api/v1/order/{order["_id"]}/',
- method='PATCH',
- data=indata,
- ret_json=True)
+ indata = {
+ "description": "Test description",
+ "editors": str(orders_user["_id"]),
+ "title": "Test title",
+ }
+ responses = make_request_all_roles(
+ f'/api/v1/order/{order["_id"]}/', method="PATCH", data=indata, ret_json=True
+ )
for response in responses:
- if response.role in ('orders', 'data', 'root'):
+ if response.role in ("orders", "data", "root"):
assert response.code == 400
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 403
assert not response.data
- indata = {'description': 'Test description',
- 'editors': [str(uuid.uuid4())],
- 'title': 'Test title'}
- responses = make_request_all_roles(f'/api/v1/order/{order["_id"]}/',
- method='PATCH',
- data=indata,
- ret_json=True)
+ indata = {
+ "description": "Test description",
+ "editors": [str(uuid.uuid4())],
+ "title": "Test title",
+ }
+ responses = make_request_all_roles(
+ f'/api/v1/order/{order["_id"]}/', method="PATCH", data=indata, ret_json=True
+ )
for response in responses:
- if response.role in ('orders', 'data', 'root'):
+ if response.role in ("orders", "data", "root"):
assert response.code == 400
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 403
assert not response.data
-
for _ in range(2):
- indata = {'title': 'Test title'}
- responses = make_request_all_roles(f'/api/v1/order/{uuid.uuid4()}/',
- method='PATCH',
- data=indata,
- ret_json=True)
+ indata = {"title": "Test title"}
+ responses = make_request_all_roles(
+ f"/api/v1/order/{uuid.uuid4()}/", method="PATCH", data=indata, ret_json=True
+ )
for response in responses:
- if response.role in ('orders', 'data', 'root'):
+ if response.role in ("orders", "data", "root"):
assert response.code == 404
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 403
assert not response.data
- indata = {'title': 'Test title'}
- responses = make_request_all_roles(f'/api/v1/order/{random_string}/',
- method='PATCH',
- data=indata,
- ret_json=True)
+ indata = {"title": "Test title"}
+ responses = make_request_all_roles(
+ f"/api/v1/order/{random_string}/",
+ method="PATCH",
+ data=indata,
+ ret_json=True,
+ )
for response in responses:
- if response.role in ('orders', 'data', 'root'):
+ if response.role in ("orders", "data", "root"):
assert response.code == 404
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -696,53 +746,60 @@ def test_delete_order(mdb):
db = mdb
- orders_user = db['users'].find_one({'auth_ids': USERS['orders']})
+ orders_user = db["users"].find_one({"auth_ids": USERS["orders"]})
- orders = list(db['orders'].find(TEST_LABEL))
+ orders = list(db["orders"].find(TEST_LABEL))
if not orders:
assert False
i = 0
while i < len(orders):
for role in USERS:
as_user(session, USERS[role])
- response = make_request(session,
- f'/api/v1/order/{orders[i]["_id"]}/',
- method='DELETE')
- if role in ('orders', 'data', 'root'):
- if role != 'orders' or orders_user['_id'] in orders[i]['editors']:
+ response = make_request(
+ session, f'/api/v1/order/{orders[i]["_id"]}/', method="DELETE"
+ )
+ if role in ("orders", "data", "root"):
+ if role != "orders" or orders_user["_id"] in orders[i]["editors"]:
assert response.code == 200
assert not response.data
- assert not db['orders'].find_one({'_id': orders[i]['_id']})
- assert db['logs'].find_one({'data._id': orders[i]['_id'],
- 'action': 'delete',
- 'data_type': 'order'})
- for dataset_uuid in orders[i]['datasets']:
- assert not db['datasets'].find_one({'_id': dataset_uuid})
- assert db['logs'].find_one({'data._id': dataset_uuid,
- 'action': 'delete',
- 'data_type': 'dataset'})
+ assert not db["orders"].find_one({"_id": orders[i]["_id"]})
+ assert db["logs"].find_one(
+ {
+ "data._id": orders[i]["_id"],
+ "action": "delete",
+ "data_type": "order",
+ }
+ )
+ for dataset_uuid in orders[i]["datasets"]:
+ assert not db["datasets"].find_one({"_id": dataset_uuid})
+ assert db["logs"].find_one(
+ {
+ "data._id": dataset_uuid,
+ "action": "delete",
+ "data_type": "dataset",
+ }
+ )
i += 1
if i >= len(orders):
break
else:
assert response.code == 403
assert not response.data
- elif role == 'no-login':
+ elif role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 403
assert not response.data
- as_user(session, USERS['orders'])
- response = make_request(session,
- '/api/v1/order/',
- data={'title': 'tmp'},
- method='POST')
+ as_user(session, USERS["orders"])
+ response = make_request(
+ session, "/api/v1/order/", data={"title": "tmp"}, method="POST"
+ )
assert response.code == 200
- response = make_request(session,
- f'/api/v1/order/{response.data["_id"]}/',
- method='DELETE')
+ response = make_request(
+ session, f'/api/v1/order/{response.data["_id"]}/', method="DELETE"
+ )
assert response.code == 200
assert not response.data
@@ -751,18 +808,18 @@ def test_delete_order_bad():
"""Attempt bad order delete requests."""
session = requests.Session()
- as_user(session, USERS['data'])
+ as_user(session, USERS["data"])
for _ in range(2):
- response = make_request(session,
- f'/api/v1/order/{random_string()}/',
- method='DELETE')
+ response = make_request(
+ session, f"/api/v1/order/{random_string()}/", method="DELETE"
+ )
assert response.code == 404
assert not response.data
for _ in range(2):
- response = make_request(session,
- f'/api/v1/order/{uuid.uuid4()}/',
- method='DELETE')
+ response = make_request(
+ session, f"/api/v1/order/{uuid.uuid4()}/", method="DELETE"
+ )
assert response.code == 404
assert not response.data
@@ -774,24 +831,26 @@ def test_list_all_orders(mdb):
Check that the number of fields per order is correct.
"""
db = mdb
- nr_orders = db['orders'].count_documents({})
+ nr_orders = db["orders"].count_documents({})
- responses = make_request_all_roles('/api/v1/order/', ret_json=True)
+ responses = make_request_all_roles("/api/v1/order/", ret_json=True)
for response in responses:
- if response.role in ('data', 'root'):
+ if response.role in ("data", "root"):
assert response.code == 200
- assert len(response.data['orders']) == nr_orders
- assert set(response.data['orders'][0].keys()) == {'title',
- '_id',
- 'tags',
- 'properties'}
- elif response.role == 'no-login':
+ assert len(response.data["orders"]) == nr_orders
+ assert set(response.data["orders"][0].keys()) == {
+ "title",
+ "_id",
+ "tags",
+ "properties",
+ }
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
- elif response.role == 'orders':
+ elif response.role == "orders":
assert response.code == 200
- assert len(response.data['orders']) == 0
+ assert len(response.data["orders"]) == 0
else:
assert response.code == 403
diff --git a/backend/tests/test_permissions.py b/backend/tests/test_permissions.py
index baee0a71..d4837243 100644
--- a/backend/tests/test_permissions.py
+++ b/backend/tests/test_permissions.py
@@ -11,19 +11,21 @@
def test_request_no_permissions_required():
"""Request target with no permission requirements."""
- responses = helpers.make_request_all_roles('/api/v1/developer/hello', ret_json=True)
+ responses = helpers.make_request_all_roles("/api/v1/developer/hello", ret_json=True)
for response in responses:
assert response.code == 200
- assert response.data == {'test': "success"}
+ assert response.data == {"test": "success"}
def test_request_login_required():
"""Request target with no permission requirements apart from being logged in."""
- responses = helpers.make_request_all_roles('/api/v1/developer/loginhello', ret_json=True)
+ responses = helpers.make_request_all_roles(
+ "/api/v1/developer/loginhello", ret_json=True
+ )
for response in responses:
- if response.role != 'no-login':
+ if response.role != "no-login":
assert response.code == 200
- assert response.data == {'test': "success"}
+ assert response.data == {"test": "success"}
else:
assert response.code == 401
assert not response.data
@@ -31,11 +33,13 @@ def test_request_login_required():
def test_request_permission_orders_self():
"""Request requiring ORDERS permissions."""
- responses = helpers.make_request_all_roles('/api/v1/developer/hello/ORDERS', ret_json=True)
+ responses = helpers.make_request_all_roles(
+ "/api/v1/developer/hello/ORDERS", ret_json=True
+ )
for response in responses:
- if response.role in ('orders', 'data', 'root'):
+ if response.role in ("orders", "data", "root"):
assert response.code == 200
- assert response.data == {'test': "success"}
+ assert response.data == {"test": "success"}
else:
assert response.code == 403
assert not response.data
@@ -43,11 +47,13 @@ def test_request_permission_orders_self():
def test_request_permission_owners_read():
"""Request requiring OWNERS_READ permissions."""
- responses = helpers.make_request_all_roles('/api/v1/developer/hello/OWNERS_READ', ret_json=True)
+ responses = helpers.make_request_all_roles(
+ "/api/v1/developer/hello/OWNERS_READ", ret_json=True
+ )
for response in responses:
- if response.role in ('owners', 'data', 'root'):
+ if response.role in ("owners", "data", "root"):
assert response.code == 200
- assert response.data == {'test': "success"}
+ assert response.data == {"test": "success"}
else:
assert response.code == 403
assert not response.data
@@ -55,11 +61,13 @@ def test_request_permission_owners_read():
def test_request_permission_user_management():
"""Request requiring USER_MANAGEMENT permissions."""
- responses = helpers.make_request_all_roles('/api/v1/developer/hello/USER_MANAGEMENT', ret_json=True)
+ responses = helpers.make_request_all_roles(
+ "/api/v1/developer/hello/USER_MANAGEMENT", ret_json=True
+ )
for response in responses:
- if response.role in ('users', 'root'):
+ if response.role in ("users", "root"):
assert response.code == 200
- assert response.data == {'test': "success"}
+ assert response.data == {"test": "success"}
else:
assert response.code == 403
assert not response.data
@@ -67,11 +75,13 @@ def test_request_permission_user_management():
def test_request_permission_data_management():
"""Request requiring DATA_MANAGEMENT permissions."""
- responses = helpers.make_request_all_roles('/api/v1/developer/hello/DATA_MANAGEMENT', ret_json=True)
+ responses = helpers.make_request_all_roles(
+ "/api/v1/developer/hello/DATA_MANAGEMENT", ret_json=True
+ )
for response in responses:
- if response.role in ('data', 'root'):
+ if response.role in ("data", "root"):
assert response.code == 200
- assert response.data == {'test': "success"}
+ assert response.data == {"test": "success"}
else:
assert response.code == 403
assert not response.data
@@ -79,43 +89,46 @@ def test_request_permission_data_management():
def test_csrf():
"""Perform POST, POST and DELETE requests to confirm that CSRF works correctly."""
-
- for method in ('POST', 'PATCH', 'POST', 'DELETE'):
- responses = helpers.make_request_all_roles('/api/v1/developer/csrftest',
- method=method,
- set_csrf=False,
- ret_json=True)
+
+ for method in ("POST", "PATCH", "POST", "DELETE"):
+ responses = helpers.make_request_all_roles(
+ "/api/v1/developer/csrftest", method=method, set_csrf=False, ret_json=True
+ )
for response in responses:
assert response.code == 400
assert not response.data
- responses = helpers.make_request_all_roles('/api/v1/developer/csrftest',
- method=method,
- ret_json=True)
+ responses = helpers.make_request_all_roles(
+ "/api/v1/developer/csrftest", method=method, ret_json=True
+ )
for response in responses:
assert response.code == 200
- assert response.data == {'test': "success"}
+ assert response.data == {"test": "success"}
def test_api_key_auth():
"""Request target with login requirment using an API key"""
- response = requests.get(helpers.BASE_URL + '/api/v1/developer/loginhello',
- headers={'X-API-Key': '0',
- 'X-API-User': 'base::testers'})
+ response = requests.get(
+ helpers.BASE_URL + "/api/v1/developer/loginhello",
+ headers={"X-API-Key": "0", "X-API-User": "base::testers"},
+ )
assert response.status_code == 200
- assert json.loads(response.text) == {'test': 'success'}
- response = requests.get(helpers.BASE_URL + '/api/v1/developer/loginhello',
- headers={'X-API-Key': '0',
- 'X-API-User': 'root::testers'})
+ assert json.loads(response.text) == {"test": "success"}
+ response = requests.get(
+ helpers.BASE_URL + "/api/v1/developer/loginhello",
+ headers={"X-API-Key": "0", "X-API-User": "root::testers"},
+ )
assert response.status_code == 401
assert not response.text
- response = requests.get(helpers.BASE_URL + '/api/v1/developer/loginhello',
- headers={'X-API-Key': 'asd',
- 'X-API-User': 'root::testers'})
+ response = requests.get(
+ helpers.BASE_URL + "/api/v1/developer/loginhello",
+ headers={"X-API-Key": "asd", "X-API-User": "root::testers"},
+ )
assert response.status_code == 401
assert not response.text
- response = requests.get(helpers.BASE_URL + '/api/v1/developer/loginhello',
- headers={'X-API-Key': '0',
- 'X-API-User': 'asd'})
+ response = requests.get(
+ helpers.BASE_URL + "/api/v1/developer/loginhello",
+ headers={"X-API-Key": "0", "X-API-User": "asd"},
+ )
assert response.status_code == 401
assert not response.text
diff --git a/backend/tests/test_users.py b/backend/tests/test_users.py
index 590d5619..b98d4204 100644
--- a/backend/tests/test_users.py
+++ b/backend/tests/test_users.py
@@ -3,7 +3,15 @@
import requests
import uuid
-from helpers import make_request, as_user, make_request_all_roles, USERS, mdb, random_string
+from helpers import (
+ make_request,
+ as_user,
+ make_request_all_roles,
+ USERS,
+ mdb,
+ random_string,
+)
+
# pylint: disable=redefined-outer-name
@@ -13,13 +21,12 @@ def test_list_users(mdb):
Assert that USER_MANAGEMENT is required.
"""
- responses = make_request_all_roles('/api/v1/user/',
- ret_json=True)
+ responses = make_request_all_roles("/api/v1/user/", ret_json=True)
for response in responses:
- if response.role in ('users', 'root', 'orders'):
+ if response.role in ("users", "root", "orders"):
assert response.code == 200
- assert len(response.data['users']) == mdb['users'].count_documents({})
- elif response.role == 'no-login':
+ assert len(response.data["users"]) == mdb["users"].count_documents({})
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -29,13 +36,12 @@ def test_list_users(mdb):
def test_list_info():
"""Retrieve info about current user."""
- responses = make_request_all_roles('/api/v1/user/me/',
- ret_json=True)
+ responses = make_request_all_roles("/api/v1/user/me/", ret_json=True)
for response in responses:
assert response.code == 200
- assert len(response.data['user']) == 9
- if response.role != 'no-login':
- assert response.data['user']['name'] == f'{response.role.capitalize()}'
+ assert len(response.data["user"]) == 9
+ if response.role != "no-login":
+ assert response.data["user"]["name"] == f"{response.role.capitalize()}"
def test_update_current_user(mdb):
@@ -45,40 +51,35 @@ def test_update_current_user(mdb):
indata = {}
for user in USERS:
as_user(session, USERS[user])
- user_info = mdb['users'].find_one({'auth_ids': USERS[user]})
- response = make_request(session,
- '/api/v1/user/me/',
- ret_json=True,
- method='PATCH',
- data=indata)
- if user != 'no-login':
+ user_info = mdb["users"].find_one({"auth_ids": USERS[user]})
+ response = make_request(
+ session, "/api/v1/user/me/", ret_json=True, method="PATCH", data=indata
+ )
+ if user != "no-login":
assert response.code == 200
else:
assert response.code == 401
assert not response.data
- new_user_info = mdb['users'].find_one({'auth_ids': USERS[user]})
+ new_user_info = mdb["users"].find_one({"auth_ids": USERS[user]})
assert user_info == new_user_info
- indata = {'affiliation': 'Updated University',
- 'name': 'Updated name'}
+ indata = {"affiliation": "Updated University", "name": "Updated name"}
session = requests.Session()
for user in USERS:
as_user(session, USERS[user])
- user_info = mdb['users'].find_one({'auth_ids': USERS[user]})
- response = make_request(session,
- '/api/v1/user/me/',
- ret_json=True,
- method='PATCH',
- data=indata)
- if user != 'no-login':
+ user_info = mdb["users"].find_one({"auth_ids": USERS[user]})
+ response = make_request(
+ session, "/api/v1/user/me/", ret_json=True, method="PATCH", data=indata
+ )
+ if user != "no-login":
assert response.code == 200
assert not response.data
- new_user_info = mdb['users'].find_one({'auth_ids': USERS[user]})
+ new_user_info = mdb["users"].find_one({"auth_ids": USERS[user]})
for key in new_user_info:
if key in indata.keys():
assert new_user_info[key] == indata[key]
else:
- mdb['users'].update_one(new_user_info, {'$set': user_info})
+ mdb["users"].update_one(new_user_info, {"$set": user_info})
else:
assert response.code == 401
assert not response.data
@@ -86,61 +87,56 @@ def test_update_current_user(mdb):
def test_update_current_user_bad():
"""Update the info about the current user."""
- indata = {'_id': str(uuid.uuid4())}
- responses = make_request_all_roles('/api/v1/user/me/',
- ret_json=True,
- method='PATCH',
- data=indata)
+ indata = {"_id": str(uuid.uuid4())}
+ responses = make_request_all_roles(
+ "/api/v1/user/me/", ret_json=True, method="PATCH", data=indata
+ )
for response in responses:
- if response.role == 'no-login':
+ if response.role == "no-login":
assert response.code == 401
else:
assert response.code == 403
assert not response.data
- indata = {'api_key': uuid.uuid4().hex}
- responses = make_request_all_roles('/api/v1/user/me/',
- ret_json=True,
- method='PATCH',
- data=indata)
+ indata = {"api_key": uuid.uuid4().hex}
+ responses = make_request_all_roles(
+ "/api/v1/user/me/", ret_json=True, method="PATCH", data=indata
+ )
for response in responses:
- if response.role == 'no-login':
+ if response.role == "no-login":
assert response.code == 401
else:
assert response.code == 403
assert not response.data
- indata = {'auth_ids': [uuid.uuid4().hex]}
- responses = make_request_all_roles('/api/v1/user/me/',
- ret_json=True,
- method='PATCH',
- data=indata)
+ indata = {"auth_ids": [uuid.uuid4().hex]}
+ responses = make_request_all_roles(
+ "/api/v1/user/me/", ret_json=True, method="PATCH", data=indata
+ )
for response in responses:
- if response.role == 'no-login':
+ if response.role == "no-login":
assert response.code == 401
else:
assert response.code == 403
assert not response.data
- indata = {'email': 'email@example.com'}
- responses = make_request_all_roles('/api/v1/user/me/',
- ret_json=True,
- method='PATCH',
- data=indata)
+ indata = {"email": "email@example.com"}
+ responses = make_request_all_roles(
+ "/api/v1/user/me/", ret_json=True, method="PATCH", data=indata
+ )
for response in responses:
- if response.role == 'no-login':
+ if response.role == "no-login":
assert response.code == 401
else:
assert response.code == 403
assert not response.data
- indata = {'permissions': ['USER_MANAGEMENT', 'DATA_MANAGEMENT']}
- responses = make_request_all_roles('/api/v1/user/me/',
- ret_json=True,
- method='PATCH',
- data=indata)
+ indata = {"permissions": ["USER_MANAGEMENT", "DATA_MANAGEMENT"]}
+ responses = make_request_all_roles(
+ "/api/v1/user/me/", ret_json=True, method="PATCH", data=indata
+ )
for response in responses:
- if response.role == 'no-login':
+ if response.role == "no-login":
assert response.code == 401
else:
assert response.code == 403
@@ -149,42 +145,44 @@ def test_update_current_user_bad():
def test_update_user(mdb):
"""Update the info for a user."""
- user_info = mdb['users'].find_one({'auth_ids': USERS['base']})
+ user_info = mdb["users"].find_one({"auth_ids": USERS["base"]})
indata = {}
- responses = make_request_all_roles(f'/api/v1/user/{user_info["_id"]}/',
- ret_json=True,
- method='PATCH',
- data=indata)
+ responses = make_request_all_roles(
+ f'/api/v1/user/{user_info["_id"]}/', ret_json=True, method="PATCH", data=indata
+ )
for response in responses:
- if response.role in ('users', 'root'):
+ if response.role in ("users", "root"):
assert response.code == 200
- new_user_info = mdb['users'].find_one({'auth_ids': {'$in': user_info['auth_ids']}})
+ new_user_info = mdb["users"].find_one(
+ {"auth_ids": {"$in": user_info["auth_ids"]}}
+ )
assert user_info == new_user_info
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
else:
assert response.code == 403
assert not response.data
- indata = {'affiliation': 'Updated University',
- 'name': 'Updated name'}
+ indata = {"affiliation": "Updated University", "name": "Updated name"}
session = requests.session()
for user_type in USERS:
as_user(session, USERS[user_type])
- response = make_request(session,
- f'/api/v1/user/{user_info["_id"]}/',
- ret_json=True,
- method='PATCH',
- data=indata)
- if user_type in ('users', 'root'):
+ response = make_request(
+ session,
+ f'/api/v1/user/{user_info["_id"]}/',
+ ret_json=True,
+ method="PATCH",
+ data=indata,
+ )
+ if user_type in ("users", "root"):
assert response.code == 200
assert not response.data
- new_user_info = mdb['users'].find_one({'auth_ids': user_info['auth_ids']})
+ new_user_info = mdb["users"].find_one({"auth_ids": user_info["auth_ids"]})
for key in indata:
assert new_user_info[key] == indata[key]
- mdb['users'].update_one(new_user_info, {'$set': user_info})
- elif user_type == 'no-login':
+ mdb["users"].update_one(new_user_info, {"$set": user_info})
+ elif user_type == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -198,130 +196,122 @@ def test_update_user_bad(mdb):
Bad requests.
"""
- user_info = mdb['users'].find_one({'auth_ids': USERS['base']})
+ user_info = mdb["users"].find_one({"auth_ids": USERS["base"]})
- indata = {'_id': str(uuid.uuid4())}
- responses = make_request_all_roles(f'/api/v1/user/{user_info["_id"]}/',
- ret_json=True,
- method='PATCH',
- data=indata)
+ indata = {"_id": str(uuid.uuid4())}
+ responses = make_request_all_roles(
+ f'/api/v1/user/{user_info["_id"]}/', ret_json=True, method="PATCH", data=indata
+ )
for response in responses:
- if response.role == 'no-login':
+ if response.role == "no-login":
assert response.code == 401
else:
assert response.code == 403
assert not response.data
- indata = {'api_key': uuid.uuid4().hex}
- responses = make_request_all_roles(f'/api/v1/user/{user_info["_id"]}/',
- ret_json=True,
- method='PATCH',
- data=indata)
+ indata = {"api_key": uuid.uuid4().hex}
+ responses = make_request_all_roles(
+ f'/api/v1/user/{user_info["_id"]}/', ret_json=True, method="PATCH", data=indata
+ )
for response in responses:
- if response.role == 'no-login':
+ if response.role == "no-login":
assert response.code == 401
else:
assert response.code == 403
assert not response.data
- indata = {'email': 'bad@email'}
- responses = make_request_all_roles(f'/api/v1/user/{user_info["_id"]}/',
- ret_json=True,
- method='PATCH',
- data=indata)
+ indata = {"email": "bad@email"}
+ responses = make_request_all_roles(
+ f'/api/v1/user/{user_info["_id"]}/', ret_json=True, method="PATCH", data=indata
+ )
for response in responses:
- if response.role in ('users', 'root'):
+ if response.role in ("users", "root"):
assert response.code == 400
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
else:
assert response.code == 403
assert not response.data
indata = {}
- responses = make_request_all_roles(f'/api/v1/user/{uuid.uuid4()}/',
- ret_json=True,
- method='PATCH',
- data=indata)
+ responses = make_request_all_roles(
+ f"/api/v1/user/{uuid.uuid4()}/", ret_json=True, method="PATCH", data=indata
+ )
for response in responses:
- if response.role in ('users', 'root'):
+ if response.role in ("users", "root"):
assert response.code == 404
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
else:
assert response.code == 403
assert not response.data
indata = {}
- responses = make_request_all_roles(f'/api/v1/user/{random_string()}/',
- ret_json=True,
- method='PATCH',
- data=indata)
+ responses = make_request_all_roles(
+ f"/api/v1/user/{random_string()}/", ret_json=True, method="PATCH", data=indata
+ )
for response in responses:
- if response.role in ('users', 'root'):
+ if response.role in ("users", "root"):
assert response.code == 404
- elif response.role == 'no-login':
+ elif response.role == "no-login":
assert response.code == 401
else:
assert response.code == 403
assert not response.data
-
def test_add_user(mdb):
"""Add a user."""
- indata = {'email': 'new_user@added.example.com'}
+ indata = {"email": "new_user@added.example.com"}
session = requests.Session()
for role in USERS:
as_user(session, USERS[role])
- response = make_request(session,
- '/api/v1/user/',
- ret_json=True,
- method='POST',
- data=indata)
- if role in ('users', 'root', 'orders'):
+ response = make_request(
+ session, "/api/v1/user/", ret_json=True, method="POST", data=indata
+ )
+ if role in ("users", "root", "orders"):
assert response.code == 200
- assert '_id' in response.data
- new_user_info = mdb['users'].find_one({'_id': uuid.UUID(response.data['_id'])})
- assert indata['email'] == new_user_info['email']
- indata['email'] = 'new_' + indata['email']
- elif role == 'no-login':
+ assert "_id" in response.data
+ new_user_info = mdb["users"].find_one(
+ {"_id": uuid.UUID(response.data["_id"])}
+ )
+ assert indata["email"] == new_user_info["email"]
+ indata["email"] = "new_" + indata["email"]
+ elif role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 403
assert not response.data
- indata = {'affiliation': 'Added University',
- 'name': 'Added name',
- 'email': 'user2@added.example.com',
- 'permissions': ['ORDERS']}
+ indata = {
+ "affiliation": "Added University",
+ "name": "Added name",
+ "email": "user2@added.example.com",
+ "permissions": ["ORDERS"],
+ }
session = requests.session()
- as_user(session, USERS['orders'])
- response = make_request(session,
- '/api/v1/user/',
- ret_json=True,
- method='POST',
- data=indata)
+ as_user(session, USERS["orders"])
+ response = make_request(
+ session, "/api/v1/user/", ret_json=True, method="POST", data=indata
+ )
assert response.code == 403
- as_user(session, USERS['root'])
- response = make_request(session,
- '/api/v1/user/',
- ret_json=True,
- method='POST',
- data=indata)
+ as_user(session, USERS["root"])
+ response = make_request(
+ session, "/api/v1/user/", ret_json=True, method="POST", data=indata
+ )
assert response.code == 200
- assert '_id' in response.data
- new_user_info = mdb['users'].find_one({'_id': uuid.UUID(response.data['_id'])})
+ assert "_id" in response.data
+ new_user_info = mdb["users"].find_one({"_id": uuid.UUID(response.data["_id"])})
for key in indata:
assert new_user_info[key] == indata[key]
def test_delete_user(mdb):
"""Test deleting users (added when testing to add users)"""
- re_users = re.compile('@added.example.com')
- users = list(mdb['users'].find({'email': re_users}, {'_id': 1}))
+ re_users = re.compile("@added.example.com")
+ users = list(mdb["users"].find({"email": re_users}, {"_id": 1}))
if not users:
assert False
session = requests.Session()
@@ -329,20 +319,24 @@ def test_delete_user(mdb):
while i < len(users):
for role in USERS:
as_user(session, USERS[role])
- response = make_request(session,
- f'/api/v1/user/{users[i]["_id"]}/',
- method='DELETE')
- if role in ('users', 'root'):
+ response = make_request(
+ session, f'/api/v1/user/{users[i]["_id"]}/', method="DELETE"
+ )
+ if role in ("users", "root"):
assert response.code == 200
assert not response.data
- assert not mdb['users'].find_one({'_id': users[i]['_id']})
- assert mdb['logs'].find_one({'data._id': users[i]['_id'],
- 'action': 'delete',
- 'data_type': 'user'})
+ assert not mdb["users"].find_one({"_id": users[i]["_id"]})
+ assert mdb["logs"].find_one(
+ {
+ "data._id": users[i]["_id"],
+ "action": "delete",
+ "data_type": "user",
+ }
+ )
i += 1
if i >= len(users):
break
- elif role == 'no-login':
+ elif role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -352,46 +346,47 @@ def test_delete_user(mdb):
def test_key_reset(mdb):
"""Test generation of new API keys"""
- mod_user = {'auth_ids': 'facility18::local'}
+ mod_user = {"auth_ids": "facility18::local"}
mod_user_info = mdb.users.find_one(mod_user)
session = requests.Session()
for i, userid in enumerate(USERS):
as_user(session, USERS[userid])
- response = make_request(session,
- '/api/v1/user/me/apikey/',
- method='POST')
- if userid == 'no-login':
+ response = make_request(session, "/api/v1/user/me/apikey/", method="POST")
+ if userid == "no-login":
assert response.code == 401
assert not response.data
continue
assert response.code == 200
- new_key = response.data['key']
- response = make_request(session,
- '/api/v1/login/apikey/',
- data = {'api-user': USERS[userid],
- 'api-key': new_key},
- method='POST')
+ new_key = response.data["key"]
+ response = make_request(
+ session,
+ "/api/v1/login/apikey/",
+ data={"api-user": USERS[userid], "api-key": new_key},
+ method="POST",
+ )
assert response.code == 200
assert not response.data
- mdb.users.update_one({'auth_ids': userid}, {'$set': {'api_salt': 'abc',
- 'api_key': str(i-1)}})
-
- response = make_request(session,
- f'/api/v1/user/{mod_user_info["_id"]}/apikey/',
- method='POST')
- if userid not in ('users', 'root'):
+ mdb.users.update_one(
+ {"auth_ids": userid}, {"$set": {"api_salt": "abc", "api_key": str(i - 1)}}
+ )
+
+ response = make_request(
+ session, f'/api/v1/user/{mod_user_info["_id"]}/apikey/', method="POST"
+ )
+ if userid not in ("users", "root"):
assert response.code == 403
assert not response.data
else:
assert response.code == 200
- new_key = response.data['key']
- response = make_request(session,
- '/api/v1/login/apikey/',
- data = {'api-user': mod_user['auth_ids'],
- 'api-key': new_key},
- method='POST')
+ new_key = response.data["key"]
+ response = make_request(
+ session,
+ "/api/v1/login/apikey/",
+ data={"api-user": mod_user["auth_ids"], "api-key": new_key},
+ method="POST",
+ )
assert response.code == 200
assert not response.data
@@ -402,14 +397,13 @@ def test_get_user_logs_permissions(mdb):
Assert that USER_MANAGEMENT or actual user is required.
"""
- user_uuid = mdb['users'].find_one({'auth_ids': USERS['base']}, {'_id': 1})['_id']
- responses = make_request_all_roles(f'/api/v1/user/{user_uuid}/log/',
- ret_json=True)
+ user_uuid = mdb["users"].find_one({"auth_ids": USERS["base"]}, {"_id": 1})["_id"]
+ responses = make_request_all_roles(f"/api/v1/user/{user_uuid}/log/", ret_json=True)
for response in responses:
- if response.role in ('base', 'users', 'root'):
+ if response.role in ("base", "users", "root"):
assert response.code == 200
- assert 'logs' in response.data
- elif response.role == 'no-login':
+ assert "logs" in response.data
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -424,14 +418,16 @@ def test_get_user_logs(mdb):
Confirm that the logs contain only the intended fields.
"""
session = requests.session()
- users = mdb['users'].aggregate([{'$sample': {'size': 2}}])
+ users = mdb["users"].aggregate([{"$sample": {"size": 2}}])
for user in users:
- logs = list(mdb['logs'].find({'data_type': 'user', 'data._id': user['_id']}))
- as_user(session, USERS['users'])
- response = make_request(session, f'/api/v1/user/{user["_id"]}/log/', ret_json=True)
- assert response.data['dataType'] == 'user'
- assert response.data['entryId'] == str(user['_id'])
- assert len(response.data['logs']) == len(logs)
+ logs = list(mdb["logs"].find({"data_type": "user", "data._id": user["_id"]}))
+ as_user(session, USERS["users"])
+ response = make_request(
+ session, f'/api/v1/user/{user["_id"]}/log/', ret_json=True
+ )
+ assert response.data["dataType"] == "user"
+ assert response.data["entryId"] == str(user["_id"])
+ assert len(response.data["logs"]) == len(logs)
assert response.code == 200
@@ -441,15 +437,14 @@ def test_get_current_user_logs():
Should return logs for all logged in users.
"""
- responses = make_request_all_roles('/api/v1/user/me/log/',
- ret_json=True)
+ responses = make_request_all_roles("/api/v1/user/me/log/", ret_json=True)
for response in responses:
- if response.role == 'no-login':
+ if response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 200
- assert 'logs' in response.data
+ assert "logs" in response.data
def test_get_user_actions_access(mdb):
@@ -458,14 +453,15 @@ def test_get_user_actions_access(mdb):
Assert that USER_MANAGEMENT or actual user is required.
"""
- user_uuid = mdb['users'].find_one({'auth_ids': USERS['base']}, {'_id': 1})['_id']
- responses = make_request_all_roles(f'/api/v1/user/{user_uuid}/actions/',
- ret_json=True)
+ user_uuid = mdb["users"].find_one({"auth_ids": USERS["base"]}, {"_id": 1})["_id"]
+ responses = make_request_all_roles(
+ f"/api/v1/user/{user_uuid}/actions/", ret_json=True
+ )
for response in responses:
- if response.role in ('base', 'users', 'root'):
+ if response.role in ("base", "users", "root"):
assert response.code == 200
- assert 'logs' in response.data
- elif response.role == 'no-login':
+ assert "logs" in response.data
+ elif response.role == "no-login":
assert response.code == 401
assert not response.data
else:
@@ -479,12 +475,11 @@ def test_get_current_user_actions():
Should return logs for all logged in users.
"""
- responses = make_request_all_roles('/api/v1/user/me/actions/',
- ret_json=True)
+ responses = make_request_all_roles("/api/v1/user/me/actions/", ret_json=True)
for response in responses:
- if response.role == 'no-login':
+ if response.role == "no-login":
assert response.code == 401
assert not response.data
else:
assert response.code == 200
- assert 'logs' in response.data
+ assert "logs" in response.data
diff --git a/backend/user.py b/backend/user.py
index 9c5c6566..4a4b2a80 100644
--- a/backend/user.py
+++ b/backend/user.py
@@ -20,14 +20,16 @@
import structure
import utils
-blueprint = flask.Blueprint('user', __name__) # pylint: disable=invalid-name
+blueprint = flask.Blueprint("user", __name__) # pylint: disable=invalid-name
-PERMISSIONS = {'ORDERS': ('ORDERS', 'USER_ADD', 'USER_SEARCH'),
- 'OWNERS_READ': ('OWNERS_READ',),
- 'USER_ADD': ('USER_ADD',),
- 'USER_SEARCH': ('USER_SEARCH',),
- 'USER_MANAGEMENT': ('USER_MANAGEMENT', 'USER_ADD', 'USER_SEARCH'),
- 'DATA_MANAGEMENT': ('ORDERS', 'OWNERS_READ', 'DATA_MANAGEMENT')}
+PERMISSIONS = {
+ "ORDERS": ("ORDERS", "USER_ADD", "USER_SEARCH"),
+ "OWNERS_READ": ("OWNERS_READ",),
+ "USER_ADD": ("USER_ADD",),
+ "USER_SEARCH": ("USER_SEARCH",),
+ "USER_MANAGEMENT": ("USER_MANAGEMENT", "USER_ADD", "USER_SEARCH"),
+ "DATA_MANAGEMENT": ("ORDERS", "OWNERS_READ", "DATA_MANAGEMENT"),
+}
# Decorators
@@ -37,22 +39,24 @@ def login_required(func):
Otherwise abort with status 401 Unauthorized.
"""
+
@functools.wraps(func)
def wrap(*args, **kwargs):
if not flask.g.current_user:
flask.abort(status=401)
return func(*args, **kwargs)
+
return wrap
# requests
-@blueprint.route('/permissions/')
+@blueprint.route("/permissions/")
def get_permission_info():
"""Get a list of all permission types."""
- return utils.response_json({'permissions': list(PERMISSIONS.keys())})
+ return utils.response_json({"permissions": list(PERMISSIONS.keys())})
-@blueprint.route('/')
+@blueprint.route("/")
@login_required
def list_users():
"""
@@ -60,22 +64,21 @@ def list_users():
Admin access should be required.
"""
- if not has_permission('USER_SEARCH'):
+ if not has_permission("USER_SEARCH"):
flask.abort(403)
- fields = {'api_key': 0,
- 'api_salt': 0}
+ fields = {"api_key": 0, "api_salt": 0}
- if not has_permission('USER_MANAGEMENT'):
- fields['auth_ids'] = 0
- fields['permissions'] = 0
+ if not has_permission("USER_MANAGEMENT"):
+ fields["auth_ids"] = 0
+ fields["permissions"] = 0
- result = tuple(flask.g.db['users'].find(projection=fields))
+ result = tuple(flask.g.db["users"].find(projection=fields))
- return utils.response_json({'users': result})
+ return utils.response_json({"users": result})
-@blueprint.route('/structure/', methods=['GET'])
+@blueprint.route("/structure/", methods=["GET"])
def get_user_data_structure():
"""
Get an empty user entry.
@@ -84,12 +87,12 @@ def get_user_data_structure():
flask.Response: JSON structure with a list of users.
"""
empty_user = structure.user()
- empty_user['_id'] = ''
- return utils.response_json({'user': empty_user})
+ empty_user["_id"] = ""
+ return utils.response_json({"user": empty_user})
# requests
-@blueprint.route('/me/')
+@blueprint.route("/me/")
def get_current_user_info():
"""
List basic information about the current user.
@@ -98,25 +101,27 @@ def get_current_user_info():
flask.Response: json structure for the user
"""
data = flask.g.current_user
- outstructure = {'_id': '',
- 'affiliation': '',
- 'auth_ids': [],
- 'email': '',
- 'contact': '',
- 'name': '',
- 'orcid': '',
- 'permissions': '',
- 'url': ''}
+ outstructure = {
+ "_id": "",
+ "affiliation": "",
+ "auth_ids": [],
+ "email": "",
+ "contact": "",
+ "name": "",
+ "orcid": "",
+ "permissions": "",
+ "url": "",
+ }
if data:
for field in outstructure:
if field in data:
outstructure[field] = data[field]
- return utils.response_json({'user': outstructure})
+ return utils.response_json({"user": outstructure})
# requests
-@blueprint.route('/me/apikey/', methods=['POST'])
-@blueprint.route('//apikey/', methods=['POST'])
+@blueprint.route("/me/apikey/", methods=["POST"])
+@blueprint.route("//apikey/", methods=["POST"])
@login_required
def gen_new_api_key(identifier: str = None):
"""
@@ -131,32 +136,37 @@ def gen_new_api_key(identifier: str = None):
if not identifier:
user_data = flask.g.current_user
else:
- if not has_permission('USER_MANAGEMENT'):
+ if not has_permission("USER_MANAGEMENT"):
flask.abort(403)
try:
user_uuid = utils.str_to_uuid(identifier)
except ValueError:
flask.abort(status=404)
- if not (user_data := flask.g.db['users'].find_one({'_id': user_uuid})): # pylint: disable=superfluous-parens
+ if not (
+ user_data := flask.g.db["users"].find_one({"_id": user_uuid})
+ ): # pylint: disable=superfluous-parens
flask.abort(status=404)
apikey = utils.gen_api_key()
new_hash = utils.gen_api_key_hash(apikey.key, apikey.salt)
- new_values = {'api_key': new_hash, 'api_salt': apikey.salt}
+ new_values = {"api_key": new_hash, "api_salt": apikey.salt}
user_data.update(new_values)
- result = flask.g.db['users'].update_one({'_id': user_data['_id']},
- {'$set': new_values})
+ result = flask.g.db["users"].update_one(
+ {"_id": user_data["_id"]}, {"$set": new_values}
+ )
if not result.acknowledged:
- flask.current_app.logger.error('Updating API key for user %s failed', user_data['_id'])
+ flask.current_app.logger.error(
+ "Updating API key for user %s failed", user_data["_id"]
+ )
flask.Response(status=500)
else:
- utils.make_log('user', 'edit', 'New API key', user_data)
+ utils.make_log("user", "edit", "New API key", user_data)
- return utils.response_json({'key': apikey.key})
+ return utils.response_json({"key": apikey.key})
-@blueprint.route('//', methods=['GET'])
+@blueprint.route("//", methods=["GET"])
@login_required
def get_user_data(identifier: str):
"""
@@ -168,7 +178,7 @@ def get_user_data(identifier: str):
Returns:
flask.Response: Information about the user as json.
"""
- if not has_permission('USER_MANAGEMENT'):
+ if not has_permission("USER_MANAGEMENT"):
flask.abort(403)
try:
@@ -176,17 +186,19 @@ def get_user_data(identifier: str):
except ValueError:
flask.abort(status=404)
- if not (user_info := flask.g.db['users'].find_one({'_id': user_uuid})): # pylint: disable=superfluous-parens
+ if not (
+ user_info := flask.g.db["users"].find_one({"_id": user_uuid})
+ ): # pylint: disable=superfluous-parens
flask.abort(status=404)
# The hash and salt should never leave the system
- del user_info['api_key']
- del user_info['api_salt']
+ del user_info["api_key"]
+ del user_info["api_salt"]
- return utils.response_json({'user': user_info})
+ return utils.response_json({"user": user_info})
-@blueprint.route('/', methods=['POST'])
+@blueprint.route("/", methods=["POST"])
@login_required
def add_user():
"""
@@ -195,7 +207,7 @@ def add_user():
Returns:
flask.Response: Information about the user as json.
"""
- if not has_permission('USER_ADD'):
+ if not has_permission("USER_ADD"):
flask.abort(403)
new_user = structure.user()
@@ -203,41 +215,40 @@ def add_user():
indata = flask.json.loads(flask.request.data)
except json.decoder.JSONDecodeError:
flask.abort(status=400)
- validation = utils.basic_check_indata(indata, new_user, ('_id',
- 'api_key',
- 'api_salt',
- 'auth_ids'))
+ validation = utils.basic_check_indata(
+ indata, new_user, ("_id", "api_key", "api_salt", "auth_ids")
+ )
if not validation.result:
flask.abort(status=validation.status)
- if 'email' not in indata:
- flask.current_app.logger.debug('Email must be set')
+ if "email" not in indata:
+ flask.current_app.logger.debug("Email must be set")
flask.abort(status=400)
- old_user = flask.g.db['users'].find_one({'email': indata['email']})
+ old_user = flask.g.db["users"].find_one({"email": indata["email"]})
if old_user:
- flask.current_app.logger.debug('User already exists')
+ flask.current_app.logger.debug("User already exists")
flask.abort(status=400)
- if not has_permission('USER_MANAGEMENT') and 'permissions' in indata:
- flask.current_app.logger.debug('USER_MANAGEMENT required for permissions')
+ if not has_permission("USER_MANAGEMENT") and "permissions" in indata:
+ flask.current_app.logger.debug("USER_MANAGEMENT required for permissions")
flask.abort(403)
new_user.update(indata)
- new_user['auth_ids'] = [f'{new_user["_id"]}::local']
+ new_user["auth_ids"] = [f'{new_user["_id"]}::local']
- result = flask.g.db['users'].insert_one(new_user)
+ result = flask.g.db["users"].insert_one(new_user)
if not result.acknowledged:
- flask.current_app.logger.error('User Addition failed: %s', new_user['email'])
+ flask.current_app.logger.error("User Addition failed: %s", new_user["email"])
flask.Response(status=500)
else:
- utils.make_log('user', 'add', 'User added by admin', new_user)
+ utils.make_log("user", "add", "User added by admin", new_user)
- return utils.response_json({'_id': result.inserted_id})
+ return utils.response_json({"_id": result.inserted_id})
-@blueprint.route('//', methods=['DELETE'])
+@blueprint.route("//", methods=["DELETE"])
@login_required
def delete_user(identifier: str):
"""
@@ -249,7 +260,7 @@ def delete_user(identifier: str):
Returns:
flask.Response: Response code.
"""
- if not has_permission('USER_MANAGEMENT'):
+ if not has_permission("USER_MANAGEMENT"):
flask.abort(403)
try:
@@ -257,20 +268,20 @@ def delete_user(identifier: str):
except ValueError:
flask.abort(status=404)
- if not flask.g.db['users'].find_one({'_id': user_uuid}):
+ if not flask.g.db["users"].find_one({"_id": user_uuid}):
flask.abort(status=404)
- result = flask.g.db['users'].delete_one({'_id': user_uuid})
+ result = flask.g.db["users"].delete_one({"_id": user_uuid})
if not result.acknowledged:
- flask.current_app.logger.error('User deletion failed: %s', user_uuid)
+ flask.current_app.logger.error("User deletion failed: %s", user_uuid)
flask.Response(status=500)
else:
- utils.make_log('user', 'delete', 'User delete', {'_id': user_uuid})
+ utils.make_log("user", "delete", "User delete", {"_id": user_uuid})
return flask.Response(status=200)
-@blueprint.route('/me/', methods=['PATCH'])
+@blueprint.route("/me/", methods=["PATCH"])
@login_required
def update_current_user_info():
"""
@@ -285,29 +296,29 @@ def update_current_user_info():
indata = flask.json.loads(flask.request.data)
except json.decoder.JSONDecodeError:
flask.abort(status=400)
- validation = utils.basic_check_indata(indata, user_data, ('_id',
- 'api_key',
- 'api_salt',
- 'auth_ids',
- 'email',
- 'permissions'))
+ validation = utils.basic_check_indata(
+ indata,
+ user_data,
+ ("_id", "api_key", "api_salt", "auth_ids", "email", "permissions"),
+ )
if not validation[0]:
flask.abort(status=validation[1])
user_data.update(indata)
- result = flask.g.db['users'].update_one({'_id': user_data['_id']},
- {'$set': user_data})
+ result = flask.g.db["users"].update_one(
+ {"_id": user_data["_id"]}, {"$set": user_data}
+ )
if not result.acknowledged:
- flask.current_app.logger.error('User self-update failed: %s', indata)
+ flask.current_app.logger.error("User self-update failed: %s", indata)
flask.Response(status=500)
else:
- utils.make_log('user', 'edit', 'User self-updated', user_data)
+ utils.make_log("user", "edit", "User self-updated", user_data)
return flask.Response(status=200)
-@blueprint.route('//', methods=['PATCH'])
+@blueprint.route("//", methods=["PATCH"])
@login_required
def update_user_info(identifier: str):
"""
@@ -319,7 +330,7 @@ def update_user_info(identifier: str):
Returns:
flask.Response: Response code.
"""
- if not has_permission('USER_MANAGEMENT'):
+ if not has_permission("USER_MANAGEMENT"):
flask.abort(403)
try:
@@ -327,25 +338,26 @@ def update_user_info(identifier: str):
except ValueError:
flask.abort(status=404)
- if not (user_data := flask.g.db['users'].find_one({'_id': user_uuid})): # pylint: disable=superfluous-parens
+ if not (
+ user_data := flask.g.db["users"].find_one({"_id": user_uuid})
+ ): # pylint: disable=superfluous-parens
flask.abort(status=404)
try:
indata = flask.json.loads(flask.request.data)
except json.decoder.JSONDecodeError:
flask.abort(status=400)
- validation = utils.basic_check_indata(indata, user_data, ('_id',
- 'api_key',
- 'api_salt',
- 'auth_ids'))
+ validation = utils.basic_check_indata(
+ indata, user_data, ("_id", "api_key", "api_salt", "auth_ids")
+ )
if not validation.result:
flask.abort(status=validation.status)
- if 'email' in indata:
- old_user = flask.g.db['users'].find_one({'email': indata['email']})
- if old_user and old_user['_id'] != user_data['_id']:
- flask.current_app.logger.debug('User already exists')
+ if "email" in indata:
+ old_user = flask.g.db["users"].find_one({"email": indata["email"]})
+ if old_user and old_user["_id"] != user_data["_id"]:
+ flask.current_app.logger.debug("User already exists")
flask.abort(status=400)
# Avoid "updating" and making log if there are no changes
@@ -356,20 +368,21 @@ def update_user_info(identifier: str):
break
if indata and is_different:
- result = flask.g.db['users'].update_one({'_id': user_data['_id']},
- {'$set': indata})
+ result = flask.g.db["users"].update_one(
+ {"_id": user_data["_id"]}, {"$set": indata}
+ )
if not result.acknowledged:
- flask.current_app.logger.error('User update failed: %s', indata)
+ flask.current_app.logger.error("User update failed: %s", indata)
flask.Response(status=500)
else:
user_data.update(indata)
- utils.make_log('user', 'edit', 'User updated', user_data)
+ utils.make_log("user", "edit", "User updated", user_data)
return flask.Response(status=200)
-@blueprint.route('/me/log/', methods=['GET'])
-@blueprint.route('//log/', methods=['GET'])
+@blueprint.route("/me/log/", methods=["GET"])
+@blueprint.route("//log/", methods=["GET"])
@login_required
def get_user_log(identifier: str = None):
"""
@@ -384,9 +397,11 @@ def get_user_log(identifier: str = None):
flask.Response: Information about the user as json.
"""
if identifier is None:
- identifier = str(flask.g.current_user['_id'])
+ identifier = str(flask.g.current_user["_id"])
- if str(flask.g.current_user['_id']) != identifier and not has_permission('USER_MANAGEMENT'):
+ if str(flask.g.current_user["_id"]) != identifier and not has_permission(
+ "USER_MANAGEMENT"
+ ):
flask.abort(403)
try:
@@ -394,20 +409,22 @@ def get_user_log(identifier: str = None):
except ValueError:
flask.abort(status=404)
- user_logs = list(flask.g.db['logs'].find({'data_type': 'user', 'data._id': user_uuid}))
+ user_logs = list(
+ flask.g.db["logs"].find({"data_type": "user", "data._id": user_uuid})
+ )
for log in user_logs:
- del log['data_type']
+ del log["data_type"]
utils.incremental_logs(user_logs)
- return utils.response_json({'entry_id': user_uuid,
- 'data_type': 'user',
- 'logs': user_logs})
+ return utils.response_json(
+ {"entry_id": user_uuid, "data_type": "user", "logs": user_logs}
+ )
-@blueprint.route('/me/actions/', methods=['GET'])
-@blueprint.route('//actions/', methods=['GET'])
+@blueprint.route("/me/actions/", methods=["GET"])
+@blueprint.route("//actions/", methods=["GET"])
@login_required
def get_user_actions(identifier: str = None):
"""
@@ -422,9 +439,11 @@ def get_user_actions(identifier: str = None):
flask.Response: Information about the user as json.
"""
if identifier is None:
- identifier = str(flask.g.current_user['_id'])
+ identifier = str(flask.g.current_user["_id"])
- if str(flask.g.current_user['_id']) != identifier and not has_permission('USER_MANAGEMENT'):
+ if str(flask.g.current_user["_id"]) != identifier and not has_permission(
+ "USER_MANAGEMENT"
+ ):
flask.abort(403)
try:
@@ -433,13 +452,13 @@ def get_user_actions(identifier: str = None):
flask.abort(status=404)
# only report a list of actions, not the actual data
- user_logs = list(flask.g.db['logs'].find({'user': user_uuid}, {'user': 0}))
+ user_logs = list(flask.g.db["logs"].find({"user": user_uuid}, {"user": 0}))
for entry in user_logs:
- entry['entry_id'] = entry['data']['_id']
- del entry['data']
+ entry["entry_id"] = entry["data"]["_id"]
+ del entry["data"]
- return utils.response_json({'logs': user_logs})
+ return utils.response_json({"logs": user_logs})
# helper functions
@@ -453,35 +472,38 @@ def add_new_user(user_info: dict):
Args:
user_info (dict): Information about the user
"""
- db_user = flask.g.db['users'].find_one({'email': user_info['email']})
+ db_user = flask.g.db["users"].find_one({"email": user_info["email"]})
if db_user:
- db_user['auth_ids'].append(user_info['auth_id'])
- result = flask.g.db['users'].update_one({'email': user_info['email']},
- {'$set': {'auth_ids': db_user['auth_ids']}})
+ db_user["auth_ids"].append(user_info["auth_id"])
+ result = flask.g.db["users"].update_one(
+ {"email": user_info["email"]}, {"$set": {"auth_ids": db_user["auth_ids"]}}
+ )
if not result.acknowledged:
- flask.current_app.logger.error('Failed to add new auth_id to user with email %s',
- user_info['email'])
+ flask.current_app.logger.error(
+ "Failed to add new auth_id to user with email %s", user_info["email"]
+ )
flask.Response(status=500)
else:
- utils.make_log('user',
- 'edit',
- 'Add OIDC entry to auth_ids',
- db_user,
- no_user=True)
+ utils.make_log(
+ "user", "edit", "Add OIDC entry to auth_ids", db_user, no_user=True
+ )
else:
new_user = structure.user()
- new_user['email'] = user_info['email']
- new_user['name'] = user_info['name']
- new_user['auth_ids'] = [user_info['auth_id']]
+ new_user["email"] = user_info["email"]
+ new_user["name"] = user_info["name"]
+ new_user["auth_ids"] = [user_info["auth_id"]]
- result = flask.g.db['users'].insert_one(new_user)
+ result = flask.g.db["users"].insert_one(new_user)
if not result.acknowledged:
- flask.current_app.logger.error('Failed to add user with email %s via oidc',
- user_info['email'])
+ flask.current_app.logger.error(
+ "Failed to add user with email %s via oidc", user_info["email"]
+ )
flask.Response(status=500)
else:
- utils.make_log('user', 'add', 'Creating new user from OAuth', new_user, no_user=True)
+ utils.make_log(
+ "user", "add", "Creating new user from OAuth", new_user, no_user=True
+ )
def do_login(auth_id: str):
@@ -493,12 +515,12 @@ def do_login(auth_id: str):
Returns bool: Whether the login succeeded.
"""
- user = flask.g.db['users'].find_one({'auth_ids': auth_id})
+ user = flask.g.db["users"].find_one({"auth_ids": auth_id})
if not user:
return False
- flask.session['user_id'] = user['_id']
+ flask.session["user_id"] = user["_id"]
flask.session.permanent = True
return True
@@ -510,7 +532,7 @@ def get_current_user():
Returns:
dict: The current user.
"""
- return get_user(user_uuid=flask.session.get('user_id'))
+ return get_user(user_uuid=flask.session.get("user_id"))
def get_user(user_uuid=None):
@@ -524,7 +546,7 @@ def get_user(user_uuid=None):
dict: The current user.
"""
if user_uuid:
- user = flask.g.db['users'].find_one({'_id': user_uuid})
+ user = flask.g.db["users"].find_one({"_id": user_uuid})
if user:
return user
return None
@@ -542,8 +564,11 @@ def has_permission(permission: str):
"""
if not flask.g.permissions and permission:
return False
- user_permissions = set(chain.from_iterable(PERMISSIONS[permission]
- for permission in flask.g.permissions))
+ user_permissions = set(
+ chain.from_iterable(
+ PERMISSIONS[permission] for permission in flask.g.permissions
+ )
+ )
if permission not in user_permissions:
return False
return True
diff --git a/backend/utils.py b/backend/utils.py
index bd4d6b52..b4850e31 100644
--- a/backend/utils.py
+++ b/backend/utils.py
@@ -3,11 +3,12 @@
from collections import abc, namedtuple
from typing import Any, Union
import datetime
-import hashlib
+import html
import re
import secrets
import uuid
+import argon2
import bson
import flask
import pymongo
@@ -16,12 +17,12 @@
import validate
-ValidationResult = namedtuple('ValidationResult', ['result', 'status'])
+ValidationResult = namedtuple("ValidationResult", ["result", "status"])
-def basic_check_indata(indata: dict,
- reference_data: dict,
- prohibited: Union[tuple, list]) -> tuple:
+def basic_check_indata(
+ indata: dict, reference_data: dict, prohibited: Union[tuple, list]
+) -> tuple:
"""
Perform basic checks of indata.
@@ -43,18 +44,20 @@ def basic_check_indata(indata: dict,
if prohibited is None:
prohibited = []
- if 'title' in reference_data and \
- not reference_data['title'] and \
- not indata.get('title'):
- flask.current_app.logger.debug('Title empty')
+ if (
+ "title" in reference_data
+ and not reference_data["title"]
+ and not indata.get("title")
+ ):
+ flask.current_app.logger.debug("Title empty")
return ValidationResult(result=False, status=400)
for key in indata:
if key in prohibited and indata[key] != reference_data[key]:
- flask.current_app.logger.debug('Prohibited key (%s) with new value', key)
+ flask.current_app.logger.debug("Prohibited key (%s) with new value", key)
return ValidationResult(result=False, status=403)
if key not in reference_data:
- flask.current_app.logger.debug('Bad key (%s)', key)
+ flask.current_app.logger.debug("Bad key (%s)", key)
return ValidationResult(result=False, status=400)
if indata[key] != reference_data[key]:
if not validate.validate_field(key, indata[key]):
@@ -62,6 +65,22 @@ def basic_check_indata(indata: dict,
return ValidationResult(result=True, status=200)
+def secure_description(data: str):
+ """
+ Process the description to make sure it does not contain dangerous data.
+
+ Current checks:
+ * Escape HTML
+
+ Args:
+ data: The description to process.
+
+ Returns:
+ str: The processed description.
+ """
+ return html.escape(data)
+
+
# csrf
def verify_csrf_token():
"""
@@ -69,9 +88,9 @@ def verify_csrf_token():
Aborts with status 400 if the verification fails.
"""
- token = flask.request.headers.get('X-CSRFToken')
- if not token or (token != flask.request.cookies.get('_csrf_token')):
- flask.current_app.logger.warning('Bad csrf token received')
+ token = flask.request.headers.get("X-CSRFToken")
+ if not token or (token != flask.request.cookies.get("_csrf_token")):
+ flask.current_app.logger.warning("Bad csrf token received")
flask.abort(status=400)
@@ -93,9 +112,8 @@ def gen_api_key():
Returns:
APIkey: The API key with salt.
"""
- ApiKey = namedtuple('ApiKey', ['key', 'salt'])
- return ApiKey(key=secrets.token_hex(48),
- salt=secrets.token_hex(32))
+ ApiKey = namedtuple("ApiKey", ["key", "salt"])
+ return ApiKey(key=secrets.token_urlsafe(64), salt=secrets.token_hex(32))
def gen_api_key_hash(api_key: str, salt: str):
@@ -109,8 +127,8 @@ def gen_api_key_hash(api_key: str, salt: str):
Returns:
str: SHA512 hash as hex.
"""
- ct_bytes = bytes.fromhex(api_key + salt)
- return hashlib.sha512(ct_bytes).hexdigest()
+ ph = argon2.PasswordHasher()
+ return ph.hash(api_key + salt)
def verify_api_key(username: str, api_key: str):
@@ -123,18 +141,15 @@ def verify_api_key(username: str, api_key: str):
username (str): The username to check.
api_key (str): The received API key (hex).
"""
- user_info = flask.g.db['users'].find_one({'auth_ids': username})
+ ph = argon2.PasswordHasher()
+ user_info = flask.g.db["users"].find_one({"auth_ids": username})
if not user_info:
- flask.current_app.logger.warning('API key verification failed (bad username)')
+ flask.current_app.logger.info("API key verification failed (bad username)")
flask.abort(status=401)
try:
- ct_bytes = bytes.fromhex(api_key + user_info['api_salt'])
- except ValueError:
- flask.current_app.logger.warning('Non-hex API key provided')
- flask.abort(status=401)
- new_hash = hashlib.sha512(ct_bytes).hexdigest()
- if not new_hash == user_info['api_key']:
- flask.current_app.logger.warning('API key verification failed (bad hash)')
+ ph.verify(user_info["api_key"], api_key + user_info["api_salt"])
+ except argon2.exceptions.VerifyMismatchError:
+ flask.current_app.logger.info("API key verification failed (bad hash)")
flask.abort(status=401)
@@ -148,13 +163,17 @@ def get_dbclient(conf) -> pymongo.mongo_client.MongoClient:
Returns:
pymongo.mongo_client.MongoClient: The client connection.
"""
- return pymongo.MongoClient(host=conf['mongo']['host'],
- port=conf['mongo']['port'],
- username=conf['mongo']['user'],
- password=conf['mongo']['password'])
+ return pymongo.MongoClient(
+ host=conf["mongo"]["host"],
+ port=conf["mongo"]["port"],
+ username=conf["mongo"]["user"],
+ password=conf["mongo"]["password"],
+ )
-def get_db(dbserver: pymongo.mongo_client.MongoClient, conf) -> pymongo.database.Database:
+def get_db(
+ dbserver: pymongo.mongo_client.MongoClient, conf
+) -> pymongo.database.Database:
"""
Get the connection to the MongoDB database.
@@ -165,9 +184,10 @@ def get_db(dbserver: pymongo.mongo_client.MongoClient, conf) -> pymongo.database
Returns:
pymongo.database.Database: The database connection.
"""
- codec_options = bson.codec_options.CodecOptions(uuid_representation=bson.binary.STANDARD)
- return dbserver.get_database(conf['mongo']['db'],
- codec_options=(codec_options))
+ codec_options = bson.codec_options.CodecOptions(
+ uuid_representation=bson.binary.STANDARD
+ )
+ return dbserver.get_database(conf["mongo"]["db"], codec_options=(codec_options))
def new_uuid() -> uuid.UUID:
@@ -218,16 +238,16 @@ def convert_keys_to_camel(chunk: Any) -> Any:
new_chunk = {}
for key, value in chunk.items():
- if key == '_id':
+ if key == "_id":
new_chunk[key] = value
continue
# First character should be the same as in the original string
- new_key = key[0] + ''.join([a[0].upper() + a[1:] for a in key.split('_')])[1:]
+ new_key = key[0] + "".join([a[0].upper() + a[1:] for a in key.split("_")])[1:]
new_chunk[new_key] = convert_keys_to_camel(value)
return new_chunk
-REGEX = {'email': re.compile(r'.*@.*\..*')}
+REGEX = {"email": re.compile(r".*@.*\..*")}
def is_email(indata: str):
@@ -242,7 +262,7 @@ def is_email(indata: str):
"""
if not isinstance(indata, str):
return False
- return bool(REGEX['email'].search(indata))
+ return bool(REGEX["email"].search(indata))
def response_json(json_structure: dict):
@@ -270,12 +290,14 @@ def make_timestamp():
# pylint: disable=too-many-arguments
-def make_log(data_type: str,
- action: str,
- comment: str,
- data: dict = None,
- no_user: bool = False,
- dbsession=None):
+def make_log(
+ data_type: str,
+ action: str,
+ comment: str,
+ data: dict = None,
+ no_user: bool = False,
+ dbsession=None,
+):
"""
Log a change in the system.
@@ -299,19 +321,25 @@ def make_log(data_type: str,
"""
log = structure.log()
if no_user:
- active_user = 'system'
+ active_user = "system"
else:
- active_user = flask.g.current_user['_id']
-
- log.update({'action': action,
- 'comment': comment,
- 'data_type': data_type,
- 'data': data,
- 'user': active_user})
- result = flask.g.db['logs'].insert_one(log, session=dbsession)
+ active_user = flask.g.current_user["_id"]
+
+ log.update(
+ {
+ "action": action,
+ "comment": comment,
+ "data_type": data_type,
+ "data": data,
+ "user": active_user,
+ }
+ )
+ result = flask.g.db["logs"].insert_one(log, session=dbsession)
if not result.acknowledged:
- flask.current_app.logger.error(f'Log failed: A:{action} C:{comment} D:{data} ' +
- f'DT: {data_type} U: {flask.g.current_user["_id"]}')
+ flask.current_app.logger.error(
+ f"Log failed: A:{action} C:{comment} D:{data} "
+ + f'DT: {data_type} U: {flask.g.current_user["_id"]}'
+ )
return result.acknowledged
@@ -324,14 +352,14 @@ def incremental_logs(logs: list):
``logs`` is changed in-place.
"""
- logs.sort(key=lambda x: x['timestamp'])
- for i in range(len(logs)-1, 0, -1):
+ logs.sort(key=lambda x: x["timestamp"])
+ for i in range(len(logs) - 1, 0, -1):
del_keys = []
- for key in logs[i]['data']:
- if logs[i]['data'][key] == logs[i-1]['data'][key]:
+ for key in logs[i]["data"]:
+ if logs[i]["data"][key] == logs[i - 1]["data"][key]:
del_keys.append(key)
for key in del_keys:
- del logs[i]['data'][key]
+ del logs[i]["data"][key]
def check_email_uuid(user_identifier: str) -> Union[str, uuid.UUID]:
@@ -352,22 +380,23 @@ def check_email_uuid(user_identifier: str) -> Union[str, uuid.UUID]:
Union[str, uuid.UUID]: The new value for the field.
"""
if is_email(user_identifier):
- user_entry = flask.g.db['users'].find_one({'email': user_identifier})
+ user_entry = flask.g.db["users"].find_one({"email": user_identifier})
if user_entry:
- return user_entry['_id']
+ return user_entry["_id"]
return user_identifier
try:
user_uuid = str_to_uuid(user_identifier)
except ValueError:
- return ''
- user_entry = flask.g.db['users'].find_one({'_id': user_uuid})
+ return ""
+ user_entry = flask.g.db["users"].find_one({"_id": user_uuid})
if user_entry:
- return user_entry['_id']
- return ''
+ return user_entry["_id"]
+ return ""
-def user_uuid_data(user_ids: Union[str, list, uuid.UUID],
- mongodb: pymongo.database.Database) -> list:
+def user_uuid_data(
+ user_ids: Union[str, list, uuid.UUID], mongodb: pymongo.database.Database
+) -> list:
"""
Retrieve some extra information about a user using a uuid as input.
@@ -386,11 +415,15 @@ def user_uuid_data(user_ids: Union[str, list, uuid.UUID],
user_uuids = [str_to_uuid(entry) for entry in user_ids]
else:
user_uuids = [user_ids]
- data = mongodb['users'].find({'_id': {'$in': user_uuids}})
- return [{'_id': str(entry['_id']),
- 'affiliation': entry['affiliation'],
- 'name': entry['name'],
- 'contact': entry['contact'],
- 'url': entry['url'],
- 'orcid': entry['orcid']}
- for entry in data]
+ data = mongodb["users"].find({"_id": {"$in": user_uuids}})
+ return [
+ {
+ "_id": str(entry["_id"]),
+ "affiliation": entry["affiliation"],
+ "name": entry["name"],
+ "contact": entry["contact"],
+ "url": entry["url"],
+ "orcid": entry["orcid"],
+ }
+ for entry in data
+ ]
diff --git a/backend/validate.py b/backend/validate.py
index cbf0ad03..f788fc21 100644
--- a/backend/validate.py
+++ b/backend/validate.py
@@ -37,13 +37,15 @@ def validate_field(field_key: str, field_value: Any) -> bool:
try:
VALIDATION_MAPPER[field_key](field_value)
except KeyError:
- flask.current_app.logger.debug('Unknown key: %s', field_key)
+ flask.current_app.logger.debug("Unknown key: %s", field_key)
return False
except ValueError as err:
- flask.current_app.logger.debug('Indata validation failed: %s - %s', field_key, err)
+ flask.current_app.logger.debug(
+ "Indata validation failed: %s - %s", field_key, err
+ )
return False
except exceptions.AuthError as err:
- flask.current_app.logger.debug('Permission failed: %s - %s', field_key, err)
+ flask.current_app.logger.debug("Permission failed: %s - %s", field_key, err)
return False
return True
@@ -64,16 +66,16 @@ def validate_datasets(data: list) -> bool:
ValueError: Validation failed.
"""
if not isinstance(data, list):
- raise ValueError(f'Must be list ({data})')
+ raise ValueError(f"Must be list ({data})")
for ds_entry in data:
if not isinstance(ds_entry, str):
- raise ValueError(f'Must be str ({ds_entry})')
+ raise ValueError(f"Must be str ({ds_entry})")
try:
ds_uuid = uuid.UUID(ds_entry)
except ValueError as err:
- raise ValueError(f'Not a valid uuid ({data})') from err
- if not flask.g.db['datasets'].find_one({'_id': ds_uuid}):
- raise ValueError(f'Uuid not in db ({data})')
+ raise ValueError(f"Not a valid uuid ({data})") from err
+ if not flask.g.db["datasets"].find_one({"_id": ds_uuid}):
+ raise ValueError(f"Uuid not in db ({data})")
return True
@@ -93,9 +95,9 @@ def validate_email(data) -> bool:
ValueError: Validation failed.
"""
if not isinstance(data, str):
- raise ValueError(f'Not a string ({data})')
+ raise ValueError(f"Not a string ({data})")
if not utils.is_email(data):
- raise ValueError(f'Not a valid email address ({data})')
+ raise ValueError(f"Not a valid email address ({data})")
return True
@@ -113,10 +115,10 @@ def validate_list_of_strings(data: list) -> bool:
ValueError: Validation failed.
"""
if not isinstance(data, list):
- raise ValueError(f'Not a list ({data})')
+ raise ValueError(f"Not a list ({data})")
for entry in data:
if not isinstance(entry, str):
- raise ValueError(f'Not a string ({entry})')
+ raise ValueError(f"Not a string ({entry})")
return True
@@ -136,10 +138,10 @@ def validate_permissions(data: list) -> bool:
ValueError: Validation failed.
"""
if not isinstance(data, list):
- raise ValueError('Must be a list')
+ raise ValueError("Must be a list")
for entry in data:
if entry not in user.PERMISSIONS:
- raise ValueError(f'Bad entry ({entry})')
+ raise ValueError(f"Bad entry ({entry})")
return True
@@ -157,7 +159,7 @@ def validate_string(data: str) -> bool:
ValueError: Validation failed.
"""
if not isinstance(data, str):
- raise ValueError(f'Not a string ({data})')
+ raise ValueError(f"Not a string ({data})")
return True
@@ -177,15 +179,14 @@ def validate_cross_references(data: list) -> bool:
ValueError: Validation failed.
"""
if not isinstance(data, list):
- raise ValueError(f'Not a list ({data})')
+ raise ValueError(f"Not a list ({data})")
for entry in data:
if not isinstance(entry, dict):
- raise ValueError(f'List entries must be dicts ({entry})')
- if list(entry.keys()) != ['title', 'value']:
- raise KeyError(f'Incorrect keys ({entry.keys})')
- if not isinstance(entry['title'], str) or \
- not isinstance(entry['value'], str):
- raise ValueError(f'Values must be strings ({entry.values()})')
+ raise ValueError(f"List entries must be dicts ({entry})")
+ if list(entry.keys()) != ["title", "value"]:
+ raise KeyError(f"Incorrect keys ({entry.keys})")
+ if not isinstance(entry["title"], str) or not isinstance(entry["value"], str):
+ raise ValueError(f"Values must be strings ({entry.values()})")
return True
@@ -204,13 +205,13 @@ def validate_properties(data: dict) -> bool:
Raises:
ValueError: Validation failed.
"""
- if not user.has_permission('DATA_MANAGEMENT'):
- raise exceptions.AuthError('Permission DATA_MANAGEMENT required')
+ if not user.has_permission("DATA_MANAGEMENT"):
+ raise exceptions.AuthError("Permission DATA_MANAGEMENT required")
if not isinstance(data, dict):
- raise ValueError(f'Not a dict ({data})')
+ raise ValueError(f"Not a dict ({data})")
for key in data:
if not isinstance(key, str) or not isinstance(data[key], str):
- raise ValueError(f'Keys and values must be strings ({key}, {data[key]})')
+ raise ValueError(f"Keys and values must be strings ({key}, {data[key]})")
return True
@@ -230,10 +231,10 @@ def validate_tags(data: Union[tuple, list]) -> bool:
ValueError: Validation failed.
"""
if not isinstance(data, list) and not isinstance(data, tuple):
- raise ValueError(f'Not a list ({data})')
+ raise ValueError(f"Not a list ({data})")
for value in data:
if not isinstance(value, str):
- raise ValueError(f'All list entries must be str ({value})')
+ raise ValueError(f"All list entries must be str ({value})")
return True
@@ -253,7 +254,7 @@ def validate_title(data: str) -> bool:
ValueError: Validation failed.
"""
if validate_string(data) and not data:
- raise ValueError('Must not be empty')
+ raise ValueError("Must not be empty")
return True
@@ -273,9 +274,9 @@ def validate_url(data: str) -> bool:
ValueError: Validation failed.
"""
if not isinstance(data, str):
- raise ValueError('Must be a string')
- if data and not data.startswith('http://') and not data.startswith('https://'):
- raise ValueError('URLs must start with http(s)://')
+ raise ValueError("Must be a string")
+ if data and not data.startswith("http://") and not data.startswith("https://"):
+ raise ValueError("URLs must start with http(s)://")
return True
@@ -295,7 +296,7 @@ def validate_user(data: str) -> bool:
ValueError: Validation failed.
"""
if not isinstance(data, str):
- raise ValueError(f'Bad data type (must be str): {data}')
+ raise ValueError(f"Bad data type (must be str): {data}")
if not data:
return True
@@ -303,9 +304,9 @@ def validate_user(data: str) -> bool:
try:
user_uuid = uuid.UUID(data)
except ValueError as err:
- raise ValueError(f'Not a valid uuid ({data})') from err
- if not flask.g.db['users'].find_one({'_id': user_uuid}):
- raise ValueError(f'Uuid not in db ({data})')
+ raise ValueError(f"Not a valid uuid ({data})") from err
+ if not flask.g.db["users"].find_one({"_id": user_uuid}):
+ raise ValueError(f"Uuid not in db ({data})")
return True
@@ -328,33 +329,35 @@ def validate_user_list(data: Union[tuple, list]) -> bool:
ValueError: Validation failed.
"""
if not isinstance(data, list):
- raise ValueError(f'Bad data type (must be list): {data}')
+ raise ValueError(f"Bad data type (must be list): {data}")
for u_uuid in data:
try:
user_uuid = uuid.UUID(u_uuid)
except ValueError as err:
- raise ValueError(f'Not a valid uuid ({data})') from err
- if not flask.g.db['users'].find_one({'_id': user_uuid}):
- raise ValueError(f'Uuid not in db ({data})')
+ raise ValueError(f"Not a valid uuid ({data})") from err
+ if not flask.g.db["users"].find_one({"_id": user_uuid}):
+ raise ValueError(f"Uuid not in db ({data})")
return True
-VALIDATION_MAPPER = {'affiliation': validate_string,
- 'auth_ids': validate_list_of_strings,
- 'authors': validate_user_list,
- 'contact': validate_string,
- 'cross_references': validate_cross_references,
- 'description': validate_string,
- 'datasets': validate_datasets,
- 'editors': validate_user_list,
- 'email': validate_email,
- 'generators': validate_user_list,
- 'name': validate_string,
- 'orcid': validate_string,
- 'organisation': validate_user,
- 'permissions': validate_permissions,
- 'properties': validate_properties,
- 'tags': validate_tags,
- 'title': validate_title,
- 'url': validate_url}
+VALIDATION_MAPPER = {
+ "affiliation": validate_string,
+ "auth_ids": validate_list_of_strings,
+ "authors": validate_user_list,
+ "contact": validate_string,
+ "cross_references": validate_cross_references,
+ "description": validate_string,
+ "datasets": validate_datasets,
+ "editors": validate_user_list,
+ "email": validate_email,
+ "generators": validate_user_list,
+ "name": validate_string,
+ "orcid": validate_string,
+ "organisation": validate_user,
+ "permissions": validate_permissions,
+ "properties": validate_properties,
+ "tags": validate_tags,
+ "title": validate_title,
+ "url": validate_url,
+}
diff --git a/docs/build/html/.buildinfo b/docs/build/html/.buildinfo
index 42a0f807..303bfc80 100644
--- a/docs/build/html/.buildinfo
+++ b/docs/build/html/.buildinfo
@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
-config: dd6174e40970c54baed18b7fa2e66762
+config: a5bf3c7429818817824ca7b29a92f078
tags: 645f666f9bcd5a90fca523b33c5a78b7
diff --git a/docs/build/html/api.html b/docs/build/html/api.html
index 54bd9f75..220e4667 100644
--- a/docs/build/html/api.html
+++ b/docs/build/html/api.html
@@ -533,7 +533,7 @@ Navigation
+
+
+
+
+
+
+
+
+
app module
+
Main app for the Data Tracker.
+
+-
+
app.
api_base
()[source]
+List entities.
+
+
+
+-
+
app.
error_bad_request
(_)[source]
+Make sure a simple 400 is returned instead of an html page.
+
+
+
+-
+
app.
error_forbidden
(_)[source]
+Make sure a simple 403 is returned instead of an html page.
+
+
+
+-
+
app.
error_not_found
(_)[source]
+Make sure a simple 404 is returned instead of an html page.
+
+
+
+-
+
app.
error_unauthorized
(_)[source]
+Make sure a simple 401 is returned instead of an html page.
+
+
+
+-
+
app.
finalize
(response)[source]
+Finalize the response and clean up.
+
+
+
+-
+
app.
key_login
()[source]
+Log in using an apikey.
+
+
+
+-
+
app.
login_types
()[source]
+List login types.
+
+
+
+-
+
app.
logout
()[source]
+Log out the current user.
+
+
+
+-
+
app.
oidc_authorize
(auth_name)[source]
+Authorize a login using OpenID Connect (e.g. Elixir AAI).
+
+
+
+-
+
app.
oidc_login
(auth_name)[source]
+Perform a login using OpenID Connect (e.g. Elixir AAI).
+
+
+
+-
+
app.
oidc_types
()[source]
+List OpenID Connect types.
+
+
+
+-
+
app.
prepare
()[source]
+Open the database connection and get the current user.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/build/html/app.html b/docs/build/html/app.html
new file mode 100644
index 00000000..c51040fb
--- /dev/null
+++ b/docs/build/html/app.html
@@ -0,0 +1,170 @@
+
+
+
+
+