Web API: module ported (not tested!)
This commit is contained in:
@@ -1,32 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<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
|
||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<name>Web API</name>
|
||||
|
||||
<artifactId>web_api</artifactId>
|
||||
<version>0.1-SNAPSHOT</version>
|
||||
<packaging>bundle</packaging>
|
||||
|
||||
<parent>
|
||||
<artifactId>arcadexsystem</artifactId>
|
||||
<groupId>eu.arcadex.system</groupId>
|
||||
<version>${global.version}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>web_api</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>eu.arcadex.system</groupId>
|
||||
<artifactId>database</artifactId>
|
||||
<version>${global.version}</version>
|
||||
<artifactId>core</artifactId>
|
||||
<version>0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>eu.arcadex.system</groupId>
|
||||
<artifactId>configuration</artifactId>
|
||||
<version>${global.version}</version>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
<version>9.3.6.v20151106</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>eu.arcadex.system</groupId>
|
||||
<artifactId>server_manager</artifactId>
|
||||
<version>${global.version}</version>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.3.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${groupId}.${artifactId}-${version}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<version>2.3.5</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Bundle-Name>Arcadex System: ${name} ${version}</Bundle-Name>
|
||||
<Bundle-SymbolicName>${groupId}.${artifactId}</Bundle-SymbolicName>
|
||||
<Bundle-Activator>eu.arcadex.system.web_api.Activator</Bundle-Activator>
|
||||
<Import-Package>eu.arcadex.system.core.api, *</Import-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,30 @@
|
||||
package eu.arcadex.system.web_api;
|
||||
|
||||
import org.osgi.framework.BundleActivator;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
import eu.arcadex.system.core.api.ICore;
|
||||
|
||||
/**
|
||||
* @author DmitriyMX <mail@dmitriymx.ru>
|
||||
* 2016
|
||||
*/
|
||||
public class Activator implements BundleActivator {
|
||||
private ServiceTracker<?, ICore> tracker;
|
||||
private WebModule webModule;
|
||||
|
||||
@Override
|
||||
public void start(BundleContext bundleContext) throws Exception {
|
||||
tracker = new ServiceTracker(bundleContext, ICore.class.getName(), null);
|
||||
tracker.open();
|
||||
|
||||
webModule = new WebModule(tracker);
|
||||
webModule.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(BundleContext bundleContext) throws Exception {
|
||||
webModule.stop();
|
||||
tracker.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
package eu.arcadex.system.web_api;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Daniil on 19.03.2016.
|
||||
* @author DmitriyMX <mail@dmitriymx.ru> 2016 (порт на ArcadexSystem Reborn)
|
||||
*/
|
||||
public class RequestHandler extends AbstractHandler {
|
||||
private final DatabaseModule database;
|
||||
private final ArcadexSystem system;
|
||||
private final Gson gson;
|
||||
private Logger logger = LoggerFactory.getLogger(RequestHandler.class.getName());
|
||||
private final String dbKey = "jhkljdsjklfjkljdasf";
|
||||
private final String apiKey = "hkdhaskhafasdf";
|
||||
private List<String> blackList = new ArrayList<>();
|
||||
|
||||
|
||||
protected String md5(String origin) {
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
return "";
|
||||
}
|
||||
md.update(origin.getBytes());
|
||||
byte[] digest = md.digest();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (byte b : digest) {
|
||||
sb.append(String.format("%02x", b & 0xff));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getRequestFingerprint(Request request, String body, String url, String apiKey) {
|
||||
String text = apiKey + ":" + request.getMethod() + ":" + url + ":" + (request.getMethod().equalsIgnoreCase("POST") ? body + ":" + apiKey : apiKey);
|
||||
System.out.println("Fingerprint text: " + text);
|
||||
System.out.println("Fingerprint: " + md5(text));
|
||||
return md5(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(String url, Request request, HttpServletRequest httpRequest, HttpServletResponse response) throws IOException, ServletException {
|
||||
response.setContentType("text/html;charset=utf-8");
|
||||
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||
response.setHeader("Access-Control-Allow-Headers", "Authorization");
|
||||
request.setHandled(true);
|
||||
if (!request.getMethod().equalsIgnoreCase("OPTIONS")) {
|
||||
Response response1 = handleInternal(url, request, httpRequest, response);
|
||||
response.getWriter().println(response1.toJson(gson));
|
||||
} else {
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
response.getWriter().println(new Response("success", "Ok").toJson(gson));
|
||||
}
|
||||
}
|
||||
|
||||
protected Response handleInternal(String url, Request request, HttpServletRequest httpRequest, HttpServletResponse response) throws IOException, ServletException {
|
||||
try {
|
||||
if (url.trim().startsWith("/db/")) {
|
||||
// Form handling and authorization
|
||||
|
||||
String postData = "";
|
||||
if (request.getMethod().equalsIgnoreCase("POST")) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String aux;
|
||||
|
||||
while ((aux = httpRequest.getReader().readLine()) != null) {
|
||||
builder.append(aux);
|
||||
}
|
||||
|
||||
postData = builder.toString();
|
||||
}
|
||||
|
||||
if (request.getHeader("Authorization") == null ||
|
||||
!request.getHeader("Authorization").equals("Basic " + getRequestFingerprint(request, postData, url, dbKey))) {
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
return new Response("failed", "Error: missing or wrong Authorization header");
|
||||
}
|
||||
|
||||
// Here comes actual DB listener
|
||||
|
||||
String key = url.substring(4).trim();
|
||||
if (request.getMethod().equalsIgnoreCase("GET")) {
|
||||
// Methods for reading db values
|
||||
if (!database.containsKey(key)) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return (new Response("error", "Error: key not found"));
|
||||
} else {
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
return (new Response("success", database.get(key)));
|
||||
}
|
||||
} else if (request.getMethod().equalsIgnoreCase("POST")) {
|
||||
database.set(key, postData);
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
return (new Response("success", "Ok"));
|
||||
} else if (request.getMethod().equalsIgnoreCase("DELETE")) {
|
||||
if (!database.containsKey(key)) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return (new Response("error", "Error: key not found"));
|
||||
} else {
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
database.remove(key);
|
||||
return (new Response("success", "Ok"));
|
||||
}
|
||||
} else {
|
||||
response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
|
||||
return new Response("error", "Error: this method is not supported for RESTful database");
|
||||
}
|
||||
|
||||
} else if (url.trim().startsWith("/api/")) {
|
||||
return handleApiRequests(url, request, httpRequest, response);
|
||||
} else {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return (new Response("error", "Error: action not found"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
return (new Response("error", "Error: internal server error"));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public Response handleApiRequests(String url, Request request, HttpServletRequest httpRequest, HttpServletResponse response) throws IOException, ServletException {
|
||||
// Auth check
|
||||
if (request.getHeader("Authorization") == null) {
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
return (new Response("failed", "Error: missing Authorization header"));
|
||||
}
|
||||
|
||||
String[] general = request.getHeader("Authorization").substring(6).trim().split(":");
|
||||
if (general.length != 2) {
|
||||
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||
return (new Response("failed", "Error: malformed Authorization header"));
|
||||
}
|
||||
|
||||
String userName = general[0];
|
||||
String hash = general[1];
|
||||
|
||||
if (!md5(apiKey + ":" + userName + ":" + apiKey).equals(hash)) {
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
return (new Response("failed", "Error: wrong Authorization header"));
|
||||
}
|
||||
|
||||
if (blackList.contains(userName)) {
|
||||
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||
return (new Response("failed", "Error: this user is forbidden to connect this server"));
|
||||
}
|
||||
|
||||
if (!request.getMethod().equalsIgnoreCase("POST")) {
|
||||
response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
|
||||
return (new Response("failed", "Error: all API requests are done by POST"));
|
||||
}
|
||||
|
||||
// General methods
|
||||
url = url.substring(5);
|
||||
if (url.startsWith("check_login")) {
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
logger.info("Login accepted: " + userName);
|
||||
return (new Response("success", "Ok"));
|
||||
}
|
||||
|
||||
if (url.startsWith("list_servers")) {
|
||||
JsonArray array = new JsonArray();
|
||||
for (ServerData srv : new ArrayList<>(system.getServerManager().getServerList())) {
|
||||
JsonObject object = new JsonObject();
|
||||
object.addProperty("id", srv.getId());
|
||||
object.addProperty("type", srv.getType());
|
||||
object.addProperty("modification", srv.getModification());
|
||||
object.addProperty("status", srv.getStatus().toString());
|
||||
object.addProperty("canAccept", srv.canAcceptPlayers());
|
||||
object.addProperty("port", srv.getPort());
|
||||
object.addProperty("online", srv.getOnline());
|
||||
object.addProperty("max", srv.getMaxOnline());
|
||||
array.add(object);
|
||||
}
|
||||
return new Response("success", array);
|
||||
}
|
||||
|
||||
if (url.startsWith("stop_server")) {
|
||||
String sid = url.substring(12);
|
||||
ServerData serverData = system.getServerManager().getServerById(sid);
|
||||
if (serverData == null) {
|
||||
return new Response("error", "Error: server not found");
|
||||
}
|
||||
serverData.getWorker().stopServer();
|
||||
return new Response("success", "Ok");
|
||||
}
|
||||
|
||||
if (url.startsWith("change_variables")) {
|
||||
String sid = url.substring(17);
|
||||
ServerData serverData = system.getServerManager().getServerById(sid);
|
||||
if (serverData == null) {
|
||||
return new Response("error", "Error: server not found");
|
||||
}
|
||||
|
||||
if (request.getParameter("status") != null) {
|
||||
try {
|
||||
serverData.setStatus(ServerData.ServerStatus.valueOf(request.getParameter("status")));
|
||||
} catch (Exception e) {
|
||||
return new Response("error", "Error: unknown server status '" + request.getParameter("status") + "'!");
|
||||
}
|
||||
}
|
||||
|
||||
if (request.getParameter("policy") != null) {
|
||||
try {
|
||||
serverData.setServerJoinPolicy(ServerData.ServerJoinMode.valueOf(request.getParameter("policy")));
|
||||
} catch (Exception e) {
|
||||
return new Response("error", "Error: unknown server join policy '" + request.getParameter("policy") + "'!");
|
||||
}
|
||||
}
|
||||
|
||||
if (request.getParameter("frozen") != null) {
|
||||
serverData.setFrozen(request.getParameter("frozen").equals("true") || request.getParameter("frozen").equals("1"));
|
||||
}
|
||||
|
||||
return new Response("success", "Ok");
|
||||
}
|
||||
|
||||
if (url.startsWith("full_information")) {
|
||||
String sid = url.substring(17);
|
||||
ServerData serverData = system.getServerManager().getServerById(sid);
|
||||
if (serverData == null) {
|
||||
return new Response("error", "Error: server not found");
|
||||
}
|
||||
|
||||
JsonObject object = new JsonObject();
|
||||
object.addProperty("id", serverData.getId());
|
||||
object.addProperty("type", serverData.getType());
|
||||
object.addProperty("modification", serverData.getModification());
|
||||
object.addProperty("status", serverData.getStatus().toString());
|
||||
object.addProperty("canAccept", serverData.canAcceptPlayers());
|
||||
object.addProperty("port", serverData.getPort());
|
||||
object.addProperty("online", serverData.getOnline());
|
||||
object.addProperty("max", serverData.getMaxOnline());
|
||||
object.addProperty("frozen", serverData.isFrozen());
|
||||
object.addProperty("policy", serverData.getServerJoinPolicy().toString());
|
||||
JsonArray array = new JsonArray();
|
||||
array.add(new JsonPrimitive("This operation is not supported"));
|
||||
|
||||
object.add("players", array);
|
||||
|
||||
return new Response("success", object);
|
||||
}
|
||||
|
||||
if (url.startsWith("stop")) {
|
||||
String sid = url.substring(5);
|
||||
ServerData serverData = system.getServerManager().getServerById(sid);
|
||||
if (serverData == null) {
|
||||
return new Response("error", "Error: server not found");
|
||||
}
|
||||
|
||||
serverData.getWorker().stopServer();
|
||||
return new Response("success", "Ok");
|
||||
}
|
||||
|
||||
if (url.startsWith("freeze")) {
|
||||
String sid = url.substring("freeze".length() + 1);
|
||||
ServerData serverData = system.getServerManager().getServerById(sid);
|
||||
if (serverData == null) {
|
||||
return new Response("error", "Error: server not found");
|
||||
}
|
||||
serverData.setFrozen(!serverData.isFrozen());
|
||||
}
|
||||
|
||||
if (url.startsWith("command")) {
|
||||
String sid = url.substring("command".length() + 1);
|
||||
ServerData serverData = system.getServerManager().getServerById(sid);
|
||||
if (serverData == null) {
|
||||
return new Response("error", "Error: server not found");
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String aux;
|
||||
|
||||
while ((aux = httpRequest.getReader().readLine()) != null) {
|
||||
builder.append(aux);
|
||||
}
|
||||
|
||||
String postData = builder.toString();
|
||||
serverData.getWorker().consoleCommand(postData);
|
||||
return new Response("success", "Ok");
|
||||
}
|
||||
|
||||
return (new Response("success", "Not implemented yet"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package eu.arcadex.system.web_api;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
/**
|
||||
* @author Daniil 05.11.2015
|
||||
* @author DmitriyMX <mail@dmitriymx.ru> 2016 (порт на ArcadexSystem Reborn)
|
||||
*/
|
||||
public class Response {
|
||||
private String status;
|
||||
private JsonElement response;
|
||||
|
||||
public Response(String status, JsonElement response) {
|
||||
this.status = status;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public Response(String status, String response) {
|
||||
this.status = status;
|
||||
this.response = new JsonPrimitive(response);
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public JsonElement getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public void setResponse(JsonElement response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public String toJson(Gson gson) {
|
||||
return gson.toJson(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package eu.arcadex.system.web_api;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import eu.arcadex.system.core.api.ICore;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Модуль для поддержки Web-интерфейса ArcadexSystem
|
||||
*
|
||||
* @author Daniil 2015
|
||||
* @author DmitriyMX <mail@dmitriymx.ru> 2016 (порт на ArcadexSystem Reborn)
|
||||
*/
|
||||
class WebModule {
|
||||
private Logger logger = LoggerFactory.getLogger(WebModule.class.getName());
|
||||
private ICore core;
|
||||
private Server server;
|
||||
private final int port = 8090;
|
||||
|
||||
WebModule(ServiceTracker<?, ICore> tracker) {
|
||||
core = tracker.getService();
|
||||
if (core == null) {
|
||||
throw new RuntimeException("Service \"ICore\" not found!");
|
||||
}
|
||||
}
|
||||
|
||||
void init() {
|
||||
logger.trace("Initializing WEB-server on localhost: {}", port);
|
||||
|
||||
server = new Server(port);
|
||||
server.setStopAtShutdown(true);
|
||||
server.setHandler(new RequestHandler((DatabaseModule) system.getModule("database"), system, new Gson(), system.getLogger()));
|
||||
|
||||
try {
|
||||
server.start();
|
||||
} catch (Exception e) {
|
||||
logger.error("Error start JettyServer", e);
|
||||
}
|
||||
}
|
||||
|
||||
void stop() {
|
||||
try {
|
||||
server.stop();
|
||||
} catch (Exception e) {
|
||||
logger.error("Error stop JettyServer", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user