55from propelauth_py .api .end_user_api_keys import _validate_api_key , _validate_api_key_async
66from propelauth_py .types .user import Organization , OrgQueryResponse , Org , PendingInvite , PendingInvitesPage , CreatedOrg , OrgApiKeyValidation
77from propelauth_py .types .custom_role_mappings import CustomRoleMappings , CustomRoleMapping
8- from propelauth_py .types .saml_types import SamlIdpMetadata , SpMetadata
8+ from propelauth_py .types .saml_types import SamlIdpMetadata , SpMetadata , SetOidcIdpMetadataRequest , SetGenericOidcMetadataRequest , SetOktaOidcMetadataRequest , SetAzureOidcMetadataRequest
99from propelauth_py .errors import (
1010 BadRequestException ,
1111 EndUserApiKeyException ,
@@ -61,7 +61,10 @@ def _fetch_org(auth_hostname, integration_api_key, org_id) -> Optional[Organizat
6161 domain_autojoin = json_response .get ('domain_autojoin' ),
6262 domain_restrict = json_response .get ('domain_restrict' ),
6363 custom_role_mapping_name = json_response .get ('custom_role_mapping_name' ),
64- legacy_org_id = json_response .get ('legacy_org_id' )
64+ legacy_org_id = json_response .get ('legacy_org_id' ),
65+ password_rotation_enabled = json_response .get ('password_rotation_enabled' ),
66+ password_rotation_history_size = json_response .get ('password_rotation_history_size' ),
67+ password_rotation_period = json_response .get ('password_rotation_period' ),
6568 )
6669
6770async def _fetch_org_async (
@@ -108,7 +111,10 @@ async def _fetch_org_async(
108111 domain_autojoin = json_response .get ('domain_autojoin' ),
109112 domain_restrict = json_response .get ('domain_restrict' ),
110113 custom_role_mapping_name = json_response .get ('custom_role_mapping_name' ),
111- legacy_org_id = json_response .get ('legacy_org_id' )
114+ legacy_org_id = json_response .get ('legacy_org_id' ),
115+ password_rotation_enabled = json_response .get ('password_rotation_enabled' ),
116+ password_rotation_history_size = json_response .get ('password_rotation_history_size' ),
117+ password_rotation_period = json_response .get ('password_rotation_period' ),
112118 )
113119
114120
@@ -155,7 +161,8 @@ def _fetch_org_by_query(
155161 is_saml_configured = key .get ('is_saml_configured' ),
156162 legacy_org_id = key .get ('legacy_org_id' ),
157163 metadata = key .get ('metadata' ),
158- custom_role_mapping_name = key .get ('custom_role_mapping_name' )
164+ custom_role_mapping_name = key .get ('custom_role_mapping_name' ),
165+ created_at = key .get ('created_at' ),
159166 )
160167 for key in json_response .get ('orgs' )
161168 ]
@@ -220,7 +227,8 @@ async def _fetch_org_by_query_async(
220227 is_saml_configured = key .get ('is_saml_configured' ),
221228 legacy_org_id = key .get ('legacy_org_id' ),
222229 metadata = key .get ('metadata' ),
223- custom_role_mapping_name = key .get ('custom_role_mapping_name' )
230+ custom_role_mapping_name = key .get ('custom_role_mapping_name' ),
231+ created_at = key .get ('created_at' ),
224232 )
225233 for key in json_response .get ('orgs' )
226234 ]
@@ -1020,6 +1028,92 @@ async def _set_saml_idp_metadata_async(
10201028
10211029 response .raise_for_status ()
10221030 return True
1031+
1032+ def _set_oidc_idp_metadata (auth_hostname , integration_api_key , request : SetOidcIdpMetadataRequest ) -> bool :
1033+ if not _is_valid_id (request .org_id ):
1034+ return False
1035+
1036+ json : dict = {
1037+ "org_id" : request .org_id ,
1038+ "client_id" : request .client_id ,
1039+ "client_secret" : request .client_secret ,
1040+ "uses_pkce" : request .uses_pkce ,
1041+ "idp_type" : request .idp_type ,
1042+ }
1043+
1044+ if isinstance (request , SetGenericOidcMetadataRequest ):
1045+ json ["auth_url" ] = request .auth_url
1046+ json ["token_url" ] = request .token_url
1047+ json ["userinfo_url" ] = request .userinfo_url
1048+ elif isinstance (request , SetOktaOidcMetadataRequest ):
1049+ json ["okta_sso_domain" ] = request .okta_sso_domain
1050+ elif isinstance (request , SetAzureOidcMetadataRequest ):
1051+ json ["entra_tenant_id" ] = request .entra_tenant_id
1052+
1053+ response = requests .post (
1054+ f"{ BASE_ENDPOINT_URL } /oidc_idp_metadata" ,
1055+ json = json ,
1056+ auth = _ApiKeyAuth (integration_api_key ),
1057+ headers = _auth_hostname_header (auth_hostname ),
1058+ )
1059+
1060+ if response .status_code == 401 :
1061+ raise ValueError ("integration_api_key is incorrect" )
1062+ elif response .status_code == 429 :
1063+ raise RateLimitedException (response .text )
1064+ elif response .status_code == 400 :
1065+ raise BadRequestException (response .json ())
1066+ elif response .status_code == 404 :
1067+ return False
1068+ elif not response .ok :
1069+ raise RuntimeError ("Unknown error when setting the OIDC IdP metadata for an org's OIDC connection" )
1070+ return True
1071+
1072+ async def _set_oidc_idp_metadata_async (
1073+ httpx_client : httpx .AsyncClient ,
1074+ auth_hostname ,
1075+ integration_api_key ,
1076+ request : SetOidcIdpMetadataRequest
1077+ ) -> bool :
1078+ if not _is_valid_id (request .org_id ):
1079+ return False
1080+
1081+ url = f"{ BASE_ENDPOINT_URL } /oidc_idp_metadata"
1082+
1083+ json : dict = {
1084+ "org_id" : request .org_id ,
1085+ "client_id" : request .client_id ,
1086+ "client_secret" : request .client_secret ,
1087+ "uses_pkce" : request .uses_pkce ,
1088+ "idp_type" : request .idp_type ,
1089+ }
1090+
1091+ if isinstance (request , SetGenericOidcMetadataRequest ):
1092+ json ["auth_url" ] = request .auth_url
1093+ json ["token_url" ] = request .token_url
1094+ json ["userinfo_url" ] = request .userinfo_url
1095+ elif isinstance (request , SetOktaOidcMetadataRequest ):
1096+ json ["okta_sso_domain" ] = request .okta_sso_domain
1097+ elif isinstance (request , SetAzureOidcMetadataRequest ):
1098+ json ["entra_tenant_id" ] = request .entra_tenant_id
1099+
1100+ response = await httpx_client .post (
1101+ url = url ,
1102+ json = json ,
1103+ headers = _get_async_headers (auth_hostname = auth_hostname , integration_api_key = integration_api_key )
1104+ )
1105+
1106+ if response .status_code == 401 :
1107+ raise ValueError ("integration_api_key is incorrect" )
1108+ elif response .status_code == 429 :
1109+ raise RateLimitedException (response .text )
1110+ elif response .status_code == 400 :
1111+ raise BadRequestException (response .json ())
1112+ elif response .status_code == 404 :
1113+ return False
1114+
1115+ response .raise_for_status ()
1116+ return True
10231117
10241118def _saml_go_live (auth_hostname , integration_api_key , org_id ) -> bool :
10251119 if not _is_valid_id (org_id ):
@@ -1091,6 +1185,9 @@ def _update_org_metadata(
10911185 legacy_org_id = None ,
10921186 require_2fa_by = None ,
10931187 extra_domains = None ,
1188+ password_rotation_enabled = None ,
1189+ password_rotation_history_size = None ,
1190+ password_rotation_period = None ,
10941191) -> bool :
10951192 if not _is_valid_id (org_id ):
10961193 return False
@@ -1117,6 +1214,12 @@ def _update_org_metadata(
11171214 json ["require_2fa_by" ] = require_2fa_by
11181215 if extra_domains is not None :
11191216 json ["extra_domains" ] = extra_domains
1217+ if password_rotation_enabled is not None :
1218+ json ["password_rotation_enabled" ] = password_rotation_enabled
1219+ if password_rotation_history_size is not None :
1220+ json ["password_rotation_history_size" ] = password_rotation_history_size
1221+ if password_rotation_period is not None :
1222+ json ["password_rotation_period" ] = password_rotation_period
11201223
11211224 response = requests .put (
11221225 url ,
@@ -1153,6 +1256,9 @@ async def _update_org_metadata_async(
11531256 legacy_org_id = None ,
11541257 require_2fa_by = None ,
11551258 extra_domains = None ,
1259+ password_rotation_enabled = None ,
1260+ password_rotation_history_size = None ,
1261+ password_rotation_period = None ,
11561262) -> bool :
11571263 if not _is_valid_id (org_id ):
11581264 return False
@@ -1179,6 +1285,12 @@ async def _update_org_metadata_async(
11791285 json_body ["require_2fa_by" ] = require_2fa_by
11801286 if extra_domains is not None :
11811287 json_body ["extra_domains" ] = extra_domains
1288+ if password_rotation_enabled is not None :
1289+ json_body ["password_rotation_enabled" ] = password_rotation_enabled
1290+ if password_rotation_history_size is not None :
1291+ json_body ["password_rotation_history_size" ] = password_rotation_history_size
1292+ if password_rotation_period is not None :
1293+ json_body ["password_rotation_period" ] = password_rotation_period
11821294
11831295 response = await httpx_client .put (
11841296 url = url ,
0 commit comments