RemoteRF 2.2.3__tar.gz → 2.2.5__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.3/src/remoterf.egg-info → remoterf-2.2.5}/PKG-INFO +1 -1
- {remoterf-2.2.3 → remoterf-2.2.5}/pyproject.toml +1 -1
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/core/app.py +14 -3
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/deployment/homes.py +12 -19
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/remoterf_cli.py +22 -19
- {remoterf-2.2.3 → remoterf-2.2.5/src/remoterf.egg-info}/PKG-INFO +1 -1
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_cli_login_fresh.py +68 -27
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_homes.py +3 -3
- {remoterf-2.2.3 → remoterf-2.2.5}/LICENSE +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/README.md +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/setup.cfg +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/__init__.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/common/__init__.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/common/grpc/__init__.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/common/grpc/deployment_capabilities.proto +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/common/grpc/deployment_capabilities_pb2.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/common/grpc/deployment_capabilities_pb2_grpc.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/common/grpc/grpc.proto +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/common/grpc/grpc_pb2.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/common/grpc/grpc_pb2_grpc.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/common/grpc/v2_codec.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/common/tos_notice.txt +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/common/utils/__init__.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/common/utils/ansi_codes.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/common/utils/api_token.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/common/utils/banner.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/common/utils/list_string.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/common/utils/process_arg.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/config/__init__.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/config/cert_fetcher.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/config/config.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/core/__init__.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/core/acc_login.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/core/banner_note.txt +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/core/direct_path.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/core/dynamic_v2_transport.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/core/grpc_acc.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/core/grpc_client.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/core/secure_channel.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/core/v2_errors.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/deployment/__init__.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/deployment/backend.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/deployment/direct.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/deployment/http.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/deployment/state.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/__init__.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/_virtual.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/adalm_pluto/__init__.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/adalm_pluto/adalm_pluto_remote.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/dynamic_device.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/dynamic_v2.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/hackrf/__init__.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/hackrf/hackrf_remote.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/rtl_sdr/__init__.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/rtl_sdr/rtl_sdr_remote.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/sim_sdr/__init__.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/sim_sdr/sim_sdr_remote.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/support/__init__.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/support/ti_mmwave.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/support/uhd.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/support/uhd_dsp.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/support/uhd_v2.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/ti_mmwave/__init__.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/ti_mmwave/ti_mmwave_remote.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/usrp/__init__.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/usrp/usrp_remote.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/drivers/usrp/usrp_remote.pyi +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf/version.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf.egg-info/SOURCES.txt +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf.egg-info/dependency_links.txt +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf.egg-info/entry_points.txt +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf.egg-info/requires.txt +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf.egg-info/top_level.txt +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/src/remoterf.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_banner.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_config_address.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_deployment_accounts.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_direct_path_fixed.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_direct_path_partner.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_direct_path_race.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_dynamic_device_codegen.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_dynamic_v2.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_federated_devices.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_fetch_idl_fallback.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_finish_prompt.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_gate_i_direct_path.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_grpc_client_target.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_native_account_credentials.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_native_devices.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_partner_dynamic_v2.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_pluto_virtual.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_process_arg.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_remoterf_cli.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/tests/test_ti_mmwave.py +0 -0
- {remoterf-2.2.3 → remoterf-2.2.5}/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.5
|
|
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.5"
|
|
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"
|
|
@@ -116,7 +116,7 @@ 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 (
|
|
119
|
+
# A username already given (`name@lab` at `-l`): the login asks only the password.
|
|
120
120
|
pending_username = None
|
|
121
121
|
# The saved home this shell is attached to (None on a bare direct profile).
|
|
122
122
|
home_name = None
|
|
@@ -209,6 +209,9 @@ def _account_command(command):
|
|
|
209
209
|
account.email = _ask('Email') if policy['email_required'] else ''
|
|
210
210
|
if not account.create_user():
|
|
211
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)
|
|
212
215
|
except BaseException:
|
|
213
216
|
# The code was fine; only the details failed. Keep it for the retry.
|
|
214
217
|
pending_enrollment_code = account.enrollment_code or None
|
|
@@ -252,8 +255,13 @@ def _account_command(command):
|
|
|
252
255
|
if command in ('l', 'login'):
|
|
253
256
|
printf(f"Login to {_home_name()}", (Sty.BOLD, Sty.BLUE))
|
|
254
257
|
global pending_username
|
|
255
|
-
|
|
258
|
+
from ..deployment import homes
|
|
259
|
+
typed = pending_username or _ask('Username or Email' if account.is_https_home else 'Username')
|
|
256
260
|
pending_username = None
|
|
261
|
+
# `alice@ucla` typed at ucla is `alice` 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
|
|
257
265
|
account.password = _ask('Password (Hidden)', hidden=True)
|
|
258
266
|
ok = bool(account.login_user(remember=not account.is_https_home))
|
|
259
267
|
if ok:
|
|
@@ -1620,9 +1628,12 @@ def run(backend=None, *, register=False, enrollment_code=None, server_label=None
|
|
|
1620
1628
|
# Continue below the banner rather than wiping the screen: with a
|
|
1621
1629
|
# stored login the wipe came a split second after everything appeared.
|
|
1622
1630
|
session_line()
|
|
1631
|
+
from ..deployment import homes
|
|
1632
|
+
# The prompt is the account's full name: alice@ucla, alice@global.
|
|
1633
|
+
where = homes.GLOBAL_ACCOUNTS if account.is_https_home else (home_name or 'remoterf')
|
|
1623
1634
|
while True:
|
|
1624
1635
|
try:
|
|
1625
|
-
inpu = session.prompt(stylize(f'{account.username}@
|
|
1636
|
+
inpu = session.prompt(stylize(f'{account.username}@{where}: ', Sty.BOLD))
|
|
1626
1637
|
if inpu in ('register', 'verify', 'login', 'forgot-password', 'reset-password'):
|
|
1627
1638
|
authenticated = _account_command(inpu)
|
|
1628
1639
|
if inpu == 'reset-password' and not welcome(show_banner=False):
|
|
@@ -268,25 +268,18 @@ def connect(name: str) -> dict:
|
|
|
268
268
|
return route
|
|
269
269
|
|
|
270
270
|
|
|
271
|
-
#
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
def
|
|
276
|
-
"""
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
return
|
|
282
|
-
|
|
283
|
-
)["deployment"]
|
|
284
|
-
except AccountBackendError as exc:
|
|
285
|
-
if exc.code == "not_found":
|
|
286
|
-
return None
|
|
287
|
-
raise
|
|
288
|
-
finally:
|
|
289
|
-
transport.close()
|
|
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
|
+
"""`alice@ucla` -> ("alice", "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()
|
|
290
283
|
|
|
291
284
|
|
|
292
285
|
def register_home(host: str) -> tuple[str, dict, dict]:
|
|
@@ -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 (username@lab)", 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 jdoe@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)
|
|
@@ -295,7 +296,8 @@ def _reason(exc: BaseException) -> str:
|
|
|
295
296
|
return str(exc)
|
|
296
297
|
|
|
297
298
|
|
|
298
|
-
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:
|
|
299
301
|
from remoterf.deployment import homes
|
|
300
302
|
|
|
301
303
|
try:
|
|
@@ -311,6 +313,7 @@ def _use_home(name: str, *, register: bool, enrollment_code: str | None = None)
|
|
|
311
313
|
server_label=f'{name} ({_route_label(route)})',
|
|
312
314
|
home=name,
|
|
313
315
|
route={**route, 'home': home},
|
|
316
|
+
username=username,
|
|
314
317
|
)
|
|
315
318
|
|
|
316
319
|
|
|
@@ -384,6 +387,8 @@ def _login(target: str | None) -> int:
|
|
|
384
387
|
if not require_tos_for_login():
|
|
385
388
|
return 1
|
|
386
389
|
homes.migrate_legacy_target()
|
|
390
|
+
if target is not None and '@' in target:
|
|
391
|
+
return _login_as(target)
|
|
387
392
|
if target is None:
|
|
388
393
|
last = homes.last_target()
|
|
389
394
|
saved = sorted(homes.homes())
|
|
@@ -412,30 +417,28 @@ def _login(target: str | None) -> int:
|
|
|
412
417
|
|
|
413
418
|
|
|
414
419
|
def _login_by_username() -> int:
|
|
415
|
-
"""A fresh install:
|
|
416
|
-
|
|
417
|
-
from remoterf.deployment import homes
|
|
418
|
-
|
|
420
|
+
"""A fresh install: an account says where it lives (`alice@ucla`), so
|
|
421
|
+
asking for it is enough. Enter registers, as `-l` here always did."""
|
|
419
422
|
try:
|
|
420
|
-
name = input('
|
|
423
|
+
name = input('Account (username@lab, Enter to register): ').strip()
|
|
421
424
|
except (EOFError, KeyboardInterrupt):
|
|
422
425
|
print()
|
|
423
426
|
return 1
|
|
424
427
|
if not name:
|
|
425
428
|
return _register()
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
return 2
|
|
436
|
-
if deployment == homes.GLOBAL_HOME:
|
|
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:
|
|
437
438
|
return _use_global(register=False, username=name)
|
|
438
|
-
|
|
439
|
+
if lab in homes.homes():
|
|
440
|
+
return _use_home(lab, register=False, username=name)
|
|
441
|
+
host = f'{lab}.{homes.GLOBAL_DOMAIN}'
|
|
439
442
|
try:
|
|
440
443
|
with _working(f"Looking up {host}…"):
|
|
441
444
|
lab, home, route = homes.register_home(host)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: remoterf
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.5
|
|
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
|
|
@@ -19,63 +19,88 @@ def test_login_on_a_fresh_install_registers_on_enter():
|
|
|
19
19
|
with tmp, env, \
|
|
20
20
|
mock.patch("remoterf.config.config.require_tos_for_login", return_value=True), \
|
|
21
21
|
mock.patch("builtins.input", return_value=""), \
|
|
22
|
-
mock.patch("remoterf.deployment.homes.lookup_username") as lookup, \
|
|
23
22
|
mock.patch.object(remoterf_cli, "_register", return_value=0) as register, \
|
|
24
23
|
mock.patch.object(remoterf_cli, "_account_shell") as shell:
|
|
25
24
|
assert remoterf_cli._login(None) == 0
|
|
26
25
|
register.assert_called_once_with()
|
|
27
26
|
shell.assert_not_called()
|
|
28
|
-
lookup.assert_not_called()
|
|
29
27
|
|
|
30
28
|
|
|
31
|
-
def
|
|
29
|
+
def test_an_account_name_says_where_it_lives():
|
|
30
|
+
from remoterf.deployment.homes import split_account
|
|
31
|
+
|
|
32
|
+
assert split_account(" jdoe@ucla ") == ("jdoe", "ucla")
|
|
33
|
+
assert split_account("JDoe@UCLA") == ("JDoe", "ucla")
|
|
34
|
+
assert split_account("jdoe@global") == ("jdoe", "global")
|
|
35
|
+
assert split_account("jdoe") == ("jdoe", None)
|
|
36
|
+
assert split_account("jdoe@ucla.edu") == ("jdoe@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("jdoe@") == ("jdoe@", None)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_login_on_a_fresh_install_goes_to_the_lab_in_the_account_name():
|
|
32
43
|
from remoterf import remoterf_cli
|
|
33
44
|
|
|
34
45
|
tmp, env = _fresh_home()
|
|
35
46
|
route = {"kind": "relay", "host": "ucla.global.remoterf.net", "port": 12321}
|
|
36
47
|
with tmp, env, \
|
|
37
48
|
mock.patch("remoterf.config.config.require_tos_for_login", return_value=True), \
|
|
38
|
-
mock.patch("builtins.input", return_value="
|
|
39
|
-
mock.patch("remoterf.deployment.homes.lookup_username", return_value="ucla") as lookup, \
|
|
49
|
+
mock.patch("builtins.input", return_value="jdoe@ucla"), \
|
|
40
50
|
mock.patch("remoterf.deployment.homes.register_home",
|
|
41
51
|
return_value=("ucla", {"display_name": "UCLA"}, route)) as register_home, \
|
|
42
52
|
mock.patch.object(remoterf_cli, "_account_shell", return_value=0) as shell:
|
|
43
53
|
assert remoterf_cli._login(None) == 0
|
|
44
|
-
assert lookup.call_args.args[1] == "ian"
|
|
45
54
|
register_home.assert_called_once_with("ucla.global.remoterf.net")
|
|
46
55
|
assert shell.call_args.kwargs["register"] is False
|
|
47
56
|
assert shell.call_args.kwargs["home"] == "ucla"
|
|
48
|
-
assert shell.call_args.kwargs["username"] == "
|
|
57
|
+
assert shell.call_args.kwargs["username"] == "jdoe"
|
|
49
58
|
|
|
50
59
|
|
|
51
|
-
def
|
|
60
|
+
def test_login_on_a_fresh_install_global_bare_names_and_emails_go_to_global():
|
|
52
61
|
from remoterf import remoterf_cli
|
|
53
62
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
for typed, username in (("jdoe@global", "jdoe"), ("jdoe", "jdoe"), ("jdoe@ucla.edu", "jdoe@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()
|
|
62
73
|
|
|
63
74
|
|
|
64
|
-
def
|
|
75
|
+
def test_login_to_a_lab_that_does_not_answer():
|
|
65
76
|
from remoterf import remoterf_cli
|
|
66
77
|
|
|
67
78
|
tmp, env = _fresh_home()
|
|
68
79
|
with tmp, env, \
|
|
69
80
|
mock.patch("remoterf.config.config.require_tos_for_login", return_value=True), \
|
|
70
|
-
mock.patch("builtins.input", return_value="
|
|
71
|
-
mock.patch("remoterf.deployment.homes.
|
|
72
|
-
|
|
81
|
+
mock.patch("builtins.input", return_value="jdoe@nolab"), \
|
|
82
|
+
mock.patch("remoterf.deployment.homes.register_home",
|
|
83
|
+
side_effect=RuntimeError("no such deployment")) as register_home, \
|
|
73
84
|
mock.patch.object(remoterf_cli, "_account_shell") as shell:
|
|
74
85
|
assert remoterf_cli._login(None) == 2
|
|
75
|
-
|
|
86
|
+
register_home.assert_called_once_with("nolab.global.remoterf.net")
|
|
76
87
|
shell.assert_not_called()
|
|
77
88
|
|
|
78
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("jdoe@ucla") == 0
|
|
100
|
+
use_home.assert_called_once_with("ucla", register=False, username="jdoe")
|
|
101
|
+
register_home.assert_not_called()
|
|
102
|
+
|
|
103
|
+
|
|
79
104
|
def test_login_global_on_a_fresh_install_logs_in_to_global():
|
|
80
105
|
from remoterf import remoterf_cli
|
|
81
106
|
|
|
@@ -144,19 +169,35 @@ def test_register_global_asks_for_the_tos_like_register(monkeypatch):
|
|
|
144
169
|
use_global.assert_not_called()
|
|
145
170
|
|
|
146
171
|
|
|
147
|
-
def
|
|
172
|
+
def _login_at(home, typed, *, https=False, pending=None):
|
|
148
173
|
from remoterf.core import app
|
|
149
174
|
|
|
150
|
-
account = mock.Mock(is_https_home=
|
|
175
|
+
account = mock.Mock(is_https_home=https, backend=None)
|
|
151
176
|
account.login_user.return_value = True
|
|
152
177
|
with mock.patch.object(app, "account", account), \
|
|
153
|
-
mock.patch.object(app, "
|
|
178
|
+
mock.patch.object(app, "home_name", home), \
|
|
179
|
+
mock.patch.object(app, "pending_username", pending), \
|
|
154
180
|
mock.patch.object(app, "local_capabilities",
|
|
155
181
|
return_value={"registration_policy": {}}), \
|
|
156
|
-
mock.patch.object(app, "_home_name", return_value=
|
|
182
|
+
mock.patch.object(app, "_home_name", return_value=home), \
|
|
157
183
|
mock.patch.object(app, "_remember_login"), \
|
|
158
|
-
mock.patch.object(app, "_ask",
|
|
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": {}})
|
|
159
187
|
assert app._account_command("login") is True
|
|
160
188
|
assert app.pending_username is None
|
|
161
|
-
|
|
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="jdoe")
|
|
194
|
+
assert username == "jdoe"
|
|
162
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", "jdoe@ucla")[0] == "jdoe"
|
|
200
|
+
assert _login_at("ucla", "jdoe")[0] == "jdoe"
|
|
201
|
+
assert _login_at("otheruni", "jdoe@ucla")[0] == "jdoe@ucla" # not this lab's: no such name here
|
|
202
|
+
assert _login_at(None, "jdoe@global", https=True)[0] == "jdoe"
|
|
203
|
+
assert _login_at(None, "jdoe@ucla.edu", https=True)[0] == "jdoe@ucla.edu" # an email
|
|
@@ -444,7 +444,7 @@ def test_every_command_after_login_goes_native_to_the_home(reachable, monkeypatc
|
|
|
444
444
|
return response
|
|
445
445
|
|
|
446
446
|
monkeypatch.setattr(grpc_acc, "rpc_client", rpc_client)
|
|
447
|
-
account = grpc_acc.RemoteRFAccount("
|
|
447
|
+
account = grpc_acc.RemoteRFAccount("jdoe", "secret")
|
|
448
448
|
# A deployment-native account is not an HTTPS home: no Global in the path.
|
|
449
449
|
assert not account.is_https_home
|
|
450
450
|
|
|
@@ -466,7 +466,7 @@ def test_every_command_after_login_goes_native_to_the_home(reachable, monkeypatc
|
|
|
466
466
|
assert token == "reservation-token"
|
|
467
467
|
# Credentials go to the HOME itself, never through any broker.
|
|
468
468
|
for _, args in sent:
|
|
469
|
-
assert args["un"].string_value == "
|
|
469
|
+
assert args["un"].string_value == "jdoe"
|
|
470
470
|
|
|
471
471
|
# And the transport those calls ride is this HOME's own direct channel.
|
|
472
472
|
monkeypatch.setattr("grpc.secure_channel", lambda *a, **k: mock.Mock())
|
|
@@ -626,7 +626,7 @@ def test_reserve_shows_the_token_even_when_the_driver_fetch_fails(monkeypatch, c
|
|
|
626
626
|
|
|
627
627
|
monkeypatch.setattr(grpc_acc, "rpc_client", rpc_client)
|
|
628
628
|
monkeypatch.setattr("remoterf.drivers.dynamic_device.install_driver", install_driver)
|
|
629
|
-
account = grpc_acc.RemoteRFAccount("
|
|
629
|
+
account = grpc_acc.RemoteRFAccount("jdoe", "secret")
|
|
630
630
|
now = datetime.datetime.now()
|
|
631
631
|
|
|
632
632
|
assert account.reserve_device(0, now, now + datetime.timedelta(hours=1)) == "reservation-token"
|
|
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.3 → remoterf-2.2.5}/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
|