borecli 1.0.0__tar.gz → 1.4.1__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.
- {borecli-1.0.0/borecli.egg-info → borecli-1.4.1}/PKG-INFO +1 -1
- borecli-1.4.1/bore/auth.py +176 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/cli.py +41 -6
- {borecli-1.0.0 → borecli-1.4.1/borecli.egg-info}/PKG-INFO +1 -1
- borecli-1.4.1/borecli.egg-info/top_level.txt +2 -0
- {borecli-1.0.0 → borecli-1.4.1}/pyproject.toml +1 -1
- borecli-1.0.0/bore/auth.py +0 -104
- borecli-1.0.0/borecli.egg-info/top_level.txt +0 -1
- {borecli-1.0.0 → borecli-1.4.1}/LICENSE +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/README.md +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/__init__.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/config.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/frames.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/handlers/__init__.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/handlers/error_pages.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/handlers/http_handler.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/handlers/websocket_handler.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/parse_duration.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/protocol.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/tunnel/__init__.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/tunnel/client.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/tunnel/dispatcher.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/tunnel/heartbeat.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/tunnel.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/websocket/__init__.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/websocket/connection.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/websocket/frames.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/websocket/manager.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/websocket/proxy.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/bore/websocket_client.py +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/borecli.egg-info/SOURCES.txt +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/borecli.egg-info/dependency_links.txt +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/borecli.egg-info/entry_points.txt +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/borecli.egg-info/requires.txt +0 -0
- {borecli-1.0.0 → borecli-1.4.1}/setup.cfg +0 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
from .config import (
|
|
4
|
+
DEFAULT_TOKEN_LIFETIME,
|
|
5
|
+
clear_credentials,
|
|
6
|
+
save_credentials,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
API_URL = "https://api.borehook.com"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BoreAuthError(Exception):
|
|
14
|
+
"""Raised when BoreHook authentication fails."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _get_error_message(response):
|
|
18
|
+
"""
|
|
19
|
+
Extract a safe, user-facing error message from an API response.
|
|
20
|
+
|
|
21
|
+
This deliberately does not include the API URL or raw HTTP exception.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
data = response.json()
|
|
26
|
+
except ValueError:
|
|
27
|
+
data = {}
|
|
28
|
+
|
|
29
|
+
if isinstance(data, dict):
|
|
30
|
+
message = (
|
|
31
|
+
data.get("message")
|
|
32
|
+
or data.get("detail")
|
|
33
|
+
or data.get("error")
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if isinstance(message, str) and message.strip():
|
|
37
|
+
return message.strip()
|
|
38
|
+
|
|
39
|
+
if response.status_code in {401, 403}:
|
|
40
|
+
return (
|
|
41
|
+
"Login failed. Your email/password may be incorrect, "
|
|
42
|
+
"or your account may be suspended or unverified."
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if response.status_code == 429:
|
|
46
|
+
return "Too many login attempts. Please try again later."
|
|
47
|
+
|
|
48
|
+
if response.status_code >= 500:
|
|
49
|
+
return (
|
|
50
|
+
"BoreHook is temporarily unavailable. "
|
|
51
|
+
"Please try again later."
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return "Login failed. Please check your details and try again."
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def login(
|
|
58
|
+
email,
|
|
59
|
+
password,
|
|
60
|
+
lifetime=DEFAULT_TOKEN_LIFETIME,
|
|
61
|
+
):
|
|
62
|
+
"""
|
|
63
|
+
Authenticate a user and save the session locally.
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
BoreAuthError: If authentication fails.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
response = requests.post(
|
|
71
|
+
f"{API_URL}/api/accounts/login/",
|
|
72
|
+
json={
|
|
73
|
+
"email": email,
|
|
74
|
+
"password": password,
|
|
75
|
+
},
|
|
76
|
+
timeout=30,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
except requests.Timeout as exc:
|
|
80
|
+
raise BoreAuthError(
|
|
81
|
+
"Unable to reach BoreHook. The request timed out. "
|
|
82
|
+
"Please try again."
|
|
83
|
+
) from exc
|
|
84
|
+
|
|
85
|
+
except requests.ConnectionError as exc:
|
|
86
|
+
raise BoreAuthError(
|
|
87
|
+
"Unable to connect to BoreHook. "
|
|
88
|
+
"Please check your internet connection and try again."
|
|
89
|
+
) from exc
|
|
90
|
+
|
|
91
|
+
except requests.RequestException as exc:
|
|
92
|
+
raise BoreAuthError(
|
|
93
|
+
"An unexpected network error occurred while logging in."
|
|
94
|
+
) from exc
|
|
95
|
+
|
|
96
|
+
if not response.ok:
|
|
97
|
+
raise BoreAuthError(_get_error_message(response))
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
data = response.json()
|
|
101
|
+
except ValueError as exc:
|
|
102
|
+
raise BoreAuthError(
|
|
103
|
+
"BoreHook returned an invalid login response."
|
|
104
|
+
) from exc
|
|
105
|
+
|
|
106
|
+
token = data.get("token")
|
|
107
|
+
|
|
108
|
+
if not token:
|
|
109
|
+
raise BoreAuthError(
|
|
110
|
+
"Login succeeded but no authentication token was returned."
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
save_credentials(
|
|
114
|
+
email=email,
|
|
115
|
+
token=token,
|
|
116
|
+
lifetime=lifetime,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
"email": email,
|
|
121
|
+
"token": token,
|
|
122
|
+
"lifetime": lifetime,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def verify_token(token):
|
|
127
|
+
"""
|
|
128
|
+
Verify token validity against the API.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
True if the token is valid.
|
|
132
|
+
False otherwise.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
response = requests.get(
|
|
137
|
+
f"{API_URL}/api/accounts/me/",
|
|
138
|
+
headers={
|
|
139
|
+
"Authorization": f"Token {token}",
|
|
140
|
+
},
|
|
141
|
+
timeout=30,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
return response.status_code == 200
|
|
145
|
+
|
|
146
|
+
except requests.RequestException:
|
|
147
|
+
return False
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def logout():
|
|
151
|
+
"""
|
|
152
|
+
Remove locally stored credentials.
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
clear_credentials()
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def refresh_session(email, token):
|
|
159
|
+
"""
|
|
160
|
+
Refresh the saved session timestamp.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
save_credentials(
|
|
164
|
+
email=email,
|
|
165
|
+
token=token,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def get_authenticated_headers(token):
|
|
170
|
+
"""
|
|
171
|
+
Return authorization headers for API requests.
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
"Authorization": f"Token {token}",
|
|
176
|
+
}
|
|
@@ -21,6 +21,8 @@ from bore.parse_duration import parse_duration
|
|
|
21
21
|
from bore.auth import (
|
|
22
22
|
login,
|
|
23
23
|
logout,
|
|
24
|
+
BoreAuthError,
|
|
25
|
+
|
|
24
26
|
)
|
|
25
27
|
|
|
26
28
|
from bore.tunnel.client import TunnelClient
|
|
@@ -54,7 +56,6 @@ def cli():
|
|
|
54
56
|
pass
|
|
55
57
|
|
|
56
58
|
|
|
57
|
-
|
|
58
59
|
@cli.command(name="login")
|
|
59
60
|
@click.option("--email", prompt=True)
|
|
60
61
|
@click.option(
|
|
@@ -76,7 +77,6 @@ def login_cmd(email, password, time):
|
|
|
76
77
|
return
|
|
77
78
|
|
|
78
79
|
try:
|
|
79
|
-
|
|
80
80
|
if time:
|
|
81
81
|
lifetime = parse_duration(time)
|
|
82
82
|
else:
|
|
@@ -92,11 +92,46 @@ def login_cmd(email, password, time):
|
|
|
92
92
|
click.echo(f"Account: {session['email']}")
|
|
93
93
|
|
|
94
94
|
except ValueError as exc:
|
|
95
|
-
click.echo(
|
|
95
|
+
click.echo(
|
|
96
|
+
f"\n❌ Invalid time: {exc}",
|
|
97
|
+
err=True,
|
|
98
|
+
)
|
|
99
|
+
raise click.exceptions.Exit(1)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
except BoreAuthError as exc:
|
|
103
|
+
click.echo(
|
|
104
|
+
f"\n❌ {exc}",
|
|
105
|
+
err=True,
|
|
106
|
+
)
|
|
107
|
+
raise click.exceptions.Exit(1)
|
|
108
|
+
|
|
109
|
+
except Exception:
|
|
110
|
+
# Never print the raw exception because it may contain
|
|
111
|
+
# the API URL, internal paths, or other implementation details.
|
|
112
|
+
click.echo(
|
|
113
|
+
"\n❌ Unable to log in. Please try again later.",
|
|
114
|
+
err=True,
|
|
115
|
+
)
|
|
116
|
+
raise click.exceptions.Exit(1)
|
|
117
|
+
|
|
118
|
+
|
|
96
119
|
|
|
97
|
-
except
|
|
98
|
-
click.echo(
|
|
120
|
+
except BoreAuthError as exc:
|
|
121
|
+
click.echo(
|
|
122
|
+
f"\n❌ {exc}",
|
|
123
|
+
err=True,
|
|
124
|
+
)
|
|
125
|
+
raise click.exceptions.Exit(1)
|
|
99
126
|
|
|
127
|
+
except Exception:
|
|
128
|
+
# Never print the raw exception because it may contain
|
|
129
|
+
# the API URL, internal paths, or other implementation details.
|
|
130
|
+
click.echo(
|
|
131
|
+
"\n❌ Unable to log in. Please try again later.",
|
|
132
|
+
err=True,
|
|
133
|
+
)
|
|
134
|
+
raise click.exceptions.Exit(1)
|
|
100
135
|
|
|
101
136
|
|
|
102
137
|
@cli.command()
|
|
@@ -375,4 +410,4 @@ def connect():
|
|
|
375
410
|
)
|
|
376
411
|
|
|
377
412
|
if __name__ == "__main__":
|
|
378
|
-
cli()
|
|
413
|
+
cli()
|
borecli-1.0.0/bore/auth.py
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
#borecli/bore/auth.py
|
|
2
|
-
import requests
|
|
3
|
-
|
|
4
|
-
from .config import (DEFAULT_TOKEN_LIFETIME, clear_credentials, save_credentials,)
|
|
5
|
-
|
|
6
|
-
API_URL = "https://api.borehook.com"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def login(
|
|
11
|
-
email,
|
|
12
|
-
password,
|
|
13
|
-
lifetime=DEFAULT_TOKEN_LIFETIME,
|
|
14
|
-
):
|
|
15
|
-
"""
|
|
16
|
-
Authenticate a user and save the session locally.
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
response = requests.post(
|
|
20
|
-
f"{API_URL}/api/accounts/login/",
|
|
21
|
-
json={
|
|
22
|
-
"email": email,
|
|
23
|
-
"password": password,
|
|
24
|
-
},
|
|
25
|
-
timeout=30,
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
response.raise_for_status()
|
|
29
|
-
|
|
30
|
-
data = response.json()
|
|
31
|
-
|
|
32
|
-
token = data.get("token")
|
|
33
|
-
|
|
34
|
-
if not token:
|
|
35
|
-
raise Exception(
|
|
36
|
-
"Login succeeded but no token was returned."
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
save_credentials(
|
|
40
|
-
email=email,
|
|
41
|
-
token=token,
|
|
42
|
-
lifetime=lifetime,
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
"email": email,
|
|
47
|
-
"token": token,
|
|
48
|
-
"lifetime": lifetime,
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def verify_token(token):
|
|
53
|
-
"""
|
|
54
|
-
Verify token validity against the API.
|
|
55
|
-
|
|
56
|
-
Returns:
|
|
57
|
-
True if valid.
|
|
58
|
-
False if invalid.
|
|
59
|
-
"""
|
|
60
|
-
|
|
61
|
-
try:
|
|
62
|
-
response = requests.get(
|
|
63
|
-
f"{API_URL}/api/accounts/me/",
|
|
64
|
-
headers={
|
|
65
|
-
"Authorization": (
|
|
66
|
-
f"Token {token}"
|
|
67
|
-
)
|
|
68
|
-
},
|
|
69
|
-
timeout=30,
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
return response.status_code == 200
|
|
73
|
-
|
|
74
|
-
except Exception:
|
|
75
|
-
return False
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def logout():
|
|
79
|
-
"""
|
|
80
|
-
Remove locally stored credentials.
|
|
81
|
-
"""
|
|
82
|
-
|
|
83
|
-
clear_credentials()
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def refresh_session(email, token):
|
|
87
|
-
"""
|
|
88
|
-
Refresh the saved session timestamp.
|
|
89
|
-
"""
|
|
90
|
-
|
|
91
|
-
save_credentials(
|
|
92
|
-
email=email,
|
|
93
|
-
token=token,
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def get_authenticated_headers(token):
|
|
98
|
-
"""
|
|
99
|
-
Return authorization headers for API requests.
|
|
100
|
-
"""
|
|
101
|
-
|
|
102
|
-
return {
|
|
103
|
-
"Authorization": f"Token {token}",
|
|
104
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
bore
|
|
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
|