#No module named 'humanize'

1 messages · Page 1 of 1 (latest)

lusty nymph
#

Hi, I get the following error when trying to update user details: No module named 'humanize'.
I just open the settings and press save without changing anything.
Any idea anyone?

stark jetty
#

which verison are you running?

lusty nymph
#

2026.2.1

stark jetty
#

Clean install or upgraded? Also is there any error in the server logs or the event log?

lusty nymph
#

Upgraded, this instance runs since 2024. Where can I find server logs? Event logs has a "Policy failed to execute" log

stark jetty
#

can you post that log event from the event logs?

lusty nymph
#

Binding

UID
e17f11d3c71e48d6bc7a1d92311f28a6
Name
Binding - #1 to Policy default-user-settings-avatar-authorization
App
authentik_policies
Model Name
policybinding

Request

Object:

UID
    f8dee408bae9415a98307d5bb85caccd
Name
    PolicyBindingModel f8dee408-bae9-415a-9830-7d5bb85caccd
App
    authentik_policies
Model Name
    policybindingmodel

Exception
Policy failed to execute

#

I see now it comes from the avatar policy

#

is there no straightforward method to allow users upload their avatar?

#

I copied this policy from somewhere, but it appears to not work any longer

stark jetty
#

its just the usage of the humanize module in that policy, which we no longer include (it was never a direct dependency of ours, it got pulled in through another dependency)

#

whats the policy?

lusty nymph
#

the policy converts the selected file into a base64 representation and saves it in the user profile

stark jetty
#

sorry I meant whats the expression in the policy

lusty nymph
stark jetty
#

change it to this

AK_DOMAIN = "authentik.company"
FILE_PATH = "/media/user-avatars/"
URL_PATH = f"https://sso.domain.org/{FILE_PATH}"
MAX_UPLOAD_SIZE = 5 * 1024 * 1024
ACCEPTED_FILE_TYPES = {
  "image/png": "png",
  "image/jpeg": "jpeg",
  "image/jpeg": "jpg",
  "image/webp": "webp",
  # "image/svg+xml": "svg", # if you trust your users not to upload malicious svg files
  "image/gif": "gif",
  "image/bmp": "bmp",
  "image/vnd.microsoft.icon": "ico"
}

EMPTY_FILE = "data:application/octet-stream;base64,"

from django.template.defaultfilters import filesizeformat
from uuid import uuid4
from base64 import b64decode
from os import remove
from os.path import isfile

def remove_old_avatar_file():
  avatar = request.user.attributes.get("avatar", None)
  if avatar:
    components = avatar.split(URL_PATH, 1)
    if len(components) == 2 and components[0] == "" and components[1]:
      old_filename = FILE_PATH + components[1]
      if isfile(old_filename):
        remove(old_filename)

prompt_data = request.context.get("prompt_data")
avatar_overwritten = False

if "avatar" in prompt_data.get("attributes", {}):
  avatar = prompt_data["attributes"]["avatar"]
  if avatar == EMPTY_FILE:
    # No upload file specified, ignore
    del prompt_data["attributes"]["avatar"]
  else:
    avatar_mimetype = avatar.split("data:", 1)[1].split(";", 1)[0]
    if avatar_mimetype not in ACCEPTED_FILE_TYPES:
      ak_message("User avatar must be an image (" + ", ".join(ACCEPTED_FILE_TYPES.values()) + ") file.")
      return False
    # Now we know it is one of the accepted image file types
    avatar_base64 = avatar.split(",", 1)[1]
    avatar_binary = b64decode(avatar_base64)
    avatar_size = len(avatar_binary)
    if avatar_size > MAX_UPLOAD_SIZE:
      ak_message("User avatar file size must not exceed " + filesizeformat(MAX_UPLOAD_SIZE, binary = True, format = "%.0f") + ".")
      return False
    # Set a random file name with extension reflecting mime type
    avatar_filename = str(uuid4()) + "." + ACCEPTED_FILE_TYPES[avatar_mimetype]
    try:
      with (open(FILE_PATH + avatar_filename, "wb") as f):
        f.write(avatar_binary)
    except:
      ak_message("Could not write avatar file.")
      return False
    avatar_overwritten = True
    remove_old_avatar_file()
    prompt_data["attributes"]["avatar"] = URL_PATH + avatar_filename

if "avatar_reset" in prompt_data.get("attributes", {}):
  del prompt_data["attributes"]["avatar_reset"]
  # If a new avatar was uploaded, previous avatar is deleted anyway, so ignore
  if not avatar_overwritten:
    prompt_data["attributes"]["avatar"] = None
    remove_old_avatar_file()
    ak_message("Deleted user avatar.")

return True
#

changes humanize to django.template.defaultfilters.filesizeformat which will always be there

lusty nymph
#

works, thank you! I dont quite understand why there is no built-in way of uploading an image to the authentik instance and use it in user attributes

stark jetty
#

file management used to be quite painful up until the recent file update, so now that thats in, we're definitely want an avatar system