Archived
0

Web API: module ported (not tested!)

This commit is contained in:
2016-04-06 16:31:10 +03:00
parent 57c03d3323
commit 8c3f49c97b
5 changed files with 466 additions and 15 deletions

View File

@@ -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>

View File

@@ -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();
}
}

View File

@@ -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"));
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}