mirror of
https://github.com/nuxsmin/sysPass.git
synced 2026-03-03 07:04:07 +01:00
* Closes 40. Accounts can be imported from KeePass or KeePassX XML file format.
This commit is contained in:
@@ -1 +1 @@
|
||||
<?php
|
||||
<?php
|
||||
@@ -218,7 +218,10 @@ foreach ($resQuery as $account) {
|
||||
}
|
||||
}
|
||||
|
||||
$strAccNotes = (strlen($account->account_notes) > 300) ? substr($account->account_notes, 0, 300) . "..." : $account->account_notes;
|
||||
if ($account->account_notes){
|
||||
$strAccNotes = (strlen($account->account_notes) > 300) ? substr($account->account_notes, 0, 300) . "..." : $account->account_notes;
|
||||
$strAccNotes = nl2br(wordwrap(htmlspecialchars($strAccNotes), 50, '<br>', true));
|
||||
}
|
||||
}
|
||||
|
||||
//echo '<div class="account-label round shadow" onMouseOver="this.style.backgroundColor=\'RGBA('.$rgbaColor.')\'" onMouseOut="this.style.backgroundColor=\'#FFFFFF\'" >';
|
||||
@@ -254,7 +257,7 @@ foreach ($resQuery as $account) {
|
||||
echo '<div class="account-info">';
|
||||
echo '<img src="imgs/btn_group.png" title="' . $secondaryAccesses . '" />';
|
||||
|
||||
echo ($strAccNotes) ? '<img src="imgs/notes.png" title="' . _('Notas') . ': <br><br>' . nl2br(wordwrap(htmlspecialchars($strAccNotes), 50, '<br>', true)) . '" />' : '';
|
||||
echo ($strAccNotes) ? '<img src="imgs/notes.png" title="' . _('Notas') . ': <br><br>' . $strAccNotes . '" />' : '';
|
||||
|
||||
if ($filesEnabled) {
|
||||
$intNumFiles = SP_Files::countFiles($account->account_id);
|
||||
|
||||
@@ -550,16 +550,20 @@ class SP_Account
|
||||
|
||||
$message['action'] = __FUNCTION__;
|
||||
|
||||
if (!SP_Groups::addGroupsForAccount($this->accountId, $this->accountUserGroupsId)) {
|
||||
$message['text'][] = _('Error al actualizar los grupos secundarios');
|
||||
SP_Log::wrLogInfo($message);
|
||||
$message['text'] = array();
|
||||
if ( is_array($this->accountUserGroupsId) ){
|
||||
if (!SP_Groups::addGroupsForAccount($this->accountId, $this->accountUserGroupsId)) {
|
||||
$message['text'][] = _('Error al actualizar los grupos secundarios');
|
||||
SP_Log::wrLogInfo($message);
|
||||
$message['text'] = array();
|
||||
}
|
||||
}
|
||||
|
||||
if (!SP_Users::addUsersForAccount($this->accountId, $this->accountUsersId)) {
|
||||
$message['text'][] = _('Error al actualizar los usuarios de la cuenta');
|
||||
SP_Log::wrLogInfo($message);
|
||||
$message['text'] = array();
|
||||
if ( is_array($this->accountUsersId) ){
|
||||
if (!SP_Users::addUsersForAccount($this->accountId, $this->accountUsersId)) {
|
||||
$message['text'][] = _('Error al actualizar los usuarios de la cuenta');
|
||||
SP_Log::wrLogInfo($message);
|
||||
$message['text'] = array();
|
||||
}
|
||||
}
|
||||
|
||||
$accountInfo = array('customer_name');
|
||||
|
||||
@@ -225,7 +225,7 @@ class SP_Common
|
||||
$msgHelp[20] = _('Guarda las acciones realizadas en la aplicación');
|
||||
$msgHelp[21] = _('Comprobar actualizaciones de la aplicación (sólo para los usuarios administradores)');
|
||||
$msgHelp[22] = _('Extensiones de máximo 4 caracteres.') . "<br><br>" . _('Escribir extensión y pulsar intro para añadir.');
|
||||
$msgHelp[23] = _('Importar desde un archivo CSV con el formato') . ":<br><br>" . _('nombre_de_cuenta;cliente;categoría;url;usuario;clave;notas') . "<br><br>" . _('Si el cliente o la categoría no están creados, se crean automáticamente.');
|
||||
$msgHelp[23] = _('Importar desde KeePass o KeePassX. El nombre del cliente será igual a KeePass o KeePassX')."<br><br>"._('Importar desde un archivo CSV con el formato') . ":<br><br>" . _('nombre_de_cuenta;cliente;categoría;url;usuario;clave;notas') . "<br><br>" . _('Si el cliente o la categoría no están creados, se crean automáticamente.');
|
||||
$msgHelp[24] = _('Permite que las cuentas sin acceso sean visibles sólo para las búsquedas.');
|
||||
$msgHelp[25] = _('Muestra los resultados de búsqueda de cuentas en formato tarjeta.');
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ class SP_Import
|
||||
{
|
||||
private static $result = array();
|
||||
private static $fileContent;
|
||||
private static $tmpFile;
|
||||
|
||||
/**
|
||||
* @brief Iniciar la importación de cuentas
|
||||
@@ -75,7 +76,6 @@ class SP_Import
|
||||
{
|
||||
try {
|
||||
self::readDataFromFile($fileData);
|
||||
self::parseData();
|
||||
} catch (ImportException $e) {
|
||||
$message['action'] = _('Importar Cuentas');
|
||||
$message['text'][] = $e->getMessage();
|
||||
@@ -99,22 +99,21 @@ class SP_Import
|
||||
*/
|
||||
private static function readDataFromFile(&$fileData)
|
||||
{
|
||||
|
||||
if (!is_array($fileData)) {
|
||||
throw new ImportException('critical', _('Archivo no subido correctamente'), _('Verifique los permisos del usuario del servidor web'));
|
||||
}
|
||||
|
||||
if ($fileData['inFile']['name']) {
|
||||
if ($fileData['name']) {
|
||||
// Comprobamos la extensión del archivo
|
||||
$fileExtension = strtoupper(pathinfo($_FILES['inFile']['name'], PATHINFO_EXTENSION));
|
||||
$fileExtension = strtoupper(pathinfo($fileData['name'], PATHINFO_EXTENSION));
|
||||
|
||||
if ($fileExtension != 'csv') {
|
||||
if ($fileExtension != 'CSV' && $fileExtension != 'XML') {
|
||||
throw new ImportException('critical', _('Tipo de archivo no soportado'), _('Compruebe la extensión del archivo'));
|
||||
}
|
||||
}
|
||||
|
||||
// Variables con información del archivo
|
||||
$tmpName = $_FILES['inFile']['tmp_name'];
|
||||
$tmpName = $fileData['tmp_name'];
|
||||
|
||||
if (!file_exists($tmpName) || !is_readable($tmpName)) {
|
||||
// Registramos el máximo tamaño permitido por PHP
|
||||
@@ -123,12 +122,21 @@ class SP_Import
|
||||
throw new ImportException('critical', _('Error interno al leer el archivo'), _('Compruebe la configuración de PHP para subir archivos'));
|
||||
}
|
||||
|
||||
if ($fileData['type'] === 'text/csv'){
|
||||
// Leemos el archivo a un array
|
||||
self::$fileContent = file($tmpName);
|
||||
|
||||
// Leemos el archivo a una variable
|
||||
self::$fileContent = file($tmpName);
|
||||
|
||||
if (!is_array(self::$fileContent)) {
|
||||
throw new ImportException('critical', _('Error interno al leer el archivo'), _('Compruebe los permisos del directorio temporal'));
|
||||
if (!is_array(self::$fileContent)) {
|
||||
throw new ImportException('critical', _('Error interno al leer el archivo'), _('Compruebe los permisos del directorio temporal'));
|
||||
}
|
||||
// Obtenemos las cuentas desde el archivo CSV
|
||||
self::parseFileData();
|
||||
} elseif ($fileData['type'] === 'text/xml'){
|
||||
self::$tmpFile = $tmpName;
|
||||
// Analizamos el XML y seleccionamos el formato a importar
|
||||
self::detectXMLFormat();
|
||||
} else{
|
||||
throw new ImportException('critical', _('Tipo mime no soportado'), _('Compruebe el formato del archivo'));
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -139,52 +147,16 @@ class SP_Import
|
||||
* @throws ImportException
|
||||
* @return bool
|
||||
*/
|
||||
private static function parseData()
|
||||
private static function parseFileData()
|
||||
{
|
||||
// Datos del Usuario
|
||||
$userId = SP_Common::parseParams('s', 'uid', 0);
|
||||
$groupId = SP_Common::parseParams('s', 'ugroup', 0);
|
||||
|
||||
$account = new SP_Account;
|
||||
|
||||
foreach (self::$fileContent as $data) {
|
||||
$fields = explode(';', $data);
|
||||
|
||||
if (count($fields) != 7) {
|
||||
if (count($fields) < 7) {
|
||||
throw new ImportException('critical', _('El número de campos es incorrecto'), _('Compruebe el formato del archivo CSV'));
|
||||
}
|
||||
|
||||
list($accountName, $customerName, $categoryName, $url, $username, $password, $notes) = $fields;
|
||||
|
||||
SP_Customer::$customerName = $customerName;
|
||||
if (!SP_Customer::checkDupCustomer()) {
|
||||
$customerId = SP_Customer::getCustomerByName();
|
||||
} else {
|
||||
SP_Customer::addCustomer();
|
||||
$customerId = SP_Customer::$customerLastId;
|
||||
}
|
||||
|
||||
$categoryId = SP_Category::getCategoryIdByName($categoryName);
|
||||
if ($categoryId === 0 || $categoryId === false) {
|
||||
SP_Category::$categoryName = $categoryName;
|
||||
SP_Category::addCategory($categoryName);
|
||||
$categoryId = SP_Category::$categoryLastId;
|
||||
}
|
||||
|
||||
$pass = self::encryptPass($password);
|
||||
|
||||
$account->accountName = $accountName;
|
||||
$account->accountCustomerId = $customerId;
|
||||
$account->accountCategoryId = $categoryId;
|
||||
$account->accountLogin = $username;
|
||||
$account->accountUrl = $url;
|
||||
$account->accountPass = $pass['pass'];
|
||||
$account->accountIV = $pass['IV'];
|
||||
$account->accountNotes = $notes;
|
||||
$account->accountUserId = $userId;
|
||||
$account->accountUserGroupId = $groupId;
|
||||
|
||||
if (!$account->createAccount()) {
|
||||
if (!self::addAccountData($fields)){
|
||||
$message['action'] = _('Importar Cuentas');
|
||||
$message['text'][] = _('Error importando cuenta');
|
||||
$message['text'][] = $data;
|
||||
@@ -196,6 +168,56 @@ class SP_Import
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Crear una cuenta con los datos obtenidos
|
||||
* @param array $data con los datos de la cuenta
|
||||
* @throws ImportException
|
||||
* @return bool
|
||||
*/
|
||||
public static function addAccountData($data)
|
||||
{
|
||||
// Datos del Usuario
|
||||
$userId = SP_Common::parseParams('s', 'uid', 0);
|
||||
$groupId = SP_Common::parseParams('s', 'ugroup', 0);
|
||||
|
||||
// Asignamos los valores del array a variables
|
||||
list($accountName, $customerName, $categoryName, $url, $username, $password, $notes) = $data;
|
||||
|
||||
// Comprobamos si existe el cliente o lo creamos
|
||||
SP_Customer::$customerName = $customerName;
|
||||
if (!SP_Customer::checkDupCustomer()) {
|
||||
$customerId = SP_Customer::getCustomerByName();
|
||||
} else {
|
||||
SP_Customer::addCustomer();
|
||||
$customerId = SP_Customer::$customerLastId;
|
||||
}
|
||||
|
||||
// Comprobamos si existe la categoría o la creamos
|
||||
$categoryId = SP_Category::getCategoryIdByName($categoryName);
|
||||
if ($categoryId == 0) {
|
||||
SP_Category::$categoryName = $categoryName;
|
||||
SP_Category::addCategory($categoryName);
|
||||
$categoryId = SP_Category::$categoryLastId;
|
||||
}
|
||||
|
||||
$pass = self::encryptPass($password);
|
||||
|
||||
$account = new SP_Account;
|
||||
$account->accountName = $accountName;
|
||||
$account->accountCustomerId = $customerId;
|
||||
$account->accountCategoryId = $categoryId;
|
||||
$account->accountLogin = $username;
|
||||
$account->accountUrl = $url;
|
||||
$account->accountPass = $pass['pass'];
|
||||
$account->accountIV = $pass['IV'];
|
||||
$account->accountNotes = $notes;
|
||||
$account->accountUserId = $userId;
|
||||
$account->accountUserGroupId = $groupId;
|
||||
|
||||
// Creamos la cuenta
|
||||
return $account->createAccount();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encriptar la clave de una cuenta
|
||||
* @param string $password con la clave de la cuenta
|
||||
@@ -223,4 +245,72 @@ class SP_Import
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* @brief Leer el archivo de KeePass a un objeto XML
|
||||
* @throws ImportException
|
||||
* @return bool
|
||||
*/
|
||||
private static function readXMLFile()
|
||||
{
|
||||
if ($xmlFile = simplexml_load_file(self::$tmpFile)){
|
||||
return $xmlFile;
|
||||
} else{
|
||||
throw new ImportException('critical', _('Error interno'), _('No es posible procesar el archivo XML'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Detectar la aplicación que generó el XML
|
||||
* @throws ImportException
|
||||
* @return bool
|
||||
*/
|
||||
private static function detectXMLFormat()
|
||||
{
|
||||
$xml = self::readXMLFile();
|
||||
|
||||
if ( $xml->Meta->Generator == 'KeePass' ){
|
||||
SP_KeePassImport::addKeepassAccounts($xml);
|
||||
} else if ($xmlApp = self::parseFileHeader()){
|
||||
switch ($xmlApp) {
|
||||
case 'keepassx_database':
|
||||
SP_KeePassXImport::addKeepassXAccounts($xml);
|
||||
break;
|
||||
case 'revelationdata':
|
||||
error_log('REVELATION');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else{
|
||||
throw new ImportException('critical', _('Archivo XML no soportado'), _('No es posible detectar la aplicación que exportó los datos'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Leer la cabecera del archivo XML y obtener patrones de aplicaciones conocidas
|
||||
* @return bool
|
||||
*/
|
||||
private static function parseFileHeader()
|
||||
{
|
||||
$handle = @fopen(self::$tmpFile, "r");
|
||||
$headersRegex = '/(KEEPASSX_DATABASE|revelationdata)/i';
|
||||
|
||||
if ( $handle ){
|
||||
// No. de líneas a leer como máximo
|
||||
$maxLines = 5;
|
||||
$count = 0;
|
||||
|
||||
while (($buffer = fgets($handle, 4096)) !== false && $count <= $maxLines){
|
||||
if ( preg_match($headersRegex,$buffer,$app) ){
|
||||
fclose($handle);
|
||||
return strtolower($app[0]);
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
112
inc/keepassimport.class.php
Normal file
112
inc/keepassimport.class.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* sysPass
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link http://syspass.org
|
||||
* @copyright 2012-2014 Rubén Domínguez nuxsmin@syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
* sysPass is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sysPass is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
defined('APP_ROOT') || die(_('No es posible acceder directamente a este archivo'));
|
||||
|
||||
/**
|
||||
* Esta clase es la encargada de importar cuentas desde KeePass
|
||||
*/
|
||||
class SP_KeePassImport
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Iniciar la importación desde KeePass
|
||||
* @param object $xml
|
||||
* @return bool
|
||||
*/
|
||||
public static function addKeepassAccounts($xml)
|
||||
{
|
||||
self::getGroups($xml->Root->Group);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtener los datos de las entradas de KeePass
|
||||
* @param object $entries con el objeto XML con las entradas
|
||||
* @param string $groupName con nombre del grupo a procesar
|
||||
* @throws ImportException
|
||||
* @return bool
|
||||
*/
|
||||
private static function getEntryData($entries, $groupName)
|
||||
{
|
||||
foreach ( $entries as $entry ){
|
||||
foreach ( $entry->String as $account ){
|
||||
switch ($account->Key){
|
||||
case 'Notes':
|
||||
$notes = $account->Value;
|
||||
break;
|
||||
case 'Password':
|
||||
$password = $account->Value;
|
||||
break;
|
||||
case 'Title':
|
||||
$name = $account->Value;
|
||||
break;
|
||||
case 'url':
|
||||
$url = $account->Value;
|
||||
break;
|
||||
case 'UserName':
|
||||
$username = $account->Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$accountData = array($name,'KeePass',$groupName,$url,$username,$password,$notes);
|
||||
SP_Import::addAccountData($accountData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtener los grupos y procesar lan entradas de KeePass
|
||||
* @param object $xml con objeto XML del archivo de KeePass
|
||||
* @throws ImportException
|
||||
* @return bool
|
||||
*/
|
||||
private static function getGroups($xml)
|
||||
{
|
||||
foreach($xml as $node){
|
||||
if ( $node->Group ){
|
||||
foreach ( $node->Group as $group ){
|
||||
$groupName = $group->Name;
|
||||
// Analizar grupo
|
||||
if ( $node->Group->Entry ){
|
||||
// Obtener entradas
|
||||
self::getEntryData($group->Entry,$groupName);
|
||||
}
|
||||
|
||||
if ( $group->Group ){
|
||||
// Analizar subgrupo
|
||||
self::getGroups($group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $node->Entry ){
|
||||
$groupName = $node->Name;
|
||||
// Obtener entradas
|
||||
self::getEntryData($node->Entry,$groupName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
98
inc/keepassximport.class.php
Normal file
98
inc/keepassximport.class.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* sysPass
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link http://syspass.org
|
||||
* @copyright 2012-2014 Rubén Domínguez nuxsmin@syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
* sysPass is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sysPass is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
defined('APP_ROOT') || die(_('No es posible acceder directamente a este archivo'));
|
||||
|
||||
/**
|
||||
* Esta clase es la encargada de importar cuentas desde KeePassX
|
||||
*/
|
||||
class SP_KeePassXImport
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Iniciar la importación desde KeePass
|
||||
* @param object $xml
|
||||
* @return bool
|
||||
*/
|
||||
public static function addKeepassXAccounts($xml)
|
||||
{
|
||||
self::getGroups($xml);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtener los datos de las entradas de KeePass
|
||||
* @param object $entries con el objeto XML con las entradas
|
||||
* @param string $groupName con nombre del grupo a procesar
|
||||
* @throws ImportException
|
||||
* @return bool
|
||||
*/
|
||||
private static function getEntryData($entries, $groupName)
|
||||
{
|
||||
foreach ( $entries as $entry ){
|
||||
$notes = $entry->comment;
|
||||
$password = $entry->password;
|
||||
$name = $entry->title;
|
||||
$url = $entry->url;
|
||||
$username = $entry->username;
|
||||
|
||||
$accountData = array($name,'KeePassX',$groupName,$url,$username,$password,$notes);
|
||||
SP_Import::addAccountData($accountData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtener los grupos y procesar lan entradas de KeePass
|
||||
* @param object $xml con objeto XML del archivo de KeePass
|
||||
* @throws ImportException
|
||||
* @return bool
|
||||
*/
|
||||
private static function getGroups($xml)
|
||||
{
|
||||
foreach($xml as $node){
|
||||
if ( $node->group ){
|
||||
foreach ( $node->group as $group ){
|
||||
$groupName = $group->title;
|
||||
// Analizar grupo
|
||||
if ( $node->group->entry ){
|
||||
// Obtener entradas
|
||||
self::getEntryData($group->entry,$groupName);
|
||||
}
|
||||
|
||||
if ( $group->group ){
|
||||
// Analizar subgrupo
|
||||
self::getGroups($group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $node->entry ){
|
||||
$groupName = $node->title;
|
||||
// Obtener entradas
|
||||
self::getEntryData($node->entry,$groupName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -100,7 +100,7 @@ $onCloseAction = $data['onCloseAction'];
|
||||
</div>
|
||||
|
||||
<div id="title" class="midroundup titleNormal">
|
||||
<?php echo _('Importar CSV'); ?>
|
||||
<?php echo _('Importar CSV/XML'); ?>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -254,7 +254,7 @@ class SP_Util
|
||||
*/
|
||||
public static function getVersion($retBuild = false)
|
||||
{
|
||||
$build = 7;
|
||||
$build = 8;
|
||||
$version = array(1, 1, 2);
|
||||
|
||||
if ($retBuild) {
|
||||
|
||||
@@ -557,7 +557,7 @@ function dropFile(accountId, sk, maxsize) {
|
||||
// Función para activar el Drag&Drop de archivos en la importación de cuentas
|
||||
function importFile(sk) {
|
||||
var dropfiles = $('#dropzone');
|
||||
var file_exts_ok = ['csv'];
|
||||
var file_exts_ok = ['csv','xml'];
|
||||
|
||||
dropfiles.filedrop({
|
||||
fallback_id: 'inFile',
|
||||
|
||||
Reference in New Issue
Block a user