Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
885 changes: 472 additions & 413 deletions propelauth_py/__init__.py

Large diffs are not rendered by default.

23 changes: 18 additions & 5 deletions propelauth_py/api/end_user_api_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def _fetch_api_key(auth_hostname, integration_api_key, api_key_id) -> ApiKeyFull
metadata=json_response.get("metadata"),
user_id=json_response.get("user_id"),
org_id=json_response.get("org_id"),
display_name=json_response.get("display_name"),
)


Expand Down Expand Up @@ -93,6 +94,7 @@ async def _fetch_api_key_async(httpx_client: httpx.AsyncClient, auth_hostname, i
metadata=json_response.get("metadata"),
user_id=json_response.get("user_id"),
org_id=json_response.get("org_id"),
display_name=json_response.get("display_name"),
)

def _get_paged_api_keys(json_response) -> ApiKeyResultPage:
Expand All @@ -104,6 +106,7 @@ def _get_paged_api_keys(json_response) -> ApiKeyResultPage:
metadata=key.get("metadata"),
user_id=key.get("user_id"),
org_id=key.get("org_id"),
display_name=key.get("display_name"),
)
for key in json_response.get("api_keys")
]
Expand Down Expand Up @@ -388,7 +391,7 @@ async def _fetch_api_key_usage_async(
# POST #
####################
def _create_api_key(
auth_hostname, integration_api_key, org_id, user_id, expires_at_seconds, metadata
auth_hostname, integration_api_key, org_id, user_id, expires_at_seconds, metadata, display_name
) -> ApiKeyNew:
url = ENDPOINT_URL

Expand All @@ -401,6 +404,8 @@ def _create_api_key(
json["expires_at_seconds"] = expires_at_seconds
if metadata:
json["metadata"] = metadata
if display_name:
json["display_name"] = display_name

response = requests.post(
url,
Expand Down Expand Up @@ -431,7 +436,8 @@ async def _create_api_key_async(
org_id,
user_id,
expires_at_seconds,
metadata
metadata,
display_name,
) -> ApiKeyNew:

json_body = {}
Expand All @@ -443,7 +449,9 @@ async def _create_api_key_async(
json_body["expires_at_seconds"] = expires_at_seconds
if metadata:
json_body["metadata"] = metadata

if display_name:
json_body["display_name"] = display_name

response = await httpx_client.post(
url=ENDPOINT_URL,
json=json_body,
Expand Down Expand Up @@ -649,7 +657,7 @@ def _get_api_key_validation(json_response) -> ApiKeyValidation:


def _import_api_key(
auth_hostname, integration_api_key, imported_api_key, org_id, user_id, expires_at_seconds, metadata
auth_hostname, integration_api_key, imported_api_key, org_id, user_id, expires_at_seconds, metadata, display_name,
) -> ImportedApiKeyNew:
url = f"{ENDPOINT_URL}/import"

Expand All @@ -664,6 +672,8 @@ def _import_api_key(
json["expires_at_seconds"] = expires_at_seconds
if metadata:
json["metadata"] = metadata
if display_name:
json["display_name"] = display_name

response = requests.post(
url,
Expand Down Expand Up @@ -694,7 +704,8 @@ async def _import_api_key_async(
org_id,
user_id,
expires_at_seconds,
metadata
metadata,
display_name
) -> ImportedApiKeyNew:

json_body = {}
Expand All @@ -708,6 +719,8 @@ async def _import_api_key_async(
json_body["expires_at_seconds"] = expires_at_seconds
if metadata:
json_body["metadata"] = metadata
if display_name:
json_body["display_name"] = display_name

response = await httpx_client.post(
url=f"{ENDPOINT_URL}/import",
Expand Down
14 changes: 10 additions & 4 deletions propelauth_py/api/magic_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ def _create_magic_link(
expires_in_hours=None,
create_new_user_if_one_doesnt_exist=None,
user_signup_query_parameters=None,
expire_after_first_use=None
expire_after_first_use=None,
requires_interstitial=None,
) -> CreateMagicLinkResponse:
url = ENDPOINT_URL
json = {"email": email}
Expand All @@ -46,7 +47,9 @@ def _create_magic_link(
"create_new_user_if_one_doesnt_exist"
] = create_new_user_if_one_doesnt_exist
if expire_after_first_use is not None:
json["expire_after_first_use"] = expire_after_first_use
json["expire_after_first_use"] = expire_after_first_use
if requires_interstitial is not None:
json["requires_interstitial"] = requires_interstitial


response = requests.post(
Expand Down Expand Up @@ -79,7 +82,8 @@ async def _create_magic_link_async(
expires_in_hours=None,
create_new_user_if_one_doesnt_exist=None,
user_signup_query_parameters=None,
expire_after_first_use=None
expire_after_first_use=None,
requires_interstitial=None,
) -> CreateMagicLinkResponse:
json_body = {"email": email}
if redirect_to_url is not None:
Expand All @@ -92,7 +96,9 @@ async def _create_magic_link_async(
json_body["create_new_user_if_one_doesnt_exist"] = create_new_user_if_one_doesnt_exist
if expire_after_first_use is not None:
json_body["expire_after_first_use"] = expire_after_first_use

if requires_interstitial is not None:
json_body["requires_interstitial"] = requires_interstitial

response = await httpx_client.post(
url=ENDPOINT_URL,
json=json_body,
Expand Down
122 changes: 117 additions & 5 deletions propelauth_py/api/org.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from propelauth_py.api.end_user_api_keys import _validate_api_key, _validate_api_key_async
from propelauth_py.types.user import Organization, OrgQueryResponse, Org, PendingInvite, PendingInvitesPage, CreatedOrg, OrgApiKeyValidation
from propelauth_py.types.custom_role_mappings import CustomRoleMappings, CustomRoleMapping
from propelauth_py.types.saml_types import SamlIdpMetadata, SpMetadata
from propelauth_py.types.saml_types import SamlIdpMetadata, SpMetadata, SetOidcIdpMetadataRequest, SetGenericOidcMetadataRequest, SetOktaOidcMetadataRequest, SetAzureOidcMetadataRequest
from propelauth_py.errors import (
BadRequestException,
EndUserApiKeyException,
Expand Down Expand Up @@ -61,7 +61,10 @@ def _fetch_org(auth_hostname, integration_api_key, org_id) -> Optional[Organizat
domain_autojoin=json_response.get('domain_autojoin'),
domain_restrict=json_response.get('domain_restrict'),
custom_role_mapping_name=json_response.get('custom_role_mapping_name'),
legacy_org_id=json_response.get('legacy_org_id')
legacy_org_id=json_response.get('legacy_org_id'),
password_rotation_enabled=json_response.get('password_rotation_enabled'),
password_rotation_history_size=json_response.get('password_rotation_history_size'),
password_rotation_period=json_response.get('password_rotation_period'),
)

async def _fetch_org_async(
Expand Down Expand Up @@ -108,7 +111,10 @@ async def _fetch_org_async(
domain_autojoin=json_response.get('domain_autojoin'),
domain_restrict=json_response.get('domain_restrict'),
custom_role_mapping_name=json_response.get('custom_role_mapping_name'),
legacy_org_id=json_response.get('legacy_org_id')
legacy_org_id=json_response.get('legacy_org_id'),
password_rotation_enabled=json_response.get('password_rotation_enabled'),
password_rotation_history_size=json_response.get('password_rotation_history_size'),
password_rotation_period=json_response.get('password_rotation_period'),
)


Expand Down Expand Up @@ -155,7 +161,8 @@ def _fetch_org_by_query(
is_saml_configured=key.get('is_saml_configured'),
legacy_org_id=key.get('legacy_org_id'),
metadata=key.get('metadata'),
custom_role_mapping_name=key.get('custom_role_mapping_name')
custom_role_mapping_name=key.get('custom_role_mapping_name'),
created_at=key.get('created_at'),
)
for key in json_response.get('orgs')
]
Expand Down Expand Up @@ -220,7 +227,8 @@ async def _fetch_org_by_query_async(
is_saml_configured=key.get('is_saml_configured'),
legacy_org_id=key.get('legacy_org_id'),
metadata=key.get('metadata'),
custom_role_mapping_name=key.get('custom_role_mapping_name')
custom_role_mapping_name=key.get('custom_role_mapping_name'),
created_at=key.get('created_at'),
)
for key in json_response.get('orgs')
]
Expand Down Expand Up @@ -1020,6 +1028,92 @@ async def _set_saml_idp_metadata_async(

response.raise_for_status()
return True

def _set_oidc_idp_metadata(auth_hostname, integration_api_key, request: SetOidcIdpMetadataRequest) -> bool:
if not _is_valid_id(request.org_id):
return False

json: dict = {
"org_id": request.org_id,
"client_id": request.client_id,
"client_secret": request.client_secret,
"uses_pkce": request.uses_pkce,
"idp_type": request.idp_type,
}

if isinstance(request, SetGenericOidcMetadataRequest):
json["auth_url"] = request.auth_url
json["token_url"] = request.token_url
json["userinfo_url"] = request.userinfo_url
elif isinstance(request, SetOktaOidcMetadataRequest):
json["okta_sso_domain"] = request.okta_sso_domain
elif isinstance(request, SetAzureOidcMetadataRequest):
json["entra_tenant_id"] = request.entra_tenant_id

response = requests.post(
f"{BASE_ENDPOINT_URL}/oidc_idp_metadata",
json=json,
auth=_ApiKeyAuth(integration_api_key),
headers=_auth_hostname_header(auth_hostname),
)

if response.status_code == 401:
raise ValueError("integration_api_key is incorrect")
elif response.status_code == 429:
raise RateLimitedException(response.text)
elif response.status_code == 400:
raise BadRequestException(response.json())
elif response.status_code == 404:
return False
elif not response.ok:
raise RuntimeError("Unknown error when setting the OIDC IdP metadata for an org's OIDC connection")
return True

async def _set_oidc_idp_metadata_async(
httpx_client: httpx.AsyncClient,
auth_hostname,
integration_api_key,
request: SetOidcIdpMetadataRequest
) -> bool:
if not _is_valid_id(request.org_id):
return False

url = f"{BASE_ENDPOINT_URL}/oidc_idp_metadata"

json: dict = {
"org_id": request.org_id,
"client_id": request.client_id,
"client_secret": request.client_secret,
"uses_pkce": request.uses_pkce,
"idp_type": request.idp_type,
}

if isinstance(request, SetGenericOidcMetadataRequest):
json["auth_url"] = request.auth_url
json["token_url"] = request.token_url
json["userinfo_url"] = request.userinfo_url
elif isinstance(request, SetOktaOidcMetadataRequest):
json["okta_sso_domain"] = request.okta_sso_domain
elif isinstance(request, SetAzureOidcMetadataRequest):
json["entra_tenant_id"] = request.entra_tenant_id

response = await httpx_client.post(
url=url,
json=json,
headers=_get_async_headers(auth_hostname=auth_hostname, integration_api_key=integration_api_key)
)

if response.status_code == 401:
raise ValueError("integration_api_key is incorrect")
elif response.status_code == 429:
raise RateLimitedException(response.text)
elif response.status_code == 400:
raise BadRequestException(response.json())
elif response.status_code == 404:
return False

response.raise_for_status()
return True

def _saml_go_live(auth_hostname, integration_api_key, org_id) -> bool:
if not _is_valid_id(org_id):
Expand Down Expand Up @@ -1091,6 +1185,9 @@ def _update_org_metadata(
legacy_org_id=None,
require_2fa_by=None,
extra_domains=None,
password_rotation_enabled=None,
password_rotation_history_size=None,
password_rotation_period=None,
) -> bool:
if not _is_valid_id(org_id):
return False
Expand All @@ -1117,6 +1214,12 @@ def _update_org_metadata(
json["require_2fa_by"] = require_2fa_by
if extra_domains is not None:
json["extra_domains"] = extra_domains
if password_rotation_enabled is not None:
json["password_rotation_enabled"] = password_rotation_enabled
if password_rotation_history_size is not None:
json["password_rotation_history_size"] = password_rotation_history_size
if password_rotation_period is not None:
json["password_rotation_period"] = password_rotation_period

response = requests.put(
url,
Expand Down Expand Up @@ -1153,6 +1256,9 @@ async def _update_org_metadata_async(
legacy_org_id=None,
require_2fa_by=None,
extra_domains=None,
password_rotation_enabled=None,
password_rotation_history_size=None,
password_rotation_period=None,
) -> bool:
if not _is_valid_id(org_id):
return False
Expand All @@ -1179,6 +1285,12 @@ async def _update_org_metadata_async(
json_body["require_2fa_by"] = require_2fa_by
if extra_domains is not None:
json_body["extra_domains"] = extra_domains
if password_rotation_enabled is not None:
json_body["password_rotation_enabled"] = password_rotation_enabled
if password_rotation_history_size is not None:
json_body["password_rotation_history_size"] = password_rotation_history_size
if password_rotation_period is not None:
json_body["password_rotation_period"] = password_rotation_period

response = await httpx_client.put(
url=url,
Expand Down
Loading
Loading