diff --git a/.gitignore b/.gitignore index e8bf92ead3e777406d4d7eeb484af7b7bfb557ad..33b68146aa9a024e14107b02d40c194a884ca0f2 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,6 @@ coverage.xml htmlcov dist venv +.venv .vscode docs/build diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 3502d074966387065a92af957caaae1a22c2aacd..28b30adfb9cf32669953e9562676164001524a1f 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -54,7 +54,7 @@ This module has been tested in the following execution environments: .. code-block:: bash $ export FLASK_APP=app.py - $ export SETTINGS_FILENAME=settings.cfg + $ export FLASK_SETTINGS_FILENAME=settings.cfg $ flask run * As an Apache/`mod_wsgi` service. diff --git a/inventory_provider/__init__.py b/inventory_provider/__init__.py index 09574fb6f842e27db80a60b3fd47de92d4b2e91b..590610d4658972d3aa0ed6c4ccad4fb8c5571d01 100644 --- a/inventory_provider/__init__.py +++ b/inventory_provider/__init__.py @@ -7,6 +7,7 @@ from flask import Flask from flask_cors import CORS from inventory_provider import environment +from inventory_provider.auth import auth def create_app(setup_logging=True): @@ -48,6 +49,13 @@ def create_app(setup_logging=True): app.config['INVENTORY_PROVIDER_CONFIG'] = inventory_provider_config + # Apply authentication globally to all routes + @app.before_request + @auth.login_required + def secure_before_request(): + """Enforces authentication for all routes""" + pass + # IMS based routes from inventory_provider.routes import lg diff --git a/inventory_provider/auth.py b/inventory_provider/auth.py new file mode 100644 index 0000000000000000000000000000000000000000..1f995033b2b468995712e9d41f266e14a48a49e1 --- /dev/null +++ b/inventory_provider/auth.py @@ -0,0 +1,17 @@ +from flask import Blueprint, current_app +from flask_httpauth import HTTPTokenAuth + +auth = HTTPTokenAuth(scheme="ApiKey") + +@auth.verify_token +def verify_api_key(api_key): + config = current_app.config["INVENTORY_PROVIDER_CONFIG"] + # This is to enable anonymous access for testing. + if not api_key: + return "test" + + for service, details in config['api-keys'].items(): + if details.get('api-key') == api_key: + return service + return None + diff --git a/inventory_provider/config.py b/inventory_provider/config.py index 3fa03b9de584b4783a4305ad9f4e9ee7ec014ba3..0df0412919f4ecc78cd659c7c9141dd2e3f72f3d 100644 --- a/inventory_provider/config.py +++ b/inventory_provider/config.py @@ -10,6 +10,24 @@ CONFIG_SCHEMA = { 'maximum': 60, # sanity 'exclusiveMinimum': 0 }, + "api-keys-credentials": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9-_]+$": { + "type": "object", + "properties": { + "api-key": { + "type": "string", + # "minLength": 32, + # "description": "API key (Base64, UUID, or Hexadecimal format)" + } + }, + "required": ["api-key"], + "additionalProperties": False + } + }, + "additionalProperties": False + }, 'ssh-credentials': { 'type': 'object', 'properties': { @@ -235,6 +253,7 @@ CONFIG_SCHEMA = { 'type': 'object', 'properties': { + 'api-keys': {'$ref': '#/definitions/api-keys-credentials'}, 'ssh': {'$ref': '#/definitions/ssh-credentials'}, 'nokia-ssh': {'$ref': '#/definitions/nokia-ssh-credentials'}, 'redis': {'$ref': '#/definitions/redis-credentials'}, diff --git a/requirements.txt b/requirements.txt index 0d226651b91236f8b7bd716d444d6d71eb3c1d0a..9095eb74b7d4b98f3f86a727cbb003ab23ba72f9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,6 +16,7 @@ lxml==4.9.4 requests netifaces tree-format +Flask-HTTPAuth pytest pytest-mock