Skip to content

Commit

Permalink
Merge pull request #41 from cloudblue/LITE-25362_LITE-25285
Browse files Browse the repository at this point in the history
LITE-25362, LITE-25285 return list of variables instead of dict in se…
  • Loading branch information
ffaraone authored Oct 20, 2022
2 parents 2c6c8c9 + 54bf841 commit 46ea277
Show file tree
Hide file tree
Showing 19 changed files with 456 additions and 232 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install poetry
poetry install
poetry install --no-root
- name: Linting
run: |
poetry run flake8
Expand All @@ -48,7 +48,7 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install poetry
poetry install
poetry install --no-root
- name: Generate coverage report
run: |
poetry run pytest
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install poetry
poetry install
poetry install --no-root
- name: Linting
run: |
poetry run flake8
Expand Down
13 changes: 3 additions & 10 deletions connect/eaas/core/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from logzio.handler import LogzioHandler

from connect.eaas.core.utils import obfuscate_header


class ExtensionLogHandler(LogzioHandler):
def __init__(self, *args, **kwargs):
Expand All @@ -19,16 +21,7 @@ def __init__(self, logger):
self.logger = logger

def obfuscate(self, key, value):
if key in ('authorization', 'authentication'):
if value.startswith('ApiKey '):
return value.split(':')[0] + ':' + '*' * 10
else:
return '*' * 20
if key in ('cookie', 'set-cookie') and 'api_key="' in value:
start_idx = value.index('api_key="') + len('api_key="')
end_idx = value.index('"', start_idx)
return f'{value[0:start_idx + 2]}******{value[end_idx - 2:]}'
return value
return obfuscate_header(key, value)

def log_request(self, method, url, kwargs):
other_args = {k: v for k, v in kwargs.items() if k not in ('headers', 'json', 'params')}
Expand Down
39 changes: 33 additions & 6 deletions connect/eaas/core/proto.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,28 @@
from pydantic import BaseModel as PydanticBaseModel, Field
from pydantic.utils import DUNDER_ATTRIBUTES

from connect.eaas.core.utils import obfuscate_header


class BaseModel(PydanticBaseModel):

def get_sensitive_fields(self):
return [] # pragma: no cover

def __obfuscate_args__(self, k, v):
if isinstance(v, str) and v and k in self.get_sensitive_fields():
if k not in self.get_sensitive_fields() or not v:
return k, v

if hasattr(self, f'obfuscate_{k}'):
return getattr(self, f'obfuscate_{k}')(k, v)

if isinstance(v, str):
return k, f'{v[0:2]}******{v[-2:]}'

if isinstance(v, (list, dict)) and v and k in self.get_sensitive_fields():
if isinstance(v, (list, dict)):
return k, '******'

return k, v
return k, v # pragma: no cover

def __repr_args__(self):
return [
Expand Down Expand Up @@ -103,7 +111,7 @@ class EventDefinition(BaseModel):


class SetupResponse(BaseModel):
variables: Optional[dict]
variables: Optional[list]
# delete after stop using version 1
environment_type: Optional[str]
logging: Optional[Logging]
Expand All @@ -113,6 +121,19 @@ class SetupResponse(BaseModel):
def get_sensitive_fields(self):
return ['variables']

def obfuscate_variables(self, k, v):
return k, [
{
'name': item['name'],
'value': (
f'{item["value"][0:2]}******{item["value"][-2:]}'
if item['secure'] else item['value']
),
'secure': item['secure'],
}
for item in v
]


class Schedulable(BaseModel):
method: str
Expand Down Expand Up @@ -156,6 +177,9 @@ class HttpRequest(BaseModel):
def get_sensitive_fields(self):
return ['headers']

def obfuscate_headers(self, k, v):
return k, {key: obfuscate_header(key.lower(), value) for key, value in v.items()}


class WebTaskOptions(BaseModel):
correlation_id: str
Expand Down Expand Up @@ -218,7 +242,7 @@ def serialize(self, protocol_version=2):
return {
'message_type': MessageType.CONFIGURATION,
'data': {
'configuration': self.data.variables,
'configuration': {item['name']: item['value'] for item in self.data.variables},
'environment_type': self.data.environment_type,
'logging_api_key': self.data.logging.logging_api_key,
'log_level': self.data.logging.log_level,
Expand Down Expand Up @@ -286,7 +310,10 @@ def deserialize(cls, raw):
message_type=MessageType.SETUP_RESPONSE,
data=SetupResponse(
environment_type=raw_data.get('environment_type'),
variables=raw_data.get('configuration'),
variables=[
{'name': name, 'value': value, 'secure': False}
for name, value in raw_data.get('configuration', {}).items()
],
logging=Logging(
logging_api_key=raw_data.get('logging_api_key'),
log_level=raw_data.get('log_level'),
Expand Down
28 changes: 0 additions & 28 deletions connect/eaas/core/testing.py

This file was deleted.

1 change: 1 addition & 0 deletions connect/eaas/core/testing/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from connect.eaas.core.testing.testclient import WebAppTestClient # noqa
16 changes: 16 additions & 0 deletions connect/eaas/core/testing/fixtures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import pytest

from connect.eaas.core.testing import WebAppTestClient


@pytest.fixture
def test_client_factory():
"""
This fixture allows to instantiate a WebAppTestClient
given a webapp class.
"""

def _get_client(webapp):
return WebAppTestClient(webapp)

return _get_client
152 changes: 152 additions & 0 deletions connect/eaas/core/testing/testclient.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import inspect
import json
from urllib.parse import urlparse

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from starlette.routing import Match
from starlette.testclient import TestClient

from connect.client.testing import AsyncConnectClientMocker, ConnectClientMocker
from connect.eaas.core.inject.models import Context


class WebAppTestClient(TestClient):

def __init__(self, webapp):
self._webapp_class = webapp
self._app = self._get_application()

super().__init__(app=self._app, base_url='https://localhost/public/v1')

self.headers = {
'X-Connect-Api-Gateway-Url': self.base_url,
'X-Connect-User-Agent': 'eaas-test-client',
'X-Connect-Installation-Api-Key': 'ApiKey XXXX',
}

def request(
self,
method,
url,
params=None,
data=None,
headers=None,
cookies=None,
files=None,
auth=None,
timeout=None,
allow_redirects=True,
proxies=None,
hooks=None,
stream=None,
verify=None,
cert=None,
json=None,
context=None,
installation=None,
config=None,
log_level=None,
):
headers = self._populate_internal_headers(
headers or {},
context=context,
installation=installation,
config=config,
log_level=log_level,
)
mocker = self._get_client_mocker(method, url)
if installation and mocker:
with mocker(self.base_url) as mocker:
mocker.ns('devops').collection('installations').resource(
installation['id'],
).get(return_value=installation)
return super().request(
method,
url,
params=params,
data=data,
headers=headers,
cookies=cookies,
files=files,
auth=auth,
timeout=timeout,
allow_redirects=allow_redirects,
proxies=proxies,
hooks=hooks,
stream=stream,
verify=verify,
cert=cert,
json=json,
)
return super().request(
method,
url,
params=params,
data=data,
headers=headers,
cookies=cookies,
files=files,
auth=auth,
timeout=timeout,
allow_redirects=allow_redirects,
proxies=proxies,
hooks=hooks,
stream=stream,
verify=verify,
cert=cert,
json=json,
)

def _get_client_mocker(self, method, url):
path = urlparse(url).path
for route in self.app.router.routes:
match, child_scope = route.matches({'type': 'http', 'method': method, 'path': path})
if match == Match.FULL:
if inspect.iscoroutinefunction(child_scope['endpoint']):
return AsyncConnectClientMocker
return ConnectClientMocker

def _generate_call_context(self, installation):
return Context(**{
'installation_id': installation['id'] if installation else 'EIN-000',
'user_id': 'UR-000',
'account_id': 'VA-000',
'account_role': 'vendor',
'call_source': 'ui',
'call_type': 'user',
})

def _populate_internal_headers(
self,
headers,
config=None,
installation=None,
context=None,
log_level=None,
):
headers['X-Connect-Logging-Level'] = log_level or 'INFO'
if config:
headers['X-Connect-Config'] = json.dumps(config)

context: Context = context or self._generate_call_context(installation)
headers['X-Connect-Installation-id'] = context.installation_id
headers['X-Connect-User-Id'] = context.user_id
headers['X-Connect-Account-Id'] = context.account_id
headers['X-Connect-Account-Role'] = context.account_role
headers['X-Connect-Call-Source'] = context.call_source
headers['X-Connect-Call-Type'] = context.call_type
return headers

def _get_application(self):
app = FastAPI()

auth_router, no_auth_router = self._webapp_class.get_routers()
app.include_router(auth_router, prefix='/api')
app.include_router(no_auth_router, prefix='/guest')

static_root = self._webapp_class.get_static_root()
if static_root:
app.mount('/static', StaticFiles(directory=static_root), name='static')

return app
11 changes: 11 additions & 0 deletions connect/eaas/core/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
def obfuscate_header(key, value):
if key in ('authorization', 'authentication'):
if value.startswith('ApiKey '):
return value.split(':')[0] + ':' + '*' * 10
else:
return '*' * 20
if key in ('cookie', 'set-cookie') and 'api_key="' in value:
start_idx = value.index('api_key="') + len('api_key="')
end_idx = value.index('"', start_idx)
return f'{value[0:start_idx + 2]}******{value[end_idx - 2:]}'
return value
Loading

0 comments on commit 46ea277

Please sign in to comment.