From 525a1bb9f36437776c31d6cfec71a4de39a425fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Fri, 16 Jan 2026 14:02:51 +0100 Subject: [PATCH 1/5] Introduce OpenIdProviderKind --- ...d49aadbd2516b1ff4fb962d1fc2437401064.json} | 18 ++++++- ...64b9920b263b3d668704ecbe66d80aa7c586.json} | 6 +-- ...5da87df96619428383083bded91c2dfdf598.json} | 53 +++++++++++++------ ...42ab7efcb9a7375d01ce85078389edf1340b.json} | 53 +++++++++++++------ ...b1506bf33b00c4ef21c0761d6c3a4f6be964.json} | 53 +++++++++++++------ ...11ed9e490ab111a2c0cbb499587f351697b8.json} | 18 ++++++- ...89773b783a1b738c453791c45747b11aa106.json} | 53 +++++++++++++------ Cargo.lock | 12 ++--- .../defguard_common/src/db/models/settings.rs | 8 +-- .../src/db/models/activity_log/metadata.rs | 4 +- .../enterprise/db/models/openid_provider.rs | 21 ++++++-- .../src/enterprise/directory_sync/tests.rs | 3 +- .../src/enterprise/handlers/openid_login.rs | 16 +++--- .../enterprise/handlers/openid_providers.rs | 13 +++-- .../tests/integration/api/openid_login.rs | 12 +++-- .../tests/integration/api/wireguard.rs | 12 +++-- ...3545_[2.0.0]_openid_provider_kind.down.sql | 2 + ...123545_[2.0.0]_openid_provider_kind.up.sql | 9 ++++ .../forms/GoogleProviderForm.tsx | 3 ++ .../forms/JumpcloudProviderForm.tsx | 7 ++- .../forms/MicrosoftProviderForm.tsx | 6 ++- .../forms/OktaProviderForm.tsx | 6 ++- .../useAddExternalOpenIdStore.tsx | 27 ++++++---- web/src/shared/api/types.ts | 21 ++++++-- 24 files changed, 306 insertions(+), 130 deletions(-) rename .sqlx/{query-c6ebb402f91d242754872addc3604fb6ffac7e6fdd0d9428070cecb68c666cd8.json => query-1332e9c8c375774902845e5bebddd49aadbd2516b1ff4fb962d1fc2437401064.json} (59%) rename .sqlx/{query-217e11c616a96fdd002fbf31bb0a37735aec6f1bac187cc4a331207154996346.json => query-23d55f2b3d7f82c0bb6afdd4e88e64b9920b263b3d668704ecbe66d80aa7c586.json} (97%) rename .sqlx/{query-558fb8aa5e223f6fc273c1431410dabb2ca9c2a831cacb7ebc8d696020b0556c.json => query-4c259991024808243d03443cb8725da87df96619428383083bded91c2dfdf598.json} (73%) rename .sqlx/{query-14302b1c6c7d72d6e6f38c80538040b9fa3479c919f1ace2a787470690be9de3.json => query-7642a85c7e4a299c2ae6b7deba3642ab7efcb9a7375d01ce85078389edf1340b.json} (73%) rename .sqlx/{query-e28b02ccc616d67fcb1a1aa940ea35be58cb652e8287a6f5028421656048b58a.json => query-bbff781ddf881678e2d24bf292a2b1506bf33b00c4ef21c0761d6c3a4f6be964.json} (73%) rename .sqlx/{query-c8e9800861c7bc853235858650be8ad3d3d19b0c4f0e69b9002a6a1fbd46a324.json => query-f6110462d7fd58131e1a2d8cb52711ed9e490ab111a2c0cbb499587f351697b8.json} (57%) rename .sqlx/{query-d8db674150231de0063227000a8b39f45c6da9836f93b67307787851a1804f13.json => query-f851e3667522ee439c614f66e13d89773b783a1b738c453791c45747b11aa106.json} (73%) create mode 100644 migrations/20260115123545_[2.0.0]_openid_provider_kind.down.sql create mode 100644 migrations/20260115123545_[2.0.0]_openid_provider_kind.up.sql diff --git a/.sqlx/query-c6ebb402f91d242754872addc3604fb6ffac7e6fdd0d9428070cecb68c666cd8.json b/.sqlx/query-1332e9c8c375774902845e5bebddd49aadbd2516b1ff4fb962d1fc2437401064.json similarity index 59% rename from .sqlx/query-c6ebb402f91d242754872addc3604fb6ffac7e6fdd0d9428070cecb68c666cd8.json rename to .sqlx/query-1332e9c8c375774902845e5bebddd49aadbd2516b1ff4fb962d1fc2437401064.json index 7994ebf797..c484209476 100644 --- a/.sqlx/query-c6ebb402f91d242754872addc3604fb6ffac7e6fdd0d9428070cecb68c666cd8.json +++ b/.sqlx/query-1332e9c8c375774902845e5bebddd49aadbd2516b1ff4fb962d1fc2437401064.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO \"openidprovider\" (\"name\",\"base_url\",\"client_id\",\"client_secret\",\"display_name\",\"google_service_account_key\",\"google_service_account_email\",\"admin_email\",\"directory_sync_enabled\",\"directory_sync_interval\",\"directory_sync_user_behavior\",\"directory_sync_admin_behavior\",\"directory_sync_target\",\"okta_private_jwk\",\"okta_dirsync_client_id\",\"directory_sync_group_match\",\"jumpcloud_api_key\",\"prefetch_users\") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18) RETURNING id", + "query": "INSERT INTO \"openidprovider\" (\"name\",\"base_url\",\"kind\",\"client_id\",\"client_secret\",\"display_name\",\"google_service_account_key\",\"google_service_account_email\",\"admin_email\",\"directory_sync_enabled\",\"directory_sync_interval\",\"directory_sync_user_behavior\",\"directory_sync_admin_behavior\",\"directory_sync_target\",\"okta_private_jwk\",\"okta_dirsync_client_id\",\"directory_sync_group_match\",\"jumpcloud_api_key\",\"prefetch_users\") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19) RETURNING id", "describe": { "columns": [ { @@ -13,6 +13,20 @@ "Left": [ "Text", "Text", + { + "Custom": { + "name": "openid_provider_kind", + "kind": { + "Enum": [ + "Google", + "Microsoft", + "Okta", + "JumpCloud", + "Custom" + ] + } + } + }, "Text", "Text", "Text", @@ -68,5 +82,5 @@ false ] }, - "hash": "c6ebb402f91d242754872addc3604fb6ffac7e6fdd0d9428070cecb68c666cd8" + "hash": "1332e9c8c375774902845e5bebddd49aadbd2516b1ff4fb962d1fc2437401064" } diff --git a/.sqlx/query-217e11c616a96fdd002fbf31bb0a37735aec6f1bac187cc4a331207154996346.json b/.sqlx/query-23d55f2b3d7f82c0bb6afdd4e88e64b9920b263b3d668704ecbe66d80aa7c586.json similarity index 97% rename from .sqlx/query-217e11c616a96fdd002fbf31bb0a37735aec6f1bac187cc4a331207154996346.json rename to .sqlx/query-23d55f2b3d7f82c0bb6afdd4e88e64b9920b263b3d668704ecbe66d80aa7c586.json index f52051d44e..b6b62477c9 100644 --- a/.sqlx/query-217e11c616a96fdd002fbf31bb0a37735aec6f1bac187cc4a331207154996346.json +++ b/.sqlx/query-23d55f2b3d7f82c0bb6afdd4e88e64b9920b263b3d668704ecbe66d80aa7c586.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT openid_enabled, wireguard_enabled, webhooks_enabled, worker_enabled, challenge_template, instance_name, main_logo_url, nav_logo_url, smtp_server, smtp_port, smtp_encryption \"smtp_encryption: _\", smtp_user, smtp_password \"smtp_password?: SecretStringWrapper\", smtp_sender, enrollment_vpn_step_optional, enrollment_welcome_message, enrollment_welcome_email, enrollment_welcome_email_subject, enrollment_use_welcome_message_as_email, uuid, ldap_url, ldap_bind_username, ldap_bind_password \"ldap_bind_password?: SecretStringWrapper\", ldap_group_search_base, ldap_user_search_base, ldap_user_obj_class, ldap_group_obj_class, ldap_username_attr, ldap_groupname_attr, ldap_group_member_attr, ldap_member_attr, openid_create_account, license, gateway_disconnect_notifications_enabled, ldap_use_starttls, ldap_tls_verify_cert, gateway_disconnect_notifications_inactivity_threshold, gateway_disconnect_notifications_reconnect_notification_enabled, ldap_sync_status \"ldap_sync_status: LdapSyncStatus\", ldap_enabled, ldap_sync_enabled, ldap_is_authoritative, ldap_sync_interval, ldap_user_auxiliary_obj_classes, ldap_uses_ad, ldap_user_rdn_attr, ldap_sync_groups, openid_username_handling \"openid_username_handling: OpenidUsernameHandling\", ca_key_der, ca_cert_der FROM \"settings\" WHERE id = 1", + "query": "SELECT openid_enabled, wireguard_enabled, webhooks_enabled, worker_enabled, challenge_template, instance_name, main_logo_url, nav_logo_url, smtp_server, smtp_port, smtp_encryption \"smtp_encryption: _\", smtp_user, smtp_password \"smtp_password?: SecretStringWrapper\", smtp_sender, enrollment_vpn_step_optional, enrollment_welcome_message, enrollment_welcome_email, enrollment_welcome_email_subject, enrollment_use_welcome_message_as_email, uuid, ldap_url, ldap_bind_username, ldap_bind_password \"ldap_bind_password?: SecretStringWrapper\", ldap_group_search_base, ldap_user_search_base, ldap_user_obj_class, ldap_group_obj_class, ldap_username_attr, ldap_groupname_attr, ldap_group_member_attr, ldap_member_attr, openid_create_account, license, gateway_disconnect_notifications_enabled, ldap_use_starttls, ldap_tls_verify_cert, gateway_disconnect_notifications_inactivity_threshold, gateway_disconnect_notifications_reconnect_notification_enabled, ldap_sync_status \"ldap_sync_status: LdapSyncStatus\", ldap_enabled, ldap_sync_enabled, ldap_is_authoritative, ldap_sync_interval, ldap_user_auxiliary_obj_classes, ldap_uses_ad, ldap_user_rdn_attr, ldap_sync_groups, openid_username_handling \"openid_username_handling: OpenIdUsernameHandling\", ca_key_der, ca_cert_der FROM \"settings\" WHERE id = 1", "describe": { "columns": [ { @@ -261,7 +261,7 @@ }, { "ordinal": 47, - "name": "openid_username_handling: OpenidUsernameHandling", + "name": "openid_username_handling: OpenIdUsernameHandling", "type_info": { "Custom": { "name": "openid_username_handling", @@ -342,5 +342,5 @@ true ] }, - "hash": "217e11c616a96fdd002fbf31bb0a37735aec6f1bac187cc4a331207154996346" + "hash": "23d55f2b3d7f82c0bb6afdd4e88e64b9920b263b3d668704ecbe66d80aa7c586" } diff --git a/.sqlx/query-558fb8aa5e223f6fc273c1431410dabb2ca9c2a831cacb7ebc8d696020b0556c.json b/.sqlx/query-4c259991024808243d03443cb8725da87df96619428383083bded91c2dfdf598.json similarity index 73% rename from .sqlx/query-558fb8aa5e223f6fc273c1431410dabb2ca9c2a831cacb7ebc8d696020b0556c.json rename to .sqlx/query-4c259991024808243d03443cb8725da87df96619428383083bded91c2dfdf598.json index bfd59988cf..55cbebf320 100644 --- a/.sqlx/query-558fb8aa5e223f6fc273c1431410dabb2ca9c2a831cacb7ebc8d696020b0556c.json +++ b/.sqlx/query-4c259991024808243d03443cb8725da87df96619428383083bded91c2dfdf598.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, name, base_url, client_id, client_secret, display_name, google_service_account_key, google_service_account_email, admin_email, directory_sync_enabled,\n directory_sync_interval, directory_sync_user_behavior \"directory_sync_user_behavior: DirectorySyncUserBehavior\", directory_sync_admin_behavior \"directory_sync_admin_behavior: DirectorySyncUserBehavior\", directory_sync_target \"directory_sync_target: DirectorySyncTarget\", okta_private_jwk, okta_dirsync_client_id, directory_sync_group_match, jumpcloud_api_key, prefetch_users FROM openidprovider WHERE name = $1", + "query": "SELECT id, name, base_url, kind \"kind: OpenIdProviderKind\", client_id, client_secret, display_name, google_service_account_key, google_service_account_email, admin_email, directory_sync_enabled,\n directory_sync_interval, directory_sync_user_behavior \"directory_sync_user_behavior: DirectorySyncUserBehavior\", directory_sync_admin_behavior \"directory_sync_admin_behavior: DirectorySyncUserBehavior\", directory_sync_target \"directory_sync_target: DirectorySyncTarget\", okta_private_jwk, okta_dirsync_client_id, directory_sync_group_match, jumpcloud_api_key, prefetch_users FROM openidprovider WHERE name = $1", "describe": { "columns": [ { @@ -20,46 +20,64 @@ }, { "ordinal": 3, + "name": "kind: OpenIdProviderKind", + "type_info": { + "Custom": { + "name": "openid_provider_kind", + "kind": { + "Enum": [ + "Google", + "Microsoft", + "Okta", + "JumpCloud", + "Custom" + ] + } + } + } + }, + { + "ordinal": 4, "name": "client_id", "type_info": "Text" }, { - "ordinal": 4, + "ordinal": 5, "name": "client_secret", "type_info": "Text" }, { - "ordinal": 5, + "ordinal": 6, "name": "display_name", "type_info": "Text" }, { - "ordinal": 6, + "ordinal": 7, "name": "google_service_account_key", "type_info": "Text" }, { - "ordinal": 7, + "ordinal": 8, "name": "google_service_account_email", "type_info": "Text" }, { - "ordinal": 8, + "ordinal": 9, "name": "admin_email", "type_info": "Text" }, { - "ordinal": 9, + "ordinal": 10, "name": "directory_sync_enabled", "type_info": "Bool" }, { - "ordinal": 10, + "ordinal": 11, "name": "directory_sync_interval", "type_info": "Int4" }, { - "ordinal": 11, + "ordinal": 12, "name": "directory_sync_user_behavior: DirectorySyncUserBehavior", "type_info": { "Custom": { @@ -75,7 +93,7 @@ } }, { - "ordinal": 12, + "ordinal": 13, "name": "directory_sync_admin_behavior: DirectorySyncUserBehavior", "type_info": { "Custom": { @@ -91,7 +109,7 @@ } }, { - "ordinal": 13, + "ordinal": 14, "name": "directory_sync_target: DirectorySyncTarget", "type_info": { "Custom": { @@ -107,27 +125,27 @@ } }, { - "ordinal": 14, + "ordinal": 15, "name": "okta_private_jwk", "type_info": "Text" }, { - "ordinal": 15, + "ordinal": 16, "name": "okta_dirsync_client_id", "type_info": "Text" }, { - "ordinal": 16, + "ordinal": 17, "name": "directory_sync_group_match", "type_info": "TextArray" }, { - "ordinal": 17, + "ordinal": 18, "name": "jumpcloud_api_key", "type_info": "Text" }, { - "ordinal": 18, + "ordinal": 19, "name": "prefetch_users", "type_info": "Bool" } @@ -143,6 +161,7 @@ false, false, false, + false, true, true, true, @@ -159,5 +178,5 @@ false ] }, - "hash": "558fb8aa5e223f6fc273c1431410dabb2ca9c2a831cacb7ebc8d696020b0556c" + "hash": "4c259991024808243d03443cb8725da87df96619428383083bded91c2dfdf598" } diff --git a/.sqlx/query-14302b1c6c7d72d6e6f38c80538040b9fa3479c919f1ace2a787470690be9de3.json b/.sqlx/query-7642a85c7e4a299c2ae6b7deba3642ab7efcb9a7375d01ce85078389edf1340b.json similarity index 73% rename from .sqlx/query-14302b1c6c7d72d6e6f38c80538040b9fa3479c919f1ace2a787470690be9de3.json rename to .sqlx/query-7642a85c7e4a299c2ae6b7deba3642ab7efcb9a7375d01ce85078389edf1340b.json index 8ba998d2cb..a40c7d155f 100644 --- a/.sqlx/query-14302b1c6c7d72d6e6f38c80538040b9fa3479c919f1ace2a787470690be9de3.json +++ b/.sqlx/query-7642a85c7e4a299c2ae6b7deba3642ab7efcb9a7375d01ce85078389edf1340b.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, \"name\",\"base_url\",\"client_id\",\"client_secret\",\"display_name\",\"google_service_account_key\",\"google_service_account_email\",\"admin_email\",\"directory_sync_enabled\",\"directory_sync_interval\",\"directory_sync_user_behavior\" \"directory_sync_user_behavior: _\",\"directory_sync_admin_behavior\" \"directory_sync_admin_behavior: _\",\"directory_sync_target\" \"directory_sync_target: _\",\"okta_private_jwk\",\"okta_dirsync_client_id\",\"directory_sync_group_match\" \"directory_sync_group_match: _\",\"jumpcloud_api_key\",\"prefetch_users\" FROM \"openidprovider\" WHERE id = $1", + "query": "SELECT id, \"name\",\"base_url\",\"kind\" \"kind: _\",\"client_id\",\"client_secret\",\"display_name\",\"google_service_account_key\",\"google_service_account_email\",\"admin_email\",\"directory_sync_enabled\",\"directory_sync_interval\",\"directory_sync_user_behavior\" \"directory_sync_user_behavior: _\",\"directory_sync_admin_behavior\" \"directory_sync_admin_behavior: _\",\"directory_sync_target\" \"directory_sync_target: _\",\"okta_private_jwk\",\"okta_dirsync_client_id\",\"directory_sync_group_match\" \"directory_sync_group_match: _\",\"jumpcloud_api_key\",\"prefetch_users\" FROM \"openidprovider\" WHERE id = $1", "describe": { "columns": [ { @@ -20,46 +20,64 @@ }, { "ordinal": 3, + "name": "kind: _", + "type_info": { + "Custom": { + "name": "openid_provider_kind", + "kind": { + "Enum": [ + "Google", + "Microsoft", + "Okta", + "JumpCloud", + "Custom" + ] + } + } + } + }, + { + "ordinal": 4, "name": "client_id", "type_info": "Text" }, { - "ordinal": 4, + "ordinal": 5, "name": "client_secret", "type_info": "Text" }, { - "ordinal": 5, + "ordinal": 6, "name": "display_name", "type_info": "Text" }, { - "ordinal": 6, + "ordinal": 7, "name": "google_service_account_key", "type_info": "Text" }, { - "ordinal": 7, + "ordinal": 8, "name": "google_service_account_email", "type_info": "Text" }, { - "ordinal": 8, + "ordinal": 9, "name": "admin_email", "type_info": "Text" }, { - "ordinal": 9, + "ordinal": 10, "name": "directory_sync_enabled", "type_info": "Bool" }, { - "ordinal": 10, + "ordinal": 11, "name": "directory_sync_interval", "type_info": "Int4" }, { - "ordinal": 11, + "ordinal": 12, "name": "directory_sync_user_behavior: _", "type_info": { "Custom": { @@ -75,7 +93,7 @@ } }, { - "ordinal": 12, + "ordinal": 13, "name": "directory_sync_admin_behavior: _", "type_info": { "Custom": { @@ -91,7 +109,7 @@ } }, { - "ordinal": 13, + "ordinal": 14, "name": "directory_sync_target: _", "type_info": { "Custom": { @@ -107,27 +125,27 @@ } }, { - "ordinal": 14, + "ordinal": 15, "name": "okta_private_jwk", "type_info": "Text" }, { - "ordinal": 15, + "ordinal": 16, "name": "okta_dirsync_client_id", "type_info": "Text" }, { - "ordinal": 16, + "ordinal": 17, "name": "directory_sync_group_match: _", "type_info": "TextArray" }, { - "ordinal": 17, + "ordinal": 18, "name": "jumpcloud_api_key", "type_info": "Text" }, { - "ordinal": 18, + "ordinal": 19, "name": "prefetch_users", "type_info": "Bool" } @@ -143,6 +161,7 @@ false, false, false, + false, true, true, true, @@ -159,5 +178,5 @@ false ] }, - "hash": "14302b1c6c7d72d6e6f38c80538040b9fa3479c919f1ace2a787470690be9de3" + "hash": "7642a85c7e4a299c2ae6b7deba3642ab7efcb9a7375d01ce85078389edf1340b" } diff --git a/.sqlx/query-e28b02ccc616d67fcb1a1aa940ea35be58cb652e8287a6f5028421656048b58a.json b/.sqlx/query-bbff781ddf881678e2d24bf292a2b1506bf33b00c4ef21c0761d6c3a4f6be964.json similarity index 73% rename from .sqlx/query-e28b02ccc616d67fcb1a1aa940ea35be58cb652e8287a6f5028421656048b58a.json rename to .sqlx/query-bbff781ddf881678e2d24bf292a2b1506bf33b00c4ef21c0761d6c3a4f6be964.json index f59dbf3807..6f23e92cc9 100644 --- a/.sqlx/query-e28b02ccc616d67fcb1a1aa940ea35be58cb652e8287a6f5028421656048b58a.json +++ b/.sqlx/query-bbff781ddf881678e2d24bf292a2b1506bf33b00c4ef21c0761d6c3a4f6be964.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, name, base_url, client_id, client_secret, display_name, google_service_account_key, google_service_account_email, admin_email, directory_sync_enabled, directory_sync_interval, directory_sync_user_behavior \"directory_sync_user_behavior: DirectorySyncUserBehavior\", directory_sync_admin_behavior \"directory_sync_admin_behavior: DirectorySyncUserBehavior\", directory_sync_target \"directory_sync_target: DirectorySyncTarget\", okta_private_jwk, okta_dirsync_client_id, directory_sync_group_match, jumpcloud_api_key, prefetch_users FROM openidprovider LIMIT 1", + "query": "SELECT id, name, base_url, kind \"kind: OpenIdProviderKind\", client_id, client_secret, display_name, google_service_account_key, google_service_account_email, admin_email, directory_sync_enabled, directory_sync_interval, directory_sync_user_behavior \"directory_sync_user_behavior: DirectorySyncUserBehavior\", directory_sync_admin_behavior \"directory_sync_admin_behavior: DirectorySyncUserBehavior\", directory_sync_target \"directory_sync_target: DirectorySyncTarget\", okta_private_jwk, okta_dirsync_client_id, directory_sync_group_match, jumpcloud_api_key, prefetch_users FROM openidprovider LIMIT 1", "describe": { "columns": [ { @@ -20,46 +20,64 @@ }, { "ordinal": 3, + "name": "kind: OpenIdProviderKind", + "type_info": { + "Custom": { + "name": "openid_provider_kind", + "kind": { + "Enum": [ + "Google", + "Microsoft", + "Okta", + "JumpCloud", + "Custom" + ] + } + } + } + }, + { + "ordinal": 4, "name": "client_id", "type_info": "Text" }, { - "ordinal": 4, + "ordinal": 5, "name": "client_secret", "type_info": "Text" }, { - "ordinal": 5, + "ordinal": 6, "name": "display_name", "type_info": "Text" }, { - "ordinal": 6, + "ordinal": 7, "name": "google_service_account_key", "type_info": "Text" }, { - "ordinal": 7, + "ordinal": 8, "name": "google_service_account_email", "type_info": "Text" }, { - "ordinal": 8, + "ordinal": 9, "name": "admin_email", "type_info": "Text" }, { - "ordinal": 9, + "ordinal": 10, "name": "directory_sync_enabled", "type_info": "Bool" }, { - "ordinal": 10, + "ordinal": 11, "name": "directory_sync_interval", "type_info": "Int4" }, { - "ordinal": 11, + "ordinal": 12, "name": "directory_sync_user_behavior: DirectorySyncUserBehavior", "type_info": { "Custom": { @@ -75,7 +93,7 @@ } }, { - "ordinal": 12, + "ordinal": 13, "name": "directory_sync_admin_behavior: DirectorySyncUserBehavior", "type_info": { "Custom": { @@ -91,7 +109,7 @@ } }, { - "ordinal": 13, + "ordinal": 14, "name": "directory_sync_target: DirectorySyncTarget", "type_info": { "Custom": { @@ -107,27 +125,27 @@ } }, { - "ordinal": 14, + "ordinal": 15, "name": "okta_private_jwk", "type_info": "Text" }, { - "ordinal": 15, + "ordinal": 16, "name": "okta_dirsync_client_id", "type_info": "Text" }, { - "ordinal": 16, + "ordinal": 17, "name": "directory_sync_group_match", "type_info": "TextArray" }, { - "ordinal": 17, + "ordinal": 18, "name": "jumpcloud_api_key", "type_info": "Text" }, { - "ordinal": 18, + "ordinal": 19, "name": "prefetch_users", "type_info": "Bool" } @@ -141,6 +159,7 @@ false, false, false, + false, true, true, true, @@ -157,5 +176,5 @@ false ] }, - "hash": "e28b02ccc616d67fcb1a1aa940ea35be58cb652e8287a6f5028421656048b58a" + "hash": "bbff781ddf881678e2d24bf292a2b1506bf33b00c4ef21c0761d6c3a4f6be964" } diff --git a/.sqlx/query-c8e9800861c7bc853235858650be8ad3d3d19b0c4f0e69b9002a6a1fbd46a324.json b/.sqlx/query-f6110462d7fd58131e1a2d8cb52711ed9e490ab111a2c0cbb499587f351697b8.json similarity index 57% rename from .sqlx/query-c8e9800861c7bc853235858650be8ad3d3d19b0c4f0e69b9002a6a1fbd46a324.json rename to .sqlx/query-f6110462d7fd58131e1a2d8cb52711ed9e490ab111a2c0cbb499587f351697b8.json index 5c80f34900..7d4d925cbb 100644 --- a/.sqlx/query-c8e9800861c7bc853235858650be8ad3d3d19b0c4f0e69b9002a6a1fbd46a324.json +++ b/.sqlx/query-f6110462d7fd58131e1a2d8cb52711ed9e490ab111a2c0cbb499587f351697b8.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE \"openidprovider\" SET \"name\" = $2,\"base_url\" = $3,\"client_id\" = $4,\"client_secret\" = $5,\"display_name\" = $6,\"google_service_account_key\" = $7,\"google_service_account_email\" = $8,\"admin_email\" = $9,\"directory_sync_enabled\" = $10,\"directory_sync_interval\" = $11,\"directory_sync_user_behavior\" = $12,\"directory_sync_admin_behavior\" = $13,\"directory_sync_target\" = $14,\"okta_private_jwk\" = $15,\"okta_dirsync_client_id\" = $16,\"directory_sync_group_match\" = $17,\"jumpcloud_api_key\" = $18,\"prefetch_users\" = $19 WHERE id = $1", + "query": "UPDATE \"openidprovider\" SET \"name\" = $2,\"base_url\" = $3,\"kind\" = $4,\"client_id\" = $5,\"client_secret\" = $6,\"display_name\" = $7,\"google_service_account_key\" = $8,\"google_service_account_email\" = $9,\"admin_email\" = $10,\"directory_sync_enabled\" = $11,\"directory_sync_interval\" = $12,\"directory_sync_user_behavior\" = $13,\"directory_sync_admin_behavior\" = $14,\"directory_sync_target\" = $15,\"okta_private_jwk\" = $16,\"okta_dirsync_client_id\" = $17,\"directory_sync_group_match\" = $18,\"jumpcloud_api_key\" = $19,\"prefetch_users\" = $20 WHERE id = $1", "describe": { "columns": [], "parameters": { @@ -8,6 +8,20 @@ "Int8", "Text", "Text", + { + "Custom": { + "name": "openid_provider_kind", + "kind": { + "Enum": [ + "Google", + "Microsoft", + "Okta", + "JumpCloud", + "Custom" + ] + } + } + }, "Text", "Text", "Text", @@ -61,5 +75,5 @@ }, "nullable": [] }, - "hash": "c8e9800861c7bc853235858650be8ad3d3d19b0c4f0e69b9002a6a1fbd46a324" + "hash": "f6110462d7fd58131e1a2d8cb52711ed9e490ab111a2c0cbb499587f351697b8" } diff --git a/.sqlx/query-d8db674150231de0063227000a8b39f45c6da9836f93b67307787851a1804f13.json b/.sqlx/query-f851e3667522ee439c614f66e13d89773b783a1b738c453791c45747b11aa106.json similarity index 73% rename from .sqlx/query-d8db674150231de0063227000a8b39f45c6da9836f93b67307787851a1804f13.json rename to .sqlx/query-f851e3667522ee439c614f66e13d89773b783a1b738c453791c45747b11aa106.json index 29d36e4226..c2b15c0aa8 100644 --- a/.sqlx/query-d8db674150231de0063227000a8b39f45c6da9836f93b67307787851a1804f13.json +++ b/.sqlx/query-f851e3667522ee439c614f66e13d89773b783a1b738c453791c45747b11aa106.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, \"name\",\"base_url\",\"client_id\",\"client_secret\",\"display_name\",\"google_service_account_key\",\"google_service_account_email\",\"admin_email\",\"directory_sync_enabled\",\"directory_sync_interval\",\"directory_sync_user_behavior\" \"directory_sync_user_behavior: _\",\"directory_sync_admin_behavior\" \"directory_sync_admin_behavior: _\",\"directory_sync_target\" \"directory_sync_target: _\",\"okta_private_jwk\",\"okta_dirsync_client_id\",\"directory_sync_group_match\" \"directory_sync_group_match: _\",\"jumpcloud_api_key\",\"prefetch_users\" FROM \"openidprovider\"", + "query": "SELECT id, \"name\",\"base_url\",\"kind\" \"kind: _\",\"client_id\",\"client_secret\",\"display_name\",\"google_service_account_key\",\"google_service_account_email\",\"admin_email\",\"directory_sync_enabled\",\"directory_sync_interval\",\"directory_sync_user_behavior\" \"directory_sync_user_behavior: _\",\"directory_sync_admin_behavior\" \"directory_sync_admin_behavior: _\",\"directory_sync_target\" \"directory_sync_target: _\",\"okta_private_jwk\",\"okta_dirsync_client_id\",\"directory_sync_group_match\" \"directory_sync_group_match: _\",\"jumpcloud_api_key\",\"prefetch_users\" FROM \"openidprovider\"", "describe": { "columns": [ { @@ -20,46 +20,64 @@ }, { "ordinal": 3, + "name": "kind: _", + "type_info": { + "Custom": { + "name": "openid_provider_kind", + "kind": { + "Enum": [ + "Google", + "Microsoft", + "Okta", + "JumpCloud", + "Custom" + ] + } + } + } + }, + { + "ordinal": 4, "name": "client_id", "type_info": "Text" }, { - "ordinal": 4, + "ordinal": 5, "name": "client_secret", "type_info": "Text" }, { - "ordinal": 5, + "ordinal": 6, "name": "display_name", "type_info": "Text" }, { - "ordinal": 6, + "ordinal": 7, "name": "google_service_account_key", "type_info": "Text" }, { - "ordinal": 7, + "ordinal": 8, "name": "google_service_account_email", "type_info": "Text" }, { - "ordinal": 8, + "ordinal": 9, "name": "admin_email", "type_info": "Text" }, { - "ordinal": 9, + "ordinal": 10, "name": "directory_sync_enabled", "type_info": "Bool" }, { - "ordinal": 10, + "ordinal": 11, "name": "directory_sync_interval", "type_info": "Int4" }, { - "ordinal": 11, + "ordinal": 12, "name": "directory_sync_user_behavior: _", "type_info": { "Custom": { @@ -75,7 +93,7 @@ } }, { - "ordinal": 12, + "ordinal": 13, "name": "directory_sync_admin_behavior: _", "type_info": { "Custom": { @@ -91,7 +109,7 @@ } }, { - "ordinal": 13, + "ordinal": 14, "name": "directory_sync_target: _", "type_info": { "Custom": { @@ -107,27 +125,27 @@ } }, { - "ordinal": 14, + "ordinal": 15, "name": "okta_private_jwk", "type_info": "Text" }, { - "ordinal": 15, + "ordinal": 16, "name": "okta_dirsync_client_id", "type_info": "Text" }, { - "ordinal": 16, + "ordinal": 17, "name": "directory_sync_group_match: _", "type_info": "TextArray" }, { - "ordinal": 17, + "ordinal": 18, "name": "jumpcloud_api_key", "type_info": "Text" }, { - "ordinal": 18, + "ordinal": 19, "name": "prefetch_users", "type_info": "Bool" } @@ -141,6 +159,7 @@ false, false, false, + false, true, true, true, @@ -157,5 +176,5 @@ false ] }, - "hash": "d8db674150231de0063227000a8b39f45c6da9836f93b67307787851a1804f13" + "hash": "f851e3667522ee439c614f66e13d89773b783a1b738c453791c45747b11aa106" } diff --git a/Cargo.lock b/Cargo.lock index 535698a6ec..5f3bf8f25f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4550,9 +4550,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +checksum = "4910321ebe4151be888e35fe062169554e74aad01beafed60410131420ceffbc" dependencies = [ "web-time", "zeroize", @@ -6273,9 +6273,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ "wit-bindgen", ] @@ -6831,9 +6831,9 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "writeable" diff --git a/crates/defguard_common/src/db/models/settings.rs b/crates/defguard_common/src/db/models/settings.rs index c9d6481721..1801c65274 100644 --- a/crates/defguard_common/src/db/models/settings.rs +++ b/crates/defguard_common/src/db/models/settings.rs @@ -54,7 +54,7 @@ pub enum SmtpEncryption { #[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Serialize, PartialEq, ToSchema, Type)] #[sqlx(type_name = "openid_username_handling", rename_all = "snake_case")] -pub enum OpenidUsernameHandling { +pub enum OpenIdUsernameHandling { #[default] /// Removes all forbidden characters RemoveForbidden, @@ -138,7 +138,7 @@ pub struct Settings { pub ldap_sync_groups: Vec, // Whether to create a new account when users try to log in with external OpenID pub openid_create_account: bool, - pub openid_username_handling: OpenidUsernameHandling, + pub openid_username_handling: OpenIdUsernameHandling, pub license: Option, // Gateway disconnect notifications pub gateway_disconnect_notifications_enabled: bool, @@ -252,7 +252,7 @@ impl Settings { ldap_enabled, ldap_sync_enabled, ldap_is_authoritative, \ ldap_sync_interval, ldap_user_auxiliary_obj_classes, ldap_uses_ad, \ ldap_user_rdn_attr, ldap_sync_groups, \ - openid_username_handling \"openid_username_handling: OpenidUsernameHandling\", \ + openid_username_handling \"openid_username_handling: OpenIdUsernameHandling\", \ ca_key_der, ca_cert_der \ FROM \"settings\" WHERE id = 1", ) @@ -381,7 +381,7 @@ impl Settings { self.ldap_uses_ad, self.ldap_user_rdn_attr, &self.ldap_sync_groups as &Vec, - &self.openid_username_handling as &OpenidUsernameHandling, + &self.openid_username_handling as &OpenIdUsernameHandling, &self.ca_key_der as &Option>, &self.ca_cert_der as &Option>, ) diff --git a/crates/defguard_core/src/db/models/activity_log/metadata.rs b/crates/defguard_core/src/db/models/activity_log/metadata.rs index 582897cc30..adeb3888e3 100644 --- a/crates/defguard_core/src/db/models/activity_log/metadata.rs +++ b/crates/defguard_core/src/db/models/activity_log/metadata.rs @@ -6,7 +6,7 @@ use defguard_common::db::{ WireguardNetwork, group::Group, oauth2client::OAuth2Client, - settings::{LdapSyncStatus, OpenidUsernameHandling, SmtpEncryption}, + settings::{LdapSyncStatus, OpenIdUsernameHandling, SmtpEncryption}, user::User, }, }; @@ -391,7 +391,7 @@ pub struct SettingsNoSecrets { pub ldap_sync_groups: Vec, // Whether to create a new account when users try to log in with external OpenID pub openid_create_account: bool, - pub openid_username_handling: OpenidUsernameHandling, + pub openid_username_handling: OpenIdUsernameHandling, pub license: Option, // Gateway disconnect notifications pub gateway_disconnect_notifications_enabled: bool, diff --git a/crates/defguard_core/src/enterprise/db/models/openid_provider.rs b/crates/defguard_core/src/enterprise/db/models/openid_provider.rs index 8279b29a2c..936821a0cb 100644 --- a/crates/defguard_core/src/enterprise/db/models/openid_provider.rs +++ b/crates/defguard_core/src/enterprise/db/models/openid_provider.rs @@ -3,6 +3,7 @@ use std::fmt; use defguard_common::db::{Id, NoId}; use model_derive::Model; use sqlx::{Error as SqlxError, PgExecutor, PgPool, Type, query, query_as}; +use utoipa::ToSchema; // The behavior when a user is deleted from the directory // Keep: Keep the user, despite being deleted from the external provider's directory @@ -86,11 +87,23 @@ impl From for DirectorySyncTarget { } } -#[derive(Clone, Debug, Deserialize, Model, Serialize, PartialEq)] +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, ToSchema, Type)] +#[sqlx(type_name = "openid_provider_kind")] +pub enum OpenIdProviderKind { + Google, + Microsoft, + Okta, + JumpCloud, + Custom, +} + +#[derive(Clone, Debug, Deserialize, Model, PartialEq, Serialize)] pub struct OpenIdProvider { pub id: I, pub name: String, pub base_url: String, + #[model(enum)] + pub kind: OpenIdProviderKind, pub client_id: String, pub client_secret: String, pub display_name: Option, @@ -125,6 +138,7 @@ impl OpenIdProvider { pub fn new>( name: S, base_url: S, + kind: OpenIdProviderKind, client_id: S, client_secret: S, display_name: Option, @@ -146,6 +160,7 @@ impl OpenIdProvider { id: NoId, name: name.into(), base_url: base_url.into(), + kind, client_id: client_id.into(), client_secret: client_secret.into(), display_name, @@ -214,7 +229,7 @@ impl OpenIdProvider { { query_as!( OpenIdProvider, - "SELECT id, name, base_url, client_id, client_secret, display_name, \ + "SELECT id, name, base_url, kind \"kind: OpenIdProviderKind\", client_id, client_secret, display_name, \ google_service_account_key, google_service_account_email, admin_email, directory_sync_enabled, directory_sync_interval, directory_sync_user_behavior \"directory_sync_user_behavior: DirectorySyncUserBehavior\", \ directory_sync_admin_behavior \"directory_sync_admin_behavior: DirectorySyncUserBehavior\", \ @@ -233,7 +248,7 @@ impl OpenIdProvider { { query_as!( OpenIdProvider, - "SELECT id, name, base_url, client_id, client_secret, display_name, \ + "SELECT id, name, base_url, kind \"kind: OpenIdProviderKind\", client_id, client_secret, display_name, \ google_service_account_key, google_service_account_email, admin_email, directory_sync_enabled, \ directory_sync_interval, directory_sync_user_behavior \"directory_sync_user_behavior: DirectorySyncUserBehavior\", \ directory_sync_admin_behavior \"directory_sync_admin_behavior: DirectorySyncUserBehavior\", \ diff --git a/crates/defguard_core/src/enterprise/directory_sync/tests.rs b/crates/defguard_core/src/enterprise/directory_sync/tests.rs index 1d02ea3d21..c3e6079240 100644 --- a/crates/defguard_core/src/enterprise/directory_sync/tests.rs +++ b/crates/defguard_core/src/enterprise/directory_sync/tests.rs @@ -19,7 +19,7 @@ mod test { use tokio::sync::broadcast; use super::super::*; - use crate::enterprise::db::models::openid_provider::DirectorySyncTarget; + use crate::enterprise::db::models::openid_provider::{DirectorySyncTarget, OpenIdProviderKind}; async fn get_test_network(pool: &PgPool) -> WireguardNetwork { WireguardNetwork::find_by_name(pool, "test") @@ -69,6 +69,7 @@ mod test { OpenIdProvider::new( "Test".to_string(), "base_url".to_string(), + OpenIdProviderKind::Google, "client_id".to_string(), "client_secret".to_string(), Some("display_name".to_string()), diff --git a/crates/defguard_core/src/enterprise/handlers/openid_login.rs b/crates/defguard_core/src/enterprise/handlers/openid_login.rs index b2ab080333..e894f426f0 100644 --- a/crates/defguard_core/src/enterprise/handlers/openid_login.rs +++ b/crates/defguard_core/src/enterprise/handlers/openid_login.rs @@ -13,7 +13,7 @@ use defguard_common::{ config::server_config, db::{ Id, - models::{Settings, settings::OpenidUsernameHandling, user::User}, + models::{Settings, settings::OpenIdUsernameHandling, user::User}, }, }; use openidconnect::{ @@ -59,7 +59,7 @@ use crate::{ /// - only special characters allowed: . - _ /// - no whitespaces #[must_use] -pub fn prune_username(username: &str, handling: OpenidUsernameHandling) -> String { +pub fn prune_username(username: &str, handling: OpenIdUsernameHandling) -> String { let mut result = username.to_string(); // Go through the string and remove any non-alphanumeric characters at the beginning @@ -70,16 +70,16 @@ pub fn prune_username(username: &str, handling: OpenidUsernameHandling) -> Strin let is_char_valid = |c: char| c.is_ascii_alphanumeric() || c == '.' || c == '-' || c == '_'; match handling { - OpenidUsernameHandling::RemoveForbidden => { + OpenIdUsernameHandling::RemoveForbidden => { result.retain(&is_char_valid); } - OpenidUsernameHandling::ReplaceForbidden => { + OpenIdUsernameHandling::ReplaceForbidden => { result = result .chars() .map(|c| if is_char_valid(c) { c } else { '_' }) .collect(); } - OpenidUsernameHandling::PruneEmailDomain => { + OpenIdUsernameHandling::PruneEmailDomain => { if let Some(at_index) = result.find('@') { result.truncate(at_index); } @@ -658,7 +658,7 @@ mod test { #[test] fn test_prune_username() { // Test RemoveForbidden handling - let handling_remove = OpenidUsernameHandling::RemoveForbidden; + let handling_remove = OpenIdUsernameHandling::RemoveForbidden; assert_eq!(prune_username("zenek", handling_remove), "zenek"); assert_eq!(prune_username("zenek34", handling_remove), "zenek34"); assert_eq!(prune_username("zenek@34", handling_remove), "zenek34"); @@ -679,7 +679,7 @@ mod test { assert_eq!(prune_username("...zenek", handling_remove), "zenek"); // Test ReplaceForbidden handling - let handling_replace = OpenidUsernameHandling::ReplaceForbidden; + let handling_replace = OpenIdUsernameHandling::ReplaceForbidden; assert_eq!(prune_username("zenek", handling_replace), "zenek"); assert_eq!(prune_username("zenek34", handling_replace), "zenek34"); assert_eq!(prune_username("zenek@34", handling_replace), "zenek_34"); @@ -696,7 +696,7 @@ mod test { ); // Test PruneEmailDomain handling - let handling_prune_email = OpenidUsernameHandling::PruneEmailDomain; + let handling_prune_email = OpenIdUsernameHandling::PruneEmailDomain; assert_eq!( prune_username("zenek@example.com", handling_prune_email), "zenek" diff --git a/crates/defguard_core/src/enterprise/handlers/openid_providers.rs b/crates/defguard_core/src/enterprise/handlers/openid_providers.rs index e559418c39..8524173b1d 100644 --- a/crates/defguard_core/src/enterprise/handlers/openid_providers.rs +++ b/crates/defguard_core/src/enterprise/handlers/openid_providers.rs @@ -5,7 +5,7 @@ use axum::{ }; use defguard_common::db::models::{ Settings, WireguardNetwork, - settings::{OpenidUsernameHandling, update_current_settings}, + settings::{OpenIdUsernameHandling, update_current_settings}, wireguard::LocationMfaMode, }; use rsa::{RsaPrivateKey, pkcs8::DecodePrivateKey}; @@ -17,7 +17,8 @@ use crate::{ appstate::AppState, auth::{AdminRole, SessionInfo}, enterprise::{ - db::models::openid_provider::OpenIdProvider, directory_sync::test_directory_sync_connection, + db::models::openid_provider::{OpenIdProvider, OpenIdProviderKind}, + directory_sync::test_directory_sync_connection, }, events::{ApiEvent, ApiEventType, ApiRequestContext}, handlers::{ApiResponse, ApiResult}, @@ -27,6 +28,7 @@ use crate::{ pub struct AddProviderData { pub name: String, pub base_url: String, + pub kind: OpenIdProviderKind, pub client_id: String, pub client_secret: String, pub display_name: Option, @@ -38,13 +40,14 @@ pub struct AddProviderData { pub directory_sync_user_behavior: String, pub directory_sync_admin_behavior: String, pub directory_sync_target: String, - pub create_account: bool, pub okta_private_jwk: Option, pub okta_dirsync_client_id: Option, pub directory_sync_group_match: Option, - pub username_handling: OpenidUsernameHandling, pub jumpcloud_api_key: Option, pub prefetch_users: bool, + // Core settings + pub create_account: bool, + pub username_handling: OpenIdUsernameHandling, } /// Add OpenID provider. @@ -156,6 +159,7 @@ pub(crate) async fn add_openid_provider( let new_provider = OpenIdProvider::new( provider_data.name, provider_data.base_url, + provider_data.kind, provider_data.client_id, provider_data.client_secret, provider_data.display_name, @@ -337,6 +341,7 @@ pub(crate) async fn modify_openid_provider( let provider = OpenIdProvider::find_by_name(&mut *transaction, &provider_data.name).await?; if let Some(mut provider) = provider { provider.base_url = provider_data.base_url; + provider.kind = provider_data.kind; provider.client_id = provider_data.client_id; provider.client_secret = provider_data.client_secret; provider.save(&mut *transaction).await?; diff --git a/crates/defguard_core/tests/integration/api/openid_login.rs b/crates/defguard_core/tests/integration/api/openid_login.rs index 2810c54772..69453455d9 100644 --- a/crates/defguard_core/tests/integration/api/openid_login.rs +++ b/crates/defguard_core/tests/integration/api/openid_login.rs @@ -1,11 +1,13 @@ use chrono::{Duration, Utc}; use defguard_common::db::{ Id, - models::{oauth2client::OAuth2Client, settings::OpenidUsernameHandling}, + models::{oauth2client::OAuth2Client, settings::OpenIdUsernameHandling}, }; use defguard_core::{ enterprise::{ - db::models::openid_provider::{DirectorySyncTarget, DirectorySyncUserBehavior}, + db::models::openid_provider::{ + DirectorySyncTarget, DirectorySyncUserBehavior, OpenIdProviderKind, + }, handlers::openid_providers::AddProviderData, license::{License, LicenseTier, set_cached_license}, }, @@ -38,6 +40,7 @@ async fn test_openid_providers(_: PgPoolOptions, options: PgConnectOptions) { name: "test".to_string(), // FIXME: this won't work offline. base_url: "https://accounts.google.com".to_string(), + kind: OpenIdProviderKind::Google, client_id: "client_id".to_string(), client_secret: "client_secret".to_string(), display_name: Some("display_name".to_string()), @@ -53,7 +56,7 @@ async fn test_openid_providers(_: PgPoolOptions, options: PgConnectOptions) { okta_dirsync_client_id: None, okta_private_jwk: None, directory_sync_group_match: None, - username_handling: OpenidUsernameHandling::PruneEmailDomain, + username_handling: OpenIdUsernameHandling::PruneEmailDomain, jumpcloud_api_key: None, prefetch_users: false, }; @@ -140,6 +143,7 @@ async fn test_openid_login(_: PgPoolOptions, options: PgConnectOptions) { let provider_data = AddProviderData { name: "Custom".into(), base_url: url, + kind: OpenIdProviderKind::Custom, client_id: openid_client.client_id.clone(), client_secret: openid_client.client_secret.clone(), display_name: Some("Defguard".to_string()), @@ -155,7 +159,7 @@ async fn test_openid_login(_: PgPoolOptions, options: PgConnectOptions) { okta_dirsync_client_id: None, okta_private_jwk: None, directory_sync_group_match: None, - username_handling: OpenidUsernameHandling::PruneEmailDomain, + username_handling: OpenIdUsernameHandling::PruneEmailDomain, jumpcloud_api_key: None, prefetch_users: false, }; diff --git a/crates/defguard_core/tests/integration/api/wireguard.rs b/crates/defguard_core/tests/integration/api/wireguard.rs index 80a213f5fa..69667beeb7 100644 --- a/crates/defguard_core/tests/integration/api/wireguard.rs +++ b/crates/defguard_core/tests/integration/api/wireguard.rs @@ -5,7 +5,7 @@ use defguard_common::db::{ models::{ Device, WireguardNetwork, device::WireguardNetworkDevice, - settings::OpenidUsernameHandling, + settings::OpenIdUsernameHandling, wireguard::{ DEFAULT_DISCONNECT_THRESHOLD, DEFAULT_KEEPALIVE_INTERVAL, LocationMfaMode, ServiceLocationMode, @@ -14,7 +14,9 @@ use defguard_common::db::{ }; use defguard_core::{ enterprise::{ - db::models::openid_provider::{DirectorySyncTarget, DirectorySyncUserBehavior}, + db::models::openid_provider::{ + DirectorySyncTarget, DirectorySyncUserBehavior, OpenIdProviderKind, + }, handlers::openid_providers::AddProviderData, license::{get_cached_license, set_cached_license}, }, @@ -177,6 +179,7 @@ async fn test_location_mfa_mode_validation_create(_: PgPoolOptions, options: PgC let provider_data = AddProviderData { name: "test".to_string(), base_url: "https://accounts.google.com".to_string(), + kind: OpenIdProviderKind::Custom, client_id: "client_id".to_string(), client_secret: "client_secret".to_string(), display_name: Some("display_name".to_string()), @@ -192,7 +195,7 @@ async fn test_location_mfa_mode_validation_create(_: PgPoolOptions, options: PgC okta_dirsync_client_id: None, okta_private_jwk: None, directory_sync_group_match: None, - username_handling: OpenidUsernameHandling::PruneEmailDomain, + username_handling: OpenIdUsernameHandling::PruneEmailDomain, jumpcloud_api_key: None, prefetch_users: false, }; @@ -275,6 +278,7 @@ async fn test_location_mfa_mode_validation_modify(_: PgPoolOptions, options: PgC let provider_data = AddProviderData { name: "test".to_string(), base_url: "https://accounts.google.com".to_string(), + kind: OpenIdProviderKind::Google, client_id: "client_id".to_string(), client_secret: "client_secret".to_string(), display_name: Some("display_name".to_string()), @@ -290,7 +294,7 @@ async fn test_location_mfa_mode_validation_modify(_: PgPoolOptions, options: PgC okta_dirsync_client_id: None, okta_private_jwk: None, directory_sync_group_match: None, - username_handling: OpenidUsernameHandling::PruneEmailDomain, + username_handling: OpenIdUsernameHandling::PruneEmailDomain, jumpcloud_api_key: None, prefetch_users: false, }; diff --git a/migrations/20260115123545_[2.0.0]_openid_provider_kind.down.sql b/migrations/20260115123545_[2.0.0]_openid_provider_kind.down.sql new file mode 100644 index 0000000000..9451ea998e --- /dev/null +++ b/migrations/20260115123545_[2.0.0]_openid_provider_kind.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE openidprovider DROP COLUMN kind; +DROP TYPE openid_provider_kind; diff --git a/migrations/20260115123545_[2.0.0]_openid_provider_kind.up.sql b/migrations/20260115123545_[2.0.0]_openid_provider_kind.up.sql new file mode 100644 index 0000000000..e0867787b9 --- /dev/null +++ b/migrations/20260115123545_[2.0.0]_openid_provider_kind.up.sql @@ -0,0 +1,9 @@ +CREATE TYPE openid_provider_kind AS ENUM ( + 'Google', + 'Microsoft', + 'Okta', + 'JumpCloud', + 'Custom' +); + +ALTER TABLE openidprovider ADD COLUMN kind openid_provider_kind NOT NULL DEFAULT 'Custom'::openid_provider_kind; diff --git a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/GoogleProviderForm.tsx b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/GoogleProviderForm.tsx index ef1ed1b27e..6b81990867 100644 --- a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/GoogleProviderForm.tsx +++ b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/GoogleProviderForm.tsx @@ -3,6 +3,7 @@ import { omit } from 'lodash-es'; import { useCallback, useMemo } from 'react'; import type z from 'zod'; import { m } from '../../../../../paraglide/messages'; +import { OpenIdProviderKind } from '../../../../../shared/api/types'; import { DescriptionBlock } from '../../../../../shared/components/DescriptionBlock/DescriptionBlock'; import { EvenSplit } from '../../../../../shared/defguard-ui/components/EvenSplit/EvenSplit'; import { SizedBox } from '../../../../../shared/defguard-ui/components/SizedBox/SizedBox'; @@ -59,6 +60,7 @@ export const GoogleProviderForm = ({ onSubmit }: ProviderFormProps) => { if (isPresent(fileData)) { await onSubmit({ ...value, + kind: OpenIdProviderKind.Google, google_service_account_email: fileData?.client_email ?? '', google_service_account_key: fileData?.private_key ?? '', }); @@ -81,6 +83,7 @@ export const GoogleProviderForm = ({ onSubmit }: ProviderFormProps) => { const fileData = await parseGoogleKeyFile(state.google_service_account_file as File); return { ...omit(state, ['google_service_account_file']), + kind: OpenIdProviderKind.Google, google_service_account_key: fileData?.private_key ?? null, google_service_account_email: fileData?.client_email ?? null, }; diff --git a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/JumpcloudProviderForm.tsx b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/JumpcloudProviderForm.tsx index ee51131d65..123291a1d1 100644 --- a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/JumpcloudProviderForm.tsx +++ b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/JumpcloudProviderForm.tsx @@ -2,6 +2,7 @@ import { useMutation } from '@tanstack/react-query'; import { useMemo } from 'react'; import type z from 'zod'; import { useShallow } from 'zustand/react/shallow'; +import { OpenIdProviderKind } from '../../../../../shared/api/types'; import { EvenSplit } from '../../../../../shared/defguard-ui/components/EvenSplit/EvenSplit'; import { SizedBox } from '../../../../../shared/defguard-ui/components/SizedBox/SizedBox'; import { ThemeSpacing } from '../../../../../shared/defguard-ui/types'; @@ -34,6 +35,7 @@ export const JumpcloudProviderForm = ({ onSubmit }: ProviderFormProps) => { directory_sync_target: providerState.directory_sync_target, directory_sync_user_behavior: providerState.directory_sync_user_behavior, jumpcloud_api_key: providerState.jumpcloud_api_key ?? '', + kind: OpenIdProviderKind.JumpCloud, }), [providerState], ); @@ -46,7 +48,10 @@ export const JumpcloudProviderForm = ({ onSubmit }: ProviderFormProps) => { onChange: jumpcloudProviderSyncSchema, }, onSubmit: async ({ value }) => { - await onSubmit(value); + await onSubmit({ + ...value, + kind: OpenIdProviderKind.JumpCloud, + }); }, }); diff --git a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/MicrosoftProviderForm.tsx b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/MicrosoftProviderForm.tsx index 2b3fb8c9bb..0d78e0b866 100644 --- a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/MicrosoftProviderForm.tsx +++ b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/MicrosoftProviderForm.tsx @@ -1,6 +1,7 @@ import { useMutation } from '@tanstack/react-query'; import { useMemo } from 'react'; import type z from 'zod'; +import { OpenIdProviderKind } from '../../../../../shared/api/types'; import { EvenSplit } from '../../../../../shared/defguard-ui/components/EvenSplit/EvenSplit'; import { SizedBox } from '../../../../../shared/defguard-ui/components/SizedBox/SizedBox'; import { ThemeSpacing } from '../../../../../shared/defguard-ui/types'; @@ -46,7 +47,10 @@ export const MicrosoftProviderForm = ({ onSubmit }: ProviderFormProps) => { onChange: microsoftProviderSyncSchema, }, onSubmit: async ({ value }) => { - await onSubmit(value); + await onSubmit({ + ...value, + kind: OpenIdProviderKind.Microsoft, + }); }, }); diff --git a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/OktaProviderForm.tsx b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/OktaProviderForm.tsx index dce561626b..ddf5249df1 100644 --- a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/OktaProviderForm.tsx +++ b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/OktaProviderForm.tsx @@ -1,6 +1,7 @@ import { useMutation } from '@tanstack/react-query'; import { useMemo } from 'react'; import type z from 'zod'; +import { OpenIdProviderKind } from '../../../../../shared/api/types'; import { EvenSplit } from '../../../../../shared/defguard-ui/components/EvenSplit/EvenSplit'; import { SizedBox } from '../../../../../shared/defguard-ui/components/SizedBox/SizedBox'; import { ThemeSpacing } from '../../../../../shared/defguard-ui/types'; @@ -46,7 +47,10 @@ export const OktaProviderForm = ({ onSubmit }: ProviderFormProps) => { onChange: oktaProviderSyncSchema, }, onSubmit: async ({ value }) => { - await onSubmit(value); + await onSubmit({ + ...value, + kind: OpenIdProviderKind.Okta, + }); }, }); diff --git a/web/src/pages/AddExternalOpenIdWizardPage/useAddExternalOpenIdStore.tsx b/web/src/pages/AddExternalOpenIdWizardPage/useAddExternalOpenIdStore.tsx index 93888f78fa..3ac15e1df0 100644 --- a/web/src/pages/AddExternalOpenIdWizardPage/useAddExternalOpenIdStore.tsx +++ b/web/src/pages/AddExternalOpenIdWizardPage/useAddExternalOpenIdStore.tsx @@ -5,6 +5,7 @@ import { type AddOpenIdProvider, DirectorySyncBehavior, DirectorySyncTarget, + OpenIdProviderKind, OpenIdProviderUsernameHandling, } from '../../shared/api/types'; import { @@ -35,26 +36,30 @@ export const addExternalOpenIdStoreDefaults: StoreValues = { testMessage: null, providerState: { name: ExternalProvider.Custom, - display_name: '', - admin_email: '', base_url: '', + kind: OpenIdProviderKind.Custom, client_id: '', client_secret: '', - create_account: false, - microsoftTenantId: null, - directory_sync_group_match: null, - google_service_account_email: null, + display_name: '', google_service_account_key: null, - okta_dirsync_client_id: null, - okta_private_jwk: null, - jumpcloud_api_key: null, + google_service_account_email: null, + admin_email: '', directory_sync_enabled: false, directory_sync_interval: 600, - directory_sync_target: DirectorySyncTarget.All, - directory_sync_admin_behavior: DirectorySyncBehavior.Keep, directory_sync_user_behavior: DirectorySyncBehavior.Keep, + directory_sync_admin_behavior: DirectorySyncBehavior.Keep, + directory_sync_target: DirectorySyncTarget.All, + okta_private_jwk: null, + okta_dirsync_client_id: null, + directory_sync_group_match: null, + jumpcloud_api_key: null, prefetch_users: false, + + // Core settings + create_account: false, username_handling: OpenIdProviderUsernameHandling.RemoveForbidden, + + microsoftTenantId: null, }, }; diff --git a/web/src/shared/api/types.ts b/web/src/shared/api/types.ts index a4c1cb140f..6a3cfec662 100644 --- a/web/src/shared/api/types.ts +++ b/web/src/shared/api/types.ts @@ -679,6 +679,17 @@ export interface OpenIdProviderSettings { username_handling: OpenIdProviderUsernameHandlingValue; } +export const OpenIdProviderKind = { + Google: 'Google', + Microsoft: 'Microsoft', + Okta: 'Okta', + JumpCloud: 'JumpCloud', + Custom: 'Custom', +} as const; + +export type OpenIdProviderKindValue = + (typeof OpenIdProviderKind)[keyof typeof OpenIdProviderKind]; + export const DirectorySyncBehavior = { Keep: 'keep', Disable: 'disable', @@ -710,22 +721,22 @@ export interface OpenIdProvider { id: number; name: ExternalProviderValue; base_url: string; + kind: OpenIdProviderKindValue; client_id: string; client_secret: string; display_name: string; + google_service_account_key?: string | null; + google_service_account_email?: string | null; + admin_email?: string | null; directory_sync_enabled: boolean; directory_sync_interval: number; directory_sync_user_behavior: DirectorySyncBehaviorValue; directory_sync_admin_behavior: DirectorySyncBehaviorValue; directory_sync_target: DirectorySyncTargetValue; - google_service_account_key?: string | null; - google_service_account_email?: string | null; - admin_email?: string | null; okta_private_jwk?: string | null; okta_dirsync_client_id?: string | null; - jumpcloud_api_key?: string | null; - // microsoft directory_sync_group_match?: string | null; + jumpcloud_api_key?: string | null; prefetch_users: boolean; } From 379cde33e6a5be9113d19f4f2353226283d7d9d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Fri, 16 Jan 2026 14:07:23 +0100 Subject: [PATCH 2/5] Remove unwanted line --- .../forms/JumpcloudProviderForm.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/JumpcloudProviderForm.tsx b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/JumpcloudProviderForm.tsx index 123291a1d1..5ebe53c7a0 100644 --- a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/JumpcloudProviderForm.tsx +++ b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/JumpcloudProviderForm.tsx @@ -35,7 +35,6 @@ export const JumpcloudProviderForm = ({ onSubmit }: ProviderFormProps) => { directory_sync_target: providerState.directory_sync_target, directory_sync_user_behavior: providerState.directory_sync_user_behavior, jumpcloud_api_key: providerState.jumpcloud_api_key ?? '', - kind: OpenIdProviderKind.JumpCloud, }), [providerState], ); From e24b8d4b4c815232eb9887f1a5a4ca158b3bbe04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Fri, 16 Jan 2026 17:01:04 +0100 Subject: [PATCH 3/5] Re-factor --- ...dd49aadbd2516b1ff4fb962d1fc2437401064.json | 3 +- ...25da87df96619428383083bded91c2dfdf598.json | 3 +- ...642ab7efcb9a7375d01ce85078389edf1340b.json | 3 +- ...2b1506bf33b00c4ef21c0761d6c3a4f6be964.json | 3 +- ...711ed9e490ab111a2c0cbb499587f351697b8.json | 3 +- ...d89773b783a1b738c453791c45747b11aa106.json | 3 +- .../enterprise/db/models/openid_provider.rs | 3 +- ...123545_[2.0.0]_openid_provider_kind.up.sql | 3 +- .../AddExternalOpenIdClientSettingsStep.tsx | 22 ++++++------- .../AddExternalOpenIdDirectoryStep.tsx | 8 ++--- .../forms/GoogleProviderForm.tsx | 3 -- .../forms/JumpcloudProviderForm.tsx | 6 +--- .../forms/MicrosoftProviderForm.tsx | 6 +--- .../forms/OktaProviderForm.tsx | 6 +--- .../useAddExternalOpenIdStore.tsx | 22 ++++++------- .../SettingsEditOpenIdProviderPage.tsx | 13 ++++---- .../SettingsExternalOpenIdPage.tsx | 11 ++++--- .../ExternalProviderCard.tsx | 32 +++++++++---------- web/src/pages/settings/shared/types.ts | 11 ------- web/src/shared/api/types.ts | 6 ++-- web/src/shared/constants.ts | 29 ++++++++--------- 21 files changed, 90 insertions(+), 109 deletions(-) delete mode 100644 web/src/pages/settings/shared/types.ts diff --git a/.sqlx/query-1332e9c8c375774902845e5bebddd49aadbd2516b1ff4fb962d1fc2437401064.json b/.sqlx/query-1332e9c8c375774902845e5bebddd49aadbd2516b1ff4fb962d1fc2437401064.json index c484209476..0cd4df0c7e 100644 --- a/.sqlx/query-1332e9c8c375774902845e5bebddd49aadbd2516b1ff4fb962d1fc2437401064.json +++ b/.sqlx/query-1332e9c8c375774902845e5bebddd49aadbd2516b1ff4fb962d1fc2437401064.json @@ -18,11 +18,12 @@ "name": "openid_provider_kind", "kind": { "Enum": [ + "Custom", "Google", "Microsoft", "Okta", "JumpCloud", - "Custom" + "Zitadel" ] } } diff --git a/.sqlx/query-4c259991024808243d03443cb8725da87df96619428383083bded91c2dfdf598.json b/.sqlx/query-4c259991024808243d03443cb8725da87df96619428383083bded91c2dfdf598.json index 55cbebf320..fb787d4617 100644 --- a/.sqlx/query-4c259991024808243d03443cb8725da87df96619428383083bded91c2dfdf598.json +++ b/.sqlx/query-4c259991024808243d03443cb8725da87df96619428383083bded91c2dfdf598.json @@ -26,11 +26,12 @@ "name": "openid_provider_kind", "kind": { "Enum": [ + "Custom", "Google", "Microsoft", "Okta", "JumpCloud", - "Custom" + "Zitadel" ] } } diff --git a/.sqlx/query-7642a85c7e4a299c2ae6b7deba3642ab7efcb9a7375d01ce85078389edf1340b.json b/.sqlx/query-7642a85c7e4a299c2ae6b7deba3642ab7efcb9a7375d01ce85078389edf1340b.json index a40c7d155f..075f8cf098 100644 --- a/.sqlx/query-7642a85c7e4a299c2ae6b7deba3642ab7efcb9a7375d01ce85078389edf1340b.json +++ b/.sqlx/query-7642a85c7e4a299c2ae6b7deba3642ab7efcb9a7375d01ce85078389edf1340b.json @@ -26,11 +26,12 @@ "name": "openid_provider_kind", "kind": { "Enum": [ + "Custom", "Google", "Microsoft", "Okta", "JumpCloud", - "Custom" + "Zitadel" ] } } diff --git a/.sqlx/query-bbff781ddf881678e2d24bf292a2b1506bf33b00c4ef21c0761d6c3a4f6be964.json b/.sqlx/query-bbff781ddf881678e2d24bf292a2b1506bf33b00c4ef21c0761d6c3a4f6be964.json index 6f23e92cc9..351367d6ff 100644 --- a/.sqlx/query-bbff781ddf881678e2d24bf292a2b1506bf33b00c4ef21c0761d6c3a4f6be964.json +++ b/.sqlx/query-bbff781ddf881678e2d24bf292a2b1506bf33b00c4ef21c0761d6c3a4f6be964.json @@ -26,11 +26,12 @@ "name": "openid_provider_kind", "kind": { "Enum": [ + "Custom", "Google", "Microsoft", "Okta", "JumpCloud", - "Custom" + "Zitadel" ] } } diff --git a/.sqlx/query-f6110462d7fd58131e1a2d8cb52711ed9e490ab111a2c0cbb499587f351697b8.json b/.sqlx/query-f6110462d7fd58131e1a2d8cb52711ed9e490ab111a2c0cbb499587f351697b8.json index 7d4d925cbb..789a2f2782 100644 --- a/.sqlx/query-f6110462d7fd58131e1a2d8cb52711ed9e490ab111a2c0cbb499587f351697b8.json +++ b/.sqlx/query-f6110462d7fd58131e1a2d8cb52711ed9e490ab111a2c0cbb499587f351697b8.json @@ -13,11 +13,12 @@ "name": "openid_provider_kind", "kind": { "Enum": [ + "Custom", "Google", "Microsoft", "Okta", "JumpCloud", - "Custom" + "Zitadel" ] } } diff --git a/.sqlx/query-f851e3667522ee439c614f66e13d89773b783a1b738c453791c45747b11aa106.json b/.sqlx/query-f851e3667522ee439c614f66e13d89773b783a1b738c453791c45747b11aa106.json index c2b15c0aa8..088bb362dc 100644 --- a/.sqlx/query-f851e3667522ee439c614f66e13d89773b783a1b738c453791c45747b11aa106.json +++ b/.sqlx/query-f851e3667522ee439c614f66e13d89773b783a1b738c453791c45747b11aa106.json @@ -26,11 +26,12 @@ "name": "openid_provider_kind", "kind": { "Enum": [ + "Custom", "Google", "Microsoft", "Okta", "JumpCloud", - "Custom" + "Zitadel" ] } } diff --git a/crates/defguard_core/src/enterprise/db/models/openid_provider.rs b/crates/defguard_core/src/enterprise/db/models/openid_provider.rs index 936821a0cb..a2a4b4c466 100644 --- a/crates/defguard_core/src/enterprise/db/models/openid_provider.rs +++ b/crates/defguard_core/src/enterprise/db/models/openid_provider.rs @@ -90,11 +90,12 @@ impl From for DirectorySyncTarget { #[derive(Clone, Debug, Deserialize, PartialEq, Serialize, ToSchema, Type)] #[sqlx(type_name = "openid_provider_kind")] pub enum OpenIdProviderKind { + Custom, Google, Microsoft, Okta, JumpCloud, - Custom, + Zitadel, } #[derive(Clone, Debug, Deserialize, Model, PartialEq, Serialize)] diff --git a/migrations/20260115123545_[2.0.0]_openid_provider_kind.up.sql b/migrations/20260115123545_[2.0.0]_openid_provider_kind.up.sql index e0867787b9..20dee5a954 100644 --- a/migrations/20260115123545_[2.0.0]_openid_provider_kind.up.sql +++ b/migrations/20260115123545_[2.0.0]_openid_provider_kind.up.sql @@ -1,9 +1,10 @@ CREATE TYPE openid_provider_kind AS ENUM ( + 'Custom', 'Google', 'Microsoft', 'Okta', 'JumpCloud', - 'Custom' + 'Zitadel' ); ALTER TABLE openidprovider ADD COLUMN kind openid_provider_kind NOT NULL DEFAULT 'Custom'::openid_provider_kind; diff --git a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdClientSettingsStep/AddExternalOpenIdClientSettingsStep.tsx b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdClientSettingsStep/AddExternalOpenIdClientSettingsStep.tsx index ccdd07a92f..8038e5e75e 100644 --- a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdClientSettingsStep/AddExternalOpenIdClientSettingsStep.tsx +++ b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdClientSettingsStep/AddExternalOpenIdClientSettingsStep.tsx @@ -2,7 +2,11 @@ import { useMutation } from '@tanstack/react-query'; import { useMemo } from 'react'; import z from 'zod'; import { m } from '../../../../paraglide/messages'; -import { OpenIdProviderUsernameHandling } from '../../../../shared/api/types'; +import { + OpenIdProviderKind, + type OpenIdProviderKindValue, + OpenIdProviderUsernameHandling, +} from '../../../../shared/api/types'; import { Controls } from '../../../../shared/components/Controls/Controls'; import { WizardCard } from '../../../../shared/components/wizard/WizardCard/WizardCard'; import { SUPPORTED_SYNC_PROVIDERS } from '../../../../shared/constants'; @@ -11,10 +15,6 @@ import { SizedBox } from '../../../../shared/defguard-ui/components/SizedBox/Siz import { ThemeSpacing } from '../../../../shared/defguard-ui/types'; import { useAppForm } from '../../../../shared/form'; import { formChangeLogic } from '../../../../shared/formLogic'; -import { - ExternalProvider, - type ExternalProviderValue, -} from '../../../settings/shared/types'; import { formatMicrosoftBaseUrl, providerUsernameHandlingOptions, @@ -22,10 +22,10 @@ import { } from '../../consts'; import { useAddExternalOpenIdStore } from '../../useAddExternalOpenIdStore'; -const baseUrlHidden: Set = new Set([ - ExternalProvider.JumpCloud, - ExternalProvider.Microsoft, - ExternalProvider.Google, +const baseUrlHidden: Set = new Set([ + OpenIdProviderKind.JumpCloud, + OpenIdProviderKind.Microsoft, + OpenIdProviderKind.Google, ]); export const AddExternalOpenIdClientSettingsStep = () => { @@ -76,7 +76,7 @@ export const AddExternalOpenIdClientSettingsStep = () => { microsoftTenantId: z.string().trim().nullable(), }) .superRefine((values, ctx) => { - if (provider === ExternalProvider.Microsoft) { + if (provider === OpenIdProviderKind.Microsoft) { const schema = z .string(m.form_error_required()) .trim() @@ -143,7 +143,7 @@ export const AddExternalOpenIdClientSettingsStep = () => { {(field) => } - {provider === ExternalProvider.Microsoft && ( + {provider === OpenIdProviderKind.Microsoft && ( <> { const formRender = useMemo(() => { switch (provider) { - case 'google': + case 'Google': return ; - case 'microsoft': + case 'Microsoft': return ; - case 'okta': + case 'Okta': return ; - case 'jumpCloud': + case 'JumpCloud': return ; } return null; diff --git a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/GoogleProviderForm.tsx b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/GoogleProviderForm.tsx index 6b81990867..ef1ed1b27e 100644 --- a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/GoogleProviderForm.tsx +++ b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/GoogleProviderForm.tsx @@ -3,7 +3,6 @@ import { omit } from 'lodash-es'; import { useCallback, useMemo } from 'react'; import type z from 'zod'; import { m } from '../../../../../paraglide/messages'; -import { OpenIdProviderKind } from '../../../../../shared/api/types'; import { DescriptionBlock } from '../../../../../shared/components/DescriptionBlock/DescriptionBlock'; import { EvenSplit } from '../../../../../shared/defguard-ui/components/EvenSplit/EvenSplit'; import { SizedBox } from '../../../../../shared/defguard-ui/components/SizedBox/SizedBox'; @@ -60,7 +59,6 @@ export const GoogleProviderForm = ({ onSubmit }: ProviderFormProps) => { if (isPresent(fileData)) { await onSubmit({ ...value, - kind: OpenIdProviderKind.Google, google_service_account_email: fileData?.client_email ?? '', google_service_account_key: fileData?.private_key ?? '', }); @@ -83,7 +81,6 @@ export const GoogleProviderForm = ({ onSubmit }: ProviderFormProps) => { const fileData = await parseGoogleKeyFile(state.google_service_account_file as File); return { ...omit(state, ['google_service_account_file']), - kind: OpenIdProviderKind.Google, google_service_account_key: fileData?.private_key ?? null, google_service_account_email: fileData?.client_email ?? null, }; diff --git a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/JumpcloudProviderForm.tsx b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/JumpcloudProviderForm.tsx index 5ebe53c7a0..ee51131d65 100644 --- a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/JumpcloudProviderForm.tsx +++ b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/JumpcloudProviderForm.tsx @@ -2,7 +2,6 @@ import { useMutation } from '@tanstack/react-query'; import { useMemo } from 'react'; import type z from 'zod'; import { useShallow } from 'zustand/react/shallow'; -import { OpenIdProviderKind } from '../../../../../shared/api/types'; import { EvenSplit } from '../../../../../shared/defguard-ui/components/EvenSplit/EvenSplit'; import { SizedBox } from '../../../../../shared/defguard-ui/components/SizedBox/SizedBox'; import { ThemeSpacing } from '../../../../../shared/defguard-ui/types'; @@ -47,10 +46,7 @@ export const JumpcloudProviderForm = ({ onSubmit }: ProviderFormProps) => { onChange: jumpcloudProviderSyncSchema, }, onSubmit: async ({ value }) => { - await onSubmit({ - ...value, - kind: OpenIdProviderKind.JumpCloud, - }); + await onSubmit(value); }, }); diff --git a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/MicrosoftProviderForm.tsx b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/MicrosoftProviderForm.tsx index 0d78e0b866..2b3fb8c9bb 100644 --- a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/MicrosoftProviderForm.tsx +++ b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/MicrosoftProviderForm.tsx @@ -1,7 +1,6 @@ import { useMutation } from '@tanstack/react-query'; import { useMemo } from 'react'; import type z from 'zod'; -import { OpenIdProviderKind } from '../../../../../shared/api/types'; import { EvenSplit } from '../../../../../shared/defguard-ui/components/EvenSplit/EvenSplit'; import { SizedBox } from '../../../../../shared/defguard-ui/components/SizedBox/SizedBox'; import { ThemeSpacing } from '../../../../../shared/defguard-ui/types'; @@ -47,10 +46,7 @@ export const MicrosoftProviderForm = ({ onSubmit }: ProviderFormProps) => { onChange: microsoftProviderSyncSchema, }, onSubmit: async ({ value }) => { - await onSubmit({ - ...value, - kind: OpenIdProviderKind.Microsoft, - }); + await onSubmit(value); }, }); diff --git a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/OktaProviderForm.tsx b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/OktaProviderForm.tsx index ddf5249df1..dce561626b 100644 --- a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/OktaProviderForm.tsx +++ b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/forms/OktaProviderForm.tsx @@ -1,7 +1,6 @@ import { useMutation } from '@tanstack/react-query'; import { useMemo } from 'react'; import type z from 'zod'; -import { OpenIdProviderKind } from '../../../../../shared/api/types'; import { EvenSplit } from '../../../../../shared/defguard-ui/components/EvenSplit/EvenSplit'; import { SizedBox } from '../../../../../shared/defguard-ui/components/SizedBox/SizedBox'; import { ThemeSpacing } from '../../../../../shared/defguard-ui/types'; @@ -47,10 +46,7 @@ export const OktaProviderForm = ({ onSubmit }: ProviderFormProps) => { onChange: oktaProviderSyncSchema, }, onSubmit: async ({ value }) => { - await onSubmit({ - ...value, - kind: OpenIdProviderKind.Okta, - }); + await onSubmit(value); }, }); diff --git a/web/src/pages/AddExternalOpenIdWizardPage/useAddExternalOpenIdStore.tsx b/web/src/pages/AddExternalOpenIdWizardPage/useAddExternalOpenIdStore.tsx index 3ac15e1df0..8492d444e9 100644 --- a/web/src/pages/AddExternalOpenIdWizardPage/useAddExternalOpenIdStore.tsx +++ b/web/src/pages/AddExternalOpenIdWizardPage/useAddExternalOpenIdStore.tsx @@ -6,6 +6,7 @@ import { DirectorySyncBehavior, DirectorySyncTarget, OpenIdProviderKind, + type OpenIdProviderKindValue, OpenIdProviderUsernameHandling, } from '../../shared/api/types'; import { @@ -14,7 +15,6 @@ import { jumpcloudProviderBaseUrl, SUPPORTED_SYNC_PROVIDERS, } from '../../shared/constants'; -import { ExternalProvider, type ExternalProviderValue } from '../settings/shared/types'; import { AddExternalProviderStep, type AddExternalProviderStepValue } from './types'; type ProviderState = AddOpenIdProvider & { @@ -22,7 +22,7 @@ type ProviderState = AddOpenIdProvider & { }; interface StoreValues { - provider: ExternalProviderValue; + provider: OpenIdProviderKindValue; activeStep: AddExternalProviderStepValue; providerState: ProviderState; testResult: boolean | null; @@ -30,12 +30,12 @@ interface StoreValues { } export const addExternalOpenIdStoreDefaults: StoreValues = { - provider: ExternalProvider.Custom, + provider: OpenIdProviderKind.Custom, activeStep: 'client-settings', testResult: null, testMessage: null, providerState: { - name: ExternalProvider.Custom, + name: OpenIdProviderKind.Custom, base_url: '', kind: OpenIdProviderKind.Custom, client_id: '', @@ -65,7 +65,7 @@ export const addExternalOpenIdStoreDefaults: StoreValues = { interface Store extends StoreValues { reset: () => void; - initialize: (provider: ExternalProviderValue) => void; + initialize: (provider: OpenIdProviderKindValue) => void; next: (data?: Partial) => void; back: (data?: Partial) => void; } @@ -120,24 +120,24 @@ export const useAddExternalOpenIdStore = create()( initialize: (provider) => { const initialProviderState = addExternalOpenIdStoreDefaults.providerState; initialProviderState.name = provider; - if (provider !== ExternalProvider.Custom) { + initialProviderState.kind = provider; + if (provider !== OpenIdProviderKind.Custom) { initialProviderState.display_name = externalProviderName[provider]; } switch (provider) { - case 'google': + case 'Google': initialProviderState.base_url = googleProviderBaseUrl; break; - case 'microsoft': + case 'Microsoft': break; - case 'jumpCloud': + case 'JumpCloud': initialProviderState.base_url = jumpcloudProviderBaseUrl; break; - case 'okta': + case 'Okta': break; } set({ activeStep: 'client-settings', - provider, providerState: initialProviderState, }); }, diff --git a/web/src/pages/settings/SettingsEditOpenIdProviderPage/SettingsEditOpenIdProviderPage.tsx b/web/src/pages/settings/SettingsEditOpenIdProviderPage/SettingsEditOpenIdProviderPage.tsx index 9e7cd381ca..d2b7c5bec9 100644 --- a/web/src/pages/settings/SettingsEditOpenIdProviderPage/SettingsEditOpenIdProviderPage.tsx +++ b/web/src/pages/settings/SettingsEditOpenIdProviderPage/SettingsEditOpenIdProviderPage.tsx @@ -2,11 +2,10 @@ import { useMutation, useSuspenseQuery } from '@tanstack/react-query'; import { Link, useRouter } from '@tanstack/react-router'; import { useCallback, useMemo } from 'react'; import api from '../../../shared/api/api'; -import type { AddOpenIdProvider } from '../../../shared/api/types'; +import { type AddOpenIdProvider, OpenIdProviderKind } from '../../../shared/api/types'; import { EditPage } from '../../../shared/components/EditPage/EditPage'; import { isPresent } from '../../../shared/defguard-ui/utils/isPresent'; import { getExternalProviderQueryOptions } from '../../../shared/query'; -import { ExternalProvider } from '../shared/types'; import { EditCustomProviderForm } from './form/EditCustomProviderForm'; import { EditGoogleProviderForm } from './form/EditGoogleProviderForm'; import { EditJumpCloudProviderForm } from './form/EditJumpCloudProviderForm'; @@ -78,7 +77,7 @@ export const SettingsEditOpenIdProviderPage = () => { title: 'Edit external OpenID provider', }} > - {formData.name === ExternalProvider.Google && ( + {formData.name === OpenIdProviderKind.Google && ( { loading={deletePending} /> )} - {formData.name === ExternalProvider.Microsoft && ( + {formData.name === OpenIdProviderKind.Microsoft && ( { loading={deletePending} /> )} - {formData.name === ExternalProvider.Okta && ( + {formData.name === OpenIdProviderKind.Okta && ( { loading={deletePending} /> )} - {formData.name === ExternalProvider.JumpCloud && ( + {formData.name === OpenIdProviderKind.JumpCloud && ( { loading={deletePending} /> )} - {formData.name === ExternalProvider.Custom && ( + {formData.name === OpenIdProviderKind.Custom && ( { }); const visibleProviders = useMemo(() => { - const res = Object.values(ExternalProvider).filter( - (p) => p !== ExternalProvider.Zitadel, + const res = Object.values(OpenIdProviderKind).filter( + (p) => p !== OpenIdProviderKind.Zitadel, ); if (activeProvider) { return res.filter((p) => p !== activeProvider.name); @@ -47,7 +50,7 @@ export const SettingsExternalOpenIdPage = () => { { navigate({ to: '/settings/edit-openid' }); diff --git a/web/src/pages/settings/SettingsExternalOpenIdPage/components/ExternalProviderCard/ExternalProviderCard.tsx b/web/src/pages/settings/SettingsExternalOpenIdPage/components/ExternalProviderCard/ExternalProviderCard.tsx index ba2f323429..298a22d405 100644 --- a/web/src/pages/settings/SettingsExternalOpenIdPage/components/ExternalProviderCard/ExternalProviderCard.tsx +++ b/web/src/pages/settings/SettingsExternalOpenIdPage/components/ExternalProviderCard/ExternalProviderCard.tsx @@ -1,8 +1,8 @@ import { type ReactNode, useMemo } from 'react'; -import type { ExternalProviderValue } from '../../../shared/types'; import './style.scss'; import clsx from 'clsx'; import { m } from '../../../../../paraglide/messages'; +import type { OpenIdProviderKindValue } from '../../../../../shared/api/types'; import { externalProviderName } from '../../../../../shared/constants'; import { Button } from '../../../../../shared/defguard-ui/components/Button/Button'; import { Icon } from '../../../../../shared/defguard-ui/components/Icon'; @@ -15,33 +15,33 @@ import okta from './assets/okta.png'; import zitadel from './assets/zitadel.png'; type Props = { - provider: ExternalProviderValue; + provider: OpenIdProviderKindValue; displayName?: string; onClick: () => void; disabled?: boolean; edit?: boolean; }; -const providerImage: Record = { - custom: , - google: , - jumpCloud: , - microsoft: , - okta: , - zitadel: , +const providerImage: Record = { + Custom: , + Google: , + JumpCloud: , + Microsoft: , + Okta: , + Zitadel: , }; -const providerDescription: Record = { - custom: +const providerDescription: Record = { + Custom: 'Enter the required details to link your account securely and manage logins with your custom setup.', - zitadel: + Zitadel: 'Get started with a multi-tenant, API-first identity platform with comprehensive SDKs that enable security, compliance, and extensibility.', - jumpCloud: + JumpCloud: "Enable users to log in with their JumpCloud accounts through JumpCloud's secure directory and authentication platform.", - okta: "Allow users to sign in with their Okta accounts using Okta's secure identity management service.", - microsoft: + Okta: "Allow users to sign in with their Okta accounts using Okta's secure identity management service.", + Microsoft: "Enable users to log in with their Microsoft accounts through Microsoft's secure authentication platform.", - google: + Google: "Allow users to sign in securely with their Google accounts using Google's trusted authentication service.", }; diff --git a/web/src/pages/settings/shared/types.ts b/web/src/pages/settings/shared/types.ts deleted file mode 100644 index 1814d531b4..0000000000 --- a/web/src/pages/settings/shared/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -export const ExternalProvider = { - Google: 'google', - Microsoft: 'microsoft', - Okta: 'okta', - JumpCloud: 'jumpCloud', - Zitadel: 'zitadel', - Custom: 'custom', -} as const; - -export type ExternalProviderValue = - (typeof ExternalProvider)[keyof typeof ExternalProvider]; diff --git a/web/src/shared/api/types.ts b/web/src/shared/api/types.ts index 6a3cfec662..9a0656dafb 100644 --- a/web/src/shared/api/types.ts +++ b/web/src/shared/api/types.ts @@ -1,4 +1,3 @@ -import type { ExternalProviderValue } from '../../pages/settings/shared/types'; import type { ActivityLogEventTypeValue, ActivityLogModuleValue, @@ -680,11 +679,12 @@ export interface OpenIdProviderSettings { } export const OpenIdProviderKind = { + Custom: 'Custom', Google: 'Google', Microsoft: 'Microsoft', Okta: 'Okta', JumpCloud: 'JumpCloud', - Custom: 'Custom', + Zitadel: 'Zitadel', } as const; export type OpenIdProviderKindValue = @@ -719,7 +719,7 @@ export type OpenIdProviderUsernameHandlingValue = export interface OpenIdProvider { id: number; - name: ExternalProviderValue; + name: OpenIdProviderKindValue; base_url: string; kind: OpenIdProviderKindValue; client_id: string; diff --git a/web/src/shared/constants.ts b/web/src/shared/constants.ts index d40741216a..3505edf36c 100644 --- a/web/src/shared/constants.ts +++ b/web/src/shared/constants.ts @@ -1,7 +1,4 @@ -import { - ExternalProvider, - type ExternalProviderValue, -} from '../pages/settings/shared/types'; +import { OpenIdProviderKind, type OpenIdProviderKindValue } from './api/types'; export const externalLink = { defguard: { @@ -20,20 +17,20 @@ export const externalLink = { }, } as const; -export const externalProviderName: Record = { - custom: 'Custom provider', - google: 'Google', - jumpCloud: 'JumpCloud', - microsoft: 'Microsoft', - okta: 'Okta', - zitadel: 'Zitadel', +export const externalProviderName: Record = { + Custom: 'Custom provider', + Google: 'Google', + JumpCloud: 'JumpCloud', + Microsoft: 'Microsoft', + Okta: 'Okta', + Zitadel: 'Zitadel', }; -export const SUPPORTED_SYNC_PROVIDERS: Set = new Set([ - ExternalProvider.Google, - ExternalProvider.Microsoft, - ExternalProvider.Okta, - ExternalProvider.JumpCloud, +export const SUPPORTED_SYNC_PROVIDERS: Set = new Set([ + OpenIdProviderKind.Google, + OpenIdProviderKind.Microsoft, + OpenIdProviderKind.Okta, + OpenIdProviderKind.JumpCloud, ]); export const googleProviderBaseUrl = 'https://accounts.google.com'; From d2f675d46a2fc83b947002fb4cba56b249c7302f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Fri, 16 Jan 2026 17:14:39 +0100 Subject: [PATCH 4/5] Final touch --- ...e824bbcf5ba0c8198c55bc610186f6dc3779.json} | 19 +++++++++++++++++-- Cargo.lock | 19 +++++++++++++++---- .../enterprise/db/models/openid_provider.rs | 18 +++++++++--------- .../AddExternalOpenIdDirectoryStep.tsx | 6 ++++-- 4 files changed, 45 insertions(+), 17 deletions(-) rename .sqlx/{query-796ef2b0b73f5689a592497320b98ddb54a2a673a531cb882aadea3d5aa25d66.json => query-20cd8429969e36695d67c52d7c27e824bbcf5ba0c8198c55bc610186f6dc3779.json} (57%) diff --git a/.sqlx/query-796ef2b0b73f5689a592497320b98ddb54a2a673a531cb882aadea3d5aa25d66.json b/.sqlx/query-20cd8429969e36695d67c52d7c27e824bbcf5ba0c8198c55bc610186f6dc3779.json similarity index 57% rename from .sqlx/query-796ef2b0b73f5689a592497320b98ddb54a2a673a531cb882aadea3d5aa25d66.json rename to .sqlx/query-20cd8429969e36695d67c52d7c27e824bbcf5ba0c8198c55bc610186f6dc3779.json index 528c633238..9232c0709f 100644 --- a/.sqlx/query-796ef2b0b73f5689a592497320b98ddb54a2a673a531cb882aadea3d5aa25d66.json +++ b/.sqlx/query-20cd8429969e36695d67c52d7c27e824bbcf5ba0c8198c55bc610186f6dc3779.json @@ -1,12 +1,27 @@ { "db_name": "PostgreSQL", - "query": "UPDATE openidprovider SET name = $1, base_url = $2, client_id = $3, client_secret = $4, display_name = $5, google_service_account_key = $6, google_service_account_email = $7, admin_email = $8, directory_sync_enabled = $9, directory_sync_interval = $10, directory_sync_user_behavior = $11, directory_sync_admin_behavior = $12, directory_sync_target = $13, okta_private_jwk = $14, okta_dirsync_client_id = $15, directory_sync_group_match = $16, jumpcloud_api_key = $17, prefetch_users = $18 WHERE id = $19", + "query": "UPDATE openidprovider SET name = $1, base_url = $2, kind = $3, client_id = $4, client_secret = $5, display_name = $6, google_service_account_key = $7, google_service_account_email = $8, admin_email = $9, directory_sync_enabled = $10, directory_sync_interval = $11, directory_sync_user_behavior = $12, directory_sync_admin_behavior = $13, directory_sync_target = $14, okta_private_jwk = $15, okta_dirsync_client_id = $16, directory_sync_group_match = $17, jumpcloud_api_key = $18, prefetch_users = $19 WHERE id = $20", "describe": { "columns": [], "parameters": { "Left": [ "Text", "Text", + { + "Custom": { + "name": "openid_provider_kind", + "kind": { + "Enum": [ + "Custom", + "Google", + "Microsoft", + "Okta", + "JumpCloud", + "Zitadel" + ] + } + } + }, "Text", "Text", "Text", @@ -61,5 +76,5 @@ }, "nullable": [] }, - "hash": "796ef2b0b73f5689a592497320b98ddb54a2a673a531cb882aadea3d5aa25d66" + "hash": "20cd8429969e36695d67c52d7c27e824bbcf5ba0c8198c55bc610186f6dc3779" } diff --git a/Cargo.lock b/Cargo.lock index 5f3bf8f25f..5a465bf7ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6203,15 +6203,15 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vergen" -version = "9.0.6" +version = "9.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b2bf58be11fc9414104c6d3a2e464163db5ef74b12296bda593cac37b6e4777" +checksum = "b849a1f6d8639e8de261e81ee0fc881e3e3620db1af9f2e0da015d4382ceaf75" dependencies = [ "anyhow", "derive_builder", "rustversion", "time", - "vergen-lib", + "vergen-lib 9.1.0", ] [[package]] @@ -6226,7 +6226,7 @@ dependencies = [ "rustversion", "time", "vergen", - "vergen-lib", + "vergen-lib 0.1.6", ] [[package]] @@ -6240,6 +6240,17 @@ dependencies = [ "rustversion", ] +[[package]] +name = "vergen-lib" +version = "9.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b34a29ba7e9c59e62f229ae1932fb1b8fb8a6fdcc99215a641913f5f5a59a569" +dependencies = [ + "anyhow", + "derive_builder", + "rustversion", +] + [[package]] name = "version_check" version = "0.9.5" diff --git a/crates/defguard_core/src/enterprise/db/models/openid_provider.rs b/crates/defguard_core/src/enterprise/db/models/openid_provider.rs index a2a4b4c466..16f15d51ff 100644 --- a/crates/defguard_core/src/enterprise/db/models/openid_provider.rs +++ b/crates/defguard_core/src/enterprise/db/models/openid_provider.rs @@ -184,17 +184,17 @@ impl OpenIdProvider { pub(crate) async fn upsert(self, pool: &PgPool) -> Result, SqlxError> { if let Some(provider) = OpenIdProvider::::get_current(pool).await? { query!( - "UPDATE openidprovider SET name = $1, base_url = $2, client_id = $3, \ - client_secret = $4, display_name = $5, google_service_account_key = $6, \ - google_service_account_email = $7, admin_email = $8, directory_sync_enabled = $9, \ - directory_sync_interval = $10, directory_sync_user_behavior = $11, \ - directory_sync_admin_behavior = $12, directory_sync_target = $13, \ - okta_private_jwk = $14, okta_dirsync_client_id = $15, \ - directory_sync_group_match = $16, jumpcloud_api_key = $17, \ - prefetch_users = $18 \ - WHERE id = $19", + "UPDATE openidprovider SET name = $1, base_url = $2, kind = $3, client_id = $4, \ + client_secret = $5, display_name = $6, google_service_account_key = $7, \ + google_service_account_email = $8, admin_email = $9, directory_sync_enabled = $10, \ + directory_sync_interval = $11, directory_sync_user_behavior = $12, \ + directory_sync_admin_behavior = $13, directory_sync_target = $14, \ + okta_private_jwk = $15, okta_dirsync_client_id = $16, \ + directory_sync_group_match = $17, jumpcloud_api_key = $18, prefetch_users = $19 \ + WHERE id = $20", self.name, self.base_url, + self.kind as OpenIdProviderKind, self.client_id, self.client_secret, self.display_name, diff --git a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/AddExternalOpenIdDirectoryStep.tsx b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/AddExternalOpenIdDirectoryStep.tsx index d29da4a1d9..5b90759188 100644 --- a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/AddExternalOpenIdDirectoryStep.tsx +++ b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/AddExternalOpenIdDirectoryStep.tsx @@ -44,8 +44,10 @@ export const AddExternalOpenIdDirectoryStep = () => { const handleValidSubmit = useCallback( async (value: Partial) => { - const providerState = useAddExternalOpenIdStore.getState().providerState; - const submitValues = { ...cloneDeep(providerState), value }; + const state = useAddExternalOpenIdStore.getState() + const providerState = state.providerState; + const provider = state.provider; + const submitValues = { ...cloneDeep(providerState), value, kind: provider }; await mutateAsync(submitValues); }, [mutateAsync], From b2cb7c3e7f6bb29a5a6e144013a144208a9b1f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Fri, 16 Jan 2026 17:22:39 +0100 Subject: [PATCH 5/5] Fix lint --- .../AddExternalOpenIdDirectoryStep.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/AddExternalOpenIdDirectoryStep.tsx b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/AddExternalOpenIdDirectoryStep.tsx index 5b90759188..9c1ad1c669 100644 --- a/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/AddExternalOpenIdDirectoryStep.tsx +++ b/web/src/pages/AddExternalOpenIdWizardPage/steps/AddExternalOpenIdDirectoryStep/AddExternalOpenIdDirectoryStep.tsx @@ -44,7 +44,7 @@ export const AddExternalOpenIdDirectoryStep = () => { const handleValidSubmit = useCallback( async (value: Partial) => { - const state = useAddExternalOpenIdStore.getState() + const state = useAddExternalOpenIdStore.getState(); const providerState = state.providerState; const provider = state.provider; const submitValues = { ...cloneDeep(providerState), value, kind: provider };