Add Github SSH keys (#19879)

albatross
grekiki 2021-01-27 12:07:17 +01:00 committed by GitHub
parent 80799c7272
commit 7408569c1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 301 additions and 36 deletions

View File

@ -140,6 +140,7 @@ def flash_agnos_update(manifest_path, cloudlog, spinner=None):
break
except requests.exceptions.RequestException:
cloudlog.exception("Failed")
spinner.update("Waiting for internet...")
cloudlog.info(f"Failed to download {partition['name']}, retrying ({retries})")
time.sleep(10)

View File

@ -18,8 +18,8 @@ else:
qt_libs = qt_env["LIBS"] + libs + ["pthread", "ssl", "crypto"]
widgets = qt_env.Library("qt_widgets",
["qt/qt_window.cc", "qt/qt_sound.cc", "qt/widgets/keyboard.cc", "qt/widgets/input_field.cc", "qt/widgets/drive_stats.cc",
"qt/offroad/wifi.cc", "qt/offroad/wifiManager.cc", "qt/widgets/toggle.cc", "qt/widgets/offroad_alerts.cc", "qt/widgets/setup.cc"],
["qt/qt_window.cc", "qt/qt_sound.cc", "qt/widgets/keyboard.cc", "qt/widgets/input_field.cc", "qt/widgets/drive_stats.cc", "qt/widgets/ssh_keys.cc",
"qt/offroad/networking.cc", "qt/offroad/wifiManager.cc", "qt/widgets/toggle.cc", "qt/widgets/offroad_alerts.cc", "qt/widgets/setup.cc"],
LIBS=qt_libs)
qt_libs.append(widgets)

View File

@ -6,8 +6,7 @@
#include <QLineEdit>
#include <QRandomGenerator>
#include "wifi.hpp"
#include "widgets/toggle.hpp"
#include "networking.hpp"
void clearLayout(QLayout* layout) {
while (QLayoutItem* item = layout->takeAt(0)) {
@ -27,6 +26,20 @@ QWidget* layoutToWidget(QLayout* l, QWidget* parent){
return q;
}
// https://stackoverflow.com/questions/478898/how-do-i-execute-a-command-and-get-the-output-of-the-command-within-c-using-po
std::string exec(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}
// Networking functions
Networking::Networking(QWidget* parent) : QWidget(parent){
@ -179,7 +192,7 @@ QFrame* hline(QWidget* parent = 0){
// AdvancedNetworking functions
AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWidget(parent), wifi(wifi){
s = new QStackedLayout;// inputField and settings
s = new QStackedLayout;// inputField, mainPage, SSH settings
inputField = new InputField(this, 8);
connect(inputField, SIGNAL(emitText(QString)), this, SLOT(receiveText(QString)));
connect(inputField, SIGNAL(cancel()), this, SLOT(abortTextInput()));
@ -191,7 +204,7 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
//Back button
QHBoxLayout* backLayout = new QHBoxLayout;
QPushButton* back = new QPushButton("BACK");
back->setFixedWidth(500);
back->setFixedSize(500, 100);
connect(back, &QPushButton::released, [=](){emit backPress();});
backLayout->addWidget(back, 0, Qt::AlignLeft);
vlayout->addWidget(layoutToWidget(backLayout, this), 0, Qt::AlignLeft);
@ -222,28 +235,36 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
//IP adress
QHBoxLayout* IPlayout = new QHBoxLayout;
IPlayout->addWidget(new QLabel("IP address: "), 0);
IPlayout->addWidget(new QLabel("IP address"), 0);
ipLabel = new QLabel(wifi->ipv4_address);
ipLabel->setStyleSheet("color: #aaaaaa");
IPlayout->addWidget(ipLabel, 0, Qt::AlignRight);
vlayout->addWidget(layoutToWidget(IPlayout, this), 0);
vlayout->addWidget(hline(), 0);
vlayout->addSpacing(300);
// //Enable SSH
// QHBoxLayout* enableSSHLayout = new QHBoxLayout(this);
// enableSSHLayout->addWidget(new QLabel("Enable SSH", this));
// Toggle* toggle_switch_SSH = new Toggle(this);
// toggle_switch_SSH->setFixedSize(150, 100);
// enableSSHLayout->addWidget(toggle_switch_SSH);
// vlayout->addWidget(layoutToWidget(enableSSHLayout, this));
//Enable SSH
QHBoxLayout* enableSSHLayout = new QHBoxLayout(this);
enableSSHLayout->addWidget(new QLabel("Enable SSH", this));
toggle_switch_SSH = new Toggle(this);
toggle_switch_SSH->immediateOffset = 40;
toggle_switch_SSH->setFixedSize(150, 100);
if (isSSHEnabled()) {
toggle_switch_SSH->togglePosition();
}
QObject::connect(toggle_switch_SSH, SIGNAL(stateChanged(int)), this, SLOT(toggleSSH(int)));
enableSSHLayout->addWidget(toggle_switch_SSH);
vlayout->addWidget(layoutToWidget(enableSSHLayout, this));
vlayout->addWidget(hline(), 0);
// //Authorized SSH keys
// QHBoxLayout* authSSHLayout = new QHBoxLayout(this);
// authSSHLayout->addWidget(new QLabel("Authorized SSH keys", this));
// QPushButton* editAuthSSHButton = new QPushButton("EDIT", this);
// authSSHLayout->addWidget(editAuthSSHButton);
// vlayout->addWidget(layoutToWidget(authSSHLayout, this));
//Authorized SSH keys
QHBoxLayout* authSSHLayout = new QHBoxLayout(this);
authSSHLayout->addWidget(new QLabel("Authorized SSH keys", this));
QPushButton* editAuthSSHButton = new QPushButton("EDIT", this);
editAuthSSHButton->setFixedWidth(500);
connect(editAuthSSHButton, &QPushButton::released, [=](){s->setCurrentIndex(2);});
authSSHLayout->addWidget(editAuthSSHButton);
vlayout->addWidget(layoutToWidget(authSSHLayout, this));
vlayout->addSpacing(50);
// //Disconnect or delete connections
// QHBoxLayout* dangerZone = new QHBoxLayout(this);
@ -258,11 +279,24 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
settingsWidget->setStyleSheet("margin-left: 40px; margin-right: 40px;");
s->addWidget(settingsWidget);
s->setCurrentIndex(1);
ssh = new SSH;
connect(ssh, &SSH::closeSSHSettings, [=](){s->setCurrentIndex(1);});
s->addWidget(ssh);
setLayout(s);
}
bool AdvancedNetworking::isSSHEnabled(){
QString response = QString::fromStdString(exec("systemctl is-active ssh"));
return response.startsWith("active");
}
void AdvancedNetworking::refresh(){
ipLabel->setText(wifi->ipv4_address);
if (toggle_switch_SSH->on != isSSHEnabled()) {
toggle_switch_SSH->togglePosition();
}
}
void AdvancedNetworking::toggleTethering(int enable) {
@ -273,7 +307,16 @@ void AdvancedNetworking::toggleTethering(int enable) {
}
editPasswordButton->setEnabled(!enable);
}
void AdvancedNetworking::toggleSSH(int enable) {
if (enable) {
system("sudo systemctl enable ssh");
system("sudo systemctl start ssh");
} else {
system("sudo systemctl stop ssh");
system("sudo systemctl disable ssh");
}
}
void AdvancedNetworking::receiveText(QString text){
wifi->changeTetheringPassword(text);
s->setCurrentIndex(1);

View File

@ -9,7 +9,8 @@
#include "wifiManager.hpp"
#include "widgets/input_field.hpp"
#include "widgets/ssh_keys.hpp"
#include "widgets/toggle.hpp"
class WifiUI : public QWidget {
Q_OBJECT
@ -49,10 +50,12 @@ private:
InputField* inputField;
QLabel* ipLabel;
QPushButton* editPasswordButton;
SSH* ssh;
Toggle* toggle_switch_SSH;
WifiManager* wifi = nullptr;
bool isSSHEnabled();
signals:
void openKeyboard();
void closeKeyboard();
@ -62,6 +65,7 @@ public slots:
void receiveText(QString text);
void abortTextInput();
void toggleTethering(int enable);
void toggleSSH(int enable);
void refresh();
};

View File

@ -10,7 +10,7 @@
#include <QLabel>
#include <QPixmap>
#include "wifi.hpp"
#include "networking.hpp"
#include "settings.hpp"
#include "widgets/toggle.hpp"
#include "widgets/offroad_alerts.hpp"
@ -145,7 +145,7 @@ QWidget * device_panel() {
//}
for (auto &l : labels) {
device_layout->addWidget(labelWidget(QString::fromStdString(l.first+":"), QString::fromStdString(l.second)), 0, Qt::AlignTop);
device_layout->addWidget(labelWidget(QString::fromStdString(l.first), QString::fromStdString(l.second)), 0, Qt::AlignTop);
}
// TODO: show current calibration values
@ -206,7 +206,7 @@ QWidget * developer_panel() {
for (int i = 0; i<labels.size(); i++) {
auto l = labels[i];
main_layout->addWidget(labelWidget(QString::fromStdString(l.first+":"), QString::fromStdString(l.second)));
main_layout->addWidget(labelWidget(QString::fromStdString(l.first), QString::fromStdString(l.second)));
if(i+1<labels.size()) {
main_layout->addWidget(horizontal_line());

View File

@ -7,7 +7,7 @@
#include <QButtonGroup>
#include <QStackedLayout>
#include "wifi.hpp"
#include "networking.hpp"
// *** settings widgets ***

View File

@ -81,7 +81,7 @@ Keyboard::Keyboard(QWidget *parent) : QWidget(parent) {
// Special characters
std::vector<QVector<QString>> specials = {
{"[","]","{","}","#","%","^","*","+","="},
{"_","\\","|","~","<",">","","£","¥"," "},
{"_","\\","|","~","<",">","","£","¥",""},
{"123",".",",","?","!","`",""},
{"ABC"," ",""},
};

View File

@ -0,0 +1,165 @@
#include <QDebug>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QState>
#include <QStateMachine>
#include <QNetworkReply>
#include "widgets/ssh_keys.hpp"
#include "widgets/input_field.hpp"
#include "common/params.h"
QWidget* layout_to_widget(QLayout* l){
QWidget* q = new QWidget;
q->setLayout(l);
return q;
}
SSH::SSH(QWidget* parent) : QWidget(parent){
// init variables
manager = new QNetworkAccessManager(this);
networkTimer = new QTimer(this);
networkTimer->setSingleShot(true);
networkTimer->setInterval(5000);
connect(networkTimer, SIGNAL(timeout()), this, SLOT(timeout()));
// Construct the layouts to display
slayout = new QStackedLayout(this); // Initial screen, input, waiting for response
//Layout on entering
QVBoxLayout* initialLayout = new QVBoxLayout;
initialLayout->setContentsMargins(80, 80, 80, 80);
QHBoxLayout* header = new QHBoxLayout;
QPushButton* exitButton = new QPushButton("BACK", this);
exitButton->setFixedSize(500, 100);
header->addWidget(exitButton, 0, Qt::AlignLeft | Qt::AlignTop);
initialLayout->addWidget(layout_to_widget(header));
QLabel* title = new QLabel("Authorize SSH keys");
title->setStyleSheet(R"(font-size: 75px;)");
header->addWidget(title, 0, Qt::AlignRight | Qt::AlignTop);
QLabel* wallOfText = new QLabel("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.");
wallOfText->setWordWrap(true);
wallOfText->setStyleSheet(R"(font-size: 60px;)");
initialLayout->addWidget(wallOfText, 0);
QPushButton* actionButton = new QPushButton;
actionButton->setFixedHeight(100);
initialLayout->addWidget(actionButton, 0, Qt::AlignBottom);
slayout->addWidget(layout_to_widget(initialLayout));
InputField* input = new InputField;
slayout->addWidget(input);
QLabel* loading = new QLabel("Loading SSH keys from GitHub.");
slayout->addWidget(loading);
setStyleSheet(R"(
QPushButton {
font-size: 60px;
margin: 0px;
padding: 15px;
border-radius: 25px;
color: #dddddd;
background-color: #444444;
}
)");
setLayout(slayout);
//Initialize the state machine and states
QStateMachine* state = new QStateMachine(this);
QState* initialState = new QState(); //State when entering the widget
QState* initialStateNoGithub = new QState(); //Starting state, key not connected
QState* initialStateConnected = new QState(); //Starting state, ssh connected
QState* quitState = new QState(); // State when exiting the widget
QState* removeSSH_State = new QState(); // State when user wants to remove the SSH keys
QState* defaultInputFieldState = new QState(); // State when we want the user to give us the username
QState* loadingState = new QState(); // State while waiting for the network response
// Adding states to the state machine and adding the transitions
state->addState(initialState);
connect(initialState, &QState::entered, [=](){checkForSSHKey(); slayout->setCurrentIndex(0);});
initialState->addTransition(this, &SSH::NoSSHAdded, initialStateNoGithub);
initialState->addTransition(this, &SSH::SSHAdded, initialStateConnected);
state->addState(quitState);
connect(quitState, &QState::entered, [=](){emit closeSSHSettings();});
quitState->addTransition(quitState, &QState::entered, initialState);
state->addState(initialStateConnected);
connect(initialStateConnected, &QState::entered, [=](){actionButton->setText("Remove GitHub SSH keys"); actionButton->setStyleSheet(R"(background-color: #750c0c;)");});
initialStateConnected->addTransition(exitButton, &QPushButton::released, quitState);
initialStateConnected->addTransition(actionButton, &QPushButton::released, removeSSH_State);
state->addState(removeSSH_State);
connect(removeSSH_State, &QState::entered, [=](){Params().delete_db_value("GithubSshKeys");});
removeSSH_State->addTransition(removeSSH_State, &QState::entered, initialState);
state->addState(initialStateNoGithub);
connect(initialStateNoGithub, &QState::entered, [=](){actionButton->setText("Link GitHub SSH keys"); actionButton->setStyleSheet(R"(background-color: #444444;)");});
initialStateNoGithub->addTransition(exitButton, &QPushButton::released, quitState);
initialStateNoGithub->addTransition(actionButton, &QPushButton::released, defaultInputFieldState);
connect(actionButton, &QPushButton::released, [=](){input->setPromptText("Enter your GitHub username");});
state->addState(defaultInputFieldState);
connect(defaultInputFieldState, &QState::entered, [=](){slayout->setCurrentIndex(1);});
connect(input, &InputField::emitText, [=](QString a){usernameGitHub = a;}); // Store the string the user provided
defaultInputFieldState->addTransition(input, &InputField::cancel, initialState);
defaultInputFieldState->addTransition(input, &InputField::emitText, loadingState);
state->addState(loadingState);
connect(loadingState, &QState::entered, [=](){slayout->setCurrentIndex(2); getSSHKeys();});
connect(this, &SSH::failedResponse, [=](QString message){input->setPromptText(message);});
loadingState->addTransition(this, &SSH::failedResponse, defaultInputFieldState);
loadingState->addTransition(this, &SSH::gotSSHKeys, initialState);
state->setInitialState(initialState);
state->start();
}
void SSH::checkForSSHKey(){
QString SSHKey = QString::fromStdString(Params().get("GithubSshKeys"));
if (SSHKey.length()) {
emit SSHAdded();
} else {
emit NoSSHAdded();
}
}
void SSH::getSSHKeys(){
QString url = "https://github.com/" + usernameGitHub + ".keys";
aborted = false;
reply = manager->get(QNetworkRequest(QUrl(url)));
connect(reply, SIGNAL(finished()), this, SLOT(parseResponse()));
networkTimer->start();
}
void SSH::timeout(){
aborted = true;
reply->abort();
}
void SSH::parseResponse(){
if (!aborted) {
networkTimer->stop();
QString response = reply->readAll();
if (reply->error() == QNetworkReply::NoError && response.length()) {
Params().write_db_value("GithubSshKeys", response.toStdString());
emit gotSSHKeys();
} else {
emit failedResponse("Username "+usernameGitHub+" doesn't exist");
}
}else{
emit failedResponse("Request timed out");
}
reply->deleteLater();
reply = NULL;
}

View File

@ -0,0 +1,43 @@
#pragma once
#include <QWidget>
#include <QButtonGroup>
#include <QVBoxLayout>
#include <QStackedWidget>
#include <QPushButton>
#include <QTimer>
#include <QNetworkAccessManager>
#include "widgets/input_field.hpp"
class SSH : public QWidget {
Q_OBJECT
public:
explicit SSH(QWidget* parent = 0);
private:
InputField* inputField;
QStackedLayout* slayout;
QString usernameGitHub;
QNetworkAccessManager* manager;
QNetworkReply* reply;
QTimer* networkTimer;
bool aborted;
signals:
void closeSSHSettings();
void openKeyboard();
void closeKeyboard();
void NoSSHAdded();
void SSHAdded();
void failedResponse(QString errorString);
void gotSSHKeys();
private slots:
void checkForSSHKey();
void getSSHKeys();
void timeout();
void parseResponse();
};

View File

@ -3,7 +3,7 @@
Toggle::Toggle(QWidget *parent) : QAbstractButton(parent),
_height(80),
_height_rect(60),
_on(false),
on(false),
_anim(new QPropertyAnimation(this, "offset_circle", this))
{
_radius = _height / 2;
@ -32,20 +32,27 @@ void Toggle::paintEvent(QPaintEvent *e) {
}
void Toggle::mouseReleaseEvent(QMouseEvent *e) {
const int left = _radius;
const int right = width() - _radius;
if(_x_circle != left && _x_circle != right){
//Don't parse touch events, while the animation is running
return;
}
if (e->button() & Qt::LeftButton) {
togglePosition();
emit stateChanged(_on);
emit stateChanged(on);
}
}
void Toggle::togglePosition() {
_on = !_on;
on = !on;
const int left = _radius;
const int right = width() - _radius;
_anim->setStartValue(_on ? left : right);
_anim->setEndValue(_on ? right : left);
_anim->setDuration(120);
_anim->setStartValue(on ? left + immediateOffset : right - immediateOffset);
_anim->setEndValue(on ? right : left);
_anim->setDuration(animation_duration);
_anim->start();
repaint();
}
void Toggle::enterEvent(QEvent *e) {

View File

@ -8,7 +8,9 @@ class Toggle : public QAbstractButton {
public:
Toggle(QWidget* parent = nullptr);
void togglePosition();
bool on;
int animation_duration = 250;
int immediateOffset = 0;
int offset_circle() const {
return _x_circle;
}
@ -18,13 +20,13 @@ public:
update();
}
protected:
void paintEvent(QPaintEvent*) override;
void mouseReleaseEvent(QMouseEvent*) override;
void enterEvent(QEvent*) override;
private:
bool _on;
int _x_circle, _y_circle;
int _height, _radius;
int _height_rect, _y_rect;