extends the functionality of oauth in Stirling PDF 2.
This commit is contained in:
parent
b904a46bca
commit
ffec5f7b54
52 changed files with 1055 additions and 786 deletions
|
@ -15,7 +15,14 @@ public class CleanUrlInterceptor implements HandlerInterceptor {
|
||||||
|
|
||||||
private static final List<String> ALLOWED_PARAMS =
|
private static final List<String> ALLOWED_PARAMS =
|
||||||
Arrays.asList(
|
Arrays.asList(
|
||||||
"lang", "endpoint", "endpoints", "logout", "error", "file", "messageType");
|
"lang",
|
||||||
|
"endpoint",
|
||||||
|
"endpoints",
|
||||||
|
"logout",
|
||||||
|
"error",
|
||||||
|
"erroroauth",
|
||||||
|
"file",
|
||||||
|
"messageType");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(
|
public boolean preHandle(
|
||||||
|
|
|
@ -6,18 +6,17 @@ import java.util.Optional;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||||
import org.springframework.security.authentication.LockedException;
|
import org.springframework.security.authentication.LockedException;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import stirling.software.SPDF.model.User;
|
import stirling.software.SPDF.model.User;
|
||||||
|
|
||||||
@Component
|
|
||||||
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
||||||
|
|
||||||
private LoginAttemptService loginAttemptService;
|
private LoginAttemptService loginAttemptService;
|
||||||
|
@ -28,7 +27,7 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF
|
||||||
LoggerFactory.getLogger(CustomAuthenticationFailureHandler.class);
|
LoggerFactory.getLogger(CustomAuthenticationFailureHandler.class);
|
||||||
|
|
||||||
public CustomAuthenticationFailureHandler(
|
public CustomAuthenticationFailureHandler(
|
||||||
LoginAttemptService loginAttemptService, UserService userService) {
|
final LoginAttemptService loginAttemptService, UserService userService) {
|
||||||
this.loginAttemptService = loginAttemptService;
|
this.loginAttemptService = loginAttemptService;
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
}
|
}
|
||||||
|
@ -41,24 +40,29 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF
|
||||||
throws IOException, ServletException {
|
throws IOException, ServletException {
|
||||||
|
|
||||||
String ip = request.getRemoteAddr();
|
String ip = request.getRemoteAddr();
|
||||||
logger.error("Failed login attempt from IP: " + ip);
|
logger.error("Failed login attempt from IP: {}", ip);
|
||||||
|
|
||||||
String username = request.getParameter("username");
|
if (exception.getClass().isAssignableFrom(InternalAuthenticationServiceException.class)
|
||||||
if (!isDemoUser(username)) {
|
|| "Password must not be null".equalsIgnoreCase(exception.getMessage())) {
|
||||||
if (loginAttemptService.loginAttemptCheck(username)) {
|
|
||||||
response.sendRedirect("/login?error=locked");
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
if (exception.getClass().isAssignableFrom(LockedException.class)) {
|
|
||||||
response.sendRedirect("/login?error=locked");
|
|
||||||
return;
|
|
||||||
} else if (exception instanceof UsernameNotFoundException) {
|
|
||||||
response.sendRedirect("/login?error=oauth2AuthenticationError");
|
response.sendRedirect("/login?error=oauth2AuthenticationError");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String username = request.getParameter("username");
|
||||||
|
if (username != null && !isDemoUser(username)) {
|
||||||
|
logger.info(
|
||||||
|
"Remaining attempts for user {}: {}",
|
||||||
|
username,
|
||||||
|
loginAttemptService.getRemainingAttempts(username));
|
||||||
|
loginAttemptService.loginFailed(username);
|
||||||
|
if (loginAttemptService.isBlocked(username)
|
||||||
|
|| exception.getClass().isAssignableFrom(LockedException.class)) {
|
||||||
|
response.sendRedirect("/login?error=locked");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)) {
|
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)
|
||||||
|
|| exception.getClass().isAssignableFrom(UsernameNotFoundException.class)) {
|
||||||
response.sendRedirect("/login?error=badcredentials");
|
response.sendRedirect("/login?error=badcredentials");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,9 @@ package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
@ -14,23 +12,30 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.servlet.http.HttpSession;
|
import jakarta.servlet.http.HttpSession;
|
||||||
import stirling.software.SPDF.utils.RequestUriUtils;
|
import stirling.software.SPDF.utils.RequestUriUtils;
|
||||||
|
|
||||||
@Component
|
|
||||||
public class CustomAuthenticationSuccessHandler
|
public class CustomAuthenticationSuccessHandler
|
||||||
extends SavedRequestAwareAuthenticationSuccessHandler {
|
extends SavedRequestAwareAuthenticationSuccessHandler {
|
||||||
@Autowired private LoginAttemptService loginAttemptService;
|
|
||||||
|
private LoginAttemptService loginAttemptService;
|
||||||
|
|
||||||
|
public CustomAuthenticationSuccessHandler(LoginAttemptService loginAttemptService) {
|
||||||
|
this.loginAttemptService = loginAttemptService;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAuthenticationSuccess(
|
public void onAuthenticationSuccess(
|
||||||
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
String username = request.getParameter("username");
|
|
||||||
loginAttemptService.loginSucceeded(username);
|
String userName = request.getParameter("username");
|
||||||
|
loginAttemptService.loginSucceeded(userName);
|
||||||
|
|
||||||
// Get the saved request
|
// Get the saved request
|
||||||
HttpSession session = request.getSession(false);
|
HttpSession session = request.getSession(false);
|
||||||
SavedRequest savedRequest =
|
SavedRequest savedRequest =
|
||||||
session != null
|
(session != null)
|
||||||
? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")
|
? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
if (savedRequest != null
|
if (savedRequest != null
|
||||||
&& !RequestUriUtils.isStaticResource(savedRequest.getRedirectUrl())) {
|
&& !RequestUriUtils.isStaticResource(savedRequest.getRedirectUrl())) {
|
||||||
// Redirect to the original destination
|
// Redirect to the original destination
|
||||||
|
|
|
@ -24,6 +24,8 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
String sessionId = session.getId();
|
String sessionId = session.getId();
|
||||||
sessionRegistry.removeSessionInformation(sessionId);
|
sessionRegistry.removeSessionInformation(sessionId);
|
||||||
|
session.invalidate();
|
||||||
|
logger.debug("Session invalidated: " + sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.sendRedirect(request.getContextPath() + "/login?logout=true");
|
response.sendRedirect(request.getContextPath() + "/login?logout=true");
|
||||||
|
|
|
@ -22,11 +22,7 @@ public class CustomUserDetailsService implements UserDetailsService {
|
||||||
|
|
||||||
@Autowired private UserRepository userRepository;
|
@Autowired private UserRepository userRepository;
|
||||||
|
|
||||||
private LoginAttemptService loginAttemptService;
|
@Autowired private LoginAttemptService loginAttemptService;
|
||||||
|
|
||||||
CustomUserDetailsService(LoginAttemptService loginAttemptService) {
|
|
||||||
this.loginAttemptService = loginAttemptService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
@ -43,8 +39,8 @@ public class CustomUserDetailsService implements UserDetailsService {
|
||||||
"Your account has been locked due to too many failed login attempts.");
|
"Your account has been locked due to too many failed login attempts.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.getPassword() == null || user.getPassword().isEmpty()) {
|
if (!user.hasPassword()) {
|
||||||
throw new UsernameNotFoundException("Password must not be null");
|
throw new IllegalArgumentException("Password must not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new org.springframework.security.core.userdetails.User(
|
return new org.springframework.security.core.userdetails.User(
|
||||||
|
|
|
@ -21,47 +21,16 @@ public class InitialSecuritySetup {
|
||||||
|
|
||||||
@Autowired private UserService userService;
|
@Autowired private UserService userService;
|
||||||
|
|
||||||
@Autowired ApplicationProperties applicationProperties;
|
@Autowired private ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(InitialSecuritySetup.class);
|
private static final Logger logger = LoggerFactory.getLogger(InitialSecuritySetup.class);
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
if (!userService.hasUsers()) {
|
if (!userService.hasUsers()) {
|
||||||
|
initializeAdminUser();
|
||||||
String initialUsername =
|
|
||||||
applicationProperties.getSecurity().getInitialLogin().getUsername();
|
|
||||||
String initialPassword =
|
|
||||||
applicationProperties.getSecurity().getInitialLogin().getPassword();
|
|
||||||
if (initialUsername != null && initialPassword != null) {
|
|
||||||
try {
|
|
||||||
// https://github.com/Stirling-Tools/Stirling-PDF/issues/976
|
|
||||||
userService.isUsernameValidWithReturn(initialUsername);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
Path pathToFile = Paths.get("configs/settings.yml");
|
|
||||||
|
|
||||||
if (Files.exists(pathToFile)) {
|
|
||||||
logger.error(
|
|
||||||
"Invalid initial username provided , username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.");
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
userService.saveUser(initialUsername, initialPassword, Role.ADMIN.getRoleId());
|
|
||||||
} else {
|
|
||||||
initialUsername = "admin";
|
|
||||||
initialPassword = "stirling";
|
|
||||||
userService.saveUser(
|
|
||||||
initialUsername, initialPassword, Role.ADMIN.getRoleId(), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!userService.usernameExistsIgnoreCase(Role.INTERNAL_API_USER.getRoleId())) {
|
|
||||||
userService.saveUser(
|
|
||||||
Role.INTERNAL_API_USER.getRoleId(),
|
|
||||||
UUID.randomUUID().toString(),
|
|
||||||
Role.INTERNAL_API_USER.getRoleId());
|
|
||||||
userService.addApiKeyToUser(Role.INTERNAL_API_USER.getRoleId());
|
|
||||||
}
|
}
|
||||||
|
initializeInternalApiUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
|
@ -73,6 +42,51 @@ public class InitialSecuritySetup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initializeAdminUser() {
|
||||||
|
String initialUsername =
|
||||||
|
applicationProperties.getSecurity().getInitialLogin().getUsername();
|
||||||
|
String initialPassword =
|
||||||
|
applicationProperties.getSecurity().getInitialLogin().getPassword();
|
||||||
|
|
||||||
|
if (initialUsername != null
|
||||||
|
&& !initialUsername.isEmpty()
|
||||||
|
&& initialPassword != null
|
||||||
|
&& !initialPassword.isEmpty()
|
||||||
|
&& !userService.findByUsernameIgnoreCase(initialUsername).isPresent()) {
|
||||||
|
try {
|
||||||
|
if (userService.isUsernameValid(initialUsername)) {
|
||||||
|
userService.saveUser(initialUsername, initialPassword, Role.ADMIN.getRoleId());
|
||||||
|
logger.info("Admin user created: " + initialUsername);
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
logger.error("Failed to initialize security setup", e);
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
createDefaultAdminUser();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createDefaultAdminUser() {
|
||||||
|
String defaultUsername = "admin";
|
||||||
|
String defaultPassword = "stirling";
|
||||||
|
if (!userService.findByUsernameIgnoreCase(defaultUsername).isPresent()) {
|
||||||
|
userService.saveUser(defaultUsername, defaultPassword, Role.ADMIN.getRoleId(), true);
|
||||||
|
logger.info("Default admin user created: " + defaultUsername);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeInternalApiUser() {
|
||||||
|
if (!userService.usernameExistsIgnoreCase(Role.INTERNAL_API_USER.getRoleId())) {
|
||||||
|
userService.saveUser(
|
||||||
|
Role.INTERNAL_API_USER.getRoleId(),
|
||||||
|
UUID.randomUUID().toString(),
|
||||||
|
Role.INTERNAL_API_USER.getRoleId());
|
||||||
|
userService.addApiKeyToUser(Role.INTERNAL_API_USER.getRoleId());
|
||||||
|
logger.info("Internal API user created: " + Role.INTERNAL_API_USER.getRoleId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void saveKeyToConfig(String key) throws IOException {
|
private void saveKeyToConfig(String key) throws IOException {
|
||||||
Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
|
Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
|
||||||
List<String> lines = Files.readAllLines(path);
|
List<String> lines = Files.readAllLines(path);
|
||||||
|
|
|
@ -3,6 +3,8 @@ package stirling.software.SPDF.config.security;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@ -15,47 +17,62 @@ public class LoginAttemptService {
|
||||||
|
|
||||||
@Autowired ApplicationProperties applicationProperties;
|
@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 long ATTEMPT_INCREMENT_TIME;
|
||||||
|
private ConcurrentHashMap<String, AttemptCounter> attemptsCache;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
MAX_ATTEMPTS = applicationProperties.getSecurity().getLoginAttemptCount();
|
MAX_ATTEMPT = applicationProperties.getSecurity().getLoginAttemptCount();
|
||||||
ATTEMPT_INCREMENT_TIME =
|
ATTEMPT_INCREMENT_TIME =
|
||||||
TimeUnit.MINUTES.toMillis(
|
TimeUnit.MINUTES.toMillis(
|
||||||
applicationProperties.getSecurity().getLoginResetTimeMinutes());
|
applicationProperties.getSecurity().getLoginResetTimeMinutes());
|
||||||
|
attemptsCache = new ConcurrentHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ConcurrentHashMap<String, AttemptCounter> attemptsCache =
|
|
||||||
new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public void loginSucceeded(String key) {
|
public void loginSucceeded(String key) {
|
||||||
|
logger.info(key + " " + attemptsCache.mappingCount());
|
||||||
|
if (key == null || key.trim().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
attemptsCache.remove(key.toLowerCase());
|
attemptsCache.remove(key.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean loginAttemptCheck(String key) {
|
public void loginFailed(String key) {
|
||||||
return attemptsCache
|
if (key == null || key.trim().isEmpty()) return;
|
||||||
.compute(
|
|
||||||
key.toLowerCase(),
|
AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
|
||||||
(k, attemptCounter) -> {
|
if (attemptCounter == null) {
|
||||||
if (attemptCounter == null
|
attemptCounter = new AttemptCounter();
|
||||||
|| attemptCounter.shouldReset(ATTEMPT_INCREMENT_TIME)) {
|
attemptsCache.put(key.toLowerCase(), attemptCounter);
|
||||||
return new AttemptCounter();
|
|
||||||
} else {
|
} else {
|
||||||
attemptCounter.increment();
|
if (attemptCounter.shouldReset(ATTEMPT_INCREMENT_TIME)) {
|
||||||
return attemptCounter;
|
attemptCounter.reset();
|
||||||
|
}
|
||||||
|
attemptCounter.increment();
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.getAttemptCount()
|
|
||||||
>= MAX_ATTEMPTS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBlocked(String key) {
|
public boolean isBlocked(String key) {
|
||||||
|
if (key == null || key.trim().isEmpty()) return false;
|
||||||
AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
|
AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
|
||||||
if (attemptCounter != null) {
|
if (attemptCounter == null) {
|
||||||
return attemptCounter.getAttemptCount() >= MAX_ATTEMPTS
|
|
||||||
&& !attemptCounter.shouldReset(ATTEMPT_INCREMENT_TIME);
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return attemptCounter.getAttemptCount() >= MAX_ATTEMPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRemainingAttempts(String key) {
|
||||||
|
if (key == null || key.trim().isEmpty()) return MAX_ATTEMPT;
|
||||||
|
|
||||||
|
AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
|
||||||
|
if (attemptCounter == null) {
|
||||||
|
return MAX_ATTEMPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MAX_ATTEMPT - attemptCounter.getAttemptCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,8 @@ public class SecurityConfiguration {
|
||||||
formLogin
|
formLogin
|
||||||
.loginPage("/login")
|
.loginPage("/login")
|
||||||
.successHandler(
|
.successHandler(
|
||||||
new CustomAuthenticationSuccessHandler())
|
new CustomAuthenticationSuccessHandler(
|
||||||
|
loginAttemptService))
|
||||||
.defaultSuccessUrl("/")
|
.defaultSuccessUrl("/")
|
||||||
.failureHandler(
|
.failureHandler(
|
||||||
new CustomAuthenticationFailureHandler(
|
new CustomAuthenticationFailureHandler(
|
||||||
|
@ -146,7 +147,6 @@ public class SecurityConfiguration {
|
||||||
.permitAll()
|
.permitAll()
|
||||||
.anyRequest()
|
.anyRequest()
|
||||||
.authenticated())
|
.authenticated())
|
||||||
.userDetailsService(userDetailsService)
|
|
||||||
.authenticationProvider(authenticationProvider());
|
.authenticationProvider(authenticationProvider());
|
||||||
|
|
||||||
// Handle OAUTH2 Logins
|
// Handle OAUTH2 Logins
|
||||||
|
@ -162,7 +162,9 @@ public class SecurityConfiguration {
|
||||||
*/
|
*/
|
||||||
.successHandler(
|
.successHandler(
|
||||||
new CustomOAuth2AuthenticationSuccessHandler(
|
new CustomOAuth2AuthenticationSuccessHandler(
|
||||||
applicationProperties, userService))
|
loginAttemptService,
|
||||||
|
applicationProperties,
|
||||||
|
userService))
|
||||||
.failureHandler(
|
.failureHandler(
|
||||||
new CustomOAuth2AuthenticationFailureHandler())
|
new CustomOAuth2AuthenticationFailureHandler())
|
||||||
// Add existing Authorities from the database
|
// Add existing Authorities from the database
|
||||||
|
@ -171,15 +173,17 @@ public class SecurityConfiguration {
|
||||||
userInfoEndpoint
|
userInfoEndpoint
|
||||||
.oidcUserService(
|
.oidcUserService(
|
||||||
new CustomOAuth2UserService(
|
new CustomOAuth2UserService(
|
||||||
applicationProperties))
|
applicationProperties,
|
||||||
|
userService,
|
||||||
|
loginAttemptService))
|
||||||
.userAuthoritiesMapper(
|
.userAuthoritiesMapper(
|
||||||
userAuthoritiesMapper())))
|
userAuthoritiesMapper())))
|
||||||
.userDetailsService(userDetailsService)
|
|
||||||
.logout(
|
.logout(
|
||||||
logout ->
|
logout ->
|
||||||
logout.logoutSuccessHandler(
|
logout.logoutSuccessHandler(
|
||||||
new CustomOAuth2LogoutSuccessHandler(
|
new CustomOAuth2LogoutSuccessHandler(
|
||||||
this.applicationProperties)));
|
this.applicationProperties,
|
||||||
|
sessionRegistry())));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
http.csrf(csrf -> csrf.disable())
|
http.csrf(csrf -> csrf.disable())
|
||||||
|
|
|
@ -40,11 +40,11 @@ public class UserService implements UserServiceInterface {
|
||||||
|
|
||||||
// Handle OAUTH2 login and user auto creation.
|
// Handle OAUTH2 login and user auto creation.
|
||||||
public boolean processOAuth2PostLogin(String username, boolean autoCreateUser) {
|
public boolean processOAuth2PostLogin(String username, boolean autoCreateUser) {
|
||||||
if (!isUsernameValidWithReturn(username).equals(username)) {
|
if (!isUsernameValid(username)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Optional<User> existUser = userRepository.findByUsernameIgnoreCase(username);
|
Optional<User> existingUser = userRepository.findByUsernameIgnoreCase(username);
|
||||||
if (existUser.isPresent()) {
|
if (existingUser.isPresent()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (autoCreateUser) {
|
if (autoCreateUser) {
|
||||||
|
@ -114,9 +114,8 @@ public class UserService implements UserServiceInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserDetails loadUserByApiKey(String apiKey) {
|
public UserDetails loadUserByApiKey(String apiKey) {
|
||||||
User userOptional = userRepository.findByApiKey(apiKey);
|
User user = userRepository.findByApiKey(apiKey);
|
||||||
if (userOptional != null) {
|
if (user != null) {
|
||||||
User user = userOptional;
|
|
||||||
// Convert your User entity to a UserDetails object with authorities
|
// Convert your User entity to a UserDetails object with authorities
|
||||||
return new org.springframework.security.core.userdetails.User(
|
return new org.springframework.security.core.userdetails.User(
|
||||||
user.getUsername(),
|
user.getUsername(),
|
||||||
|
@ -128,13 +127,16 @@ public class UserService implements UserServiceInterface {
|
||||||
|
|
||||||
public boolean validateApiKeyForUser(String username, String apiKey) {
|
public boolean validateApiKeyForUser(String username, String apiKey) {
|
||||||
Optional<User> userOpt = userRepository.findByUsernameIgnoreCase(username);
|
Optional<User> userOpt = userRepository.findByUsernameIgnoreCase(username);
|
||||||
return userOpt.isPresent() && userOpt.get().getApiKey().equals(apiKey);
|
return userOpt.isPresent() && apiKey.equals(userOpt.get().getApiKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveUser(String username, AuthenticationType authenticationType)
|
public void saveUser(String username, AuthenticationType authenticationType)
|
||||||
throws IllegalArgumentException {
|
throws IllegalArgumentException {
|
||||||
|
if (!isUsernameValid(username)) {
|
||||||
|
throw new IllegalArgumentException(getInvalidUsernameMessage());
|
||||||
|
}
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setUsername(isUsernameValidWithReturn(username));
|
user.setUsername(username);
|
||||||
user.setEnabled(true);
|
user.setEnabled(true);
|
||||||
user.setFirstLogin(false);
|
user.setFirstLogin(false);
|
||||||
user.addAuthority(new Authority(Role.USER.getRoleId(), user));
|
user.addAuthority(new Authority(Role.USER.getRoleId(), user));
|
||||||
|
@ -143,8 +145,11 @@ public class UserService implements UserServiceInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveUser(String username, String password) throws IllegalArgumentException {
|
public void saveUser(String username, String password) throws IllegalArgumentException {
|
||||||
|
if (!isUsernameValid(username)) {
|
||||||
|
throw new IllegalArgumentException(getInvalidUsernameMessage());
|
||||||
|
}
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setUsername(isUsernameValidWithReturn(username));
|
user.setUsername(username);
|
||||||
user.setPassword(passwordEncoder.encode(password));
|
user.setPassword(passwordEncoder.encode(password));
|
||||||
user.setEnabled(true);
|
user.setEnabled(true);
|
||||||
user.setAuthenticationType(AuthenticationType.WEB);
|
user.setAuthenticationType(AuthenticationType.WEB);
|
||||||
|
@ -153,8 +158,11 @@ public class UserService implements UserServiceInterface {
|
||||||
|
|
||||||
public void saveUser(String username, String password, String role, boolean firstLogin)
|
public void saveUser(String username, String password, String role, boolean firstLogin)
|
||||||
throws IllegalArgumentException {
|
throws IllegalArgumentException {
|
||||||
|
if (!isUsernameValid(username)) {
|
||||||
|
throw new IllegalArgumentException(getInvalidUsernameMessage());
|
||||||
|
}
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setUsername(isUsernameValidWithReturn(username));
|
user.setUsername(username);
|
||||||
user.setPassword(passwordEncoder.encode(password));
|
user.setPassword(passwordEncoder.encode(password));
|
||||||
user.addAuthority(new Authority(role, user));
|
user.addAuthority(new Authority(role, user));
|
||||||
user.setEnabled(true);
|
user.setEnabled(true);
|
||||||
|
@ -165,14 +173,7 @@ public class UserService implements UserServiceInterface {
|
||||||
|
|
||||||
public void saveUser(String username, String password, String role)
|
public void saveUser(String username, String password, String role)
|
||||||
throws IllegalArgumentException {
|
throws IllegalArgumentException {
|
||||||
User user = new User();
|
saveUser(username, password, role, false);
|
||||||
user.setUsername(isUsernameValidWithReturn(username));
|
|
||||||
user.setPassword(passwordEncoder.encode(password));
|
|
||||||
user.addAuthority(new Authority(role, user));
|
|
||||||
user.setEnabled(true);
|
|
||||||
user.setAuthenticationType(AuthenticationType.WEB);
|
|
||||||
user.setFirstLogin(false);
|
|
||||||
userRepository.save(user);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteUser(String username) {
|
public void deleteUser(String username) {
|
||||||
|
@ -206,7 +207,7 @@ public class UserService implements UserServiceInterface {
|
||||||
Map<String, String> settingsMap = user.getSettings();
|
Map<String, String> settingsMap = user.getSettings();
|
||||||
|
|
||||||
if (settingsMap == null) {
|
if (settingsMap == null) {
|
||||||
settingsMap = new HashMap<String, String>();
|
settingsMap = new HashMap<>();
|
||||||
}
|
}
|
||||||
settingsMap.clear();
|
settingsMap.clear();
|
||||||
settingsMap.putAll(updates);
|
settingsMap.putAll(updates);
|
||||||
|
@ -229,7 +230,10 @@ public class UserService implements UserServiceInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void changeUsername(User user, String newUsername) throws IllegalArgumentException {
|
public void changeUsername(User user, String newUsername) throws IllegalArgumentException {
|
||||||
user.setUsername(isUsernameValidWithReturn(newUsername));
|
if (!isUsernameValid(newUsername)) {
|
||||||
|
throw new IllegalArgumentException(getInvalidUsernameMessage());
|
||||||
|
}
|
||||||
|
user.setUsername(newUsername);
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,30 +268,20 @@ public class UserService implements UserServiceInterface {
|
||||||
return isValidSimpleUsername || isValidEmail;
|
return isValidSimpleUsername || isValidEmail;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String isUsernameValidWithReturn(String username) throws IllegalArgumentException {
|
private String getInvalidUsernameMessage() {
|
||||||
if (!isUsernameValid(username)) {
|
return messageSource.getMessage(
|
||||||
String message =
|
|
||||||
messageSource.getMessage(
|
|
||||||
"invalidUsernameMessage", null, LocaleContextHolder.getLocale());
|
"invalidUsernameMessage", null, LocaleContextHolder.getLocale());
|
||||||
throw new IllegalArgumentException(message);
|
|
||||||
}
|
|
||||||
return username;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasPassword(String username) {
|
public boolean hasPassword(String username) {
|
||||||
Optional<User> user = userRepository.findByUsernameIgnoreCase(username);
|
Optional<User> user = userRepository.findByUsernameIgnoreCase(username);
|
||||||
if (user.isPresent() && user.get().hasPassword()) {
|
return user.isPresent() && user.get().hasPassword();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAuthenticationTypeByUsername(
|
public boolean isAuthenticationTypeByUsername(
|
||||||
String username, AuthenticationType authenticationType) {
|
String username, AuthenticationType authenticationType) {
|
||||||
Optional<User> user = userRepository.findByUsernameIgnoreCase(username);
|
Optional<User> user = userRepository.findByUsernameIgnoreCase(username);
|
||||||
if (user.isPresent() && user.get().getAuthenticationType() != null) {
|
return user.isPresent()
|
||||||
return user.get().getAuthenticationType().equalsIgnoreCase(authenticationType.name());
|
&& authenticationType.name().equalsIgnoreCase(user.get().getAuthenticationType());
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,22 +2,24 @@ package stirling.software.SPDF.config.security.oauth2;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.security.authentication.LockedException;
|
import org.springframework.security.authentication.LockedException;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
|
||||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
@Component
|
|
||||||
public class CustomOAuth2AuthenticationFailureHandler
|
public class CustomOAuth2AuthenticationFailureHandler
|
||||||
extends SimpleUrlAuthenticationFailureHandler {
|
extends SimpleUrlAuthenticationFailureHandler {
|
||||||
|
|
||||||
|
private static final Logger logger =
|
||||||
|
LoggerFactory.getLogger(CustomOAuth2AuthenticationFailureHandler.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAuthenticationFailure(
|
public void onAuthenticationFailure(
|
||||||
HttpServletRequest request,
|
HttpServletRequest request,
|
||||||
|
@ -26,14 +28,21 @@ public class CustomOAuth2AuthenticationFailureHandler
|
||||||
throws IOException, ServletException {
|
throws IOException, ServletException {
|
||||||
if (exception instanceof OAuth2AuthenticationException) {
|
if (exception instanceof OAuth2AuthenticationException) {
|
||||||
OAuth2Error error = ((OAuth2AuthenticationException) exception).getError();
|
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()
|
getRedirectStrategy()
|
||||||
.sendRedirect(request, response, "/login?error=oAuth::" + error.getErrorCode());
|
.sendRedirect(request, response, "/logout?erroroauth=" + errorCode);
|
||||||
|
return;
|
||||||
} else if (exception instanceof LockedException) {
|
} else if (exception instanceof LockedException) {
|
||||||
getRedirectStrategy().sendRedirect(request, response, "/login?error=locked");
|
logger.error("Account locked: ", exception);
|
||||||
} else if (exception instanceof UsernameNotFoundException) {
|
getRedirectStrategy().sendRedirect(request, response, "/logout?error=locked");
|
||||||
getRedirectStrategy()
|
|
||||||
.sendRedirect(request, response, "/login?error=oauth2AuthenticationError");
|
|
||||||
} else {
|
} else {
|
||||||
|
logger.error("Unhandled authentication exception", exception);
|
||||||
super.onAuthenticationFailure(request, response, exception);
|
super.onAuthenticationFailure(request, response, exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,33 +2,43 @@ package stirling.software.SPDF.config.security.oauth2;
|
||||||
|
|
||||||
import java.io.IOException;
|
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.core.Authentication;
|
||||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.servlet.http.HttpSession;
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
import stirling.software.SPDF.config.security.LoginAttemptService;
|
||||||
import stirling.software.SPDF.config.security.UserService;
|
import stirling.software.SPDF.config.security.UserService;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||||
import stirling.software.SPDF.model.AuthenticationType;
|
import stirling.software.SPDF.model.AuthenticationType;
|
||||||
import stirling.software.SPDF.utils.RequestUriUtils;
|
import stirling.software.SPDF.utils.RequestUriUtils;
|
||||||
|
|
||||||
@Component
|
|
||||||
public class CustomOAuth2AuthenticationSuccessHandler
|
public class CustomOAuth2AuthenticationSuccessHandler
|
||||||
extends SavedRequestAwareAuthenticationSuccessHandler {
|
extends SavedRequestAwareAuthenticationSuccessHandler {
|
||||||
|
|
||||||
ApplicationProperties applicationProperties;
|
private LoginAttemptService loginAttemptService;
|
||||||
UserService userService;
|
|
||||||
|
private static final Logger logger =
|
||||||
|
LoggerFactory.getLogger(CustomOAuth2AuthenticationSuccessHandler.class);
|
||||||
|
|
||||||
|
private ApplicationProperties applicationProperties;
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
public CustomOAuth2AuthenticationSuccessHandler(
|
public CustomOAuth2AuthenticationSuccessHandler(
|
||||||
ApplicationProperties applicationProperties, UserService userService) {
|
final LoginAttemptService loginAttemptService,
|
||||||
|
ApplicationProperties applicationProperties,
|
||||||
|
UserService userService) {
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
|
this.loginAttemptService = loginAttemptService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -36,28 +46,37 @@ public class CustomOAuth2AuthenticationSuccessHandler
|
||||||
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
|
|
||||||
OAuth2User oauthUser = (OAuth2User) authentication.getPrincipal();
|
|
||||||
|
|
||||||
// Get the saved request
|
// Get the saved request
|
||||||
HttpSession session = request.getSession(false);
|
HttpSession session = request.getSession(false);
|
||||||
SavedRequest savedRequest =
|
SavedRequest savedRequest =
|
||||||
session != null
|
(session != null)
|
||||||
? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")
|
? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
if (savedRequest != null
|
if (savedRequest != null
|
||||||
&& !RequestUriUtils.isStaticResource(savedRequest.getRedirectUrl())) {
|
&& !RequestUriUtils.isStaticResource(savedRequest.getRedirectUrl())) {
|
||||||
// Redirect to the original destination
|
// Redirect to the original destination
|
||||||
super.onAuthenticationSuccess(request, response, authentication);
|
super.onAuthenticationSuccess(request, response, authentication);
|
||||||
} else {
|
} else {
|
||||||
|
OAuth2User oauthUser = (OAuth2User) authentication.getPrincipal();
|
||||||
OAUTH2 oAuth = applicationProperties.getSecurity().getOAUTH2();
|
OAUTH2 oAuth = applicationProperties.getSecurity().getOAUTH2();
|
||||||
String username = oauthUser.getAttribute(oAuth.getUseAsUsername());
|
|
||||||
|
String username = oauthUser.getName();
|
||||||
|
|
||||||
|
if (loginAttemptService.isBlocked(username)) {
|
||||||
|
if (session != null) {
|
||||||
|
session.removeAttribute("SPRING_SECURITY_SAVED_REQUEST");
|
||||||
|
}
|
||||||
|
throw new LockedException(
|
||||||
|
"Your account has been locked due to too many failed login attempts.");
|
||||||
|
}
|
||||||
if (userService.usernameExistsIgnoreCase(username)
|
if (userService.usernameExistsIgnoreCase(username)
|
||||||
&& userService.hasPassword(username)
|
&& userService.hasPassword(username)
|
||||||
&& !userService.isAuthenticationTypeByUsername(
|
&& !userService.isAuthenticationTypeByUsername(
|
||||||
username, AuthenticationType.OAUTH2)
|
username, AuthenticationType.OAUTH2)
|
||||||
&& oAuth.getAutoCreateUser()) {
|
&& oAuth.getAutoCreateUser()) {
|
||||||
response.sendRedirect(
|
response.sendRedirect(
|
||||||
request.getContextPath() + "/logout?oauth2AuthenticationError=true");
|
request.getContextPath() + "/logout?oauth2AuthenticationErrorWeb=true");
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -2,13 +2,11 @@ package stirling.software.SPDF.config.security.oauth2;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.session.SessionRegistry;
|
import org.springframework.security.core.session.SessionRegistry;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
|
||||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
|
||||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
@ -17,14 +15,17 @@ import jakarta.servlet.http.HttpSession;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||||
|
|
||||||
@Component
|
|
||||||
public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
||||||
|
|
||||||
@Autowired SessionRegistry sessionRegistry;
|
private static final Logger logger =
|
||||||
|
LoggerFactory.getLogger(CustomOAuth2LogoutSuccessHandler.class);
|
||||||
|
|
||||||
private ApplicationProperties applicationProperties;
|
private final SessionRegistry sessionRegistry;
|
||||||
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
public CustomOAuth2LogoutSuccessHandler(ApplicationProperties applicationProperties) {
|
public CustomOAuth2LogoutSuccessHandler(
|
||||||
|
ApplicationProperties applicationProperties, SessionRegistry sessionRegistry) {
|
||||||
|
this.sessionRegistry = sessionRegistry;
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,32 +34,27 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
|
||||||
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||||
throws IOException, ServletException {
|
throws IOException, ServletException {
|
||||||
|
|
||||||
boolean isOAuthUser = true;
|
|
||||||
String param = "logout=true";
|
String param = "logout=true";
|
||||||
if (authentication == null) {
|
|
||||||
response.sendRedirect("/");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Object pri = authentication.getPrincipal();
|
|
||||||
if (pri instanceof UserDetails) {
|
|
||||||
UserDetails userDetails = (UserDetails) pri;
|
|
||||||
isOAuthUser = userDetails.getPassword() == null;
|
|
||||||
} else if (pri instanceof OAuth2User) {
|
|
||||||
isOAuthUser = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
|
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
|
||||||
String provider = oauth.getProvider() != null && isOAuthUser ? oauth.getProvider() : "";
|
String provider = oauth.getProvider() != null ? oauth.getProvider() : "";
|
||||||
|
|
||||||
if (request.getParameter("oauth2AuthenticationError") != null) {
|
if (request.getParameter("oauth2AuthenticationErrorWeb") != null) {
|
||||||
param = "error=oauth2AuthenticationError";
|
param = "erroroauth=oauth2AuthenticationErrorWeb";
|
||||||
} else if (request.getParameter("invalidUsername") != null) {
|
} else if (request.getParameter("error") != null) {
|
||||||
param = "error=invalidUsername";
|
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);
|
HttpSession session = request.getSession(false);
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
String sessionId = session.getId();
|
String sessionId = session.getId();
|
||||||
sessionRegistry.removeSessionInformation(sessionId);
|
sessionRegistry.removeSessionInformation(sessionId);
|
||||||
|
session.invalidate();
|
||||||
|
logger.debug("Session invalidated: " + sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (provider) {
|
switch (provider) {
|
||||||
|
@ -70,17 +66,20 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
|
||||||
+ oauth.getClientId()
|
+ oauth.getClientId()
|
||||||
+ "&post_logout_redirect_uri="
|
+ "&post_logout_redirect_uri="
|
||||||
+ response.encodeRedirectURL(
|
+ response.encodeRedirectURL(
|
||||||
"http://" + request.getHeader("host") + "/login?" + param);
|
request.getScheme()
|
||||||
|
+ "://"
|
||||||
|
+ request.getHeader("host")
|
||||||
|
+ "/login?"
|
||||||
|
+ param);
|
||||||
|
logger.debug("Redirecting to Keycloak logout URL: " + logoutUrl);
|
||||||
response.sendRedirect(logoutUrl);
|
response.sendRedirect(logoutUrl);
|
||||||
break;
|
break;
|
||||||
case "google":
|
case "google":
|
||||||
|
// Add Google specific logout URL if needed
|
||||||
default:
|
default:
|
||||||
if (request.getParameter("oauth2AutoCreateDisabled") != null) {
|
String redirectUrl = request.getContextPath() + "/login?" + param;
|
||||||
response.sendRedirect(
|
logger.debug("Redirecting to default logout URL: " + redirectUrl);
|
||||||
request.getContextPath() + "/login?error=oauth2AutoCreateDisabled");
|
response.sendRedirect(redirectUrl);
|
||||||
} else {
|
|
||||||
response.sendRedirect(request.getContextPath() + "/login?logout=true");
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package stirling.software.SPDF.config.security.oauth2;
|
package stirling.software.SPDF.config.security.oauth2;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.Optional;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
|
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.OidcUserRequest;
|
||||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
|
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
|
||||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
||||||
|
@ -11,16 +13,30 @@ import org.springframework.security.oauth2.core.OAuth2Error;
|
||||||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
||||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
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.ApplicationProperties;
|
||||||
|
import stirling.software.SPDF.model.User;
|
||||||
|
|
||||||
public class CustomOAuth2UserService implements OAuth2UserService<OidcUserRequest, OidcUser> {
|
public class CustomOAuth2UserService implements OAuth2UserService<OidcUserRequest, OidcUser> {
|
||||||
|
|
||||||
private final OidcUserService delegate = new OidcUserService();
|
private final OidcUserService delegate = new OidcUserService();
|
||||||
|
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
private LoginAttemptService loginAttemptService;
|
||||||
|
|
||||||
private ApplicationProperties applicationProperties;
|
private ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
public CustomOAuth2UserService(ApplicationProperties applicationProperties) {
|
private static final Logger logger = LoggerFactory.getLogger(CustomOAuth2UserService.class);
|
||||||
|
|
||||||
|
public CustomOAuth2UserService(
|
||||||
|
ApplicationProperties applicationProperties,
|
||||||
|
UserService userService,
|
||||||
|
LoginAttemptService loginAttemptService) {
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
|
this.userService = userService;
|
||||||
|
this.loginAttemptService = loginAttemptService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -28,16 +44,18 @@ public class CustomOAuth2UserService implements OAuth2UserService<OidcUserReques
|
||||||
String usernameAttribute =
|
String usernameAttribute =
|
||||||
applicationProperties.getSecurity().getOAUTH2().getUseAsUsername();
|
applicationProperties.getSecurity().getOAUTH2().getUseAsUsername();
|
||||||
try {
|
try {
|
||||||
|
|
||||||
OidcUser user = delegate.loadUser(userRequest);
|
OidcUser user = delegate.loadUser(userRequest);
|
||||||
Map<String, Object> attributes = new HashMap<>(user.getAttributes());
|
String username = user.getUserInfo().getClaimAsString(usernameAttribute);
|
||||||
|
Optional<User> duser = userService.findByUsernameIgnoreCase(username);
|
||||||
// Ensure the preferred username attribute is present
|
if (duser.isPresent()) {
|
||||||
if (!attributes.containsKey(usernameAttribute)) {
|
if (loginAttemptService.isBlocked(username)) {
|
||||||
attributes.put(usernameAttribute, attributes.getOrDefault("email", ""));
|
throw new LockedException(
|
||||||
usernameAttribute = "email";
|
"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 a new OidcUser with adjusted attributes
|
||||||
return new DefaultOidcUser(
|
return new DefaultOidcUser(
|
||||||
user.getAuthorities(),
|
user.getAuthorities(),
|
||||||
|
@ -45,8 +63,11 @@ public class CustomOAuth2UserService implements OAuth2UserService<OidcUserReques
|
||||||
user.getUserInfo(),
|
user.getUserInfo(),
|
||||||
usernameAttribute);
|
usernameAttribute);
|
||||||
} catch (java.lang.IllegalArgumentException e) {
|
} catch (java.lang.IllegalArgumentException e) {
|
||||||
throw new OAuth2AuthenticationException(
|
logger.error("Error loading OIDC user: {}", e.getMessage());
|
||||||
new OAuth2Error(e.getMessage()), e.getMessage(), e);
|
throw new OAuth2AuthenticationException(new OAuth2Error(e.getMessage()), e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Unexpected error loading OIDC user", e);
|
||||||
|
throw new OAuth2AuthenticationException("Unexpected error during authentication");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,9 +43,52 @@ public class AccountWebController {
|
||||||
|
|
||||||
model.addAttribute("currentPage", "login");
|
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) {
|
if (request.getParameter("logout") != null) {
|
||||||
|
|
||||||
|
@ -60,7 +103,8 @@ public class AccountWebController {
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||||
@GetMapping("/addUsers")
|
@GetMapping("/addUsers")
|
||||||
public String showAddUserForm(Model model, Authentication authentication) {
|
public String showAddUserForm(
|
||||||
|
HttpServletRequest request, Model model, Authentication authentication) {
|
||||||
List<User> allUsers = userRepository.findAll();
|
List<User> allUsers = userRepository.findAll();
|
||||||
Iterator<User> iterator = allUsers.iterator();
|
Iterator<User> iterator = allUsers.iterator();
|
||||||
Map<String, String> roleDetails = Role.getAllRoleDetails();
|
Map<String, String> roleDetails = Role.getAllRoleDetails();
|
||||||
|
@ -78,6 +122,52 @@ public class AccountWebController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String messageType = request.getParameter("messageType");
|
||||||
|
|
||||||
|
String deleteMessage = null;
|
||||||
|
if (messageType != null) {
|
||||||
|
switch (messageType) {
|
||||||
|
case "deleteCurrentUser":
|
||||||
|
deleteMessage = "deleteCurrentUserMessage";
|
||||||
|
break;
|
||||||
|
case "deleteUsernameExists":
|
||||||
|
deleteMessage = "deleteUsernameExistsMessage";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
model.addAttribute("deleteMessage", deleteMessage);
|
||||||
|
|
||||||
|
String addMessage = null;
|
||||||
|
switch (messageType) {
|
||||||
|
case "usernameExists":
|
||||||
|
addMessage = "usernameExistsMessage";
|
||||||
|
break;
|
||||||
|
case "invalidUsername":
|
||||||
|
addMessage = "invalidUsernameMessage";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
model.addAttribute("addMessage", addMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
String changeMessage = null;
|
||||||
|
if (messageType != null) {
|
||||||
|
switch (messageType) {
|
||||||
|
case "userNotFound":
|
||||||
|
changeMessage = "userNotFoundMessage";
|
||||||
|
break;
|
||||||
|
case "downgradeCurrentUser":
|
||||||
|
changeMessage = "downgradeCurrentUserMessage";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
model.addAttribute("changeMessage", changeMessage);
|
||||||
|
}
|
||||||
|
|
||||||
model.addAttribute("users", allUsers);
|
model.addAttribute("users", allUsers);
|
||||||
model.addAttribute("currentUsername", authentication.getName());
|
model.addAttribute("currentUsername", authentication.getName());
|
||||||
model.addAttribute("roleDetails", roleDetails);
|
model.addAttribute("roleDetails", roleDetails);
|
||||||
|
@ -136,6 +226,30 @@ public class AccountWebController {
|
||||||
return "redirect:/error"; // Example redirection in case of error
|
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
|
// Add attributes to the model
|
||||||
model.addAttribute("username", username);
|
model.addAttribute("username", username);
|
||||||
model.addAttribute("role", user.get().getRolesAsString());
|
model.addAttribute("role", user.get().getRolesAsString());
|
||||||
|
@ -174,6 +288,28 @@ public class AccountWebController {
|
||||||
// Handle error appropriately
|
// Handle error appropriately
|
||||||
return "redirect:/error"; // Example redirection in case of error
|
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
|
// Add attributes to the model
|
||||||
model.addAttribute("username", username);
|
model.addAttribute("username", username);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ public class AttemptCounter {
|
||||||
private long lastAttemptTime;
|
private long lastAttemptTime;
|
||||||
|
|
||||||
public AttemptCounter() {
|
public AttemptCounter() {
|
||||||
this.attemptCount = 1;
|
this.attemptCount = 0;
|
||||||
this.lastAttemptTime = System.currentTimeMillis();
|
this.lastAttemptTime = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,11 +18,16 @@ public class AttemptCounter {
|
||||||
return attemptCount;
|
return attemptCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getlastAttemptTime() {
|
public long getLastAttemptTime() {
|
||||||
return lastAttemptTime;
|
return lastAttemptTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean shouldReset(long ATTEMPT_INCREMENT_TIME) {
|
public boolean shouldReset(long attemptIncrementTime) {
|
||||||
return System.currentTimeMillis() - lastAttemptTime > ATTEMPT_INCREMENT_TIME;
|
return System.currentTimeMillis() - lastAttemptTime > attemptIncrementTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
this.attemptCount = 0;
|
||||||
|
this.lastAttemptTime = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,6 +150,6 @@ public class User {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasPassword() {
|
public boolean hasPassword() {
|
||||||
return this.getPassword() != "" ? true : false;
|
return this.password != null && !this.password.isEmpty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=لا يمكن خفض دور المستخدم الحالي
|
downgradeCurrentUserMessage=لا يمكن خفض دور المستخدم الحالي
|
||||||
downgradeCurrentUserLongMessage=لا يمكن تخفيض دور المستخدم الحالي. وبالتالي، لن يظهر المستخدم الحالي.
|
downgradeCurrentUserLongMessage=لا يمكن تخفيض دور المستخدم الحالي. وبالتالي، لن يظهر المستخدم الحالي.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Error
|
error=Error
|
||||||
oops=Oops!
|
oops=Oops!
|
||||||
help=Help
|
help=Help
|
||||||
|
@ -73,6 +74,7 @@ sponsor=Sponsor
|
||||||
info=Info
|
info=Info
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Pipeline #
|
# Pipeline #
|
||||||
###############
|
###############
|
||||||
|
@ -183,7 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
|
||||||
adminUserSettings.forceChange=Force user to change password on login
|
adminUserSettings.forceChange=Force user to change password on login
|
||||||
adminUserSettings.submit=Save User
|
adminUserSettings.submit=Save User
|
||||||
adminUserSettings.changeUserRole=تغيير دور المستخدم
|
adminUserSettings.changeUserRole=تغيير دور المستخدم
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Не може да се изтрие вписания
|
||||||
deleteUsernameExistsMessage=Потребителското име не съществува и не може да бъде изтрито.
|
deleteUsernameExistsMessage=Потребителското име не съществува и не може да бъде изтрито.
|
||||||
downgradeCurrentUserMessage=Не може да се понижи ролята на текущия потребител
|
downgradeCurrentUserMessage=Не може да се понижи ролята на текущия потребител
|
||||||
downgradeCurrentUserLongMessage=Не може да се понижи ролята на текущия потребител. Следователно текущият потребител няма да бъде показан.
|
downgradeCurrentUserLongMessage=Не може да се понижи ролята на текущия потребител. Следователно текущият потребител няма да бъде показан.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Грешка
|
error=Грешка
|
||||||
oops=Опаа!
|
oops=Опаа!
|
||||||
help=Помощ
|
help=Помощ
|
||||||
|
@ -74,7 +75,6 @@ info=Info
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Pipeline #
|
# Pipeline #
|
||||||
###############
|
###############
|
||||||
|
@ -185,7 +185,7 @@ adminUserSettings.internalApiUser=Вътрешен API потребител
|
||||||
adminUserSettings.forceChange=Принудете потребителя да промени потребителското име/парола при влизане
|
adminUserSettings.forceChange=Принудете потребителя да промени потребителското име/парола при влизане
|
||||||
adminUserSettings.submit=Съхранете потребителя
|
adminUserSettings.submit=Съхранете потребителя
|
||||||
adminUserSettings.changeUserRole=Промяна на ролята на потребителя
|
adminUserSettings.changeUserRole=Промяна на ролята на потребителя
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -807,7 +807,6 @@ multiTool.title=PDF Мулти инструмент
|
||||||
multiTool.header=PDF Мулти инструмент
|
multiTool.header=PDF Мулти инструмент
|
||||||
multiTool.uploadPrompts=File Name
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Преглед на PDF
|
viewPdf.title=Преглед на PDF
|
||||||
viewPdf.header=Преглед на PDF
|
viewPdf.header=Преглед на PDF
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=No es pot reduir la funció de l'usuari actual
|
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.
|
downgradeCurrentUserLongMessage=No es pot baixar la funció de l'usuari actual. Per tant, no es mostrarà l'usuari actual.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Error
|
error=Error
|
||||||
oops=Oops!
|
oops=Oops!
|
||||||
help=Help
|
help=Help
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
|
||||||
adminUserSettings.forceChange=Force user to change password on login
|
adminUserSettings.forceChange=Force user to change password on login
|
||||||
adminUserSettings.submit=Desar Usuari
|
adminUserSettings.submit=Desar Usuari
|
||||||
adminUserSettings.changeUserRole=Canvia el rol de l'usuari
|
adminUserSettings.changeUserRole=Canvia el rol de l'usuari
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF Multi Tool
|
multiTool.title=PDF Multi Tool
|
||||||
multiTool.header=PDF Multi Tool
|
multiTool.header=PDF Multi Tool
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=View PDF
|
viewPdf.title=View PDF
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Der aktuell angemeldete Benutzer kann nicht gelöscht w
|
||||||
deleteUsernameExistsMessage=Der Benutzername existiert nicht und 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
|
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.
|
downgradeCurrentUserLongMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden. Daher wird der aktuelle Benutzer nicht angezeigt.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Fehler
|
error=Fehler
|
||||||
oops=Hoppla!
|
oops=Hoppla!
|
||||||
help=Hilfe
|
help=Hilfe
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=Interner API-Benutzer
|
||||||
adminUserSettings.forceChange=Benutzer dazu zwingen, Benutzernamen/Passwort bei der Anmeldung zu ändern
|
adminUserSettings.forceChange=Benutzer dazu zwingen, Benutzernamen/Passwort bei der Anmeldung zu ändern
|
||||||
adminUserSettings.submit=Benutzer speichern
|
adminUserSettings.submit=Benutzer speichern
|
||||||
adminUserSettings.changeUserRole=Benutzerrolle ändern
|
adminUserSettings.changeUserRole=Benutzerrolle ändern
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(z.B. 1,3,2 oder 4-8,2,10-12 oder 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF-Multitool
|
multiTool.title=PDF-Multitool
|
||||||
multiTool.header=PDF-Multitool
|
multiTool.header=PDF-Multitool
|
||||||
multiTool.uploadPrompts=Bitte PDF hochladen
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=PDF anzeigen
|
viewPdf.title=PDF anzeigen
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Δεν είναι δυνατή η διαγραφή το
|
||||||
deleteUsernameExistsMessage=Το όνομα χρήστη δεν υπάρχει και δεν μπορεί να διαγραφεί.
|
deleteUsernameExistsMessage=Το όνομα χρήστη δεν υπάρχει και δεν μπορεί να διαγραφεί.
|
||||||
downgradeCurrentUserMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη
|
downgradeCurrentUserMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη
|
||||||
downgradeCurrentUserLongMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη. Ως εκ τούτου, ο τρέχων χρήστης δεν θα εμφανίζεται.
|
downgradeCurrentUserLongMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη. Ως εκ τούτου, ο τρέχων χρήστης δεν θα εμφανίζεται.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Σφάλμα
|
error=Σφάλμα
|
||||||
oops=Ωχ!
|
oops=Ωχ!
|
||||||
help=Βοήθεια
|
help=Βοήθεια
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=Εσωτερικός API χρήστης
|
||||||
adminUserSettings.forceChange=Αναγκάστε τον χρήστη να αλλάξει το όνομα χρήστη/κωδικό πρόσβασης κατά τη σύνδεση
|
adminUserSettings.forceChange=Αναγκάστε τον χρήστη να αλλάξει το όνομα χρήστη/κωδικό πρόσβασης κατά τη σύνδεση
|
||||||
adminUserSettings.submit=Αποθήκευση Χρήστη
|
adminUserSettings.submit=Αποθήκευση Χρήστη
|
||||||
adminUserSettings.changeUserRole=Αλλαγή ρόλου χρήστη
|
adminUserSettings.changeUserRole=Αλλαγή ρόλου χρήστη
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(π.χ. 1,3,2 ή 4-8,2,10-12 ή 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF Πολυεργαλείο
|
multiTool.title=PDF Πολυεργαλείο
|
||||||
multiTool.header=PDF Πολυεργαλείο
|
multiTool.header=PDF Πολυεργαλείο
|
||||||
multiTool.uploadPrompts=Ανεβάστε το PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Προβολή PDF
|
viewPdf.title=Προβολή PDF
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=Cannot downgrade current user's role
|
downgradeCurrentUserMessage=Cannot downgrade current user's role
|
||||||
downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown.
|
downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Error
|
error=Error
|
||||||
oops=Oops!
|
oops=Oops!
|
||||||
help=Help
|
help=Help
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
|
||||||
adminUserSettings.forceChange=Force user to change password on login
|
adminUserSettings.forceChange=Force user to change password on login
|
||||||
adminUserSettings.submit=Save User
|
adminUserSettings.submit=Save User
|
||||||
adminUserSettings.changeUserRole=Change User's Role
|
adminUserSettings.changeUserRole=Change User's Role
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=Cannot downgrade current user's role
|
downgradeCurrentUserMessage=Cannot downgrade current user's role
|
||||||
downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown.
|
downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Error
|
error=Error
|
||||||
oops=Oops!
|
oops=Oops!
|
||||||
help=Help
|
help=Help
|
||||||
|
@ -73,6 +74,7 @@ sponsor=Sponsor
|
||||||
info=Info
|
info=Info
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Pipeline #
|
# Pipeline #
|
||||||
###############
|
###############
|
||||||
|
@ -183,7 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
|
||||||
adminUserSettings.forceChange=Force user to change password on login
|
adminUserSettings.forceChange=Force user to change password on login
|
||||||
adminUserSettings.submit=Save User
|
adminUserSettings.submit=Save User
|
||||||
adminUserSettings.changeUserRole=Change User's Role
|
adminUserSettings.changeUserRole=Change User's Role
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=No puede eliminar el usuario que tiene la sesión actua
|
||||||
deleteUsernameExistsMessage=El usuario no existe y no puede eliminarse.
|
deleteUsernameExistsMessage=El usuario no existe y no puede eliminarse.
|
||||||
downgradeCurrentUserMessage=No se puede degradar el rol del usuario actual
|
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á.
|
downgradeCurrentUserLongMessage=No se puede degradar el rol del usuario actual. Por lo tanto, el usuario actual no se mostrará.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Error
|
error=Error
|
||||||
oops=Ups!
|
oops=Ups!
|
||||||
help=Help
|
help=Help
|
||||||
|
@ -74,9 +75,6 @@ info=Info
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Pipeline #
|
# Pipeline #
|
||||||
###############
|
###############
|
||||||
|
@ -187,7 +185,7 @@ adminUserSettings.internalApiUser=Usuario interno de API
|
||||||
adminUserSettings.forceChange=Forzar usuario a cambiar usuario/contraseña en el acceso
|
adminUserSettings.forceChange=Forzar usuario a cambiar usuario/contraseña en el acceso
|
||||||
adminUserSettings.submit=Guardar Usuario
|
adminUserSettings.submit=Guardar Usuario
|
||||||
adminUserSettings.changeUserRole=Cambiar rol de usuario
|
adminUserSettings.changeUserRole=Cambiar rol de usuario
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -807,7 +805,7 @@ pdfOrganiser.placeholder=(por ej., 1,3,2 o 4-8,2,10-12 o 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=Multi-herramienta PDF
|
multiTool.title=Multi-herramienta PDF
|
||||||
multiTool.header=Multi-herramienta PDF
|
multiTool.header=Multi-herramienta PDF
|
||||||
multiTool.uploadPrompts=Por favor, cargue PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Ver PDF
|
viewPdf.title=Ver PDF
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=Ezin da uneko erabiltzailearen rola jaitsi
|
downgradeCurrentUserMessage=Ezin da uneko erabiltzailearen rola jaitsi
|
||||||
downgradeCurrentUserLongMessage=Ezin da uneko erabiltzailearen rola jaitsi. Beraz, oraingo erabiltzailea ez da erakutsiko.
|
downgradeCurrentUserLongMessage=Ezin da uneko erabiltzailearen rola jaitsi. Beraz, oraingo erabiltzailea ez da erakutsiko.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Error
|
error=Error
|
||||||
oops=Oops!
|
oops=Oops!
|
||||||
help=Help
|
help=Help
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
|
||||||
adminUserSettings.forceChange=Force user to change password on login
|
adminUserSettings.forceChange=Force user to change password on login
|
||||||
adminUserSettings.submit=Gorde Erabiltzailea
|
adminUserSettings.submit=Gorde Erabiltzailea
|
||||||
adminUserSettings.changeUserRole=Erabiltzailearen rola aldatu
|
adminUserSettings.changeUserRole=Erabiltzailearen rola aldatu
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF erabilera anitzeko tresna
|
multiTool.title=PDF erabilera anitzeko tresna
|
||||||
multiTool.header=PDF erabilera anitzeko tresna
|
multiTool.header=PDF erabilera anitzeko tresna
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=View PDF
|
viewPdf.title=View PDF
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Impossible de supprimer l’utilisateur actuellement co
|
||||||
deleteUsernameExistsMessage=Le nom d’utilisateur n’existe pas et ne peut pas être supprimé.
|
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
|
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é.
|
downgradeCurrentUserLongMessage=Impossible de rétrograder le rôle de l'utilisateur actuel. Par conséquent, l'utilisateur actuel ne sera pas affiché.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Erreur
|
error=Erreur
|
||||||
oops=Oups !
|
oops=Oups !
|
||||||
help=Aide
|
help=Aide
|
||||||
|
@ -73,6 +74,7 @@ sponsor=Sponsor
|
||||||
info=Info
|
info=Info
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Pipeline #
|
# Pipeline #
|
||||||
###############
|
###############
|
||||||
|
@ -183,7 +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.forceChange=Forcer l’utilisateur à changer son nom d’utilisateur/mot de passe lors de la connexion
|
||||||
adminUserSettings.submit=Ajouter
|
adminUserSettings.submit=Ajouter
|
||||||
adminUserSettings.changeUserRole=Changer le rôle de l'utilisateur
|
adminUserSettings.changeUserRole=Changer le rôle de l'utilisateur
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -803,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=Outil multifonction PDF
|
multiTool.title=Outil multifonction PDF
|
||||||
multiTool.header=Outil multifonction PDF
|
multiTool.header=Outil multifonction PDF
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Visualiser un PDF
|
viewPdf.title=Visualiser un PDF
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता
|
downgradeCurrentUserMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता
|
||||||
downgradeCurrentUserLongMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता। इसलिए, वर्तमान उपयोगकर्ता को नहीं दिखाया जाएगा।
|
downgradeCurrentUserLongMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता। इसलिए, वर्तमान उपयोगकर्ता को नहीं दिखाया जाएगा।
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Error
|
error=Error
|
||||||
oops=Oops!
|
oops=Oops!
|
||||||
help=Help
|
help=Help
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
|
||||||
adminUserSettings.forceChange=उपयोगकर्ता को लॉगिन पर उपयोगकर्ता नाम/पासवर्ड बदलने के लिए मजबूर करें
|
adminUserSettings.forceChange=उपयोगकर्ता को लॉगिन पर उपयोगकर्ता नाम/पासवर्ड बदलने के लिए मजबूर करें
|
||||||
adminUserSettings.submit=उपयोगकर्ता को सहेजें
|
adminUserSettings.submit=उपयोगकर्ता को सहेजें
|
||||||
adminUserSettings.changeUserRole=यूज़र की भूमिका बदलें
|
adminUserSettings.changeUserRole=यूज़र की भूमिका बदलें
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=पीडीएफ मल्टी टूल
|
multiTool.title=पीडीएफ मल्टी टूल
|
||||||
multiTool.header=पीडीएफ मल्टी टूल
|
multiTool.header=पीडीएफ मल्टी टूल
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=पीडीएफ देखें
|
viewPdf.title=पीडीएफ देखें
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=A jelenlegi felhasználó szerepkörét nem lehet visszaminősíteni
|
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.
|
downgradeCurrentUserLongMessage=Az aktuális felhasználó szerepkörét nem lehet visszaminősíteni. Ezért az aktuális felhasználó nem jelenik meg.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Error
|
error=Error
|
||||||
oops=Oops!
|
oops=Oops!
|
||||||
help=Help
|
help=Help
|
||||||
|
@ -184,7 +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.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.submit=Felhasználó mentése
|
||||||
adminUserSettings.changeUserRole=Felhasználó szerepkörének módosítása
|
adminUserSettings.changeUserRole=Felhasználó szerepkörének módosítása
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF többfunkciós eszköz
|
multiTool.title=PDF többfunkciós eszköz
|
||||||
multiTool.header=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
|
#view pdf
|
||||||
viewPdf.title=PDF megtekintése
|
viewPdf.title=PDF megtekintése
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=Tidak dapat menurunkan peran pengguna saat ini
|
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.
|
downgradeCurrentUserLongMessage=Tidak dapat menurunkan peran pengguna saat ini. Oleh karena itu, pengguna saat ini tidak akan ditampilkan.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Error
|
error=Error
|
||||||
oops=Oops!
|
oops=Oops!
|
||||||
help=Help
|
help=Help
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
|
||||||
adminUserSettings.forceChange=Memaksa pengguna untuk mengubah nama pengguna/kata sandi saat masuk
|
adminUserSettings.forceChange=Memaksa pengguna untuk mengubah nama pengguna/kata sandi saat masuk
|
||||||
adminUserSettings.submit=Simpan Pengguna
|
adminUserSettings.submit=Simpan Pengguna
|
||||||
adminUserSettings.changeUserRole=Ubah Peran Pengguna
|
adminUserSettings.changeUserRole=Ubah Peran Pengguna
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=Alat Multi PDF
|
multiTool.title=Alat Multi PDF
|
||||||
multiTool.header=Alat Multi PDF
|
multiTool.header=Alat Multi PDF
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Lihat PDF
|
viewPdf.title=Lihat PDF
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Impossibile eliminare l'utente attualmente connesso.
|
||||||
deleteUsernameExistsMessage=Il nome utente non esiste e non può essere eliminato.
|
deleteUsernameExistsMessage=Il nome utente non esiste e non può essere eliminato.
|
||||||
downgradeCurrentUserMessage=Impossibile declassare il ruolo dell'utente corrente
|
downgradeCurrentUserMessage=Impossibile declassare il ruolo dell'utente corrente
|
||||||
downgradeCurrentUserLongMessage=Impossibile declassare il ruolo dell'utente corrente. Pertanto, l'utente corrente non verrà visualizzato.
|
downgradeCurrentUserLongMessage=Impossibile declassare il ruolo dell'utente corrente. Pertanto, l'utente corrente non verrà visualizzato.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Errore
|
error=Errore
|
||||||
oops=Oops!
|
oops=Oops!
|
||||||
help=Aiuto
|
help=Aiuto
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=API utente interna
|
||||||
adminUserSettings.forceChange=Forza l'utente a cambiare nome username/password all'accesso
|
adminUserSettings.forceChange=Forza l'utente a cambiare nome username/password all'accesso
|
||||||
adminUserSettings.submit=Salva utente
|
adminUserSettings.submit=Salva utente
|
||||||
adminUserSettings.changeUserRole=Cambia il ruolo dell'utente
|
adminUserSettings.changeUserRole=Cambia il ruolo dell'utente
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(ad es. 1,3,2 o 4-8,2,10-12 o 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=Multifunzione PDF
|
multiTool.title=Multifunzione PDF
|
||||||
multiTool.header=Multifunzione PDF
|
multiTool.header=Multifunzione PDF
|
||||||
multiTool.uploadPrompts=Caricare il PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Visualizza PDF
|
viewPdf.title=Visualizza PDF
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=現在ログインしているユーザーは削除で
|
||||||
deleteUsernameExistsMessage=そのユーザー名は存在しないため削除できません。
|
deleteUsernameExistsMessage=そのユーザー名は存在しないため削除できません。
|
||||||
downgradeCurrentUserMessage=現在のユーザーの役割をダウングレードできません
|
downgradeCurrentUserMessage=現在のユーザーの役割をダウングレードできません
|
||||||
downgradeCurrentUserLongMessage=現在のユーザーの役割をダウングレードできません。したがって、現在のユーザーは表示されません。
|
downgradeCurrentUserLongMessage=現在のユーザーの役割をダウングレードできません。したがって、現在のユーザーは表示されません。
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=エラー
|
error=エラー
|
||||||
oops=おっと!
|
oops=おっと!
|
||||||
help=ヘルプ
|
help=ヘルプ
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=内部APIユーザー
|
||||||
adminUserSettings.forceChange=ログイン時にユーザー名/パスワードを強制的に変更する
|
adminUserSettings.forceChange=ログイン時にユーザー名/パスワードを強制的に変更する
|
||||||
adminUserSettings.submit=ユーザーの保存
|
adminUserSettings.submit=ユーザーの保存
|
||||||
adminUserSettings.changeUserRole=ユーザーの役割を変更する
|
adminUserSettings.changeUserRole=ユーザーの役割を変更する
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(例:1,3,2または4-8,2,10-12または2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDFマルチツール
|
multiTool.title=PDFマルチツール
|
||||||
multiTool.header=PDFマルチツール
|
multiTool.header=PDFマルチツール
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=PDFを表示
|
viewPdf.title=PDFを表示
|
||||||
|
|
|
@ -57,11 +57,10 @@ usernameExistsMessage=새 사용자명이 이미 존재합니다.
|
||||||
invalidUsernameMessage=잘못된 사용자 이름입니다. 사용자 이름에는 문자, 숫자 및 다음 특수 문자(@._+-)만 포함할 수 있거나 유효한 이메일 주소여야 합니다.
|
invalidUsernameMessage=잘못된 사용자 이름입니다. 사용자 이름에는 문자, 숫자 및 다음 특수 문자(@._+-)만 포함할 수 있거나 유효한 이메일 주소여야 합니다.
|
||||||
deleteCurrentUserMessage=현재 로그인한 사용자를 삭제할 수 없습니다.
|
deleteCurrentUserMessage=현재 로그인한 사용자를 삭제할 수 없습니다.
|
||||||
deleteUsernameExistsMessage=사용자 이름이 존재하지 않으며 삭제할 수 없습니다.
|
deleteUsernameExistsMessage=사용자 이름이 존재하지 않으며 삭제할 수 없습니다.
|
||||||
|
|
||||||
info=Info
|
|
||||||
downgradeCurrentUserMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다
|
downgradeCurrentUserMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다
|
||||||
downgradeCurrentUserLongMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다. 따라서 현재 사용자는 표시되지 않습니다.
|
downgradeCurrentUserLongMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다. 따라서 현재 사용자는 표시되지 않습니다.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
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 +71,7 @@ visitGithub=GitHub 저장소 방문하기
|
||||||
donate=기부하기
|
donate=기부하기
|
||||||
color=색상
|
color=색상
|
||||||
sponsor=스폰서
|
sponsor=스폰서
|
||||||
|
info=Info
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ adminUserSettings.internalApiUser=내부 API 사용자
|
||||||
adminUserSettings.forceChange=다음 로그인 때 사용자명과 비밀번호를 변경하도록 강제
|
adminUserSettings.forceChange=다음 로그인 때 사용자명과 비밀번호를 변경하도록 강제
|
||||||
adminUserSettings.submit=사용자 저장
|
adminUserSettings.submit=사용자 저장
|
||||||
adminUserSettings.changeUserRole=사용자의 역할 변경
|
adminUserSettings.changeUserRole=사용자의 역할 변경
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -805,7 +805,7 @@ pdfOrganiser.placeholder=(예: 1,3,2 또는 4-8,2,10-12 또는 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF 멀티툴
|
multiTool.title=PDF 멀티툴
|
||||||
multiTool.header=PDF 멀티툴
|
multiTool.header=PDF 멀티툴
|
||||||
multiTool.uploadPrompts=PDF를 업로드하십시오
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=PDF 뷰어
|
viewPdf.title=PDF 뷰어
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=Kan de rol van de huidige gebruiker niet downgraden
|
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.
|
downgradeCurrentUserLongMessage=Kan de rol van de huidige gebruiker niet downgraden. Huidige gebruiker wordt dus niet weergegeven.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Error
|
error=Error
|
||||||
oops=Oops!
|
oops=Oops!
|
||||||
help=Help
|
help=Help
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
|
||||||
adminUserSettings.forceChange=Forceer gebruiker om gebruikersnaam/wachtwoord te wijzigen bij inloggen
|
adminUserSettings.forceChange=Forceer gebruiker om gebruikersnaam/wachtwoord te wijzigen bij inloggen
|
||||||
adminUserSettings.submit=Gebruiker opslaan
|
adminUserSettings.submit=Gebruiker opslaan
|
||||||
adminUserSettings.changeUserRole=De rol van de gebruiker wijzigen
|
adminUserSettings.changeUserRole=De rol van de gebruiker wijzigen
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF Multitool
|
multiTool.title=PDF Multitool
|
||||||
multiTool.header=PDF Multitool
|
multiTool.header=PDF Multitool
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=PDF bekijken
|
viewPdf.title=PDF bekijken
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=Nie można obniżyć roli bieżącego użytkownika
|
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.
|
downgradeCurrentUserLongMessage=Nie można obniżyć roli bieżącego użytkownika. W związku z tym bieżący użytkownik nie zostanie wyświetlony.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Error
|
error=Error
|
||||||
oops=Oops!
|
oops=Oops!
|
||||||
help=Help
|
help=Help
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
|
||||||
adminUserSettings.forceChange=Force user to change password on login
|
adminUserSettings.forceChange=Force user to change password on login
|
||||||
adminUserSettings.submit=Save User
|
adminUserSettings.submit=Save User
|
||||||
adminUserSettings.changeUserRole=Zmień rolę użytkownika
|
adminUserSettings.changeUserRole=Zmień rolę użytkownika
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=Multi narzędzie PDF
|
multiTool.title=Multi narzędzie PDF
|
||||||
multiTool.header=Multi narzędzie PDF
|
multiTool.header=Multi narzędzie PDF
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=View PDF
|
viewPdf.title=View PDF
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=Não é possível fazer downgrade da função do usuário atual
|
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.
|
downgradeCurrentUserLongMessage=Não é possível fazer downgrade da função do usuário atual. Portanto, o usuário atual não será mostrado.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Error
|
error=Error
|
||||||
oops=Oops!
|
oops=Oops!
|
||||||
help=Help
|
help=Help
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
|
||||||
adminUserSettings.forceChange=Force user to change password on login
|
adminUserSettings.forceChange=Force user to change password on login
|
||||||
adminUserSettings.submit=Save User
|
adminUserSettings.submit=Save User
|
||||||
adminUserSettings.changeUserRole=Alterar Função de Usuário
|
adminUserSettings.changeUserRole=Alterar Função de Usuário
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=Multiferramenta de PDF
|
multiTool.title=Multiferramenta de PDF
|
||||||
multiTool.header=Multiferramenta de PDF
|
multiTool.header=Multiferramenta de PDF
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=View PDF
|
viewPdf.title=View PDF
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=Não é possível fazer downgrade da função do utilizador atual
|
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.
|
downgradeCurrentUserLongMessage=Não é possível fazer downgrade da função do utilizador atual. Portanto, o utilizador atual não será mostrado.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Error
|
error=Error
|
||||||
oops=Oops!
|
oops=Oops!
|
||||||
help=Help
|
help=Help
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
|
||||||
adminUserSettings.forceChange=Force user to change password on login
|
adminUserSettings.forceChange=Force user to change password on login
|
||||||
adminUserSettings.submit=Save User
|
adminUserSettings.submit=Save User
|
||||||
adminUserSettings.changeUserRole=Alterar usuário
|
adminUserSettings.changeUserRole=Alterar usuário
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=Multiferramenta de PDF
|
multiTool.title=Multiferramenta de PDF
|
||||||
multiTool.header=Multiferramenta de PDF
|
multiTool.header=Multiferramenta de PDF
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=View PDF
|
viewPdf.title=View PDF
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=Rolul utilizatorului curent nu poate fi retrogradat
|
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.
|
downgradeCurrentUserLongMessage=Rolul utilizatorului curent nu poate fi retrogradat. Prin urmare, utilizatorul curent nu va fi afișat.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Error
|
error=Error
|
||||||
oops=Oops!
|
oops=Oops!
|
||||||
help=Help
|
help=Help
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
|
||||||
adminUserSettings.forceChange=Force user to change password on login
|
adminUserSettings.forceChange=Force user to change password on login
|
||||||
adminUserSettings.submit=Save User
|
adminUserSettings.submit=Save User
|
||||||
adminUserSettings.changeUserRole=Schimbați rolul utilizatorului
|
adminUserSettings.changeUserRole=Schimbați rolul utilizatorului
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=Instrument PDF multiplu
|
multiTool.title=Instrument PDF multiplu
|
||||||
multiTool.header=Instrument PDF multiplu
|
multiTool.header=Instrument PDF multiplu
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=View PDF
|
viewPdf.title=View PDF
|
||||||
|
|
|
@ -57,10 +57,10 @@ usernameExistsMessage=Новое имя пользователя уже суще
|
||||||
invalidUsernameMessage=Неверное имя пользователя. Имя пользователя может содержать только буквы, цифры и следующие специальные символы @._+- или должно быть действительным адресом электронной почты.
|
invalidUsernameMessage=Неверное имя пользователя. Имя пользователя может содержать только буквы, цифры и следующие специальные символы @._+- или должно быть действительным адресом электронной почты.
|
||||||
deleteCurrentUserMessage=Невозможно удалить пользователя, вошедшего в систему.
|
deleteCurrentUserMessage=Невозможно удалить пользователя, вошедшего в систему.
|
||||||
deleteUsernameExistsMessage=Имя пользователя не существует и не может быть удалено.
|
deleteUsernameExistsMessage=Имя пользователя не существует и не может быть удалено.
|
||||||
info=Info
|
|
||||||
downgradeCurrentUserMessage=Невозможно понизить роль текущего пользователя
|
downgradeCurrentUserMessage=Невозможно понизить роль текущего пользователя
|
||||||
downgradeCurrentUserLongMessage=Невозможно понизить роль текущего пользователя. Следовательно, текущий пользователь не будет отображаться.
|
downgradeCurrentUserLongMessage=Невозможно понизить роль текущего пользователя. Следовательно, текущий пользователь не будет отображаться.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Ошибка
|
error=Ошибка
|
||||||
oops=Ой!
|
oops=Ой!
|
||||||
help=Помощь
|
help=Помощь
|
||||||
|
@ -71,7 +71,7 @@ visitGithub=Посетить репозиторий на GitHub
|
||||||
donate=Пожертвовать
|
donate=Пожертвовать
|
||||||
color=Цвет
|
color=Цвет
|
||||||
sponsor=Спонсор
|
sponsor=Спонсор
|
||||||
|
info=Info
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ adminUserSettings.internalApiUser=Внутренний пользователь
|
||||||
adminUserSettings.forceChange=Просить пользователя изменить пароль при входе в систему
|
adminUserSettings.forceChange=Просить пользователя изменить пароль при входе в систему
|
||||||
adminUserSettings.submit=Сохранить пользователя
|
adminUserSettings.submit=Сохранить пользователя
|
||||||
adminUserSettings.changeUserRole=Изменить роль пользователя
|
adminUserSettings.changeUserRole=Изменить роль пользователя
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -805,7 +805,7 @@ pdfOrganiser.placeholder=(например, 1,3,2 или 4-8,2,10-12 или 2n-1
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=Мультиинструмент PDF
|
multiTool.title=Мультиинструмент PDF
|
||||||
multiTool.header=Мультиинструмент PDF
|
multiTool.header=Мультиинструмент PDF
|
||||||
multiTool.uploadPrompts=Пожалуйста, загрузите PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Просмотреть PDF
|
viewPdf.title=Просмотреть PDF
|
||||||
|
|
|
@ -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é.
|
deleteUsernameExistsMessage=Používateľské meno neexistuje a nemôže byť zmazané.
|
||||||
downgradeCurrentUserMessage=Nie je možné znížiť rolu aktuálneho používateľa
|
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ý.
|
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
|
error=Chyba
|
||||||
oops=Ups!
|
oops=Ups!
|
||||||
help=Pomoc
|
help=Pomoc
|
||||||
|
@ -69,6 +71,7 @@ visitGithub=Navštíviť GitHub repozitár
|
||||||
donate=Darovať
|
donate=Darovať
|
||||||
color=Farba
|
color=Farba
|
||||||
sponsor=Sponzorovať
|
sponsor=Sponzorovať
|
||||||
|
info=Info
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -102,12 +105,18 @@ pipelineOptions.validateButton=Overiť
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
#############
|
#############
|
||||||
navbar.convert=Konvertovať
|
navbar.favorite=Favorites
|
||||||
navbar.security=Bezpečnosť
|
|
||||||
navbar.other=Rôzne
|
|
||||||
navbar.darkmode=Tmavý režim
|
navbar.darkmode=Tmavý režim
|
||||||
navbar.pageOps=Operácie so stránkami
|
navbar.language=Languages
|
||||||
navbar.settings=Nastavenia
|
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 #
|
# SETTINGS #
|
||||||
|
@ -176,6 +185,7 @@ adminUserSettings.internalApiUser=Interný API používateľ
|
||||||
adminUserSettings.forceChange=Donútiť používateľa zmeniť heslo pri prihlásení
|
adminUserSettings.forceChange=Donútiť používateľa zmeniť heslo pri prihlásení
|
||||||
adminUserSettings.submit=Uložiť používateľa
|
adminUserSettings.submit=Uložiť používateľa
|
||||||
adminUserSettings.changeUserRole=Zmeniť rolu používateľa
|
adminUserSettings.changeUserRole=Zmeniť rolu používateľa
|
||||||
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -742,6 +752,7 @@ extractImages.submit=Extrahovať
|
||||||
fileToPDF.title=Súbor do PDF
|
fileToPDF.title=Súbor do PDF
|
||||||
fileToPDF.header=Konvertovať akýkoľvek 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.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.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
|
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
|
||||||
multiTool.title=PDF Multi Nástroj
|
multiTool.title=PDF Multi Nástroj
|
||||||
multiTool.header=PDF Multi Nástroj
|
multiTool.header=PDF Multi Nástroj
|
||||||
multiTool.uploadPrompts=Prosím, nahrajte PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Zobraziť PDF
|
viewPdf.title=Zobraziť PDF
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=Nije moguće degradirati ulogu trenutnog korisnika
|
downgradeCurrentUserMessage=Nije moguće degradirati ulogu trenutnog korisnika
|
||||||
downgradeCurrentUserLongMessage=Nije moguće unazaditi ulogu trenutnog korisnika. Dakle, trenutni korisnik neće biti prikazan.
|
downgradeCurrentUserLongMessage=Nije moguće unazaditi ulogu trenutnog korisnika. Dakle, trenutni korisnik neće biti prikazan.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Error
|
error=Error
|
||||||
oops=Oops!
|
oops=Oops!
|
||||||
help=Help
|
help=Help
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
|
||||||
adminUserSettings.forceChange=Prisili korisnika da promeni korisničko ime/lozinku pri prijavi
|
adminUserSettings.forceChange=Prisili korisnika da promeni korisničko ime/lozinku pri prijavi
|
||||||
adminUserSettings.submit=Sačuvaj korisnika
|
adminUserSettings.submit=Sačuvaj korisnika
|
||||||
adminUserSettings.changeUserRole=Promenite ulogu korisnika
|
adminUserSettings.changeUserRole=Promenite ulogu korisnika
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF Multi Alatka
|
multiTool.title=PDF Multi Alatka
|
||||||
multiTool.header=PDF Multi Alatka
|
multiTool.header=PDF Multi Alatka
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Prikaz
|
viewPdf.title=Prikaz
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=Kan inte nedgradera nuvarande användares roll
|
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.
|
downgradeCurrentUserLongMessage=Kan inte nedgradera nuvarande användares roll. Därför kommer den aktuella användaren inte att visas.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Error
|
error=Error
|
||||||
oops=Oops!
|
oops=Oops!
|
||||||
help=Help
|
help=Help
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
|
||||||
adminUserSettings.forceChange=Force user to change password on login
|
adminUserSettings.forceChange=Force user to change password on login
|
||||||
adminUserSettings.submit=Save User
|
adminUserSettings.submit=Save User
|
||||||
adminUserSettings.changeUserRole=Ändra användarens roll
|
adminUserSettings.changeUserRole=Ändra användarens roll
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF-multiverktyg
|
multiTool.title=PDF-multiverktyg
|
||||||
multiTool.header=PDF Multi-verktyg
|
multiTool.header=PDF Multi-verktyg
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=View PDF
|
viewPdf.title=View PDF
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Şu anda oturum açmış olan kullanıcı silinemiyor.
|
||||||
deleteUsernameExistsMessage=Kullanıcı adı mevcut değil ve silinemez.
|
deleteUsernameExistsMessage=Kullanıcı adı mevcut değil ve silinemez.
|
||||||
downgradeCurrentUserMessage=Mevcut kullanıcının rolü düşürülemiyor
|
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.
|
downgradeCurrentUserLongMessage=Mevcut kullanıcının rolü düşürülemiyor. Bu nedenle, mevcut kullanıcı gösterilmeyecektir.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Hata
|
error=Hata
|
||||||
oops=Tüh!
|
oops=Tüh!
|
||||||
help=Yardım
|
help=Yardım
|
||||||
|
@ -69,8 +70,8 @@ seeDockerHub=Docker Hub'a bakın
|
||||||
visitGithub=Github Deposunu Ziyaret Edin
|
visitGithub=Github Deposunu Ziyaret Edin
|
||||||
donate=Bağış Yapın
|
donate=Bağış Yapın
|
||||||
color=Renk
|
color=Renk
|
||||||
info=Info
|
|
||||||
sponsor=Bağış
|
sponsor=Bağış
|
||||||
|
info=Info
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -184,7 +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.forceChange=Kullanıcının girişte kullanıcı adı/şifre değiştirmesini zorla
|
||||||
adminUserSettings.submit=Kullanıcıyı Kaydet
|
adminUserSettings.submit=Kullanıcıyı Kaydet
|
||||||
adminUserSettings.changeUserRole=Kullanıcı rolünü değiştir
|
adminUserSettings.changeUserRole=Kullanıcı rolünü değiştir
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(örn. 1,3,2 veya 4-8,2,10-12 veya 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF Çoklu Araç
|
multiTool.title=PDF Çoklu Araç
|
||||||
multiTool.header=PDF Çoklu Araç
|
multiTool.header=PDF Çoklu Araç
|
||||||
multiTool.uploadPrompts=Lütfen PDF Yükleyin
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=PDF Görüntüle
|
viewPdf.title=PDF Görüntüle
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=Неможливо видалити користува
|
||||||
deleteUsernameExistsMessage=Ім'я користувача не існує і не може бути видалено.
|
deleteUsernameExistsMessage=Ім'я користувача не існує і не може бути видалено.
|
||||||
downgradeCurrentUserMessage=Неможливо понизити роль поточного користувача
|
downgradeCurrentUserMessage=Неможливо понизити роль поточного користувача
|
||||||
downgradeCurrentUserLongMessage=Неможливо понизити роль поточного користувача. Отже, поточний користувач не відображатиметься.
|
downgradeCurrentUserLongMessage=Неможливо понизити роль поточного користувача. Отже, поточний користувач не відображатиметься.
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=Error
|
error=Error
|
||||||
oops=Oops!
|
oops=Oops!
|
||||||
help=Help
|
help=Help
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=Внутрішній користувач API
|
||||||
adminUserSettings.forceChange=Примусити користувача змінити пароль при вході в систему
|
adminUserSettings.forceChange=Примусити користувача змінити пароль при вході в систему
|
||||||
adminUserSettings.submit=Зберегти користувача
|
adminUserSettings.submit=Зберегти користувача
|
||||||
adminUserSettings.changeUserRole=Змінити роль користувача
|
adminUserSettings.changeUserRole=Змінити роль користувача
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(наприклад, 1,3,2 або 4-8,2,10-12 або 2n
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=Мультіінструмент PDF
|
multiTool.title=Мультіінструмент PDF
|
||||||
multiTool.header=Мультіінструмент PDF
|
multiTool.header=Мультіінструмент PDF
|
||||||
multiTool.uploadPrompts=Будь ласка, завантажте PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Переглянути PDF
|
viewPdf.title=Переглянути PDF
|
||||||
|
|
|
@ -59,7 +59,8 @@ deleteCurrentUserMessage=无法删除当前登录的用户。
|
||||||
deleteUsernameExistsMessage=用户名不存在,无法删除。
|
deleteUsernameExistsMessage=用户名不存在,无法删除。
|
||||||
downgradeCurrentUserMessage=无法降级当前用户的角色
|
downgradeCurrentUserMessage=无法降级当前用户的角色
|
||||||
downgradeCurrentUserLongMessage=无法降级当前用户的角色。因此,当前用户将不会显示。
|
downgradeCurrentUserLongMessage=无法降级当前用户的角色。因此,当前用户将不会显示。
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=错误
|
error=错误
|
||||||
oops=哎呀!
|
oops=哎呀!
|
||||||
help=帮助
|
help=帮助
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=内部API用户
|
||||||
adminUserSettings.forceChange=强制用户在登录时更改用户名/密码
|
adminUserSettings.forceChange=强制用户在登录时更改用户名/密码
|
||||||
adminUserSettings.submit=保存用户
|
adminUserSettings.submit=保存用户
|
||||||
adminUserSettings.changeUserRole=更改用户角色
|
adminUserSettings.changeUserRole=更改用户角色
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(例如 1,3,2 或 4-8,2,10-12 或 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF多功能工具
|
multiTool.title=PDF多功能工具
|
||||||
multiTool.header=PDF多功能工具
|
multiTool.header=PDF多功能工具
|
||||||
multiTool.uploadPrompts=上传PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=浏览PDF
|
viewPdf.title=浏览PDF
|
||||||
|
|
|
@ -57,10 +57,10 @@ usernameExistsMessage=新使用者名稱已存在。
|
||||||
invalidUsernameMessage=使用者名稱無效,使用者名稱只能包含字母、數字和以下特殊字元@._+- 或必須是有效的電子郵件地址。
|
invalidUsernameMessage=使用者名稱無效,使用者名稱只能包含字母、數字和以下特殊字元@._+- 或必須是有效的電子郵件地址。
|
||||||
deleteCurrentUserMessage=無法刪除目前登錄的使用者。
|
deleteCurrentUserMessage=無法刪除目前登錄的使用者。
|
||||||
deleteUsernameExistsMessage=使用者名不存在,無法刪除。
|
deleteUsernameExistsMessage=使用者名不存在,無法刪除。
|
||||||
info=Info
|
|
||||||
downgradeCurrentUserMessage=無法降級目前使用者的角色
|
downgradeCurrentUserMessage=無法降級目前使用者的角色
|
||||||
downgradeCurrentUserLongMessage=無法降級目前使用者的角色。因此,不會顯示目前的使用者。
|
downgradeCurrentUserLongMessage=無法降級目前使用者的角色。因此,不會顯示目前的使用者。
|
||||||
userAlreadyExistsMessage=OAuth2: User already exists.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
error=錯誤
|
error=錯誤
|
||||||
oops=哎呀!
|
oops=哎呀!
|
||||||
help=幫助
|
help=幫助
|
||||||
|
@ -71,6 +71,7 @@ visitGithub=訪問Github存儲庫
|
||||||
donate=捐贈
|
donate=捐贈
|
||||||
color=顏色
|
color=顏色
|
||||||
sponsor=贊助
|
sponsor=贊助
|
||||||
|
info=Info
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -184,7 +185,7 @@ adminUserSettings.internalApiUser=內部 API 使用者
|
||||||
adminUserSettings.forceChange=強制使用者在登入時修改使用者名稱/密碼
|
adminUserSettings.forceChange=強制使用者在登入時修改使用者名稱/密碼
|
||||||
adminUserSettings.submit=儲存
|
adminUserSettings.submit=儲存
|
||||||
adminUserSettings.changeUserRole=更改使用者身份
|
adminUserSettings.changeUserRole=更改使用者身份
|
||||||
adminUserSettings.authenticated=Authentication type
|
adminUserSettings.authenticated=Authenticated
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -804,7 +805,7 @@ pdfOrganiser.placeholder=(例如 1,3,2 或 4-8,2,10-12 或 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF 多工具
|
multiTool.title=PDF 多工具
|
||||||
multiTool.header=PDF 多工具
|
multiTool.header=PDF 多工具
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
multiTool.uploadPrompts=File Name
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=檢視 PDF
|
viewPdf.title=檢視 PDF
|
||||||
|
|
|
@ -8,12 +8,11 @@ security:
|
||||||
loginAttemptCount: 5 # lock user account after 5 tries
|
loginAttemptCount: 5 # lock user account after 5 tries
|
||||||
loginResetTimeMinutes : 120 # lock account for 2 hours after x attempts
|
loginResetTimeMinutes : 120 # lock account for 2 hours after x attempts
|
||||||
# oauth2:
|
# oauth2:
|
||||||
# enabled: true # set to 'true' to enable login (Note: enableLogin must also be 'true' for this to work)
|
# 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
|
# issuer: "" # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) end-point
|
||||||
# clientId: "" # Client ID from your provider
|
# clientId: "" # Client ID from your provider
|
||||||
# clientSecret: "" # Client Secret from your provider
|
# clientSecret: "" # Client Secret from your provider
|
||||||
# autoCreateUser: true # set to 'true' to allow auto-creation of non-existing users
|
# autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users
|
||||||
# autoCreateUser: true # Set to 'true' to automatically create users who do not already exist in the system
|
|
||||||
# useAsUsername: "email" # Default is 'email'; custom fields can be used as the username
|
# 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
|
# 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'
|
# provider: "google" # Set this to your OAuth provider's name, e.g., 'google' or 'keycloak'
|
||||||
|
|
4
src/main/resources/static/js/thirdParty/jquery.validate.min.js
vendored
Normal file
4
src/main/resources/static/js/thirdParty/jquery.validate.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,5 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
|
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="http://www.thymeleaf.org">
|
||||||
xmlns:th="http://www.thymeleaf.org">
|
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{account.title})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{account.title})}"></th:block>
|
||||||
</head>
|
</head>
|
||||||
|
@ -14,25 +12,13 @@
|
||||||
<br><br>
|
<br><br>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-9" id="bg-card">
|
<div class="col-md-9">
|
||||||
|
|
||||||
<!-- User Settings Title -->
|
<!-- User Settings Title -->
|
||||||
<h2 class="text-center" th:text="#{account.accountSettings}">User Settings</h2>
|
<h2 class="text-center" th:text="#{account.accountSettings}">User Settings</h2>
|
||||||
<th:block th:if="${param.messageType != null and param.messageType.size() > 0}">
|
<th:block th:if="${messageType}">
|
||||||
<div th:if="${param.messageType[0] == 'notAuthenticated'}" class="alert alert-danger">
|
<div class="alert alert-danger">
|
||||||
<span th:text="#{notAuthenticatedMessage}">Default message if not found</span>
|
<span th:text="#{${messageType}}">Default message if not found</span>
|
||||||
</div>
|
|
||||||
<div th:if="${param.messageType[0] == 'userNotFound'}" class="alert alert-danger">
|
|
||||||
<span th:text="#{userNotFoundMessage}">Default message if not found</span>
|
|
||||||
</div>
|
|
||||||
<div th:if="${param.messageType[0] == 'incorrectPassword'}" class="alert alert-danger">
|
|
||||||
<span th:text="#{incorrectPasswordMessage}">Default message if not found</span>
|
|
||||||
</div>
|
|
||||||
<div th:if="${param.messageType[0] == 'usernameExists'}" class="alert alert-danger">
|
|
||||||
<span th:text="#{usernameExistsMessage}">Default message if not found</span>
|
|
||||||
</div>
|
|
||||||
<div th:if="${param.messageType[0] == 'invalidUsername'}" class="alert alert-danger">
|
|
||||||
<span th:text="#{invalidUsernameMessage}">Default message if not found</span>
|
|
||||||
</div>
|
</div>
|
||||||
</th:block>
|
</th:block>
|
||||||
<!-- At the top of the user settings -->
|
<!-- At the top of the user settings -->
|
||||||
|
@ -43,48 +29,44 @@
|
||||||
</div>
|
</div>
|
||||||
</th:block>
|
</th:block>
|
||||||
<!-- Change Username Form -->
|
<!-- Change Username Form -->
|
||||||
|
<th:block th:if="${!oAuth2Login}">
|
||||||
<h4 th:text="#{account.changeUsername}">Change Username?</h4>
|
<h4 th:text="#{account.changeUsername}">Change Username?</h4>
|
||||||
<form th:if="${!oAuth2Login}" id="bg-card" class="mt-4 mb-4" action="api/v1/user/change-username" method="post">
|
<form id="bg-card" class="mt-4 mb-4" action="api/v1/user/change-username" method="post">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="newUsername" th:text="#{account.newUsername}">Change Username</label>
|
<label for="newUsername" th:text="#{account.newUsername}">Change Username</label>
|
||||||
<input type="text" class="form-control" name="newUsername" id="newUsername"
|
<input type="text" class="form-control" name="newUsername" id="newUsername" th:placeholder="#{account.newUsername}">
|
||||||
th:placeholder="#{account.newUsername}">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="currentPassword" th:text="#{password}">Password</label>
|
<label for="currentPassword" th:text="#{password}">Password</label>
|
||||||
<input type="password" class="form-control" name="currentPassword" id="currentPassword"
|
<input type="password" class="form-control" name="currentPassword" id="currentPassword" th:placeholder="#{password}">
|
||||||
th:placeholder="#{password}">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<button type="submit" class="btn btn-primary" th:text="#{account.changeUsername}">Change
|
<button type="submit" class="btn btn-primary" th:text="#{account.changeUsername}">Change Username</button>
|
||||||
Username</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</th:block>
|
||||||
|
|
||||||
<!-- Change Password Form -->
|
<!-- Change Password Form -->
|
||||||
<h4 th:if="${!oAuth2Login}" th:text="#{account.changePassword}">Change Password?</h4>
|
<th:block th:if="${!oAuth2Login}">
|
||||||
|
<h4 th:text="#{account.changePassword}">Change Password?</h4>
|
||||||
<form id="bg-card" class="mt-4 mb-4" action="api/v1/user/change-password" method="post">
|
<form id="bg-card" class="mt-4 mb-4" action="api/v1/user/change-password" method="post">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="currentPassword" th:text="#{account.oldPassword}">Old Password</label>
|
<label for="currentPassword" th:text="#{account.oldPassword}">Old Password</label>
|
||||||
<input type="password" class="form-control" name="currentPassword" id="currentPasswordPassword"
|
<input type="password" class="form-control" name="currentPassword" id="currentPasswordPassword" th:placeholder="#{account.oldPassword}">
|
||||||
th:placeholder="#{account.oldPassword}">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="newPassword" th:text="#{account.newPassword}">New Password</label>
|
<label for="newPassword" th:text="#{account.newPassword}">New Password</label>
|
||||||
<input type="password" class="form-control" name="newPassword" id="newPassword"
|
<input type="password" class="form-control" name="newPassword" id="newPassword" th:placeholder="#{account.newPassword}">
|
||||||
th:placeholder="#{account.newPassword}">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="confirmNewPassword" th:text="#{account.confirmNewPassword}">Confirm New Password</label>
|
<label for="confirmNewPassword" th:text="#{account.confirmNewPassword}">Confirm New Password</label>
|
||||||
<input type="password" class="form-control" name="confirmNewPassword" id="confirmNewPassword"
|
<input type="password" class="form-control" name="confirmNewPassword" id="confirmNewPassword" th:placeholder="#{account.confirmNewPassword}">
|
||||||
th:placeholder="#{account.confirmNewPassword}">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<button type="submit" class="btn btn-primary" th:text="#{account.changePassword}">Change
|
<button type="submit" class="btn btn-primary" th:text="#{account.changePassword}">Change Password</button>
|
||||||
Password</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</th:block>
|
||||||
|
|
||||||
<!-- API Key Form -->
|
<!-- API Key Form -->
|
||||||
<h4 th:text="#{account.yourApiKey}">API Key</h4>
|
<h4 th:text="#{account.yourApiKey}">API Key</h4>
|
||||||
|
@ -92,8 +74,7 @@
|
||||||
<div class="card-header" th:text="#{account.yourApiKey}"></div>
|
<div class="card-header" th:text="#{account.yourApiKey}"></div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
<input type="password" class="form-control" id="apiKey" th:placeholder="#{account.yourApiKey}"
|
<input type="password" class="form-control" id="apiKey" th:placeholder="#{account.yourApiKey}" readonly>
|
||||||
readonly>
|
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button class="btn btn-secondary" id="copyBtn" type="button" onclick="copyToClipboard()">
|
<button class="btn btn-secondary" id="copyBtn" type="button" onclick="copyToClipboard()">
|
||||||
<span class="material-symbols-rounded">
|
<span class="material-symbols-rounded">
|
||||||
|
@ -215,10 +196,8 @@
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div class="buttons-container mt-3 text-center">
|
<div class="buttons-container mt-3 text-center">
|
||||||
<button id="syncToBrowser" class="btn btn-primary btn-sm" th:text="#{account.syncToBrowser}">Sync
|
<button id="syncToBrowser" class="btn btn-primary btn-sm" th:text="#{account.syncToBrowser}">Sync Account -> Browser</button>
|
||||||
Account -> Browser</button>
|
<button id="syncToAccount" class="btn btn-secondary btn-sm" th:text="#{account.syncToAccount}">Sync Account <- Browser</button>
|
||||||
<button id="syncToAccount" class="btn btn-secondary btn-sm" th:text="#{account.syncToAccount}">Sync
|
|
||||||
Account <- Browser</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -286,8 +265,7 @@
|
||||||
</script>
|
</script>
|
||||||
<div class="mb-3 mt-4 text-center">
|
<div class="mb-3 mt-4 text-center">
|
||||||
<a href="logout" role="button" class="btn btn-danger" th:text="#{account.signOut}">Sign Out</a>
|
<a href="logout" role="button" class="btn btn-danger" th:text="#{account.signOut}">Sign Out</a>
|
||||||
<a th:if="${role == 'ROLE_ADMIN'}" class="btn btn-info" href="addUsers" role="button"
|
<a th:if="${role == 'ROLE_ADMIN'}" class="btn btn-info" href="addUsers" role="button" th:text="#{account.adminSettings}" target="_blank">Admin Settings</a>
|
||||||
th:text="#{account.adminSettings}" target="_blank">Admin Settings</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -296,5 +274,4 @@
|
||||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -16,9 +16,8 @@
|
||||||
|
|
||||||
<!-- User Settings Title -->
|
<!-- User Settings Title -->
|
||||||
<h2 class="text-center" th:text="#{adminUserSettings.header}">Admin User Control Settings</h2>
|
<h2 class="text-center" th:text="#{adminUserSettings.header}">Admin User Control Settings</h2>
|
||||||
<div th:if="${param.messageType != null and param.messageType.size() > 0 and (param.messageType[0] == 'deleteCurrentUser' or param.messageType[0] == 'deleteUsernameExists')}" class="alert alert-danger">
|
<div th:if="${deleteMessage}" class="alert alert-danger">
|
||||||
<span th:if="${param.messageType[0] == 'deleteCurrentUser'}" th:text="#{deleteCurrentUserMessage}">Cannot delete currently logged in user.</span>
|
<span th:text="#{${deleteMessage}}">Message</span>
|
||||||
<span th:if="${param.messageType[0] == 'deleteUsernameExists'}" th:text="#{deleteUsernameExistsMessage}">The username does not exist and cannot be deleted.</span>
|
|
||||||
</div>
|
</div>
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -26,7 +25,7 @@
|
||||||
<th th:text="#{username}">Username</th>
|
<th th:text="#{username}">Username</th>
|
||||||
<th th:text="#{adminUserSettings.roles}">Roles</th>
|
<th th:text="#{adminUserSettings.roles}">Roles</th>
|
||||||
<th th:text="#{adminUserSettings.actions}">Actions</th>
|
<th th:text="#{adminUserSettings.actions}">Actions</th>
|
||||||
<th th:text="#{adminUserSettings.authenticated}">Authentication type</th>
|
<th th:text="#{adminUserSettings.authenticated}">Authenticated</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -44,15 +43,15 @@
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2 th:text="#{adminUserSettings.addUser}">Add New User</h2>
|
<h2 th:text="#{adminUserSettings.addUser}">Add New User</h2>
|
||||||
<div th:if="${param.messageType != null and param.messageType.size() > 0 and (param.messageType[0] == 'usernameExists' or param.messageType[0] == 'invalidUsername')}" class="alert alert-danger">
|
<div th:if="${addMessage}" class="alert alert-danger">
|
||||||
<span th:if="${param.messageType[0] == 'usernameExists'}" th:text="#{usernameExistsMessage}">Default message if not found</span>
|
<span th:text="#{${addMessage}}">Default message if not found</span>
|
||||||
<span th:if="${param.messageType[0] == 'invalidUsername'}" th:text="#{invalidUsernameMessage}">Default message if not found</span>
|
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-outline-info" data-toggle="tooltip" data-placement="auto" th:title="#{adminUserSettings.usernameInfo}" th:text="#{help}">Help</button>
|
<button class="btn btn-outline-info" data-toggle="tooltip" data-placement="auto" th:title="#{adminUserSettings.usernameInfo}" th:text="#{help}">Help</button>
|
||||||
<form action="/api/v1/user/admin/saveUser" method="post">
|
<form id="formsaveuser" action="/api/v1/user/admin/saveUser" method="post">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="username" th:text="#{username}">Username</label>
|
<label for="username" th:text="#{username}">Username</label>
|
||||||
<input type="text" class="form-control" name="username" id="username" th:title="#{adminUserSettings.usernameInfo}" required>
|
<input type="text" class="form-control" name="username" id="username" th:title="#{adminUserSettings.usernameInfo}" required>
|
||||||
|
<span id="usernameError" style="display: none;" th:text="#{invalidUsernameMessage}">Invalid username!</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="password" th:text="#{password}">Password</label>
|
<label for="password" th:text="#{password}">Password</label>
|
||||||
|
@ -76,9 +75,8 @@
|
||||||
<hr />
|
<hr />
|
||||||
<h2 th:text="#{adminUserSettings.changeUserRole}">Change User's Role</h2>
|
<h2 th:text="#{adminUserSettings.changeUserRole}">Change User's Role</h2>
|
||||||
<button class="btn btn-outline-info" data-toggle="tooltip" data-placement="auto" th:title="#{downgradeCurrentUserLongMessage}" th:text="#{help}">Help</button>
|
<button class="btn btn-outline-info" data-toggle="tooltip" data-placement="auto" th:title="#{downgradeCurrentUserLongMessage}" th:text="#{help}">Help</button>
|
||||||
<div th:if="${param.messageType != null and param.messageType.size() > 0 and (param.messageType[0] == 'userNotFound' or param.messageType[0] == 'downgradeCurrentUser')}" class="alert alert-danger">
|
<div th:if="${changeMessage}" class="alert alert-danger">
|
||||||
<span th:if="${param.messageType[0] == 'userNotFound'}" th:text="#{userNotFoundMessage}">Username not found</span>
|
<span th:text="#{${changeMessage}}">Default message if not found</span>
|
||||||
<span th:if="${param.messageType[0] == 'downgradeCurrentUser'}" th:text="#{downgradeCurrentUserMessage}">Cannot downgrade current user's role</span>
|
|
||||||
</div>
|
</div>
|
||||||
<form action="/api/v1/user/admin/changeRole" method="post">
|
<form action="/api/v1/user/admin/changeRole" method="post">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
|
@ -104,9 +102,55 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script th:inline="javascript">
|
<script th:inline="javascript">
|
||||||
|
jQuery.validator.addMethod("usernamePattern", function(value, element) {
|
||||||
|
return this.optional(element) || /^[a-zA-Z0-9][a-zA-Z0-9@._+-]*[a-zA-Z0-9]$|^(?=.{1,64}@)[A-Za-z0-9]+(\.[A-Za-z0-9_+.-]+)*@[^-][A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(\.[A-Za-z]{2,})$/.test(value);
|
||||||
|
}, /*[[#{invalidUsernameMessage}]]*/ "Invalid username format");
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('[data-toggle="tooltip"]').tooltip()
|
$('[data-toggle="tooltip"]').tooltip();
|
||||||
})
|
|
||||||
|
$('#formsaveuser').validate({
|
||||||
|
rules: {
|
||||||
|
username: {
|
||||||
|
required: true,
|
||||||
|
usernamePattern: true
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
username: {
|
||||||
|
usernamePattern: /*[[#{invalidUsernameMessage}]]*/ "Invalid username format"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errorPlacement: function(error, element) {
|
||||||
|
if (element.attr("name") === "username") {
|
||||||
|
$("#usernameError").text(error.text()).show();
|
||||||
|
} else {
|
||||||
|
error.insertAfter(element);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
success: function(label, element) {
|
||||||
|
if ($(element).attr("name") === "username") {
|
||||||
|
$("#usernameError").hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#username').on('input', function() {
|
||||||
|
var usernameInput = $(this);
|
||||||
|
var isValid = usernameInput[0].checkValidity();
|
||||||
|
var errorSpan = $('#usernameError');
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
usernameInput.removeClass('invalid').addClass('valid');
|
||||||
|
errorSpan.hide();
|
||||||
|
} else {
|
||||||
|
usernameInput.removeClass('valid').addClass('invalid');
|
||||||
|
errorSpan.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,18 +17,9 @@
|
||||||
<!-- User Settings Title -->
|
<!-- User Settings Title -->
|
||||||
<h2 class="text-center" th:text="#{changeCreds.header}">User Settings</h2>
|
<h2 class="text-center" th:text="#{changeCreds.header}">User Settings</h2>
|
||||||
<hr>
|
<hr>
|
||||||
<th:block th:if="${param.messageType != null and param.messageType.size() > 0}">
|
<th:block th:if="${messageType}">
|
||||||
<div th:if="${param.messageType[0] == 'notAuthenticated'}" class="alert alert-danger">
|
<div class="alert alert-danger">
|
||||||
<span th:text="#{notAuthenticatedMessage}">Default message if not found</span>
|
<span th:text="#{${messageType}}">Default message if not found</span>
|
||||||
</div>
|
|
||||||
<div th:if="${param.messageType[0] == 'userNotFound'}" class="alert alert-danger">
|
|
||||||
<span th:text="#{userNotFoundMessage}">Default message if not found</span>
|
|
||||||
</div>
|
|
||||||
<div th:if="${param.messageType[0] == 'incorrectPassword'}" class="alert alert-danger">
|
|
||||||
<span th:text="#{incorrectPasswordMessage}">Default message if not found</span>
|
|
||||||
</div>
|
|
||||||
<div th:if="${param.messageType[0] == 'usernameExists'}" class="alert alert-danger">
|
|
||||||
<span th:text="#{usernameExistsMessage}">Default message if not found</span>
|
|
||||||
</div>
|
</div>
|
||||||
</th:block>
|
</th:block>
|
||||||
<!-- At the top of the user settings -->
|
<!-- At the top of the user settings -->
|
||||||
|
|
|
@ -10,12 +10,12 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<!-- Icons -->
|
<!-- Icons -->
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png?v=2">
|
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png?v=2">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png?v=2">
|
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png?v=2">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png?v=2">
|
<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png?v=2">
|
||||||
<link rel="manifest" href="/site.webmanifest?v=2">
|
<link rel="manifest" href="site.webmanifest?v=2">
|
||||||
<link rel="mask-icon" href="/safari-pinned-tab.svg?v=2" color="#ca2b2a">
|
<link rel="mask-icon" href="safari-pinned-tab.svg?v=2" color="#ca2b2a">
|
||||||
<link rel="shortcut icon" href="/favicon.ico?v=2">
|
<link rel="shortcut icon" href="favicon.ico?v=2">
|
||||||
<meta name="apple-mobile-web-app-title" content="Stirling PDF">
|
<meta name="apple-mobile-web-app-title" content="Stirling PDF">
|
||||||
<meta name="application-name" content="Stirling PDF">
|
<meta name="application-name" content="Stirling PDF">
|
||||||
<meta name="msapplication-TileColor" content="#00aba9">
|
<meta name="msapplication-TileColor" content="#00aba9">
|
||||||
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
<!-- jQuery -->
|
<!-- jQuery -->
|
||||||
<script src="js/thirdParty/jquery.min.js"></script>
|
<script src="js/thirdParty/jquery.min.js"></script>
|
||||||
|
<script src="js/thirdParty/jquery.validate.min.js"></script>
|
||||||
<script src="js/thirdParty/jszip.min.js"></script>
|
<script src="js/thirdParty/jszip.min.js"></script>
|
||||||
|
|
||||||
<!-- Bootstrap -->
|
<!-- Bootstrap -->
|
||||||
|
@ -72,7 +73,6 @@
|
||||||
<script src="js/darkmode.js"></script>
|
<script src="js/darkmode.js"></script>
|
||||||
</th:block>
|
</th:block>
|
||||||
|
|
||||||
|
|
||||||
<th:block th:fragment="game">
|
<th:block th:fragment="game">
|
||||||
<dialog id="game-container-wrapper" class="game-container-wrapper" data-bs-modal>
|
<dialog id="game-container-wrapper" class="game-container-wrapper" data-bs-modal>
|
||||||
<script th:inline="javascript">
|
<script th:inline="javascript">
|
||||||
|
@ -144,8 +144,7 @@
|
||||||
</dialog>
|
</dialog>
|
||||||
</th:block>
|
</th:block>
|
||||||
|
|
||||||
<th:block th:fragment="fileSelector(name, multiple)"
|
<th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: true, notRequired=${notRequired} ?: false">
|
||||||
th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: true, notRequired=${notRequired} ?: false">
|
|
||||||
<script th:inline="javascript">
|
<script th:inline="javascript">
|
||||||
const pdfPasswordPrompt = /*[[#{error.pdfPassword}]]*/ '';
|
const pdfPasswordPrompt = /*[[#{error.pdfPassword}]]*/ '';
|
||||||
const multiple = [[${multiple}]] || false;
|
const multiple = [[${multiple}]] || false;
|
||||||
|
@ -153,19 +152,16 @@
|
||||||
</script>
|
</script>
|
||||||
<script src="js/downloader.js"></script>
|
<script src="js/downloader.js"></script>
|
||||||
|
|
||||||
<div class="custom-file-chooser"
|
<div class="custom-file-chooser" th:attr="data-bs-unique-id=${name}, data-bs-element-id=${name+'-input'}, data-bs-files-selected=#{filesSelected}, data-bs-pdf-prompt=#{pdfPrompt}">
|
||||||
th:attr="data-bs-unique-id=${name}, data-bs-element-id=${name+'-input'}, data-bs-files-selected=#{filesSelected}, data-bs-pdf-prompt=#{pdfPrompt}">
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<input type="file" class="form-control" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" multiple
|
<input type="file" class="form-control" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" multiple th:required="${notRequired} ? null : 'required'">
|
||||||
th:required="${notRequired} ? null : 'required'">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="selected-files"></div>
|
<div class="selected-files"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="progressBarContainer" style="display: none; position: relative;">
|
<div class="progressBarContainer" style="display: none; position: relative;">
|
||||||
<div class="progress" style="height: 1rem;">
|
<div class="progress" style="height: 1rem;">
|
||||||
<div class="progressBar progress-bar progress-bar-striped progress-bar-animated bg-success" role="progressbar"
|
<div class="progressBar progress-bar progress-bar-striped progress-bar-animated bg-success" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;">
|
||||||
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;">
|
|
||||||
<span class="visually-hidden">Loading...</span>
|
<span class="visually-hidden">Loading...</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -112,41 +112,32 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div th:if="${oAuth2Enabled}">
|
|
||||||
<a href="oauth2/authorization/oidc" class="w-100 btn btn-lg btn-primary" th:text="#{login.ssoSignIn}">Login Via SSO</a>
|
|
||||||
<br>
|
|
||||||
<div th:if="${error}" class="alert alert-danger text-danger text-center">
|
|
||||||
<!-- oauth2AutoCreateDisabled -->
|
|
||||||
<div th:if="${error == 'oauth2AutoCreateDisabled'}" th:text="#{login.oauth2AutoCreateDisabled}">OAuth2: Auto-Create User Disabled.</div>
|
|
||||||
<!-- oauth2AuthenticationError -->
|
|
||||||
<div th:if="${error == 'oauth2AuthenticationError'}" th:text="#{userAlreadyExistsMessage}">OAuth2: User already exists.</div>
|
|
||||||
<!-- invalidUsername -->
|
|
||||||
<div th:if="${error == 'invalidUsername'}" th:text="#{invalidUsernameMessage}">OAUTH2: Invalid username.</div>
|
|
||||||
<!-- badcredentials -->
|
|
||||||
<div th:if="${error == 'badcredentials'}" th:text="#{login.invalid}">Invalid username or password.</div>
|
|
||||||
<!-- locked -->
|
|
||||||
<div th:if="${error == 'locked'}" th:text="#{login.locked}">Your account has been locked. </div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
</div>
|
|
||||||
<div class="text-danger text-center">
|
|
||||||
<div th:if="${logoutMessage}" class="alert alert-success" th:text="${logoutMessage}"></div>
|
|
||||||
<div th:if="${param.messageType != null and param.messageType.size() > 0 and param.messageType[0] == 'credsUpdated'}" class="alert alert-success">
|
|
||||||
<span th:text="#{changedCredsMessage}">Default message if not found</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<form th:action="@{login}" method="post">
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<img class="mb-4" src="favicon.svg?v=2" alt="" width="144" height="144">
|
<img class="mb-4" src="favicon.svg?v=2" alt="favicon" width="144" height="144">
|
||||||
|
|
||||||
<h1 class="h1 mb-3 fw-normal" th:text="${@appName}">Stirling-PDF</h1>
|
<h1 class="h1 mb-3 fw-normal" th:text="${@appName}">Stirling-PDF</h1>
|
||||||
<div th:if="${oAuth2Enabled}">
|
<div th:if="${oAuth2Enabled}">
|
||||||
<a href="oauth2/authorization/oidc" class="w-100 btn btn-lg btn-primary" th:text="#{login.ssoSignIn}">Login Via SSO</a>
|
<a href="oauth2/authorization/oidc" class="w-100 btn btn-lg btn-primary" th:text="#{login.ssoSignIn}">Login Via SSO</a>
|
||||||
<div class="text-danger text-center">
|
<br>
|
||||||
<div th:if="${error == 'oauth2AutoCreateDisabled'}" th:text="#{login.oauth2AutoCreateDisabled}">OAUTH2 Auto-Create User Disabled.</div>
|
<br>
|
||||||
|
<div th:if="${erroroauth}" class="alert alert-danger text-center">
|
||||||
|
<div th:if="${erroroauth}" th:text="#{${erroroauth}}">OAuth2: Error Message</div>
|
||||||
</div>
|
</div>
|
||||||
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div th:if="${error}" class="alert alert-danger text-danger text-center">
|
||||||
|
<div th:if="${error}" th:text="#{${error}}">OAuth2: Error Message</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-danger text-center">
|
||||||
|
<div th:if="${logoutMessage}" class="alert alert-success" th:text="${logoutMessage}"></div>
|
||||||
|
<div th:if="${messageType}" class="alert alert-success">
|
||||||
|
<span th:text="#{${messageType}}">Default message if not found</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<form th:action="@{login}" method="post">
|
||||||
|
<h2 class="h5 mb-3 fw-normal" th:text="#{login.signinTitle}">Please sign in</h2>
|
||||||
<div class="form-floating">
|
<div class="form-floating">
|
||||||
<input type="text" class="form-control" id="username" name="username" placeholder="admin">
|
<input type="text" class="form-control" id="username" name="username" placeholder="admin">
|
||||||
<label for="username" th:text="#{username}">Username</label>
|
<label for="username" th:text="#{username}">Username</label>
|
||||||
|
|
Loading…
Reference in a new issue