uralkal: django — Job создаёт в Zitadel action createUser на External Authentication / Pre Creation

This commit is contained in:
ivan 2026-11-05 16:15:10 +05:00
parent 1fc4742931
commit 59a02bc7dc
3 changed files with 182 additions and 0 deletions

View File

@ -4,6 +4,8 @@ resources:
- ../base
- kc-sync-configmap.yaml
- kc-sync-cronjob.yaml
- zitadel-action-configmap.yaml
- zitadel-action-job.yaml
patches:
- path: nginx-configmap.yaml
target:

View File

@ -0,0 +1,117 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: zitadel-action-create-user
namespace: django
data:
createUser.js: |
function createUser(ctx, api) {
let http = require('zitadel/http');
let logger = require('zitadel/log');
const BACKEND_HOST = 'http://backend-svc.django.svc.cluster.local:80/api'
const USER_COMPANIES = [1]
const BASIC_AUTH = '__BASIC_AUTH__'
logger.log('Creating external user in backend');
logger.log('Context: ' + JSON.stringify(ctx));
const userData = {
first_name: ctx.v1.user.human.firstName,
last_name: ctx.v1.user.human.lastName,
email: ctx.v1.user.human.email,
username: ctx.v1.user.username,
companies: USER_COMPANIES,
departments: [],
user_permissions: []
};
logger.log('Sending user data: ' + JSON.stringify(userData));
const response = http.fetch(BACKEND_HOST + '/core/admin/users/external/', {
method: 'POST',
body: userData,
headers: {
'Content-Type': 'application/json',
'Authorization': BASIC_AUTH
}
});
if (response.status != 200 && response.status != 201) {
logger.log('Error response status: ' + response.status);
logger.log('Error response body: ' + response.text());
throw "Backend API error: " + response.status;
}
const metadata = response.json();
logger.log('Metadata received: ' + JSON.stringify(metadata));
Object.keys(metadata).forEach(function(key) {
var value = metadata[key];
logger.log('Setting metadata - Key: ' + key + ', Value: ' + value);
api.v1.user.appendMetadata(key, value);
});
logger.log('External user successfully created and metadata set');
}
apply.py: |
import os
import sys
import httpx
ZITADEL = os.environ["ZITADEL_URL"]
HEADERS = {
"Host": os.environ["ZITADEL_HOST"],
"X-Forwarded-Proto": "https",
"Authorization": "Bearer " + open("/vault/secrets/zitadel-token").read().strip(),
"Content-Type": "application/json",
}
ORG_NAME = os.environ["ZITADEL_ORG_NAME"]
ACTION_NAME = os.environ["ACTION_NAME"]
FLOW_TYPE = os.environ["FLOW_TYPE"]
TRIGGER_TYPE = os.environ["TRIGGER_TYPE"]
client = httpx.Client(base_url=ZITADEL, headers=HEADERS, timeout=30)
def call(method, path, body=None, org_id=None):
headers = {"x-zitadel-orgid": org_id} if org_id else {}
r = client.request(method, path, json=body, headers=headers)
if r.status_code >= 300:
print(f"{method} {path} -> {r.status_code}: {r.text}")
sys.exit(1)
return r.json() if r.content else {}
orgs = call("POST", "/admin/v1/orgs/_search", {"queries": []}).get("result", [])
org = next((o for o in orgs if o.get("name", "").lower() == ORG_NAME.lower()), None)
if org is None:
print(f"org '{ORG_NAME}' not found, available: {[o.get('name') for o in orgs]}")
sys.exit(1)
org_id = org["id"]
print(f"org {org['name']} id={org_id}")
script = open("/opt/action/createUser.js").read().replace(
"__BASIC_AUTH__", "Basic " + open("/vault/secrets/backend-basic").read().strip()
)
body = {"name": ACTION_NAME, "script": script, "timeout": "10s", "allowedToFail": False}
found = call(
"POST",
"/management/v1/actions/_search",
{"queries": [{"actionNameQuery": {"name": ACTION_NAME, "method": "TEXT_QUERY_METHOD_EQUALS"}}]},
org_id,
).get("result", [])
if found:
action_id = found[0]["id"]
call("PUT", f"/management/v1/actions/{action_id}", body, org_id)
print(f"action {ACTION_NAME} updated id={action_id}")
else:
action_id = call("POST", "/management/v1/actions", body, org_id)["id"]
print(f"action {ACTION_NAME} created id={action_id}")
call("POST", f"/management/v1/flows/{FLOW_TYPE}/trigger/{TRIGGER_TYPE}", {"actionIds": [action_id]}, org_id)
print(f"flow {FLOW_TYPE} trigger {TRIGGER_TYPE} -> {ACTION_NAME}")

View File

@ -0,0 +1,63 @@
apiVersion: batch/v1
kind: Job
metadata:
name: zitadel-action-create-user
namespace: django
annotations:
kustomize.toolkit.fluxcd.io/force: enabled
spec:
backoffLimit: 2
template:
metadata:
annotations:
sidecar.istio.io/inject: "false"
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-pre-populate-only: "true"
vault.hashicorp.com/auth-path: auth/kubernetes
vault.hashicorp.com/role: django
vault.hashicorp.com/agent-inject-secret-zitadel-token: secrets/data/vault/common/django_auth
vault.hashicorp.com/agent-inject-template-zitadel-token: |-
{{- with secret "secrets/data/vault/common/django_auth" -}}
{{ index .Data.data "django_zitadel_access_token" }}
{{- end -}}
vault.hashicorp.com/agent-inject-secret-backend-basic: secrets/data/vault/common/django_auth
vault.hashicorp.com/agent-inject-template-backend-basic: |-
{{- with secret "secrets/data/vault/common/django_auth" -}}
{{ index .Data.data "key" }}
{{- end -}}
spec:
serviceAccountName: django-vault
automountServiceAccountToken: true
restartPolicy: Never
imagePullSecrets:
- name: regcred
containers:
- name: apply
image: cr.yandex/crp3ccidau046kdj8g9q/backend:production_7d0ec48b
imagePullPolicy: IfNotPresent
command: ["python", "/opt/action/apply.py"]
env:
- name: ZITADEL_URL
value: http://zitadel-idp-contour.zitadel.svc.cluster.local:8080
- name: ZITADEL_HOST
value: login.sarex.local.uralkali.com
- name: ZITADEL_ORG_NAME
value: zitadel
- name: ACTION_NAME
value: createUser
- name: FLOW_TYPE
value: "1"
- name: TRIGGER_TYPE
value: "2"
resources:
requests:
cpu: 20m
memory: 128Mi
volumeMounts:
- name: action
mountPath: /opt/action
readOnly: true
volumes:
- name: action
configMap:
name: zitadel-action-create-user