Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): add AuthorizedKeys reset option for the OT-2 + fixes #13745

Merged
merged 3 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/src/opentrons/config/reset.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class ResetOptionId(str, Enum):
ResetOptionId.pipette_offset,
ResetOptionId.tip_length_calibrations,
ResetOptionId.runs_history,
ResetOptionId.authorized_keys,
]
_FLEX_RESET_OPTIONS = [
ResetOptionId.boot_scripts,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ stages:
- id: runsHistory
name: Clear Runs History
description: !re_search 'Erase this device''s stored history of protocols and runs.'
- id: authorizedKeys
name: SSH Authorized Keys
description: !re_search 'Clear the ssh authorized keys'

---
test_name: POST Reset bootScripts option
marks:
Expand Down Expand Up @@ -123,6 +127,32 @@ stages:
message: "gripperOffsetCalibrations is not a valid reset option."
errorCode: "4000"
---
test_name: POST Reset authorizedKeys option
marks:
- usefixtures:
- ot2_server_base_url
stages:
- name: POST Reset authorizedKeys true
request:
url: '{ot2_server_base_url}/settings/reset'
method: POST
json:
authorizedKeys: true
response:
status_code: 200
json:
message: "Options 'authorized_keys' were reset"
- name: POST Reset authorizedKeys false
request:
url: '{ot2_server_base_url}/settings/reset'
method: POST
json:
authorizedKeys: false
response:
status_code: 200
json:
message: 'Nothing to do'
---
test_name: POST Reset non existant option
marks:
- usefixtures:
Expand Down
5 changes: 5 additions & 0 deletions robot-server/tests/service/legacy/routers/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ def test_available_resets(api_client):
"bootScripts",
"tipLengthCalibrations",
"runsHistory",
"authorizedKeys",
]
) == sorted([item["id"] for item in options_list])

Expand Down Expand Up @@ -551,6 +552,7 @@ async def mock_get_persistence_resetter() -> PersistenceResetter:
"pipetteOffsetCalibrations": False,
"tipLengthCalibrations": False,
"runsHistory": False,
"authorizedKeys": False,
},
set(),
],
Expand All @@ -562,6 +564,7 @@ async def mock_get_persistence_resetter() -> PersistenceResetter:
"tipLengthCalibrations": True,
"deckCalibration": True,
"runsHistory": True,
"authorizedKeys": True,
# TODO(mm, 2023-08-04): Figure out how to test Flex-only options,
# then add gripperOffsetCalibrations and onDeviceDisplay.
},
Expand All @@ -575,8 +578,10 @@ async def mock_get_persistence_resetter() -> PersistenceResetter:
# mark_directory_reset() being an async method, and api_client having
# its own event loop that interferes with making this test async.
ResetOptionId.runs_history,
ResetOptionId.authorized_keys,
},
],
[{"authorizedKeys": True}, {ResetOptionId.authorized_keys}],
[{"bootScripts": True}, {ResetOptionId.boot_scripts}],
[{"pipetteOffsetCalibrations": True}, {ResetOptionId.pipette_offset}],
[{"tipLengthCalibrations": True}, {ResetOptionId.tip_length_calibrations}],
Expand Down
27 changes: 16 additions & 11 deletions update-server/otupdate/common/ssh_key_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,8 @@
Path(root, file)
for root, _, files in os.walk("/media")
for file in files
if file.endswith(".pub")
# skip hidden files
if not file.startswith(".") and file.endswith(".pub")
]
if not pub_keys:
LOG.warning("No keys found")
Expand All @@ -265,16 +266,20 @@
new_keys = list()
with open(AUTHORIZED_KEYS, "a") as fh:
for key in pub_keys:
with open(key, "r") as gh:
ssh_key = gh.read()
if "ssh-rsa" not in ssh_key:
LOG.warning(f"Invalid ssh public key: {key}")
continue
key_hash = hashlib.new("md5", ssh_key.encode()).hexdigest()
if not key_present(key_hash):
fh.write(f"{ssh_key}\n")
LOG.info(f"Added new rsa key: {key}")
new_keys.append(key_hash)
try:
with open(key, "r") as gh:
ssh_key = gh.read()
if "ssh-rsa" not in ssh_key and "ecdsa" not in ssh_key:
LOG.warning(f"Invalid ssh public key: {key}")
continue
key_hash = hashlib.new("md5", ssh_key.encode()).hexdigest()
if not key_present(key_hash):
fh.write(f"{ssh_key}\n")
LOG.info(f"Added new rsa key: {key}")
new_keys.append(key_hash)
except Exception as e:
LOG.error(f"Could not process ssh public key: {key} {e}")
continue

Check warning on line 282 in update-server/otupdate/common/ssh_key_management.py

View check run for this annotation

Codecov / codecov/patch

update-server/otupdate/common/ssh_key_management.py#L269-L282

Added lines #L269 - L282 were not covered by tests

return web.json_response( # type: ignore[no-untyped-call,no-any-return]
data={"message": f"Added {len(new_keys)} new keys", "key_md5": new_keys},
Expand Down