From a9a4306a75edb31bf7ff2ad6b14b83cc64386c7a Mon Sep 17 00:00:00 2001 From: LO Kam Tao Leo Date: Wed, 26 Jan 2022 16:17:53 +0000 Subject: [PATCH] Part implement of basic access control --- .classpath | 1 + src/org/leolo/web/dm/Constant.java | 10 +++ src/org/leolo/web/dm/dao/APIKeyDao.java | 80 ++++++++++++++++++++++ src/org/leolo/web/dm/dao/ProjectDao.java | 31 ++++++++- src/org/leolo/web/dm/model/User.java | 5 ++ src/org/leolo/web/dm/servlet/BaseServlet.java | 26 ++++++- .../web/dm/servlet/ProjectMetadataServlet.java | 14 +++- src/org/leolo/web/dm/util/APIKeyGenerator.java | 26 +++++++ src/org/leolo/web/dm/util/package-info.java | 8 +++ src/test/org/leolo/web/dm/APIGeneratorTester.java | 19 +++++ 10 files changed, 214 insertions(+), 6 deletions(-) create mode 100644 src/org/leolo/web/dm/dao/APIKeyDao.java create mode 100644 src/org/leolo/web/dm/model/User.java create mode 100644 src/org/leolo/web/dm/util/APIKeyGenerator.java create mode 100644 src/org/leolo/web/dm/util/package-info.java create mode 100644 src/test/org/leolo/web/dm/APIGeneratorTester.java diff --git a/.classpath b/.classpath index a9e7765..c7e3e1d 100644 --- a/.classpath +++ b/.classpath @@ -23,5 +23,6 @@ + diff --git a/src/org/leolo/web/dm/Constant.java b/src/org/leolo/web/dm/Constant.java index 9c7c295..63cfbd4 100644 --- a/src/org/leolo/web/dm/Constant.java +++ b/src/org/leolo/web/dm/Constant.java @@ -1,6 +1,16 @@ package org.leolo.web.dm; public class Constant { + //Session name public static final String SESSION_USER_ID = "uid"; public static final String SESSION_USER_NAME = "uname"; + + //Basic user information map + public static final String BUI_KEY_USER_ID = "uid"; + public static final String BUI_KEY_USER_NAME = "uname"; + + //System parameter map + + //Common value + public static final int COM_DEFAULT_USER_ID = 1; } diff --git a/src/org/leolo/web/dm/dao/APIKeyDao.java b/src/org/leolo/web/dm/dao/APIKeyDao.java new file mode 100644 index 0000000..0d64e83 --- /dev/null +++ b/src/org/leolo/web/dm/dao/APIKeyDao.java @@ -0,0 +1,80 @@ +package org.leolo.web.dm.dao; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Hashtable; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.leolo.web.dm.Constant; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class APIKeyDao extends BaseDao { + + private static Logger log = LoggerFactory.getLogger(APIKeyDao.class); + private static ExecutorService dbPool = Executors.newFixedThreadPool(1); + + public String getUsernameByApiKey(String key) { + try( + Connection conn = getConnection(); + PreparedStatement pstmt = conn.prepareStatement("SELECT user_name FROM api_key a JOIN user u ON a.user=u.user_id WHERE api_key = ?") + ){ + pstmt.setString(1, key); + try(ResultSet rs = pstmt.executeQuery()){ + if(rs.next()) { + return rs.getString(1); + } + } + }catch(SQLException e) { + log.error(e.getMessage(), e); + } + return null; + } + + public Map getBasicUserInfomationBuApiKey(String key){ + try( + Connection conn = getConnection(); + PreparedStatement pstmt = conn.prepareStatement("SELECT user_id, user_name FROM api_key a JOIN user u ON a.user=u.user_id WHERE api_key = ?") + ){ + pstmt.setString(1, key); + try(ResultSet rs = pstmt.executeQuery()){ + if(rs.next()) { + Map map = new Hashtable<>(); + map.put(Constant.BUI_KEY_USER_ID, rs.getInt(1)); + map.put(Constant.BUI_KEY_USER_NAME, rs.getString(2)); + return map; + } + } + }catch(SQLException e) { + log.error(e.getMessage(), e); + } + + return null; + } + + public void markKeyUsed(String key) { + final long LAST_USE_TIME = System.currentTimeMillis(); + dbPool.execute(new Runnable() { + @Override + public void run() { + try( + Connection conn = getConnection(); + PreparedStatement pstmt = conn.prepareStatement("UPDATE api_key SET last_key_use = ? WHERE api_key = ?"); + ){ + pstmt.setTimestamp(1, new Timestamp(LAST_USE_TIME)); + pstmt.setString(2, key); + pstmt.executeUpdate(); + }catch(SQLException e) { + log.error(e.getMessage(), e); + } + } + }); + } +} diff --git a/src/org/leolo/web/dm/dao/ProjectDao.java b/src/org/leolo/web/dm/dao/ProjectDao.java index e9ec922..db01bcb 100644 --- a/src/org/leolo/web/dm/dao/ProjectDao.java +++ b/src/org/leolo/web/dm/dao/ProjectDao.java @@ -8,6 +8,7 @@ import java.sql.Statement; import java.util.Collection; import java.util.Vector; +import org.leolo.web.dm.Constant; import org.leolo.web.dm.model.Project; import org.leolo.web.dm.model.ProjectMode; import org.slf4j.Logger; @@ -71,7 +72,11 @@ public class ProjectDao extends BaseDao{ return null; } - public Collection getProjectPathsIgnoreCase(String path) { + @Deprecated + public Collection getProjectPathsIgnoreCase(String path){ + return getProjectPathsIgnoreCase(path, Constant.COM_DEFAULT_USER_ID); + } + public Collection getProjectPathsIgnoreCase(String path, int userId) { Vector names = new Vector<>(); try( Connection conn = getConnection(); @@ -114,4 +119,28 @@ public class ProjectDao extends BaseDao{ } return null; } + + public boolean canReadProject(int projectId, int userId) { + try( + Connection conn = getConnection(); + PreparedStatement pstmt = conn.prepareStatement("SELECT read FROM project_user WHERE project_id = ? AND user_id = ?") + ){ + pstmt.setInt(1, projectId); + pstmt.setInt(2, userId); + try(ResultSet rs = pstmt.executeQuery()){ + if(rs.next()) { + return 1 == rs.getInt(1); + } + } + pstmt.setInt(2, Constant.COM_DEFAULT_USER_ID); + try(ResultSet rs = pstmt.executeQuery()){ + if(rs.next()) { + return 1 == rs.getInt(1); + } + } + }catch(SQLException e) { + log.error(e.getMessage(), e); + } + return false;//default + } } diff --git a/src/org/leolo/web/dm/model/User.java b/src/org/leolo/web/dm/model/User.java new file mode 100644 index 0000000..4c87525 --- /dev/null +++ b/src/org/leolo/web/dm/model/User.java @@ -0,0 +1,5 @@ +package org.leolo.web.dm.model; + +public class User { + +} diff --git a/src/org/leolo/web/dm/servlet/BaseServlet.java b/src/org/leolo/web/dm/servlet/BaseServlet.java index 2edfa5a..1b47ee9 100644 --- a/src/org/leolo/web/dm/servlet/BaseServlet.java +++ b/src/org/leolo/web/dm/servlet/BaseServlet.java @@ -1,12 +1,16 @@ 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.leolo.web.dm.Constant; +import org.leolo.web.dm.dao.APIKeyDao; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,7 +20,10 @@ import org.slf4j.LoggerFactory; public class BaseServlet extends HttpServlet { private static final long serialVersionUID = 1L; private static Logger log = LoggerFactory.getLogger(BaseServlet.class); - + + protected String userName = null; + protected int userId = 0;//Special UID for anonymous user + /** * @see HttpServlet#HttpServlet() */ @@ -29,7 +36,22 @@ public class BaseServlet extends HttpServlet { * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - log.debug("Request - [GET] {}", request.getRequestURI()); + log.info("Request - [GET] {}", request.getRequestURI()); + if(request.getAttribute(Constant.SESSION_USER_NAME)!=null) { + userName = request.getSession().getAttribute(Constant.SESSION_USER_NAME).toString(); + userId = (Integer) request.getSession().getAttribute(Constant.SESSION_USER_ID); + } + if(request.getParameter("key") != null) { + APIKeyDao apiDao = new APIKeyDao(); + String key = request.getParameter("key").toString(); + log.info("Using API key {}", key); + Map buim = apiDao.getBasicUserInfomationBuApiKey(key); + if(buim!=null) { + userName = buim.get(Constant.BUI_KEY_USER_NAME).toString(); + userId = (Integer) buim.get(Constant.BUI_KEY_USER_ID); + apiDao.markKeyUsed(key); + } + } } /** diff --git a/src/org/leolo/web/dm/servlet/ProjectMetadataServlet.java b/src/org/leolo/web/dm/servlet/ProjectMetadataServlet.java index 6d1f497..3bda9d0 100644 --- a/src/org/leolo/web/dm/servlet/ProjectMetadataServlet.java +++ b/src/org/leolo/web/dm/servlet/ProjectMetadataServlet.java @@ -38,12 +38,20 @@ public class ProjectMetadataServlet extends BaseServlet { super.doGet(request, response); response.setContentType("application/json"); log.info("URL: {}", request.getRequestURI()); - String projectPath = request.getRequestURI().substring(request.getContextPath().length()+10); + log.info("User is {}", userName); + String projectPath = request.getRequestURI().substring(request.getContextPath().length()+10).trim(); log.info("Project Requested: {}", projectPath); + JSONObject retObj = new JSONObject(); + if(projectPath.length()==0) { + //list all project + retObj.put("status", "success"); + //TODO: list all project + retObj.write(response.getWriter()); + return; + } ProjectDao projDao = new ProjectDao(); Project proj = projDao.getProjectByPath(projectPath); - JSONObject retObj = new JSONObject(); - if(proj==null) { + if(proj==null || projDao.canReadProject(proj.getProjectId(), userId)) { retObj.put("status", "error"); retObj.put("message", "Specified project cannot be found"); retObj.put("requested", projectPath); diff --git a/src/org/leolo/web/dm/util/APIKeyGenerator.java b/src/org/leolo/web/dm/util/APIKeyGenerator.java new file mode 100644 index 0000000..80de83a --- /dev/null +++ b/src/org/leolo/web/dm/util/APIKeyGenerator.java @@ -0,0 +1,26 @@ +package org.leolo.web.dm.util; + +import java.security.SecureRandom; +import java.util.Random; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class APIKeyGenerator { + + private static Logger log = LoggerFactory.getLogger(APIKeyGenerator.class); + private static Random random = new SecureRandom(); + public static final int DEFAULT_LENGTH = 48; + public static final char [] AVAILABLE_CHARS = "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-_".toCharArray(); + + public static String generateApiKey() { + return generateApiKey(DEFAULT_LENGTH); + } + public static String generateApiKey(final int LENGTH) { + StringBuilder sb = new StringBuilder(); + for(int i=0;i