keycloak: allow e2e client execute-actions-email
This commit is contained in:
parent
cadb0daba0
commit
eb11eaff4e
@ -2,6 +2,7 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
import urllib.error
|
import urllib.error
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import urllib.request
|
import urllib.request
|
||||||
@ -46,7 +47,10 @@ def _request_json(method: str, url: str, token: str, payload: object | None = No
|
|||||||
return resp.status, json.loads(body.decode())
|
return resp.status, json.loads(body.decode())
|
||||||
except urllib.error.HTTPError as exc:
|
except urllib.error.HTTPError as exc:
|
||||||
raw = exc.read().decode(errors="replace")
|
raw = exc.read().decode(errors="replace")
|
||||||
raise SystemExit(f"HTTP {exc.code} from {url}: {raw}")
|
try:
|
||||||
|
return exc.code, json.loads(raw) if raw else None
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return exc.code, raw
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
@ -64,23 +68,34 @@ def main() -> int:
|
|||||||
token_url = f"{keycloak_base}/realms/{realm}/protocol/openid-connect/token"
|
token_url = f"{keycloak_base}/realms/{realm}/protocol/openid-connect/token"
|
||||||
admin_users_url = f"{keycloak_base}/admin/realms/{realm}/users"
|
admin_users_url = f"{keycloak_base}/admin/realms/{realm}/users"
|
||||||
|
|
||||||
token_payload = _post_form(
|
def get_access_token() -> str:
|
||||||
token_url,
|
token_payload = _post_form(
|
||||||
{"grant_type": "client_credentials", "client_id": client_id, "client_secret": client_secret},
|
token_url,
|
||||||
timeout_s=30,
|
{"grant_type": "client_credentials", "client_id": client_id, "client_secret": client_secret},
|
||||||
)
|
timeout_s=30,
|
||||||
access_token = token_payload.get("access_token")
|
)
|
||||||
if not isinstance(access_token, str) or not access_token:
|
access_token = token_payload.get("access_token")
|
||||||
raise SystemExit("client credentials token missing access_token")
|
if not isinstance(access_token, str) or not access_token:
|
||||||
|
raise SystemExit("client credentials token missing access_token")
|
||||||
|
return access_token
|
||||||
|
|
||||||
status, users = _request_json(
|
access_token = get_access_token()
|
||||||
"GET",
|
|
||||||
f"{admin_users_url}?{urllib.parse.urlencode({'username': probe_username, 'exact': 'true'})}",
|
users: list | None = None
|
||||||
access_token,
|
search_url = f"{admin_users_url}?{urllib.parse.urlencode({'username': probe_username, 'exact': 'true'})}"
|
||||||
timeout_s=30,
|
for attempt in range(1, 11):
|
||||||
)
|
status, body = _request_json("GET", search_url, access_token, timeout_s=30)
|
||||||
if status != 200 or not isinstance(users, list):
|
if status == 200 and isinstance(body, list):
|
||||||
raise SystemExit("unexpected admin API response when searching for probe user")
|
users = body
|
||||||
|
break
|
||||||
|
if status == 403 and attempt < 10:
|
||||||
|
time.sleep(3)
|
||||||
|
access_token = get_access_token()
|
||||||
|
continue
|
||||||
|
raise SystemExit(f"unexpected admin API response when searching for probe user (status={status} body={body})")
|
||||||
|
|
||||||
|
if users is None:
|
||||||
|
raise SystemExit("probe user search did not return a list response")
|
||||||
|
|
||||||
if not users:
|
if not users:
|
||||||
create_payload = {
|
create_payload = {
|
||||||
@ -89,17 +104,27 @@ def main() -> int:
|
|||||||
"email": probe_email,
|
"email": probe_email,
|
||||||
"emailVerified": True,
|
"emailVerified": True,
|
||||||
}
|
}
|
||||||
status, _ = _request_json("POST", admin_users_url, access_token, create_payload, timeout_s=30)
|
for attempt in range(1, 6):
|
||||||
if status not in (201, 204):
|
status, body = _request_json("POST", admin_users_url, access_token, create_payload, timeout_s=30)
|
||||||
raise SystemExit(f"unexpected status creating probe user: {status}")
|
if status in (201, 204):
|
||||||
status, users = _request_json(
|
break
|
||||||
"GET",
|
if status == 403 and attempt < 5:
|
||||||
f"{admin_users_url}?{urllib.parse.urlencode({'username': probe_username, 'exact': 'true'})}",
|
time.sleep(3)
|
||||||
access_token,
|
access_token = get_access_token()
|
||||||
timeout_s=30,
|
continue
|
||||||
)
|
raise SystemExit(f"unexpected status creating probe user: {status} body={body}")
|
||||||
if status != 200 or not isinstance(users, list) or not users:
|
|
||||||
raise SystemExit("failed to refetch probe user after creation")
|
# Refetch.
|
||||||
|
for attempt in range(1, 11):
|
||||||
|
status, body = _request_json("GET", search_url, access_token, timeout_s=30)
|
||||||
|
if status == 200 and isinstance(body, list) and body:
|
||||||
|
users = body
|
||||||
|
break
|
||||||
|
if status == 403 and attempt < 10:
|
||||||
|
time.sleep(3)
|
||||||
|
access_token = get_access_token()
|
||||||
|
continue
|
||||||
|
raise SystemExit(f"failed to refetch probe user after creation (status={status} body={body})")
|
||||||
|
|
||||||
user_id = users[0].get("id")
|
user_id = users[0].get("id")
|
||||||
if not isinstance(user_id, str) or not user_id:
|
if not isinstance(user_id, str) or not user_id:
|
||||||
@ -114,9 +139,15 @@ def main() -> int:
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
url = f"{admin_users_url}/{urllib.parse.quote(user_id)}/execute-actions-email?{query}"
|
url = f"{admin_users_url}/{urllib.parse.quote(user_id)}/execute-actions-email?{query}"
|
||||||
status, _ = _request_json("PUT", url, access_token, ["UPDATE_PASSWORD"], timeout_s=30)
|
for attempt in range(1, 6):
|
||||||
if status != 204:
|
status, body = _request_json("PUT", url, access_token, ["UPDATE_PASSWORD"], timeout_s=30)
|
||||||
raise SystemExit(f"unexpected status from execute-actions-email: {status}")
|
if status == 204:
|
||||||
|
break
|
||||||
|
if status == 403 and attempt < 5:
|
||||||
|
time.sleep(3)
|
||||||
|
access_token = get_access_token()
|
||||||
|
continue
|
||||||
|
raise SystemExit(f"unexpected status from execute-actions-email: {status} body={body}")
|
||||||
|
|
||||||
print("PASS: Keycloak execute-actions-email succeeded")
|
print("PASS: Keycloak execute-actions-email succeeded")
|
||||||
return 0
|
return 0
|
||||||
@ -124,4 +155,3 @@ def main() -> int:
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sys.exit(main())
|
sys.exit(main())
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
apiVersion: batch/v1
|
apiVersion: batch/v1
|
||||||
kind: Job
|
kind: Job
|
||||||
metadata:
|
metadata:
|
||||||
name: keycloak-portal-e2e-client-1
|
name: keycloak-portal-e2e-client-2
|
||||||
namespace: sso
|
namespace: sso
|
||||||
spec:
|
spec:
|
||||||
backoffLimit: 0
|
backoffLimit: 0
|
||||||
@ -211,7 +211,7 @@ spec:
|
|||||||
if not rm_uuid:
|
if not rm_uuid:
|
||||||
raise SystemExit("realm-management client has no id")
|
raise SystemExit("realm-management client has no id")
|
||||||
|
|
||||||
wanted_roles = ("query-users", "view-users", "impersonation")
|
wanted_roles = ("query-users", "view-users", "manage-users", "impersonation")
|
||||||
role_reps = []
|
role_reps = []
|
||||||
for role_name in wanted_roles:
|
for role_name in wanted_roles:
|
||||||
status, role = http_json(
|
status, role = http_json(
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
apiVersion: batch/v1
|
apiVersion: batch/v1
|
||||||
kind: Job
|
kind: Job
|
||||||
metadata:
|
metadata:
|
||||||
name: keycloak-portal-e2e-execute-actions-email-1
|
name: keycloak-portal-e2e-execute-actions-email-2
|
||||||
namespace: sso
|
namespace: sso
|
||||||
spec:
|
spec:
|
||||||
backoffLimit: 3
|
backoffLimit: 3
|
||||||
@ -49,4 +49,3 @@ spec:
|
|||||||
configMap:
|
configMap:
|
||||||
name: portal-e2e-tests
|
name: portal-e2e-tests
|
||||||
defaultMode: 0555
|
defaultMode: 0555
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user