From 92f6ca8d34db81dd8739e346eff2ac412496f945 Mon Sep 17 00:00:00 2001 From: Samuel Wan Date: Tue, 28 Oct 2025 14:24:17 -0700 Subject: [PATCH 1/8] feat:headers-support --- src/britive/britive.py | 32 +++++----- src/britive/helpers/methods.py | 4 +- src/britive/my_access.py | 108 +++++++++++++++++++++++++++------ 3 files changed, 108 insertions(+), 36 deletions(-) diff --git a/src/britive/britive.py b/src/britive/britive.py index 30ee5b5..86e94ae 100644 --- a/src/britive/britive.py +++ b/src/britive/britive.py @@ -181,30 +181,30 @@ def features(self) -> dict: def banner(self) -> dict: return self.get(f'{self.base_url}/banner') - def get(self, url, params=None) -> dict: + def get(self, url, params: dict = None, headers: dict = None) -> dict: """Internal use only.""" - return self.__request('get', url, params=params) + return self.__request('get', url, params=params, headers=headers) - def post(self, url, params=None, data=None, json=None) -> dict: + def post(self, url, params: dict = None, data: dict = None, json: dict = None, headers: dict = None) -> dict: """Internal use only.""" - return self.__request('post', url, params=params, data=data, json=json) + return self.__request('post', url, params=params, data=data, json=json, headers=headers) - def patch(self, url, params=None, data=None, json=None) -> dict: + def patch(self, url, params: dict = None, data: dict = None, json: dict = None, headers: dict = None) -> dict: """Internal use only.""" - return self.__request('patch', url, params=params, data=data, json=json) + return self.__request('patch', url, params=params, data=data, json=json, headers=headers) - def put(self, url, params=None, data=None, json=None) -> dict: + def put(self, url, params: dict = None, data: dict = None, json: dict = None, headers: dict = None) -> dict: """Internal use only.""" - return self.__request('put', url, params=params, data=data, json=json) + return self.__request('put', url, params=params, data=data, json=json, headers=headers) - def delete(self, url, params=None, data=None, json=None) -> dict: + def delete(self, url, params: dict = None, data: dict = None, json: dict = None, headers: dict = None) -> dict: """Internal use only.""" - return self.__request('delete', url, params=params, data=data, json=json) + return self.__request('delete', url, params=params, data=data, json=json, headers=headers) # note - this method could be iffy in the future if the app changes the way it handles # file uploads. As of 2022-01-26 it is working fine with the "Upload SAML Metadata" action @@ -223,11 +223,13 @@ def post_upload(self, url, params=None, files=None) -> dict: response = self.session.post(url, params=params, files=files, headers={'Content-Type': None}) return handle_response(response) - def __request_with_exponential_backoff_and_retry(self, method, url, params, data, json) -> dict: + def __request_with_exponential_backoff_and_retry(self, method, url, params, data, json, headers) -> dict: num_retries = 0 while num_retries <= self.retry_max_times: - response = self.session.request(method, url, params=params, data=data, json=json) + response = self.session.request( + method, url, params=params, data=data, json=json, headers={**self.session.headers, **headers} + ) # handle the use case of a tenant being in maintenance mode # which means we should break out of this loop early and @@ -244,15 +246,17 @@ def __request_with_exponential_backoff_and_retry(self, method, url, params, data return response - def __request(self, method, url, params=None, data=None, json=None) -> dict: + def __request(self, method, url, params=None, data=None, json=None, headers=None) -> dict: return_data = [] _pagination_type = None if params is None: params = {} + if headers is None: + headers = {} while True: - response = self.__request_with_exponential_backoff_and_retry(method, url, params, data, json) + response = self.__request_with_exponential_backoff_and_retry(method, url, params, data, json, headers) if response_has_no_content(response): return None diff --git a/src/britive/helpers/methods.py b/src/britive/helpers/methods.py index 28d0a32..0fcf58d 100644 --- a/src/britive/helpers/methods.py +++ b/src/britive/helpers/methods.py @@ -3,12 +3,12 @@ def __init__(self, britive) -> None: self.britive = britive def get_profile_and_environment_ids_given_names( - self, profile_name: str, environment_name: str, application_name: str = None + self, profile_name: str, environment_name: str, application_name: str = None, headers: dict = None ) -> dict: ids = None environment_found = False profile_found = False - for app in self.britive.get(f'{self.britive.base_url}/access'): + for app in self.britive.get(f'{self.britive.base_url}/access', headers=headers): if application_name and app['appName'].lower() != application_name.lower(): continue if not ( diff --git a/src/britive/my_access.py b/src/britive/my_access.py index 063f459..1ef949c 100644 --- a/src/britive/my_access.py +++ b/src/britive/my_access.py @@ -73,24 +73,30 @@ def list(self, filter_text: str = None, search_text: str = None, size: int = Non return self.britive.get(self.base_url, params=params) - def list_profiles(self) -> list: + def list_profiles(self, headers=None) -> list: """ List the profiles for which the user has access. :return: List of profiles. """ - return self.britive.get(self.base_url) + return self.britive.get(self.base_url, headers=headers) - def list_checked_out_profiles(self, include_profile_details: bool = False) -> list: + def list_checked_out_profiles(self, include_profile_details: bool = False, headers: dict = None) -> list: """ Return list of details on currently checked out profiles for the user. :param include_profile_details: Include `details` for each checked out profile. + :param headers: Any additional headers + Example: + { + "X-On-Behalf-Of": "Bearer ...", + ... + } :return: List of checked out profiles. """ - checked_out_profiles = self.britive.get(f'{self.base_url}/app-access-status') + checked_out_profiles = self.britive.get(f'{self.base_url}/app-access-status', headers=headers) if include_profile_details: for profile in checked_out_profiles: @@ -103,15 +109,21 @@ def list_checked_out_profiles(self, include_profile_details: bool = False) -> li return checked_out_profiles - def get_checked_out_profile(self, transaction_id: str) -> dict: + def get_checked_out_profile(self, transaction_id: str, headers: dict = None) -> dict: """ Retrieve details of a given checked out profile. :param transaction_id: The ID of the transaction. + :param headers: Any additional headers + Example: + { + "X-On-Behalf-Of": "Bearer ...", + ... + } :return: Details of the given profile/transaction. """ - for t in self.list_checked_out_profiles(): + for t in self.list_checked_out_profiles(headers=headers): if t['transactionId'] == transaction_id: return t raise TransactionNotFound @@ -190,6 +202,7 @@ def _checkout( self, profile_id: str, environment_id: str, + headers: dict = None, include_credentials: bool = False, iteration_num: int = 1, justification: str = None, @@ -220,7 +233,7 @@ def _checkout( if progress_func and not progress_pending_checked_out_profiles_sent: progress_func('reviewing currently checked out profiles') progress_pending_checked_out_profiles_sent = True - for p in self.list_checked_out_profiles(): + for p in self.list_checked_out_profiles(headers=headers): right_profile = p['papId'] == profile_id right_env = p['environmentId'] == environment_id right_type = p['accessType'] == params['accessType'] @@ -246,7 +259,10 @@ def _checkout( try: transaction = self.britive.post( - f'{self.base_url}/{profile_id}/environments/{environment_id}', params=params, json=data + f'{self.base_url}/{profile_id}/environments/{environment_id}', + params=params, + json=data, + headers=headers, ) except StepUpAuthenticationRequiredError as e: raise StepUpAuthRequiredButNotProvided(e) from e @@ -271,7 +287,10 @@ def _checkout( # handle the response based on the value of status if status == 'approved': transaction = self.britive.post( - f'{self.base_url}/{profile_id}/environments/{environment_id}', params=params, json=data + f'{self.base_url}/{profile_id}/environments/{environment_id}', + params=params, + json=data, + headers=headers, ) else: raise approval_exceptions[status](e) from e @@ -291,6 +310,7 @@ def _checkout( raise e return self._checkout( environment_id=environment_id, + headers=headers, include_credentials=include_credentials, iteration_num=iteration_num + 1, justification=justification, @@ -327,6 +347,7 @@ def checkout( self, profile_id: str, environment_id: str, + headers: dict = None, include_credentials: bool = False, justification: str = None, max_wait_time: int = 600, @@ -349,6 +370,12 @@ def checkout( :param profile_id: The ID of the profile. Use `list_profiles()` to obtain the eligible profiles. :param environment_id: The ID of the environment. Use `list_profiles()` to obtain the eligible environments. + :param headers: Any additional headers + Example: + { + "X-On-Behalf-Of": "Bearer ...", + ... + } :param include_credentials: True if tokens should be included in the response. False if the caller wishes to call `credentials()` at a later time. If True, the `credentials` key will be included in the response which contains the response from `credentials()`. Setting this parameter to `True` will result in a synchronous @@ -374,6 +401,7 @@ def checkout( return self._checkout( profile_id=profile_id, environment_id=environment_id, + headers=headers, include_credentials=include_credentials, justification=justification, max_wait_time=max_wait_time, @@ -390,6 +418,7 @@ def checkout_by_name( profile_name: str, environment_name: str, application_name: str = None, + headers: dict = None, include_credentials: bool = False, justification: str = None, max_wait_time: int = 600, @@ -412,6 +441,12 @@ def checkout_by_name( :param environment_name: The name of the environment. Use `list_profiles()` to obtain the eligible environments. :param application_name: Optionally the name of the application, which can help disambiguate between profiles with the same name across applications. + :param headers: Any additional headers + Example: + { + "X-On-Behalf-Of": "Bearer ...", + ... + } :param include_credentials: True if tokens should be included in the response. False if the caller wishes to call `credentials()` at a later time. If True, the `credentials` key will be included in the response which contains the response from `credentials()`. Setting this parameter to `True` will result in a synchronous @@ -434,11 +469,14 @@ def checkout_by_name( :raises ProfileApprovalWithdrawn: if the approval request was withdrawn by the requester. """ - ids = self._get_profile_and_environment_ids_given_names(profile_name, environment_name, application_name) + ids = self._get_profile_and_environment_ids_given_names( + profile_name, environment_name, application_name, headers=headers + ) return self._checkout( profile_id=ids['profile_id'], environment_id=ids['environment_id'], + headers=headers, include_credentials=include_credentials, justification=justification, max_wait_time=max_wait_time, @@ -453,6 +491,7 @@ def checkout_by_name( def credentials( self, transaction_id: str, + headers: dict = None, transaction: dict = None, return_transaction_details: bool = False, progress_func: Callable = None, @@ -464,6 +503,12 @@ def credentials( details. :param transaction_id: The ID of the transaction. + :param headers: Any additional headers + Example: + { + "X-On-Behalf-Of": "Bearer ...", + ... + } :param transaction: Optional - the details of the transaction. Primary use is for internal purposes. :param return_transaction_details: Optional - whether to return the details of the transaction. Primary use is for internal purposes. @@ -476,7 +521,7 @@ def credentials( # or the transaction is not in the state of checkedOut if not transaction or transaction['status'] != 'checkedOut': while True: - transaction = self.get_checked_out_profile(transaction_id=transaction_id) + transaction = self.get_checked_out_profile(transaction_id=transaction_id, headers=headers) if transaction['status'] == 'checkOutSubmitted': # async checkout process if progress_func: progress_func('credential creation') @@ -487,24 +532,32 @@ def credentials( # step 2: make the proper API call url_part = 'url' if transaction['accessType'] == 'CONSOLE' else 'tokens' - creds = self.britive.get(f'{self.base_url}/{transaction_id}/{url_part}') + creds = self.britive.get(f'{self.base_url}/{transaction_id}/{url_part}', headers=headers) if return_transaction_details: return creds, transaction return creds - def checkin(self, transaction_id: str) -> dict: + def checkin(self, transaction_id: str, headers: dict = None) -> dict: """ Check in a checked out profile. :param transaction_id: The ID of the transaction. + :param headers: Any additional headers + Example: + { + "X-On-Behalf-Of": "Bearer ...", + ... + } :return: Details of the checked in profile. """ params = {'type': 'API'} - return self.britive.put(f'{self.base_url}/{transaction_id}', params=params) + return self.britive.put(f'{self.base_url}/{transaction_id}', params=params, headers=headers) - def checkin_by_name(self, profile_name: str, environment_name: str, application_name: str = None) -> dict: + def checkin_by_name( + self, profile_name: str, environment_name: str, application_name: str = None, headers: dict = None + ) -> dict: """ Check in a checked out profile by supplying the names of entities vs. the IDs of those entities @@ -512,29 +565,44 @@ def checkin_by_name(self, profile_name: str, environment_name: str, application_ :param environment_name: The name of the environment. Use `list_profiles()` to obtain the eligible environments. :param application_name: Optionally the name of the application, which can help disambiguate between profiles with the same name across applications. + :param headers: Any additional headers + Example: + { + "X-On-Behalf-Of": "Bearer ...", + ... + } :return: Details of the checked in profile. """ - ids = self._get_profile_and_environment_ids_given_names(profile_name, environment_name, application_name) + ids = self._get_profile_and_environment_ids_given_names( + profile_name, environment_name, application_name, headers=headers + ) transaction_id = None - for profile in self.list_checked_out_profiles(): + for profile in self.list_checked_out_profiles(headers=headers): if profile['environmentId'] == ids['environment_id'] and profile['papId'] == ids['profile_id']: transaction_id = profile['transactionId'] break if not transaction_id: raise ValueError('no checked out profile found for the given profile_name and environment_name') - return self.checkin(transaction_id=transaction_id) + return self.checkin(transaction_id=transaction_id, headers=headers) - def whoami(self) -> dict: + def whoami(self, headers=None) -> dict: """ Return details about the currently authenticated identity (user or service). + :param headers: Any additional headers + Example: + { + "X-On-Behalf-Of": "Bearer ...", + ... + } + :return: Details of the currently authenticated identity. """ - return self.britive.post(f'{self.britive.base_url}/auth/validate')['authenticationResult'] + return self.britive.post(f'{self.britive.base_url}/auth/validate', headers=headers)['authenticationResult'] def frequents(self) -> list: """ From ad33e8008535ab75fc45f20c161a70f78241d180 Mon Sep 17 00:00:00 2001 From: theborch Date: Tue, 28 Oct 2025 18:19:41 -0500 Subject: [PATCH 2/8] style:bump to 14 args --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9da742b..a767b54 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -97,7 +97,7 @@ select = [ [tool.ruff.lint.pylint] allow-magic-value-types = ["int", "str"] -max-args = 12 +max-args = 14 max-branches = 30 max-returns = 8 max-statements = 72 From 24a33ff0682bd1a6ee79fb589aa665ade17ffef3 Mon Sep 17 00:00:00 2001 From: theborch Date: Tue, 28 Oct 2025 18:31:07 -0500 Subject: [PATCH 3/8] feat:add impersonation to profiles --- .../access_broker/profiles/__init__.py | 23 ++++++++++++++--- .../profiles/__init__.py | 25 +++++++++++-------- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/britive/access_broker/profiles/__init__.py b/src/britive/access_broker/profiles/__init__.py index 044c4a0..bad81b8 100644 --- a/src/britive/access_broker/profiles/__init__.py +++ b/src/britive/access_broker/profiles/__init__.py @@ -12,17 +12,25 @@ def __init__(self, britive) -> None: self.permissions = Permissions(britive) self.policies = Policies(britive) - def create(self, name: str, description: str = '', expiration_duration: int = 900000) -> dict: + def create( + self, name: str, description: str = '', expiration_duration: int = 900000, impersonation: bool = False + ) -> dict: """ Create a new profile. :param name: Name of the profile. :param description: Description of the profile. :param expiration_duration: Expiration duration of the profile. + :param impersonation: Allow impersonation of the profile. :return: Created profile. """ - params = {'name': name, 'description': description, 'expirationDuration': expiration_duration} + params = { + 'name': name, + 'delegationEnabled': impersonation, + 'description': description, + 'expirationDuration': expiration_duration, + } return self.britive.post(f'{self.base_url}', json=params) @@ -46,7 +54,12 @@ def list(self) -> list: return self.britive.get(self.base_url, params={}) def update( - self, profile_id: str, name: str = None, description: str = None, expiration_duration: int = None + self, + profile_id: str, + name: str = None, + description: str = None, + expiration_duration: int = None, + impersonation: bool = None, ) -> dict: """ Update a profile. @@ -55,6 +68,7 @@ def update( :param name: Name of the profile. :param description: Description of the profile. :param expiration_duration: Expiration duration of the profile. + :param impersonation: Allow impersonation of the profile. :return: Updated profile. """ @@ -66,6 +80,9 @@ def update( params['description'] = description if expiration_duration: params['expirationDuration'] = expiration_duration + params['delegationEnabled'] = ( + self.get(profile_id=profile_id).get('delegationEnabled') if impersonation is None else impersonation + ) return self.britive.patch(f'{self.base_url}/{profile_id}', json=params) diff --git a/src/britive/application_management/profiles/__init__.py b/src/britive/application_management/profiles/__init__.py index 6a151e4..b938d31 100644 --- a/src/britive/application_management/profiles/__init__.py +++ b/src/britive/application_management/profiles/__init__.py @@ -7,15 +7,17 @@ from .session_attributes import SessionAttributes creation_defaults = { + 'delegationEnabled': False, + 'description': '', + 'destinationUrl': '', + 'excludeAdminFromAdminRelatedCommunication': 'DEFAULT', 'expirationDuration': 3600000, - 'extensionDuration': 1800000, - 'notificationPriorToExpiration': 300000, 'extendable': False, + 'extensionDuration': 1800000, 'extensionLimit': '1', + 'notificationPriorToExpiration': 300000, 'status': 'active', - 'destinationUrl': '', 'useDefaultAppUrl': True, - 'description': '', } update_fields_to_keep: list = list(creation_defaults) @@ -42,24 +44,25 @@ def create(self, application_id: str, name: str, **kwargs) -> dict: :param kwargs: A key/value mapping consisting of the following fields. If any/all are omitted default values will be used. The keys and default values are provided below. + - delegationEnabled: False + - description: '' + - destinationUrl: '' + - excludeAdminFromAdminRelatedCommunication: 'DEFAULT' - expirationDuration: 3600000 - - extensionDuration: 1800000 - - notificationPriorToExpiration: 300000 - extendable: False + - extensionDuration: 1800000 - extensionLimit: '1' - - status: 'active' - - destinationUrl: '' - - useDefaultAppUrl: True - - description: '' + - notificationPriorToExpiration: 300000 - scope: if not provided, no scopes will be applied. If provided it must follow the format listed below. - [ { 'type': 'EnvironmentGroup'|'Environment', 'value':'ID' }, ] + - status: 'active' + - useDefaultAppUrl: True :return: Details of the newly created profile. """ From 49e179f9b8de5650bdbed70786e85d34d4433e16 Mon Sep 17 00:00:00 2001 From: theborch Date: Tue, 28 Oct 2025 18:38:53 -0500 Subject: [PATCH 4/8] feat:add ai identities --- src/britive/identity_management/__init__.py | 1 + src/britive/identity_management/service_identities.py | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/britive/identity_management/__init__.py b/src/britive/identity_management/__init__.py index 4d45abe..fb12111 100644 --- a/src/britive/identity_management/__init__.py +++ b/src/britive/identity_management/__init__.py @@ -8,6 +8,7 @@ class IdentityManagement: def __init__(self, britive) -> None: + self.ai_identities = ServiceIdentities(britive, identity_type='AIIdentity') self.identity_attributes = IdentityAttributes(britive) self.identity_providers = IdentityProviders(britive) self.service_identities = ServiceIdentities(britive) diff --git a/src/britive/identity_management/service_identities.py b/src/britive/identity_management/service_identities.py index 865ce48..385a785 100644 --- a/src/britive/identity_management/service_identities.py +++ b/src/britive/identity_management/service_identities.py @@ -4,10 +4,11 @@ class ServiceIdentities: - def __init__(self, britive) -> None: + def __init__(self, britive, identity_type: str = 'ServiceIdentity') -> None: self.britive = britive self.base_url = f'{self.britive.base_url}/users' self.custom_attributes = CustomAttributes(britive) + self.identity_type = identity_type def list(self, filter_expression: str = None, include_tags: bool = False) -> list: """ @@ -19,7 +20,7 @@ def list(self, filter_expression: str = None, include_tags: bool = False) -> lis :return: List of service identity records """ - params = {'type': 'ServiceIdentity', 'page': 0, 'size': 100} + params = {'type': self.identity_type, 'page': 0, 'size': 100} if filter_expression: params['filter'] = filter_expression if include_tags: @@ -35,7 +36,7 @@ def get(self, service_identity_id: str) -> dict: :return: Details of the specified user. """ - params = {'type': 'ServiceIdentity'} + params = {'type': self.identity_type} return self.britive.get(f'{self.base_url}/{service_identity_id}', params=params) def get_by_name(self, name: str) -> list: @@ -70,7 +71,7 @@ def search(self, search_string: str) -> list: :return: List of user records """ - params = {'type': 'ServiceIdentity', 'page': 0, 'size': 100, 'searchText': search_string} + params = {'type': self.identity_type, 'page': 0, 'size': 100, 'searchText': search_string} return self.britive.get(self.base_url, params) @@ -87,7 +88,7 @@ def create(self, **kwargs) -> dict: required_fields = ['name'] - kwargs['type'] = 'ServiceIdentity' + kwargs['type'] = self.identity_type if 'status' not in kwargs: kwargs['status'] = 'active' From dce25a80c6f651dab0097dba5ff3c0719a24c733 Mon Sep 17 00:00:00 2001 From: Samuel Wan Date: Fri, 31 Oct 2025 10:12:53 -0700 Subject: [PATCH 5/8] docs:add additional header examples --- src/britive/my_access.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/britive/my_access.py b/src/britive/my_access.py index 1ef949c..8932aa4 100644 --- a/src/britive/my_access.py +++ b/src/britive/my_access.py @@ -90,7 +90,7 @@ def list_checked_out_profiles(self, include_profile_details: bool = False, heade :param headers: Any additional headers Example: { - "X-On-Behalf-Of": "Bearer ...", + "X-On-Behalf-Of": "Bearer ... | user@... | username", ... } :return: List of checked out profiles. @@ -117,7 +117,7 @@ def get_checked_out_profile(self, transaction_id: str, headers: dict = None) -> :param headers: Any additional headers Example: { - "X-On-Behalf-Of": "Bearer ...", + "X-On-Behalf-Of": "Bearer ... | user@... | username", ... } :return: Details of the given profile/transaction. @@ -373,7 +373,7 @@ def checkout( :param headers: Any additional headers Example: { - "X-On-Behalf-Of": "Bearer ...", + "X-On-Behalf-Of": "Bearer ... | user@... | username", ... } :param include_credentials: True if tokens should be included in the response. False if the caller wishes to @@ -444,7 +444,7 @@ def checkout_by_name( :param headers: Any additional headers Example: { - "X-On-Behalf-Of": "Bearer ...", + "X-On-Behalf-Of": "Bearer ... | user@... | username", ... } :param include_credentials: True if tokens should be included in the response. False if the caller wishes to @@ -506,7 +506,7 @@ def credentials( :param headers: Any additional headers Example: { - "X-On-Behalf-Of": "Bearer ...", + "X-On-Behalf-Of": "Bearer ... | user@... | username", ... } :param transaction: Optional - the details of the transaction. Primary use is for internal purposes. @@ -546,7 +546,7 @@ def checkin(self, transaction_id: str, headers: dict = None) -> dict: :param headers: Any additional headers Example: { - "X-On-Behalf-Of": "Bearer ...", + "X-On-Behalf-Of": "Bearer ... | user@... | username", ... } :return: Details of the checked in profile. @@ -568,7 +568,7 @@ def checkin_by_name( :param headers: Any additional headers Example: { - "X-On-Behalf-Of": "Bearer ...", + "X-On-Behalf-Of": "Bearer ... | user@... | username", ... } :return: Details of the checked in profile. @@ -595,7 +595,7 @@ def whoami(self, headers=None) -> dict: :param headers: Any additional headers Example: { - "X-On-Behalf-Of": "Bearer ...", + "X-On-Behalf-Of": "Bearer ... | user@... | username", ... } From 87d3d652fedcbe35942770f56510ec3177ba5760 Mon Sep 17 00:00:00 2001 From: theborch Date: Fri, 7 Nov 2025 13:06:25 -0600 Subject: [PATCH 6/8] docs:federation providers update --- src/britive/helpers/utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/britive/helpers/utils.py b/src/britive/helpers/utils.py index 9870557..be9cbce 100644 --- a/src/britive/helpers/utils.py +++ b/src/britive/helpers/utils.py @@ -108,6 +108,7 @@ def source_federation_token(provider: str, tenant: Optional[str] = None, duratio * Azure System Assigned Managed Identities (azuresmi) * Azure User Assigned Managed Identities (azureumi) * Bitbucket Pipelines (bitbucket) + * GCP * Github Actions (github) * Gitlab (gitlab) * spacelift.io (spacelift) @@ -115,8 +116,8 @@ def source_federation_token(provider: str, tenant: Optional[str] = None, duratio Any other OIDC federation provider can be used and tokens can be provided to this class for authentication to a Britive tenant. Details of how to construct these tokens can be found at https://docs.britive.com. - :param provider: The name of the federation provider. Valid options are `aws`, `github`, `bitbucket`, - `azuresmi`, `azureumi`, `spacelift`, and `gitlab`. + :param provider: The name of the federation provider. Valid options are `aws`, `azuresmi`, `azureumi`, `bitbucket`, + `gcp`, `github`, `gitlab`, and `spacelift`. For the AWS provider it is possible to provide a profile via value `aws-profile`. If no profile is provided then the boto3 `Session.get_credentials()` method will be used to obtain AWS credentials, which follows From 1b4f2daef0a1e285ae379beee0105a1ba42828d7 Mon Sep 17 00:00:00 2001 From: Samuel Wan Date: Fri, 21 Nov 2025 11:54:01 -0800 Subject: [PATCH 7/8] update ver --- src/britive/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/britive/__init__.py b/src/britive/__init__.py index 26a6c39..330025d 100644 --- a/src/britive/__init__.py +++ b/src/britive/__init__.py @@ -1 +1 @@ -__version__ = '4.4.0' +__version__ = '4.5.0' From 363f947914950843dce0706396a37b8ede8dbc04 Mon Sep 17 00:00:00 2001 From: Samuel Wan Date: Fri, 21 Nov 2025 11:55:07 -0800 Subject: [PATCH 8/8] update ver --- src/britive/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/britive/__init__.py b/src/britive/__init__.py index 330025d..a3daa5f 100644 --- a/src/britive/__init__.py +++ b/src/britive/__init__.py @@ -1 +1 @@ -__version__ = '4.5.0' +__version__ = '4.5.0rc1'