Skip to content

Commit 0d059d0

Browse files
committed
IDEV-2272: Change default feeds auth behavior.
1 parent b0cbb15 commit 0d059d0

File tree

8 files changed

+37
-98
lines changed

8 files changed

+37
-98
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -215,25 +215,25 @@ Please see the [supported versions](https://github.com/DomainTools/python_api/ra
215215
for the DomainTools Python support policy.
216216

217217

218-
Real-Time Threat Intelligence Feeds
218+
Real-Time Threat Feeds
219219
===================
220220

221-
Real-Time Threat Intelligence Feeds provide data on the different stages of the domain lifecycle: from first-observed in the wild, to newly re-activated after a period of quiet. Access current feed data in real-time or retrieve historical feed data through separate APIs.
221+
Real-Time Threat Feeds provide data on the different stages of the domain lifecycle: from first-observed in the wild, to newly re-activated after a period of quiet. Access current feed data in real-time or retrieve historical feed data through separate APIs.
222222

223223
Custom parameters aside from the common `GET` Request parameters:
224224
- `endpoint` (choose either `download` or `feed` API endpoint - default is `feed`)
225225
```python
226-
api = API(USERNAME, KEY, always_sign_api_key=False)
226+
api = API(USERNAME, KEY)
227227
api.nod(endpoint="feed", **kwargs)
228228
```
229229
- `header_authentication`: by default, we're using API Header Authentication. Set this False if you want to use API Key and Secret Authentication. Apparently, you can't use API Header Authentication for `download` endpoints so this will be defaulted to `False` even without explicitly setting it.
230230
```python
231-
api = API(USERNAME, KEY, always_sign_api_key=False)
232-
api.nod(header_authentication=False, **kwargs)
231+
api = API(USERNAME, KEY, header_authentication=False)
232+
api.nod(**kwargs)
233233
```
234234
- `output_format`: (choose either `csv` or `jsonl` - default is `jsonl`). Cannot be used in `domainrdap` feeds. Additionally, `csv` is not available for `download` endpoints.
235235
```python
236-
api = API(USERNAME, KEY, always_sign_api_key=False)
236+
api = API(USERNAME, KEY)
237237
api.nod(output_format="csv", **kwargs)
238238
```
239239

@@ -254,7 +254,7 @@ Since we may dealing with large feeds datasets, the python wrapper uses `generat
254254
```python
255255
from domaintools import API
256256

257-
api = API(USERNAME, KEY, always_sign_api_key=False)
257+
api = API(USERNAME, KEY)
258258
results = api.nod(sessionID="my-session-id", after=-60)
259259

260260
for result in results.response() # generator that holds NOD feeds data for the past 60 seconds and is expected to request only once
@@ -265,7 +265,7 @@ for result in results.response() # generator that holds NOD feeds data for the p
265265
```python
266266
from domaintools import API
267267

268-
api = API(USERNAME, KEY, always_sign_api_key=False)
268+
api = API(USERNAME, KEY)
269269
results = api.nod(sessionID="my-session-id", after=-7200)
270270

271271
for partial_result in results.response() # generator that holds NOD feeds data for the past 2 hours and is expected to request multiple times

domaintools/api.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import re
77
import ssl
88

9-
from domaintools.constants import Endpoint, ENDPOINT_TO_SOURCE_MAP, FEEDS_PRODUCTS_LIST, OutputFormat
9+
from domaintools.constants import Endpoint, ENDPOINT_TO_SOURCE_MAP, RTTF_PRODUCTS_LIST, OutputFormat
1010
from domaintools._version import current as version
1111
from domaintools.results import (
1212
GroupedIterable,
@@ -63,7 +63,8 @@ def __init__(
6363
verify_ssl=True,
6464
rate_limit=True,
6565
proxy_url=None,
66-
always_sign_api_key=True,
66+
always_sign_api_key=None,
67+
header_authentication=None,
6768
key_sign_hash="sha256",
6869
app_name="python_wrapper",
6970
app_version=version,
@@ -83,6 +84,7 @@ def __init__(
8384
self.proxy_url = proxy_url
8485
self.extra_request_params = {}
8586
self.always_sign_api_key = always_sign_api_key
87+
self.header_authentication = header_authentication
8688
self.key_sign_hash = key_sign_hash
8789
self.default_parameters["app_name"] = app_name
8890
self.default_parameters["app_version"] = app_version
@@ -129,20 +131,31 @@ def _results(self, product, path, cls=Results, **kwargs):
129131
uri = "/".join((self._rest_api_url, path.lstrip("/")))
130132
parameters = self.default_parameters.copy()
131133
parameters["api_username"] = self.username
132-
header_authentication = kwargs.pop("header_authentication", True) # Used only by Real-Time Threat Intelligence Feeds endpoints for now
133-
self.handle_api_key(product, path, parameters, header_authentication)
134+
is_rttf_product = product in RTTF_PRODUCTS_LIST
135+
self._handle_api_key_parameters(is_rttf_product)
136+
self.handle_api_key(is_rttf_product, path, parameters)
134137
parameters.update({key: str(value).lower() if value in (True, False) else value for key, value in kwargs.items() if value is not None})
135138

136139
return cls(self, product, uri, **parameters)
137140

138-
def handle_api_key(self, product, path, parameters, header_authentication):
141+
def _handle_api_key_parameters(self, is_rttf_product):
142+
if self.always_sign_api_key is None:
143+
self.always_sign_api_key = not is_rttf_product
144+
145+
if self.header_authentication is None:
146+
self.header_authentication = is_rttf_product
147+
148+
def handle_api_key(self, is_rttf_product, path, parameters):
139149
if self.https and not self.always_sign_api_key:
140-
if product in FEEDS_PRODUCTS_LIST and header_authentication:
150+
if self.header_authentication:
141151
parameters["X-Api-Key"] = self.key
142152
else:
143153
parameters["api_key"] = self.key
144154
else:
145-
if self.key_sign_hash and self.key_sign_hash in AVAILABLE_KEY_SIGN_HASHES:
155+
if is_rttf_product:
156+
# As per requirement in IDEV-2272, raise this error when the user explicitly sets signing of API key for RTTF endpoints
157+
raise ValueError("Real Time Threat Feeds do not support signed API keys.")
158+
elif self.key_sign_hash and self.key_sign_hash in AVAILABLE_KEY_SIGN_HASHES:
146159
signing_hash = eval(self.key_sign_hash)
147160
else:
148161
raise ValueError(

domaintools/base_results.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from datetime import datetime
1010
from httpx import Client
1111

12-
from domaintools.constants import FEEDS_PRODUCTS_LIST, OutputFormat, HEADER_ACCEPT_KEY_CSV_FORMAT
12+
from domaintools.constants import RTTF_PRODUCTS_LIST, OutputFormat, HEADER_ACCEPT_KEY_CSV_FORMAT
1313
from domaintools.exceptions import (
1414
BadRequestException,
1515
InternalServerErrorException,
@@ -107,7 +107,7 @@ def _make_request(self):
107107
patch_data = self.kwargs.copy()
108108
patch_data.update(self.api.extra_request_params)
109109
return session.patch(url=self.url, json=patch_data)
110-
elif self.product in FEEDS_PRODUCTS_LIST:
110+
elif self.product in RTTF_PRODUCTS_LIST:
111111
session_params = self._get_session_params()
112112
parameters = session_params.get("parameters")
113113
headers = session_params.get("headers")
@@ -170,7 +170,7 @@ def status(self):
170170

171171
def setStatus(self, code, response=None):
172172
self._status = code
173-
if code == 200 or (self.product in FEEDS_PRODUCTS_LIST and code == 206):
173+
if code == 200 or (self.product in RTTF_PRODUCTS_LIST and code == 206):
174174
return
175175

176176
reason = None

domaintools/cli/api.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from rich.progress import Progress, SpinnerColumn, TextColumn
1010

1111
from domaintools.api import API
12-
from domaintools.constants import Endpoint, FEEDS_PRODUCTS_LIST, OutputFormat
12+
from domaintools.constants import Endpoint, RTTF_PRODUCTS_LIST, OutputFormat
1313
from domaintools.cli.utils import get_file_extension
1414
from domaintools.exceptions import ServiceException
1515
from domaintools._version import current as version
@@ -110,7 +110,7 @@ def args_to_dict(*args) -> Dict:
110110
def _get_formatted_output(cls, cmd_name: str, response, out_format: str = "json"):
111111
if cmd_name in ("available_api_calls",):
112112
return "\n".join(response)
113-
if response.product in FEEDS_PRODUCTS_LIST:
113+
if response.product in RTTF_PRODUCTS_LIST:
114114
return "\n".join([data for data in response.response()])
115115
return str(getattr(response, out_format) if out_format != "list" else response.as_list())
116116

@@ -229,7 +229,7 @@ def run(cls, name: str, params: Optional[Dict] = {}, **kwargs):
229229

230230
if isinstance(out_file, _io.TextIOWrapper):
231231
# use rich `print` command to prettify the ouput in sys.stdout
232-
if response.product in FEEDS_PRODUCTS_LIST:
232+
if response.product in RTTF_PRODUCTS_LIST:
233233
print(output)
234234
else:
235235
print(response)

domaintools/cli/commands/feeds.py

Lines changed: 0 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,6 @@ def feeds_nad(
2727
"--no-verify-ssl",
2828
help="Skip verification of SSL certificate when making HTTPs API calls",
2929
),
30-
no_sign_api_key: bool = typer.Option(
31-
False,
32-
"--no-sign-api-key",
33-
help="Skip signing of api key",
34-
),
35-
header_authentication: bool = typer.Option(
36-
True,
37-
"--no-header-auth",
38-
help="Don't use header authentication",
39-
),
4030
output_format: str = typer.Option(
4131
"jsonl",
4232
"-f",
@@ -107,16 +97,6 @@ def feeds_nod(
10797
"--no-verify-ssl",
10898
help="Skip verification of SSL certificate when making HTTPs API calls",
10999
),
110-
no_sign_api_key: bool = typer.Option(
111-
False,
112-
"--no-sign-api-key",
113-
help="Skip signing of api key",
114-
),
115-
header_authentication: bool = typer.Option(
116-
True,
117-
"--no-header-auth",
118-
help="Don't use header authentication",
119-
),
120100
output_format: str = typer.Option(
121101
"jsonl",
122102
"-f",
@@ -187,16 +167,6 @@ def feeds_domainrdap(
187167
"--no-verify-ssl",
188168
help="Skip verification of SSL certificate when making HTTPs API calls",
189169
),
190-
no_sign_api_key: bool = typer.Option(
191-
False,
192-
"--no-sign-api-key",
193-
help="Skip signing of api key",
194-
),
195-
header_authentication: bool = typer.Option(
196-
True,
197-
"--no-header-auth",
198-
help="Don't use header authentication",
199-
),
200170
endpoint: str = typer.Option(
201171
Endpoint.FEED.value,
202172
"-e",
@@ -255,16 +225,6 @@ def feeds_domaindiscovery(
255225
"--no-verify-ssl",
256226
help="Skip verification of SSL certificate when making HTTPs API calls",
257227
),
258-
no_sign_api_key: bool = typer.Option(
259-
False,
260-
"--no-sign-api-key",
261-
help="Skip signing of api key",
262-
),
263-
header_authentication: bool = typer.Option(
264-
True,
265-
"--no-header-auth",
266-
help="Don't use header authentication",
267-
),
268228
output_format: str = typer.Option(
269229
"jsonl",
270230
"-f",
@@ -335,16 +295,6 @@ def feeds_noh(
335295
"--no-verify-ssl",
336296
help="Skip verification of SSL certificate when making HTTPs API calls",
337297
),
338-
no_sign_api_key: bool = typer.Option(
339-
False,
340-
"--no-sign-api-key",
341-
help="Skip signing of api key",
342-
),
343-
header_authentication: bool = typer.Option(
344-
True,
345-
"--no-header-auth",
346-
help="Don't use header authentication",
347-
),
348298
output_format: str = typer.Option(
349299
"jsonl",
350300
"-f",
@@ -415,16 +365,6 @@ def feeds_domainhotlist(
415365
"--no-verify-ssl",
416366
help="Skip verification of SSL certificate when making HTTPs API calls",
417367
),
418-
no_sign_api_key: bool = typer.Option(
419-
False,
420-
"--no-sign-api-key",
421-
help="Skip signing of api key",
422-
),
423-
header_authentication: bool = typer.Option(
424-
True,
425-
"--no-header-auth",
426-
help="Don't use header authentication",
427-
),
428368
output_format: str = typer.Option(
429369
"jsonl",
430370
"-f",
@@ -495,16 +435,6 @@ def feeds_realtime_domain_risk(
495435
"--no-verify-ssl",
496436
help="Skip verification of SSL certificate when making HTTPs API calls",
497437
),
498-
no_sign_api_key: bool = typer.Option(
499-
False,
500-
"--no-sign-api-key",
501-
help="Skip signing of api key",
502-
),
503-
header_authentication: bool = typer.Option(
504-
True,
505-
"--no-header-auth",
506-
help="Don't use header authentication",
507-
),
508438
output_format: str = typer.Option(
509439
"jsonl",
510440
"-f",

domaintools/constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class OutputFormat(Enum):
2323
Endpoint.DOWNLOAD.value: Source.S3,
2424
}
2525

26-
FEEDS_PRODUCTS_LIST = [
26+
RTTF_PRODUCTS_LIST = [
2727
"newly-active-domains-feed-(api)",
2828
"newly-active-domains-feed-(s3)",
2929
"newly-observed-domains-feed-(api)",

domaintools/utils.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,3 @@ def validate_feeds_parameters(params):
183183
endpoint = params.get("endpoint")
184184
if endpoint == Endpoint.DOWNLOAD.value and format == OutputFormat.CSV.value:
185185
raise ValueError(f"{format} format is not available in {Endpoint.DOWNLOAD.value} API.")
186-
187-
if endpoint == Endpoint.DOWNLOAD.value and params.get("header_authentication", True):
188-
# For download endpoint, header_authentication will be False by default
189-
params["header_authentication"] = False

domaintools_async/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from httpx import AsyncClient
77

88
from domaintools.base_results import Results
9-
from domaintools.constants import FEEDS_PRODUCTS_LIST, OutputFormat, HEADER_ACCEPT_KEY_CSV_FORMAT
9+
from domaintools.constants import RTTF_PRODUCTS_LIST, OutputFormat, HEADER_ACCEPT_KEY_CSV_FORMAT
1010
from domaintools.exceptions import ServiceUnavailableException
1111

1212

@@ -51,7 +51,7 @@ async def _make_async_request(self, session):
5151
patch_data = self.kwargs.copy()
5252
patch_data.update(self.api.extra_request_params)
5353
results = await session.patch(url=self.url, json=patch_data)
54-
elif self.product in FEEDS_PRODUCTS_LIST:
54+
elif self.product in RTTF_PRODUCTS_LIST:
5555
session_params = self._get_session_params()
5656
parameters = session_params.get("parameters")
5757
headers = session_params.get("headers")

0 commit comments

Comments
 (0)