Skip to content

Commit

Permalink
resolved conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
sniedzielski committed Aug 26, 2024
2 parents f4dcf88 + ef6848f commit ddbaa65
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 52 deletions.
21 changes: 20 additions & 1 deletion msystems/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
# URL to be redirected to after successful login
"mpass_login_redirect": "",

"mpass_key_first_name": "FirstName",
"mpass_key_last_name": "LastName",
"mpass_key_dob": "BirthDate",
"mpass_key_roles": "Role",
"mpass_key_legal_entities": "OrganizationAdministrator",
# "mpass_key_legal_entities": "AdministeredLegalEntity",

# Mpass configurations
"mpass_config": {
# Strict mode: SAML responses must be validated strictly.
Expand Down Expand Up @@ -89,7 +96,13 @@
# The same as mpass private key
"service_private_key": "",
# Mconnect certificate, PEM string format
"mconnect_certificate": ""
"mconnect_certificate": "",

# Get Person Soap Header default values
"get_person_calling_user": "", # len 13
"get_person_calling_entity": "", # len 13
"get_person_call_basis": "", # max len 256
"get_person_call_reason": "", # max len 512
},
"voucher_config": {
# The same as mpass cert
Expand All @@ -106,6 +119,12 @@ class MsystemsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "msystems"

mpass_key_first_name = None
mpass_key_last_name = None
mpass_dob = None
mpass_key_roles = None
mpass_key_legal_entities = None

# DO NOT CHANGE THIS ####
ADMIN = "Admin"
INSPECTOR = "Inspector"
Expand Down
68 changes: 29 additions & 39 deletions msystems/client/mconnect.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,13 @@
from zeep import Client, Plugin, Settings
from zeep.exceptions import SignatureVerificationFailed
import logging

from msystems.apps import MsystemsConfig
from msystems.xml_utils import add_signature, verify_signature, add_timestamp, verify_timestamp


class MconnectClientError(Exception):
pass


class SoapWssePlugin(Plugin):
def __init__(self, service_private_key, service_certificate, mconnect_certificate):
self.service_certificate = service_certificate
self.service_private_key = service_private_key
self.mconnect_certificate = mconnect_certificate

def egress(self, envelope, http_headers, operation, binding_options):
root = envelope

add_timestamp(root)
add_signature(root, self.service_private_key, self.service_certificate)

return envelope, http_headers
from zeep import Client, Settings

def ingress(self, envelope, http_headers, operation):
try:
verify_timestamp(envelope)
except ValueError as e:
raise MconnectClientError(str(e))

try:
verify_signature(envelope, self.mconnect_certificate)
except SignatureVerificationFailed:
raise MconnectClientError("Envelope signature verification failed")
from core.models import User
from msystems.apps import MsystemsConfig
from msystems.client.utils import SoapWssePlugin, SoapClientError
from policyholder.models import PolicyHolder

return envelope, http_headers
logger = logging.getLogger(__name__)


class MconnectClient:
Expand All @@ -43,13 +16,30 @@ def __init__(self):
self.url = MsystemsConfig.mconnect_config['url']

settings = Settings(strict=False, raw_response=True)
self.client = Client(self.url, settings,
self.client = Client(wsdl=self.url,
settings=settings,
plugins=[SoapWssePlugin(MsystemsConfig.mconnect_config['service_private_key'],
MsystemsConfig.mconnect_config['service_certificate'],
MsystemsConfig.mconnect_config['mconnect_certificate'])])

def get_person(self, idpn):
service_handle = self.client.service.get('GetPerson')
def get_person(self, idnp: str, user: User = None, economic_unit: PolicyHolder = None):
service_handle = self.client.service['GetPerson']
if not service_handle:
raise MconnectClientError("Service GetPerson not found")
return service_handle(IDPN=idpn)
raise SoapClientError("Service GetPerson not found")

# Bounds for headers and idnp from Mconnect documentation, should not be exceeded in normal operation
# Added for extra protection

headers = {
"CallingUser": user.username[:13] or MsystemsConfig.mconnect_config['get_person_calling_user'][:13],
"CallingEntity": economic_unit.trade_name[:13]
or MsystemsConfig.mconnect_config['get_person_calling_entity'][:13],
"CallBasis": MsystemsConfig.mconnect_config['get_person_call_basis'][:256],
"CallReason": MsystemsConfig.mconnect_config['get_person_call_reason'][:512]
}

try:
return service_handle(IDNP=idnp[:13], _soapheaders=headers)
except Exception as e:
logger.error("Error during Mconnect request", exc_info=e)
raise SoapClientError(str(e))
36 changes: 36 additions & 0 deletions msystems/client/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from zeep import Plugin
from zeep.exceptions import SignatureVerificationFailed

from msystems.xml_utils import add_timestamp, add_signature, verify_timestamp, verify_signature


class SoapClientError(Exception):
pass


class SoapWssePlugin(Plugin):
def __init__(self, service_private_key, service_certificate, mconnect_certificate):
self.service_certificate = service_certificate
self.service_private_key = service_private_key
self.mconnect_certificate = mconnect_certificate

def egress(self, envelope, http_headers, operation, binding_options):
root = envelope

add_timestamp(root)
add_signature(root, self.service_private_key, self.service_certificate)

return envelope, http_headers

def ingress(self, envelope, http_headers, operation):
try:
verify_timestamp(envelope)
except ValueError as e:
raise SoapClientError(str(e))

try:
verify_signature(envelope, self.mconnect_certificate)
except SignatureVerificationFailed:
raise SoapClientError("Envelope signature verification failed")

return envelope, http_headers
23 changes: 23 additions & 0 deletions msystems/migrations/0007_add_languages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from django.db import migrations

language_code_ro = "ro"
language_code_ru = "ru"


def on_migration(apps, schema_editor):
language_model = apps.get_model("core", "language")
if not language_model.objects.filter(code=language_code_ro).exists():
language_model(code=language_code_ro, name="Română").save()
if not language_model.objects.filter(code=language_code_ru).exists():
language_model(code=language_code_ru, name="Русский").save()


class Migration(migrations.Migration):
dependencies = [
('msystems', '0006_add_bill_query_rights'),
('core', '0001_initial'),
]

operations = [
migrations.RunPython(on_migration, migrations.RunPython.noop),
]
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
logger = logging.getLogger(__name__)


class SamlUserService:
class MpassUserService:
location = None

def __init__(self):
Expand Down Expand Up @@ -51,8 +51,8 @@ def _get_or_create_user(self, username: str, user_data: dict):
def _create_user(self, username: str, user_data: dict) -> User:
i_user = InteractiveUser(
login_name=username,
other_names=user_data.get('FirstName')[0],
last_name=user_data.get('LastName')[0],
other_names=user_data.get(MsystemsConfig.mpass_key_first_name)[0],
last_name=user_data.get(MsystemsConfig.mpass_key_last_name)[0],
language_id=MsystemsConfig.default_mpass_language,
audit_user_id=0,
is_associated=False,
Expand All @@ -70,28 +70,28 @@ def _create_user(self, username: str, user_data: dict) -> User:
return core_user

def _update_user(self, user: User, user_data: dict) -> None:
data_first_name = user_data.get('FirstName')[0]
data_last_name = user_data.get('LastName')[0]
data_first_name = user_data.get(MsystemsConfig.mpass_key_first_name)[0]
data_last_name = user_data.get(MsystemsConfig.mpass_key_last_name)[0]

# Update first and last name if they are different
if user.i_user.other_names != data_first_name or user.i_user.last_name != data_last_name:
self._update_user_name(user.i_user, data_first_name, data_last_name)

def _update_user_legal_entities(self, user: User, user_data: dict) -> None:
legal_entities = self._parse_legal_entities(user_data.get('AdministeredLegalEntity', []))
legal_entities = self._parse_legal_entities(user_data.get(MsystemsConfig.mpass_key_legal_entities, []))
policyholders = [self._get_or_create_policy_holder(user, line[1], line[0]) for line in legal_entities]

self._delete_old_user_policyholders(user, policyholders)
self._add_new_user_policyholders(user, policyholders)

def _update_user_roles(self, user, user_data):
msystem_roles_list = user_data.get('Role', [MsystemsConfig.EMPLOYER])
mpass_roles_list = user_data.get(MsystemsConfig.mpass_key_roles, [MsystemsConfig.EMPLOYER])

for role in msystem_roles_list:
for role in mpass_roles_list:
self._validate_incoming_roles(role)

self._delete_old_user_roles(user, msystem_roles_list)
self._add_new_user_roles(user, msystem_roles_list)
self._delete_old_user_roles(user, mpass_roles_list)
self._add_new_user_roles(user, mpass_roles_list)

def _update_user_name(self, i_user, first_name, last_name):
i_user.save_history()
Expand Down
4 changes: 2 additions & 2 deletions msystems/views/mpass.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.clickjacking import xframe_options_exempt
from msystems.apps import MsystemsConfig
from msystems.services.saml_user_service import SamlUserService
from msystems.services.mpass_user_service import MpassUserService
from onelogin.saml2.auth import OneLogin_Saml2_Auth, OneLogin_Saml2_Settings
from graphql_jwt.decorators import jwt_cookie
from graphql_jwt.shortcuts import get_token, create_refresh_token
Expand Down Expand Up @@ -83,7 +83,7 @@ def _handle_acs_login(request):
username = auth.get_nameid()
user_data = auth.get_attributes()

user = SamlUserService().login(username=username, user_data=user_data)
user = MpassUserService().login(username=username, user_data=user_data)

# Tokens to be set in cookies
request.jwt_token = get_token(user)
Expand Down
1 change: 1 addition & 0 deletions msystems/xml_utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import re
import datetime as py_datetime

from zeep.wsse.signature import _make_sign_key, _sign_envelope_with_key, _make_verify_key, _verify_envelope_with_key
from lxml import etree

Expand Down

0 comments on commit ddbaa65

Please sign in to comment.