Merge pull request #149 from amnezia-vpn/feature/qt6-client-management-panel
feature/qt6-client-management-panel
This commit is contained in:
commit
9a6df25280
27 changed files with 1128 additions and 6 deletions
|
@ -32,11 +32,11 @@ public:
|
||||||
|
|
||||||
// create initial config - generate passwords, etc
|
// create initial config - generate passwords, etc
|
||||||
QJsonObject createContainerInitialConfig(DockerContainer container, int port, TransportProto tp);
|
QJsonObject createContainerInitialConfig(DockerContainer container, int port, TransportProto tp);
|
||||||
|
ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject());
|
||||||
|
|
||||||
ErrorCode uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials,
|
ErrorCode uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||||
const QString &file, const QString &path,
|
const QString &file, const QString &path,
|
||||||
libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
|
libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
|
||||||
|
|
||||||
QByteArray getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
QByteArray getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||||
const QString &path, ErrorCode *errorCode = nullptr);
|
const QString &path, ErrorCode *errorCode = nullptr);
|
||||||
|
|
||||||
|
@ -62,7 +62,6 @@ private:
|
||||||
ErrorCode buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject());
|
ErrorCode buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject());
|
||||||
ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config);
|
ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config);
|
||||||
ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config);
|
ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config);
|
||||||
ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject());
|
|
||||||
|
|
||||||
ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config);
|
ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config);
|
||||||
bool isReinstallContainerRequred(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig);
|
bool isReinstallContainerRequred(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig);
|
||||||
|
|
|
@ -77,6 +77,7 @@ constexpr char defaultSubnetAddress[] = "10.8.0.0";
|
||||||
constexpr char defaultSubnetMask[] = "255.255.255.0";
|
constexpr char defaultSubnetMask[] = "255.255.255.0";
|
||||||
constexpr char defaultSubnetCidr[] = "24";
|
constexpr char defaultSubnetCidr[] = "24";
|
||||||
|
|
||||||
|
constexpr char serverConfigPath[] = "/opt/amnezia/openvpn/server.conf";
|
||||||
constexpr char caCertPath[] = "/opt/amnezia/openvpn/pki/ca.crt";
|
constexpr char caCertPath[] = "/opt/amnezia/openvpn/pki/ca.crt";
|
||||||
constexpr char clientCertPath[] = "/opt/amnezia/openvpn/pki/issued";
|
constexpr char clientCertPath[] = "/opt/amnezia/openvpn/pki/issued";
|
||||||
constexpr char taKeyPath[] = "/opt/amnezia/openvpn/ta.key";
|
constexpr char taKeyPath[] = "/opt/amnezia/openvpn/ta.key";
|
||||||
|
|
|
@ -84,6 +84,10 @@
|
||||||
<file>ui/qml/Pages/PageAbout.qml</file>
|
<file>ui/qml/Pages/PageAbout.qml</file>
|
||||||
<file>ui/qml/Pages/PageQrDecoderIos.qml</file>
|
<file>ui/qml/Pages/PageQrDecoderIos.qml</file>
|
||||||
<file>ui/qml/Pages/PageViewConfig.qml</file>
|
<file>ui/qml/Pages/PageViewConfig.qml</file>
|
||||||
|
<file>ui/qml/Pages/PageClientManagement.qml</file>
|
||||||
|
<file>ui/qml/Pages/ClientInfo/PageClientInfoBase.qml</file>
|
||||||
|
<file>ui/qml/Pages/ClientInfo/PageClientInfoOpenVPN.qml</file>
|
||||||
|
<file>ui/qml/Pages/ClientInfo/PageClientInfoWireGuard.qml</file>
|
||||||
<file>ui/qml/Pages/Protocols/PageProtoCloak.qml</file>
|
<file>ui/qml/Pages/Protocols/PageProtoCloak.qml</file>
|
||||||
<file>ui/qml/Pages/Protocols/PageProtoOpenVPN.qml</file>
|
<file>ui/qml/Pages/Protocols/PageProtoOpenVPN.qml</file>
|
||||||
<file>ui/qml/Pages/Protocols/PageProtoShadowSocks.qml</file>
|
<file>ui/qml/Pages/Protocols/PageProtoShadowSocks.qml</file>
|
||||||
|
|
|
@ -18,6 +18,7 @@ user nobody
|
||||||
group nobody
|
group nobody
|
||||||
persist-key
|
persist-key
|
||||||
persist-tun
|
persist-tun
|
||||||
|
crl-verify crl.pem
|
||||||
status openvpn-status.log
|
status openvpn-status.log
|
||||||
verb 1
|
verb 1
|
||||||
tls-server
|
tls-server
|
||||||
|
|
|
@ -5,6 +5,7 @@ resolv-retry infinite
|
||||||
nobind
|
nobind
|
||||||
persist-key
|
persist-key
|
||||||
persist-tun
|
persist-tun
|
||||||
|
crl-verify crl.pem
|
||||||
$OPENVPN_NCP_DISABLE
|
$OPENVPN_NCP_DISABLE
|
||||||
cipher $OPENVPN_CIPHER
|
cipher $OPENVPN_CIPHER
|
||||||
auth $OPENVPN_HASH
|
auth $OPENVPN_HASH
|
||||||
|
|
|
@ -18,6 +18,7 @@ user nobody
|
||||||
group nobody
|
group nobody
|
||||||
persist-key
|
persist-key
|
||||||
persist-tun
|
persist-tun
|
||||||
|
crl-verify crl.pem
|
||||||
status openvpn-status.log
|
status openvpn-status.log
|
||||||
verb 1
|
verb 1
|
||||||
tls-server
|
tls-server
|
||||||
|
|
|
@ -5,6 +5,7 @@ resolv-retry infinite
|
||||||
nobind
|
nobind
|
||||||
persist-key
|
persist-key
|
||||||
persist-tun
|
persist-tun
|
||||||
|
crl-verify crl.pem
|
||||||
$OPENVPN_NCP_DISABLE
|
$OPENVPN_NCP_DISABLE
|
||||||
cipher $OPENVPN_CIPHER
|
cipher $OPENVPN_CIPHER
|
||||||
auth $OPENVPN_HASH
|
auth $OPENVPN_HASH
|
||||||
|
|
|
@ -18,6 +18,7 @@ user nobody
|
||||||
group nobody
|
group nobody
|
||||||
persist-key
|
persist-key
|
||||||
persist-tun
|
persist-tun
|
||||||
|
crl-verify crl.pem
|
||||||
status openvpn-status.log
|
status openvpn-status.log
|
||||||
verb 1
|
verb 1
|
||||||
tls-server
|
tls-server
|
||||||
|
|
|
@ -5,6 +5,7 @@ resolv-retry infinite
|
||||||
nobind
|
nobind
|
||||||
persist-key
|
persist-key
|
||||||
persist-tun
|
persist-tun
|
||||||
|
crl-verify crl.pem
|
||||||
$OPENVPN_NCP_DISABLE
|
$OPENVPN_NCP_DISABLE
|
||||||
cipher $OPENVPN_CIPHER
|
cipher $OPENVPN_CIPHER
|
||||||
auth $OPENVPN_HASH
|
auth $OPENVPN_HASH
|
||||||
|
|
104
client/ui/models/clientManagementModel.cpp
Normal file
104
client/ui/models/clientManagementModel.cpp
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
#include "clientManagementModel.h"
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
ClientManagementModel::ClientManagementModel(QObject *parent) : QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManagementModel::clearData()
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_content.clear();
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManagementModel::setContent(const QVector<QVariant> &data)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_content = data;
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ClientManagementModel::getContent(amnezia::Proto protocol)
|
||||||
|
{
|
||||||
|
QJsonObject clientsTable;
|
||||||
|
for (const auto &item : m_content) {
|
||||||
|
if (protocol == amnezia::Proto::OpenVpn) {
|
||||||
|
clientsTable[item.toJsonObject()["openvpnCertId"].toString()] = item.toJsonObject();
|
||||||
|
} else if (protocol == amnezia::Proto::WireGuard) {
|
||||||
|
clientsTable[item.toJsonObject()["wireguardPublicKey"].toString()] = item.toJsonObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clientsTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ClientManagementModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent);
|
||||||
|
return static_cast<int>(m_content.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ClientManagementModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || index.row() < 0
|
||||||
|
|| index.row() >= static_cast<int>(m_content.size())) {
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == NameRole) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManagementModel::setData(const QModelIndex &index, QVariant data, int role)
|
||||||
|
{
|
||||||
|
if (!index.isValid() || index.row() < 0
|
||||||
|
|| index.row() >= static_cast<int>(m_content.size())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto client = m_content[index.row()].toJsonObject();
|
||||||
|
if (role == NameRole) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if (m_content[index.row()] != client) {
|
||||||
|
m_content[index.row()] = client;
|
||||||
|
emit dataChanged(index, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClientManagementModel::removeRows(int row)
|
||||||
|
{
|
||||||
|
beginRemoveRows(QModelIndex(), row, row);
|
||||||
|
m_content.removeAt(row);
|
||||||
|
endRemoveRows();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> ClientManagementModel::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> roles;
|
||||||
|
roles[NameRole] = "clientName";
|
||||||
|
roles[OpenVpnCertIdRole] = "openvpnCertId";
|
||||||
|
roles[OpenVpnCertDataRole] = "openvpnCertData";
|
||||||
|
roles[WireGuardPublicKey] = "wireguardPublicKey";
|
||||||
|
return roles;
|
||||||
|
}
|
37
client/ui/models/clientManagementModel.h
Normal file
37
client/ui/models/clientManagementModel.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef CLIENTMANAGEMENTMODEL_H
|
||||||
|
#define CLIENTMANAGEMENTMODEL_H
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
|
||||||
|
#include "protocols/protocols_defs.h"
|
||||||
|
|
||||||
|
class ClientManagementModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum ClientRoles {
|
||||||
|
NameRole = Qt::UserRole + 1,
|
||||||
|
OpenVpnCertIdRole,
|
||||||
|
OpenVpnCertDataRole,
|
||||||
|
WireGuardPublicKey,
|
||||||
|
};
|
||||||
|
|
||||||
|
ClientManagementModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
void clearData();
|
||||||
|
void setContent(const QVector<QVariant> &data);
|
||||||
|
QJsonObject getContent(amnezia::Proto protocol);
|
||||||
|
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);
|
||||||
|
bool removeRows(int row);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVector<QVariant> m_content;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CLIENTMANAGEMENTMODEL_H
|
|
@ -12,7 +12,8 @@ public:
|
||||||
enum Type {
|
enum Type {
|
||||||
Basic,
|
Basic,
|
||||||
Proto,
|
Proto,
|
||||||
ShareProto
|
ShareProto,
|
||||||
|
ClientInfo
|
||||||
};
|
};
|
||||||
Q_ENUM(Type)
|
Q_ENUM(Type)
|
||||||
};
|
};
|
||||||
|
@ -24,7 +25,8 @@ enum class Page {Start = 0, NewServer, NewServerProtocols, Vpn,
|
||||||
Wizard, WizardLow, WizardMedium, WizardHigh, WizardVpnMode, ServerConfiguringProgress,
|
Wizard, WizardLow, WizardMedium, WizardHigh, WizardVpnMode, ServerConfiguringProgress,
|
||||||
GeneralSettings, AppSettings, NetworkSettings, ServerSettings,
|
GeneralSettings, AppSettings, NetworkSettings, ServerSettings,
|
||||||
ServerContainers, ServersList, ShareConnection, Sites,
|
ServerContainers, ServersList, ShareConnection, Sites,
|
||||||
ProtocolSettings, ProtocolShare, QrDecoder, QrDecoderIos, About, ViewConfig, AdvancedServerSettings};
|
ProtocolSettings, ProtocolShare, QrDecoder, QrDecoderIos, About, ViewConfig,
|
||||||
|
AdvancedServerSettings, ClientManagement, ClientInfo};
|
||||||
Q_ENUM_NS(Page)
|
Q_ENUM_NS(Page)
|
||||||
|
|
||||||
static void declareQmlPageEnum() {
|
static void declareQmlPageEnum() {
|
||||||
|
|
213
client/ui/pages_logic/ClientInfoLogic.cpp
Normal file
213
client/ui/pages_logic/ClientInfoLogic.cpp
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
#include "ClientInfoLogic.h"
|
||||||
|
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
|
#include "core/errorstrings.h"
|
||||||
|
#include "core/servercontroller.h"
|
||||||
|
#include "ui/models/clientManagementModel.h"
|
||||||
|
#include "ui/uilogic.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
bool isErrorOccured(ErrorCode error) {
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
QMessageBox::warning(nullptr, APPLICATION_NAME,
|
||||||
|
QObject::tr("An error occurred while saving the list of clients.") + "\n" + errorString(error));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientInfoLogic::ClientInfoLogic(UiLogic *logic, QObject *parent):
|
||||||
|
PageLogicBase(logic, parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientInfoLogic::setCurrentClientId(int index)
|
||||||
|
{
|
||||||
|
m_currentClientIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientInfoLogic::onUpdatePage()
|
||||||
|
{
|
||||||
|
set_pageContentVisible(false);
|
||||||
|
set_busyIndicatorIsRunning(true);
|
||||||
|
|
||||||
|
const ServerCredentials credentials = m_settings->serverCredentials(uiLogic()->m_selectedServerIndex);
|
||||||
|
const DockerContainer container = m_settings->defaultContainer(uiLogic()->m_selectedServerIndex);
|
||||||
|
const QString containerNameString = ContainerProps::containerHumanNames().value(container);
|
||||||
|
set_labelCurrentVpnProtocolText(tr("Service: ") + containerNameString);
|
||||||
|
|
||||||
|
const QVector<amnezia::Proto> protocols = ContainerProps::protocolsForContainer(container);
|
||||||
|
if (!protocols.empty()) {
|
||||||
|
const Proto currentMainProtocol = protocols.front();
|
||||||
|
|
||||||
|
auto model = qobject_cast<ClientManagementModel*>(uiLogic()->clientManagementModel());
|
||||||
|
const QModelIndex modelIndex = model->index(m_currentClientIndex);
|
||||||
|
|
||||||
|
set_lineEditNameAliasText(model->data(modelIndex, ClientManagementModel::ClientRoles::NameRole).toString());
|
||||||
|
if (currentMainProtocol == Proto::OpenVpn) {
|
||||||
|
const QString certId = model->data(modelIndex, ClientManagementModel::ClientRoles::OpenVpnCertIdRole).toString();
|
||||||
|
QString certData = model->data(modelIndex, ClientManagementModel::ClientRoles::OpenVpnCertDataRole).toString();
|
||||||
|
|
||||||
|
if (certData.isEmpty() && !certId.isEmpty()) {
|
||||||
|
QString stdOut;
|
||||||
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
};
|
||||||
|
|
||||||
|
const QString getOpenVpnCertData = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'cat /opt/amnezia/openvpn/pki/issued/%1.crt'")
|
||||||
|
.arg(certId);
|
||||||
|
ServerController serverController(m_settings);
|
||||||
|
const QString script = serverController.replaceVars(getOpenVpnCertData, serverController.genVarsForScript(credentials, container));
|
||||||
|
ErrorCode error = serverController.runScript(credentials, script, cbReadStdOut);
|
||||||
|
certData = stdOut;
|
||||||
|
if (isErrorOccured(error)) {
|
||||||
|
set_busyIndicatorIsRunning(false);
|
||||||
|
emit uiLogic()->closePage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set_labelOpenVpnCertId(certId);
|
||||||
|
set_textAreaOpenVpnCertData(certData);
|
||||||
|
} else if (currentMainProtocol == Proto::WireGuard) {
|
||||||
|
set_textAreaWireGuardKeyData(model->data(modelIndex, ClientManagementModel::ClientRoles::WireGuardPublicKey).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set_pageContentVisible(true);
|
||||||
|
set_busyIndicatorIsRunning(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientInfoLogic::onLineEditNameAliasEditingFinished()
|
||||||
|
{
|
||||||
|
set_busyIndicatorIsRunning(true);
|
||||||
|
|
||||||
|
auto model = qobject_cast<ClientManagementModel*>(uiLogic()->clientManagementModel());
|
||||||
|
const QModelIndex modelIndex = model->index(m_currentClientIndex);
|
||||||
|
model->setData(modelIndex, m_lineEditNameAliasText, ClientManagementModel::ClientRoles::NameRole);
|
||||||
|
|
||||||
|
const DockerContainer selectedContainer = m_settings->defaultContainer(uiLogic()->m_selectedServerIndex);
|
||||||
|
const ServerCredentials credentials = m_settings->serverCredentials(uiLogic()->m_selectedServerIndex);
|
||||||
|
const QVector<amnezia::Proto> protocols = ContainerProps::protocolsForContainer(selectedContainer);
|
||||||
|
if (!protocols.empty()) {
|
||||||
|
const Proto currentMainProtocol = protocols.front();
|
||||||
|
const QJsonObject clientsTable = model->getContent(currentMainProtocol);
|
||||||
|
ErrorCode error = setClientsList(credentials,
|
||||||
|
selectedContainer,
|
||||||
|
currentMainProtocol,
|
||||||
|
clientsTable);
|
||||||
|
isErrorOccured(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_busyIndicatorIsRunning(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientInfoLogic::onRevokeOpenVpnCertificateClicked()
|
||||||
|
{
|
||||||
|
set_busyIndicatorIsRunning(true);
|
||||||
|
const DockerContainer container = m_settings->defaultContainer(uiLogic()->m_selectedServerIndex);
|
||||||
|
const ServerCredentials credentials = m_settings->serverCredentials(uiLogic()->m_selectedServerIndex);
|
||||||
|
|
||||||
|
auto model = qobject_cast<ClientManagementModel*>(uiLogic()->clientManagementModel());
|
||||||
|
const QModelIndex modelIndex = model->index(m_currentClientIndex);
|
||||||
|
const QString certId = model->data(modelIndex, ClientManagementModel::ClientRoles::OpenVpnCertIdRole).toString();
|
||||||
|
|
||||||
|
const QString getOpenVpnCertData = QString("sudo docker exec -i $CONTAINER_NAME bash -c '"
|
||||||
|
"cd /opt/amnezia/openvpn ;\\"
|
||||||
|
"easyrsa revoke %1 ;\\"
|
||||||
|
"easyrsa gen-crl ;\\"
|
||||||
|
"cp pki/crl.pem .'").arg(certId);
|
||||||
|
ServerController serverController(m_settings);
|
||||||
|
const QString script = serverController.replaceVars(getOpenVpnCertData,
|
||||||
|
serverController.genVarsForScript(credentials, container));
|
||||||
|
auto error = serverController.runScript(credentials, script);
|
||||||
|
if (isErrorOccured(error)) {
|
||||||
|
set_busyIndicatorIsRunning(false);
|
||||||
|
emit uiLogic()->goToPage(Page::ServerSettings);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
model->removeRows(m_currentClientIndex);
|
||||||
|
const QJsonObject clientsTable = model->getContent(Proto::OpenVpn);
|
||||||
|
error = setClientsList(credentials, container, Proto::OpenVpn, clientsTable);
|
||||||
|
if (isErrorOccured(error)) {
|
||||||
|
set_busyIndicatorIsRunning(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QJsonObject &containerConfig = m_settings->containerConfig(uiLogic()->m_selectedServerIndex, container);
|
||||||
|
error = serverController.startupContainerWorker(credentials, container, containerConfig);
|
||||||
|
if (isErrorOccured(error)) {
|
||||||
|
set_busyIndicatorIsRunning(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_busyIndicatorIsRunning(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientInfoLogic::onRevokeWireGuardKeyClicked()
|
||||||
|
{
|
||||||
|
set_busyIndicatorIsRunning(true);
|
||||||
|
ErrorCode error;
|
||||||
|
const DockerContainer container = m_settings->defaultContainer(uiLogic()->m_selectedServerIndex);
|
||||||
|
const ServerCredentials credentials = m_settings->serverCredentials(uiLogic()->m_selectedServerIndex);
|
||||||
|
|
||||||
|
ServerController serverController(m_settings);
|
||||||
|
|
||||||
|
const QString wireGuardConfigFile = "opt/amnezia/wireguard/wg0.conf";
|
||||||
|
const QString wireguardConfigString = serverController.getTextFileFromContainer(container, credentials, wireGuardConfigFile, &error);
|
||||||
|
if (isErrorOccured(error)) {
|
||||||
|
set_busyIndicatorIsRunning(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto model = qobject_cast<ClientManagementModel*>(uiLogic()->clientManagementModel());
|
||||||
|
const QModelIndex modelIndex = model->index(m_currentClientIndex);
|
||||||
|
const QString key = model->data(modelIndex, ClientManagementModel::ClientRoles::WireGuardPublicKey).toString();
|
||||||
|
|
||||||
|
auto configSections = wireguardConfigString.split("[", Qt::SkipEmptyParts);
|
||||||
|
for (auto §ion : configSections) {
|
||||||
|
if (section.contains(key)) {
|
||||||
|
configSections.removeOne(section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QString newWireGuardConfig = configSections.join("[");
|
||||||
|
newWireGuardConfig.insert(0, "[");
|
||||||
|
error = serverController.uploadTextFileToContainer(container, credentials, newWireGuardConfig,
|
||||||
|
protocols::wireguard::serverConfigPath,
|
||||||
|
libssh::SftpOverwriteMode::SftpOverwriteExisting);
|
||||||
|
if (isErrorOccured(error)) {
|
||||||
|
set_busyIndicatorIsRunning(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
model->removeRows(m_currentClientIndex);
|
||||||
|
const QJsonObject clientsTable = model->getContent(Proto::WireGuard);
|
||||||
|
error = setClientsList(credentials, container, Proto::WireGuard, clientsTable);
|
||||||
|
if (isErrorOccured(error)) {
|
||||||
|
set_busyIndicatorIsRunning(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString script = "sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip /opt/amnezia/wireguard/wg0.conf)'";
|
||||||
|
error = serverController.runScript(credentials,
|
||||||
|
serverController.replaceVars(script, serverController.genVarsForScript(credentials, container)));
|
||||||
|
if (isErrorOccured(error)) {
|
||||||
|
set_busyIndicatorIsRunning(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_busyIndicatorIsRunning(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode ClientInfoLogic::setClientsList(const ServerCredentials &credentials, DockerContainer container, Proto mainProtocol, const QJsonObject &clietns)
|
||||||
|
{
|
||||||
|
const QString mainProtocolString = ProtocolProps::protoToString(mainProtocol);
|
||||||
|
const QString clientsTableFile = QString("opt/amnezia/%1/clientsTable").arg(mainProtocolString);
|
||||||
|
ServerController serverController(m_settings);
|
||||||
|
ErrorCode error = serverController.uploadTextFileToContainer(container, credentials, QJsonDocument(clietns).toJson(), clientsTableFile);
|
||||||
|
return error;
|
||||||
|
}
|
42
client/ui/pages_logic/ClientInfoLogic.h
Normal file
42
client/ui/pages_logic/ClientInfoLogic.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef CLIENTINFOLOGIC_H
|
||||||
|
#define CLIENTINFOLOGIC_H
|
||||||
|
|
||||||
|
#include "PageLogicBase.h"
|
||||||
|
|
||||||
|
#include "core/defs.h"
|
||||||
|
#include "containers/containers_defs.h"
|
||||||
|
#include "protocols/protocols_defs.h"
|
||||||
|
|
||||||
|
class UiLogic;
|
||||||
|
|
||||||
|
class ClientInfoLogic : public PageLogicBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
AUTO_PROPERTY(QString, lineEditNameAliasText)
|
||||||
|
AUTO_PROPERTY(QString, labelOpenVpnCertId)
|
||||||
|
AUTO_PROPERTY(QString, textAreaOpenVpnCertData)
|
||||||
|
AUTO_PROPERTY(QString, labelCurrentVpnProtocolText)
|
||||||
|
AUTO_PROPERTY(QString, textAreaWireGuardKeyData)
|
||||||
|
AUTO_PROPERTY(bool, busyIndicatorIsRunning);
|
||||||
|
AUTO_PROPERTY(bool, pageContentVisible);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ClientInfoLogic(UiLogic *uiLogic, QObject *parent = nullptr);
|
||||||
|
~ClientInfoLogic() = default;
|
||||||
|
|
||||||
|
void setCurrentClientId(int index);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onUpdatePage() override;
|
||||||
|
void onLineEditNameAliasEditingFinished();
|
||||||
|
void onRevokeOpenVpnCertificateClicked();
|
||||||
|
void onRevokeWireGuardKeyClicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ErrorCode setClientsList(const ServerCredentials &credentials, DockerContainer container, Proto mainProtocol, const QJsonObject &clietns);
|
||||||
|
|
||||||
|
int m_currentClientIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CLIENTINFOLOGIC_H
|
143
client/ui/pages_logic/ClientManagementLogic.cpp
Normal file
143
client/ui/pages_logic/ClientManagementLogic.cpp
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
#include "ClientManagementLogic.h"
|
||||||
|
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
|
#include "core/errorstrings.h"
|
||||||
|
#include "core/servercontroller.h"
|
||||||
|
#include "ui/pages_logic/ClientInfoLogic.h"
|
||||||
|
#include "ui/models/clientManagementModel.h"
|
||||||
|
#include "ui/uilogic.h"
|
||||||
|
|
||||||
|
ClientManagementLogic::ClientManagementLogic(UiLogic *logic, QObject *parent):
|
||||||
|
PageLogicBase(logic, parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManagementLogic::onUpdatePage()
|
||||||
|
{
|
||||||
|
set_busyIndicatorIsRunning(true);
|
||||||
|
|
||||||
|
qobject_cast<ClientManagementModel*>(uiLogic()->clientManagementModel())->clearData();
|
||||||
|
DockerContainer selectedContainer = m_settings->defaultContainer(uiLogic()->m_selectedServerIndex);
|
||||||
|
QString selectedContainerName = ContainerProps::containerHumanNames().value(selectedContainer);
|
||||||
|
set_labelCurrentVpnProtocolText(tr("Service: ") + selectedContainerName);
|
||||||
|
|
||||||
|
QJsonObject clients;
|
||||||
|
|
||||||
|
auto protocols = ContainerProps::protocolsForContainer(selectedContainer);
|
||||||
|
if (!protocols.empty()) {
|
||||||
|
m_currentMainProtocol = protocols.front();
|
||||||
|
|
||||||
|
const ServerCredentials credentials = m_settings->serverCredentials(uiLogic()->m_selectedServerIndex);
|
||||||
|
|
||||||
|
ErrorCode error = getClientsList(credentials, selectedContainer, m_currentMainProtocol, clients);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
QMessageBox::warning(nullptr, APPLICATION_NAME,
|
||||||
|
tr("An error occurred while getting the list of clients.") + "\n" + errorString(error));
|
||||||
|
set_busyIndicatorIsRunning(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QVector<QVariant> clientsArray;
|
||||||
|
for (auto &clientId : clients.keys()) {
|
||||||
|
clientsArray.push_back(clients[clientId].toObject());
|
||||||
|
}
|
||||||
|
qobject_cast<ClientManagementModel*>(uiLogic()->clientManagementModel())->setContent(clientsArray);
|
||||||
|
|
||||||
|
set_busyIndicatorIsRunning(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManagementLogic::onClientItemClicked(int index)
|
||||||
|
{
|
||||||
|
uiLogic()->pageLogic<ClientInfoLogic>()->setCurrentClientId(index);
|
||||||
|
emit uiLogic()->goToClientInfoPage(m_currentMainProtocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode ClientManagementLogic::getClientsList(const ServerCredentials &credentials, DockerContainer container, Proto mainProtocol, QJsonObject &clietns)
|
||||||
|
{
|
||||||
|
ErrorCode error = ErrorCode::NoError;
|
||||||
|
QString stdOut;
|
||||||
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
};
|
||||||
|
|
||||||
|
const QString mainProtocolString = ProtocolProps::protoToString(mainProtocol);
|
||||||
|
|
||||||
|
ServerController serverController(m_settings);
|
||||||
|
|
||||||
|
const QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable").arg(mainProtocolString);
|
||||||
|
const QByteArray clientsTableString = serverController.getTextFileFromContainer(container, credentials, clientsTableFile, &error);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
QJsonObject clientsTable = QJsonDocument::fromJson(clientsTableString).object();
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
if (mainProtocol == Proto::OpenVpn) {
|
||||||
|
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 (!clientsTable.contains(openvpnCertId)) {
|
||||||
|
|
||||||
|
QJsonObject client;
|
||||||
|
client["openvpnCertId"] = openvpnCertId;
|
||||||
|
client["clientName"] = QString("Client %1").arg(count);
|
||||||
|
client["openvpnCertData"] = "";
|
||||||
|
clientsTable[openvpnCertId] = client;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mainProtocol == Proto::WireGuard) {
|
||||||
|
const QString wireGuardConfigFile = "opt/amnezia/wireguard/wg0.conf";
|
||||||
|
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 (!clientsTable.contains(wireguardKey)) {
|
||||||
|
QJsonObject client;
|
||||||
|
client["clientName"] = QString("Client %1").arg(count);
|
||||||
|
client["wireguardPublicKey"] = wireguardKey;
|
||||||
|
clientsTable[wireguardKey] = client;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QByteArray newClientsTableString = QJsonDocument(clientsTable).toJson();
|
||||||
|
if (clientsTableString != newClientsTableString) {
|
||||||
|
error = serverController.uploadTextFileToContainer(container, credentials, newClientsTableString, clientsTableFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
clietns = clientsTable;
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
33
client/ui/pages_logic/ClientManagementLogic.h
Normal file
33
client/ui/pages_logic/ClientManagementLogic.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef CLIENTMANAGMENTLOGIC_H
|
||||||
|
#define CLIENTMANAGMENTLOGIC_H
|
||||||
|
|
||||||
|
#include "PageLogicBase.h"
|
||||||
|
|
||||||
|
#include "core/defs.h"
|
||||||
|
#include "containers/containers_defs.h"
|
||||||
|
#include "protocols/protocols_defs.h"
|
||||||
|
|
||||||
|
class UiLogic;
|
||||||
|
|
||||||
|
class ClientManagementLogic : public PageLogicBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
AUTO_PROPERTY(QString, labelCurrentVpnProtocolText)
|
||||||
|
AUTO_PROPERTY(bool, busyIndicatorIsRunning);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ClientManagementLogic(UiLogic *uiLogic, QObject *parent = nullptr);
|
||||||
|
~ClientManagementLogic() = default;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onUpdatePage() override;
|
||||||
|
void onClientItemClicked(int index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ErrorCode getClientsList(const ServerCredentials &credentials, DockerContainer container, Proto mainProtocol, QJsonObject &clietns);
|
||||||
|
|
||||||
|
amnezia::Proto m_currentMainProtocol;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CLIENTMANAGMENTLOGIC_H
|
15
client/ui/qml/Pages/ClientInfo/PageClientInfoBase.qml
Normal file
15
client/ui/qml/Pages/ClientInfo/PageClientInfoBase.qml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import PageEnum 1.0
|
||||||
|
import ProtocolEnum 1.0
|
||||||
|
import "../"
|
||||||
|
import "../../Controls"
|
||||||
|
import "../../Config"
|
||||||
|
|
||||||
|
PageBase {
|
||||||
|
id: root
|
||||||
|
property var protocol: ProtocolEnum.Any
|
||||||
|
page: PageEnum.ClientInfo
|
||||||
|
logic: ClientInfoLogic
|
||||||
|
}
|
115
client/ui/qml/Pages/ClientInfo/PageClientInfoOpenVPN.qml
Normal file
115
client/ui/qml/Pages/ClientInfo/PageClientInfoOpenVPN.qml
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import ProtocolEnum 1.0
|
||||||
|
import "../"
|
||||||
|
import "../../Controls"
|
||||||
|
import "../../Config"
|
||||||
|
|
||||||
|
PageClientInfoBase {
|
||||||
|
id: root
|
||||||
|
protocol: ProtocolEnum.OpenVpn
|
||||||
|
|
||||||
|
BackButton {
|
||||||
|
id: back
|
||||||
|
enabled: !ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
}
|
||||||
|
|
||||||
|
Caption {
|
||||||
|
id: caption
|
||||||
|
text: qsTr("Client Info")
|
||||||
|
}
|
||||||
|
|
||||||
|
BusyIndicator {
|
||||||
|
z: 99
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
running: ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
}
|
||||||
|
|
||||||
|
FlickableType {
|
||||||
|
id: fl
|
||||||
|
anchors.top: caption.bottom
|
||||||
|
contentHeight: content.height
|
||||||
|
visible: ClientInfoLogic.pageContentVisible
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: GC.defaultMargin
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
enabled: !ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pixelSize: 20
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: ClientInfoLogic.labelCurrentVpnProtocolText
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
enabled: !ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
height: 21
|
||||||
|
text: qsTr("Client name")
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFieldType {
|
||||||
|
enabled: !ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 31
|
||||||
|
text: ClientInfoLogic.lineEditNameAliasText
|
||||||
|
onEditingFinished: {
|
||||||
|
if (text !== ClientInfoLogic.lineEditNameAliasText) {
|
||||||
|
ClientInfoLogic.lineEditNameAliasText = text
|
||||||
|
ClientInfoLogic.onLineEditNameAliasEditingFinished()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
enabled: !ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
Layout.topMargin: 20
|
||||||
|
height: 21
|
||||||
|
text: qsTr("Certificate id")
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
enabled: !ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
Layout.fillWidth: true
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: ClientInfoLogic.labelOpenVpnCertId
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
enabled: !ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
Layout.topMargin: 20
|
||||||
|
height: 21
|
||||||
|
text: qsTr("Certificate")
|
||||||
|
}
|
||||||
|
|
||||||
|
TextAreaType {
|
||||||
|
enabled: !ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
Layout.preferredHeight: 200
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
textArea.readOnly: true
|
||||||
|
textArea.wrapMode: TextEdit.WrapAnywhere
|
||||||
|
textArea.verticalAlignment: Text.AlignTop
|
||||||
|
textArea.text: ClientInfoLogic.textAreaOpenVpnCertData
|
||||||
|
}
|
||||||
|
|
||||||
|
BlueButtonType {
|
||||||
|
enabled: !ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 41
|
||||||
|
text: qsTr("Revoke Certificate")
|
||||||
|
onClicked: {
|
||||||
|
ClientInfoLogic.onRevokeOpenVpnCertificateClicked()
|
||||||
|
UiLogic.closePage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
100
client/ui/qml/Pages/ClientInfo/PageClientInfoWireGuard.qml
Normal file
100
client/ui/qml/Pages/ClientInfo/PageClientInfoWireGuard.qml
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import ProtocolEnum 1.0
|
||||||
|
import "../"
|
||||||
|
import "../../Controls"
|
||||||
|
import "../../Config"
|
||||||
|
|
||||||
|
PageClientInfoBase {
|
||||||
|
id: root
|
||||||
|
protocol: ProtocolEnum.WireGuard
|
||||||
|
|
||||||
|
BackButton {
|
||||||
|
id: back
|
||||||
|
enabled: !ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
}
|
||||||
|
|
||||||
|
Caption {
|
||||||
|
id: caption
|
||||||
|
text: qsTr("Client Info")
|
||||||
|
}
|
||||||
|
|
||||||
|
BusyIndicator {
|
||||||
|
z: 99
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
running: ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
}
|
||||||
|
|
||||||
|
FlickableType {
|
||||||
|
id: fl
|
||||||
|
anchors.top: caption.bottom
|
||||||
|
contentHeight: content.height
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: GC.defaultMargin
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
enabled: !ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pixelSize: 20
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: ClientInfoLogic.labelCurrentVpnProtocolText
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
enabled: !ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
height: 21
|
||||||
|
text: qsTr("Client name")
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFieldType {
|
||||||
|
enabled: !ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 31
|
||||||
|
text: ClientInfoLogic.lineEditNameAliasText
|
||||||
|
onEditingFinished: {
|
||||||
|
if (text !== ClientInfoLogic.lineEditNameAliasText) {
|
||||||
|
ClientInfoLogic.lineEditNameAliasText = text
|
||||||
|
ClientInfoLogic.onLineEditNameAliasEditingFinished()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
enabled: !ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
Layout.topMargin: 20
|
||||||
|
height: 21
|
||||||
|
text: qsTr("Public Key")
|
||||||
|
}
|
||||||
|
|
||||||
|
TextAreaType {
|
||||||
|
enabled: !ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
Layout.preferredHeight: 200
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
textArea.readOnly: true
|
||||||
|
textArea.wrapMode: TextEdit.WrapAnywhere
|
||||||
|
textArea.verticalAlignment: Text.AlignTop
|
||||||
|
textArea.text: ClientInfoLogic.textAreaWireGuardKeyData
|
||||||
|
}
|
||||||
|
|
||||||
|
BlueButtonType {
|
||||||
|
enabled: !ClientInfoLogic.busyIndicatorIsRunning
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 41
|
||||||
|
text: qsTr("Revoke Key")
|
||||||
|
onClicked: {
|
||||||
|
ClientInfoLogic.onRevokeWireGuardKeyClicked()
|
||||||
|
UiLogic.closePage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
120
client/ui/qml/Pages/PageClientManagement.qml
Normal file
120
client/ui/qml/Pages/PageClientManagement.qml
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Shapes 1.4
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
|
import PageEnum 1.0
|
||||||
|
import "./"
|
||||||
|
import "../Controls"
|
||||||
|
import "../Config"
|
||||||
|
|
||||||
|
PageBase {
|
||||||
|
id: root
|
||||||
|
page: PageEnum.ClientManagement
|
||||||
|
logic: ClientManagementLogic
|
||||||
|
enabled: !ClientManagementLogic.busyIndicatorIsRunning
|
||||||
|
|
||||||
|
BackButton {
|
||||||
|
id: back
|
||||||
|
}
|
||||||
|
|
||||||
|
Caption {
|
||||||
|
id: caption
|
||||||
|
text: qsTr("Clients Management")
|
||||||
|
}
|
||||||
|
|
||||||
|
BusyIndicator {
|
||||||
|
z: 99
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: ClientManagementLogic.busyIndicatorIsRunning
|
||||||
|
running: ClientManagementLogic.busyIndicatorIsRunning
|
||||||
|
}
|
||||||
|
|
||||||
|
FlickableType {
|
||||||
|
id: fl
|
||||||
|
anchors.top: caption.bottom
|
||||||
|
contentHeight: content.height
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: content
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
font.pixelSize: 20
|
||||||
|
leftPadding: -20
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: ClientManagementLogic.labelCurrentVpnProtocolText
|
||||||
|
}
|
||||||
|
|
||||||
|
SortFilterProxyModel {
|
||||||
|
id: proxyClientManagementModel
|
||||||
|
sourceModel: UiLogic.clientManagementModel
|
||||||
|
sorters: RoleSorter { roleName: "clientName" }
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: lv_clients
|
||||||
|
width: parent.width
|
||||||
|
implicitHeight: contentHeight + 20
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
anchors.rightMargin: 10
|
||||||
|
topMargin: 10
|
||||||
|
spacing: 10
|
||||||
|
clip: true
|
||||||
|
model: proxyClientManagementModel
|
||||||
|
highlightRangeMode: ListView.ApplyRange
|
||||||
|
highlightMoveVelocity: -1
|
||||||
|
delegate: Item {
|
||||||
|
implicitWidth: lv_clients.width
|
||||||
|
implicitHeight: 60
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: ms
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
ClientManagementLogic.onClientItemClicked(proxyClientManagementModel.mapToSource(index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
gradient: ms.containsMouse ? gradient_containsMouse : gradient_notContainsMouse
|
||||||
|
LinearGradient {
|
||||||
|
id: gradient_notContainsMouse
|
||||||
|
x1: 0 ; y1:0
|
||||||
|
x2: 0 ; y2: height
|
||||||
|
stops: [
|
||||||
|
GradientStop { position: 0.0; color: "#FAFBFE" },
|
||||||
|
GradientStop { position: 1.0; color: "#ECEEFF" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
LinearGradient {
|
||||||
|
id: gradient_containsMouse
|
||||||
|
x1: 0 ; y1:0
|
||||||
|
x2: 0 ; y2: height
|
||||||
|
stops: [
|
||||||
|
GradientStop { position: 0.0; color: "#FAFBFE" },
|
||||||
|
GradientStop { position: 1.0; color: "#DCDEDF" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
x: 20
|
||||||
|
y: 20
|
||||||
|
font.pixelSize: 20
|
||||||
|
text: clientName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ PageBase {
|
||||||
BackButton {
|
BackButton {
|
||||||
id: back
|
id: back
|
||||||
}
|
}
|
||||||
|
|
||||||
Caption {
|
Caption {
|
||||||
id: caption
|
id: caption
|
||||||
text: qsTr("Server settings")
|
text: qsTr("Server settings")
|
||||||
|
@ -96,8 +97,16 @@ PageBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
BlueButtonType {
|
BlueButtonType {
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 60
|
Layout.topMargin: 60
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Clients Management")
|
||||||
|
onClicked: {
|
||||||
|
UiLogic.goToPage(PageEnum.ClientManagement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BlueButtonType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 10
|
||||||
text: ServerSettingsLogic.pushButtonClearClientCacheText
|
text: ServerSettingsLogic.pushButtonClearClientCacheText
|
||||||
visible: ServerSettingsLogic.pushButtonClearClientCacheVisible
|
visible: ServerSettingsLogic.pushButtonClearClientCacheVisible
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|
|
@ -12,12 +12,14 @@ import "Controls"
|
||||||
import "Pages"
|
import "Pages"
|
||||||
import "Pages/Protocols"
|
import "Pages/Protocols"
|
||||||
import "Pages/Share"
|
import "Pages/Share"
|
||||||
|
import "Pages/ClientInfo"
|
||||||
import "Config"
|
import "Config"
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
property var pages: ({})
|
property var pages: ({})
|
||||||
property var protocolPages: ({})
|
property var protocolPages: ({})
|
||||||
property var sharePages: ({})
|
property var sharePages: ({})
|
||||||
|
property var clientInfoPages: ({})
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
visible: true
|
visible: true
|
||||||
|
@ -38,6 +40,7 @@ Window {
|
||||||
if (type === PageType.Basic) p_obj = pages[page]
|
if (type === PageType.Basic) p_obj = pages[page]
|
||||||
else if (type === PageType.Proto) p_obj = protocolPages[page]
|
else if (type === PageType.Proto) p_obj = protocolPages[page]
|
||||||
else if (type === PageType.ShareProto) p_obj = sharePages[page]
|
else if (type === PageType.ShareProto) p_obj = sharePages[page]
|
||||||
|
else if (type === PageType.ClientInfo) p_obj = clientInfoPages[page]
|
||||||
else return
|
else return
|
||||||
|
|
||||||
//console.debug("QML gotoPage " + type + " " + page + " " + p_obj)
|
//console.debug("QML gotoPage " + type + " " + page + " " + p_obj)
|
||||||
|
@ -154,6 +157,19 @@ Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FolderListModel {
|
||||||
|
id: folderModelClientInfo
|
||||||
|
folder: "qrc:/ui/qml/Pages/ClientInfo/"
|
||||||
|
nameFilters: ["*.qml"]
|
||||||
|
showDirs: false
|
||||||
|
|
||||||
|
onStatusChanged: if (status == FolderListModel.Ready) {
|
||||||
|
for (var i=0; i<folderModelClientInfo.count; i++) {
|
||||||
|
createPagesObjects(folderModelClientInfo.get(i, "filePath"), PageType.ClientInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function createPagesObjects(file, type) {
|
function createPagesObjects(file, type) {
|
||||||
if (file.indexOf("Base") !== -1) return; // skip Base Pages
|
if (file.indexOf("Base") !== -1) return; // skip Base Pages
|
||||||
//console.debug("Creating compenent " + file + " for " + type);
|
//console.debug("Creating compenent " + file + " for " + type);
|
||||||
|
@ -177,6 +193,9 @@ Window {
|
||||||
else if (type === PageType.ShareProto) {
|
else if (type === PageType.ShareProto) {
|
||||||
sharePages[obj.protocol] = obj
|
sharePages[obj.protocol] = obj
|
||||||
}
|
}
|
||||||
|
else if (type === PageType.ClientInfo) {
|
||||||
|
clientInfoPages[obj.protocol] = obj
|
||||||
|
}
|
||||||
|
|
||||||
// console.debug("Created compenent " + component.url + " for " + type);
|
// console.debug("Created compenent " + component.url + " for " + type);
|
||||||
}
|
}
|
||||||
|
@ -206,7 +225,10 @@ Window {
|
||||||
//console.debug("Qml Connections onGoToShareProtocolPage " + protocol);
|
//console.debug("Qml Connections onGoToShareProtocolPage " + protocol);
|
||||||
root.gotoPage(PageType.ShareProto, protocol, reset, slide)
|
root.gotoPage(PageType.ShareProto, protocol, reset, slide)
|
||||||
}
|
}
|
||||||
|
function onGoToClientInfoPage(protocol, reset, slide) {
|
||||||
|
//console.debug("Qml Connections onGoToClientInfoPage " + protocol);
|
||||||
|
root.gotoPage(PageType.ClientInfo, protocol, reset, slide)
|
||||||
|
}
|
||||||
|
|
||||||
function onClosePage() {
|
function onClosePage() {
|
||||||
root.close_page()
|
root.close_page()
|
||||||
|
|
|
@ -66,6 +66,8 @@
|
||||||
#include "pages_logic/VpnLogic.h"
|
#include "pages_logic/VpnLogic.h"
|
||||||
#include "pages_logic/WizardLogic.h"
|
#include "pages_logic/WizardLogic.h"
|
||||||
#include "pages_logic/AdvancedServerSettingsLogic.h"
|
#include "pages_logic/AdvancedServerSettingsLogic.h"
|
||||||
|
#include "pages_logic/ClientManagementLogic.h"
|
||||||
|
#include "pages_logic/ClientInfoLogic.h"
|
||||||
|
|
||||||
#include "pages_logic/protocols/CloakLogic.h"
|
#include "pages_logic/protocols/CloakLogic.h"
|
||||||
#include "pages_logic/protocols/OpenVpnLogic.h"
|
#include "pages_logic/protocols/OpenVpnLogic.h"
|
||||||
|
@ -84,6 +86,7 @@ UiLogic::UiLogic(std::shared_ptr<Settings> settings, std::shared_ptr<VpnConfigur
|
||||||
{
|
{
|
||||||
m_containersModel = new ContainersModel(settings, this);
|
m_containersModel = new ContainersModel(settings, this);
|
||||||
m_protocolsModel = new ProtocolsModel(settings, this);
|
m_protocolsModel = new ProtocolsModel(settings, this);
|
||||||
|
m_clientManagementModel = new ClientManagementModel(this);
|
||||||
m_vpnConnection = new VpnConnection(settings, configurator);
|
m_vpnConnection = new VpnConnection(settings, configurator);
|
||||||
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
|
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
|
||||||
m_vpnConnectionThread.start();
|
m_vpnConnectionThread.start();
|
||||||
|
@ -533,6 +536,8 @@ void UiLogic::registerPagesLogic()
|
||||||
registerPageLogic<ViewConfigLogic>();
|
registerPageLogic<ViewConfigLogic>();
|
||||||
registerPageLogic<VpnLogic>();
|
registerPageLogic<VpnLogic>();
|
||||||
registerPageLogic<WizardLogic>();
|
registerPageLogic<WizardLogic>();
|
||||||
|
registerPageLogic<ClientManagementLogic>();
|
||||||
|
registerPageLogic<ClientInfoLogic>();
|
||||||
registerPageLogic<AdvancedServerSettingsLogic>();
|
registerPageLogic<AdvancedServerSettingsLogic>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include "models/containers_model.h"
|
#include "models/containers_model.h"
|
||||||
#include "models/protocols_model.h"
|
#include "models/protocols_model.h"
|
||||||
|
#include "models/clientManagementModel.h"
|
||||||
|
|
||||||
#include "notificationhandler.h"
|
#include "notificationhandler.h"
|
||||||
|
|
||||||
|
@ -43,6 +44,8 @@ class StartPageLogic;
|
||||||
class ViewConfigLogic;
|
class ViewConfigLogic;
|
||||||
class VpnLogic;
|
class VpnLogic;
|
||||||
class WizardLogic;
|
class WizardLogic;
|
||||||
|
class ClientManagementLogic;
|
||||||
|
class ClientInfoLogic;
|
||||||
class AdvancedServerSettingsLogic;
|
class AdvancedServerSettingsLogic;
|
||||||
|
|
||||||
class PageProtocolLogicBase;
|
class PageProtocolLogicBase;
|
||||||
|
@ -66,6 +69,7 @@ class UiLogic : public QObject
|
||||||
|
|
||||||
READONLY_PROPERTY(QObject *, containersModel)
|
READONLY_PROPERTY(QObject *, containersModel)
|
||||||
READONLY_PROPERTY(QObject *, protocolsModel)
|
READONLY_PROPERTY(QObject *, protocolsModel)
|
||||||
|
READONLY_PROPERTY(QObject *, clientManagementModel)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit UiLogic(std::shared_ptr<Settings> settings, std::shared_ptr<VpnConfigurator> configurator, QObject *parent = nullptr);
|
explicit UiLogic(std::shared_ptr<Settings> settings, std::shared_ptr<VpnConfigurator> configurator, QObject *parent = nullptr);
|
||||||
|
@ -88,6 +92,8 @@ public:
|
||||||
friend class ViewConfigLogic;
|
friend class ViewConfigLogic;
|
||||||
friend class VpnLogic;
|
friend class VpnLogic;
|
||||||
friend class WizardLogic;
|
friend class WizardLogic;
|
||||||
|
friend class ClientManagementLogic;
|
||||||
|
friend class ClientInfoLogic;
|
||||||
friend class AdvancedServerSettingsLogic;
|
friend class AdvancedServerSettingsLogic;
|
||||||
|
|
||||||
friend class PageProtocolLogicBase;
|
friend class PageProtocolLogicBase;
|
||||||
|
@ -129,6 +135,7 @@ signals:
|
||||||
void goToPage(PageEnumNS::Page page, bool reset = true, bool slide = true);
|
void goToPage(PageEnumNS::Page page, bool reset = true, bool slide = true);
|
||||||
void goToProtocolPage(Proto protocol, bool reset = true, bool slide = true);
|
void goToProtocolPage(Proto protocol, bool reset = true, bool slide = true);
|
||||||
void goToShareProtocolPage(Proto protocol, bool reset = true, bool slide = true);
|
void goToShareProtocolPage(Proto protocol, bool reset = true, bool slide = true);
|
||||||
|
void goToClientInfoPage(Proto protocol, bool reset = true, bool slide = true);
|
||||||
|
|
||||||
void closePage();
|
void closePage();
|
||||||
void setStartPage(PageEnumNS::Page page, bool slide = true);
|
void setStartPage(PageEnumNS::Page page, bool slide = true);
|
||||||
|
|
52
docs/openVpnClientInfoWorkFlow.plantuml
Normal file
52
docs/openVpnClientInfoWorkFlow.plantuml
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
@startuml openVpnRevokeClientCertificate
|
||||||
|
|
||||||
|
|client management page|
|
||||||
|
start
|
||||||
|
|
||||||
|
:on update client management page;
|
||||||
|
:get clientsTable file from container;
|
||||||
|
:get a list of issued certificates;
|
||||||
|
|
||||||
|
repeat
|
||||||
|
|
||||||
|
if ( for each issued certificate:
|
||||||
|
clientsTable contains the issued certificate) then (yes)
|
||||||
|
else (no)
|
||||||
|
:add certificate id to clientsTable;
|
||||||
|
endif
|
||||||
|
|
||||||
|
repeat while (is this the last issued certificate?)
|
||||||
|
|
||||||
|
if (if clientsTable has been changed) then (yes)
|
||||||
|
:save the clientsTable file on the server;
|
||||||
|
else (no)
|
||||||
|
endif
|
||||||
|
|
||||||
|
:add clientsTable to the clientManagementModel;
|
||||||
|
|
||||||
|
|client info page|
|
||||||
|
:on update client info page;
|
||||||
|
floating note
|
||||||
|
clicked on one of the clients
|
||||||
|
on the client management page
|
||||||
|
end note
|
||||||
|
|
||||||
|
:get the certificate data for the selected client;
|
||||||
|
if (if client name has been changed) then (yes)
|
||||||
|
:update clientManagementModel;
|
||||||
|
:get clientsTable from clientManagementModel;
|
||||||
|
:save the clientsTable file on the server;
|
||||||
|
else (no)
|
||||||
|
if (if revoke certificate was clicked) then (yes)
|
||||||
|
:described in file openVpnRevokeClientCertificate.plantuml;
|
||||||
|
|client management page|
|
||||||
|
:return to page client management;
|
||||||
|
stop
|
||||||
|
else (no)
|
||||||
|
|client info page|
|
||||||
|
:just look at the beautiful page;
|
||||||
|
stop
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
stop
|
||||||
|
@enduml
|
34
docs/openVpnRevokeClientCertificate.plantuml
Normal file
34
docs/openVpnRevokeClientCertificate.plantuml
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
@startuml openVpnRevokeClientCertificate
|
||||||
|
actor Admin as adm
|
||||||
|
participant "Amnezia Client" as cli
|
||||||
|
participant "Amnezia Container" as cont
|
||||||
|
participant "OpenVpn Service" as ovpn
|
||||||
|
|
||||||
|
adm -> cli: revoke the selected client certificate
|
||||||
|
cli -> cli: start busy indicator
|
||||||
|
cli -> cont: execute script "revoke openvpn client"
|
||||||
|
|
||||||
|
cont -> cont: cd /opt/amnezia/openvpn
|
||||||
|
cont -> cont: easyrsa revoke openvpnCertId
|
||||||
|
cont -> cont: easyrsa gen-crl
|
||||||
|
cont -> cont: cp pki/crl.pem crl.pem
|
||||||
|
cont -> ovpn: restart openvpn service
|
||||||
|
note right
|
||||||
|
In the OpenVpn config
|
||||||
|
there should be a line "crl-verify crl.pem".
|
||||||
|
After that, the service will ignore
|
||||||
|
the certificates contained in the crl.pem file
|
||||||
|
end note
|
||||||
|
|
||||||
|
|
||||||
|
group#lightgreen #lightgreen if [successful case]
|
||||||
|
ovpn --> cont: restart result
|
||||||
|
cont --> cli: back to the client management page
|
||||||
|
else #pink some kind of failure
|
||||||
|
cont --> cli: display an error depending on when it occurred
|
||||||
|
end
|
||||||
|
|
||||||
|
cli -> cli: stop busy indicator
|
||||||
|
cli --> adm: return control to the user
|
||||||
|
|
||||||
|
@enduml
|
59
docs/wireGuardClientInfoWorkFlow.plantuml
Normal file
59
docs/wireGuardClientInfoWorkFlow.plantuml
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
@startuml wireGuardRevokeClientCertificate
|
||||||
|
|
||||||
|
|client management page|
|
||||||
|
start
|
||||||
|
|
||||||
|
:on update client management page;
|
||||||
|
:get clientsTable file from server;
|
||||||
|
:get wireguard config from server;
|
||||||
|
|
||||||
|
repeat
|
||||||
|
|
||||||
|
if ( for each public key in wireguard config:
|
||||||
|
clientsTable contains the public key) then (yes)
|
||||||
|
else (no)
|
||||||
|
:add public key to clientsTable;
|
||||||
|
endif
|
||||||
|
|
||||||
|
repeat while (is this the last public key?)
|
||||||
|
|
||||||
|
if (if clientsTable has been changed) then (yes)
|
||||||
|
:save the clientsTable file on the server;
|
||||||
|
else (no)
|
||||||
|
endif
|
||||||
|
|
||||||
|
:add clientsTable to the clientManagementModel;
|
||||||
|
|
||||||
|
|client info page|
|
||||||
|
:on update client info page;
|
||||||
|
floating note
|
||||||
|
clicked on one of the clients
|
||||||
|
on the client management page
|
||||||
|
end note
|
||||||
|
|
||||||
|
:get the certificate data for the selected client;
|
||||||
|
if (if client name has been changed) then (yes)
|
||||||
|
:update clientManagementModel;
|
||||||
|
:get clientsTable from clientManagementModel;
|
||||||
|
:save the clientsTable file on the server;
|
||||||
|
else (no)
|
||||||
|
if (if revoke key was clicked) then (yes)
|
||||||
|
:update clientManagementModel;
|
||||||
|
:get clientsTable from clientManagementModel;
|
||||||
|
|
||||||
|
:delete section with public key from wireguard config;
|
||||||
|
:save wireguard config on the server;
|
||||||
|
:restart wireguard service;
|
||||||
|
|
||||||
|
:save the clientsTable file on the server;
|
||||||
|
|client management page|
|
||||||
|
:return to page client management;
|
||||||
|
stop
|
||||||
|
else (no)
|
||||||
|
|client info page|
|
||||||
|
:just look at the beautiful page;
|
||||||
|
stop
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
stop
|
||||||
|
@enduml
|
Loading…
Add table
Add a link
Reference in a new issue