extract some utils for reuse
This commit is contained in:
parent
a01bf98dd0
commit
7db35557ab
32
util/pom.xml
Normal file
32
util/pom.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>de.hsadmin.core</groupId>
|
||||
<artifactId>hsadmin-util</artifactId>
|
||||
<version>4.0.4-SNAPSHOT</version>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
58
util/src/main/java/de/hsadmin/core/util/Config.java
Normal file
58
util/src/main/java/de/hsadmin/core/util/Config.java
Normal file
@ -0,0 +1,58 @@
|
||||
package de.hsadmin.core.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class Config {
|
||||
|
||||
private static Config instance;
|
||||
|
||||
private static Logger LOG = Logger.getLogger(Config.class.getName());
|
||||
|
||||
private Properties props;
|
||||
|
||||
private Config() {
|
||||
props = new Properties();
|
||||
File file = new File(System.getProperty("user.dir") + "/hsadmin.properties");
|
||||
if (!file.canRead()) {
|
||||
file = new File(System.getProperty("user.dir") + "/conf/hsadmin.properties");
|
||||
}
|
||||
if (!file.canRead()) {
|
||||
file = new File(System.getProperty("user.home") + "/.hsadmin.properties");
|
||||
}
|
||||
if (!file.canRead()) {
|
||||
file = new File("/etc/hsadmin.properties");
|
||||
}
|
||||
if (!file.canRead()) {
|
||||
file = new File("/etc/hsadmin/hsadmin.properties");
|
||||
}
|
||||
if (file.canRead()) {
|
||||
try {
|
||||
LOG.info("Constructor - Properties-File: " + file.getAbsolutePath());
|
||||
props.load(new FileReader(file));
|
||||
} catch (Exception e) {
|
||||
// should not happen
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Config getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new Config();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public String getProperty(String propertyName) {
|
||||
String property = props.getProperty(propertyName);
|
||||
return property;
|
||||
}
|
||||
|
||||
public String getProperty(String propertyName, String defaultValue) {
|
||||
return props.getProperty(propertyName, defaultValue).trim();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package de.hsadmin.core.util;
|
||||
|
||||
public class HSAdminException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = -5082179267383474532L;
|
||||
|
||||
public HSAdminException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public HSAdminException(Exception e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
public HSAdminException(String message, Exception aExc) {
|
||||
super(message, aExc);
|
||||
}
|
||||
|
||||
}
|
86
util/src/main/java/de/hsadmin/core/util/IPv6Trick.java
Normal file
86
util/src/main/java/de/hsadmin/core/util/IPv6Trick.java
Normal file
@ -0,0 +1,86 @@
|
||||
package de.hsadmin.core.util;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
|
||||
public class IPv6Trick {
|
||||
|
||||
private static final String IPv4_LOCALHOST = "127.0.0";
|
||||
private static final String IPv6_LOCALHOST = "::1";
|
||||
private static final String IPv6_LOCALHOST_ALT = "0:0:0:0:0:0:0:1";
|
||||
|
||||
private static final String IPv4_83_223_79 = "83.223.79"; // Alboin-Kontor Berlin
|
||||
private static final String IPv4_83_223_91 = "83.223.91"; // Alboin-Kontor Berlin
|
||||
private static final String IPv4_83_223_95 = "83.223.95"; // Alboin-Kontor Berlin
|
||||
private static final String IPv6_PREFIX_AK = "2a01:37:1000::1";
|
||||
|
||||
private static final String IPv4_83_223_78 = "83.223.78"; // e-Shelter Berlin
|
||||
private static final String IPv4_83_223_94 = "83.223.94"; // e-Shelter Berlin
|
||||
private static final String IPv6_PREFIX_ES = "2a01:37:3000::1";
|
||||
|
||||
private static final String IPv6_PREFIX_HS = "2a01:37:";
|
||||
private static final String IPv6_PREFIX_HS_ALT = "2a01:0037:";
|
||||
|
||||
public static String convertIPv4ToIPv6(final String ipv4address) throws HSAdminException {
|
||||
if (ipv4address == null || ipv4address.length() == 0) {
|
||||
throw new HSAdminException("no IPv4 address given");
|
||||
}
|
||||
try {
|
||||
InetAddress inetV4Address = InetAddress.getByName(ipv4address);
|
||||
String inetV4AddressString = inetV4Address.getHostAddress();
|
||||
if (inetV4AddressString.startsWith(IPv4_83_223_79) // Alboin-Kontor Berlin
|
||||
|| inetV4AddressString.startsWith(IPv4_83_223_91)
|
||||
|| inetV4AddressString.startsWith(IPv4_83_223_95) ) {
|
||||
return embedIPv4Address(inetV4Address, InetAddress.getByName(IPv6_PREFIX_AK));
|
||||
}
|
||||
if (inetV4AddressString.startsWith(IPv4_83_223_78) // e-Shelter Berlin
|
||||
|| inetV4AddressString.startsWith(IPv4_83_223_94) ) {
|
||||
return embedIPv4Address(inetV4Address, InetAddress.getByName(IPv6_PREFIX_ES));
|
||||
}
|
||||
} catch (UnknownHostException e) {
|
||||
throw new HSAdminException(e);
|
||||
}
|
||||
throw new HSAdminException("unknown IPv4 address given");
|
||||
}
|
||||
|
||||
public static boolean isKnownRemote(final String remoteAddress) {
|
||||
boolean isKnown = false;
|
||||
if (remoteAddress.startsWith(IPv4_LOCALHOST) || remoteAddress.startsWith(IPv6_LOCALHOST) || remoteAddress.startsWith(IPv6_LOCALHOST_ALT)) {
|
||||
// localhost
|
||||
isKnown = true;
|
||||
}
|
||||
if (remoteAddress.startsWith(IPv6_PREFIX_HS) || remoteAddress.startsWith(IPv6_PREFIX_HS_ALT)) {
|
||||
// Hostsharing IPv6
|
||||
isKnown = true;
|
||||
}
|
||||
if (remoteAddress.startsWith(IPv4_83_223_78) || remoteAddress.startsWith(IPv4_83_223_94)) {
|
||||
// e-Shelter
|
||||
isKnown = true;
|
||||
}
|
||||
if (remoteAddress.startsWith(IPv4_83_223_79) || remoteAddress.startsWith(IPv4_83_223_91) || remoteAddress.startsWith(IPv4_83_223_95)) {
|
||||
// Speedbone Alboin Kontor
|
||||
isKnown = true;
|
||||
}
|
||||
return isKnown;
|
||||
}
|
||||
|
||||
private static String embedIPv4Address(final InetAddress ipv4address, final InetAddress ipv6Mask) throws UnknownHostException {
|
||||
final byte[] ipv4Bytes = ipv4address.getAddress();
|
||||
final byte[] ipv6Bytes = ipv6Mask.getAddress();
|
||||
ipv6Bytes[10] = ipv4Bytes[0];
|
||||
ipv6Bytes[11] = ipv4Bytes[1];
|
||||
ipv6Bytes[12] = ipv4Bytes[2];
|
||||
ipv6Bytes[13] = ipv4Bytes[3];
|
||||
ipv6Bytes[14] = 0;
|
||||
ipv6Bytes[15] = 0;
|
||||
final InetAddress ipv6Address = InetAddress.getByAddress(ipv6Bytes);
|
||||
final String ipv6String = ipv6Address.getHostAddress();
|
||||
if (ipv6String.startsWith("/")) {
|
||||
return ipv6String.substring(1);
|
||||
} else {
|
||||
return ipv6String;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
147
util/src/main/java/de/hsadmin/core/util/PasswordGenerator.java
Normal file
147
util/src/main/java/de/hsadmin/core/util/PasswordGenerator.java
Normal file
@ -0,0 +1,147 @@
|
||||
package de.hsadmin.core.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public final class PasswordGenerator {
|
||||
|
||||
private static final String LOWER = "abcdefghijklmnopqrstuvwxyz";
|
||||
private static final String UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
private static final String DIGITS = "0123456789";
|
||||
private static final String PUNCTUATION = "!@#$%&*()_+-=[]|,./?><";
|
||||
private boolean useLower;
|
||||
private boolean useUpper;
|
||||
private boolean useDigits;
|
||||
private boolean usePunctuation;
|
||||
|
||||
private PasswordGenerator() {
|
||||
throw new UnsupportedOperationException("Empty constructor is not supported.");
|
||||
}
|
||||
|
||||
private PasswordGenerator(PasswordGeneratorBuilder builder) {
|
||||
this.useLower = builder.useLower;
|
||||
this.useUpper = builder.useUpper;
|
||||
this.useDigits = builder.useDigits;
|
||||
this.usePunctuation = builder.usePunctuation;
|
||||
}
|
||||
|
||||
public static class PasswordGeneratorBuilder {
|
||||
|
||||
private boolean useLower;
|
||||
private boolean useUpper;
|
||||
private boolean useDigits;
|
||||
private boolean usePunctuation;
|
||||
|
||||
public PasswordGeneratorBuilder() {
|
||||
this.useLower = false;
|
||||
this.useUpper = false;
|
||||
this.useDigits = false;
|
||||
this.usePunctuation = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set true in case you would like to include lower characters (abc...xyz).
|
||||
* Default false.
|
||||
*
|
||||
* @param useLower true in case you would like to include lower characters
|
||||
* (abc...xyz). Default false.
|
||||
* @return the builder for chaining.
|
||||
*/
|
||||
public PasswordGeneratorBuilder useLower(boolean useLower) {
|
||||
this.useLower = useLower;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set true in case you would like to include upper characters (ABC...XYZ).
|
||||
* Default false.
|
||||
*
|
||||
* @param useUpper true in case you would like to include upper characters
|
||||
* (ABC...XYZ). Default false.
|
||||
* @return the builder for chaining.
|
||||
*/
|
||||
public PasswordGeneratorBuilder useUpper(boolean useUpper) {
|
||||
this.useUpper = useUpper;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set true in case you would like to include digit characters (123..). Default
|
||||
* false.
|
||||
*
|
||||
* @param useDigits true in case you would like to include digit characters
|
||||
* (123..). Default false.
|
||||
* @return the builder for chaining.
|
||||
*/
|
||||
public PasswordGeneratorBuilder useDigits(boolean useDigits) {
|
||||
this.useDigits = useDigits;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set true in case you would like to include punctuation characters (!@#..).
|
||||
* Default false.
|
||||
*
|
||||
* @param usePunctuation true in case you would like to include punctuation
|
||||
* characters (!@#..). Default false.
|
||||
* @return the builder for chaining.
|
||||
*/
|
||||
public PasswordGeneratorBuilder usePunctuation(boolean usePunctuation) {
|
||||
this.usePunctuation = usePunctuation;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object to use.
|
||||
*
|
||||
* @return the {@link gr.idrymavmela.business.lib.PasswordGenerator} object.
|
||||
*/
|
||||
public PasswordGenerator build() {
|
||||
return new PasswordGenerator(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will generate a password depending the use* properties you
|
||||
* define. It will use the categories with a probability. It is not sure that
|
||||
* all of the defined categories will be used.
|
||||
*
|
||||
* @param length the length of the password you would like to generate.
|
||||
* @return a password that uses the categories you define when constructing the
|
||||
* object with a probability.
|
||||
*/
|
||||
public String generate(int length) {
|
||||
// Argument Validation.
|
||||
if (length <= 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Variables.
|
||||
StringBuilder password = new StringBuilder(length);
|
||||
Random random = new Random(System.nanoTime());
|
||||
|
||||
// Collect the categories to use.
|
||||
List<String> charCategories = new ArrayList<>(4);
|
||||
if (useLower) {
|
||||
charCategories.add(LOWER);
|
||||
}
|
||||
if (useUpper) {
|
||||
charCategories.add(UPPER);
|
||||
}
|
||||
if (useDigits) {
|
||||
charCategories.add(DIGITS);
|
||||
}
|
||||
if (usePunctuation) {
|
||||
charCategories.add(PUNCTUATION);
|
||||
}
|
||||
|
||||
// Build the password.
|
||||
for (int i = 0; i < length; i++) {
|
||||
String charCategory = charCategories.get(random.nextInt(charCategories.size()));
|
||||
int position = random.nextInt(charCategory.length());
|
||||
password.append(charCategory.charAt(position));
|
||||
}
|
||||
return new String(password);
|
||||
}
|
||||
}
|
44
util/src/main/java/de/hsadmin/core/util/PasswordTool.java
Normal file
44
util/src/main/java/de/hsadmin/core/util/PasswordTool.java
Normal file
@ -0,0 +1,44 @@
|
||||
package de.hsadmin.core.util;
|
||||
|
||||
public final class PasswordTool {
|
||||
|
||||
public static final int MIN_COMPLEXITY = 3;
|
||||
public static final int MIN_PASSWORD_LENGTH = 6;
|
||||
|
||||
private static final PasswordGenerator passwordGenerator = new PasswordGenerator.PasswordGeneratorBuilder()
|
||||
.useDigits(true)
|
||||
.useLower(true)
|
||||
.useUpper(true)
|
||||
.build();
|
||||
|
||||
public static String generatePassword() {
|
||||
return passwordGenerator.generate(14);
|
||||
}
|
||||
|
||||
public static void checkPasswordComplexity(final String password) throws HSAdminException {
|
||||
int containsLowercaseCharacter = 0;
|
||||
int containsUppercaseCharacter = 0;
|
||||
int containsSpecialCharacter = 0;
|
||||
int containsDigit = 0;
|
||||
for (int idx = 0; idx < password.length(); idx++) {
|
||||
char chr = password.charAt(idx);
|
||||
if (chr >= 'a' && chr <= 'z') {
|
||||
containsLowercaseCharacter = 1;
|
||||
} else
|
||||
if (chr >= 'A' && chr <= 'Z') {
|
||||
containsUppercaseCharacter = 1;
|
||||
} else
|
||||
if (chr >= '0' && chr <= '1') {
|
||||
containsDigit = 1;
|
||||
} else {
|
||||
containsSpecialCharacter = 1;
|
||||
}
|
||||
}
|
||||
int complexity = containsLowercaseCharacter + containsUppercaseCharacter
|
||||
+ containsSpecialCharacter + containsDigit;
|
||||
if (complexity < MIN_COMPLEXITY || password.length() < MIN_PASSWORD_LENGTH) {
|
||||
throw new HSAdminException("simple password");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
87
util/src/main/java/de/hsadmin/core/util/TextUtil.java
Normal file
87
util/src/main/java/de/hsadmin/core/util/TextUtil.java
Normal file
@ -0,0 +1,87 @@
|
||||
package de.hsadmin.core.util;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public class TextUtil {
|
||||
|
||||
private static final DateFormat df = SimpleDateFormat.getDateInstance(DateFormat.SHORT, Locale.GERMAN);
|
||||
|
||||
public static synchronized String replaceUmlautCharacters(String umlautString) {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
for (char c : umlautString.toCharArray()) {
|
||||
if ( (c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
(c == ' ') ) {
|
||||
buffer.append(c);
|
||||
} else {
|
||||
switch (c) {
|
||||
case 'ä':
|
||||
buffer.append("ae");
|
||||
break;
|
||||
case 'ö':
|
||||
buffer.append("oe");
|
||||
break;
|
||||
case 'ü':
|
||||
buffer.append("ue");
|
||||
break;
|
||||
case 'Ä':
|
||||
buffer.append("Ae");
|
||||
break;
|
||||
case 'Ö':
|
||||
buffer.append("Oe");
|
||||
break;
|
||||
case 'Ü':
|
||||
buffer.append("Ue");
|
||||
break;
|
||||
case 'ß':
|
||||
buffer.append("ss");
|
||||
break;
|
||||
default:
|
||||
buffer.append('-');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public static synchronized String format(Date date) {
|
||||
return df.format(date);
|
||||
}
|
||||
|
||||
public static synchronized Date parseDate(String dateString) {
|
||||
try {
|
||||
return df.parse(dateString);
|
||||
} catch (ParseException e) {
|
||||
try {
|
||||
return df.parse("01.01.1970");
|
||||
} catch (ParseException e1) {
|
||||
// don't care
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized String format(boolean free) {
|
||||
return free ? "true" : "false";
|
||||
}
|
||||
|
||||
public static synchronized boolean parseBool(String boolString) {
|
||||
boolean parsedValue = "T".equals(boolString.toUpperCase()) || "TRUE".equals(boolString.toUpperCase());
|
||||
return parsedValue;
|
||||
}
|
||||
|
||||
public static synchronized String hidePassword(String passwd) {
|
||||
final StringBuffer val = new StringBuffer(passwd.substring(0, 2));
|
||||
for (int i = 2; i < 6; i++) {
|
||||
val.append('*');
|
||||
}
|
||||
return val.toString();
|
||||
}
|
||||
|
||||
}
|
39
util/src/test/java/de/hsadmin/core/util/IPv6TrickTest.java
Normal file
39
util/src/test/java/de/hsadmin/core/util/IPv6TrickTest.java
Normal file
@ -0,0 +1,39 @@
|
||||
package de.hsadmin.core.util;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
public class IPv6TrickTest {
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertIPv4ToIPv6() {
|
||||
try {
|
||||
assertTrue(IPv6Trick.convertIPv4ToIPv6("83.223.78.2").startsWith("2a01:37:"));
|
||||
assertTrue(IPv6Trick.convertIPv4ToIPv6("83.223.79.22").startsWith("2a01:37:"));
|
||||
assertTrue(IPv6Trick.convertIPv4ToIPv6("83.223.91.222").startsWith("2a01:37:"));
|
||||
assertTrue(IPv6Trick.convertIPv4ToIPv6("83.223.94.2").startsWith("2a01:37:"));
|
||||
assertTrue(IPv6Trick.convertIPv4ToIPv6("83.223.95.2").startsWith("2a01:37:"));
|
||||
} catch (HSAdminException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
try {
|
||||
IPv6Trick.convertIPv4ToIPv6("4.4.4.4");
|
||||
fail("unknown adress");
|
||||
} catch (HSAdminException e) {
|
||||
// Ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsKnownRemote() {
|
||||
assertTrue(IPv6Trick.isKnownRemote("83.223.78.2"));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package de.hsadmin.core.util;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PasswordToolTest {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckPasswordComplexity() {
|
||||
String pw1 = "TTtt1";
|
||||
String pw2 = "TTtt11";
|
||||
String pw3 = "TTT12_";
|
||||
String pw4 = "Testt1";
|
||||
String pw5 = "Tthdsuhdalkdj";
|
||||
String pw6 = "zaehler42";
|
||||
try {
|
||||
PasswordTool.checkPasswordComplexity(pw1);
|
||||
fail("should fail");
|
||||
} catch (HSAdminException e) {
|
||||
assertTrue("simple password".equals(e.getMessage()));
|
||||
}
|
||||
try {
|
||||
PasswordTool.checkPasswordComplexity(pw2);
|
||||
} catch (HSAdminException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
try {
|
||||
PasswordTool.checkPasswordComplexity(pw3);
|
||||
} catch (HSAdminException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
try {
|
||||
PasswordTool.checkPasswordComplexity(pw4);
|
||||
} catch (HSAdminException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
try {
|
||||
PasswordTool.checkPasswordComplexity(pw5);
|
||||
fail("should fail");
|
||||
} catch (HSAdminException e) {
|
||||
assertTrue("simple password".equals(e.getMessage()));
|
||||
}
|
||||
try {
|
||||
PasswordTool.checkPasswordComplexity(pw6);
|
||||
fail("should fail");
|
||||
} catch (HSAdminException e) {
|
||||
assertTrue("simple password".equals(e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user