Source code for smarter.apps.secret.admin

# pylint: disable=C0115,W0212
"""Account admin."""

import logging

from django import forms

# from django.contrib import admin
from django.http.request import HttpRequest
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _

from smarter.apps.account.models import User, UserProfile, get_resolved_user
from smarter.apps.dashboard.admin import (
    SmarterCustomerModelAdmin,
    smarter_restricted_admin_site,
)
from smarter.common.helpers.console_helpers import formatted_text
from smarter.common.mixins import SmarterHelperMixin

from .models import Secret

logger = logging.getLogger(__name__)


[docs] class SecretAdminForm(forms.ModelForm): """Custom form for SecretAdmin to handle the transient 'value' field.""" value = forms.CharField( required=False, widget=forms.PasswordInput(render_value=True), help_text="Put your secret here...", ) model = Secret list_display = ["name", "user_profile", "description", "expires_at", "value"] logger_prefix = formatted_text(f"{__name__}.SecretAdminForm()")
[docs] def __init__(self, *args, **kwargs): self.user = kwargs.pop("user", None) # Expecting the user to be passed in via kwargs logger.debug("%s Initializing SecretAdminForm with args: %s and kwargs: %s", self.logger_prefix, args, kwargs) super().__init__(*args, **kwargs) if not self.user or not isinstance(self.user, User) or not self.user.is_authenticated: logger.error( "%s SecretAdminForm initialized without an authenticated user. All fields will be read-only.", self.logger_prefix, ) for field in self.fields.values(): field.disabled = True return def has_all_permission(): return False if self.instance and self.instance.pk: logger.debug("%s Initializing SecretAdminForm for existing Secret: %s", self.logger_prefix, self.instance) try: if has_all_permission(): instance: Secret = self.instance self.fields["value"].initial = instance.get_secret(update_last_accessed=False) else: logger.debug( "%s User %s does not have permission to view the Secret value. Setting 'value' field to '********'.", self.logger_prefix, self.user, ) self.fields["value"].initial = "********" # pylint: disable=broad-except except Exception as e: logger.exception( "%s Failed to initialize 'value' field for Secret with id %s. Got the following error: %s", self.logger_prefix, self.instance.pk, e, ) self.fields["value"].initial = None
[docs] def clean(self): """Ensure the transient 'value' field is included in cleaned_data.""" cleaned_data = super().clean() if not cleaned_data or "value" not in cleaned_data: raise forms.ValidationError("The 'value' field is required.") return cleaned_data
[docs] def clean_value(self): value = self.cleaned_data.get("value") if value and not isinstance(value, str): raise forms.ValidationError("The value must be a string.") return value
# @admin.register(Secret)
[docs] class SecretAdmin(SmarterCustomerModelAdmin, SmarterHelperMixin): """ Secret model admin. This is a primary Smarter resource, that descends directly from MetaDataWithOwnershipModel. Visibility of Secrets is determined by ownership and role. """ logger_prefix = formatted_text(f"{__name__}.SecretAdmin()") request: HttpRequest user: User user_profile: UserProfile model = Secret form = SecretAdminForm readonly_fields = ( "created_at", "updated_at", "last_accessed", "display_value", ) fields = ( "name", "user_profile", "description", "expires_at", "display_value", ) list_display = ("user_profile", "name", "description", "created_at", "updated_at", "last_accessed", "expires_at")
[docs] def changelist_view(self, request, extra_context=None): self.request = request return super().changelist_view(request, extra_context=extra_context)
[docs] def change_view(self, request, object_id, form_url="", extra_context=None): self.request = request return super().change_view(request, object_id, form_url, extra_context=extra_context)
[docs] def display_value(self, obj: Secret): """ Display the secret value as '********' for users who do not have permission to view it. """ def has_all_permission() -> bool: """ Determine if the current user has permission to view the Secret value. - Superusers can view all secrets. - The owner of the secret can view it. - All other users cannot view the secret value. """ if Secret.objects.filter(pk=obj.pk).with_ownership_permission_for(user=self.user).exists(): return True return False logger.debug("%s.display_value() called for Secret: %s", self.logger_prefix, str(obj)) if not isinstance(obj, Secret): logger.error( "%s.display_value() called with an object that is not a Secret instance: %s", self.logger_prefix, obj, ) return "********" if not hasattr(self.request, "user"): logger.error( "%s.display_value() called without a request user. Cannot determine permissions to display Secret value.", self.logger_prefix, ) return "********" self.user = self.request.user # type: ignore self.user_profile = UserProfile.get_cached_object(user=self.user) # type: ignore retval = obj.get_secret(update_last_accessed=False) logger.debug( "%s.display_value() Retrieved secret value for Secret %s. Checking permissions for user %s. Actual value: %s", self.logger_prefix, str(obj), self.user, self.mask_string(retval), ) if has_all_permission(): logger.debug( "%s.display_value() User %s has permission to view the Secret value. Displaying actual value.", self.logger_prefix, self.user, ) return retval logger.debug( "%s.display_value() User %s does not have permission to view the Secret value. Displaying masked value.", self.logger_prefix, self.user, ) return self.mask_string(retval)
display_value.short_description = "Value"
[docs] def get_form(self, request, obj=None, change=False, **kwargs): # Get the base form class form = super().get_form(request, obj, change=change, **kwargs) # Create a dynamic subclass to inject the request/user at initialization class CustomForm(form): def __init__(self, *args, **kwargs): # Inject custom kwargs here kwargs["user"] = request.user super().__init__(*args, **kwargs) return CustomForm
[docs] def save_model(self, request: HttpRequest, obj: Secret, form: SecretAdminForm, change): value = form.cleaned_data.get("value") if value: obj.encrypted_value = Secret.encrypt(value=value) super().save_model(request, obj, form, change)
[docs] def get_queryset(self, request: HttpRequest): user = get_resolved_user(request.user) # type: ignore qs = super().get_queryset(request) if not isinstance(user, User): return qs.none() return Secret.objects.with_ownership_permission_for(user=user).filter(id__in=qs)
[docs] class CustomPasswordWidget(forms.Widget): """Custom widget for the password field in the UserChangeForm."""
[docs] def render(self, name, value, attrs=None, renderer=None): """ use a placeholder and let the admin render the anchor correctly This works because the admin will replace __pk__ with the actual user id """ url = "../password/" # relative to the change page, works in Django admin return mark_safe(f'<a href="{url}" style="color: blue;">CHANGE PASSWORD</a>') # nosec
smarter_restricted_admin_site.register(Secret, SecretAdmin)