Skip to content

Commit

Permalink
BI-4885: add basic RLS tests (#120)
Browse files Browse the repository at this point in the history
  • Loading branch information
MCPN authored Nov 24, 2023
1 parent d0f188c commit 218d3e5
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 15 deletions.
58 changes: 58 additions & 0 deletions lib/dl_api_lib/dl_api_lib_tests/db/control_api/test_rls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import pytest

from dl_api_lib_testing.rls import (
RLS_CONFIG_CASES,
config_to_comparable,
load_rls_config,
)
from dl_api_lib_tests.db.base import DefaultApiTestBase


class TestDataset(DefaultApiTestBase):
@staticmethod
def add_rls_to_dataset(control_api, dataset, rls_config):
field_guid = dataset.result_schema[0].id
dataset.rls = {field_guid: rls_config}
resp = control_api.save_dataset(dataset, fail_ok=True)
return field_guid, resp

@pytest.mark.parametrize("case", RLS_CONFIG_CASES, ids=[c["name"] for c in RLS_CONFIG_CASES])
def test_create_and_update_rls(self, control_api, saved_dataset, case):
config = case["config"]
ds = saved_dataset
field_guid, rls_resp = self.add_rls_to_dataset(control_api, ds, config)
assert rls_resp.status_code == 200, rls_resp.json

resp = control_api.load_dataset(ds)
assert resp.status_code == 200, resp.json
ds = resp.dataset
assert config_to_comparable(ds.rls[field_guid]) == config_to_comparable(case["config_to_compare"])

config_updated = case.get("config_updated")
if config_updated is None:
return
field_guid, rls_resp = self.add_rls_to_dataset(control_api, ds, config_updated)
assert rls_resp.status_code == 200, rls_resp.json

resp = control_api.load_dataset(ds)
assert resp.status_code == 200, resp.json
assert config_to_comparable(resp.dataset.rls[field_guid]) == config_to_comparable(config_updated)

def test_create_rls_for_nonexistent_user(self, control_api, saved_dataset):
config = load_rls_config("bad_login")
ds = saved_dataset
field_guid, rls_resp = self.add_rls_to_dataset(control_api, ds, config)
assert rls_resp.status_code == 200, rls_resp.json

resp = control_api.load_dataset(ds)
assert resp.status_code == 200, resp.json
assert "!FAILED_robot-user2" in resp.dataset.rls[field_guid]

def test_create_rls_from_invalid_config(self, control_api, saved_dataset):
config = load_rls_config("bad")
_, rls_resp = self.add_rls_to_dataset(control_api, saved_dataset, config)

assert rls_resp.status_code == 400
assert rls_resp.bi_status_code == "ERR.DS_API.RLS.PARSE"
assert rls_resp.json["message"] == "RLS: Parsing failed at line 2"
assert rls_resp.json["details"] == {"description": "Wrong format"}
52 changes: 41 additions & 11 deletions lib/dl_api_lib_testing/dl_api_lib_testing/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@
from dl_api_lib.app.control_api.app import EnvSetupResult as ControlApiEnvSetupResult
from dl_api_lib.app.data_api.app import DataApiAppFactory
from dl_api_lib.app.data_api.app import EnvSetupResult as DataApiEnvSetupResult
from dl_api_lib.app_common import (
SRFactoryBuilder,
StandaloneServiceRegistryFactory,
)
from dl_api_lib.app_common import SRFactoryBuilder
from dl_api_lib.app_common_settings import ConnOptionsMutatorsFactory
from dl_api_lib.app_settings import (
AppSettings,
Expand All @@ -27,26 +24,33 @@
from dl_api_lib.connector_availability.base import ConnectorAvailabilityConfig
from dl_api_lib_testing.configuration import ApiTestEnvironmentConfiguration
from dl_configs.connectors_settings import ConnectorSettingsBase
from dl_configs.enums import (
RedisMode,
RequiredService,
)
from dl_configs.enums import RequiredService
from dl_configs.rqe import (
RQEBaseURL,
RQEConfig,
)
from dl_configs.settings_submodels import RedisSettings
from dl_constants.enums import (
ConnectionType,
RLSSubjectType,
USAuthMode,
)
from dl_core.aio.middlewares.services_registry import services_registry_middleware
from dl_core.aio.middlewares.us_manager import service_us_manager_middleware
from dl_core.data_processing.cache.primitives import CacheTTLConfig
from dl_core.rls import (
RLS_FAILED_USER_NAME_PREFIX,
BaseSubjectResolver,
RLSSubject,
)
from dl_core.services_registry import ServicesRegistry
from dl_core.services_registry.entity_checker import EntityUsageChecker
from dl_core.services_registry.env_manager_factory_base import EnvManagerFactory
from dl_core.services_registry.inst_specific_sr import InstallationSpecificServiceRegistryFactory
from dl_core.services_registry.inst_specific_sr import (
InstallationSpecificServiceRegistry,
InstallationSpecificServiceRegistryFactory,
)
from dl_core.services_registry.rqe_caches import RQECachesSetting
from dl_core.utils import FutureRef
from dl_core_testing.app_test_workarounds import TestEnvManagerFactory
from dl_core_testing.fixture_server_runner import WSGIRunner

Expand Down Expand Up @@ -98,6 +102,32 @@ def rqe_config_subprocess_cm(self) -> Generator[RQEConfig, None, None]:
)


@attr.s
class TestingSubjectResolver(BaseSubjectResolver):
def get_subjects_by_names(self, names: list[str]) -> list[RLSSubject]:
"""Mock resolver. Considers a user real if his name starts with 'user'"""
return [
RLSSubject(
subject_id="",
subject_type=RLSSubjectType.user if name.startswith("user") else RLSSubjectType.notfound,
subject_name=name if name.startswith("user") else RLS_FAILED_USER_NAME_PREFIX + name,
)
for name in names
]


@attr.s
class TestingServiceRegistry(InstallationSpecificServiceRegistry):
async def get_subject_resolver(self) -> BaseSubjectResolver:
return TestingSubjectResolver()


@attr.s
class TestingServiceRegistryFactory(InstallationSpecificServiceRegistryFactory):
def get_inst_specific_sr(self, sr_ref: FutureRef[ServicesRegistry]) -> TestingServiceRegistry:
return TestingServiceRegistry(service_registry_ref=sr_ref)


class TestingSRFactoryBuilder(SRFactoryBuilder[AppSettings]):
def _get_required_services(self, settings: AppSettings) -> set[RequiredService]:
return {RequiredService.RQE_INT_SYNC, RequiredService.RQE_EXT_SYNC}
Expand All @@ -109,7 +139,7 @@ def _get_inst_specific_sr_factory(
self,
settings: AppSettings,
) -> Optional[InstallationSpecificServiceRegistryFactory]:
return StandaloneServiceRegistryFactory()
return TestingServiceRegistryFactory()

def _get_entity_usage_checker(self, settings: AppSettings) -> Optional[EntityUsageChecker]:
return None
Expand Down
4 changes: 4 additions & 0 deletions lib/dl_api_lib_testing/dl_api_lib_testing/rls.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ def load_rls(name: str) -> list[RLSEntry]:
MAIN_TEST_CASE = RLS_CONFIG_CASES[0]


def config_to_comparable(conf: str):
return set((line.split(": ")[0], ",".join(sorted(line.split(": ")[1]))) for line in conf.strip().split("\n"))


def check_text_config_to_rls_entries(case: dict, subject_resolver: BaseSubjectResolver) -> None:
field_guid, config, expected_rls_entries = case["field_guid"], case["config"], case["rls_entries"]
entries = FieldRLSSerializer.from_text_config(config, field_guid, subject_resolver=subject_resolver)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
'Москва': user2, user1
'Самара': user3, !FAILED_someuser, user1
'Омск': user5, pg
'Омск': user5, user7
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"subject": {
"subject_id": "1120000000000251",
"subject_type": "user",
"subject_name": "pg"
"subject_name": "user7"
}
}
]
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
'Москва': user2
'Самара': user1, user3
'Омск': user5, pg
'Омск': user5, user7
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"subject": {
"subject_id": "1120000000000251",
"subject_type": "user",
"subject_name": "pg"
"subject_name": "user7"
}
}
]

0 comments on commit 218d3e5

Please sign in to comment.