Skip to content

Commit

Permalink
fix(crb): add right check in crb #2401 (#297)
Browse files Browse the repository at this point in the history
  • Loading branch information
npuchois-aphp authored Dec 8, 2023
1 parent d171d5e commit d87310a
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .conf/.env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
SERVER_VERSION=prod
DEBUG=1
ADMINS=

META_SECURITY_PSEUDED=meta.security=PSEUDED
################## ENVIRONMENT #############################################################
INCLUDED_APPS=accesses,cohort,workspaces,exports

Expand Down
2 changes: 1 addition & 1 deletion .conf/.test.env
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ OIDC_CLIENT_ID=
OIDC_CLIENT_SECRET=
OIDC_GRANT_TYPE=
OIDC_REDIRECT_URI=

META_SECURITY_PSEUDED=meta.security=PSEUDED
################## ACCESSES #################################################################
ACCESS_EXPIRY_FIRST_ALERT_IN_DAYS=30
ACCESS_EXPIRY_SECOND_ALERT_IN_DAYS=2
Expand Down
5 changes: 1 addition & 4 deletions accesses/tools/perimeter_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ def get_perimeters_filtered_by_search(cohort_ids, owner_id, default_perimeters):
return default_perimeters


def get_read_nominative_boolean_from_specific_logic_function(request, filter_queryset,
def get_read_nominative_boolean_from_specific_logic_function(perimeters_filtered_by_search, filter_queryset,
all_read_patient_nominative_accesses,
all_read_patient_pseudo_accesses,
right_perimeter_compute_function) -> bool:
Expand All @@ -215,9 +215,6 @@ def get_read_nominative_boolean_from_specific_logic_function(request, filter_que
The right_perimeter_compute_function can be used to find right for all cohorts in "is-read-patient-pseudo" or
at least on one perimeter in "is-one-read-patient-right"
"""

perimeters_filtered_by_search = get_perimeters_filtered_by_search(request.query_params.get("cohort_id"),
request.user, filter_queryset)
if not perimeters_filtered_by_search:
raise Http404("ERROR No Perimeters Found")
return right_perimeter_compute_function(perimeters_filtered_by_search,
Expand Down
30 changes: 17 additions & 13 deletions accesses/views/perimeter.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
get_top_perimeter_from_read_patient_accesses, is_pseudo_perimeter_in_top_perimeter, \
has_at_least_one_read_nominative_right, \
get_read_nominative_boolean_from_specific_logic_function, get_all_read_patient_accesses, \
get_read_opposing_patient_accesses
get_read_opposing_patient_accesses, get_perimeters_filtered_by_search


class PerimeterFilter(filters.FilterSet):
Expand Down Expand Up @@ -147,12 +147,14 @@ def get_read_patient_pseudo_right(self, request, *args, **kwargs):
request.user)
is_opposing_patient_read = get_read_opposing_patient_accesses(request.user)
if request.query_params:
is_read_patient_nominative = get_read_nominative_boolean_from_specific_logic_function(request,
self.filter_queryset(
self.get_queryset()),
all_read_patient_nominative_accesses,
all_read_patient_pseudo_accesses,
get_read_patient_right)
perimeters_filtered_by_search = get_perimeters_filtered_by_search(request.query_params.get("cohort_id"),
request.user,
self.filter_queryset(self.get_queryset()))
is_read_patient_nominative = get_read_nominative_boolean_from_specific_logic_function(
perimeters_filtered_by_search,
all_read_patient_nominative_accesses,
all_read_patient_pseudo_accesses,
get_read_patient_right)
is_read_patient_pseudo = not is_read_patient_nominative
else:
is_read_patient_pseudo = is_pseudo_perimeter_in_top_perimeter(all_read_patient_nominative_accesses,
Expand All @@ -172,12 +174,14 @@ def get_read_one_nominative_patient_right_access(self, request, *args, **kwargs)
request.user)
is_opposing_patient_read = get_read_opposing_patient_accesses(request.user)
if request.query_params:
is_read_patient_nominative = get_read_nominative_boolean_from_specific_logic_function(request,
self.filter_queryset(
self.get_queryset()),
all_read_patient_nominative_accesses,
all_read_patient_pseudo_accesses,
has_at_least_one_read_nominative_right)
perimeters_filtered_by_search = get_perimeters_filtered_by_search(request.query_params.get("cohort_id"),
request.user,
self.filter_queryset(self.get_queryset()))
is_read_patient_nominative = get_read_nominative_boolean_from_specific_logic_function(
perimeters_filtered_by_search,
all_read_patient_nominative_accesses,
all_read_patient_pseudo_accesses,
has_at_least_one_read_nominative_right)
return Response(data={"is_one_read_nominative_patient_right": is_read_patient_nominative,
"is_opposing_patient_read": is_opposing_patient_read},
status=status.HTTP_200_OK)
Expand Down
20 changes: 20 additions & 0 deletions admin_cohort/auth/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,26 @@ def get_token_issuer(token: str) -> str:
return issuer


def get_user_from_token(token: str, auth_method: str) -> Union[None, User]:
if auth_method == JWT_AUTH_MODE:
try:
decoded = jwt.decode(token, key=JWT_SIGNING_KEY, algorithms=JWT_ALGORITHMS, leeway=15)
return User.objects.get(pk=decoded["username"])

except User.DoesNotExist as e:
raise ServerError(f"Error verifying token. User not found - {e}")
elif auth_method == OIDC_AUTH_MODE:
try:
issuer = get_token_issuer(token=token)
decoded = decode_oidc_token(token=token, issuer=issuer)
return User.objects.get(pk=decoded["preferred_username"])
except Exception as e:
_logger.info(f"Error decoding token: {e} - `{token}`")
raise e
else:
raise ValueError(f"Invalid authentication method : {auth_method}")


def get_userinfo_from_token(token: str, auth_method: str) -> Union[None, UserInfo]:
if token == env("ETL_TOKEN"):
_logger.info("ETL token connexion")
Expand Down
20 changes: 18 additions & 2 deletions cohort/crb/cohort_requests/abstract_cohort_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING

from admin_cohort.auth.utils import get_userinfo_from_token
from accesses.tools.perimeter_process import get_all_read_patient_accesses, \
get_read_nominative_boolean_from_specific_logic_function, get_read_patient_right
from admin_cohort.auth.utils import get_userinfo_from_token, get_user_from_token
from cohort.crb.enums import Mode
from cohort.crb.exceptions import FhirException
from cohort.crb.query_formatter import QueryFormatter
Expand All @@ -14,6 +16,17 @@
from cohort.crb.schemas import CohortQuery


def is_cohort_request_pseudo_read(auth_headers: dict, source_population: list) -> bool:
user = get_user_from_token(auth_headers['Authorization'].replace('Bearer ', ''),
auth_headers['authorizationMethod'])
all_read_patient_nominative_accesses, all_read_patient_pseudo_accesses = get_all_read_patient_accesses(
user)
return not get_read_nominative_boolean_from_specific_logic_function(source_population,
all_read_patient_nominative_accesses,
all_read_patient_pseudo_accesses,
get_read_patient_right)


class AbstractCohortRequest(ABC):
def __init__(self, mode: Mode, sjs_client: SjsClient, auth_headers: dict):
self.mode = mode
Expand All @@ -33,7 +46,10 @@ def create_request_for_sjs(self, cohort_query: CohortQuery) -> str:
if cohort_query is None:
raise FhirException("No query received to format.")

sjs_request = QueryFormatter(self.auth_headers).format_to_fhir(cohort_query)
is_pseudo = is_cohort_request_pseudo_read(self.auth_headers,
cohort_query.source_population.care_site_cohort_list)

sjs_request = QueryFormatter(self.auth_headers).format_to_fhir(cohort_query, is_pseudo)
cohort_query.criteria = sjs_request

spark_job_request = SparkJobObject(
Expand Down
13 changes: 10 additions & 3 deletions cohort/crb/query_formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
if TYPE_CHECKING:
from cohort.crb.schemas import CohortQuery, Criteria, SourcePopulation

FHIR_URL = os.environ.get("FHIR_URL")
env = os.environ
FHIR_URL = env.get("FHIR_URL")
META_SECURITY_PSEUDED = env.get("META_SECURITY_PSEUDED")

_logger = logging.getLogger("info")

Expand All @@ -36,20 +38,25 @@ def query_fhir(resource: str, params: dict[str, list[str]], auth_headers: dict)
return FhirParameters(**result)


def add_security_params_to_filter_fhir(sub_criteria: Criteria, source_population: SourcePopulation, is_pseudo: bool):
filter_fhir_enriched = sub_criteria.add_criteria(source_population)
return META_SECURITY_PSEUDED + "&" + filter_fhir_enriched if is_pseudo else filter_fhir_enriched


class QueryFormatter:
IDENTIFIER_VALUE = "identifier.value"

def __init__(self, auth_headers: dict):
self.auth_headers = auth_headers

def format_to_fhir(self, cohort_query: CohortQuery) -> Criteria | None:
def format_to_fhir(self, cohort_query: CohortQuery, is_pseudo: bool) -> Criteria | None:
def build_solr_criteria(criteria: Criteria, source_population: SourcePopulation) -> Criteria | None:
if criteria is None:
return None

for sub_criteria in criteria.criteria:
if sub_criteria.criteria_type == CriteriaType.BASIC_RESOURCE:
filter_fhir_enriched = sub_criteria.add_criteria(source_population)
filter_fhir_enriched = add_security_params_to_filter_fhir(sub_criteria, source_population, is_pseudo)

_logger.info(f"filterFhirEnriched {filter_fhir_enriched}")

Expand Down
14 changes: 12 additions & 2 deletions cohort/tests/test_crb.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,17 @@ def load_query(filename: str) -> CohortQuery:
@mock.patch("cohort.crb.query_formatter.query_fhir")
def test_format_to_fhir_simple_query(self, query_fhir):
query_fhir.return_value = self.mocked_query_fhir_result
res = self.query_formatter.format_to_fhir(self.cohort_query_simple)
res = self.query_formatter.format_to_fhir(self.cohort_query_simple, False)
self.assertEquals(1, len(res.criteria))
res_criteria = res.criteria[0]
self.assertEquals(ResourceType.PATIENT, res_criteria.resource_type)
self.assertEquals(self.fq_value_string, res_criteria.filter_solr, )
self.assertEquals("docstatus=final&type:not=doc-impor&empty=false&patient-active=true&_text=ok",
res_criteria.filter_fhir)
@mock.patch("cohort.crb.query_formatter.query_fhir")
def test_format_to_fhir_simple_query_pseudo(self, query_fhir):
query_fhir.return_value = self.mocked_query_fhir_result
res = self.query_formatter.format_to_fhir(self.cohort_query_simple, True)
self.assertEquals(1, len(res.criteria))
res_criteria = res.criteria[0]
self.assertEquals(ResourceType.PATIENT, res_criteria.resource_type)
Expand All @@ -72,7 +82,7 @@ def test_format_to_fhir_simple_query(self, query_fhir):
@mock.patch("cohort.crb.query_formatter.query_fhir")
def test_format_to_fhir_complex_query(self, query_fhir):
query_fhir.return_value = self.mocked_query_fhir_result
res = self.query_formatter.format_to_fhir(self.cohort_query_complex)
res = self.query_formatter.format_to_fhir(self.cohort_query_complex, False)
self.assertEquals(6, len(res.criteria))
res_criteria = res.criteria[1]
self.assertEquals(ResourceType.PATIENT, res_criteria.resource_type)
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ packaging==22.0
prompt-toolkit==3.0.36
psycopg2-binary==2.9.5
pycparser==2.21
pydantic==2.3.0
pydantic==2.4.0
PyJWT==2.6.0
pyspnego==0.7.0
python-json-logger==2.0.7
Expand Down

0 comments on commit d87310a

Please sign in to comment.