tabpy 2.8.0__tar.gz → 2.10.0__tar.gz

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.
Files changed (143) hide show
  1. {tabpy-2.8.0 → tabpy-2.10.0}/.github/workflows/pull_request.yml +5 -2
  2. {tabpy-2.8.0 → tabpy-2.10.0}/.github/workflows/push.yml +6 -3
  3. {tabpy-2.8.0 → tabpy-2.10.0}/.scrutinizer.yml +7 -1
  4. {tabpy-2.8.0 → tabpy-2.10.0}/CHANGELOG +15 -0
  5. {tabpy-2.8.0/tabpy.egg-info → tabpy-2.10.0}/PKG-INFO +41 -1
  6. {tabpy-2.8.0 → tabpy-2.10.0}/README.md +12 -0
  7. {tabpy-2.8.0 → tabpy-2.10.0}/docs/server-config.md +8 -4
  8. {tabpy-2.8.0 → tabpy-2.10.0}/docs/server-install.md +11 -1
  9. tabpy-2.10.0/tabpy/VERSION +1 -0
  10. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy.py +9 -4
  11. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/app/app.py +50 -8
  12. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/app/app_parameters.py +2 -0
  13. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/app/util.py +1 -1
  14. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/common/default.conf +1 -0
  15. {tabpy-2.8.0 → tabpy-2.10.0/tabpy.egg-info}/PKG-INFO +41 -1
  16. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy.egg-info/SOURCES.txt +1 -0
  17. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/integ_test_base.py +3 -2
  18. tabpy-2.10.0/tests/integration/test_minimum_tls_version.py +54 -0
  19. {tabpy-2.8.0 → tabpy-2.10.0}/tests/unit/server_tests/test_config.py +34 -0
  20. tabpy-2.8.0/tabpy/VERSION +0 -1
  21. {tabpy-2.8.0 → tabpy-2.10.0}/.github/ISSUE_TEMPLATE/tabpy-issue-report.md +0 -0
  22. {tabpy-2.8.0 → tabpy-2.10.0}/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -0
  23. {tabpy-2.8.0 → tabpy-2.10.0}/.github/workflows/coverage.yml +0 -0
  24. {tabpy-2.8.0 → tabpy-2.10.0}/.github/workflows/docker-publish.yml +0 -0
  25. {tabpy-2.8.0 → tabpy-2.10.0}/.github/workflows/lint.yml +0 -0
  26. {tabpy-2.8.0 → tabpy-2.10.0}/.gitignore +0 -0
  27. {tabpy-2.8.0 → tabpy-2.10.0}/.pep8speaks.yml +0 -0
  28. {tabpy-2.8.0 → tabpy-2.10.0}/CONTRIBUTING.md +0 -0
  29. {tabpy-2.8.0 → tabpy-2.10.0}/Dockerfile +0 -0
  30. {tabpy-2.8.0 → tabpy-2.10.0}/LICENSE +0 -0
  31. {tabpy-2.8.0 → tabpy-2.10.0}/MANIFEST.in +0 -0
  32. {tabpy-2.8.0 → tabpy-2.10.0}/Procfile +0 -0
  33. {tabpy-2.8.0 → tabpy-2.10.0}/_config.yml +0 -0
  34. {tabpy-2.8.0 → tabpy-2.10.0}/app.json +0 -0
  35. {tabpy-2.8.0 → tabpy-2.10.0}/codeql-analysis.yml +0 -0
  36. {tabpy-2.8.0 → tabpy-2.10.0}/docs/TableauConfiguration.md +0 -0
  37. {tabpy-2.8.0 → tabpy-2.10.0}/docs/about.md +0 -0
  38. {tabpy-2.8.0 → tabpy-2.10.0}/docs/deploy-to-heroku.md +0 -0
  39. {tabpy-2.8.0 → tabpy-2.10.0}/docs/img/Example1-SimpleFunctionCall.png +0 -0
  40. {tabpy-2.8.0 → tabpy-2.10.0}/docs/img/Example2-MultipleFunctionCalls.png +0 -0
  41. {tabpy-2.8.0 → tabpy-2.10.0}/docs/img/python-calculated-field.png +0 -0
  42. {tabpy-2.8.0 → tabpy-2.10.0}/docs/security.md +0 -0
  43. {tabpy-2.8.0 → tabpy-2.10.0}/docs/server-rest.md +0 -0
  44. {tabpy-2.8.0 → tabpy-2.10.0}/docs/tabpy-tools.md +0 -0
  45. {tabpy-2.8.0 → tabpy-2.10.0}/docs/tabpy-virtualenv.md +0 -0
  46. {tabpy-2.8.0 → tabpy-2.10.0}/misc/TabPy.postman_collection.json +0 -0
  47. {tabpy-2.8.0 → tabpy-2.10.0}/misc/TabPy.yml +0 -0
  48. {tabpy-2.8.0 → tabpy-2.10.0}/requirements.txt +0 -0
  49. {tabpy-2.8.0 → tabpy-2.10.0}/requirements_dev.txt +0 -0
  50. {tabpy-2.8.0 → tabpy-2.10.0}/setup.cfg +0 -0
  51. {tabpy-2.8.0 → tabpy-2.10.0}/setup.py +0 -0
  52. {tabpy-2.8.0 → tabpy-2.10.0}/start.sh +0 -0
  53. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/__init__.py +0 -0
  54. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/models/__init__.py +0 -0
  55. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/models/deploy_models.py +0 -0
  56. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/models/scripts/ANOVA.py +0 -0
  57. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/models/scripts/PCA.py +0 -0
  58. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/models/scripts/SentimentAnalysis.py +0 -0
  59. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/models/scripts/__init__.py +0 -0
  60. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/models/scripts/tTest.py +0 -0
  61. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/models/utils/__init__.py +0 -0
  62. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/models/utils/setup_utils.py +0 -0
  63. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/__init__.py +0 -0
  64. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/app/__init__.py +0 -0
  65. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/app/arrow_server.py +0 -0
  66. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/common/__init__.py +0 -0
  67. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/common/endpoint_file_mgr.py +0 -0
  68. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/common/messages.py +0 -0
  69. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/common/util.py +0 -0
  70. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/__init__.py +0 -0
  71. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/base_handler.py +0 -0
  72. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/basic_auth_server_middleware_factory.py +0 -0
  73. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/endpoint_handler.py +0 -0
  74. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/endpoints_handler.py +0 -0
  75. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/evaluation_plane_handler.py +0 -0
  76. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/management_handler.py +0 -0
  77. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/no_op_auth_handler.py +0 -0
  78. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/query_plane_handler.py +0 -0
  79. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/service_info_handler.py +0 -0
  80. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/status_handler.py +0 -0
  81. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/upload_destination_handler.py +0 -0
  82. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/util.py +0 -0
  83. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/management/__init__.py +0 -0
  84. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/management/state.py +0 -0
  85. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/management/util.py +0 -0
  86. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/psws/__init__.py +0 -0
  87. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/psws/callbacks.py +0 -0
  88. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/psws/python_service.py +0 -0
  89. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/state.ini.template +0 -0
  90. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/static/TabPy_logo.png +0 -0
  91. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/static/index.html +0 -0
  92. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_server/static/tableau.png +0 -0
  93. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_tools/__init__.py +0 -0
  94. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_tools/client.py +0 -0
  95. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_tools/custom_query_object.py +0 -0
  96. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_tools/query_object.py +0 -0
  97. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_tools/rest.py +0 -0
  98. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_tools/rest_client.py +0 -0
  99. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/tabpy_tools/schema.py +0 -0
  100. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/utils/__init__.py +0 -0
  101. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy/utils/tabpy_user.py +0 -0
  102. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy.egg-info/dependency_links.txt +0 -0
  103. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy.egg-info/entry_points.txt +0 -0
  104. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy.egg-info/requires.txt +0 -0
  105. {tabpy-2.8.0 → tabpy-2.10.0}/tabpy.egg-info/top_level.txt +0 -0
  106. {tabpy-2.8.0 → tabpy-2.10.0}/tests/__init__.py +0 -0
  107. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/__init__.py +0 -0
  108. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/resources/2019_04_24_to_3018_08_25.crt +0 -0
  109. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/resources/2019_04_24_to_3018_08_25.key +0 -0
  110. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/resources/data.csv +0 -0
  111. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/resources/deploy_and_evaluate_model.conf +0 -0
  112. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/resources/deploy_and_evaluate_model_auth.conf +0 -0
  113. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/resources/pwdfile.txt +0 -0
  114. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/test_arrow_server.py +0 -0
  115. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/test_auth.py +0 -0
  116. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/test_custom_evaluate_timeout.py +0 -0
  117. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/test_deploy_and_evaluate_model.py +0 -0
  118. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/test_deploy_and_evaluate_model_auth_on.py +0 -0
  119. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/test_deploy_and_evaluate_model_ssl.py +0 -0
  120. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/test_deploy_model_ssl_off_auth_off.py +0 -0
  121. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/test_deploy_model_ssl_off_auth_on.py +0 -0
  122. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/test_deploy_model_ssl_on_auth_off.py +0 -0
  123. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/test_deploy_model_ssl_on_auth_on.py +0 -0
  124. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/test_evaluate.py +0 -0
  125. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/test_gzip.py +0 -0
  126. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/test_url.py +0 -0
  127. {tabpy-2.8.0 → tabpy-2.10.0}/tests/integration/test_url_ssl.py +0 -0
  128. {tabpy-2.8.0 → tabpy-2.10.0}/tests/unit/__init__.py +0 -0
  129. {tabpy-2.8.0 → tabpy-2.10.0}/tests/unit/server_tests/__init__.py +0 -0
  130. {tabpy-2.8.0 → tabpy-2.10.0}/tests/unit/server_tests/resources/expired.crt +0 -0
  131. {tabpy-2.8.0 → tabpy-2.10.0}/tests/unit/server_tests/resources/future.crt +0 -0
  132. {tabpy-2.8.0 → tabpy-2.10.0}/tests/unit/server_tests/resources/valid.crt +0 -0
  133. {tabpy-2.8.0 → tabpy-2.10.0}/tests/unit/server_tests/test_endpoint_file_manager.py +0 -0
  134. {tabpy-2.8.0 → tabpy-2.10.0}/tests/unit/server_tests/test_endpoint_handler.py +0 -0
  135. {tabpy-2.8.0 → tabpy-2.10.0}/tests/unit/server_tests/test_endpoints_handler.py +0 -0
  136. {tabpy-2.8.0 → tabpy-2.10.0}/tests/unit/server_tests/test_evaluation_plane_handler.py +0 -0
  137. {tabpy-2.8.0 → tabpy-2.10.0}/tests/unit/server_tests/test_pwd_file.py +0 -0
  138. {tabpy-2.8.0 → tabpy-2.10.0}/tests/unit/server_tests/test_service_info_handler.py +0 -0
  139. {tabpy-2.8.0 → tabpy-2.10.0}/tests/unit/tools_tests/__init__.py +0 -0
  140. {tabpy-2.8.0 → tabpy-2.10.0}/tests/unit/tools_tests/test_client.py +0 -0
  141. {tabpy-2.8.0 → tabpy-2.10.0}/tests/unit/tools_tests/test_rest.py +0 -0
  142. {tabpy-2.8.0 → tabpy-2.10.0}/tests/unit/tools_tests/test_rest_object.py +0 -0
  143. {tabpy-2.8.0 → tabpy-2.10.0}/tests/unit/tools_tests/test_schema.py +0 -0
@@ -9,8 +9,11 @@ jobs:
9
9
 
10
10
  strategy:
11
11
  matrix:
12
- python-version: [3.7, 3.8, 3.9]
13
- os: [ubuntu-latest, windows-latest, macos-latest]
12
+ # TODO: Add 3.7 to python-versions after GitHub action regression is resolved.
13
+ # https://github.com/actions/setup-python/issues/682
14
+ # TODO: switch macos-13 to macos-latest@arm64
15
+ python-version: ['3.8', '3.9', '3.10']
16
+ os: [ubuntu-latest, windows-latest, macos-13]
14
17
 
15
18
  steps:
16
19
  - uses: actions/checkout@v1
@@ -9,9 +9,12 @@ jobs:
9
9
 
10
10
  strategy:
11
11
  matrix:
12
- python-version: [3.7, 3.8, 3.9]
13
- os: [ubuntu-latest, windows-latest, macos-latest]
14
-
12
+ # TODO: Add 3.7 to python-versions after GitHub action regression is resolved.
13
+ # https://github.com/actions/setup-python/issues/682
14
+ # TODO: switch macos-13 to macos-latest@arm64
15
+ python-version: ['3.8', '3.9', '3.10']
16
+ os: [ubuntu-latest, windows-latest, macos-13]
17
+
15
18
  steps:
16
19
  - uses: actions/checkout@v1
17
20
 
@@ -10,6 +10,7 @@ build:
10
10
  tests:
11
11
  override:
12
12
  - command: 'pytest tests --cov=tabpy --cov-config=setup.cfg'
13
+ idle_timeout: 600
13
14
  coverage:
14
15
  file: '.coverage'
15
16
  config_file: 'setup.cfg'
@@ -20,10 +21,15 @@ build:
20
21
  - pip install -r requirements.txt
21
22
  tests:
22
23
  override: [py-scrutinizer-run]
24
+ dependencies:
25
+ override:
26
+ - pip install .
23
27
  tests:
24
28
  before:
25
29
  - pip install -r requirements.txt
26
- override: [pytest]
30
+ override:
31
+ pytest:
32
+ idle_timeout: 600
27
33
  checks:
28
34
  python:
29
35
  code_rating: true
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## v2.10.0
4
+
5
+ ### Improvements
6
+
7
+ - Add TabPy parameter (TABPY_MINIMUM_TLS_VERSION) to specify the minimum TLS
8
+ version that the server will accept for secure connections. Default is
9
+ set to TLSv1_2.
10
+
11
+ ## v2.9.0
12
+
13
+ ### Improvements
14
+
15
+ - Require confirmation to continue when starting TabPy without authentication,
16
+ with a warning that this is an insecure state and not recommended.
17
+
3
18
  ## v2.8.0
4
19
 
5
20
  ### Improvements
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tabpy
3
- Version: 2.8.0
3
+ Version: 2.10.0
4
4
  Summary: Web server Tableau uses to run Python scripts.
5
5
  Home-page: https://github.com/tableau/TabPy
6
6
  Download-URL: https://pypi.org/project/tabpy
@@ -33,6 +33,31 @@ Classifier: Operating System :: MacOS
33
33
  Requires-Python: >=3.7
34
34
  Description-Content-Type: text/markdown
35
35
  License-File: LICENSE
36
+ Requires-Dist: cloudpickle
37
+ Requires-Dist: configparser
38
+ Requires-Dist: coverage
39
+ Requires-Dist: coveralls
40
+ Requires-Dist: docopt
41
+ Requires-Dist: future
42
+ Requires-Dist: genson
43
+ Requires-Dist: hypothesis
44
+ Requires-Dist: jsonschema
45
+ Requires-Dist: mock
46
+ Requires-Dist: nltk
47
+ Requires-Dist: numpy
48
+ Requires-Dist: pandas
49
+ Requires-Dist: pyopenssl
50
+ Requires-Dist: pytest
51
+ Requires-Dist: pytest-cov
52
+ Requires-Dist: requests
53
+ Requires-Dist: scipy
54
+ Requires-Dist: simplejson
55
+ Requires-Dist: scikit-learn
56
+ Requires-Dist: textblob
57
+ Requires-Dist: tornado
58
+ Requires-Dist: twisted
59
+ Requires-Dist: urllib3
60
+ Requires-Dist: pyarrow
36
61
 
37
62
 
38
63
  TabPy (the Tableau Python Server) is an external service implementation
@@ -41,6 +66,21 @@ scripts and saved functions via Tableau's table calculations.
41
66
 
42
67
  # Changelog
43
68
 
69
+ ## v2.10.0
70
+
71
+ ### Improvements
72
+
73
+ - Add TabPy parameter (TABPY_MINIMUM_TLS_VERSION) to specify the minimum TLS
74
+ version that the server will accept for secure connections. Default is
75
+ set to TLSv1_2.
76
+
77
+ ## v2.9.0
78
+
79
+ ### Improvements
80
+
81
+ - Require confirmation to continue when starting TabPy without authentication,
82
+ with a warning that this is an insecure state and not recommended.
83
+
44
84
  ## v2.8.0
45
85
 
46
86
  ### Improvements
@@ -27,6 +27,18 @@ Consider reading TabPy documentation in the following order:
27
27
  * [Authoring Python calculations in Tableau](docs/TableauConfiguration.md).
28
28
  * [TabPy Tools](docs/tabpy-tools.md)
29
29
 
30
+ Important security note:
31
+
32
+ * By default, TabPy is configured without username/password authentication.
33
+ We strongly advise using TabPy only with authentication enabled. For more
34
+ information, see
35
+ [TabPy Server Configuration Instructions](docs/server-config.md#authentication).
36
+ Without authentication in place, if the TABPY_EVALUATE_ENABLE feature is
37
+ enabled (as it is by default), there is the possibility that unauthenticated
38
+ individuals could remotely execute code on the machine running TabPy.
39
+ Leaving these two settings in their default states together is highly
40
+ discouraged.
41
+
30
42
  Troubleshooting:
31
43
 
32
44
  * [TabPy Wiki](https://github.com/tableau/TabPy/wiki)
@@ -8,7 +8,6 @@
8
8
  * [Configuration File Content](#configuration-file-content)
9
9
  * [Configuration File Example](#configuration-file-example)
10
10
  - [Configuring HTTP vs HTTPS](#configuring-http-vs-https)
11
- - [Configuring TPS](#configuring-http-vs-https)
12
11
  - [Authentication](#authentication)
13
12
  * [Enabling Authentication](#enabling-authentication)
14
13
  * [Password File](#password-file)
@@ -83,6 +82,10 @@ at [`logging.config` documentation page](https://docs.python.org/3.6/library/log
83
82
  - `TABPY_KEY_FILE` - absolute path to private key file to run TabPy with.
84
83
  Only used with `TABPY_TRANSFER_PROTOCOL` set to `https`. Default value -
85
84
  not set.
85
+ - `TABPY_MINIMUM_TLS_VERSION` - set the minimum TLS version that the server
86
+ will accept for secure connections (`TLSv1_2`, `TLSv1_3`, etc). Refer to
87
+ [docs.python.org](https://docs.python.org/3/library/ssl.html#ssl.TLSVersion.MINIMUM_SUPPORTED)
88
+ for acceptable values. Default value - `TLSv1_2`.
86
89
  - `TABPY_LOG_DETAILS` - when set to `true` additional call information
87
90
  (caller IP, URL, client info, etc.) is logged. Default value - `false`.
88
91
  - `TABPY_MAX_REQUEST_SIZE_MB` - maximal request size supported by TabPy server
@@ -124,6 +127,7 @@ settings._
124
127
  # TABPY_TRANSFER_PROTOCOL = https
125
128
  # TABPY_CERTIFICATE_FILE = /path/to/certificate/file.crt
126
129
  # TABPY_KEY_FILE = /path/to/key/file.key
130
+ # TABPY_MINIMUM_TLS_VERSION = TLSv1_2
127
131
 
128
132
  # Log additional request details including caller IP, full URL, client
129
133
  # end user info if provided.
@@ -314,14 +318,14 @@ For extended logging (e.g. for auditing purposes) additional logging can be turn
314
318
  on with setting `TABPY_LOG_DETAILS` configuration file parameter to `true`.
315
319
 
316
320
  With the feature on additional information is logged for HTTP requests: caller ip,
317
- URL, client infomation (Tableau Desktop\Server), Tableau user name (for Tableau Server)
318
- and TabPy user name as shown in the example below:
321
+ URL, client infomation (Tableau Desktop\Server) and TabPy user name as shown in
322
+ the example below:
319
323
 
320
324
  <!-- markdownlint-disable MD013 -->
321
325
  <!-- markdownlint-disable MD040 -->
322
326
 
323
327
  ```
324
- 2019-05-02,13:50:08 [INFO] (base_handler.py:base_handler:90): Call ID: 934073bd-0d29-46d3-b693-b1e4b1efa9e4, Caller: ::1, Method: POST, Resource: http://localhost:9004/evaluate, Client: Postman for manual testing, Tableau user: ogolovatyi
328
+ 2019-05-02,13:50:08 [INFO] (base_handler.py:base_handler:90): Call ID: 934073bd-0d29-46d3-b693-b1e4b1efa9e4, Caller: ::1, Method: POST, Resource: http://localhost:9004/evaluate, Client: Postman for manual testing
325
329
  2019-05-02,13:50:08 [DEBUG] (base_handler.py:base_handler:120): Checking if need to handle authentication, <<
326
330
  call ID: 934073bd-0d29-46d3-b693-b1e4b1efa9e4>>
327
331
  2019-05-02,13:50:08 [DEBUG] (base_handler.py:base_handler:120): Handling authentication, <<call ID: 934073bd-
@@ -15,6 +15,16 @@ These instructions explain how to install and start up TabPy Server.
15
15
 
16
16
  ## TabPy Installation
17
17
 
18
+ ### Prerequisites
19
+
20
+ - TabPy only supports Python versions 3.7, 3.8, 3.9, and 3.10
21
+ - TabPy 2.7.0 added support for data streaming via
22
+ Apache Arrow Flight (`pyarrow`). When using TabPy >= 2.7.0, we
23
+ recommend using a Python 64-bit installer to avoid incompatibility issues
24
+ related to `pyarrow`.
25
+
26
+ ### Installation
27
+
18
28
  To install TabPy on to an environment `pip` needs to be installed and
19
29
  updated first:
20
30
 
@@ -30,7 +40,7 @@ pip install tabpy
30
40
 
31
41
  ## Starting TabPy
32
42
 
33
- To start TabPy with default setting run the following command:
43
+ To start TabPy with default settings run the following command:
34
44
 
35
45
  ```sh
36
46
  tabpy
@@ -0,0 +1 @@
1
+ 2.10.0
@@ -3,11 +3,12 @@ TabPy Server.
3
3
 
4
4
  Usage:
5
5
  tabpy [-h] | [--help]
6
- tabpy [--config <CONFIG>]
6
+ tabpy [--config <CONFIG>] [--disable-auth-warning]
7
7
 
8
8
  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.
11
12
  """
12
13
 
13
14
  import docopt
@@ -38,9 +39,13 @@ def main():
38
39
  args = docopt.docopt(__doc__)
39
40
  config = args["--config"] or None
40
41
 
42
+ disable_auth_warning = False
43
+ if args["--disable-auth-warning"]:
44
+ disable_auth_warning = True
45
+
41
46
  from tabpy.tabpy_server.app.app import TabPyApp
42
47
 
43
- app = TabPyApp(config)
48
+ app = TabPyApp(config, disable_auth_warning)
44
49
  app.run()
45
50
 
46
51
 
@@ -5,6 +5,7 @@ import multiprocessing
5
5
  import os
6
6
  import shutil
7
7
  import signal
8
+ import ssl
8
9
  import sys
9
10
  import _thread
10
11
 
@@ -67,7 +68,8 @@ class TabPyApp:
67
68
  arrow_server = None
68
69
  max_request_size = None
69
70
 
70
- def __init__(self, config_file):
71
+ def __init__(self, config_file, disable_auth_warning=True):
72
+ self.disable_auth_warning = disable_auth_warning
71
73
  if config_file is None:
72
74
  config_file = os.path.join(
73
75
  os.path.dirname(__file__), os.path.pardir, "common", "default.conf"
@@ -82,6 +84,24 @@ class TabPyApp:
82
84
 
83
85
  self._parse_config(config_file)
84
86
 
87
+ def _initialize_ssl_context(self):
88
+ ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
89
+
90
+ ssl_context.load_cert_chain(
91
+ certfile=self.settings[SettingsParameters.CertificateFile],
92
+ keyfile=self.settings[SettingsParameters.KeyFile]
93
+ )
94
+
95
+ min_tls = self.settings[SettingsParameters.MinimumTLSVersion]
96
+ if not hasattr(ssl.TLSVersion, min_tls):
97
+ logger.warning(f"Unrecognized value for TABPY_MINIMUM_TLS_VERSION: {min_tls}")
98
+ min_tls = "TLSv1_2"
99
+
100
+ logger.info(f"Setting minimum TLS version to {min_tls}")
101
+ ssl_context.minimum_version = ssl.TLSVersion[min_tls]
102
+
103
+ return ssl_context
104
+
85
105
  def _get_tls_certificates(self, config):
86
106
  tls_certificates = []
87
107
  cert = config[SettingsParameters.CertificateFile]
@@ -126,10 +146,7 @@ class TabPyApp:
126
146
  protocol = self.settings[SettingsParameters.TransferProtocol]
127
147
  ssl_options = None
128
148
  if protocol == "https":
129
- ssl_options = {
130
- "certfile": self.settings[SettingsParameters.CertificateFile],
131
- "keyfile": self.settings[SettingsParameters.KeyFile],
132
- }
149
+ ssl_options = self._initialize_ssl_context()
133
150
  elif protocol != "http":
134
151
  msg = f"Unsupported transfer protocol {protocol}."
135
152
  logger.critical(msg)
@@ -327,6 +344,8 @@ class TabPyApp:
327
344
  (SettingsParameters.CertificateFile, ConfigParameters.TABPY_CERTIFICATE_FILE,
328
345
  None, None),
329
346
  (SettingsParameters.KeyFile, ConfigParameters.TABPY_KEY_FILE, None, None),
347
+ (SettingsParameters.MinimumTLSVersion, ConfigParameters.TABPY_MINIMUM_TLS_VERSION,
348
+ "TLSv1_2", None),
330
349
  (SettingsParameters.StateFilePath, ConfigParameters.TABPY_STATE_PATH,
331
350
  os.path.join(pkg_path, "tabpy_server"), None),
332
351
  (SettingsParameters.StaticPath, ConfigParameters.TABPY_STATIC_PATH,
@@ -394,9 +413,7 @@ class TabPyApp:
394
413
  logger.critical(msg)
395
414
  raise RuntimeError(msg)
396
415
  else:
397
- logger.info(
398
- "Password file is not specified: " "Authentication is not enabled"
399
- )
416
+ self._handle_configuration_without_authentication()
400
417
 
401
418
  features = self._get_features()
402
419
  self.settings[SettingsParameters.ApiVersions] = {"v1": {"features": features}}
@@ -471,6 +488,31 @@ class TabPyApp:
471
488
 
472
489
  return succeeded
473
490
 
491
+ def _handle_configuration_without_authentication(self):
492
+ std_no_auth_msg = "Password file is not specified: Authentication is not enabled"
493
+
494
+ if self.disable_auth_warning == True:
495
+ logger.info(std_no_auth_msg)
496
+ return
497
+
498
+ confirm_no_auth_msg = "\nWARNING: This TabPy server is not currently configured for username/password authentication. "
499
+
500
+ if self.settings[SettingsParameters.EvaluateEnabled]:
501
+ confirm_no_auth_msg += ("This means that, because the TABPY_EVALUATE_ENABLE feature is enabled, there is "
502
+ "the potential that unauthenticated individuals may be able to remotely execute code on this machine. ")
503
+
504
+ confirm_no_auth_msg += ("We strongly advise against proceeding without authentication as it poses a significant security risk.\n\n"
505
+ "Do you wish to proceed without authentication? (y/N): ")
506
+
507
+ confirm_no_auth_input = input(confirm_no_auth_msg)
508
+
509
+ if confirm_no_auth_input == 'y':
510
+ logger.info(std_no_auth_msg)
511
+ else:
512
+ print("\nAborting start up. To enable authentication for your TabPy server, see "
513
+ "https://github.com/tableau/TabPy/blob/master/docs/server-config.md#authentication.")
514
+ exit()
515
+
474
516
  def _get_features(self):
475
517
  features = {}
476
518
 
@@ -10,6 +10,7 @@ class ConfigParameters:
10
10
  TABPY_TRANSFER_PROTOCOL = "TABPY_TRANSFER_PROTOCOL"
11
11
  TABPY_CERTIFICATE_FILE = "TABPY_CERTIFICATE_FILE"
12
12
  TABPY_KEY_FILE = "TABPY_KEY_FILE"
13
+ TABPY_MINIMUM_TLS_VERSION = "TABPY_MINIMUM_TLS_VERSION"
13
14
  TABPY_LOG_DETAILS = "TABPY_LOG_DETAILS"
14
15
  TABPY_STATIC_PATH = "TABPY_STATIC_PATH"
15
16
  TABPY_MAX_REQUEST_SIZE_MB = "TABPY_MAX_REQUEST_SIZE_MB"
@@ -33,6 +34,7 @@ class SettingsParameters:
33
34
  UploadDir = "upload_dir"
34
35
  CertificateFile = "certificate_file"
35
36
  KeyFile = "key_file"
37
+ MinimumTLSVersion = "minimum_tls_version"
36
38
  StateFilePath = "state_file_path"
37
39
  ApiVersions = "versions"
38
40
  LogRequestContext = "log_request_context"
@@ -17,7 +17,7 @@ def validate_cert(cert_file_path):
17
17
  date_format, encoding = "%Y%m%d%H%M%SZ", "ascii"
18
18
  not_before = datetime.strptime(cert.get_notBefore().decode(encoding), date_format)
19
19
  not_after = datetime.strptime(cert.get_notAfter().decode(encoding), date_format)
20
- now = datetime.now()
20
+ now = datetime.utcnow()
21
21
 
22
22
  https_error = "Error using HTTPS: "
23
23
  if now < not_before:
@@ -15,6 +15,7 @@
15
15
  # TABPY_TRANSFER_PROTOCOL = https
16
16
  # TABPY_CERTIFICATE_FILE = /path/to/certificate/file.crt
17
17
  # TABPY_KEY_FILE = /path/to/key/file.key
18
+ # TABPY_MINIMUM_TLS_VERSION = TLSv1_2
18
19
 
19
20
  # Log additional request details including caller IP, full URL, client
20
21
  # end user info if provided.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tabpy
3
- Version: 2.8.0
3
+ Version: 2.10.0
4
4
  Summary: Web server Tableau uses to run Python scripts.
5
5
  Home-page: https://github.com/tableau/TabPy
6
6
  Download-URL: https://pypi.org/project/tabpy
@@ -33,6 +33,31 @@ Classifier: Operating System :: MacOS
33
33
  Requires-Python: >=3.7
34
34
  Description-Content-Type: text/markdown
35
35
  License-File: LICENSE
36
+ Requires-Dist: cloudpickle
37
+ Requires-Dist: configparser
38
+ Requires-Dist: coverage
39
+ Requires-Dist: coveralls
40
+ Requires-Dist: docopt
41
+ Requires-Dist: future
42
+ Requires-Dist: genson
43
+ Requires-Dist: hypothesis
44
+ Requires-Dist: jsonschema
45
+ Requires-Dist: mock
46
+ Requires-Dist: nltk
47
+ Requires-Dist: numpy
48
+ Requires-Dist: pandas
49
+ Requires-Dist: pyopenssl
50
+ Requires-Dist: pytest
51
+ Requires-Dist: pytest-cov
52
+ Requires-Dist: requests
53
+ Requires-Dist: scipy
54
+ Requires-Dist: simplejson
55
+ Requires-Dist: scikit-learn
56
+ Requires-Dist: textblob
57
+ Requires-Dist: tornado
58
+ Requires-Dist: twisted
59
+ Requires-Dist: urllib3
60
+ Requires-Dist: pyarrow
36
61
 
37
62
 
38
63
  TabPy (the Tableau Python Server) is an external service implementation
@@ -41,6 +66,21 @@ scripts and saved functions via Tableau's table calculations.
41
66
 
42
67
  # Changelog
43
68
 
69
+ ## v2.10.0
70
+
71
+ ### Improvements
72
+
73
+ - Add TabPy parameter (TABPY_MINIMUM_TLS_VERSION) to specify the minimum TLS
74
+ version that the server will accept for secure connections. Default is
75
+ set to TLSv1_2.
76
+
77
+ ## v2.9.0
78
+
79
+ ### Improvements
80
+
81
+ - Require confirmation to continue when starting TabPy without authentication,
82
+ with a warning that this is an insecure state and not recommended.
83
+
44
84
  ## v2.8.0
45
85
 
46
86
  ### Improvements
@@ -113,6 +113,7 @@ tests/integration/test_deploy_model_ssl_on_auth_off.py
113
113
  tests/integration/test_deploy_model_ssl_on_auth_on.py
114
114
  tests/integration/test_evaluate.py
115
115
  tests/integration/test_gzip.py
116
+ tests/integration/test_minimum_tls_version.py
116
117
  tests/integration/test_url.py
117
118
  tests/integration/test_url_ssl.py
118
119
  tests/integration/resources/2019_04_24_to_3018_08_25.crt
@@ -225,8 +225,9 @@ class IntegTestBase(unittest.TestCase):
225
225
 
226
226
  # Platform specific - for integration tests we want to engage
227
227
  # startup script
228
- with open(self.tmp_dir + "/output.txt", "w") as outfile:
229
- cmd = ["tabpy", "--config=" + self.config_file_name]
228
+ self.log_file_path = os.path.join(self.tmp_dir, "output.txt")
229
+ with open(self.log_file_path, "w") as outfile:
230
+ cmd = ["tabpy", "--config=" + self.config_file_name, "--disable-auth-warning"]
230
231
  preexec_fn = None
231
232
  if platform.system() == "Windows":
232
233
  self.py = "python"
@@ -0,0 +1,54 @@
1
+ from . import integ_test_base
2
+ import os
3
+
4
+ class TestMinimumTLSVersion(integ_test_base.IntegTestBase):
5
+ def _get_log_contents(self):
6
+ with open(self.log_file_path, 'r') as f:
7
+ return f.read()
8
+
9
+ def _get_config_file_name(self, tls_version: str) -> str:
10
+ config_file = open(os.path.join(self.tmp_dir, "test.conf"), "w+")
11
+ config_file.write(
12
+ "[TabPy]\n"
13
+ "TABPY_PORT = 9005\n"
14
+ "TABPY_TRANSFER_PROTOCOL = https\n"
15
+ "TABPY_CERTIFICATE_FILE = ./tests/integration/resources/2019_04_24_to_3018_08_25.crt\n"
16
+ "TABPY_KEY_FILE = ./tests/integration/resources/2019_04_24_to_3018_08_25.key\n"
17
+ )
18
+
19
+ if tls_version is not None:
20
+ config_file.write(f"TABPY_MINIMUM_TLS_VERSION = {tls_version}")
21
+
22
+ pwd_file = self._get_pwd_file()
23
+ if pwd_file is not None:
24
+ pwd_file = os.path.abspath(pwd_file)
25
+ config_file.write(f"TABPY_PWD_FILE = {pwd_file}\n")
26
+
27
+ config_file.close()
28
+ self.delete_config_file = True
29
+ return config_file.name
30
+
31
+ class TestMinimumTLSVersionValid(TestMinimumTLSVersion):
32
+ def _get_config_file_name(self) -> str:
33
+ return super()._get_config_file_name("TLSv1_3")
34
+
35
+ def test_minimum_tls_version_valid(self):
36
+ log_contents = self._get_log_contents()
37
+ self.assertIn("Setting minimum TLS version to TLSv1_3", log_contents)
38
+
39
+ class TestMinimumTLSVersionInvalid(TestMinimumTLSVersion):
40
+ def _get_config_file_name(self) -> str:
41
+ return super()._get_config_file_name("TLSv-1.3")
42
+
43
+ def test_minimum_tls_version_invalid(self):
44
+ log_contents = self._get_log_contents()
45
+ self.assertIn("Unrecognized value for TABPY_MINIMUM_TLS_VERSION", log_contents)
46
+ self.assertIn("Setting minimum TLS version to TLSv1_2", log_contents)
47
+
48
+ class TestMinimumTLSVersionNotSpecified(TestMinimumTLSVersion):
49
+ def _get_config_file_name(self) -> str:
50
+ return super()._get_config_file_name(None)
51
+
52
+ def test_minimum_tls_version_not_specified(self):
53
+ log_contents = self._get_log_contents()
54
+ self.assertIn("Setting minimum TLS version to TLSv1_2", log_contents)
@@ -68,6 +68,26 @@ class TestConfigEnvironmentCalls(unittest.TestCase):
68
68
  TabPyApp(None)
69
69
  self.assertEqual(len(mock_os.makedirs.mock_calls), 1)
70
70
 
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()
71
91
 
72
92
  class TestPartialConfigFile(unittest.TestCase):
73
93
  def setUp(self):
@@ -189,6 +209,20 @@ class TestPartialConfigFile(unittest.TestCase):
189
209
  app = TabPyApp(self.config_file.name)
190
210
  self.assertEqual(app.settings["gzip_enabled"], False)
191
211
 
212
+ @patch("tabpy.tabpy_server.app.app.os.path.exists", return_value=True)
213
+ @patch("tabpy.tabpy_server.app.app._get_state_from_file")
214
+ @patch("tabpy.tabpy_server.app.app.TabPyState")
215
+ def test_min_tls_setting_valid(
216
+ self, mock_state, mock_get_state_from_file, mock_path_exists
217
+ ):
218
+ self.assertTrue(self.config_file is not None)
219
+ config_file = self.config_file
220
+ config_file.write("[TabPy]\n" "TABPY_MINIMUM_TLS_VERSION = TLSv1_3".encode())
221
+ config_file.close()
222
+
223
+ app = TabPyApp(self.config_file.name)
224
+ self.assertEqual(app.settings["minimum_tls_version"], "TLSv1_3")
225
+
192
226
  class TestTransferProtocolValidation(unittest.TestCase):
193
227
  def assertTabPyAppRaisesRuntimeError(self, expected_message):
194
228
  with self.assertRaises(RuntimeError) as err:
tabpy-2.8.0/tabpy/VERSION DELETED
@@ -1 +0,0 @@
1
- 2.8.0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes