mirror of
https://gitee.com/BDWare/router-backend
synced 2025-01-10 09:54:04 +00:00
feat: add FileActions handleUploadRequest
This commit is contained in:
parent
d08bf2ec86
commit
f84177c850
@ -24,6 +24,7 @@ import org.apache.logging.log4j.core.config.Configurator;
|
||||
import org.bdware.sc.DoConfig;
|
||||
import org.bdware.sc.db.KeyValueDBUtil;
|
||||
import org.bdware.sc.db.MultiIndexTimeRocksDBUtil;
|
||||
import org.bdware.sc.db.TimeDBUtil;
|
||||
import org.bdware.server.irp.LocalLHSProxy;
|
||||
import org.bdware.server.nodecenter.*;
|
||||
import org.bdware.server.ws.DelimiterCodec;
|
||||
@ -50,6 +51,7 @@ public class NodeCenterServer {
|
||||
static {
|
||||
KeyValueDBUtil.setupNC();
|
||||
// TimeDBUtil.setupNC();
|
||||
TimeDBUtil.setupNC();
|
||||
Configurator.setLevel(
|
||||
"io.netty.handler.codec.http.websocketx.WebSocket08FrameDecoder",
|
||||
Level.OFF);
|
||||
|
395
src/main/java/org/bdware/server/nodecenter/FileActions.java
Normal file
395
src/main/java/org/bdware/server/nodecenter/FileActions.java
Normal file
@ -0,0 +1,395 @@
|
||||
package org.bdware.server.nodecenter;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.QueryStringDecoder;
|
||||
import io.netty.handler.codec.http.multipart.*;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bdware.sc.conn.ResultCallback;
|
||||
import org.bdware.sc.db.CMTables;
|
||||
import org.bdware.sc.db.KeyValueDBUtil;
|
||||
import org.bdware.sc.db.TimeDBUtil;
|
||||
import org.bdware.sc.util.JsonUtil;
|
||||
import org.bdware.server.action.Action;
|
||||
import org.bdware.server.http.HttpMethod;
|
||||
import org.bdware.server.http.URIPath;
|
||||
import org.bdware.server.permission.Role;
|
||||
import org.zz.gmhelper.SM2Util;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.*;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
|
||||
|
||||
public class FileActions {
|
||||
private static final Logger LOGGER = LogManager.getLogger(FileActions.class);
|
||||
private static final Set<String> TEXT_FILE_SUFFIXES = new HashSet<>();
|
||||
public NodeCenterFrameHandler controller;
|
||||
static Map<String, FileOutputStream> fileMap = new HashMap<>();
|
||||
static Map<String, Long> updateTime = new HashMap<>();
|
||||
|
||||
public FileActions(NodeCenterFrameHandler nodeCenterFrameHandler) {
|
||||
controller = nodeCenterFrameHandler;
|
||||
}
|
||||
|
||||
static NodeCenterWSFrameHandler handler;
|
||||
|
||||
public static boolean isLocked(String pubKey) {
|
||||
String ret = KeyValueDBUtil.instance.getValue(CMTables.LockedUser.toString(), pubKey);
|
||||
return ret != null && ret.equals("locked");
|
||||
}
|
||||
|
||||
@URIPath(
|
||||
method = org.bdware.server.http.HttpMethod.POST,
|
||||
value = {"/upload"})
|
||||
public static void handleUploadRequest(ChannelHandlerContext ctx, FullHttpRequest request) {
|
||||
// logger.info("[CMHttpHandler] handleUploadRequest : ");
|
||||
// Upload method is POST
|
||||
|
||||
QueryStringDecoder decoderQuery = new QueryStringDecoder(request.uri());
|
||||
Map<String, List<String>> params = decoderQuery.parameters();
|
||||
Map<String, String> transformedParam = new HashMap<>();
|
||||
for (String key : params.keySet()) {
|
||||
List<String> val = params.get(key);
|
||||
if (val != null) {
|
||||
transformedParam.put(key, val.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
if (!transformedParam.containsKey("path")
|
||||
|| !transformedParam.containsKey("fileName")
|
||||
|| !transformedParam.containsKey("isPrivate")
|
||||
|| !transformedParam.containsKey("order")
|
||||
|| !transformedParam.containsKey("count")
|
||||
|| !transformedParam.containsKey("pubKey")
|
||||
|| !transformedParam.containsKey("sign")) {
|
||||
DefaultFullHttpResponse fullResponse =
|
||||
new DefaultFullHttpResponse(
|
||||
request.protocolVersion(),
|
||||
OK,
|
||||
Unpooled.wrappedBuffer(
|
||||
"{\"status\":\"false\",\"data\":\"Missing argument!\"}"
|
||||
.getBytes()));
|
||||
|
||||
ChannelFuture f = ctx.write(fullResponse);
|
||||
f.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
|
||||
// 验签
|
||||
// HttpMethod method = request.method();
|
||||
String uri =
|
||||
URLDecoder.decode(request.uri())
|
||||
.split("\\?")[1]; // http请求中规定签名必须是最后一个且公钥名必须为pubKey,否则验签失败
|
||||
int index = uri.lastIndexOf('&');
|
||||
String str = uri.substring(0, index);
|
||||
// logger.info("uri=" + uri);
|
||||
// logger.info("str=" + str);
|
||||
|
||||
long permission;
|
||||
String pubkey = transformedParam.get("pubKey");
|
||||
String sign = transformedParam.get("sign");
|
||||
// logger.info("pubKey " + pubkey);
|
||||
// logger.info("sign " + sign);
|
||||
// logger.info("toVerify " + str);
|
||||
boolean verify = SM2Util.plainStrVerify(pubkey, str, sign);
|
||||
|
||||
LOGGER.info("[CMHttpHandler] upload http请求验签结果 : " + verify);
|
||||
|
||||
if (verify) {
|
||||
// 查permission
|
||||
String ret = KeyValueDBUtil.instance.getValue(NCTables.NodeUser.toString(), pubkey);
|
||||
|
||||
if (ret != null && ret.length() > 0) {
|
||||
permission = 0x86000d41L | Role.compoundValue(ret.split(","));
|
||||
} else {
|
||||
assert ret != null;
|
||||
permission = Role.compoundValue(ret.split(","));
|
||||
}
|
||||
} else {
|
||||
permission = 0;
|
||||
}
|
||||
|
||||
// 验权限
|
||||
Method mm;
|
||||
Action a = null;
|
||||
try {
|
||||
mm =
|
||||
FileActions.class.getDeclaredMethod(
|
||||
"uploadFile", JsonObject.class, ResultCallback.class);
|
||||
a = mm.getAnnotation(Action.class);
|
||||
} catch (SecurityException | NoSuchMethodException e1) {
|
||||
// TODO Auto-generated catch block
|
||||
e1.printStackTrace();
|
||||
}
|
||||
|
||||
assert a != null;
|
||||
boolean flag = a.httpAccess();
|
||||
long val = a.userPermission(); // 使用者必须达到的permission
|
||||
|
||||
// logger.info("htttttttttttttp--request permission:" + val);
|
||||
// logger.info("htttttttttttttp--permission:" + permission); //
|
||||
// 现有的permission
|
||||
|
||||
boolean flag2 = false; // 启动http权限后应改为false
|
||||
String status = "refuse";
|
||||
String action = "uploadRequest";
|
||||
|
||||
if (val == 0) {
|
||||
flag2 = true;
|
||||
} else if ((permission & val) == val) {
|
||||
LOGGER.debug((permission & val));
|
||||
flag2 = true;
|
||||
}
|
||||
|
||||
if (flag && flag2 && !isLocked(pubkey)) {
|
||||
status = "accept";
|
||||
}
|
||||
|
||||
TimeDBUtil.instance.put(
|
||||
NCTables.NodeHttpLog.toString(),
|
||||
"{\"action\":\""
|
||||
+ action
|
||||
+ "\",\"pubKey\":\""
|
||||
+ transformedParam.get("pubKey")
|
||||
+ "\",\"status\":\""
|
||||
+ status
|
||||
+ "\",\"date\":"
|
||||
+ System.currentTimeMillis()
|
||||
+ "}");
|
||||
|
||||
// logger.info("[CMHttpHandler] flag = " + flag + " flag2 = " + flag2);
|
||||
|
||||
if (!flag || !flag2) {
|
||||
DefaultFullHttpResponse fullResponse =
|
||||
new DefaultFullHttpResponse(
|
||||
request.protocolVersion(),
|
||||
OK,
|
||||
Unpooled.wrappedBuffer(
|
||||
"{\"status\":\"false\",\"data\":\"Permission denied!\"}"
|
||||
.getBytes()));
|
||||
|
||||
ChannelFuture f = ctx.write(fullResponse);
|
||||
f.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
|
||||
String fileName = transformedParam.get("fileName");
|
||||
String dirName = transformedParam.get("path");
|
||||
int order = Integer.parseInt(transformedParam.get("order"));
|
||||
int count = Integer.parseInt(transformedParam.get("count"));
|
||||
HttpPostRequestDecoder httpDecoder =
|
||||
new HttpPostRequestDecoder(new DefaultHttpDataFactory(true), request);
|
||||
httpDecoder.setDiscardThreshold(0);
|
||||
File dir;
|
||||
boolean isPrivate =
|
||||
transformedParam.containsKey("isPrivate")
|
||||
&& Boolean.parseBoolean(transformedParam.get("isPrivate"));
|
||||
|
||||
// if (isPrivate) {
|
||||
// String pub = "/" + transformedParam.get("pubKey");
|
||||
// dir = new File(GlobalConf.instance.projectDir + "/private" + pub, dirName);
|
||||
// } else {
|
||||
// dir = new File(GlobalConf.instance.publicDir, dirName);
|
||||
// }
|
||||
|
||||
String pub = "/" + transformedParam.get("pubKey");
|
||||
dir = new File("./router" + pub, dirName);
|
||||
if (!dir.isDirectory()) {
|
||||
dir = dir.getParentFile();
|
||||
}
|
||||
File target = new File(dir, fileName);
|
||||
if (fileName.contains("..")) {
|
||||
DefaultFullHttpResponse fullResponse =
|
||||
new DefaultFullHttpResponse(
|
||||
request.protocolVersion(),
|
||||
OK,
|
||||
Unpooled.wrappedBuffer(
|
||||
"{\"status\":\"false\",\"data\":\"FileName illegal!\"}"
|
||||
.getBytes()));
|
||||
|
||||
ChannelFuture f = ctx.write(fullResponse);
|
||||
f.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
// LOGGER.info(request.refCnt());
|
||||
// httpDecoder.offer(request);
|
||||
// LOGGER.info(request.refCnt());
|
||||
|
||||
FileOutputStream fout;
|
||||
if (order == 0) {
|
||||
try {
|
||||
LOGGER.debug("Path:" + target.getAbsolutePath());
|
||||
if (!target.getParentFile().exists()) target.getParentFile().mkdirs();
|
||||
fout = new FileOutputStream(target, false);
|
||||
fileMap.put(target.getAbsolutePath(), fout);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
updateTime.put(target.getAbsolutePath(), System.currentTimeMillis());
|
||||
writeChunk(ctx, httpDecoder, target);
|
||||
|
||||
httpDecoder.destroy();
|
||||
String retStr = "{\"status\":\"true\",\"data\":\"success\",\"handle\":\"null\"}";
|
||||
if (order == count - 1) {
|
||||
fout = fileMap.get(target.getAbsolutePath());
|
||||
try {
|
||||
fout.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
fileMap.remove(target.getAbsolutePath());
|
||||
updateTime.remove(target.getAbsolutePath());
|
||||
// String doi = unzipIfYpk(target, dir);
|
||||
String doi = null;
|
||||
if (null != doi) {
|
||||
retStr = retStr.replaceFirst("null", doi);
|
||||
}
|
||||
}
|
||||
DefaultFullHttpResponse fullResponse =
|
||||
new DefaultFullHttpResponse(
|
||||
request.protocolVersion(), OK, Unpooled.wrappedBuffer(retStr.getBytes()));
|
||||
fullResponse.headers().add("Access-Control-Allow-Origin", "*");
|
||||
ChannelFuture f = ctx.write(fullResponse);
|
||||
f.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
private static void writeChunk(
|
||||
ChannelHandlerContext ctx, HttpPostRequestDecoder httpDecoder, File file) {
|
||||
try {
|
||||
if (!file.exists()) {
|
||||
file.createNewFile();
|
||||
}
|
||||
FileOutputStream fout = fileMap.get(file.getAbsolutePath());
|
||||
while (httpDecoder.hasNext()) {
|
||||
InterfaceHttpData data = httpDecoder.next();
|
||||
|
||||
if (null != data) {
|
||||
InputStream input;
|
||||
if (InterfaceHttpData.HttpDataType.FileUpload.equals(data.getHttpDataType())) {
|
||||
final FileUpload fileUpload = (FileUpload) data;
|
||||
input = new FileInputStream(fileUpload.getFile());
|
||||
} else {
|
||||
final DiskAttribute diskAttribute = (DiskAttribute) data;
|
||||
input = new ByteArrayInputStream(diskAttribute.get());
|
||||
}
|
||||
transfer(input, fout);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@URIPath(method = HttpMethod.OPTIONS)
|
||||
public static void crossOrigin(ChannelHandlerContext ctx, FullHttpRequest request) {
|
||||
DefaultFullHttpResponse fullResponse =
|
||||
new DefaultFullHttpResponse(
|
||||
request.protocolVersion(),
|
||||
OK,
|
||||
Unpooled.wrappedBuffer("success".getBytes()));
|
||||
fullResponse.headers().remove("Access-Control-Allow-Origin");
|
||||
fullResponse.headers().remove("Access-Control-Allow-Headers");
|
||||
fullResponse.headers().add("Access-Control-Allow-Origin", "*");
|
||||
fullResponse
|
||||
.headers()
|
||||
.add("Access-Control-Allow-Headers",
|
||||
"Content-Type, Cookie, Accept-Encoding, User-Agent, Host, Referer, " +
|
||||
"X-Requested-With, Accept, Accept-Language, Cache-Control, Connection");
|
||||
ChannelFuture f = ctx.write(fullResponse);
|
||||
f.addListener(ChannelFutureListener.CLOSE);
|
||||
LOGGER.info("[OOOOOOOOption] received!");
|
||||
}
|
||||
|
||||
private static void transfer(InputStream input, FileOutputStream fout) throws IOException {
|
||||
byte[] buff = new byte[1024 * 51];
|
||||
int k;
|
||||
while ((k = input.read(buff)) > 0) {
|
||||
fout.write(buff, 0, k);
|
||||
}
|
||||
}
|
||||
|
||||
// 参数:isAppend
|
||||
// 参数:content
|
||||
@Action(userPermission = 0)
|
||||
public static void uploadFile(JsonObject json, ResultCallback resultCallback) throws Exception {
|
||||
Response response = new Response();
|
||||
response.action = "onUploadFile";
|
||||
response.data = "failed";
|
||||
response.isPrivate = false;
|
||||
|
||||
String fileName = json.get("fileName").getAsString();
|
||||
if (!checkFileType(fileName)) {
|
||||
response.data = "Illegal file";
|
||||
resultCallback.onResult(JsonUtil.toJson(response));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
boolean isAppend = json.get("isAppend").getAsBoolean();
|
||||
String path = json.get("path").getAsString();
|
||||
|
||||
String content = json.get("content").getAsString();
|
||||
File target;
|
||||
|
||||
String parPath;
|
||||
if (json.has("isPrivate") && json.get("isPrivate").getAsBoolean()) {
|
||||
response.isPrivate = true;
|
||||
parPath = "./router/" + "/" + handler.getPubKey();
|
||||
} else {
|
||||
parPath = "./router";
|
||||
}
|
||||
|
||||
target = new File(parPath, path);
|
||||
if (!target.isDirectory()) {
|
||||
target = target.getParentFile();
|
||||
}
|
||||
|
||||
// 文本文件
|
||||
if (!path.contains("..") && isTextFile(path)) {
|
||||
LOGGER.debug("[FileActions] 上传文本文件类型 : ");
|
||||
BufferedWriter bw =
|
||||
new BufferedWriter(
|
||||
new FileWriter(
|
||||
target.getAbsolutePath() + "/" + fileName, isAppend));
|
||||
bw.write(content);
|
||||
bw.close();
|
||||
} else { // 其他类型文件
|
||||
LOGGER.debug("[FileActions] 上传其他文件类型 : ");
|
||||
String b64 = content.split(",")[1];
|
||||
byte[] data = Base64.getDecoder().decode(b64);
|
||||
OutputStream out =
|
||||
new FileOutputStream(target.getAbsolutePath() + "/" + fileName, isAppend);
|
||||
out.write(data);
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
response.data = "success";
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
resultCallback.onResult(JsonUtil.toJson(response));
|
||||
}
|
||||
|
||||
private static boolean checkFileType(String fileName) {
|
||||
if (fileName.contains("..")) {
|
||||
return false;
|
||||
}
|
||||
String[] part_slash = fileName.split("/");
|
||||
String[] part_dot = part_slash[part_slash.length - 1].split(".");
|
||||
String suffix = part_dot[part_dot.length - 1];
|
||||
// 可能为恶意文件的后缀
|
||||
return !suffix.equals("exe") && !suffix.equals("jdb") && !suffix.equals("sh");
|
||||
}
|
||||
|
||||
private static boolean isTextFile(String path) {
|
||||
return TEXT_FILE_SUFFIXES.contains(path.substring(path.lastIndexOf(".")));
|
||||
}
|
||||
}
|
@ -105,6 +105,7 @@ public class NCHttpHandler extends SimpleChannelInboundHandler<HttpObject> {
|
||||
handler = new URIHandler();
|
||||
handler.register(this);
|
||||
handler.register(fileAdapter);
|
||||
handler.register(FileActions.class);
|
||||
handler.printURIHandlers();
|
||||
}
|
||||
|
||||
|
@ -27,16 +27,18 @@ public class NodeCenterFrameHandler extends SimpleChannelInboundHandler<Object>
|
||||
public String pubKey;
|
||||
NodeCenterActions actions;
|
||||
MasterActions masterActions;
|
||||
FileActions fileActions;
|
||||
ActionExecutor<ResultCallback, JsonObject> ae;
|
||||
ChannelHandlerContext ctx;
|
||||
|
||||
public NodeCenterFrameHandler() {
|
||||
actions = new NodeCenterActions(this);
|
||||
fileActions = new FileActions(this);
|
||||
MetaIndexAction.controller = this;
|
||||
// TODO 添加那个UnitAction.
|
||||
ae =
|
||||
new ActionExecutor<ResultCallback, JsonObject>(
|
||||
executorService, actions, new MetaIndexAction()) {
|
||||
executorService, actions, fileActions, new MetaIndexAction()) {
|
||||
@Override
|
||||
public boolean checkPermission(
|
||||
Action a, final JsonObject args, long permission) {
|
||||
|
@ -69,6 +69,10 @@ public class NodeCenterWSFrameHandler extends SimpleChannelInboundHandler<WebSoc
|
||||
return ae;
|
||||
}
|
||||
|
||||
public String getPubKey() {
|
||||
return managerAction.pubKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(final ChannelHandlerContext ctx, WebSocketFrame frame)
|
||||
throws Exception {
|
||||
|
Loading…
Reference in New Issue
Block a user