From e8ceeb6e20a022133f5e7e561b2a01a79327697b Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 23 Nov 2023 00:03:43 +0700 Subject: [PATCH] added full access sharing --- client/resources.qrc | 1 + client/ui/controllers/pageController.h | 4 +- client/ui/models/clientManagementModel.cpp | 254 +++++++++++-------- client/ui/models/clientManagementModel.h | 7 +- client/ui/qml/Pages2/PageShare.qml | 94 ++++--- client/ui/qml/Pages2/PageShareFullAccess.qml | 155 +++++++++++ 6 files changed, 376 insertions(+), 139 deletions(-) create mode 100644 client/ui/qml/Pages2/PageShareFullAccess.qml diff --git a/client/resources.qrc b/client/resources.qrc index 4c63383c..1baf993b 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -222,5 +222,6 @@ server_scripts/awg/configure_container.sh server_scripts/awg/run_container.sh server_scripts/awg/Dockerfile + ui/qml/Pages2/PageShareFullAccess.qml diff --git a/client/ui/controllers/pageController.h b/client/ui/controllers/pageController.h index 20c3bbed..f7e697fb 100644 --- a/client/ui/controllers/pageController.h +++ b/client/ui/controllers/pageController.h @@ -51,7 +51,9 @@ namespace PageLoader PageProtocolWireGuardSettings, PageProtocolAwgSettings, PageProtocolIKev2Settings, - PageProtocolRaw + PageProtocolRaw, + + PageShareFullAccess }; Q_ENUM_NS(PageEnum) diff --git a/client/ui/models/clientManagementModel.cpp b/client/ui/models/clientManagementModel.cpp index c4c4fb7a..f53794d5 100644 --- a/client/ui/models/clientManagementModel.cpp +++ b/client/ui/models/clientManagementModel.cpp @@ -4,6 +4,19 @@ #include #include "core/servercontroller.h" +#include "logger.h" + +namespace +{ + Logger logger("ClientManagementModel"); + + namespace configKey { + constexpr char clientId[] = "clientId"; + constexpr char clientName[] = "clientName"; + constexpr char container[] = "container"; + constexpr char userData[] = "userData"; + } +} ClientManagementModel::ClientManagementModel(std::shared_ptr settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent) @@ -23,13 +36,13 @@ QVariant ClientManagementModel::data(const QModelIndex &index, int role) const } auto client = m_clientsTable.at(index.row()).toObject(); - auto userData = client.value("userData").toObject(); + auto userData = client.value(configKey::userData).toObject(); switch (role) { - case UserNameRole: return userData.value("clientName").toString(); + case ClientNameRole: return userData.value(configKey::clientName).toString(); case ContainerNameRole: return ContainerProps::containerHumanNames().value( - static_cast(userData.value("container").toInt())); + static_cast(userData.value(configKey::container).toInt())); } return QVariant(); @@ -40,17 +53,13 @@ ErrorCode ClientManagementModel::updateModel(DockerContainer container, ServerCr ServerController serverController(m_settings); ErrorCode error = ErrorCode::NoError; - QString stdOut; - auto cbReadStdOut = [&](const QString &data, libssh::Client &) { - stdOut += data + "\n"; - return ErrorCode::NoError; - }; const QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); const QByteArray clientsTableString = serverController.getTextFileFromContainer(container, credentials, clientsTableFile, &error); if (error != ErrorCode::NoError) { + logger.error() << "Failed to get the clientsTable file from the server"; return error; } @@ -62,75 +71,21 @@ ErrorCode ClientManagementModel::updateModel(DockerContainer container, ServerCr if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks || container == DockerContainer::Cloak) { - const QString getOpenVpnClientsList = - "sudo docker exec -i $CONTAINER_NAME bash -c 'ls /opt/amnezia/openvpn/pki/issued'"; - QString script = serverController.replaceVars(getOpenVpnClientsList, - serverController.genVarsForScript(credentials, container)); - error = serverController.runScript(credentials, script, cbReadStdOut); - if (error != ErrorCode::NoError) { - return error; - } - - if (!stdOut.isEmpty()) { - QStringList certsIds = stdOut.split("\n", Qt::SkipEmptyParts); - certsIds.removeAll("AmneziaReq.crt"); - - for (auto &openvpnCertId : certsIds) { - openvpnCertId.replace(".crt", ""); - if (!isClientExists(openvpnCertId)) { - QJsonObject client; - client["userId"] = openvpnCertId; - - QJsonObject userData; - userData["clientName"] = QString("Client %1").arg(count); - userData["container"] = container; - client["userData"] = userData; - - m_clientsTable.push_back(client); - - count++; - } - } - } + error = getOpenVpnClients(serverController, container, credentials, count); } else if (container == DockerContainer::WireGuard || container == DockerContainer::Awg) { - const QString wireGuardConfigFile = - QString("opt/amnezia/%1/wg0.conf").arg(container == DockerContainer::WireGuard ? "wireguard" : "awg"); - const QString wireguardConfigString = - serverController.getTextFileFromContainer(container, credentials, wireGuardConfigFile, &error); - if (error != ErrorCode::NoError) { - return error; - } - - auto configLines = wireguardConfigString.split("\n", Qt::SkipEmptyParts); - QStringList wireguardKeys; - for (const auto &line : configLines) { - auto configPair = line.split(" = ", Qt::SkipEmptyParts); - if (configPair.front() == "PublicKey") { - wireguardKeys.push_back(configPair.back()); - } - } - - for (auto &wireguardKey : wireguardKeys) { - if (!isClientExists(wireguardKey)) { - QJsonObject client; - client["userId"] = wireguardKey; - - QJsonObject userData; - userData["clientName"] = QString("Client %1").arg(count); - userData["container"] = container; - client["userData"] = userData; - - m_clientsTable.push_back(client); - - count++; - } - } + error = getWireGuardClients(serverController, container, credentials, count); + } + if (error != ErrorCode::NoError) { + return error; } const QByteArray newClientsTableString = QJsonDocument(m_clientsTable).toJson(); if (clientsTableString != newClientsTableString) { error = serverController.uploadTextFileToContainer(container, credentials, newClientsTableString, clientsTableFile); + if (error != ErrorCode::NoError) { + logger.error() << "Failed to upload the clientsTable file to the server"; + } } } @@ -138,12 +93,95 @@ ErrorCode ClientManagementModel::updateModel(DockerContainer container, ServerCr return error; } +ErrorCode ClientManagementModel::getOpenVpnClients(ServerController &serverController, DockerContainer container, ServerCredentials credentials, int &count) +{ + ErrorCode error = ErrorCode::NoError; + QString stdOut; + auto cbReadStdOut = [&](const QString &data, libssh::Client &) { + stdOut += data + "\n"; + return ErrorCode::NoError; + }; + + const QString getOpenVpnClientsList = + "sudo docker exec -i $CONTAINER_NAME bash -c 'ls /opt/amnezia/openvpn/pki/issued'"; + QString script = serverController.replaceVars(getOpenVpnClientsList, + serverController.genVarsForScript(credentials, container)); + error = serverController.runScript(credentials, script, cbReadStdOut); + if (error != ErrorCode::NoError) { + logger.error() << "Failed to retrieve the list of issued certificates on the server"; + return error; + } + + if (!stdOut.isEmpty()) { + QStringList certsIds = stdOut.split("\n", Qt::SkipEmptyParts); + certsIds.removeAll("AmneziaReq.crt"); + + for (auto &openvpnCertId : certsIds) { + openvpnCertId.replace(".crt", ""); + if (!isClientExists(openvpnCertId)) { + QJsonObject client; + client[configKey::clientId] = openvpnCertId; + + QJsonObject userData; + userData[configKey::clientName] = QString("Client %1").arg(count); + userData[configKey::container] = container; + client[configKey::userData] = userData; + + m_clientsTable.push_back(client); + + count++; + } + } + } + return error; +} + +ErrorCode ClientManagementModel::getWireGuardClients(ServerController &serverController, DockerContainer container, ServerCredentials credentials, int &count) +{ + ErrorCode error = ErrorCode::NoError; + + const QString wireGuardConfigFile = + QString("opt/amnezia/%1/wg0.conf").arg(container == DockerContainer::WireGuard ? "wireguard" : "awg"); + const QString wireguardConfigString = + serverController.getTextFileFromContainer(container, credentials, wireGuardConfigFile, &error); + if (error != ErrorCode::NoError) { + logger.error() << "Failed to get the wg conf file from the server"; + return error; + } + + auto configLines = wireguardConfigString.split("\n", Qt::SkipEmptyParts); + QStringList wireguardKeys; + for (const auto &line : configLines) { + auto configPair = line.split(" = ", Qt::SkipEmptyParts); + if (configPair.front() == "PublicKey") { + wireguardKeys.push_back(configPair.back()); + } + } + + for (auto &wireguardKey : wireguardKeys) { + if (!isClientExists(wireguardKey)) { + QJsonObject client; + client[configKey::clientId] = wireguardKey; + + QJsonObject userData; + userData[configKey::clientName] = QString("Client %1").arg(count); + userData[configKey::container] = container; + client[configKey::userData] = userData; + + m_clientsTable.push_back(client); + + count++; + } + } + return error; +} + bool ClientManagementModel::isClientExists(const QString &clientId) { for (const QJsonValue &value : qAsConst(m_clientsTable)) { if (value.isObject()) { QJsonObject obj = value.toObject(); - if (obj.contains("userId") && obj["userId"].toString() == clientId) { + if (obj.contains(configKey::clientId) && obj[configKey::clientId].toString() == clientId) { return true; } } @@ -155,39 +193,38 @@ ErrorCode ClientManagementModel::appendClient(const QString &clientId, const QSt const DockerContainer container, ServerCredentials credentials) { ErrorCode error; - if (m_clientsTable.empty()) { - error = updateModel(container, credentials); - if (error != ErrorCode::NoError) { - return error; + + error = updateModel(container, credentials); + if (error != ErrorCode::NoError) { + return error; + } + + for (int i = 0; i < m_clientsTable.size(); i++) { + if (m_clientsTable.at(i).toObject().value(configKey::clientId) == clientId) { + return renameClient(i, clientName, container, credentials); } + } - for (int i = 0; i < m_clientsTable.size(); i++) { - if (m_clientsTable.at(i).toObject().value("userId") == (clientId)) { - error = renameClient(i, clientName, container, credentials); - if (error != ErrorCode::NoError) { - return error; - } - } - } - } else { - beginResetModel(); - QJsonObject client; - client["userId"] = clientId; + beginResetModel(); + QJsonObject client; + client[configKey::clientId] = clientId; - QJsonObject userData; - userData["clientName"] = clientName; - userData["container"] = container; - client["userData"] = userData; - m_clientsTable.push_back(client); - endResetModel(); + QJsonObject userData; + userData[configKey::clientName] = clientName; + userData[configKey::container] = container; + client[configKey::userData] = userData; + m_clientsTable.push_back(client); + endResetModel(); - const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson(); + const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson(); - ServerController serverController(m_settings); - const QString clientsTableFile = - QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); + ServerController serverController(m_settings); + const QString clientsTableFile = + QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); - error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); + error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); + if (error != ErrorCode::NoError) { + logger.error() << "Failed to upload the clientsTable file to the server"; } return error; @@ -197,9 +234,9 @@ ErrorCode ClientManagementModel::renameClient(const int row, const QString &clie ServerCredentials credentials) { auto client = m_clientsTable.at(row).toObject(); - auto userData = client["userData"].toObject(); - userData["clientName"] = clientName; - client["userData"] = userData; + auto userData = client[configKey::userData].toObject(); + userData[configKey::clientName] = clientName; + client[configKey::userData] = userData; m_clientsTable.replace(row, client); emit dataChanged(index(row, 0), index(row, 0)); @@ -212,6 +249,9 @@ ErrorCode ClientManagementModel::renameClient(const int row, const QString &clie ErrorCode error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); + if (error != ErrorCode::NoError) { + logger.error() << "Failed to upload the clientsTable file to the server"; + } return error; } @@ -232,7 +272,7 @@ ErrorCode ClientManagementModel::revokeOpenVpn(const int row, const DockerContai ServerCredentials credentials) { auto client = m_clientsTable.at(row).toObject(); - QString clientId = client.value("userId").toString(); + QString clientId = client.value(configKey::clientId).toString(); const QString getOpenVpnCertData = QString("sudo docker exec -i $CONTAINER_NAME bash -c '" "cd /opt/amnezia/openvpn ;\\" @@ -246,6 +286,7 @@ ErrorCode ClientManagementModel::revokeOpenVpn(const int row, const DockerContai serverController.replaceVars(getOpenVpnCertData, serverController.genVarsForScript(credentials, container)); ErrorCode error = serverController.runScript(credentials, script); if (error != ErrorCode::NoError) { + logger.error() << "Failed to revoke the certificate"; return error; } @@ -259,6 +300,7 @@ ErrorCode ClientManagementModel::revokeOpenVpn(const int row, const DockerContai QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); if (error != ErrorCode::NoError) { + logger.error() << "Failed to upload the clientsTable file to the server"; return error; } @@ -276,11 +318,12 @@ ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerCont const QString wireguardConfigString = serverController.getTextFileFromContainer(container, credentials, wireGuardConfigFile, &error); if (error != ErrorCode::NoError) { + logger.error() << "Failed to get the wg conf file from the server"; return error; } auto client = m_clientsTable.at(row).toObject(); - QString clientId = client.value("userId").toString(); + QString clientId = client.value(configKey::clientId).toString(); auto configSections = wireguardConfigString.split("[", Qt::SkipEmptyParts); for (auto §ion : configSections) { @@ -293,6 +336,7 @@ ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerCont newWireGuardConfig.insert(0, "["); error = serverController.uploadTextFileToContainer(container, credentials, newWireGuardConfig, wireGuardConfigFile); if (error != ErrorCode::NoError) { + logger.error() << "Failed to upload the wg conf file to the server"; return error; } @@ -306,6 +350,7 @@ ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerCont QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); if (error != ErrorCode::NoError) { + logger.error() << "Failed to upload the clientsTable file to the server"; return error; } @@ -315,6 +360,7 @@ ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerCont serverController.replaceVars(script.arg(wireGuardConfigFile), serverController.genVarsForScript(credentials, container))); if (error != ErrorCode::NoError) { + logger.error() << "Failed to execute the command 'wg syncconf' on the server"; return error; } @@ -324,7 +370,7 @@ ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerCont QHash ClientManagementModel::roleNames() const { QHash roles; - roles[UserNameRole] = "userName"; + roles[ClientNameRole] = "clientName"; roles[ContainerNameRole] = "containerName"; return roles; } diff --git a/client/ui/models/clientManagementModel.h b/client/ui/models/clientManagementModel.h index c51ce816..25089b66 100644 --- a/client/ui/models/clientManagementModel.h +++ b/client/ui/models/clientManagementModel.h @@ -4,7 +4,7 @@ #include #include -#include "protocols/protocols_defs.h" +#include "core/servercontroller.h" #include "settings.h" class ClientManagementModel : public QAbstractListModel @@ -29,7 +29,7 @@ class ClientManagementModel : public QAbstractListModel public: enum Roles { - UserNameRole = Qt::UserRole + 1, + ClientNameRole = Qt::UserRole + 1, ContainerNameRole, }; @@ -55,6 +55,9 @@ private: ErrorCode revokeOpenVpn(const int row, const DockerContainer container, ServerCredentials credentials); ErrorCode revokeWireGuard(const int row, const DockerContainer container, ServerCredentials credentials); + ErrorCode getOpenVpnClients(ServerController &serverController, DockerContainer container, ServerCredentials credentials, int &count); + ErrorCode getWireGuardClients(ServerController &serverController, DockerContainer container, ServerCredentials credentials, int &count); + QJsonArray m_clientsTable; std::shared_ptr m_settings; diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 01813703..cb847583 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -18,7 +18,6 @@ PageType { enum ConfigType { AmneziaConnection, - AmneziaFullAccess, OpenVpn, WireGuard } @@ -46,24 +45,16 @@ PageType { PageController.showBusyIndicator(true) switch (type) { - case PageShare.ConfigType.AmneziaConnection: ExportController.generateConnectionConfig(userNameTextField.textFieldText); break; - case PageShare.ConfigType.AmneziaFullAccess: { - if (Qt.platform.os === "android") { - ExportController.generateFullAccessConfigAndroid(); - } else { - ExportController.generateFullAccessConfig(); - } - break; - } + case PageShare.ConfigType.AmneziaConnection: ExportController.generateConnectionConfig(clientNameTextField.textFieldText); break; case PageShare.ConfigType.OpenVpn: { - ExportController.generateOpenVpnConfig(userNameTextField.textFieldText) + ExportController.generateOpenVpnConfig(clientNameTextField.textFieldText) shareConnectionDrawer.configCaption = qsTr("Save OpenVPN config") shareConnectionDrawer.configExtension = ".ovpn" shareConnectionDrawer.configFileName = "amnezia_for_openvpn" break; } case PageShare.ConfigType.WireGuard: { - ExportController.generateWireGuardConfig(userNameTextField.textFieldText) + ExportController.generateWireGuardConfig(clientNameTextField.textFieldText) shareConnectionDrawer.configCaption = qsTr("Save WireGuard config") shareConnectionDrawer.configExtension = ".conf" shareConnectionDrawer.configFileName = "amnezia_for_wireguard" @@ -129,6 +120,51 @@ PageType { Layout.topMargin: 24 headerText: qsTr("Share VPN Access") + + actionButtonImage: "qrc:/images/controls/more-vertical.svg" + actionButtonFunction: function() { + shareFullAccessDrawer.open() + } + + DrawerType { + id: shareFullAccessDrawer + + width: root.width + height: root.height * 0.45 + + + ColumnLayout { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: 16 + + spacing: 0 + + Header2Type { + Layout.fillWidth: true + Layout.bottomMargin: 16 + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + headerText: qsTr("Share full access to the server and VPN") + descriptionText: qsTr("Use for your own devices, or share with those you trust to manage the server.") + } + + + LabelWithButtonType { + Layout.fillWidth: true + + text: qsTr("Share") + rightImageSource: "qrc:/images/controls/chevron-right.svg" + + clickedFunction: function() { + PageController.goToPage(PageEnum.PageShareFullAccess) + shareFullAccessDrawer.close() + } + } + } + } } Rectangle { @@ -189,7 +225,7 @@ PageType { } TextFieldWithHeaderType { - id: userNameTextField + id: clientNameTextField Layout.fillWidth: true Layout.topMargin: 16 @@ -242,11 +278,6 @@ PageType { serverSelector.severSelectorIndexChanged() } - //full access label -// if (accessTypeSelector.currentIndex !== 0) { -// shareConnectionDrawer.headerText = qsTr("Accessing ") + serverSelector.text -// shareConnectionDrawer.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text -// } serverSelector.menuVisible = false } @@ -419,7 +450,7 @@ PageType { } ListView { - id: usersListView + id: clientsListView Layout.fillWidth: true Layout.preferredHeight: childrenRect.height @@ -431,7 +462,7 @@ PageType { interactive: false delegate: Item { - implicitWidth: usersListView.width + implicitWidth: clientsListView.width implicitHeight: delegateContent.implicitHeight ColumnLayout { @@ -447,19 +478,19 @@ PageType { LabelWithButtonType { Layout.fillWidth: true - text: userName + text: clientName descriptionText: containerName rightImageSource: "qrc:/images/controls/chevron-right.svg" clickedFunction: function() { - userInfoDrower.open() + clientInfoDrawer.open() } } DividerType {} DrawerType { - id: userInfoDrower + id: clientInfoDrawer width: root.width height: root.height * 0.45 @@ -478,7 +509,7 @@ PageType { Layout.fillWidth: true Layout.bottomMargin: 24 - headerText: userName + headerText: clientName descriptionText: serverSelector.text + ", " + containerName } @@ -507,7 +538,7 @@ PageType { onVisibleChanged: { if (clientNameEditDrawer.visible) { - clientName.textField.forceActiveFocus() + clientNameEditor.textField.forceActiveFocus() } } @@ -520,11 +551,10 @@ PageType { anchors.rightMargin: 16 TextFieldWithHeaderType { - id: clientName - + id: clientNameEditor Layout.fillWidth: true headerText: qsTr("Client name") - textFieldText: userName + textFieldText: clientName } BasicButtonType { @@ -533,10 +563,10 @@ PageType { text: qsTr("Save") onClicked: { - if (clientName.textFieldText !== userName) { + if (clientNameEditor.textFieldText !== clientName) { PageController.showBusyIndicator(true) ExportController.renameClient(index, - clientName.textFieldText, + clientNameEditor.textFieldText, ContainersModel.getCurrentlyProcessedContainerIndex(), ServersModel.getCurrentlyProcessedServerCredentials()) PageController.showBusyIndicator(false) @@ -561,14 +591,14 @@ PageType { text: qsTr("Revoke") onClicked: function() { - questionDrawer.headerText = qsTr("Revoke the config for a user - ") + userName + "?" + questionDrawer.headerText = qsTr("Revoke the config for a user - ") + clientName + "?" questionDrawer.descriptionText = qsTr("The user will no longer be able to connect to your server.") questionDrawer.yesButtonText = qsTr("Continue") questionDrawer.noButtonText = qsTr("Cancel") questionDrawer.yesButtonFunction = function() { questionDrawer.close() - userInfoDrower.close() + clientInfoDrawer.close() root.revokeConfig(index) } questionDrawer.noButtonFunction = function() { diff --git a/client/ui/qml/Pages2/PageShareFullAccess.qml b/client/ui/qml/Pages2/PageShareFullAccess.qml new file mode 100644 index 00000000..e59c57ef --- /dev/null +++ b/client/ui/qml/Pages2/PageShareFullAccess.qml @@ -0,0 +1,155 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs + +import SortFilterProxyModel 0.2 + +import PageEnum 1.0 +import ContainerProps 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Components" + +PageType { + id: root + + BackButtonType { + id: backButton + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: 20 + } + + FlickableType { + anchors.top: backButton.bottom + anchors.bottom: parent.bottom + contentHeight: content.height + + ColumnLayout { + id: content + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.rightMargin: 16 + anchors.leftMargin: 16 + + spacing: 0 + + HeaderType { + Layout.fillWidth: true + Layout.topMargin: 24 + + headerText: qsTr("Full access to the server and VPN") + } + + ParagraphTextType { + Layout.fillWidth: true + Layout.topMargin: 24 + Layout.bottomMargin: 24 + + text: qsTr("We recommend that you use full access to the server only for your own additional devices.\n") + + qsTr("If you share full access with other people, they can remove and add protocols and services to the server, which will cause the VPN to work incorrectly for all users. ") + color: "#878B91" + } + + DropDownType { + id: serverSelector + + signal severSelectorIndexChanged + property int currentIndex: 0 + + Layout.fillWidth: true + Layout.topMargin: 16 + + drawerHeight: 0.4375 + + descriptionText: qsTr("Server") + headerText: qsTr("Server") + + listView: ListViewWithRadioButtonType { + id: serverSelectorListView + + rootWidth: root.width + imageSource: "qrc:/images/controls/check.svg" + + model: SortFilterProxyModel { + id: proxyServersModel + sourceModel: ServersModel + filters: [ + ValueFilter { + roleName: "hasWriteAccess" + value: true + } + ] + } + + currentIndex: 0 + + clickedFunction: function() { + handler() + + if (serverSelector.currentIndex !== serverSelectorListView.currentIndex) { + serverSelector.currentIndex = serverSelectorListView.currentIndex + } + + shareConnectionDrawer.headerText = qsTr("Accessing ") + serverSelector.text + shareConnectionDrawer.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text + serverSelector.menuVisible = false + } + + Component.onCompleted: { + handler() + } + + function handler() { + serverSelector.text = selectedText + ServersModel.currentlyProcessedIndex = proxyServersModel.mapToSource(currentIndex) + } + } + } + + BasicButtonType { + Layout.fillWidth: true + Layout.topMargin: 40 + + text: qsTr("Share") + imageSource: "qrc:/images/controls/share-2.svg" + + onClicked: function() { + shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text + shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text + + shareConnectionDrawer.needCloseButton = false + + shareConnectionDrawer.open() + shareConnectionDrawer.contentVisible = false + PageController.showBusyIndicator(true) + + if (Qt.platform.os === "android") { + ExportController.generateFullAccessConfigAndroid(); + } else { + ExportController.generateFullAccessConfig(); + } + + PageController.showBusyIndicator(false) + + shareConnectionDrawer.needCloseButton = true + PageController.showTopCloseButton(true) + + shareConnectionDrawer.contentVisible = true + } + } + + ShareConnectionDrawer { + id: shareConnectionDrawer + } + } + } +}