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);
// TODO error processing
if (!stdOut.isEmpty()) {
QStringList clietnsNames = stdOut.split("\n", Qt::SkipEmptyParts);
clietnsNames.removeAll("AmneziaReq.crt");
QStringList certsIds = stdOut.split("\n", Qt::SkipEmptyParts);
certsIds.removeAll("AmneziaReq.crt");
QByteArray clientsTableString = getTextFileFromContainer(container, credentials, "opt/amnezia/openvpn/clientsTable");
QJsonObject clientsTable = QJsonDocument::fromJson(clientsTableString).object();
for (auto &clietnId : clietnsNames) {
clietnId.replace(".crt", "");
if (!clientsTable.contains(clietnId)) {
int count = 0;
for (auto &openvpnCertId : certsIds) {
openvpnCertId.replace(".crt", "");
if (!clientsTable.contains(openvpnCertId)) {
stdOut.clear();
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);
// TODO error processing
QJsonObject client;
client["name"] = "";
client["certificate"] = stdOut;
clientsTable[clietnId] = client;
client["openvpnCertId"] = openvpnCertId;
client["clientName"] = QString("Client %1").arg(count);
client["openvpnCertData"] = stdOut;
clientsTable[openvpnCertId] = client;
count++;
}
}
QByteArray newClientsTableString = QJsonDocument(clientsTable).toJson();
@ -837,7 +840,33 @@ ErrorCode ServerController::getClientsList(const ServerCredentials &credentials,
clietns = clientsTable;
}
} 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;
@ -845,7 +874,12 @@ ErrorCode ServerController::getClientsList(const ServerCredentials &credentials,
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;
}

View file

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

View file

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

View file

@ -21,11 +21,21 @@ void ClientInfoLogic::onUpdatePage()
QString selectedContainerName = ContainerProps::containerHumanNames().value(selectedContainer);
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 modelIndex = model->index(m_currentClientIndex);
set_lineEditNameAliasText(model->data(modelIndex, ClientManagementModel::ClientRoles::NameRole).toString());
set_labelCertId(model->data(modelIndex, ClientManagementModel::ClientRoles::CertIdRole).toString());
set_textAreaCertificate(model->data(modelIndex, ClientManagementModel::ClientRoles::CertDataRole).toString());
if (currentMainProtocol == Proto::OpenVpn) {
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()
@ -34,10 +44,24 @@ void ClientInfoLogic::onLineEditNameAliasEditingFinished()
auto modelIndex = model->index(m_currentClientIndex);
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
AUTO_PROPERTY(QString, lineEditNameAliasText)
AUTO_PROPERTY(QString, labelCertId)
AUTO_PROPERTY(QString, textAreaCertificate)
AUTO_PROPERTY(QString, labelOpenVpnCertId)
AUTO_PROPERTY(QString, textAreaOpenVpnCertData)
AUTO_PROPERTY(QString, labelCurrentVpnProtocolText)
AUTO_PROPERTY(QString, textAreaWireGuardKeyData)
public:
ClientInfoLogic(UiLogic *uiLogic, QObject *parent = nullptr);
@ -23,7 +24,8 @@ public:
public slots:
void onUpdatePage() override;
void onLineEditNameAliasEditingFinished();
void onRevokeCertificateClicked();
void onRevokeOpenVpnCertificateClicked();
void onRevokeWireGuardKeyClicked();
private:
int m_currentClientIndex;

View file

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

View file

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

View file

@ -7,5 +7,84 @@ import "../../Controls"
import "../../Config"
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.Layouts
import QtQuick.Shapes 1.4
import SortFilterProxyModel 0.2
import PageEnum 1.0
import "./"
import "../Controls"
@ -45,6 +46,12 @@ PageBase {
text: ServerSettingsLogic.labelCurrentVpnProtocolText
}
SortFilterProxyModel {
id: proxyClientManagementModel
sourceModel: UiLogic.clientManagementModel
sorters: RoleSorter { roleName: "clientName" }
}
ListView {
id: lv_clients
width: parent.width
@ -56,7 +63,7 @@ PageBase {
topMargin: 10
spacing: 10
clip: true
model: UiLogic.clientManagementModel
model: proxyClientManagementModel
highlightRangeMode: ListView.ApplyRange
highlightMoveVelocity: -1
delegate: Item {
@ -69,7 +76,7 @@ PageBase {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
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 {
x: 20
y: 40
// width: 141
height: 16
text: certId
y: 20
font.pixelSize: 20
text: clientName
}
}
}