Delete-FTP-User Button und Fix für sudoers-Pfad
- Plugin: Neuer "Delete FTP User" Button im FTP-User-Bereich - Mit Bestätigungsdialog - Löscht nur FTP-Account + Bind-Mounts, System-User bleibt unberührt - Helper-Script: neues 'deluser' Kommando - sudoers-Fix: tee für /etc/pure-ftpd/* (statt nur conf/*) — cmd_passwd muss nach /etc/pure-ftpd/pureftpd.passwd schreiben können, das war vorher nicht erlaubt und führte zu 'Failed to create FTP user' beim Anlegen weiterer User Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
76beda113e
commit
ee4bf75547
|
|
@ -141,17 +141,20 @@ FtpSharePlugin::FtpSharePlugin(QObject *parent, const QVariantList &args)
|
||||||
usersLayout->addWidget(scrollArea);
|
usersLayout->addWidget(scrollArea);
|
||||||
mainLayout->addWidget(usersGroup);
|
mainLayout->addWidget(usersGroup);
|
||||||
|
|
||||||
// Change password section
|
// FTP user management section
|
||||||
auto *pwGroup = new QGroupBox(i18n("FTP Password"));
|
auto *pwGroup = new QGroupBox(i18n("FTP User"));
|
||||||
auto *pwLayout = new QHBoxLayout(pwGroup);
|
auto *pwLayout = new QHBoxLayout(pwGroup);
|
||||||
m_pwUserCombo = new QComboBox();
|
m_pwUserCombo = new QComboBox();
|
||||||
m_pwUserCombo->addItems(users);
|
m_pwUserCombo->addItems(users);
|
||||||
auto *pwButton = new QPushButton(i18n("Change Password..."));
|
auto *pwButton = new QPushButton(i18n("Change Password..."));
|
||||||
|
auto *deleteButton = new QPushButton(i18n("Delete FTP User"));
|
||||||
pwLayout->addWidget(m_pwUserCombo);
|
pwLayout->addWidget(m_pwUserCombo);
|
||||||
pwLayout->addWidget(pwButton);
|
pwLayout->addWidget(pwButton);
|
||||||
|
pwLayout->addWidget(deleteButton);
|
||||||
mainLayout->addWidget(pwGroup);
|
mainLayout->addWidget(pwGroup);
|
||||||
|
|
||||||
connect(pwButton, &QPushButton::clicked, this, &FtpSharePlugin::onChangePassword);
|
connect(pwButton, &QPushButton::clicked, this, &FtpSharePlugin::onChangePassword);
|
||||||
|
connect(deleteButton, &QPushButton::clicked, this, &FtpSharePlugin::onDeleteFtpUser);
|
||||||
|
|
||||||
// Server configuration section
|
// Server configuration section
|
||||||
auto *serverGroup = new QGroupBox(i18n("Server Configuration"));
|
auto *serverGroup = new QGroupBox(i18n("Server Configuration"));
|
||||||
|
|
@ -483,4 +486,44 @@ void FtpSharePlugin::onChangePassword()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FtpSharePlugin::onDeleteFtpUser()
|
||||||
|
{
|
||||||
|
const QString username = m_pwUserCombo->currentText();
|
||||||
|
if (username.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!ftpUserExists(username)) {
|
||||||
|
QMessageBox::information(m_page,
|
||||||
|
i18n("Delete FTP User"),
|
||||||
|
i18n("User '%1' has no FTP account.", username));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = QMessageBox::question(m_page,
|
||||||
|
i18n("Delete FTP User"),
|
||||||
|
i18n("Really delete FTP account for '%1'?\n\n"
|
||||||
|
"The system user stays untouched. Only the FTP login and\n"
|
||||||
|
"this user's bind-mounts in the FTP root will be removed.", username),
|
||||||
|
QMessageBox::Yes | QMessageBox::No,
|
||||||
|
QMessageBox::No);
|
||||||
|
|
||||||
|
if (result != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QProcess proc;
|
||||||
|
proc.start(QStringLiteral("dolphin-ftp-share"),
|
||||||
|
{QStringLiteral("deluser"), username});
|
||||||
|
proc.waitForFinished(10000);
|
||||||
|
|
||||||
|
if (proc.exitCode() == 0) {
|
||||||
|
QMessageBox::information(m_page,
|
||||||
|
i18n("Delete FTP User"),
|
||||||
|
i18n("FTP user '%1' deleted.", username));
|
||||||
|
} else {
|
||||||
|
QMessageBox::warning(m_page,
|
||||||
|
i18n("Delete FTP User"),
|
||||||
|
i18n("Failed to delete FTP user '%1'.", username));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#include "ftpshareplugin.moc"
|
#include "ftpshareplugin.moc"
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ private:
|
||||||
void loadServerConfig();
|
void loadServerConfig();
|
||||||
void onShareToggled(bool checked);
|
void onShareToggled(bool checked);
|
||||||
void onChangePassword();
|
void onChangePassword();
|
||||||
|
void onDeleteFtpUser();
|
||||||
void onApplyServerConfig();
|
void onApplyServerConfig();
|
||||||
void onModeChanged();
|
void onModeChanged();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -141,17 +141,20 @@ FtpSharePlugin::FtpSharePlugin(QObject *parent, const QVariantList &args)
|
||||||
usersLayout->addWidget(scrollArea);
|
usersLayout->addWidget(scrollArea);
|
||||||
mainLayout->addWidget(usersGroup);
|
mainLayout->addWidget(usersGroup);
|
||||||
|
|
||||||
// Change password section
|
// FTP user management section
|
||||||
auto *pwGroup = new QGroupBox(i18n("FTP Password"));
|
auto *pwGroup = new QGroupBox(i18n("FTP User"));
|
||||||
auto *pwLayout = new QHBoxLayout(pwGroup);
|
auto *pwLayout = new QHBoxLayout(pwGroup);
|
||||||
m_pwUserCombo = new QComboBox();
|
m_pwUserCombo = new QComboBox();
|
||||||
m_pwUserCombo->addItems(users);
|
m_pwUserCombo->addItems(users);
|
||||||
auto *pwButton = new QPushButton(i18n("Change Password..."));
|
auto *pwButton = new QPushButton(i18n("Change Password..."));
|
||||||
|
auto *deleteButton = new QPushButton(i18n("Delete FTP User"));
|
||||||
pwLayout->addWidget(m_pwUserCombo);
|
pwLayout->addWidget(m_pwUserCombo);
|
||||||
pwLayout->addWidget(pwButton);
|
pwLayout->addWidget(pwButton);
|
||||||
|
pwLayout->addWidget(deleteButton);
|
||||||
mainLayout->addWidget(pwGroup);
|
mainLayout->addWidget(pwGroup);
|
||||||
|
|
||||||
connect(pwButton, &QPushButton::clicked, this, &FtpSharePlugin::onChangePassword);
|
connect(pwButton, &QPushButton::clicked, this, &FtpSharePlugin::onChangePassword);
|
||||||
|
connect(deleteButton, &QPushButton::clicked, this, &FtpSharePlugin::onDeleteFtpUser);
|
||||||
|
|
||||||
// Server configuration section
|
// Server configuration section
|
||||||
auto *serverGroup = new QGroupBox(i18n("Server Configuration"));
|
auto *serverGroup = new QGroupBox(i18n("Server Configuration"));
|
||||||
|
|
@ -483,4 +486,44 @@ void FtpSharePlugin::onChangePassword()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FtpSharePlugin::onDeleteFtpUser()
|
||||||
|
{
|
||||||
|
const QString username = m_pwUserCombo->currentText();
|
||||||
|
if (username.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!ftpUserExists(username)) {
|
||||||
|
QMessageBox::information(m_page,
|
||||||
|
i18n("Delete FTP User"),
|
||||||
|
i18n("User '%1' has no FTP account.", username));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = QMessageBox::question(m_page,
|
||||||
|
i18n("Delete FTP User"),
|
||||||
|
i18n("Really delete FTP account for '%1'?\n\n"
|
||||||
|
"The system user stays untouched. Only the FTP login and\n"
|
||||||
|
"this user's bind-mounts in the FTP root will be removed.", username),
|
||||||
|
QMessageBox::Yes | QMessageBox::No,
|
||||||
|
QMessageBox::No);
|
||||||
|
|
||||||
|
if (result != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QProcess proc;
|
||||||
|
proc.start(QStringLiteral("dolphin-ftp-share"),
|
||||||
|
{QStringLiteral("deluser"), username});
|
||||||
|
proc.waitForFinished(10000);
|
||||||
|
|
||||||
|
if (proc.exitCode() == 0) {
|
||||||
|
QMessageBox::information(m_page,
|
||||||
|
i18n("Delete FTP User"),
|
||||||
|
i18n("FTP user '%1' deleted.", username));
|
||||||
|
} else {
|
||||||
|
QMessageBox::warning(m_page,
|
||||||
|
i18n("Delete FTP User"),
|
||||||
|
i18n("Failed to delete FTP user '%1'.", username));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#include "ftpshareplugin.moc"
|
#include "ftpshareplugin.moc"
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ private:
|
||||||
void loadServerConfig();
|
void loadServerConfig();
|
||||||
void onShareToggled(bool checked);
|
void onShareToggled(bool checked);
|
||||||
void onChangePassword();
|
void onChangePassword();
|
||||||
|
void onDeleteFtpUser();
|
||||||
void onApplyServerConfig();
|
void onApplyServerConfig();
|
||||||
void onModeChanged();
|
void onModeChanged();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,8 @@ ${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/mount --bind *
|
||||||
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/mount -o remount?ro?bind *
|
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/mount -o remount?ro?bind *
|
||||||
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/umount ${REAL_FTP_ROOT}/*
|
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/umount ${REAL_FTP_ROOT}/*
|
||||||
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/pure-pw *
|
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/pure-pw *
|
||||||
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/tee /etc/pure-ftpd/conf/*
|
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/tee /etc/pure-ftpd/*
|
||||||
|
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/tee -a /etc/pure-ftpd/*
|
||||||
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/rm -f /etc/pure-ftpd/conf/PassivePortRange
|
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/rm -f /etc/pure-ftpd/conf/PassivePortRange
|
||||||
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/sed -i *pureftpd.passwd*
|
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/sed -i *pureftpd.passwd*
|
||||||
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/systemctl restart pure-ftpd
|
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/systemctl restart pure-ftpd
|
||||||
|
|
@ -229,6 +230,38 @@ cmd_passwd() {
|
||||||
rebuild_db
|
rebuild_db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd_deluser() {
|
||||||
|
local username="$1"
|
||||||
|
|
||||||
|
if [ -z "$username" ]; then
|
||||||
|
echo "Error: username is required" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Unmount all shares from this user's root first
|
||||||
|
local user_root="$FTP_ROOT/users/$username"
|
||||||
|
if [ -d "$user_root" ]; then
|
||||||
|
for mp in "$user_root"/*/; do
|
||||||
|
[ -d "$mp" ] || continue
|
||||||
|
if mountpoint -q "$mp" 2>/dev/null; then
|
||||||
|
sudo umount "$mp"
|
||||||
|
fi
|
||||||
|
rmdir "$mp" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
rmdir "$user_root" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove user entry from passwd file
|
||||||
|
if sudo pure-pw show "$username" -f "$PASSWD_FILE" >/dev/null 2>&1; then
|
||||||
|
sudo sed -i "/^${username}:/d" "$PASSWD_FILE"
|
||||||
|
rebuild_db
|
||||||
|
echo "FTP user '$username' deleted"
|
||||||
|
else
|
||||||
|
echo "FTP user '$username' does not exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
cmd_serverconfig() {
|
cmd_serverconfig() {
|
||||||
local action="$1"
|
local action="$1"
|
||||||
|
|
||||||
|
|
@ -276,9 +309,10 @@ case "${1:-}" in
|
||||||
delete) cmd_delete "$2" ;;
|
delete) cmd_delete "$2" ;;
|
||||||
userexists) cmd_userexists "$2" ;;
|
userexists) cmd_userexists "$2" ;;
|
||||||
passwd) cmd_passwd "$2" "$3" ;;
|
passwd) cmd_passwd "$2" "$3" ;;
|
||||||
|
deluser) cmd_deluser "$2" ;;
|
||||||
serverconfig) cmd_serverconfig "$2" "$3" "$4" ;;
|
serverconfig) cmd_serverconfig "$2" "$3" "$4" ;;
|
||||||
*)
|
*)
|
||||||
echo "Usage: dolphin-ftp-share {setup|info|add|delete|userexists|passwd|serverconfig}" >&2
|
echo "Usage: dolphin-ftp-share {setup|info|add|delete|userexists|passwd|deluser|serverconfig}" >&2
|
||||||
echo "" >&2
|
echo "" >&2
|
||||||
echo "Commands:" >&2
|
echo "Commands:" >&2
|
||||||
echo " setup Initial Pure-FTPd setup" >&2
|
echo " setup Initial Pure-FTPd setup" >&2
|
||||||
|
|
@ -287,6 +321,7 @@ case "${1:-}" in
|
||||||
echo " delete <name> Remove a share" >&2
|
echo " delete <name> Remove a share" >&2
|
||||||
echo " userexists <username> Check if FTP user exists" >&2
|
echo " userexists <username> Check if FTP user exists" >&2
|
||||||
echo " passwd <username> <password> Set/create FTP password" >&2
|
echo " passwd <username> <password> Set/create FTP password" >&2
|
||||||
|
echo " deluser <username> Delete FTP user (not system user)" >&2
|
||||||
echo " serverconfig show Show current server config" >&2
|
echo " serverconfig show Show current server config" >&2
|
||||||
echo " serverconfig active Set active mode" >&2
|
echo " serverconfig active Set active mode" >&2
|
||||||
echo " serverconfig passive <start> <end> Set passive mode with port range" >&2
|
echo " serverconfig passive <start> <end> Set passive mode with port range" >&2
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue