crowdsec-tracker-api 1.92.0__py3-none-any.whl

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.
@@ -0,0 +1,59 @@
1
+ from enum import Enum
2
+ from .models import *
3
+ from .base_model import Page
4
+ from .services.integrations import Integrations
5
+ from .services.cves import Cves
6
+ from .http_client import ApiKeyAuth
7
+
8
+ class Server(Enum):
9
+ production_server = 'https://admin.api.crowdsec.net/v1'
10
+
11
+ __all__ = [
12
+ 'Integrations',
13
+ 'Cves',
14
+ 'ApiKeyCredentials',
15
+ 'BasicAuthCredentials',
16
+ 'BlocklistSubscription',
17
+ 'CVESubscription',
18
+ 'HTTPValidationError',
19
+ 'IntegrationCreateRequest',
20
+ 'IntegrationCreateResponse',
21
+ 'IntegrationGetResponse',
22
+ 'IntegrationGetResponsePage',
23
+ 'IntegrationType',
24
+ 'IntegrationUpdateRequest',
25
+ 'IntegrationUpdateResponse',
26
+ 'Links',
27
+ 'OutputFormat',
28
+ 'Stats',
29
+ 'ValidationError',
30
+ 'AffectedComponent',
31
+ 'AllowlistSubscription',
32
+ 'AttackDetail',
33
+ 'Behavior',
34
+ 'CVEEvent',
35
+ 'CVEResponseBase',
36
+ 'CVEsubscription',
37
+ 'CWE',
38
+ 'Classification',
39
+ 'Classifications',
40
+ 'EntityType',
41
+ 'GetCVEIPsResponsePage',
42
+ 'GetCVEResponse',
43
+ 'GetCVESubscribedIntegrationsResponsePage',
44
+ 'GetCVEsResponsePage',
45
+ 'GetCVEsSortBy',
46
+ 'GetCVEsSortOrder',
47
+ 'History',
48
+ 'IPItem',
49
+ 'IntegrationResponse',
50
+ 'Location',
51
+ 'MitreTechnique',
52
+ 'Reference',
53
+ 'ScoreBreakdown',
54
+ 'Scores',
55
+ 'SubscribeCVEIntegrationRequest',
56
+ 'ApiKeyAuth',
57
+ 'Server',
58
+ 'Page'
59
+ ]
@@ -0,0 +1,58 @@
1
+ from urllib.parse import urlparse
2
+ from pydantic import BaseModel, ConfigDict, PrivateAttr, RootModel
3
+ from typing import Generic, Sequence, Optional, TypeVar, Any
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
+ _client: Optional["Service"] = PrivateAttr(default=None)
13
+
14
+ def __init__(self, /, _client: "Service" = None, **data):
15
+ super().__init__(**data)
16
+ self._client = _client
17
+
18
+ def next(self, client: "Service" = None) -> Optional["BaseModelSdk"]:
19
+ return (client if client is not None else self._client).next_page(self)
20
+
21
+
22
+ class RootModelSdk(RootModel):
23
+ def __getattr__(self, item: str) -> Any:
24
+ return getattr(self.root, item)
25
+
26
+
27
+ T = TypeVar("T")
28
+
29
+
30
+ class Page(BaseModelSdk, Generic[T]):
31
+ items: Sequence[T]
32
+ total: Optional[int]
33
+ page: Optional[int]
34
+ size: Optional[int]
35
+ pages: Optional[int] = None
36
+ links: Optional[dict] = None
37
+
38
+
39
+ class Service:
40
+ def __init__(self, base_url: str, auth: Auth) -> None:
41
+ self.http_client = HttpClient(base_url=base_url, auth=auth)
42
+
43
+ def next_page(self, page: BaseModelSdk) -> Optional[BaseModelSdk]:
44
+ if not hasattr(page, "links") or not page.links:
45
+ raise ValueError(
46
+ "No links found in the response, this is not a paginated response."
47
+ )
48
+ if page.links.next:
49
+ # links are relative to host not to full base url. We need to pass a full formatted url here
50
+ parsed_url = urlparse(self.http_client.base_url)
51
+ response = self.http_client.get(
52
+ f"{parsed_url.scheme}://{parsed_url.netloc}{page.links.next}",
53
+ path_params=None,
54
+ params=None,
55
+ headers=None,
56
+ )
57
+ return page.__class__(_client=self, **response.json())
58
+ return None
@@ -0,0 +1,153 @@
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(headers={"Accept-Encoding": "gzip"})
53
+ self.timeout = 30
54
+
55
+ def _replace_path_params(self, url: str, path_params: dict):
56
+ if path_params:
57
+ for param, value in path_params.items():
58
+ if not value:
59
+ raise ValueError(
60
+ f"Parameter {param} is required, cannot be empty or blank."
61
+ )
62
+ url = url.replace(f"{{{param}}}", quote(str(value)))
63
+ return url
64
+
65
+ def _normalize_url(self, url: str):
66
+ self.base_url = self.base_url.rstrip("/")
67
+ parsed_url = urlparse(url)
68
+ if not parsed_url.netloc:
69
+ return f"{self.base_url}{url}"
70
+ return url
71
+
72
+ def get(
73
+ self,
74
+ url: str,
75
+ path_params: dict = None,
76
+ params: dict = None,
77
+ headers: dict = None,
78
+ ):
79
+ url = self._replace_path_params(
80
+ url=self._normalize_url(url), path_params=path_params
81
+ )
82
+ response = self.client.get(
83
+ url=url,
84
+ params=params,
85
+ headers=headers,
86
+ auth=self.auth,
87
+ timeout=self.timeout,
88
+ )
89
+ response.raise_for_status()
90
+ return response
91
+
92
+ def post(
93
+ self, url: str, path_params: dict, params: dict, headers: dict, json: dict
94
+ ):
95
+ url = self._replace_path_params(
96
+ url=self._normalize_url(url), path_params=path_params
97
+ )
98
+ response = self.client.post(
99
+ url=url,
100
+ params=params,
101
+ headers=headers,
102
+ json=json,
103
+ auth=self.auth,
104
+ timeout=self.timeout,
105
+ )
106
+ response.raise_for_status()
107
+ return response
108
+
109
+ def put(self, url: str, path_params: dict, params: dict, headers: dict, json: dict):
110
+ url = self._replace_path_params(
111
+ url=self._normalize_url(url), path_params=path_params
112
+ )
113
+ response = self.client.put(
114
+ url=url,
115
+ params=params,
116
+ headers=headers,
117
+ json=json,
118
+ auth=self.auth,
119
+ timeout=self.timeout,
120
+ )
121
+ response.raise_for_status()
122
+ return response
123
+
124
+ def patch(
125
+ self, url: str, path_params: dict, params: dict, headers: dict, json: dict
126
+ ):
127
+ url = self._replace_path_params(
128
+ url=self._normalize_url(url), path_params=path_params
129
+ )
130
+ response = self.client.patch(
131
+ url=url,
132
+ params=params,
133
+ headers=headers,
134
+ json=json,
135
+ auth=self.auth,
136
+ timeout=self.timeout,
137
+ )
138
+ response.raise_for_status()
139
+ return response
140
+
141
+ def delete(self, url: str, path_params: dict, params: dict, headers: dict):
142
+ url = self._replace_path_params(
143
+ url=self._normalize_url(url), path_params=path_params
144
+ )
145
+ response = self.client.delete(
146
+ url=url,
147
+ params=params,
148
+ headers=headers,
149
+ auth=self.auth,
150
+ timeout=self.timeout,
151
+ )
152
+ response.raise_for_status()
153
+ return response