Updated to new standard
This commit is contained in:
@@ -1,48 +0,0 @@
|
|||||||
package github.openautonomousconnection.webserver;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class AuthManager {
|
|
||||||
|
|
||||||
private static Map<String, String> users = new HashMap<>();
|
|
||||||
|
|
||||||
public static void loadAuthFile(File authFile) throws IOException {
|
|
||||||
if (!authFile.exists()) authFile.createNewFile();
|
|
||||||
for (String line : Files.readAllLines(authFile.toPath(), StandardCharsets.UTF_8)) {
|
|
||||||
line = line.trim();
|
|
||||||
if (line.isEmpty() || line.startsWith("#")) continue;
|
|
||||||
String[] parts = line.split(":", 2);
|
|
||||||
if (parts.length == 2) {
|
|
||||||
users.put(parts[0], parts[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean checkAuth(String login, String password) {
|
|
||||||
|
|
||||||
String storedHash = users.get(login);
|
|
||||||
if (storedHash == null) return false;
|
|
||||||
|
|
||||||
String hash = sha256(password);
|
|
||||||
return storedHash.equalsIgnoreCase(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String sha256(String input) {
|
|
||||||
try {
|
|
||||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
|
||||||
byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (byte b : digest) sb.append(String.format("%02x", b));
|
|
||||||
return sb.toString();
|
|
||||||
} catch (Exception e) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,387 +1,9 @@
|
|||||||
package github.openautonomousconnection.webserver;
|
package github.openautonomousconnection.webserver;
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import me.finn.unlegitlibrary.event.EventManager;
|
|
||||||
import me.finn.unlegitlibrary.file.ConfigurationManager;
|
|
||||||
import me.finn.unlegitlibrary.file.FileUtils;
|
|
||||||
import me.finn.unlegitlibrary.network.system.packets.PacketHandler;
|
|
||||||
import me.finn.unlegitlibrary.network.system.server.NetworkServer;
|
|
||||||
import me.finn.unlegitlibrary.network.utils.NetworkUtils;
|
|
||||||
import me.finn.unlegitlibrary.utils.Logger;
|
|
||||||
import org.apache.commons.fileupload2.core.DiskFileItemFactory;
|
|
||||||
import org.apache.commons.fileupload2.core.FileItem;
|
|
||||||
import org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletFileUpload;
|
|
||||||
import org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletRequestContext;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLServerSocket;
|
|
||||||
import javax.net.ssl.SSLSocket;
|
|
||||||
import java.io.*;
|
|
||||||
import java.net.URLDecoder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.ZonedDateTime;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
public static void main(String[] args) throws Exception {
|
|
||||||
|
|
||||||
AuthManager.loadAuthFile(authFile);
|
public static void main(String[] args) {
|
||||||
RuleManager.loadRules(rulesFile);
|
|
||||||
|
|
||||||
pipelineServer = new NetworkServer.ServerBuilder().
|
|
||||||
setPort(configurationManager.getInt("port.pipeline")).setTimeout(0).
|
|
||||||
setPacketHandler(new PacketHandler()).setEventManager(new EventManager()).
|
|
||||||
setLogger(new Logger(new File("logs"), false, true)).
|
|
||||||
setServerCertificate(certFile, keyFile).setRootCAFolder(folderStructure.publicCAFolder).
|
|
||||||
build();
|
|
||||||
|
|
||||||
pipelineServer.start();
|
|
||||||
|
|
||||||
SSLServerSocket webServer = (SSLServerSocket)NetworkServer.ServerBuilder.
|
|
||||||
createSSLServerSocketFactory(folderStructure.publicCAFolder, certFile,keyFile).
|
|
||||||
createServerSocket(configurationManager.getInt("port"));
|
|
||||||
webServer.setSoTimeout(0);
|
|
||||||
webServer.setEnabledProtocols(pipelineServer.getServerSocket().getEnabledProtocols());
|
|
||||||
|
|
||||||
// Stop WebServer if pipelineServer dies
|
|
||||||
new Thread(() -> {
|
|
||||||
while (true) {
|
|
||||||
if (!pipelineServer.getServerSocket().isBound()) {
|
|
||||||
try {
|
|
||||||
webServer.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
pipelineServer.getLogger().exception("Failed to stop WebServer", e);
|
|
||||||
} finally {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
|
|
||||||
new Thread(() -> {
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
SSLSocket client = (SSLSocket) webServer.accept();
|
|
||||||
threadPool.submit(() -> handleBrowser(client));
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void handleBrowser(SSLSocket client) {
|
}
|
||||||
try (InputStream in = client.getInputStream(); OutputStream out = client.getOutputStream()) {
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
|
|
||||||
String line;
|
|
||||||
String path = "/main.html";
|
|
||||||
Map<String, String> headers = new HashMap<>();
|
|
||||||
while ((line = reader.readLine()) != null && !line.isEmpty()) {
|
|
||||||
if (line.toLowerCase().startsWith("get") || line.toLowerCase().startsWith("post")) {
|
|
||||||
path = line.split(" ")[1];
|
|
||||||
}
|
|
||||||
if (line.contains(":")) {
|
|
||||||
String[] parts = line.split(":", 2);
|
|
||||||
headers.put(parts[0].trim().toLowerCase(), parts[1].trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
path = URLDecoder.decode(path, StandardCharsets.UTF_8);
|
|
||||||
path = normalizePath(path);
|
|
||||||
|
|
||||||
File file = new File(contentFolder, path);
|
|
||||||
|
|
||||||
String sessionId = null;
|
|
||||||
if (headers.containsKey("cookie")) {
|
|
||||||
for (String cookie : headers.get("cookie").split(";")) {
|
|
||||||
cookie = cookie.trim();
|
|
||||||
if (cookie.startsWith("SESSIONID=")) {
|
|
||||||
sessionId = cookie.substring("SESSIONID=".length());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!file.exists() || !file.isFile()) {
|
|
||||||
sendResponse(out, 404, new File(errorsFolder, "404.html"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String clientIp = client.getInetAddress().getHostAddress();
|
|
||||||
String userAgent = headers.getOrDefault("user-agent", null);
|
|
||||||
|
|
||||||
boolean loggedIn = sessionId != null && SessionManager.isValid(sessionId, clientIp, userAgent);
|
|
||||||
|
|
||||||
if (path.equals("/403-login") && headers.getOrDefault("content-type","").startsWith("application/x-www-form-urlencoded")) {
|
|
||||||
Map<String,String> postParams = parsePostParams(in);
|
|
||||||
String login = postParams.get("login");
|
|
||||||
String password = postParams.get("password");
|
|
||||||
|
|
||||||
if (AuthManager.checkAuth(login, password)) {
|
|
||||||
String newSessionId = SessionManager.create(login, clientIp, userAgent);
|
|
||||||
Map<String,String> cookies = Map.of("Set-Cookie", "SESSIONID=" + newSessionId + "; HttpOnly; Path=/");
|
|
||||||
sendRedirect(out, "/main.html", cookies);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
sendRedirect(out, "/403.php", null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMultipart(headers)) {
|
|
||||||
handleMultipart(in, headers, new File(contentFolder, "uploads"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RuleManager.requiresAuth(path) && !loggedIn) {
|
|
||||||
PHPResponse phpResp = renderPHPWithCookies(new File(contentFolder, "403.php"));
|
|
||||||
sendResponse(out, 200, phpResp.body.getBytes(StandardCharsets.UTF_8), "text/html", phpResp.cookies);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (RuleManager.isDenied(path) && !RuleManager.isAllowed(path)) {
|
|
||||||
sendResponse(out, 403, new File(errorsFolder, "403.php"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.endsWith(".php")) {
|
|
||||||
PHPResponse phpResp = renderPHPWithCookies(file);
|
|
||||||
sendResponse(out, 200, phpResp.body.getBytes(StandardCharsets.UTF_8), "text/html", phpResp.cookies);
|
|
||||||
} else {
|
|
||||||
sendResponse(out, 200, Files.readAllBytes(file.toPath()), getContentType(path), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
|
||||||
try { client.close(); } catch (IOException ignored) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void sendRedirect(OutputStream out, String location, Map<String,String> cookies) throws IOException {
|
|
||||||
out.write(("HTTP/1.1 302 Found\r\n").getBytes());
|
|
||||||
out.write(("Location: " + location + "\r\n").getBytes());
|
|
||||||
if (cookies != null) {
|
|
||||||
for (var entry : cookies.entrySet()) {
|
|
||||||
out.write((entry.getKey() + ": " + entry.getValue() + "\r\n").getBytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.write("\r\n".getBytes());
|
|
||||||
out.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Map<String,String> parsePostParams(InputStream in) throws IOException {
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
while (reader.ready()) {
|
|
||||||
sb.append((char) reader.read());
|
|
||||||
}
|
|
||||||
Map<String,String> map = new HashMap<>();
|
|
||||||
String[] pairs = sb.toString().split("&");
|
|
||||||
for (String p : pairs) {
|
|
||||||
String[] kv = p.split("=",2);
|
|
||||||
if (kv.length == 2) map.put(URLDecoder.decode(kv[0], StandardCharsets.UTF_8), URLDecoder.decode(kv[1], StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String normalizePath(String path) {
|
|
||||||
path = path.replace("/", File.separator).replace("\\","/");
|
|
||||||
while (path.contains("..")) path = path.replace("..", "");
|
|
||||||
if (path.startsWith("/")) path = path.substring(1);
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Map<String, String> parseQueryParams(String rawPath) {
|
|
||||||
Map<String, String> map = new HashMap<>();
|
|
||||||
if (rawPath.contains("?")) {
|
|
||||||
String[] params = rawPath.substring(rawPath.indexOf("?") + 1).split("&");
|
|
||||||
for (String p : params) {
|
|
||||||
String[] kv = p.split("=");
|
|
||||||
if (kv.length == 2) map.put(kv[0], kv[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isMultipart(Map<String,String> headers) {
|
|
||||||
String contentType = headers.get("content-type");
|
|
||||||
return contentType != null && contentType.startsWith("multipart/form-data");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void handleMultipart(InputStream in, Map<String, String> headers, File uploadDir) throws IOException {
|
|
||||||
if (!uploadDir.exists()) uploadDir.mkdirs();
|
|
||||||
|
|
||||||
String contentType = headers.get("content-type");
|
|
||||||
String boundary = "--" + contentType.split("boundary=")[1];
|
|
||||||
|
|
||||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
|
||||||
byte[] lineBuffer = new byte[8192];
|
|
||||||
int read;
|
|
||||||
while ((read = in.read(lineBuffer)) != -1) {
|
|
||||||
buffer.write(lineBuffer, 0, read);
|
|
||||||
if (buffer.size() > 10 * 1024 * 1024) break; // 10 MB max
|
|
||||||
}
|
|
||||||
|
|
||||||
String data = buffer.toString(StandardCharsets.UTF_8);
|
|
||||||
String[] parts = data.split(boundary);
|
|
||||||
|
|
||||||
for (String part : parts) {
|
|
||||||
if (part.contains("Content-Disposition")) {
|
|
||||||
String name = null;
|
|
||||||
String filename = null;
|
|
||||||
|
|
||||||
for (String headerLine : part.split("\r\n")) {
|
|
||||||
if (headerLine.startsWith("Content-Disposition")) {
|
|
||||||
if (headerLine.contains("filename=\"")) {
|
|
||||||
int start = headerLine.indexOf("filename=\"") + 10;
|
|
||||||
int end = headerLine.indexOf("\"", start);
|
|
||||||
filename = headerLine.substring(start, end);
|
|
||||||
}
|
|
||||||
if (headerLine.contains("name=\"")) {
|
|
||||||
int start = headerLine.indexOf("name=\"") + 6;
|
|
||||||
int end = headerLine.indexOf("\"", start);
|
|
||||||
name = headerLine.substring(start, end);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filename != null && !filename.isEmpty()) {
|
|
||||||
int headerEnd = part.indexOf("\r\n\r\n");
|
|
||||||
byte[] fileData = part.substring(headerEnd + 4).getBytes(StandardCharsets.UTF_8);
|
|
||||||
File outFile = new File(uploadDir, filename);
|
|
||||||
Files.write(outFile.toPath(), fileData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String renderPHP(File file) throws IOException, InterruptedException {
|
|
||||||
ProcessBuilder pb = new ProcessBuilder("php", file.getAbsolutePath());
|
|
||||||
pb.redirectErrorStream(true);
|
|
||||||
Process p = pb.start();
|
|
||||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
|
||||||
InputStream processIn = p.getInputStream();
|
|
||||||
byte[] buf = new byte[8192];
|
|
||||||
int read;
|
|
||||||
while ((read = processIn.read(buf)) != -1) {
|
|
||||||
output.write(buf, 0, read);
|
|
||||||
}
|
|
||||||
p.waitFor();
|
|
||||||
return output.toString(StandardCharsets.UTF_8);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void sendResponse(OutputStream out, int code, File file, Map<String,String> headers) throws IOException {
|
|
||||||
byte[] body = Files.readAllBytes(file.toPath());
|
|
||||||
sendResponse(out, code, body, "text/html", headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void sendResponse(OutputStream out, int code, File file) throws IOException {
|
|
||||||
sendResponse(out, code, Files.readString(file.toPath()), "text/html");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void sendResponse(OutputStream out, int code, String body, String contentType) throws IOException {
|
|
||||||
sendResponse(out, code, body.getBytes(StandardCharsets.UTF_8), contentType, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void sendResponse(OutputStream out, int code, File file, String contentType) throws IOException {
|
|
||||||
byte[] bytes = Files.readAllBytes(file.toPath());
|
|
||||||
sendResponse(out, code, bytes, contentType, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void sendResponse(OutputStream out, int code, byte[] body, String contentType, Map<String, String> headers) throws IOException {
|
|
||||||
out.write(("HTTP/1.1 " + code + " " + getStatusText(code) + "\r\n").getBytes());
|
|
||||||
out.write(("Content-Type: " + contentType + "\r\n").getBytes());
|
|
||||||
out.write(("Content-Length: " + body.length + "\r\n").getBytes());
|
|
||||||
if (headers != null) headers.forEach((k, v) -> {
|
|
||||||
try { out.write((k + ": " + v + "\r\n").getBytes()); } catch (IOException ignored) {}
|
|
||||||
});
|
|
||||||
out.write("\r\n".getBytes());
|
|
||||||
out.write(body);
|
|
||||||
out.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getStatusText(int code) {
|
|
||||||
return switch (code) {
|
|
||||||
case 200 -> "OK";
|
|
||||||
case 301 -> "Moved Permanently";
|
|
||||||
case 302 -> "Found";
|
|
||||||
case 400 -> "Bad Request";
|
|
||||||
case 401 -> "Unauthorized";
|
|
||||||
case 403 -> "Forbidden";
|
|
||||||
case 404 -> "Not Found";
|
|
||||||
case 500 -> "Internal Server Error";
|
|
||||||
default -> "Unknown";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getContentType(String name) {
|
|
||||||
return switch (name.substring(name.lastIndexOf('.') + 1).toLowerCase()) {
|
|
||||||
case "html", "php" -> "text/html";
|
|
||||||
case "js" -> "text/javascript";
|
|
||||||
case "css" -> "text/css";
|
|
||||||
case "json" -> "application/json";
|
|
||||||
case "png" -> "image/png";
|
|
||||||
case "jpg", "jpeg" -> "image/jpeg";
|
|
||||||
case "mp4" -> "video/mp4";
|
|
||||||
case "mp3" -> "audio/mpeg3";
|
|
||||||
case "wav" -> "audio/wav";
|
|
||||||
case "pdf" -> "application/pdf";
|
|
||||||
default -> "text/plain";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static PHPResponse renderPHPWithCookies(File file) throws IOException, InterruptedException {
|
|
||||||
ProcessBuilder pb = new ProcessBuilder("php", file.getAbsolutePath());
|
|
||||||
pb.redirectErrorStream(true);
|
|
||||||
Process p = pb.start();
|
|
||||||
|
|
||||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
|
||||||
InputStream processIn = p.getInputStream();
|
|
||||||
byte[] buf = new byte[8192];
|
|
||||||
int read;
|
|
||||||
while ((read = processIn.read(buf)) != -1) {
|
|
||||||
output.write(buf, 0, read);
|
|
||||||
}
|
|
||||||
p.waitFor();
|
|
||||||
|
|
||||||
String fullOutput = output.toString(StandardCharsets.UTF_8);
|
|
||||||
Map<String, String> cookies = new HashMap<>();
|
|
||||||
|
|
||||||
String[] parts = fullOutput.split("\r\n\r\n", 2);
|
|
||||||
String body;
|
|
||||||
if (parts.length == 2) {
|
|
||||||
String headers = parts[0];
|
|
||||||
body = parts[1];
|
|
||||||
for (String headerLine : headers.split("\r\n")) {
|
|
||||||
if (headerLine.toLowerCase().startsWith("set-cookie:")) {
|
|
||||||
String cookie = headerLine.substring("set-cookie:".length()).trim();
|
|
||||||
String[] kv = cookie.split(";", 2);
|
|
||||||
String[] pair = kv[0].split("=", 2);
|
|
||||||
if (pair.length == 2) cookies.put(pair[0], pair[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
body = fullOutput;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PHPResponse(body, cookies);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class PHPResponse {
|
|
||||||
String body;
|
|
||||||
Map<String, String> cookies;
|
|
||||||
|
|
||||||
public PHPResponse(String body, Map<String, String> cookies) {
|
|
||||||
this.body = body;
|
|
||||||
this.cookies = cookies;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
package github.openautonomousconnection.webserver;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
import java.io.File;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class RuleManager {
|
|
||||||
private static List<String> allow;
|
|
||||||
private static List<String> deny;
|
|
||||||
private static List<String> auth;
|
|
||||||
|
|
||||||
public static void loadRules(File rulesFile) throws Exception {
|
|
||||||
String json = new String(Files.readAllBytes(rulesFile.toPath()));
|
|
||||||
Map<String, List<String>> map = new Gson().fromJson(json, new TypeToken<Map<String, List<String>>>(){}.getType());
|
|
||||||
allow = map.getOrDefault("allow", List.of());
|
|
||||||
deny = map.getOrDefault("deny", List.of());
|
|
||||||
auth = map.getOrDefault("auth", List.of());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isAllowed(String path) {
|
|
||||||
return allow.stream().anyMatch(p -> pathMatches(path, p));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isDenied(String path) {
|
|
||||||
return deny.stream().anyMatch(p -> pathMatches(path, p));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean requiresAuth(String path) {
|
|
||||||
return auth.stream().anyMatch(p -> pathMatches(path, p));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean pathMatches(String path, String pattern) {
|
|
||||||
pattern = pattern.replace("/", File.separator).replace("*", ".*");
|
|
||||||
return path.matches(pattern);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
package github.openautonomousconnection.webserver;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
public class SessionManager {
|
|
||||||
|
|
||||||
private static final Map<String, Session> sessions = new ConcurrentHashMap<>();
|
|
||||||
private static final SecureRandom secureRandom = new SecureRandom();
|
|
||||||
|
|
||||||
private static class Session {
|
|
||||||
@Getter
|
|
||||||
String login;
|
|
||||||
String ip;
|
|
||||||
String userAgent;
|
|
||||||
long expiresAt;
|
|
||||||
|
|
||||||
Session(String login, String ip, String userAgent) throws IOException {
|
|
||||||
this.login = login;
|
|
||||||
this.ip = ip;
|
|
||||||
this.userAgent = userAgent;
|
|
||||||
this.expiresAt = System.currentTimeMillis() + (long) Main.getConfigurationManager().getInt("sessionexpireminutes") * 60 * 1000;;;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isExpired() {
|
|
||||||
return System.currentTimeMillis() > expiresAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean matches(String ip, String userAgent) {
|
|
||||||
return this.ip.equals(ip) && this.userAgent.equals(userAgent);
|
|
||||||
}
|
|
||||||
|
|
||||||
void refresh() throws IOException {
|
|
||||||
this.expiresAt = System.currentTimeMillis() + (long) Main.getConfigurationManager().getInt("sessionexpireminutes") * 60 * 1000;;;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String create(String login, String ip, String userAgent) throws IOException {
|
|
||||||
byte[] bytes = new byte[32];
|
|
||||||
secureRandom.nextBytes(bytes);
|
|
||||||
String sessionId = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
|
|
||||||
sessions.put(sessionId, new Session(login, ip, userAgent));
|
|
||||||
return sessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isValid(String sessionId, String ip, String userAgent) throws IOException {
|
|
||||||
Session session = sessions.get(sessionId);
|
|
||||||
if (session == null || session.isExpired() || !session.matches(ip, userAgent)) {
|
|
||||||
sessions.remove(sessionId);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
session.refresh();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void invalidate(String sessionId) {
|
|
||||||
sessions.remove(sessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getUser(String sessionId) {
|
|
||||||
Session session = sessions.get(sessionId);
|
|
||||||
if (session == null || session.isExpired()) {
|
|
||||||
sessions.remove(sessionId);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return session.getLogin();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void cleanupExpiredSessions() {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
sessions.entrySet().removeIf(entry -> entry.getValue().isExpired());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package github.openautonomousconnection.webserver.api;
|
||||||
|
|
||||||
|
public interface WebModule {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Path this module handles (e.g. "/api/user")
|
||||||
|
*/
|
||||||
|
String path();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a web request.
|
||||||
|
*/
|
||||||
|
WebResponsePacket handle(ConnectedWebClient client, WebRequestPacket request) throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user