diff --git a/README.md b/README.md index de3a17ad..0a428975 100644 --- a/README.md +++ b/README.md @@ -352,16 +352,7 @@ $ oc create secret generic \ The CERN data service collects data from various sources and expose them via a REST API endpoint. -Create secret: - -```console -$ read DATA_SERVICE_CLIENT_EMAIL -$ read DATA_SERVICE_CLIENT_PASSWORD -$ oc create secret generic \ - --from-literal="DATA_SERVICE_CLIENT_EMAIL=$DATA_SERVICE_CLIENT_EMAIL" \ - --from-literal="DATA_SERVICE_CLIENT_PASSWORD=$DATA_SERVICE_CLIENT_PASSWORD" \ - data-service-api -``` +To enable the service set the environment variable `DATA_SERVICE_ENABLED` as `True`. ## Update configuration diff --git a/app-config/calculator-app/app.sh b/app-config/calculator-app/app.sh index 8bf1d5c0..8292627b 100755 --- a/app-config/calculator-app/app.sh +++ b/app-config/calculator-app/app.sh @@ -24,8 +24,6 @@ if [[ "$APP_NAME" == "calculator-app" ]]; then export "EXTRA_PAGES"="$EXTRA_PAGES" export "DATA_SERVICE_ENABLED"="${DATA_SERVICE_ENABLED:=False}" - export "DATA_SERVICE_CLIENT_EMAIL"="$DATA_SERVICE_CLIENT_EMAIL" - export "DATA_SERVICE_CLIENT_PASSWORD"="$DATA_SERVICE_CLIENT_PASSWORD" echo "Starting the caimira webservice with: python -m caimira.apps.calculator ${args[@]}" python -m caimira.apps.calculator "${args[@]}" diff --git a/app-config/openshift/deploymentconfig.yaml b/app-config/openshift/deploymentconfig.yaml index 43357153..01023cd3 100644 --- a/app-config/openshift/deploymentconfig.yaml +++ b/app-config/openshift/deploymentconfig.yaml @@ -285,16 +285,6 @@ name: arve-api - name: DATA_SERVICE_ENABLED value: 'False' - - name: DATA_SERVICE_CLIENT_EMAIL - valueFrom: - secretKeyRef: - key: DATA_SERVICE_CLIENT_EMAIL - name: data-service-api - - name: DATA_SERVICE_CLIENT_PASSWORD - valueFrom: - secretKeyRef: - key: DATA_SERVICE_CLIENT_PASSWORD - name: data-service-api image: '${PROJECT_NAME}/calculator-app' ports: - containerPort: 8080 diff --git a/caimira/apps/calculator/__init__.py b/caimira/apps/calculator/__init__.py index 842f2de6..a71727d8 100644 --- a/caimira/apps/calculator/__init__.py +++ b/caimira/apps/calculator/__init__.py @@ -494,12 +494,7 @@ def make_app( data_service = None data_service_enabled = os.environ.get("DATA_SERVICE_ENABLED", "False") is_enabled = data_service_enabled.lower() == "true" - if is_enabled: - credentials = { - "email": os.environ.get("DATA_SERVICE_CLIENT_EMAIL", None), - "password": os.environ.get("DATA_SERVICE_CLIENT_PASSWORD", None), - } - data_service = DataService.create(credentials) + if is_enabled: data_service = DataService.create() return Application( urls, diff --git a/caimira/store/data_service.py b/caimira/store/data_service.py index a8964031..6ec03eed 100644 --- a/caimira/store/data_service.py +++ b/caimira/store/data_service.py @@ -1,8 +1,5 @@ import logging import typing -from datetime import datetime, timedelta, timezone - -import jwt import requests from caimira.store.data_registry import DataRegistry @@ -18,78 +15,18 @@ class DataService: def __init__( self, - credentials: typing.Dict[str, typing.Optional[str]], host: str, ): - self._credentials = credentials self._host = host @classmethod - def create(cls, credentials: typing.Dict[str, typing.Optional[str]], host: str = "https://caimira-data-api.app.cern.ch"): + def create(cls, host: str = "https://caimira-data-api-qa.app.cern.ch"): # UPDATE QA TO PROD ONCE ALL IS WORKING """Factory.""" - return cls(credentials, host) - - def _is_valid(self, access_token): - """Return True if the expiration token is still valid.""" - try: - decoded = jwt.decode( - access_token, algorithms=["HS256"], options={"verify_signature": False} - ) - expiration_timestamp = decoded["exp"] - expiration = datetime.utcfromtimestamp(expiration_timestamp).replace( - tzinfo=timezone.utc - ) - now = datetime.now(timezone.utc) - is_valid = now < expiration - timedelta( - seconds=5 - ) # 5 seconds time delta to avoid timing issues - - logger.debug(f"Access token expiration: {expiration_timestamp}. Is valid? {is_valid}") - - return is_valid - except jwt.ExpiredSignatureError: - logger.warning("JWT token expired.") - except jwt.InvalidTokenError: - logger.warning("JWT token invalid.") - return False - - def _login(self): - logger.debug(f"Access token: {self._access_token}") - - if self._access_token and self._is_valid(self._access_token): - return self._access_token - - # invalid access_token, fetch it again - client_email = self._credentials["email"] - client_password = self._credentials["password"] - - if client_email == None or client_password == None: - # If the credentials are not defined, an exception is raised. - raise Exception("DataService credentials not set") - - url = f"{self._host}/login" - headers = {"Content-Type": "application/json"} - json_body = dict(email=client_email, password=client_password) - - try: - response = requests.post(url, json=json_body, headers=headers) - response.raise_for_status() - if response.status_code == 200: - self._access_token = response.json()["access_token"] - logger.debug(f"Obtained new access token: {self._access_token}") - return self._access_token - else: - logger.error( - f"Unexpected error on login. Response status code: {response.status_code}, body: f{response.text}" - ) - except requests.exceptions.RequestException as e: - logger.exception(e) + return cls(host) def _fetch(self): - access_token = self._login() headers = { - "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", } url = f"{self._host}/data" diff --git a/caimira/tests/test_data_service.py b/caimira/tests/test_data_service.py index 5cd02f8e..c6c21343 100644 --- a/caimira/tests/test_data_service.py +++ b/caimira/tests/test_data_service.py @@ -1,78 +1,20 @@ -import time import unittest from unittest.mock import Mock, patch -import jwt - from caimira.store.data_service import DataService class DataServiceTests(unittest.TestCase): def setUp(self): # Set up any necessary test data or configurations - self.credentials = {"email": "test@example.com", "password": "password123"} - self.data_service = DataService.create(self.credentials, host="https://dataservice.example.com") - - def test_jwt_expiration(self): - is_valid = self.data_service._is_valid(None) - self.assertFalse(is_valid) - - now = time.time() - - encoded = jwt.encode({"exp": now - 10}, "very secret", algorithm="HS256") - is_valid = self.data_service._is_valid(encoded) - self.assertFalse(is_valid) - - encoded = jwt.encode({"exp": now}, "very secret", algorithm="HS256") - is_valid = self.data_service._is_valid(encoded) - self.assertFalse(is_valid) - - encoded = jwt.encode({"exp": now + 10}, "very secret", algorithm="HS256") - is_valid = self.data_service._is_valid(encoded) - self.assertTrue(is_valid) - - @patch("requests.post") - def test_login_successful(self, mock_post): - # Mock successful login response - mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = {"access_token": "dummy_token"} - mock_post.return_value = mock_response - - # Call the login method - access_token = self.data_service._login() - - # Assert that the access token is returned correctly - self.assertEqual(access_token, "dummy_token") - - # Verify that the fetch method was called with the expected arguments - mock_post.assert_called_once_with( - "https://dataservice.example.com/login", - json=dict(email="test@example.com", password="password123"), - headers={"Content-Type": "application/json"}, - ) - - @patch("requests.post") - def test_login_error(self, mock_post): - # Mock login error response - mock_post.return_value = Mock() - mock_post.return_value.status_code = 500 - - # Call the login method - access_token = self.data_service._login() - - # Assert that the login method returns None in case of an error - self.assertIsNone(access_token) + self.data_service = DataService.create(host="https://dataservice.example.com") @patch("requests.get") - @patch.object(DataService, "_login") - def test_fetch_successful(self, mock_login, mock_get): + def test_fetch_successful(self, mock_get): # Mock successful fetch response mock_get.return_value = Mock() mock_get.return_value.status_code = 200 mock_get.return_value.json.return_value = {"data": "dummy_data"} - # Call the fetch method with a mock access token - mock_login.return_value = "dummy_token" data = self.data_service._fetch() # Assert that the data is returned correctly @@ -82,20 +24,17 @@ class DataServiceTests(unittest.TestCase): mock_get.assert_called_once_with( "https://dataservice.example.com/data", headers={ - "Authorization": "Bearer dummy_token", "Content-Type": "application/json", }, ) @patch("requests.get") - @patch.object(DataService, "_login") - def test_fetch_error(self, mock_login, mock_get): + def test_fetch_error(self, mock_get): # Mock fetch error response mock_get.return_value = Mock() mock_get.return_value.status_code = 500 - # Call the fetch method with a mock access token - mock_login.return_value = "dummy_token" + # Call the fetch method data = self.data_service._fetch() # Assert that the fetch method returns None in case of an error