pyntcli 0.1.47__py3-none-any.whl → 0.1.48__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.
Files changed (101) hide show
  1. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/__init__.py +1 -0
  2. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/main.py +74 -0
  3. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/auth/test_login.py +96 -0
  4. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/conftest.py +24 -0
  5. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/store/test_cred_store.py +11 -0
  6. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/__init__.py +1 -0
  7. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/main.py +74 -0
  8. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/auth/test_login.py +96 -0
  9. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/conftest.py +24 -0
  10. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/store/test_cred_store.py +11 -0
  11. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/__init__.py +1 -0
  12. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/main.py +74 -0
  13. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/auth/test_login.py +96 -0
  14. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/conftest.py +24 -0
  15. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/store/test_cred_store.py +11 -0
  16. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/__init__.py +1 -0
  17. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/main.py +74 -0
  18. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/auth/test_login.py +96 -0
  19. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/conftest.py +24 -0
  20. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/store/test_cred_store.py +11 -0
  21. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/__init__.py +1 -0
  22. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/main.py +74 -0
  23. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/auth/test_login.py +96 -0
  24. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/conftest.py +24 -0
  25. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/store/test_cred_store.py +11 -0
  26. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/__init__.py +1 -0
  27. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/main.py +74 -0
  28. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/auth/test_login.py +96 -0
  29. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/conftest.py +24 -0
  30. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/store/test_cred_store.py +11 -0
  31. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/__init__.py +1 -0
  32. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/main.py +74 -0
  33. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/auth/test_login.py +96 -0
  34. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/conftest.py +24 -0
  35. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/store/test_cred_store.py +11 -0
  36. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/__init__.py +1 -0
  37. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/main.py +74 -0
  38. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/auth/test_login.py +96 -0
  39. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/conftest.py +24 -0
  40. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/store/test_cred_store.py +11 -0
  41. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/__init__.py +1 -0
  42. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/main.py +74 -0
  43. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/auth/test_login.py +96 -0
  44. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/conftest.py +24 -0
  45. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/store/test_cred_store.py +11 -0
  46. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/__init__.py +1 -0
  47. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/main.py +74 -0
  48. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/auth/test_login.py +96 -0
  49. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/conftest.py +24 -0
  50. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/store/test_cred_store.py +11 -0
  51. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/__init__.py +1 -0
  52. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/main.py +74 -0
  53. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/auth/test_login.py +96 -0
  54. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/conftest.py +24 -0
  55. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/store/test_cred_store.py +11 -0
  56. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/__init__.py +1 -0
  57. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/main.py +74 -0
  58. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/auth/test_login.py +96 -0
  59. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/conftest.py +24 -0
  60. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/store/test_cred_store.py +11 -0
  61. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/__init__.py +1 -0
  62. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/main.py +74 -0
  63. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/auth/test_login.py +96 -0
  64. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/conftest.py +24 -0
  65. build/lib/build/lib/build/lib/build/lib/build/lib/build/lib/tests/store/test_cred_store.py +11 -0
  66. build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/__init__.py +1 -0
  67. build/lib/build/lib/build/lib/build/lib/build/lib/pyntcli/main.py +74 -0
  68. build/lib/build/lib/build/lib/build/lib/build/lib/tests/auth/test_login.py +96 -0
  69. build/lib/build/lib/build/lib/build/lib/build/lib/tests/conftest.py +24 -0
  70. build/lib/build/lib/build/lib/build/lib/build/lib/tests/store/test_cred_store.py +11 -0
  71. build/lib/build/lib/build/lib/build/lib/pyntcli/__init__.py +1 -0
  72. build/lib/build/lib/build/lib/build/lib/pyntcli/main.py +74 -0
  73. build/lib/build/lib/build/lib/build/lib/tests/auth/test_login.py +96 -0
  74. build/lib/build/lib/build/lib/build/lib/tests/conftest.py +24 -0
  75. build/lib/build/lib/build/lib/build/lib/tests/store/test_cred_store.py +11 -0
  76. build/lib/build/lib/build/lib/pyntcli/__init__.py +1 -0
  77. build/lib/build/lib/build/lib/pyntcli/main.py +74 -0
  78. build/lib/build/lib/build/lib/tests/auth/test_login.py +96 -0
  79. build/lib/build/lib/build/lib/tests/conftest.py +24 -0
  80. build/lib/build/lib/build/lib/tests/store/test_cred_store.py +11 -0
  81. build/lib/build/lib/pyntcli/__init__.py +1 -0
  82. build/lib/build/lib/pyntcli/main.py +74 -0
  83. build/lib/build/lib/tests/auth/test_login.py +96 -0
  84. build/lib/build/lib/tests/conftest.py +24 -0
  85. build/lib/build/lib/tests/store/test_cred_store.py +11 -0
  86. build/lib/pyntcli/__init__.py +1 -0
  87. build/lib/pyntcli/main.py +74 -0
  88. build/lib/tests/auth/test_login.py +96 -0
  89. build/lib/tests/conftest.py +24 -0
  90. build/lib/tests/store/test_cred_store.py +11 -0
  91. pyntcli/__init__.py +1 -1
  92. pyntcli/auth/login.py +3 -0
  93. pyntcli/commands/postman.py +4 -0
  94. pyntcli/commands/root.py +1 -0
  95. pyntcli/pynt_docker/pynt_container.py +10 -2
  96. {pyntcli-0.1.47.dist-info → pyntcli-0.1.48.dist-info}/METADATA +1 -1
  97. pyntcli-0.1.48.dist-info/RECORD +127 -0
  98. {pyntcli-0.1.47.dist-info → pyntcli-0.1.48.dist-info}/top_level.txt +1 -0
  99. pyntcli-0.1.47.dist-info/RECORD +0 -37
  100. {pyntcli-0.1.47.dist-info → pyntcli-0.1.48.dist-info}/WHEEL +0 -0
  101. {pyntcli-0.1.47.dist-info → pyntcli-0.1.48.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,74 @@
1
+ from sys import argv, exit
2
+ import signal
3
+
4
+ from pyntcli.commands import pynt_cmd
5
+ from pyntcli.pynt_docker import pynt_container
6
+ from pyntcli.ui import ui_thread
7
+ from pyntcli.ui import pynt_errors
8
+ from pyntcli.auth import login
9
+ from pyntcli.analytics import send as analytics
10
+ from requests.exceptions import SSLError
11
+ from pyntcli.transport.pynt_requests import InvalidPathException, InvalidCertFormat
12
+ from pyntcli.commands.util import HtmlReportNotCreatedException
13
+ import os
14
+
15
+ def signal_handler(signal_number, frame):
16
+ ui_thread.print(ui_thread.PrinterText("Exiting..."))
17
+
18
+ analytics.stop()
19
+ pynt_container.PyntContainerRegistery.instance().stop_all_containers()
20
+ ui_thread.stop()
21
+
22
+ exit(0)
23
+
24
+ def check_for_dependecies():
25
+ pynt_container.get_docker_type()
26
+
27
+ def print_header():
28
+ ui_thread.print(ui_thread.PrinterText(*ui_thread.pynt_header()) \
29
+ .with_line(*ui_thread.pynt_version()) \
30
+ .with_line(""))
31
+ def start_analytics():
32
+ user_id = login.user_id()
33
+ if user_id:
34
+ analytics.set_user_id(user_id)
35
+
36
+ def main():
37
+ print_header()
38
+ try:
39
+ start_analytics()
40
+ check_for_dependecies()
41
+ signal.signal(signal.SIGINT, signal_handler)
42
+ cli = pynt_cmd.PyntCommand()
43
+ cli.run_cmd(cli.parse_args(argv[1:]))
44
+ analytics.stop()
45
+ except pynt_cmd.PyntCommandException:
46
+ pynt_cmd.root.usage()
47
+ except pynt_container.DockerNotAvailableException:
48
+ ui_thread.print(ui_thread.PrinterText("Docker was unavailable, please make sure docker is installed and running.", ui_thread.PrinterText.WARNING))
49
+ analytics.emit(analytics.ERROR, {"error": "docker unavailable"})
50
+ except SSLError:
51
+ ui_thread.print(ui_thread.PrinterText("We encountered SSL issues and could not proceed, this may be the cause of a VPN or a Firewall in place.", ui_thread.PrinterText.WARNING))
52
+ except login.Timeout:
53
+ ui_thread.print(ui_thread.PrinterText("Pynt CLI exited due to incomplete registration, please try again.", ui_thread.PrinterText.WARNING))
54
+ analytics.emit(analytics.ERROR, {"error": "login timeout"})
55
+ except login.InvalidTokenInEnvVarsException:
56
+ ui_thread.print(ui_thread.PrinterText("Pynt CLI exited due to malformed credentials provided in env vars.", ui_thread.PrinterText.WARNING))
57
+ analytics.emit(analytics.ERROR, {"error": "invalid pynt cli credentials in env vars"})
58
+ except pynt_container.ImageUnavailableException:
59
+ analytics.emit(analytics.ERROR,{"error": "Couldnt pull pynt image and no local image found"})
60
+ pynt_errors.unexpected_error()
61
+ except HtmlReportNotCreatedException:
62
+ analytics.emit(analytics.ERROR,{"error": "Html report was not created"})
63
+ pynt_errors.unexpected_error()
64
+ except InvalidPathException as e:
65
+ ui_thread.print(ui_thread.PrinterText("Pynt CLI exited due to invalid host-CA path: {}".format(e), ui_thread.PrinterText.WARNING))
66
+ analytics.emit(analytics.ERROR,{"error": "Host CA path provided was invalid"})
67
+ except InvalidCertFormat as e:
68
+ ui_thread.print(ui_thread.PrinterText("Pynt CLI exited due to invalid host-CA. Please provide a file in PEM format: {}".format(e), ui_thread.PrinterText.WARNING))
69
+ analytics.emit(analytics.ERROR,{"error": "Host CA provided was not in valid pem format"})
70
+ finally:
71
+ ui_thread.stop()
72
+
73
+ if __name__ == "__main__":
74
+ main()
@@ -0,0 +1,96 @@
1
+ from urllib.parse import urlparse
2
+ import json
3
+ from _pytest.monkeypatch import monkeypatch
4
+ import pytest
5
+ import requests
6
+ import requests_mock
7
+ import datetime
8
+ import jwt
9
+ import os
10
+ from cryptography.hazmat.primitives.asymmetric import rsa
11
+ from cryptography.hazmat.primitives import serialization
12
+
13
+ from pyntcli.auth.login import Login, Timeout, InvalidTokenInEnvVarsException, is_jwt_expired, should_login, PYNT_CREDENTIALS
14
+ from pyntcli.store import CredStore
15
+
16
+
17
+ class TestLogin():
18
+ @pytest.fixture
19
+ def mock_webbrowser(self, mocker):
20
+ try:
21
+ mocker.patch("webbrowser.open", return_value=None)
22
+ yield
23
+ finally:
24
+ pass
25
+
26
+ def get_request_url_parameters(self, req: requests.PreparedRequest) :
27
+ u = req.url
28
+ parsed_url = urlparse(u)
29
+ return parsed_url.query
30
+
31
+ def poll_matcher(self, request: requests.PreparedRequest):
32
+ assert "request_id" in self.get_request_url_parameters(request)
33
+
34
+ resp = requests.Response()
35
+ self.login_request_cnt += 1
36
+ if self.login_request_cnt < 2:
37
+ resp.status_code = 404
38
+ return resp
39
+
40
+ resp.status_code = 200
41
+ resp._content = json.dumps({"token": "testToken"}).encode()
42
+ return resp
43
+
44
+ def test_login(self, mock_webbrowser, mock_sleep, mock_expanduser):
45
+ l = Login()
46
+ self.login_request_cnt = 0
47
+ with requests_mock.mock() as m:
48
+ m.add_matcher(self.poll_matcher)
49
+ l.login()
50
+
51
+ assert self.login_request_cnt == 2
52
+ c = CredStore()
53
+ assert c.get("token") == {"token": "testToken"}
54
+
55
+ def test_login_timeout(self, mock_webbrowser, mock_sleep, mock_expanduser):
56
+ l = Login()
57
+ l.login_wait_period = 0
58
+ self.login_request_cnt = 0
59
+ with pytest.raises(Timeout):
60
+ with requests_mock.mock() as m:
61
+ m.add_matcher(self.poll_matcher)
62
+ l.get_token_using_request_id("some_id")
63
+
64
+
65
+ def test_is_jwt_expired(self):
66
+
67
+ private_key = rsa.generate_private_key(
68
+ public_exponent=65537,
69
+ key_size=2048
70
+ ).private_bytes(encoding=serialization.Encoding.PEM,
71
+ format=serialization.PrivateFormat.PKCS8,
72
+ encryption_algorithm=serialization.NoEncryption())
73
+
74
+
75
+ token_data = {
76
+ "exp": int((datetime.datetime.now() - datetime.timedelta(days=1)).timestamp())
77
+ }
78
+
79
+ token = jwt.encode(token_data, private_key.decode(), algorithm="RS256").decode("utf-8")
80
+ assert is_jwt_expired(token) == True
81
+
82
+ token_data = {
83
+ "exp": int((datetime.datetime.now() + datetime.timedelta(days=1)).timestamp())
84
+ }
85
+
86
+ token = jwt.encode(token_data, private_key.decode(), algorithm="RS256").decode("utf-8")
87
+ assert is_jwt_expired(token) == False
88
+
89
+ def test_login_using_env_vars(self, mocker, mock_expanduser):
90
+ creds = json.dumps({"token": {"refresh_token": "some data"}})
91
+ mocker.patch.dict(os.environ, {PYNT_CREDENTIALS: creds})
92
+ assert should_login() == False
93
+
94
+ os.environ[PYNT_CREDENTIALS] = "some bad credentials"
95
+ with pytest.raises(InvalidTokenInEnvVarsException):
96
+ should_login()
@@ -0,0 +1,24 @@
1
+ import tempfile
2
+ import pytest
3
+ import shutil
4
+
5
+ import sys
6
+ sys.path.append("../")
7
+
8
+ @pytest.fixture
9
+ def mock_expanduser( mocker):
10
+ dir = tempfile.mkdtemp()
11
+ try:
12
+ mocker.patch("os.path.expanduser", return_value=dir)
13
+ yield
14
+ finally:
15
+ shutil.rmtree(dir)
16
+
17
+ @pytest.fixture
18
+ def mock_sleep( mocker):
19
+ try:
20
+ mocker.patch("time.sleep", return_value=None)
21
+ yield
22
+ finally:
23
+ pass
24
+
@@ -0,0 +1,11 @@
1
+ import tempfile
2
+ import shutil
3
+ import pytest
4
+
5
+ from pyntcli.store import CredStore
6
+
7
+ class TestCredStore():
8
+ def test_get_credentials(self, mock_expanduser):
9
+ c = CredStore()
10
+ c.put("data", "value")
11
+ assert c.get("data") == "value"
@@ -0,0 +1,74 @@
1
+ from sys import argv, exit
2
+ import signal
3
+
4
+ from pyntcli.commands import pynt_cmd
5
+ from pyntcli.pynt_docker import pynt_container
6
+ from pyntcli.ui import ui_thread
7
+ from pyntcli.ui import pynt_errors
8
+ from pyntcli.auth import login
9
+ from pyntcli.analytics import send as analytics
10
+ from requests.exceptions import SSLError
11
+ from pyntcli.transport.pynt_requests import InvalidPathException, InvalidCertFormat
12
+ from pyntcli.commands.util import HtmlReportNotCreatedException
13
+ import os
14
+
15
+ def signal_handler(signal_number, frame):
16
+ ui_thread.print(ui_thread.PrinterText("Exiting..."))
17
+
18
+ analytics.stop()
19
+ pynt_container.PyntContainerRegistery.instance().stop_all_containers()
20
+ ui_thread.stop()
21
+
22
+ exit(0)
23
+
24
+ def check_for_dependecies():
25
+ pynt_container.get_docker_type()
26
+
27
+ def print_header():
28
+ ui_thread.print(ui_thread.PrinterText(*ui_thread.pynt_header()) \
29
+ .with_line(*ui_thread.pynt_version()) \
30
+ .with_line(""))
31
+ def start_analytics():
32
+ user_id = login.user_id()
33
+ if user_id:
34
+ analytics.set_user_id(user_id)
35
+
36
+ def main():
37
+ print_header()
38
+ try:
39
+ start_analytics()
40
+ check_for_dependecies()
41
+ signal.signal(signal.SIGINT, signal_handler)
42
+ cli = pynt_cmd.PyntCommand()
43
+ cli.run_cmd(cli.parse_args(argv[1:]))
44
+ analytics.stop()
45
+ except pynt_cmd.PyntCommandException:
46
+ pynt_cmd.root.usage()
47
+ except pynt_container.DockerNotAvailableException:
48
+ ui_thread.print(ui_thread.PrinterText("Docker was unavailable, please make sure docker is installed and running.", ui_thread.PrinterText.WARNING))
49
+ analytics.emit(analytics.ERROR, {"error": "docker unavailable"})
50
+ except SSLError:
51
+ ui_thread.print(ui_thread.PrinterText("We encountered SSL issues and could not proceed, this may be the cause of a VPN or a Firewall in place.", ui_thread.PrinterText.WARNING))
52
+ except login.Timeout:
53
+ ui_thread.print(ui_thread.PrinterText("Pynt CLI exited due to incomplete registration, please try again.", ui_thread.PrinterText.WARNING))
54
+ analytics.emit(analytics.ERROR, {"error": "login timeout"})
55
+ except login.InvalidTokenInEnvVarsException:
56
+ ui_thread.print(ui_thread.PrinterText("Pynt CLI exited due to malformed credentials provided in env vars.", ui_thread.PrinterText.WARNING))
57
+ analytics.emit(analytics.ERROR, {"error": "invalid pynt cli credentials in env vars"})
58
+ except pynt_container.ImageUnavailableException:
59
+ analytics.emit(analytics.ERROR,{"error": "Couldnt pull pynt image and no local image found"})
60
+ pynt_errors.unexpected_error()
61
+ except HtmlReportNotCreatedException:
62
+ analytics.emit(analytics.ERROR,{"error": "Html report was not created"})
63
+ pynt_errors.unexpected_error()
64
+ except InvalidPathException as e:
65
+ ui_thread.print(ui_thread.PrinterText("Pynt CLI exited due to invalid host-CA path: {}".format(e), ui_thread.PrinterText.WARNING))
66
+ analytics.emit(analytics.ERROR,{"error": "Host CA path provided was invalid"})
67
+ except InvalidCertFormat as e:
68
+ ui_thread.print(ui_thread.PrinterText("Pynt CLI exited due to invalid host-CA. Please provide a file in PEM format: {}".format(e), ui_thread.PrinterText.WARNING))
69
+ analytics.emit(analytics.ERROR,{"error": "Host CA provided was not in valid pem format"})
70
+ finally:
71
+ ui_thread.stop()
72
+
73
+ if __name__ == "__main__":
74
+ main()
@@ -0,0 +1,96 @@
1
+ from urllib.parse import urlparse
2
+ import json
3
+ from _pytest.monkeypatch import monkeypatch
4
+ import pytest
5
+ import requests
6
+ import requests_mock
7
+ import datetime
8
+ import jwt
9
+ import os
10
+ from cryptography.hazmat.primitives.asymmetric import rsa
11
+ from cryptography.hazmat.primitives import serialization
12
+
13
+ from pyntcli.auth.login import Login, Timeout, InvalidTokenInEnvVarsException, is_jwt_expired, should_login, PYNT_CREDENTIALS
14
+ from pyntcli.store import CredStore
15
+
16
+
17
+ class TestLogin():
18
+ @pytest.fixture
19
+ def mock_webbrowser(self, mocker):
20
+ try:
21
+ mocker.patch("webbrowser.open", return_value=None)
22
+ yield
23
+ finally:
24
+ pass
25
+
26
+ def get_request_url_parameters(self, req: requests.PreparedRequest) :
27
+ u = req.url
28
+ parsed_url = urlparse(u)
29
+ return parsed_url.query
30
+
31
+ def poll_matcher(self, request: requests.PreparedRequest):
32
+ assert "request_id" in self.get_request_url_parameters(request)
33
+
34
+ resp = requests.Response()
35
+ self.login_request_cnt += 1
36
+ if self.login_request_cnt < 2:
37
+ resp.status_code = 404
38
+ return resp
39
+
40
+ resp.status_code = 200
41
+ resp._content = json.dumps({"token": "testToken"}).encode()
42
+ return resp
43
+
44
+ def test_login(self, mock_webbrowser, mock_sleep, mock_expanduser):
45
+ l = Login()
46
+ self.login_request_cnt = 0
47
+ with requests_mock.mock() as m:
48
+ m.add_matcher(self.poll_matcher)
49
+ l.login()
50
+
51
+ assert self.login_request_cnt == 2
52
+ c = CredStore()
53
+ assert c.get("token") == {"token": "testToken"}
54
+
55
+ def test_login_timeout(self, mock_webbrowser, mock_sleep, mock_expanduser):
56
+ l = Login()
57
+ l.login_wait_period = 0
58
+ self.login_request_cnt = 0
59
+ with pytest.raises(Timeout):
60
+ with requests_mock.mock() as m:
61
+ m.add_matcher(self.poll_matcher)
62
+ l.get_token_using_request_id("some_id")
63
+
64
+
65
+ def test_is_jwt_expired(self):
66
+
67
+ private_key = rsa.generate_private_key(
68
+ public_exponent=65537,
69
+ key_size=2048
70
+ ).private_bytes(encoding=serialization.Encoding.PEM,
71
+ format=serialization.PrivateFormat.PKCS8,
72
+ encryption_algorithm=serialization.NoEncryption())
73
+
74
+
75
+ token_data = {
76
+ "exp": int((datetime.datetime.now() - datetime.timedelta(days=1)).timestamp())
77
+ }
78
+
79
+ token = jwt.encode(token_data, private_key.decode(), algorithm="RS256").decode("utf-8")
80
+ assert is_jwt_expired(token) == True
81
+
82
+ token_data = {
83
+ "exp": int((datetime.datetime.now() + datetime.timedelta(days=1)).timestamp())
84
+ }
85
+
86
+ token = jwt.encode(token_data, private_key.decode(), algorithm="RS256").decode("utf-8")
87
+ assert is_jwt_expired(token) == False
88
+
89
+ def test_login_using_env_vars(self, mocker, mock_expanduser):
90
+ creds = json.dumps({"token": {"refresh_token": "some data"}})
91
+ mocker.patch.dict(os.environ, {PYNT_CREDENTIALS: creds})
92
+ assert should_login() == False
93
+
94
+ os.environ[PYNT_CREDENTIALS] = "some bad credentials"
95
+ with pytest.raises(InvalidTokenInEnvVarsException):
96
+ should_login()
@@ -0,0 +1,24 @@
1
+ import tempfile
2
+ import pytest
3
+ import shutil
4
+
5
+ import sys
6
+ sys.path.append("../")
7
+
8
+ @pytest.fixture
9
+ def mock_expanduser( mocker):
10
+ dir = tempfile.mkdtemp()
11
+ try:
12
+ mocker.patch("os.path.expanduser", return_value=dir)
13
+ yield
14
+ finally:
15
+ shutil.rmtree(dir)
16
+
17
+ @pytest.fixture
18
+ def mock_sleep( mocker):
19
+ try:
20
+ mocker.patch("time.sleep", return_value=None)
21
+ yield
22
+ finally:
23
+ pass
24
+
@@ -0,0 +1,11 @@
1
+ import tempfile
2
+ import shutil
3
+ import pytest
4
+
5
+ from pyntcli.store import CredStore
6
+
7
+ class TestCredStore():
8
+ def test_get_credentials(self, mock_expanduser):
9
+ c = CredStore()
10
+ c.put("data", "value")
11
+ assert c.get("data") == "value"
@@ -0,0 +1,74 @@
1
+ from sys import argv, exit
2
+ import signal
3
+
4
+ from pyntcli.commands import pynt_cmd
5
+ from pyntcli.pynt_docker import pynt_container
6
+ from pyntcli.ui import ui_thread
7
+ from pyntcli.ui import pynt_errors
8
+ from pyntcli.auth import login
9
+ from pyntcli.analytics import send as analytics
10
+ from requests.exceptions import SSLError
11
+ from pyntcli.transport.pynt_requests import InvalidPathException, InvalidCertFormat
12
+ from pyntcli.commands.util import HtmlReportNotCreatedException
13
+ import os
14
+
15
+ def signal_handler(signal_number, frame):
16
+ ui_thread.print(ui_thread.PrinterText("Exiting..."))
17
+
18
+ analytics.stop()
19
+ pynt_container.PyntContainerRegistery.instance().stop_all_containers()
20
+ ui_thread.stop()
21
+
22
+ exit(0)
23
+
24
+ def check_for_dependecies():
25
+ pynt_container.get_docker_type()
26
+
27
+ def print_header():
28
+ ui_thread.print(ui_thread.PrinterText(*ui_thread.pynt_header()) \
29
+ .with_line(*ui_thread.pynt_version()) \
30
+ .with_line(""))
31
+ def start_analytics():
32
+ user_id = login.user_id()
33
+ if user_id:
34
+ analytics.set_user_id(user_id)
35
+
36
+ def main():
37
+ print_header()
38
+ try:
39
+ start_analytics()
40
+ check_for_dependecies()
41
+ signal.signal(signal.SIGINT, signal_handler)
42
+ cli = pynt_cmd.PyntCommand()
43
+ cli.run_cmd(cli.parse_args(argv[1:]))
44
+ analytics.stop()
45
+ except pynt_cmd.PyntCommandException:
46
+ pynt_cmd.root.usage()
47
+ except pynt_container.DockerNotAvailableException:
48
+ ui_thread.print(ui_thread.PrinterText("Docker was unavailable, please make sure docker is installed and running.", ui_thread.PrinterText.WARNING))
49
+ analytics.emit(analytics.ERROR, {"error": "docker unavailable"})
50
+ except SSLError:
51
+ ui_thread.print(ui_thread.PrinterText("We encountered SSL issues and could not proceed, this may be the cause of a VPN or a Firewall in place.", ui_thread.PrinterText.WARNING))
52
+ except login.Timeout:
53
+ ui_thread.print(ui_thread.PrinterText("Pynt CLI exited due to incomplete registration, please try again.", ui_thread.PrinterText.WARNING))
54
+ analytics.emit(analytics.ERROR, {"error": "login timeout"})
55
+ except login.InvalidTokenInEnvVarsException:
56
+ ui_thread.print(ui_thread.PrinterText("Pynt CLI exited due to malformed credentials provided in env vars.", ui_thread.PrinterText.WARNING))
57
+ analytics.emit(analytics.ERROR, {"error": "invalid pynt cli credentials in env vars"})
58
+ except pynt_container.ImageUnavailableException:
59
+ analytics.emit(analytics.ERROR,{"error": "Couldnt pull pynt image and no local image found"})
60
+ pynt_errors.unexpected_error()
61
+ except HtmlReportNotCreatedException:
62
+ analytics.emit(analytics.ERROR,{"error": "Html report was not created"})
63
+ pynt_errors.unexpected_error()
64
+ except InvalidPathException as e:
65
+ ui_thread.print(ui_thread.PrinterText("Pynt CLI exited due to invalid host-CA path: {}".format(e), ui_thread.PrinterText.WARNING))
66
+ analytics.emit(analytics.ERROR,{"error": "Host CA path provided was invalid"})
67
+ except InvalidCertFormat as e:
68
+ ui_thread.print(ui_thread.PrinterText("Pynt CLI exited due to invalid host-CA. Please provide a file in PEM format: {}".format(e), ui_thread.PrinterText.WARNING))
69
+ analytics.emit(analytics.ERROR,{"error": "Host CA provided was not in valid pem format"})
70
+ finally:
71
+ ui_thread.stop()
72
+
73
+ if __name__ == "__main__":
74
+ main()
@@ -0,0 +1,96 @@
1
+ from urllib.parse import urlparse
2
+ import json
3
+ from _pytest.monkeypatch import monkeypatch
4
+ import pytest
5
+ import requests
6
+ import requests_mock
7
+ import datetime
8
+ import jwt
9
+ import os
10
+ from cryptography.hazmat.primitives.asymmetric import rsa
11
+ from cryptography.hazmat.primitives import serialization
12
+
13
+ from pyntcli.auth.login import Login, Timeout, InvalidTokenInEnvVarsException, is_jwt_expired, should_login, PYNT_CREDENTIALS
14
+ from pyntcli.store import CredStore
15
+
16
+
17
+ class TestLogin():
18
+ @pytest.fixture
19
+ def mock_webbrowser(self, mocker):
20
+ try:
21
+ mocker.patch("webbrowser.open", return_value=None)
22
+ yield
23
+ finally:
24
+ pass
25
+
26
+ def get_request_url_parameters(self, req: requests.PreparedRequest) :
27
+ u = req.url
28
+ parsed_url = urlparse(u)
29
+ return parsed_url.query
30
+
31
+ def poll_matcher(self, request: requests.PreparedRequest):
32
+ assert "request_id" in self.get_request_url_parameters(request)
33
+
34
+ resp = requests.Response()
35
+ self.login_request_cnt += 1
36
+ if self.login_request_cnt < 2:
37
+ resp.status_code = 404
38
+ return resp
39
+
40
+ resp.status_code = 200
41
+ resp._content = json.dumps({"token": "testToken"}).encode()
42
+ return resp
43
+
44
+ def test_login(self, mock_webbrowser, mock_sleep, mock_expanduser):
45
+ l = Login()
46
+ self.login_request_cnt = 0
47
+ with requests_mock.mock() as m:
48
+ m.add_matcher(self.poll_matcher)
49
+ l.login()
50
+
51
+ assert self.login_request_cnt == 2
52
+ c = CredStore()
53
+ assert c.get("token") == {"token": "testToken"}
54
+
55
+ def test_login_timeout(self, mock_webbrowser, mock_sleep, mock_expanduser):
56
+ l = Login()
57
+ l.login_wait_period = 0
58
+ self.login_request_cnt = 0
59
+ with pytest.raises(Timeout):
60
+ with requests_mock.mock() as m:
61
+ m.add_matcher(self.poll_matcher)
62
+ l.get_token_using_request_id("some_id")
63
+
64
+
65
+ def test_is_jwt_expired(self):
66
+
67
+ private_key = rsa.generate_private_key(
68
+ public_exponent=65537,
69
+ key_size=2048
70
+ ).private_bytes(encoding=serialization.Encoding.PEM,
71
+ format=serialization.PrivateFormat.PKCS8,
72
+ encryption_algorithm=serialization.NoEncryption())
73
+
74
+
75
+ token_data = {
76
+ "exp": int((datetime.datetime.now() - datetime.timedelta(days=1)).timestamp())
77
+ }
78
+
79
+ token = jwt.encode(token_data, private_key.decode(), algorithm="RS256").decode("utf-8")
80
+ assert is_jwt_expired(token) == True
81
+
82
+ token_data = {
83
+ "exp": int((datetime.datetime.now() + datetime.timedelta(days=1)).timestamp())
84
+ }
85
+
86
+ token = jwt.encode(token_data, private_key.decode(), algorithm="RS256").decode("utf-8")
87
+ assert is_jwt_expired(token) == False
88
+
89
+ def test_login_using_env_vars(self, mocker, mock_expanduser):
90
+ creds = json.dumps({"token": {"refresh_token": "some data"}})
91
+ mocker.patch.dict(os.environ, {PYNT_CREDENTIALS: creds})
92
+ assert should_login() == False
93
+
94
+ os.environ[PYNT_CREDENTIALS] = "some bad credentials"
95
+ with pytest.raises(InvalidTokenInEnvVarsException):
96
+ should_login()
@@ -0,0 +1,24 @@
1
+ import tempfile
2
+ import pytest
3
+ import shutil
4
+
5
+ import sys
6
+ sys.path.append("../")
7
+
8
+ @pytest.fixture
9
+ def mock_expanduser( mocker):
10
+ dir = tempfile.mkdtemp()
11
+ try:
12
+ mocker.patch("os.path.expanduser", return_value=dir)
13
+ yield
14
+ finally:
15
+ shutil.rmtree(dir)
16
+
17
+ @pytest.fixture
18
+ def mock_sleep( mocker):
19
+ try:
20
+ mocker.patch("time.sleep", return_value=None)
21
+ yield
22
+ finally:
23
+ pass
24
+
@@ -0,0 +1,11 @@
1
+ import tempfile
2
+ import shutil
3
+ import pytest
4
+
5
+ from pyntcli.store import CredStore
6
+
7
+ class TestCredStore():
8
+ def test_get_credentials(self, mock_expanduser):
9
+ c = CredStore()
10
+ c.put("data", "value")
11
+ assert c.get("data") == "value"