diff --git a/arches/app/views/api/__init__.py b/arches/app/views/api/__init__.py index 50ce89a8c1..376007eb81 100644 --- a/arches/app/views/api/__init__.py +++ b/arches/app/views/api/__init__.py @@ -24,6 +24,7 @@ from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.core.files.base import ContentFile from django.views.decorators.csrf import csrf_exempt +from django.views.decorators.debug import sensitive_variables from django.utils import translation from django.utils.decorators import method_decorator from django_ratelimit.decorators import ratelimit @@ -1127,6 +1128,7 @@ def get(self, request, plugin_id=None): class SearchExport(View): + @sensitive_variables("user_cred") @method_decorator( ratelimit( key="header:http-authorization", rate=settings.RATE_LIMIT, block=False diff --git a/arches/app/views/api/auth.py b/arches/app/views/api/auth.py index 6f95f5b759..27ff81e1e0 100644 --- a/arches/app/views/api/auth.py +++ b/arches/app/views/api/auth.py @@ -4,6 +4,7 @@ from django.contrib.auth import authenticate, login, logout from django.utils.decorators import method_decorator from django.utils.translation import gettext as _ +from django.views.decorators.debug import sensitive_variables, sensitive_post_parameters from django_ratelimit.decorators import ratelimit from arches.app.models.system_settings import settings @@ -18,7 +19,11 @@ class Login(LoginView, APIBase): http_method_names = ["post"] @method_decorator( - ratelimit(key="post:username", rate=settings.RATE_LIMIT, block=False) + ( + sensitive_variables(), + sensitive_post_parameters(), + ratelimit(key="post:username", rate=settings.RATE_LIMIT, block=False), + ) ) def post(self, request): if getattr(request, "limited", False): diff --git a/arches/app/views/auth.py b/arches/app/views/auth.py index 4bc74ac2d1..7a94dbcebc 100644 --- a/arches/app/views/auth.py +++ b/arches/app/views/auth.py @@ -19,7 +19,6 @@ import base64 import io -from django.http import response from arches.app.utils.external_oauth_backend import ExternalOauthAuthenticationBackend import qrcode import pyotp @@ -33,6 +32,7 @@ from django.utils.decorators import method_decorator from django.views.decorators.cache import never_cache from django.views.decorators.csrf import csrf_exempt +from django.views.decorators.debug import sensitive_post_parameters, sensitive_variables from django.utils.html import strip_tags from django.utils.translation import gettext as _ from django.utils.http import urlencode @@ -88,19 +88,23 @@ def get(self, request): ) @method_decorator( - ratelimit( - key="post:username", - rate=( - ( - "{}/{}".format( - int(settings.RATE_LIMIT.split("/")[0]) * 2, - settings.RATE_LIMIT.split("/")[1], + ( + sensitive_variables(), + sensitive_post_parameters(), + ratelimit( + key="post:username", + rate=( + ( + "{}/{}".format( + int(settings.RATE_LIMIT.split("/")[0]) * 2, + settings.RATE_LIMIT.split("/")[1], + ) ) - ) - if isinstance(settings.RATE_LIMIT, str) - else settings.RATE_LIMIT + if isinstance(settings.RATE_LIMIT, str) + else settings.RATE_LIMIT + ), + block=False, ), - block=False, ) ) def post(self, request): @@ -202,13 +206,8 @@ def get(self, request): confirmation_message = "" if not settings.ENABLE_USER_SIGNUP: - raise ( - Exception( - _( - "User signup has been disabled. Please contact your administrator." - ) - ) - ) + msg = _("User signup has been disabled. Please contact your administrator.") + raise Exception(msg) return render( request, @@ -223,6 +222,12 @@ def get(self, request): }, ) + @method_decorator( + ( + sensitive_variables(), + sensitive_post_parameters(), + ) + ) def post(self, request): showform = True confirmation_message = "" @@ -231,13 +236,8 @@ def post(self, request): form = ArchesUserCreationForm(postdata, enable_captcha=settings.ENABLE_CAPTCHA) if not settings.ENABLE_USER_SIGNUP: - raise ( - Exception( - _( - "User signup has been disabled. Please contact your administrator." - ) - ) - ) + msg = _("User signup has been disabled. Please contact your administrator.") + raise Exception(msg) if form.is_valid(): AES = AESCipher(settings.SECRET_KEY) @@ -311,13 +311,8 @@ def post(self, request): class ConfirmSignupView(View): def get(self, request): if not settings.ENABLE_USER_SIGNUP: - raise ( - Exception( - _( - "User signup has been disabled. Please contact your administrator." - ) - ) - ) + msg = _("User signup has been disabled. Please contact your administrator.") + raise Exception(msg) link = request.GET.get("link", None) AES = AESCipher(settings.SECRET_KEY) @@ -369,7 +364,13 @@ def get(self, request): } return JSONResponse(messages) - @method_decorator(ratelimit(key="user", rate=settings.RATE_LIMIT, block=False)) + @method_decorator( + ( + sensitive_variables(), + sensitive_post_parameters(), + ratelimit(key="user", rate=settings.RATE_LIMIT, block=False), + ) + ) def post(self, request): messages = { "invalid_password": None, @@ -425,7 +426,13 @@ class PasswordResetConfirmView(auth_views.PasswordResetConfirmView): @method_decorator(csrf_exempt, name="dispatch") class UserProfileView(View): - @method_decorator(ratelimit(key="post:username", rate=settings.RATE_LIMIT)) + @method_decorator( + ( + sensitive_variables(), + sensitive_post_parameters(), + ratelimit(key="post:username", rate=settings.RATE_LIMIT), + ) + ) def post(self, request): username = request.POST.get("username", None) password = request.POST.get("password", None) @@ -446,7 +453,13 @@ def post(self, request): @method_decorator(csrf_exempt, name="dispatch") class GetClientIdView(View): - @method_decorator(ratelimit(key="post:username", rate=settings.RATE_LIMIT)) + @method_decorator( + ( + sensitive_variables(), + sensitive_post_parameters(), + ratelimit(key="post:username", rate=settings.RATE_LIMIT), + ) + ) def post(self, request): if settings.OAUTH_CLIENT_ID == "": message = _("Make sure to set your OAUTH_CLIENT_ID in settings.py") @@ -465,7 +478,13 @@ def post(self, request): @method_decorator(csrf_exempt, name="dispatch") class ServerSettingView(View): - @method_decorator(ratelimit(key="post:username", rate=settings.RATE_LIMIT)) + @method_decorator( + ( + sensitive_variables(), + sensitive_post_parameters(), + ratelimit(key="post:username", rate=settings.RATE_LIMIT), + ) + ) def post(self, request): if settings.OAUTH_CLIENT_ID == "": message = _("Make sure to set your OAUTH_CLIENT_ID in settings.py") @@ -573,6 +592,7 @@ def post(self, request): @method_decorator(never_cache, name="dispatch") class TwoFactorAuthenticationLoginView(View): + @method_decorator((sensitive_variables(), sensitive_post_parameters())) def post(self, request): username = request.POST.get("username", None) password = request.POST.get("password", None) @@ -718,6 +738,7 @@ def post(self, request): @method_decorator(csrf_exempt, name="dispatch") class Token(View): + @method_decorator(sensitive_variables()) def get(self, request): if settings.DEBUG: data = { diff --git a/releases/8.0.0.md b/releases/8.0.0.md index 91aaf49361..7d4116399d 100644 --- a/releases/8.0.0.md +++ b/releases/8.0.0.md @@ -19,6 +19,7 @@ Arches 8.0.0 Release Notes ### Additional highlights - Add session-based REST APIs for login, logout [#11261](https://github.com/archesproject/arches/issues/11261) +- Auth views now filter out passwords from error reports when running in production [#11652](https://github.com/archesproject/arches/issues/11652) - Improve handling of longer model names [#11317](https://github.com/archesproject/arches/issues/11317) - New column `NodeGroup.grouping_node`: one-to-one field to the grouping node [#11613](https://github.com/archesproject/arches/issues/11613) - Support more expressive plugin URLs [#11320](https://github.com/archesproject/arches/issues/11320)