Add --setup-only and --base-uri flags to seed-keycloak script
--setup-only creates just the realm, service client, OIDC client, and sponsor attribute — no demo users, groups, or memberships. Ideal for production Keycloak setup. --base-uri sets the OIDC redirect URIs and web origins to the given app URL instead of localhost. Also updates URIs on existing clients. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,15 @@ Usage:
|
||||
# Against local Keycloak (defaults)
|
||||
python scripts/seed-keycloak.py
|
||||
|
||||
# Against a remote instance
|
||||
# Production setup — realm + clients only, no demo data
|
||||
python scripts/seed-keycloak.py --setup-only \
|
||||
--url https://keycloak.example.com \
|
||||
--admin-user admin \
|
||||
--admin-password changeme \
|
||||
--realm osa \
|
||||
--base-uri https://myapp.example.com
|
||||
|
||||
# Against a remote instance with demo data
|
||||
python scripts/seed-keycloak.py \
|
||||
--url https://keycloak.example.com \
|
||||
--admin-user admin \
|
||||
@@ -635,8 +643,21 @@ class KeycloakSeeder:
|
||||
|
||||
# ── OIDC client (for browser login) ─────────────────────────
|
||||
|
||||
def ensure_oidc_client(self):
|
||||
def ensure_oidc_client(self, base_uri=None):
|
||||
"""Create the osa-web OIDC client for browser-based login. Returns client secret."""
|
||||
if base_uri:
|
||||
base = base_uri.rstrip("/")
|
||||
redirect_uris = [f"{base}/api/auth/oidc/callback", f"{base}/*"]
|
||||
web_origins = [base]
|
||||
logout_uris = f"{base}/*"
|
||||
else:
|
||||
redirect_uris = [
|
||||
"http://localhost:5001/api/auth/oidc/callback",
|
||||
"http://localhost:5001/*",
|
||||
]
|
||||
web_origins = ["http://localhost:5001", "http://localhost:5173"]
|
||||
logout_uris = "http://localhost:5173/*"
|
||||
|
||||
resp = requests.get(
|
||||
self._admin_url(f"/clients?clientId={OIDC_CLIENT_ID}"),
|
||||
headers=self._headers(),
|
||||
@@ -646,6 +667,26 @@ class KeycloakSeeder:
|
||||
if clients:
|
||||
client_uuid = clients[0]["id"]
|
||||
print(f" OIDC client '{OIDC_CLIENT_ID}' already exists (id={client_uuid[:8]}...)")
|
||||
if base_uri:
|
||||
# Update redirect URIs and web origins on existing client
|
||||
resp = requests.put(
|
||||
self._admin_url(f"/clients/{client_uuid}"),
|
||||
headers=self._headers(),
|
||||
json={
|
||||
**clients[0],
|
||||
"redirectUris": redirect_uris,
|
||||
"webOrigins": web_origins,
|
||||
"attributes": {
|
||||
**clients[0].get("attributes", {}),
|
||||
"post.logout.redirect.uris": logout_uris,
|
||||
},
|
||||
},
|
||||
timeout=10,
|
||||
)
|
||||
if resp.status_code == 204:
|
||||
print(f" OIDC client redirect URIs updated for {base}")
|
||||
else:
|
||||
print(f" WARN: Failed to update OIDC client URIs: {resp.status_code}")
|
||||
else:
|
||||
resp = requests.post(
|
||||
self._admin_url("/clients"),
|
||||
@@ -660,13 +701,10 @@ class KeycloakSeeder:
|
||||
"standardFlowEnabled": True,
|
||||
"directAccessGrantsEnabled": False,
|
||||
"serviceAccountsEnabled": False,
|
||||
"redirectUris": [
|
||||
"http://localhost:5001/api/auth/oidc/callback",
|
||||
"http://localhost:5001/*",
|
||||
],
|
||||
"webOrigins": ["http://localhost:5001", "http://localhost:5173"],
|
||||
"redirectUris": redirect_uris,
|
||||
"webOrigins": web_origins,
|
||||
"attributes": {
|
||||
"post.logout.redirect.uris": "http://localhost:5173/*",
|
||||
"post.logout.redirect.uris": logout_uris,
|
||||
},
|
||||
},
|
||||
timeout=10,
|
||||
@@ -867,40 +905,57 @@ def main():
|
||||
default=os.environ.get("KEYCLOAK_REALM", "osa"),
|
||||
help="Realm name to create/seed (default: $KEYCLOAK_REALM or osa)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--setup-only",
|
||||
action="store_true",
|
||||
help="Only create realm, clients, and sponsor attribute — no demo users/groups/data",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--base-uri",
|
||||
default=None,
|
||||
help="App base URI for OIDC redirect URIs (e.g. https://myapp.example.com). "
|
||||
"Defaults to localhost for dev.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
print(f"Seeding Keycloak at {args.url}, realm '{args.realm}'")
|
||||
mode = "setup-only" if args.setup_only else "full seed"
|
||||
print(f"Seeding Keycloak at {args.url}, realm '{args.realm}' ({mode})")
|
||||
if args.base_uri:
|
||||
print(f" OIDC redirect base: {args.base_uri}")
|
||||
print()
|
||||
|
||||
seeder = KeycloakSeeder(args.url, args.admin_user, args.admin_password, args.realm)
|
||||
|
||||
print("[1/9] Realm")
|
||||
total = 4 if args.setup_only else 9
|
||||
|
||||
print(f"[1/{total}] Realm")
|
||||
seeder.ensure_realm()
|
||||
|
||||
print("[2/9] Service client")
|
||||
print(f"[2/{total}] Service client")
|
||||
secret = seeder.ensure_service_client()
|
||||
|
||||
print("[3/9] OIDC client")
|
||||
oidc_secret = seeder.ensure_oidc_client()
|
||||
print(f"[3/{total}] OIDC client")
|
||||
oidc_secret = seeder.ensure_oidc_client(base_uri=args.base_uri)
|
||||
|
||||
print("[4/9] User profile (sponsor attribute)")
|
||||
print(f"[4/{total}] User profile (sponsor attribute)")
|
||||
seeder.ensure_sponsor_attribute()
|
||||
|
||||
print("[5/9] Users")
|
||||
user_map = seeder.create_users()
|
||||
if not args.setup_only:
|
||||
print(f"[5/{total}] Users")
|
||||
user_map = seeder.create_users()
|
||||
|
||||
print("[6/9] User passwords")
|
||||
seeder.set_user_passwords(user_map)
|
||||
print(f"[6/{total}] User passwords")
|
||||
seeder.set_user_passwords(user_map)
|
||||
|
||||
print("[7/9] Groups & child groups")
|
||||
group_map = seeder.create_groups()
|
||||
child_map = seeder.create_child_groups(group_map)
|
||||
print(f"[7/{total}] Groups & child groups")
|
||||
group_map = seeder.create_groups()
|
||||
child_map = seeder.create_child_groups(group_map)
|
||||
|
||||
print("[8/9] Memberships")
|
||||
seeder.assign_memberships(user_map, group_map, child_map)
|
||||
print(f"[8/{total}] Memberships")
|
||||
seeder.assign_memberships(user_map, group_map, child_map)
|
||||
|
||||
print("[9/9] Sponsor attributes")
|
||||
seeder.assign_sponsors(user_map)
|
||||
print(f"[9/{total}] Sponsor attributes")
|
||||
seeder.assign_sponsors(user_map)
|
||||
|
||||
print()
|
||||
print("Done! To connect OSA Suite to this Keycloak instance:")
|
||||
|
||||
Reference in New Issue
Block a user