Skip to content

Commit

Permalink
feat: 🔒 Secure Batch Data Import endpoint with a token key
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremyarancio authored and raphael0202 committed Sep 10, 2024
1 parent 93c1232 commit 360f2e4
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 7 deletions.
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ x-robotoff-base-env:
NUM_RQ_WORKERS: 4 # Update worker service command accordingly if you change this settings
GOOGLE_APPLICATION_CREDENTIALS: /opt/credentials/google/application_default_credentials.json
GOOGLE_CLOUD_PROJECT: "robotoff"
BATCH_JOB_KEY: # Secure Batch job import with a token key

x-robotoff-worker-base:
&robotoff-worker
Expand Down
45 changes: 38 additions & 7 deletions robotoff/app/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@

from robotoff import settings
from robotoff.app import schema
from robotoff.app.auth import BasicAuthDecodeError, basic_decode
from robotoff.app.auth import (
BasicAuthDecodeError,
basic_decode,
validate_token,
)
from robotoff.app.core import (
SkipVotedOn,
SkipVotedType,
Expand Down Expand Up @@ -302,6 +306,29 @@ def parse_auth(req: falcon.Request) -> Optional[OFFAuthentication]:
)


def parse_valid_token(req: falcon.Request, ref_token_name: str) -> bool:
"""Parse and validate authentification token from request.
:param req: Request.
:type req: falcon.Request
:param ref_token_name: Secret environment variable name.
:type ref_token_name: str
:return: Token valid or not.
:rtype: bool
"""
auth_header = req.get_header("Authorization", required=True)

scheme, token = auth_header.split()
if scheme.lower() != 'bearer':
raise falcon.HTTPUnauthorized('Invalid authentication scheme.')

is_token_valid = validate_token(token, ref_token_name)
if not is_token_valid:
raise falcon.HTTPUnauthorized('Invalid token.')
else:
return True


def device_id_from_request(req: falcon.Request) -> str:
"""Returns the 'device_id' from the request parameters, or a hash of the
access route (which should be the IPs of the proxies and the client)."""
Expand Down Expand Up @@ -1770,12 +1797,16 @@ def on_post(self, req: falcon.Request, resp: falcon.Response):
raise falcon.HTTPBadRequest(
description=f"invalid job_type: {job_type_str}. Valid job_types are: {[elt.value for elt in BatchJobType]}"
)
enqueue_job(
import_batch_predictions,
job_type=job_type,
queue=low_queue,
job_kwargs={"timeout": "10m"},
)
# We secure the endpoint
if parse_valid_token(req, "batch_job_key"):
enqueue_job(
import_batch_predictions,
job_type=job_type,
queue=low_queue,
job_kwargs={"timeout": "10m"},
)
else:
raise falcon.HTTPForbidden(description="Invalid batch_job_key. Be sure to indicate the authentification key in the request.")
logger.info("Batch import %s has been queued.", job_type)


Expand Down
20 changes: 20 additions & 0 deletions robotoff/app/auth.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from base64 import b64decode
from urllib.parse import unquote

Expand All @@ -6,6 +7,10 @@ class BasicAuthDecodeError(Exception):
pass


class APITokenError(Exception):
pass


def basic_decode(encoded_str: str) -> tuple[str, str]:
"""Decode an encrypted HTTP basic authentication string. Returns a tuple of
the form (username, password), and raises a BasicAuthDecodeError exception
Expand Down Expand Up @@ -39,3 +44,18 @@ def basic_decode(encoded_str: str) -> tuple[str, str]:
raise BasicAuthDecodeError()

return unquote(username), unquote(password)


def validate_token(token: str, ref_token_name: str) -> bool:
"""Validate token.
:param token: Authentification token
:type token: str
:param api_token_name: Validation token, stored in environment variables.
:type api_token_name: str
:rtype: bool
"""
api_token = os.getenv(ref_token_name.upper())
if not api_token:
raise APITokenError("API token not set in environment variables.")
return token == api_token

0 comments on commit 360f2e4

Please sign in to comment.