mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-10-01 02:35:48 -04:00
Made autologin optional at compile time
Autologin is disabled by default at compile time, and a warning to discourage it's usage is printed if it is enabled. This will make default RetroShare build safer and reduce dependencies as example we don't depends anymore on gnome keyring is not needed in default build for linux anymore.
This commit is contained in:
parent
6890669820
commit
8656452c68
@ -151,7 +151,7 @@ HEADERS += $$PUBLIC_HEADERS
|
||||
|
||||
################################# Linux ##########################################
|
||||
linux-* {
|
||||
CONFIG += link_pkgconfig
|
||||
CONFIG += link_pkgconfig
|
||||
|
||||
QMAKE_CXXFLAGS *= -Wall -D_FILE_OFFSET_BITS=64
|
||||
QMAKE_CC = $${QMAKE_CXX}
|
||||
@ -191,8 +191,6 @@ linux-* {
|
||||
DEFINES *= PATCHED_LIBUPNP
|
||||
}
|
||||
|
||||
DEFINES *= HAS_GNOME_KEYRING
|
||||
PKGCONFIG *= gnome-keyring-1
|
||||
PKGCONFIG *= libssl libupnp
|
||||
PKGCONFIG *= libcrypto zlib
|
||||
LIBS *= -lpthread -ldl
|
||||
|
@ -60,14 +60,16 @@ const int p3facemsgzone = 11453;
|
||||
/* RsIface Config */
|
||||
/* Config */
|
||||
|
||||
void RsServer::ConfigFinalSave()
|
||||
void RsServer::ConfigFinalSave()
|
||||
{
|
||||
/* force saving of transfers TODO */
|
||||
//TODO: force saving of transfers
|
||||
//ftserver->saveFileTransferStatus();
|
||||
if(!RsInit::getAutoLogin())
|
||||
RsInit::RsClearAutoLogin();
|
||||
|
||||
//AuthSSL::getAuthSSL()->FinalSaveCertificates();
|
||||
#ifdef RS_AUTOLOGIN
|
||||
if(!RsInit::getAutoLogin()) RsInit::RsClearAutoLogin();
|
||||
#endif // RS_AUTOLOGIN
|
||||
|
||||
//AuthSSL::getAuthSSL()->FinalSaveCertificates();
|
||||
mConfigMgr->completeConfiguration();
|
||||
}
|
||||
|
||||
|
@ -717,14 +717,15 @@ int RsInit::LoadCertificates(bool autoLoginNT)
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
#ifdef RS_AUTOLOGIN
|
||||
if(autoLoginNT)
|
||||
{
|
||||
std::cerr << "RetroShare will AutoLogin next time";
|
||||
std::cerr << std::endl;
|
||||
std::cerr << "RetroShare will AutoLogin next time" << std::endl;
|
||||
|
||||
RsLoginHandler::enableAutoLogin(preferredId,rsInitConfig->passwd);
|
||||
rsInitConfig->autoLogin = true ;
|
||||
}
|
||||
#endif // RS_AUTOLOGIN
|
||||
|
||||
/* wipe out password */
|
||||
|
||||
@ -733,10 +734,11 @@ int RsInit::LoadCertificates(bool autoLoginNT)
|
||||
rsInitConfig->gxs_passwd = rsInitConfig->passwd;
|
||||
rsInitConfig->passwd = "";
|
||||
|
||||
rsAccounts->storePreferredAccount();
|
||||
rsAccounts->storePreferredAccount();
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef RS_AUTOLOGIN
|
||||
bool RsInit::RsClearAutoLogin()
|
||||
{
|
||||
RsPeerId preferredId;
|
||||
@ -747,6 +749,7 @@ bool RsInit::RsClearAutoLogin()
|
||||
}
|
||||
return RsLoginHandler::clearAutoLogin(preferredId);
|
||||
}
|
||||
#endif // RS_AUTOLOGIN
|
||||
|
||||
|
||||
bool RsInit::isPortable()
|
||||
|
@ -4,19 +4,103 @@
|
||||
#include "rsloginhandler.h"
|
||||
#include "util/rsdir.h"
|
||||
#include "rsaccounts.h"
|
||||
|
||||
bool RsLoginHandler::getSSLPassword( const RsPeerId& ssl_id,
|
||||
bool enable_gpg_ask_passwd,
|
||||
std::string& ssl_passwd )
|
||||
{
|
||||
|
||||
#ifdef RS_AUTOLOGIN
|
||||
// First, see if autologin is available
|
||||
if(tryAutoLogin(ssl_id,ssl_passwd)) return true;
|
||||
#endif // RS_AUTOLOGIN
|
||||
|
||||
// If we're not expecting to enter a passwd (e.g. test for autologin before
|
||||
// display of the login window), safely respond false.
|
||||
if(!enable_gpg_ask_passwd) return false;
|
||||
|
||||
return getSSLPasswdFromGPGFile(ssl_id,ssl_passwd);
|
||||
}
|
||||
|
||||
bool RsLoginHandler::checkAndStoreSSLPasswdIntoGPGFile(
|
||||
const RsPeerId& ssl_id, const std::string& ssl_passwd )
|
||||
{
|
||||
// We want to pursue login with gpg passwd. Let's do it:
|
||||
FILE *sslPassphraseFile = RsDirUtil::rs_fopen(
|
||||
getSSLPasswdFileName(ssl_id).c_str(), "r");
|
||||
|
||||
if(sslPassphraseFile != NULL) // already have it.
|
||||
{
|
||||
fclose(sslPassphraseFile);
|
||||
return true ;
|
||||
}
|
||||
|
||||
bool ok = AuthGPG::getAuthGPG()->encryptTextToFile(
|
||||
ssl_passwd, getSSLPasswdFileName(ssl_id));
|
||||
|
||||
if (!ok) std::cerr << "Encrypting went wrong !" << std::endl;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool RsLoginHandler::getSSLPasswdFromGPGFile(const RsPeerId& ssl_id,std::string& sslPassword)
|
||||
{
|
||||
/* Let's read the password from an encrypted file, before check if there's
|
||||
* an ssl_passpharese_file that we can decrypt with PGP */
|
||||
FILE *sslPassphraseFile = RsDirUtil::rs_fopen(
|
||||
getSSLPasswdFileName(ssl_id).c_str(), "r");
|
||||
|
||||
if (sslPassphraseFile == NULL)
|
||||
{
|
||||
std::cerr << "No password provided, and no sslPassphraseFile : "
|
||||
<< getSSLPasswdFileName(ssl_id).c_str() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
fclose(sslPassphraseFile);
|
||||
|
||||
std::cerr << "opening sslPassphraseFile : "
|
||||
<< getSSLPasswdFileName(ssl_id).c_str() << std::endl;
|
||||
|
||||
std::string plain;
|
||||
if ( AuthGPG::getAuthGPG()->decryptTextFromFile(
|
||||
plain, getSSLPasswdFileName(ssl_id)) )
|
||||
{
|
||||
std::cerr << "Decrypting went ok !" << std::endl;
|
||||
sslPassword = plain;
|
||||
|
||||
return sslPassword.length() > 0 ;
|
||||
}
|
||||
else
|
||||
{
|
||||
sslPassword = "";
|
||||
std::cerr << "Error : decrypting went wrong !" << std::endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string RsLoginHandler::getSSLPasswdFileName(const RsPeerId& /*ssl_id*/)
|
||||
{
|
||||
return rsAccounts->PathAccountKeysDirectory() + "/" + "ssl_passphrase.pgp";
|
||||
}
|
||||
|
||||
#ifdef RS_AUTOLOGIN
|
||||
|
||||
#if defined(HAS_GNOME_KEYRING) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
#include <gnome-keyring-1/gnome-keyring.h>
|
||||
# include <gnome-keyring-1/gnome-keyring.h>
|
||||
|
||||
GnomeKeyringPasswordSchema my_schema = {
|
||||
GNOME_KEYRING_ITEM_ENCRYPTION_KEY_PASSWORD,
|
||||
{
|
||||
{ "RetroShare SSL Id", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
|
||||
{ NULL, (GnomeKeyringAttributeType)0 }
|
||||
},
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
GNOME_KEYRING_ITEM_ENCRYPTION_KEY_PASSWORD,
|
||||
{
|
||||
{ "RetroShare SSL Id", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
|
||||
{ NULL, (GnomeKeyringAttributeType)0 }
|
||||
},
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@ -99,22 +183,6 @@ extern BOOL WINAPI CryptUnprotectData(
|
||||
#endif
|
||||
|
||||
|
||||
bool RsLoginHandler::getSSLPassword(const RsPeerId& ssl_id,bool enable_gpg_ask_passwd,std::string& ssl_passwd)
|
||||
{
|
||||
// First, see if autologin is available
|
||||
//
|
||||
if(tryAutoLogin(ssl_id,ssl_passwd))
|
||||
return true ;
|
||||
|
||||
// If we're not expecting to enter a passwd (e.g. test for autologin before
|
||||
// display of the login window), safely respond false.
|
||||
//
|
||||
if(!enable_gpg_ask_passwd)
|
||||
return false ;
|
||||
|
||||
return getSSLPasswdFromGPGFile(ssl_id,ssl_passwd) ;
|
||||
}
|
||||
|
||||
bool RsLoginHandler::tryAutoLogin(const RsPeerId& ssl_id,std::string& ssl_passwd)
|
||||
{
|
||||
std::cerr << "RsTryAutoLogin()" << std::endl;
|
||||
@ -587,79 +655,9 @@ bool RsLoginHandler::clearAutoLogin(const RsPeerId& ssl_id)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool RsLoginHandler::checkAndStoreSSLPasswdIntoGPGFile(const RsPeerId& ssl_id,const std::string& ssl_passwd)
|
||||
{
|
||||
// We want to pursue login with gpg passwd. Let's do it:
|
||||
//
|
||||
std::cerr << "let's store the ssl Password into a pgp ecrypted file" << std::endl;
|
||||
|
||||
FILE *sslPassphraseFile = RsDirUtil::rs_fopen(getSSLPasswdFileName(ssl_id).c_str(), "r");
|
||||
|
||||
if(sslPassphraseFile != NULL) // already have it.
|
||||
{
|
||||
fclose(sslPassphraseFile) ;
|
||||
return true ;
|
||||
}
|
||||
|
||||
bool ok ;
|
||||
std::string cipher ;
|
||||
|
||||
if(AuthGPG::getAuthGPG()->encryptTextToFile(ssl_passwd, getSSLPasswdFileName(ssl_id)))
|
||||
{
|
||||
std::cerr << "Encrypting went ok !" << std::endl;
|
||||
ok= true ;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Encrypting went wrong !" << std::endl;
|
||||
ok= false ;
|
||||
}
|
||||
|
||||
return ok ;
|
||||
}
|
||||
|
||||
bool RsLoginHandler::getSSLPasswdFromGPGFile(const RsPeerId& ssl_id,std::string& sslPassword)
|
||||
{
|
||||
// Let's read the password from an encrypted file
|
||||
// Let's check if there's a ssl_passpharese_file that we can decrypt with PGP
|
||||
//
|
||||
FILE *sslPassphraseFile = RsDirUtil::rs_fopen(getSSLPasswdFileName(ssl_id).c_str(), "r");
|
||||
|
||||
if (sslPassphraseFile == NULL)
|
||||
{
|
||||
std::cerr << "No password provided, and no sslPassphraseFile : " << getSSLPasswdFileName(ssl_id).c_str() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
fclose(sslPassphraseFile);
|
||||
|
||||
std::cerr << "opening sslPassphraseFile : " << getSSLPasswdFileName(ssl_id).c_str() << std::endl;
|
||||
std::string plain ;
|
||||
|
||||
if (AuthGPG::getAuthGPG()->decryptTextFromFile(plain,getSSLPasswdFileName(ssl_id)))
|
||||
{
|
||||
std::cerr << "Decrypting went ok !" << std::endl;
|
||||
sslPassword = plain ;
|
||||
std::cerr << "sslpassword: " << "******************** (length = " << sslPassword.length() << ")" << std::endl;
|
||||
|
||||
return sslPassword.length() > 0 ;
|
||||
}
|
||||
else
|
||||
{
|
||||
sslPassword = "" ;
|
||||
std::cerr << "Error : decrypting went wrong !" << std::endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string RsLoginHandler::getSSLPasswdFileName(const RsPeerId& /*ssl_id*/)
|
||||
{
|
||||
return rsAccounts->PathAccountKeysDirectory() + "/" + "ssl_passphrase.pgp";
|
||||
}
|
||||
|
||||
std::string RsLoginHandler::getAutologinFileName(const RsPeerId& /*ssl_id*/)
|
||||
{
|
||||
return rsAccounts->PathAccountKeysDirectory() + "/" + "help.dta" ;
|
||||
}
|
||||
|
||||
#endif // RS_AUTOLOGIN
|
||||
|
@ -2,46 +2,60 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
// This class handles login, meaning that it retrieves the SSL password from either
|
||||
// the keyring or help.dta file, if autologin is enabled, or from the ssl_passphrase.pgp
|
||||
// file, asking for the GPG password to decrypt it.
|
||||
//
|
||||
// This class should handle the following scenario:
|
||||
//
|
||||
// Normal login:
|
||||
// - SSL key is stored -> do autologin
|
||||
// - SSL key is not stored
|
||||
// - if we're actually in the login process, ask for the gpg passwd, and decrypt the key file
|
||||
// - if we're just trying for autologin, don't ask for the gpg passwd and return null
|
||||
//
|
||||
// Key creation:
|
||||
// - the key should be stored in the gpg file.
|
||||
//
|
||||
/**
|
||||
* This class handles login, meaning that it retrieves the SSL password from
|
||||
* either the keyring or help.dta file, if autologin is enabled, or from the
|
||||
* ssl_passphrase.pgp file, asking for the GPG password to decrypt it.
|
||||
*
|
||||
* This class should handle the following scenario:
|
||||
*
|
||||
* Normal login:
|
||||
* - SSL key is stored -> do autologin
|
||||
* - SSL key is not stored
|
||||
* - if we're actually in the login process, ask for the gpg passwd, and
|
||||
* decrypt the key file
|
||||
* - if we're just trying for autologin, don't ask for the gpg passwd and
|
||||
* return null
|
||||
*
|
||||
* Key creation:
|
||||
* - the key should be stored in the gpg file.
|
||||
*/
|
||||
class RsLoginHandler
|
||||
{
|
||||
public:
|
||||
// Gets the SSL passwd by any means: try autologin, and look into gpg file if enable_gpg_key_callback==true
|
||||
//
|
||||
static bool getSSLPassword(const RsPeerId& ssl_id,bool enable_gpg_key_callback,std::string& ssl_password) ;
|
||||
public:
|
||||
/**
|
||||
* Gets the SSL passwd by any means: try autologin, and look into gpg file
|
||||
* if enable_gpg_key_callback==true
|
||||
*/
|
||||
static bool getSSLPassword( const RsPeerId& ssl_id,
|
||||
bool enable_gpg_key_callback,
|
||||
std::string& ssl_password);
|
||||
|
||||
// Checks whether the ssl passwd is already in the gpg file. If the file's not here, the passwd is stored there,
|
||||
// encrypted with the current GPG key.
|
||||
//
|
||||
static bool checkAndStoreSSLPasswdIntoGPGFile(const RsPeerId& ssl_id,const std::string& ssl_passwd) ;
|
||||
/**
|
||||
* Checks whether the ssl passwd is already in the gpg file. If the file's
|
||||
* not here, the passwd is stored there, encrypted with the current GPG key.
|
||||
*/
|
||||
static bool checkAndStoreSSLPasswdIntoGPGFile(
|
||||
const RsPeerId& ssl_id, const std::string& ssl_passwd );
|
||||
|
||||
// Stores the given ssl_id/passwd pair into the keyring, or by default into a file in /[ssl_id]/keys/help.dta
|
||||
//
|
||||
static bool enableAutoLogin(const RsPeerId& ssl_id,const std::string& passwd) ;
|
||||
#ifdef RS_AUTOLOGIN
|
||||
/**
|
||||
* Stores the given ssl_id/passwd pair into the keyring, or by default into
|
||||
* a file in /[ssl_id]/keys/help.dta
|
||||
*/
|
||||
static bool enableAutoLogin(const RsPeerId& ssl_id,const std::string& passwd) ;
|
||||
|
||||
// Clears autologin entry.
|
||||
//
|
||||
static bool clearAutoLogin(const RsPeerId& ssl_id) ;
|
||||
/// Clears autologin entry.
|
||||
static bool clearAutoLogin(const RsPeerId& ssl_id) ;
|
||||
#endif // RS_AUTOLOGIN
|
||||
|
||||
private:
|
||||
static bool tryAutoLogin(const RsPeerId& ssl_id,std::string& ssl_passwd) ;
|
||||
static bool getSSLPasswdFromGPGFile(const RsPeerId& ssl_id,std::string& sslPassword) ;
|
||||
private:
|
||||
static bool getSSLPasswdFromGPGFile(const RsPeerId& ssl_id,std::string& sslPassword);
|
||||
static std::string getSSLPasswdFileName(const RsPeerId& ssl_id);
|
||||
|
||||
static std::string getSSLPasswdFileName(const RsPeerId& ssl_id) ;
|
||||
static std::string getAutologinFileName(const RsPeerId& ssl_id) ;
|
||||
#ifdef RS_AUTOLOGIN
|
||||
static bool tryAutoLogin(const RsPeerId& ssl_id,std::string& ssl_passwd);
|
||||
static std::string getAutologinFileName(const RsPeerId& ssl_id);
|
||||
#endif // RS_AUTOLOGIN
|
||||
};
|
||||
|
||||
|
@ -35,12 +35,17 @@ StartDialog::StartDialog(QWidget *parent)
|
||||
/* Invoke Qt Designer generated QObject setup routine */
|
||||
ui.setupUi(this);
|
||||
|
||||
#ifdef RS_AUTOLOGIN
|
||||
connect(ui.autologin_checkbox, SIGNAL(clicked()), this, SLOT(notSecureWarning()));
|
||||
#else
|
||||
ui.autologin_checkbox->setHidden(true);
|
||||
#endif
|
||||
|
||||
Settings->loadWidgetInformation(this);
|
||||
|
||||
ui.loadButton->setFocus();
|
||||
|
||||
connect(ui.loadButton, SIGNAL(clicked()), this, SLOT(loadPerson()));
|
||||
connect(ui.autologin_checkbox, SIGNAL(clicked()), this, SLOT(notSecureWarning()));
|
||||
|
||||
/* get all available pgp private certificates....
|
||||
* mark last one as default.
|
||||
@ -115,6 +120,7 @@ bool StartDialog::requestedNewCert()
|
||||
return reqNewCert;
|
||||
}
|
||||
|
||||
#ifdef RS_AUTOLOGIN
|
||||
void StartDialog::notSecureWarning()
|
||||
{
|
||||
/* some error msg */
|
||||
@ -130,3 +136,4 @@ void StartDialog::notSecureWarning()
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif // RS_AUTOLOGIN
|
||||
|
@ -40,10 +40,12 @@ protected:
|
||||
private slots:
|
||||
void loadPerson();
|
||||
|
||||
#ifdef RS_AUTOLOGIN
|
||||
/**
|
||||
* Warns the user that autologin is not secure
|
||||
*/
|
||||
void notSecureWarning();
|
||||
#endif // RS_AUTOLOGIN
|
||||
|
||||
void on_labelProfile_linkActivated(QString link);
|
||||
|
||||
|
@ -18,7 +18,16 @@
|
||||
<normaloff>:/images/logo/logo_32.png</normaloff>:/images/logo/logo_32.png</iconset>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
@ -198,6 +207,12 @@
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
|
@ -70,7 +70,8 @@ INCLUDEPATH *= retroshare-gui
|
||||
################################# Linux ##########################################
|
||||
# Put lib dir in QMAKE_LFLAGS so it appears before -L/usr/lib
|
||||
linux-* {
|
||||
CONFIG += link_pkgconfig
|
||||
CONFIG += link_pkgconfig
|
||||
|
||||
#CONFIG += version_detail_bash_script
|
||||
QMAKE_CXXFLAGS *= -D_FILE_OFFSET_BITS=64
|
||||
|
||||
@ -78,7 +79,6 @@ linux-* {
|
||||
|
||||
LIBS *= -rdynamic
|
||||
DEFINES *= HAVE_XSS # for idle time, libx screensaver extensions
|
||||
DEFINES *= HAS_GNOME_KEYRING
|
||||
}
|
||||
|
||||
unix {
|
||||
|
@ -39,6 +39,12 @@ no_libresapihttpserver:CONFIG -= libresapihttpserver
|
||||
CONFIG *= sqlcipher
|
||||
no_sqlcipher:CONFIG -= sqlcipher
|
||||
|
||||
# To enable autologin (this is higly discouraged as it may compromise your node
|
||||
# security in multiple ways) append the following assignation to qmake command
|
||||
# line "CONFIG+=rs_autologin"
|
||||
CONFIG *= no_rs_autologin
|
||||
rs_autologin:CONFIG -= no_rs_autologin
|
||||
|
||||
# To disable GXS (General eXchange System) append the following
|
||||
# assignation to qmake command line "CONFIG+=no_rs_gxs"
|
||||
CONFIG *= rs_gxs
|
||||
@ -52,6 +58,13 @@ unix {
|
||||
isEmpty(LIB_DIR) { LIB_DIR = "$${PREFIX}/lib" }
|
||||
isEmpty(DATA_DIR) { DATA_DIR = "$${PREFIX}/share/RetroShare06" }
|
||||
isEmpty(PLUGIN_DIR) { PLUGIN_DIR = "$${LIB_DIR}/retroshare/extensions6" }
|
||||
|
||||
rs_autologin {
|
||||
!macx {
|
||||
DEFINES *= HAS_GNOME_KEYRING
|
||||
PKGCONFIG *= gnome-keyring-1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android-g++ {
|
||||
@ -136,3 +149,7 @@ libresapilocalserver:DEFINES *= LIBRESAPI_LOCAL_SERVER
|
||||
libresapihttpserver:DEFINES *= ENABLE_WEBUI
|
||||
sqlcipher:DEFINES -= NO_SQLCIPHER
|
||||
no_sqlcipher:DEFINES *= NO_SQLCIPHER
|
||||
rs_autologin {
|
||||
DEFINES *= RS_AUTOLOGIN
|
||||
warning(You have enabled RetroShare autologin this is strongly discouraged as it may compromise your node security in multiple ways)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user