Skip to content

Commit e00d40c

Browse files
authored
Merge pull request #4 from crowdsecurity/$main-c3fb4ee
Update python SDK version: 1.16.0
2 parents c3fb4ee + 1ea9746 commit e00d40c

19 files changed

+3344
-1
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
## 📦 1.16.0
2+
3+
- first public SDK release
4+
5+

README.md

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,111 @@
1-
# console-api-sdk-python
1+
# crowdsec_service_api - 1.16.0
2+
3+
4+
## Installation
5+
6+
```bash
7+
pip install crowdsec_service_api
8+
```
9+
10+
## License
11+
12+
13+
14+
## API Endpoint services
15+
16+
[Blocklists](./doc/Blocklists.md)
17+
18+
[Integrations](./doc/Integrations.md)
19+
20+
[Info](./doc/Info.md)
21+
22+
## API Endpoint models
23+
24+
[ApiKeyCredentials](./doc/Models.md#apikeycredentials)
25+
26+
[BasicAuthCredentials](./doc/Models.md#basicauthcredentials)
27+
28+
[BlocklistAddIPsRequest](./doc/Models.md#blocklistaddipsrequest)
29+
30+
[BlocklistContentStats](./doc/Models.md#blocklistcontentstats)
31+
32+
[BlocklistCreateRequest](./doc/Models.md#blocklistcreaterequest)
33+
34+
[BlocklistCreateResponse](./doc/Models.md#blocklistcreateresponse)
35+
36+
[BlocklistDeleteIPsRequest](./doc/Models.md#blocklistdeleteipsrequest)
37+
38+
[BlocklistGetResponse](./doc/Models.md#blocklistgetresponse)
39+
40+
[BlocklistIncludeFilters](./doc/Models.md#blocklistincludefilters)
41+
42+
[BlocklistResponse](./doc/Models.md#blocklistresponse)
43+
44+
[BlocklistShareRequest](./doc/Models.md#blocklistsharerequest)
45+
46+
[BlocklistSources](./doc/Models.md#blocklistsources)
47+
48+
[BlocklistStats](./doc/Models.md#blockliststats)
49+
50+
[BlocklistSubscriberEntity](./doc/Models.md#blocklistsubscriberentity)
51+
52+
[BlocklistSubscribersResponse](./doc/Models.md#blocklistsubscribersresponse)
53+
54+
[BlocklistSubscription](./doc/Models.md#blocklistsubscription)
55+
56+
[BlocklistSubscriptionRequest](./doc/Models.md#blocklistsubscriptionrequest)
57+
58+
[BlocklistSubscriptionResponse](./doc/Models.md#blocklistsubscriptionresponse)
59+
60+
[BlocklistUpdateRequest](./doc/Models.md#blocklistupdaterequest)
61+
62+
[BlocklistUsageStats](./doc/Models.md#blocklistusagestats)
63+
64+
[Body_uploadBlocklistContent](./doc/Models.md#body_uploadblocklistcontent)
65+
66+
[CtiAs](./doc/Models.md#ctias)
67+
68+
[CtiBehavior](./doc/Models.md#ctibehavior)
69+
70+
[CtiCategory](./doc/Models.md#cticategory)
71+
72+
[CtiCountry](./doc/Models.md#cticountry)
73+
74+
[CtiIp](./doc/Models.md#ctiip)
75+
76+
[CtiScenario](./doc/Models.md#ctiscenario)
77+
78+
[EntityType](./doc/Models.md#entitytype)
79+
80+
[HTTPValidationError](./doc/Models.md#httpvalidationerror)
81+
82+
[IntegrationCreateRequest](./doc/Models.md#integrationcreaterequest)
83+
84+
[IntegrationCreateResponse](./doc/Models.md#integrationcreateresponse)
85+
86+
[IntegrationGetResponse](./doc/Models.md#integrationgetresponse)
87+
88+
[IntegrationType](./doc/Models.md#integrationtype)
89+
90+
[IntegrationUpdateRequest](./doc/Models.md#integrationupdaterequest)
91+
92+
[IntegrationUpdateResponse](./doc/Models.md#integrationupdateresponse)
93+
94+
[Links](./doc/Models.md#links)
95+
96+
[OutputFormat](./doc/Models.md#outputformat)
97+
98+
[Page_BlocklistResponse_](./doc/Models.md#page_blocklistresponse_)
99+
100+
[Page_IntegrationGetResponse_](./doc/Models.md#page_integrationgetresponse_)
101+
102+
[Permission](./doc/Models.md#permission)
103+
104+
[PricingTiers](./doc/Models.md#pricingtiers)
105+
106+
[Share](./doc/Models.md#share)
107+
108+
[Stats](./doc/Models.md#stats)
109+
110+
[ValidationError](./doc/Models.md#validationerror)
111+
```

crowdsec_service_api/__init__.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from enum import Enum
2+
from .base_model import Page
3+
from .models import *
4+
from .services.blocklists import Blocklists
5+
from .services.integrations import Integrations
6+
from .services.info import Info
7+
from .http_client import ApiKeyAuth
8+
9+
class Server(Enum):
10+
production_server = 'https://admin.api.crowdsec.net/v1/'
11+
12+
__all__ = [
13+
'Blocklists',
14+
'Integrations',
15+
'Info',
16+
'ApiKeyCredentials',
17+
'BasicAuthCredentials',
18+
'BlocklistAddIPsRequest',
19+
'BlocklistContentStats',
20+
'BlocklistCreateRequest',
21+
'BlocklistCreateResponse',
22+
'BlocklistDeleteIPsRequest',
23+
'BlocklistGetResponse',
24+
'BlocklistIncludeFilters',
25+
'BlocklistResponse',
26+
'BlocklistShareRequest',
27+
'BlocklistSources',
28+
'BlocklistStats',
29+
'BlocklistSubscriberEntity',
30+
'BlocklistSubscribersResponse',
31+
'BlocklistSubscription',
32+
'BlocklistSubscriptionRequest',
33+
'BlocklistSubscriptionResponse',
34+
'BlocklistUpdateRequest',
35+
'BlocklistUsageStats',
36+
'Body_uploadBlocklistContent',
37+
'CtiAs',
38+
'CtiBehavior',
39+
'CtiCategory',
40+
'CtiCountry',
41+
'CtiIp',
42+
'CtiScenario',
43+
'EntityType',
44+
'HTTPValidationError',
45+
'IntegrationCreateRequest',
46+
'IntegrationCreateResponse',
47+
'IntegrationGetResponse',
48+
'IntegrationType',
49+
'IntegrationUpdateRequest',
50+
'IntegrationUpdateResponse',
51+
'Links',
52+
'OutputFormat',
53+
'Page_BlocklistResponse_',
54+
'Page_IntegrationGetResponse_',
55+
'Permission',
56+
'PricingTiers',
57+
'Share',
58+
'Stats',
59+
'ValidationError',
60+
'ApiKeyAuth',
61+
'Server',
62+
'Page'
63+
]
3.11 KB
Binary file not shown.
7.34 KB
Binary file not shown.

crowdsec_service_api/base_model.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from urllib.parse import urlparse
2+
from pydantic import BaseModel, ConfigDict
3+
from typing import Generic, Sequence, Optional, TypeVar
4+
from httpx import Auth
5+
from .http_client import HttpClient
6+
7+
8+
class BaseModelSdk(BaseModel):
9+
model_config = ConfigDict(
10+
extra="ignore",
11+
)
12+
13+
14+
T = TypeVar("T")
15+
16+
17+
class Page(BaseModelSdk, Generic[T]):
18+
items: Sequence[T]
19+
total: Optional[int]
20+
page: Optional[int]
21+
size: Optional[int]
22+
pages: Optional[int] = None
23+
links: Optional[dict] = None
24+
25+
def next(self, client: "Service") -> "Page[T]":
26+
return client.next_page(self)
27+
28+
29+
class Service:
30+
def __init__(self, base_url: str, auth: Auth) -> None:
31+
self.http_client = HttpClient(base_url=base_url, auth=auth)
32+
33+
def next_page(self, page: Page[T]) -> Page[T]:
34+
if not page.links:
35+
raise ValueError(
36+
"No links found in the response, this is not a paginated response."
37+
)
38+
if page.links.get("next"):
39+
# links are relative to host not to full base url. We need to pass a full formatted url here
40+
parsed_url = urlparse(self.http_client.base_url)
41+
response = self.http_client.get(
42+
f"{parsed_url.scheme}://{parsed_url.netloc}{page.links['next']}"
43+
)
44+
return Page[T](**response.json())
45+
return None
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
from urllib.parse import quote, urlparse
2+
3+
from botocore.auth import SigV4Auth
4+
from botocore.awsrequest import AWSRequest
5+
import botocore.session
6+
7+
import httpx
8+
9+
10+
class AWSSignV4Auth(httpx.Auth):
11+
def __init__(self, aws_region="eu-west-1") -> None:
12+
self.aws_region = aws_region
13+
14+
def auth_flow(self, request):
15+
request = self.sign_request(request)
16+
yield request
17+
18+
def sign_request(self, request: httpx.Request) -> httpx.Request:
19+
"""Signs an httpx request with AWS Signature Version 4."""
20+
21+
session = botocore.session.get_session()
22+
credentials = session.get_credentials()
23+
aws_request = AWSRequest(
24+
method=request.method.upper(), url=str(request.url), data=request.content
25+
)
26+
region = self.aws_region
27+
service = "execute-api"
28+
29+
# Sign the request
30+
SigV4Auth(credentials, service, region).add_auth(aws_request)
31+
32+
# Update the httpx request headers with the signed headers
33+
request.headers.update(dict(aws_request.headers))
34+
35+
return request
36+
37+
38+
class ApiKeyAuth(httpx.Auth):
39+
def __init__(self, api_key: str) -> None:
40+
self.api_key = api_key
41+
42+
def auth_flow(self, request):
43+
request.headers["x-api-key"] = self.api_key
44+
yield request
45+
46+
47+
class HttpClient:
48+
def __init__(self, base_url: str, auth: httpx.Auth, aws_region="eu-west-1") -> None:
49+
self.aws_region = aws_region
50+
self.base_url = base_url
51+
self.auth = auth
52+
self.client = httpx.Client()
53+
self.timeout = 30
54+
55+
def _replace_path_params(self, url: str, path_params: dict):
56+
for param, value in path_params.items():
57+
if not value:
58+
raise ValueError(
59+
f"Parameter {param} is required, cannot be empty or blank."
60+
)
61+
url = url.replace(f"{{{param}}}", quote(str(value)))
62+
return url
63+
64+
def _normalize_url(self, url: str):
65+
parsed_url = urlparse(url)
66+
if not parsed_url.netloc:
67+
return f"{self.base_url}{url}"
68+
return url
69+
70+
def get(
71+
self,
72+
url: str,
73+
path_params: dict = {},
74+
params: dict = {},
75+
headers: dict = {},
76+
):
77+
url = self._replace_path_params(
78+
url=self._normalize_url(url), path_params=path_params
79+
)
80+
response = self.client.get(
81+
url=url,
82+
params=params,
83+
headers=headers,
84+
auth=self.auth,
85+
timeout=self.timeout,
86+
)
87+
response.raise_for_status()
88+
return response
89+
90+
def post(
91+
self, url: str, path_params: dict, params: dict, headers: dict, json: dict
92+
):
93+
url = self._replace_path_params(
94+
url=self._normalize_url(url), path_params=path_params
95+
)
96+
response = self.client.post(
97+
url=url,
98+
params=params,
99+
headers=headers,
100+
json=json,
101+
auth=self.auth,
102+
timeout=self.timeout,
103+
)
104+
response.raise_for_status()
105+
return response
106+
107+
def put(self, url: str, path_params: dict, params: dict, headers: dict, json: dict):
108+
url = self._replace_path_params(
109+
url=self._normalize_url(url), path_params=path_params
110+
)
111+
response = self.client.put(
112+
url=url,
113+
params=params,
114+
headers=headers,
115+
json=json,
116+
auth=self.auth,
117+
timeout=self.timeout,
118+
)
119+
response.raise_for_status()
120+
return response
121+
122+
def patch(
123+
self, url: str, path_params: dict, params: dict, headers: dict, json: dict
124+
):
125+
url = self._replace_path_params(
126+
url=self._normalize_url(url), path_params=path_params
127+
)
128+
response = self.client.patch(
129+
url=url,
130+
params=params,
131+
headers=headers,
132+
json=json,
133+
auth=self.auth,
134+
timeout=self.timeout,
135+
)
136+
response.raise_for_status()
137+
return response
138+
139+
def delete(self, url: str, path_params: dict, params: dict, headers: dict):
140+
url = self._replace_path_params(
141+
url=self._normalize_url(url), path_params=path_params
142+
)
143+
response = self.client.delete(
144+
url=url,
145+
params=params,
146+
headers=headers,
147+
auth=self.auth,
148+
timeout=self.timeout,
149+
)
150+
response.raise_for_status()
151+
return response

0 commit comments

Comments
 (0)