diff --git a/src/plugins/copilot/authwidget.cpp b/src/plugins/copilot/authwidget.cpp index 07071d98f7e..a0890cae3dc 100644 --- a/src/plugins/copilot/authwidget.cpp +++ b/src/plugins/copilot/authwidget.cpp @@ -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); diff --git a/src/plugins/copilot/authwidget.h b/src/plugins/copilot/authwidget.h index acb18810fe4..6da873bd001 100644 --- a/src/plugins/copilot/authwidget.h +++ b/src/plugins/copilot/authwidget.h @@ -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(); diff --git a/src/plugins/copilot/copilotclient.cpp b/src/plugins/copilot/copilotclient.cpp index 3f624ca071c..d5592b7861c 100644 --- a/src/plugins/copilot/copilotclient.cpp +++ b/src/plugins/copilot/copilotclient.cpp @@ -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 #include #include #include +#include #include #include +#include #include -#include - #include #include -#include +#include +#include +#include +#include +#include #include #include @@ -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(settings().proxyPort()), + settings().proxyUser(), + currentProxyPassword, + settings().proxyRejectUnauthorized()}); + } + + SetEditorInfoRequest request(params); + sendMessage(request); +} + void CopilotClient::requestCheckStatus( bool localChecksOnly, std::function 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 diff --git a/src/plugins/copilot/copilotclient.h b/src/plugins/copilot/copilotclient.h index 47208236b52..aa03b70960f 100644 --- a/src/plugins/copilot/copilotclient.h +++ b/src/plugins/copilot/copilotclient.h @@ -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 m_runningRequests; struct ScheduleData { @@ -59,6 +64,7 @@ private: }; QMap m_scheduledRequests; CopilotHoverHandler m_hoverHandler; + bool m_isAskingForPassword{false}; }; } // namespace Copilot::Internal diff --git a/src/plugins/copilot/copilotsettings.cpp b/src/plugins/copilot/copilotsettings.cpp index c03ada7dce5..ef064372ca5 100644 --- a/src/plugins/copilot/copilotsettings.cpp +++ b/src/plugins/copilot/copilotsettings.cpp @@ -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("" + Tr::tr("Note:") + ""), 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 diff --git a/src/plugins/copilot/copilotsettings.h b/src/plugins/copilot/copilotsettings.h index 11e9ace9c6b..b92106c3f73 100644 --- a/src/plugins/copilot/copilotsettings.h +++ b/src/plugins/copilot/copilotsettings.h @@ -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(); diff --git a/src/plugins/copilot/requests/seteditorinfo.h b/src/plugins/copilot/requests/seteditorinfo.h new file mode 100644 index 00000000000..321a2e43755 --- /dev/null +++ b/src/plugins/copilot/requests/seteditorinfo.h @@ -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 +#include + +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 +{ +public: + explicit SetEditorInfoRequest(const SetEditorInfoParams ¶ms) + : Request(methodName, params) + {} + using Request::Request; + constexpr static const char methodName[] = "setEditorInfo"; +}; + +} // namespace Copilot diff --git a/src/plugins/copilot/tests/proxy/Dockerfile b/src/plugins/copilot/tests/proxy/Dockerfile new file mode 100644 index 00000000000..ee053080402 --- /dev/null +++ b/src/plugins/copilot/tests/proxy/Dockerfile @@ -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" ] diff --git a/src/plugins/copilot/tests/proxy/buildandrun.sh b/src/plugins/copilot/tests/proxy/buildandrun.sh new file mode 100755 index 00000000000..1bffd699bc5 --- /dev/null +++ b/src/plugins/copilot/tests/proxy/buildandrun.sh @@ -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 diff --git a/src/plugins/copilot/tests/proxy/noauth.conf b/src/plugins/copilot/tests/proxy/noauth.conf new file mode 100644 index 00000000000..04c7e12eecf --- /dev/null +++ b/src/plugins/copilot/tests/proxy/noauth.conf @@ -0,0 +1 @@ +http_access allow all diff --git a/src/plugins/copilot/tests/proxy/run.sh b/src/plugins/copilot/tests/proxy/run.sh new file mode 100755 index 00000000000..2a0d77d481c --- /dev/null +++ b/src/plugins/copilot/tests/proxy/run.sh @@ -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 diff --git a/src/plugins/copilot/tests/proxy/userauth.conf b/src/plugins/copilot/tests/proxy/userauth.conf new file mode 100644 index 00000000000..1a344e04657 --- /dev/null +++ b/src/plugins/copilot/tests/proxy/userauth.conf @@ -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