mirror of
https://github.com/tnodir/fort
synced 2024-11-15 05:57:57 +00:00
UI: Programs: Add "Kill Process" option
This commit is contained in:
parent
194bfb825d
commit
efebf6b2c0
@ -158,7 +158,7 @@ typedef struct fort_app_flags
|
||||
struct
|
||||
{
|
||||
UINT16 group_index : 5;
|
||||
/* UINT16 reserved : 2; */
|
||||
/* UINT16 reserved : 1; */
|
||||
UINT16 use_group_perm : 1;
|
||||
|
||||
UINT16 apply_child : 1;
|
||||
@ -167,6 +167,7 @@ typedef struct fort_app_flags
|
||||
UINT16 log_conn : 1;
|
||||
|
||||
UINT16 blocked : 1;
|
||||
UINT16 kill_process : 1;
|
||||
UINT16 alerted : 1;
|
||||
UINT16 is_new : 1;
|
||||
UINT16 found : 1;
|
||||
|
@ -4,5 +4,6 @@ bool App::isEqual(const App &o) const
|
||||
{
|
||||
return useGroupPerm == o.useGroupPerm && applyChild == o.applyChild && lanOnly == o.lanOnly
|
||||
&& logBlocked == o.logBlocked && logConn == o.logConn && blocked == o.blocked
|
||||
&& groupIndex == o.groupIndex && appPath == o.appPath && endTime == o.endTime;
|
||||
&& killProcess == o.killProcess && groupIndex == o.groupIndex && appPath == o.appPath
|
||||
&& endTime == o.endTime;
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ public:
|
||||
bool logBlocked = true;
|
||||
bool logConn = true;
|
||||
bool blocked = false;
|
||||
bool killProcess = false;
|
||||
bool alerted = false;
|
||||
|
||||
int groupIndex = 0;
|
||||
|
@ -36,7 +36,7 @@ namespace {
|
||||
|
||||
const QLoggingCategory LC("conf");
|
||||
|
||||
constexpr int DATABASE_USER_VERSION = 18;
|
||||
constexpr int DATABASE_USER_VERSION = 19;
|
||||
|
||||
constexpr int APP_END_TIMER_INTERVAL_MIN = 100;
|
||||
constexpr int APP_END_TIMER_INTERVAL_MAX = 24 * 60 * 60 * 1000; // 1 day
|
||||
@ -122,6 +122,7 @@ const char *const sqlSelectAppById = "SELECT"
|
||||
" t.log_blocked,"
|
||||
" t.log_conn,"
|
||||
" t.blocked,"
|
||||
" t.kill_process,"
|
||||
" (alert.app_id IS NOT NULL) as alerted"
|
||||
" FROM app t"
|
||||
" JOIN app_group g ON g.app_group_id = t.app_group_id"
|
||||
@ -137,6 +138,7 @@ const char *const sqlSelectApps = "SELECT"
|
||||
" t.log_blocked,"
|
||||
" t.log_conn,"
|
||||
" t.blocked,"
|
||||
" t.kill_process,"
|
||||
" (alert.app_id IS NOT NULL) as alerted"
|
||||
" FROM app t"
|
||||
" JOIN app_group g ON g.app_group_id = t.app_group_id"
|
||||
@ -156,14 +158,15 @@ const char *const sqlSelectAppIdByPath = "SELECT app_id FROM app WHERE path = ?1
|
||||
|
||||
const char *const sqlUpsertApp = "INSERT INTO app(app_group_id, path, name,"
|
||||
" use_group_perm, apply_child, lan_only,"
|
||||
" log_blocked, log_conn, blocked,"
|
||||
" log_blocked, log_conn, blocked, kill_process,"
|
||||
" creat_time, end_time)"
|
||||
" VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)"
|
||||
" VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)"
|
||||
" ON CONFLICT(path) DO UPDATE"
|
||||
" SET app_group_id = ?1, name = ?3,"
|
||||
" use_group_perm = ?4, apply_child = ?5,"
|
||||
" lan_only = ?6, log_blocked = ?7, log_conn = ?8,"
|
||||
" blocked = ?9, creat_time = ?10, end_time = ?11"
|
||||
" blocked = ?9, kill_process = ?10,"
|
||||
" creat_time = ?10, end_time = ?11"
|
||||
" RETURNING app_id;";
|
||||
|
||||
const char *const sqlInsertAppAlert = "INSERT INTO app_alert(app_id) VALUES(?1);";
|
||||
@ -176,12 +179,13 @@ const char *const sqlUpdateApp = "UPDATE app"
|
||||
" SET app_group_id = ?2, name = ?3, use_group_perm = ?4,"
|
||||
" apply_child = ?5, lan_only = ?6,"
|
||||
" log_blocked = ?7, log_conn = ?8,"
|
||||
" blocked = ?9, end_time = ?10"
|
||||
" blocked = ?9, kill_process = ?10, end_time = ?11"
|
||||
" WHERE app_id = ?1;";
|
||||
|
||||
const char *const sqlUpdateAppName = "UPDATE app SET name = ?2 WHERE app_id = ?1;";
|
||||
|
||||
const char *const sqlUpdateAppBlocked = "UPDATE app SET blocked = ?2, end_time = NULL"
|
||||
const char *const sqlUpdateAppBlocked = "UPDATE app SET blocked = ?2, kill_process = ?3,"
|
||||
" end_time = NULL"
|
||||
" WHERE app_id = ?1;";
|
||||
|
||||
const char *const sqlUpdateAppResetGroup = "UPDATE app"
|
||||
@ -847,7 +851,7 @@ bool ConfManager::updateApp(const App &app)
|
||||
|
||||
const auto vars = QVariantList()
|
||||
<< app.appId << appGroup->id() << app.appName << app.useGroupPerm << app.applyChild
|
||||
<< app.lanOnly << app.logBlocked << app.logConn << app.blocked
|
||||
<< app.lanOnly << app.logBlocked << app.logConn << app.blocked << app.killProcess
|
||||
<< (!app.endTime.isNull() ? app.endTime : QVariant());
|
||||
|
||||
sqliteDb()->executeEx(sqlUpdateApp, vars, 0, &ok);
|
||||
@ -868,17 +872,17 @@ bool ConfManager::updateApp(const App &app)
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool ConfManager::updateAppBlocked(qint64 appId, bool blocked)
|
||||
bool ConfManager::updateAppBlocked(qint64 appId, bool blocked, bool killProcess)
|
||||
{
|
||||
bool changed = false;
|
||||
if (!updateDriverAppBlocked(appId, blocked, changed))
|
||||
if (!updateDriverAppBlocked(appId, blocked, killProcess, changed))
|
||||
return false;
|
||||
|
||||
bool ok = true;
|
||||
|
||||
sqliteDb()->beginTransaction();
|
||||
|
||||
const auto vars = QVariantList() << appId << blocked;
|
||||
const auto vars = QVariantList() << appId << blocked << killProcess;
|
||||
|
||||
if (changed) {
|
||||
sqliteDb()->executeEx(sqlUpdateAppBlocked, vars, 0, &ok);
|
||||
@ -929,7 +933,8 @@ bool ConfManager::walkApps(const std::function<walkAppsCallback> &func)
|
||||
app.logBlocked = stmt.columnBool(5);
|
||||
app.logConn = stmt.columnBool(6);
|
||||
app.blocked = stmt.columnBool(7);
|
||||
app.alerted = stmt.columnBool(8);
|
||||
app.killProcess = stmt.columnBool(8);
|
||||
app.alerted = stmt.columnBool(9);
|
||||
|
||||
if (!func(app))
|
||||
return false;
|
||||
@ -958,6 +963,7 @@ void ConfManager::updateAppEndTimes()
|
||||
app.logBlocked = stmt.columnBool(7);
|
||||
app.logConn = stmt.columnBool(8);
|
||||
app.blocked = true;
|
||||
app.killProcess = false;
|
||||
|
||||
updateApp(app);
|
||||
}
|
||||
@ -1188,7 +1194,7 @@ bool ConfManager::addOrUpdateApp(const App &app)
|
||||
|
||||
const auto vars = QVariantList()
|
||||
<< appGroup->id() << app.appPath << app.appName << app.useGroupPerm << app.applyChild
|
||||
<< app.lanOnly << app.logBlocked << app.logConn << app.blocked
|
||||
<< app.lanOnly << app.logBlocked << app.logConn << app.blocked << app.killProcess
|
||||
<< QDateTime::currentDateTime() << (!app.endTime.isNull() ? app.endTime : QVariant());
|
||||
|
||||
const auto appIdVar = sqliteDb()->executeEx(sqlUpsertApp, vars, 1, &ok);
|
||||
@ -1212,7 +1218,8 @@ bool ConfManager::addOrUpdateApp(const App &app)
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool ConfManager::updateDriverAppBlocked(qint64 appId, bool blocked, bool &changed)
|
||||
bool ConfManager::updateDriverAppBlocked(
|
||||
qint64 appId, bool blocked, bool killProcess, bool &changed)
|
||||
{
|
||||
SqliteStmt stmt;
|
||||
if (!sqliteDb()->prepare(stmt, sqlSelectAppById))
|
||||
@ -1231,10 +1238,12 @@ bool ConfManager::updateDriverAppBlocked(qint64 appId, bool blocked, bool &chang
|
||||
app.logBlocked = stmt.columnBool(5);
|
||||
app.logConn = stmt.columnBool(6);
|
||||
app.blocked = stmt.columnBool(7);
|
||||
const bool wasAlerted = stmt.columnBool(8);
|
||||
app.killProcess = stmt.columnBool(8);
|
||||
const bool wasAlerted = stmt.columnBool(9);
|
||||
|
||||
if (blocked != app.blocked || wasAlerted) {
|
||||
if (blocked != app.blocked || killProcess != app.killProcess || wasAlerted) {
|
||||
app.blocked = blocked;
|
||||
app.killProcess = killProcess;
|
||||
|
||||
if (!updateDriverUpdateApp(app))
|
||||
return false;
|
||||
|
@ -67,7 +67,7 @@ public:
|
||||
virtual bool deleteApp(qint64 appId);
|
||||
virtual bool purgeApps();
|
||||
virtual bool updateApp(const App &app);
|
||||
virtual bool updateAppBlocked(qint64 appId, bool blocked);
|
||||
virtual bool updateAppBlocked(qint64 appId, bool blocked, bool killProcess = false);
|
||||
virtual bool updateAppName(qint64 appId, const QString &appName);
|
||||
|
||||
bool walkApps(const std::function<walkAppsCallback> &func) override;
|
||||
@ -118,7 +118,7 @@ private:
|
||||
void emitAppUpdated();
|
||||
|
||||
bool addOrUpdateApp(const App &app);
|
||||
bool updateDriverAppBlocked(qint64 appId, bool blocked, bool &changed);
|
||||
bool updateDriverAppBlocked(qint64 appId, bool blocked, bool killProcess, bool &changed);
|
||||
|
||||
bool validateConf(const FirewallConf &newConf);
|
||||
|
||||
|
@ -130,6 +130,7 @@ CREATE TABLE app(
|
||||
log_blocked BOOLEAN NOT NULL DEFAULT 1,
|
||||
log_conn BOOLEAN NOT NULL DEFAULT 1,
|
||||
blocked BOOLEAN NOT NULL,
|
||||
kill_process BOOLEAN NOT NULL DEFAULT 0,
|
||||
creat_time INTEGER NOT NULL,
|
||||
end_time INTEGER,
|
||||
policy_id INTEGER
|
||||
|
@ -83,6 +83,7 @@ void ProgramEditDialog::initialize(const AppRow &appRow, const QVector<qint64> &
|
||||
m_cbLogConn->setChecked(appRow.logConn);
|
||||
m_rbAllowApp->setChecked(!appRow.blocked);
|
||||
m_rbBlockApp->setChecked(appRow.blocked);
|
||||
m_rbKillProcess->setChecked(appRow.killProcess);
|
||||
m_cscBlockAppIn->checkBox()->setChecked(false);
|
||||
m_cscBlockAppIn->spinBox()->setValue(1);
|
||||
m_cbBlockAppAt->setChecked(!appRow.endTime.isNull());
|
||||
@ -129,6 +130,7 @@ void ProgramEditDialog::retranslateUi()
|
||||
|
||||
m_rbAllowApp->setText(tr("Allow"));
|
||||
m_rbBlockApp->setText(tr("Block"));
|
||||
m_rbKillProcess->setText(tr("Kill Process"));
|
||||
|
||||
m_cscBlockAppIn->checkBox()->setText(tr("Block In:"));
|
||||
retranslateAppBlockInHours();
|
||||
@ -347,8 +349,12 @@ QLayout *ProgramEditDialog::setupAllowLayout()
|
||||
m_rbBlockApp = new QRadioButton();
|
||||
m_rbBlockApp->setIcon(IconCache::icon(":/icons/deny.png"));
|
||||
|
||||
m_rbKillProcess = new QRadioButton();
|
||||
m_rbKillProcess->setIcon(IconCache::icon(":/icons/cancel.png"));
|
||||
|
||||
allowLayout->addWidget(m_rbAllowApp, 1, Qt::AlignRight);
|
||||
allowLayout->addWidget(m_rbBlockApp, 1, Qt::AlignLeft);
|
||||
allowLayout->addWidget(m_rbBlockApp, 1, Qt::AlignHCenter);
|
||||
allowLayout->addWidget(m_rbKillProcess, 1, Qt::AlignLeft);
|
||||
|
||||
// Block after N hours
|
||||
m_cscBlockAppIn = new CheckSpinCombo();
|
||||
@ -466,7 +472,8 @@ void ProgramEditDialog::fillApp(App &app) const
|
||||
app.lanOnly = m_cbLanOnly->isChecked();
|
||||
app.logBlocked = m_cbLogBlocked->isChecked();
|
||||
app.logConn = m_cbLogConn->isChecked();
|
||||
app.blocked = m_rbBlockApp->isChecked();
|
||||
app.blocked = !m_rbAllowApp->isChecked();
|
||||
app.killProcess = m_rbKillProcess->isChecked();
|
||||
app.groupIndex = m_comboAppGroup->currentIndex();
|
||||
app.appPath = m_editPath->text();
|
||||
app.appName = m_editName->text();
|
||||
|
@ -78,6 +78,7 @@ private:
|
||||
QCheckBox *m_cbLogConn = nullptr;
|
||||
QRadioButton *m_rbAllowApp = nullptr;
|
||||
QRadioButton *m_rbBlockApp = nullptr;
|
||||
QRadioButton *m_rbKillProcess = nullptr;
|
||||
CheckSpinCombo *m_cscBlockAppIn = nullptr;
|
||||
QCheckBox *m_cbBlockAppAt = nullptr;
|
||||
QDateTimeEdit *m_dteBlockAppAt = nullptr;
|
||||
|
@ -22,8 +22,29 @@ namespace {
|
||||
const auto alertColor = QColor("orange");
|
||||
const auto allowColor = QColor("green");
|
||||
const auto blockColor = QColor("red");
|
||||
const auto killProcessColor = QColor("magenta");
|
||||
const auto inactiveColor = QColor("slategray");
|
||||
|
||||
QString appStateIconPath(const AppRow &appRow)
|
||||
{
|
||||
if (appRow.alerted)
|
||||
return ":/icons/error.png";
|
||||
|
||||
if (appRow.killProcess)
|
||||
return ":/icons/cancel.png";
|
||||
|
||||
if (appRow.blocked)
|
||||
return ":/icons/deny.png";
|
||||
|
||||
if (!appRow.endTime.isNull())
|
||||
return ":/icons/time.png";
|
||||
|
||||
if (appRow.lanOnly)
|
||||
return ":/icons/hostname.png";
|
||||
|
||||
return ":/icons/accept.png";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AppListModel::AppListModel(QObject *parent) : TableSqlModel(parent) { }
|
||||
@ -231,6 +252,9 @@ QVariant AppListModel::appGroupColor(const AppRow &appRow) const
|
||||
|
||||
QString AppListModel::appStateText(const AppRow &appRow)
|
||||
{
|
||||
if (appRow.killProcess)
|
||||
return tr("Kill Process");
|
||||
|
||||
if (appRow.blocked)
|
||||
return tr("Block");
|
||||
|
||||
@ -239,6 +263,9 @@ QString AppListModel::appStateText(const AppRow &appRow)
|
||||
|
||||
QColor AppListModel::appStateColor(const AppRow &appRow)
|
||||
{
|
||||
if (appRow.killProcess)
|
||||
return killProcessColor;
|
||||
|
||||
if (appRow.blocked)
|
||||
return blockColor;
|
||||
|
||||
@ -247,13 +274,7 @@ QColor AppListModel::appStateColor(const AppRow &appRow)
|
||||
|
||||
QIcon AppListModel::appStateIcon(const AppRow &appRow)
|
||||
{
|
||||
return IconCache::icon(appRow.alerted
|
||||
? ":/icons/error.png"
|
||||
: (appRow.blocked ? ":/icons/deny.png"
|
||||
: (appRow.endTime.isNull()
|
||||
? (appRow.lanOnly ? ":/icons/hostname.png"
|
||||
: ":/icons/accept.png")
|
||||
: ":/icons/time.png")));
|
||||
return IconCache::icon(appStateIconPath(appRow));
|
||||
}
|
||||
|
||||
bool AppListModel::updateAppRow(const QString &sql, const QVariantList &vars, AppRow &appRow) const
|
||||
@ -274,9 +295,10 @@ bool AppListModel::updateAppRow(const QString &sql, const QVariantList &vars, Ap
|
||||
appRow.logBlocked = stmt.columnBool(7);
|
||||
appRow.logConn = stmt.columnBool(8);
|
||||
appRow.blocked = stmt.columnBool(9);
|
||||
appRow.alerted = stmt.columnBool(10);
|
||||
appRow.endTime = stmt.columnDateTime(11);
|
||||
appRow.creatTime = stmt.columnDateTime(12);
|
||||
appRow.killProcess = stmt.columnBool(10);
|
||||
appRow.alerted = stmt.columnBool(11);
|
||||
appRow.endTime = stmt.columnDateTime(12);
|
||||
appRow.creatTime = stmt.columnDateTime(13);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -322,6 +344,7 @@ QString AppListModel::sqlBase() const
|
||||
" t.log_blocked,"
|
||||
" t.log_conn,"
|
||||
" t.blocked,"
|
||||
" t.kill_process,"
|
||||
" (alert.app_id IS NOT NULL) as alerted,"
|
||||
" t.end_time,"
|
||||
" t.creat_time"
|
||||
@ -338,7 +361,7 @@ QString AppListModel::sqlOrderColumn() const
|
||||
columnsStr = "4 " + sqlOrderAsc() + ", 3";
|
||||
break;
|
||||
case 1: // State
|
||||
columnsStr = "11 DESC, 10 " + sqlOrderAsc() + ", 1";
|
||||
columnsStr = "12 DESC, 11, 10 " + sqlOrderAsc() + ", 1";
|
||||
break;
|
||||
case 2: // Group
|
||||
columnsStr = "2";
|
||||
|
@ -19,7 +19,8 @@ bool ConfManagerRpc::addApp(const App &app)
|
||||
{
|
||||
return IoC<RpcManager>()->doOnServer(Control::Rpc_ConfManager_addApp,
|
||||
{ app.useGroupPerm, app.applyChild, app.lanOnly, app.logBlocked, app.logConn,
|
||||
app.blocked, app.groupIndex, app.appPath, app.appName, app.endTime });
|
||||
app.blocked, app.killProcess, app.groupIndex, app.appPath, app.appName,
|
||||
app.endTime });
|
||||
}
|
||||
|
||||
bool ConfManagerRpc::deleteApp(qint64 appId)
|
||||
@ -36,14 +37,14 @@ bool ConfManagerRpc::updateApp(const App &app)
|
||||
{
|
||||
return IoC<RpcManager>()->doOnServer(Control::Rpc_ConfManager_updateApp,
|
||||
{ app.useGroupPerm, app.applyChild, app.lanOnly, app.logBlocked, app.logConn,
|
||||
app.blocked, app.groupIndex, app.appId, app.appPath, app.appName,
|
||||
app.endTime });
|
||||
app.blocked, app.killProcess, app.groupIndex, app.appId, app.appPath,
|
||||
app.appName, app.endTime });
|
||||
}
|
||||
|
||||
bool ConfManagerRpc::updateAppBlocked(qint64 appId, bool blocked)
|
||||
bool ConfManagerRpc::updateAppBlocked(qint64 appId, bool blocked, bool killProcess)
|
||||
{
|
||||
return IoC<RpcManager>()->doOnServer(
|
||||
Control::Rpc_ConfManager_updateAppBlocked, { appId, blocked });
|
||||
Control::Rpc_ConfManager_updateAppBlocked, { appId, blocked, killProcess });
|
||||
}
|
||||
|
||||
bool ConfManagerRpc::updateAppName(qint64 appId, const QString &appName)
|
||||
|
@ -17,7 +17,7 @@ public:
|
||||
bool deleteApp(qint64 appId) override;
|
||||
bool purgeApps() override;
|
||||
bool updateApp(const App &app) override;
|
||||
bool updateAppBlocked(qint64 appId, bool blocked) override;
|
||||
bool updateAppBlocked(qint64 appId, bool blocked, bool killProcess = false) override;
|
||||
bool updateAppName(qint64 appId, const QString &appName) override;
|
||||
|
||||
bool addZone(Zone &zone) override;
|
||||
|
@ -72,10 +72,11 @@ bool processConfManager_addApp(
|
||||
app.logBlocked = p.args.value(3).toBool();
|
||||
app.logConn = p.args.value(4).toBool();
|
||||
app.blocked = p.args.value(5).toBool();
|
||||
app.groupIndex = p.args.value(6).toInt();
|
||||
app.appPath = p.args.value(7).toString();
|
||||
app.appName = p.args.value(8).toString();
|
||||
app.endTime = p.args.value(9).toDateTime();
|
||||
app.killProcess = p.args.value(6).toBool();
|
||||
app.groupIndex = p.args.value(7).toInt();
|
||||
app.appPath = p.args.value(8).toString();
|
||||
app.appName = p.args.value(9).toString();
|
||||
app.endTime = p.args.value(10).toDateTime();
|
||||
|
||||
return confManager->addApp(app);
|
||||
}
|
||||
@ -102,11 +103,12 @@ bool processConfManager_updateApp(
|
||||
app.logBlocked = p.args.value(3).toBool();
|
||||
app.logConn = p.args.value(4).toBool();
|
||||
app.blocked = p.args.value(5).toBool();
|
||||
app.groupIndex = p.args.value(6).toInt();
|
||||
app.appId = p.args.value(7).toLongLong();
|
||||
app.appPath = p.args.value(8).toString();
|
||||
app.appName = p.args.value(9).toString();
|
||||
app.endTime = p.args.value(10).toDateTime();
|
||||
app.killProcess = p.args.value(6).toBool();
|
||||
app.groupIndex = p.args.value(7).toInt();
|
||||
app.appId = p.args.value(8).toLongLong();
|
||||
app.appPath = p.args.value(9).toString();
|
||||
app.appName = p.args.value(10).toString();
|
||||
app.endTime = p.args.value(11).toDateTime();
|
||||
|
||||
return confManager->updateApp(app);
|
||||
}
|
||||
@ -114,7 +116,8 @@ bool processConfManager_updateApp(
|
||||
bool processConfManager_updateAppBlocked(
|
||||
ConfManager *confManager, const ProcessCommandArgs &p, QVariantList & /*resArgs*/)
|
||||
{
|
||||
return confManager->updateAppBlocked(p.args.value(0).toLongLong(), p.args.value(1).toBool());
|
||||
return confManager->updateAppBlocked(
|
||||
p.args.value(0).toLongLong(), p.args.value(1).toBool(), p.args.value(2).toBool());
|
||||
}
|
||||
|
||||
bool processConfManager_updateAppName(
|
||||
|
@ -474,6 +474,7 @@ bool ConfUtil::addApp(
|
||||
appEntry.flags.log_blocked = app.logBlocked;
|
||||
appEntry.flags.log_conn = app.logConn;
|
||||
appEntry.flags.blocked = app.blocked;
|
||||
appEntry.flags.kill_process = app.killProcess;
|
||||
appEntry.flags.alerted = app.alerted;
|
||||
appEntry.flags.is_new = isNew;
|
||||
appEntry.flags.found = 1;
|
||||
|
Loading…
Reference in New Issue
Block a user