forked from qt-creator/qt-creator
Copilot: Add support for proxy settings
For easier testing a docker file is added. You can start "buildandrun.sh" in copilot/tests/proxy to get a simple proxy server up and running. The argument "PWDMODE" in buildandrun.sh can be set to "with" and "without" to get a proxy server that needs a password or not. The username and password are user/1234. Fixes: QTCREATORBUG-29485 Change-Id: I3859c9ad04ebd4f9349e25665ba710e23fb64dea Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -44,18 +44,21 @@ AuthWidget::AuthWidget(QWidget *parent)
|
||||
}.attachTo(this);
|
||||
// clang-format on
|
||||
|
||||
connect(m_button, &QPushButton::clicked, this, [this]() {
|
||||
if (m_status == Status::SignedIn)
|
||||
signOut();
|
||||
else if (m_status == Status::SignedOut)
|
||||
signIn();
|
||||
});
|
||||
|
||||
auto update = [this] {
|
||||
updateClient(FilePath::fromUserInput(settings().nodeJsPath.volatileValue()),
|
||||
FilePath::fromUserInput(settings().distPath.volatileValue()));
|
||||
};
|
||||
|
||||
connect(m_button, &QPushButton::clicked, this, [this, update]() {
|
||||
if (m_status == Status::SignedIn)
|
||||
signOut();
|
||||
else if (m_status == Status::SignedOut)
|
||||
signIn();
|
||||
else
|
||||
update();
|
||||
});
|
||||
|
||||
connect(&settings(), &CopilotSettings::applied, this, update);
|
||||
connect(settings().nodeJsPath.pathChooser(), &PathChooser::textChanged, this, update);
|
||||
connect(settings().distPath.pathChooser(), &PathChooser::textChanged, this, update);
|
||||
|
||||
@@ -68,35 +71,39 @@ AuthWidget::~AuthWidget()
|
||||
LanguageClientManager::shutdownClient(m_client);
|
||||
}
|
||||
|
||||
void AuthWidget::setState(const QString &buttonText, bool working)
|
||||
void AuthWidget::setState(const QString &buttonText, const QString &errorText, bool working)
|
||||
{
|
||||
m_button->setText(buttonText);
|
||||
m_button->setVisible(true);
|
||||
m_progressIndicator->setVisible(working);
|
||||
m_statusLabel->setText(errorText);
|
||||
m_statusLabel->setVisible(!m_statusLabel->text().isEmpty());
|
||||
m_button->setEnabled(!working);
|
||||
}
|
||||
|
||||
void AuthWidget::checkStatus()
|
||||
{
|
||||
if (!isEnabled())
|
||||
return;
|
||||
|
||||
QTC_ASSERT(m_client && m_client->reachable(), return);
|
||||
|
||||
setState("Checking status ...", true);
|
||||
setState("Checking status ...", {}, true);
|
||||
|
||||
m_client->requestCheckStatus(false, [this](const CheckStatusRequest::Response &response) {
|
||||
if (response.error()) {
|
||||
setState("failed: " + response.error()->message(), false);
|
||||
setState("Failed to authenticate", response.error()->message(), false);
|
||||
return;
|
||||
}
|
||||
const CheckStatusResponse result = *response.result();
|
||||
|
||||
if (result.user().isEmpty()) {
|
||||
setState("Sign in", false);
|
||||
setState("Sign in", {}, false);
|
||||
m_status = Status::SignedOut;
|
||||
return;
|
||||
}
|
||||
|
||||
setState("Sign out " + result.user(), false);
|
||||
setState("Sign out " + result.user(), {}, false);
|
||||
m_status = Status::SignedIn;
|
||||
});
|
||||
}
|
||||
@@ -105,12 +112,12 @@ void AuthWidget::updateClient(const FilePath &nodeJs, const FilePath &agent)
|
||||
{
|
||||
LanguageClientManager::shutdownClient(m_client);
|
||||
m_client = nullptr;
|
||||
setState(Tr::tr("Sign In"), false);
|
||||
setState(Tr::tr("Sign In"), {}, false);
|
||||
m_button->setEnabled(false);
|
||||
if (!nodeJs.isExecutableFile() || !agent.exists())
|
||||
return;
|
||||
|
||||
setState(Tr::tr("Sign In"), true);
|
||||
setState(Tr::tr("Sign In"), {}, true);
|
||||
|
||||
m_client = new CopilotClient(nodeJs, agent);
|
||||
connect(m_client, &Client::initialized, this, &AuthWidget::checkStatus);
|
||||
@@ -127,7 +134,7 @@ void AuthWidget::signIn()
|
||||
qCritical() << "Not implemented";
|
||||
QTC_ASSERT(m_client && m_client->reachable(), return);
|
||||
|
||||
setState("Signing in ...", true);
|
||||
setState("Signing in ...", {}, true);
|
||||
|
||||
m_client->requestSignInInitiate([this](const SignInInitiateRequest::Response &response) {
|
||||
QTC_ASSERT(!response.error(), return);
|
||||
@@ -144,19 +151,17 @@ void AuthWidget::signIn()
|
||||
m_client
|
||||
->requestSignInConfirm(response.result()->userCode(),
|
||||
[this](const SignInConfirmRequest::Response &response) {
|
||||
m_statusLabel->setText("");
|
||||
|
||||
if (response.error()) {
|
||||
QMessageBox::critical(this,
|
||||
Tr::tr("Login Failed"),
|
||||
Tr::tr(
|
||||
"The login request failed: ")
|
||||
+ response.error()->message());
|
||||
setState("Sign in", false);
|
||||
setState("Sign in", response.error()->message(), false);
|
||||
return;
|
||||
}
|
||||
|
||||
setState("Sign Out " + response.result()->user(), false);
|
||||
setState("Sign Out " + response.result()->user(), {}, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -165,7 +170,7 @@ void AuthWidget::signOut()
|
||||
{
|
||||
QTC_ASSERT(m_client && m_client->reachable(), return);
|
||||
|
||||
setState("Signing out ...", true);
|
||||
setState("Signing out ...", {}, true);
|
||||
|
||||
m_client->requestSignOut([this](const SignOutRequest::Response &response) {
|
||||
QTC_ASSERT(!response.error(), return);
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
void updateClient(const Utils::FilePath &nodeJs, const Utils::FilePath &agent);
|
||||
|
||||
private:
|
||||
void setState(const QString &buttonText, bool working);
|
||||
void setState(const QString &buttonText, const QString &errorText, bool working);
|
||||
void checkStatus();
|
||||
|
||||
|
||||
|
||||
@@ -2,26 +2,32 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "copilotclient.h"
|
||||
#include "copilotconstants.h"
|
||||
#include "copilotsettings.h"
|
||||
#include "copilotsuggestion.h"
|
||||
#include "copilottr.h"
|
||||
|
||||
#include <app/app_version.h>
|
||||
|
||||
#include <languageclient/languageclientinterface.h>
|
||||
#include <languageclient/languageclientmanager.h>
|
||||
#include <languageclient/languageclientsettings.h>
|
||||
#include <languageserverprotocol/lsptypes.h>
|
||||
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <projectexplorer/projectmanager.h>
|
||||
|
||||
#include <utils/filepath.h>
|
||||
|
||||
#include <texteditor/textdocumentlayout.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include <languageserverprotocol/lsptypes.h>
|
||||
#include <utils/checkablemessagebox.h>
|
||||
#include <utils/filepath.h>
|
||||
#include <utils/passworddialog.h>
|
||||
|
||||
#include <QInputDialog>
|
||||
#include <QLoggingCategory>
|
||||
#include <QTimer>
|
||||
#include <QToolButton>
|
||||
|
||||
@@ -31,6 +37,8 @@ using namespace Utils;
|
||||
using namespace ProjectExplorer;
|
||||
using namespace Core;
|
||||
|
||||
Q_LOGGING_CATEGORY(copilotClientLog, "qtc.copilot.client", QtWarningMsg)
|
||||
|
||||
namespace Copilot::Internal {
|
||||
|
||||
static LanguageClient::BaseClientInterface *clientInterface(const FilePath &nodePath,
|
||||
@@ -52,6 +60,23 @@ CopilotClient::CopilotClient(const FilePath &nodePath, const FilePath &distPath)
|
||||
langFilter.filePattern = {"*"};
|
||||
|
||||
setSupportedLanguage(langFilter);
|
||||
|
||||
registerCustomMethod("LogMessage", [this](const LanguageServerProtocol::JsonRpcMessage &message) {
|
||||
QString msg = message.toJsonObject().value("params").toObject().value("message").toString();
|
||||
qCDebug(copilotClientLog) << message.toJsonObject()
|
||||
.value("params")
|
||||
.toObject()
|
||||
.value("message")
|
||||
.toString();
|
||||
|
||||
if (msg.contains("Socket Connect returned status code,407")) {
|
||||
qCWarning(copilotClientLog) << "Proxy authentication required";
|
||||
QMetaObject::invokeMethod(this,
|
||||
&CopilotClient::proxyAuthenticationFailed,
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
});
|
||||
|
||||
start();
|
||||
|
||||
auto openDoc = [this](IDocument *document) {
|
||||
@@ -68,6 +93,8 @@ CopilotClient::CopilotClient(const FilePath &nodePath, const FilePath &distPath)
|
||||
closeDocument(textDocument);
|
||||
});
|
||||
|
||||
connect(this, &LanguageClient::Client::initialized, this, &CopilotClient::requestSetEditorInfo);
|
||||
|
||||
for (IDocument *doc : DocumentModel::openedDocuments())
|
||||
openDoc(doc);
|
||||
}
|
||||
@@ -218,6 +245,32 @@ void CopilotClient::cancelRunningRequest(TextEditor::TextEditorWidget *editor)
|
||||
m_runningRequests.erase(it);
|
||||
}
|
||||
|
||||
static QString currentProxyPassword;
|
||||
|
||||
void CopilotClient::requestSetEditorInfo()
|
||||
{
|
||||
if (settings().saveProxyPassword())
|
||||
currentProxyPassword = settings().proxyPassword();
|
||||
|
||||
const EditorInfo editorInfo{Core::Constants::IDE_VERSION_DISPLAY, "Qt Creator"};
|
||||
const EditorPluginInfo editorPluginInfo{Core::Constants::IDE_VERSION_DISPLAY,
|
||||
"Qt Creator Copilot plugin"};
|
||||
|
||||
SetEditorInfoParams params(editorInfo, editorPluginInfo);
|
||||
|
||||
if (settings().useProxy()) {
|
||||
params.setNetworkProxy(
|
||||
Copilot::NetworkProxy{settings().proxyHost(),
|
||||
static_cast<int>(settings().proxyPort()),
|
||||
settings().proxyUser(),
|
||||
currentProxyPassword,
|
||||
settings().proxyRejectUnauthorized()});
|
||||
}
|
||||
|
||||
SetEditorInfoRequest request(params);
|
||||
sendMessage(request);
|
||||
}
|
||||
|
||||
void CopilotClient::requestCheckStatus(
|
||||
bool localChecksOnly, std::function<void(const CheckStatusRequest::Response &response)> callback)
|
||||
{
|
||||
@@ -269,4 +322,36 @@ bool CopilotClient::isEnabled(Project *project)
|
||||
return settings.isEnabled();
|
||||
}
|
||||
|
||||
void CopilotClient::proxyAuthenticationFailed()
|
||||
{
|
||||
static bool doNotAskAgain = false;
|
||||
|
||||
if (m_isAskingForPassword || !settings().enableCopilot())
|
||||
return;
|
||||
|
||||
m_isAskingForPassword = true;
|
||||
|
||||
auto answer = Utils::PasswordDialog::getUserAndPassword(
|
||||
Tr::tr("Copilot"),
|
||||
Tr::tr("Proxy username and password required:"),
|
||||
Tr::tr("Do not ask again. This will disable Copilot for now."),
|
||||
settings().proxyUser(),
|
||||
&doNotAskAgain,
|
||||
Core::ICore::dialogParent());
|
||||
|
||||
if (answer) {
|
||||
settings().proxyUser.setValue(answer->first);
|
||||
currentProxyPassword = answer->second;
|
||||
} else {
|
||||
settings().enableCopilot.setValue(false);
|
||||
}
|
||||
|
||||
if (settings().saveProxyPassword())
|
||||
settings().proxyPassword.setValue(currentProxyPassword);
|
||||
|
||||
settings().apply();
|
||||
|
||||
m_isAskingForPassword = false;
|
||||
}
|
||||
|
||||
} // namespace Copilot::Internal
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "copilothoverhandler.h"
|
||||
#include "requests/checkstatus.h"
|
||||
#include "requests/getcompletions.h"
|
||||
#include "requests/seteditorinfo.h"
|
||||
#include "requests/signinconfirm.h"
|
||||
#include "requests/signininitiate.h"
|
||||
#include "requests/signout.h"
|
||||
@@ -50,7 +51,11 @@ public:
|
||||
|
||||
bool isEnabled(ProjectExplorer::Project *project);
|
||||
|
||||
void proxyAuthenticationFailed();
|
||||
|
||||
private:
|
||||
void requestSetEditorInfo();
|
||||
|
||||
QMap<TextEditor::TextEditorWidget *, GetCompletionRequest> m_runningRequests;
|
||||
struct ScheduleData
|
||||
{
|
||||
@@ -59,6 +64,7 @@ private:
|
||||
};
|
||||
QMap<TextEditor::TextEditorWidget *, ScheduleData> m_scheduledRequests;
|
||||
CopilotHoverHandler m_hoverHandler;
|
||||
bool m_isAskingForPassword{false};
|
||||
};
|
||||
|
||||
} // namespace Copilot::Internal
|
||||
|
||||
@@ -44,7 +44,6 @@ CopilotSettings::CopilotSettings()
|
||||
const FilePath nodeFromPath = FilePath("node").searchInPath();
|
||||
|
||||
const FilePaths searchDirs
|
||||
|
||||
= {FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/dist/agent.js"),
|
||||
FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/copilot/dist/agent.js"),
|
||||
FilePath::fromUserInput(
|
||||
@@ -80,13 +79,75 @@ CopilotSettings::CopilotSettings()
|
||||
|
||||
autoComplete.setDisplayName(Tr::tr("Auto Complete"));
|
||||
autoComplete.setSettingsKey("Copilot.Autocomplete");
|
||||
autoComplete.setLabelText(Tr::tr("Request completions automatically"));
|
||||
autoComplete.setLabelText(Tr::tr("Auto request"));
|
||||
autoComplete.setDefaultValue(true);
|
||||
autoComplete.setEnabler(&enableCopilot);
|
||||
autoComplete.setToolTip(Tr::tr("Automatically request suggestions for the current text cursor "
|
||||
"position after changes to the document."));
|
||||
autoComplete.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel);
|
||||
|
||||
useProxy.setDisplayName(Tr::tr("Use Proxy"));
|
||||
useProxy.setSettingsKey("Copilot.UseProxy");
|
||||
useProxy.setLabelText(Tr::tr("Use Proxy"));
|
||||
useProxy.setDefaultValue(false);
|
||||
useProxy.setEnabler(&enableCopilot);
|
||||
useProxy.setToolTip(Tr::tr("Use a proxy to connect to the Copilot servers."));
|
||||
useProxy.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel);
|
||||
|
||||
proxyHost.setDisplayName(Tr::tr("Proxy Host"));
|
||||
proxyHost.setDisplayStyle(StringAspect::LineEditDisplay);
|
||||
proxyHost.setSettingsKey("Copilot.ProxyHost");
|
||||
proxyHost.setLabelText(Tr::tr("Proxy Host"));
|
||||
proxyHost.setDefaultValue("");
|
||||
proxyHost.setEnabler(&useProxy);
|
||||
proxyHost.setToolTip(Tr::tr("The host name of the proxy server."));
|
||||
proxyHost.setHistoryCompleter("Copilot.ProxyHost.History");
|
||||
|
||||
proxyPort.setDisplayName(Tr::tr("Proxy Port"));
|
||||
proxyPort.setSettingsKey("Copilot.ProxyPort");
|
||||
proxyPort.setLabelText(Tr::tr("Proxy Port"));
|
||||
proxyPort.setDefaultValue(3128);
|
||||
proxyPort.setEnabler(&useProxy);
|
||||
proxyPort.setToolTip(Tr::tr("The port of the proxy server."));
|
||||
proxyPort.setRange(1, 65535);
|
||||
|
||||
proxyUser.setDisplayName(Tr::tr("Proxy User"));
|
||||
proxyUser.setDisplayStyle(StringAspect::LineEditDisplay);
|
||||
proxyUser.setSettingsKey("Copilot.ProxyUser");
|
||||
proxyUser.setLabelText(Tr::tr("Proxy User"));
|
||||
proxyUser.setDefaultValue("");
|
||||
proxyUser.setEnabler(&useProxy);
|
||||
proxyUser.setToolTip(Tr::tr("The user name for the proxy server."));
|
||||
proxyUser.setHistoryCompleter("Copilot.ProxyUser.History");
|
||||
|
||||
saveProxyPassword.setDisplayName(Tr::tr("Save Proxy Password"));
|
||||
saveProxyPassword.setSettingsKey("Copilot.SaveProxyPassword");
|
||||
saveProxyPassword.setLabelText(Tr::tr("Save Proxy Password"));
|
||||
saveProxyPassword.setDefaultValue(false);
|
||||
saveProxyPassword.setEnabler(&useProxy);
|
||||
saveProxyPassword.setToolTip(
|
||||
Tr::tr("Save the password for the proxy server (Password is stored insecurely!)."));
|
||||
saveProxyPassword.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel);
|
||||
|
||||
proxyPassword.setDisplayName(Tr::tr("Proxy Password"));
|
||||
proxyPassword.setDisplayStyle(StringAspect::PasswordLineEditDisplay);
|
||||
proxyPassword.setSettingsKey("Copilot.ProxyPassword");
|
||||
proxyPassword.setLabelText(Tr::tr("Proxy Password"));
|
||||
proxyPassword.setDefaultValue("");
|
||||
proxyPassword.setEnabler(&saveProxyPassword);
|
||||
proxyPassword.setToolTip(Tr::tr("The password for the proxy server."));
|
||||
|
||||
proxyRejectUnauthorized.setDisplayName(Tr::tr("Reject Unauthorized"));
|
||||
proxyRejectUnauthorized.setSettingsKey("Copilot.ProxyRejectUnauthorized");
|
||||
proxyRejectUnauthorized.setLabelText(Tr::tr("Reject Unauthorized"));
|
||||
proxyRejectUnauthorized.setDefaultValue(true);
|
||||
proxyRejectUnauthorized.setEnabler(&useProxy);
|
||||
proxyRejectUnauthorized.setToolTip(Tr::tr("Reject unauthorized certificates from the proxy "
|
||||
"server. This is a security risk."));
|
||||
proxyRejectUnauthorized.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel);
|
||||
|
||||
initEnableAspect(enableCopilot);
|
||||
enableCopilot.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel);
|
||||
|
||||
readSettings();
|
||||
}
|
||||
@@ -140,25 +201,24 @@ public:
|
||||
|
||||
auto warningLabel = new QLabel;
|
||||
warningLabel->setWordWrap(true);
|
||||
warningLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse
|
||||
| Qt::LinksAccessibleByKeyboard
|
||||
| Qt::TextSelectableByMouse);
|
||||
warningLabel->setText(Tr::tr(
|
||||
"Enabling %1 is subject to your agreement and abidance with your applicable "
|
||||
"%1 terms. It is your responsibility to know and accept the requirements and "
|
||||
"parameters of using tools like %1. This may include, but is not limited to, "
|
||||
"ensuring you have the rights to allow %1 access to your code, as well as "
|
||||
"understanding any implications of your use of %1 and suggestions produced "
|
||||
"(like copyright, accuracy, etc.)." ).arg("Copilot"));
|
||||
warningLabel->setTextInteractionFlags(
|
||||
Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard | Qt::TextSelectableByMouse);
|
||||
warningLabel->setText(
|
||||
Tr::tr("Enabling %1 is subject to your agreement and abidance with your applicable "
|
||||
"%1 terms. It is your responsibility to know and accept the requirements and "
|
||||
"parameters of using tools like %1. This may include, but is not limited to, "
|
||||
"ensuring you have the rights to allow %1 access to your code, as well as "
|
||||
"understanding any implications of your use of %1 and suggestions produced "
|
||||
"(like copyright, accuracy, etc.).")
|
||||
.arg("Copilot"));
|
||||
|
||||
auto authWidget = new AuthWidget();
|
||||
|
||||
auto helpLabel = new QLabel();
|
||||
helpLabel->setTextFormat(Qt::MarkdownText);
|
||||
helpLabel->setWordWrap(true);
|
||||
helpLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse
|
||||
| Qt::LinksAccessibleByKeyboard
|
||||
| Qt::TextSelectableByMouse);
|
||||
helpLabel->setTextInteractionFlags(
|
||||
Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard | Qt::TextSelectableByMouse);
|
||||
helpLabel->setOpenExternalLinks(true);
|
||||
connect(helpLabel, &QLabel::linkHovered, [](const QString &link) {
|
||||
QToolTip::showText(QCursor::pos(), link);
|
||||
@@ -176,14 +236,28 @@ public:
|
||||
.arg("[agent.js](https://github.com/github/copilot.vim/tree/release/dist)"));
|
||||
|
||||
Column {
|
||||
QString("<b>" + Tr::tr("Note:") + "</b>"), br,
|
||||
warningLabel, br,
|
||||
settings().enableCopilot, br,
|
||||
authWidget, br,
|
||||
settings().nodeJsPath, br,
|
||||
settings().distPath, br,
|
||||
settings().autoComplete, br,
|
||||
helpLabel, br,
|
||||
Group {
|
||||
title(Tr::tr("Note")),
|
||||
Column {
|
||||
warningLabel, br,
|
||||
helpLabel, br,
|
||||
}
|
||||
},
|
||||
Form {
|
||||
authWidget, br,
|
||||
settings().enableCopilot, br,
|
||||
settings().nodeJsPath, br,
|
||||
settings().distPath, br,
|
||||
settings().autoComplete, br,
|
||||
hr, br,
|
||||
settings().useProxy, br,
|
||||
settings().proxyHost, br,
|
||||
settings().proxyPort, br,
|
||||
settings().proxyRejectUnauthorized, br,
|
||||
settings().proxyUser, br,
|
||||
settings().saveProxyPassword, br,
|
||||
settings().proxyPassword, br,
|
||||
},
|
||||
st
|
||||
}.attachTo(this);
|
||||
// clang-format on
|
||||
@@ -211,4 +285,4 @@ public:
|
||||
|
||||
const CopilotSettingsPage settingsPage;
|
||||
|
||||
} // Copilot
|
||||
} // namespace Copilot
|
||||
|
||||
@@ -18,6 +18,15 @@ public:
|
||||
Utils::FilePathAspect distPath{this};
|
||||
Utils::BoolAspect autoComplete{this};
|
||||
Utils::BoolAspect enableCopilot{this};
|
||||
|
||||
Utils::BoolAspect useProxy{this};
|
||||
Utils::StringAspect proxyHost{this};
|
||||
Utils::IntegerAspect proxyPort{this};
|
||||
Utils::StringAspect proxyUser{this};
|
||||
|
||||
Utils::BoolAspect saveProxyPassword{this};
|
||||
Utils::StringAspect proxyPassword{this};
|
||||
Utils::BoolAspect proxyRejectUnauthorized{this};
|
||||
};
|
||||
|
||||
CopilotSettings &settings();
|
||||
|
||||
126
src/plugins/copilot/requests/seteditorinfo.h
Normal file
126
src/plugins/copilot/requests/seteditorinfo.h
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "checkstatus.h"
|
||||
|
||||
#include <languageserverprotocol/jsonrpcmessages.h>
|
||||
#include <languageserverprotocol/lsptypes.h>
|
||||
|
||||
namespace Copilot {
|
||||
|
||||
class EditorPluginInfo : public LanguageServerProtocol::JsonObject
|
||||
{
|
||||
static constexpr char version[] = "version";
|
||||
static constexpr char name[] = "name";
|
||||
|
||||
public:
|
||||
using JsonObject::JsonObject;
|
||||
|
||||
EditorPluginInfo(const QString &version, const QString &name)
|
||||
{
|
||||
setEditorVersion(version);
|
||||
setEditorName(name);
|
||||
}
|
||||
|
||||
void setEditorVersion(const QString &v) { insert(version, v); }
|
||||
void setEditorName(const QString &n) { insert(name, n); }
|
||||
};
|
||||
|
||||
class EditorInfo : public LanguageServerProtocol::JsonObject
|
||||
{
|
||||
static constexpr char version[] = "version";
|
||||
static constexpr char name[] = "name";
|
||||
|
||||
public:
|
||||
using JsonObject::JsonObject;
|
||||
|
||||
EditorInfo(const QString &version, const QString &name)
|
||||
{
|
||||
setEditorVersion(version);
|
||||
setEditorName(name);
|
||||
}
|
||||
|
||||
void setEditorVersion(const QString &v) { insert(version, v); }
|
||||
void setEditorName(const QString &n) { insert(name, n); }
|
||||
};
|
||||
|
||||
class NetworkProxy : public LanguageServerProtocol::JsonObject
|
||||
{
|
||||
static constexpr char host[] = "host";
|
||||
static constexpr char port[] = "port";
|
||||
static constexpr char user[] = "username";
|
||||
static constexpr char password[] = "password";
|
||||
static constexpr char rejectUnauthorized[] = "rejectUnauthorized";
|
||||
|
||||
public:
|
||||
using JsonObject::JsonObject;
|
||||
|
||||
NetworkProxy(const QString &host,
|
||||
int port,
|
||||
const QString &user,
|
||||
const QString &password,
|
||||
bool rejectUnauthorized)
|
||||
{
|
||||
setHost(host);
|
||||
setPort(port);
|
||||
setUser(user);
|
||||
setPassword(password);
|
||||
setRejectUnauthorized(rejectUnauthorized);
|
||||
}
|
||||
|
||||
void insertIfNotEmpty(const std::string_view key, const QString &value)
|
||||
{
|
||||
if (!value.isEmpty())
|
||||
insert(key, value);
|
||||
}
|
||||
|
||||
void setHost(const QString &h) { insert(host, h); }
|
||||
void setPort(int p) { insert(port, p); }
|
||||
void setUser(const QString &u) { insertIfNotEmpty(user, u); }
|
||||
void setPassword(const QString &p) { insertIfNotEmpty(password, p); }
|
||||
void setRejectUnauthorized(bool r) { insert(rejectUnauthorized, r); }
|
||||
};
|
||||
|
||||
class SetEditorInfoParams : public LanguageServerProtocol::JsonObject
|
||||
{
|
||||
static constexpr char editorInfo[] = "editorInfo";
|
||||
static constexpr char editorPluginInfo[] = "editorPluginInfo";
|
||||
static constexpr char networkProxy[] = "networkProxy";
|
||||
|
||||
public:
|
||||
using JsonObject::JsonObject;
|
||||
|
||||
SetEditorInfoParams(const EditorInfo &editorInfo, const EditorPluginInfo &editorPluginInfo)
|
||||
{
|
||||
setEditorInfo(editorInfo);
|
||||
setEditorPluginInfo(editorPluginInfo);
|
||||
}
|
||||
|
||||
SetEditorInfoParams(const EditorInfo &editorInfo,
|
||||
const EditorPluginInfo &editorPluginInfo,
|
||||
const NetworkProxy &networkProxy)
|
||||
{
|
||||
setEditorInfo(editorInfo);
|
||||
setEditorPluginInfo(editorPluginInfo);
|
||||
setNetworkProxy(networkProxy);
|
||||
}
|
||||
|
||||
void setEditorInfo(const EditorInfo &info) { insert(editorInfo, info); }
|
||||
void setEditorPluginInfo(const EditorPluginInfo &info) { insert(editorPluginInfo, info); }
|
||||
void setNetworkProxy(const NetworkProxy &proxy) { insert(networkProxy, proxy); }
|
||||
};
|
||||
|
||||
class SetEditorInfoRequest
|
||||
: public LanguageServerProtocol::Request<CheckStatusResponse, std::nullptr_t, SetEditorInfoParams>
|
||||
{
|
||||
public:
|
||||
explicit SetEditorInfoRequest(const SetEditorInfoParams ¶ms)
|
||||
: Request(methodName, params)
|
||||
{}
|
||||
using Request::Request;
|
||||
constexpr static const char methodName[] = "setEditorInfo";
|
||||
};
|
||||
|
||||
} // namespace Copilot
|
||||
20
src/plugins/copilot/tests/proxy/Dockerfile
Normal file
20
src/plugins/copilot/tests/proxy/Dockerfile
Normal file
@@ -0,0 +1,20 @@
|
||||
ARG PWDMODE=with
|
||||
|
||||
|
||||
FROM ubuntu:20.04 AS base
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ENV TZ=Etc/UTC
|
||||
RUN apt-get update && apt-get install -y squid apache2-utils && rm -rf /var/lib/apt/lists/*
|
||||
COPY run.sh /
|
||||
RUN chmod +x /run.sh
|
||||
|
||||
|
||||
FROM base as image-with-pwd
|
||||
RUN echo 1234 | htpasswd -i -c /etc/squid/pswds user
|
||||
COPY userauth.conf /etc/squid/conf.d/
|
||||
|
||||
FROM base as image-without-pwd
|
||||
COPY noauth.conf /etc/squid/conf.d/
|
||||
|
||||
FROM image-${PWDMODE}-pwd AS final
|
||||
CMD [ "/run.sh" ]
|
||||
4
src/plugins/copilot/tests/proxy/buildandrun.sh
Executable file
4
src/plugins/copilot/tests/proxy/buildandrun.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker build --build-arg PWDMODE=with -t copilot-proxy-test . && \
|
||||
docker run --rm -it -p 3128:3128 copilot-proxy-test
|
||||
1
src/plugins/copilot/tests/proxy/noauth.conf
Normal file
1
src/plugins/copilot/tests/proxy/noauth.conf
Normal file
@@ -0,0 +1 @@
|
||||
http_access allow all
|
||||
7
src/plugins/copilot/tests/proxy/run.sh
Executable file
7
src/plugins/copilot/tests/proxy/run.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
touch /var/log/squid/access.log
|
||||
chmod 640 /var/log/squid/access.log
|
||||
chown proxy:proxy /var/log/squid/access.log
|
||||
tail -f /var/log/squid/access.log &
|
||||
exec squid --foreground
|
||||
4
src/plugins/copilot/tests/proxy/userauth.conf
Normal file
4
src/plugins/copilot/tests/proxy/userauth.conf
Normal file
@@ -0,0 +1,4 @@
|
||||
auth_param basic program /usr/lib/squid3/basic_ncsa_auth /etc/squid/pswds
|
||||
auth_param basic realm proxy
|
||||
acl authenticated proxy_auth REQUIRED
|
||||
http_access allow authenticated
|
||||
Reference in New Issue
Block a user