diff --git a/src/de/steamwar/sql/BauweltMember.java b/src/de/steamwar/sql/BauweltMember.java
index 37be705..4efd9c3 100644
--- a/src/de/steamwar/sql/BauweltMember.java
+++ b/src/de/steamwar/sql/BauweltMember.java
@@ -102,4 +102,22 @@ public class BauweltMember {
delete.update(bauweltID, memberID);
memberCache.remove(memberID);
}
+
+ public boolean isBuild() {
+ return worldEdit;
+ }
+
+ public boolean isSupervisor() {
+ return world;
+ }
+
+ public void setBuild(boolean build) {
+ this.worldEdit = build;
+ updateDB();
+ }
+
+ public void setSupervisor(boolean supervisor) {
+ this.world = supervisor;
+ updateDB();
+ }
}
diff --git a/src/de/steamwar/sql/Event.java b/src/de/steamwar/sql/Event.java
index c284903..f06f95e 100644
--- a/src/de/steamwar/sql/Event.java
+++ b/src/de/steamwar/sql/Event.java
@@ -84,6 +84,7 @@ public class Event {
private SchematicType schemType;
@Field
private boolean publicSchemsOnly;
+ @Deprecated
@Field
private boolean spectateSystem;
diff --git a/src/de/steamwar/sql/EventFight.java b/src/de/steamwar/sql/EventFight.java
index c7b80db..b2a86c4 100644
--- a/src/de/steamwar/sql/EventFight.java
+++ b/src/de/steamwar/sql/EventFight.java
@@ -93,8 +93,12 @@ public class EventFight {
@Getter
@Setter
@Field
+ @Deprecated
private int kampfleiter;
@Getter
+ @Field
+ private int spectatePort;
+ @Getter
@Field(def = "0")
private int ergebnis;
@Field(nullable = true)
diff --git a/src/de/steamwar/sql/Referee.java b/src/de/steamwar/sql/Referee.java
new file mode 100644
index 0000000..2f606a9
--- /dev/null
+++ b/src/de/steamwar/sql/Referee.java
@@ -0,0 +1,44 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.sql;
+
+import de.steamwar.sql.internal.Field;
+import de.steamwar.sql.internal.SelectStatement;
+import de.steamwar.sql.internal.Table;
+import lombok.AllArgsConstructor;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@AllArgsConstructor
+public class Referee {
+
+ private static final Table table = new Table<>(Referee.class);
+ private static final SelectStatement byEvent = table.select("eventID");
+
+ public static Set get(int eventID) {
+ return byEvent.listSelect(eventID).stream().map(referee -> referee.userID).collect(Collectors.toSet());
+ }
+
+ @Field(keys = {"eventReferee"})
+ private final int eventID;
+ @Field(keys = {"eventReferee"})
+ private final int userID;
+}
diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java
index b535a56..ecfd736 100644
--- a/src/de/steamwar/sql/SchematicNode.java
+++ b/src/de/steamwar/sql/SchematicNode.java
@@ -56,7 +56,7 @@ public class SchematicNode {
private static final SelectStatement list = new SelectStatement<>(table, "SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId " + Statement.NULL_SAFE_EQUALS + "? AND NM.UserId = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode WHERE (? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?) ORDER BY NodeName");
private static final SelectStatement byParentName = new SelectStatement<>(table, "SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId " + Statement.NULL_SAFE_EQUALS + "? AND NM.UserId = ? AND SchematicNode.NodeName = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode WHERE ((? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?)) AND NodeName = ?");
private static final SelectStatement schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?");
- private static final SelectStatement accessibleByUserTypeInParent = new SelectStatement<>(table, "WITH RECURSIVE RSASN AS (WITH RSAN AS (WITH RECURSIVE RSA AS (SELECT SN.NodeId, NM.ParentId FROM SchematicNode SN LEFT JOIN NodeMember NM on SN.NodeId = NM.NodeId WHERE UserId = ? UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSA WHERE RSA.NodeId = SN.ParentNode) SELECT * FROM RSA UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?) SELECT RSAN.NodeId, RSAN.ParentId FROM RSAN INNER JOIN SchematicNode SN ON SN.NodeId = RSAN.NodeId WHERE NodeType = ? UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSASN WHERE SN.NodeId = RSASN.ParentId) SELECT SN.*, ? as EffectiveOwner, RSASN.ParentId AS ParentNode FROM RSASN INNER JOIN SchematicNode SN ON SN.NodeId = RSASN.NodeId WHERE RSASN.ParentId" + Statement.NULL_SAFE_EQUALS + "?");
+ private static final SelectStatement accessibleByUserTypeInParent = new SelectStatement<>(table, "WITH RECURSIVE RSASN AS(WITH RECURSIVE RSAN AS (WITH RSANH AS (WITH RECURSIVE RSA AS (SELECT SN.NodeId, NM.ParentId FROM SchematicNode SN LEFT JOIN NodeMember NM on SN.NodeId = NM.NodeId WHERE NM.UserId = ? UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN INNER JOIN RSA ON RSA.NodeId = SN.ParentNode) SELECT * FROM RSA UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?) SELECT * FROM RSANH UNION SELECT SN.NodeId, SN.ParentNode FROM RSANH JOIN SchematicNode SN ON SN.ParentNode = RSANH.NodeId) SELECT RSAN.NodeId, RSAN.ParentId FROM RSAN JOIN SchematicNode SN ON SN.NodeId = RSAN.NodeId WHERE NodeType = ? UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN JOIN RSASN ON SN.NodeId = RSASN.ParentId) SELECT SN.*, ? as EffectiveOwner, RSASN.ParentId AS ParentNode FROM RSASN JOIN SchematicNode SN ON SN.NodeId = RSASN.NodeId WHERE RSASN.ParentId" + Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName");
private static final SelectStatement accessibleByUserType = new SelectStatement<>(table, "WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ? UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.*, ? AS EffectiveOwner FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE NodeType = ?");
private static final SelectStatement byIdAndUser = new SelectStatement<>(table, "SELECT NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode WHERE NodeId = ?");
private static final SelectStatement allParentsOfNode = new SelectStatement<>(table, "WITH RECURSIVE R AS (SELECT NodeId, ParentNode FROM EffectiveSchematicNode WHERE NodeId = ? AND EffectiveOwner = ? UNION SELECT E.NodeId, E.ParentNode FROM R, EffectiveSchematicNode E WHERE R.ParentNode = E.NodeId AND E.EffectiveOwner = ?) SELECT SN.NodeId, SN.NodeOwner, ? AS EffectiveOwner, SN.NodeName, R.ParentNode, SN.LastUpdate, SN.NodeItem, SN.NodeType, SN.NodeRank, SN.ReplaceColor, SN.AllowReplay FROM R INNER JOIN SchematicNode SN ON SN.NodeId = R.NodeId");
diff --git a/src/de/steamwar/sql/SteamwarUser.java b/src/de/steamwar/sql/SteamwarUser.java
index 48375a5..2e5e3a8 100644
--- a/src/de/steamwar/sql/SteamwarUser.java
+++ b/src/de/steamwar/sql/SteamwarUser.java
@@ -23,11 +23,11 @@ import de.steamwar.sql.internal.*;
import lombok.Getter;
import lombok.SneakyThrows;
-import javax.crypto.Mac;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
-import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
+import java.security.spec.InvalidKeySpecException;
import java.sql.Timestamp;
import java.util.*;
import java.util.function.BiConsumer;
@@ -36,9 +36,17 @@ import java.util.logging.Level;
import java.util.stream.Collectors;
public class SteamwarUser {
+ private static final SecureRandom random = new SecureRandom();
+ private static final SecretKeyFactory factory;
static {
- new SqlTypeMapper<>(UUID.class, "CHAR(36)", (rs, identifier) -> UUID.fromString(rs.getString(identifier)), (st, index, value) -> st.setString(index, value.toString()));
+ try {
+ factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
+ } catch (NoSuchAlgorithmException e) {
+ throw new SecurityException(e);
+ }
+
+ new SqlTypeMapper<>(UUID.class, "CHAR(36)", (rs, identifier) -> UUID.fromString(rs.getString(identifier)), (st, index, value) -> st.setString(index, value.toString()));
new SqlTypeMapper<>(Locale.class, "VARCHAR(32)", (rs, identifier) -> {
String l = rs.getString(identifier);
return l != null ? Locale.forLanguageTag(l) : null;
@@ -92,7 +100,7 @@ public class SteamwarUser {
if (user != null) {
if (!user.userName.equals(name)) {
updateName.update(name, user.id);
- nameUpdate.accept(user.userName, name);
+ nameUpdate.accept(user.userName, name);
user.userName = name;
}
@@ -244,45 +252,50 @@ public class SteamwarUser {
updateDiscord.update(discordId, id);
}
- @SneakyThrows
public void setPassword(String password) {
- SecureRandom random = new SecureRandom();
- byte[] salt = new byte[16];
- random.nextBytes(salt);
- String saltString = Base64.getEncoder().encodeToString(salt);
+ try {
+ byte[] salt = new byte[16];
+ random.nextBytes(salt);
+ String saltString = Base64.getEncoder().encodeToString(salt);
- PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 512);
- SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
- byte[] hash = factory.generateSecret(spec).getEncoded();
-
- String hashString = Base64.getEncoder().encodeToString(hash);
-
- this.password = hashString + ":" + saltString;
- updatePassword.update(this.password, id);
+ byte[] hash = generateHash(password, salt);
+ String hashString = Base64.getEncoder().encodeToString(hash);
+ this.password = hashString + ":" + saltString;
+ updatePassword.update(this.password, id);
+ } catch (Exception e) {
+ throw new SecurityException(e);
+ }
}
- @SneakyThrows
public boolean verifyPassword(String password) {
- if (this.password == null) {
+ try {
+ if (this.password == null) {
+ return false;
+ }
+
+ String[] parts = this.password.split(":");
+ if (parts.length != 2) {
+ SQLConfig.impl.getLogger().log(Level.SEVERE ,"Invalid password hash for user {0} ({1})", new Object[]{userName, id});
+ return false;
+ }
+
+ String hashString = parts[0];
+ byte[] realHash = Base64.getDecoder().decode(hashString);
+ String saltString = parts[1];
+ byte[] salt = Base64.getDecoder().decode(saltString);
+ byte[] hash = generateHash(password, salt);
+ return Arrays.equals(realHash, hash);
+ } catch (Exception e) {
+ SQLConfig.impl.getLogger().log(Level.SEVERE, "Error while verifying password for user " + userName + " (" + id + ")", e);
return false;
}
+ }
- String[] parts = this.password.split(":");
- if (parts.length != 2) {
- SQLConfig.impl.getLogger().log(Level.SEVERE ,"Invalid password hash for user {0} ({1})", new Object[]{userName, id});
- return false;
- }
-
- String hashString = parts[0];
- byte[] realHash = Base64.getDecoder().decode(hashString);
- String saltString = parts[1];
- byte[] salt = Base64.getDecoder().decode(saltString);
-
+ private byte[] generateHash(String password, byte[] salt)
+ throws InvalidKeySpecException {
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 512);
- SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
- byte[] hash = factory.generateSecret(spec).getEncoded();
+ return factory.generateSecret(spec).getEncoded();
- return Arrays.equals(realHash, hash);
}
private void initPunishments() {
diff --git a/src/de/steamwar/sql/internal/Statement.java b/src/de/steamwar/sql/internal/Statement.java
index e850906..8375fe2 100644
--- a/src/de/steamwar/sql/internal/Statement.java
+++ b/src/de/steamwar/sql/internal/Statement.java
@@ -134,7 +134,11 @@ public class Statement implements AutoCloseable {
public T select(ResultSetUser user, Object... objects) {
return withConnection(st -> {
- ResultSet rs = st.executeQuery();
+ boolean res = st.execute();
+ if(!res) {
+ throw new SecurityException("No result set for select statement");
+ }
+ ResultSet rs = st.getResultSet();
T result = user.use(rs);
rs.close();
return result;