added page to display WireGuard client information

This commit is contained in:
vladimir.kuznetsov 2023-01-10 16:21:45 +03:00
parent a42beb86c0
commit 8c137ecc52
9 changed files with 216 additions and 76 deletions

View file

@ -810,23 +810,26 @@ ErrorCode ServerController::getClientsList(const ServerCredentials &credentials,
genVarsForScript(credentials, container)), cbReadStdOut); genVarsForScript(credentials, container)), cbReadStdOut);
// TODO error processing // TODO error processing
if (!stdOut.isEmpty()) { if (!stdOut.isEmpty()) {
QStringList clietnsNames = stdOut.split("\n", Qt::SkipEmptyParts); QStringList certsIds = stdOut.split("\n", Qt::SkipEmptyParts);
clietnsNames.removeAll("AmneziaReq.crt"); certsIds.removeAll("AmneziaReq.crt");
QByteArray clientsTableString = getTextFileFromContainer(container, credentials, "opt/amnezia/openvpn/clientsTable"); QByteArray clientsTableString = getTextFileFromContainer(container, credentials, "opt/amnezia/openvpn/clientsTable");
QJsonObject clientsTable = QJsonDocument::fromJson(clientsTableString).object(); QJsonObject clientsTable = QJsonDocument::fromJson(clientsTableString).object();
for (auto &clietnId : clietnsNames) { int count = 0;
clietnId.replace(".crt", ""); for (auto &openvpnCertId : certsIds) {
if (!clientsTable.contains(clietnId)) { openvpnCertId.replace(".crt", "");
if (!clientsTable.contains(openvpnCertId)) {
stdOut.clear(); stdOut.clear();
error = runScript(credentials, error = runScript(credentials,
replaceVars(QString("sudo docker exec -i $CONTAINER_NAME bash -c 'cat /opt/amnezia/openvpn/pki/issued/%1.crt'").arg(clietnId), replaceVars(QString("sudo docker exec -i $CONTAINER_NAME bash -c 'cat /opt/amnezia/openvpn/pki/issued/%1.crt'").arg(openvpnCertId),
genVarsForScript(credentials, container)), cbReadStdOut); genVarsForScript(credentials, container)), cbReadStdOut);
// TODO error processing // TODO error processing
QJsonObject client; QJsonObject client;
client["name"] = ""; client["openvpnCertId"] = openvpnCertId;
client["certificate"] = stdOut; client["clientName"] = QString("Client %1").arg(count);
clientsTable[clietnId] = client; client["openvpnCertData"] = stdOut;
clientsTable[openvpnCertId] = client;
count++;
} }
} }
QByteArray newClientsTableString = QJsonDocument(clientsTable).toJson(); QByteArray newClientsTableString = QJsonDocument(clientsTable).toJson();
@ -837,7 +840,33 @@ ErrorCode ServerController::getClientsList(const ServerCredentials &credentials,
clietns = clientsTable; clietns = clientsTable;
} }
} else if (mainProtocol == Proto::WireGuard) { } else if (mainProtocol == Proto::WireGuard) {
QString wireguardConfigString = getTextFileFromContainer(container, credentials, "opt/amnezia/wireguard/wg0.conf");
auto configSections = wireguardConfigString.split("[", Qt::SkipEmptyParts);
QJsonObject clientsTable;
int count = 0;
for (const auto &section : configSections) {
auto configLines = section.split("\n", Qt::SkipEmptyParts);
if (!configLines.contains("Peer]")) {
continue;
}
QJsonObject client;
for (const auto &line : configLines) {
auto configPair = line.split(" = ", Qt::SkipEmptyParts);
if (configPair.front() == "# Name") {
client["clientName"] = configPair.size() == 2 ? configPair.back() : "";
} else if (configPair.front() == "PublicKey") {
client["wireguardPublicKey"] = configPair.back();
}
}
if (client["clientName"].isNull()) {
client["clientName"] = QString("Client %1").arg(count);
count++;
}
clientsTable[client["wireguardPublicKey"].toString()] = client;
}
// TODO error processing
clietns = clientsTable;
} }
return error; return error;
@ -845,7 +874,12 @@ ErrorCode ServerController::getClientsList(const ServerCredentials &credentials,
ErrorCode ServerController::setClientsList(const ServerCredentials &credentials, DockerContainer container, Proto mainProtocol, QJsonObject &clietns) ErrorCode ServerController::setClientsList(const ServerCredentials &credentials, DockerContainer container, Proto mainProtocol, QJsonObject &clietns)
{ {
auto error = uploadTextFileToContainer(container, credentials, QJsonDocument(clietns).toJson(), "opt/amnezia/openvpn/clientsTable"); ErrorCode error = ErrorCode::NoError;
if (mainProtocol == Proto::OpenVpn) {
error = uploadTextFileToContainer(container, credentials, QJsonDocument(clietns).toJson(), "opt/amnezia/openvpn/clientsTable");
} else if (mainProtocol == Proto::WireGuard) {
}
return error; return error;
} }

View file

@ -14,13 +14,22 @@ void ClientManagementModel::clearData()
endResetModel(); endResetModel();
} }
void ClientManagementModel::setContent(const QVector<ClientInfo> &data) void ClientManagementModel::setContent(const QVector<QVariant> &data)
{ {
beginResetModel(); beginResetModel();
m_content = data; m_content = data;
endResetModel(); endResetModel();
} }
QJsonObject ClientManagementModel::getContent()
{
QJsonObject clientsTable;
for (const auto &item : m_content) {
clientsTable[item.toJsonObject()["openvpnCertId"].toString()] = item.toJsonObject();
}
return clientsTable;
}
int ClientManagementModel::rowCount(const QModelIndex &parent) const int ClientManagementModel::rowCount(const QModelIndex &parent) const
{ {
Q_UNUSED(parent); Q_UNUSED(parent);
@ -35,14 +44,15 @@ QVariant ClientManagementModel::data(const QModelIndex &index, int role) const
} }
if (role == NameRole) { if (role == NameRole) {
return m_content[index.row()].name; return m_content[index.row()].toJsonObject()["clientName"].toString();
} } else if (role == OpenVpnCertIdRole) {
if (role == CertIdRole) { return m_content[index.row()].toJsonObject()["openvpnCertId"].toString();
return m_content[index.row()].certId; } else if (role == OpenVpnCertDataRole) {
} return m_content[index.row()].toJsonObject()["openvpnCertData"].toString();
if (role == CertDataRole) { } else if (role == WireGuardPublicKey) {
return m_content[index.row()].certData; return m_content[index.row()].toJsonObject()["wireguardPublicKey"].toString();
} }
return QVariant(); return QVariant();
} }
@ -53,15 +63,19 @@ void ClientManagementModel::setData(const QModelIndex &index, QVariant data, int
return; return;
} }
auto client = m_content[index.row()].toJsonObject();
if (role == NameRole) { if (role == NameRole) {
m_content[index.row()].name = data.toString(); client["clientName"] = data.toString();
} } else if (role == OpenVpnCertIdRole) {
if (role == CertIdRole) { client["openvpnCertId"] = data.toString();
m_content[index.row()].certId = data.toString(); } else if (role == OpenVpnCertDataRole) {
} client["openvpnCertData"] = data.toString();
if (role == CertDataRole) { } else if (role == WireGuardPublicKey) {
m_content[index.row()].certData = data.toString(); client["wireguardPublicKey"] = data.toString();
} else {
return;
} }
m_content[index.row()] = client;
emit dataChanged(index, index); emit dataChanged(index, index);
} }
@ -69,7 +83,8 @@ QHash<int, QByteArray> ClientManagementModel::roleNames() const
{ {
QHash<int, QByteArray> roles; QHash<int, QByteArray> roles;
roles[NameRole] = "clientName"; roles[NameRole] = "clientName";
roles[CertIdRole] = "certId"; roles[OpenVpnCertIdRole] = "openvpnCertId";
roles[CertDataRole] = "certData"; roles[OpenVpnCertDataRole] = "openvpnCertData";
roles[WireGuardPublicKey] = "wireguardPublicKey";
return roles; return roles;
} }

View file

@ -12,21 +12,16 @@ class ClientManagementModel : public QAbstractListModel
public: public:
enum ClientRoles { enum ClientRoles {
NameRole = Qt::UserRole + 1, NameRole = Qt::UserRole + 1,
CertIdRole, OpenVpnCertIdRole,
CertDataRole OpenVpnCertDataRole,
}; WireGuardPublicKey,
struct ClientInfo
{
QString name;
QString certId;
QString certData;
}; };
ClientManagementModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr); ClientManagementModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
void clearData(); void clearData();
void setContent(const QVector<ClientInfo> &data); void setContent(const QVector<QVariant> &data);
QJsonObject getContent();
int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void setData(const QModelIndex &index, QVariant data, int role = Qt::DisplayRole); void setData(const QModelIndex &index, QVariant data, int role = Qt::DisplayRole);
@ -36,7 +31,7 @@ protected:
private: private:
std::shared_ptr<Settings> m_settings; //TODO remove this? std::shared_ptr<Settings> m_settings; //TODO remove this?
QVector<ClientInfo> m_content; QVector<QVariant> m_content;
}; };
#endif // CLIENTMANAGEMENTMODEL_H #endif // CLIENTMANAGEMENTMODEL_H

View file

@ -21,11 +21,21 @@ void ClientInfoLogic::onUpdatePage()
QString selectedContainerName = ContainerProps::containerHumanNames().value(selectedContainer); QString selectedContainerName = ContainerProps::containerHumanNames().value(selectedContainer);
set_labelCurrentVpnProtocolText(tr("Service: ") + selectedContainerName); set_labelCurrentVpnProtocolText(tr("Service: ") + selectedContainerName);
auto protocols = ContainerProps::protocolsForContainer(selectedContainer);
if (!protocols.empty()) {
auto currentMainProtocol = protocols.front();
auto model = qobject_cast<ClientManagementModel*>(uiLogic()->clientManagementModel()); auto model = qobject_cast<ClientManagementModel*>(uiLogic()->clientManagementModel());
auto modelIndex = model->index(m_currentClientIndex); auto modelIndex = model->index(m_currentClientIndex);
set_lineEditNameAliasText(model->data(modelIndex, ClientManagementModel::ClientRoles::NameRole).toString()); set_lineEditNameAliasText(model->data(modelIndex, ClientManagementModel::ClientRoles::NameRole).toString());
set_labelCertId(model->data(modelIndex, ClientManagementModel::ClientRoles::CertIdRole).toString()); if (currentMainProtocol == Proto::OpenVpn) {
set_textAreaCertificate(model->data(modelIndex, ClientManagementModel::ClientRoles::CertDataRole).toString()); set_labelOpenVpnCertId(model->data(modelIndex, ClientManagementModel::ClientRoles::OpenVpnCertIdRole).toString());
set_textAreaOpenVpnCertData(model->data(modelIndex, ClientManagementModel::ClientRoles::OpenVpnCertDataRole).toString());
} else if (currentMainProtocol == Proto::WireGuard) {
set_textAreaWireGuardKeyData(model->data(modelIndex, ClientManagementModel::ClientRoles::WireGuardPublicKey).toString());
}
}
} }
void ClientInfoLogic::onLineEditNameAliasEditingFinished() void ClientInfoLogic::onLineEditNameAliasEditingFinished()
@ -34,10 +44,24 @@ void ClientInfoLogic::onLineEditNameAliasEditingFinished()
auto modelIndex = model->index(m_currentClientIndex); auto modelIndex = model->index(m_currentClientIndex);
model->setData(modelIndex, m_lineEditNameAliasText, ClientManagementModel::ClientRoles::NameRole); model->setData(modelIndex, m_lineEditNameAliasText, ClientManagementModel::ClientRoles::NameRole);
m_serverController->setClientsList(); auto clientsTable = model->getContent();
DockerContainer selectedContainer = m_settings->defaultContainer(uiLogic()->selectedServerIndex);
auto protocols = ContainerProps::protocolsForContainer(selectedContainer);
if (!protocols.empty()) {
auto currentMainProtocol = protocols.front();
m_serverController->setClientsList(m_settings->serverCredentials(uiLogic()->selectedServerIndex),
selectedContainer,
currentMainProtocol,
clientsTable);
}
} }
void ClientInfoLogic::onRevokeCertificateClicked() void ClientInfoLogic::onRevokeOpenVpnCertificateClicked()
{
}
void ClientInfoLogic::onRevokeWireGuardKeyClicked()
{ {
} }

View file

@ -10,9 +10,10 @@ class ClientInfoLogic : public PageLogicBase
Q_OBJECT Q_OBJECT
AUTO_PROPERTY(QString, lineEditNameAliasText) AUTO_PROPERTY(QString, lineEditNameAliasText)
AUTO_PROPERTY(QString, labelCertId) AUTO_PROPERTY(QString, labelOpenVpnCertId)
AUTO_PROPERTY(QString, textAreaCertificate) AUTO_PROPERTY(QString, textAreaOpenVpnCertData)
AUTO_PROPERTY(QString, labelCurrentVpnProtocolText) AUTO_PROPERTY(QString, labelCurrentVpnProtocolText)
AUTO_PROPERTY(QString, textAreaWireGuardKeyData)
public: public:
ClientInfoLogic(UiLogic *uiLogic, QObject *parent = nullptr); ClientInfoLogic(UiLogic *uiLogic, QObject *parent = nullptr);
@ -23,7 +24,8 @@ public:
public slots: public slots:
void onUpdatePage() override; void onUpdatePage() override;
void onLineEditNameAliasEditingFinished(); void onLineEditNameAliasEditingFinished();
void onRevokeCertificateClicked(); void onRevokeOpenVpnCertificateClicked();
void onRevokeWireGuardKeyClicked();
private: private:
int m_currentClientIndex; int m_currentClientIndex;

View file

@ -13,6 +13,7 @@ ClientManagementLogic::ClientManagementLogic(UiLogic *logic, QObject *parent):
void ClientManagementLogic::onUpdatePage() void ClientManagementLogic::onUpdatePage()
{ {
qobject_cast<ClientManagementModel*>(uiLogic()->clientManagementModel())->clearData();
DockerContainer selectedContainer = m_settings->defaultContainer(uiLogic()->selectedServerIndex); DockerContainer selectedContainer = m_settings->defaultContainer(uiLogic()->selectedServerIndex);
QString selectedContainerName = ContainerProps::containerHumanNames().value(selectedContainer); QString selectedContainerName = ContainerProps::containerHumanNames().value(selectedContainer);
set_labelCurrentVpnProtocolText(tr("Service: ") + selectedContainerName); set_labelCurrentVpnProtocolText(tr("Service: ") + selectedContainerName);
@ -26,13 +27,9 @@ void ClientManagementLogic::onUpdatePage()
ErrorCode e = m_serverController->getClientsList(m_settings->serverCredentials(uiLogic()->selectedServerIndex), ErrorCode e = m_serverController->getClientsList(m_settings->serverCredentials(uiLogic()->selectedServerIndex),
selectedContainer, m_currentMainProtocol, clients); selectedContainer, m_currentMainProtocol, clients);
} }
QVector<ClientManagementModel::ClientInfo> clientsArray; QVector<QVariant> clientsArray;
for (auto &clientId : clients.keys()) { for (auto &clientId : clients.keys()) {
ClientManagementModel::ClientInfo clientInfo; clientsArray.push_back(clients[clientId].toObject());
clientInfo.certId = clientId;
clientInfo.name = clients[clientId].toObject()["name"].toString();
clientInfo.certData = clients[clientId].toObject()["certificate"].toString();
clientsArray.push_back(clientInfo);
} }
qobject_cast<ClientManagementModel*>(uiLogic()->clientManagementModel())->setContent(clientsArray); qobject_cast<ClientManagementModel*>(uiLogic()->clientManagementModel())->setContent(clientsArray);
} }

View file

@ -9,7 +9,6 @@ import "../../Config"
PageClientInfoBase { PageClientInfoBase {
id: root id: root
protocol: ProtocolEnum.OpenVpn protocol: ProtocolEnum.OpenVpn
BackButton { BackButton {
id: back id: back
} }
@ -71,7 +70,7 @@ PageClientInfoBase {
LabelType { LabelType {
Layout.fillWidth: true Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
text: ClientInfoLogic.labelCertId text: ClientInfoLogic.labelOpenVpnCertId
} }
LabelType { LabelType {
@ -87,7 +86,7 @@ PageClientInfoBase {
textArea.readOnly: true textArea.readOnly: true
textArea.wrapMode: TextEdit.WrapAnywhere textArea.wrapMode: TextEdit.WrapAnywhere
textArea.verticalAlignment: Text.AlignTop textArea.verticalAlignment: Text.AlignTop
textArea.text: ClientInfoLogic.textAreaCertificate textArea.text: ClientInfoLogic.textAreaOpenVpnCertData
} }
BlueButtonType { BlueButtonType {
@ -95,7 +94,7 @@ PageClientInfoBase {
Layout.preferredHeight: 41 Layout.preferredHeight: 41
text: qsTr("Revoke Certificate") text: qsTr("Revoke Certificate")
onClicked: { onClicked: {
ClientInfoLogic.onRevokeCertificateClicked() ClientInfoLogic.onRevokeOpenVpnCertificateClicked()
} }
} }
} }

View file

@ -7,5 +7,84 @@ import "../../Controls"
import "../../Config" import "../../Config"
PageClientInfoBase { PageClientInfoBase {
id: root
protocol: ProtocolEnum.WireGuard
BackButton {
id: back
}
Caption {
id: caption
text: qsTr("Client Info")
}
Flickable {
id: fl
width: root.width
anchors.top: caption.bottom
anchors.topMargin: 20
anchors.bottom: root.bottom
anchors.bottomMargin: 20
anchors.left: root.left
anchors.leftMargin: 30
anchors.right: root.right
anchors.rightMargin: 30
contentHeight: content.height
clip: true
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
LabelType {
Layout.fillWidth: true
font.pixelSize: 20
horizontalAlignment: Text.AlignHCenter
text: ClientInfoLogic.labelCurrentVpnProtocolText
}
LabelType {
height: 21
text: qsTr("Client name")
}
TextFieldType {
Layout.fillWidth: true
Layout.preferredHeight: 31
text: ClientInfoLogic.lineEditNameAliasText
onEditingFinished: {
ClientInfoLogic.lineEditNameAliasText = text
ClientInfoLogic.onLineEditNameAliasEditingFinished()
}
}
LabelType {
Layout.topMargin: 20
height: 21
text: qsTr("Public Key")
}
TextAreaType {
Layout.preferredHeight: 200
Layout.fillWidth: true
textArea.readOnly: true
textArea.wrapMode: TextEdit.WrapAnywhere
textArea.verticalAlignment: Text.AlignTop
textArea.text: ClientInfoLogic.textAreaWireGuardKeyData
}
BlueButtonType {
Layout.fillWidth: true
Layout.preferredHeight: 41
text: qsTr("Revoke Key")
onClicked: {
ClientInfoLogic.onRevokeWireGuardKeyClicked()
}
}
}
}
} }

View file

@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Shapes 1.4 import QtQuick.Shapes 1.4
import SortFilterProxyModel 0.2
import PageEnum 1.0 import PageEnum 1.0
import "./" import "./"
import "../Controls" import "../Controls"
@ -45,6 +46,12 @@ PageBase {
text: ServerSettingsLogic.labelCurrentVpnProtocolText text: ServerSettingsLogic.labelCurrentVpnProtocolText
} }
SortFilterProxyModel {
id: proxyClientManagementModel
sourceModel: UiLogic.clientManagementModel
sorters: RoleSorter { roleName: "clientName" }
}
ListView { ListView {
id: lv_clients id: lv_clients
width: parent.width width: parent.width
@ -56,7 +63,7 @@ PageBase {
topMargin: 10 topMargin: 10
spacing: 10 spacing: 10
clip: true clip: true
model: UiLogic.clientManagementModel model: proxyClientManagementModel
highlightRangeMode: ListView.ApplyRange highlightRangeMode: ListView.ApplyRange
highlightMoveVelocity: -1 highlightMoveVelocity: -1
delegate: Item { delegate: Item {
@ -69,7 +76,7 @@ PageBase {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
ClientManagementLogic.onClientItemClicked(index) ClientManagementLogic.onClientItemClicked(proxyClientManagementModel.mapToSource(index))
} }
} }
@ -96,23 +103,11 @@ PageBase {
} }
} }
Text {
x: 10
y: 10
font.family: "Lato"
font.styleName: "normal"
color: "#181922"
verticalAlignment: Text.AlignVCenter
wrapMode: Text.Wrap
text: clientName
}
LabelType { LabelType {
x: 20 x: 20
y: 40 y: 20
// width: 141 font.pixelSize: 20
height: 16 text: clientName
text: certId
} }
} }
} }