diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/LoginUtils.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/LoginUtils.java
new file mode 100644
index 0000000..967a311
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/LoginUtils.java
@@ -0,0 +1,116 @@
+package marketing.heyday.hartmann.fotodocumentation.core.utils;
+
+import java.nio.charset.Charset;
+import java.security.Principal;
+
+import java.util.Optional;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.wildfly.security.auth.principal.NamePrincipal;
+import org.wildfly.security.auth.server.RealmUnavailableException;
+import org.wildfly.security.auth.server.SecurityDomain;
+import org.wildfly.security.auth.server.SecurityIdentity;
+import org.wildfly.security.evidence.PasswordGuessEvidence;
+
+import jakarta.servlet.http.HttpServletRequest;
+import javaslang.Tuple;
+import javaslang.Tuple2;
+
+/**
+ *
+ *
Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 20 Jan 2026
+ */
+
+public class LoginUtils {
+ private static final Log LOG = LogFactory.getLog(LoginUtils.class);
+ private static final int BASIC_HEADER_SIZE = 6;
+ private static final int USERPASS_LENGTH = 1;
+
+ public Optional authenticate(HttpServletRequest httpServletRequest) {
+ Tuple2 userPass = extractUsernamePassword(httpServletRequest);
+ if (userPass._1.isBlank() || userPass._2.isBlank()) {
+ return Optional.empty();
+ }
+ return authenticate(userPass._1, userPass._2);
+ }
+
+ public Optional getSecurityToken(HttpServletRequest httpServletRequest) {
+ Optional headerOpt = extractAuthHeader(httpServletRequest);
+ if (headerOpt.isPresent() && headerOpt.get().length > 2) {
+ return Optional.ofNullable(headerOpt.get()[2]);
+ }
+ return Optional.empty();
+ }
+
+ private Optional authenticate(String username, String password) {
+ try {
+ Principal principal = new NamePrincipal(username);
+ PasswordGuessEvidence evidence = new PasswordGuessEvidence(password.toCharArray());
+
+ SecurityDomain sd = SecurityDomain.getCurrent();
+ return Optional.ofNullable(sd.authenticate(principal, evidence));
+ } catch (RealmUnavailableException | SecurityException e) {
+ LOG.warn("Failed to authenticate user " + e.getMessage(), e);
+ return Optional.empty();
+ }
+ }
+
+ private Tuple2 extractUsernamePassword(HttpServletRequest httpServletRequest) {
+ Optional userPassOptional = extractAuthHeader(httpServletRequest);
+
+ if (userPassOptional.isPresent() && userPassOptional.get().length >= USERPASS_LENGTH) {
+ String[] userpass = userPassOptional.get();
+ String username = userpass[0];
+ String password = userpass.length > USERPASS_LENGTH ? userpass[1] : "";
+
+ return Tuple.of(username, password);
+ }
+ return Tuple.of("", "");
+ }
+
+ private Optional extractAuthHeader(HttpServletRequest httpServletRequest) {
+ Optional retVal = Optional.empty();
+
+ String authorization = httpServletRequest.getHeader("Authorization");
+ if (authorization != null && StringUtils.length(authorization) > BASIC_HEADER_SIZE) {
+ authorization = authorization.substring(BASIC_HEADER_SIZE);
+ String decoded = StringUtils.toEncodedString(Base64.decodeBase64(authorization), Charset.forName("utf-8"));
+ retVal = Optional.of(decoded.split(":"));
+ }
+ return retVal;
+ }
+
+ /**
+ * Extract device information from User-Agent header
+ *
+ * @param request HTTP servlet request
+ * @return Device/browser information
+ */
+ public String extractDeviceInfo(HttpServletRequest request) {
+ String userAgent = request.getHeader("User-Agent");
+ return userAgent != null ? userAgent : "Unknown";
+ }
+
+ /**
+ * Extract client IP address, considering proxies
+ *
+ * @param request HTTP servlet request
+ * @return Client IP address
+ */
+ public String extractIpAddress(HttpServletRequest request) {
+ String xForwardedFor = request.getHeader("X-Forwarded-For");
+ if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
+ // X-Forwarded-For can contain multiple IPs, take the first one
+ return xForwardedFor.split(",")[0].trim();
+ }
+ return request.getRemoteAddr();
+ }
+}
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/CustomerPictureResource.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/CustomerPictureResource.java
index 3a01182..4fd6627 100644
--- a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/CustomerPictureResource.java
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/CustomerPictureResource.java
@@ -1,18 +1,27 @@
package marketing.heyday.hartmann.fotodocumentation.rest;
+import java.util.Optional;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.jboss.resteasy.annotations.GZIP;
+import org.wildfly.security.auth.server.SecurityIdentity;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import jakarta.ejb.EJB;
import jakarta.enterprise.context.RequestScoped;
+import jakarta.inject.Inject;
+import jakarta.servlet.http.HttpServletRequest;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import marketing.heyday.hartmann.fotodocumentation.core.service.CustomerPictureService;
+import marketing.heyday.hartmann.fotodocumentation.core.utils.LoginUtils;
import marketing.heyday.hartmann.fotodocumentation.rest.jackson.JsonSchemaValidate;
import marketing.heyday.hartmann.fotodocumentation.rest.vo.CustomerPictureValue;
@@ -28,18 +37,28 @@ import marketing.heyday.hartmann.fotodocumentation.rest.vo.CustomerPictureValue;
@RequestScoped
@Path("customer-picture")
public class CustomerPictureResource {
-
+ private static final Log LOG = LogFactory.getLog(CustomerPictureResource.class);
+
@EJB
private CustomerPictureService customerPictureService;
+ @Inject
+ private LoginUtils loginUtils;
+
@GZIP
@POST
@Path("")
- //@Authenticate(shouldBeInAllRoles = RightUtils.ADMIN_RIGHT)
@Consumes(MediaType.APPLICATION_JSON)
@Operation(summary = "Add Customer Image to database")
@ApiResponse(responseCode = "200", description = "Add successfull")
- public Response doAddCustomerPicture(@JsonSchemaValidate("schema/customer_picture_add.json") CustomerPictureValue customerPictureValue) {
+ public Response doAddCustomerPicture(@Context HttpServletRequest httpServletRequest, @JsonSchemaValidate("schema/customer_picture_add.json") CustomerPictureValue customerPictureValue) {
+ Optional identity = loginUtils.authenticate(httpServletRequest);
+ if (identity.isEmpty()) {
+ LOG.debug("identity empty login invalid");
+ return Response.status(401).build();
+ }
+
+
boolean success = customerPictureService.addCustomerPicture(customerPictureValue);
return success ? Response.ok().build() : Response.status(Status.BAD_REQUEST).build();
diff --git a/hartmann-foto-documentation-docker/src/main/docker/hartmann-foto-documentation-web-1.0.0-SNAPSHOT.war b/hartmann-foto-documentation-docker/src/main/docker/hartmann-foto-documentation-web-1.0.0-SNAPSHOT.war
index 6b21898..ed3659b 100644
Binary files a/hartmann-foto-documentation-docker/src/main/docker/hartmann-foto-documentation-web-1.0.0-SNAPSHOT.war and b/hartmann-foto-documentation-docker/src/main/docker/hartmann-foto-documentation-web-1.0.0-SNAPSHOT.war differ
diff --git a/hartmann-foto-documentation-docker/src/main/docker/standalone-fotodocumentation.xml b/hartmann-foto-documentation-docker/src/main/docker/standalone-fotodocumentation.xml
index 80c65ef..6144f70 100644
--- a/hartmann-foto-documentation-docker/src/main/docker/standalone-fotodocumentation.xml
+++ b/hartmann-foto-documentation-docker/src/main/docker/standalone-fotodocumentation.xml
@@ -144,6 +144,7 @@
+
jdbc:postgresql://hartmann_postgres:5432/fotodocumentation
postgres
@@ -168,6 +169,7 @@
+
org.postgresql.xa.PGXADataSource
@@ -300,7 +302,7 @@
-
+
diff --git a/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/CustomerPictureTest.java b/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/CustomerPictureResourceTest.java
similarity index 74%
rename from hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/CustomerPictureTest.java
rename to hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/CustomerPictureResourceTest.java
index f5b1ca2..74d8ec1 100644
--- a/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/CustomerPictureTest.java
+++ b/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/CustomerPictureResourceTest.java
@@ -26,8 +26,8 @@ import org.junit.jupiter.api.TestMethodOrder;
* created: 14 Nov 2024
*/
@TestMethodOrder(OrderAnnotation.class)
-public class CustomerPictureTest extends AbstractRestTest {
- private static final Log LOG = LogFactory.getLog(CustomerPictureTest.class);
+public class CustomerPictureResourceTest extends AbstractRestTest {
+ private static final Log LOG = LogFactory.getLog(CustomerPictureResourceTest.class);
private static final String PATH = "api/customer-picture";
private static final String BASE_UPLOAD = "src/test/resources/upload/";
@@ -50,4 +50,18 @@ public class CustomerPictureTest extends AbstractRestTest {
int code = httpResponse.getStatusLine().getStatusCode();
assertEquals(200, code);
}
+
+ @Test
+ @Order(2)
+ public void doAddCustomerPictureNoAuth() throws IOException {
+ LOG.info("doAddCustomerPicture");
+
+ String path = deploymentURL + PATH;
+ Request request = Request.Post(path).addHeader("Accept", "application/json; charset=utf-8")
+ .bodyFile(new File(BASE_UPLOAD + "add.json"), ContentType.APPLICATION_JSON);
+
+ HttpResponse httpResponse = executeRequest(request);
+ int code = httpResponse.getStatusLine().getStatusCode();
+ assertEquals(401, code);
+ }
}