diff --git a/WebContent/META-INF/context.xml b/WebContent/META-INF/context.xml
new file mode 100644
index 0000000..57a1c0c
--- /dev/null
+++ b/WebContent/META-INF/context.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/WebContent/WEB-INF/undertow-handlers.conf b/WebContent/WEB-INF/undertow-handlers.conf
new file mode 100644
index 0000000..ed74c67
--- /dev/null
+++ b/WebContent/WEB-INF/undertow-handlers.conf
@@ -0,0 +1 @@
+samesite-cookie(mode=Lax)
\ No newline at end of file
diff --git a/WebContent/index-old.jsp b/WebContent/index-old.jsp
new file mode 100644
index 0000000..d7b96a7
--- /dev/null
+++ b/WebContent/index-old.jsp
@@ -0,0 +1,18 @@
+<%@
+page import="java.util.Collection,org.leolo.web.dm.dao.*,org.leolo.web.dm.model.*,org.leolo.web.dm.util.*"
+%>
+
+ Download Manager
+
+
+ Download Manager
+ <% Collection projs = new ProjectDao().getAllProjects(); %>
+ Project List
+
+
+
\ No newline at end of file
diff --git a/WebContent/index.jsp b/WebContent/index.jsp
index d7b96a7..c09ee74 100644
--- a/WebContent/index.jsp
+++ b/WebContent/index.jsp
@@ -1,18 +1,100 @@
-<%@
-page import="java.util.Collection,org.leolo.web.dm.dao.*,org.leolo.web.dm.model.*,org.leolo.web.dm.util.*"
-%>
-
- Download Manager
-
-
- Download Manager
- <% Collection projs = new ProjectDao().getAllProjects(); %>
- Project List
-
-
+
+
+
+
+ Download Manager
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/org/leolo/web/dm/Constant.java b/src/org/leolo/web/dm/Constant.java
index f25aa8e..2f5fe1d 100644
--- a/src/org/leolo/web/dm/Constant.java
+++ b/src/org/leolo/web/dm/Constant.java
@@ -8,11 +8,20 @@ public class Constant {
//Basic user information map
public static final String BUI_KEY_USER_ID = "uid";
public static final String BUI_KEY_USER_NAME = "uname";
+ public static final String BUI_KEY_PASSWORD = "passwd";
+ public static final String BUI_KEY_FAIL_CNT = "fail_cnt";
//System parameter map
public static final String SP_DATA_DIR = "data_dir";
public static final String SP_CACHE_DIR = "cache_dir";
public static final String SP_MAX_CACHE_SIZE = "cache_size";
+ public static final String SP_SYSNAME = "sys_name";
+ public static final String SP_MAX_LOGIN_FAIL_CNT = "max_fail_login";
+ public static final String SP_BCRYPT_COST = "bcrypt_cost";
+ public static final String SP_QUEUED_JOB_THREAD = "qj_thread";
+
+ //System Information
+ public static final String SI_API_VERSION = "0.1";
//Common value
public static final int COM_DEFAULT_USER_ID = 1;
diff --git a/src/org/leolo/web/dm/dao/UserDao.java b/src/org/leolo/web/dm/dao/UserDao.java
new file mode 100644
index 0000000..5a7ff24
--- /dev/null
+++ b/src/org/leolo/web/dm/dao/UserDao.java
@@ -0,0 +1,82 @@
+package org.leolo.web.dm.dao;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.leolo.web.dm.Constant;
+import org.leolo.web.dm.model.User;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class UserDao extends BaseDao {
+
+ private static Logger log = LoggerFactory.getLogger(UserDao.class);
+
+ public Map getBasicUserInfoByUsernameWithPassword(String username) {
+ Map u = new Hashtable<>();
+ try (Connection conn = getConnection()) {
+ // Step 1: get basic user info
+ try (PreparedStatement pstmt = conn
+ .prepareStatement("SELECT user_id, user_name, password, pwd_err_cnt FROM user WHERE user_name = ?")) {
+ pstmt.setString(1, username);
+ try (ResultSet rs = pstmt.executeQuery()) {
+ if (rs.next()) {
+ u.put(Constant.BUI_KEY_USER_NAME, rs.getString(2));
+ u.put(Constant.BUI_KEY_USER_ID, rs.getInt(1));
+ u.put(Constant.BUI_KEY_PASSWORD, rs.getString(3));
+ u.put(Constant.BUI_KEY_FAIL_CNT, rs.getInt(4));
+ } else {
+ return null;
+ }
+ }
+ }
+ } catch (SQLException e) {
+ log.error(e.getMessage(), e);
+ return null;
+ }
+ return u;
+ }
+
+ public void updateLastLogin(int userId) {
+ try(
+ Connection conn = getConnection();
+ PreparedStatement pstmt = conn.prepareStatement("UPDATE user SET pwd_err_cnt = 0, last_login=NOW() WHERE user_id = ?")
+ ){
+ pstmt.setInt(1, userId);
+ pstmt.executeUpdate();
+ }catch(SQLException e) {
+ log.error(e.getMessage(), e);
+ }
+
+ }
+
+ public void updateLoginFailedCount(int userId) {
+ try(
+ Connection conn = getConnection();
+ PreparedStatement pstmt = conn.prepareStatement("UPDATE user SET pwd_err_cnt = nvl(pwd_err_cnt,0) + 1 WHERE user_id = ?")
+ ){
+ pstmt.setInt(1, userId);
+ pstmt.executeUpdate();
+ }catch(SQLException e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+
+ public void lockUser(int userId) {
+ try(
+ Connection conn = getConnection();
+ PreparedStatement pstmt = conn.prepareStatement("UPDATE user SET password = concat('!!',password) WHERE user_id = ? AND password NOT LIKE '!!%'")
+ ){
+ pstmt.setInt(1, userId);
+ pstmt.executeUpdate();
+
+ }catch(SQLException e) {
+ log.error(e.getMessage(), e);
+ }
+
+ }
+}
diff --git a/src/org/leolo/web/dm/model/User.java b/src/org/leolo/web/dm/model/User.java
index 4c87525..36c4cbe 100644
--- a/src/org/leolo/web/dm/model/User.java
+++ b/src/org/leolo/web/dm/model/User.java
@@ -1,5 +1,42 @@
package org.leolo.web.dm.model;
+import java.util.HashSet;
+import java.util.Set;
+
public class User {
+
+ private String userName;
+ private int userId;
+ private String password;
+
+ private Set roles = new HashSet<>();
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public int getUserId() {
+ return userId;
+ }
+
+ public void setUserId(int userId) {
+ this.userId = userId;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+ public Set getRoles() {
+ return roles;
+ }
+
}
diff --git a/src/org/leolo/web/dm/servlet/BaseServlet.java b/src/org/leolo/web/dm/servlet/BaseServlet.java
index bbf92a2..0963312 100644
--- a/src/org/leolo/web/dm/servlet/BaseServlet.java
+++ b/src/org/leolo/web/dm/servlet/BaseServlet.java
@@ -25,6 +25,7 @@ public class BaseServlet extends HttpServlet {
protected String userName = null;
protected int userId = Constant.COM_DEFAULT_USER_ID;
protected boolean fatalError = false;
+ protected boolean identifiedByKey = false;
/**
* @see HttpServlet#HttpServlet()
@@ -33,18 +34,22 @@ public class BaseServlet extends HttpServlet {
super();
// TODO Auto-generated constructor stub
}
-
+
+ private void resetParam() {
+ userName = null;
+ userId = Constant.COM_DEFAULT_USER_ID;
+ fatalError = false;
+ identifiedByKey = false;
+ }
+
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- log.info("Request - [GET] {}", request.getRequestURI());
- //Reset parameters
- userName = null;
- userId = Constant.COM_DEFAULT_USER_ID;
- fatalError = false;
-
- if(request.getAttribute(Constant.SESSION_USER_NAME)!=null) {
+// log.info("Request - [GET] {}", request.getRequestURI());
+ resetParam();
+// log.info("SUN : {}", request.getSession().getAttribute(Constant.SESSION_USER_NAME));
+ if(request.getSession().getAttribute(Constant.SESSION_USER_NAME)!=null) {
userName = request.getSession().getAttribute(Constant.SESSION_USER_NAME).toString();
userId = (Integer) request.getSession().getAttribute(Constant.SESSION_USER_ID);
}
@@ -56,6 +61,7 @@ public class BaseServlet extends HttpServlet {
if(buim!=null) {
userName = buim.get(Constant.BUI_KEY_USER_NAME).toString();
userId = (Integer) buim.get(Constant.BUI_KEY_USER_ID);
+ identifiedByKey = true;
apiDao.markKeyUsed(key);
}else {
response.setContentType("application/json");
diff --git a/src/org/leolo/web/dm/servlet/LoginServlet.java b/src/org/leolo/web/dm/servlet/LoginServlet.java
new file mode 100644
index 0000000..ed96a54
--- /dev/null
+++ b/src/org/leolo/web/dm/servlet/LoginServlet.java
@@ -0,0 +1,96 @@
+package org.leolo.web.dm.servlet;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONObject;
+import org.leolo.web.dm.Constant;
+import org.leolo.web.dm.dao.SystemParameterDao;
+import org.leolo.web.dm.dao.UserDao;
+import org.leolo.web.dm.util.QueuedJobs;
+import org.leolo.web.dm.util.ServletUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Marker;
+import org.slf4j.MarkerFactory;
+
+import at.favre.lib.crypto.bcrypt.BCrypt;
+
+/**
+ * Servlet implementation class LoginServlet
+ */
+@WebServlet("/Login")
+public class LoginServlet extends HttpServlet {
+ private static Logger log = LoggerFactory.getLogger(LoginServlet.class);
+ private static Marker mark = MarkerFactory.getMarker("user");
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * @see HttpServlet#HttpServlet()
+ */
+ public LoginServlet() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ /**
+ * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
+ */
+ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ // TODO Auto-generated method stub
+ UserDao udao = new UserDao();
+ SystemParameterDao spdao = new SystemParameterDao();
+ log.info("Login attempted! {}@{}:{}", request.getParameter("username"), request.getRemoteHost(), request.getParameter("password"));
+ Map bui = udao.getBasicUserInfoByUsernameWithPassword(request.getParameter("username"));
+ if(bui!=null) {
+ if(BCrypt.verifyer().verify(request.getParameter("password").toCharArray(), bui.get(Constant.BUI_KEY_PASSWORD).toString().toCharArray()).verified) {
+ log.info(mark, "User {}@{} logged in successfully!", bui.get(Constant.BUI_KEY_USER_NAME), ServletUtil.getClientIpAddr(request));
+ request.getSession().setAttribute(Constant.SESSION_USER_ID, (int)bui.get(Constant.BUI_KEY_USER_ID));
+ request.getSession().setAttribute(Constant.SESSION_USER_NAME, bui.get(Constant.BUI_KEY_USER_NAME));
+ sendSuccessResponse(response);
+ QueuedJobs.getInstance().queue(()->{
+ udao.updateLastLogin((int)bui.get(Constant.BUI_KEY_USER_ID));
+ });
+ }else {
+ log.info(mark, "User {}@{} attempted to login![ICP/UL]", bui.get(Constant.BUI_KEY_USER_NAME), ServletUtil.getClientIpAddr(request));
+ sendFailedResponse(response);
+ QueuedJobs.getInstance().queue(()->{
+ udao.updateLoginFailedCount((int)bui.get(Constant.BUI_KEY_USER_ID));
+ int failedCount = (int) bui.get(Constant.BUI_KEY_FAIL_CNT);
+ failedCount++;
+ if(failedCount >= spdao.getInt(Constant.SP_MAX_LOGIN_FAIL_CNT, 5)) {
+ //lock account
+ udao.lockUser((int)bui.get(Constant.BUI_KEY_USER_ID));
+ }
+ });
+ }
+ }else {
+ //User does not exist!
+ log.info(mark, "User {}@{} attempted to login![UNE]", request.getParameter("username"), ServletUtil.getClientIpAddr(request));
+ BCrypt.withDefaults().hashToString(spdao.getInt(Constant.SP_BCRYPT_COST, 12), request.getParameter("password").toCharArray());
+ sendFailedResponse(response);
+ }
+ }
+
+ private void sendFailedResponse(HttpServletResponse response) throws IOException{
+ response.setContentType("application/json");
+ JSONObject obj = new JSONObject();
+ obj.put("status", "failed");
+ obj.put("message", "Username and/or password incorrect, or account is being locked.");
+ obj.write(response.getWriter());
+ }
+
+ private void sendSuccessResponse(HttpServletResponse response) throws IOException{
+ response.setContentType("application/json");
+ JSONObject obj = new JSONObject();
+ obj.put("status", "success");
+ obj.write(response.getWriter());
+ }
+
+}
diff --git a/src/org/leolo/web/dm/servlet/UserInfoServlet.java b/src/org/leolo/web/dm/servlet/UserInfoServlet.java
new file mode 100644
index 0000000..421d985
--- /dev/null
+++ b/src/org/leolo/web/dm/servlet/UserInfoServlet.java
@@ -0,0 +1,49 @@
+package org.leolo.web.dm.servlet;
+
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONObject;
+import org.leolo.web.dm.Constant;
+import org.leolo.web.dm.dao.SystemParameterDao;
+
+/**
+ * Servlet implementation class UserInfoServlet
+ */
+@WebServlet("/UserInfo")
+public class UserInfoServlet extends BaseServlet {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * @see BaseServlet#BaseServlet()
+ */
+ public UserInfoServlet() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ /**
+ * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
+ */
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ super.doGet(request, response);
+ if(this.fatalError) {
+ return;
+ }
+ response.setContentType("application/json");
+ JSONObject obj = new JSONObject();
+ obj.put("status", "success");
+ obj.put("identified", this.userName!=null);
+ if(this.userName!=null) {
+ obj.put("username", this.userName);
+ obj.put("method", identifiedByKey?"api-key":"session");
+ }
+ obj.put("system-name", new SystemParameterDao().getString(Constant.SP_SYSNAME));
+ obj.put("api-version", Constant.SI_API_VERSION);
+ obj.write(response.getWriter());
+ }
+
+}
diff --git a/src/org/leolo/web/dm/util/QueuedJobs.java b/src/org/leolo/web/dm/util/QueuedJobs.java
new file mode 100644
index 0000000..ccc7084
--- /dev/null
+++ b/src/org/leolo/web/dm/util/QueuedJobs.java
@@ -0,0 +1,29 @@
+package org.leolo.web.dm.util;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+import org.leolo.web.dm.Constant;
+import org.leolo.web.dm.dao.SystemParameterDao;
+
+
+public class QueuedJobs {
+ private static QueuedJobs instance;
+ private ExecutorService pool = Executors.newFixedThreadPool(new SystemParameterDao().getInt(Constant.SP_QUEUED_JOB_THREAD, 10));
+
+ public static synchronized QueuedJobs getInstance() {
+ if(instance==null) {
+ instance=new QueuedJobs();
+ }
+ return instance;
+ }
+
+ private QueuedJobs() {
+
+ }
+
+ public void queue(Runnable r) {
+ pool.execute(r);
+ }
+}
diff --git a/src/org/leolo/web/dm/util/ServletUtil.java b/src/org/leolo/web/dm/util/ServletUtil.java
new file mode 100644
index 0000000..282c4cf
--- /dev/null
+++ b/src/org/leolo/web/dm/util/ServletUtil.java
@@ -0,0 +1,25 @@
+package org.leolo.web.dm.util;
+
+import javax.servlet.http.HttpServletRequest;
+
+public class ServletUtil {
+ public static String getClientIpAddr(HttpServletRequest request) {
+ String ip = request.getHeader("X-Forwarded-For");
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("Proxy-Client-IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("WL-Proxy-Client-IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("HTTP_CLIENT_IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("HTTP_X_FORWARDED_FOR");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getRemoteAddr();
+ }
+ return ip;
+ }
+}
diff --git a/src/org/leolo/web/dm/util/SystemInformation.java b/src/org/leolo/web/dm/util/SystemInformation.java
new file mode 100644
index 0000000..bbe6832
--- /dev/null
+++ b/src/org/leolo/web/dm/util/SystemInformation.java
@@ -0,0 +1,5 @@
+package org.leolo.web.dm.util;
+
+public class SystemInformation {
+
+}