audit: add cleanse_dict function to ensure no passwords end in logs

This commit is contained in:
Jens Langhammer 2020-06-29 19:13:07 +02:00
parent 21ba969072
commit 96a6ac85df
3 changed files with 21 additions and 4 deletions

View File

@ -12,6 +12,7 @@ from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.http import HttpRequest from django.http import HttpRequest
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.debug import CLEANSED_SUBSTITUTE, HIDDEN_SETTINGS
from guardian.shortcuts import get_anonymous_user from guardian.shortcuts import get_anonymous_user
from structlog import get_logger from structlog import get_logger
@ -20,6 +21,22 @@ from passbook.lib.utils.http import get_client_ip
LOGGER = get_logger() LOGGER = get_logger()
def cleanse_dict(source: Dict[Any, Any]) -> Dict[Any, Any]:
"""Cleanse a dictionary, recursively"""
final_dict = {}
for key, value in source.items():
try:
if HIDDEN_SETTINGS.search(key):
final_dict[key] = CLEANSED_SUBSTITUTE
else:
final_dict[key] = value
except TypeError:
final_dict[key] = value
if isinstance(value, dict):
final_dict[key] = cleanse_dict(value)
return final_dict
def sanitize_dict(source: Dict[Any, Any]) -> Dict[Any, Any]: def sanitize_dict(source: Dict[Any, Any]) -> Dict[Any, Any]:
"""clean source of all Models that would interfere with the JSONField. """clean source of all Models that would interfere with the JSONField.
Models are replaced with a dictionary of { Models are replaced with a dictionary of {
@ -107,7 +124,7 @@ class Event(models.Model):
) )
if not app: if not app:
app = getmodule(stack()[_inspect_offset][0]).__name__ app = getmodule(stack()[_inspect_offset][0]).__name__
cleaned_kwargs = sanitize_dict(kwargs) cleaned_kwargs = cleanse_dict(sanitize_dict(kwargs))
event = Event(action=action.value, app=app, context=cleaned_kwargs) event = Event(action=action.value, app=app, context=cleaned_kwargs)
return event return event

View File

@ -15,7 +15,7 @@ from django.views.decorators.clickjacking import xframe_options_sameorigin
from django.views.generic import TemplateView, View from django.views.generic import TemplateView, View
from structlog import get_logger from structlog import get_logger
from passbook.audit.models import sanitize_dict from passbook.audit.models import cleanse_dict
from passbook.core.views.utils import PermissionDeniedView from passbook.core.views.utils import PermissionDeniedView
from passbook.flows.exceptions import EmptyFlowException, FlowNonApplicableException from passbook.flows.exceptions import EmptyFlowException, FlowNonApplicableException
from passbook.flows.models import Flow, FlowDesignation, Stage from passbook.flows.models import Flow, FlowDesignation, Stage
@ -162,7 +162,7 @@ class FlowExecutorView(View):
LOGGER.debug( LOGGER.debug(
"f(exec): User passed all stages", "f(exec): User passed all stages",
flow_slug=self.flow.slug, flow_slug=self.flow.slug,
context=sanitize_dict(self.plan.context), context=cleanse_dict(self.plan.context),
) )
return self._flow_done() return self._flow_done()

View File

@ -22,7 +22,7 @@ class ChangeFlowInitView(LoginRequiredMixin, View):
raise Http404 raise Http404
plan = FlowPlanner(stage.change_flow).plan( plan = FlowPlanner(stage.change_flow).plan(
request, {PLAN_CONTEXT_PENDING_USER: request.user,} request, {PLAN_CONTEXT_PENDING_USER: request.user}
) )
request.session[SESSION_KEY_PLAN] = plan request.session[SESSION_KEY_PLAN] = plan
return redirect_with_qs( return redirect_with_qs(