Skip to content
This repository was archived by the owner on May 17, 2024. It is now read-only.

Commit 867100e

Browse files
authored
Merge pull request #172 from datafold/redacted_log
Print configuration during debug, but with passwords redacted
2 parents 4afeaf0 + 8e934c1 commit 867100e

File tree

3 files changed

+45
-6
lines changed

3 files changed

+45
-6
lines changed

data_diff/__main__.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import logging
66
from itertools import islice
77

8+
from .utils import remove_password_from_url
9+
810
from .diff_tables import (
911
TableSegment,
1012
TableDiffer,
@@ -33,6 +35,8 @@ def _remove_passwords_in_dict(d: dict):
3335
d[k] = "*" * len(v)
3436
elif isinstance(v, dict):
3537
_remove_passwords_in_dict(v)
38+
elif k.startswith("database"):
39+
d[k] = remove_password_from_url(v)
3640

3741

3842
@click.command()
@@ -120,11 +124,10 @@ def _main(
120124

121125
if debug:
122126
logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=DATE_FORMAT)
123-
# XXX Temporarily commented out, until we remove the passwords from URIs as well. See issue #150.
124-
# if __conf__:
125-
# __conf__ = deepcopy(__conf__)
126-
# _remove_passwords_in_dict(__conf__)
127-
# logging.debug(f"Applied run configuration: {__conf__}")
127+
if __conf__:
128+
__conf__ = deepcopy(__conf__)
129+
_remove_passwords_in_dict(__conf__)
130+
logging.debug(f"Applied run configuration: {__conf__}")
128131
elif verbose:
129132
logging.basicConfig(level=logging.INFO, format=LOG_FORMAT, datefmt=DATE_FORMAT)
130133

data_diff/utils.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import math
2+
from urllib.parse import urlparse
23

3-
from typing import Sequence, Optional, Tuple, Union, Dict, Any
4+
from typing import Union, Any
45
from uuid import UUID
56

67

@@ -53,9 +54,26 @@ def number_to_human(n):
5354
return "{:.0f}{}".format(n / 10 ** (3 * millidx), millnames[millidx])
5455

5556

57+
def _join_if_any(sym, args):
58+
args = list(args)
59+
if not args:
60+
return ''
61+
return sym.join(str(a) for a in args if a)
62+
63+
def remove_password_from_url(url: str, replace_with: str="***") -> str:
64+
parsed = urlparse(url)
65+
account = parsed.username or ''
66+
if parsed.password:
67+
account += ':' + replace_with
68+
host = _join_if_any(":", filter(None, [parsed.hostname, parsed.port]))
69+
netloc = _join_if_any("@", filter(None, [account, host]))
70+
replaced = parsed._replace(netloc=netloc)
71+
return replaced.geturl()
72+
5673
def join_iter(joiner: Any, iterable: iter) -> iter:
5774
it = iter(iterable)
5875
yield next(it)
5976
for i in it:
6077
yield joiner
6178
yield i
79+

tests/test_config.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import unittest
22

33
from data_diff.config import apply_config_from_string, ConfigParseError
4+
from data_diff.utils import remove_password_from_url
45

56

67
class TestConfig(unittest.TestCase):
@@ -41,3 +42,20 @@ def test_basic(self):
4142
res = apply_config_from_string(config, "pg_pg", {"update_column": "foo", "table2": "bar"})
4243
assert res["update_column"] == "foo"
4344
assert res["table2"] == "bar"
45+
46+
def test_remove_password(self):
47+
replace_with = "*****"
48+
urls = [
49+
'd://host/',
50+
'd://host:123/',
51+
'd://user@host:123/',
52+
'd://user:PASS@host:123/',
53+
'd://:PASS@host:123/',
54+
'd://:PASS@host:123/path',
55+
'd://:PASS@host:123/path?whatever#blabla',
56+
]
57+
for url in urls:
58+
removed = remove_password_from_url(url, replace_with)
59+
expected = url.replace('PASS', replace_with)
60+
removed = remove_password_from_url(url, replace_with)
61+
self.assertEqual(removed, expected)

0 commit comments

Comments
 (0)