diff --git a/reference.md b/reference.md index 3ebab326..57d89eb1 100644 --- a/reference.md +++ b/reference.md @@ -2344,7 +2344,7 @@ client.clients.update(
-**allowed_logout_urls:** `typing.Optional[typing.Sequence[str]]` — URLs that are valid to redirect to after logout from Auth0. +**allowed_logout_urls:** `typing.Optional[typing.Sequence[str]]` — URLs that are valid to redirect to after logout from Auth0
@@ -11969,7 +11969,7 @@ client.self_service_profiles.create(
-**allowed_strategies:** `typing.Optional[typing.Sequence[SelfServiceProfileAllowedStrategyEnum]]` — List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `keycloak-samlp`, `pingfederate`] +**allowed_strategies:** `typing.Optional[typing.Sequence[SelfServiceProfileAllowedStrategyEnum]]` — List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `auth0-samlp`, `okta-samlp`, `keycloak-samlp`, `pingfederate`]
@@ -12227,7 +12227,7 @@ client.self_service_profiles.update(
-**allowed_strategies:** `typing.Optional[typing.Sequence[SelfServiceProfileAllowedStrategyEnum]]` — List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `keycloak-samlp`, `pingfederate`] +**allowed_strategies:** `typing.Optional[typing.Sequence[SelfServiceProfileAllowedStrategyEnum]]` — List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `auth0-samlp`, `okta-samlp`, `keycloak-samlp`, `pingfederate`]
diff --git a/src/auth0/management/actions/modules/raw_client.py b/src/auth0/management/actions/modules/raw_client.py index e424f76a..8cd04365 100644 --- a/src/auth0/management/actions/modules/raw_client.py +++ b/src/auth0/management/actions/modules/raw_client.py @@ -86,7 +86,7 @@ def list( _items = _parsed_response.modules _has_next = True _get_next = lambda: self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, request_options=request_options, ) @@ -649,7 +649,7 @@ def list_actions( _has_next = True _get_next = lambda: self.list_actions( id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, request_options=request_options, ) @@ -885,7 +885,7 @@ async def list( async def _get_next(): return await self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, request_options=request_options, ) @@ -1453,7 +1453,7 @@ async def list_actions( async def _get_next(): return await self.list_actions( id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, request_options=request_options, ) diff --git a/src/auth0/management/actions/modules/versions/raw_client.py b/src/auth0/management/actions/modules/versions/raw_client.py index 00f01d0a..37418f8b 100644 --- a/src/auth0/management/actions/modules/versions/raw_client.py +++ b/src/auth0/management/actions/modules/versions/raw_client.py @@ -81,7 +81,7 @@ def list( _has_next = True _get_next = lambda: self.list( id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, request_options=request_options, ) @@ -409,7 +409,7 @@ async def list( async def _get_next(): return await self.list( id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, request_options=request_options, ) diff --git a/src/auth0/management/actions/raw_client.py b/src/auth0/management/actions/raw_client.py index f63d793e..1950b940 100644 --- a/src/auth0/management/actions/raw_client.py +++ b/src/auth0/management/actions/raw_client.py @@ -110,7 +110,7 @@ def list( trigger_id=trigger_id, action_name=action_name, deployed=deployed, - page=page + len(_items or []), + page=page + 1, per_page=per_page, installed=installed, request_options=request_options, @@ -887,7 +887,7 @@ async def _get_next(): trigger_id=trigger_id, action_name=action_name, deployed=deployed, - page=page + len(_items or []), + page=page + 1, per_page=per_page, installed=installed, request_options=request_options, diff --git a/src/auth0/management/actions/triggers/bindings/raw_client.py b/src/auth0/management/actions/triggers/bindings/raw_client.py index 3b3cf250..d393e30e 100644 --- a/src/auth0/management/actions/triggers/bindings/raw_client.py +++ b/src/auth0/management/actions/triggers/bindings/raw_client.py @@ -83,7 +83,7 @@ def list( _has_next = True _get_next = lambda: self.list( trigger_id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, request_options=request_options, ) @@ -297,7 +297,7 @@ async def list( async def _get_next(): return await self.list( trigger_id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, request_options=request_options, ) diff --git a/src/auth0/management/actions/versions/raw_client.py b/src/auth0/management/actions/versions/raw_client.py index 708efd27..8da3a88c 100644 --- a/src/auth0/management/actions/versions/raw_client.py +++ b/src/auth0/management/actions/versions/raw_client.py @@ -84,7 +84,7 @@ def list( _has_next = True _get_next = lambda: self.list( action_id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, request_options=request_options, ) @@ -396,7 +396,7 @@ async def list( async def _get_next(): return await self.list( action_id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, request_options=request_options, ) diff --git a/src/auth0/management/client.py b/src/auth0/management/client.py index 60e53e55..64e95bed 100644 --- a/src/auth0/management/client.py +++ b/src/auth0/management/client.py @@ -6,6 +6,7 @@ import httpx from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from .core.logging import LogConfig, Logger from .environment import Auth0Environment if typing.TYPE_CHECKING: @@ -88,6 +89,9 @@ class Auth0: httpx_client : typing.Optional[httpx.Client] The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration. + logging : typing.Optional[typing.Union[LogConfig, Logger]] + Configure logging for the SDK. Accepts a LogConfig dict with 'level' (debug/info/warn/error), 'logger' (custom logger implementation), and 'silent' (boolean, defaults to True) fields. You can also pass a pre-configured Logger instance. + Examples -------- from auth0 import Auth0 @@ -108,6 +112,7 @@ def __init__( timeout: typing.Optional[float] = None, follow_redirects: typing.Optional[bool] = True, httpx_client: typing.Optional[httpx.Client] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): _defaulted_timeout = ( timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read @@ -125,6 +130,7 @@ def __init__( if follow_redirects is not None else httpx.Client(timeout=_defaulted_timeout), timeout=_defaulted_timeout, + logging=logging, ) self._actions: typing.Optional[ActionsClient] = None self._branding: typing.Optional[BrandingClient] = None @@ -549,6 +555,9 @@ class AsyncAuth0: httpx_client : typing.Optional[httpx.AsyncClient] The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration. + logging : typing.Optional[typing.Union[LogConfig, Logger]] + Configure logging for the SDK. Accepts a LogConfig dict with 'level' (debug/info/warn/error), 'logger' (custom logger implementation), and 'silent' (boolean, defaults to True) fields. You can also pass a pre-configured Logger instance. + Examples -------- from auth0 import AsyncAuth0 @@ -569,6 +578,7 @@ def __init__( timeout: typing.Optional[float] = None, follow_redirects: typing.Optional[bool] = True, httpx_client: typing.Optional[httpx.AsyncClient] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): _defaulted_timeout = ( timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read @@ -586,6 +596,7 @@ def __init__( if follow_redirects is not None else httpx.AsyncClient(timeout=_defaulted_timeout), timeout=_defaulted_timeout, + logging=logging, ) self._actions: typing.Optional[AsyncActionsClient] = None self._branding: typing.Optional[AsyncBrandingClient] = None diff --git a/src/auth0/management/clients/client.py b/src/auth0/management/clients/client.py index 22a5b0ec..02382ac1 100644 --- a/src/auth0/management/clients/client.py +++ b/src/auth0/management/clients/client.py @@ -696,7 +696,7 @@ def update( Ids of clients that will be allowed to perform delegation requests. Clients that will be allowed to make delegation request. By default, all your clients will be allowed. This field allows you to specify specific clients allowed_logout_urls : typing.Optional[typing.Sequence[str]] - URLs that are valid to redirect to after logout from Auth0. + URLs that are valid to redirect to after logout from Auth0 jwt_configuration : typing.Optional[ClientJwtConfiguration] @@ -1589,7 +1589,7 @@ async def update( Ids of clients that will be allowed to perform delegation requests. Clients that will be allowed to make delegation request. By default, all your clients will be allowed. This field allows you to specify specific clients allowed_logout_urls : typing.Optional[typing.Sequence[str]] - URLs that are valid to redirect to after logout from Auth0. + URLs that are valid to redirect to after logout from Auth0 jwt_configuration : typing.Optional[ClientJwtConfiguration] diff --git a/src/auth0/management/clients/raw_client.py b/src/auth0/management/clients/raw_client.py index 5e36643a..f31800d2 100644 --- a/src/auth0/management/clients/raw_client.py +++ b/src/auth0/management/clients/raw_client.py @@ -185,7 +185,7 @@ def list( _get_next = lambda: self.list( fields=fields, include_fields=include_fields, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, is_global=is_global, @@ -949,7 +949,7 @@ def update( Ids of clients that will be allowed to perform delegation requests. Clients that will be allowed to make delegation request. By default, all your clients will be allowed. This field allows you to specify specific clients allowed_logout_urls : typing.Optional[typing.Sequence[str]] - URLs that are valid to redirect to after logout from Auth0. + URLs that are valid to redirect to after logout from Auth0 jwt_configuration : typing.Optional[ClientJwtConfiguration] @@ -1447,7 +1447,7 @@ async def _get_next(): return await self.list( fields=fields, include_fields=include_fields, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, is_global=is_global, @@ -2214,7 +2214,7 @@ async def update( Ids of clients that will be allowed to perform delegation requests. Clients that will be allowed to make delegation request. By default, all your clients will be allowed. This field allows you to specify specific clients allowed_logout_urls : typing.Optional[typing.Sequence[str]] - URLs that are valid to redirect to after logout from Auth0. + URLs that are valid to redirect to after logout from Auth0 jwt_configuration : typing.Optional[ClientJwtConfiguration] diff --git a/src/auth0/management/core/__init__.py b/src/auth0/management/core/__init__.py index 3b5240ad..5385a6ce 100644 --- a/src/auth0/management/core/__init__.py +++ b/src/auth0/management/core/__init__.py @@ -8,12 +8,12 @@ if typing.TYPE_CHECKING: from .api_error import ApiError from .client_wrapper import AsyncClientWrapper, BaseClientWrapper, SyncClientWrapper - from .custom_pagination import AsyncCustomPager, SyncCustomPager from .datetime_utils import serialize_datetime from .file import File, convert_file_dict_to_httpx_tuples, with_content_type from .http_client import AsyncHttpClient, HttpClient from .http_response import AsyncHttpResponse, HttpResponse from .jsonable_encoder import jsonable_encoder + from .logging import ConsoleLogger, ILogger, LogConfig, LogLevel, Logger, create_logger from .pagination import AsyncPager, SyncPager from .pydantic_utilities import ( IS_PYDANTIC_V2, @@ -31,24 +31,28 @@ _dynamic_imports: typing.Dict[str, str] = { "ApiError": ".api_error", "AsyncClientWrapper": ".client_wrapper", - "AsyncCustomPager": ".custom_pagination", "AsyncHttpClient": ".http_client", "AsyncHttpResponse": ".http_response", "AsyncPager": ".pagination", "BaseClientWrapper": ".client_wrapper", + "ConsoleLogger": ".logging", "FieldMetadata": ".serialization", "File": ".file", "HttpClient": ".http_client", "HttpResponse": ".http_response", + "ILogger": ".logging", "IS_PYDANTIC_V2": ".pydantic_utilities", + "LogConfig": ".logging", + "LogLevel": ".logging", + "Logger": ".logging", "RequestOptions": ".request_options", "SyncClientWrapper": ".client_wrapper", - "SyncCustomPager": ".custom_pagination", "SyncPager": ".pagination", "UniversalBaseModel": ".pydantic_utilities", "UniversalRootModel": ".pydantic_utilities", "convert_and_respect_annotation_metadata": ".serialization", "convert_file_dict_to_httpx_tuples": ".file", + "create_logger": ".logging", "encode_query": ".query_encoder", "jsonable_encoder": ".jsonable_encoder", "parse_obj_as": ".pydantic_utilities", @@ -85,24 +89,28 @@ def __dir__(): __all__ = [ "ApiError", "AsyncClientWrapper", - "AsyncCustomPager", "AsyncHttpClient", "AsyncHttpResponse", "AsyncPager", "BaseClientWrapper", + "ConsoleLogger", "FieldMetadata", "File", "HttpClient", "HttpResponse", + "ILogger", "IS_PYDANTIC_V2", + "LogConfig", + "LogLevel", + "Logger", "RequestOptions", "SyncClientWrapper", - "SyncCustomPager", "SyncPager", "UniversalBaseModel", "UniversalRootModel", "convert_and_respect_annotation_metadata", "convert_file_dict_to_httpx_tuples", + "create_logger", "encode_query", "jsonable_encoder", "parse_obj_as", diff --git a/src/auth0/management/core/custom_pagination.py b/src/auth0/management/core/custom_pagination.py deleted file mode 100644 index 5de2c7a8..00000000 --- a/src/auth0/management/core/custom_pagination.py +++ /dev/null @@ -1,152 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -""" -Custom Pagination Support - -This file is designed to be modified by SDK users to implement their own -pagination logic. The generator will import SyncCustomPager and AsyncCustomPager -from this module when custom pagination is used. - -Users should: -1. Implement their custom pager (e.g., PayrocPager, MyCustomPager, etc.) -2. Create adapter classes (SyncCustomPager/AsyncCustomPager) that bridge - between the generated SDK code and their custom pager implementation -""" - -from __future__ import annotations - -from typing import Any, AsyncIterator, Generic, Iterator, TypeVar - -# Import the base utilities you'll need -# Adjust these imports based on your actual structure -try: - from .client_wrapper import AsyncClientWrapper, SyncClientWrapper -except ImportError: - # Fallback for type hints - AsyncClientWrapper = Any # type: ignore - SyncClientWrapper = Any # type: ignore - -TItem = TypeVar("TItem") -TResponse = TypeVar("TResponse") - - -class SyncCustomPager(Generic[TItem, TResponse]): - """ - Adapter for custom synchronous pagination. - - The generator will call this with: - SyncCustomPager(initial_response=response, client_wrapper=client_wrapper) - - Implement this class to extract pagination metadata from your response - and delegate to your custom pager implementation. - - Example implementation: - - class SyncCustomPager(Generic[TItem, TResponse]): - def __init__( - self, - *, - initial_response: TResponse, - client_wrapper: SyncClientWrapper, - ): - # Extract data and pagination metadata from response - data = initial_response.data # Adjust based on your response structure - links = initial_response.links - - # Initialize your custom pager - self._pager = MyCustomPager( - current_page=Page(data), - httpx_client=client_wrapper.httpx_client, - get_headers=client_wrapper.get_headers, - # ... other parameters - ) - - def __iter__(self): - return iter(self._pager) - - # Delegate other methods to your pager... - """ - - def __init__( - self, - *, - initial_response: TResponse, - client_wrapper: SyncClientWrapper, - ): - """ - Initialize the custom pager. - - Args: - initial_response: The parsed API response from the first request - client_wrapper: The client wrapper providing HTTP client and utilities - """ - raise NotImplementedError( - "SyncCustomPager must be implemented. " - "Please implement this class in core/custom_pagination.py to define your pagination logic. " - "See the class docstring for examples." - ) - - def __iter__(self) -> Iterator[TItem]: - """Iterate through all items across all pages.""" - raise NotImplementedError("Must implement __iter__ method") - - -class AsyncCustomPager(Generic[TItem, TResponse]): - """ - Adapter for custom asynchronous pagination. - - The generator will call this with: - AsyncCustomPager(initial_response=response, client_wrapper=client_wrapper) - - Implement this class to extract pagination metadata from your response - and delegate to your custom async pager implementation. - - Example implementation: - - class AsyncCustomPager(Generic[TItem, TResponse]): - def __init__( - self, - *, - initial_response: TResponse, - client_wrapper: AsyncClientWrapper, - ): - # Extract data and pagination metadata from response - data = initial_response.data # Adjust based on your response structure - links = initial_response.links - - # Initialize your custom async pager - self._pager = MyAsyncCustomPager( - current_page=Page(data), - httpx_client=client_wrapper.httpx_client, - get_headers=client_wrapper.get_headers, - # ... other parameters - ) - - async def __aiter__(self): - return self._pager.__aiter__() - - # Delegate other methods to your pager... - """ - - def __init__( - self, - *, - initial_response: TResponse, - client_wrapper: AsyncClientWrapper, - ): - """ - Initialize the custom async pager. - - Args: - initial_response: The parsed API response from the first request - client_wrapper: The client wrapper providing HTTP client and utilities - """ - raise NotImplementedError( - "AsyncCustomPager must be implemented. " - "Please implement this class in core/custom_pagination.py to define your pagination logic. " - "See the class docstring for examples." - ) - - async def __aiter__(self) -> AsyncIterator[TItem]: - """Asynchronously iterate through all items across all pages.""" - raise NotImplementedError("Must implement __aiter__ method") diff --git a/src/auth0/management/core/http_client.py b/src/auth0/management/core/http_client.py index 7c6c936f..ee937589 100644 --- a/src/auth0/management/core/http_client.py +++ b/src/auth0/management/core/http_client.py @@ -12,6 +12,7 @@ from .file import File, convert_file_dict_to_httpx_tuples from .force_multipart import FORCE_MULTIPART from .jsonable_encoder import jsonable_encoder +from .logging import LogConfig, Logger, create_logger from .query_encoder import encode_query from .remove_none_from_dict import remove_none_from_dict as remove_none_from_dict from .request_options import RequestOptions @@ -122,6 +123,32 @@ def _should_retry(response: httpx.Response) -> bool: return response.status_code >= 500 or response.status_code in retryable_400s +_SENSITIVE_HEADERS = frozenset( + { + "authorization", + "www-authenticate", + "x-api-key", + "api-key", + "apikey", + "x-api-token", + "x-auth-token", + "auth-token", + "cookie", + "set-cookie", + "proxy-authorization", + "proxy-authenticate", + "x-csrf-token", + "x-xsrf-token", + "x-session-token", + "x-access-token", + } +) + + +def _redact_headers(headers: typing.Dict[str, str]) -> typing.Dict[str, str]: + return {k: ("[REDACTED]" if k.lower() in _SENSITIVE_HEADERS else v) for k, v in headers.items()} + + def _build_url(base_url: str, path: typing.Optional[str]) -> str: """ Build a full URL by joining a base URL with a path. @@ -238,11 +265,13 @@ def __init__( base_timeout: typing.Callable[[], typing.Optional[float]], base_headers: typing.Callable[[], typing.Dict[str, str]], base_url: typing.Optional[typing.Callable[[], str]] = None, + logging_config: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): self.base_url = base_url self.base_timeout = base_timeout self.base_headers = base_headers self.httpx_client = httpx_client + self.logger = create_logger(logging_config) def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str: base_url = maybe_base_url @@ -315,18 +344,30 @@ def request( ) ) + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + has_body=json_body is not None or data_body is not None, + ) + response = self.httpx_client.request( method=method, - url=_build_url(base_url, path), - headers=jsonable_encoder( - remove_none_from_dict( - { - **self.base_headers(), - **(headers if headers is not None else {}), - **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), - } - ) - ), + url=_request_url, + headers=_request_headers, params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, @@ -353,6 +394,24 @@ def request( omit=omit, ) + if self.logger.is_debug(): + if 200 <= response.status_code < 400: + self.logger.debug( + "HTTP request succeeded", + method=method, + url=_request_url, + status_code=response.status_code, + ) + + if self.logger.is_error(): + if response.status_code >= 400: + self.logger.error( + "HTTP request failed with error status", + method=method, + url=_request_url, + status_code=response.status_code, + ) + return response @contextmanager @@ -418,18 +477,29 @@ def stream( ) ) + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making streaming HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + ) + with self.httpx_client.stream( method=method, - url=_build_url(base_url, path), - headers=jsonable_encoder( - remove_none_from_dict( - { - **self.base_headers(), - **(headers if headers is not None else {}), - **(request_options.get("additional_headers", {}) if request_options is not None else {}), - } - ) - ), + url=_request_url, + headers=_request_headers, params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, @@ -449,12 +519,14 @@ def __init__( base_headers: typing.Callable[[], typing.Dict[str, str]], base_url: typing.Optional[typing.Callable[[], str]] = None, async_base_headers: typing.Optional[typing.Callable[[], typing.Awaitable[typing.Dict[str, str]]]] = None, + logging_config: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): self.base_url = base_url self.base_timeout = base_timeout self.base_headers = base_headers self.async_base_headers = async_base_headers self.httpx_client = httpx_client + self.logger = create_logger(logging_config) async def _get_headers(self) -> typing.Dict[str, str]: if self.async_base_headers is not None: @@ -535,19 +607,30 @@ async def request( ) ) - # Add the input to each of these and do None-safety checks + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **_headers, + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + has_body=json_body is not None or data_body is not None, + ) + response = await self.httpx_client.request( method=method, - url=_build_url(base_url, path), - headers=jsonable_encoder( - remove_none_from_dict( - { - **_headers, - **(headers if headers is not None else {}), - **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), - } - ) - ), + url=_request_url, + headers=_request_headers, params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, @@ -573,6 +656,25 @@ async def request( retries=retries + 1, omit=omit, ) + + if self.logger.is_debug(): + if 200 <= response.status_code < 400: + self.logger.debug( + "HTTP request succeeded", + method=method, + url=_request_url, + status_code=response.status_code, + ) + + if self.logger.is_error(): + if response.status_code >= 400: + self.logger.error( + "HTTP request failed with error status", + method=method, + url=_request_url, + status_code=response.status_code, + ) + return response @asynccontextmanager @@ -641,18 +743,29 @@ async def stream( ) ) + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **_headers, + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making streaming HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + ) + async with self.httpx_client.stream( method=method, - url=_build_url(base_url, path), - headers=jsonable_encoder( - remove_none_from_dict( - { - **_headers, - **(headers if headers is not None else {}), - **(request_options.get("additional_headers", {}) if request_options is not None else {}), - } - ) - ), + url=_request_url, + headers=_request_headers, params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, diff --git a/src/auth0/management/core/logging.py b/src/auth0/management/core/logging.py new file mode 100644 index 00000000..e5e57245 --- /dev/null +++ b/src/auth0/management/core/logging.py @@ -0,0 +1,107 @@ +# This file was auto-generated by Fern from our API Definition. + +import logging +import typing + +LogLevel = typing.Literal["debug", "info", "warn", "error"] + +_LOG_LEVEL_MAP: typing.Dict[LogLevel, int] = { + "debug": 1, + "info": 2, + "warn": 3, + "error": 4, +} + + +class ILogger(typing.Protocol): + def debug(self, message: str, **kwargs: typing.Any) -> None: ... + def info(self, message: str, **kwargs: typing.Any) -> None: ... + def warn(self, message: str, **kwargs: typing.Any) -> None: ... + def error(self, message: str, **kwargs: typing.Any) -> None: ... + + +class ConsoleLogger: + _logger: logging.Logger + + def __init__(self) -> None: + self._logger = logging.getLogger("fern") + if not self._logger.handlers: + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter("%(levelname)s - %(message)s")) + self._logger.addHandler(handler) + self._logger.setLevel(logging.DEBUG) + + def debug(self, message: str, **kwargs: typing.Any) -> None: + self._logger.debug(message, extra=kwargs) + + def info(self, message: str, **kwargs: typing.Any) -> None: + self._logger.info(message, extra=kwargs) + + def warn(self, message: str, **kwargs: typing.Any) -> None: + self._logger.warning(message, extra=kwargs) + + def error(self, message: str, **kwargs: typing.Any) -> None: + self._logger.error(message, extra=kwargs) + + +class LogConfig(typing.TypedDict, total=False): + level: LogLevel + logger: ILogger + silent: bool + + +class Logger: + _level: int + _logger: ILogger + _silent: bool + + def __init__(self, *, level: LogLevel, logger: ILogger, silent: bool) -> None: + self._level = _LOG_LEVEL_MAP[level] + self._logger = logger + self._silent = silent + + def _should_log(self, level: LogLevel) -> bool: + return not self._silent and self._level <= _LOG_LEVEL_MAP[level] + + def is_debug(self) -> bool: + return self._should_log("debug") + + def is_info(self) -> bool: + return self._should_log("info") + + def is_warn(self) -> bool: + return self._should_log("warn") + + def is_error(self) -> bool: + return self._should_log("error") + + def debug(self, message: str, **kwargs: typing.Any) -> None: + if self.is_debug(): + self._logger.debug(message, **kwargs) + + def info(self, message: str, **kwargs: typing.Any) -> None: + if self.is_info(): + self._logger.info(message, **kwargs) + + def warn(self, message: str, **kwargs: typing.Any) -> None: + if self.is_warn(): + self._logger.warn(message, **kwargs) + + def error(self, message: str, **kwargs: typing.Any) -> None: + if self.is_error(): + self._logger.error(message, **kwargs) + + +_default_logger: Logger = Logger(level="info", logger=ConsoleLogger(), silent=True) + + +def create_logger(config: typing.Optional[typing.Union[LogConfig, Logger]] = None) -> Logger: + if config is None: + return _default_logger + if isinstance(config, Logger): + return config + return Logger( + level=config.get("level", "info"), + logger=config.get("logger", ConsoleLogger()), + silent=config.get("silent", True), + ) diff --git a/src/auth0/management/core/pydantic_utilities.py b/src/auth0/management/core/pydantic_utilities.py index 789081b0..831aadc3 100644 --- a/src/auth0/management/core/pydantic_utilities.py +++ b/src/auth0/management/core/pydantic_utilities.py @@ -35,14 +35,31 @@ IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.") if IS_PYDANTIC_V2: - from pydantic.v1.datetime_parse import parse_date as parse_date - from pydantic.v1.datetime_parse import parse_datetime as parse_datetime - from pydantic.v1.fields import ModelField as ModelField - from pydantic.v1.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[attr-defined] - from pydantic.v1.typing import get_args as get_args - from pydantic.v1.typing import get_origin as get_origin - from pydantic.v1.typing import is_literal_type as is_literal_type - from pydantic.v1.typing import is_union as is_union + import warnings + + _datetime_adapter = pydantic.TypeAdapter(dt.datetime) # type: ignore[attr-defined] + _date_adapter = pydantic.TypeAdapter(dt.date) # type: ignore[attr-defined] + + def parse_datetime(value: Any) -> dt.datetime: # type: ignore[misc] + if isinstance(value, dt.datetime): + return value + return _datetime_adapter.validate_python(value) + + def parse_date(value: Any) -> dt.date: # type: ignore[misc] + if isinstance(value, dt.datetime): + return value.date() + if isinstance(value, dt.date): + return value + return _date_adapter.validate_python(value) + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", UserWarning) + from pydantic.v1.fields import ModelField as ModelField + from pydantic.v1.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[attr-defined] + from pydantic.v1.typing import get_args as get_args + from pydantic.v1.typing import get_origin as get_origin + from pydantic.v1.typing import is_literal_type as is_literal_type + from pydantic.v1.typing import is_union as is_union else: from pydantic.datetime_parse import parse_date as parse_date # type: ignore[no-redef] from pydantic.datetime_parse import parse_datetime as parse_datetime # type: ignore[no-redef] diff --git a/src/auth0/management/device_credentials/raw_client.py b/src/auth0/management/device_credentials/raw_client.py index edca458b..56dbfca4 100644 --- a/src/auth0/management/device_credentials/raw_client.py +++ b/src/auth0/management/device_credentials/raw_client.py @@ -110,7 +110,7 @@ def list( _items = _parsed_response.device_credentials _has_next = True _get_next = lambda: self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, fields=fields, @@ -454,7 +454,7 @@ async def list( async def _get_next(): return await self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, fields=fields, diff --git a/src/auth0/management/flows/raw_client.py b/src/auth0/management/flows/raw_client.py index 8171bc8f..f94f6c65 100644 --- a/src/auth0/management/flows/raw_client.py +++ b/src/auth0/management/flows/raw_client.py @@ -97,7 +97,7 @@ def list( _items = _parsed_response.flows _has_next = True _get_next = lambda: self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, hydrate=hydrate, @@ -607,7 +607,7 @@ async def list( async def _get_next(): return await self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, hydrate=hydrate, diff --git a/src/auth0/management/flows/vault/connections/raw_client.py b/src/auth0/management/flows/vault/connections/raw_client.py index c52c4986..b786fb4a 100644 --- a/src/auth0/management/flows/vault/connections/raw_client.py +++ b/src/auth0/management/flows/vault/connections/raw_client.py @@ -86,7 +86,7 @@ def list( _items = _parsed_response.connections _has_next = True _get_next = lambda: self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, @@ -565,7 +565,7 @@ async def list( async def _get_next(): return await self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, diff --git a/src/auth0/management/forms/raw_client.py b/src/auth0/management/forms/raw_client.py index 05aaaa27..cdedc5de 100644 --- a/src/auth0/management/forms/raw_client.py +++ b/src/auth0/management/forms/raw_client.py @@ -104,7 +104,7 @@ def list( _items = _parsed_response.forms _has_next = True _get_next = lambda: self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, hydrate=hydrate, @@ -654,7 +654,7 @@ async def list( async def _get_next(): return await self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, hydrate=hydrate, diff --git a/src/auth0/management/hooks/raw_client.py b/src/auth0/management/hooks/raw_client.py index 3d02c29b..cb8047d2 100644 --- a/src/auth0/management/hooks/raw_client.py +++ b/src/auth0/management/hooks/raw_client.py @@ -101,7 +101,7 @@ def list( _items = _parsed_response.hooks _has_next = True _get_next = lambda: self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, enabled=enabled, @@ -674,7 +674,7 @@ async def list( async def _get_next(): return await self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, enabled=enabled, diff --git a/src/auth0/management/keys/encryption/raw_client.py b/src/auth0/management/keys/encryption/raw_client.py index a2d3c957..786bd630 100644 --- a/src/auth0/management/keys/encryption/raw_client.py +++ b/src/auth0/management/keys/encryption/raw_client.py @@ -90,7 +90,7 @@ def list( _items = _parsed_response.keys _has_next = True _get_next = lambda: self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, @@ -730,7 +730,7 @@ async def list( async def _get_next(): return await self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, diff --git a/src/auth0/management/logs/raw_client.py b/src/auth0/management/logs/raw_client.py index a35a49bf..399c5cff 100644 --- a/src/auth0/management/logs/raw_client.py +++ b/src/auth0/management/logs/raw_client.py @@ -128,7 +128,7 @@ def list( _items = _parsed_response.logs _has_next = True _get_next = lambda: self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, sort=sort, fields=fields, @@ -392,7 +392,7 @@ async def list( async def _get_next(): return await self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, sort=sort, fields=fields, diff --git a/src/auth0/management/network_acls/raw_client.py b/src/auth0/management/network_acls/raw_client.py index 933a2891..e14bbee0 100644 --- a/src/auth0/management/network_acls/raw_client.py +++ b/src/auth0/management/network_acls/raw_client.py @@ -87,7 +87,7 @@ def list( _items = _parsed_response.network_acls _has_next = True _get_next = lambda: self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, @@ -738,7 +738,7 @@ async def list( async def _get_next(): return await self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, diff --git a/src/auth0/management/organizations/client_grants/raw_client.py b/src/auth0/management/organizations/client_grants/raw_client.py index b4ceb3f1..47ea8c3e 100644 --- a/src/auth0/management/organizations/client_grants/raw_client.py +++ b/src/auth0/management/organizations/client_grants/raw_client.py @@ -107,7 +107,7 @@ def list( audience=audience, client_id=client_id, grant_ids=grant_ids, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, @@ -444,7 +444,7 @@ async def _get_next(): audience=audience, client_id=client_id, grant_ids=grant_ids, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, diff --git a/src/auth0/management/organizations/enabled_connections/raw_client.py b/src/auth0/management/organizations/enabled_connections/raw_client.py index 720eca5b..87406eda 100644 --- a/src/auth0/management/organizations/enabled_connections/raw_client.py +++ b/src/auth0/management/organizations/enabled_connections/raw_client.py @@ -89,7 +89,7 @@ def list( _has_next = True _get_next = lambda: self.list( id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, @@ -590,7 +590,7 @@ async def list( async def _get_next(): return await self.list( id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, diff --git a/src/auth0/management/organizations/invitations/raw_client.py b/src/auth0/management/organizations/invitations/raw_client.py index accf6221..e8b7dd36 100644 --- a/src/auth0/management/organizations/invitations/raw_client.py +++ b/src/auth0/management/organizations/invitations/raw_client.py @@ -109,7 +109,7 @@ def list( _has_next = True _get_next = lambda: self.list( id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, fields=fields, @@ -594,7 +594,7 @@ async def list( async def _get_next(): return await self.list( id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, fields=fields, diff --git a/src/auth0/management/organizations/members/roles/raw_client.py b/src/auth0/management/organizations/members/roles/raw_client.py index e6824cef..1b604041 100644 --- a/src/auth0/management/organizations/members/roles/raw_client.py +++ b/src/auth0/management/organizations/members/roles/raw_client.py @@ -94,7 +94,7 @@ def list( _get_next = lambda: self.list( id, user_id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, @@ -423,7 +423,7 @@ async def _get_next(): return await self.list( id, user_id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, diff --git a/src/auth0/management/prompts/rendering/raw_client.py b/src/auth0/management/prompts/rendering/raw_client.py index bfab2ac4..978af856 100644 --- a/src/auth0/management/prompts/rendering/raw_client.py +++ b/src/auth0/management/prompts/rendering/raw_client.py @@ -119,7 +119,7 @@ def list( _get_next = lambda: self.list( fields=fields, include_fields=include_fields, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, prompt=prompt, @@ -630,7 +630,7 @@ async def _get_next(): return await self.list( fields=fields, include_fields=include_fields, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, prompt=prompt, diff --git a/src/auth0/management/resource_servers/raw_client.py b/src/auth0/management/resource_servers/raw_client.py index 8f938ad4..1c50cfa1 100644 --- a/src/auth0/management/resource_servers/raw_client.py +++ b/src/auth0/management/resource_servers/raw_client.py @@ -105,7 +105,7 @@ def list( _has_next = True _get_next = lambda: self.list( identifiers=identifiers, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, include_fields=include_fields, @@ -771,7 +771,7 @@ async def list( async def _get_next(): return await self.list( identifiers=identifiers, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, include_fields=include_fields, diff --git a/src/auth0/management/roles/permissions/raw_client.py b/src/auth0/management/roles/permissions/raw_client.py index 8d08af64..52a94d72 100644 --- a/src/auth0/management/roles/permissions/raw_client.py +++ b/src/auth0/management/roles/permissions/raw_client.py @@ -90,7 +90,7 @@ def list( _get_next = lambda: self.list( id, per_page=per_page, - page=page + len(_items or []), + page=page + 1, include_totals=include_totals, request_options=request_options, ) @@ -404,7 +404,7 @@ async def _get_next(): return await self.list( id, per_page=per_page, - page=page + len(_items or []), + page=page + 1, include_totals=include_totals, request_options=request_options, ) diff --git a/src/auth0/management/roles/raw_client.py b/src/auth0/management/roles/raw_client.py index 56840dc7..59d5f9ac 100644 --- a/src/auth0/management/roles/raw_client.py +++ b/src/auth0/management/roles/raw_client.py @@ -92,7 +92,7 @@ def list( _has_next = True _get_next = lambda: self.list( per_page=per_page, - page=page + len(_items or []), + page=page + 1, include_totals=include_totals, name_filter=name_filter, request_options=request_options, @@ -607,7 +607,7 @@ async def list( async def _get_next(): return await self.list( per_page=per_page, - page=page + len(_items or []), + page=page + 1, include_totals=include_totals, name_filter=name_filter, request_options=request_options, diff --git a/src/auth0/management/rules/raw_client.py b/src/auth0/management/rules/raw_client.py index 47a35543..39f66c49 100644 --- a/src/auth0/management/rules/raw_client.py +++ b/src/auth0/management/rules/raw_client.py @@ -99,7 +99,7 @@ def list( _items = _parsed_response.rules _has_next = True _get_next = lambda: self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, enabled=enabled, @@ -681,7 +681,7 @@ async def list( async def _get_next(): return await self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, enabled=enabled, diff --git a/src/auth0/management/self_service_profiles/client.py b/src/auth0/management/self_service_profiles/client.py index f3dd0145..836b07ee 100644 --- a/src/auth0/management/self_service_profiles/client.py +++ b/src/auth0/management/self_service_profiles/client.py @@ -124,7 +124,7 @@ def create( branding : typing.Optional[SelfServiceProfileBrandingProperties] allowed_strategies : typing.Optional[typing.Sequence[SelfServiceProfileAllowedStrategyEnum]] - List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `keycloak-samlp`, `pingfederate`] + List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `auth0-samlp`, `okta-samlp`, `keycloak-samlp`, `pingfederate`] user_attributes : typing.Optional[typing.Sequence[SelfServiceProfileUserAttribute]] List of attributes to be mapped that will be shown to the user during the SS-SSO flow. @@ -253,7 +253,7 @@ def update( branding : typing.Optional[SelfServiceProfileBranding] allowed_strategies : typing.Optional[typing.Sequence[SelfServiceProfileAllowedStrategyEnum]] - List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `keycloak-samlp`, `pingfederate`] + List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `auth0-samlp`, `okta-samlp`, `keycloak-samlp`, `pingfederate`] user_attributes : typing.Optional[SelfServiceProfileUserAttributes] @@ -412,7 +412,7 @@ async def create( branding : typing.Optional[SelfServiceProfileBrandingProperties] allowed_strategies : typing.Optional[typing.Sequence[SelfServiceProfileAllowedStrategyEnum]] - List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `keycloak-samlp`, `pingfederate`] + List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `auth0-samlp`, `okta-samlp`, `keycloak-samlp`, `pingfederate`] user_attributes : typing.Optional[typing.Sequence[SelfServiceProfileUserAttribute]] List of attributes to be mapped that will be shown to the user during the SS-SSO flow. @@ -565,7 +565,7 @@ async def update( branding : typing.Optional[SelfServiceProfileBranding] allowed_strategies : typing.Optional[typing.Sequence[SelfServiceProfileAllowedStrategyEnum]] - List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `keycloak-samlp`, `pingfederate`] + List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `auth0-samlp`, `okta-samlp`, `keycloak-samlp`, `pingfederate`] user_attributes : typing.Optional[SelfServiceProfileUserAttributes] diff --git a/src/auth0/management/self_service_profiles/raw_client.py b/src/auth0/management/self_service_profiles/raw_client.py index df566fe1..e84fdf70 100644 --- a/src/auth0/management/self_service_profiles/raw_client.py +++ b/src/auth0/management/self_service_profiles/raw_client.py @@ -94,7 +94,7 @@ def list( _items = _parsed_response.self_service_profiles _has_next = True _get_next = lambda: self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, @@ -174,7 +174,7 @@ def create( branding : typing.Optional[SelfServiceProfileBrandingProperties] allowed_strategies : typing.Optional[typing.Sequence[SelfServiceProfileAllowedStrategyEnum]] - List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `keycloak-samlp`, `pingfederate`] + List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `auth0-samlp`, `okta-samlp`, `keycloak-samlp`, `pingfederate`] user_attributes : typing.Optional[typing.Sequence[SelfServiceProfileUserAttribute]] List of attributes to be mapped that will be shown to the user during the SS-SSO flow. @@ -511,7 +511,7 @@ def update( branding : typing.Optional[SelfServiceProfileBranding] allowed_strategies : typing.Optional[typing.Sequence[SelfServiceProfileAllowedStrategyEnum]] - List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `keycloak-samlp`, `pingfederate`] + List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `auth0-samlp`, `okta-samlp`, `keycloak-samlp`, `pingfederate`] user_attributes : typing.Optional[SelfServiceProfileUserAttributes] @@ -691,7 +691,7 @@ async def list( async def _get_next(): return await self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, @@ -772,7 +772,7 @@ async def create( branding : typing.Optional[SelfServiceProfileBrandingProperties] allowed_strategies : typing.Optional[typing.Sequence[SelfServiceProfileAllowedStrategyEnum]] - List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `keycloak-samlp`, `pingfederate`] + List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `auth0-samlp`, `okta-samlp`, `keycloak-samlp`, `pingfederate`] user_attributes : typing.Optional[typing.Sequence[SelfServiceProfileUserAttribute]] List of attributes to be mapped that will be shown to the user during the SS-SSO flow. @@ -1111,7 +1111,7 @@ async def update( branding : typing.Optional[SelfServiceProfileBranding] allowed_strategies : typing.Optional[typing.Sequence[SelfServiceProfileAllowedStrategyEnum]] - List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `keycloak-samlp`, `pingfederate`] + List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `auth0-samlp`, `okta-samlp`, `keycloak-samlp`, `pingfederate`] user_attributes : typing.Optional[SelfServiceProfileUserAttributes] diff --git a/src/auth0/management/types/__init__.py b/src/auth0/management/types/__init__.py index 66bd0bdd..1ef279e0 100644 --- a/src/auth0/management/types/__init__.py +++ b/src/auth0/management/types/__init__.py @@ -259,6 +259,7 @@ from .client_token_exchange_type_enum import ClientTokenExchangeTypeEnum from .connected_account import ConnectedAccount from .connected_account_access_type_enum import ConnectedAccountAccessTypeEnum + from .connection_access_token_urlo_auth_1 import ConnectionAccessTokenUrloAuth1 from .connection_acr_values_supported import ConnectionAcrValuesSupported from .connection_admin_access_token_expires_in_google_apps import ConnectionAdminAccessTokenExpiresInGoogleApps from .connection_admin_access_token_google_apps import ConnectionAdminAccessTokenGoogleApps @@ -302,6 +303,7 @@ from .connection_client_id_google_apps import ConnectionClientIdGoogleApps from .connection_client_id_google_o_auth_2 import ConnectionClientIdGoogleOAuth2 from .connection_client_id_line import ConnectionClientIdLine + from .connection_client_id_o_auth_1 import ConnectionClientIdOAuth1 from .connection_client_id_o_auth_2 import ConnectionClientIdOAuth2 from .connection_client_id_oidc import ConnectionClientIdOidc from .connection_client_id_paypal import ConnectionClientIdPaypal @@ -317,6 +319,7 @@ from .connection_client_secret_google_apps import ConnectionClientSecretGoogleApps from .connection_client_secret_google_o_auth_2 import ConnectionClientSecretGoogleOAuth2 from .connection_client_secret_line import ConnectionClientSecretLine + from .connection_client_secret_o_auth_1 import ConnectionClientSecretOAuth1 from .connection_client_secret_o_auth_2 import ConnectionClientSecretOAuth2 from .connection_client_secret_oidc import ConnectionClientSecretOidc from .connection_client_secret_paypal import ConnectionClientSecretPaypal @@ -350,6 +353,8 @@ from .connection_domain_aliases_saml import ConnectionDomainAliasesSaml from .connection_domain_google_apps import ConnectionDomainGoogleApps from .connection_domain_okta import ConnectionDomainOkta + from .connection_dpop_signing_alg_enum import ConnectionDpopSigningAlgEnum + from .connection_dpop_signing_alg_values_supported import ConnectionDpopSigningAlgValuesSupported from .connection_email_body_email import ConnectionEmailBodyEmail from .connection_email_email import ConnectionEmailEmail from .connection_email_email_syntax import ConnectionEmailEmailSyntax @@ -568,6 +573,7 @@ from .connection_request_object_signing_alg_values_supported import ConnectionRequestObjectSigningAlgValuesSupported from .connection_request_parameter_supported import ConnectionRequestParameterSupported from .connection_request_template_saml import ConnectionRequestTemplateSaml + from .connection_request_token_urlo_auth_1 import ConnectionRequestTokenUrloAuth1 from .connection_request_uri_parameter_supported import ConnectionRequestUriParameterSupported from .connection_require_request_uri_registration import ConnectionRequireRequestUriRegistration from .connection_requires_username import ConnectionRequiresUsername @@ -715,6 +721,7 @@ from .connection_scope_paypal import ConnectionScopePaypal from .connection_scope_salesforce import ConnectionScopeSalesforce from .connection_scopes_supported import ConnectionScopesSupported + from .connection_scripts_o_auth_1 import ConnectionScriptsOAuth1 from .connection_scripts_o_auth_2 import ConnectionScriptsOAuth2 from .connection_send_back_channel_nonce import ConnectionSendBackChannelNonce from .connection_service_documentation import ConnectionServiceDocumentation @@ -729,6 +736,7 @@ from .connection_sign_saml_request_saml import ConnectionSignSamlRequestSaml from .connection_signature_algorithm_enum_saml import ConnectionSignatureAlgorithmEnumSaml from .connection_signature_algorithm_saml import ConnectionSignatureAlgorithmSaml + from .connection_signature_method_o_auth_1 import ConnectionSignatureMethodOAuth1 from .connection_signing_cert_saml import ConnectionSigningCertSaml from .connection_signing_certificate_der_saml import ConnectionSigningCertificateDerSaml from .connection_signing_certificate_pem_ping_federate import ConnectionSigningCertificatePemPingFederate @@ -780,6 +788,7 @@ from .connection_upstream_params_facebook import ConnectionUpstreamParamsFacebook from .connection_upstream_value import ConnectionUpstreamValue from .connection_use_common_endpoint_azure_ad import ConnectionUseCommonEndpointAzureAd + from .connection_user_authorization_urlo_auth_1 import ConnectionUserAuthorizationUrloAuth1 from .connection_user_id_attribute_saml import ConnectionUserIdAttributeSaml from .connection_userid_attribute_azure_ad import ConnectionUseridAttributeAzureAd from .connection_userid_attribute_enum_azure_ad import ConnectionUseridAttributeEnumAzureAd @@ -2691,6 +2700,7 @@ "ClientTokenExchangeTypeEnum": ".client_token_exchange_type_enum", "ConnectedAccount": ".connected_account", "ConnectedAccountAccessTypeEnum": ".connected_account_access_type_enum", + "ConnectionAccessTokenUrloAuth1": ".connection_access_token_urlo_auth_1", "ConnectionAcrValuesSupported": ".connection_acr_values_supported", "ConnectionAdminAccessTokenExpiresInGoogleApps": ".connection_admin_access_token_expires_in_google_apps", "ConnectionAdminAccessTokenGoogleApps": ".connection_admin_access_token_google_apps", @@ -2734,6 +2744,7 @@ "ConnectionClientIdGoogleApps": ".connection_client_id_google_apps", "ConnectionClientIdGoogleOAuth2": ".connection_client_id_google_o_auth_2", "ConnectionClientIdLine": ".connection_client_id_line", + "ConnectionClientIdOAuth1": ".connection_client_id_o_auth_1", "ConnectionClientIdOAuth2": ".connection_client_id_o_auth_2", "ConnectionClientIdOidc": ".connection_client_id_oidc", "ConnectionClientIdPaypal": ".connection_client_id_paypal", @@ -2749,6 +2760,7 @@ "ConnectionClientSecretGoogleApps": ".connection_client_secret_google_apps", "ConnectionClientSecretGoogleOAuth2": ".connection_client_secret_google_o_auth_2", "ConnectionClientSecretLine": ".connection_client_secret_line", + "ConnectionClientSecretOAuth1": ".connection_client_secret_o_auth_1", "ConnectionClientSecretOAuth2": ".connection_client_secret_o_auth_2", "ConnectionClientSecretOidc": ".connection_client_secret_oidc", "ConnectionClientSecretPaypal": ".connection_client_secret_paypal", @@ -2782,6 +2794,8 @@ "ConnectionDomainAliasesSaml": ".connection_domain_aliases_saml", "ConnectionDomainGoogleApps": ".connection_domain_google_apps", "ConnectionDomainOkta": ".connection_domain_okta", + "ConnectionDpopSigningAlgEnum": ".connection_dpop_signing_alg_enum", + "ConnectionDpopSigningAlgValuesSupported": ".connection_dpop_signing_alg_values_supported", "ConnectionEmailBodyEmail": ".connection_email_body_email", "ConnectionEmailEmail": ".connection_email_email", "ConnectionEmailEmailSyntax": ".connection_email_email_syntax", @@ -2988,6 +3002,7 @@ "ConnectionRequestObjectSigningAlgValuesSupported": ".connection_request_object_signing_alg_values_supported", "ConnectionRequestParameterSupported": ".connection_request_parameter_supported", "ConnectionRequestTemplateSaml": ".connection_request_template_saml", + "ConnectionRequestTokenUrloAuth1": ".connection_request_token_urlo_auth_1", "ConnectionRequestUriParameterSupported": ".connection_request_uri_parameter_supported", "ConnectionRequireRequestUriRegistration": ".connection_require_request_uri_registration", "ConnectionRequiresUsername": ".connection_requires_username", @@ -3129,6 +3144,7 @@ "ConnectionScopePaypal": ".connection_scope_paypal", "ConnectionScopeSalesforce": ".connection_scope_salesforce", "ConnectionScopesSupported": ".connection_scopes_supported", + "ConnectionScriptsOAuth1": ".connection_scripts_o_auth_1", "ConnectionScriptsOAuth2": ".connection_scripts_o_auth_2", "ConnectionSendBackChannelNonce": ".connection_send_back_channel_nonce", "ConnectionServiceDocumentation": ".connection_service_documentation", @@ -3143,6 +3159,7 @@ "ConnectionSignSamlRequestSaml": ".connection_sign_saml_request_saml", "ConnectionSignatureAlgorithmEnumSaml": ".connection_signature_algorithm_enum_saml", "ConnectionSignatureAlgorithmSaml": ".connection_signature_algorithm_saml", + "ConnectionSignatureMethodOAuth1": ".connection_signature_method_o_auth_1", "ConnectionSigningCertSaml": ".connection_signing_cert_saml", "ConnectionSigningCertificateDerSaml": ".connection_signing_certificate_der_saml", "ConnectionSigningCertificatePemPingFederate": ".connection_signing_certificate_pem_ping_federate", @@ -3192,6 +3209,7 @@ "ConnectionUpstreamParamsFacebook": ".connection_upstream_params_facebook", "ConnectionUpstreamValue": ".connection_upstream_value", "ConnectionUseCommonEndpointAzureAd": ".connection_use_common_endpoint_azure_ad", + "ConnectionUserAuthorizationUrloAuth1": ".connection_user_authorization_urlo_auth_1", "ConnectionUserIdAttributeSaml": ".connection_user_id_attribute_saml", "ConnectionUseridAttributeAzureAd": ".connection_userid_attribute_azure_ad", "ConnectionUseridAttributeEnumAzureAd": ".connection_userid_attribute_enum_azure_ad", @@ -4967,6 +4985,7 @@ def __dir__(): "ClientTokenExchangeTypeEnum", "ConnectedAccount", "ConnectedAccountAccessTypeEnum", + "ConnectionAccessTokenUrloAuth1", "ConnectionAcrValuesSupported", "ConnectionAdminAccessTokenExpiresInGoogleApps", "ConnectionAdminAccessTokenGoogleApps", @@ -5010,6 +5029,7 @@ def __dir__(): "ConnectionClientIdGoogleApps", "ConnectionClientIdGoogleOAuth2", "ConnectionClientIdLine", + "ConnectionClientIdOAuth1", "ConnectionClientIdOAuth2", "ConnectionClientIdOidc", "ConnectionClientIdPaypal", @@ -5025,6 +5045,7 @@ def __dir__(): "ConnectionClientSecretGoogleApps", "ConnectionClientSecretGoogleOAuth2", "ConnectionClientSecretLine", + "ConnectionClientSecretOAuth1", "ConnectionClientSecretOAuth2", "ConnectionClientSecretOidc", "ConnectionClientSecretPaypal", @@ -5058,6 +5079,8 @@ def __dir__(): "ConnectionDomainAliasesSaml", "ConnectionDomainGoogleApps", "ConnectionDomainOkta", + "ConnectionDpopSigningAlgEnum", + "ConnectionDpopSigningAlgValuesSupported", "ConnectionEmailBodyEmail", "ConnectionEmailEmail", "ConnectionEmailEmailSyntax", @@ -5264,6 +5287,7 @@ def __dir__(): "ConnectionRequestObjectSigningAlgValuesSupported", "ConnectionRequestParameterSupported", "ConnectionRequestTemplateSaml", + "ConnectionRequestTokenUrloAuth1", "ConnectionRequestUriParameterSupported", "ConnectionRequireRequestUriRegistration", "ConnectionRequiresUsername", @@ -5405,6 +5429,7 @@ def __dir__(): "ConnectionScopePaypal", "ConnectionScopeSalesforce", "ConnectionScopesSupported", + "ConnectionScriptsOAuth1", "ConnectionScriptsOAuth2", "ConnectionSendBackChannelNonce", "ConnectionServiceDocumentation", @@ -5419,6 +5444,7 @@ def __dir__(): "ConnectionSignSamlRequestSaml", "ConnectionSignatureAlgorithmEnumSaml", "ConnectionSignatureAlgorithmSaml", + "ConnectionSignatureMethodOAuth1", "ConnectionSigningCertSaml", "ConnectionSigningCertificateDerSaml", "ConnectionSigningCertificatePemPingFederate", @@ -5468,6 +5494,7 @@ def __dir__(): "ConnectionUpstreamParamsFacebook", "ConnectionUpstreamValue", "ConnectionUseCommonEndpointAzureAd", + "ConnectionUserAuthorizationUrloAuth1", "ConnectionUserIdAttributeSaml", "ConnectionUseridAttributeAzureAd", "ConnectionUseridAttributeEnumAzureAd", diff --git a/src/auth0/management/types/client_token_exchange_type_enum.py b/src/auth0/management/types/client_token_exchange_type_enum.py index bd3d05ab..64a50c6e 100644 --- a/src/auth0/management/types/client_token_exchange_type_enum.py +++ b/src/auth0/management/types/client_token_exchange_type_enum.py @@ -2,4 +2,6 @@ import typing -ClientTokenExchangeTypeEnum = typing.Union[typing.Literal["custom_authentication"], typing.Any] +ClientTokenExchangeTypeEnum = typing.Union[ + typing.Literal["custom_authentication", "on_behalf_of_token_exchange"], typing.Any +] diff --git a/src/auth0/management/types/connection_access_token_urlo_auth_1.py b/src/auth0/management/types/connection_access_token_urlo_auth_1.py new file mode 100644 index 00000000..3674283c --- /dev/null +++ b/src/auth0/management/types/connection_access_token_urlo_auth_1.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +from .connection_https_url_with_http_fallback_255 import ConnectionHttpsUrlWithHttpFallback255 + +ConnectionAccessTokenUrloAuth1 = ConnectionHttpsUrlWithHttpFallback255 diff --git a/src/auth0/management/types/connection_client_id_o_auth_1.py b/src/auth0/management/types/connection_client_id_o_auth_1.py new file mode 100644 index 00000000..353dd0e4 --- /dev/null +++ b/src/auth0/management/types/connection_client_id_o_auth_1.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +from .connection_client_id import ConnectionClientId + +ConnectionClientIdOAuth1 = ConnectionClientId diff --git a/src/auth0/management/types/connection_client_secret_o_auth_1.py b/src/auth0/management/types/connection_client_secret_o_auth_1.py new file mode 100644 index 00000000..cf71a738 --- /dev/null +++ b/src/auth0/management/types/connection_client_secret_o_auth_1.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +from .connection_client_secret import ConnectionClientSecret + +ConnectionClientSecretOAuth1 = ConnectionClientSecret diff --git a/src/auth0/management/types/connection_dpop_signing_alg_enum.py b/src/auth0/management/types/connection_dpop_signing_alg_enum.py new file mode 100644 index 00000000..10728b02 --- /dev/null +++ b/src/auth0/management/types/connection_dpop_signing_alg_enum.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ConnectionDpopSigningAlgEnum = typing.Union[typing.Literal["ES256", "Ed25519"], typing.Any] diff --git a/src/auth0/management/types/connection_dpop_signing_alg_values_supported.py b/src/auth0/management/types/connection_dpop_signing_alg_values_supported.py new file mode 100644 index 00000000..46dc21ee --- /dev/null +++ b/src/auth0/management/types/connection_dpop_signing_alg_values_supported.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ConnectionDpopSigningAlgValuesSupported = typing.List[str] diff --git a/src/auth0/management/types/connection_options_common_oidc.py b/src/auth0/management/types/connection_options_common_oidc.py index 342985d3..bd76f090 100644 --- a/src/auth0/management/types/connection_options_common_oidc.py +++ b/src/auth0/management/types/connection_options_common_oidc.py @@ -9,6 +9,7 @@ from .connection_client_secret_oidc import ConnectionClientSecretOidc from .connection_connection_settings import ConnectionConnectionSettings from .connection_domain_aliases import ConnectionDomainAliases +from .connection_dpop_signing_alg_enum import ConnectionDpopSigningAlgEnum from .connection_federated_connections_access_tokens import ConnectionFederatedConnectionsAccessTokens from .connection_icon_url import ConnectionIconUrl from .connection_id_token_signed_response_algs import ConnectionIdTokenSignedResponseAlgs @@ -35,8 +36,9 @@ class ConnectionOptionsCommonOidc(UniversalBaseModel): client_id: ConnectionClientIdOidc client_secret: typing.Optional[ConnectionClientSecretOidc] = None connection_settings: typing.Optional[ConnectionConnectionSettings] = None - federated_connections_access_tokens: typing.Optional[ConnectionFederatedConnectionsAccessTokens] = None domain_aliases: typing.Optional[ConnectionDomainAliases] = None + dpop_signing_alg: typing.Optional[ConnectionDpopSigningAlgEnum] = None + federated_connections_access_tokens: typing.Optional[ConnectionFederatedConnectionsAccessTokens] = None icon_url: typing.Optional[ConnectionIconUrl] = None id_token_signed_response_algs: typing.Optional[ConnectionIdTokenSignedResponseAlgs] = None issuer: typing.Optional[ConnectionIssuer] = None diff --git a/src/auth0/management/types/connection_options_o_auth_1.py b/src/auth0/management/types/connection_options_o_auth_1.py index 610e1f6c..f1e75307 100644 --- a/src/auth0/management/types/connection_options_o_auth_1.py +++ b/src/auth0/management/types/connection_options_o_auth_1.py @@ -3,18 +3,49 @@ import typing import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .connection_client_id import ConnectionClientId -from .connection_client_secret import ConnectionClientSecret +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.serialization import FieldMetadata +from .connection_access_token_urlo_auth_1 import ConnectionAccessTokenUrloAuth1 +from .connection_client_id_o_auth_1 import ConnectionClientIdOAuth1 +from .connection_client_secret_o_auth_1 import ConnectionClientSecretOAuth1 +from .connection_options_common import ConnectionOptionsCommon +from .connection_request_token_urlo_auth_1 import ConnectionRequestTokenUrloAuth1 +from .connection_scripts_o_auth_1 import ConnectionScriptsOAuth1 +from .connection_signature_method_o_auth_1 import ConnectionSignatureMethodOAuth1 +from .connection_upstream_params import ConnectionUpstreamParams +from .connection_user_authorization_urlo_auth_1 import ConnectionUserAuthorizationUrloAuth1 -class ConnectionOptionsOAuth1(UniversalBaseModel): +class ConnectionOptionsOAuth1(ConnectionOptionsCommon): """ Options for the 'oauth1' connection """ - client_id: typing.Optional[ConnectionClientId] = None - client_secret: typing.Optional[ConnectionClientSecret] = None + access_token_url: typing_extensions.Annotated[ + typing.Optional[ConnectionAccessTokenUrloAuth1], + FieldMetadata(alias="accessTokenURL"), + pydantic.Field(alias="accessTokenURL"), + ] = None + client_id: typing.Optional[ConnectionClientIdOAuth1] = None + client_secret: typing.Optional[ConnectionClientSecretOAuth1] = None + request_token_url: typing_extensions.Annotated[ + typing.Optional[ConnectionRequestTokenUrloAuth1], + FieldMetadata(alias="requestTokenURL"), + pydantic.Field(alias="requestTokenURL"), + ] = None + scripts: typing.Optional[ConnectionScriptsOAuth1] = None + signature_method: typing_extensions.Annotated[ + typing.Optional[ConnectionSignatureMethodOAuth1], + FieldMetadata(alias="signatureMethod"), + pydantic.Field(alias="signatureMethod"), + ] = None + upstream_params: typing.Optional[ConnectionUpstreamParams] = None + user_authorization_url: typing_extensions.Annotated[ + typing.Optional[ConnectionUserAuthorizationUrloAuth1], + FieldMetadata(alias="userAuthorizationURL"), + pydantic.Field(alias="userAuthorizationURL"), + ] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/auth0/management/types/connection_options_oidc_metadata.py b/src/auth0/management/types/connection_options_oidc_metadata.py index 6ac03618..9d456507 100644 --- a/src/auth0/management/types/connection_options_oidc_metadata.py +++ b/src/auth0/management/types/connection_options_oidc_metadata.py @@ -11,6 +11,7 @@ from .connection_claims_parameter_supported import ConnectionClaimsParameterSupported from .connection_claims_supported import ConnectionClaimsSupported from .connection_display_values_supported import ConnectionDisplayValuesSupported +from .connection_dpop_signing_alg_values_supported import ConnectionDpopSigningAlgValuesSupported from .connection_end_session_endpoint import ConnectionEndSessionEndpoint from .connection_grant_types_supported import ConnectionGrantTypesSupported from .connection_id_token_encryption_alg_values_supported import ConnectionIdTokenEncryptionAlgValuesSupported @@ -60,6 +61,7 @@ class ConnectionOptionsOidcMetadata(UniversalBaseModel): claims_parameter_supported: typing.Optional[ConnectionClaimsParameterSupported] = None claims_supported: typing.Optional[ConnectionClaimsSupported] = None display_values_supported: typing.Optional[ConnectionDisplayValuesSupported] = None + dpop_signing_alg_values_supported: typing.Optional[ConnectionDpopSigningAlgValuesSupported] = None end_session_endpoint: typing.Optional[ConnectionEndSessionEndpoint] = None grant_types_supported: typing.Optional[ConnectionGrantTypesSupported] = None id_token_encryption_alg_values_supported: typing.Optional[ConnectionIdTokenEncryptionAlgValuesSupported] = None diff --git a/src/auth0/management/types/connection_request_token_urlo_auth_1.py b/src/auth0/management/types/connection_request_token_urlo_auth_1.py new file mode 100644 index 00000000..309b6009 --- /dev/null +++ b/src/auth0/management/types/connection_request_token_urlo_auth_1.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +from .connection_https_url_with_http_fallback_255 import ConnectionHttpsUrlWithHttpFallback255 + +ConnectionRequestTokenUrloAuth1 = ConnectionHttpsUrlWithHttpFallback255 diff --git a/src/auth0/management/types/connection_scripts_o_auth_1.py b/src/auth0/management/types/connection_scripts_o_auth_1.py new file mode 100644 index 00000000..ce70a1c2 --- /dev/null +++ b/src/auth0/management/types/connection_scripts_o_auth_1.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.serialization import FieldMetadata + + +class ConnectionScriptsOAuth1(UniversalBaseModel): + """ + Custom scripts to transform user profile data or modify OAuth1 flow behavior + """ + + fetch_user_profile: typing_extensions.Annotated[ + typing.Optional[str], + FieldMetadata(alias="fetchUserProfile"), + pydantic.Field( + alias="fetchUserProfile", + description="Custom JavaScript function to retrieve and transform user profile data from the identity provider. Called with the access token and token exchange response. Must return a user profile object. Executed in a sandboxed environment. If not provided, an empty profile object is used.", + ), + ] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/auth0/management/types/connection_signature_method_o_auth_1.py b/src/auth0/management/types/connection_signature_method_o_auth_1.py new file mode 100644 index 00000000..e7a8e95e --- /dev/null +++ b/src/auth0/management/types/connection_signature_method_o_auth_1.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ConnectionSignatureMethodOAuth1 = typing.Union[typing.Literal["RSA-SHA1"], typing.Any] diff --git a/src/auth0/management/types/connection_user_authorization_urlo_auth_1.py b/src/auth0/management/types/connection_user_authorization_urlo_auth_1.py new file mode 100644 index 00000000..74755abb --- /dev/null +++ b/src/auth0/management/types/connection_user_authorization_urlo_auth_1.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +from .connection_https_url_with_http_fallback_255 import ConnectionHttpsUrlWithHttpFallback255 + +ConnectionUserAuthorizationUrloAuth1 = ConnectionHttpsUrlWithHttpFallback255 diff --git a/src/auth0/management/types/create_self_service_profile_response_content.py b/src/auth0/management/types/create_self_service_profile_response_content.py index 07e06b0d..7db67c95 100644 --- a/src/auth0/management/types/create_self_service_profile_response_content.py +++ b/src/auth0/management/types/create_self_service_profile_response_content.py @@ -46,7 +46,7 @@ class CreateSelfServiceProfileResponseContent(UniversalBaseModel): default=None ) """ - List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `keycloak-samlp`, `pingfederate`] + List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `auth0-samlp`, `okta-samlp`, `keycloak-samlp`, `pingfederate`] """ user_attribute_profile_id: typing.Optional[str] = pydantic.Field(default=None) diff --git a/src/auth0/management/types/get_self_service_profile_response_content.py b/src/auth0/management/types/get_self_service_profile_response_content.py index 4aa7e689..fde20226 100644 --- a/src/auth0/management/types/get_self_service_profile_response_content.py +++ b/src/auth0/management/types/get_self_service_profile_response_content.py @@ -46,7 +46,7 @@ class GetSelfServiceProfileResponseContent(UniversalBaseModel): default=None ) """ - List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `keycloak-samlp`, `pingfederate`] + List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `auth0-samlp`, `okta-samlp`, `keycloak-samlp`, `pingfederate`] """ user_attribute_profile_id: typing.Optional[str] = pydantic.Field(default=None) diff --git a/src/auth0/management/types/resource_server_proof_of_possession_required_for_enum.py b/src/auth0/management/types/resource_server_proof_of_possession_required_for_enum.py index bf8cadd0..322056fc 100644 --- a/src/auth0/management/types/resource_server_proof_of_possession_required_for_enum.py +++ b/src/auth0/management/types/resource_server_proof_of_possession_required_for_enum.py @@ -3,5 +3,5 @@ import typing ResourceServerProofOfPossessionRequiredForEnum = typing.Union[ - typing.Literal["public_clients", "confidential_clients", "all_clients"], typing.Any + typing.Literal["public_clients", "all_clients"], typing.Any ] diff --git a/src/auth0/management/types/self_service_profile.py b/src/auth0/management/types/self_service_profile.py index 0c227295..43fbad20 100644 --- a/src/auth0/management/types/self_service_profile.py +++ b/src/auth0/management/types/self_service_profile.py @@ -46,7 +46,7 @@ class SelfServiceProfile(UniversalBaseModel): default=None ) """ - List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `keycloak-samlp`, `pingfederate`] + List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `auth0-samlp`, `okta-samlp`, `keycloak-samlp`, `pingfederate`] """ user_attribute_profile_id: typing.Optional[str] = pydantic.Field(default=None) diff --git a/src/auth0/management/types/update_self_service_profile_response_content.py b/src/auth0/management/types/update_self_service_profile_response_content.py index 10c20b35..08d91b14 100644 --- a/src/auth0/management/types/update_self_service_profile_response_content.py +++ b/src/auth0/management/types/update_self_service_profile_response_content.py @@ -46,7 +46,7 @@ class UpdateSelfServiceProfileResponseContent(UniversalBaseModel): default=None ) """ - List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `keycloak-samlp`, `pingfederate`] + List of IdP strategies that will be shown to users during the Self-Service SSO flow. Possible values: [`oidc`, `samlp`, `waad`, `google-apps`, `adfs`, `okta`, `auth0-samlp`, `okta-samlp`, `keycloak-samlp`, `pingfederate`] """ user_attribute_profile_id: typing.Optional[str] = pydantic.Field(default=None) diff --git a/src/auth0/management/user_grants/raw_client.py b/src/auth0/management/user_grants/raw_client.py index 0de53c8d..b3ae3ba6 100644 --- a/src/auth0/management/user_grants/raw_client.py +++ b/src/auth0/management/user_grants/raw_client.py @@ -91,7 +91,7 @@ def list( _has_next = True _get_next = lambda: self.list( per_page=per_page, - page=page + len(_items or []), + page=page + 1, include_totals=include_totals, user_id=user_id, client_id=client_id, @@ -343,7 +343,7 @@ async def list( async def _get_next(): return await self.list( per_page=per_page, - page=page + len(_items or []), + page=page + 1, include_totals=include_totals, user_id=user_id, client_id=client_id, diff --git a/src/auth0/management/users/authentication_methods/raw_client.py b/src/auth0/management/users/authentication_methods/raw_client.py index ce622dec..1d98356f 100644 --- a/src/auth0/management/users/authentication_methods/raw_client.py +++ b/src/auth0/management/users/authentication_methods/raw_client.py @@ -96,7 +96,7 @@ def list( _has_next = True _get_next = lambda: self.list( id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, @@ -867,7 +867,7 @@ async def list( async def _get_next(): return await self.list( id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, diff --git a/src/auth0/management/users/logs/raw_client.py b/src/auth0/management/users/logs/raw_client.py index d054cbf8..973d02a1 100644 --- a/src/auth0/management/users/logs/raw_client.py +++ b/src/auth0/management/users/logs/raw_client.py @@ -91,7 +91,7 @@ def list( _has_next = True _get_next = lambda: self.list( id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, sort=sort, include_totals=include_totals, @@ -224,7 +224,7 @@ async def list( async def _get_next(): return await self.list( id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, sort=sort, include_totals=include_totals, diff --git a/src/auth0/management/users/organizations/raw_client.py b/src/auth0/management/users/organizations/raw_client.py index 7b2b3066..cb0dfa49 100644 --- a/src/auth0/management/users/organizations/raw_client.py +++ b/src/auth0/management/users/organizations/raw_client.py @@ -81,7 +81,7 @@ def list( _has_next = True _get_next = lambda: self.list( id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, @@ -191,7 +191,7 @@ async def list( async def _get_next(): return await self.list( id, - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, request_options=request_options, diff --git a/src/auth0/management/users/permissions/raw_client.py b/src/auth0/management/users/permissions/raw_client.py index ea90dbd5..2b272860 100644 --- a/src/auth0/management/users/permissions/raw_client.py +++ b/src/auth0/management/users/permissions/raw_client.py @@ -90,7 +90,7 @@ def list( _get_next = lambda: self.list( id, per_page=per_page, - page=page + len(_items or []), + page=page + 1, include_totals=include_totals, request_options=request_options, ) @@ -404,7 +404,7 @@ async def _get_next(): return await self.list( id, per_page=per_page, - page=page + len(_items or []), + page=page + 1, include_totals=include_totals, request_options=request_options, ) diff --git a/src/auth0/management/users/raw_client.py b/src/auth0/management/users/raw_client.py index 47ef7d60..2ff161bc 100644 --- a/src/auth0/management/users/raw_client.py +++ b/src/auth0/management/users/raw_client.py @@ -137,7 +137,7 @@ def list( _items = _parsed_response.users _has_next = True _get_next = lambda: self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, sort=sort, @@ -1225,7 +1225,7 @@ async def list( async def _get_next(): return await self.list( - page=page + len(_items or []), + page=page + 1, per_page=per_page, include_totals=include_totals, sort=sort, diff --git a/src/auth0/management/users/roles/raw_client.py b/src/auth0/management/users/roles/raw_client.py index a696577d..efeb5d58 100644 --- a/src/auth0/management/users/roles/raw_client.py +++ b/src/auth0/management/users/roles/raw_client.py @@ -87,7 +87,7 @@ def list( _get_next = lambda: self.list( id, per_page=per_page, - page=page + len(_items or []), + page=page + 1, include_totals=include_totals, request_options=request_options, ) @@ -362,7 +362,7 @@ async def _get_next(): return await self.list( id, per_page=per_page, - page=page + len(_items or []), + page=page + 1, include_totals=include_totals, request_options=request_options, ) diff --git a/tests/conftest.py b/tests/conftest.py index dd48d29f..02300944 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -16,35 +16,18 @@ _STARTED: bool = False _WIREMOCK_PORT: str = "8080" # Default, will be updated after container starts +_PROJECT_NAME: str = "auth0-api" - -def _compose_file() -> str: - """Returns the path to the docker-compose file for WireMock.""" - # This file lives in tests/conftest.py, so the project root is the parent of tests. - tests_dir = os.path.dirname(__file__) - project_root = os.path.abspath(os.path.join(tests_dir, "..")) - wiremock_dir = os.path.join(project_root, "wiremock") - return os.path.join(wiremock_dir, "docker-compose.test.yml") - - -def _project_name() -> str: - """Returns a unique project name for this test fixture to avoid container name conflicts.""" - tests_dir = os.path.dirname(__file__) - project_root = os.path.abspath(os.path.join(tests_dir, "..")) - # Use the last two directory names to create a unique project name - # e.g., "python-streaming-parameter-openapi-with-wire-tests" - parent = os.path.basename(os.path.dirname(project_root)) - current = os.path.basename(project_root) - return f"{parent}-{current}".replace("_", "-").lower() +# This file lives at tests/conftest.py, so the project root is one level up. +_PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +_COMPOSE_FILE = os.path.join(_PROJECT_ROOT, "wiremock", "docker-compose.test.yml") def _get_wiremock_port() -> str: """Gets the dynamically assigned port for the WireMock container.""" - compose_file = _compose_file() - project = _project_name() try: result = subprocess.run( - ["docker", "compose", "-f", compose_file, "-p", project, "port", "wiremock", "8080"], + ["docker", "compose", "-f", _COMPOSE_FILE, "-p", _PROJECT_NAME, "port", "wiremock", "8080"], check=True, capture_output=True, text=True, @@ -62,12 +45,10 @@ def _start_wiremock() -> None: if _STARTED: return - compose_file = _compose_file() - project = _project_name() - print(f"\nStarting WireMock container (project: {project})...") + print(f"\nStarting WireMock container (project: {_PROJECT_NAME})...") try: subprocess.run( - ["docker", "compose", "-f", compose_file, "-p", project, "up", "-d", "--wait"], + ["docker", "compose", "-f", _COMPOSE_FILE, "-p", _PROJECT_NAME, "up", "-d", "--wait"], check=True, capture_output=True, text=True, @@ -83,11 +64,9 @@ def _start_wiremock() -> None: def _stop_wiremock() -> None: """Stops and removes the WireMock container.""" - compose_file = _compose_file() - project = _project_name() print("\nStopping WireMock container...") subprocess.run( - ["docker", "compose", "-f", compose_file, "-p", project, "down", "-v"], + ["docker", "compose", "-f", _COMPOSE_FILE, "-p", _PROJECT_NAME, "down", "-v"], check=False, capture_output=True, ) diff --git a/tests/wire/conftest.py b/tests/wire/conftest.py index 8498ca58..252a18dd 100644 --- a/tests/wire/conftest.py +++ b/tests/wire/conftest.py @@ -5,18 +5,24 @@ WireMock and for verifying requests in WireMock. The WireMock container lifecycle itself is managed by a top-level pytest -plugin (wiremock_pytest_plugin.py) so that the container is started exactly -once per test run, even when using pytest-xdist. +plugin (tests/conftest.py) so that the container is started exactly once +per test run, even when using pytest-xdist. """ import inspect import os from typing import Any, Dict, Optional -import requests +import httpx from auth0.management.client import Auth0 +# Check once at import time whether the client constructor accepts a headers kwarg. +try: + _CLIENT_SUPPORTS_HEADERS: bool = "headers" in inspect.signature(Auth0).parameters +except (TypeError, ValueError): + _CLIENT_SUPPORTS_HEADERS = False + def _get_wiremock_base_url() -> str: """Returns the WireMock base URL using the dynamically assigned port.""" @@ -37,18 +43,12 @@ def get_client(test_id: str) -> Auth0: test_headers = {"X-Test-Id": test_id} base_url = _get_wiremock_base_url() - # Prefer passing headers directly if the client constructor supports it. - try: - if "headers" in inspect.signature(Auth0).parameters: - return Auth0( - base_url=base_url, - headers=test_headers, - token="test_token", - ) - except (TypeError, ValueError): - pass - - import httpx + if _CLIENT_SUPPORTS_HEADERS: + return Auth0( + base_url=base_url, + headers=test_headers, + token="test_token", + ) return Auth0( base_url=base_url, @@ -64,7 +64,7 @@ def verify_request_count( query_params: Optional[Dict[str, str]], expected: int, ) -> None: - """Verifies the number of requests made to WireMock filtered by test ID for concurrency safety""" + """Verifies the number of requests made to WireMock filtered by test ID for concurrency safety.""" wiremock_admin_url = f"{_get_wiremock_base_url()}/__admin" request_body: Dict[str, Any] = { "method": method, @@ -74,7 +74,7 @@ def verify_request_count( if query_params: query_parameters = {k: {"equalTo": v} for k, v in query_params.items()} request_body["queryParameters"] = query_parameters - response = requests.post(f"{wiremock_admin_url}/requests/find", json=request_body) + response = httpx.post(f"{wiremock_admin_url}/requests/find", json=request_body) assert response.status_code == 200, "Failed to query WireMock requests" result = response.json() requests_found = len(result.get("requests", []))