pyntcli 0.1.29__py3-none-any.whl → 0.1.31__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.
- build/lib/build/lib/build/lib/pyntcli/__init__.py +1 -0
- build/lib/build/lib/build/lib/pyntcli/analytics/__init__.py +0 -0
- build/lib/build/lib/build/lib/pyntcli/analytics/send.py +70 -0
- build/lib/build/lib/build/lib/pyntcli/auth/__init__.py +0 -0
- build/lib/build/lib/build/lib/pyntcli/auth/login.py +156 -0
- build/lib/build/lib/build/lib/pyntcli/commands/__init__.py +0 -0
- build/lib/build/lib/build/lib/pyntcli/commands/har.py +68 -0
- build/lib/build/lib/build/lib/pyntcli/commands/id_command.py +28 -0
- build/lib/build/lib/build/lib/pyntcli/commands/newman.py +85 -0
- build/lib/build/lib/build/lib/pyntcli/commands/postman.py +58 -0
- build/lib/build/lib/build/lib/pyntcli/commands/proxy.py +145 -0
- build/lib/build/lib/build/lib/pyntcli/commands/pynt_cmd.py +47 -0
- build/lib/build/lib/build/lib/pyntcli/commands/root.py +57 -0
- build/lib/build/lib/build/lib/pyntcli/commands/sub_command.py +15 -0
- build/lib/build/lib/build/lib/pyntcli/commands/util.py +26 -0
- build/lib/build/lib/build/lib/pyntcli/main.py +77 -0
- build/lib/build/lib/build/lib/pyntcli/pynt_docker/__init__.py +1 -0
- build/lib/build/lib/build/lib/pyntcli/pynt_docker/pynt_container.py +178 -0
- build/lib/build/lib/build/lib/pyntcli/store/__init__.py +1 -0
- build/lib/build/lib/build/lib/pyntcli/store/json_connector.py +23 -0
- build/lib/build/lib/build/lib/pyntcli/store/store.py +55 -0
- build/lib/build/lib/build/lib/pyntcli/store/store_connector.py +17 -0
- build/lib/build/lib/build/lib/pyntcli/ui/__init__.py +0 -0
- build/lib/build/lib/build/lib/pyntcli/ui/progress.py +31 -0
- build/lib/build/lib/build/lib/pyntcli/ui/ui_thread.py +168 -0
- build/lib/build/lib/build/lib/tests/auth/test_login.py +96 -0
- build/lib/build/lib/build/lib/tests/conftest.py +24 -0
- build/lib/build/lib/build/lib/tests/store/test_cred_store.py +11 -0
- build/lib/build/lib/pyntcli/__init__.py +1 -0
- build/lib/build/lib/pyntcli/analytics/__init__.py +0 -0
- build/lib/build/lib/pyntcli/analytics/send.py +70 -0
- build/lib/build/lib/pyntcli/auth/__init__.py +0 -0
- build/lib/build/lib/pyntcli/auth/login.py +156 -0
- build/lib/build/lib/pyntcli/commands/__init__.py +0 -0
- build/lib/build/lib/pyntcli/commands/har.py +68 -0
- build/lib/build/lib/pyntcli/commands/id_command.py +28 -0
- build/lib/build/lib/pyntcli/commands/newman.py +85 -0
- build/lib/build/lib/pyntcli/commands/postman.py +58 -0
- build/lib/build/lib/pyntcli/commands/proxy.py +147 -0
- build/lib/build/lib/pyntcli/commands/pynt_cmd.py +47 -0
- build/lib/build/lib/pyntcli/commands/root.py +57 -0
- build/lib/build/lib/pyntcli/commands/sub_command.py +15 -0
- build/lib/build/lib/pyntcli/commands/util.py +26 -0
- build/lib/build/lib/pyntcli/main.py +77 -0
- build/lib/build/lib/pyntcli/pynt_docker/__init__.py +1 -0
- build/lib/build/lib/pyntcli/pynt_docker/pynt_container.py +178 -0
- build/lib/build/lib/pyntcli/store/__init__.py +1 -0
- build/lib/build/lib/pyntcli/store/json_connector.py +23 -0
- build/lib/build/lib/pyntcli/store/store.py +55 -0
- build/lib/build/lib/pyntcli/store/store_connector.py +17 -0
- build/lib/build/lib/pyntcli/ui/__init__.py +0 -0
- build/lib/build/lib/pyntcli/ui/progress.py +31 -0
- build/lib/build/lib/pyntcli/ui/ui_thread.py +168 -0
- build/lib/build/lib/tests/auth/test_login.py +96 -0
- build/lib/build/lib/tests/conftest.py +24 -0
- build/lib/build/lib/tests/store/test_cred_store.py +11 -0
- build/lib/pyntcli/__init__.py +1 -1
- build/lib/pyntcli/commands/proxy.py +1 -0
- pyntcli/__init__.py +1 -1
- pyntcli/commands/proxy.py +0 -2
- {pyntcli-0.1.29.dist-info → pyntcli-0.1.31.dist-info}/METADATA +1 -1
- pyntcli-0.1.31.dist-info/RECORD +117 -0
- pyntcli-0.1.29.dist-info/RECORD +0 -61
- {pyntcli-0.1.29.dist-info → pyntcli-0.1.31.dist-info}/WHEEL +0 -0
- {pyntcli-0.1.29.dist-info → pyntcli-0.1.31.dist-info}/entry_points.txt +0 -0
- {pyntcli-0.1.29.dist-info → pyntcli-0.1.31.dist-info}/top_level.txt +0 -0
|
@@ -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 @@
|
|
|
1
|
+
__version__ = "0.1.31"
|
|
File without changes
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import time
|
|
3
|
+
import platform
|
|
4
|
+
|
|
5
|
+
from pyntcli import __version__
|
|
6
|
+
|
|
7
|
+
PYNT_DEFAULT_USER_ID = "d9e3b82b-2900-43bf-8c8f-7ffe2f0cda36"
|
|
8
|
+
MIXPANEL_TOKEN = "05c26edb86084bbbb803eed6818cd8aa"
|
|
9
|
+
MIXPANEL_URL = "https://api-eu.mixpanel.com/track?ip=1"
|
|
10
|
+
|
|
11
|
+
def stop():
|
|
12
|
+
if not AnalyticsSender._instance:
|
|
13
|
+
return
|
|
14
|
+
AnalyticsSender.instance().done()
|
|
15
|
+
|
|
16
|
+
def emit(event, properties=None):
|
|
17
|
+
AnalyticsSender.instance().emit(event, properties)
|
|
18
|
+
|
|
19
|
+
def set_user_id(user_id):
|
|
20
|
+
AnalyticsSender.instance().set_user_id(user_id)
|
|
21
|
+
|
|
22
|
+
CLI_START = "cli_start"
|
|
23
|
+
LOGIN_START = "cli_login_start"
|
|
24
|
+
LOGIN_DONE = "cli_login_done"
|
|
25
|
+
ERROR = "error"
|
|
26
|
+
|
|
27
|
+
class AnalyticsSender():
|
|
28
|
+
_instance = None
|
|
29
|
+
|
|
30
|
+
def __init__(self, user_id=PYNT_DEFAULT_USER_ID) -> None:
|
|
31
|
+
self.user_id = user_id
|
|
32
|
+
self.version = __version__
|
|
33
|
+
self.events = []
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def instance():
|
|
37
|
+
if not AnalyticsSender._instance:
|
|
38
|
+
AnalyticsSender._instance = AnalyticsSender()
|
|
39
|
+
|
|
40
|
+
return AnalyticsSender._instance
|
|
41
|
+
|
|
42
|
+
def base_event(self, event_type):
|
|
43
|
+
return {
|
|
44
|
+
"event": event_type,
|
|
45
|
+
"properties": {
|
|
46
|
+
"time": time.time(),
|
|
47
|
+
"distinct_id": self.user_id,
|
|
48
|
+
"$os": platform.platform(),
|
|
49
|
+
"cli_version": self.version,
|
|
50
|
+
"token": MIXPANEL_TOKEN
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
def emit(self, event, properties):
|
|
55
|
+
base_event = self.base_event(event)
|
|
56
|
+
|
|
57
|
+
if properties:
|
|
58
|
+
for k,v in properties.items():
|
|
59
|
+
base_event["properties"][k] = v
|
|
60
|
+
|
|
61
|
+
self.events.append(base_event)
|
|
62
|
+
|
|
63
|
+
def set_user_id(self, user_id):
|
|
64
|
+
self.user_id = user_id
|
|
65
|
+
for i, _ in enumerate(self.events):
|
|
66
|
+
self.events[i]["properties"]["distinct_id"] = user_id
|
|
67
|
+
|
|
68
|
+
def done(self):
|
|
69
|
+
requests.post(MIXPANEL_URL, json=self.events)
|
|
70
|
+
self.events = []
|
|
File without changes
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
from base64 import b64decode
|
|
2
|
+
import requests
|
|
3
|
+
import webbrowser
|
|
4
|
+
import uuid
|
|
5
|
+
import urllib.parse
|
|
6
|
+
import datetime
|
|
7
|
+
import time
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
from pyntcli.ui import ui_thread
|
|
12
|
+
from pyntcli.store import CredStore
|
|
13
|
+
|
|
14
|
+
class LoginException(Exception):
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
class Timeout(LoginException):
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
class InvalidTokenInEnvVarsException(LoginException):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
PYNT_CREDENTIALS = "PYNT_CREDENTIALS"
|
|
24
|
+
|
|
25
|
+
class Login():
|
|
26
|
+
def __init__(self) -> None:
|
|
27
|
+
self.delay = 5
|
|
28
|
+
self.base_authorization_url = "https://pynt.io/login?"
|
|
29
|
+
self.poll_url = "https://n592meacjj.execute-api.us-east-1.amazonaws.com/default/cli_validate_login"
|
|
30
|
+
self.login_wait_period = (60 *3) #3 minutes
|
|
31
|
+
|
|
32
|
+
def create_login_request(self):
|
|
33
|
+
request_id = uuid.uuid4()
|
|
34
|
+
request_url = self.base_authorization_url + urllib.parse.urlencode({"request_id": request_id, "utm_source": "cli"})
|
|
35
|
+
webbrowser.open(request_url)
|
|
36
|
+
|
|
37
|
+
ui_thread.print(ui_thread.PrinterText("To continue, you need to log in to your account.")\
|
|
38
|
+
.with_line("You will now be redirected to the login page.") \
|
|
39
|
+
.with_line("") \
|
|
40
|
+
.with_line("If you are not automatically redirected, please click on the link provided below (or copy to your web browser)") \
|
|
41
|
+
.with_line(request_url))
|
|
42
|
+
return request_id
|
|
43
|
+
|
|
44
|
+
def get_token_using_request_id(self, request_id):
|
|
45
|
+
with ui_thread.spinner("Waiting...", "point"):
|
|
46
|
+
start = time.time()
|
|
47
|
+
while start + self.login_wait_period > time.time():
|
|
48
|
+
response = requests.get(self.poll_url, params={"request_id": request_id})
|
|
49
|
+
if response.status_code == 200:
|
|
50
|
+
return response.json()
|
|
51
|
+
time.sleep(self.delay)
|
|
52
|
+
raise Timeout()
|
|
53
|
+
|
|
54
|
+
def login(self):
|
|
55
|
+
id = self.create_login_request()
|
|
56
|
+
token = self.get_token_using_request_id(id)
|
|
57
|
+
with CredStore() as store:
|
|
58
|
+
store.put("token", token)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def refresh_request(refresh_token):
|
|
62
|
+
return requests.post("https://auth.pynt.io/default/refresh", json={"refresh_token": refresh_token})
|
|
63
|
+
|
|
64
|
+
def refresh_token():
|
|
65
|
+
token = None
|
|
66
|
+
with CredStore() as store:
|
|
67
|
+
token = store.get("token")
|
|
68
|
+
|
|
69
|
+
if not token:
|
|
70
|
+
Login().login()
|
|
71
|
+
|
|
72
|
+
access_token = token.get("access_token")
|
|
73
|
+
if access_token and not is_jwt_expired(access_token):
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
refresh = token.get("refresh_token", None)
|
|
77
|
+
if not refresh:
|
|
78
|
+
Login().login()
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
refresh_response = refresh_request(refresh)
|
|
82
|
+
if refresh_response.status_code != 200:
|
|
83
|
+
Login().login()
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
with CredStore() as store:
|
|
87
|
+
token["access_token"] = refresh_response.json()["token"]
|
|
88
|
+
store.put("token", token)
|
|
89
|
+
|
|
90
|
+
def decode_jwt(jwt_token):
|
|
91
|
+
splited = jwt_token.split(".")
|
|
92
|
+
if len(splited) != 3:
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
return json.loads(b64decode(splited[1] + '=' * (-len(splited[1]) % 4)))
|
|
96
|
+
|
|
97
|
+
def user_id():
|
|
98
|
+
with CredStore() as store:
|
|
99
|
+
token = store.get("token")
|
|
100
|
+
if not token:
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
decoded = decode_jwt(token["access_token"])
|
|
104
|
+
if not decoded:
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
return decoded.get("sub", None)
|
|
108
|
+
|
|
109
|
+
return None
|
|
110
|
+
|
|
111
|
+
def is_jwt_expired(jwt_token):
|
|
112
|
+
decoded = decode_jwt(jwt_token)
|
|
113
|
+
if not decoded:
|
|
114
|
+
return True
|
|
115
|
+
|
|
116
|
+
exp = decoded.get("exp", None)
|
|
117
|
+
if not exp:
|
|
118
|
+
return True
|
|
119
|
+
|
|
120
|
+
return datetime.datetime.fromtimestamp(exp) < datetime.datetime.now() + datetime.timedelta(minutes=1)
|
|
121
|
+
|
|
122
|
+
def validate_creds_structure(data):
|
|
123
|
+
try:
|
|
124
|
+
creds = json.loads(data.replace("\n", ""))
|
|
125
|
+
token = creds.get("token", None)
|
|
126
|
+
if not token:
|
|
127
|
+
raise InvalidTokenInEnvVarsException()
|
|
128
|
+
if not isinstance(token, dict):
|
|
129
|
+
raise InvalidTokenInEnvVarsException()
|
|
130
|
+
|
|
131
|
+
refresh_token = token.get("refresh_token", None)
|
|
132
|
+
if not refresh_token:
|
|
133
|
+
raise InvalidTokenInEnvVarsException()
|
|
134
|
+
|
|
135
|
+
return token
|
|
136
|
+
except json.JSONDecodeError:
|
|
137
|
+
raise InvalidTokenInEnvVarsException()
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def should_login():
|
|
141
|
+
env_creds = os.environ.get(PYNT_CREDENTIALS, None)
|
|
142
|
+
if env_creds:
|
|
143
|
+
validated_creds = validate_creds_structure(env_creds)
|
|
144
|
+
with CredStore() as store:
|
|
145
|
+
store.put("token", validated_creds)
|
|
146
|
+
|
|
147
|
+
with CredStore() as store:
|
|
148
|
+
token = store.get("token")
|
|
149
|
+
|
|
150
|
+
if not token or token == store.connector.default_value:
|
|
151
|
+
return True
|
|
152
|
+
|
|
153
|
+
if not token.get("refresh_token"):
|
|
154
|
+
return True
|
|
155
|
+
|
|
156
|
+
return False
|
|
File without changes
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import time
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from pyntcli.store.store import CredStore
|
|
6
|
+
from pyntcli.pynt_docker import pynt_container
|
|
7
|
+
from pyntcli.ui import ui_thread
|
|
8
|
+
from pyntcli.ui.progress import connect_progress_ws, wrap_ws_progress
|
|
9
|
+
from pyntcli.commands import sub_command, util
|
|
10
|
+
|
|
11
|
+
def har_usage():
|
|
12
|
+
return ui_thread.PrinterText("Integration with static har file testing") \
|
|
13
|
+
.with_line("") \
|
|
14
|
+
.with_line("Usage:", style=ui_thread.PrinterText.HEADER) \
|
|
15
|
+
.with_line("\tpynt har [OPTIONS]") \
|
|
16
|
+
.with_line("") \
|
|
17
|
+
.with_line("Options:", style=ui_thread.PrinterText.HEADER) \
|
|
18
|
+
.with_line("\t--har - Path to har file")
|
|
19
|
+
|
|
20
|
+
class HarSubCommand(sub_command.PyntSubCommand):
|
|
21
|
+
def __init__(self, name) -> None:
|
|
22
|
+
super().__init__(name)
|
|
23
|
+
|
|
24
|
+
def usage(self, *args):
|
|
25
|
+
ui_thread.print(har_usage())
|
|
26
|
+
|
|
27
|
+
def add_cmd(self, parent: argparse._SubParsersAction) -> argparse.ArgumentParser:
|
|
28
|
+
har_cmd = parent.add_parser(self.name)
|
|
29
|
+
har_cmd.add_argument("--har", type=str, required=True)
|
|
30
|
+
har_cmd.print_usage = self.usage
|
|
31
|
+
har_cmd.print_help = self.usage
|
|
32
|
+
return har_cmd
|
|
33
|
+
|
|
34
|
+
def run_cmd(self, args: argparse.Namespace):
|
|
35
|
+
port = str(util.find_open_port())
|
|
36
|
+
docker_type , docker_arguments = pynt_container.get_container_with_arguments(pynt_container.PyntDockerPort(src=port, dest=port, name="--port"))
|
|
37
|
+
mounts = []
|
|
38
|
+
|
|
39
|
+
if not os.path.isfile(args.har):
|
|
40
|
+
ui_thread.print(ui_thread.PrinterText("Could not find the provided har path, please provide with a valid har path", ui_thread.PrinterText.WARNING))
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
har_name = os.path.basename(args.har)
|
|
44
|
+
docker_arguments += ["--har", har_name]
|
|
45
|
+
mounts.append(pynt_container.create_mount(os.path.abspath(args.har), "/etc/pynt/{}".format(har_name)))
|
|
46
|
+
|
|
47
|
+
mounts.append(pynt_container.create_mount(CredStore().get_path(), "/app/creds.json"))
|
|
48
|
+
|
|
49
|
+
if "insecure" in args and args.insecure:
|
|
50
|
+
docker_arguments.append("--insecure")
|
|
51
|
+
|
|
52
|
+
if "dev_flags" in args:
|
|
53
|
+
docker_arguments += args.dev_flags.split(" ")
|
|
54
|
+
|
|
55
|
+
har_docker = pynt_container.PyntContainer(image_name=pynt_container.PYNT_DOCKER_IMAGE,
|
|
56
|
+
tag="har-latest",
|
|
57
|
+
detach=True,
|
|
58
|
+
mounts=mounts,
|
|
59
|
+
args=docker_arguments)
|
|
60
|
+
|
|
61
|
+
har_docker.run(docker_type)
|
|
62
|
+
|
|
63
|
+
util.wait_for_healthcheck("http://localhost:{}".format(port))
|
|
64
|
+
ui_thread.print_generator(ui_thread.AnsiText.wrap_gen(har_docker.stdout))
|
|
65
|
+
|
|
66
|
+
with ui_thread.progress(wrap_ws_progress(connect_progress_ws("ws://localhost:{}/progress".format(port))), "scan in progress..."):
|
|
67
|
+
while har_docker.is_alive():
|
|
68
|
+
time.sleep(1)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from pyntcli.store.store import CredStore
|
|
4
|
+
from pyntcli.commands import sub_command
|
|
5
|
+
from pyntcli.ui import ui_thread
|
|
6
|
+
|
|
7
|
+
def pyntid_usage():
|
|
8
|
+
return ui_thread.PrinterText("View your pynt-id to use when running pynt in CI pipeline") \
|
|
9
|
+
.with_line("") \
|
|
10
|
+
.with_line("Usage:",style=ui_thread.PrinterText.HEADER) \
|
|
11
|
+
.with_line("\tpynt pynt-id")
|
|
12
|
+
|
|
13
|
+
class PyntShowIdCommand(sub_command.PyntSubCommand):
|
|
14
|
+
def __init__(self, name) -> None:
|
|
15
|
+
super().__init__(name)
|
|
16
|
+
|
|
17
|
+
def usage(self, *args):
|
|
18
|
+
ui_thread.print(pyntid_usage())
|
|
19
|
+
|
|
20
|
+
def add_cmd(self, parent: argparse._SubParsersAction) -> argparse.ArgumentParser:
|
|
21
|
+
cmd = parent.add_parser(self.name)
|
|
22
|
+
cmd.print_usage = self.usage
|
|
23
|
+
cmd.print_help = self.usage
|
|
24
|
+
return cmd
|
|
25
|
+
|
|
26
|
+
def run_cmd(self, args: argparse.Namespace):
|
|
27
|
+
creds_path = CredStore().get_path()
|
|
28
|
+
ui_thread.print(open(creds_path, "r").read())
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import time
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from pyntcli.store.store import CredStore
|
|
6
|
+
from pyntcli.pynt_docker import pynt_container
|
|
7
|
+
from pyntcli.commands import sub_command, util
|
|
8
|
+
from pyntcli.ui import ui_thread
|
|
9
|
+
from pyntcli.ui.progress import connect_progress_ws, wrap_ws_progress
|
|
10
|
+
|
|
11
|
+
def newman_usage():
|
|
12
|
+
return ui_thread.PrinterText("Integration with newman, run scan using postman collection from the CLI") \
|
|
13
|
+
.with_line("") \
|
|
14
|
+
.with_line("Usage:", style=ui_thread.PrinterText.HEADER) \
|
|
15
|
+
.with_line("\tpynt newman [OPTIONS]") \
|
|
16
|
+
.with_line("") \
|
|
17
|
+
.with_line("Options:", style=ui_thread.PrinterText.HEADER) \
|
|
18
|
+
.with_line("\t--collection - Postman collection file name") \
|
|
19
|
+
.with_line("\t--environment - Postman environment file name") \
|
|
20
|
+
.with_line("\t--reporters output results to json")
|
|
21
|
+
|
|
22
|
+
class NewmanSubCommand(sub_command.PyntSubCommand):
|
|
23
|
+
def __init__(self, name) -> None:
|
|
24
|
+
super().__init__(name)
|
|
25
|
+
|
|
26
|
+
def usage(self, *args):
|
|
27
|
+
ui_thread.print(newman_usage())
|
|
28
|
+
|
|
29
|
+
def add_cmd(self, parent: argparse._SubParsersAction) -> argparse.ArgumentParser:
|
|
30
|
+
newman_cmd = parent.add_parser(self.name)
|
|
31
|
+
newman_cmd.add_argument("--collection", type=str, required=True)
|
|
32
|
+
newman_cmd.add_argument("--environment", type=str, required=False)
|
|
33
|
+
newman_cmd.add_argument("--reporters", type=bool, required=False)
|
|
34
|
+
newman_cmd.print_usage = self.usage
|
|
35
|
+
newman_cmd.print_help = self.usage
|
|
36
|
+
return newman_cmd
|
|
37
|
+
|
|
38
|
+
def run_cmd(self, args: argparse.Namespace):
|
|
39
|
+
port = str(util.find_open_port())
|
|
40
|
+
docker_type , docker_arguments = pynt_container.get_container_with_arguments(pynt_container.PyntDockerPort(src=port, dest=port, name="--port"))
|
|
41
|
+
mounts = []
|
|
42
|
+
|
|
43
|
+
if not os.path.isfile(args.collection):
|
|
44
|
+
ui_thread.print(ui_thread.PrinterText("Could not find the provided collection path, please provide with a valid collection path", ui_thread.PrinterText.WARNING))
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
collection_name = os.path.basename(args.collection)
|
|
48
|
+
docker_arguments += ["-c", collection_name]
|
|
49
|
+
mounts.append(pynt_container.create_mount(os.path.abspath(args.collection), "/etc/pynt/{}".format(collection_name)))
|
|
50
|
+
|
|
51
|
+
if "environment" in args and args.environment:
|
|
52
|
+
if not os.path.isfile(args.environment):
|
|
53
|
+
ui_thread.print(ui_thread.PrinterText("Could not find the provided environment path, please provide with a valid environment path", ui_thread.PrinterText.WARNING))
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
env_name = os.path.basename(args.environment)
|
|
57
|
+
docker_arguments += ["-e", env_name]
|
|
58
|
+
mounts.append(pynt_container.create_mount(os.path.abspath(args.environment), "/etc/pynt/{}".format(env_name)))
|
|
59
|
+
|
|
60
|
+
mounts.append(pynt_container.create_mount(CredStore().get_path(), "/app/creds.json"))
|
|
61
|
+
|
|
62
|
+
if "reporters" in args and args.reporters:
|
|
63
|
+
open(os.path.join(os.getcwd(), "results.json"), "w").close()
|
|
64
|
+
mounts.append(pynt_container.create_mount(os.path.join(os.getcwd(), "results.json"), "/etc/pynt/results/results.json"))
|
|
65
|
+
docker_arguments.append("--reporters")
|
|
66
|
+
|
|
67
|
+
if "insecure" in args and args.insecure:
|
|
68
|
+
docker_arguments.append("--insecure")
|
|
69
|
+
|
|
70
|
+
if "dev_flags" in args:
|
|
71
|
+
docker_arguments += args.dev_flags.split(" ")
|
|
72
|
+
|
|
73
|
+
newman_docker = pynt_container.PyntContainer(image_name=pynt_container.PYNT_DOCKER_IMAGE,
|
|
74
|
+
tag="latest",
|
|
75
|
+
detach=True,
|
|
76
|
+
mounts=mounts,
|
|
77
|
+
args=docker_arguments)
|
|
78
|
+
|
|
79
|
+
newman_docker.run(docker_type)
|
|
80
|
+
util.wait_for_healthcheck("http://localhost:{}".format(port))
|
|
81
|
+
ui_thread.print_generator(ui_thread.AnsiText.wrap_gen(newman_docker.stdout))
|
|
82
|
+
|
|
83
|
+
with ui_thread.progress(wrap_ws_progress(connect_progress_ws("ws://localhost:{}/progress".format(port))), "scan in progress..."):
|
|
84
|
+
while newman_docker.is_alive():
|
|
85
|
+
time.sleep(1)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
from pyntcli.store.store import CredStore
|
|
5
|
+
from . import sub_command, util
|
|
6
|
+
from pyntcli.pynt_docker import pynt_container
|
|
7
|
+
from pyntcli.ui import ui_thread
|
|
8
|
+
|
|
9
|
+
def postman_usage():
|
|
10
|
+
return ui_thread.PrinterText("Integration with postman, run scan from pynt postman collection") \
|
|
11
|
+
.with_line("") \
|
|
12
|
+
.with_line("Usage:",style=ui_thread.PrinterText.HEADER) \
|
|
13
|
+
.with_line("\tpynt postman [OPTIONS]") \
|
|
14
|
+
.with_line("") \
|
|
15
|
+
.with_line("Options:",style=ui_thread.PrinterText.HEADER) \
|
|
16
|
+
.with_line("\t--port - set the port pynt will listen to (DEFAULT: 5001)") \
|
|
17
|
+
.with_line("\t--insecure - use when target uses self signed certificates")
|
|
18
|
+
|
|
19
|
+
class PostmanSubCommand(sub_command.PyntSubCommand):
|
|
20
|
+
def __init__(self, name) -> None:
|
|
21
|
+
super().__init__(name)
|
|
22
|
+
|
|
23
|
+
def usage(self, *args):
|
|
24
|
+
ui_thread.print(postman_usage())
|
|
25
|
+
|
|
26
|
+
def add_cmd(self, parent_command: argparse._SubParsersAction) -> argparse.ArgumentParser:
|
|
27
|
+
postman_cmd = parent_command.add_parser(self.name)
|
|
28
|
+
postman_cmd.add_argument("--port", "-p", help="set the port pynt will listen to (DEFAULT: 5001)", type=int, default=5001)
|
|
29
|
+
postman_cmd.print_usage = self.usage
|
|
30
|
+
postman_cmd.print_help = self.usage
|
|
31
|
+
return postman_cmd
|
|
32
|
+
|
|
33
|
+
def run_cmd(self, args: argparse.Namespace):
|
|
34
|
+
docker_type, docker_arguments = pynt_container.get_container_with_arguments(pynt_container.PyntDockerPort("5001", args.port, "--port"))
|
|
35
|
+
|
|
36
|
+
creds_path = CredStore().get_path()
|
|
37
|
+
|
|
38
|
+
if "insecure" in args and args.insecure:
|
|
39
|
+
docker_arguments.append("--insecure")
|
|
40
|
+
|
|
41
|
+
if "dev_flags" in args:
|
|
42
|
+
docker_arguments += args.dev_flags.split(" ")
|
|
43
|
+
|
|
44
|
+
if util.is_port_in_use(args.port):
|
|
45
|
+
ui_thread.print(ui_thread.PrinterText("Port: {} already in use, please use a different one".format(args.port), ui_thread.PrinterText.WARNING))
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
postman_docker = pynt_container.PyntContainer(image_name=pynt_container.PYNT_DOCKER_IMAGE,
|
|
49
|
+
tag="postman-latest",
|
|
50
|
+
mounts=[pynt_container.create_mount(creds_path, "/app/creds.json")],
|
|
51
|
+
detach=True,
|
|
52
|
+
args=docker_arguments)
|
|
53
|
+
|
|
54
|
+
postman_docker.run(docker_type)
|
|
55
|
+
ui_thread.print_generator(postman_docker.stdout)
|
|
56
|
+
|
|
57
|
+
while postman_docker.is_alive():
|
|
58
|
+
time.sleep(1)
|