Skip to content
Open
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
52 changes: 44 additions & 8 deletions propelauth_py/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
TokenVerificationMetadata,
OrgQueryOrderBy,
UserQueryOrderBy,
SsoTrustLevel,
)
from propelauth_py.api.user import (
_clear_user_password,
Expand Down Expand Up @@ -148,6 +149,8 @@
_saml_go_live_async,
_delete_saml_connection,
_delete_saml_connection_async,
_migrate_org_to_isolated,
_migrate_org_to_isolated_async
)
from propelauth_py.api.employee import (
_fetch_employee_by_id,
Expand Down Expand Up @@ -260,16 +263,16 @@ def fetch_user_metadata_by_user_id(self, user_id: str, include_orgs: bool = Fals
self.auth_hostname, self.integration_api_key, user_id, include_orgs
)

def fetch_user_metadata_by_email(self, email: str, include_orgs: bool = False):
def fetch_user_metadata_by_email(self, email: str, include_orgs: bool = False, isolated_org_id: Optional[str] = None):
return _fetch_user_metadata_by_email(
self.auth_hostname, self.integration_api_key, email, include_orgs
self.auth_hostname, self.integration_api_key, email, include_orgs, isolated_org_id
)

def fetch_user_metadata_by_username(
self, username: str, include_orgs: bool = False
self, username: str, include_orgs: bool = False, isolated_org_id: Optional[str] = None
):
return _fetch_user_metadata_by_username(
self.auth_hostname, self.integration_api_key, username, include_orgs
self.auth_hostname, self.integration_api_key, username, include_orgs, include_orgs
)

def fetch_user_signup_query_params_by_user_id(self, user_id: str):
Expand Down Expand Up @@ -346,6 +349,7 @@ def fetch_users_by_query(
email_or_username: Optional[str] = None,
include_orgs: bool = False,
legacy_user_id: Optional[str] = None,
isolated_org_id: Optional[str] = None
):
return _fetch_users_by_query(
self.auth_hostname,
Expand All @@ -356,6 +360,7 @@ def fetch_users_by_query(
email_or_username,
include_orgs,
legacy_user_id,
isolated_org_id
)

def fetch_users_in_org(
Expand All @@ -375,6 +380,16 @@ def fetch_users_in_org(
include_orgs,
role,
)

def migrate_org_to_isolated(
self,
org_id: str
):
return _migrate_org_to_isolated(
self.auth_hostname,
self.integration_api_key,
org_id
)

def create_user(
self,
Expand Down Expand Up @@ -614,6 +629,7 @@ def update_org_metadata(
domain: Optional[str] = None,
require_2fa_by: Optional[str] = None,
extra_domains: Optional[List[str]] = None,
sso_trust_level: Optional[SsoTrustLevel] = None
):
return _update_org_metadata(
self.auth_hostname,
Expand All @@ -628,6 +644,7 @@ def update_org_metadata(
domain=domain,
require_2fa_by=require_2fa_by,
extra_domains=extra_domains,
sso_trust_level=sso_trust_level
)

def subscribe_org_to_role_mapping(self, org_id: str, custom_role_mapping_name: str):
Expand Down Expand Up @@ -1579,6 +1596,7 @@ async def update_org_metadata(
domain: Optional[str] = None,
require_2fa_by: Optional[str] = None,
extra_domains: Optional[List[str]] = None,
sso_trust_level: Optional[SsoTrustLevel] = None,
):
return await _update_org_metadata_async(
self.httpx_client,
Expand All @@ -1594,6 +1612,7 @@ async def update_org_metadata(
domain=domain,
require_2fa_by=require_2fa_by,
extra_domains=extra_domains,
sso_trust_level=sso_trust_level
)

async def validate_org_api_key(
Expand Down Expand Up @@ -1681,27 +1700,31 @@ async def fetch_user_metadata_by_user_id(
async def fetch_user_metadata_by_email(
self,
email: str,
include_orgs: bool = False
include_orgs: bool = False,
isolated_org_id: Optional[str] = None
):
return await _fetch_user_metadata_by_email_async(
self.httpx_client,
self.auth_hostname,
self.integration_api_key,
email,
include_orgs
include_orgs,
isolated_org_id
)

async def fetch_user_metadata_by_username(
self,
username: str,
include_orgs: bool = False
include_orgs: bool = False,
isolated_org_id: Optional[str] = None
):
return await _fetch_user_metadata_by_username_async(
self.httpx_client,
self.auth_hostname,
self.integration_api_key,
username,
include_orgs
include_orgs,
isolated_org_id
)

async def fetch_batch_user_metadata_by_user_ids(
Expand Down Expand Up @@ -1759,6 +1782,7 @@ async def fetch_users_by_query(
email_or_username: Optional[str] = None,
include_orgs: bool = False,
legacy_user_id: Optional[str] = None,
isolated_org_id: Optional[str] = None
):
return await _fetch_users_by_query_async(
self.httpx_client,
Expand All @@ -1770,6 +1794,7 @@ async def fetch_users_by_query(
email_or_username,
include_orgs,
legacy_user_id,
isolated_org_id
)

async def fetch_users_in_org(
Expand All @@ -1791,6 +1816,17 @@ async def fetch_users_in_org(
role,
)

async def migrate_org_to_isolated(
self,
org_id: str,
):
return await _migrate_org_to_isolated_async(
self.httpx_client,
self.auth_hostname,
self.integration_api_key,
org_id
)

async def create_user(
self,
email: str,
Expand Down
7 changes: 6 additions & 1 deletion propelauth_py/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ class UserQueryOrderBy(str, Enum):
LAST_ACTIVE_AT_DESC = "LAST_ACTIVE_AT_DESC"
EMAIL = "EMAIL"
USERNAME = "USERNAME"

class SsoTrustLevel(str, Enum):
ALWAYS_TRUST = "AlwaysTrust"
NEVER_TRUST = "NeverTrust"
TRUST_FOR_DOMAIN = "TrustForDomain"


class _ApiKeyAuth(AuthBase):
Expand Down Expand Up @@ -87,4 +92,4 @@ def _get_async_headers(auth_hostname: str, integration_api_key: str) -> Dict[str
return {
"X-Propelauth-url": auth_hostname,
"Authorization": f"Bearer {integration_api_key}",
}
}
80 changes: 75 additions & 5 deletions propelauth_py/api/org.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
EndUserApiKeyException,
UpdateUserMetadataException,
RateLimitedException,
MigrateOrgToIsolatedException
)

BASE_ENDPOINT_URL = f"{BACKEND_API_BASE_URL}/api/backend/v1"
Expand Down Expand Up @@ -61,7 +62,8 @@ 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'),
isolated=json_response.get('isolated')
)

async def _fetch_org_async(
Expand Down Expand Up @@ -108,7 +110,8 @@ 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'),
isolated=json_response.get('isolated')
)


Expand Down Expand Up @@ -155,7 +158,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'),
isolated=key.get('isolated')
)
for key in json_response.get('orgs')
]
Expand Down Expand Up @@ -220,7 +224,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'),
isolated=key.get('isolated')
)
for key in json_response.get('orgs')
]
Expand Down Expand Up @@ -1091,6 +1096,7 @@ def _update_org_metadata(
legacy_org_id=None,
require_2fa_by=None,
extra_domains=None,
sso_trust_level=None
) -> bool:
if not _is_valid_id(org_id):
return False
Expand All @@ -1117,7 +1123,9 @@ def _update_org_metadata(
json["require_2fa_by"] = require_2fa_by
if extra_domains is not None:
json["extra_domains"] = extra_domains

if sso_trust_level is not None:
json["sso_trust_level"] = sso_trust_level

response = requests.put(
url,
json=json,
Expand Down Expand Up @@ -1153,6 +1161,7 @@ async def _update_org_metadata_async(
legacy_org_id=None,
require_2fa_by=None,
extra_domains=None,
sso_trust_level=None
) -> bool:
if not _is_valid_id(org_id):
return False
Expand All @@ -1179,6 +1188,8 @@ 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 sso_trust_level is not None:
json_body["sso_trust_level"] = sso_trust_level

response = await httpx_client.put(
url=url,
Expand Down Expand Up @@ -1427,6 +1438,65 @@ async def _delete_saml_connection_async(
response.raise_for_status()
return True

def _migrate_org_to_isolated(auth_hostname, integration_api_key, org_id) -> bool:
if not _is_valid_id(org_id):
return False

url = f"{BASE_ENDPOINT_URL}/isolate_org"

json = {"org_id": org_id}

response = requests.post(
url,
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 == 404:
return False
elif response.status_code == 400:
raise MigrateOrgToIsolatedException(response.json())
elif not response.ok:
raise RuntimeError("Unknown error when allowing org to setup SAML connection")

return True

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

url = f"{BASE_ENDPOINT_URL}/isolate_org"

json = {"org_id": org_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 == 404:
return False
elif response.status_code == 400:
raise MigrateOrgToIsolatedException(response.json())

response.raise_for_status()
return True


####################
# HELPERS #
Expand Down
Loading