diff --git a/src/main/java/stirling/software/SPDF/config/CleanUrlInterceptor.java b/src/main/java/stirling/software/SPDF/config/CleanUrlInterceptor.java index 18393581..3c8efd3c 100644 --- a/src/main/java/stirling/software/SPDF/config/CleanUrlInterceptor.java +++ b/src/main/java/stirling/software/SPDF/config/CleanUrlInterceptor.java @@ -15,7 +15,14 @@ public class CleanUrlInterceptor implements HandlerInterceptor { private static final List ALLOWED_PARAMS = Arrays.asList( - "lang", "endpoint", "endpoints", "logout", "error", "file", "messageType"); + "lang", + "endpoint", + "endpoints", + "logout", + "error", + "erroroauth", + "file", + "messageType"); @Override public boolean preHandle( diff --git a/src/main/java/stirling/software/SPDF/config/security/CustomAuthenticationFailureHandler.java b/src/main/java/stirling/software/SPDF/config/security/CustomAuthenticationFailureHandler.java index f4f5a37d..85447376 100644 --- a/src/main/java/stirling/software/SPDF/config/security/CustomAuthenticationFailureHandler.java +++ b/src/main/java/stirling/software/SPDF/config/security/CustomAuthenticationFailureHandler.java @@ -3,27 +3,31 @@ package stirling.software.SPDF.config.security; import java.io.IOException; import java.util.Optional; -import org.springframework.beans.factory.annotation.Autowired; +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 { - @Autowired private final LoginAttemptService loginAttemptService; + private LoginAttemptService loginAttemptService; - @Autowired private final UserService userService; // Inject the UserService + private UserService userService; + + private static final Logger logger = + LoggerFactory.getLogger(CustomAuthenticationFailureHandler.class); public CustomAuthenticationFailureHandler( - LoginAttemptService loginAttemptService, UserService userService) { + final LoginAttemptService loginAttemptService, UserService userService) { this.loginAttemptService = loginAttemptService; this.userService = userService; } @@ -34,22 +38,33 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF HttpServletResponse response, AuthenticationException exception) 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)) { - setDefaultFailureUrl("/login?error=locked"); - - } else { - if (exception.getClass().isAssignableFrom(LockedException.class)) { - setDefaultFailureUrl("/login?error=locked"); - } + 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; } } - if (exception.getClass().isAssignableFrom(BadCredentialsException.class)) { - setDefaultFailureUrl("/login?error=badcredentials"); + if (exception.getClass().isAssignableFrom(BadCredentialsException.class) + || exception.getClass().isAssignableFrom(UsernameNotFoundException.class)) { + response.sendRedirect("/login?error=badcredentials"); + return; } super.onAuthenticationFailure(request, response, exception); diff --git a/src/main/java/stirling/software/SPDF/config/security/CustomAuthenticationSuccessHandler.java b/src/main/java/stirling/software/SPDF/config/security/CustomAuthenticationSuccessHandler.java index d14466ea..f10db07c 100644 --- a/src/main/java/stirling/software/SPDF/config/security/CustomAuthenticationSuccessHandler.java +++ b/src/main/java/stirling/software/SPDF/config/security/CustomAuthenticationSuccessHandler.java @@ -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,25 +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 diff --git a/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java b/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java index 300eee00..71a17f07 100644 --- a/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java +++ b/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java @@ -2,10 +2,9 @@ package stirling.software.SPDF.config.security; import java.io.IOException; -import org.springframework.context.annotation.Bean; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.core.session.SessionRegistry; -import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler; import jakarta.servlet.ServletException; @@ -14,10 +13,8 @@ import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { - @Bean - public SessionRegistry sessionRegistry() { - return new SessionRegistryImpl(); - } + + @Autowired SessionRegistry sessionRegistry; @Override public void onLogoutSuccess( @@ -26,14 +23,11 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { HttpSession session = request.getSession(false); if (session != null) { String sessionId = session.getId(); - sessionRegistry().removeSessionInformation(sessionId); + sessionRegistry.removeSessionInformation(sessionId); + session.invalidate(); + logger.debug("Session invalidated: " + sessionId); } - if (request.getParameter("oauth2AutoCreateDisabled") != null) { - response.sendRedirect( - request.getContextPath() + "/login?error=oauth2AutoCreateDisabled"); - } else { - response.sendRedirect(request.getContextPath() + "/login?logout=true"); - } + response.sendRedirect(request.getContextPath() + "/login?logout=true"); } } diff --git a/src/main/java/stirling/software/SPDF/config/security/CustomUserDetailsService.java b/src/main/java/stirling/software/SPDF/config/security/CustomUserDetailsService.java index cd048eb9..35718196 100644 --- a/src/main/java/stirling/software/SPDF/config/security/CustomUserDetailsService.java +++ b/src/main/java/stirling/software/SPDF/config/security/CustomUserDetailsService.java @@ -39,6 +39,10 @@ public class CustomUserDetailsService implements UserDetailsService { "Your account has been locked due to too many failed login attempts."); } + if (!user.hasPassword()) { + throw new IllegalArgumentException("Password must not be null"); + } + return new org.springframework.security.core.userdetails.User( user.getUsername(), user.getPassword(), diff --git a/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java b/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java index b483ba10..9ec02e17 100644 --- a/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java +++ b/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java @@ -7,6 +7,8 @@ import java.nio.file.Paths; import java.util.List; import java.util.UUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -19,32 +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) { - 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 @@ -56,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 lines = Files.readAllLines(path); diff --git a/src/main/java/stirling/software/SPDF/config/security/LoginAttemptService.java b/src/main/java/stirling/software/SPDF/config/security/LoginAttemptService.java index 40a54ecc..f398f13c 100644 --- a/src/main/java/stirling/software/SPDF/config/security/LoginAttemptService.java +++ b/src/main/java/stirling/software/SPDF/config/security/LoginAttemptService.java @@ -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,44 +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 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 attemptsCache = - new ConcurrentHashMap<>(); - public void loginSucceeded(String key) { - attemptsCache.remove(key); + logger.info(key + " " + attemptsCache.mappingCount()); + if (key == null || key.trim().isEmpty()) { + return; + } + attemptsCache.remove(key.toLowerCase()); } - public boolean loginAttemptCheck(String key) { - attemptsCache.compute( - key, - (k, attemptCounter) -> { - if (attemptCounter == null - || attemptCounter.shouldReset(ATTEMPT_INCREMENT_TIME)) { - return new AttemptCounter(); - } else { - attemptCounter.increment(); - return attemptCounter; - } - }); - return attemptsCache.get(key).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) { - AttemptCounter attemptCounter = attemptsCache.get(key); - if (attemptCounter != null) { - return attemptCounter.getAttemptCount() >= MAX_ATTEMPTS; + if (key == null || key.trim().isEmpty()) return false; + AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase()); + 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(); } } diff --git a/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java b/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java index 8eb80cdc..3b3cdaf3 100644 --- a/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java +++ b/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java @@ -1,6 +1,5 @@ package stirling.software.SPDF.config.security; -import java.io.IOException; import java.util.*; import org.springframework.beans.factory.annotation.Autowired; @@ -14,33 +13,30 @@ import org.springframework.security.config.annotation.method.configuration.Enabl import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.core.session.SessionRegistry; import org.springframework.security.core.session.SessionRegistryImpl; -import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.ClientRegistrations; import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; -import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.oauth2.core.user.OAuth2UserAuthority; import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; import org.springframework.security.web.savedrequest.NullRequestCache; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -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.oauth2.CustomOAuth2AuthenticationFailureHandler; +import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationSuccessHandler; +import stirling.software.SPDF.config.security.oauth2.CustomOAuth2LogoutSuccessHandler; +import stirling.software.SPDF.config.security.oauth2.CustomOAuth2UserService; import stirling.software.SPDF.model.ApplicationProperties; +import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2; import stirling.software.SPDF.model.User; import stirling.software.SPDF.repository.JPATokenRepositoryImpl; @@ -49,7 +45,7 @@ import stirling.software.SPDF.repository.JPATokenRepositoryImpl; @EnableMethodSecurity public class SecurityConfiguration { - @Autowired private UserDetailsService userDetailsService; + @Autowired private CustomUserDetailsService userDetailsService; @Bean public PasswordEncoder passwordEncoder() { @@ -98,7 +94,8 @@ public class SecurityConfiguration { formLogin .loginPage("/login") .successHandler( - new CustomAuthenticationSuccessHandler()) + new CustomAuthenticationSuccessHandler( + loginAttemptService)) .defaultSuccessUrl("/") .failureHandler( new CustomAuthenticationFailureHandler( @@ -111,18 +108,7 @@ public class SecurityConfiguration { new AntPathRequestMatcher("/logout")) .logoutSuccessHandler(new CustomLogoutSuccessHandler()) .invalidateHttpSession(true) // Invalidate session - .deleteCookies("JSESSIONID", "remember-me") - .addLogoutHandler( - (request, response, authentication) -> { - HttpSession session = - request.getSession(false); - if (session != null) { - String sessionId = session.getId(); - sessionRegistry() - .removeSessionInformation( - sessionId); - } - })) + .deleteCookies("JSESSIONID", "remember-me")) .rememberMe( rememberMeConfigurer -> rememberMeConfigurer // Use the configurator directly @@ -161,50 +147,43 @@ public class SecurityConfiguration { .permitAll() .anyRequest() .authenticated()) - .userDetailsService(userDetailsService) .authenticationProvider(authenticationProvider()); // Handle OAUTH2 Logins if (applicationProperties.getSecurity().getOAUTH2().getEnabled()) { http.oauth2Login( - oauth2 -> - oauth2.loginPage("/oauth2") - /* - This Custom handler is used to check if the OAUTH2 user trying to log in, already exists in the database. - If user exists, login proceeds as usual. If user does not exist, then it is autocreated but only if 'OAUTH2AutoCreateUser' - is set as true, else login fails with an error message advising the same. - */ - .successHandler( - new AuthenticationSuccessHandler() { - @Override - public void onAuthenticationSuccess( - HttpServletRequest request, - HttpServletResponse response, - Authentication authentication) - throws ServletException, IOException { - OAuth2User oauthUser = - (OAuth2User) - authentication - .getPrincipal(); - if (userService.processOAuth2PostLogin( - oauthUser.getAttribute("email"), - applicationProperties - .getSecurity() - .getOAUTH2() - .getAutoCreateUser())) { - response.sendRedirect("/"); - } else { - response.sendRedirect( - "/logout?oauth2AutoCreateDisabled=true"); - } - } - }) - // Add existing Authorities from the database - .userInfoEndpoint( - userInfoEndpoint -> - userInfoEndpoint.userAuthoritiesMapper( - userAuthoritiesMapper()))); + oauth2 -> + oauth2.loginPage("/oauth2") + /* + This Custom handler is used to check if the OAUTH2 user trying to log in, already exists in the database. + If user exists, login proceeds as usual. If user does not exist, then it is autocreated but only if 'OAUTH2AutoCreateUser' + is set as true, else login fails with an error message advising the same. + */ + .successHandler( + new CustomOAuth2AuthenticationSuccessHandler( + loginAttemptService, + applicationProperties, + userService)) + .failureHandler( + new CustomOAuth2AuthenticationFailureHandler()) + // Add existing Authorities from the database + .userInfoEndpoint( + userInfoEndpoint -> + userInfoEndpoint + .oidcUserService( + new CustomOAuth2UserService( + applicationProperties, + userService, + loginAttemptService)) + .userAuthoritiesMapper( + userAuthoritiesMapper()))) + .logout( + logout -> + logout.logoutSuccessHandler( + new CustomOAuth2LogoutSuccessHandler( + this.applicationProperties, + sessionRegistry()))); } } else { http.csrf(csrf -> csrf.disable()) @@ -225,13 +204,13 @@ public class SecurityConfiguration { } private ClientRegistration oidcClientRegistration() { - return ClientRegistrations.fromOidcIssuerLocation( - applicationProperties.getSecurity().getOAUTH2().getIssuer()) + OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2(); + return ClientRegistrations.fromIssuerLocation(oauth.getIssuer()) .registrationId("oidc") - .clientId(applicationProperties.getSecurity().getOAUTH2().getClientId()) - .clientSecret(applicationProperties.getSecurity().getOAUTH2().getClientSecret()) - .scope("openid", "profile", "email") - .userNameAttributeName("email") + .clientId(oauth.getClientId()) + .clientSecret(oauth.getClientSecret()) + .scope(oauth.getScopes()) + .userNameAttributeName(oauth.getUseAsUsername()) .clientName("OIDC") .build(); } @@ -256,9 +235,14 @@ public class SecurityConfiguration { // Add Authorities from database for existing user, if user is present. if (authority instanceof OAuth2UserAuthority oauth2Auth) { + String useAsUsername = + applicationProperties + .getSecurity() + .getOAUTH2() + .getUseAsUsername(); Optional userOpt = userService.findByUsernameIgnoreCase( - (String) oauth2Auth.getAttributes().get("email")); + (String) oauth2Auth.getAttributes().get(useAsUsername)); if (userOpt.isPresent()) { User user = userOpt.get(); if (user != null) { diff --git a/src/main/java/stirling/software/SPDF/config/security/UserService.java b/src/main/java/stirling/software/SPDF/config/security/UserService.java index 7d57f8f6..1fcf63f8 100644 --- a/src/main/java/stirling/software/SPDF/config/security/UserService.java +++ b/src/main/java/stirling/software/SPDF/config/security/UserService.java @@ -8,6 +8,8 @@ import java.util.UUID; import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; @@ -18,6 +20,7 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface; +import stirling.software.SPDF.model.AuthenticationType; import stirling.software.SPDF.model.Authority; import stirling.software.SPDF.model.Role; import stirling.software.SPDF.model.User; @@ -33,19 +36,19 @@ public class UserService implements UserServiceInterface { @Autowired private PasswordEncoder passwordEncoder; + @Autowired private MessageSource messageSource; + // Handle OAUTH2 login and user auto creation. public boolean processOAuth2PostLogin(String username, boolean autoCreateUser) { - Optional existUser = userRepository.findByUsernameIgnoreCase(username); - if (existUser.isPresent()) { + if (!isUsernameValid(username)) { + return false; + } + Optional existingUser = userRepository.findByUsernameIgnoreCase(username); + if (existingUser.isPresent()) { return true; } if (autoCreateUser) { - User user = new User(); - user.setUsername(username); - user.setEnabled(true); - user.setFirstLogin(false); - user.addAuthority(new Authority(Role.USER.getRoleId(), user)); - userRepository.save(user); + saveUser(username, AuthenticationType.OAUTH2); return true; } return false; @@ -111,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(), @@ -125,35 +127,53 @@ public class UserService implements UserServiceInterface { public boolean validateApiKeyForUser(String username, String apiKey) { Optional 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, String password) { + public void saveUser(String username, AuthenticationType authenticationType) + throws IllegalArgumentException { + if (!isUsernameValid(username)) { + throw new IllegalArgumentException(getInvalidUsernameMessage()); + } + User user = new User(); + user.setUsername(username); + user.setEnabled(true); + user.setFirstLogin(false); + user.addAuthority(new Authority(Role.USER.getRoleId(), user)); + user.setAuthenticationType(authenticationType); + userRepository.save(user); + } + + public void saveUser(String username, String password) throws IllegalArgumentException { + if (!isUsernameValid(username)) { + throw new IllegalArgumentException(getInvalidUsernameMessage()); + } User user = new User(); user.setUsername(username); user.setPassword(passwordEncoder.encode(password)); user.setEnabled(true); + user.setAuthenticationType(AuthenticationType.WEB); userRepository.save(user); } - public void saveUser(String username, String password, String role, boolean firstLogin) { + 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(username); user.setPassword(passwordEncoder.encode(password)); user.addAuthority(new Authority(role, user)); user.setEnabled(true); + user.setAuthenticationType(AuthenticationType.WEB); user.setFirstLogin(firstLogin); userRepository.save(user); } - public void saveUser(String username, String password, String role) { - User user = new User(); - user.setUsername(username); - user.setPassword(passwordEncoder.encode(password)); - user.addAuthority(new Authority(role, user)); - user.setEnabled(true); - user.setFirstLogin(false); - userRepository.save(user); + public void saveUser(String username, String password, String role) + throws IllegalArgumentException { + saveUser(username, password, role, false); } public void deleteUser(String username) { @@ -187,7 +207,7 @@ public class UserService implements UserServiceInterface { Map settingsMap = user.getSettings(); if (settingsMap == null) { - settingsMap = new HashMap(); + settingsMap = new HashMap<>(); } settingsMap.clear(); settingsMap.putAll(updates); @@ -209,7 +229,10 @@ public class UserService implements UserServiceInterface { return authorityRepository.findByUserId(user.getId()); } - public void changeUsername(User user, String newUsername) { + public void changeUsername(User user, String newUsername) throws IllegalArgumentException { + if (!isUsernameValid(newUsername)) { + throw new IllegalArgumentException(getInvalidUsernameMessage()); + } user.setUsername(newUsername); userRepository.save(user); } @@ -235,6 +258,30 @@ public class UserService implements UserServiceInterface { } public boolean isUsernameValid(String username) { - return username.matches("[a-zA-Z0-9]+"); + // Checks whether the simple username is formatted correctly + boolean isValidSimpleUsername = + username.matches("^[a-zA-Z0-9][a-zA-Z0-9@._+-]*[a-zA-Z0-9]$"); + // Checks whether the email address is formatted correctly + boolean isValidEmail = + username.matches( + "^(?=.{1,64}@)[A-Za-z0-9]+(\\.[A-Za-z0-9_+.-]+)*@[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$"); + return isValidSimpleUsername || isValidEmail; + } + + private String getInvalidUsernameMessage() { + return messageSource.getMessage( + "invalidUsernameMessage", null, LocaleContextHolder.getLocale()); + } + + public boolean hasPassword(String username) { + Optional user = userRepository.findByUsernameIgnoreCase(username); + return user.isPresent() && user.get().hasPassword(); + } + + public boolean isAuthenticationTypeByUsername( + String username, AuthenticationType authenticationType) { + Optional user = userRepository.findByUsernameIgnoreCase(username); + return user.isPresent() + && authenticationType.name().equalsIgnoreCase(user.get().getAuthenticationType()); } } diff --git a/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationFailureHandler.java b/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationFailureHandler.java new file mode 100644 index 00000000..6b6a72c1 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationFailureHandler.java @@ -0,0 +1,49 @@ +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.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.OAuth2Error; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class CustomOAuth2AuthenticationFailureHandler + extends SimpleUrlAuthenticationFailureHandler { + + private static final Logger logger = + LoggerFactory.getLogger(CustomOAuth2AuthenticationFailureHandler.class); + + @Override + public void onAuthenticationFailure( + HttpServletRequest request, + HttpServletResponse response, + AuthenticationException exception) + 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, "/logout?erroroauth=" + errorCode); + return; + } else if (exception instanceof LockedException) { + logger.error("Account locked: ", exception); + getRedirectStrategy().sendRedirect(request, response, "/logout?error=locked"); + } else { + logger.error("Unhandled authentication exception", exception); + super.onAuthenticationFailure(request, response, exception); + } + } +} diff --git a/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java b/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java new file mode 100644 index 00000000..8468de5d --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java @@ -0,0 +1,93 @@ +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 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; + +public class CustomOAuth2AuthenticationSuccessHandler + extends SavedRequestAwareAuthenticationSuccessHandler { + + private LoginAttemptService loginAttemptService; + + private static final Logger logger = + LoggerFactory.getLogger(CustomOAuth2AuthenticationSuccessHandler.class); + + private ApplicationProperties applicationProperties; + private UserService userService; + + public CustomOAuth2AuthenticationSuccessHandler( + final LoginAttemptService loginAttemptService, + ApplicationProperties applicationProperties, + UserService userService) { + this.applicationProperties = applicationProperties; + this.userService = userService; + this.loginAttemptService = loginAttemptService; + } + + @Override + public void onAuthenticationSuccess( + HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws ServletException, IOException { + + // Get the saved request + HttpSession session = request.getSession(false); + SavedRequest savedRequest = + (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.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?oauth2AuthenticationErrorWeb=true"); + return; + } else { + try { + userService.processOAuth2PostLogin(username, oAuth.getAutoCreateUser()); + response.sendRedirect("/"); + return; + } catch (IllegalArgumentException e) { + response.sendRedirect("/logout?invalidUsername=true"); + return; + } + } + } + } +} diff --git a/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2LogoutSuccessHandler.java b/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2LogoutSuccessHandler.java new file mode 100644 index 00000000..ea1b05b2 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2LogoutSuccessHandler.java @@ -0,0 +1,86 @@ +package stirling.software.SPDF.config.security.oauth2; + +import java.io.IOException; + +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.web.authentication.logout.SimpleUrlLogoutSuccessHandler; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import stirling.software.SPDF.model.ApplicationProperties; +import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2; + +public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { + + private static final Logger logger = + LoggerFactory.getLogger(CustomOAuth2LogoutSuccessHandler.class); + + private final SessionRegistry sessionRegistry; + private final ApplicationProperties applicationProperties; + + public CustomOAuth2LogoutSuccessHandler( + ApplicationProperties applicationProperties, SessionRegistry sessionRegistry) { + this.sessionRegistry = sessionRegistry; + this.applicationProperties = applicationProperties; + } + + @Override + public void onLogoutSuccess( + HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException { + + String param = "logout=true"; + + OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2(); + String provider = oauth.getProvider() != null ? oauth.getProvider() : ""; + + 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) { + case "keycloak": + String logoutUrl = + oauth.getIssuer() + + "/protocol/openid-connect/logout" + + "?client_id=" + + oauth.getClientId() + + "&post_logout_redirect_uri=" + + response.encodeRedirectURL( + 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: + String redirectUrl = request.getContextPath() + "/login?" + param; + logger.debug("Redirecting to default logout URL: " + redirectUrl); + response.sendRedirect(redirectUrl); + break; + } + } +} diff --git a/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2UserService.java b/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2UserService.java new file mode 100644 index 00000000..a5d65ff0 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2UserService.java @@ -0,0 +1,73 @@ +package stirling.software.SPDF.config.security.oauth2; + +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; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +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 { + + private final OidcUserService delegate = new OidcUserService(); + + private UserService userService; + + private LoginAttemptService loginAttemptService; + + private 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 + public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { + String usernameAttribute = + applicationProperties.getSecurity().getOAUTH2().getUseAsUsername(); + try { + OidcUser user = delegate.loadUser(userRequest); + String username = user.getUserInfo().getClaimAsString(usernameAttribute); + Optional 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(), + userRequest.getIdToken(), + user.getUserInfo(), + usernameAttribute); + } catch (java.lang.IllegalArgumentException 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"); + } + } +} diff --git a/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuthUserService.java b/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuthUserService.java new file mode 100644 index 00000000..69900b4a --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuthUserService.java @@ -0,0 +1,57 @@ +package stirling.software.SPDF.config.security.oauth2; + +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +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.model.ApplicationProperties; + +public class CustomOAuthUserService implements OAuth2UserService { + + private static final Logger logger = LoggerFactory.getLogger(CustomOAuthUserService.class); + + private final OidcUserService delegate = new OidcUserService(); + + private ApplicationProperties applicationProperties; + + public CustomOAuthUserService(ApplicationProperties applicationProperties) { + this.applicationProperties = applicationProperties; + } + + @Override + public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { + String usernameAttribute = + applicationProperties.getSecurity().getOAUTH2().getUseAsUsername(); + try { + + OidcUser user = delegate.loadUser(userRequest); + Map attributes = new HashMap<>(user.getAttributes()); + + // Ensure the preferred username attribute is present + if (!attributes.containsKey(usernameAttribute)) { + attributes.put(usernameAttribute, attributes.getOrDefault("email", "")); + usernameAttribute = "email"; + logger.info("Adjusted username attribute to use email"); + } + + // Return a new OidcUser with adjusted attributes + return new DefaultOidcUser( + user.getAuthorities(), + userRequest.getIdToken(), + user.getUserInfo(), + usernameAttribute); + } catch (java.lang.IllegalArgumentException e) { + throw new OAuth2AuthenticationException( + new OAuth2Error(e.getMessage()), e.getMessage(), e); + } + } +} diff --git a/src/main/java/stirling/software/SPDF/controller/api/UserController.java b/src/main/java/stirling/software/SPDF/controller/api/UserController.java index f5b7b159..ae85d650 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/UserController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/UserController.java @@ -47,8 +47,11 @@ public class UserController { model.addAttribute("error", "Username already exists"); return "register"; } - - userService.saveUser(requestModel.getUsername(), requestModel.getPassword()); + try { + userService.saveUser(requestModel.getUsername(), requestModel.getPassword()); + } catch (IllegalArgumentException e) { + return "redirect:/login?messageType=invalidUsername"; + } return "redirect:/login?registered=true"; } @@ -92,7 +95,11 @@ public class UserController { } if (newUsername != null && newUsername.length() > 0) { - userService.changeUsername(user, newUsername); + try { + userService.changeUsername(user, newUsername); + } catch (IllegalArgumentException e) { + return new RedirectView("/account?messageType=invalidUsername"); + } } // Logout using Spring's utility diff --git a/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java b/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java index bc43bd4b..3a2074a7 100644 --- a/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java +++ b/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java @@ -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 allUsers = userRepository.findAll(); Iterator iterator = allUsers.iterator(); Map 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); @@ -109,8 +199,9 @@ public class AccountWebController { OAuth2User userDetails = (OAuth2User) principal; // Retrieve username and other attributes - username = userDetails.getAttribute("email"); - + username = + userDetails.getAttribute( + applicationProperties.getSecurity().getOAUTH2().getUseAsUsername()); // Add oAuth2 Login attributes to the model model.addAttribute("oAuth2Login", true); } @@ -135,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()); @@ -173,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); } diff --git a/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java b/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java index ce109299..2623d8f8 100644 --- a/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java +++ b/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java @@ -1,6 +1,10 @@ package stirling.software.SPDF.model; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; @@ -221,6 +225,10 @@ public class ApplicationProperties { private String clientId; private String clientSecret; private boolean autoCreateUser; + private String useAsUsername; + private String provider; + + private Collection scopes = new ArrayList(); public boolean getEnabled() { return enabled; @@ -262,6 +270,37 @@ public class ApplicationProperties { this.autoCreateUser = autoCreateUser; } + public String getUseAsUsername() { + if (useAsUsername != null && useAsUsername.trim().length() > 0) { + return useAsUsername; + } + return "email"; + } + + public void setUseAsUsername(String useAsUsername) { + this.useAsUsername = useAsUsername; + } + + public String getProvider() { + return provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + public Collection getScopes() { + return scopes; + } + + public void setScopes(String scpoes) { + List scopesList = + Arrays.stream(scpoes.split(",")) + .map(String::trim) + .collect(Collectors.toList()); + this.scopes.addAll(scopesList); + } + @Override public String toString() { return "OAUTH2 [enabled=" @@ -274,6 +313,12 @@ public class ApplicationProperties { + (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL") + ", autoCreateUser=" + autoCreateUser + + ", useAsUsername=" + + useAsUsername + + ", provider" + + provider + + ", scopes=" + + scopes + "]"; } } diff --git a/src/main/java/stirling/software/SPDF/model/AttemptCounter.java b/src/main/java/stirling/software/SPDF/model/AttemptCounter.java index 7cb13ee0..8267bddb 100644 --- a/src/main/java/stirling/software/SPDF/model/AttemptCounter.java +++ b/src/main/java/stirling/software/SPDF/model/AttemptCounter.java @@ -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(); } } diff --git a/src/main/java/stirling/software/SPDF/model/AuthenticationType.java b/src/main/java/stirling/software/SPDF/model/AuthenticationType.java new file mode 100644 index 00000000..58e7befb --- /dev/null +++ b/src/main/java/stirling/software/SPDF/model/AuthenticationType.java @@ -0,0 +1,6 @@ +package stirling.software.SPDF.model; + +public enum AuthenticationType { + WEB, + OAUTH2 +} diff --git a/src/main/java/stirling/software/SPDF/model/User.java b/src/main/java/stirling/software/SPDF/model/User.java index f6f9e532..74780dd8 100644 --- a/src/main/java/stirling/software/SPDF/model/User.java +++ b/src/main/java/stirling/software/SPDF/model/User.java @@ -47,6 +47,9 @@ public class User { @Column(name = "roleName") private String roleName; + @Column(name = "authenticationtype") + private String authenticationType; + @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user") private Set authorities = new HashSet<>(); @@ -116,6 +119,14 @@ public class User { this.enabled = enabled; } + public void setAuthenticationType(AuthenticationType authenticationType) { + this.authenticationType = authenticationType.toString().toLowerCase(); + } + + public String getAuthenticationType() { + return authenticationType; + } + public Set getAuthorities() { return authorities; } @@ -137,4 +148,8 @@ public class User { .map(Authority::getAuthority) .collect(Collectors.joining(", ")); } + + public boolean hasPassword() { + return this.password != null && !this.password.isEmpty(); + } } diff --git a/src/main/resources/messages_ar_AR.properties b/src/main/resources/messages_ar_AR.properties index 175ee607..ec327fcd 100644 --- a/src/main/resources/messages_ar_AR.properties +++ b/src/main/resources/messages_ar_AR.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. -invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. deleteCurrentUserMessage=Cannot delete currently logged in user. deleteUsernameExistsMessage=The username does not exist and cannot be deleted. downgradeCurrentUserMessage=لا يمكن خفض دور المستخدم الحالي downgradeCurrentUserLongMessage=لا يمكن تخفيض دور المستخدم الحالي. وبالتالي، لن يظهر المستخدم الحالي. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Error oops=Oops! help=Help @@ -72,6 +74,7 @@ sponsor=Sponsor info=Info + ############### # Pipeline # ############### @@ -170,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings adminUserSettings.admin=Admin adminUserSettings.user=User adminUserSettings.addUser=Add New User -adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. adminUserSettings.roles=Roles adminUserSettings.role=Role adminUserSettings.actions=Actions @@ -182,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Save User adminUserSettings.changeUserRole=تغيير دور المستخدم +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -1056,7 +1060,7 @@ licenses.version=Version licenses.license=License -# error +#error error.sorry=Sorry for the issue! error.needHelp=Need help / Found an issue? error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: diff --git a/src/main/resources/messages_bg_BG.properties b/src/main/resources/messages_bg_BG.properties index 60fb5220..69c63c60 100644 --- a/src/main/resources/messages_bg_BG.properties +++ b/src/main/resources/messages_bg_BG.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=Потребителят не е автентикира userNotFoundMessage=Потребителят не е намерен incorrectPasswordMessage=Текущата парола е неправилна. usernameExistsMessage=Новият потребител вече съществува. -invalidUsernameMessage=Невалидно потребителско име, потребителското име трябва да съдържа само букви и цифри. +invalidUsernameMessage=Невалидно потребителско име, потребителското име може да съдържа само букви, цифри и следните специални знаци @._+- или трябва да е валиден имейл адрес. deleteCurrentUserMessage=Не може да се изтрие вписания в момента потребител. deleteUsernameExistsMessage=Потребителското име не съществува и не може да бъде изтрито. downgradeCurrentUserMessage=Не може да се понижи ролята на текущия потребител downgradeCurrentUserLongMessage=Не може да се понижи ролята на текущия потребител. Следователно текущият потребител няма да бъде показан. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Грешка oops=Опаа! help=Помощ @@ -73,7 +75,6 @@ info=Info - ############### # Pipeline # ############### @@ -172,7 +173,7 @@ adminUserSettings.header=Настройки за администраторск adminUserSettings.admin=Администратор adminUserSettings.user=Потребител adminUserSettings.addUser=Добавяне на нов потребител -adminUserSettings.usernameInfo=Потребителското име трябва да съдържа само букви и цифри, без интервали или специални знаци. +adminUserSettings.usernameInfo=Потребителското име може да съдържа само букви, цифри и следните специални символи @._+- или трябва да е валиден имейл адрес. adminUserSettings.roles=Роли adminUserSettings.role=Роля adminUserSettings.actions=Действия @@ -184,6 +185,7 @@ adminUserSettings.internalApiUser=Вътрешен API потребител adminUserSettings.forceChange=Принудете потребителя да промени потребителското име/парола при влизане adminUserSettings.submit=Съхранете потребителя adminUserSettings.changeUserRole=Промяна на ролята на потребителя +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -805,7 +807,6 @@ multiTool.title=PDF Мулти инструмент multiTool.header=PDF Мулти инструмент multiTool.uploadPrompts=File Name - #view pdf viewPdf.title=Преглед на PDF viewPdf.header=Преглед на PDF @@ -1059,7 +1060,7 @@ licenses.version=Версия licenses.license=Лиценз -# error +#error error.sorry=Извинете за проблема! error.needHelp=Нуждаете се от помощ / Открихте проблем? error.contactTip=Ако все още имате проблеми, не се колебайте да се свържете с нас за помощ. Можете да изпратите запитване на нашата страница в GitHub или да се свържете с нас чрез Discord: diff --git a/src/main/resources/messages_ca_CA.properties b/src/main/resources/messages_ca_CA.properties index 9c4451a0..938de9b4 100644 --- a/src/main/resources/messages_ca_CA.properties +++ b/src/main/resources/messages_ca_CA.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. -invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. deleteCurrentUserMessage=Cannot delete currently logged in user. deleteUsernameExistsMessage=The username does not exist and cannot be deleted. downgradeCurrentUserMessage=No es pot reduir la funció de l'usuari actual downgradeCurrentUserLongMessage=No es pot baixar la funció de l'usuari actual. Per tant, no es mostrarà l'usuari actual. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Error oops=Oops! help=Help @@ -171,7 +173,7 @@ adminUserSettings.header=Usuari Admin Opcions Control adminUserSettings.admin=Admin adminUserSettings.user=Usuari adminUserSettings.addUser=Afegir Usuari -adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. adminUserSettings.roles=Rols adminUserSettings.role=Rol adminUserSettings.actions=Accions @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Desar Usuari adminUserSettings.changeUserRole=Canvia el rol de l'usuari +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=PDF Multi Tool multiTool.header=PDF Multi Tool -multiTool.uploadPrompts=Please Upload PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=View PDF @@ -1057,7 +1060,7 @@ licenses.version=Version licenses.license=License -# error +#error error.sorry=Sorry for the issue! error.needHelp=Need help / Found an issue? error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: diff --git a/src/main/resources/messages_de_DE.properties b/src/main/resources/messages_de_DE.properties index 19a82fce..26a643fd 100644 --- a/src/main/resources/messages_de_DE.properties +++ b/src/main/resources/messages_de_DE.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=Benutzer nicht authentifiziert. userNotFoundMessage=Benutzer nicht gefunden. incorrectPasswordMessage=Das Passwort ist falsch. usernameExistsMessage=Neuer Benutzername existiert bereits. -invalidUsernameMessage=Ungültiger Benutzername. Der Benutzername darf nur Buchstaben und Zahlen enthalten. +invalidUsernameMessage=Ungültiger Benutzername. Der Benutzername darf nur Buchstaben, Zahlen und die folgenden Sonderzeichen @._+- enthalten oder muss eine gültige E-Mail-Adresse sein. deleteCurrentUserMessage=Der aktuell angemeldete Benutzer kann nicht gelöscht werden. deleteUsernameExistsMessage=Der Benutzername existiert nicht und kann nicht gelöscht werden. downgradeCurrentUserMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden downgradeCurrentUserLongMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden. Daher wird der aktuelle Benutzer nicht angezeigt. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Fehler oops=Hoppla! help=Hilfe @@ -171,7 +173,7 @@ adminUserSettings.header=Administrator-Benutzerkontrolle adminUserSettings.admin=Administrator adminUserSettings.user=Benutzer adminUserSettings.addUser=Neuen Benutzer hinzufügen -adminUserSettings.usernameInfo=Der Benutzername darf nur Buchstaben und Zahlen enthalten, keine Leerzeichen oder Sonderzeichen. +adminUserSettings.usernameInfo=Der Benutzername darf nur Buchstaben, Zahlen und die folgenden Sonderzeichen @._+- enthalten oder muss eine gültige E-Mail-Adresse sein. adminUserSettings.roles=Rollen adminUserSettings.role=Rolle adminUserSettings.actions=Aktion @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Interner API-Benutzer adminUserSettings.forceChange=Benutzer dazu zwingen, Benutzernamen/Passwort bei der Anmeldung zu ändern adminUserSettings.submit=Benutzer speichern adminUserSettings.changeUserRole=Benutzerrolle ändern +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(z.B. 1,3,2 oder 4-8,2,10-12 oder 2n-1) #multiTool multiTool.title=PDF-Multitool multiTool.header=PDF-Multitool -multiTool.uploadPrompts=Bitte PDF hochladen +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=PDF anzeigen @@ -1057,7 +1060,7 @@ licenses.version=Version licenses.license=Lizenz -# error +#error error.sorry=Entschuldigung für das Problem! error.needHelp=Brauchst du Hilfe / Ein Problem gefunden? error.contactTip=Wenn du weiterhin Probleme hast, zögere nicht, uns um Hilfe zu bitten. Du kannst ein Ticket auf unserer GitHub-Seite einreichen oder uns über Discord kontaktieren: diff --git a/src/main/resources/messages_el_GR.properties b/src/main/resources/messages_el_GR.properties index b093516f..2c4b589d 100644 --- a/src/main/resources/messages_el_GR.properties +++ b/src/main/resources/messages_el_GR.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=Ο χρήστης δεν έχει αυθεντικοπο userNotFoundMessage=Ο χρήστης δεν βρέθηκε. incorrectPasswordMessage=Ο τρέχων κωδικός πρόσβασης είναι λανθασμένος. usernameExistsMessage=Το νέο όνομα χρήστη υπάρχει ήδη. -invalidUsernameMessage=Μη έγκυρο όνομα χρήστη, το όνομα χρήστη πρέπει να περιέχει μόνο αλφαβητικούς χαρακτήρες και αριθμούς. +invalidUsernameMessage=Μη έγκυρο όνομα χρήστη, όνομα χρήστη μπορεί να περιέχει μόνο γράμματα, αριθμούς και τους ακόλουθους ειδικούς χαρακτήρες @._+- ή πρέπει να είναι έγκυρη διεύθυνση email. deleteCurrentUserMessage=Δεν είναι δυνατή η διαγραφή του τρέχοντος συνδεδεμένου χρήστη. deleteUsernameExistsMessage=Το όνομα χρήστη δεν υπάρχει και δεν μπορεί να διαγραφεί. downgradeCurrentUserMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη downgradeCurrentUserLongMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη. Ως εκ τούτου, ο τρέχων χρήστης δεν θα εμφανίζεται. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Σφάλμα oops=Ωχ! help=Βοήθεια @@ -171,7 +173,7 @@ adminUserSettings.header=Ρυθμίσεις ελέγχου Διαχειριστ adminUserSettings.admin=Διαχειριστής adminUserSettings.user=Χρήστης adminUserSettings.addUser=Προσθήκη νέου Χρήστη -adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. adminUserSettings.roles=Ρόλοι adminUserSettings.role=Ρόλος adminUserSettings.actions=Ενέργειες @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Εσωτερικός API χρήστης adminUserSettings.forceChange=Αναγκάστε τον χρήστη να αλλάξει το όνομα χρήστη/κωδικό πρόσβασης κατά τη σύνδεση adminUserSettings.submit=Αποθήκευση Χρήστη adminUserSettings.changeUserRole=Αλλαγή ρόλου χρήστη +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(π.χ. 1,3,2 ή 4-8,2,10-12 ή 2n-1) #multiTool multiTool.title=PDF Πολυεργαλείο multiTool.header=PDF Πολυεργαλείο -multiTool.uploadPrompts=Ανεβάστε το PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=Προβολή PDF @@ -1057,7 +1060,7 @@ licenses.version=Εκδοχή licenses.license=Άδεια -# error +#error error.sorry=Συγγνώμη για το ζήτημα! error.needHelp=Χρειάζεστε βοήθεια / Βρήκατε πρόβλημα; error.contactTip=Εάν εξακολουθείτε να αντιμετωπίζετε προβλήματα, μη διστάσετε να επικοινωνήσετε μαζί μας για βοήθεια. Μπορείτε να υποβάλετε ένα ticket στη σελίδα μας στο GitHub ή να επικοινωνήσετε μαζί μας μέσω του Discord: diff --git a/src/main/resources/messages_en_GB.properties b/src/main/resources/messages_en_GB.properties index aa0c4d69..4db7aa9c 100644 --- a/src/main/resources/messages_en_GB.properties +++ b/src/main/resources/messages_en_GB.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. -invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. deleteCurrentUserMessage=Cannot delete currently logged in user. deleteUsernameExistsMessage=The username does not exist and cannot be deleted. downgradeCurrentUserMessage=Cannot downgrade current user's role downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Error oops=Oops! help=Help @@ -171,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings adminUserSettings.admin=Admin adminUserSettings.user=User adminUserSettings.addUser=Add New User -adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. adminUserSettings.roles=Roles adminUserSettings.role=Role adminUserSettings.actions=Actions @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Save User adminUserSettings.changeUserRole=Change User's Role +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -1057,7 +1060,7 @@ licenses.version=Version licenses.license=License -# error +#error error.sorry=Sorry for the issue! error.needHelp=Need help / Found an issue? error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: diff --git a/src/main/resources/messages_en_US.properties b/src/main/resources/messages_en_US.properties index 66c2b4bd..fc9ef598 100644 --- a/src/main/resources/messages_en_US.properties +++ b/src/main/resources/messages_en_US.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. -invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. deleteCurrentUserMessage=Cannot delete currently logged in user. deleteUsernameExistsMessage=The username does not exist and cannot be deleted. downgradeCurrentUserMessage=Cannot downgrade current user's role downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Error oops=Oops! help=Help @@ -72,6 +74,7 @@ sponsor=Sponsor info=Info + ############### # Pipeline # ############### @@ -170,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings adminUserSettings.admin=Admin adminUserSettings.user=User adminUserSettings.addUser=Add New User -adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. adminUserSettings.roles=Roles adminUserSettings.role=Role adminUserSettings.actions=Actions @@ -182,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Save User adminUserSettings.changeUserRole=Change User's Role +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -1056,7 +1060,7 @@ licenses.version=Version licenses.license=License -# error +#error error.sorry=Sorry for the issue! error.needHelp=Need help / Found an issue? error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: diff --git a/src/main/resources/messages_es_ES.properties b/src/main/resources/messages_es_ES.properties index 4cb6360a..c5277774 100644 --- a/src/main/resources/messages_es_ES.properties +++ b/src/main/resources/messages_es_ES.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=Usuario no autentificado. userNotFoundMessage=Usuario no encontrado. incorrectPasswordMessage=La contraseña actual no es correcta. usernameExistsMessage=El nuevo nombre de usuario está en uso. -invalidUsernameMessage=Nombre de usuario no válido, El nombre de ususario debe contener únicamente números y caracteres alfabéticos. +invalidUsernameMessage=Nombre de usuario no válido, el nombre de usuario solo puede contener letras, números y los siguientes caracteres especiales @._+- o debe ser una dirección de correo electrónico válida. deleteCurrentUserMessage=No puede eliminar el usuario que tiene la sesión actualmente en uso. deleteUsernameExistsMessage=El usuario no existe y no puede eliminarse. downgradeCurrentUserMessage=No se puede degradar el rol del usuario actual downgradeCurrentUserLongMessage=No se puede degradar el rol del usuario actual. Por lo tanto, el usuario actual no se mostrará. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Error oops=Ups! help=Help @@ -73,9 +75,6 @@ info=Info - - - ############### # Pipeline # ############### @@ -174,7 +173,7 @@ adminUserSettings.header=Configuración de control de usuario administrador adminUserSettings.admin=Administrador adminUserSettings.user=Usuario adminUserSettings.addUser=Añadir Nuevo Usuario -adminUserSettings.usernameInfo=El nombrede usuario debe contener únicamente letras y números, no espacios ni caracteres especiales. +adminUserSettings.usernameInfo=El nombre de usuario solo puede contener letras, números y los siguientes caracteres especiales @._+- o debe ser una dirección de correo electrónico válida. adminUserSettings.roles=Roles adminUserSettings.role=Rol adminUserSettings.actions=Acciones @@ -186,6 +185,7 @@ adminUserSettings.internalApiUser=Usuario interno de API adminUserSettings.forceChange=Forzar usuario a cambiar usuario/contraseña en el acceso adminUserSettings.submit=Guardar Usuario adminUserSettings.changeUserRole=Cambiar rol de usuario +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -805,7 +805,7 @@ pdfOrganiser.placeholder=(por ej., 1,3,2 o 4-8,2,10-12 o 2n-1) #multiTool multiTool.title=Multi-herramienta PDF multiTool.header=Multi-herramienta PDF -multiTool.uploadPrompts=Por favor, cargue PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=Ver PDF @@ -1060,7 +1060,7 @@ licenses.version=Versión licenses.license=Licencia -# error +#error error.sorry=¡Perdón por el fallo! error.needHelp=Necesita ayuda / Encontró un fallo? error.contactTip=Si sigue experimentando errores, no dude en contactarnos para solicitar soporte. Puede enviarnos un ticket en la página de GitHub o contactarnos mediante Discord: diff --git a/src/main/resources/messages_eu_ES.properties b/src/main/resources/messages_eu_ES.properties index 285d41c5..44a07a11 100644 --- a/src/main/resources/messages_eu_ES.properties +++ b/src/main/resources/messages_eu_ES.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. -invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. deleteCurrentUserMessage=Cannot delete currently logged in user. deleteUsernameExistsMessage=The username does not exist and cannot be deleted. downgradeCurrentUserMessage=Ezin da uneko erabiltzailearen rola jaitsi downgradeCurrentUserLongMessage=Ezin da uneko erabiltzailearen rola jaitsi. Beraz, oraingo erabiltzailea ez da erakutsiko. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Error oops=Oops! help=Help @@ -171,7 +173,7 @@ adminUserSettings.header=Admin Erabiltzailearen Ezarpenen Kontrolak adminUserSettings.admin=Admin adminUserSettings.user=Erabiltzaile adminUserSettings.addUser=Erabiltzaile berria -adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. adminUserSettings.roles=Rolak adminUserSettings.role=Rol adminUserSettings.actions=Ekintzak @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Gorde Erabiltzailea adminUserSettings.changeUserRole=Erabiltzailearen rola aldatu +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=PDF erabilera anitzeko tresna multiTool.header=PDF erabilera anitzeko tresna -multiTool.uploadPrompts=Please Upload PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=View PDF @@ -1057,7 +1060,7 @@ licenses.version=Version licenses.license=License -# error +#error error.sorry=Sorry for the issue! error.needHelp=Need help / Found an issue? error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: diff --git a/src/main/resources/messages_fr_FR.properties b/src/main/resources/messages_fr_FR.properties index e3abadef..75357694 100644 --- a/src/main/resources/messages_fr_FR.properties +++ b/src/main/resources/messages_fr_FR.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=Utilisateur non authentifié. userNotFoundMessage=Utilisateur non trouvé. incorrectPasswordMessage=Le mot de passe actuel est incorrect. usernameExistsMessage=Le nouveau nom d’utilisateur existe déjà. -invalidUsernameMessage=Nom d’utilisateur invalide, le nom d’utilisateur ne peut contenir que des chiffres et des lettres. +invalidUsernameMessage=Nom d’utilisateur invalide, le nom d’utilisateur ne peut contenir que des lettres, des chiffres et les caractères spéciaux suivants @._+- ou doit être une adresse e-mail valide. deleteCurrentUserMessage=Impossible de supprimer l’utilisateur actuellement connecté. deleteUsernameExistsMessage=Le nom d’utilisateur n’existe pas et ne peut pas être supprimé. downgradeCurrentUserMessage=Impossible de rétrograder le rôle de l'utilisateur actuel downgradeCurrentUserLongMessage=Impossible de rétrograder le rôle de l'utilisateur actuel. Par conséquent, l'utilisateur actuel ne sera pas affiché. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Erreur oops=Oups ! help=Aide @@ -72,6 +74,7 @@ sponsor=Sponsor info=Info + ############### # Pipeline # ############### @@ -170,7 +173,7 @@ adminUserSettings.header=Administration des paramètres des utilisateurs adminUserSettings.admin=Administateur adminUserSettings.user=Utilisateur adminUserSettings.addUser=Ajouter un utilisateur -adminUserSettings.usernameInfo=Le nom d’utilisateur ne doit contenir que des lettres et des chiffres, sans espaces ni caractères spéciaux. +adminUserSettings.usernameInfo=Le nom d'utilisateur ne peut contenir que des lettres, des chiffres et les caractères spéciaux suivants @._+- ou doit être une adresse e-mail valide. adminUserSettings.roles=Rôles adminUserSettings.role=Rôle adminUserSettings.actions=Actions @@ -182,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Forcer l’utilisateur à changer son nom d’utilisateur/mot de passe lors de la connexion adminUserSettings.submit=Ajouter adminUserSettings.changeUserRole=Changer le rôle de l'utilisateur +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -801,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=Outil multifonction PDF multiTool.header=Outil multifonction PDF -multiTool.uploadPrompts=Please Upload PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=Visualiser un PDF @@ -1056,7 +1060,7 @@ licenses.version=Version licenses.license=Licence -# error +#error error.sorry=Désolé pour ce problème ! error.needHelp=Besoin d’aide / Vous avez trouvé un problème ? error.contactTip=Si vous avez encore des problèmes, n’hésitez pas à nous contacter pour obtenir de l’aide. Vous pouvez soumettre un ticket sur notre page GitHub ou nous contacter via Discord : diff --git a/src/main/resources/messages_hi_IN.properties b/src/main/resources/messages_hi_IN.properties index f91d4900..8fb9d830 100644 --- a/src/main/resources/messages_hi_IN.properties +++ b/src/main/resources/messages_hi_IN.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=उपयोगकर्ता प्रमाणित userNotFoundMessage=उपयोगकर्ता नहीं मिला। incorrectPasswordMessage=वर्तमान पासवर्ड गलत है। usernameExistsMessage=नया उपयोगकर्ता नाम पहले से मौजूद है। -invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. deleteCurrentUserMessage=Cannot delete currently logged in user. deleteUsernameExistsMessage=The username does not exist and cannot be deleted. downgradeCurrentUserMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता downgradeCurrentUserLongMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता। इसलिए, वर्तमान उपयोगकर्ता को नहीं दिखाया जाएगा। +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Error oops=Oops! help=Help @@ -171,7 +173,7 @@ adminUserSettings.header=व्यवस्थापक उपयोगकर् adminUserSettings.admin=व्यवस्थापक adminUserSettings.user=उपयोगकर्ता adminUserSettings.addUser=नया उपयोगकर्ता जोड़ें -adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. adminUserSettings.roles=रोल्स adminUserSettings.role=रोल adminUserSettings.actions=क्रियाएँ @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=उपयोगकर्ता को लॉगिन पर उपयोगकर्ता नाम/पासवर्ड बदलने के लिए मजबूर करें adminUserSettings.submit=उपयोगकर्ता को सहेजें adminUserSettings.changeUserRole=यूज़र की भूमिका बदलें +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=पीडीएफ मल्टी टूल multiTool.header=पीडीएफ मल्टी टूल -multiTool.uploadPrompts=Please Upload PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=पीडीएफ देखें @@ -1057,7 +1060,7 @@ licenses.version=Version licenses.license=License -# error +#error error.sorry=Sorry for the issue! error.needHelp=Need help / Found an issue? error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: diff --git a/src/main/resources/messages_hu_HU.properties b/src/main/resources/messages_hu_HU.properties index e05a1598..6cb09630 100644 --- a/src/main/resources/messages_hu_HU.properties +++ b/src/main/resources/messages_hu_HU.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=Felhasználó nincs hitelesítve. userNotFoundMessage=A felhasználó nem található. incorrectPasswordMessage=A jelenlegi jelszó helytelen. usernameExistsMessage=Az új felhasználónév már létezik. -invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. deleteCurrentUserMessage=Cannot delete currently logged in user. deleteUsernameExistsMessage=The username does not exist and cannot be deleted. downgradeCurrentUserMessage=A jelenlegi felhasználó szerepkörét nem lehet visszaminősíteni downgradeCurrentUserLongMessage=Az aktuális felhasználó szerepkörét nem lehet visszaminősíteni. Ezért az aktuális felhasználó nem jelenik meg. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Error oops=Oops! help=Help @@ -171,7 +173,7 @@ adminUserSettings.header=Adminisztrátori Felhasználói Vezérlési Beállítá adminUserSettings.admin=Adminisztrátor adminUserSettings.user=Felhasználó adminUserSettings.addUser=Új felhasználó hozzáadása -adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. adminUserSettings.roles=Szerepek adminUserSettings.role=Szerep adminUserSettings.actions=Műveletek @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Kényszerítse a felhasználót a felhasználónév/jelszó megváltoztatására bejelentkezéskor adminUserSettings.submit=Felhasználó mentése adminUserSettings.changeUserRole=Felhasználó szerepkörének módosítása +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=PDF többfunkciós eszköz multiTool.header=PDF többfunkciós eszköz -multiTool.uploadPrompts=Please Upload PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=PDF megtekintése @@ -1057,7 +1060,7 @@ licenses.version=Version licenses.license=License -# error +#error error.sorry=Sorry for the issue! error.needHelp=Need help / Found an issue? error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: diff --git a/src/main/resources/messages_id_ID.properties b/src/main/resources/messages_id_ID.properties index 9a8d04f6..7dee099f 100644 --- a/src/main/resources/messages_id_ID.properties +++ b/src/main/resources/messages_id_ID.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=Pengguna tidak ter-autentikasi. userNotFoundMessage=Pengguna tidak ditemukan. incorrectPasswordMessage=Kata sandi saat ini salah. usernameExistsMessage=Nama pengguna baru sudah ada. -invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. deleteCurrentUserMessage=Cannot delete currently logged in user. deleteUsernameExistsMessage=The username does not exist and cannot be deleted. downgradeCurrentUserMessage=Tidak dapat menurunkan peran pengguna saat ini downgradeCurrentUserLongMessage=Tidak dapat menurunkan peran pengguna saat ini. Oleh karena itu, pengguna saat ini tidak akan ditampilkan. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Error oops=Oops! help=Help @@ -171,7 +173,7 @@ adminUserSettings.header=Pengaturan Kontrol Admin adminUserSettings.admin=Admin adminUserSettings.user=Pengguna adminUserSettings.addUser=Tambahkan Pengguna Baru -adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. adminUserSettings.roles=Peran adminUserSettings.role=Peran adminUserSettings.actions=Tindakan @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Memaksa pengguna untuk mengubah nama pengguna/kata sandi saat masuk adminUserSettings.submit=Simpan Pengguna adminUserSettings.changeUserRole=Ubah Peran Pengguna +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=Alat Multi PDF multiTool.header=Alat Multi PDF -multiTool.uploadPrompts=Please Upload PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=Lihat PDF @@ -1057,7 +1060,7 @@ licenses.version=Version licenses.license=License -# error +#error error.sorry=Sorry for the issue! error.needHelp=Need help / Found an issue? error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: diff --git a/src/main/resources/messages_it_IT.properties b/src/main/resources/messages_it_IT.properties index 775e70b7..fb58c0af 100644 --- a/src/main/resources/messages_it_IT.properties +++ b/src/main/resources/messages_it_IT.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=Utente non autenticato. userNotFoundMessage=Utente non trovato. incorrectPasswordMessage=La password attuale non è corretta. usernameExistsMessage=Il nuovo nome utente esiste già. -invalidUsernameMessage=Nome utente non valido, il nome utente deve contenere solo caratteri alfabetici e numeri. +invalidUsernameMessage=Nome utente non valido, il nome utente può contenere solo lettere, numeri e i seguenti caratteri speciali @._+- o deve essere un indirizzo email valido. deleteCurrentUserMessage=Impossibile eliminare l'utente attualmente connesso. deleteUsernameExistsMessage=Il nome utente non esiste e non può essere eliminato. downgradeCurrentUserMessage=Impossibile declassare il ruolo dell'utente corrente downgradeCurrentUserLongMessage=Impossibile declassare il ruolo dell'utente corrente. Pertanto, l'utente corrente non verrà visualizzato. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Errore oops=Oops! help=Aiuto @@ -171,7 +173,7 @@ adminUserSettings.header=Impostazioni di controllo utente amministratore adminUserSettings.admin=Amministratore adminUserSettings.user=Utente adminUserSettings.addUser=Aggiungi un nuovo Utente -adminUserSettings.usernameInfo=Il nome utente deve contenere solo lettere e numeri, senza spazi o caratteri speciali. +adminUserSettings.usernameInfo=Il nome utente può contenere solo lettere, numeri e i seguenti caratteri speciali @._+- oppure deve essere un indirizzo email valido. adminUserSettings.roles=Ruoli adminUserSettings.role=Ruolo adminUserSettings.actions=Azioni @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=API utente interna adminUserSettings.forceChange=Forza l'utente a cambiare nome username/password all'accesso adminUserSettings.submit=Salva utente adminUserSettings.changeUserRole=Cambia il ruolo dell'utente +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(ad es. 1,3,2 o 4-8,2,10-12 o 2n-1) #multiTool multiTool.title=Multifunzione PDF multiTool.header=Multifunzione PDF -multiTool.uploadPrompts=Caricare il PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=Visualizza PDF @@ -1057,7 +1060,7 @@ licenses.version=Versione licenses.license=Licenza -# error +#error error.sorry=Ci scusiamo per il problema! error.needHelp=Hai bisogno di aiuto / trovato un problema? error.contactTip=Se i problemi persistono, non esitare a contattarci per chiedere aiuto. Puoi inviare un ticket sulla nostra pagina GitHub o contattarci tramite Discord: diff --git a/src/main/resources/messages_ja_JP.properties b/src/main/resources/messages_ja_JP.properties index 7c210c59..fe06fffc 100644 --- a/src/main/resources/messages_ja_JP.properties +++ b/src/main/resources/messages_ja_JP.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=ユーザーが認証されていません。 userNotFoundMessage=ユーザーが見つかりません。 incorrectPasswordMessage=現在のパスワードが正しくありません。 usernameExistsMessage=新しいユーザー名はすでに存在します。 -invalidUsernameMessage=ユーザー名が無効です。ユーザー名にはアルファベットと数字のみを使用してください。 +invalidUsernameMessage=ユーザー名が無効です。ユーザー名には文字、数字、およびそれに続く特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります。 deleteCurrentUserMessage=現在ログインしているユーザーは削除できません。 deleteUsernameExistsMessage=そのユーザー名は存在しないため削除できません。 downgradeCurrentUserMessage=現在のユーザーの役割をダウングレードできません downgradeCurrentUserLongMessage=現在のユーザーの役割をダウングレードできません。したがって、現在のユーザーは表示されません。 +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=エラー oops=おっと! help=ヘルプ @@ -171,7 +173,7 @@ adminUserSettings.header=管理者ユーザー制御設定 adminUserSettings.admin=管理者 adminUserSettings.user=ユーザー adminUserSettings.addUser=新しいユーザを追加 -adminUserSettings.usernameInfo=ユーザー名には文字と数字のみが使用でき、スペースや特殊文字は使用できません。 +adminUserSettings.usernameInfo=ユーザー名には、文字、数字、および次の特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります。 adminUserSettings.roles=役割 adminUserSettings.role=役割 adminUserSettings.actions=アクション @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=内部APIユーザー adminUserSettings.forceChange=ログイン時にユーザー名/パスワードを強制的に変更する adminUserSettings.submit=ユーザーの保存 adminUserSettings.changeUserRole=ユーザーの役割を変更する +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(例:1,3,2または4-8,2,10-12または2n-1) #multiTool multiTool.title=PDFマルチツール multiTool.header=PDFマルチツール -multiTool.uploadPrompts=Please Upload PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=PDFを表示 @@ -1057,7 +1060,7 @@ licenses.version=バージョン licenses.license=ライセンス -# error +#error error.sorry=問題が発生したことをお詫び申し上げます! error.needHelp=助けが必要/問題が見つかりましたか? error.contactTip=まだ問題が解決していない場合は、お手数ですが、GitHubページでチケットを提出するか、Discordで私たちに連絡してください: diff --git a/src/main/resources/messages_ko_KR.properties b/src/main/resources/messages_ko_KR.properties index ddc77b39..02f19932 100644 --- a/src/main/resources/messages_ko_KR.properties +++ b/src/main/resources/messages_ko_KR.properties @@ -54,13 +54,13 @@ notAuthenticatedMessage=사용자가 인증되지 않았습니다. userNotFoundMessage=사용자를 찾을 수 없습니다. incorrectPasswordMessage=현재 비밀번호가 틀립니다. usernameExistsMessage=새 사용자명이 이미 존재합니다. -invalidUsernameMessage=사용자 이름이 잘못되었습니다. 사용자 이름에는 알파벳 문자와 숫자만 포함되어야 합니다. +invalidUsernameMessage=잘못된 사용자 이름입니다. 사용자 이름에는 문자, 숫자 및 다음 특수 문자(@._+-)만 포함할 수 있거나 유효한 이메일 주소여야 합니다. deleteCurrentUserMessage=현재 로그인한 사용자를 삭제할 수 없습니다. deleteUsernameExistsMessage=사용자 이름이 존재하지 않으며 삭제할 수 없습니다. - -info=Info downgradeCurrentUserMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다 downgradeCurrentUserLongMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다. 따라서 현재 사용자는 표시되지 않습니다. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=오류 oops=어머나! help=도움말 @@ -71,6 +71,7 @@ visitGithub=GitHub 저장소 방문하기 donate=기부하기 color=색상 sponsor=스폰서 +info=Info @@ -172,7 +173,7 @@ adminUserSettings.header=사용자 관리 adminUserSettings.admin=관리자 adminUserSettings.user=사용자 adminUserSettings.addUser=새 사용자 추가 -adminUserSettings.usernameInfo=사용자 이름은 문자와 숫자만 포함해야 하며 공백이나 특수 문자는 포함할 수 없습니다. +adminUserSettings.usernameInfo=사용자 이름은 문자, 숫자, 특수 문자 @._+-만 포함할 수 있으며 유효한 이메일 주소여야 합니다. adminUserSettings.roles=역할 adminUserSettings.role=역할 adminUserSettings.actions=동작 @@ -184,6 +185,7 @@ adminUserSettings.internalApiUser=내부 API 사용자 adminUserSettings.forceChange=다음 로그인 때 사용자명과 비밀번호를 변경하도록 강제 adminUserSettings.submit=사용자 저장 adminUserSettings.changeUserRole=사용자의 역할 변경 +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -803,7 +805,7 @@ pdfOrganiser.placeholder=(예: 1,3,2 또는 4-8,2,10-12 또는 2n-1) #multiTool multiTool.title=PDF 멀티툴 multiTool.header=PDF 멀티툴 -multiTool.uploadPrompts=PDF를 업로드하십시오 +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=PDF 뷰어 @@ -1058,7 +1060,7 @@ licenses.version=버전 licenses.license=라이센스 -# error +#error error.sorry=문제를 끼친 점 죄송합니다! error.needHelp=도움이 필요하신가요 / 문제가 있으신가요? error.contactTip=여전히 문제가 해결되지 않는다면 망설이지 마시고 도움을 요청하십시오. GitHub 페이지에서 티켓을 제출하거나 Discord를 통해 우리에게 연락하실 수 있습니다: diff --git a/src/main/resources/messages_nl_NL.properties b/src/main/resources/messages_nl_NL.properties index d4cbbcf0..73525895 100644 --- a/src/main/resources/messages_nl_NL.properties +++ b/src/main/resources/messages_nl_NL.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=Gebruiker niet ingelogd. userNotFoundMessage=Gebruiker niet gevonden. incorrectPasswordMessage=Huidige wachtwoord is onjuist. usernameExistsMessage=Nieuwe gebruikersnaam bestaat al. -invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. deleteCurrentUserMessage=Cannot delete currently logged in user. deleteUsernameExistsMessage=The username does not exist and cannot be deleted. downgradeCurrentUserMessage=Kan de rol van de huidige gebruiker niet downgraden downgradeCurrentUserLongMessage=Kan de rol van de huidige gebruiker niet downgraden. Huidige gebruiker wordt dus niet weergegeven. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Error oops=Oops! help=Help @@ -171,7 +173,7 @@ adminUserSettings.header=Beheer gebruikers adminUserSettings.admin=Beheerder adminUserSettings.user=Gebruiker adminUserSettings.addUser=Voeg nieuwe gebruiker toe -adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. adminUserSettings.roles=Rollen adminUserSettings.role=Rol adminUserSettings.actions=Acties @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Forceer gebruiker om gebruikersnaam/wachtwoord te wijzigen bij inloggen adminUserSettings.submit=Gebruiker opslaan adminUserSettings.changeUserRole=De rol van de gebruiker wijzigen +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=PDF Multitool multiTool.header=PDF Multitool -multiTool.uploadPrompts=Please Upload PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=PDF bekijken @@ -1057,7 +1060,7 @@ licenses.version=Versie licenses.license=Licentie -# error +#error error.sorry=Sorry for the issue! error.needHelp=Need help / Found an issue? error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: diff --git a/src/main/resources/messages_pl_PL.properties b/src/main/resources/messages_pl_PL.properties index 50c22dd7..6b98571b 100644 --- a/src/main/resources/messages_pl_PL.properties +++ b/src/main/resources/messages_pl_PL.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. -invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. deleteCurrentUserMessage=Cannot delete currently logged in user. deleteUsernameExistsMessage=The username does not exist and cannot be deleted. downgradeCurrentUserMessage=Nie można obniżyć roli bieżącego użytkownika downgradeCurrentUserLongMessage=Nie można obniżyć roli bieżącego użytkownika. W związku z tym bieżący użytkownik nie zostanie wyświetlony. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Error oops=Oops! help=Help @@ -171,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings adminUserSettings.admin=Admin adminUserSettings.user=User adminUserSettings.addUser=Add New User -adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. adminUserSettings.roles=Roles adminUserSettings.role=Role adminUserSettings.actions=Actions @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Save User adminUserSettings.changeUserRole=Zmień rolę użytkownika +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=Multi narzędzie PDF multiTool.header=Multi narzędzie PDF -multiTool.uploadPrompts=Please Upload PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=View PDF @@ -1057,7 +1060,7 @@ licenses.version=Version licenses.license=License -# error +#error error.sorry=Sorry for the issue! error.needHelp=Need help / Found an issue? error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: diff --git a/src/main/resources/messages_pt_BR.properties b/src/main/resources/messages_pt_BR.properties index 7a029978..8f16f1e5 100644 --- a/src/main/resources/messages_pt_BR.properties +++ b/src/main/resources/messages_pt_BR.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. -invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. deleteCurrentUserMessage=Cannot delete currently logged in user. deleteUsernameExistsMessage=The username does not exist and cannot be deleted. downgradeCurrentUserMessage=Não é possível fazer downgrade da função do usuário atual downgradeCurrentUserLongMessage=Não é possível fazer downgrade da função do usuário atual. Portanto, o usuário atual não será mostrado. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Error oops=Oops! help=Help @@ -171,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings adminUserSettings.admin=Admin adminUserSettings.user=User adminUserSettings.addUser=Add New User -adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. adminUserSettings.roles=Roles adminUserSettings.role=Role adminUserSettings.actions=Actions @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Save User adminUserSettings.changeUserRole=Alterar Função de Usuário +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=Multiferramenta de PDF multiTool.header=Multiferramenta de PDF -multiTool.uploadPrompts=Please Upload PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=View PDF @@ -1057,7 +1060,7 @@ licenses.version=Version licenses.license=License -# error +#error error.sorry=Sorry for the issue! error.needHelp=Need help / Found an issue? error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: diff --git a/src/main/resources/messages_pt_PT.properties b/src/main/resources/messages_pt_PT.properties index f814f1a7..94a076f3 100644 --- a/src/main/resources/messages_pt_PT.properties +++ b/src/main/resources/messages_pt_PT.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=Utilizador não autenticado. userNotFoundMessage=Utilizador inexistente. incorrectPasswordMessage=Senha incorreta. usernameExistsMessage=Esse utilizador já existe. -invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. deleteCurrentUserMessage=Cannot delete currently logged in user. deleteUsernameExistsMessage=The username does not exist and cannot be deleted. downgradeCurrentUserMessage=Não é possível fazer downgrade da função do utilizador atual downgradeCurrentUserLongMessage=Não é possível fazer downgrade da função do utilizador atual. Portanto, o utilizador atual não será mostrado. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Error oops=Oops! help=Help @@ -171,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings adminUserSettings.admin=Admin adminUserSettings.user=User adminUserSettings.addUser=Add New User -adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. adminUserSettings.roles=Roles adminUserSettings.role=Role adminUserSettings.actions=Actions @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Save User adminUserSettings.changeUserRole=Alterar usuário +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=Multiferramenta de PDF multiTool.header=Multiferramenta de PDF -multiTool.uploadPrompts=Please Upload PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=View PDF @@ -1057,7 +1060,7 @@ licenses.version=Versão licenses.license=Licença -# error +#error error.sorry=Sorry for the issue! error.needHelp=Need help / Found an issue? error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: diff --git a/src/main/resources/messages_ro_RO.properties b/src/main/resources/messages_ro_RO.properties index 382b1760..7297ddf3 100644 --- a/src/main/resources/messages_ro_RO.properties +++ b/src/main/resources/messages_ro_RO.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. -invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. deleteCurrentUserMessage=Cannot delete currently logged in user. deleteUsernameExistsMessage=The username does not exist and cannot be deleted. downgradeCurrentUserMessage=Rolul utilizatorului curent nu poate fi retrogradat downgradeCurrentUserLongMessage=Rolul utilizatorului curent nu poate fi retrogradat. Prin urmare, utilizatorul curent nu va fi afișat. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Error oops=Oops! help=Help @@ -171,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings adminUserSettings.admin=Admin adminUserSettings.user=User adminUserSettings.addUser=Add New User -adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. adminUserSettings.roles=Roles adminUserSettings.role=Role adminUserSettings.actions=Actions @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Save User adminUserSettings.changeUserRole=Schimbați rolul utilizatorului +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=Instrument PDF multiplu multiTool.header=Instrument PDF multiplu -multiTool.uploadPrompts=Please Upload PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=View PDF @@ -1057,7 +1060,7 @@ licenses.version=Version licenses.license=License -# error +#error error.sorry=Sorry for the issue! error.needHelp=Need help / Found an issue? error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: diff --git a/src/main/resources/messages_ru_RU.properties b/src/main/resources/messages_ru_RU.properties index 38d51610..98453a1e 100644 --- a/src/main/resources/messages_ru_RU.properties +++ b/src/main/resources/messages_ru_RU.properties @@ -54,12 +54,13 @@ notAuthenticatedMessage=Пользователь не прошел провер userNotFoundMessage=Пользователь не найден. incorrectPasswordMessage=Текущий пароль неверен. usernameExistsMessage=Новое имя пользователя уже существует. -invalidUsernameMessage=Недопустимое имя пользователя, Имя пользователя должно содержать только буквы алфавита и цифры. +invalidUsernameMessage=Неверное имя пользователя. Имя пользователя может содержать только буквы, цифры и следующие специальные символы @._+- или должно быть действительным адресом электронной почты. deleteCurrentUserMessage=Невозможно удалить пользователя, вошедшего в систему. deleteUsernameExistsMessage=Имя пользователя не существует и не может быть удалено. -info=Info downgradeCurrentUserMessage=Невозможно понизить роль текущего пользователя downgradeCurrentUserLongMessage=Невозможно понизить роль текущего пользователя. Следовательно, текущий пользователь не будет отображаться. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Ошибка oops=Ой! help=Помощь @@ -70,7 +71,7 @@ visitGithub=Посетить репозиторий на GitHub donate=Пожертвовать color=Цвет sponsor=Спонсор - +info=Info @@ -172,7 +173,7 @@ adminUserSettings.header=Настройки контроля пользоват adminUserSettings.admin=Администратор adminUserSettings.user=Пользователь adminUserSettings.addUser=Добавить нового пользователя -adminUserSettings.usernameInfo=Имя пользователя должно содержать только буквы и цифры, без пробелов и специальных символов. +adminUserSettings.usernameInfo=Имя пользователя может содержать только буквы, цифры и следующие специальные символы @._+- или должно быть действительным адресом электронной почты. adminUserSettings.roles=Роли adminUserSettings.role=Роль adminUserSettings.actions=Действия @@ -184,6 +185,7 @@ adminUserSettings.internalApiUser=Внутренний пользователь adminUserSettings.forceChange=Просить пользователя изменить пароль при входе в систему adminUserSettings.submit=Сохранить пользователя adminUserSettings.changeUserRole=Изменить роль пользователя +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -803,7 +805,7 @@ pdfOrganiser.placeholder=(например, 1,3,2 или 4-8,2,10-12 или 2n-1 #multiTool multiTool.title=Мультиинструмент PDF multiTool.header=Мультиинструмент PDF -multiTool.uploadPrompts=Пожалуйста, загрузите PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=Просмотреть PDF @@ -1058,7 +1060,7 @@ licenses.version=Версия licenses.license=Лицензия -# error +#error error.sorry=Извините за проблему! error.needHelp=Нужна помощь / Нашли проблему? error.contactTip=Если у вас все еще есть проблемы, не стесняйтесь обращаться к нам за помощью. Вы можете отправить заявку на нашей странице GitHub или связаться с нами через Discord: diff --git a/src/main/resources/messages_sk_SK.properties b/src/main/resources/messages_sk_SK.properties index 124f51f9..00970c4a 100644 --- a/src/main/resources/messages_sk_SK.properties +++ b/src/main/resources/messages_sk_SK.properties @@ -59,6 +59,8 @@ deleteCurrentUserMessage=Nie je možné zmazať aktuálne prihláseného použí deleteUsernameExistsMessage=Používateľské meno neexistuje a nemôže byť zmazané. downgradeCurrentUserMessage=Nie je možné znížiť rolu aktuálneho používateľa downgradeCurrentUserLongMessage=Nie je možné znížiť rolu aktuálneho používateľa. Preto, aktuálny používateľ nebude zobrazený. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Chyba oops=Ups! help=Pomoc @@ -69,6 +71,7 @@ visitGithub=Navštíviť GitHub repozitár donate=Darovať color=Farba sponsor=Sponzorovať +info=Info @@ -102,12 +105,18 @@ pipelineOptions.validateButton=Overiť ############# # NAVBAR # ############# -navbar.convert=Konvertovať -navbar.security=Bezpečnosť -navbar.other=Rôzne +navbar.favorite=Favorites navbar.darkmode=Tmavý režim -navbar.pageOps=Operácie so stránkami +navbar.language=Languages navbar.settings=Nastavenia +navbar.allTools=Tools +navbar.multiTool=Multi Tools +navbar.sections.organize=Organize +navbar.sections.convertTo=Convert to PDF +navbar.sections.convertFrom=Convert from PDF +navbar.sections.security=Sign & Security +navbar.sections.advance=Advanced +navbar.sections.edit=View & Edit ############# # SETTINGS # @@ -176,6 +185,7 @@ adminUserSettings.internalApiUser=Interný API používateľ adminUserSettings.forceChange=Donútiť používateľa zmeniť heslo pri prihlásení adminUserSettings.submit=Uložiť používateľa adminUserSettings.changeUserRole=Zmeniť rolu používateľa +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -742,6 +752,7 @@ extractImages.submit=Extrahovať fileToPDF.title=Súbor do PDF fileToPDF.header=Konvertovať akýkoľvek súbor do PDF fileToPDF.credit=Táto služba používa LibreOffice a Unoconv pre konverziu súborov. +fileToPDF.supportedFileTypesInfo=Supported File types fileToPDF.supportedFileTypes=Podporované typy súborov by mali zahŕňať nižšie uvedené, avšak pre úplný aktualizovaný zoznam podporovaných formátov, prosím, odkazujte na dokumentáciu LibreOffice fileToPDF.submit=Konvertovať do PDF @@ -794,7 +805,7 @@ pdfOrganiser.placeholder=(napr. 1,3,2 alebo 4-8,2,10-12 alebo 2n-1) #multiTool multiTool.title=PDF Multi Nástroj multiTool.header=PDF Multi Nástroj -multiTool.uploadPrompts=Prosím, nahrajte PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=Zobraziť PDF @@ -1049,7 +1060,7 @@ licenses.version=Verzia licenses.license=Licencia -# error +#error error.sorry=Ospravedlňujeme sa za problém! error.needHelp=Potrebujete pomoc / Našli ste problém? error.contactTip=Ak máte stále problémy, neváhajte nás kontaktovať pre pomoc. Môžete podať tiket na našej stránke GitHub alebo nás kontaktovať cez Discord: diff --git a/src/main/resources/messages_sr_LATN_RS.properties b/src/main/resources/messages_sr_LATN_RS.properties index a200e591..4ac3d903 100644 --- a/src/main/resources/messages_sr_LATN_RS.properties +++ b/src/main/resources/messages_sr_LATN_RS.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=Korisnik nije autentifikovan. userNotFoundMessage=Korisnik nije pronađen. incorrectPasswordMessage=Trenutna šifra je netačna. usernameExistsMessage=Novi korisnik već postoji -invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. deleteCurrentUserMessage=Cannot delete currently logged in user. deleteUsernameExistsMessage=The username does not exist and cannot be deleted. downgradeCurrentUserMessage=Nije moguće degradirati ulogu trenutnog korisnika downgradeCurrentUserLongMessage=Nije moguće unazaditi ulogu trenutnog korisnika. Dakle, trenutni korisnik neće biti prikazan. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Error oops=Oops! help=Help @@ -171,7 +173,7 @@ adminUserSettings.header=Podešavanja kontrole korisnika za administratora adminUserSettings.admin=Administrator adminUserSettings.user=Korisnik adminUserSettings.addUser=Dodaj novog korisnika -adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. adminUserSettings.roles=Uloge adminUserSettings.role=Uloga adminUserSettings.actions=Akcije @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Prisili korisnika da promeni korisničko ime/lozinku pri prijavi adminUserSettings.submit=Sačuvaj korisnika adminUserSettings.changeUserRole=Promenite ulogu korisnika +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=PDF Multi Alatka multiTool.header=PDF Multi Alatka -multiTool.uploadPrompts=Please Upload PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=Prikaz @@ -1057,7 +1060,7 @@ licenses.version=Version licenses.license=License -# error +#error error.sorry=Sorry for the issue! error.needHelp=Need help / Found an issue? error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: diff --git a/src/main/resources/messages_sv_SE.properties b/src/main/resources/messages_sv_SE.properties index 6120e96d..049325a9 100644 --- a/src/main/resources/messages_sv_SE.properties +++ b/src/main/resources/messages_sv_SE.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. -invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. deleteCurrentUserMessage=Cannot delete currently logged in user. deleteUsernameExistsMessage=The username does not exist and cannot be deleted. downgradeCurrentUserMessage=Kan inte nedgradera nuvarande användares roll downgradeCurrentUserLongMessage=Kan inte nedgradera nuvarande användares roll. Därför kommer den aktuella användaren inte att visas. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Error oops=Oops! help=Help @@ -171,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings adminUserSettings.admin=Admin adminUserSettings.user=User adminUserSettings.addUser=Add New User -adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address. adminUserSettings.roles=Roles adminUserSettings.role=Role adminUserSettings.actions=Actions @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Save User adminUserSettings.changeUserRole=Ändra användarens roll +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=PDF-multiverktyg multiTool.header=PDF Multi-verktyg -multiTool.uploadPrompts=Please Upload PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=View PDF @@ -1057,7 +1060,7 @@ licenses.version=Version licenses.license=License -# error +#error error.sorry=Sorry for the issue! error.needHelp=Need help / Found an issue? error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: diff --git a/src/main/resources/messages_tr_TR.properties b/src/main/resources/messages_tr_TR.properties index e0475f25..10649a96 100644 --- a/src/main/resources/messages_tr_TR.properties +++ b/src/main/resources/messages_tr_TR.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=Kullanıcı doğrulanmadı. userNotFoundMessage=Kullanıcı bulunamadı. incorrectPasswordMessage=Mevcut şifre yanlış. usernameExistsMessage=Yeni Kullanıcı Adı zaten var. -invalidUsernameMessage=Geçersiz kullanıcı adı, Kullanıcı adı yalnızca alfabe karakterleri ve sayılar içermelidir. +invalidUsernameMessage=Geçersiz kullanıcı adı, kullanıcı adı yalnızca harf, rakam ve aşağıdaki özel karakterleri @._+- içerebilir veya geçerli bir e-posta adresi olmalıdır. deleteCurrentUserMessage=Şu anda oturum açmış olan kullanıcı silinemiyor. deleteUsernameExistsMessage=Kullanıcı adı mevcut değil ve silinemez. downgradeCurrentUserMessage=Mevcut kullanıcının rolü düşürülemiyor downgradeCurrentUserLongMessage=Mevcut kullanıcının rolü düşürülemiyor. Bu nedenle, mevcut kullanıcı gösterilmeyecektir. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Hata oops=Tüh! help=Yardım @@ -68,8 +70,8 @@ seeDockerHub=Docker Hub'a bakın visitGithub=Github Deposunu Ziyaret Edin donate=Bağış Yapın color=Renk -info=Info sponsor=Bağış +info=Info @@ -171,7 +173,7 @@ adminUserSettings.header=Yönetici Kullanıcı Kontrol Ayarları adminUserSettings.admin=Yönetici adminUserSettings.user=Kullanıcı adminUserSettings.addUser=Yeni Kullanıcı Ekle -adminUserSettings.usernameInfo=Kullanıcı adı yalnızca harf ve rakamlardan oluşmalı, boşluk veya özel karakter içermemelidir. +adminUserSettings.usernameInfo=Kullanıcı adı yalnızca harf, rakam ve aşağıdaki özel karakterleri @._+- içerebilir veya geçerli bir e-posta adresi olmalıdır. adminUserSettings.roles=Roller adminUserSettings.role=Rol adminUserSettings.actions=Eylemler @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Dahili API Kullanıcısı adminUserSettings.forceChange=Kullanıcının girişte kullanıcı adı/şifre değiştirmesini zorla adminUserSettings.submit=Kullanıcıyı Kaydet adminUserSettings.changeUserRole=Kullanıcı rolünü değiştir +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(örn. 1,3,2 veya 4-8,2,10-12 veya 2n-1) #multiTool multiTool.title=PDF Çoklu Araç multiTool.header=PDF Çoklu Araç -multiTool.uploadPrompts=Lütfen PDF Yükleyin +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=PDF Görüntüle @@ -1057,7 +1060,7 @@ licenses.version=Versiyon licenses.license=Lisans -# error +#error error.sorry=Sorun için özür dileriz! error.needHelp=Yardıma mı ihtiyacınız var / Bir sorun mu buldunuz? error.contactTip=Hala sorun yaşıyorsanız, yardım için bize ulaşmaktan çekinmeyin. GitHub sayfamızdan bir bilet gönderebilir veya Discord üzerinden bizimle iletişime geçebilirsiniz: diff --git a/src/main/resources/messages_uk_UA.properties b/src/main/resources/messages_uk_UA.properties index 622f4ec8..c77f63de 100644 --- a/src/main/resources/messages_uk_UA.properties +++ b/src/main/resources/messages_uk_UA.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=Користувач не пройшов перевір userNotFoundMessage=Користувача не знайдено. incorrectPasswordMessage=Поточний пароль невірний. usernameExistsMessage=Нове ім'я користувача вже існує. -invalidUsernameMessage=Недійсне ім'я користувача, Ім'я користувача повинно містити тільки літери алфавіту та цифри. +invalidUsernameMessage=Недійсне ім’я користувача, ім’я користувача може містити лише літери, цифри та наступні спеціальні символи @._+- або має бути дійсною електронною адресою. deleteCurrentUserMessage=Неможливо видалити користувача, який увійшов в систему. deleteUsernameExistsMessage=Ім'я користувача не існує і не може бути видалено. downgradeCurrentUserMessage=Неможливо понизити роль поточного користувача downgradeCurrentUserLongMessage=Неможливо понизити роль поточного користувача. Отже, поточний користувач не відображатиметься. +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=Error oops=Oops! help=Help @@ -171,7 +173,7 @@ adminUserSettings.header=Налаштування контролю корист adminUserSettings.admin=Адміністратор adminUserSettings.user=Користувач adminUserSettings.addUser=Додати нового користувача -adminUserSettings.usernameInfo=Ім'я користувача має містити тільки літери та цифри, без пробілів та спеціальних символів. +adminUserSettings.usernameInfo=Ім’я користувача може містити лише літери, цифри та наступні спеціальні символи @._+- або має бути дійсною електронною адресою. adminUserSettings.roles=Ролі adminUserSettings.role=Роль adminUserSettings.actions=Дії @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Внутрішній користувач API adminUserSettings.forceChange=Примусити користувача змінити пароль при вході в систему adminUserSettings.submit=Зберегти користувача adminUserSettings.changeUserRole=Змінити роль користувача +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(наприклад, 1,3,2 або 4-8,2,10-12 або 2n #multiTool multiTool.title=Мультіінструмент PDF multiTool.header=Мультіінструмент PDF -multiTool.uploadPrompts=Будь ласка, завантажте PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=Переглянути PDF @@ -1057,7 +1060,7 @@ licenses.version=Версія licenses.license=Ліцензія -# error +#error error.sorry=Вибачте за незручності! error.needHelp=Потрібна допомога / Знайшли проблему? error.contactTip=Якщо у вас досі виникають проблеми, не соромтеся звертатися до нас за допомогою. Ви можете надіслати запит на нашій сторінці GitHub або зв'язатися з нами через Discord: diff --git a/src/main/resources/messages_zh_CN.properties b/src/main/resources/messages_zh_CN.properties index 063d1e03..a94cf9a8 100644 --- a/src/main/resources/messages_zh_CN.properties +++ b/src/main/resources/messages_zh_CN.properties @@ -54,11 +54,13 @@ notAuthenticatedMessage=用户未经过身份验证。 userNotFoundMessage=未找到用户。 incorrectPasswordMessage=当前密码不正确。 usernameExistsMessage=新用户名已存在。 -invalidUsernameMessage=用户名无效,用户名只能由字母字符和数字组成。 +invalidUsernameMessage=用户名无效,用户名只能包含字母、数字和以下特殊字符@._+- 或必须是有效的电子邮件地址。 deleteCurrentUserMessage=无法删除当前登录的用户。 deleteUsernameExistsMessage=用户名不存在,无法删除。 downgradeCurrentUserMessage=无法降级当前用户的角色 downgradeCurrentUserLongMessage=无法降级当前用户的角色。因此,当前用户将不会显示。 +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=错误 oops=哎呀! help=帮助 @@ -171,7 +173,7 @@ adminUserSettings.header=管理员用户控制设置 adminUserSettings.admin=管理员 adminUserSettings.user=用户 adminUserSettings.addUser=添加新用户 -adminUserSettings.usernameInfo=用户名只能由字母和数字组成,不能包含空格或特殊字符。 +adminUserSettings.usernameInfo=用户名只能包含字母、数字和以下特殊字符@._+-,或者必须是有效的电子邮件地址。 adminUserSettings.roles=角色 adminUserSettings.role=角色 adminUserSettings.actions=操作 @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=内部API用户 adminUserSettings.forceChange=强制用户在登录时更改用户名/密码 adminUserSettings.submit=保存用户 adminUserSettings.changeUserRole=更改用户角色 +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(例如 1,3,2 或 4-8,2,10-12 或 2n-1) #multiTool multiTool.title=PDF多功能工具 multiTool.header=PDF多功能工具 -multiTool.uploadPrompts=上传PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=浏览PDF @@ -1057,7 +1060,7 @@ licenses.version=版本 licenses.license=许可证 -# error +#error error.sorry=对此问题感到抱歉! error.needHelp=需要帮助 / 发现问题? error.contactTip=如果你仍然遇到问题,不要犹豫,向我们寻求帮助。你可以在我们的GitHub页面上提交工单,或者通过Discord与我们联系: diff --git a/src/main/resources/messages_zh_TW.properties b/src/main/resources/messages_zh_TW.properties index a218d0f1..0ed9e43a 100644 --- a/src/main/resources/messages_zh_TW.properties +++ b/src/main/resources/messages_zh_TW.properties @@ -54,12 +54,13 @@ notAuthenticatedMessage=使用者未認證。 userNotFoundMessage=找不到使用者。 incorrectPasswordMessage=目前密碼不正確。 usernameExistsMessage=新使用者名稱已存在。 -invalidUsernameMessage=使用者名無效,使用者名只能包含字母字元和數位。 +invalidUsernameMessage=使用者名稱無效,使用者名稱只能包含字母、數字和以下特殊字元@._+- 或必須是有效的電子郵件地址。 deleteCurrentUserMessage=無法刪除目前登錄的使用者。 deleteUsernameExistsMessage=使用者名不存在,無法刪除。 -info=Info downgradeCurrentUserMessage=無法降級目前使用者的角色 downgradeCurrentUserLongMessage=無法降級目前使用者的角色。因此,不會顯示目前的使用者。 +userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. +userAlreadyExistsWebMessage=The user already exists as an web user. error=錯誤 oops=哎呀! help=幫助 @@ -70,6 +71,7 @@ visitGithub=訪問Github存儲庫 donate=捐贈 color=顏色 sponsor=贊助 +info=Info @@ -171,7 +173,7 @@ adminUserSettings.header=管理使用者控制設定 adminUserSettings.admin=管理員 adminUserSettings.user=使用者 adminUserSettings.addUser=新增使用者 -adminUserSettings.usernameInfo=使用者名只能包含字母和數位,不能包含空格或特殊字元。 +adminUserSettings.usernameInfo=使用者名稱只能包含字母、數字和以下特殊字元@._+-,或必須是有效的電子郵件地址。 adminUserSettings.roles=角色 adminUserSettings.role=角色 adminUserSettings.actions=操作 @@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=內部 API 使用者 adminUserSettings.forceChange=強制使用者在登入時修改使用者名稱/密碼 adminUserSettings.submit=儲存 adminUserSettings.changeUserRole=更改使用者身份 +adminUserSettings.authenticated=Authenticated ############# # HOME-PAGE # @@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(例如 1,3,2 或 4-8,2,10-12 或 2n-1) #multiTool multiTool.title=PDF 多工具 multiTool.header=PDF 多工具 -multiTool.uploadPrompts=Please Upload PDF +multiTool.uploadPrompts=File Name #view pdf viewPdf.title=檢視 PDF @@ -1057,7 +1060,7 @@ licenses.version=版本 licenses.license=許可證 -# error +#error error.sorry=對於這個問題,我們感到抱歉! error.needHelp=需要幫助/發現了一個問題? error.contactTip=如果你仍然遇到問題,請不要猶豫,隨時向我們尋求幫助。你可以在我們的GitHub頁面提交工單,或通過Discord與我們聯繋: diff --git a/src/main/resources/settings.yml.template b/src/main/resources/settings.yml.template index 45f1762d..d7d1fcfa 100644 --- a/src/main/resources/settings.yml.template +++ b/src/main/resources/settings.yml.template @@ -7,12 +7,15 @@ security: csrfDisabled: true loginAttemptCount: 5 # lock user account after 5 tries loginResetTimeMinutes : 120 # lock account for 2 hours after x attempts - #oauth2: - # enabled: false # set to 'true' to enable login (Note: enableLogin must also be 'true' for this to work) - # issuer: "" # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) end-point - # clientId: "" # Client ID from your provider - # clientSecret: "" # Client Secret from your provider - # autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users + # oauth2: + # enabled: false # set to 'true' to enable login (Note: enableLogin must also be 'true' for this to work) + # issuer: "" # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) end-point + # clientId: "" # Client ID from your provider + # clientSecret: "" # Client Secret from your provider + # autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users + # useAsUsername: "email" # Default is 'email'; custom fields can be used as the username + # scopes: "openid, profile, email" # Specify the scopes for which the application will request permissions + # provider: "google" # Set this to your OAuth provider's name, e.g., 'google' or 'keycloak' system: defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc) @@ -20,7 +23,7 @@ system: enableAlphaFunctionality: false # Set to enable functionality which might need more testing before it fully goes live (This feature might make no changes) showUpdate: true # see when a new update is available showUpdateOnlyAdmin: false # Only admins can see when a new update is available, depending on showUpdate it must be set to 'true' - + ui: appName: null # Application's visible name homeDescription: null # Short description or tagline shown on homepage. diff --git a/src/main/resources/static/js/thirdParty/jquery.validate.min.js b/src/main/resources/static/js/thirdParty/jquery.validate.min.js new file mode 100644 index 00000000..442a4131 --- /dev/null +++ b/src/main/resources/static/js/thirdParty/jquery.validate.min.js @@ -0,0 +1,4 @@ +/*! jQuery Validation Plugin - v1.19.5 - 7/1/2022 + * https://jqueryvalidation.org/ + * Copyright (c) 2022 Jörn Zaefferer; Licensed MIT */ +!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){a.extend(a.fn,{validate:function(b){if(!this.length)return void(b&&b.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing."));var c=a.data(this[0],"validator");return c?c:(this.attr("novalidate","novalidate"),c=new a.validator(b,this[0]),a.data(this[0],"validator",c),c.settings.onsubmit&&(this.on("click.validate",":submit",function(b){c.submitButton=b.currentTarget,a(this).hasClass("cancel")&&(c.cancelSubmit=!0),void 0!==a(this).attr("formnovalidate")&&(c.cancelSubmit=!0)}),this.on("submit.validate",function(b){function d(){var d,e;return c.submitButton&&(c.settings.submitHandler||c.formSubmitted)&&(d=a("").attr("name",c.submitButton.name).val(a(c.submitButton).val()).appendTo(c.currentForm)),!(c.settings.submitHandler&&!c.settings.debug)||(e=c.settings.submitHandler.call(c,c.currentForm,b),d&&d.remove(),void 0!==e&&e)}return c.settings.debug&&b.preventDefault(),c.cancelSubmit?(c.cancelSubmit=!1,d()):c.form()?c.pendingRequest?(c.formSubmitted=!0,!1):d():(c.focusInvalid(),!1)})),c)},valid:function(){var b,c,d;return a(this[0]).is("form")?b=this.validate().form():(d=[],b=!0,c=a(this[0].form).validate(),this.each(function(){b=c.element(this)&&b,b||(d=d.concat(c.errorList))}),c.errorList=d),b},rules:function(b,c){var d,e,f,g,h,i,j=this[0],k="undefined"!=typeof this.attr("contenteditable")&&"false"!==this.attr("contenteditable");if(null!=j&&(!j.form&&k&&(j.form=this.closest("form")[0],j.name=this.attr("name")),null!=j.form)){if(b)switch(d=a.data(j.form,"validator").settings,e=d.rules,f=a.validator.staticRules(j),b){case"add":a.extend(f,a.validator.normalizeRule(c)),delete f.messages,e[j.name]=f,c.messages&&(d.messages[j.name]=a.extend(d.messages[j.name],c.messages));break;case"remove":return c?(i={},a.each(c.split(/\s/),function(a,b){i[b]=f[b],delete f[b]}),i):(delete e[j.name],f)}return g=a.validator.normalizeRules(a.extend({},a.validator.classRules(j),a.validator.attributeRules(j),a.validator.dataRules(j),a.validator.staticRules(j)),j),g.required&&(h=g.required,delete g.required,g=a.extend({required:h},g)),g.remote&&(h=g.remote,delete g.remote,g=a.extend(g,{remote:h})),g}}});var b=function(a){return a.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")};a.extend(a.expr.pseudos||a.expr[":"],{blank:function(c){return!b(""+a(c).val())},filled:function(c){var d=a(c).val();return null!==d&&!!b(""+d)},unchecked:function(b){return!a(b).prop("checked")}}),a.validator=function(b,c){this.settings=a.extend(!0,{},a.validator.defaults,b),this.currentForm=c,this.init()},a.validator.format=function(b,c){return 1===arguments.length?function(){var c=a.makeArray(arguments);return c.unshift(b),a.validator.format.apply(this,c)}:void 0===c?b:(arguments.length>2&&c.constructor!==Array&&(c=a.makeArray(arguments).slice(1)),c.constructor!==Array&&(c=[c]),a.each(c,function(a,c){b=b.replace(new RegExp("\\{"+a+"\\}","g"),function(){return c})}),b)},a.extend(a.validator,{defaults:{messages:{},groups:{},rules:{},errorClass:"error",pendingClass:"pending",validClass:"valid",errorElement:"label",focusCleanup:!1,focusInvalid:!0,errorContainer:a([]),errorLabelContainer:a([]),onsubmit:!0,ignore:":hidden",ignoreTitle:!1,onfocusin:function(a){this.lastActive=a,this.settings.focusCleanup&&(this.settings.unhighlight&&this.settings.unhighlight.call(this,a,this.settings.errorClass,this.settings.validClass),this.hideThese(this.errorsFor(a)))},onfocusout:function(a){this.checkable(a)||!(a.name in this.submitted)&&this.optional(a)||this.element(a)},onkeyup:function(b,c){var d=[16,17,18,20,35,36,37,38,39,40,45,144,225];9===c.which&&""===this.elementValue(b)||a.inArray(c.keyCode,d)!==-1||(b.name in this.submitted||b.name in this.invalid)&&this.element(b)},onclick:function(a){a.name in this.submitted?this.element(a):a.parentNode.name in this.submitted&&this.element(a.parentNode)},highlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).addClass(c).removeClass(d):a(b).addClass(c).removeClass(d)},unhighlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).removeClass(c).addClass(d):a(b).removeClass(c).addClass(d)}},setDefaults:function(b){a.extend(a.validator.defaults,b)},messages:{required:"This field is required.",remote:"Please fix this field.",email:"Please enter a valid email address.",url:"Please enter a valid URL.",date:"Please enter a valid date.",dateISO:"Please enter a valid date (ISO).",number:"Please enter a valid number.",digits:"Please enter only digits.",equalTo:"Please enter the same value again.",maxlength:a.validator.format("Please enter no more than {0} characters."),minlength:a.validator.format("Please enter at least {0} characters."),rangelength:a.validator.format("Please enter a value between {0} and {1} characters long."),range:a.validator.format("Please enter a value between {0} and {1}."),max:a.validator.format("Please enter a value less than or equal to {0}."),min:a.validator.format("Please enter a value greater than or equal to {0}."),step:a.validator.format("Please enter a multiple of {0}.")},autoCreateRanges:!1,prototype:{init:function(){function b(b){var c="undefined"!=typeof a(this).attr("contenteditable")&&"false"!==a(this).attr("contenteditable");if(!this.form&&c&&(this.form=a(this).closest("form")[0],this.name=a(this).attr("name")),d===this.form){var e=a.data(this.form,"validator"),f="on"+b.type.replace(/^validate/,""),g=e.settings;g[f]&&!a(this).is(g.ignore)&&g[f].call(e,this,b)}}this.labelContainer=a(this.settings.errorLabelContainer),this.errorContext=this.labelContainer.length&&this.labelContainer||a(this.currentForm),this.containers=a(this.settings.errorContainer).add(this.settings.errorLabelContainer),this.submitted={},this.valueCache={},this.pendingRequest=0,this.pending={},this.invalid={},this.reset();var c,d=this.currentForm,e=this.groups={};a.each(this.settings.groups,function(b,c){"string"==typeof c&&(c=c.split(/\s/)),a.each(c,function(a,c){e[c]=b})}),c=this.settings.rules,a.each(c,function(b,d){c[b]=a.validator.normalizeRule(d)}),a(this.currentForm).on("focusin.validate focusout.validate keyup.validate",":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], [type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], [type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], [type='radio'], [type='checkbox'], [contenteditable], [type='button']",b).on("click.validate","select, option, [type='radio'], [type='checkbox']",b),this.settings.invalidHandler&&a(this.currentForm).on("invalid-form.validate",this.settings.invalidHandler)},form:function(){return this.checkForm(),a.extend(this.submitted,this.errorMap),this.invalid=a.extend({},this.errorMap),this.valid()||a(this.currentForm).triggerHandler("invalid-form",[this]),this.showErrors(),this.valid()},checkForm:function(){this.prepareForm();for(var a=0,b=this.currentElements=this.elements();b[a];a++)this.check(b[a]);return this.valid()},element:function(b){var c,d,e=this.clean(b),f=this.validationTargetFor(e),g=this,h=!0;return void 0===f?delete this.invalid[e.name]:(this.prepareElement(f),this.currentElements=a(f),d=this.groups[f.name],d&&a.each(this.groups,function(a,b){b===d&&a!==f.name&&(e=g.validationTargetFor(g.clean(g.findByName(a))),e&&e.name in g.invalid&&(g.currentElements.push(e),h=g.check(e)&&h))}),c=this.check(f)!==!1,h=h&&c,c?this.invalid[f.name]=!1:this.invalid[f.name]=!0,this.numberOfInvalids()||(this.toHide=this.toHide.add(this.containers)),this.showErrors(),a(b).attr("aria-invalid",!c)),h},showErrors:function(b){if(b){var c=this;a.extend(this.errorMap,b),this.errorList=a.map(this.errorMap,function(a,b){return{message:a,element:c.findByName(b)[0]}}),this.successList=a.grep(this.successList,function(a){return!(a.name in b)})}this.settings.showErrors?this.settings.showErrors.call(this,this.errorMap,this.errorList):this.defaultShowErrors()},resetForm:function(){a.fn.resetForm&&a(this.currentForm).resetForm(),this.invalid={},this.submitted={},this.prepareForm(),this.hideErrors();var b=this.elements().removeData("previousValue").removeAttr("aria-invalid");this.resetElements(b)},resetElements:function(a){var b;if(this.settings.unhighlight)for(b=0;a[b];b++)this.settings.unhighlight.call(this,a[b],this.settings.errorClass,""),this.findByName(a[b].name).removeClass(this.settings.validClass);else a.removeClass(this.settings.errorClass).removeClass(this.settings.validClass)},numberOfInvalids:function(){return this.objectLength(this.invalid)},objectLength:function(a){var b,c=0;for(b in a)void 0!==a[b]&&null!==a[b]&&a[b]!==!1&&c++;return c},hideErrors:function(){this.hideThese(this.toHide)},hideThese:function(a){a.not(this.containers).text(""),this.addWrapper(a).hide()},valid:function(){return 0===this.size()},size:function(){return this.errorList.length},focusInvalid:function(){if(this.settings.focusInvalid)try{a(this.findLastActive()||this.errorList.length&&this.errorList[0].element||[]).filter(":visible").trigger("focus").trigger("focusin")}catch(b){}},findLastActive:function(){var b=this.lastActive;return b&&1===a.grep(this.errorList,function(a){return a.element.name===b.name}).length&&b},elements:function(){var b=this,c={};return a(this.currentForm).find("input, select, textarea, [contenteditable]").not(":submit, :reset, :image, :disabled").not(this.settings.ignore).filter(function(){var d=this.name||a(this).attr("name"),e="undefined"!=typeof a(this).attr("contenteditable")&&"false"!==a(this).attr("contenteditable");return!d&&b.settings.debug&&window.console&&console.error("%o has no name assigned",this),e&&(this.form=a(this).closest("form")[0],this.name=d),this.form===b.currentForm&&(!(d in c||!b.objectLength(a(this).rules()))&&(c[d]=!0,!0))})},clean:function(b){return a(b)[0]},errors:function(){var b=this.settings.errorClass.split(" ").join(".");return a(this.settings.errorElement+"."+b,this.errorContext)},resetInternals:function(){this.successList=[],this.errorList=[],this.errorMap={},this.toShow=a([]),this.toHide=a([])},reset:function(){this.resetInternals(),this.currentElements=a([])},prepareForm:function(){this.reset(),this.toHide=this.errors().add(this.containers)},prepareElement:function(a){this.reset(),this.toHide=this.errorsFor(a)},elementValue:function(b){var c,d,e=a(b),f=b.type,g="undefined"!=typeof e.attr("contenteditable")&&"false"!==e.attr("contenteditable");return"radio"===f||"checkbox"===f?this.findByName(b.name).filter(":checked").val():"number"===f&&"undefined"!=typeof b.validity?b.validity.badInput?"NaN":e.val():(c=g?e.text():e.val(),"file"===f?"C:\\fakepath\\"===c.substr(0,12)?c.substr(12):(d=c.lastIndexOf("/"),d>=0?c.substr(d+1):(d=c.lastIndexOf("\\"),d>=0?c.substr(d+1):c)):"string"==typeof c?c.replace(/\r/g,""):c)},check:function(b){b=this.validationTargetFor(this.clean(b));var c,d,e,f,g=a(b).rules(),h=a.map(g,function(a,b){return b}).length,i=!1,j=this.elementValue(b);"function"==typeof g.normalizer?f=g.normalizer:"function"==typeof this.settings.normalizer&&(f=this.settings.normalizer),f&&(j=f.call(b,j),delete g.normalizer);for(d in g){e={method:d,parameters:g[d]};try{if(c=a.validator.methods[d].call(this,j,b,e.parameters),"dependency-mismatch"===c&&1===h){i=!0;continue}if(i=!1,"pending"===c)return void(this.toHide=this.toHide.not(this.errorsFor(b)));if(!c)return this.formatAndAdd(b,e),!1}catch(k){throw this.settings.debug&&window.console&&console.log("Exception occurred when checking element "+b.id+", check the '"+e.method+"' method.",k),k instanceof TypeError&&(k.message+=". Exception occurred when checking element "+b.id+", check the '"+e.method+"' method."),k}}if(!i)return this.objectLength(g)&&this.successList.push(b),!0},customDataMessage:function(b,c){return a(b).data("msg"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase())||a(b).data("msg")},customMessage:function(a,b){var c=this.settings.messages[a];return c&&(c.constructor===String?c:c[b])},findDefined:function(){for(var a=0;aWarning: No message defined for "+b.name+""),e=/\$?\{(\d+)\}/g;return"function"==typeof d?d=d.call(this,c.parameters,b):e.test(d)&&(d=a.validator.format(d.replace(e,"{$1}"),c.parameters)),d},formatAndAdd:function(a,b){var c=this.defaultMessage(a,b);this.errorList.push({message:c,element:a,method:b.method}),this.errorMap[a.name]=c,this.submitted[a.name]=c},addWrapper:function(a){return this.settings.wrapper&&(a=a.add(a.parent(this.settings.wrapper))),a},defaultShowErrors:function(){var a,b,c;for(a=0;this.errorList[a];a++)c=this.errorList[a],this.settings.highlight&&this.settings.highlight.call(this,c.element,this.settings.errorClass,this.settings.validClass),this.showLabel(c.element,c.message);if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(a=0;this.successList[a];a++)this.showLabel(this.successList[a]);if(this.settings.unhighlight)for(a=0,b=this.validElements();b[a];a++)this.settings.unhighlight.call(this,b[a],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return a(this.errorList).map(function(){return this.element})},showLabel:function(b,c){var d,e,f,g,h=this.errorsFor(b),i=this.idOrName(b),j=a(b).attr("aria-describedby");h.length?(h.removeClass(this.settings.validClass).addClass(this.settings.errorClass),h.html(c)):(h=a("<"+this.settings.errorElement+">").attr("id",i+"-error").addClass(this.settings.errorClass).html(c||""),d=h,this.settings.wrapper&&(d=h.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.length?this.labelContainer.append(d):this.settings.errorPlacement?this.settings.errorPlacement.call(this,d,a(b)):d.insertAfter(b),h.is("label")?h.attr("for",i):0===h.parents("label[for='"+this.escapeCssMeta(i)+"']").length&&(f=h.attr("id"),j?j.match(new RegExp("\\b"+this.escapeCssMeta(f)+"\\b"))||(j+=" "+f):j=f,a(b).attr("aria-describedby",j),e=this.groups[b.name],e&&(g=this,a.each(g.groups,function(b,c){c===e&&a("[name='"+g.escapeCssMeta(b)+"']",g.currentForm).attr("aria-describedby",h.attr("id"))})))),!c&&this.settings.success&&(h.text(""),"string"==typeof this.settings.success?h.addClass(this.settings.success):this.settings.success(h,b)),this.toShow=this.toShow.add(h)},errorsFor:function(b){var c=this.escapeCssMeta(this.idOrName(b)),d=a(b).attr("aria-describedby"),e="label[for='"+c+"'], label[for='"+c+"'] *";return d&&(e=e+", #"+this.escapeCssMeta(d).replace(/\s+/g,", #")),this.errors().filter(e)},escapeCssMeta:function(a){return void 0===a?"":a.replace(/([\\!"#$%&'()*+,./:;<=>?@\[\]^`{|}~])/g,"\\$1")},idOrName:function(a){return this.groups[a.name]||(this.checkable(a)?a.name:a.id||a.name)},validationTargetFor:function(b){return this.checkable(b)&&(b=this.findByName(b.name)),a(b).not(this.settings.ignore)[0]},checkable:function(a){return/radio|checkbox/i.test(a.type)},findByName:function(b){return a(this.currentForm).find("[name='"+this.escapeCssMeta(b)+"']")},getLength:function(b,c){switch(c.nodeName.toLowerCase()){case"select":return a("option:selected",c).length;case"input":if(this.checkable(c))return this.findByName(c.name).filter(":checked").length}return b.length},depend:function(a,b){return!this.dependTypes[typeof a]||this.dependTypes[typeof a](a,b)},dependTypes:{"boolean":function(a){return a},string:function(b,c){return!!a(b,c.form).length},"function":function(a,b){return a(b)}},optional:function(b){var c=this.elementValue(b);return!a.validator.methods.required.call(this,c,b)&&"dependency-mismatch"},startRequest:function(b){this.pending[b.name]||(this.pendingRequest++,a(b).addClass(this.settings.pendingClass),this.pending[b.name]=!0)},stopRequest:function(b,c){this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[b.name],a(b).removeClass(this.settings.pendingClass),c&&0===this.pendingRequest&&this.formSubmitted&&this.form()&&0===this.pendingRequest?(a(this.currentForm).trigger("submit"),this.submitButton&&a("input:hidden[name='"+this.submitButton.name+"']",this.currentForm).remove(),this.formSubmitted=!1):!c&&0===this.pendingRequest&&this.formSubmitted&&(a(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(b,c){return c="string"==typeof c&&c||"remote",a.data(b,"previousValue")||a.data(b,"previousValue",{old:null,valid:!0,message:this.defaultMessage(b,{method:c})})},destroy:function(){this.resetForm(),a(this.currentForm).off(".validate").removeData("validator").find(".validate-equalTo-blur").off(".validate-equalTo").removeClass("validate-equalTo-blur").find(".validate-lessThan-blur").off(".validate-lessThan").removeClass("validate-lessThan-blur").find(".validate-lessThanEqual-blur").off(".validate-lessThanEqual").removeClass("validate-lessThanEqual-blur").find(".validate-greaterThanEqual-blur").off(".validate-greaterThanEqual").removeClass("validate-greaterThanEqual-blur").find(".validate-greaterThan-blur").off(".validate-greaterThan").removeClass("validate-greaterThan-blur")}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(b,c){b.constructor===String?this.classRuleSettings[b]=c:a.extend(this.classRuleSettings,b)},classRules:function(b){var c={},d=a(b).attr("class");return d&&a.each(d.split(" "),function(){this in a.validator.classRuleSettings&&a.extend(c,a.validator.classRuleSettings[this])}),c},normalizeAttributeRule:function(a,b,c,d){/min|max|step/.test(c)&&(null===b||/number|range|text/.test(b))&&(d=Number(d),isNaN(d)&&(d=void 0)),d||0===d?a[c]=d:b===c&&"range"!==b&&(a["date"===b?"dateISO":c]=!0)},attributeRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)"required"===c?(d=b.getAttribute(c),""===d&&(d=!0),d=!!d):d=f.attr(c),this.normalizeAttributeRule(e,g,c,d);return e.maxlength&&/-1|2147483647|524288/.test(e.maxlength)&&delete e.maxlength,e},dataRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)d=f.data("rule"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase()),""===d&&(d=!0),this.normalizeAttributeRule(e,g,c,d);return e},staticRules:function(b){var c={},d=a.data(b.form,"validator");return d.settings.rules&&(c=a.validator.normalizeRule(d.settings.rules[b.name])||{}),c},normalizeRules:function(b,c){return a.each(b,function(d,e){if(e===!1)return void delete b[d];if(e.param||e.depends){var f=!0;switch(typeof e.depends){case"string":f=!!a(e.depends,c.form).length;break;case"function":f=e.depends.call(c,c)}f?b[d]=void 0===e.param||e.param:(a.data(c.form,"validator").resetElements(a(c)),delete b[d])}}),a.each(b,function(a,d){b[a]="function"==typeof d&&"normalizer"!==a?d(c):d}),a.each(["minlength","maxlength"],function(){b[this]&&(b[this]=Number(b[this]))}),a.each(["rangelength","range"],function(){var a;b[this]&&(Array.isArray(b[this])?b[this]=[Number(b[this][0]),Number(b[this][1])]:"string"==typeof b[this]&&(a=b[this].replace(/[\[\]]/g,"").split(/[\s,]+/),b[this]=[Number(a[0]),Number(a[1])]))}),a.validator.autoCreateRanges&&(null!=b.min&&null!=b.max&&(b.range=[b.min,b.max],delete b.min,delete b.max),null!=b.minlength&&null!=b.maxlength&&(b.rangelength=[b.minlength,b.maxlength],delete b.minlength,delete b.maxlength)),b},normalizeRule:function(b){if("string"==typeof b){var c={};a.each(b.split(/\s/),function(){c[this]=!0}),b=c}return b},addMethod:function(b,c,d){a.validator.methods[b]=c,a.validator.messages[b]=void 0!==d?d:a.validator.messages[b],c.length<3&&a.validator.addClassRules(b,a.validator.normalizeRule(b))},methods:{required:function(b,c,d){if(!this.depend(d,c))return"dependency-mismatch";if("select"===c.nodeName.toLowerCase()){var e=a(c).val();return e&&e.length>0}return this.checkable(c)?this.getLength(b,c)>0:void 0!==b&&null!==b&&b.length>0},email:function(a,b){return this.optional(b)||/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(a)},url:function(a,b){return this.optional(b)||/^(?:(?:(?:https?|ftp):)?\/\/)(?:(?:[^\]\[?\/<~#`!@$^&*()+=}|:";',>{ ]|%[0-9A-Fa-f]{2})+(?::(?:[^\]\[?\/<~#`!@$^&*()+=}|:";',>{ ]|%[0-9A-Fa-f]{2})*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(a)},date:function(){var a=!1;return function(b,c){return a||(a=!0,this.settings.debug&&window.console&&console.warn("The `date` method is deprecated and will be removed in version '2.0.0'.\nPlease don't use it, since it relies on the Date constructor, which\nbehaves very differently across browsers and locales. Use `dateISO`\ninstead or one of the locale specific methods in `localizations/`\nand `additional-methods.js`.")),this.optional(c)||!/Invalid|NaN/.test(new Date(b).toString())}}(),dateISO:function(a,b){return this.optional(b)||/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(a)},number:function(a,b){return this.optional(b)||/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(a)},digits:function(a,b){return this.optional(b)||/^\d+$/.test(a)},minlength:function(a,b,c){var d=Array.isArray(a)?a.length:this.getLength(a,b);return this.optional(b)||d>=c},maxlength:function(a,b,c){var d=Array.isArray(a)?a.length:this.getLength(a,b);return this.optional(b)||d<=c},rangelength:function(a,b,c){var d=Array.isArray(a)?a.length:this.getLength(a,b);return this.optional(b)||d>=c[0]&&d<=c[1]},min:function(a,b,c){return this.optional(b)||a>=c},max:function(a,b,c){return this.optional(b)||a<=c},range:function(a,b,c){return this.optional(b)||a>=c[0]&&a<=c[1]},step:function(b,c,d){var e,f=a(c).attr("type"),g="Step attribute on input type "+f+" is not supported.",h=["text","number","range"],i=new RegExp("\\b"+f+"\\b"),j=f&&!i.test(h.join()),k=function(a){var b=(""+a).match(/(?:\.(\d+))?$/);return b&&b[1]?b[1].length:0},l=function(a){return Math.round(a*Math.pow(10,e))},m=!0;if(j)throw new Error(g);return e=k(d),(k(b)>e||l(b)%l(d)!==0)&&(m=!1),this.optional(c)||m},equalTo:function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.not(".validate-equalTo-blur").length&&e.addClass("validate-equalTo-blur").on("blur.validate-equalTo",function(){a(c).valid()}),b===e.val()},remote:function(b,c,d,e){if(this.optional(c))return"dependency-mismatch";e="string"==typeof e&&e||"remote";var f,g,h,i=this.previousValue(c,e);return this.settings.messages[c.name]||(this.settings.messages[c.name]={}),i.originalMessage=i.originalMessage||this.settings.messages[c.name][e],this.settings.messages[c.name][e]=i.message,d="string"==typeof d&&{url:d}||d,h=a.param(a.extend({data:b},d.data)),i.old===h?i.valid:(i.old=h,f=this,this.startRequest(c),g={},g[c.name]=b,a.ajax(a.extend(!0,{mode:"abort",port:"validate"+c.name,dataType:"json",data:g,context:f.currentForm,success:function(a){var d,g,h,j=a===!0||"true"===a;f.settings.messages[c.name][e]=i.originalMessage,j?(h=f.formSubmitted,f.resetInternals(),f.toHide=f.errorsFor(c),f.formSubmitted=h,f.successList.push(c),f.invalid[c.name]=!1,f.showErrors()):(d={},g=a||f.defaultMessage(c,{method:e,parameters:b}),d[c.name]=i.message=g,f.invalid[c.name]=!0,f.showErrors(d)),i.valid=j,f.stopRequest(c,j)}},d)),"pending")}}});var c,d={};return a.ajaxPrefilter?a.ajaxPrefilter(function(a,b,c){var e=a.port;"abort"===a.mode&&(d[e]&&d[e].abort(),d[e]=c)}):(c=a.ajax,a.ajax=function(b){var e=("mode"in b?b:a.ajaxSettings).mode,f=("port"in b?b:a.ajaxSettings).port;return"abort"===e?(d[f]&&d[f].abort(),d[f]=c.apply(this,arguments),d[f]):c.apply(this,arguments)}),a}); \ No newline at end of file diff --git a/src/main/resources/templates/account.html b/src/main/resources/templates/account.html index c82c97cb..727de224 100644 --- a/src/main/resources/templates/account.html +++ b/src/main/resources/templates/account.html @@ -1,300 +1,277 @@ - - - + + - + - - -
-
- -

-
-
-
+ + +
+
+ +

+
+
+
- -

User Settings

- -
- Default message if not found + +

User Settings

+ +
+ Default message if not found
-
- Default message if not found -
-
- Default message if not found -
-
- Default message if not found -
-
- Default message if not found -
-
- -

User!

- + + +

User!

+ - - -

Change Username?

-
-
- - -
-
- - -
-
- -
-
- - -

Change Password?

-
-
- - -
-
- - -
-
- - -
-
- -
-
+ + + +

Change Username?

+
+
+ + +
+
+ + +
+
+ +
+
+
+ + +

Change Password?

+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+

API Key

-
-
-
-
- -
- - - + + + +
-
- -

Sync browser settings with Account

-
-

Settings Comparison:

- - - - - - - - - - - -
PropertyAccount SettingWeb Browser Setting
+ async function refreshApiKey() { + try { + let response = await fetch('/api/v1/user/update-api-key', { method: 'POST' }); + if (response.status === 200) { + let apiKey = await response.text(); + manageUIState(apiKey); + document.getElementById("apiKey").type = 'text'; + document.getElementById("copyBtn").disabled = false; + } else { + alert('Error refreshing API key.'); + } + } catch (error) { + console.error('There was an error:', error); + } + } -
- - + function manageUIState(apiKey) { + const apiKeyElement = document.getElementById("apiKey"); + const showBtn = document.getElementById("showBtn"); + const copyBtn = document.getElementById("copyBtn"); + + if (apiKey && apiKey.trim().length > 0) { + apiKeyElement.value = apiKey; + showBtn.disabled = false; + copyBtn.disabled = true; + } else { + apiKeyElement.value = ""; + showBtn.disabled = true; + copyBtn.disabled = true; + } + } + + document.addEventListener("DOMContentLoaded", function() { + const form = document.querySelector('form[action="api/v1/user/change-password"]'); + + form.addEventListener('submit', function(event) { + const newPassword = document.getElementById('newPassword').value; + const confirmNewPassword = document.getElementById('confirmNewPassword').value; + + if (newPassword !== confirmNewPassword) { + alert('New Password and Confirm New Password must match.'); + event.preventDefault(); // Prevent form submission + } + }); + }); + + +

Sync browser settings with Account

+
+

Settings Comparison:

+ + + + + + + + + + + +
PropertyAccount SettingWeb Browser Setting
+ +
+ + +
-
- -
+
- -
- - + \ No newline at end of file diff --git a/src/main/resources/templates/addUsers.html b/src/main/resources/templates/addUsers.html index 8eda8eab..e87726cf 100644 --- a/src/main/resources/templates/addUsers.html +++ b/src/main/resources/templates/addUsers.html @@ -16,9 +16,8 @@

Admin User Control Settings

-
- Cannot delete currently logged in user. - The username does not exist and cannot be deleted. +
+ Message
@@ -26,6 +25,7 @@ + @@ -37,19 +37,21 @@ +
Username Roles ActionsAuthenticated

Add New User

-
- Default message if not found - Default message if not found +
+ Default message if not found
-
+ +
- + +
@@ -73,9 +75,8 @@

Change User's Role

-
- Username not found - Cannot downgrade current user's role +
+ Default message if not found
@@ -101,9 +102,55 @@
diff --git a/src/main/resources/templates/change-creds.html b/src/main/resources/templates/change-creds.html index feee43c1..30e06688 100644 --- a/src/main/resources/templates/change-creds.html +++ b/src/main/resources/templates/change-creds.html @@ -17,18 +17,9 @@

User Settings


- -
- Default message if not found -
-
- Default message if not found -
-
- Default message if not found -
-
- Default message if not found + +
+ Default message if not found
diff --git a/src/main/resources/templates/fragments/common.html b/src/main/resources/templates/fragments/common.html index a1b6828b..635d0952 100644 --- a/src/main/resources/templates/fragments/common.html +++ b/src/main/resources/templates/fragments/common.html @@ -1,174 +1,170 @@ - - + + - - - - - - + + + + + + - - - - - - - - - - - + + + + + + + + + + + - - - + + + + - - - - + + + + - - + + - - + + - - + + - - - - - - - - - + + + + + + + + + - + - - - - - - - - - + + + + + + + + + - - + + - - - - - - - + + + + + + - - -
-
Lives: 3
-
Score: 0
-
High Score: 0
-
Level: 1
- favicon -
- -
+ +
+
Lives: 3
+
Score: 0
+
High Score: 0
+
Level: 1
+ favicon +
+ +
- - - + + + -
-
- -
-
-
+
+
+ +
+
+
- - + +
\ No newline at end of file diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html index 3d510f00..085283c2 100644 --- a/src/main/resources/templates/login.html +++ b/src/main/resources/templates/login.html @@ -112,25 +112,32 @@ } } -
-
- Default message if not found -
- -
- -

Stirling-PDF

-
+
+ favicon + +

Stirling-PDF

+
Login Via SSO -
-
OAUTH2 Auto-Create User Disabled.
+
+
+
+
OAuth2: Error Message

-

Please sign in

+ +
+
OAuth2: Error Message
- - +
+
+
+ Default message if not found +
+
+
+ +

Please sign in

@@ -155,15 +162,11 @@
-
-
Invalid username or password.
-
Your account has been locked.
-