diff --git a/terrareg/analytics.py b/terrareg/analytics.py index 6e0d6284..06f974b2 100644 --- a/terrareg/analytics.py +++ b/terrareg/analytics.py @@ -1,7 +1,7 @@ import re import datetime -from typing import Union, List +from typing import Union, List, Optional import sqlalchemy @@ -70,7 +70,7 @@ def _join_filter_analytics_table_by_module_provider(db, query, module_provider): ) @staticmethod - def get_environment_from_token(auth_token): + def get_environment_from_token(auth_token: Optional[str]): """Check if auth token matches required environment analytics tokens.""" # If no analytics tokens have been defined, return default environment if not AnalyticsEngine.are_tokens_enabled(): @@ -98,18 +98,18 @@ def record_module_version_download( module_name: str, provider_name: str, module_version, - analytics_token: str, - terraform_version: str, - user_agent: str, - auth_token: str): + analytics_token: Optional[str], + terraform_version: Optional[str], + user_agent: Optional[str], + auth_token: Optional[str]): """Store information about module version download in database.""" - # If Terraform version not present from header, - # attempt to determine from user agent - if not terraform_version: - user_agent_match = re.match(r'^Terraform/(\d+\.\d+\.\d+)$', user_agent) - if user_agent_match: - terraform_version = user_agent_match.group(1) + # Use the X-Terraform-Version header, if the user agent matches an allowed + # list of user agents. + # If the user agent does not match any of the expected prefixes, do not + # record the terraform version. + if (not user_agent) or (not any([user_agent.startswith(prefix) for prefix in ['Terraform/', 'OpenTofu/']])): + terraform_version = None # Obtain environment from auth token. # If auth token is not provided, diff --git a/test/integration/terrareg/analytics_engine/__init__.py b/test/integration/terrareg/analytics_engine/__init__.py index 82fd0e67..23ee1773 100644 --- a/test/integration/terrareg/analytics_engine/__init__.py +++ b/test/integration/terrareg/analytics_engine/__init__.py @@ -129,4 +129,5 @@ def _import_test_analytics(self, download_data): AnalyticsEngine.record_module_version_download( namespace_name=namespace, module_name=module, provider_name=provider, module_version=module_version, terraform_version=terraform_version, - analytics_token=analytics_token, user_agent=None, auth_token=auth_token) + analytics_token=analytics_token, user_agent="Terraform/{}".format(terraform_version), + auth_token=auth_token) diff --git a/test/integration/terrareg/analytics_engine/test_check_module_provider_redirect_usage.py b/test/integration/terrareg/analytics_engine/test_check_module_provider_redirect_usage.py index fbfa535c..331f53aa 100644 --- a/test/integration/terrareg/analytics_engine/test_check_module_provider_redirect_usage.py +++ b/test/integration/terrareg/analytics_engine/test_check_module_provider_redirect_usage.py @@ -27,7 +27,7 @@ def _create_analytics(self, namespace, module, provider, version, token, timesta module_version=version, analytics_token=token, terraform_version='1.1.1', - user_agent='', + user_agent='Terraform/1.1.1', auth_token=None ) diff --git a/test/integration/terrareg/analytics_engine/test_record_module_version_download.py b/test/integration/terrareg/analytics_engine/test_record_module_version_download.py new file mode 100644 index 00000000..0d0db634 --- /dev/null +++ b/test/integration/terrareg/analytics_engine/test_record_module_version_download.py @@ -0,0 +1,91 @@ + +import pytest + +import terrareg.models +from terrareg.analytics import AnalyticsEngine +from . import AnalyticsIntegrationTest + + +class TestRecordModuleVersionDownload(AnalyticsIntegrationTest): + """Test record_module_version_download function.""" + + _TEST_ANALYTICS_DATA = {} + + @pytest.mark.parametrize("user_agent, terraform_version", [ + # No user agent + (None, "1.5.3"), + + # No version + ("Terraform/1.5.2", None), + + # Invalid user-agent + ("Go-http-client/1.1", "1.5.3"), + ]) + def test_ignore_terraform_version(self, user_agent, terraform_version): + """Test function with ignoring terraform version headers.""" + namespace = "testnamespace" + module = "publishedmodule" + provider = "testprovider" + analytics_token = "test-invalid-version-headers" + + namespace_obj = terrareg.models.Namespace.get(namespace) + module_obj = terrareg.models.Module(namespace_obj, module) + provider_obj = terrareg.models.ModuleProvider.get(module_obj, provider) + version_obj = terrareg.models.ModuleVersion.get(provider_obj, "1.4.0") + + # Clean up any analytics + AnalyticsEngine.delete_analytics_for_module_version(version_obj) + + AnalyticsEngine.record_module_version_download( + namespace_name=namespace, module_name=module, provider_name=provider, + module_version=version_obj, terraform_version=terraform_version, + analytics_token=analytics_token, user_agent=user_agent, + auth_token=None + ) + + results = AnalyticsEngine.get_module_provider_token_versions(provider_obj) + assert results == { + 'test-invalid-version-headers': { + 'environment': 'Default', + 'module_version': '1.4.0', + 'terraform_version': '0.0.0' + } + } + + @pytest.mark.parametrize("user_agent, terraform_version", [ + # Terraform + ("Terraform/1.5.0", "1.5.3"), + + # OpenTofu + ("OpenTofu/1.5.2", "1.5.3"), + ]) + def test_valid_terraform_version(self, user_agent, terraform_version): + """Test function with ignoring terraform version headers.""" + namespace = "testnamespace" + module = "publishedmodule" + provider = "testprovider" + analytics_token = "test-with-version" + + namespace_obj = terrareg.models.Namespace.get(namespace) + module_obj = terrareg.models.Module(namespace_obj, module) + provider_obj = terrareg.models.ModuleProvider.get(module_obj, provider) + version_obj = terrareg.models.ModuleVersion.get(provider_obj, "1.4.0") + + # Clean up any analytics + AnalyticsEngine.delete_analytics_for_module_version(version_obj) + + AnalyticsEngine.record_module_version_download( + namespace_name=namespace, module_name=module, provider_name=provider, + module_version=version_obj, terraform_version=terraform_version, + analytics_token=analytics_token, user_agent=user_agent, + auth_token=None + ) + + results = AnalyticsEngine.get_module_provider_token_versions(provider_obj) + assert results == { + 'test-with-version': { + 'environment': 'Default', + 'module_version': '1.4.0', + 'terraform_version': '1.5.3' + } + }