RemoteRF 2.2.2__tar.gz → 2.2.4__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.
- {remoterf-2.2.2/src/remoterf.egg-info → remoterf-2.2.4}/PKG-INFO +1 -1
- {remoterf-2.2.2 → remoterf-2.2.4}/pyproject.toml +1 -1
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/core/acc_login.py +2 -1
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/core/app.py +16 -3
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/deployment/homes.py +14 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/remoterf_cli.py +48 -6
- {remoterf-2.2.2 → remoterf-2.2.4/src/remoterf.egg-info}/PKG-INFO +1 -1
- remoterf-2.2.4/tests/test_cli_login_fresh.py +203 -0
- remoterf-2.2.2/tests/test_cli_login_fresh.py +0 -93
- {remoterf-2.2.2 → remoterf-2.2.4}/LICENSE +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/README.md +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/setup.cfg +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/__init__.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/common/__init__.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/common/grpc/__init__.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/common/grpc/deployment_capabilities.proto +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/common/grpc/deployment_capabilities_pb2.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/common/grpc/deployment_capabilities_pb2_grpc.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/common/grpc/grpc.proto +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/common/grpc/grpc_pb2.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/common/grpc/grpc_pb2_grpc.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/common/grpc/v2_codec.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/common/tos_notice.txt +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/common/utils/__init__.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/common/utils/ansi_codes.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/common/utils/api_token.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/common/utils/banner.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/common/utils/list_string.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/common/utils/process_arg.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/config/__init__.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/config/cert_fetcher.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/config/config.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/core/__init__.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/core/banner_note.txt +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/core/direct_path.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/core/dynamic_v2_transport.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/core/grpc_acc.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/core/grpc_client.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/core/secure_channel.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/core/v2_errors.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/deployment/__init__.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/deployment/backend.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/deployment/direct.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/deployment/http.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/deployment/state.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/__init__.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/_virtual.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/adalm_pluto/__init__.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/adalm_pluto/adalm_pluto_remote.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/dynamic_device.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/dynamic_v2.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/hackrf/__init__.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/hackrf/hackrf_remote.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/rtl_sdr/__init__.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/rtl_sdr/rtl_sdr_remote.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/sim_sdr/__init__.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/sim_sdr/sim_sdr_remote.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/support/__init__.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/support/ti_mmwave.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/support/uhd.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/support/uhd_dsp.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/support/uhd_v2.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/ti_mmwave/__init__.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/ti_mmwave/ti_mmwave_remote.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/usrp/__init__.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/usrp/usrp_remote.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/drivers/usrp/usrp_remote.pyi +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/version.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf.egg-info/SOURCES.txt +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf.egg-info/dependency_links.txt +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf.egg-info/entry_points.txt +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf.egg-info/requires.txt +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf.egg-info/top_level.txt +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_banner.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_config_address.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_deployment_accounts.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_direct_path_fixed.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_direct_path_partner.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_direct_path_race.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_dynamic_device_codegen.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_dynamic_v2.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_federated_devices.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_fetch_idl_fallback.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_finish_prompt.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_gate_i_direct_path.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_grpc_client_target.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_homes.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_native_account_credentials.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_native_devices.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_partner_dynamic_v2.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_pluto_virtual.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_process_arg.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_remoterf_cli.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_ti_mmwave.py +0 -0
- {remoterf-2.2.2 → remoterf-2.2.4}/tests/test_upgrade_rescue.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: remoterf
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.4
|
|
4
4
|
Summary: A python API to remotely access signal centric hardware. Client-side only! Courtesy of Wireless Lab @ UCLA & Prof. Ian Roberts.
|
|
5
5
|
Author-email: Ethan Ge <ethoGalaxy@gmail.com>
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "remoterf"
|
|
7
|
-
version = "2.2.
|
|
7
|
+
version = "2.2.4"
|
|
8
8
|
description = "A python API to remotely access signal centric hardware. Client-side only! Courtesy of Wireless Lab @ UCLA & Prof. Ian Roberts."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "GPL-3.0-or-later"
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
15
15
|
|
|
16
16
|
def main(*, register=False, enrollment_code=None, server_label=None, show_banner=True,
|
|
17
|
-
home=None, route=None):
|
|
17
|
+
home=None, route=None, username=None):
|
|
18
18
|
from .app import run
|
|
19
19
|
return run(
|
|
20
20
|
register=register,
|
|
@@ -23,4 +23,5 @@ def main(*, register=False, enrollment_code=None, server_label=None, show_banner
|
|
|
23
23
|
show_banner=show_banner,
|
|
24
24
|
home=home,
|
|
25
25
|
route=route,
|
|
26
|
+
username=username,
|
|
26
27
|
)
|
|
@@ -116,6 +116,8 @@ def _password_confirmation(*, enforce_policy=False):
|
|
|
116
116
|
|
|
117
117
|
# The code typed at `remoterf -r`, used once for this registration.
|
|
118
118
|
pending_enrollment_code = None
|
|
119
|
+
# A username already given (`name@lab` at `-l`): the login asks only the password.
|
|
120
|
+
pending_username = None
|
|
119
121
|
# The saved home this shell is attached to (None on a bare direct profile).
|
|
120
122
|
home_name = None
|
|
121
123
|
# The route it was reached over (None on a bare direct profile or an HTTPS home).
|
|
@@ -207,6 +209,9 @@ def _account_command(command):
|
|
|
207
209
|
account.email = _ask('Email') if policy['email_required'] else ''
|
|
208
210
|
if not account.create_user():
|
|
209
211
|
raise AccountBackendError('Registration failed')
|
|
212
|
+
if account.is_https_home:
|
|
213
|
+
from ..deployment import homes
|
|
214
|
+
printf(f"Log in from anywhere as {account.username}@{homes.GLOBAL_ACCOUNTS}", Sty.GRAY)
|
|
210
215
|
except BaseException:
|
|
211
216
|
# The code was fine; only the details failed. Keep it for the retry.
|
|
212
217
|
pending_enrollment_code = account.enrollment_code or None
|
|
@@ -249,7 +254,14 @@ def _account_command(command):
|
|
|
249
254
|
return False
|
|
250
255
|
if command in ('l', 'login'):
|
|
251
256
|
printf(f"Login to {_home_name()}", (Sty.BOLD, Sty.BLUE))
|
|
252
|
-
|
|
257
|
+
global pending_username
|
|
258
|
+
from ..deployment import homes
|
|
259
|
+
typed = pending_username or _ask('Username or Email' if account.is_https_home else 'Username')
|
|
260
|
+
pending_username = None
|
|
261
|
+
# `ethan@ucla` typed at ucla is `ethan` there.
|
|
262
|
+
name, where = homes.split_account(typed)
|
|
263
|
+
here = homes.GLOBAL_ACCOUNTS if account.is_https_home else home_name
|
|
264
|
+
account.username = name if where == here else typed
|
|
253
265
|
account.password = _ask('Password (Hidden)', hidden=True)
|
|
254
266
|
ok = bool(account.login_user(remember=not account.is_https_home))
|
|
255
267
|
if ok:
|
|
@@ -1585,8 +1597,9 @@ def interactive_reserve_next_days_auto():
|
|
|
1585
1597
|
|
|
1586
1598
|
|
|
1587
1599
|
def run(backend=None, *, register=False, enrollment_code=None, server_label=None,
|
|
1588
|
-
show_banner=True, home=None, route=None):
|
|
1589
|
-
global account, session, server_addr, pending_enrollment_code, home_name, current_route
|
|
1600
|
+
show_banner=True, home=None, route=None, username=None):
|
|
1601
|
+
global account, session, server_addr, pending_enrollment_code, home_name, current_route, pending_username
|
|
1602
|
+
pending_username = username
|
|
1590
1603
|
home_name = home
|
|
1591
1604
|
current_route = route
|
|
1592
1605
|
from ..deployment.backend import selected_backend
|
|
@@ -268,6 +268,20 @@ def connect(name: str) -> dict:
|
|
|
268
268
|
return route
|
|
269
269
|
|
|
270
270
|
|
|
271
|
+
# An account is `name@<deployment>`; Global's own accounts are `name@global`.
|
|
272
|
+
GLOBAL_ACCOUNTS = "global"
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def split_account(text: str) -> tuple[str, str | None]:
|
|
276
|
+
"""`ethan@ucla` -> ("ethan", "ucla"). A bare name, or an email (a dot
|
|
277
|
+
after the @; deployment names never have one), names no deployment."""
|
|
278
|
+
text = text.strip()
|
|
279
|
+
name, at, where = text.rpartition("@")
|
|
280
|
+
if not at or not name or not where or "." in where:
|
|
281
|
+
return text, None
|
|
282
|
+
return name, where.lower()
|
|
283
|
+
|
|
284
|
+
|
|
271
285
|
def register_home(host: str) -> tuple[str, dict, dict]:
|
|
272
286
|
"""Learn a deployment from its code, save it, and connect to it."""
|
|
273
287
|
document = discover(host)
|
|
@@ -150,7 +150,7 @@ def print_help() -> None:
|
|
|
150
150
|
printf(" remoterf -h | --help", Sty.CYAN, " Show this help", Sty.DEFAULT)
|
|
151
151
|
print()
|
|
152
152
|
printf("Commands:", (Sty.BOLD, Sty.MAGENTA))
|
|
153
|
-
printf(" remoterf -l | --login [target]", Sty.CYAN, " Login: last used, 'global',
|
|
153
|
+
printf(" remoterf -l | --login [target]", Sty.CYAN, " Login: last used, 'global', a home name, or an account (ethan@ucla)", Sty.DEFAULT)
|
|
154
154
|
printf(" remoterf -r | --register [global]", Sty.CYAN, " Register with an enrollment code (or a RemoteRF Global account)", Sty.DEFAULT)
|
|
155
155
|
printf(" remoterf homes", Sty.CYAN, " List saved deployment homes", Sty.DEFAULT)
|
|
156
156
|
printf(" remoterf -v | --version", Sty.CYAN, " Print version", Sty.DEFAULT)
|
|
@@ -171,6 +171,7 @@ def print_help() -> None:
|
|
|
171
171
|
printf(" remoterf --login", Sty.GREEN)
|
|
172
172
|
printf(" remoterf --login ucla", Sty.GREEN)
|
|
173
173
|
printf(" remoterf --login global", Sty.GREEN)
|
|
174
|
+
printf(" remoterf --login ethan@ucla", Sty.GREEN)
|
|
174
175
|
printf(" remoterf homes", Sty.GREEN)
|
|
175
176
|
printf(" remoterf --version", Sty.GREEN)
|
|
176
177
|
printf(" remoterf --config --addr 203.0.113.10:12321", Sty.GREEN)
|
|
@@ -180,7 +181,8 @@ def print_help() -> None:
|
|
|
180
181
|
|
|
181
182
|
def _account_shell(*, register: bool, enrollment_code: str | None = None,
|
|
182
183
|
server_label: str | None = None, show_banner: bool = True,
|
|
183
|
-
home: str | None = None, route: dict | None = None
|
|
184
|
+
home: str | None = None, route: dict | None = None,
|
|
185
|
+
username: str | None = None) -> int:
|
|
184
186
|
ok, _ = _ensure_config_present()
|
|
185
187
|
if not ok:
|
|
186
188
|
_print_server_unavailable()
|
|
@@ -198,6 +200,7 @@ def _account_shell(*, register: bool, enrollment_code: str | None = None,
|
|
|
198
200
|
show_banner=show_banner,
|
|
199
201
|
home=home,
|
|
200
202
|
route=route,
|
|
203
|
+
username=username,
|
|
201
204
|
)
|
|
202
205
|
or 0
|
|
203
206
|
)
|
|
@@ -212,7 +215,7 @@ def _global_origin() -> str:
|
|
|
212
215
|
return os.getenv('REMOTERF_GLOBAL_ORIGIN', 'https://global.remoterf.net')
|
|
213
216
|
|
|
214
217
|
|
|
215
|
-
def _use_global(*, register: bool, show_banner: bool = True) -> int:
|
|
218
|
+
def _use_global(*, register: bool, show_banner: bool = True, username: str | None = None) -> int:
|
|
216
219
|
"""Register or log in with the RemoteRF Global identity itself.
|
|
217
220
|
|
|
218
221
|
A Global-native account is complete on its own: it needs no deployment
|
|
@@ -238,7 +241,7 @@ def _use_global(*, register: bool, show_banner: bool = True) -> int:
|
|
|
238
241
|
except (RuntimeError, ValueError) as exc:
|
|
239
242
|
print(f'Could not reach RemoteRF Global: {exc}')
|
|
240
243
|
return 2
|
|
241
|
-
return _account_shell(register=register, show_banner=show_banner)
|
|
244
|
+
return _account_shell(register=register, show_banner=show_banner, username=username)
|
|
242
245
|
|
|
243
246
|
|
|
244
247
|
@contextmanager
|
|
@@ -293,7 +296,8 @@ def _reason(exc: BaseException) -> str:
|
|
|
293
296
|
return str(exc)
|
|
294
297
|
|
|
295
298
|
|
|
296
|
-
def _use_home(name: str, *, register: bool, enrollment_code: str | None = None
|
|
299
|
+
def _use_home(name: str, *, register: bool, enrollment_code: str | None = None,
|
|
300
|
+
username: str | None = None) -> int:
|
|
297
301
|
from remoterf.deployment import homes
|
|
298
302
|
|
|
299
303
|
try:
|
|
@@ -309,6 +313,7 @@ def _use_home(name: str, *, register: bool, enrollment_code: str | None = None)
|
|
|
309
313
|
server_label=f'{name} ({_route_label(route)})',
|
|
310
314
|
home=name,
|
|
311
315
|
route={**route, 'home': home},
|
|
316
|
+
username=username,
|
|
312
317
|
)
|
|
313
318
|
|
|
314
319
|
|
|
@@ -382,6 +387,8 @@ def _login(target: str | None) -> int:
|
|
|
382
387
|
if not require_tos_for_login():
|
|
383
388
|
return 1
|
|
384
389
|
homes.migrate_legacy_target()
|
|
390
|
+
if target is not None and '@' in target:
|
|
391
|
+
return _login_as(target)
|
|
385
392
|
if target is None:
|
|
386
393
|
last = homes.last_target()
|
|
387
394
|
saved = sorted(homes.homes())
|
|
@@ -397,7 +404,7 @@ def _login(target: str | None) -> int:
|
|
|
397
404
|
# Nothing saved. A legacy address in .env still logs in there;
|
|
398
405
|
# a fresh install has nothing to log in to, so `-l` is `-r`.
|
|
399
406
|
if not _ensure_config_present()[0]:
|
|
400
|
-
return
|
|
407
|
+
return _login_by_username()
|
|
401
408
|
return _account_shell(register=False)
|
|
402
409
|
if target == 'global':
|
|
403
410
|
# Global's address is built in, so a fresh install can log in to an
|
|
@@ -409,6 +416,41 @@ def _login(target: str | None) -> int:
|
|
|
409
416
|
return _use_home(target, register=False)
|
|
410
417
|
|
|
411
418
|
|
|
419
|
+
def _login_by_username() -> int:
|
|
420
|
+
"""A fresh install: an account says where it lives (`ethan@ucla`), so
|
|
421
|
+
asking for it is enough. Enter registers, as `-l` here always did."""
|
|
422
|
+
try:
|
|
423
|
+
name = input('Account, e.g. ethan@ucla (Enter to register): ').strip()
|
|
424
|
+
except (EOFError, KeyboardInterrupt):
|
|
425
|
+
print()
|
|
426
|
+
return 1
|
|
427
|
+
if not name:
|
|
428
|
+
return _register()
|
|
429
|
+
return _login_as(name)
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def _login_as(account: str) -> int:
|
|
433
|
+
"""Log in to `name@lab` at that lab; a bare name or an email is Global."""
|
|
434
|
+
from remoterf.deployment import homes
|
|
435
|
+
|
|
436
|
+
name, lab = homes.split_account(account)
|
|
437
|
+
if lab is None or lab == homes.GLOBAL_ACCOUNTS:
|
|
438
|
+
return _use_global(register=False, username=name)
|
|
439
|
+
if lab in homes.homes():
|
|
440
|
+
return _use_home(lab, register=False, username=name)
|
|
441
|
+
host = f'{lab}.{homes.GLOBAL_DOMAIN}'
|
|
442
|
+
try:
|
|
443
|
+
with _working(f"Looking up {host}…"):
|
|
444
|
+
lab, home, route = homes.register_home(host)
|
|
445
|
+
except (RuntimeError, ValueError) as exc:
|
|
446
|
+
printf(f'Could not reach {host}: {_reason(exc)}', Sty.WARNING)
|
|
447
|
+
return 2
|
|
448
|
+
return _account_shell(
|
|
449
|
+
register=False, server_label=f'{lab} ({_route_label(route)})',
|
|
450
|
+
home=lab, route={**route, 'home': home}, username=name,
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
|
|
412
454
|
def _print_pick_a_target() -> int:
|
|
413
455
|
_print_homes()
|
|
414
456
|
printf('Choose one: ', Sty.GRAY, 'remoterf --login <name>', Sty.CYAN)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: remoterf
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.4
|
|
4
4
|
Summary: A python API to remotely access signal centric hardware. Client-side only! Courtesy of Wireless Lab @ UCLA & Prof. Ian Roberts.
|
|
5
5
|
Author-email: Ethan Ge <ethoGalaxy@gmail.com>
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""`remoterf -l` on a fresh install is `remoterf -r`; a configured client logs in."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import tempfile
|
|
5
|
+
from unittest import mock
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _fresh_home():
|
|
9
|
+
tmp = tempfile.TemporaryDirectory()
|
|
10
|
+
env = {k: v for k, v in os.environ.items() if not k.startswith("REMOTERF_")}
|
|
11
|
+
env["HOME"] = tmp.name
|
|
12
|
+
return tmp, mock.patch.dict(os.environ, env, clear=True)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_login_on_a_fresh_install_registers_on_enter():
|
|
16
|
+
from remoterf import remoterf_cli
|
|
17
|
+
|
|
18
|
+
tmp, env = _fresh_home()
|
|
19
|
+
with tmp, env, \
|
|
20
|
+
mock.patch("remoterf.config.config.require_tos_for_login", return_value=True), \
|
|
21
|
+
mock.patch("builtins.input", return_value=""), \
|
|
22
|
+
mock.patch.object(remoterf_cli, "_register", return_value=0) as register, \
|
|
23
|
+
mock.patch.object(remoterf_cli, "_account_shell") as shell:
|
|
24
|
+
assert remoterf_cli._login(None) == 0
|
|
25
|
+
register.assert_called_once_with()
|
|
26
|
+
shell.assert_not_called()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_an_account_name_says_where_it_lives():
|
|
30
|
+
from remoterf.deployment.homes import split_account
|
|
31
|
+
|
|
32
|
+
assert split_account(" ian@ucla ") == ("ian", "ucla")
|
|
33
|
+
assert split_account("Ian@UCLA") == ("Ian", "ucla")
|
|
34
|
+
assert split_account("ian@global") == ("ian", "global")
|
|
35
|
+
assert split_account("ian") == ("ian", None)
|
|
36
|
+
assert split_account("ian@ucla.edu") == ("ian@ucla.edu", None) # an email
|
|
37
|
+
assert split_account("a@b@ucla") == ("a@b", "ucla")
|
|
38
|
+
assert split_account("@ucla") == ("@ucla", None)
|
|
39
|
+
assert split_account("ian@") == ("ian@", None)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_login_on_a_fresh_install_goes_to_the_lab_in_the_account_name():
|
|
43
|
+
from remoterf import remoterf_cli
|
|
44
|
+
|
|
45
|
+
tmp, env = _fresh_home()
|
|
46
|
+
route = {"kind": "relay", "host": "ucla.global.remoterf.net", "port": 12321}
|
|
47
|
+
with tmp, env, \
|
|
48
|
+
mock.patch("remoterf.config.config.require_tos_for_login", return_value=True), \
|
|
49
|
+
mock.patch("builtins.input", return_value="ian@ucla"), \
|
|
50
|
+
mock.patch("remoterf.deployment.homes.register_home",
|
|
51
|
+
return_value=("ucla", {"display_name": "UCLA"}, route)) as register_home, \
|
|
52
|
+
mock.patch.object(remoterf_cli, "_account_shell", return_value=0) as shell:
|
|
53
|
+
assert remoterf_cli._login(None) == 0
|
|
54
|
+
register_home.assert_called_once_with("ucla.global.remoterf.net")
|
|
55
|
+
assert shell.call_args.kwargs["register"] is False
|
|
56
|
+
assert shell.call_args.kwargs["home"] == "ucla"
|
|
57
|
+
assert shell.call_args.kwargs["username"] == "ian"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_login_on_a_fresh_install_global_bare_names_and_emails_go_to_global():
|
|
61
|
+
from remoterf import remoterf_cli
|
|
62
|
+
|
|
63
|
+
for typed, username in (("ian@global", "ian"), ("ian", "ian"), ("ian@ucla.edu", "ian@ucla.edu")):
|
|
64
|
+
tmp, env = _fresh_home()
|
|
65
|
+
with tmp, env, \
|
|
66
|
+
mock.patch("remoterf.config.config.require_tos_for_login", return_value=True), \
|
|
67
|
+
mock.patch("builtins.input", return_value=typed), \
|
|
68
|
+
mock.patch("remoterf.deployment.homes.register_home") as register_home, \
|
|
69
|
+
mock.patch.object(remoterf_cli, "_use_global", return_value=0) as use_global:
|
|
70
|
+
assert remoterf_cli._login(None) == 0
|
|
71
|
+
use_global.assert_called_once_with(register=False, username=username)
|
|
72
|
+
register_home.assert_not_called()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_login_to_a_lab_that_does_not_answer():
|
|
76
|
+
from remoterf import remoterf_cli
|
|
77
|
+
|
|
78
|
+
tmp, env = _fresh_home()
|
|
79
|
+
with tmp, env, \
|
|
80
|
+
mock.patch("remoterf.config.config.require_tos_for_login", return_value=True), \
|
|
81
|
+
mock.patch("builtins.input", return_value="ian@nolab"), \
|
|
82
|
+
mock.patch("remoterf.deployment.homes.register_home",
|
|
83
|
+
side_effect=RuntimeError("no such deployment")) as register_home, \
|
|
84
|
+
mock.patch.object(remoterf_cli, "_account_shell") as shell:
|
|
85
|
+
assert remoterf_cli._login(None) == 2
|
|
86
|
+
register_home.assert_called_once_with("nolab.global.remoterf.net")
|
|
87
|
+
shell.assert_not_called()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_login_with_an_account_uses_the_saved_home():
|
|
91
|
+
from remoterf import remoterf_cli
|
|
92
|
+
|
|
93
|
+
tmp, env = _fresh_home()
|
|
94
|
+
with tmp, env, \
|
|
95
|
+
mock.patch("remoterf.config.config.require_tos_for_login", return_value=True), \
|
|
96
|
+
mock.patch("remoterf.deployment.homes.homes", return_value={"ucla": {}}), \
|
|
97
|
+
mock.patch("remoterf.deployment.homes.register_home") as register_home, \
|
|
98
|
+
mock.patch.object(remoterf_cli, "_use_home", return_value=0) as use_home:
|
|
99
|
+
assert remoterf_cli._login("ian@ucla") == 0
|
|
100
|
+
use_home.assert_called_once_with("ucla", register=False, username="ian")
|
|
101
|
+
register_home.assert_not_called()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_login_global_on_a_fresh_install_logs_in_to_global():
|
|
105
|
+
from remoterf import remoterf_cli
|
|
106
|
+
|
|
107
|
+
tmp, env = _fresh_home()
|
|
108
|
+
with tmp, env, \
|
|
109
|
+
mock.patch("remoterf.config.config.require_tos_for_login", return_value=True), \
|
|
110
|
+
mock.patch.object(remoterf_cli, "_use_global", return_value=0) as use_global:
|
|
111
|
+
assert remoterf_cli._login("global") == 0
|
|
112
|
+
use_global.assert_called_once_with(register=False)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_login_with_a_legacy_address_still_logs_in_there():
|
|
116
|
+
from remoterf import remoterf_cli
|
|
117
|
+
|
|
118
|
+
tmp, env = _fresh_home()
|
|
119
|
+
with tmp, env, \
|
|
120
|
+
mock.patch("remoterf.config.config.require_tos_for_login", return_value=True), \
|
|
121
|
+
mock.patch.object(remoterf_cli, "_ensure_config_present", return_value=(True, "")), \
|
|
122
|
+
mock.patch.object(remoterf_cli, "_register") as register, \
|
|
123
|
+
mock.patch.object(remoterf_cli, "_account_shell", return_value=0) as shell:
|
|
124
|
+
assert remoterf_cli._login(None) == 0
|
|
125
|
+
shell.assert_called_once_with(register=False)
|
|
126
|
+
register.assert_not_called()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_register_without_a_code_makes_a_global_account(monkeypatch):
|
|
130
|
+
from remoterf import remoterf_cli
|
|
131
|
+
|
|
132
|
+
monkeypatch.setattr("builtins.input", lambda prompt="": "")
|
|
133
|
+
with mock.patch("remoterf.config.config._confirm_tos", return_value=True), \
|
|
134
|
+
mock.patch("remoterf.config.config.forget_tos_agreement"), \
|
|
135
|
+
mock.patch("remoterf.config.config.remember_tos_agreement"), \
|
|
136
|
+
mock.patch.object(remoterf_cli, "print_client_banner"), \
|
|
137
|
+
mock.patch.object(remoterf_cli, "_use_global", return_value=0) as use_global, \
|
|
138
|
+
mock.patch.object(remoterf_cli, "_account_shell") as shell:
|
|
139
|
+
assert remoterf_cli._register() == 0
|
|
140
|
+
use_global.assert_called_once_with(register=True, show_banner=False)
|
|
141
|
+
shell.assert_not_called()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_register_global_asks_for_the_tos_like_register(monkeypatch):
|
|
145
|
+
from remoterf import remoterf_cli
|
|
146
|
+
|
|
147
|
+
for arg in ("global", "-global"):
|
|
148
|
+
monkeypatch.setattr("sys.argv", ["remoterf", "-r", arg])
|
|
149
|
+
with mock.patch("remoterf.config.config._confirm_tos", return_value=True) as confirm, \
|
|
150
|
+
mock.patch("remoterf.config.config.forget_tos_agreement") as forget, \
|
|
151
|
+
mock.patch("remoterf.config.config.remember_tos_agreement") as remember, \
|
|
152
|
+
mock.patch.object(remoterf_cli, "print_client_banner"), \
|
|
153
|
+
mock.patch.object(remoterf_cli, "_use_global", return_value=0) as use_global:
|
|
154
|
+
assert remoterf_cli.main() == 0
|
|
155
|
+
forget.assert_called_once_with()
|
|
156
|
+
confirm.assert_called_once()
|
|
157
|
+
remember.assert_called_once_with()
|
|
158
|
+
use_global.assert_called_once_with(register=True, show_banner=False)
|
|
159
|
+
|
|
160
|
+
# Declining stops before any account is made.
|
|
161
|
+
monkeypatch.setattr("sys.argv", ["remoterf", "-r", "global"])
|
|
162
|
+
with mock.patch("remoterf.config.config._confirm_tos", return_value=False), \
|
|
163
|
+
mock.patch("remoterf.config.config.forget_tos_agreement"), \
|
|
164
|
+
mock.patch("remoterf.config.config.remember_tos_agreement") as remember, \
|
|
165
|
+
mock.patch.object(remoterf_cli, "print_client_banner"), \
|
|
166
|
+
mock.patch.object(remoterf_cli, "_use_global") as use_global:
|
|
167
|
+
assert remoterf_cli.main() == 1
|
|
168
|
+
remember.assert_not_called()
|
|
169
|
+
use_global.assert_not_called()
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _login_at(home, typed, *, https=False, pending=None):
|
|
173
|
+
from remoterf.core import app
|
|
174
|
+
|
|
175
|
+
account = mock.Mock(is_https_home=https, backend=None)
|
|
176
|
+
account.login_user.return_value = True
|
|
177
|
+
with mock.patch.object(app, "account", account), \
|
|
178
|
+
mock.patch.object(app, "home_name", home), \
|
|
179
|
+
mock.patch.object(app, "pending_username", pending), \
|
|
180
|
+
mock.patch.object(app, "local_capabilities",
|
|
181
|
+
return_value={"registration_policy": {}}), \
|
|
182
|
+
mock.patch.object(app, "_home_name", return_value=home), \
|
|
183
|
+
mock.patch.object(app, "_remember_login"), \
|
|
184
|
+
mock.patch.object(app, "_ask", side_effect=[typed, "secret"] if typed else ["secret"]) as ask:
|
|
185
|
+
if https:
|
|
186
|
+
account.backend = mock.Mock(capabilities={"registration_policy": {}})
|
|
187
|
+
assert app._account_command("login") is True
|
|
188
|
+
assert app.pending_username is None
|
|
189
|
+
return account.username, ask
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def test_a_found_username_is_not_asked_again():
|
|
193
|
+
username, ask = _login_at("ucla", None, pending="ian")
|
|
194
|
+
assert username == "ian"
|
|
195
|
+
ask.assert_called_once_with("Password (Hidden)", hidden=True)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def test_the_full_account_name_works_at_its_own_home_only():
|
|
199
|
+
assert _login_at("ucla", "ian@ucla")[0] == "ian"
|
|
200
|
+
assert _login_at("ucla", "ian")[0] == "ian"
|
|
201
|
+
assert _login_at("otheruni", "ian@ucla")[0] == "ian@ucla" # not this lab's: no such name here
|
|
202
|
+
assert _login_at(None, "ian@global", https=True)[0] == "ian"
|
|
203
|
+
assert _login_at(None, "ian@ucla.edu", https=True)[0] == "ian@ucla.edu" # an email
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
"""`remoterf -l` on a fresh install is `remoterf -r`; a configured client logs in."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import tempfile
|
|
5
|
-
from unittest import mock
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def _fresh_home():
|
|
9
|
-
tmp = tempfile.TemporaryDirectory()
|
|
10
|
-
env = {k: v for k, v in os.environ.items() if not k.startswith("REMOTERF_")}
|
|
11
|
-
env["HOME"] = tmp.name
|
|
12
|
-
return tmp, mock.patch.dict(os.environ, env, clear=True)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def test_login_on_a_fresh_install_registers():
|
|
16
|
-
from remoterf import remoterf_cli
|
|
17
|
-
|
|
18
|
-
tmp, env = _fresh_home()
|
|
19
|
-
with tmp, env, \
|
|
20
|
-
mock.patch("remoterf.config.config.require_tos_for_login", return_value=True), \
|
|
21
|
-
mock.patch.object(remoterf_cli, "_register", return_value=0) as register, \
|
|
22
|
-
mock.patch.object(remoterf_cli, "_account_shell") as shell:
|
|
23
|
-
assert remoterf_cli._login(None) == 0
|
|
24
|
-
register.assert_called_once_with()
|
|
25
|
-
shell.assert_not_called()
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def test_login_global_on_a_fresh_install_logs_in_to_global():
|
|
29
|
-
from remoterf import remoterf_cli
|
|
30
|
-
|
|
31
|
-
tmp, env = _fresh_home()
|
|
32
|
-
with tmp, env, \
|
|
33
|
-
mock.patch("remoterf.config.config.require_tos_for_login", return_value=True), \
|
|
34
|
-
mock.patch.object(remoterf_cli, "_use_global", return_value=0) as use_global:
|
|
35
|
-
assert remoterf_cli._login("global") == 0
|
|
36
|
-
use_global.assert_called_once_with(register=False)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def test_login_with_a_legacy_address_still_logs_in_there():
|
|
40
|
-
from remoterf import remoterf_cli
|
|
41
|
-
|
|
42
|
-
tmp, env = _fresh_home()
|
|
43
|
-
with tmp, env, \
|
|
44
|
-
mock.patch("remoterf.config.config.require_tos_for_login", return_value=True), \
|
|
45
|
-
mock.patch.object(remoterf_cli, "_ensure_config_present", return_value=(True, "")), \
|
|
46
|
-
mock.patch.object(remoterf_cli, "_register") as register, \
|
|
47
|
-
mock.patch.object(remoterf_cli, "_account_shell", return_value=0) as shell:
|
|
48
|
-
assert remoterf_cli._login(None) == 0
|
|
49
|
-
shell.assert_called_once_with(register=False)
|
|
50
|
-
register.assert_not_called()
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def test_register_without_a_code_makes_a_global_account(monkeypatch):
|
|
54
|
-
from remoterf import remoterf_cli
|
|
55
|
-
|
|
56
|
-
monkeypatch.setattr("builtins.input", lambda prompt="": "")
|
|
57
|
-
with mock.patch("remoterf.config.config._confirm_tos", return_value=True), \
|
|
58
|
-
mock.patch("remoterf.config.config.forget_tos_agreement"), \
|
|
59
|
-
mock.patch("remoterf.config.config.remember_tos_agreement"), \
|
|
60
|
-
mock.patch.object(remoterf_cli, "print_client_banner"), \
|
|
61
|
-
mock.patch.object(remoterf_cli, "_use_global", return_value=0) as use_global, \
|
|
62
|
-
mock.patch.object(remoterf_cli, "_account_shell") as shell:
|
|
63
|
-
assert remoterf_cli._register() == 0
|
|
64
|
-
use_global.assert_called_once_with(register=True, show_banner=False)
|
|
65
|
-
shell.assert_not_called()
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def test_register_global_asks_for_the_tos_like_register(monkeypatch):
|
|
69
|
-
from remoterf import remoterf_cli
|
|
70
|
-
|
|
71
|
-
for arg in ("global", "-global"):
|
|
72
|
-
monkeypatch.setattr("sys.argv", ["remoterf", "-r", arg])
|
|
73
|
-
with mock.patch("remoterf.config.config._confirm_tos", return_value=True) as confirm, \
|
|
74
|
-
mock.patch("remoterf.config.config.forget_tos_agreement") as forget, \
|
|
75
|
-
mock.patch("remoterf.config.config.remember_tos_agreement") as remember, \
|
|
76
|
-
mock.patch.object(remoterf_cli, "print_client_banner"), \
|
|
77
|
-
mock.patch.object(remoterf_cli, "_use_global", return_value=0) as use_global:
|
|
78
|
-
assert remoterf_cli.main() == 0
|
|
79
|
-
forget.assert_called_once_with()
|
|
80
|
-
confirm.assert_called_once()
|
|
81
|
-
remember.assert_called_once_with()
|
|
82
|
-
use_global.assert_called_once_with(register=True, show_banner=False)
|
|
83
|
-
|
|
84
|
-
# Declining stops before any account is made.
|
|
85
|
-
monkeypatch.setattr("sys.argv", ["remoterf", "-r", "global"])
|
|
86
|
-
with mock.patch("remoterf.config.config._confirm_tos", return_value=False), \
|
|
87
|
-
mock.patch("remoterf.config.config.forget_tos_agreement"), \
|
|
88
|
-
mock.patch("remoterf.config.config.remember_tos_agreement") as remember, \
|
|
89
|
-
mock.patch.object(remoterf_cli, "print_client_banner"), \
|
|
90
|
-
mock.patch.object(remoterf_cli, "_use_global") as use_global:
|
|
91
|
-
assert remoterf_cli.main() == 1
|
|
92
|
-
remember.assert_not_called()
|
|
93
|
-
use_global.assert_not_called()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf-2.2.2 → remoterf-2.2.4}/src/remoterf/common/grpc/deployment_capabilities_pb2_grpc.py
RENAMED
|
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
|
|
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
|
|
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
|