feat: add FileActions handleUploadRequest

This commit is contained in:
Huanyu Yang 2021-11-15 19:28:42 +08:00
parent d08bf2ec86
commit f84177c850
5 changed files with 406 additions and 2 deletions

View File

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

View 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(".")));
}
}

View File

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

View File

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

View File

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