extends the functionality of oauth in Stirling PDF 2.
This commit is contained in:
@@ -15,7 +15,14 @@ public class CleanUrlInterceptor implements HandlerInterceptor {
|
||||
|
||||
private static final List<String> ALLOWED_PARAMS =
|
||||
Arrays.asList(
|
||||
"lang", "endpoint", "endpoints", "logout", "error", "file", "messageType");
|
||||
"lang",
|
||||
"endpoint",
|
||||
"endpoints",
|
||||
"logout",
|
||||
"error",
|
||||
"erroroauth",
|
||||
"file",
|
||||
"messageType");
|
||||
|
||||
@Override
|
||||
public boolean preHandle(
|
||||
|
||||
@@ -6,18 +6,17 @@ import java.util.Optional;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||
import org.springframework.security.authentication.LockedException;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import stirling.software.SPDF.model.User;
|
||||
|
||||
@Component
|
||||
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
||||
|
||||
private LoginAttemptService loginAttemptService;
|
||||
@@ -28,7 +27,7 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF
|
||||
LoggerFactory.getLogger(CustomAuthenticationFailureHandler.class);
|
||||
|
||||
public CustomAuthenticationFailureHandler(
|
||||
LoginAttemptService loginAttemptService, UserService userService) {
|
||||
final LoginAttemptService loginAttemptService, UserService userService) {
|
||||
this.loginAttemptService = loginAttemptService;
|
||||
this.userService = userService;
|
||||
}
|
||||
@@ -41,24 +40,29 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF
|
||||
throws IOException, ServletException {
|
||||
|
||||
String ip = request.getRemoteAddr();
|
||||
logger.error("Failed login attempt from IP: " + ip);
|
||||
logger.error("Failed login attempt from IP: {}", ip);
|
||||
|
||||
if (exception.getClass().isAssignableFrom(InternalAuthenticationServiceException.class)
|
||||
|| "Password must not be null".equalsIgnoreCase(exception.getMessage())) {
|
||||
response.sendRedirect("/login?error=oauth2AuthenticationError");
|
||||
return;
|
||||
}
|
||||
|
||||
String username = request.getParameter("username");
|
||||
if (!isDemoUser(username)) {
|
||||
if (loginAttemptService.loginAttemptCheck(username)) {
|
||||
if (username != null && !isDemoUser(username)) {
|
||||
logger.info(
|
||||
"Remaining attempts for user {}: {}",
|
||||
username,
|
||||
loginAttemptService.getRemainingAttempts(username));
|
||||
loginAttemptService.loginFailed(username);
|
||||
if (loginAttemptService.isBlocked(username)
|
||||
|| exception.getClass().isAssignableFrom(LockedException.class)) {
|
||||
response.sendRedirect("/login?error=locked");
|
||||
return;
|
||||
} else {
|
||||
if (exception.getClass().isAssignableFrom(LockedException.class)) {
|
||||
response.sendRedirect("/login?error=locked");
|
||||
return;
|
||||
} else if (exception instanceof UsernameNotFoundException) {
|
||||
response.sendRedirect("/login?error=oauth2AuthenticationError");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)) {
|
||||
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)
|
||||
|| exception.getClass().isAssignableFrom(UsernameNotFoundException.class)) {
|
||||
response.sendRedirect("/login?error=badcredentials");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ package stirling.software.SPDF.config.security;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
@@ -14,23 +12,30 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import stirling.software.SPDF.utils.RequestUriUtils;
|
||||
|
||||
@Component
|
||||
public class CustomAuthenticationSuccessHandler
|
||||
extends SavedRequestAwareAuthenticationSuccessHandler {
|
||||
@Autowired private LoginAttemptService loginAttemptService;
|
||||
|
||||
private LoginAttemptService loginAttemptService;
|
||||
|
||||
public CustomAuthenticationSuccessHandler(LoginAttemptService loginAttemptService) {
|
||||
this.loginAttemptService = loginAttemptService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(
|
||||
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||
throws ServletException, IOException {
|
||||
String username = request.getParameter("username");
|
||||
loginAttemptService.loginSucceeded(username);
|
||||
|
||||
String userName = request.getParameter("username");
|
||||
loginAttemptService.loginSucceeded(userName);
|
||||
|
||||
// Get the saved request
|
||||
HttpSession session = request.getSession(false);
|
||||
SavedRequest savedRequest =
|
||||
session != null
|
||||
(session != null)
|
||||
? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")
|
||||
: null;
|
||||
|
||||
if (savedRequest != null
|
||||
&& !RequestUriUtils.isStaticResource(savedRequest.getRedirectUrl())) {
|
||||
// Redirect to the original destination
|
||||
|
||||
@@ -24,6 +24,8 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
||||
if (session != null) {
|
||||
String sessionId = session.getId();
|
||||
sessionRegistry.removeSessionInformation(sessionId);
|
||||
session.invalidate();
|
||||
logger.debug("Session invalidated: " + sessionId);
|
||||
}
|
||||
|
||||
response.sendRedirect(request.getContextPath() + "/login?logout=true");
|
||||
|
||||
@@ -22,11 +22,7 @@ public class CustomUserDetailsService implements UserDetailsService {
|
||||
|
||||
@Autowired private UserRepository userRepository;
|
||||
|
||||
private LoginAttemptService loginAttemptService;
|
||||
|
||||
CustomUserDetailsService(LoginAttemptService loginAttemptService) {
|
||||
this.loginAttemptService = loginAttemptService;
|
||||
}
|
||||
@Autowired private LoginAttemptService loginAttemptService;
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
@@ -43,8 +39,8 @@ public class CustomUserDetailsService implements UserDetailsService {
|
||||
"Your account has been locked due to too many failed login attempts.");
|
||||
}
|
||||
|
||||
if (user.getPassword() == null || user.getPassword().isEmpty()) {
|
||||
throw new UsernameNotFoundException("Password must not be null");
|
||||
if (!user.hasPassword()) {
|
||||
throw new IllegalArgumentException("Password must not be null");
|
||||
}
|
||||
|
||||
return new org.springframework.security.core.userdetails.User(
|
||||
|
||||
@@ -21,47 +21,16 @@ public class InitialSecuritySetup {
|
||||
|
||||
@Autowired private UserService userService;
|
||||
|
||||
@Autowired ApplicationProperties applicationProperties;
|
||||
@Autowired private ApplicationProperties applicationProperties;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(InitialSecuritySetup.class);
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (!userService.hasUsers()) {
|
||||
|
||||
String initialUsername =
|
||||
applicationProperties.getSecurity().getInitialLogin().getUsername();
|
||||
String initialPassword =
|
||||
applicationProperties.getSecurity().getInitialLogin().getPassword();
|
||||
if (initialUsername != null && initialPassword != null) {
|
||||
try {
|
||||
// https://github.com/Stirling-Tools/Stirling-PDF/issues/976
|
||||
userService.isUsernameValidWithReturn(initialUsername);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Path pathToFile = Paths.get("configs/settings.yml");
|
||||
|
||||
if (Files.exists(pathToFile)) {
|
||||
logger.error(
|
||||
"Invalid initial username provided , username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.");
|
||||
System.exit(1);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
userService.saveUser(initialUsername, initialPassword, Role.ADMIN.getRoleId());
|
||||
} else {
|
||||
initialUsername = "admin";
|
||||
initialPassword = "stirling";
|
||||
userService.saveUser(
|
||||
initialUsername, initialPassword, Role.ADMIN.getRoleId(), true);
|
||||
}
|
||||
}
|
||||
if (!userService.usernameExistsIgnoreCase(Role.INTERNAL_API_USER.getRoleId())) {
|
||||
userService.saveUser(
|
||||
Role.INTERNAL_API_USER.getRoleId(),
|
||||
UUID.randomUUID().toString(),
|
||||
Role.INTERNAL_API_USER.getRoleId());
|
||||
userService.addApiKeyToUser(Role.INTERNAL_API_USER.getRoleId());
|
||||
initializeAdminUser();
|
||||
}
|
||||
initializeInternalApiUser();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
@@ -73,6 +42,51 @@ public class InitialSecuritySetup {
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeAdminUser() {
|
||||
String initialUsername =
|
||||
applicationProperties.getSecurity().getInitialLogin().getUsername();
|
||||
String initialPassword =
|
||||
applicationProperties.getSecurity().getInitialLogin().getPassword();
|
||||
|
||||
if (initialUsername != null
|
||||
&& !initialUsername.isEmpty()
|
||||
&& initialPassword != null
|
||||
&& !initialPassword.isEmpty()
|
||||
&& !userService.findByUsernameIgnoreCase(initialUsername).isPresent()) {
|
||||
try {
|
||||
if (userService.isUsernameValid(initialUsername)) {
|
||||
userService.saveUser(initialUsername, initialPassword, Role.ADMIN.getRoleId());
|
||||
logger.info("Admin user created: " + initialUsername);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.error("Failed to initialize security setup", e);
|
||||
System.exit(1);
|
||||
}
|
||||
} else {
|
||||
createDefaultAdminUser();
|
||||
}
|
||||
}
|
||||
|
||||
private void createDefaultAdminUser() {
|
||||
String defaultUsername = "admin";
|
||||
String defaultPassword = "stirling";
|
||||
if (!userService.findByUsernameIgnoreCase(defaultUsername).isPresent()) {
|
||||
userService.saveUser(defaultUsername, defaultPassword, Role.ADMIN.getRoleId(), true);
|
||||
logger.info("Default admin user created: " + defaultUsername);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeInternalApiUser() {
|
||||
if (!userService.usernameExistsIgnoreCase(Role.INTERNAL_API_USER.getRoleId())) {
|
||||
userService.saveUser(
|
||||
Role.INTERNAL_API_USER.getRoleId(),
|
||||
UUID.randomUUID().toString(),
|
||||
Role.INTERNAL_API_USER.getRoleId());
|
||||
userService.addApiKeyToUser(Role.INTERNAL_API_USER.getRoleId());
|
||||
logger.info("Internal API user created: " + Role.INTERNAL_API_USER.getRoleId());
|
||||
}
|
||||
}
|
||||
|
||||
private void saveKeyToConfig(String key) throws IOException {
|
||||
Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
|
||||
List<String> lines = Files.readAllLines(path);
|
||||
|
||||
@@ -3,6 +3,8 @@ package stirling.software.SPDF.config.security;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -15,47 +17,62 @@ public class LoginAttemptService {
|
||||
|
||||
@Autowired ApplicationProperties applicationProperties;
|
||||
|
||||
private int MAX_ATTEMPTS;
|
||||
private static final Logger logger = LoggerFactory.getLogger(LoginAttemptService.class);
|
||||
|
||||
private int MAX_ATTEMPT;
|
||||
private long ATTEMPT_INCREMENT_TIME;
|
||||
private ConcurrentHashMap<String, AttemptCounter> attemptsCache;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
MAX_ATTEMPTS = applicationProperties.getSecurity().getLoginAttemptCount();
|
||||
MAX_ATTEMPT = applicationProperties.getSecurity().getLoginAttemptCount();
|
||||
ATTEMPT_INCREMENT_TIME =
|
||||
TimeUnit.MINUTES.toMillis(
|
||||
applicationProperties.getSecurity().getLoginResetTimeMinutes());
|
||||
attemptsCache = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
private final ConcurrentHashMap<String, AttemptCounter> attemptsCache =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
public void loginSucceeded(String key) {
|
||||
logger.info(key + " " + attemptsCache.mappingCount());
|
||||
if (key == null || key.trim().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
attemptsCache.remove(key.toLowerCase());
|
||||
}
|
||||
|
||||
public boolean loginAttemptCheck(String key) {
|
||||
return attemptsCache
|
||||
.compute(
|
||||
key.toLowerCase(),
|
||||
(k, attemptCounter) -> {
|
||||
if (attemptCounter == null
|
||||
|| attemptCounter.shouldReset(ATTEMPT_INCREMENT_TIME)) {
|
||||
return new AttemptCounter();
|
||||
} else {
|
||||
attemptCounter.increment();
|
||||
return attemptCounter;
|
||||
}
|
||||
})
|
||||
.getAttemptCount()
|
||||
>= MAX_ATTEMPTS;
|
||||
public void loginFailed(String key) {
|
||||
if (key == null || key.trim().isEmpty()) return;
|
||||
|
||||
AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
|
||||
if (attemptCounter == null) {
|
||||
attemptCounter = new AttemptCounter();
|
||||
attemptsCache.put(key.toLowerCase(), attemptCounter);
|
||||
} else {
|
||||
if (attemptCounter.shouldReset(ATTEMPT_INCREMENT_TIME)) {
|
||||
attemptCounter.reset();
|
||||
}
|
||||
attemptCounter.increment();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBlocked(String key) {
|
||||
if (key == null || key.trim().isEmpty()) return false;
|
||||
AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
|
||||
if (attemptCounter != null) {
|
||||
return attemptCounter.getAttemptCount() >= MAX_ATTEMPTS
|
||||
&& !attemptCounter.shouldReset(ATTEMPT_INCREMENT_TIME);
|
||||
if (attemptCounter == null) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
return attemptCounter.getAttemptCount() >= MAX_ATTEMPT;
|
||||
}
|
||||
|
||||
public int getRemainingAttempts(String key) {
|
||||
if (key == null || key.trim().isEmpty()) return MAX_ATTEMPT;
|
||||
|
||||
AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
|
||||
if (attemptCounter == null) {
|
||||
return MAX_ATTEMPT;
|
||||
}
|
||||
|
||||
return MAX_ATTEMPT - attemptCounter.getAttemptCount();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,8 @@ public class SecurityConfiguration {
|
||||
formLogin
|
||||
.loginPage("/login")
|
||||
.successHandler(
|
||||
new CustomAuthenticationSuccessHandler())
|
||||
new CustomAuthenticationSuccessHandler(
|
||||
loginAttemptService))
|
||||
.defaultSuccessUrl("/")
|
||||
.failureHandler(
|
||||
new CustomAuthenticationFailureHandler(
|
||||
@@ -146,7 +147,6 @@ public class SecurityConfiguration {
|
||||
.permitAll()
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
.userDetailsService(userDetailsService)
|
||||
.authenticationProvider(authenticationProvider());
|
||||
|
||||
// Handle OAUTH2 Logins
|
||||
@@ -162,7 +162,9 @@ public class SecurityConfiguration {
|
||||
*/
|
||||
.successHandler(
|
||||
new CustomOAuth2AuthenticationSuccessHandler(
|
||||
applicationProperties, userService))
|
||||
loginAttemptService,
|
||||
applicationProperties,
|
||||
userService))
|
||||
.failureHandler(
|
||||
new CustomOAuth2AuthenticationFailureHandler())
|
||||
// Add existing Authorities from the database
|
||||
@@ -171,15 +173,17 @@ public class SecurityConfiguration {
|
||||
userInfoEndpoint
|
||||
.oidcUserService(
|
||||
new CustomOAuth2UserService(
|
||||
applicationProperties))
|
||||
applicationProperties,
|
||||
userService,
|
||||
loginAttemptService))
|
||||
.userAuthoritiesMapper(
|
||||
userAuthoritiesMapper())))
|
||||
.userDetailsService(userDetailsService)
|
||||
.logout(
|
||||
logout ->
|
||||
logout.logoutSuccessHandler(
|
||||
new CustomOAuth2LogoutSuccessHandler(
|
||||
this.applicationProperties)));
|
||||
this.applicationProperties,
|
||||
sessionRegistry())));
|
||||
}
|
||||
} else {
|
||||
http.csrf(csrf -> csrf.disable())
|
||||
|
||||
@@ -40,11 +40,11 @@ public class UserService implements UserServiceInterface {
|
||||
|
||||
// Handle OAUTH2 login and user auto creation.
|
||||
public boolean processOAuth2PostLogin(String username, boolean autoCreateUser) {
|
||||
if (!isUsernameValidWithReturn(username).equals(username)) {
|
||||
if (!isUsernameValid(username)) {
|
||||
return false;
|
||||
}
|
||||
Optional<User> existUser = userRepository.findByUsernameIgnoreCase(username);
|
||||
if (existUser.isPresent()) {
|
||||
Optional<User> existingUser = userRepository.findByUsernameIgnoreCase(username);
|
||||
if (existingUser.isPresent()) {
|
||||
return true;
|
||||
}
|
||||
if (autoCreateUser) {
|
||||
@@ -114,9 +114,8 @@ public class UserService implements UserServiceInterface {
|
||||
}
|
||||
|
||||
public UserDetails loadUserByApiKey(String apiKey) {
|
||||
User userOptional = userRepository.findByApiKey(apiKey);
|
||||
if (userOptional != null) {
|
||||
User user = userOptional;
|
||||
User user = userRepository.findByApiKey(apiKey);
|
||||
if (user != null) {
|
||||
// Convert your User entity to a UserDetails object with authorities
|
||||
return new org.springframework.security.core.userdetails.User(
|
||||
user.getUsername(),
|
||||
@@ -128,13 +127,16 @@ public class UserService implements UserServiceInterface {
|
||||
|
||||
public boolean validateApiKeyForUser(String username, String apiKey) {
|
||||
Optional<User> userOpt = userRepository.findByUsernameIgnoreCase(username);
|
||||
return userOpt.isPresent() && userOpt.get().getApiKey().equals(apiKey);
|
||||
return userOpt.isPresent() && apiKey.equals(userOpt.get().getApiKey());
|
||||
}
|
||||
|
||||
public void saveUser(String username, AuthenticationType authenticationType)
|
||||
throws IllegalArgumentException {
|
||||
if (!isUsernameValid(username)) {
|
||||
throw new IllegalArgumentException(getInvalidUsernameMessage());
|
||||
}
|
||||
User user = new User();
|
||||
user.setUsername(isUsernameValidWithReturn(username));
|
||||
user.setUsername(username);
|
||||
user.setEnabled(true);
|
||||
user.setFirstLogin(false);
|
||||
user.addAuthority(new Authority(Role.USER.getRoleId(), user));
|
||||
@@ -143,8 +145,11 @@ public class UserService implements UserServiceInterface {
|
||||
}
|
||||
|
||||
public void saveUser(String username, String password) throws IllegalArgumentException {
|
||||
if (!isUsernameValid(username)) {
|
||||
throw new IllegalArgumentException(getInvalidUsernameMessage());
|
||||
}
|
||||
User user = new User();
|
||||
user.setUsername(isUsernameValidWithReturn(username));
|
||||
user.setUsername(username);
|
||||
user.setPassword(passwordEncoder.encode(password));
|
||||
user.setEnabled(true);
|
||||
user.setAuthenticationType(AuthenticationType.WEB);
|
||||
@@ -153,8 +158,11 @@ public class UserService implements UserServiceInterface {
|
||||
|
||||
public void saveUser(String username, String password, String role, boolean firstLogin)
|
||||
throws IllegalArgumentException {
|
||||
if (!isUsernameValid(username)) {
|
||||
throw new IllegalArgumentException(getInvalidUsernameMessage());
|
||||
}
|
||||
User user = new User();
|
||||
user.setUsername(isUsernameValidWithReturn(username));
|
||||
user.setUsername(username);
|
||||
user.setPassword(passwordEncoder.encode(password));
|
||||
user.addAuthority(new Authority(role, user));
|
||||
user.setEnabled(true);
|
||||
@@ -165,14 +173,7 @@ public class UserService implements UserServiceInterface {
|
||||
|
||||
public void saveUser(String username, String password, String role)
|
||||
throws IllegalArgumentException {
|
||||
User user = new User();
|
||||
user.setUsername(isUsernameValidWithReturn(username));
|
||||
user.setPassword(passwordEncoder.encode(password));
|
||||
user.addAuthority(new Authority(role, user));
|
||||
user.setEnabled(true);
|
||||
user.setAuthenticationType(AuthenticationType.WEB);
|
||||
user.setFirstLogin(false);
|
||||
userRepository.save(user);
|
||||
saveUser(username, password, role, false);
|
||||
}
|
||||
|
||||
public void deleteUser(String username) {
|
||||
@@ -206,7 +207,7 @@ public class UserService implements UserServiceInterface {
|
||||
Map<String, String> settingsMap = user.getSettings();
|
||||
|
||||
if (settingsMap == null) {
|
||||
settingsMap = new HashMap<String, String>();
|
||||
settingsMap = new HashMap<>();
|
||||
}
|
||||
settingsMap.clear();
|
||||
settingsMap.putAll(updates);
|
||||
@@ -229,7 +230,10 @@ public class UserService implements UserServiceInterface {
|
||||
}
|
||||
|
||||
public void changeUsername(User user, String newUsername) throws IllegalArgumentException {
|
||||
user.setUsername(isUsernameValidWithReturn(newUsername));
|
||||
if (!isUsernameValid(newUsername)) {
|
||||
throw new IllegalArgumentException(getInvalidUsernameMessage());
|
||||
}
|
||||
user.setUsername(newUsername);
|
||||
userRepository.save(user);
|
||||
}
|
||||
|
||||
@@ -264,30 +268,20 @@ public class UserService implements UserServiceInterface {
|
||||
return isValidSimpleUsername || isValidEmail;
|
||||
}
|
||||
|
||||
public String isUsernameValidWithReturn(String username) throws IllegalArgumentException {
|
||||
if (!isUsernameValid(username)) {
|
||||
String message =
|
||||
messageSource.getMessage(
|
||||
"invalidUsernameMessage", null, LocaleContextHolder.getLocale());
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
return username;
|
||||
private String getInvalidUsernameMessage() {
|
||||
return messageSource.getMessage(
|
||||
"invalidUsernameMessage", null, LocaleContextHolder.getLocale());
|
||||
}
|
||||
|
||||
public boolean hasPassword(String username) {
|
||||
Optional<User> user = userRepository.findByUsernameIgnoreCase(username);
|
||||
if (user.isPresent() && user.get().hasPassword()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return user.isPresent() && user.get().hasPassword();
|
||||
}
|
||||
|
||||
public boolean isAuthenticationTypeByUsername(
|
||||
String username, AuthenticationType authenticationType) {
|
||||
Optional<User> user = userRepository.findByUsernameIgnoreCase(username);
|
||||
if (user.isPresent() && user.get().getAuthenticationType() != null) {
|
||||
return user.get().getAuthenticationType().equalsIgnoreCase(authenticationType.name());
|
||||
}
|
||||
return false;
|
||||
return user.isPresent()
|
||||
&& authenticationType.name().equalsIgnoreCase(user.get().getAuthenticationType());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,22 +2,24 @@ package stirling.software.SPDF.config.security.oauth2;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.authentication.LockedException;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
@Component
|
||||
public class CustomOAuth2AuthenticationFailureHandler
|
||||
extends SimpleUrlAuthenticationFailureHandler {
|
||||
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(CustomOAuth2AuthenticationFailureHandler.class);
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailure(
|
||||
HttpServletRequest request,
|
||||
@@ -26,14 +28,21 @@ public class CustomOAuth2AuthenticationFailureHandler
|
||||
throws IOException, ServletException {
|
||||
if (exception instanceof OAuth2AuthenticationException) {
|
||||
OAuth2Error error = ((OAuth2AuthenticationException) exception).getError();
|
||||
|
||||
String errorCode = error.getErrorCode();
|
||||
|
||||
if (error.getErrorCode().equals("Password must not be null")) {
|
||||
errorCode = "userAlreadyExistsWeb";
|
||||
}
|
||||
logger.error("OAuth2 Authentication error: " + errorCode);
|
||||
getRedirectStrategy()
|
||||
.sendRedirect(request, response, "/login?error=oAuth::" + error.getErrorCode());
|
||||
.sendRedirect(request, response, "/logout?erroroauth=" + errorCode);
|
||||
return;
|
||||
} else if (exception instanceof LockedException) {
|
||||
getRedirectStrategy().sendRedirect(request, response, "/login?error=locked");
|
||||
} else if (exception instanceof UsernameNotFoundException) {
|
||||
getRedirectStrategy()
|
||||
.sendRedirect(request, response, "/login?error=oauth2AuthenticationError");
|
||||
logger.error("Account locked: ", exception);
|
||||
getRedirectStrategy().sendRedirect(request, response, "/logout?error=locked");
|
||||
} else {
|
||||
logger.error("Unhandled authentication exception", exception);
|
||||
super.onAuthenticationFailure(request, response, exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,33 +2,43 @@ package stirling.software.SPDF.config.security.oauth2;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.authentication.LockedException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import stirling.software.SPDF.config.security.LoginAttemptService;
|
||||
import stirling.software.SPDF.config.security.UserService;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||
import stirling.software.SPDF.model.AuthenticationType;
|
||||
import stirling.software.SPDF.utils.RequestUriUtils;
|
||||
|
||||
@Component
|
||||
public class CustomOAuth2AuthenticationSuccessHandler
|
||||
extends SavedRequestAwareAuthenticationSuccessHandler {
|
||||
|
||||
ApplicationProperties applicationProperties;
|
||||
UserService userService;
|
||||
private LoginAttemptService loginAttemptService;
|
||||
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(CustomOAuth2AuthenticationSuccessHandler.class);
|
||||
|
||||
private ApplicationProperties applicationProperties;
|
||||
private UserService userService;
|
||||
|
||||
public CustomOAuth2AuthenticationSuccessHandler(
|
||||
ApplicationProperties applicationProperties, UserService userService) {
|
||||
final LoginAttemptService loginAttemptService,
|
||||
ApplicationProperties applicationProperties,
|
||||
UserService userService) {
|
||||
this.applicationProperties = applicationProperties;
|
||||
this.userService = userService;
|
||||
this.loginAttemptService = loginAttemptService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -36,28 +46,37 @@ public class CustomOAuth2AuthenticationSuccessHandler
|
||||
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||
throws ServletException, IOException {
|
||||
|
||||
OAuth2User oauthUser = (OAuth2User) authentication.getPrincipal();
|
||||
|
||||
// Get the saved request
|
||||
HttpSession session = request.getSession(false);
|
||||
SavedRequest savedRequest =
|
||||
session != null
|
||||
(session != null)
|
||||
? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")
|
||||
: null;
|
||||
|
||||
if (savedRequest != null
|
||||
&& !RequestUriUtils.isStaticResource(savedRequest.getRedirectUrl())) {
|
||||
// Redirect to the original destination
|
||||
super.onAuthenticationSuccess(request, response, authentication);
|
||||
} else {
|
||||
OAuth2User oauthUser = (OAuth2User) authentication.getPrincipal();
|
||||
OAUTH2 oAuth = applicationProperties.getSecurity().getOAUTH2();
|
||||
String username = oauthUser.getAttribute(oAuth.getUseAsUsername());
|
||||
|
||||
String username = oauthUser.getName();
|
||||
|
||||
if (loginAttemptService.isBlocked(username)) {
|
||||
if (session != null) {
|
||||
session.removeAttribute("SPRING_SECURITY_SAVED_REQUEST");
|
||||
}
|
||||
throw new LockedException(
|
||||
"Your account has been locked due to too many failed login attempts.");
|
||||
}
|
||||
if (userService.usernameExistsIgnoreCase(username)
|
||||
&& userService.hasPassword(username)
|
||||
&& !userService.isAuthenticationTypeByUsername(
|
||||
username, AuthenticationType.OAUTH2)
|
||||
&& oAuth.getAutoCreateUser()) {
|
||||
response.sendRedirect(
|
||||
request.getContextPath() + "/logout?oauth2AuthenticationError=true");
|
||||
request.getContextPath() + "/logout?oauth2AuthenticationErrorWeb=true");
|
||||
return;
|
||||
} else {
|
||||
try {
|
||||
|
||||
@@ -2,13 +2,11 @@ package stirling.software.SPDF.config.security.oauth2;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.session.SessionRegistry;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
@@ -17,14 +15,17 @@ import jakarta.servlet.http.HttpSession;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||
|
||||
@Component
|
||||
public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
||||
|
||||
@Autowired SessionRegistry sessionRegistry;
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(CustomOAuth2LogoutSuccessHandler.class);
|
||||
|
||||
private ApplicationProperties applicationProperties;
|
||||
private final SessionRegistry sessionRegistry;
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
public CustomOAuth2LogoutSuccessHandler(ApplicationProperties applicationProperties) {
|
||||
public CustomOAuth2LogoutSuccessHandler(
|
||||
ApplicationProperties applicationProperties, SessionRegistry sessionRegistry) {
|
||||
this.sessionRegistry = sessionRegistry;
|
||||
this.applicationProperties = applicationProperties;
|
||||
}
|
||||
|
||||
@@ -33,32 +34,27 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
|
||||
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||
throws IOException, ServletException {
|
||||
|
||||
boolean isOAuthUser = true;
|
||||
String param = "logout=true";
|
||||
if (authentication == null) {
|
||||
response.sendRedirect("/");
|
||||
return;
|
||||
}
|
||||
Object pri = authentication.getPrincipal();
|
||||
if (pri instanceof UserDetails) {
|
||||
UserDetails userDetails = (UserDetails) pri;
|
||||
isOAuthUser = userDetails.getPassword() == null;
|
||||
} else if (pri instanceof OAuth2User) {
|
||||
isOAuthUser = true;
|
||||
}
|
||||
|
||||
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
|
||||
String provider = oauth.getProvider() != null && isOAuthUser ? oauth.getProvider() : "";
|
||||
String provider = oauth.getProvider() != null ? oauth.getProvider() : "";
|
||||
|
||||
if (request.getParameter("oauth2AuthenticationError") != null) {
|
||||
param = "error=oauth2AuthenticationError";
|
||||
} else if (request.getParameter("invalidUsername") != null) {
|
||||
param = "error=invalidUsername";
|
||||
if (request.getParameter("oauth2AuthenticationErrorWeb") != null) {
|
||||
param = "erroroauth=oauth2AuthenticationErrorWeb";
|
||||
} else if (request.getParameter("error") != null) {
|
||||
param = "error=" + request.getParameter("error");
|
||||
} else if (request.getParameter("erroroauth") != null) {
|
||||
param = "erroroauth=" + request.getParameter("erroroauth");
|
||||
} else if (request.getParameter("oauth2AutoCreateDisabled") != null) {
|
||||
param = "error=oauth2AutoCreateDisabled";
|
||||
}
|
||||
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session != null) {
|
||||
String sessionId = session.getId();
|
||||
sessionRegistry.removeSessionInformation(sessionId);
|
||||
session.invalidate();
|
||||
logger.debug("Session invalidated: " + sessionId);
|
||||
}
|
||||
|
||||
switch (provider) {
|
||||
@@ -70,17 +66,20 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
|
||||
+ oauth.getClientId()
|
||||
+ "&post_logout_redirect_uri="
|
||||
+ response.encodeRedirectURL(
|
||||
"http://" + request.getHeader("host") + "/login?" + param);
|
||||
request.getScheme()
|
||||
+ "://"
|
||||
+ request.getHeader("host")
|
||||
+ "/login?"
|
||||
+ param);
|
||||
logger.debug("Redirecting to Keycloak logout URL: " + logoutUrl);
|
||||
response.sendRedirect(logoutUrl);
|
||||
break;
|
||||
case "google":
|
||||
// Add Google specific logout URL if needed
|
||||
default:
|
||||
if (request.getParameter("oauth2AutoCreateDisabled") != null) {
|
||||
response.sendRedirect(
|
||||
request.getContextPath() + "/login?error=oauth2AutoCreateDisabled");
|
||||
} else {
|
||||
response.sendRedirect(request.getContextPath() + "/login?logout=true");
|
||||
}
|
||||
String redirectUrl = request.getContextPath() + "/login?" + param;
|
||||
logger.debug("Redirecting to default logout URL: " + redirectUrl);
|
||||
response.sendRedirect(redirectUrl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package stirling.software.SPDF.config.security.oauth2;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.authentication.LockedException;
|
||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
|
||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
||||
@@ -11,16 +13,30 @@ import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||
|
||||
import stirling.software.SPDF.config.security.LoginAttemptService;
|
||||
import stirling.software.SPDF.config.security.UserService;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.User;
|
||||
|
||||
public class CustomOAuth2UserService implements OAuth2UserService<OidcUserRequest, OidcUser> {
|
||||
|
||||
private final OidcUserService delegate = new OidcUserService();
|
||||
|
||||
private UserService userService;
|
||||
|
||||
private LoginAttemptService loginAttemptService;
|
||||
|
||||
private ApplicationProperties applicationProperties;
|
||||
|
||||
public CustomOAuth2UserService(ApplicationProperties applicationProperties) {
|
||||
private static final Logger logger = LoggerFactory.getLogger(CustomOAuth2UserService.class);
|
||||
|
||||
public CustomOAuth2UserService(
|
||||
ApplicationProperties applicationProperties,
|
||||
UserService userService,
|
||||
LoginAttemptService loginAttemptService) {
|
||||
this.applicationProperties = applicationProperties;
|
||||
this.userService = userService;
|
||||
this.loginAttemptService = loginAttemptService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -28,16 +44,18 @@ public class CustomOAuth2UserService implements OAuth2UserService<OidcUserReques
|
||||
String usernameAttribute =
|
||||
applicationProperties.getSecurity().getOAUTH2().getUseAsUsername();
|
||||
try {
|
||||
|
||||
OidcUser user = delegate.loadUser(userRequest);
|
||||
Map<String, Object> attributes = new HashMap<>(user.getAttributes());
|
||||
|
||||
// Ensure the preferred username attribute is present
|
||||
if (!attributes.containsKey(usernameAttribute)) {
|
||||
attributes.put(usernameAttribute, attributes.getOrDefault("email", ""));
|
||||
usernameAttribute = "email";
|
||||
String username = user.getUserInfo().getClaimAsString(usernameAttribute);
|
||||
Optional<User> duser = userService.findByUsernameIgnoreCase(username);
|
||||
if (duser.isPresent()) {
|
||||
if (loginAttemptService.isBlocked(username)) {
|
||||
throw new LockedException(
|
||||
"Your account has been locked due to too many failed login attempts.");
|
||||
}
|
||||
if (userService.hasPassword(username)) {
|
||||
throw new IllegalArgumentException("Password must not be null");
|
||||
}
|
||||
}
|
||||
|
||||
// Return a new OidcUser with adjusted attributes
|
||||
return new DefaultOidcUser(
|
||||
user.getAuthorities(),
|
||||
@@ -45,8 +63,11 @@ public class CustomOAuth2UserService implements OAuth2UserService<OidcUserReques
|
||||
user.getUserInfo(),
|
||||
usernameAttribute);
|
||||
} catch (java.lang.IllegalArgumentException e) {
|
||||
throw new OAuth2AuthenticationException(
|
||||
new OAuth2Error(e.getMessage()), e.getMessage(), e);
|
||||
logger.error("Error loading OIDC user: {}", e.getMessage());
|
||||
throw new OAuth2AuthenticationException(new OAuth2Error(e.getMessage()), e);
|
||||
} catch (Exception e) {
|
||||
logger.error("Unexpected error loading OIDC user", e);
|
||||
throw new OAuth2AuthenticationException("Unexpected error during authentication");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,9 +43,52 @@ public class AccountWebController {
|
||||
|
||||
model.addAttribute("currentPage", "login");
|
||||
|
||||
if (request.getParameter("error") != null) {
|
||||
String error = request.getParameter("error");
|
||||
if (error != null) {
|
||||
|
||||
model.addAttribute("error", request.getParameter("error"));
|
||||
switch (error) {
|
||||
case "badcredentials":
|
||||
error = "login.invalid";
|
||||
break;
|
||||
case "locked":
|
||||
error = "login.locked";
|
||||
break;
|
||||
case "oauth2AuthenticationError":
|
||||
error = "userAlreadyExistsOAuthMessage";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
model.addAttribute("error", error);
|
||||
}
|
||||
String erroroauth = request.getParameter("erroroauth");
|
||||
if (erroroauth != null) {
|
||||
|
||||
switch (erroroauth) {
|
||||
case "oauth2AutoCreateDisabled":
|
||||
erroroauth = "login.oauth2AutoCreateDisabled";
|
||||
break;
|
||||
case "invalidUsername":
|
||||
erroroauth = "login.invalid";
|
||||
break;
|
||||
case "userAlreadyExistsWeb":
|
||||
erroroauth = "userAlreadyExistsWebMessage";
|
||||
break;
|
||||
case "oauth2AuthenticationErrorWeb":
|
||||
erroroauth = "login.oauth2InvalidUserType";
|
||||
break;
|
||||
case "invalid_token_response":
|
||||
erroroauth = "login.oauth2InvalidTokenResponse";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
model.addAttribute("erroroauth", erroroauth);
|
||||
}
|
||||
if (request.getParameter("messageType") != null) {
|
||||
|
||||
model.addAttribute("messageType", "changedCredsMessage");
|
||||
}
|
||||
if (request.getParameter("logout") != null) {
|
||||
|
||||
@@ -60,7 +103,8 @@ public class AccountWebController {
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@GetMapping("/addUsers")
|
||||
public String showAddUserForm(Model model, Authentication authentication) {
|
||||
public String showAddUserForm(
|
||||
HttpServletRequest request, Model model, Authentication authentication) {
|
||||
List<User> allUsers = userRepository.findAll();
|
||||
Iterator<User> iterator = allUsers.iterator();
|
||||
Map<String, String> roleDetails = Role.getAllRoleDetails();
|
||||
@@ -78,6 +122,52 @@ public class AccountWebController {
|
||||
}
|
||||
}
|
||||
|
||||
String messageType = request.getParameter("messageType");
|
||||
|
||||
String deleteMessage = null;
|
||||
if (messageType != null) {
|
||||
switch (messageType) {
|
||||
case "deleteCurrentUser":
|
||||
deleteMessage = "deleteCurrentUserMessage";
|
||||
break;
|
||||
case "deleteUsernameExists":
|
||||
deleteMessage = "deleteUsernameExistsMessage";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
model.addAttribute("deleteMessage", deleteMessage);
|
||||
|
||||
String addMessage = null;
|
||||
switch (messageType) {
|
||||
case "usernameExists":
|
||||
addMessage = "usernameExistsMessage";
|
||||
break;
|
||||
case "invalidUsername":
|
||||
addMessage = "invalidUsernameMessage";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
model.addAttribute("addMessage", addMessage);
|
||||
}
|
||||
|
||||
String changeMessage = null;
|
||||
if (messageType != null) {
|
||||
switch (messageType) {
|
||||
case "userNotFound":
|
||||
changeMessage = "userNotFoundMessage";
|
||||
break;
|
||||
case "downgradeCurrentUser":
|
||||
changeMessage = "downgradeCurrentUserMessage";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
model.addAttribute("changeMessage", changeMessage);
|
||||
}
|
||||
|
||||
model.addAttribute("users", allUsers);
|
||||
model.addAttribute("currentUsername", authentication.getName());
|
||||
model.addAttribute("roleDetails", roleDetails);
|
||||
@@ -136,6 +226,30 @@ public class AccountWebController {
|
||||
return "redirect:/error"; // Example redirection in case of error
|
||||
}
|
||||
|
||||
String messageType = request.getParameter("messageType");
|
||||
if (messageType != null) {
|
||||
switch (messageType) {
|
||||
case "notAuthenticated":
|
||||
messageType = "notAuthenticatedMessage";
|
||||
break;
|
||||
case "userNotFound":
|
||||
messageType = "userNotFoundMessage";
|
||||
break;
|
||||
case "incorrectPassword":
|
||||
messageType = "incorrectPasswordMessage";
|
||||
break;
|
||||
case "usernameExists":
|
||||
messageType = "usernameExistsMessage";
|
||||
break;
|
||||
case "invalidUsername":
|
||||
messageType = "invalidUsernameMessage";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
model.addAttribute("messageType", messageType);
|
||||
}
|
||||
|
||||
// Add attributes to the model
|
||||
model.addAttribute("username", username);
|
||||
model.addAttribute("role", user.get().getRolesAsString());
|
||||
@@ -174,6 +288,28 @@ public class AccountWebController {
|
||||
// Handle error appropriately
|
||||
return "redirect:/error"; // Example redirection in case of error
|
||||
}
|
||||
|
||||
String messageType = request.getParameter("messageType");
|
||||
if (messageType != null) {
|
||||
switch (messageType) {
|
||||
case "notAuthenticated":
|
||||
messageType = "notAuthenticatedMessage";
|
||||
break;
|
||||
case "userNotFound":
|
||||
messageType = "userNotFoundMessage";
|
||||
break;
|
||||
case "incorrectPassword":
|
||||
messageType = "incorrectPasswordMessage";
|
||||
break;
|
||||
case "usernameExists":
|
||||
messageType = "usernameExistsMessage";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
model.addAttribute("messageType", messageType);
|
||||
}
|
||||
|
||||
// Add attributes to the model
|
||||
model.addAttribute("username", username);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ public class AttemptCounter {
|
||||
private long lastAttemptTime;
|
||||
|
||||
public AttemptCounter() {
|
||||
this.attemptCount = 1;
|
||||
this.attemptCount = 0;
|
||||
this.lastAttemptTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@@ -18,11 +18,16 @@ public class AttemptCounter {
|
||||
return attemptCount;
|
||||
}
|
||||
|
||||
public long getlastAttemptTime() {
|
||||
public long getLastAttemptTime() {
|
||||
return lastAttemptTime;
|
||||
}
|
||||
|
||||
public boolean shouldReset(long ATTEMPT_INCREMENT_TIME) {
|
||||
return System.currentTimeMillis() - lastAttemptTime > ATTEMPT_INCREMENT_TIME;
|
||||
public boolean shouldReset(long attemptIncrementTime) {
|
||||
return System.currentTimeMillis() - lastAttemptTime > attemptIncrementTime;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.attemptCount = 0;
|
||||
this.lastAttemptTime = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,6 +150,6 @@ public class User {
|
||||
}
|
||||
|
||||
public boolean hasPassword() {
|
||||
return this.getPassword() != "" ? true : false;
|
||||
return this.password != null && !this.password.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user