Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
cd4d37968b
|
@ -1,6 +1,7 @@
|
||||||
package com.loafle.overflow.crawler.wmi;
|
package com.loafle.overflow.crawler.wmi;
|
||||||
|
|
||||||
import com.loafle.overflow.crawler.Crawler;
|
import com.loafle.overflow.crawler.Crawler;
|
||||||
|
import com.loafle.overflow.crawler.config.Config;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -10,9 +11,9 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class WMICrawler extends Crawler {
|
public class WMICrawler extends Crawler {
|
||||||
|
|
||||||
public Object getInternal(Map<String, Object> map) throws Exception {
|
@Override
|
||||||
return WMICrawlerOS.getInstance().processWMI(map);
|
public Object getInternal(Config config) throws Exception {
|
||||||
|
|
||||||
|
return WMICrawlerOS.getInstance().processWMI(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.loafle.overflow.crawler.wmi;
|
package com.loafle.overflow.crawler.wmi;
|
||||||
|
|
||||||
|
import com.loafle.overflow.crawler.config.Config;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,17 +14,18 @@ public class WMICrawlerLinux extends WMICrawlerOS {
|
||||||
private final String DELIMITER = "||";
|
private final String DELIMITER = "||";
|
||||||
private final String DELIMITER_SPLIT = "\\|\\|";
|
private final String DELIMITER_SPLIT = "\\|\\|";
|
||||||
|
|
||||||
public Object processWMI(Map<String, Object> params) throws Exception {
|
public Object processWMI(Config config) throws Exception {
|
||||||
return processCommand(params);
|
return processCommand(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object processCommand(Map<String, Object> params) throws Exception {
|
public Object processCommand(Config config) throws Exception {
|
||||||
|
|
||||||
// String id = "administrator";
|
Map<String, Object> params = new HashMap<>();
|
||||||
// String pw = "dbseogns18";
|
params.put("id", "administrator");
|
||||||
// String nameSpace = "root/cimv2";
|
params.put("pw", "!@#$qwer1234");
|
||||||
// String query = "select * from Win32_OperatingSystem";
|
params.put("nameSpace", "root/cimv2");
|
||||||
// String ip = "192.168.1.106";
|
params.put("query", "select * from Win32_OperatingSystem");
|
||||||
|
params.put("ip", "192.168.1.1");
|
||||||
|
|
||||||
String id = (String)params.get("id");
|
String id = (String)params.get("id");
|
||||||
String pw = (String)params.get("pw");
|
String pw = (String)params.get("pw");
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.loafle.overflow.crawler.wmi;
|
package com.loafle.overflow.crawler.wmi;
|
||||||
|
|
||||||
|
import com.loafle.overflow.crawler.config.Config;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,5 +28,5 @@ public abstract class WMICrawlerOS {
|
||||||
return WMICrawlerOS.wmiCrawlerOS;
|
return WMICrawlerOS.wmiCrawlerOS;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Object processWMI(Map<String, Object> params) throws Exception;
|
public abstract Object processWMI(Config config) throws Exception;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@ import com.jacob.activeX.ActiveXComponent;
|
||||||
import com.jacob.com.Dispatch;
|
import com.jacob.com.Dispatch;
|
||||||
import com.jacob.com.EnumVariant;
|
import com.jacob.com.EnumVariant;
|
||||||
import com.jacob.com.Variant;
|
import com.jacob.com.Variant;
|
||||||
|
import com.loafle.overflow.crawler.config.Config;
|
||||||
|
import com.loafle.overflow.crawler.config.Item;
|
||||||
|
import com.loafle.overflow.crawler.config.Query;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -15,88 +18,145 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class WMICrawlerWindows extends WMICrawlerOS {
|
public class WMICrawlerWindows extends WMICrawlerOS {
|
||||||
|
|
||||||
public Object processWMI(Map<String, Object> params) {
|
public Object processWMI(Config config) throws Exception {
|
||||||
|
|
||||||
// String id = "administrator";
|
String query = "";
|
||||||
// String pw = "!@#$qwer1234";
|
|
||||||
// String nameSpace = "root/cimv2";
|
|
||||||
// String query = "select * from Win32_OperatingSystem";
|
|
||||||
// String ip = "192.168.1.1";
|
|
||||||
|
|
||||||
String id = (String)params.get("id");
|
Item cItem = null;
|
||||||
String pw = (String)params.get("pw");
|
Query cQuery = null;
|
||||||
String nameSpace = (String)params.get("nameSpace");
|
|
||||||
String query = (String)params.get("query");
|
|
||||||
String ip = (String)params.get("ip");
|
|
||||||
|
|
||||||
ActiveXComponent wmi = null;
|
int idxMetric = 0;
|
||||||
wmi = new ActiveXComponent("WbemScripting.SWbemLocator");
|
|
||||||
|
|
||||||
Variant vIp = new Variant(ip);
|
Map<String, String> resultMap = new HashMap<>();
|
||||||
Variant vNs = new Variant(nameSpace);
|
ActiveXComponent wmiconnect = null;
|
||||||
Variant vId = new Variant(id);
|
for(int cIndexI = 0 ; cIndexI < config.getItems().size() ; ++cIndexI) {
|
||||||
Variant vPw = new Variant(pw);
|
|
||||||
|
|
||||||
Variant conRet = wmi.invoke("ConnectServer", vIp, vNs, vId, vPw);
|
cItem = config.getItems().get(cIndexI);
|
||||||
|
|
||||||
vIp.safeRelease();
|
for( int indexJ = 0; indexJ < cItem.getQueries().size(); ++indexJ) {
|
||||||
vNs.safeRelease();
|
|
||||||
vId.safeRelease();
|
|
||||||
vPw.safeRelease();
|
|
||||||
|
|
||||||
Dispatch disConret = conRet.toDispatch();
|
cQuery = cItem.getQueries().get(indexJ);
|
||||||
|
query =cQuery.getQuery();
|
||||||
|
|
||||||
ActiveXComponent wmiconnect = new ActiveXComponent(disConret);
|
wmiconnect = connectServer(config);
|
||||||
disConret.safeRelease();
|
|
||||||
|
|
||||||
Variant vQuery = new Variant(query);
|
Variant vQuery = new Variant(query);
|
||||||
|
|
||||||
Variant vCollection = wmiconnect.invoke("ExecQuery", vQuery);
|
Variant vCollection = wmiconnect.invoke("ExecQuery", vQuery);
|
||||||
|
|
||||||
wmiconnect.safeRelease();
|
wmiconnect.safeRelease();
|
||||||
vQuery.safeRelease();
|
vQuery.safeRelease();
|
||||||
|
|
||||||
EnumVariant enumVariant = new EnumVariant(vCollection.toDispatch());
|
Dispatch dConnection = vCollection.toDispatch();
|
||||||
|
vCollection.safeRelease();
|
||||||
|
|
||||||
wmi.safeRelease();
|
|
||||||
conRet.safeRelease();
|
|
||||||
vCollection.safeRelease();
|
|
||||||
|
|
||||||
|
int count = getCount(dConnection);
|
||||||
|
|
||||||
|
EnumVariant enumVariant = new EnumVariant(dConnection);
|
||||||
|
dConnection.safeRelease();
|
||||||
|
|
||||||
|
if( count <= 1 ) {
|
||||||
|
getSingleValue(enumVariant, cQuery, cItem, idxMetric, resultMap);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
getMultiValue(enumVariant, cQuery, cItem, idxMetric, resultMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enumVariant.safeRelease();
|
||||||
|
idxMetric += cQuery.getKeys().size();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
return resultMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void getMultiValue(EnumVariant enumVariant, Query cQuery, Item cItem, int idxMetric, Map<String, String> resultMap) {
|
||||||
|
|
||||||
Variant vItem = null;
|
Variant vItem = null;
|
||||||
Dispatch item = null;
|
Dispatch item = null;
|
||||||
|
|
||||||
List<String> columns = null;
|
|
||||||
Map<String, String> resultMap = null;
|
int keyIdx = 0;
|
||||||
List<Map<String, String>> resultMapList = new ArrayList<Map<String, String>>();
|
|
||||||
while (enumVariant.hasMoreElements()) {
|
while (enumVariant.hasMoreElements()) {
|
||||||
|
|
||||||
vItem = enumVariant.nextElement();
|
vItem = enumVariant.nextElement();
|
||||||
item = vItem.toDispatch();
|
item = vItem.toDispatch();
|
||||||
|
|
||||||
if (columns == null) {
|
|
||||||
columns = getColumns(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
resultMap = new HashMap<String, String>();
|
|
||||||
String name = null;
|
String name = null;
|
||||||
String value = null;
|
String value = null;
|
||||||
Variant vValue = null;
|
Variant vValue = null;
|
||||||
|
|
||||||
for(int indexI = 0 ; indexI < columns.size(); ++indexI) {
|
for(int indexI = 0 ; indexI < cQuery.getKeys().size(); ++indexI) {
|
||||||
name = columns.get(indexI);
|
name = cQuery.getKeys().get(indexI);
|
||||||
vValue = Dispatch.call(item, name);
|
vValue = Dispatch.call(item, name);
|
||||||
value = vValue.toString();
|
value = vValue.toString();
|
||||||
resultMap.put(name, value);
|
String keyStr = convertArrayKey(cItem.getMetrics().get(indexI),keyIdx );
|
||||||
|
resultMap.put(keyStr, value);
|
||||||
vValue.safeRelease();
|
vValue.safeRelease();
|
||||||
}
|
}
|
||||||
|
|
||||||
resultMapList.add(resultMap);
|
vItem.safeRelease();
|
||||||
|
item.safeRelease();
|
||||||
|
++idxMetric;
|
||||||
|
++keyIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String convertArrayKey(String key, int index) {
|
||||||
|
int idx = key.indexOf("[");
|
||||||
|
String ret = "";
|
||||||
|
|
||||||
|
if(idx <= 0) {
|
||||||
|
idx = key.indexOf(".");
|
||||||
|
ret += key.substring(0, idx);
|
||||||
|
ret += "[";
|
||||||
|
ret += String.valueOf(index);
|
||||||
|
ret += "]";
|
||||||
|
ret += key.substring(idx +1);
|
||||||
|
}else {
|
||||||
|
|
||||||
|
ret += key.substring(0, idx +1);
|
||||||
|
ret += String.valueOf(index);
|
||||||
|
ret += key.substring(idx +1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void getSingleValue(EnumVariant enumVariant, Query cQuery, Item cItem, int idxMetric, Map<String, String> resultMap) {
|
||||||
|
|
||||||
|
Variant vItem = null;
|
||||||
|
Dispatch item = null;
|
||||||
|
|
||||||
|
while (enumVariant.hasMoreElements()) {
|
||||||
|
|
||||||
|
vItem = enumVariant.nextElement();
|
||||||
|
item = vItem.toDispatch();
|
||||||
|
|
||||||
|
String name = null;
|
||||||
|
String value = null;
|
||||||
|
Variant vValue = null;
|
||||||
|
|
||||||
|
for(int indexI = 0 ; indexI < cQuery.getKeys().size(); ++indexI) {
|
||||||
|
name = cQuery.getKeys().get(indexI);
|
||||||
|
vValue = Dispatch.call(item, name);
|
||||||
|
value = vValue.toString();
|
||||||
|
resultMap.put(cItem.getMetrics().get(idxMetric++), value);
|
||||||
|
vValue.safeRelease();
|
||||||
|
}
|
||||||
|
|
||||||
vItem.safeRelease();
|
vItem.safeRelease();
|
||||||
item.safeRelease();
|
item.safeRelease();
|
||||||
}
|
}
|
||||||
return resultMapList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<String> getColumns(Dispatch item) {
|
protected List<String> getColumns(Dispatch item) {
|
||||||
|
@ -140,4 +200,51 @@ public class WMICrawlerWindows extends WMICrawlerOS {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected int getCount(Dispatch d) {
|
||||||
|
|
||||||
|
Variant c = Dispatch.call(d, "Count");
|
||||||
|
|
||||||
|
int retInt = Integer.valueOf(c.toString());
|
||||||
|
|
||||||
|
c.safeRelease();
|
||||||
|
|
||||||
|
return retInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ActiveXComponent connectServer(Config config) {
|
||||||
|
|
||||||
|
String id = (String)config.getTarget().getAuth().get("id");
|
||||||
|
String pw = (String)config.getTarget().getAuth().get("pw");
|
||||||
|
String nameSpace = "";
|
||||||
|
// String query = (String)config.get.get("query");
|
||||||
|
|
||||||
|
String ip = config.getTarget().getConnection().getIp();
|
||||||
|
|
||||||
|
ActiveXComponent wmi = null;
|
||||||
|
wmi = new ActiveXComponent("WbemScripting.SWbemLocator");
|
||||||
|
|
||||||
|
Variant vIp = new Variant(ip);
|
||||||
|
Variant vNs = new Variant(nameSpace);
|
||||||
|
Variant vId = new Variant(id);
|
||||||
|
Variant vPw = new Variant(pw);
|
||||||
|
|
||||||
|
Variant conRet = wmi.invoke("ConnectServer", vIp, vNs, vId, vPw);
|
||||||
|
|
||||||
|
vIp.safeRelease();
|
||||||
|
vNs.safeRelease();
|
||||||
|
vId.safeRelease();
|
||||||
|
vPw.safeRelease();
|
||||||
|
|
||||||
|
Dispatch disConret = conRet.toDispatch();
|
||||||
|
|
||||||
|
ActiveXComponent wmiconnect = new ActiveXComponent(disConret);
|
||||||
|
disConret.safeRelease();
|
||||||
|
|
||||||
|
wmi.safeRelease();
|
||||||
|
conRet.safeRelease();
|
||||||
|
|
||||||
|
return wmiconnect;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
package com.loafle.overflow.crawler.wmi;
|
package com.loafle.overflow.crawler.wmi;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.loafle.overflow.crawler.config.Config;
|
||||||
|
import com.sun.xml.internal.ws.api.ResourceLoader;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -58,21 +64,31 @@ public class WMICrawlerTest {
|
||||||
// String query = "select * from Win32_OperatingSystem";
|
// String query = "select * from Win32_OperatingSystem";
|
||||||
// String ip = "192.168.1.1";
|
// String ip = "192.168.1.1";
|
||||||
|
|
||||||
map.put("id", "administrator");
|
// map.put("id", "administrator");
|
||||||
map.put("pw", "!@#$qwer1234");
|
// map.put("pw", "!@#$qwer1234");
|
||||||
map.put("nameSpace", "root/cimv2");
|
// map.put("nameSpace", "root/cimv2");
|
||||||
map.put("query", "select * from Win32_OperatingSystem");
|
// map.put("query", "select * from Win32_OperatingSystem");
|
||||||
map.put("ip", "192.168.1.1");
|
// map.put("ip", "192.168.1.1");
|
||||||
|
|
||||||
|
Config config = new Config();
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
ClassLoader classLoader = getClass().getClassLoader();
|
||||||
|
|
||||||
|
String s = classLoader.getResource("config").getFile();
|
||||||
|
System.out.println(s);
|
||||||
|
|
||||||
|
// Config c = mapper.readValue(new File(classLoader.getResource("config").getFile()),Config.class);
|
||||||
|
|
||||||
|
|
||||||
List<Map<String, String>> resultList = (List<Map<String, String>>)wmiCrawler.getInternal(map);
|
// List<Map<String, String>> resultList = (List<Map<String, String>>)wmiCrawler.getInternal(map);
|
||||||
|
//
|
||||||
|
//
|
||||||
for( Map<String,String> rMap : resultList) {
|
// for( Map<String,String> rMap : resultList) {
|
||||||
for( String key : rMap.keySet() ){
|
// for( String key : rMap.keySet() ){
|
||||||
System.out.println( String.format("key : %s ||| value : %s", key, rMap.get(key)) );
|
// System.out.println( String.format("key : %s ||| value : %s", key, rMap.get(key)) );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -83,4 +99,83 @@ public class WMICrawlerTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConfig() throws IOException {
|
||||||
|
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
String p = ResourceLoader.class.getClass().getResource("/config/example.json").getPath();
|
||||||
|
|
||||||
|
System.out.println(p);
|
||||||
|
|
||||||
|
|
||||||
|
Config c = mapper.readValue(new File(p),Config.class);
|
||||||
|
|
||||||
|
System.out.println(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWMIJson() throws Exception {
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
String p = ResourceLoader.class.getClass().getResource("/config/example.json").getPath();
|
||||||
|
Config c = mapper.readValue(new File(p),Config.class);
|
||||||
|
|
||||||
|
// Map<String, Object> map = new HashMap<>();
|
||||||
|
// map.put("id", "administrator");
|
||||||
|
// map.put("pw", "!@#$qwer1234");
|
||||||
|
|
||||||
|
// c.getTarget().setAuth(map);
|
||||||
|
|
||||||
|
c.getTarget().getAuth().put("id", "administrator");
|
||||||
|
c.getTarget().getAuth().put("pw", "!@#$qwer1234");
|
||||||
|
|
||||||
|
|
||||||
|
WMICrawler wmiCrawler = new WMICrawler();
|
||||||
|
|
||||||
|
|
||||||
|
Map<String, String> resultMap = (Map<String, String>)wmiCrawler.getInternal(c);
|
||||||
|
|
||||||
|
|
||||||
|
for( String key : resultMap.keySet() ){
|
||||||
|
System.out.println( String.format("key : %s ||| value : %s", key, resultMap.get(key)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInt() {
|
||||||
|
|
||||||
|
Integer a = 1;
|
||||||
|
|
||||||
|
aaa(a);
|
||||||
|
|
||||||
|
System.out.println(a);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void aaa(Integer a) {
|
||||||
|
++a;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testString() {
|
||||||
|
|
||||||
|
String str = "cpu[].usage.system";
|
||||||
|
|
||||||
|
int idx = str.indexOf("[");
|
||||||
|
int arrIdx = 0;
|
||||||
|
|
||||||
|
String ret = "";
|
||||||
|
ret += str.substring(0, idx +1);
|
||||||
|
ret += String.valueOf(arrIdx);
|
||||||
|
ret += str.substring(idx +1);
|
||||||
|
|
||||||
|
System.out.println(ret);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
51
src/test/resources/config/example.json
Normal file
51
src/test/resources/config/example.json
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
{
|
||||||
|
"id" : "SOEJWEOJWOEJOSDJFOASDJFOSDFO2903870928734",
|
||||||
|
"target" : {
|
||||||
|
"connection" : {
|
||||||
|
"ip" : "192.168.1.1",
|
||||||
|
"port" : "135",
|
||||||
|
"ssl" : false,
|
||||||
|
"portType" : "tcp"
|
||||||
|
},
|
||||||
|
"auth" : {
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schedule" : {
|
||||||
|
"interval" : "10"
|
||||||
|
},
|
||||||
|
"crawler" : {
|
||||||
|
"name":"redis_protocol_crawler",
|
||||||
|
"container":"network_crawler"
|
||||||
|
},
|
||||||
|
"items" : [
|
||||||
|
{
|
||||||
|
"metrics": [
|
||||||
|
"cpu.usage.system",
|
||||||
|
"cpu.usage.idle",
|
||||||
|
"cpu.usage.user"
|
||||||
|
],
|
||||||
|
"queries": [
|
||||||
|
{
|
||||||
|
"query":"select PercentProcessorTime, PercentIdleTime, PercentUserTime from Win32_PerfFormattedData_PerfOS_Processor where Name='_Total'",
|
||||||
|
"keys":["PercentProcessorTime","PercentIdleTime", "PercentUserTime"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metrics": [
|
||||||
|
"cpu[].usage.system",
|
||||||
|
"cpu[].usage.idle",
|
||||||
|
"cpu[].usage.user"
|
||||||
|
],
|
||||||
|
"queries": [
|
||||||
|
{
|
||||||
|
"query":"select PercentProcessorTime, PercentIdleTime, PercentUserTime from Win32_PerfFormattedData_PerfOS_Processor where Name!='_Total'",
|
||||||
|
"keys":["PercentProcessorTime","PercentIdleTime", "PercentUserTime"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user