From d890b3b5263ce02adac0b5ef6dbcf1b33b87835e Mon Sep 17 00:00:00 2001
From: BEAUVAIS ANTOINE <antoine.beauvais@etu.unistra.fr>
Date: Fri, 24 Sep 2021 01:03:45 +0200
Subject: [PATCH] Added authentication system through API key.

---
 .gitignore                                    |   4 +-
 .../java/fr/unistra/sil/erp/back/Config.java  |  46 --------
 .../fr/unistra/sil/erp/back/WebMvcConfig.java |  88 +++++++++++++++
 .../api/ApiRetrieveCategoriesController.java  |   2 +-
 .../api/ApiRetrieveInfoController.java        |   2 +-
 .../controller/api/ApiRetrieveStocks.java     |   2 +-
 .../api/ApiSubmitTransactionController.java   |   2 +-
 .../api/ApiAuthenticationInterceptor.java     | 101 ++++++++++++++++++
 8 files changed, 196 insertions(+), 51 deletions(-)
 delete mode 100644 src/main/java/fr/unistra/sil/erp/back/Config.java
 create mode 100644 src/main/java/fr/unistra/sil/erp/back/WebMvcConfig.java
 create mode 100644 src/main/java/fr/unistra/sil/erp/back/interceptor/api/ApiAuthenticationInterceptor.java

diff --git a/.gitignore b/.gitignore
index 6ff2c46..9639095 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@ build/
 !gradle/wrapper/gradle-wrapper.jar
 !**/src/main/**/build/
 !**/src/test/**/build/
+src/main/resources/apikey.properties
 
 ### STS ###
 .apt_generated
@@ -45,7 +46,8 @@ dev.db
 .LSOverride
 
 # Icon must end with two \r
-Icon

+Icon
+
 
 # Thumbnails
 ._*
diff --git a/src/main/java/fr/unistra/sil/erp/back/Config.java b/src/main/java/fr/unistra/sil/erp/back/Config.java
deleted file mode 100644
index ed8cc4d..0000000
--- a/src/main/java/fr/unistra/sil/erp/back/Config.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B
- * https://cecill.info/licences/Licence_CeCILL-B_V1-fr.html
- */
-package fr.unistra.sil.erp.back;
-
-/**
- * Main configuration file for the application.
- * @author BEAUVAIS ANTOINE
- */
-public class Config {
-    
-    /**
-     * API version.
-     */
-    public static final String API_VERSION = "v1";
-    
-    /**
-     * Prefix for API calls.
-     */
-    public static final String URL_PREFIX = "/api/" + API_VERSION;
-    
-    /**
-     * API Mapping for retrieving all items.
-     */
-    public static final String MAPPING_RETRIEVEALL = URL_PREFIX +
-            "/retrieveAll";
-    
-    /**
-     * API Mapping for retrieving all categories.
-     */
-    public static final String MAPPING_GETCATEGORIES = URL_PREFIX +
-            "/retrieveCategories";
-    
-    /**
-     * API Mapping for submitting transactions.
-     */
-    public static final String MAPPING_SUBTRANSAC = URL_PREFIX +
-            "/submitTransaction";
-    
-    /**
-     * API Mapping for retrieving stocks.
-     */
-    public static final String MAPPING_GETSTOCKS = URL_PREFIX +
-            "/retrieveStocks";
-}
diff --git a/src/main/java/fr/unistra/sil/erp/back/WebMvcConfig.java b/src/main/java/fr/unistra/sil/erp/back/WebMvcConfig.java
new file mode 100644
index 0000000..9a5b3b3
--- /dev/null
+++ b/src/main/java/fr/unistra/sil/erp/back/WebMvcConfig.java
@@ -0,0 +1,88 @@
+/*
+ * CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B
+ * https://cecill.info/licences/Licence_CeCILL-B_V1-fr.html
+ */
+package fr.unistra.sil.erp.back;
+
+import fr.unistra.sil.erp.back.interceptor.api.ApiAuthenticationInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.context.annotation.PropertySources;
+import org.springframework.core.env.Environment;
+import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * Main configuration file for the application.
+ * @author BEAUVAIS ANTOINE
+ */
+@Configuration
+@EnableWebMvc
+@PropertySources(
+        @PropertySource("/apikey.properties")
+)
+public class WebMvcConfig implements WebMvcConfigurer {
+    
+    @Autowired
+    private Environment env;
+    
+    /**
+     * API version.
+     */
+    public static final String API_VERSION = "v1";
+    
+    /**
+     * API prefix.
+     */
+    public static final String API_PREFIX = "/api/";
+    
+    /**
+     * Prefix for API calls.
+     */
+    public static final String API_FULL_PREFIX = API_PREFIX + API_VERSION;
+    
+    /**
+     * API Mapping for retrieving all items.
+     */
+    public static final String MAPPING_RETRIEVEALL = API_FULL_PREFIX +
+            "/retrieveAll";
+    
+    /**
+     * API Mapping for retrieving all categories.
+     */
+    public static final String MAPPING_GETCATEGORIES = API_FULL_PREFIX +
+            "/retrieveCategories";
+    
+    /**
+     * API Mapping for submitting transactions.
+     */
+    public static final String MAPPING_SUBTRANSAC = API_FULL_PREFIX +
+            "/submitTransaction";
+    
+    /**
+     * API Mapping for retrieving stocks.
+     */
+    public static final String MAPPING_GETSTOCKS = API_FULL_PREFIX +
+            "/retrieveStocks";
+    
+    
+    /*
+    // TODO: Define default servlet.
+    @Override
+    public void configureDefaultServletHandling(
+            DefaultServletHandlerConfigurer configurer) {
+        configurer.enable();
+    }*/
+    
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        
+        registry.addInterceptor(new ApiAuthenticationInterceptor(
+                this.env.getProperty("api.key")))
+                .addPathPatterns(API_PREFIX + "**");
+        
+    }
+}
diff --git a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiRetrieveCategoriesController.java b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiRetrieveCategoriesController.java
index fa4a767..03f98dd 100644
--- a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiRetrieveCategoriesController.java
+++ b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiRetrieveCategoriesController.java
@@ -4,7 +4,7 @@
  */
 package fr.unistra.sil.erp.back.controller.api;
 
-import static fr.unistra.sil.erp.back.Config.MAPPING_GETCATEGORIES;
+import static fr.unistra.sil.erp.back.WebMvcConfig.MAPPING_GETCATEGORIES;
 import fr.unistra.sil.erp.back.DatabaseSystem;
 import fr.unistra.sil.erp.back.controller.IRetrieveCategoriesController;
 import fr.unistra.sil.erp.back.model.Category;
diff --git a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiRetrieveInfoController.java b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiRetrieveInfoController.java
index ba8c6af..1c8801d 100644
--- a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiRetrieveInfoController.java
+++ b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiRetrieveInfoController.java
@@ -4,7 +4,7 @@
  */
 package fr.unistra.sil.erp.back.controller.api;
 
-import static fr.unistra.sil.erp.back.Config.MAPPING_RETRIEVEALL;
+import static fr.unistra.sil.erp.back.WebMvcConfig.MAPPING_RETRIEVEALL;
 import fr.unistra.sil.erp.back.DatabaseSystem;
 import fr.unistra.sil.erp.back.controller.IRetrieveInfoController;
 import fr.unistra.sil.erp.back.model.Item;
diff --git a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiRetrieveStocks.java b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiRetrieveStocks.java
index 0e47d00..251034e 100644
--- a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiRetrieveStocks.java
+++ b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiRetrieveStocks.java
@@ -4,7 +4,7 @@
  */
 package fr.unistra.sil.erp.back.controller.api;
 
-import static fr.unistra.sil.erp.back.Config.MAPPING_GETSTOCKS;
+import static fr.unistra.sil.erp.back.WebMvcConfig.MAPPING_GETSTOCKS;
 import fr.unistra.sil.erp.back.DatabaseSystem;
 import fr.unistra.sil.erp.back.controller.IRetrieveStocks;
 import fr.unistra.sil.erp.back.db.DatabaseConnectionException;
diff --git a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiSubmitTransactionController.java b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiSubmitTransactionController.java
index b39c303..8396f29 100644
--- a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiSubmitTransactionController.java
+++ b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiSubmitTransactionController.java
@@ -6,7 +6,7 @@ package fr.unistra.sil.erp.back.controller.api;
 
 import com.google.gson.Gson;
 import com.google.gson.JsonParseException;
-import static fr.unistra.sil.erp.back.Config.MAPPING_SUBTRANSAC;
+import static fr.unistra.sil.erp.back.WebMvcConfig.MAPPING_SUBTRANSAC;
 import fr.unistra.sil.erp.back.DatabaseSystem;
 import fr.unistra.sil.erp.back.controller.ISubmitTransactionController;
 import fr.unistra.sil.erp.back.db.DatabaseConnectionException;
diff --git a/src/main/java/fr/unistra/sil/erp/back/interceptor/api/ApiAuthenticationInterceptor.java b/src/main/java/fr/unistra/sil/erp/back/interceptor/api/ApiAuthenticationInterceptor.java
new file mode 100644
index 0000000..c605793
--- /dev/null
+++ b/src/main/java/fr/unistra/sil/erp/back/interceptor/api/ApiAuthenticationInterceptor.java
@@ -0,0 +1,101 @@
+/*
+ * CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B
+ * https://cecill.info/licences/Licence_CeCILL-B_V1-fr.html
+ */
+package fr.unistra.sil.erp.back.interceptor.api;
+
+import com.google.gson.Gson;
+import fr.unistra.sil.erp.back.model.ErrorMessage;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+/**
+ * API Authentication Interceptor.
+ * 
+ * Each request going through an API call will be processed
+ * by this interceptor first.
+ * @author BEAUVAIS ANTOINE
+ */
+public class ApiAuthenticationInterceptor implements HandlerInterceptor {
+    
+    /**
+     * API key.
+     */
+    private final String apikey;
+    
+    public ApiAuthenticationInterceptor(String apikey)
+    {
+        this.apikey = apikey;
+    }
+    
+    @Override
+    public boolean preHandle(HttpServletRequest request,
+            HttpServletResponse response, Object handler)
+    {
+        if(this.apikey == null)
+        {
+            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            Gson gson = new Gson();
+            ErrorMessage errMsg = new ErrorMessage("Missing API key on server");
+            String responseBody = gson.toJson(errMsg);
+            
+            response.setContentType("application/json");
+            response.setCharacterEncoding("UTF-8");
+            try {
+                PrintWriter printBody = response.getWriter();
+                printBody.print(responseBody);
+            } catch (IOException ex) {
+                Logger.getLogger(ApiAuthenticationInterceptor.class.getName())
+                        .log(Level.SEVERE, "Failed to write body.", ex);
+            }
+            return false;
+        }
+        
+        String login = request.getHeader("apikey");
+        if(login == null)
+        {
+            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+            Gson gson = new Gson();
+            ErrorMessage errMsg = new ErrorMessage("Missing API key.");
+            String responseBody = gson.toJson(errMsg);
+            
+            response.setContentType("application/json");
+            response.setCharacterEncoding("UTF-8");
+            try {
+                PrintWriter printBody = response.getWriter();
+                printBody.print(responseBody);
+            } catch (IOException ex) {
+                Logger.getLogger(ApiAuthenticationInterceptor.class.getName())
+                        .log(Level.SEVERE, "Failed to write body.", ex);
+            }
+            return false;
+        }
+        
+        if(! login.equals(this.apikey) )
+        {
+            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+            Gson gson = new Gson();
+            ErrorMessage errMsg = new ErrorMessage("Invalid API key.");
+            String responseBody = gson.toJson(errMsg);
+            
+            response.setContentType("application/json");
+            response.setCharacterEncoding("UTF-8");
+            try {
+                PrintWriter printBody = response.getWriter();
+                printBody.print(responseBody);
+            } catch (IOException ex) {
+                Logger.getLogger(ApiAuthenticationInterceptor.class.getName())
+                        .log(Level.SEVERE, "Failed to write body.", ex);
+            }
+            return false;
+        }
+        
+        return true;
+    }
+    
+}
-- 
GitLab