Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jimfuqian/BB2-3276-auth-flow-events-adding-language-field #1215

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ for more details.
The information below outlines setting up the server for development or your own environment.
For general information on deploying Django see https://docs.djangoproject.com/en/1.11/howto/deployment/.

NOTE: Internal software engineers or other interested parties should follow the documentation for running a Dockerized local development enviornment. For more information see https://github.com/CMSgov/bluebutton-web-server/blob/master/docker-compose/readme.md.
NOTE: Internal software engineers or other interested parties should follow the documentation for running a Docker compose based local development enviornment. For more information see https://github.com/CMSgov/bluebutton-web-server/blob/master/docker-compose/readme.md.

Setup
-----
Expand Down
8 changes: 7 additions & 1 deletion apps/dot_ext/loggers.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def create_session_auth_flow_trace(request):

client_id_param = request.GET.get("client_id", None)
auth_pkce_method = request.GET.get("code_challenge_method", None)
auth_language = request.GET.get("lang", None)

if client_id_param:
try:
Expand All @@ -86,6 +87,7 @@ def create_session_auth_flow_trace(request):
"auth_require_demographic_scopes": str(application.require_demographic_scopes),
"auth_client_id": application.client_id,
"auth_pkce_method": auth_pkce_method,
"auth_language": auth_language,
}
set_session_auth_flow_trace(request, auth_flow_dict)

Expand All @@ -94,7 +96,8 @@ def create_session_auth_flow_trace(request):
with transaction.atomic():
AuthFlowUuid.objects.create(auth_uuid=new_auth_uuid,
client_id=application.client_id,
auth_pkce_method=auth_pkce_method)
auth_pkce_method=auth_pkce_method,
auth_language=auth_language)
except IntegrityError:
pass
except Application.DoesNotExist:
Expand All @@ -106,6 +109,7 @@ def create_session_auth_flow_trace(request):
"auth_require_demographic_scopes": "",
"auth_client_id": "",
"auth_pkce_method": "",
"auth_language": "",
}
set_session_auth_flow_trace(request, auth_flow_dict)

Expand Down Expand Up @@ -160,6 +164,8 @@ def set_session_values_from_auth_flow_uuid(request, auth_flow_uuid):
request.session['auth_crosswalk_action'] = auth_flow_uuid.auth_crosswalk_action
if auth_flow_uuid.auth_share_demographic_scopes is not None:
request.session['auth_share_demographic_scopes'] = str(auth_flow_uuid.auth_share_demographic_scopes)
if auth_flow_uuid.auth_language is not None:
request.session['auth_language'] = auth_flow_uuid.auth_language

try:
application = Application.objects.get(client_id=auth_flow_uuid.client_id)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 4.2.11 on 2024-07-07 22:39

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('dot_ext', '0007_merge_20231020_2004'),
]

operations = [
migrations.AddField(
model_name='authflowuuid',
name='auth_language',
field=models.CharField(max_length=12, null=True),
),
migrations.AddField(
model_name='authflowuuidcopy',
name='auth_language',
field=models.CharField(max_length=12, null=True),
),
migrations.AlterField(
model_name='application',
name='data_access_type',
field=models.CharField(choices=[('ONE_TIME', 'ONE_TIME - No refresh token needed.'), ('RESEARCH_STUDY', 'RESEARCH_STUDY - No expiration.'), ('THIRTEEN_MONTH', 'THIRTEEN_MONTH - Access expires in 13-months.')], default='THIRTEEN_MONTH', max_length=16, null=True, verbose_name='Data Access Type:'),
),
]
2 changes: 2 additions & 0 deletions apps/dot_ext/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ class AuthFlowUuid(models.Model):
created = models.DateTimeField(auto_now_add=True, null=True)
auth_crosswalk_action = models.CharField(max_length=1, null=True)
auth_share_demographic_scopes = models.BooleanField(null=True)
auth_language = models.CharField(max_length=12, null=True)

def __str__(self):
return str(self.auth_uuid)
Expand Down Expand Up @@ -463,6 +464,7 @@ class AuthFlowUuidCopy(models.Model):
created = models.DateTimeField(null=True)
auth_crosswalk_action = models.CharField(max_length=1, null=True)
auth_share_demographic_scopes = models.BooleanField(null=True)
auth_language = models.CharField(max_length=12, null=True)

def __str__(self):
return str(self.auth_uuid)
Expand Down
4 changes: 4 additions & 0 deletions apps/fhir/server/loggers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import apps.logging.request_logger as logging

from apps.logging.utils import lookup_language

"""
Logger functions for fhir/server module
Expand All @@ -13,6 +14,8 @@ def log_match_fhir_id(request, fhir_id, mbi_hash, hicn_hash,
used in match_fhir_id()
'''
match_fhir_id_logger = logging.getLogger(logging.AUDIT_AUTHN_MATCH_FHIR_ID_LOGGER, request)
# splunk dashboard auth flow baseSearch4
lang = lookup_language(request)
match_fhir_id_logger.info({
"type": "fhir.server.authentication.match_fhir_id",
"fhir_id": fhir_id,
Expand All @@ -21,4 +24,5 @@ def log_match_fhir_id(request, fhir_id, mbi_hash, hicn_hash,
"match_found": match_found,
"hash_lookup_type": hash_lookup_type,
"hash_lookup_mesg": hash_lookup_mesg,
"auth_language": lang,
})
8 changes: 6 additions & 2 deletions apps/logging/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import hashlib
from apps.logging.utils import lookup_language


class DataAccessGrantSerializer:
Expand Down Expand Up @@ -39,9 +40,10 @@ class Token:
tkn = None
action = None

def __init__(self, obj, action=None):
def __init__(self, obj, action=None, request=None):
self.tkn = obj
self.action = action
self.request = request

def to_dict(self):
# seems like this should be a serializer
Expand All @@ -56,7 +58,8 @@ def to_dict(self):
scopes = " ".join(scopes_dict.keys())
else:
scopes = ""

# splunk dashboard auth flow dashboard baseSearch12
lang = lookup_language(self.request)
result = {
"type": "AccessToken",
"action": self.action,
Expand Down Expand Up @@ -84,6 +87,7 @@ def to_dict(self):
"fhir_id": getattr(crosswalk, "fhir_id", None),
"user_id_type": getattr(crosswalk, "user_id_type", None),
},
"auth_language": lang,
}

return result
Expand Down
8 changes: 6 additions & 2 deletions apps/logging/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@
FHIRResponseForAuth,
)

from apps.logging.utils import lookup_language


@receiver(app_authorized)
def handle_token_created(sender, request, token, **kwargs):
# Get auth flow dict from session for logging
token_logger = logging.getLogger(logging.AUDIT_AUTHZ_TOKEN_LOGGER, request)
token_logger.info(Token(token, action="authorized").to_dict())
token_logger.info(Token(token, action="authorized", request=request).to_dict())


@receiver(beneficiary_authorized_application)
Expand Down Expand Up @@ -62,7 +64,8 @@ def handle_app_authorized(sender, request, auth_status, auth_status_code, user,
# TODO consider logging exception name here
# once we get the generic logger hooked up
pass

# splunk dashboard auth flow baseSearch11
lang = lookup_language(request)
log_dict = {
"type": "Authorization",
"auth_status": auth_status,
Expand All @@ -83,6 +86,7 @@ def handle_app_authorized(sender, request, auth_status, auth_status_code, user,
"access_token_delete_cnt": access_token_delete_cnt,
"refresh_token_delete_cnt": access_token_delete_cnt,
"data_access_grant_delete_cnt": data_access_grant_delete_cnt,
"auth_language": lang,
}

token_logger.info(log_dict)
Expand Down
15 changes: 15 additions & 0 deletions apps/logging/utils.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
import io

import apps.logging.request_logger as logging
from apps.dot_ext.loggers import (
get_session_auth_flow_trace,
)

"""
Utility functions for logging, and logging manipulations (used in tests)
"""


def lookup_language(request):
# keep lang code from session if presents
# otherwise grab it from parameters
if request is not None:
auth_dict = get_session_auth_flow_trace(request)
qparam_lang = request.GET.get('lang', request.GET.get('Lang', ""))
qparam_lang = qparam_lang if qparam_lang else request.POST.get('lang', request.POST.get('Lang', ""))
return auth_dict.get('auth_language', qparam_lang)
else:
return ""


def format_timestamp(dt):
"""
Returns an ISO 6801 format string in UTC that works well with AWS Glue/Athena
Expand Down
12 changes: 10 additions & 2 deletions apps/mymedicare_cb/authorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from .signals import response_hook_wrapper
from .validators import is_mbi_format_valid, is_mbi_format_synthetic
from apps.logging.utils import lookup_language


MSG_SLS_RESP_MISSING_AUTHTOKEN = "Exchange auth_token is missing in response error"
Expand Down Expand Up @@ -365,7 +366,8 @@ def validate_asserts(self, request, asserts, err_enum):
# asserts is a list of tuple : (boolean expression, err message)
# iterate boolean expressions and log err message if the expression evalaute to true
logger = logging.getLogger(logging.AUDIT_AUTHN_SLS_LOGGER, request)

# splunk dashboard auth flow dashboard baseSearch3
lang = lookup_language(request)
log_dict = {
"type": "Authentication:start",
"sls_status": "FAIL",
Expand All @@ -380,6 +382,7 @@ def validate_asserts(self, request, asserts, err_enum):
"sls_mbi_format_synthetic": None,
"sls_hicn_hash": None,
"sls_mbi_hash": None,
"auth_language": lang,
}

for t in asserts:
Expand Down Expand Up @@ -413,7 +416,8 @@ def validate_asserts(self, request, asserts, err_enum):

def log_event(self, request, extra):
logger = logging.getLogger(logging.AUDIT_AUTHN_SLS_LOGGER, request)

# splunk dashboard auth flow dashboard baseSearch3
lang = lookup_language(request)
log_dict = {
"type": "Authentication:start",
"sub": self.user_id,
Expand All @@ -428,17 +432,21 @@ def log_event(self, request, extra):
"sls_mbi_format_synthetic": self.mbi_format_synthetic,
"sls_hicn_hash": self.hicn_hash,
"sls_mbi_hash": self.mbi_hash,
"auth_language": lang,
}

log_dict.update(extra)
logger.info(log_dict)

def log_authn_success(self, request, extra):
logger = logging.getLogger(logging.AUDIT_AUTHN_SLS_LOGGER, request)
# splunk dashboard auth flow dashboard baseSearch7
lang = lookup_language(request)
log_dict = {
"type": "Authentication:success",
"sub": self.user_id,
"user": None,
"auth_language": lang,
}
log_dict.update(extra)
logger.info(log_dict)
9 changes: 7 additions & 2 deletions apps/mymedicare_cb/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from apps.fhir.server.authentication import match_fhir_id

from .authorization import OAuth2ConfigSLSx, MedicareCallbackExceptionType
from apps.logging.utils import lookup_language


class BBMyMedicareCallbackCrosswalkCreateException(APIException):
Expand Down Expand Up @@ -56,7 +57,8 @@ def get_and_update_user(slsx_client: OAuth2ConfigSLSx, request=None):
mbi_hash=slsx_client.mbi_hash,
hicn_hash=slsx_client.hicn_hash, request=request
)

# splunk auth flow baseSearch5 baseSearch6a
lang = lookup_language(request)
log_dict = {
"type": "mymedicare_cb:get_and_update_user",
"subject": slsx_client.user_id,
Expand All @@ -66,6 +68,7 @@ def get_and_update_user(slsx_client: OAuth2ConfigSLSx, request=None):
"hash_lookup_type": hash_lookup_type,
"crosswalk": {},
"crosswalk_before": {},
"auth_language": lang,
}

# Init for types of crosswalk updates.
Expand Down Expand Up @@ -176,14 +179,16 @@ def get_and_update_user(slsx_client: OAuth2ConfigSLSx, request=None):
def create_beneficiary_record(slsx_client: OAuth2ConfigSLSx, fhir_id=None, user_id_type="H", request=None):

logger = logging.getLogger(logging.AUDIT_AUTHN_MED_CALLBACK_LOGGER, request)

# splunk dashboard auth flow baseSearch6b
lang = lookup_language(request)
log_dict = {
"type": "mymedicare_cb:create_beneficiary_record",
"username": slsx_client.user_id,
"fhir_id": fhir_id,
"user_mbi_hash": slsx_client.mbi_hash,
"user_hicn_hash": slsx_client.hicn_hash,
"crosswalk": {},
"auth_language": lang,
}

_validate_asserts(logger, log_dict, [
Expand Down
3 changes: 3 additions & 0 deletions apps/testclient/templates/authorize.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ <h3 class="ds-c-alert__heading">You'll need sample beneficiary credentials to lo
<br>
<a href="{{authorization_url}}&lang=es" class="ds-c-button ds-c-button--solid">Authorize as a Beneficiary (Spanish)</a>
</div>
<div class="action-container">
<a href="{{authorization_url}}&lang=es" class="ds-c-button ds-c-button--solid">Authorize as a Beneficiary (medicare.gov login in Spanish)</a>
</div>
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion apps/testclient/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def restart(request):

@waffle_switch('enable_testclient')
def callback(request):
# Authorization has been denied or another error has occured, remove token if existing
# Authorization has been denied or another error has occurred, remove token if existing
# and redirect to home page view to force re-authorization
if 'error' in request.GET:
if 'token' in request.session:
Expand Down
Loading
Loading