Skip to content

Commit 96aa262

Browse files
Merge pull request #612 from tableau/jichikawa-dev-auth-warning
Security: Require confirmation to start TabPy without authentication
2 parents 5716a67 + 59e3dbd commit 96aa262

File tree

8 files changed

+72
-12
lines changed

8 files changed

+72
-12
lines changed

.github/workflows/pull_request.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ jobs:
99

1010
strategy:
1111
matrix:
12-
python-version: [3.7, 3.8, 3.9]
12+
# TODO: Add 3.7 to python-versions after GitHub action regression is resolved.
13+
# https://github.com/actions/setup-python/issues/682
14+
python-version: [3.8, 3.9]
1315
os: [ubuntu-latest, windows-latest, macos-latest]
1416

1517
steps:

.github/workflows/push.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ jobs:
99

1010
strategy:
1111
matrix:
12-
python-version: [3.7, 3.8, 3.9]
12+
# TODO: Add 3.7 to python-versions after GitHub action regression is resolved.
13+
# https://github.com/actions/setup-python/issues/682
14+
python-version: [3.8, 3.9]
1315
os: [ubuntu-latest, windows-latest, macos-latest]
1416

1517
steps:

CHANGELOG

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## v2.9.0
4+
5+
### Improvements
6+
7+
- Require confirmation to continue when starting TabPy without authentication,
8+
with a warning that this is an insecure state and not recommended.
9+
310
## v2.8.0
411

512
### Improvements

tabpy/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.8.0
1+
2.9.0

tabpy/tabpy.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
44
Usage:
55
tabpy [-h] | [--help]
6-
tabpy [--config <CONFIG>]
6+
tabpy [--config <CONFIG>] [--disable-auth-warning]
77
88
Options:
9-
-h --help Show this screen.
10-
--config <CONFIG> Path to a config file.
9+
-h --help Show this screen.
10+
--config <CONFIG> Path to a config file.
11+
--disable-auth-warning Disable authentication warning.
1112
"""
1213

1314
import docopt
@@ -38,9 +39,13 @@ def main():
3839
args = docopt.docopt(__doc__)
3940
config = args["--config"] or None
4041

42+
disable_auth_warning = False
43+
if args["--disable-auth-warning"]:
44+
disable_auth_warning = True
45+
4146
from tabpy.tabpy_server.app.app import TabPyApp
4247

43-
app = TabPyApp(config)
48+
app = TabPyApp(config, disable_auth_warning)
4449
app.run()
4550

4651

tabpy/tabpy_server/app/app.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ class TabPyApp:
6767
arrow_server = None
6868
max_request_size = None
6969

70-
def __init__(self, config_file):
70+
def __init__(self, config_file, disable_auth_warning=True):
71+
self.disable_auth_warning = disable_auth_warning
7172
if config_file is None:
7273
config_file = os.path.join(
7374
os.path.dirname(__file__), os.path.pardir, "common", "default.conf"
@@ -394,9 +395,7 @@ def _parse_config(self, config_file):
394395
logger.critical(msg)
395396
raise RuntimeError(msg)
396397
else:
397-
logger.info(
398-
"Password file is not specified: " "Authentication is not enabled"
399-
)
398+
self._handle_configuration_without_authentication()
400399

401400
features = self._get_features()
402401
self.settings[SettingsParameters.ApiVersions] = {"v1": {"features": features}}
@@ -471,6 +470,31 @@ def _parse_pwd_file(self):
471470

472471
return succeeded
473472

473+
def _handle_configuration_without_authentication(self):
474+
std_no_auth_msg = "Password file is not specified: Authentication is not enabled"
475+
476+
if self.disable_auth_warning == True:
477+
logger.info(std_no_auth_msg)
478+
return
479+
480+
confirm_no_auth_msg = "\nWARNING: This TabPy server is not currently configured for username/password authentication. "
481+
482+
if self.settings[SettingsParameters.EvaluateEnabled]:
483+
confirm_no_auth_msg += ("This means that, because the TABPY_EVALUATE_ENABLE feature is enabled, there is "
484+
"the potential that unauthenticated individuals may be able to remotely execute code on this machine. ")
485+
486+
confirm_no_auth_msg += ("We strongly advise against proceeding without authentication as it poses a significant security risk.\n\n"
487+
"Do you wish to proceed without authentication? (y/N): ")
488+
489+
confirm_no_auth_input = input(confirm_no_auth_msg)
490+
491+
if confirm_no_auth_input == 'y':
492+
logger.info(std_no_auth_msg)
493+
else:
494+
print("\nAborting start up. To enable authentication for your TabPy server, see "
495+
"https://github.com/tableau/TabPy/blob/master/docs/server-config.md#authentication.")
496+
exit()
497+
474498
def _get_features(self):
475499
features = {}
476500

tests/integration/integ_test_base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ def setUp(self):
226226
# Platform specific - for integration tests we want to engage
227227
# startup script
228228
with open(self.tmp_dir + "/output.txt", "w") as outfile:
229-
cmd = ["tabpy", "--config=" + self.config_file_name]
229+
cmd = ["tabpy", "--config=" + self.config_file_name, "--disable-auth-warning"]
230230
preexec_fn = None
231231
if platform.system() == "Windows":
232232
self.py = "python"

tests/unit/server_tests/test_config.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,26 @@ def test_no_state_ini_file_or_state_dir(
6868
TabPyApp(None)
6969
self.assertEqual(len(mock_os.makedirs.mock_calls), 1)
7070

71+
@patch('builtins.input', return_value='y')
72+
@patch("tabpy.tabpy_server.app.app.os")
73+
@patch("tabpy.tabpy_server.app.app.os.path.exists", return_value=False)
74+
@patch("tabpy.tabpy_server.app.app.PythonServiceHandler")
75+
@patch("tabpy.tabpy_server.app.app._get_state_from_file")
76+
@patch("tabpy.tabpy_server.app.app.TabPyState")
77+
def test_handle_configuration_without_authentication(
78+
self,
79+
mock_tabpy_state,
80+
mock_get_state_from_file,
81+
mock_psws,
82+
mock_os_path_exists,
83+
mock_os,
84+
mock_input,
85+
):
86+
TabPyApp(None)
87+
mock_input.assert_not_called()
88+
89+
TabPyApp(None, False)
90+
mock_input.assert_called()
7191

7292
class TestPartialConfigFile(unittest.TestCase):
7393
def setUp(self):

0 commit comments

Comments
 (0)