Problem: OX Shared Account access delayed in large environments

Problem

Opening a Shared Account in the IAM Administration Portal (UMC) becomes slow when its permissions selectors resolve thousands of OX users. The form appears frozen even when the administrator only wants to review permissions.
Administrators must wait before reviewing or changing access, cannot reliably distinguish a busy form from a failed request, and risk repeating actions while the selector is still loading. This affects routine Shared Account administration, not just bulk provisioning.

Evidence Before optimization After optimization Measurement scope
Local reproduction, 13,000 OX-enabled users ~20-30 s ~1-2 s Opening the Shared Account form

Root Cause

Related upstream reference: Univention Bug 60036.

The original /usr/lib/python3/dist-packages/univention/admin/syntax.d/50_ox.py defines OxUser and OxGroup as UDM_Objects subclasses without overriding use_objects.

  1. UDM_Objects defaults to use_objects = True.
  2. OxUser._is_simple() returns (False, set()).
  3. The fallback users.user.lookup constructs full user objects, maps attributes, and applies authorization processing through filter_lookup_results.
  4. With a limit of 12,000, ldapSizelimitExceeded is caught after expensive work. A customer response still returned HTTP 200 with size_limit_exceeded: true and performs_well: true, after approximately 25 seconds. These flags and HTTP success do not demonstrate a fast lookup.

Setting use_objects = False enables lightweight selection of username and univentionObjectIdentifier for users, and name and identifier for groups. The existing filters remain (isOxUser=OK) and (isOxGroup=OK).


Workaround

The two-line change below is a proposed, locally tested workaround: an unreleased candidate provided in this case, not a vendor-supported production update or an officially released fix. Check Bug 60036 for current status and finally fix.

1. Establish the operator shell and preserve evidence

Run the examples in Bash unless explicitly marked otherwise. Requirements: kubectl, jq, Python 3, patch, Docker, and approved cluster access.

export NAMESPACE=<YOUR_NAMESPACE>
export DOMAIN=${NAMESPACE?}.univention.dev
export WORK=/home/USERNAME/Downloads/ox-syntax-test
export PLUGIN=/usr/lib/python3/dist-packages/univention/admin/syntax.d/50_ox.py
mkdir -p "$WORK"
cd "$WORK"
kubectl config current-context
kubectl -n "$NAMESPACE" get sts ums-umc-server

Confirm the context and namespace before any write. Backups may contain internal configuration; keep them private.

kubectl -n "$NAMESPACE" get sts ums-umc-server -o yaml \
  > "ums-umc-server-before-$(date +%Y%m%d-%H%M%S).yaml"

kubectl -n "$NAMESPACE" exec ums-umc-server-0 -c main -- cat "$PLUGIN" \
  > 50_ox.py

2. Prepare and validate the two-line change locally

Do not run patch against the read-only pod path. Preserve the existing local artifacts by using a new staging directory.

The patch: Possible Patch from Bug 60036
Save the patch with the name ox-user-group-use-objects.patch

--- a/50_ox.py
+++ b/50_ox.py
@@ -425,6 +425,7 @@


 class OxUser(UDM_Objects):
+    use_objects = False
     udm_modules = ('users/user',)
     key = '%(univentionObjectIdentifier)s'
     label = '%(username)s'
@@ -432,6 +433,7 @@


 class OxGroup(UDM_Objects):
+    use_objects = False
     udm_modules = ('groups/group',)
     key = '%(univentionObjectIdentifier)s'
     label = '%(name)s'
STAGE=$(mktemp -d "$WORK/patch-stage.XXXXXX")
cp 50_ox.py "$STAGE/50_ox.py"
patch --dry-run -d "$STAGE" -p1 < ox-user-group-use-objects.patch
patch -d "$STAGE" -p1 < ox-user-group-use-objects.patch
diff -u 50_ox.py "$STAGE/50_ox.py" || test "$?" -eq 1

python3 - <<'PY'
import ast
from pathlib import Path
ast.parse(Path("50_ox.py").read_text())
print("Python-Syntax OK")
PY

3. Overlay the single plugin file with a ConfigMap

First inspect existing mounts as described under Investigation. Stop if ox-syntax-test already exists or this exact file path already has an overlay; reconcile ownership rather than overwriting another experiment.

kubectl -n "$NAMESPACE" get configmap ox-syntax-test

Expected output:

Error from server (NotFound): configmaps "ox-syntax-test" not found

Use the following command to create a new configmap with including the patched 50_ox.py.

kubectl -n "$NAMESPACE" create configmap ox-syntax-test \
  --from-file=50_ox.py="$STAGE/50_ox.py"
cat > "$WORK/ox-overlay.yaml" <<'YAML'
spec:
  template:
    spec:
      volumes:
        - name: ox-syntax-test
          configMap:
            name: ox-syntax-test
      containers:
        - name: main
          volumeMounts:
            - name: ox-syntax-test
              mountPath: /usr/lib/python3/dist-packages/univention/admin/syntax.d/50_ox.py
              subPath: 50_ox.py
              readOnly: true
YAML

Re-check the created configmap:

kubectl -n "$NAMESPACE" get configmap ox-syntax-test
NAME             DATA   AGE
ox-syntax-test   1      8s

Install the patch to the ums-umc-server

kubectl -n "$NAMESPACE" patch sts ums-umc-server --type=strategic \
  --patch-file "$WORK/ox-overlay.yaml"

Rollout the pod with the patch.

kubectl -n "$NAMESPACE" rollout status sts/ums-umc-server --timeout=300s

The template change normally triggers a rollout under a RollingUpdate strategy. If the StatefulSet uses OnDelete or a partition, stop and arrange an explicitly approved replacement procedure. Expect downtime with a single replica. A ConfigMap subPath mount does not update live; changing the ConfigMap later requires a pod replacement.

kubectl -n "$NAMESPACE" exec ums-umc-server-0 -c main -- cat "$PLUGIN" \
  > "$WORK/50_ox.pod-after.py"

diff -u "$STAGE/50_ox.py" "$WORK/50_ox.pod-after.py"

Helm upgrades or reconciliation may overwrite temporary StatefulSet and ConfigMap changes. A durable fix belongs in the managed extension/release configuration.


Additional

Targeted rollback

Remove only this overlay. Do not use a broad rollout undo, replace the whole StatefulSet from an old backup, or delete the namespace/users.

cat > "$WORK/ox-overlay-remove.yaml" <<'YAML'
spec:
  template:
    spec:
      containers:
        - name: main
          volumeMounts:
            - mountPath: /usr/lib/python3/dist-packages/univention/admin/syntax.d/50_ox.py
              $patch: delete
      volumes:
        - name: ox-syntax-test
          $patch: delete
YAML
kubectl -n "$NAMESPACE" patch sts ums-umc-server --type=strategic \
  --patch-file "$WORK/ox-overlay-remove.yaml"

kubectl -n "$NAMESPACE" rollout status sts/ums-umc-server --timeout=300s

kubectl -n "$NAMESPACE" exec ums-umc-server-0 -c main -- cat "$PLUGIN" \
  > "$WORK/50_ox.pod-rollback.py"

diff -u "$WORK/50_ox.pod-original.py" "$WORK/50_ox.pod-rollback.py"
# Optional, only after confirming the overlay is no longer referenced:

kubectl -n "$NAMESPACE" delete configmap ox-syntax-test