From 06c1cc5b6acf2976feceae3a36aa820e2008f848 Mon Sep 17 00:00:00 2001 From: Christian Felsing Date: Sun, 3 Sep 2023 07:29:58 +0200 Subject: [PATCH 1/3] captcha-configurable --- .../alovoa/html/DeleteAccountResource.java | 4 ++++ .../com/nonononoki/alovoa/html/ImprintResource.java | 5 +++++ .../com/nonononoki/alovoa/html/LoginResource.java | 6 +++++- .../com/nonononoki/alovoa/html/PasswordResource.java | 5 +++++ .../com/nonononoki/alovoa/html/RegisterResource.java | 3 ++- .../nonononoki/alovoa/service/ImprintService.java | 12 +++++++++--- .../nonononoki/alovoa/service/PasswordService.java | 9 +++++++-- .../com/nonononoki/alovoa/service/UserService.java | 9 +++++++-- src/main/resources/application.properties | 5 +++++ src/main/resources/templates/delete-account.html | 6 ++++-- src/main/resources/templates/imprint.html | 6 ++++-- src/main/resources/templates/login.html | 6 ++++-- src/main/resources/templates/password-reset.html | 6 ++++-- 13 files changed, 65 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/nonononoki/alovoa/html/DeleteAccountResource.java b/src/main/java/com/nonononoki/alovoa/html/DeleteAccountResource.java index 3bce4cb4..fecacb80 100644 --- a/src/main/java/com/nonononoki/alovoa/html/DeleteAccountResource.java +++ b/src/main/java/com/nonononoki/alovoa/html/DeleteAccountResource.java @@ -40,6 +40,9 @@ public class DeleteAccountResource { @Value("${app.user.delete.duration.valid}") private long accountDeleteDuration; + @Value("${app.captcha.delete.enabled}") + private String captchaDeleteEnabled; + @GetMapping("/delete-account/{tokenString}") public ModelAndView deleteAccount(@PathVariable String tokenString) throws AlovoaException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, @@ -57,6 +60,7 @@ public ModelAndView deleteAccount(@PathVariable String tokenString) throws Alovo active = true; } mav.addObject("active", active); + mav.addObject("captchaEnabled", Boolean.valueOf(captchaDeleteEnabled)); return mav; } diff --git a/src/main/java/com/nonononoki/alovoa/html/ImprintResource.java b/src/main/java/com/nonononoki/alovoa/html/ImprintResource.java index 838bc1e7..9f193448 100644 --- a/src/main/java/com/nonononoki/alovoa/html/ImprintResource.java +++ b/src/main/java/com/nonononoki/alovoa/html/ImprintResource.java @@ -11,11 +11,16 @@ public class ImprintResource { @Value("${app.company.name}") private String companyName; + @Value("${app.captcha.enabled}") + private String captchaImprintEnabled; + @GetMapping("/imprint") public ModelAndView imprint() { ModelAndView mav = new ModelAndView("imprint"); mav.addObject("companyName", companyName); + mav.addObject("captchaEnabled", Boolean.valueOf(captchaImprintEnabled)); + return mav; } } diff --git a/src/main/java/com/nonononoki/alovoa/html/LoginResource.java b/src/main/java/com/nonononoki/alovoa/html/LoginResource.java index d03af09c..e0887693 100644 --- a/src/main/java/com/nonononoki/alovoa/html/LoginResource.java +++ b/src/main/java/com/nonononoki/alovoa/html/LoginResource.java @@ -18,7 +18,10 @@ public class LoginResource { @Value("${app.privacy.update-date}") private String privacyDate; - + + @Value("${app.captcha.login.enabled}") + private String captchaEnabled; + public static final String URL = "/login"; @GetMapping(URL) @@ -30,6 +33,7 @@ public ModelAndView login() throws AlovoaException { } ModelAndView mav = new ModelAndView("login"); + mav.addObject("captchaEnabled", Boolean.valueOf(captchaEnabled)); return mav; } } diff --git a/src/main/java/com/nonononoki/alovoa/html/PasswordResource.java b/src/main/java/com/nonononoki/alovoa/html/PasswordResource.java index ff2ca606..e00850cd 100644 --- a/src/main/java/com/nonononoki/alovoa/html/PasswordResource.java +++ b/src/main/java/com/nonononoki/alovoa/html/PasswordResource.java @@ -7,6 +7,7 @@ import com.nonononoki.alovoa.service.AuthService; import com.nonononoki.alovoa.service.UserService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -34,6 +35,9 @@ public class PasswordResource { @Autowired private TextEncryptorConverter textEncryptor; + @Value("${app.captcha.password.enabled}") + private String captchaPasswordEnabled; + @GetMapping("/reset") public ModelAndView passwordReset() throws AlovoaException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, @@ -41,6 +45,7 @@ public ModelAndView passwordReset() throws AlovoaException, InvalidKeyException, ModelAndView mav = new ModelAndView("password-reset"); User user = authService.getCurrentUser(); mav.addObject("user", UserDto.userToUserDto(user, user, userService, textEncryptor, UserDto.NO_MEDIA)); + mav.addObject("captchaEnabled", Boolean.valueOf(captchaPasswordEnabled)); return mav; } diff --git a/src/main/java/com/nonononoki/alovoa/html/RegisterResource.java b/src/main/java/com/nonononoki/alovoa/html/RegisterResource.java index a3809a21..b3839eee 100644 --- a/src/main/java/com/nonononoki/alovoa/html/RegisterResource.java +++ b/src/main/java/com/nonononoki/alovoa/html/RegisterResource.java @@ -1,6 +1,7 @@ package com.nonononoki.alovoa.html; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -27,7 +28,7 @@ public class RegisterResource { @Autowired private AuthService authService; - + public static final String URL = "/register"; @GetMapping(URL) diff --git a/src/main/java/com/nonononoki/alovoa/service/ImprintService.java b/src/main/java/com/nonononoki/alovoa/service/ImprintService.java index fad2e414..b22596a3 100644 --- a/src/main/java/com/nonononoki/alovoa/service/ImprintService.java +++ b/src/main/java/com/nonononoki/alovoa/service/ImprintService.java @@ -5,6 +5,7 @@ import java.util.Date; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.nonononoki.alovoa.entity.Contact; @@ -24,11 +25,16 @@ public class ImprintService { @Autowired private ContactRepository contactRepo; + @Value("${app.captcha.imprint.enabled}") + private String captchaImprintEnabled; + public Contact contact(ContactDto dto) throws UnsupportedEncodingException, NoSuchAlgorithmException, AlovoaException { - boolean isValid = captchaService.isValid(dto.getCaptchaId(), dto.getCaptchaText()); - if (!isValid) { - throw new AlovoaException(publicService.text("backend.error.captcha.invalid")); + if (Boolean.parseBoolean(captchaImprintEnabled)) { + boolean isValid = captchaService.isValid(dto.getCaptchaId(), dto.getCaptchaText()); + if (!isValid) { + throw new AlovoaException(publicService.text("backend.error.captcha.invalid")); + } } Contact c = new Contact(); diff --git a/src/main/java/com/nonononoki/alovoa/service/PasswordService.java b/src/main/java/com/nonononoki/alovoa/service/PasswordService.java index 0eb86352..0f329fe4 100644 --- a/src/main/java/com/nonononoki/alovoa/service/PasswordService.java +++ b/src/main/java/com/nonononoki/alovoa/service/PasswordService.java @@ -52,14 +52,19 @@ public class PasswordService { @Value("${app.user.password-reset.duration.valid}") private int userPasswordResetDuration; + @Value("${app.captcha.password.enabled}") + private String captchaPasswordEnabled; + public UserPasswordToken resetPassword(PasswordResetDto dto) throws AlovoaException, NoSuchAlgorithmException, MessagingException, IOException { User u = authService.getCurrentUser(); if (u == null) { - if (!captchaService.isValid(dto.getCaptchaId(), dto.getCaptchaText())) { - throw new AlovoaException("captcha_invalid"); + if (Boolean.parseBoolean(captchaPasswordEnabled)) { + if (!captchaService.isValid(dto.getCaptchaId(), dto.getCaptchaText())) { + throw new AlovoaException("captcha_invalid"); + } } u = userRepo.findByEmail(Tools.cleanEmail(dto.getEmail())); diff --git a/src/main/java/com/nonononoki/alovoa/service/UserService.java b/src/main/java/com/nonononoki/alovoa/service/UserService.java index 5952d065..c45af05e 100644 --- a/src/main/java/com/nonononoki/alovoa/service/UserService.java +++ b/src/main/java/com/nonononoki/alovoa/service/UserService.java @@ -120,6 +120,9 @@ public class UserService { @Value("${app.intention.delay}") private long intentionDelay; + @Value("${app.captcha.delete.enabled}") + private String captchaDeleteEnabled; + public static void removeUserDataCascading(User user, UserDeleteParams userDeleteParam) { UserRepository userRepo = userDeleteParam.getUserRepo(); @@ -329,8 +332,10 @@ public void deleteAccountConfirm(UserDeleteAccountDto dto) throw new AlovoaException("deletion_wrong_email"); } - if (!captchaService.isValid(dto.getCaptchaId(), dto.getCaptchaText())) { - throw new AlovoaException("captcha_invalid"); + if (Boolean.parseBoolean(captchaDeleteEnabled)) { + if (!captchaService.isValid(dto.getCaptchaId(), dto.getCaptchaText())) { + throw new AlovoaException("captcha_invalid"); + } } UserDeleteParams userDeleteParam = UserDeleteParams.builder().conversationRepo(conversationRepo) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 39f8c22a..d071d6b0 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -3,6 +3,11 @@ spring.profiles.active=test server.port=${PORT:8080} +app.captcha.login.enabled=false +app.captcha.delete.enabled=false +app.captcha.imprint.enabled=true +app.captcha.password.enabled=false + #enable to force https for development #server.ssl.key-store-type=PKCS12 #server.ssl.key-store=alovoa.p12 diff --git a/src/main/resources/templates/delete-account.html b/src/main/resources/templates/delete-account.html index 0f98e379..c954a717 100644 --- a/src/main/resources/templates/delete-account.html +++ b/src/main/resources/templates/delete-account.html @@ -38,7 +38,9 @@

-
+
+
+
-
+
+
+
@@ -77,4 +79,4 @@

- \ No newline at end of file + diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html index 1baa36f8..b978470a 100644 --- a/src/main/resources/templates/login.html +++ b/src/main/resources/templates/login.html @@ -60,7 +60,9 @@

name="password"> -
+
+
+
-
+
+
+
@@ -53,4 +55,4 @@

- \ No newline at end of file + From 46dc955539660376b24e2b32262539cab7c53da5 Mon Sep 17 00:00:00 2001 From: Christian Felsing Date: Mon, 4 Sep 2023 05:15:23 +0200 Subject: [PATCH 2/3] Fixed bugs in configurable captchas and some null pointer issues in service/UserService --- .../alovoa/component/AuthFilter.java | 54 ++++++++++++------- .../alovoa/component/AuthProvider.java | 25 ++++++--- .../alovoa/html/ImprintResource.java | 2 +- .../alovoa/service/UserService.java | 4 +- 4 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/main/java/com/nonononoki/alovoa/component/AuthFilter.java b/src/main/java/com/nonononoki/alovoa/component/AuthFilter.java index 29d806b6..be5314b6 100644 --- a/src/main/java/com/nonononoki/alovoa/component/AuthFilter.java +++ b/src/main/java/com/nonononoki/alovoa/component/AuthFilter.java @@ -3,6 +3,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; @@ -12,24 +13,37 @@ public class AuthFilter extends UsernamePasswordAuthenticationFilter { - private static final String USERNAME = "username"; - private static final String PASSWORD = "password"; - private static final String CAPTCHA_ID = "captchaId"; - private static final String CAPTCHA_TEXT = "captchaText"; - public static final String REDIRECT_URL = "redirect-url"; - - @Override - public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) - throws AuthenticationException { - - String username = request.getParameter(USERNAME); - String password = request.getParameter(PASSWORD); - long captchaId = Long.parseLong(request.getParameter(CAPTCHA_ID)); - String captchaText = request.getParameter(CAPTCHA_TEXT); - request.getSession().setAttribute(REDIRECT_URL, request.getParameter(REDIRECT_URL)); - - AuthToken auth = new AuthToken(username, password, captchaId, captchaText); - AuthenticationManager am = this.getAuthenticationManager(); - return am.authenticate(auth); - } + private static final String USERNAME = "username"; + private static final String PASSWORD = "password"; + private static final String CAPTCHA_ID = "captchaId"; + private static final String CAPTCHA_TEXT = "captchaText"; + public static final String REDIRECT_URL = "redirect-url"; + + @Value("${app.captcha.login.enabled}") + private String captchaLoginEnabled; + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) + throws AuthenticationException { + long captchaId; + String captchaText; + + String username = request.getParameter(USERNAME); + String password = request.getParameter(PASSWORD); + + if (Boolean.parseBoolean(captchaLoginEnabled)) { + captchaId = Long.parseLong(request.getParameter(CAPTCHA_ID)); + captchaText = request.getParameter(CAPTCHA_TEXT); + } else { + captchaId = -1; + captchaText = null; + } + + request.getSession().setAttribute(REDIRECT_URL, request.getParameter(REDIRECT_URL)); + + AuthToken auth = new AuthToken(username, password, captchaId, captchaText); + AuthenticationManager am = this.getAuthenticationManager(); + return am.authenticate(auth); + + } } diff --git a/src/main/java/com/nonononoki/alovoa/component/AuthProvider.java b/src/main/java/com/nonononoki/alovoa/component/AuthProvider.java index c65019e3..278775aa 100644 --- a/src/main/java/com/nonononoki/alovoa/component/AuthProvider.java +++ b/src/main/java/com/nonononoki/alovoa/component/AuthProvider.java @@ -6,6 +6,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.DisabledException; @@ -37,7 +38,10 @@ public class AuthProvider implements AuthenticationProvider { @Autowired private PasswordEncoder passwordEncoder; - + + @Value("${app.captcha.login.enabled}") + private String captchaEnabled; + @SuppressWarnings("unused") private static final Logger logger = LoggerFactory.getLogger(AuthProvider.class); @@ -50,15 +54,20 @@ public Authentication authenticate(Authentication authentication) throws Authent long captchaId = a.getCaptchaId(); String captchaText = a.getCaptchaText(); - Captcha c = captchaRepo.findById(captchaId).orElse(null); - if (c == null) { - throw new BadCredentialsException(""); - } + if (Boolean.parseBoolean(captchaEnabled)) { + logger.debug("Captcha enabled, so check captcha"); + Captcha c = captchaRepo.findById(captchaId).orElse(null); + if (c == null) { + throw new BadCredentialsException(""); + } - captchaRepo.delete(c); + captchaRepo.delete(c); - if (!c.getText().equalsIgnoreCase(captchaText)) { - throw new BadCredentialsException(""); + if (!c.getText().equalsIgnoreCase(captchaText)) { + throw new BadCredentialsException(""); + } + } else { + logger.debug("Captcha disabled, so we do not care about it"); } User user = userRepo.findByEmail(email); diff --git a/src/main/java/com/nonononoki/alovoa/html/ImprintResource.java b/src/main/java/com/nonononoki/alovoa/html/ImprintResource.java index 9f193448..626da989 100644 --- a/src/main/java/com/nonononoki/alovoa/html/ImprintResource.java +++ b/src/main/java/com/nonononoki/alovoa/html/ImprintResource.java @@ -11,7 +11,7 @@ public class ImprintResource { @Value("${app.company.name}") private String companyName; - @Value("${app.captcha.enabled}") + @Value("${app.captcha.imprint.enabled}") private String captchaImprintEnabled; @GetMapping("/imprint") diff --git a/src/main/java/com/nonononoki/alovoa/service/UserService.java b/src/main/java/com/nonononoki/alovoa/service/UserService.java index c45af05e..5fb31837 100644 --- a/src/main/java/com/nonononoki/alovoa/service/UserService.java +++ b/src/main/java/com/nonononoki/alovoa/service/UserService.java @@ -805,7 +805,7 @@ public boolean hasNewAlert(String lang) throws AlovoaException { User user = authService.getCurrentUser(true); // user always check their alerts periodically in the background, so just update // it here - if (user != null) { + if (user != null && user.getDates()!=null) { updateUserInfo(user, lang); return user.getDates().getNotificationDate().after(user.getDates().getNotificationCheckedDate()); } else { @@ -815,7 +815,7 @@ public boolean hasNewAlert(String lang) throws AlovoaException { public boolean hasNewMessage() throws AlovoaException { User user = authService.getCurrentUser(true); - if (user != null && user.getDates().getMessageDate() != null + if (user != null && user.getDates()!=null && user.getDates().getMessageDate() != null && user.getDates().getMessageCheckedDate() != null) { return user.getDates().getMessageDate().after(user.getDates().getMessageCheckedDate()); } else { From 95dde744a74a547feae9e90f96bbdb5185e396c3 Mon Sep 17 00:00:00 2001 From: Christian Felsing Date: Mon, 4 Sep 2023 06:03:55 +0200 Subject: [PATCH 3/3] Register with configurable captcha --- .../com/nonononoki/alovoa/html/RegisterResource.java | 4 ++++ .../java/com/nonononoki/alovoa/model/RegisterDto.java | 3 +++ .../com/nonononoki/alovoa/service/RegisterService.java | 10 ++++++++++ src/main/resources/application.properties | 4 ++++ src/main/resources/templates/register.html | 9 +++++++-- 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/nonononoki/alovoa/html/RegisterResource.java b/src/main/java/com/nonononoki/alovoa/html/RegisterResource.java index b3839eee..4694f26c 100644 --- a/src/main/java/com/nonononoki/alovoa/html/RegisterResource.java +++ b/src/main/java/com/nonononoki/alovoa/html/RegisterResource.java @@ -29,6 +29,9 @@ public class RegisterResource { @Autowired private AuthService authService; + @Value("${app.captcha.register.enabled}") + private String captchaRegisterEnabled; + public static final String URL = "/register"; @GetMapping(URL) @@ -41,6 +44,7 @@ public ModelAndView register() throws AlovoaException { ModelAndView mav = new ModelAndView("register"); mav.addObject("genders", genderRepo.findAll()); mav.addObject("intentions", userIntentionRepo.findAll()); + mav.addObject("captchaRegisterEnabled", Boolean.parseBoolean(captchaRegisterEnabled)); return mav; } diff --git a/src/main/java/com/nonononoki/alovoa/model/RegisterDto.java b/src/main/java/com/nonononoki/alovoa/model/RegisterDto.java index 0c70e258..ee5d9561 100644 --- a/src/main/java/com/nonononoki/alovoa/model/RegisterDto.java +++ b/src/main/java/com/nonononoki/alovoa/model/RegisterDto.java @@ -17,4 +17,7 @@ public class RegisterDto { private boolean termsConditions; private boolean privacy; + + private String captchaText; + private long captchaId; } diff --git a/src/main/java/com/nonononoki/alovoa/service/RegisterService.java b/src/main/java/com/nonononoki/alovoa/service/RegisterService.java index b5af5ef3..c1d67aff 100644 --- a/src/main/java/com/nonononoki/alovoa/service/RegisterService.java +++ b/src/main/java/com/nonononoki/alovoa/service/RegisterService.java @@ -108,6 +108,9 @@ public class RegisterService { @Autowired private TextEncryptorConverter textEncryptor; + @Value("${app.captcha.register.enabled}") + private String captchaRegisterEnabled; + private static final int MIN_PASSWORD_SIZE = 7; private static final Logger logger = LoggerFactory.getLogger(RegisterService.class); @@ -121,6 +124,13 @@ public String register(RegisterDto dto) throw new AlovoaException("email_invalid"); } + if (Boolean.parseBoolean(captchaRegisterEnabled)) { + boolean isValid = captchaService.isValid(dto.getCaptchaId(), dto.getCaptchaText()); + if (!isValid) { + throw new AlovoaException(publicService.text("backend.error.captcha.invalid")); + } + } + if (!profile.equals(Tools.DEV)) { dto.setEmail(Tools.cleanEmail(dto.getEmail())); if (plusAddressing && dto.getEmail().contains("+")) { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index d071d6b0..1f3d650f 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -2,11 +2,15 @@ spring.profiles.active=test server.port=${PORT:8080} +server.forward-headers-strategy=native +server.tomcat.remote-ip-header=x-forwarded-for +server.tomcat.protocol-header=x-forwarded-proto app.captcha.login.enabled=false app.captcha.delete.enabled=false app.captcha.imprint.enabled=true app.captcha.password.enabled=false +app.captcha.register.enabled=true #enable to force https for development #server.ssl.key-store-type=PKCS12 diff --git a/src/main/resources/templates/register.html b/src/main/resources/templates/register.html index 860ce36b..fc7b8cd7 100644 --- a/src/main/resources/templates/register.html +++ b/src/main/resources/templates/register.html @@ -142,7 +142,11 @@

th:utext="#{register.privacy-agree}"> - + +
+
+
+
@@ -166,6 +170,7 @@

+ @@ -174,4 +179,4 @@

- \ No newline at end of file +