mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-12-27 09:00:28 +08:00
Linked web users to players or console
PageExtension API: - Added WebUser#getUsername - Changed WebUser#getName to return player's name or 'console' - API version 5.1-R0.4
This commit is contained in:
parent
2f282dc7cc
commit
ea2ae5d3e1
@ -7,7 +7,7 @@ dependencies {
|
||||
compileOnly "com.google.code.gson:gson:$gsonVersion"
|
||||
}
|
||||
|
||||
ext.apiVersion = '5.1-R0.3'
|
||||
ext.apiVersion = '5.1-R0.4'
|
||||
|
||||
bintray {
|
||||
user = System.getenv('BINTRAY_USER')
|
||||
|
@ -17,21 +17,34 @@
|
||||
package com.djrapitops.plan.delivery.web.resolver.request;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public final class WebUser {
|
||||
|
||||
private final String name;
|
||||
private final String playerName;
|
||||
private final String username;
|
||||
private final Set<String> permissions;
|
||||
|
||||
public WebUser(String name) {
|
||||
this.name = name;
|
||||
public WebUser(String playerName) {
|
||||
this.playerName = playerName;
|
||||
this.username = playerName;
|
||||
this.permissions = new HashSet<>();
|
||||
}
|
||||
|
||||
public WebUser(String name, String... permissions) {
|
||||
this(name);
|
||||
public WebUser(String playerName, String username, Collection<String> permissions) {
|
||||
this.playerName = playerName;
|
||||
this.username = username;
|
||||
this.permissions = new HashSet<>(permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated WebUser now has username and player name.
|
||||
*/
|
||||
@Deprecated
|
||||
public WebUser(String playerName, String... permissions) {
|
||||
this(playerName);
|
||||
this.permissions.addAll(Arrays.asList(permissions));
|
||||
}
|
||||
|
||||
@ -40,6 +53,10 @@ public final class WebUser {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
return playerName;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.commands;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Utility for managing command arguments.
|
||||
*
|
||||
* @author Rsl1122
|
||||
*/
|
||||
public class Arguments {
|
||||
|
||||
private final List<String> args;
|
||||
|
||||
public Arguments(String[] args) {
|
||||
this.args = Arrays.asList(args);
|
||||
}
|
||||
|
||||
public Arguments(List<String> args) {
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public Optional<String> get(int index) {
|
||||
return index < args.size() ? Optional.of(args.get(index)) : Optional.empty();
|
||||
}
|
||||
|
||||
public Optional<Integer> getInteger(int index) {
|
||||
return get(index).map(Integer::parseInt);
|
||||
}
|
||||
|
||||
public Optional<String> getAfter(String argumentIdentifier) {
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
String argument = args.get(i);
|
||||
if (argumentIdentifier.equals(argument)) {
|
||||
return get(i + 1);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public boolean contains(String argument) {
|
||||
return args.contains(argument);
|
||||
}
|
||||
|
||||
public List<String> asList() {
|
||||
return args;
|
||||
}
|
||||
}
|
@ -16,11 +16,13 @@
|
||||
*/
|
||||
package com.djrapitops.plan.commands.subcommands;
|
||||
|
||||
import com.djrapitops.plan.delivery.domain.WebUser;
|
||||
import com.djrapitops.plan.commands.Arguments;
|
||||
import com.djrapitops.plan.delivery.domain.auth.User;
|
||||
import com.djrapitops.plan.delivery.webserver.Addresses;
|
||||
import com.djrapitops.plan.delivery.webserver.auth.FailReason;
|
||||
import com.djrapitops.plan.delivery.webserver.auth.RegistrationBin;
|
||||
import com.djrapitops.plan.exceptions.database.DBOpException;
|
||||
import com.djrapitops.plan.identification.UUIDUtility;
|
||||
import com.djrapitops.plan.processing.Processing;
|
||||
import com.djrapitops.plan.settings.Permissions;
|
||||
import com.djrapitops.plan.settings.locale.Locale;
|
||||
@ -39,13 +41,13 @@ import com.djrapitops.plugin.command.Sender;
|
||||
import com.djrapitops.plugin.logging.L;
|
||||
import com.djrapitops.plugin.logging.console.PluginLogger;
|
||||
import com.djrapitops.plugin.logging.error.ErrorHandler;
|
||||
import com.djrapitops.plugin.utilities.Verify;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
@ -65,6 +67,7 @@ public class RegisterCommand extends CommandNode {
|
||||
private final Locale locale;
|
||||
private final Processing processing;
|
||||
private final DBSystem dbSystem;
|
||||
private final UUIDUtility uuidUtility;
|
||||
private final Addresses addresses;
|
||||
private final PluginLogger logger;
|
||||
private final ErrorHandler errorHandler;
|
||||
@ -75,15 +78,17 @@ public class RegisterCommand extends CommandNode {
|
||||
Processing processing,
|
||||
Addresses addresses,
|
||||
DBSystem dbSystem,
|
||||
UUIDUtility uuidUtility,
|
||||
PluginLogger logger,
|
||||
ErrorHandler errorHandler
|
||||
) {
|
||||
// No Permission Requirement
|
||||
super("register", "", CommandType.PLAYER_OR_ARGS);
|
||||
super("register", "", CommandType.ALL);
|
||||
|
||||
this.locale = locale;
|
||||
this.processing = processing;
|
||||
this.addresses = addresses;
|
||||
this.uuidUtility = uuidUtility;
|
||||
this.logger = logger;
|
||||
this.dbSystem = dbSystem;
|
||||
this.errorHandler = errorHandler;
|
||||
@ -104,27 +109,23 @@ public class RegisterCommand extends CommandNode {
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.length < 1) {
|
||||
throw new IllegalArgumentException(locale.getString(CommandLang.FAIL_REQ_ARGS, 1, Arrays.toString(getArguments())));
|
||||
if (args.length == 0) {
|
||||
String url = addresses.getMainAddress().orElseGet(() -> {
|
||||
sender.sendMessage(locale.getString(CommandLang.NO_ADDRESS_NOTIFY));
|
||||
return addresses.getFallbackLocalhostAddress();
|
||||
}) + "/register";
|
||||
String linkPrefix = locale.getString(CommandLang.LINK_PREFIX);
|
||||
sender.sendMessage(linkPrefix);
|
||||
sender.sendLink(" ", locale.getString(CommandLang.LINK_CLICK_ME), url);
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> argumentList = Arrays.asList(args);
|
||||
boolean newRegister = argumentList.contains("--code");
|
||||
if (newRegister) {
|
||||
if (CommandUtils.isPlayer(sender)) {
|
||||
register(argumentList, getPermissionLevel(sender), sender);
|
||||
} else if (argumentList.contains("superuser")) {
|
||||
register(argumentList, 0, sender);
|
||||
} else {
|
||||
sender.sendMessage("§cInvalid arguments.");
|
||||
}
|
||||
Arguments arguments = new Arguments(args);
|
||||
Optional<String> code = arguments.getAfter("--code");
|
||||
if (code.isPresent()) {
|
||||
registerUsingCode(sender, code.get());
|
||||
} else {
|
||||
// Legacy support
|
||||
if (CommandUtils.isPlayer(sender)) {
|
||||
playerRegister(args, sender);
|
||||
} else {
|
||||
consoleRegister(args, sender, notEnoughArgsMsg);
|
||||
}
|
||||
registerUsingLegacy(sender, arguments);
|
||||
}
|
||||
} catch (PassEncryptUtil.CannotPerformOperationException e) {
|
||||
errorHandler.log(L.WARN, this.getClass(), e);
|
||||
@ -138,43 +139,33 @@ public class RegisterCommand extends CommandNode {
|
||||
}
|
||||
}
|
||||
|
||||
private void consoleRegister(String[] args, Sender sender, String notEnoughArgsMsg) throws PassEncryptUtil.CannotPerformOperationException {
|
||||
Verify.isTrue(args.length >= 3, () -> new IllegalArgumentException(notEnoughArgsMsg));
|
||||
|
||||
String userName = args[1];
|
||||
Verify.isTrue(userName.length() <= 100, () -> new IllegalArgumentException("Username can only be 100 characters long."));
|
||||
int permLevel = Integer.parseInt(args[2]);
|
||||
String passHash = PassEncryptUtil.createHash(args[0]);
|
||||
registerUser(new WebUser(userName, passHash, permLevel), sender);
|
||||
}
|
||||
|
||||
private void register(List<String> args, int permissionLevel, Sender sender) {
|
||||
String code = "";
|
||||
for (String arg : args) {
|
||||
if (arg.length() == 12) code = arg;
|
||||
}
|
||||
if (code.isEmpty()) {
|
||||
throw new IllegalArgumentException(locale.getString(CommandLang.FAIL_REQ_ARGS, 1, "--code !<code>!"));
|
||||
}
|
||||
Optional<WebUser> user = RegistrationBin.register(code, permissionLevel);
|
||||
if (!user.isPresent()) {
|
||||
public void registerUsingCode(Sender sender, String code) {
|
||||
UUID linkedToUUID = CommandUtils.isPlayer(sender) ? uuidUtility.getUUIDOf(sender.getName()) : null;
|
||||
Optional<User> user = RegistrationBin.register(code, linkedToUUID);
|
||||
if (user.isPresent()) {
|
||||
registerUser(user.get(), sender, getPermissionLevel(sender));
|
||||
} else {
|
||||
sender.sendMessage("§c" + locale.getString(FailReason.USER_DOES_NOT_EXIST));
|
||||
} else {
|
||||
registerUser(user.get(), sender);
|
||||
}
|
||||
}
|
||||
|
||||
private void playerRegister(String[] args, Sender sender) throws PassEncryptUtil.CannotPerformOperationException {
|
||||
boolean registerSenderAsUser = args.length == 1;
|
||||
if (registerSenderAsUser) {
|
||||
String user = sender.getName();
|
||||
String pass = PassEncryptUtil.createHash(args[0]);
|
||||
int permLvl = getPermissionLevel(sender);
|
||||
registerUser(new WebUser(user, pass, permLvl), sender);
|
||||
} else if (sender.hasPermission(Permissions.MANAGE_WEB.getPermission())) {
|
||||
consoleRegister(args, sender, notEnoughArgsMsg);
|
||||
public void registerUsingLegacy(Sender sender, Arguments arguments) {
|
||||
String password = arguments.get(0)
|
||||
.orElseThrow(() -> new IllegalArgumentException(locale.getString(CommandLang.FAIL_REQ_ARGS, 1, Arrays.toString(getArguments()))));
|
||||
String passwordHash = PassEncryptUtil.createHash(password);
|
||||
int permissionLevel = arguments.getInteger(2)
|
||||
.filter(arg -> sender.hasPermission(Permissions.MANAGE_WEB.getPerm())) // argument only allowed with plan.webmanage
|
||||
.orElseGet(() -> getPermissionLevel(sender));
|
||||
|
||||
if (CommandUtils.isPlayer(sender)) {
|
||||
String playerName = sender.getName();
|
||||
UUID linkedToUUID = uuidUtility.getUUIDOf(playerName);
|
||||
String username = arguments.get(1).orElse(playerName);
|
||||
registerUser(new User(username, playerName, linkedToUUID, passwordHash, Collections.emptyList()), sender, permissionLevel);
|
||||
} else {
|
||||
sender.sendMessage(locale.getString(CommandLang.FAIL_NO_PERMISSION));
|
||||
String username = arguments.get(1)
|
||||
.orElseThrow(() -> new IllegalArgumentException(notEnoughArgsMsg));
|
||||
registerUser(new User(username, "console", null, passwordHash, Collections.emptyList()), sender, permissionLevel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,22 +185,22 @@ public class RegisterCommand extends CommandNode {
|
||||
return 100;
|
||||
}
|
||||
|
||||
private void registerUser(WebUser webUser, Sender sender) {
|
||||
private void registerUser(User user, Sender sender, int permissionLevel) {
|
||||
processing.submitCritical(() -> {
|
||||
String userName = webUser.getName();
|
||||
String username = user.getUsername();
|
||||
try {
|
||||
Database database = dbSystem.getDatabase();
|
||||
boolean userExists = database.query(WebUserQueries.fetchWebUser(userName)).isPresent();
|
||||
boolean userExists = database.query(WebUserQueries.fetchUser(username)).isPresent();
|
||||
if (userExists) {
|
||||
sender.sendMessage(locale.getString(CommandLang.FAIL_WEB_USER_EXISTS));
|
||||
return;
|
||||
}
|
||||
database.executeTransaction(new RegisterWebUserTransaction(webUser))
|
||||
database.executeTransaction(new RegisterWebUserTransaction(user, permissionLevel))
|
||||
.get(); // Wait for completion
|
||||
|
||||
sender.sendMessage(locale.getString(CommandLang.WEB_USER_REGISTER_SUCCESS, userName));
|
||||
sender.sendMessage(locale.getString(CommandLang.WEB_USER_REGISTER_SUCCESS, username));
|
||||
sendLink(sender);
|
||||
logger.info(locale.getString(CommandLang.WEB_USER_REGISTER_NOTIFY, userName, webUser.getPermLevel()));
|
||||
logger.info(locale.getString(CommandLang.WEB_USER_REGISTER_NOTIFY, username, permissionLevel));
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (DBOpException | ExecutionException e) {
|
||||
|
@ -30,18 +30,32 @@ import java.util.Objects;
|
||||
@Deprecated
|
||||
public class WebUser {
|
||||
|
||||
private final String user;
|
||||
private final String username;
|
||||
private final String saltedPassHash;
|
||||
private final int permLevel;
|
||||
|
||||
public WebUser(String user, String saltedPassHash, int permLevel) {
|
||||
this.user = user;
|
||||
public WebUser(String username, String saltedPassHash, int permLevel) {
|
||||
this.username = username;
|
||||
this.saltedPassHash = saltedPassHash;
|
||||
this.permLevel = permLevel;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return user;
|
||||
public static List<String> getPermissionsForLevel(int level) {
|
||||
List<String> permissions = new ArrayList<>();
|
||||
if (level <= 0) {
|
||||
permissions.add("page.network");
|
||||
permissions.add("page.server");
|
||||
permissions.add("page.debug");
|
||||
// TODO Add JSON Permissions
|
||||
}
|
||||
if (level <= 1) {
|
||||
permissions.add("page.players");
|
||||
permissions.add("page.player.other");
|
||||
}
|
||||
if (level <= 2) {
|
||||
permissions.add("page.player.self");
|
||||
}
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public String getSaltedPassHash() {
|
||||
@ -52,38 +66,28 @@ public class WebUser {
|
||||
return permLevel;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
WebUser webUser = (WebUser) o;
|
||||
return permLevel == webUser.permLevel &&
|
||||
Objects.equals(user, webUser.user) &&
|
||||
Objects.equals(username, webUser.username) &&
|
||||
Objects.equals(saltedPassHash, webUser.saltedPassHash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(user, saltedPassHash, permLevel);
|
||||
return Objects.hash(username, saltedPassHash, permLevel);
|
||||
}
|
||||
|
||||
public com.djrapitops.plan.delivery.web.resolver.request.WebUser toNewWebUser() {
|
||||
List<String> permissions = new ArrayList<>();
|
||||
if (permLevel <= 0) {
|
||||
permissions.add("page.network");
|
||||
permissions.add("page.server");
|
||||
permissions.add("page.debug");
|
||||
// TODO Add JSON Permissions
|
||||
}
|
||||
if (permLevel <= 1) {
|
||||
permissions.add("page.players");
|
||||
permissions.add("page.player.other");
|
||||
}
|
||||
if (permLevel <= 2) {
|
||||
permissions.add("page.player.self");
|
||||
}
|
||||
return new com.djrapitops.plan.delivery.web.resolver.request.WebUser(
|
||||
user, permissions.toArray(new String[0])
|
||||
username, getPermissionsForLevel(permLevel).toArray(new String[0])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.delivery.domain.auth;
|
||||
|
||||
import com.djrapitops.plan.delivery.web.resolver.request.WebUser;
|
||||
import com.djrapitops.plan.utilities.PassEncryptUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents a registered user in the database.
|
||||
*
|
||||
* @author Rsl1122
|
||||
*/
|
||||
public class User {
|
||||
|
||||
private final String username;
|
||||
private final String linkedTo;
|
||||
private final UUID linkedToUUID; // null for 'console'
|
||||
private final String passwordHash;
|
||||
private final Collection<String> permissions;
|
||||
|
||||
public User(String username, String linkedTo, UUID linkedToUUID, String passwordHash, Collection<String> permissions) {
|
||||
this.username = username;
|
||||
this.linkedTo = linkedTo;
|
||||
this.linkedToUUID = linkedToUUID;
|
||||
this.passwordHash = passwordHash;
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
public boolean doesPasswordMatch(String password) {
|
||||
return PassEncryptUtil.verifyPassword(password, passwordHash);
|
||||
}
|
||||
|
||||
public WebUser toWebUser() {
|
||||
return new WebUser(linkedTo, username, permissions);
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getLinkedTo() {
|
||||
return linkedTo;
|
||||
}
|
||||
|
||||
public UUID getLinkedToUUID() {
|
||||
return linkedToUUID;
|
||||
}
|
||||
|
||||
public String getPasswordHash() {
|
||||
return passwordHash;
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
*/
|
||||
package com.djrapitops.plan.delivery.webserver;
|
||||
|
||||
import com.djrapitops.plan.delivery.domain.auth.User;
|
||||
import com.djrapitops.plan.delivery.web.resolver.Response;
|
||||
import com.djrapitops.plan.delivery.web.resolver.request.Request;
|
||||
import com.djrapitops.plan.delivery.web.resolver.request.URIPath;
|
||||
@ -153,8 +154,8 @@ public class RequestHandler implements HttpHandler {
|
||||
|
||||
private WebUser getWebUser(HttpExchange exchange) {
|
||||
return getAuthentication(exchange.getRequestHeaders())
|
||||
.map(Authentication::getWebUser) // Can throw WebUserAuthException
|
||||
.map(com.djrapitops.plan.delivery.domain.WebUser::toNewWebUser)
|
||||
.map(Authentication::getUser) // Can throw WebUserAuthException
|
||||
.map(User::toWebUser)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
package com.djrapitops.plan.delivery.webserver.auth;
|
||||
|
||||
import com.djrapitops.plan.delivery.domain.WebUser;
|
||||
import com.djrapitops.plan.delivery.domain.auth.User;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -26,14 +26,14 @@ import java.util.UUID;
|
||||
|
||||
public class ActiveCookieStore {
|
||||
|
||||
private static final Map<String, WebUser> USERS_BY_COOKIE = new HashMap<>();
|
||||
private static final Map<String, User> USERS_BY_COOKIE = new HashMap<>();
|
||||
|
||||
public static Optional<WebUser> checkCookie(String cookie) {
|
||||
public static Optional<User> checkCookie(String cookie) {
|
||||
return Optional.ofNullable(USERS_BY_COOKIE.get(cookie));
|
||||
}
|
||||
|
||||
public static String generateNewCookie(WebUser user) {
|
||||
String cookie = DigestUtils.sha256Hex(user.getName() + UUID.randomUUID() + System.currentTimeMillis());
|
||||
public static String generateNewCookie(User user) {
|
||||
String cookie = DigestUtils.sha256Hex(user.getUsername() + UUID.randomUUID() + System.currentTimeMillis());
|
||||
USERS_BY_COOKIE.put(cookie, user);
|
||||
return cookie;
|
||||
}
|
||||
@ -42,8 +42,8 @@ public class ActiveCookieStore {
|
||||
USERS_BY_COOKIE.remove(cookie);
|
||||
}
|
||||
|
||||
public static void removeCookie(WebUser user) {
|
||||
USERS_BY_COOKIE.entrySet().stream().filter(entry -> entry.getValue().getName().equals(user.getName()))
|
||||
public static void removeCookie(User user) {
|
||||
USERS_BY_COOKIE.entrySet().stream().filter(entry -> entry.getValue().getUsername().equals(user.getUsername()))
|
||||
.findAny()
|
||||
.map(Map.Entry::getKey)
|
||||
.ifPresent(ActiveCookieStore::removeCookie);
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
package com.djrapitops.plan.delivery.webserver.auth;
|
||||
|
||||
import com.djrapitops.plan.delivery.domain.WebUser;
|
||||
import com.djrapitops.plan.delivery.domain.auth.User;
|
||||
import com.djrapitops.plan.exceptions.WebUserAuthException;
|
||||
|
||||
/**
|
||||
@ -32,6 +32,6 @@ public interface Authentication {
|
||||
* @return Web user for the authentication.
|
||||
* @throws WebUserAuthException If user can't be authenticated
|
||||
*/
|
||||
WebUser getWebUser();
|
||||
User getUser();
|
||||
|
||||
}
|
||||
|
@ -16,14 +16,13 @@
|
||||
*/
|
||||
package com.djrapitops.plan.delivery.webserver.auth;
|
||||
|
||||
import com.djrapitops.plan.delivery.domain.WebUser;
|
||||
import com.djrapitops.plan.delivery.domain.auth.User;
|
||||
import com.djrapitops.plan.exceptions.PassEncryptException;
|
||||
import com.djrapitops.plan.exceptions.WebUserAuthException;
|
||||
import com.djrapitops.plan.exceptions.database.DBOpException;
|
||||
import com.djrapitops.plan.storage.database.Database;
|
||||
import com.djrapitops.plan.storage.database.queries.objects.WebUserQueries;
|
||||
import com.djrapitops.plan.utilities.Base64Util;
|
||||
import com.djrapitops.plan.utilities.PassEncryptUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
@ -47,7 +46,7 @@ public class BasicAuthentication implements Authentication {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebUser getWebUser() {
|
||||
public User getUser() {
|
||||
String decoded = Base64Util.decode(authenticationString);
|
||||
|
||||
String[] userInfo = StringUtils.split(decoded, ':');
|
||||
@ -55,7 +54,7 @@ public class BasicAuthentication implements Authentication {
|
||||
throw new WebUserAuthException(FailReason.USER_AND_PASS_NOT_SPECIFIED, Arrays.toString(userInfo));
|
||||
}
|
||||
|
||||
String user = userInfo[0];
|
||||
String username = userInfo[0];
|
||||
String passwordRaw = userInfo[1];
|
||||
|
||||
Database.State dbState = database.getState();
|
||||
@ -64,14 +63,14 @@ public class BasicAuthentication implements Authentication {
|
||||
}
|
||||
|
||||
try {
|
||||
WebUser webUser = database.query(WebUserQueries.fetchWebUser(user))
|
||||
.orElseThrow(() -> new WebUserAuthException(FailReason.USER_DOES_NOT_EXIST, user));
|
||||
User user = database.query(WebUserQueries.fetchUser(username))
|
||||
.orElseThrow(() -> new WebUserAuthException(FailReason.USER_DOES_NOT_EXIST, username));
|
||||
|
||||
boolean correctPass = PassEncryptUtil.verifyPassword(passwordRaw, webUser.getSaltedPassHash());
|
||||
boolean correctPass = user.doesPasswordMatch(passwordRaw);
|
||||
if (!correctPass) {
|
||||
throw new WebUserAuthException(FailReason.USER_PASS_MISMATCH, user);
|
||||
throw new WebUserAuthException(FailReason.USER_PASS_MISMATCH, username);
|
||||
}
|
||||
return webUser;
|
||||
return user;
|
||||
} catch (DBOpException | PassEncryptException e) {
|
||||
throw new WebUserAuthException(e);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
package com.djrapitops.plan.delivery.webserver.auth;
|
||||
|
||||
import com.djrapitops.plan.delivery.domain.WebUser;
|
||||
import com.djrapitops.plan.delivery.domain.auth.User;
|
||||
|
||||
public class CookieAuthentication implements Authentication {
|
||||
|
||||
@ -27,7 +27,7 @@ public class CookieAuthentication implements Authentication {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebUser getWebUser() {
|
||||
public User getUser() {
|
||||
return ActiveCookieStore.checkCookie(cookie).orElse(null);
|
||||
}
|
||||
}
|
||||
|
@ -16,13 +16,11 @@
|
||||
*/
|
||||
package com.djrapitops.plan.delivery.webserver.auth;
|
||||
|
||||
import com.djrapitops.plan.delivery.domain.WebUser;
|
||||
import com.djrapitops.plan.delivery.domain.auth.User;
|
||||
import com.djrapitops.plan.utilities.PassEncryptUtil;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Holds registrations of users before they are confirmed.
|
||||
@ -33,18 +31,18 @@ public class RegistrationBin {
|
||||
|
||||
private static final Map<String, AwaitingForRegistration> REGISTRATION_BIN = new HashMap<>();
|
||||
|
||||
public static String addInfoForRegistration(String username, String password) throws PassEncryptUtil.CannotPerformOperationException {
|
||||
public static String addInfoForRegistration(String username, String password) {
|
||||
String hash = PassEncryptUtil.createHash(password);
|
||||
String code = DigestUtils.sha256Hex(username + password + System.currentTimeMillis()).substring(0, 12);
|
||||
REGISTRATION_BIN.put(code, new AwaitingForRegistration(username, hash));
|
||||
return code;
|
||||
}
|
||||
|
||||
public static Optional<WebUser> register(String code, int permissionLevel) {
|
||||
public static Optional<User> register(String code, UUID linkedToUUID) {
|
||||
AwaitingForRegistration found = REGISTRATION_BIN.get(code);
|
||||
if (found == null) return Optional.empty();
|
||||
REGISTRATION_BIN.remove(code);
|
||||
return Optional.of(found.toWebUser(permissionLevel));
|
||||
return Optional.of(found.toUser(linkedToUUID));
|
||||
}
|
||||
|
||||
public static boolean contains(String code) {
|
||||
@ -60,8 +58,8 @@ public class RegistrationBin {
|
||||
this.passwordHash = passwordHash;
|
||||
}
|
||||
|
||||
public WebUser toWebUser(int permissionLevel) {
|
||||
return new WebUser(username, passwordHash, permissionLevel);
|
||||
public User toUser(UUID linkedToUUID) {
|
||||
return new User(username, null, linkedToUUID, passwordHash, Collections.emptyList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
package com.djrapitops.plan.delivery.webserver.resolver.auth;
|
||||
|
||||
import com.djrapitops.plan.delivery.domain.WebUser;
|
||||
import com.djrapitops.plan.delivery.domain.auth.User;
|
||||
import com.djrapitops.plan.delivery.web.resolver.NoAuthResolver;
|
||||
import com.djrapitops.plan.delivery.web.resolver.Response;
|
||||
import com.djrapitops.plan.delivery.web.resolver.exception.BadRequestException;
|
||||
@ -29,7 +29,6 @@ import com.djrapitops.plan.exceptions.WebUserAuthException;
|
||||
import com.djrapitops.plan.exceptions.database.DBOpException;
|
||||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.storage.database.queries.objects.WebUserQueries;
|
||||
import com.djrapitops.plan.utilities.PassEncryptUtil;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@ -52,7 +51,7 @@ public class LoginResolver implements NoAuthResolver {
|
||||
@Override
|
||||
public Optional<Response> resolve(Request request) {
|
||||
try {
|
||||
String cookie = ActiveCookieStore.generateNewCookie(getWebUser(request));
|
||||
String cookie = ActiveCookieStore.generateNewCookie(getUser(request));
|
||||
return Optional.of(getResponse(cookie));
|
||||
} catch (DBOpException | PassEncryptException e) {
|
||||
throw new WebUserAuthException(e);
|
||||
@ -67,17 +66,17 @@ public class LoginResolver implements NoAuthResolver {
|
||||
.build();
|
||||
}
|
||||
|
||||
public WebUser getWebUser(Request request) throws PassEncryptUtil.CannotPerformOperationException, PassEncryptUtil.InvalidHashException {
|
||||
public User getUser(Request request) {
|
||||
URIQuery query = request.getQuery();
|
||||
String username = query.get("user").orElseThrow(() -> new BadRequestException("'user' parameter not defined"));
|
||||
String password = query.get("password").orElseThrow(() -> new BadRequestException("'password' parameter not defined"));
|
||||
WebUser webUser = dbSystem.getDatabase().query(WebUserQueries.fetchWebUser(username))
|
||||
User user = dbSystem.getDatabase().query(WebUserQueries.fetchUser(username))
|
||||
.orElseThrow(() -> new BadRequestException(FailReason.USER_DOES_NOT_EXIST.getReason() + ": " + username));
|
||||
|
||||
boolean correctPass = PassEncryptUtil.verifyPassword(password, webUser.getSaltedPassHash());
|
||||
boolean correctPass = user.doesPasswordMatch(password);
|
||||
if (!correctPass) {
|
||||
throw new WebUserAuthException(FailReason.USER_PASS_MISMATCH);
|
||||
}
|
||||
return webUser;
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ public class RegisterResolver implements NoAuthResolver {
|
||||
|
||||
String username = query.get("user").orElseThrow(() -> new BadRequestException("'user' parameter not defined"));
|
||||
|
||||
boolean alreadyExists = dbSystem.getDatabase().query(WebUserQueries.fetchWebUser(username)).isPresent();
|
||||
boolean alreadyExists = dbSystem.getDatabase().query(WebUserQueries.fetchUser(username)).isPresent();
|
||||
if (alreadyExists) throw new BadRequestException("User '" + username + "' already exists!");
|
||||
|
||||
String password = query.get("password").orElseThrow(() -> new BadRequestException("'password' parameter not defined"));
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
package com.djrapitops.plan.exceptions;
|
||||
|
||||
public class PassEncryptException extends Exception {
|
||||
public class PassEncryptException extends IllegalArgumentException {
|
||||
|
||||
public PassEncryptException(String s) {
|
||||
super(s);
|
||||
|
@ -170,7 +170,9 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
new ExtensionTableRowValueLengthPatch(),
|
||||
new CommandUsageTableRemovalPatch(),
|
||||
new RegisterDateMinimizationPatch(),
|
||||
new BadNukkitRegisterValuePatch()
|
||||
new BadNukkitRegisterValuePatch(),
|
||||
new LinkedToSecurityTablePatch(),
|
||||
new LinkUsersToPlayersSecurityTablePatch()
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -17,10 +17,12 @@
|
||||
package com.djrapitops.plan.storage.database.queries.objects;
|
||||
|
||||
import com.djrapitops.plan.delivery.domain.WebUser;
|
||||
import com.djrapitops.plan.delivery.domain.auth.User;
|
||||
import com.djrapitops.plan.storage.database.queries.Query;
|
||||
import com.djrapitops.plan.storage.database.queries.QueryAllStatement;
|
||||
import com.djrapitops.plan.storage.database.queries.QueryStatement;
|
||||
import com.djrapitops.plan.storage.database.sql.tables.SecurityTable;
|
||||
import com.djrapitops.plan.storage.database.sql.tables.UsersTable;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
@ -28,11 +30,12 @@ import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
|
||||
|
||||
/**
|
||||
* Queries for {@link WebUser} objects.
|
||||
* Queries for web user objects.
|
||||
*
|
||||
* @author Rsl1122
|
||||
*/
|
||||
@ -47,6 +50,7 @@ public class WebUserQueries {
|
||||
*
|
||||
* @return List of Plan WebUsers.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Query<List<WebUser>> fetchAllPlanWebUsers() {
|
||||
String sql = SELECT + '*' + FROM + SecurityTable.TABLE_NAME + ORDER_BY + SecurityTable.PERMISSION_LEVEL + " ASC";
|
||||
|
||||
@ -66,6 +70,31 @@ public class WebUserQueries {
|
||||
};
|
||||
}
|
||||
|
||||
public static Query<Optional<User>> fetchUser(String username) {
|
||||
String sql = SELECT + '*' + FROM + SecurityTable.TABLE_NAME +
|
||||
LEFT_JOIN + UsersTable.TABLE_NAME + " on " + SecurityTable.LINKED_TO + "=" + UsersTable.USER_UUID +
|
||||
WHERE + SecurityTable.USERNAME + "=? LIMIT 1";
|
||||
return new QueryStatement<Optional<User>>(sql) {
|
||||
@Override
|
||||
public void prepare(PreparedStatement statement) throws SQLException {
|
||||
statement.setString(1, username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<User> processResults(ResultSet set) throws SQLException {
|
||||
if (set.next()) {
|
||||
String linkedTo = set.getString(UsersTable.USER_NAME);
|
||||
UUID linkedToUUID = linkedTo != null ? UUID.fromString(set.getString(SecurityTable.LINKED_TO)) : null;
|
||||
String passwordHash = set.getString(SecurityTable.SALT_PASSWORD_HASH);
|
||||
List<String> permissions = WebUser.getPermissionsForLevel(set.getInt(SecurityTable.PERMISSION_LEVEL));
|
||||
return Optional.of(new User(username, linkedTo != null ? linkedTo : "console", linkedToUUID, passwordHash, permissions));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static Query<Optional<WebUser>> fetchWebUser(String called) {
|
||||
String sql = SELECT + '*' + FROM + SecurityTable.TABLE_NAME +
|
||||
WHERE + SecurityTable.USERNAME + "=? LIMIT 1";
|
||||
|
@ -18,7 +18,6 @@ package com.djrapitops.plan.storage.database.sql.tables;
|
||||
|
||||
import com.djrapitops.plan.storage.database.DBType;
|
||||
import com.djrapitops.plan.storage.database.sql.building.CreateTableBuilder;
|
||||
import com.djrapitops.plan.storage.database.sql.building.Insert;
|
||||
import com.djrapitops.plan.storage.database.sql.building.Sql;
|
||||
|
||||
/**
|
||||
@ -31,10 +30,15 @@ public class SecurityTable {
|
||||
public static final String TABLE_NAME = "plan_security";
|
||||
|
||||
public static final String USERNAME = "username";
|
||||
public static final String LINKED_TO = "linked_to_uuid";
|
||||
public static final String SALT_PASSWORD_HASH = "salted_pass_hash";
|
||||
public static final String PERMISSION_LEVEL = "permission_level";
|
||||
|
||||
public static final String INSERT_STATEMENT = Insert.values(TABLE_NAME, USERNAME, SALT_PASSWORD_HASH, PERMISSION_LEVEL);
|
||||
public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" +
|
||||
USERNAME + ',' +
|
||||
LINKED_TO + ',' +
|
||||
SALT_PASSWORD_HASH + ',' +
|
||||
PERMISSION_LEVEL + ") VALUES (?,?,?,?)";
|
||||
|
||||
private SecurityTable() {
|
||||
/* Static information class */
|
||||
@ -43,6 +47,7 @@ public class SecurityTable {
|
||||
public static String createTableSQL(DBType dbType) {
|
||||
return CreateTableBuilder.create(TABLE_NAME, dbType)
|
||||
.column(USERNAME, Sql.varchar(100)).notNull().unique()
|
||||
.column(LINKED_TO, Sql.varchar(36))
|
||||
.column(SALT_PASSWORD_HASH, Sql.varchar(100)).notNull().unique()
|
||||
.column(PERMISSION_LEVEL, Sql.INT).notNull()
|
||||
.toString();
|
||||
|
@ -16,25 +16,28 @@
|
||||
*/
|
||||
package com.djrapitops.plan.storage.database.transactions.commands;
|
||||
|
||||
import com.djrapitops.plan.delivery.domain.WebUser;
|
||||
import com.djrapitops.plan.delivery.domain.auth.User;
|
||||
import com.djrapitops.plan.storage.database.sql.tables.SecurityTable;
|
||||
import com.djrapitops.plan.storage.database.transactions.ExecStatement;
|
||||
import com.djrapitops.plan.storage.database.transactions.Transaction;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* Transaction to save a new Plan {@link WebUser} to the database.
|
||||
* Transaction to save a new Plan {@link User} to the database.
|
||||
*
|
||||
* @author Rsl1122
|
||||
*/
|
||||
public class RegisterWebUserTransaction extends Transaction {
|
||||
|
||||
private final WebUser webUser;
|
||||
private final User user;
|
||||
private final int permissionLevel;
|
||||
|
||||
public RegisterWebUserTransaction(WebUser webUser) {
|
||||
this.webUser = webUser;
|
||||
public RegisterWebUserTransaction(User user, int permissionLevel) {
|
||||
this.user = user;
|
||||
this.permissionLevel = permissionLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -42,9 +45,14 @@ public class RegisterWebUserTransaction extends Transaction {
|
||||
execute(new ExecStatement(SecurityTable.INSERT_STATEMENT) {
|
||||
@Override
|
||||
public void prepare(PreparedStatement statement) throws SQLException {
|
||||
statement.setString(1, webUser.getName());
|
||||
statement.setString(2, webUser.getSaltedPassHash());
|
||||
statement.setInt(3, webUser.getPermLevel());
|
||||
statement.setString(1, user.getUsername());
|
||||
if (user.getLinkedToUUID() == null) {
|
||||
statement.setNull(2, Types.VARCHAR);
|
||||
} else {
|
||||
statement.setString(2, user.getLinkedToUUID().toString());
|
||||
}
|
||||
statement.setString(3, user.getPasswordHash());
|
||||
statement.setInt(4, permissionLevel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.storage.database.transactions.patches;
|
||||
|
||||
import com.djrapitops.plan.storage.database.queries.HasMoreThanZeroQueryStatement;
|
||||
import com.djrapitops.plan.storage.database.queries.QueryAllStatement;
|
||||
import com.djrapitops.plan.storage.database.sql.building.Sql;
|
||||
import com.djrapitops.plan.storage.database.sql.tables.SecurityTable;
|
||||
import com.djrapitops.plan.storage.database.sql.tables.UsersTable;
|
||||
import com.djrapitops.plan.storage.database.transactions.ExecBatchStatement;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
|
||||
|
||||
public class LinkUsersToPlayersSecurityTablePatch extends Patch {
|
||||
|
||||
@Override
|
||||
public boolean hasBeenApplied() {
|
||||
String sql = SELECT + "COUNT(1) as c" + FROM + SecurityTable.TABLE_NAME +
|
||||
WHERE + SecurityTable.LINKED_TO + "=''";
|
||||
return !query(new HasMoreThanZeroQueryStatement(sql) {
|
||||
@Override
|
||||
public void prepare(PreparedStatement statement) {
|
||||
// No preparation necessary
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyPatch() {
|
||||
String querySQL = SELECT + UsersTable.USER_UUID + ',' + SecurityTable.USERNAME +
|
||||
FROM + SecurityTable.TABLE_NAME +
|
||||
LEFT_JOIN + UsersTable.TABLE_NAME + " on " + UsersTable.USER_NAME + "=" + SecurityTable.USERNAME +
|
||||
WHERE + SecurityTable.LINKED_TO + "=''";
|
||||
String sql = "UPDATE " + SecurityTable.TABLE_NAME + " SET " + SecurityTable.LINKED_TO + "=?" +
|
||||
WHERE + SecurityTable.USERNAME + "=?";
|
||||
|
||||
Map<String, String> byUsername = query(new QueryAllStatement<Map<String, String>>(querySQL) {
|
||||
@Override
|
||||
public Map<String, String> processResults(ResultSet set) throws SQLException {
|
||||
Map<String, String> byUsername = new HashMap<>();
|
||||
while (set.next()) {
|
||||
byUsername.put(set.getString(SecurityTable.USERNAME), set.getString(UsersTable.USER_UUID));
|
||||
}
|
||||
return byUsername;
|
||||
}
|
||||
});
|
||||
execute(new ExecBatchStatement(sql) {
|
||||
@Override
|
||||
public void prepare(PreparedStatement statement) throws SQLException {
|
||||
for (Map.Entry<String, String> usernameUUIDPair : byUsername.entrySet()) {
|
||||
Sql.setStringOrNull(statement, 1, usernameUUIDPair.getValue());
|
||||
statement.setString(2, usernameUUIDPair.getKey());
|
||||
statement.addBatch();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.storage.database.transactions.patches;
|
||||
|
||||
import com.djrapitops.plan.storage.database.sql.building.Sql;
|
||||
import com.djrapitops.plan.storage.database.sql.tables.SecurityTable;
|
||||
|
||||
public class LinkedToSecurityTablePatch extends Patch {
|
||||
|
||||
@Override
|
||||
public boolean hasBeenApplied() {
|
||||
return hasColumn(SecurityTable.TABLE_NAME, SecurityTable.LINKED_TO);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyPatch() {
|
||||
addColumn(SecurityTable.TABLE_NAME, SecurityTable.LINKED_TO + ' ' + Sql.varchar(36) + " DEFAULT ''");
|
||||
}
|
||||
}
|
@ -55,11 +55,18 @@ public class PassEncryptUtil {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
|
||||
public static String createHash(String password) throws CannotPerformOperationException {
|
||||
/**
|
||||
* Create a hash of password + salt.
|
||||
*
|
||||
* @param password Password
|
||||
* @return Hash + salt
|
||||
* @throws CannotPerformOperationException If the hash creation fails
|
||||
*/
|
||||
public static String createHash(String password) {
|
||||
return createHash(password.toCharArray());
|
||||
}
|
||||
|
||||
private static String createHash(char[] password) throws CannotPerformOperationException {
|
||||
private static String createHash(char[] password) {
|
||||
// Generate a random salt
|
||||
SecureRandom random = new SecureRandom();
|
||||
byte[] salt = new byte[SALT_BYTE_SIZE];
|
||||
@ -77,11 +84,20 @@ public class PassEncryptUtil {
|
||||
+ ":" + toBase64(hash);
|
||||
}
|
||||
|
||||
public static boolean verifyPassword(String password, String correctHash) throws CannotPerformOperationException, InvalidHashException {
|
||||
/**
|
||||
* Verify that a password matches a hash.
|
||||
*
|
||||
* @param password Password
|
||||
* @param correctHash hash created with {@link PassEncryptUtil#createHash(String)}
|
||||
* @return true if match
|
||||
* @throws CannotPerformOperationException If hashing fails
|
||||
* @throws InvalidHashException If the hash is missing details.
|
||||
*/
|
||||
public static boolean verifyPassword(String password, String correctHash) {
|
||||
return verifyPassword(password.toCharArray(), correctHash);
|
||||
}
|
||||
|
||||
private static boolean verifyPassword(char[] password, String correctHash) throws CannotPerformOperationException, InvalidHashException {
|
||||
private static boolean verifyPassword(char[] password, String correctHash) {
|
||||
// Decode the hash into its parameters
|
||||
String[] params = StringUtils.split(correctHash, ':');
|
||||
if (params.length != HASH_SECTIONS) {
|
||||
@ -160,7 +176,7 @@ public class PassEncryptUtil {
|
||||
return diff == 0;
|
||||
}
|
||||
|
||||
private static byte[] pbkdf2(char[] password, byte[] salt, int iterations, int bytes) throws CannotPerformOperationException {
|
||||
private static byte[] pbkdf2(char[] password, byte[] salt, int iterations, int bytes) {
|
||||
try {
|
||||
PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, bytes * 8);
|
||||
SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
|
||||
|
@ -59,7 +59,7 @@ class JksHttpsServerTest implements HttpsServerTest {
|
||||
system.enable();
|
||||
|
||||
WebUser webUser = new WebUser("test", PassEncryptUtil.createHash("testPass"), 0);
|
||||
system.getDatabaseSystem().getDatabase().executeTransaction(new RegisterWebUserTransaction(webUser));
|
||||
system.getDatabaseSystem().getDatabase().executeTransaction(new RegisterWebUserTransaction(webUser, ));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
|
@ -64,7 +64,7 @@ class Pkcs12HttpsServerTest implements HttpsServerTest {
|
||||
system.enable();
|
||||
|
||||
WebUser webUser = new WebUser("test", PassEncryptUtil.createHash("testPass"), 0);
|
||||
system.getDatabaseSystem().getDatabase().executeTransaction(new RegisterWebUserTransaction(webUser));
|
||||
system.getDatabaseSystem().getDatabase().executeTransaction(new RegisterWebUserTransaction(webUser, ));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
|
@ -68,7 +68,7 @@ public interface DatabaseBackupTest extends DatabaseTestPreparer {
|
||||
);
|
||||
|
||||
WebUser webUser = new WebUser(TestConstants.PLAYER_ONE_NAME, RandomData.randomString(100), 0);
|
||||
db().executeTransaction(new RegisterWebUserTransaction(webUser));
|
||||
db().executeTransaction(new RegisterWebUserTransaction(webUser, ));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -34,7 +34,7 @@ public interface WebUserQueriesTest extends DatabaseTestPreparer {
|
||||
@Test
|
||||
default void webUserIsRegistered() {
|
||||
WebUser expected = new WebUser(TestConstants.PLAYER_ONE_NAME, "RandomGarbageBlah", 0);
|
||||
db().executeTransaction(new RegisterWebUserTransaction(expected));
|
||||
db().executeTransaction(new RegisterWebUserTransaction(expected, ));
|
||||
forcePersistenceCheck();
|
||||
|
||||
Optional<WebUser> found = db().query(WebUserQueries.fetchWebUser(TestConstants.PLAYER_ONE_NAME));
|
||||
|
Loading…
Reference in New Issue
Block a user