esd-services-api-client 1.1.3__tar.gz → 1.1.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/PKG-INFO +1 -1
  2. esd_services_api_client-1.1.5/esd_services_api_client/_version.py +1 -0
  3. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/esd_services_api_client/beast/v3/_connector.py +10 -0
  4. esd_services_api_client-1.1.5/esd_services_api_client/boxer/README.md +103 -0
  5. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/esd_services_api_client/boxer/_connector.py +112 -93
  6. esd_services_api_client-1.1.5/esd_services_api_client/boxer/_models.py +75 -0
  7. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/pyproject.toml +1 -1
  8. esd_services_api_client-1.1.3/esd_services_api_client/_version.py +0 -1
  9. esd_services_api_client-1.1.3/esd_services_api_client/boxer/README.md +0 -91
  10. esd_services_api_client-1.1.3/esd_services_api_client/boxer/_helpers.py +0 -46
  11. esd_services_api_client-1.1.3/esd_services_api_client/boxer/_models.py +0 -131
  12. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/LICENSE +0 -0
  13. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/README.md +0 -0
  14. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/esd_services_api_client/__init__.py +0 -0
  15. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/esd_services_api_client/beast/__init__.py +0 -0
  16. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/esd_services_api_client/beast/v2/__init__.py +0 -0
  17. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/esd_services_api_client/beast/v2/_connector.py +0 -0
  18. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/esd_services_api_client/beast/v2/_models.py +0 -0
  19. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/esd_services_api_client/beast/v3/__init__.py +0 -0
  20. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/esd_services_api_client/beast/v3/_models.py +0 -0
  21. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/esd_services_api_client/boxer/__init__.py +0 -0
  22. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/esd_services_api_client/boxer/_auth.py +0 -0
  23. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/esd_services_api_client/boxer/_base.py +0 -0
  24. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/esd_services_api_client/common/__init__.py +0 -0
  25. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/esd_services_api_client/crystal/__init__.py +0 -0
  26. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/esd_services_api_client/crystal/_api_versions.py +0 -0
  27. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/esd_services_api_client/crystal/_connector.py +0 -0
  28. {esd_services_api_client-1.1.3 → esd_services_api_client-1.1.5}/esd_services_api_client/crystal/_models.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: esd-services-api-client
3
- Version: 1.1.3
3
+ Version: 1.1.5
4
4
  Summary: Python clients for ESD services
5
5
  Home-page: https://github.com/SneaksAndData/esd-services-api-client
6
6
  License: Apache 2.0
@@ -0,0 +1 @@
1
+ __version__ = '1.1.5'
@@ -254,6 +254,16 @@ class BeastConnector:
254
254
 
255
255
  return response.json()["lifeCycleStage"]
256
256
 
257
+ def get_request_runtime_info(self, request_id: str) -> Optional[dict]:
258
+ """
259
+ Returns the runtime information for the given request. Returns None in case error retry fails to resolve within given timeout.
260
+ :param request_id: A request identifier to read runtime info for.
261
+ """
262
+ response = self.http.get(f"{self.base_url}/job/requests/{request_id}")
263
+ response.raise_for_status()
264
+
265
+ return response.json()
266
+
257
267
  def start_job(self, job_params: BeastJobParams, job_name: str) -> Optional[str]:
258
268
  """
259
269
  Starts a job through Beast.
@@ -0,0 +1,103 @@
1
+ # Boxer API Connector
2
+
3
+ Conenctor for working with [Boxer](https://github.com/SneaksAndData/boxer) AuthZ/AuthN API.
4
+
5
+ ## Usage
6
+
7
+ Two environment variables must be set before you can use this connector:
8
+
9
+ ```shell
10
+ export BOXER_CONSUMER_ID="my_app_consumer"
11
+ export BOXER_PRIVATE_KEY="MIIEpAIBAA..."
12
+ ```
13
+
14
+ ### Retrieving Claims:
15
+
16
+ ```python
17
+ from esd_services_api_client.boxer import select_authentication, BoxerClaimConnector
18
+ auth = select_authentication("azuread", "test")
19
+ conn = BoxerClaimConnector(base_url="https://boxer-claim.test.sneaksanddata.com", auth=auth)
20
+ resp = conn.get_claims("email@ecco.com", "azuread")
21
+ for claim in resp:
22
+ print(claim.to_dict())
23
+ ```
24
+ Output:
25
+ ```bash
26
+ {'claim_name':'test1.test.sneaksanddata.com/.*', 'claim_value':'.*'}
27
+ {'claim_name':'test2.test.sneaksanddata.com/.*', 'claim_value':'.*'}
28
+ ```
29
+
30
+ ### Insert claims:
31
+ ```python
32
+ from esd_services_api_client.boxer import select_authentication, BoxerClaimConnector, Claim
33
+ auth = select_authentication("azuread", "test")
34
+ conn = BoxerClaimConnector(base_url="https://boxer-claim.test.sneaksanddata.com", auth=auth)
35
+ claims = [Claim("some-test-1.test.sneaksanddata.com", ".*"), Claim("some-test-2.test.sneaksanddata.com", ".*")]
36
+ resp = conn.add_claim("email@ecco.com", "azuread", claims)
37
+ print(resp)
38
+ ```
39
+ Output:
40
+ ```bash
41
+ ClaimResponse(identity_provider='azuread', user_id='email@ecco.com', claims=[{'some-test-1.test.sneaksanddata.com': '.*'}, {'some-test-2.test.sneaksanddata.com': '.*'}], billing_id= None}
42
+ ```
43
+
44
+ ### Remove claims:
45
+ ```python
46
+ from esd_services_api_client.boxer import select_authentication, BoxerClaimConnector, Claim
47
+ auth = select_authentication("azuread", "test")
48
+ conn = BoxerClaimConnector(base_url="https://boxer-claim.test.sneaksanddata.com", auth=auth)
49
+ claims = [Claim("some-test-1.test.sneaksanddata.com", ".*"), Claim("some-test-2.test.sneaksanddata.com", ".*")]
50
+ resp = conn.remove_claim("email@ecco.com", "azuread", claims)
51
+ print(resp)
52
+ ```
53
+ Output:
54
+ ```bash
55
+ ClaimResponse(identity_provider='azuread', user_id='email@ecco.com', claims=[], billing_id= None}
56
+ ```
57
+
58
+ ### Add a user:
59
+ ```python
60
+ from esd_services_api_client.boxer import select_authentication, BoxerClaimConnector, Claim
61
+ auth = select_authentication("azuread", "test")
62
+ conn = BoxerClaimConnector(base_url="https://boxer-claim.test.sneaksanddata.com", auth=auth)
63
+ resp = conn.add_user("test@ecco.com", "azuread")
64
+ print(resp)
65
+ ```
66
+ Output:
67
+ ```bash
68
+ ClaimResponse(identity_provider='azuread', user_id='test@ecco.com', claims=[], billing_id=None)
69
+ ```
70
+
71
+ ### Remove a user:
72
+ ```python
73
+ from esd_services_api_client.boxer import select_authentication, BoxerClaimConnector, Claim
74
+ auth = select_authentication("azuread", "test")
75
+ conn = BoxerClaimConnector(base_url="https://boxer-claim.test.sneaksanddata.com", auth=auth)
76
+ resp = conn.remove_user("test@ecco.com", "azuread")
77
+ print(resp.status_code)
78
+ ```
79
+ Output:
80
+ ```bash
81
+ 200
82
+ ```
83
+
84
+ ### Using as an authentication provider for other connectors
85
+ ```python
86
+ from esd_services_api_client.boxer import BoxerConnector, RefreshableExternalTokenAuth, BoxerTokenAuth
87
+ from esd_services_api_client.crystal import CrystalConnector
88
+
89
+ auth_method = "example"
90
+
91
+ def get_external_token() -> str:
92
+ return "example_token"
93
+
94
+ # Configure authentication with boxer
95
+ external_auth = RefreshableExternalTokenAuth(lambda: get_external_token(), auth_method)
96
+ boxer_connector = BoxerConnector(base_url="https://example.com", auth=external_auth)
97
+
98
+ # Inject boxer auth to Crystal connector
99
+ connector = CrystalConnector(base_url="https://example.com", auth=BoxerTokenAuth(boxer_connector))
100
+
101
+ # Use Crystal connector with boxer auth
102
+ connector.await_runs("algorithm", ["id"])
103
+ ```
@@ -17,12 +17,12 @@
17
17
  #
18
18
 
19
19
  import os
20
- from typing import Iterator, Optional
20
+ from functools import reduce
21
+ from typing import Optional, Iterator, final
21
22
 
22
- import jwt
23
23
  from adapta.security.clients import AzureClient
24
24
  from adapta.utils import session_with_retries
25
- from requests import Session
25
+ from requests import Session, Response
26
26
 
27
27
  from esd_services_api_client.boxer._base import BoxerTokenProvider
28
28
  from esd_services_api_client.boxer._auth import (
@@ -32,11 +32,116 @@ from esd_services_api_client.boxer._auth import (
32
32
  ExternalAuthBase,
33
33
  RefreshableExternalTokenAuth,
34
34
  )
35
- from esd_services_api_client.boxer._helpers import (
36
- _iterate_user_claims_response,
37
- _iterate_boxer_claims_response,
35
+ from esd_services_api_client.boxer._models import (
36
+ BoxerToken,
37
+ Claim,
38
+ ClaimPayload,
39
+ ClaimResponse,
38
40
  )
39
- from esd_services_api_client.boxer._models import BoxerClaim, UserClaim, BoxerToken
41
+
42
+
43
+ @final
44
+ class BoxerClaimConnector:
45
+ """
46
+ Boxer Claims API connector
47
+ """
48
+
49
+ def __init__(self, *, base_url: str, auth: Optional[BoxerTokenAuth] = None):
50
+ """Creates Boxer Claims connector, capable of managing claims
51
+ :param base_url: Base URL for Boxer Claims endpoint
52
+ :param auth: Boxer-based authentication
53
+ """
54
+ self._base_url = base_url
55
+ self._http = session_with_retries()
56
+ if auth and isinstance(auth, BoxerTokenAuth):
57
+ self._http.hooks["response"].append(auth.get_refresh_hook(self._http))
58
+ self._http.auth = auth
59
+
60
+ def get_claims(self, user_id: str, provider: str) -> Optional[Iterator[Claim]]:
61
+ """
62
+ Returns the claims assigned to the specified user_id and provider
63
+ """
64
+ response = self._http.get(f"{self._base_url}/claim/{provider}/{user_id}")
65
+ if response.status_code == 404:
66
+ return None
67
+ response.raise_for_status()
68
+ return self._iterate_user_claims_response(response)
69
+
70
+ def add_user(self, user_id: str, provider: str) -> ClaimResponse:
71
+ """
72
+ Adds a new user_id, provider pair
73
+ """
74
+ response = self._http.post(f"{self._base_url}/claim/{provider}/{user_id}")
75
+ response.raise_for_status()
76
+ return ClaimResponse.from_dict(response.json())
77
+
78
+ def remove_user(self, user_id: str, provider: str) -> Response:
79
+ """
80
+ Removes the specified user_id, provider pair and assigned claims
81
+ """
82
+ response = self._http.delete(f"{self._base_url}/claim/{provider}/{user_id}")
83
+ response.raise_for_status()
84
+ return response
85
+
86
+ def add_claim(
87
+ self, user_id: str, provider: str, claims: list[Claim]
88
+ ) -> Optional[ClaimResponse]:
89
+ """
90
+ Adds a new claim to an existing user_id, provider pair
91
+ """
92
+ payload_json = self._prepare_claim_payload(user_id, provider, claims, "Insert")
93
+ response = self._http.patch(
94
+ f"{self._base_url}/claim/{provider}/{user_id}",
95
+ data=payload_json,
96
+ headers={"Content-Type": "application/json"},
97
+ )
98
+ response.raise_for_status()
99
+ return ClaimResponse.from_dict(response.json())
100
+
101
+ def remove_claim(
102
+ self, user_id: str, provider: str, claims: list[Claim]
103
+ ) -> Optional[ClaimResponse]:
104
+ """
105
+ Removes the specified claim
106
+ """
107
+ payload_json = self._prepare_claim_payload(user_id, provider, claims, "Delete")
108
+ response = self._http.patch(
109
+ f"{self._base_url}/claim/{provider}/{user_id}",
110
+ data=payload_json,
111
+ headers={"Content-Type": "application/json"},
112
+ )
113
+ return ClaimResponse.from_dict(response.json())
114
+
115
+ def _prepare_claim_payload(
116
+ self, user_id: str, provider: str, claims: list[Claim], operation: str
117
+ ) -> Optional[str]:
118
+ """
119
+ Prepare payload for Inserting/Deleting claims
120
+ """
121
+ if self.get_claims(user_id, provider) is not None:
122
+ payload = ClaimPayload(operation, {})
123
+ claim_payload = reduce(
124
+ lambda cp, claim: cp.add_claim(claim), claims, payload
125
+ )
126
+
127
+ return claim_payload.to_json()
128
+ return None
129
+
130
+ def _iterate_user_claims_response(
131
+ self, user_claim_response: Response
132
+ ) -> Optional[Iterator[Claim]]:
133
+ """Creates an iterator to iterate user claims from Json Response
134
+ :param user_claim_response: HTTP Response
135
+ """
136
+ response_json = user_claim_response.json()
137
+ if response_json and "claims" in response_json:
138
+ for claim in response_json["claims"]:
139
+ if isinstance(claim, dict) and len(claim) == 1:
140
+ for key, value in claim.items():
141
+ yield Claim.from_dict({"claim_name": key, "claim_value": value})
142
+ break
143
+ else:
144
+ raise ValueError("Expected response body of type application/json")
40
145
 
41
146
 
42
147
  class BoxerConnector(BoxerTokenProvider):
@@ -65,92 +170,6 @@ class BoxerConnector(BoxerTokenProvider):
65
170
  self.http.hooks["response"].append(auth.get_refresh_hook(self.http))
66
171
  self.retry_attempts = retry_attempts
67
172
 
68
- def push_user_claim(self, claim: BoxerClaim, user_id: str):
69
- """Adds/Overwrites a new Boxer Claim to a user
70
- :param claim: Boxer Claim
71
- :param user_id: User's UPN
72
- :return:
73
- """
74
- target_url = f"{self.base_url}/claims/user/{user_id}"
75
- claim_json = claim.to_dict()
76
- response = self.http.post(target_url, json=claim_json)
77
- response.raise_for_status()
78
- print(f"Successfully pushed user claim for user {user_id}")
79
-
80
- def push_group_claim(self, claim: BoxerClaim, group_name: str):
81
- """Adds/Overwrites a new Boxer Claim to a user
82
- :param claim: Boxer Claim
83
- :param group_name: Group Name
84
- :return:
85
- """
86
- target_url = f"{self.base_url}/claims/group/{group_name}"
87
- claim_json = claim.to_dict()
88
- response = self.http.post(target_url, json=claim_json)
89
- response.raise_for_status()
90
- print(f"Successfully pushed user claim for group {group_name}")
91
-
92
- def get_claims_by_type(self, claims_type: str) -> Iterator[UserClaim]:
93
- """Reads claims of specified type from Boxer.
94
- :param claims_type: claim type to filter claims by.
95
- :return: Iterator[UserClaim]
96
- """
97
- target_url = f"{self.base_url}/claims/type/{claims_type}"
98
- response = self.http.get(target_url)
99
- response.raise_for_status()
100
- return _iterate_user_claims_response(response)
101
-
102
- def get_claims_by_user(self, user_id: str) -> Iterator[BoxerClaim]:
103
- """Reads user claims from Boxer
104
- :param user_id: user upn to load claims for
105
- :return: Iterator[UserClaim]
106
- """
107
- empty_user_token = jwt.encode({"upn": user_id}, "_", algorithm="HS256")
108
- target_url = f"{self.base_url}/claims/user/{empty_user_token}"
109
- response = self.http.get(target_url)
110
- response.raise_for_status()
111
- return _iterate_boxer_claims_response(response)
112
-
113
- def get_claims_by_group(self, group_name: str) -> Iterator[BoxerClaim]:
114
- """Reads group claims from Boxer
115
- :param group_name: group name to load claims for
116
- :return: Iterator[UserClaim]
117
- """
118
- target_url = f"{self.base_url}/claims/group/{group_name}"
119
- response = self.http.get(target_url)
120
- response.raise_for_status()
121
- return _iterate_boxer_claims_response(response)
122
-
123
- def get_claims_for_token(self, jwt_token: str) -> Iterator[BoxerClaim]:
124
- """Reads user claims from Boxer based on jwt token
125
- :param jwt_token: jwt token with UPN set
126
- :return: Iterator[UserClaim]
127
- """
128
- target_url = f"{self.base_url}/claims/user/{jwt_token}"
129
- response = self.http.get(target_url)
130
- response.raise_for_status()
131
- return _iterate_boxer_claims_response(response)
132
-
133
- def create_consumer(self, consumer_id: str, overwrite: bool = False) -> str:
134
- """Adds/Overwrites a new Boxer Claim to a user
135
- :param consumer_id: Consumer ID of new consumer
136
- :param overwrite: Flag to overwrite if consumer with given consumer_id already exists
137
- :return: New consumer's private key (Base64 Encoded)
138
- """
139
- target_url = f"{self.base_url}/consumer/{consumer_id}?overwrite={overwrite}"
140
- response = self.http.post(target_url, json={})
141
- response.raise_for_status()
142
- return response.text
143
-
144
- def get_consumer_public_key(self, consumer_id: str) -> str:
145
- """Reads Consumer's public key
146
- :param consumer_id: Boxer Claim
147
- :return: public key (Base64 Encoded)
148
- """
149
- target_url = f"{self.base_url}/consumer/publicKey/{consumer_id}"
150
- response = self.http.get(target_url, json={})
151
- response.raise_for_status()
152
- return response.text
153
-
154
173
  def get_token(self) -> BoxerToken:
155
174
  """
156
175
  Authorize with external token and return BoxerToken
@@ -0,0 +1,75 @@
1
+ """
2
+ Models for Boxer
3
+ """
4
+ # Copyright (c) 2023. ECCO Sneaks & Data
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ from dataclasses import dataclass
20
+
21
+ from dataclasses_json import LetterCase, dataclass_json, DataClassJsonMixin
22
+
23
+
24
+ @dataclass_json
25
+ @dataclass
26
+ class Claim(DataClassJsonMixin):
27
+ """
28
+ Boxer Claim
29
+ """
30
+
31
+ claim_name: str
32
+ claim_value: str
33
+
34
+
35
+ @dataclass_json
36
+ @dataclass
37
+ class ClaimPayload(DataClassJsonMixin):
38
+ """
39
+ Boxer Claim Payload for Deleting/Inserting claims
40
+ """
41
+
42
+ operation: str
43
+ claims: dict
44
+
45
+ def add_claim(self, claim: Claim) -> "ClaimPayload":
46
+ """
47
+ Add a claim to the ClaimPayload
48
+ """
49
+ self.claims |= {claim.claim_name: claim.claim_value}
50
+ return self
51
+
52
+
53
+ @dataclass_json(letter_case=LetterCase.CAMEL)
54
+ @dataclass
55
+ class ClaimResponse(DataClassJsonMixin):
56
+ """
57
+ Boxer Claim request response
58
+ """
59
+
60
+ identity_provider: str
61
+ user_id: str
62
+ claims: list[dict]
63
+ billing_id: str
64
+
65
+
66
+ class BoxerToken:
67
+ """
68
+ Represents token created by BoxerConnector.get_token
69
+ """
70
+
71
+ def __init__(self, token: str):
72
+ self._token = token
73
+
74
+ def __str__(self):
75
+ return self._token
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "esd-services-api-client"
3
- version = "1.1.3"
3
+ version = "1.1.5"
4
4
  description = "Python clients for ESD services"
5
5
  authors = ["ECCO Sneaks & Data <esdsupport@ecco.com>"]
6
6
  maintainers = ['GZU <gzu@ecco.com>', 'JRB <ext-jrb@ecco.com>', 'VISA <visa@ecco.com>']
@@ -1 +0,0 @@
1
- __version__ = '1.1.3'
@@ -1,91 +0,0 @@
1
- # Boxer API Connector
2
-
3
- Conenctor for working with [Boxer](https://github.com/SneaksAndData/boxer) AuthZ/AuthN API.
4
-
5
- ## Usage
6
-
7
- Two environment variables must be set before you can use this connector:
8
-
9
- ```shell
10
- export BOXER_CONSUMER_ID="my_app_consumer"
11
- export BOXER_PRIVATE_KEY="MIIEpAIBAA..."
12
- ```
13
-
14
- ### Retrieving User Claims:
15
-
16
- ```python
17
- from esd_services_api_client.boxer import BoxerConnector
18
- conn = BoxerConnector(base_url="https://boxer.test.sneaksanddata.com")
19
- claims = conn.get_claims_by_user("user@domain.tld")
20
- for claim in claims:
21
- print(claim.to_dict())
22
- ```
23
-
24
- ### Retrieving Claims by Type:
25
-
26
- ```python
27
- from esd_services_api_client.boxer import BoxerConnector
28
- conn = BoxerConnector(base_url="https://boxer.test.sneaksanddata.com")
29
- claims = conn.get_claims_by_type("App1.AccessPolicy")
30
- ```
31
-
32
- ### Retrieving Group Claims:
33
-
34
- ```python
35
- from esd_services_api_client.boxer import BoxerConnector
36
- conn = BoxerConnector(base_url="https://boxer.test.sneaksanddata.com")
37
- claims = conn.get_claims_by_group("ad_group_name")
38
- ```
39
-
40
- ### Setting a user claim:
41
- ```python
42
- from esd_services_api_client.boxer import BoxerConnector
43
- from esd_services_api_client.boxer import BoxerClaim
44
- claim = BoxerClaim(issuer="App1", claim_type="App1.CanManageConsumers", claim_value="true")
45
- conn = BoxerConnector(base_url="https://boxer.test.sneaksanddata.com")
46
- conn.push_user_claim(claim, "app1admin")
47
- ```
48
-
49
- ### Setting a group claim:
50
- ```python
51
- from esd_services_api_client.boxer import BoxerConnector
52
- from esd_services_api_client.boxer import BoxerClaim
53
- claim = BoxerClaim(issuer="App1", claim_type="App1.CanManageConsumers", claim_value="true")
54
- conn = BoxerConnector(base_url="https://boxer.test.sneaksanddata.com")
55
- conn.push_group_claim(claim, "ad_group_name")
56
- ```
57
-
58
- ### Creating a new Auth Consumer (obtaining private key):
59
- ```python
60
- from esd_services_api_client.boxer import BoxerConnector
61
- conn = BoxerConnector(base_url="https://boxer.test.sneaksanddata.com")
62
- priv_key = conn.create_consumer("app_consumer")
63
- ```
64
-
65
- ### Getting Consumer's public key:
66
- ```python
67
- from esd_services_api_client.boxer import BoxerConnector
68
- conn = BoxerConnector(base_url="https://boxer.test.sneaksanddata.com")
69
- pub_key = conn.get_consumer_public_key("app_consumer")
70
- ```
71
-
72
- ### Using as an authentication provider for other connectors
73
- ```python
74
- from esd_services_api_client.boxer import BoxerConnector, RefreshableExternalTokenAuth, BoxerTokenAuth
75
- from esd_services_api_client.crystal import CrystalConnector
76
-
77
- auth_method = "example"
78
-
79
- def get_external_token() -> str:
80
- return "example_token"
81
-
82
- # Configure authentication with boxer
83
- external_auth = RefreshableExternalTokenAuth(lambda: get_external_token(), auth_method)
84
- boxer_connector = BoxerConnector(base_url="https://example.com", auth=external_auth)
85
-
86
- # Inject boxer auth to Crystal connector
87
- connector = CrystalConnector(base_url="https://example.com", auth=BoxerTokenAuth(boxer_connector))
88
-
89
- # Use Crystal connector with boxer auth
90
- connector.await_runs("algorithm", ["id"])
91
- ```
@@ -1,46 +0,0 @@
1
- """ Helper functions to parse responses
2
- """
3
- # Copyright (c) 2023. ECCO Sneaks & Data
4
- #
5
- # Licensed under the Apache License, Version 2.0 (the "License");
6
- # you may not use this file except in compliance with the License.
7
- # You may obtain a copy of the License at
8
- #
9
- # http://www.apache.org/licenses/LICENSE-2.0
10
- #
11
- # Unless required by applicable law or agreed to in writing, software
12
- # distributed under the License is distributed on an "AS IS" BASIS,
13
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- # See the License for the specific language governing permissions and
15
- # limitations under the License.
16
- #
17
-
18
- from requests import Response
19
-
20
- from esd_services_api_client.boxer._models import UserClaim, BoxerClaim
21
-
22
-
23
- def _iterate_user_claims_response(user_claim_response: Response):
24
- """Creates an iterator to iterate user claims from Json Response
25
- :param user_claim_response: HTTP Response containing json array of type UserClaim
26
- """
27
- response_json = user_claim_response.json()
28
-
29
- if response_json:
30
- for api_response_item in response_json:
31
- yield UserClaim.from_dict(api_response_item)
32
- else:
33
- raise ValueError("Expected response body of type application/json")
34
-
35
-
36
- def _iterate_boxer_claims_response(boxer_claim_response: Response):
37
- """Creates an iterator to iterate user claims from Json Response
38
- :param boxer_claim_response: HTTP Response containing json array of type BoxerClaim
39
- """
40
- response_json = boxer_claim_response.json()
41
-
42
- if response_json:
43
- for api_response_item in response_json:
44
- yield BoxerClaim.from_dict(api_response_item)
45
- else:
46
- raise ValueError("Expected response body of type application/json")
@@ -1,131 +0,0 @@
1
- """
2
- Models for Boxer
3
- """
4
- # Copyright (c) 2023. ECCO Sneaks & Data
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
- #
18
-
19
- from dataclasses import dataclass
20
- from typing import Dict
21
-
22
-
23
- @dataclass
24
- class BoxerClaim:
25
- """
26
- Boxer Claim
27
- """
28
-
29
- claim_type: str
30
- claim_value: str
31
- issuer: str
32
-
33
- def to_dict(self) -> Dict:
34
- """Convert to Dictionary
35
- :return: Dictionary
36
- """
37
- return {
38
- "claimType": self.claim_type,
39
- "claimValue": self.claim_value,
40
- "issuer": self.issuer,
41
- }
42
-
43
- @classmethod
44
- def from_dict(cls, json_data: Dict):
45
- """Initialize from Dictionary
46
- :param json_data: Dictionary
47
- :return:
48
- """
49
- return BoxerClaim(
50
- claim_type=json_data["claimType"],
51
- claim_value=json_data["claimValue"],
52
- issuer=json_data["issuer"],
53
- )
54
-
55
-
56
- @dataclass
57
- class UserClaim:
58
- """
59
- Boxer User Claim
60
- """
61
-
62
- user_id: str
63
- user_claim_id: str
64
- claim: BoxerClaim
65
-
66
- def to_dict(self) -> Dict:
67
- """Convert to Dictionary
68
- :return: Dictionary
69
- """
70
- return {
71
- "userId": self.user_id,
72
- "userClaimId": self.user_claim_id,
73
- "claim": self.claim.to_dict(),
74
- }
75
-
76
- @classmethod
77
- def from_dict(cls, json_data: Dict):
78
- """Initialize from Dictionary
79
- :param json_data: Dictionary
80
- :return:
81
- """
82
- return UserClaim(
83
- user_id=json_data["userId"],
84
- user_claim_id=json_data["userClaimId"],
85
- claim=BoxerClaim.from_dict(json_data["claim"]),
86
- )
87
-
88
-
89
- @dataclass
90
- class GroupClaim:
91
- """
92
- Boxer Group Claim
93
- """
94
-
95
- group_name: str
96
- group_claim_id: str
97
- claim: BoxerClaim
98
-
99
- def to_dict(self) -> Dict:
100
- """Convert to Dictionary
101
- :return: Dictionary
102
- """
103
- return {
104
- "groupName": self.group_name,
105
- "groupClaimId": self.group_claim_id,
106
- "claim": self.claim.to_dict(),
107
- }
108
-
109
- @classmethod
110
- def from_dict(cls, json_data: Dict):
111
- """Initialize from Dictionary
112
- :param json_data: Dictionary
113
- :return:
114
- """
115
- return GroupClaim(
116
- group_name=json_data["groupName"],
117
- group_claim_id=json_data["groupClaimId"],
118
- claim=BoxerClaim.from_dict(json_data["claim"]),
119
- )
120
-
121
-
122
- class BoxerToken:
123
- """
124
- Represents token created by BoxerConnector.get_token
125
- """
126
-
127
- def __init__(self, token: str):
128
- self._token = token
129
-
130
- def __str__(self):
131
- return self._token