diff --git a/src/main/java/com/bnmoboxd/Main.java b/src/main/java/com/bnmoboxd/Main.java index 33903956b7202e6890d39028e3ded737f32a5187..b7271b5bab1ce959265585e1c7a8e7dc0187a3a8 100644 --- a/src/main/java/com/bnmoboxd/Main.java +++ b/src/main/java/com/bnmoboxd/Main.java @@ -1,14 +1,13 @@ package com.bnmoboxd; import com.bnmoboxd.controllers.SubscriptionController; - -import javax.xml.ws.*; +import com.bnmoboxd.core.Endpoints; public class Main { public static void main(String[] args){ try { - Endpoint.publish("http://localhost:9000/subscription", new SubscriptionController()); - System.out.println("Server created at localhost:9000"); + Endpoints.publish("/subscription", new SubscriptionController()); + System.out.printf("Server created at %s%n", Endpoints.getHost()); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/com/bnmoboxd/controllers/SubscriptionController.java b/src/main/java/com/bnmoboxd/controllers/SubscriptionController.java index 3d5ad42c88057a10a56ea8cabb7683b9d947372e..de30f5950a0ba1c10b1b8c287a86bd3bd530c754 100644 --- a/src/main/java/com/bnmoboxd/controllers/SubscriptionController.java +++ b/src/main/java/com/bnmoboxd/controllers/SubscriptionController.java @@ -1,12 +1,9 @@ package com.bnmoboxd.controllers; -import com.bnmoboxd.middlewares.AuthMiddleware; -import com.bnmoboxd.middlewares.LoggingMiddleware; import com.bnmoboxd.models.Subscription; import com.bnmoboxd.repositories.SubscriptionRepository; import com.bnmoboxd.services.SubscriptionService; import com.bnmoboxd.struct.Pagination; -import com.bnmoboxd.struct.Pair; import com.bnmoboxd.struct.SubscriptionStatus; import javax.annotation.Resource; @@ -16,12 +13,12 @@ import javax.xml.ws.WebServiceContext; import java.util.List; @WebService +@HandlerChain(file = "SubscriptionHandlers.xml") public class SubscriptionController { private final SubscriptionService subscriptionService; @Resource private WebServiceContext serviceContext; - // TODO: Logging public SubscriptionController() { subscriptionService = new SubscriptionService(); } @@ -35,17 +32,8 @@ public class SubscriptionController { @WebParam(name = "take") Integer take ) { - if(new AuthMiddleware(serviceContext).execute()) { - new LoggingMiddleware(serviceContext, - new Pair<>("endpoint", "/subscription"), - new Pair<>("page", page), - new Pair<>("take", take) - ).execute(); - Pagination pagination = page != null && take != null ? new Pagination(page, take) : null; - return subscriptionService.getSubscriptions(null, pagination); - } else { - return null; - } + Pagination pagination = page != null && take != null ? new Pagination(page, take) : null; + return subscriptionService.getSubscriptions(null, pagination); } @WebMethod(operationName = "count") @@ -55,19 +43,12 @@ public class SubscriptionController { @XmlElement(required = true) String curatorUsername ) { - if(new AuthMiddleware(serviceContext).execute() && curatorUsername != null) { - new LoggingMiddleware(serviceContext, - new Pair<>("endpoint", "/subscription"), - new Pair<>("curatorUsername", curatorUsername) - ).execute(); - return subscriptionService.getSubscriptions(new SubscriptionRepository.Filter( - curatorUsername, - null, - SubscriptionStatus.ACCEPTED - )).size(); - } else { - return null; - } + return subscriptionService.getSubscriptions(new SubscriptionRepository.Filter( + curatorUsername, + null, + SubscriptionStatus.ACCEPTED + )).size(); + } @WebMethod(operationName = "add") @@ -85,25 +66,11 @@ public class SubscriptionController { @XmlElement(required = true) String status ) { - if(new AuthMiddleware(serviceContext).execute() - && curatorUsername != null - && subscriberUsername != null - && status != null - ) { - new LoggingMiddleware(serviceContext, - new Pair<>("endpoint", "/subscription"), - new Pair<>("curatorUsername", curatorUsername), - new Pair<>("subscriberUsername", subscriberUsername), - new Pair<>("status", status) - ).execute(); - return subscriptionService.addSubscription( - curatorUsername, - subscriberUsername, - SubscriptionStatus.valueOf(status) - ); - } else { - return null; - } + return subscriptionService.addSubscription( + curatorUsername, + subscriberUsername, + SubscriptionStatus.valueOf(status) + ); } @WebMethod(operationName = "update") @@ -121,30 +88,16 @@ public class SubscriptionController { @XmlElement(required = true) String status ) { - if(new AuthMiddleware(serviceContext).execute() - && curatorUsername != null - && subscriberUsername != null - && status != null - ) { - new LoggingMiddleware(serviceContext, - new Pair<>("endpoint", "/subscription"), - new Pair<>("curatorUsername", curatorUsername), - new Pair<>("subscriberUsername", subscriberUsername), - new Pair<>("status", status) - ).execute(); - try { - SubscriptionStatus newStatus = SubscriptionStatus.valueOf(status); - return subscriptionService.updateSubscription( - curatorUsername, - subscriberUsername, - newStatus - ); - } catch(IllegalArgumentException e) { - e.printStackTrace(); - return null; - } - } else { - return null; + try { + SubscriptionStatus newStatus = SubscriptionStatus.valueOf(status); + return subscriptionService.updateSubscription( + curatorUsername, + subscriberUsername, + newStatus + ); + } catch(IllegalArgumentException e) { + e.printStackTrace(); + return false; } } } diff --git a/src/main/java/com/bnmoboxd/config/Config.java b/src/main/java/com/bnmoboxd/core/Config.java similarity index 88% rename from src/main/java/com/bnmoboxd/config/Config.java rename to src/main/java/com/bnmoboxd/core/Config.java index f7b0c0d997b3d84c7f6807f812898602f9e3503b..688a2cf06b88b5dabf4c91c47677cd3169d780f5 100644 --- a/src/main/java/com/bnmoboxd/config/Config.java +++ b/src/main/java/com/bnmoboxd/core/Config.java @@ -1,4 +1,4 @@ -package com.bnmoboxd.config; +package com.bnmoboxd.core; import io.github.cdimascio.dotenv.Dotenv; diff --git a/src/main/java/com/bnmoboxd/core/Endpoints.java b/src/main/java/com/bnmoboxd/core/Endpoints.java new file mode 100644 index 0000000000000000000000000000000000000000..b3e9f1f0ae2c33dc32db7856bbc6e80419a10c97 --- /dev/null +++ b/src/main/java/com/bnmoboxd/core/Endpoints.java @@ -0,0 +1,30 @@ +package com.bnmoboxd.core; + +import javax.xml.ws.Endpoint; +import java.util.HashMap; +import java.util.Map; + +public class Endpoints { + private static final String host = "http://localhost:9000"; +// private static final Map<String, Object> endpoint2Executor = new HashMap<>(); + private static final Map<String, String> executor2Endpoint = new HashMap<>(); + + public static String getHost() { + return host; + } + + public static void publish(String endpointAddress, Object executor) { +// endpoint2Executor.put(endpointAddress, executor); + executor2Endpoint.put(executor.getClass().getName(), endpointAddress); + + Endpoint.publish(host + endpointAddress, executor); + } + + public static String getEndpoint(String executorName) { + return executor2Endpoint.get(executorName); + } + + public static String getEndpoint(Object executor) { + return executor2Endpoint.get(executor.getClass().getName()); + } +} diff --git a/src/main/java/com/bnmoboxd/database/Database.java b/src/main/java/com/bnmoboxd/database/Database.java index f87ebb78cc51e3210a9e95100c5d69a134105e1c..2182ca9126579bd304d9d371faa8d508863f9e29 100644 --- a/src/main/java/com/bnmoboxd/database/Database.java +++ b/src/main/java/com/bnmoboxd/database/Database.java @@ -1,6 +1,6 @@ package com.bnmoboxd.database; -import com.bnmoboxd.config.Config; +import com.bnmoboxd.core.Config; import java.sql.Connection; import java.sql.DriverManager; diff --git a/src/main/java/com/bnmoboxd/handlers/AuthHandler.java b/src/main/java/com/bnmoboxd/handlers/AuthHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..6c8ae2ea0478eb35d56fb5187346356ff450967a --- /dev/null +++ b/src/main/java/com/bnmoboxd/handlers/AuthHandler.java @@ -0,0 +1,31 @@ +package com.bnmoboxd.handlers; + +import com.bnmoboxd.middlewares.AuthMiddleware; + +import javax.xml.namespace.QName; +import javax.xml.ws.handler.MessageContext; +import javax.xml.ws.handler.soap.SOAPHandler; +import javax.xml.ws.handler.soap.SOAPMessageContext; +import java.util.Collections; +import java.util.Set; + +public class AuthHandler implements SOAPHandler<SOAPMessageContext> { + @Override + public Set<QName> getHeaders() { + return Collections.emptySet(); + } + + @Override + public boolean handleMessage(SOAPMessageContext context) { + return (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY) || new AuthMiddleware(context).execute(); + } + + @Override + public boolean handleFault(SOAPMessageContext context) { + return true; + } + + @Override + public void close(MessageContext context) { + } +} diff --git a/src/main/java/com/bnmoboxd/handlers/LoggingHandler.java b/src/main/java/com/bnmoboxd/handlers/LoggingHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..4f5771f029faf09a1ea286333612a3f916a94f69 --- /dev/null +++ b/src/main/java/com/bnmoboxd/handlers/LoggingHandler.java @@ -0,0 +1,34 @@ +package com.bnmoboxd.handlers; + +import com.bnmoboxd.middlewares.LoggingMiddleware; + +import javax.xml.namespace.QName; +import javax.xml.ws.handler.MessageContext; +import javax.xml.ws.handler.soap.SOAPHandler; +import javax.xml.ws.handler.soap.SOAPMessageContext; +import java.util.Collections; +import java.util.Set; + +public class LoggingHandler implements SOAPHandler<SOAPMessageContext> { + @Override + public Set<QName> getHeaders() { + return Collections.emptySet(); + } + + @Override + public boolean handleMessage(SOAPMessageContext context) { + if(!(Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY)) { + new LoggingMiddleware(context).execute(); + } + return true; + } + + @Override + public boolean handleFault(SOAPMessageContext context) { + return true; + } + + @Override + public void close(MessageContext context) { + } +} diff --git a/src/main/java/com/bnmoboxd/middlewares/AuthMiddleware.java b/src/main/java/com/bnmoboxd/middlewares/AuthMiddleware.java index 1909098ae57f3570bdd0be363e261c2c22e160ed..df0a01a1bb780ef7fae61f5bb65f3cbf80b1cc39 100644 --- a/src/main/java/com/bnmoboxd/middlewares/AuthMiddleware.java +++ b/src/main/java/com/bnmoboxd/middlewares/AuthMiddleware.java @@ -1,26 +1,42 @@ package com.bnmoboxd.middlewares; -import com.bnmoboxd.config.Config; +import com.bnmoboxd.core.Config; +import com.bnmoboxd.core.Endpoints; +import com.sun.net.httpserver.HttpExchange; +import com.sun.xml.internal.ws.developer.JAXWSProperties; import lombok.AllArgsConstructor; -import javax.xml.ws.WebServiceContext; +import javax.xml.namespace.QName; import javax.xml.ws.handler.MessageContext; +import javax.xml.ws.handler.soap.SOAPMessageContext; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; @SuppressWarnings("unchecked") @AllArgsConstructor public class AuthMiddleware implements Middleware { - private final WebServiceContext serviceContext; + private final SOAPMessageContext context; @Override public boolean execute() { - MessageContext msgContext = serviceContext.getMessageContext(); - Map<String, Object> headers = (Map<String, Object>) msgContext.get(MessageContext.HTTP_REQUEST_HEADERS); + Map<String, Object> headers = (Map<String, Object>) context.get(MessageContext.HTTP_REQUEST_HEADERS); try { String apiKey = ((List<String>) headers.get("x-api-key")).get(0); - return apiKey != null && apiKey.equals(Config.get("SOAP_API_KEY")); + boolean auth = apiKey != null && apiKey.equals(Config.get("SOAP_API_KEY")); + + String endpoint = Endpoints.getEndpoint(String.format("com.bnmoboxd.controllers.%s", + ((QName) context.get(MessageContext.WSDL_INTERFACE)).getLocalPart() + )); + String method = ((QName) context.get(MessageContext.WSDL_OPERATION)).getLocalPart(); + HttpExchange exchange = (HttpExchange) context.get(JAXWSProperties.HTTP_EXCHANGE); + String client = String.format("%s:%s", exchange.getRemoteAddress().getAddress(), exchange.getRemoteAddress().getPort()); + + System.out.printf("[%s %16s: %-8s] %-20s client: %s; auth: %s;%n", + LocalDateTime.now(), endpoint, method, getClass().getSimpleName(), client, auth + ); + return auth; } catch(Exception e) { e.printStackTrace(); } diff --git a/src/main/java/com/bnmoboxd/middlewares/LoggingMiddleware.java b/src/main/java/com/bnmoboxd/middlewares/LoggingMiddleware.java index d14824c3e7200199b88964036f0bee2180131e39..c9b0635c1fe6abef5a4641ac7ca2151c94c2b7f2 100644 --- a/src/main/java/com/bnmoboxd/middlewares/LoggingMiddleware.java +++ b/src/main/java/com/bnmoboxd/middlewares/LoggingMiddleware.java @@ -1,54 +1,70 @@ package com.bnmoboxd.middlewares; - -import com.bnmoboxd.struct.Pair; +import com.bnmoboxd.core.Endpoints; +import com.bnmoboxd.repositories.LogRepository; +import com.sun.net.httpserver.HttpExchange; import com.sun.xml.internal.ws.developer.JAXWSProperties; -import com.sun.xml.internal.ws.server.WSEndpointImpl; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; -import javax.xml.ws.WebServiceContext; +import javax.xml.namespace.QName; +import javax.xml.soap.SOAPBody; +import javax.xml.soap.SOAPException; +import javax.xml.soap.SOAPMessage; import javax.xml.ws.handler.MessageContext; +import javax.xml.ws.handler.soap.SOAPMessageContext; import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.Map; @SuppressWarnings("unchecked") public class LoggingMiddleware implements Middleware { - private final WebServiceContext serviceContext; - private final Pair<String, Object>[] params; + private final SOAPMessageContext context; + private final LogRepository logRepository; - @SafeVarargs - public LoggingMiddleware(WebServiceContext serviceContext, Pair<String, Object>... params) { - this.serviceContext = serviceContext; - this.params = params; + public LoggingMiddleware(SOAPMessageContext context) { + this.context = context; + this.logRepository = new LogRepository(); } @Override public boolean execute() { - // TODO: Do this the better way: call this from a Handler? - MessageContext msgContext = serviceContext.getMessageContext(); - Map<String, Object> headers = (Map<String, Object>) msgContext.get(MessageContext.HTTP_REQUEST_HEADERS); + String endpoint = Endpoints.getEndpoint(String.format("com.bnmoboxd.controllers.%s", + ((QName) context.get(MessageContext.WSDL_INTERFACE)).getLocalPart() + )); + String method = ((QName) context.get(MessageContext.WSDL_OPERATION)).getLocalPart(); + HttpExchange exchange = (HttpExchange) context.get(JAXWSProperties.HTTP_EXCHANGE); + String client = String.format("%s:%s", exchange.getRemoteAddress().getAddress(), exchange.getRemoteAddress().getPort()); + String params = buildParamString(context.getMessage()); + + logRepository.addLog(params, endpoint, client, method); + + System.out.printf("[%s %16s: %-8s] %-20s client: %s; %s%n", + LocalDateTime.now(), endpoint, method, getClass().getSimpleName(), client, params + ); + return true; + } + private String buildParamString(SOAPMessage message) { try { - String endpoint = (String) getParam("endpoint"); - String method = msgContext.get(MessageContext.WSDL_OPERATION).toString(); - method = method.substring(method.indexOf('}') + 1); - - StringBuilder paramsStr = new StringBuilder(); - for(Pair<String, Object> param : params) { - if(paramsStr.length() > 0) paramsStr.append(';'); - paramsStr.append(String.format(" %s: %s", param.getFirst(), param.getSecond())); - } + StringBuilder str = new StringBuilder(); + + SOAPBody body = context.getMessage().getSOAPBody(); + NodeList children = body.getFirstChild().getChildNodes(); - System.out.printf("[%s %8s]%s%n", LocalDateTime.now(), method, paramsStr); - } catch(Exception e) { + for(int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + if(str.length() > 0) str.append(' '); + str.append(String.format("%s: %s;", child.getLocalName(), child.getTextContent())); + } + return str.toString(); + } catch(SOAPException e) { e.printStackTrace(); + return ""; } - return true; } - private Object getParam(String key) { + /*private Object getParam(String key) { for(Pair<String, Object> param : params) { if(param.getFirst().equals(key)) return param.getSecond(); } return null; - } + }*/ } diff --git a/src/main/java/com/bnmoboxd/repositories/LogRepository.java b/src/main/java/com/bnmoboxd/repositories/LogRepository.java index 62f7a2a6ba1ee907eb13cc0cfa78fb372a80924e..868011c28d1afd6762f5907b28af13e4cbfa9992 100644 --- a/src/main/java/com/bnmoboxd/repositories/LogRepository.java +++ b/src/main/java/com/bnmoboxd/repositories/LogRepository.java @@ -2,6 +2,7 @@ package com.bnmoboxd.repositories; import com.bnmoboxd.database.Database; import com.bnmoboxd.models.Log; +import com.bnmoboxd.struct.SubscriptionStatus; import java.sql.Connection; import java.sql.PreparedStatement; @@ -12,10 +13,6 @@ import java.util.List; public class LogRepository { public List<Log> getAllLogs() { - /* - * Could be improved to be tidier - * The try catch block isn't very nice looking ? - * */ try(Connection connection = Database.getConnection()) { String query = "SELECT * FROM logs"; List<Log> logs = new ArrayList<>(); @@ -31,8 +28,6 @@ public class LogRepository { Log log = new Log(id, description, endpoint, clientIp, requestTimestamp, soapRequest); logs.add(log); - /*System.out.println(log.getId()); - System.out.println(log.getDescription());*/ } } return logs; @@ -41,4 +36,23 @@ public class LogRepository { return null; } } + + public boolean addLog(String description, String endpoint, String clientIp, String soapRequest) { + try(Connection connection = Database.getConnection()) { + String query = "INSERT INTO logs(description, endpoint, client_ip, soap_request) VALUES (?, ?, ?, ?)"; + + try(PreparedStatement statement = connection.prepareStatement(query)) { + statement.setString(1, description); + statement.setString(2, endpoint); + statement.setString(3, clientIp); + statement.setString(4, soapRequest); + + int rowCount = statement.executeUpdate(); + return rowCount > 0; + } + } catch(SQLException e) { + e.printStackTrace(); + return false; + } + } } diff --git a/src/main/java/com/bnmoboxd/repositories/SubscriptionRepository.java b/src/main/java/com/bnmoboxd/repositories/SubscriptionRepository.java index ca4ab1ded6cb587f31a909c12ecb0b01ced4a1a0..7869da6ec22c92b9c8e9791adf838698d8f73b71 100644 --- a/src/main/java/com/bnmoboxd/repositories/SubscriptionRepository.java +++ b/src/main/java/com/bnmoboxd/repositories/SubscriptionRepository.java @@ -65,16 +65,16 @@ public class SubscriptionRepository { } } - public boolean addSubscription(Subscription subscription) { + public boolean addSubscription(String curatorUsername, String subscriberUsername, SubscriptionStatus status) { try(Connection connection = Database.getConnection()) { String query = "INSERT INTO subscriptions VALUES (?, ?, ?)"; try(PreparedStatement statement = connection.prepareStatement(query)) { - statement.setString(1, subscription.getCuratorUsername()); - statement.setString(2, subscription.getSubscriberUsername()); - statement.setString(3, subscription.getStatus().toString()); + statement.setString(1, curatorUsername); + statement.setString(2, subscriberUsername); + statement.setString(3, status.toString()); - // Will fail if a subscription already exists + // Will return 0 if a subscription already exists int rowCount = statement.executeUpdate(); return rowCount > 0; } @@ -84,14 +84,14 @@ public class SubscriptionRepository { } } - public boolean updateSubscription(Subscription subscription) { + public boolean updateSubscription(String curatorUsername, String subscriberUsername, SubscriptionStatus status) { try(Connection connection = Database.getConnection()) { String query = "UPDATE subscriptions SET status = ? WHERE curator_username = ? AND subscriber_username = ?"; try(PreparedStatement statement = connection.prepareStatement(query)) { - statement.setString(1, subscription.getStatus().toString()); - statement.setString(2, subscription.getCuratorUsername()); - statement.setString(3, subscription.getSubscriberUsername()); + statement.setString(1, status.toString()); + statement.setString(2, curatorUsername); + statement.setString(3, subscriberUsername); int rowCount = statement.executeUpdate(); return rowCount > 0; diff --git a/src/main/java/com/bnmoboxd/services/SubscriptionService.java b/src/main/java/com/bnmoboxd/services/SubscriptionService.java index 3e00b5c861bf8df6f80ad00cfb7949cf10b076c3..bceb26c73760eb18dc561b9b99002fb9fcb86c4d 100644 --- a/src/main/java/com/bnmoboxd/services/SubscriptionService.java +++ b/src/main/java/com/bnmoboxd/services/SubscriptionService.java @@ -23,18 +23,10 @@ public class SubscriptionService { } public boolean addSubscription(String curatorUsername, String subscriberUsername, SubscriptionStatus status) { - return subscriptionRepository.addSubscription(new Subscription( - curatorUsername, - subscriberUsername, - status - )); + return subscriptionRepository.addSubscription(curatorUsername, subscriberUsername, status); } public boolean updateSubscription(String curatorUsername, String subscriberUsername, SubscriptionStatus status) { - return subscriptionRepository.updateSubscription(new Subscription( - curatorUsername, - subscriberUsername, - status - )); + return subscriptionRepository.updateSubscription(curatorUsername, subscriberUsername, status); } } diff --git a/src/main/java/com/bnmoboxd/struct/Pair.java b/src/main/java/com/bnmoboxd/struct/Pair.java deleted file mode 100644 index 91a4c6231007dcc50c1628e814c5179c9b6b7374..0000000000000000000000000000000000000000 --- a/src/main/java/com/bnmoboxd/struct/Pair.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.bnmoboxd.struct; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor -public class Pair<T1, T2> { - private T1 first; - private T2 second; -} diff --git a/src/main/resources/com/bnmoboxd/controllers/SubscriptionHandlers.xml b/src/main/resources/com/bnmoboxd/controllers/SubscriptionHandlers.xml new file mode 100644 index 0000000000000000000000000000000000000000..a228d1c2aee64ff13387729583c39a01412472f1 --- /dev/null +++ b/src/main/resources/com/bnmoboxd/controllers/SubscriptionHandlers.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<handler-chains xmlns="http://java.sun.com/xml/ns/javaee"> + <handler-chain> + <handler> + <handler-name>com.bnmoboxd.handlers.LoggingHandler</handler-name> + <handler-class>com.bnmoboxd.handlers.LoggingHandler</handler-class> + </handler> + <handler> + <handler-name>com.bnmoboxd.handlers.AuthHandler</handler-name> + <handler-class>com.bnmoboxd.handlers.AuthHandler</handler-class> + </handler> + </handler-chain> +</handler-chains> \ No newline at end of file