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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from typing import Dict, Any, List

import requests

from dnastack.common.tracing import Span
from dnastack.http.authenticators.oauth2_adapter.abstract import OAuth2Adapter, AuthException


class ClientCredentialsClientAssertionAdapter(OAuth2Adapter):
__grant_type = 'client_credentials'

@staticmethod
def get_expected_auth_info_fields() -> List[str]:
return [
'client_id',
'client_assertion_file', # normally at /var/run/secrets/kubernetes.io/serviceaccount/token
'grant_type',
'resource_url',
'token_endpoint',
]

def exchange_tokens(self, trace_context: Span) -> Dict[str, Any]:
auth_info = self._auth_info
resource_urls = self._prepare_resource_urls_for_request(auth_info.resource_url)
with open(auth_info.client_assertion_file, 'r') as f:
client_assertion = f.read()
auth_params = dict(
client_id=auth_info.client_id,
grant_type=self.__grant_type,
resource=resource_urls,
client_assertion_type='urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
client_assertion=client_assertion,
)

if auth_info.scope:
auth_params['scope'] = auth_info.scope

with trace_context.new_span(metadata={'oauth': 'client-credentials', 'init_url': auth_info.token_endpoint}) \
as sub_span:
span_headers = sub_span.create_http_headers()
response = requests.post(auth_info.token_endpoint, data=auth_params, headers=span_headers)

if not response.ok:
raise AuthException(f'Client assertion authentication for {auth_info.client_id} failed with '
f'HTTP {response.status_code}:\n\n{response.text}\n',
resource_urls)

return response.json()
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from dnastack.http.client_factory import HttpClientFactory


class ClientCredentialAdapter(OAuth2Adapter):
class ClientCredentialsClientSecretAdapter(OAuth2Adapter):
__grant_type = 'client_credentials'

@staticmethod
Expand Down Expand Up @@ -55,8 +55,8 @@ def exchange_tokens(self, trace_context: Span) -> Dict[str, Any]:

if not response.ok:
sub_logger.debug(f'exchange_token: Token exchange fails.')
raise AuthException(f'Failed to perform client-credential authentication for '
f'{auth_info.client_id} as the server responds with HTTP {response.status_code}:'
raise AuthException(f'Client secret authentication for {auth_info.client_id} failed with '
f'HTTP {response.status_code}:'
f'\n\n{response.text}\n',
resource_urls)

Expand Down
7 changes: 5 additions & 2 deletions dnastack/http/authenticators/oauth2_adapter/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
from imagination.decorator import service

from dnastack.http.authenticators.oauth2_adapter.abstract import OAuth2Adapter
from dnastack.http.authenticators.oauth2_adapter.client_credential import ClientCredentialAdapter
from dnastack.http.authenticators.oauth2_adapter.client_credentials_client_assertion import \
ClientCredentialsClientAssertionAdapter
from dnastack.http.authenticators.oauth2_adapter.client_credentials_client_secret import ClientCredentialsClientSecretAdapter
from dnastack.http.authenticators.oauth2_adapter.device_code_flow import DeviceCodeFlowAdapter
from dnastack.http.authenticators.oauth2_adapter.models import OAuth2Authentication

Expand All @@ -12,8 +14,9 @@
class OAuth2AdapterFactory:
# NOTE: It was ordered this way to accommodate the general intended authentication flow.
__supported_auth_adapter_classes = [
ClientCredentialsClientAssertionAdapter,
DeviceCodeFlowAdapter,
ClientCredentialAdapter,
ClientCredentialsClientSecretAdapter,
]

def get_from(self, auth_info: OAuth2Authentication) -> Optional[OAuth2Adapter]:
Expand Down
1 change: 1 addition & 0 deletions dnastack/http/authenticators/oauth2_adapter/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class OAuth2Authentication(BaseModel, HashableModel):
authorization_endpoint: Optional[str]
client_id: Optional[str]
client_secret: Optional[str]
client_assertion_file: Optional[str]
device_code_endpoint: Optional[str]
grant_type: str
personal_access_endpoint: Optional[str]
Expand Down
2 changes: 1 addition & 1 deletion dnastack/http/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def __init__(self,
self.__events.set_passthrough(authenticator.events)

if not self.__enable_auth:
self.__logger.info('Authentication has been disable for this session.')
self.__logger.info('Authentication has been disabled for this session.')

@property
def events(self) -> EventSource:
Expand Down