ui: implement ssh key control (#35518)

implement ssh key control
pull/35521/head
Dean Lee 2 days ago committed by GitHub
parent c1794e6f83
commit 2017bf970f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      selfdrive/ui/layouts/settings/developer.py
  2. 127
      selfdrive/ui/widgets/ssh_key.py

@ -1,6 +1,7 @@
from openpilot.system.ui.lib.application import Widget
from openpilot.system.ui.lib.list_view import ListView, toggle_item
from openpilot.common.params import Params
from openpilot.selfdrive.ui.widgets.ssh_key import ssh_key_item
# Description constants
DESCRIPTIONS = {
@ -9,6 +10,10 @@ DESCRIPTIONS = {
"See https://docs.comma.ai/how-to/connect-to-comma for more info."
),
'joystick_debug_mode': "Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)",
'ssh_key': (
"Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username " +
"other than your own. A comma employee will NEVER ask you to add their GitHub username."
),
}
@ -23,6 +28,7 @@ class DeveloperLayout(Widget):
initial_state=self._params.get_bool("AdbEnabled"),
callback=self._on_enable_adb,
),
ssh_key_item("SSH Key", description=DESCRIPTIONS["ssh_key"]),
toggle_item(
"Joystick Debug Mode",
description=DESCRIPTIONS["joystick_debug_mode"],

@ -0,0 +1,127 @@
import pyray as rl
import requests
import threading
import copy
from enum import Enum
from openpilot.common.params import Params
from openpilot.system.ui.lib.application import gui_app, DialogResult, FontWeight
from openpilot.system.ui.lib.button import gui_button, ButtonStyle
from openpilot.system.ui.lib.list_view import (
ItemAction,
ListItem,
BUTTON_HEIGHT,
BUTTON_BORDER_RADIUS,
BUTTON_FONT_SIZE,
BUTTON_WIDTH,
)
from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.system.ui.widgets.confirm_dialog import alert_dialog
from openpilot.system.ui.widgets.keyboard import Keyboard
class SshKeyActionState(Enum):
LOADING = "LOADING"
ADD = "ADD"
REMOVE = "REMOVE"
class SshKeyAction(ItemAction):
HTTP_TIMEOUT = 15 # seconds
MAX_WIDTH = 500
def __init__(self):
super().__init__(self.MAX_WIDTH, True)
self._keyboard = Keyboard()
self._params = Params()
self._error_message: str = ""
self._text_font = gui_app.font(FontWeight.MEDIUM)
self._refresh_state()
def _refresh_state(self):
self._username = self._params.get("GithubUsername", "")
self._state = SshKeyActionState.REMOVE if self._params.get("GithubSshKeys") else SshKeyActionState.ADD
def _render(self, rect: rl.Rectangle) -> bool:
# Show error dialog if there's an error
if self._error_message:
message = copy.copy(self._error_message)
gui_app.set_modal_overlay(lambda: alert_dialog(message))
self._username = ""
self._error_message = ""
# Draw username if exists
if self._username:
text_size = measure_text_cached(self._text_font, self._username, BUTTON_FONT_SIZE)
rl.draw_text_ex(
self._text_font,
self._username,
(rect.x + rect.width - BUTTON_WIDTH - text_size.x - 30, rect.y + (rect.height - text_size.y) / 2),
BUTTON_FONT_SIZE,
1.0,
rl.WHITE,
)
# Draw button
if gui_button(
rl.Rectangle(
rect.x + rect.width - BUTTON_WIDTH, rect.y + (rect.height - BUTTON_HEIGHT) / 2, BUTTON_WIDTH, BUTTON_HEIGHT
),
self._state.value,
is_enabled=self._state != SshKeyActionState.LOADING,
border_radius=BUTTON_BORDER_RADIUS,
font_size=BUTTON_FONT_SIZE,
button_style=ButtonStyle.LIST_ACTION,
):
self._handle_button_click()
return True
return False
def _handle_button_click(self):
if self._state == SshKeyActionState.ADD:
self._keyboard.clear()
self._keyboard.set_title("Enter your GitHub username")
gui_app.set_modal_overlay(self._keyboard, callback=self._on_username_submit)
elif self._state == SshKeyActionState.REMOVE:
self._params.remove("GithubUsername")
self._params.remove("GithubSshKeys")
self._refresh_state()
def _on_username_submit(self, result: DialogResult):
if result != DialogResult.CONFIRM:
return
username = self._keyboard.text.strip()
if not username:
return
self._state = SshKeyActionState.LOADING
threading.Thread(target=lambda: self._fetch_ssh_key(username), daemon=True).start()
def _fetch_ssh_key(self, username: str):
try:
url = f"https://github.com/{username}.keys"
response = requests.get(url, timeout=self.HTTP_TIMEOUT)
response.raise_for_status()
keys = response.text.strip()
if not keys:
raise requests.exceptions.HTTPError("No SSH keys found")
# Success - save keys
self._params.put("GithubUsername", username)
self._params.put("GithubSshKeys", keys)
self._state = SshKeyActionState.REMOVE
self._username = username
except requests.exceptions.Timeout:
self._error_message = "Request timed out"
self._state = SshKeyActionState.ADD
except Exception:
self._error_message = f"No SSH keys found for user '{username}'"
self._state = SshKeyActionState.ADD
def ssh_key_item(title: str, description: str):
return ListItem(title=title, description=description, action_item=SshKeyAction())
Loading…
Cancel
Save