diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index d8d2e07ce9a3a1c62f00c4e62e3cab8955029503..3d8eb8dd50765ff490ea0a49d6b841d552aa82c6 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -6,10 +6,16 @@
   <component name="ChangeListManager">
     <list default="true" id="8522785e-cde9-49a6-ad14-9d935d110316" name="Changes" comment="">
       <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/Dockerfile" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/src/main/java/com/example/jaxw/HelloServlet.java" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/src/main/java/com/example/jaxw/Main.java" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/src/main/java/com/example/jaxw/TestingService.java" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/docker-compose.yml" beforeDir="false" afterPath="$PROJECT_DIR$/docker-compose.yml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/pom.xml" beforeDir="false" afterPath="$PROJECT_DIR$/pom.xml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/main/java/com/gymtracker/Main.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/gymtracker/Main.java" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/main/java/com/gymtracker/controllers/GenerateAPIKeyController.java" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/main/java/com/gymtracker/example/TestingService.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/gymtracker/example/TestingService.java" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/main/java/com/gymtracker/middleware/APIKeyValidator.java" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/main/resources/META-INF/beans.xml" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/main/resources/META-INF/persistence.xml" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/main/webapp/WEB-INF/web.xml" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/main/webapp/index.jsp" beforeDir="false" />
     </list>
     <option name="SHOW_DIALOG" value="false" />
     <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -19,6 +25,7 @@
   <component name="FileTemplateManagerImpl">
     <option name="RECENT_TEMPLATES">
       <list>
+        <option value="FxmlFile" />
         <option value="Class" />
       </list>
     </option>
@@ -29,6 +36,14 @@
   <component name="MarkdownSettingsMigration">
     <option name="stateVersion" value="1" />
   </component>
+  <component name="MavenImportPreferences">
+    <option name="generalSettings">
+      <MavenGeneralSettings>
+        <option name="mavenHome" value="Use Maven wrapper" />
+        <option name="useMavenConfig" value="true" />
+      </MavenGeneralSettings>
+    </option>
+  </component>
   <component name="ProjectColorInfo">{
   &quot;associatedIndex&quot;: 4
 }</component>
diff --git a/src/main/java/com/gymtracker/Main.java b/src/main/java/com/gymtracker/Main.java
index 6e026629bb51f82c1ccb1835406626aeaeb02f2f..58ea50e5f17539cb1ced33fe97c1e0affb695bda 100644
--- a/src/main/java/com/gymtracker/Main.java
+++ b/src/main/java/com/gymtracker/Main.java
@@ -1,15 +1,13 @@
-package src.main.java.com.gymtracker;
+package com.gymtracker;
 
-import src.main.java.com.gymtracker.example.TestingService;
-import src.main.java.com.gymtracker.controllers.GenerateAPIKeyController;
+import com.gymtracker.example.TestingService;
 
 import javax.xml.ws.Endpoint;
 
 public class Main {
     public static void main(String[] args) {
         try {
-            Endpoint.publish("http://localhost:3001/ws/testing", new TestingService());
-            Endpoint.publish("http://localhost:3001/ws/generate-api-key", new GenerateAPIKeyController());
+            Endpoint.publish("http://0.0.0.0:3001/ws/testing", new TestingService());
             System.out.println("Server started");
         } catch (Exception e) {
             System.out.println("Something's wrong");
diff --git a/src/main/java/com/gymtracker/database/Database.java b/src/main/java/com/gymtracker/database/Database.java
new file mode 100644
index 0000000000000000000000000000000000000000..728efac1f37cd213cb44f8347d3b37f7dcbb51fd
--- /dev/null
+++ b/src/main/java/com/gymtracker/database/Database.java
@@ -0,0 +1,48 @@
+package com.gymtracker.database;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.sql.SQLException;
+
+import io.github.cdimascio.dotenv.Dotenv;
+
+public class Database {
+    private static Connection connection = null;
+
+    public static Connection getConnection() throws SQLException {
+        if(connection == null) {
+            try {
+                Dotenv dotenv = Dotenv.configure().directory("/").load();
+
+                String name = dotenv.get("MYSQL_DATABASE");
+                String port = dotenv.get("MYSQL_TCP_PORT");
+                String username = dotenv.get("MYSQL_USER");
+                String password = dotenv.get("MYSQL_ROOT_PASSWORD");
+
+                String url = "jdbc:mysql://db-java:" + port + "/" + name;
+                connection = DriverManager.getConnection(url, username, password);
+            } catch(SQLException e) {
+                throw new SQLException("Error establishing database connection", e);
+            }
+        }
+        return connection;
+    }
+
+    public static ResultSet executeQuery(String query) throws SQLException {
+        try (Statement statement = getConnection().createStatement()) {
+            return statement.executeQuery(query);
+        } catch(SQLException e) {
+            throw new SQLException("Error executing query: " + query, e);
+        }
+    }
+
+    public static int executeUpdate(String query) throws  SQLException {
+        try (Statement statement = getConnection().createStatement()) {
+            return statement.executeUpdate(query);
+        } catch(SQLException e) {
+            throw new SQLException("Error executing update: " + query, e);
+        }
+    }
+}
diff --git a/src/main/java/com/gymtracker/example/TestingService.java b/src/main/java/com/gymtracker/example/TestingService.java
index b7f483db714c32156647c4e1e7c738eb0679d001..49b0076280889dfcea22f9ebc8f6520440a64b71 100644
--- a/src/main/java/com/gymtracker/example/TestingService.java
+++ b/src/main/java/com/gymtracker/example/TestingService.java
@@ -1,23 +1,21 @@
-package src.main.java.com.gymtracker.example;
+package com.gymtracker.example;
 
-import src.main.java.com.gymtracker.middleware.APIKeyValidator;
+import com.gymtracker.database.Database;
 
+import javax.jws.HandlerChain;
 import javax.jws.WebMethod;
 import javax.jws.WebService;
 import javax.xml.ws.WebServiceContext;
 import javax.annotation.Resource;
+import java.sql.SQLException;
 
 @WebService
-public class TestingService extends APIKeyValidator {
+@HandlerChain(file = "log_and_auth.xml")
+public class TestingService {
     @Resource
     WebServiceContext wsContext;
     @WebMethod
-    public String HelloWorld(String name) {
-        if(verifyAPIKey(wsContext)) {
-            return "Hello " + name;
-        }
-        else {
-            return "Your key is wrong!";
-        }
+    public String Test() {
+        return "Test successful";
     }
 }
diff --git a/src/main/java/com/gymtracker/handler/LogAuthHandler.java b/src/main/java/com/gymtracker/handler/LogAuthHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..6743135c5bc7fe62c60b5f40e6b8a181904b494a
--- /dev/null
+++ b/src/main/java/com/gymtracker/handler/LogAuthHandler.java
@@ -0,0 +1,103 @@
+package com.gymtracker.handler;
+
+import com.gymtracker.util.Util;
+import com.gymtracker.model.Logging;
+import io.github.cdimascio.dotenv.Dotenv;
+
+import javax.xml.namespace.QName;
+import javax.xml.soap.SOAPBody;
+import javax.xml.soap.SOAPMessage;
+import javax.xml.ws.handler.MessageContext;
+import javax.xml.ws.handler.soap.SOAPHandler;
+import javax.xml.ws.handler.soap.SOAPMessageContext;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class LogAuthHandler implements SOAPHandler<SOAPMessageContext> {
+    @Override
+    public Set<QName> getHeaders() {
+        return null;
+    }
+
+    @Override
+    public void close(MessageContext mc) {
+    }
+
+    public boolean handleFault(SOAPMessageContext smc) {
+        return true;
+    }
+
+    public boolean handleMessage(SOAPMessageContext smc) {
+        Boolean outbound = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
+        if (!outbound) {
+            boolean authorized = authorize(smc);
+            log(smc, authorized);
+        }
+        return true;
+    }
+
+    private boolean authorize(SOAPMessageContext smc) {
+        try {
+            @SuppressWarnings("unchecked")
+            Map<String, List<?>> httpHeaders = (Map<String, List<?>>) smc.get(MessageContext.HTTP_REQUEST_HEADERS);
+            Dotenv dotenv = Dotenv.configure().directory("/").load();
+
+            if (httpHeaders.containsKey("Authorization")) {
+                String token = (String) httpHeaders.get("Authorization").get(0);
+
+                String phpKey = dotenv.get("PHP_KEY");
+                String nodeKey = dotenv.get("NODE_KEY");
+
+                return token.equals(phpKey) || token.equals(nodeKey);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    private void log(SOAPMessageContext smc, boolean authorized) {
+        try {
+            StringBuilder description = new StringBuilder();
+
+            if(authorized) {
+                @SuppressWarnings("unchecked")
+                Map<String, List<?>> httpHeaders = (Map<String, List<?>>) smc.get(MessageContext.HTTP_REQUEST_HEADERS);
+                Dotenv dotenv = Dotenv.configure().directory("/").load();
+                String token = (String) httpHeaders.get("Authorization").get(0);
+                String phpKey = dotenv.get("PHP_KEY");
+
+                description.append("Authorized access: ");
+                String caller = token.equals(phpKey) ? "PHP" : "Node";
+                description.append(caller).append(" call to ");
+
+                QName operation = (QName) smc.get(MessageContext.WSDL_OPERATION);
+                description.append(operation.getLocalPart());
+
+                SOAPMessage soapMessage = smc.getMessage();
+                SOAPBody soapBody = soapMessage.getSOAPBody();
+                String bodyContent = soapBody.getTextContent();
+
+                description.append(" with body: ");
+                description.append(bodyContent);
+
+            } else {
+                description.append("Unauthorized access: Tried to call ");
+                QName operation = (QName) smc.get(MessageContext.WSDL_OPERATION);
+                description.append(operation.getLocalPart());
+
+                SOAPMessage soapMessage = smc.getMessage();
+                SOAPBody soapBody = soapMessage.getSOAPBody();
+                String bodyContent = soapBody.getTextContent();
+
+                description.append(" with body: ");
+                description.append(bodyContent);
+            }
+            Logging log = new Logging(Util.getIP(smc), Util.getURL(smc), description.toString());
+            log.insert();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/src/main/java/com/gymtracker/model/Application.java b/src/main/java/com/gymtracker/model/Application.java
new file mode 100644
index 0000000000000000000000000000000000000000..95ac3772819a461720ac14c720fded0ae2eeb8df
--- /dev/null
+++ b/src/main/java/com/gymtracker/model/Application.java
@@ -0,0 +1,91 @@
+package com.gymtracker.model;
+
+import com.gymtracker.database.Database;
+
+import java.lang.reflect.Field;
+import java.sql.ResultSet;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Application {
+    private String username;
+    private Integer gym_id;
+    private Boolean acceptance;
+    private String trainer_name;
+    private String trainer_description;
+    private String application_description;
+
+    public Application() {
+        this.username = "";
+        this.gym_id = 0;
+        this.acceptance = Boolean.FALSE;
+        this.trainer_name = "";
+        this.trainer_description = "";
+        this.application_description = "";
+    }
+
+    public Application(String username, Integer gym_id, Boolean acceptance, String trainer_name, String trainer_description, String application_description) {
+        this.username = username;
+        this.gym_id = gym_id;
+        this.acceptance = acceptance;
+        this.trainer_name = trainer_name;
+        this.trainer_description = trainer_description;
+        this.application_description = application_description;
+    }
+
+    private static List<Application> toList(ResultSet resultSet) {
+        try {
+            Class<Application> clazz = Application.class;
+
+            List<Application> list = new ArrayList<Application>();
+            while (resultSet.next()) {
+                Application instance = clazz.newInstance();
+                for (Field field : clazz.getDeclaredFields()) {
+                    field.setAccessible(true);
+                    field.set(instance, resultSet.getObject(field.getName()));
+                }
+                list.add(instance);
+            }
+
+            return list;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+
+    public static List<Application> getAll() {
+        try {
+            String query = "SELECT * FROM application";
+            ResultSet resultSet = Database.executeQuery(query);
+            return toList(resultSet);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static Application findByUsername(String username) {
+        try {
+            String query = "SELECT * FROM application WHERE `username` = " + username;
+            ResultSet resultSet = Database.executeQuery(query);
+            return (Application) toList(resultSet).get(0);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static Application findByGymID(Integer gym_id) {
+        try {
+            String query = "SELECT * FROM application WHERE `gym_id` = " + gym_id;
+            ResultSet resultSet = Database.executeQuery(query);
+            return (Application) toList(resultSet).get(0);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+}
diff --git a/src/main/java/com/gymtracker/model/Logging.java b/src/main/java/com/gymtracker/model/Logging.java
new file mode 100644
index 0000000000000000000000000000000000000000..f061d1aa6319d5e0de6aa088d9f2b7abd5bd3ebf
--- /dev/null
+++ b/src/main/java/com/gymtracker/model/Logging.java
@@ -0,0 +1,36 @@
+package com.gymtracker.model;
+
+import com.gymtracker.database.Database;
+
+import java.sql.SQLException;
+import java.sql.Timestamp;
+
+public class Logging {
+    private String ip;
+    private Timestamp timestamp;
+    private String endpoint;
+    private String description;
+
+    public Logging(String ip, String endpoint, String description) {
+        this.ip = ip;
+        this.timestamp = new Timestamp(System.currentTimeMillis());
+        this.endpoint = endpoint;
+        this.description = description;
+    }
+
+    public int insert() {
+        try {
+            StringBuilder query = new StringBuilder("INSERT INTO logging ");
+            query.append("(ip_address,timestamp,endpoint,request_description) VALUES(");
+            query.append("'").append(this.ip).append("',");
+            query.append("'").append(this.timestamp).append("',");
+            query.append("'").append(this.endpoint).append("',");
+            query.append("'").append(this.description).append("');");
+
+            return Database.executeUpdate(query.toString());
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+        return 0;
+    }
+}
diff --git a/src/main/java/com/gymtracker/util/Util.java b/src/main/java/com/gymtracker/util/Util.java
new file mode 100644
index 0000000000000000000000000000000000000000..a764a59bf2b89b430bb29d044cd2656dba727c9c
--- /dev/null
+++ b/src/main/java/com/gymtracker/util/Util.java
@@ -0,0 +1,38 @@
+package com.gymtracker.util;
+
+import javax.xml.ws.handler.MessageContext;
+import java.lang.reflect.Method;
+import java.net.InetSocketAddress;
+import java.net.URI;
+
+public class Util {
+    public static String getIP(MessageContext mc) {
+        try {
+            Class<?> clazz = mc.get("com.sun.xml.internal.ws.http.exchange").getClass();
+            Method method = clazz.getDeclaredMethod("getRemoteAddress");
+            method.setAccessible(true);
+
+            InetSocketAddress address = (InetSocketAddress) method
+                    .invoke(mc.get("com.sun.xml.internal.ws.http.exchange"));
+            return address.getAddress().getHostAddress();
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+        return "";
+    }
+
+    public static String getURL(MessageContext mc) {
+        try {
+            Class<?> clazz = mc.get("com.sun.xml.internal.ws.http.exchange").getClass();
+            Method method = clazz.getDeclaredMethod("getRequestURI");
+            method.setAccessible(true);
+
+            URI uri = (URI) method.invoke(mc.get("com.sun.xml.internal.ws.http.exchange"));
+            return uri.toString();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "";
+        }
+    }
+}
diff --git a/src/main/resources/log_and_auth.xml b/src/main/resources/log_and_auth.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7558c3d9f3491d38fdd1ffbe1ea7a00b4c8ad6c8
--- /dev/null
+++ b/src/main/resources/log_and_auth.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
+    <handler-chain>
+        <handler>
+            <handler-class>com.gymtracker.handler.LogAuthHandler</handler-class>
+        </handler>
+    </handler-chain>
+</handler-chains>
\ No newline at end of file