added full access sharing

This commit is contained in:
vladimir.kuznetsov 2023-11-23 00:03:43 +07:00
parent c6a312845a
commit e8ceeb6e20
6 changed files with 376 additions and 139 deletions

View file

@ -222,5 +222,6 @@
<file>server_scripts/awg/configure_container.sh</file>
<file>server_scripts/awg/run_container.sh</file>
<file>server_scripts/awg/Dockerfile</file>
<file>ui/qml/Pages2/PageShareFullAccess.qml</file>
</qresource>
</RCC>

View file

@ -51,7 +51,9 @@ namespace PageLoader
PageProtocolWireGuardSettings,
PageProtocolAwgSettings,
PageProtocolIKev2Settings,
PageProtocolRaw
PageProtocolRaw,
PageShareFullAccess
};
Q_ENUM_NS(PageEnum)

View file

@ -4,6 +4,19 @@
#include <QJsonObject>
#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> 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<DockerContainer>(userData.value("container").toInt()));
static_cast<DockerContainer>(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 &section : 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<int, QByteArray> ClientManagementModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[UserNameRole] = "userName";
roles[ClientNameRole] = "clientName";
roles[ContainerNameRole] = "containerName";
return roles;
}

View file

@ -4,7 +4,7 @@
#include <QAbstractListModel>
#include <QJsonArray>
#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<Settings> m_settings;

View file

@ -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() {

View file

@ -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
}
}
}
}