mirror of
				https://github.com/Frooodle/Stirling-PDF.git
				synced 2025-11-01 01:21:18 +01:00 
			
		
		
		
	security
This commit is contained in:
		
							parent
							
								
									ad5f057733
								
							
						
					
					
						commit
						e791fee38b
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -116,7 +116,7 @@ watchedFolders/
 | 
				
			|||||||
*.zip
 | 
					*.zip
 | 
				
			||||||
*.tar.gz
 | 
					*.tar.gz
 | 
				
			||||||
*.rar
 | 
					*.rar
 | 
				
			||||||
 | 
					*.db
 | 
				
			||||||
/build
 | 
					/build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/.vscode
 | 
					/.vscode
 | 
				
			||||||
@ -70,6 +70,8 @@ dependencies {
 | 
				
			|||||||
    implementation group: 'com.google.zxing', name: 'core', version: '3.5.1'
 | 
					    implementation group: 'com.google.zxing', name: 'core', version: '3.5.1'
 | 
				
			||||||
    // https://mvnrepository.com/artifact/org.commonmark/commonmark
 | 
					    // https://mvnrepository.com/artifact/org.commonmark/commonmark
 | 
				
			||||||
	implementation 'org.commonmark:commonmark:0.21.0'
 | 
						implementation 'org.commonmark:commonmark:0.21.0'
 | 
				
			||||||
 | 
					    // https://mvnrepository.com/artifact/com.github.vladimir-bukhtoyarov/bucket4j-core
 | 
				
			||||||
 | 
						implementation 'com.github.vladimir-bukhtoyarov:bucket4j-core:7.6.0'
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    developmentOnly("org.springframework.boot:spring-boot-devtools")
 | 
					    developmentOnly("org.springframework.boot:spring-boot-devtools")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -14,9 +14,10 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import jakarta.annotation.PostConstruct;
 | 
					import jakarta.annotation.PostConstruct;
 | 
				
			||||||
import stirling.software.SPDF.utils.GeneralUtils;
 | 
					import stirling.software.SPDF.utils.GeneralUtils;
 | 
				
			||||||
 | 
					import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
 | 
				
			||||||
@SpringBootApplication
 | 
					@SpringBootApplication
 | 
				
			||||||
@EnableWebSecurity(debug = true)
 | 
					@EnableWebSecurity()
 | 
				
			||||||
 | 
					@EnableGlobalMethodSecurity(prePostEnabled = true)
 | 
				
			||||||
//@EnableScheduling
 | 
					//@EnableScheduling
 | 
				
			||||||
public class SPdfApplication {
 | 
					public class SPdfApplication {
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
package stirling.software.SPDF.config;
 | 
					package stirling.software.SPDF.config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.time.Duration;
 | 
				
			||||||
import java.util.Locale;
 | 
					import java.util.Locale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.springframework.context.annotation.Bean;
 | 
					import org.springframework.context.annotation.Bean;
 | 
				
			||||||
@ -10,9 +11,22 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 | 
				
			|||||||
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
 | 
					import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
 | 
				
			||||||
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
 | 
					import org.springframework.web.servlet.i18n.SessionLocaleResolver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import io.github.bucket4j.Bandwidth;
 | 
				
			||||||
 | 
					import io.github.bucket4j.Bucket;
 | 
				
			||||||
 | 
					import io.github.bucket4j.Bucket4j;
 | 
				
			||||||
 | 
					import io.github.bucket4j.Refill;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Configuration
 | 
					@Configuration
 | 
				
			||||||
public class Beans implements WebMvcConfigurer {
 | 
					public class Beans implements WebMvcConfigurer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Bean
 | 
				
			||||||
 | 
						public Bucket createRateLimitBucket() {
 | 
				
			||||||
 | 
						    Refill refill = Refill.of(1000, Duration.ofDays(1));
 | 
				
			||||||
 | 
						    Bandwidth limit = Bandwidth.classic(1000, refill).withInitialTokens(1000);
 | 
				
			||||||
 | 
						    return Bucket4j.builder().addLimit(limit).build();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void addInterceptors(InterceptorRegistry registry) {
 | 
					    public void addInterceptors(InterceptorRegistry registry) {
 | 
				
			||||||
        registry.addInterceptor(localeChangeInterceptor());
 | 
					        registry.addInterceptor(localeChangeInterceptor());
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,20 @@
 | 
				
			|||||||
package stirling.software.SPDF.controller.api;
 | 
					package stirling.software.SPDF.controller.api;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.security.Principal;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
					import org.springframework.beans.factory.annotation.Autowired;
 | 
				
			||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 | 
					import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 | 
				
			||||||
 | 
					import org.springframework.security.access.prepost.PreAuthorize;
 | 
				
			||||||
import org.springframework.stereotype.Controller;
 | 
					import org.springframework.stereotype.Controller;
 | 
				
			||||||
import org.springframework.ui.Model;
 | 
					import org.springframework.ui.Model;
 | 
				
			||||||
 | 
					import org.springframework.web.bind.annotation.GetMapping;
 | 
				
			||||||
 | 
					import org.springframework.web.bind.annotation.PathVariable;
 | 
				
			||||||
import org.springframework.web.bind.annotation.PostMapping;
 | 
					import org.springframework.web.bind.annotation.PostMapping;
 | 
				
			||||||
import org.springframework.web.bind.annotation.RequestParam;
 | 
					import org.springframework.web.bind.annotation.RequestParam;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import jakarta.servlet.http.HttpServletRequest;
 | 
				
			||||||
import stirling.software.SPDF.config.security.UserService;
 | 
					import stirling.software.SPDF.config.security.UserService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Controller
 | 
					@Controller
 | 
				
			||||||
@ -25,4 +33,41 @@ public class UserController {
 | 
				
			|||||||
        userService.saveUser(username, password);
 | 
					        userService.saveUser(username, password);
 | 
				
			||||||
        return "redirect:/login?registered=true";
 | 
					        return "redirect:/login?registered=true";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @PostMapping("/updateUserSettings")
 | 
				
			||||||
 | 
						public String updateUserSettings(HttpServletRequest request, Principal principal) {
 | 
				
			||||||
 | 
						    Map<String, String[]> paramMap = request.getParameterMap();
 | 
				
			||||||
 | 
						    Map<String, String> updates = new HashMap<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    System.out.println("Received parameter map: " + paramMap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    for (Map.Entry<String, String[]> entry : paramMap.entrySet()) {
 | 
				
			||||||
 | 
						        updates.put(entry.getKey(), entry.getValue()[0]);
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    System.out.println("Processed updates: " + updates);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    // Assuming you have a method in userService to update the settings for a user
 | 
				
			||||||
 | 
						    userService.updateUserSettings(principal.getName(), updates);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    return "redirect:/account";  // Redirect to a page of your choice after updating
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @PreAuthorize("hasRole('ROLE_ADMIN')")
 | 
				
			||||||
 | 
					    @PostMapping("/admin/saveUser")
 | 
				
			||||||
 | 
					    public String saveUser(@RequestParam String username, @RequestParam String password, @RequestParam String role) {
 | 
				
			||||||
 | 
					        userService.saveUser(username, password, role);
 | 
				
			||||||
 | 
					        return "redirect:/addUsers";  // Redirect to account page after adding the user
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @PreAuthorize("hasRole('ROLE_ADMIN')")
 | 
				
			||||||
 | 
					    @GetMapping("/admin/deleteUser/{username}")
 | 
				
			||||||
 | 
					    public String deleteUser(@PathVariable String username) {
 | 
				
			||||||
 | 
					    	userService.deleteUser(username); 
 | 
				
			||||||
 | 
					        return "redirect:/addUsers";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -4,11 +4,13 @@ import java.nio.charset.StandardCharsets;
 | 
				
			|||||||
import java.nio.file.Files;
 | 
					import java.nio.file.Files;
 | 
				
			||||||
import java.nio.file.Path;
 | 
					import java.nio.file.Path;
 | 
				
			||||||
import java.nio.file.Paths;
 | 
					import java.nio.file.Paths;
 | 
				
			||||||
 | 
					import java.security.Principal;
 | 
				
			||||||
import java.util.ArrayList;
 | 
					import java.util.ArrayList;
 | 
				
			||||||
import java.util.Arrays;
 | 
					import java.util.Arrays;
 | 
				
			||||||
import java.util.HashMap;
 | 
					import java.util.HashMap;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.Optional;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
import java.util.stream.Stream;
 | 
					import java.util.stream.Stream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -16,16 +18,23 @@ import org.springframework.beans.factory.annotation.Autowired;
 | 
				
			|||||||
import org.springframework.core.io.Resource;
 | 
					import org.springframework.core.io.Resource;
 | 
				
			||||||
import org.springframework.core.io.ResourceLoader;
 | 
					import org.springframework.core.io.ResourceLoader;
 | 
				
			||||||
import org.springframework.core.io.support.ResourcePatternUtils;
 | 
					import org.springframework.core.io.support.ResourcePatternUtils;
 | 
				
			||||||
 | 
					import org.springframework.security.access.prepost.PreAuthorize;
 | 
				
			||||||
import org.springframework.security.core.Authentication;
 | 
					import org.springframework.security.core.Authentication;
 | 
				
			||||||
 | 
					import org.springframework.security.core.userdetails.UserDetails;
 | 
				
			||||||
import org.springframework.stereotype.Controller;
 | 
					import org.springframework.stereotype.Controller;
 | 
				
			||||||
import org.springframework.ui.Model;
 | 
					import org.springframework.ui.Model;
 | 
				
			||||||
import org.springframework.web.bind.annotation.GetMapping;
 | 
					import org.springframework.web.bind.annotation.GetMapping;
 | 
				
			||||||
 | 
					import org.springframework.web.bind.annotation.PostMapping;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.core.JsonProcessingException;
 | 
				
			||||||
import com.fasterxml.jackson.databind.ObjectMapper;
 | 
					import com.fasterxml.jackson.databind.ObjectMapper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import io.swagger.v3.oas.annotations.Hidden;
 | 
					import io.swagger.v3.oas.annotations.Hidden;
 | 
				
			||||||
import io.swagger.v3.oas.annotations.tags.Tag;
 | 
					import io.swagger.v3.oas.annotations.tags.Tag;
 | 
				
			||||||
import jakarta.servlet.http.HttpServletRequest;
 | 
					import jakarta.servlet.http.HttpServletRequest;
 | 
				
			||||||
 | 
					import stirling.software.SPDF.config.security.UserService;
 | 
				
			||||||
 | 
					import stirling.software.SPDF.model.User;
 | 
				
			||||||
 | 
					import stirling.software.SPDF.repository.UserRepository;
 | 
				
			||||||
@Controller
 | 
					@Controller
 | 
				
			||||||
@Tag(name = "General", description = "General APIs")
 | 
					@Tag(name = "General", description = "General APIs")
 | 
				
			||||||
public class GeneralWebController {
 | 
					public class GeneralWebController {
 | 
				
			||||||
@ -48,6 +57,68 @@ public class GeneralWebController {
 | 
				
			|||||||
	    
 | 
						    
 | 
				
			||||||
	    return "login";
 | 
						    return "login";
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						@Autowired
 | 
				
			||||||
 | 
						private UserRepository userRepository;  // Assuming you have a repository for user operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Autowired
 | 
				
			||||||
 | 
						private UserService userService;  // Assuming you have a repository for user operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@PreAuthorize("hasRole('ROLE_ADMIN')")
 | 
				
			||||||
 | 
						@GetMapping("/addUsers")
 | 
				
			||||||
 | 
						public String showAddUserForm(Model model) {
 | 
				
			||||||
 | 
						    List<User> allUsers = userRepository.findAll();
 | 
				
			||||||
 | 
						    model.addAttribute("users", allUsers);
 | 
				
			||||||
 | 
						    return "addUsers";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						@GetMapping("/account")
 | 
				
			||||||
 | 
						public String account(HttpServletRequest request, Model model, Authentication authentication) {
 | 
				
			||||||
 | 
							if (authentication == null || !authentication.isAuthenticated()) {
 | 
				
			||||||
 | 
					            return "redirect:/";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
							if (authentication != null && authentication.isAuthenticated()) {
 | 
				
			||||||
 | 
						        Object principal = authentication.getPrincipal();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						        if (principal instanceof UserDetails) {
 | 
				
			||||||
 | 
						            // Cast the principal object to UserDetails
 | 
				
			||||||
 | 
						            UserDetails userDetails = (UserDetails) principal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						            // Retrieve username and other attributes
 | 
				
			||||||
 | 
						            String username = userDetails.getUsername();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						            // Fetch user details from the database
 | 
				
			||||||
 | 
						            Optional<User> user = userRepository.findByUsername(username);  // Assuming findByUsername method exists
 | 
				
			||||||
 | 
						            if (!user.isPresent()) {
 | 
				
			||||||
 | 
						                // Handle error appropriately
 | 
				
			||||||
 | 
						                return "redirect:/error";  // Example redirection in case of error
 | 
				
			||||||
 | 
						            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						            // Convert settings map to JSON string
 | 
				
			||||||
 | 
						            ObjectMapper objectMapper = new ObjectMapper();
 | 
				
			||||||
 | 
						            String settingsJson;
 | 
				
			||||||
 | 
						            try {
 | 
				
			||||||
 | 
						                settingsJson = objectMapper.writeValueAsString(user.get().getSettings());
 | 
				
			||||||
 | 
						            } catch (JsonProcessingException e) {
 | 
				
			||||||
 | 
						                // Handle JSON conversion error
 | 
				
			||||||
 | 
						                e.printStackTrace();
 | 
				
			||||||
 | 
						                return "redirect:/error";  // Example redirection in case of error
 | 
				
			||||||
 | 
						            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						            // Add attributes to the model
 | 
				
			||||||
 | 
						            model.addAttribute("username", username);
 | 
				
			||||||
 | 
						            model.addAttribute("role", user.get().getRolesAsString());
 | 
				
			||||||
 | 
						            model.addAttribute("settings", settingsJson);
 | 
				
			||||||
 | 
						        }
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
						        	return "redirect:/";
 | 
				
			||||||
 | 
						        }
 | 
				
			||||||
 | 
						    return "account";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						 
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	@GetMapping("/pipeline")
 | 
						@GetMapping("/pipeline")
 | 
				
			||||||
	@Hidden
 | 
						@Hidden
 | 
				
			||||||
 | 
				
			|||||||
@ -13,6 +13,18 @@ import jakarta.persistence.Table;
 | 
				
			|||||||
@Table(name = "authorities")
 | 
					@Table(name = "authorities")
 | 
				
			||||||
public class Authority {
 | 
					public class Authority {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public Authority() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public Authority(String authority, User user) {
 | 
				
			||||||
 | 
						    this.authority = authority;
 | 
				
			||||||
 | 
						    this.user = user;
 | 
				
			||||||
 | 
						    user.getAuthorities().add(this);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	@Id
 | 
						@Id
 | 
				
			||||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
					    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
				
			||||||
    private Long id;
 | 
					    private Long id;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										11
									
								
								src/main/java/stirling/software/SPDF/model/Role.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/main/java/stirling/software/SPDF/model/Role.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					package stirling.software.SPDF.model;
 | 
				
			||||||
 | 
					public final class Role {
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						    public static final String ADMIN = "ROLE_ADMIN";
 | 
				
			||||||
 | 
						    public static final String USER = "ROLE_USER";
 | 
				
			||||||
 | 
						    public static final String LIMITED_API_USER = "ROLE_LIMITED_API_USER";
 | 
				
			||||||
 | 
						    public static final String WEB_ONLY_USER = "ROLE_WEB_ONLY_USER";
 | 
				
			||||||
 | 
						    
 | 
				
			||||||
 | 
						    
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,15 +1,22 @@
 | 
				
			|||||||
package stirling.software.SPDF.model;
 | 
					package stirling.software.SPDF.model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import jakarta.persistence.CascadeType;
 | 
					import jakarta.persistence.CascadeType;
 | 
				
			||||||
 | 
					import jakarta.persistence.CollectionTable;
 | 
				
			||||||
import jakarta.persistence.Column;
 | 
					import jakarta.persistence.Column;
 | 
				
			||||||
 | 
					import jakarta.persistence.ElementCollection;
 | 
				
			||||||
import jakarta.persistence.Entity;
 | 
					import jakarta.persistence.Entity;
 | 
				
			||||||
import jakarta.persistence.FetchType;
 | 
					import jakarta.persistence.FetchType;
 | 
				
			||||||
import jakarta.persistence.Id;
 | 
					import jakarta.persistence.Id;
 | 
				
			||||||
 | 
					import jakarta.persistence.MapKeyColumn;
 | 
				
			||||||
import jakarta.persistence.OneToMany;
 | 
					import jakarta.persistence.OneToMany;
 | 
				
			||||||
import jakarta.persistence.Table;
 | 
					import jakarta.persistence.Table;
 | 
				
			||||||
 | 
					import jakarta.persistence.JoinColumn;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.HashSet;
 | 
				
			||||||
@Entity
 | 
					@Entity
 | 
				
			||||||
@Table(name = "users")
 | 
					@Table(name = "users")
 | 
				
			||||||
public class User {
 | 
					public class User {
 | 
				
			||||||
@ -25,7 +32,23 @@ public class User {
 | 
				
			|||||||
    private boolean enabled;
 | 
					    private boolean enabled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user")
 | 
					    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user")
 | 
				
			||||||
    private Set<Authority> authorities;
 | 
					    private Set<Authority> authorities = new HashSet<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @ElementCollection
 | 
				
			||||||
 | 
					    @MapKeyColumn(name = "setting_key")
 | 
				
			||||||
 | 
					    @Column(name = "setting_value")
 | 
				
			||||||
 | 
					    @CollectionTable(name = "user_settings", joinColumns = @JoinColumn(name = "username"))
 | 
				
			||||||
 | 
					    private Map<String, String> settings = new HashMap<>();  // Key-value pairs of settings.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
						public Map<String, String> getSettings() {
 | 
				
			||||||
 | 
							return settings;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void setSettings(Map<String, String> settings) {
 | 
				
			||||||
 | 
							this.settings = settings;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public String getUsername() {
 | 
						public String getUsername() {
 | 
				
			||||||
		return username;
 | 
							return username;
 | 
				
			||||||
@ -59,4 +82,18 @@ public class User {
 | 
				
			|||||||
		this.authorities = authorities;
 | 
							this.authorities = authorities;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
 | 
						public void addAuthorities(Set<Authority> authorities) {
 | 
				
			||||||
 | 
							this.authorities.addAll(authorities);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						public void addAuthority(Authority authorities) {
 | 
				
			||||||
 | 
							this.authorities.add(authorities);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public String getRolesAsString() {
 | 
				
			||||||
 | 
						    return this.authorities.stream()
 | 
				
			||||||
 | 
						                           .map(Authority::getAuthority)
 | 
				
			||||||
 | 
						                           .collect(Collectors.joining(", "));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -16,9 +16,9 @@ server.error.include-stacktrace=always
 | 
				
			|||||||
server.error.include-exception=true
 | 
					server.error.include-exception=true
 | 
				
			||||||
server.error.include-message=always
 | 
					server.error.include-message=always
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logging.level.org.springframework.web=DEBUG
 | 
					#logging.level.org.springframework.web=DEBUG
 | 
				
			||||||
logging.level.org.springframework=DEBUG
 | 
					#logging.level.org.springframework=DEBUG
 | 
				
			||||||
logging.level.org.springframework.security=DEBUG
 | 
					#logging.level.org.springframework.security=DEBUG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
login.enabled=true
 | 
					login.enabled=true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -55,10 +55,12 @@ settings.downloadOption.2=Open in new window
 | 
				
			|||||||
settings.downloadOption.3=Download file
 | 
					settings.downloadOption.3=Download file
 | 
				
			||||||
settings.zipThreshold=Zip files when the number of downloaded files exceeds
 | 
					settings.zipThreshold=Zip files when the number of downloaded files exceeds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					settings.accountSettings=Account Settings
 | 
				
			||||||
 | 
					settings.adminSettings=Admin - View/Add Users
 | 
				
			||||||
settings.userSettings=User Settings
 | 
					settings.userSettings=User Settings
 | 
				
			||||||
settings.changeUsername=New Username
 | 
					settings.changeUsername=New Username
 | 
				
			||||||
settings.changeUsernameButton=Change Username
 | 
					settings.changeUsernameButton=Change Username
 | 
				
			||||||
settings.password=Password
 | 
					settings.password=Confirmation Password
 | 
				
			||||||
settings.oldPassword=Old password
 | 
					settings.oldPassword=Old password
 | 
				
			||||||
settings.newPassword=New Password
 | 
					settings.newPassword=New Password
 | 
				
			||||||
settings.changePasswordButton=Change Password
 | 
					settings.changePasswordButton=Change Password
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										194
									
								
								src/main/resources/templates/account.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								src/main/resources/templates/account.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,194 @@
 | 
				
			|||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<th:block th:insert="~{fragments/common :: head(title=#{settings.userSettings})}"></th:block>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<body>
 | 
				
			||||||
 | 
					    <th:block th:insert="~{fragments/common :: game}"></th:block>
 | 
				
			||||||
 | 
					    <div id="page-container">
 | 
				
			||||||
 | 
					        <div id="content-wrap">
 | 
				
			||||||
 | 
					            <div th:insert="~{fragments/navbar.html :: navbar}"></div>
 | 
				
			||||||
 | 
					            <br> <br>
 | 
				
			||||||
 | 
					            <div class="container">
 | 
				
			||||||
 | 
					                <div class="row justify-content-center">
 | 
				
			||||||
 | 
					                    <div class="col-md-8">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        <!-- User Settings Title -->
 | 
				
			||||||
 | 
					                        <h2 class="text-center" th:text="#{settings.accountSettings}">User Settings</h2>
 | 
				
			||||||
 | 
					                        <hr>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											<!-- At the top of the user settings -->
 | 
				
			||||||
 | 
											<h3 class="text-center">Welcome <span th:text="${username}">User</span>!</h3>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        <!-- Change Username Form -->
 | 
				
			||||||
 | 
					                        <h4>Change username?</h4>
 | 
				
			||||||
 | 
					                        <form action="/change-username" method="post">
 | 
				
			||||||
 | 
					                            <div class="form-group">
 | 
				
			||||||
 | 
					                                <label for="newUsername" th:text="#{settings.changeUsername}">Change Username</label>
 | 
				
			||||||
 | 
					                                <input type="text" class="form-control" name="newUsername" id="newUsername" placeholder="New Username">
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                            <div class="form-group">
 | 
				
			||||||
 | 
					                                <label for="password" th:text="#{settings.password}">Password</label>
 | 
				
			||||||
 | 
					                                <input type="password" class="form-control" name="password" id="password" placeholder="Password">
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                            <div class="form-group">
 | 
				
			||||||
 | 
					                                <button type="submit" class="btn btn-primary" th:text="#{settings.changeUsernameButton}">Change Username</button>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </form>
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        <hr> <!-- Separator Line -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        <!-- Change Password Form -->
 | 
				
			||||||
 | 
					                        <h4>Change Password?</h4>
 | 
				
			||||||
 | 
					                        <form action="/change-password" method="post">
 | 
				
			||||||
 | 
					                            <div class="form-group">
 | 
				
			||||||
 | 
					                                <label for="oldPassword" th:text="#{settings.oldPassword}">Old Password</label>
 | 
				
			||||||
 | 
					                                <input type="password" class="form-control" name="oldPassword" id="oldPassword" placeholder="Old Password">
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                            <div class="form-group">
 | 
				
			||||||
 | 
					                                <label for="newPassword" th:text="#{settings.newPassword}">New Password</label>
 | 
				
			||||||
 | 
					                                <input type="password" class="form-control" name="newPassword" id="newPassword" placeholder="New Password">
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                            <div class="form-group">
 | 
				
			||||||
 | 
					                                <label for="confirmNewPassword" th:text="#{settings.confirmNewPassword}">Confirm New Password</label>
 | 
				
			||||||
 | 
					                                <input type="password" class="form-control" name="confirmNewPassword" id="confirmNewPassword" placeholder="Confirm New Password">
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                            <div class="form-group">
 | 
				
			||||||
 | 
					                                <button type="submit" class="btn btn-primary" th:text="#{settings.changePasswordButton}">Change Password</button>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        <hr> <!-- Separator Line -->
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        <h4>Sync browser settings with Account</h4>
 | 
				
			||||||
 | 
					                        <div class="container mt-4">
 | 
				
			||||||
 | 
					    <h3>Settings Comparison:</h3>
 | 
				
			||||||
 | 
					    <table id="settingsTable" class="table table-bordered table-sm table-striped">
 | 
				
			||||||
 | 
					        <thead>
 | 
				
			||||||
 | 
					            <tr>
 | 
				
			||||||
 | 
					                <th>Property</th>
 | 
				
			||||||
 | 
					                <th>Account Setting</th>
 | 
				
			||||||
 | 
					                <th>Web Browser Setting</th>
 | 
				
			||||||
 | 
					            </tr>
 | 
				
			||||||
 | 
					        </thead>
 | 
				
			||||||
 | 
					        <tbody>
 | 
				
			||||||
 | 
					            <!-- This will be dynamically populated by JavaScript -->
 | 
				
			||||||
 | 
					        </tbody>
 | 
				
			||||||
 | 
					    </table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="buttons-container mt-3 text-center">
 | 
				
			||||||
 | 
					        <button id="syncToBrowser" class="btn btn-primary btn-sm">Sync Account to Web Browser</button>
 | 
				
			||||||
 | 
					        <button id="syncToAccount" class="btn btn-secondary btn-sm">Sync Web Browser to Account</button>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    <a th:if="${role == 'ROLE_ADMIN'}" href="addUsers" target="_blank">
 | 
				
			||||||
 | 
					        <button type="button" class="btn btn-sm btn-outline-primary" th:text="#{settings.adminSettings}">Admin Settings</button>
 | 
				
			||||||
 | 
					    </a>    
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style>
 | 
				
			||||||
 | 
					    .container {
 | 
				
			||||||
 | 
					        width: 100%;
 | 
				
			||||||
 | 
					        max-width: 800px;
 | 
				
			||||||
 | 
					        margin: 0 auto;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .buttons-container {
 | 
				
			||||||
 | 
					        margin-top: 20px;
 | 
				
			||||||
 | 
					        text-align: center;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												<script th:inline="javascript">
 | 
				
			||||||
 | 
												document.addEventListener("DOMContentLoaded", function() {
 | 
				
			||||||
 | 
												    const settingsTableBody = document.querySelector("#settingsTable tbody");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												    /*<![CDATA[*/
 | 
				
			||||||
 | 
												    var accountSettingsString = /*[[${settings}]]*/ {};
 | 
				
			||||||
 | 
												    /*]]>*/
 | 
				
			||||||
 | 
												    var accountSettings = JSON.parse(accountSettingsString);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												    let allKeys = new Set([...Object.keys(accountSettings), ...Object.keys(localStorage)]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												    allKeys.forEach(key => {
 | 
				
			||||||
 | 
												        if(key === 'debug' || key === '0' || key === '1') return;  // Ignoring specific keys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												        const accountValue = accountSettings[key] || '-';
 | 
				
			||||||
 | 
												        const browserValue = localStorage.getItem(key) || '-';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												        const row = settingsTableBody.insertRow();
 | 
				
			||||||
 | 
												        const propertyCell = row.insertCell(0);
 | 
				
			||||||
 | 
												        const accountCell = row.insertCell(1);
 | 
				
			||||||
 | 
												        const browserCell = row.insertCell(2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												        propertyCell.textContent = key;
 | 
				
			||||||
 | 
												        accountCell.textContent = accountValue;
 | 
				
			||||||
 | 
												        browserCell.textContent = browserValue;
 | 
				
			||||||
 | 
												    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												    document.getElementById('syncToBrowser').addEventListener('click', function() {
 | 
				
			||||||
 | 
												        // First, clear the local storage
 | 
				
			||||||
 | 
												        localStorage.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												        // Then, set the account settings to local storage
 | 
				
			||||||
 | 
												        for (let key in accountSettings) {
 | 
				
			||||||
 | 
												            if(key !== 'debug' && key !== '0' && key !== '1') { // Only sync non-ignored keys
 | 
				
			||||||
 | 
												                localStorage.setItem(key, accountSettings[key]);
 | 
				
			||||||
 | 
												            }
 | 
				
			||||||
 | 
												        }
 | 
				
			||||||
 | 
												        location.reload();  // Refresh the page after sync
 | 
				
			||||||
 | 
												    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												    document.getElementById('syncToAccount').addEventListener('click', function() {
 | 
				
			||||||
 | 
												        let form = document.createElement("form");
 | 
				
			||||||
 | 
												        form.method = "POST";
 | 
				
			||||||
 | 
												        form.action = "/updateUserSettings";  // Your endpoint URL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												        for (let i = 0; i < localStorage.length; i++) {
 | 
				
			||||||
 | 
												            const key = localStorage.key(i);
 | 
				
			||||||
 | 
												            if(key !== 'debug' && key !== '0' && key !== '1') { // Only send non-ignored keys
 | 
				
			||||||
 | 
												                let hiddenField = document.createElement("input");
 | 
				
			||||||
 | 
												                hiddenField.type = "hidden";
 | 
				
			||||||
 | 
												                hiddenField.name = key;
 | 
				
			||||||
 | 
												                hiddenField.value = localStorage.getItem(key);
 | 
				
			||||||
 | 
												                form.appendChild(hiddenField);
 | 
				
			||||||
 | 
												            }
 | 
				
			||||||
 | 
												        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												        document.body.appendChild(form);
 | 
				
			||||||
 | 
												        form.submit();
 | 
				
			||||||
 | 
												    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												</script>
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
						                      
 | 
				
			||||||
 | 
						                    
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        <!-- Sign Out Button -->
 | 
				
			||||||
 | 
					                        <div class="form-group mt-4">
 | 
				
			||||||
 | 
					                            <a href="/logout">
 | 
				
			||||||
 | 
					                                <button type="button" class="btn btn-danger" th:text="#{settings.signOut}">Sign Out</button>
 | 
				
			||||||
 | 
					                            </a>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div th:insert="~{fragments/footer.html :: footer}"></div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										76
									
								
								src/main/resources/templates/addUsers.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								src/main/resources/templates/addUsers.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<th:block th:insert="~{fragments/common :: head(title=#{settings.userSettings})}"></th:block>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<body>
 | 
				
			||||||
 | 
					    <th:block th:insert="~{fragments/common :: game}"></th:block>
 | 
				
			||||||
 | 
					    <div id="page-container">
 | 
				
			||||||
 | 
					        <div id="content-wrap">
 | 
				
			||||||
 | 
					            <div th:insert="~{fragments/navbar.html :: navbar}"></div>
 | 
				
			||||||
 | 
					            <br> <br>
 | 
				
			||||||
 | 
					            <div class="container">
 | 
				
			||||||
 | 
					                <div class="row justify-content-center">
 | 
				
			||||||
 | 
					                    <div class="col-md-8">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        <!-- User Settings Title -->
 | 
				
			||||||
 | 
					                        <h2 class="text-center" th:text="#{settings.accountSettings}">User Settings</h2>
 | 
				
			||||||
 | 
					                        <hr>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											<!-- At the top of the user settings -->
 | 
				
			||||||
 | 
											<h3 class="text-center">Welcome <span th:text="${username}">User</span>!</h3>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											
 | 
				
			||||||
 | 
											
 | 
				
			||||||
 | 
											<table class="table">
 | 
				
			||||||
 | 
											    <thead>
 | 
				
			||||||
 | 
											        <tr>
 | 
				
			||||||
 | 
											            <th>Username</th>
 | 
				
			||||||
 | 
											            <th>Roles</th>
 | 
				
			||||||
 | 
											            <th>Actions</th>
 | 
				
			||||||
 | 
											        </tr>
 | 
				
			||||||
 | 
											    </thead>
 | 
				
			||||||
 | 
											    <tbody>
 | 
				
			||||||
 | 
											        <tr th:each="user : ${users}">
 | 
				
			||||||
 | 
											            <td th:text="${user.username}"></td>
 | 
				
			||||||
 | 
											            <td th:text="${user.getRolesAsString()}"></td>
 | 
				
			||||||
 | 
											            <td>
 | 
				
			||||||
 | 
											                <a th:href="@{'/admin/deleteUser/' + ${user.username}}">Delete</a>
 | 
				
			||||||
 | 
											            </td>
 | 
				
			||||||
 | 
											        </tr>
 | 
				
			||||||
 | 
											    </tbody>
 | 
				
			||||||
 | 
											</table>
 | 
				
			||||||
 | 
											
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											<h2>Add New User</h2>
 | 
				
			||||||
 | 
										    <form action="/admin/saveUser" method="post">
 | 
				
			||||||
 | 
										        <div class="form-group">
 | 
				
			||||||
 | 
										            <label for="username">Username</label>
 | 
				
			||||||
 | 
										            <input type="text" class="form-control" name="username" required>
 | 
				
			||||||
 | 
										        </div>
 | 
				
			||||||
 | 
										        <div class="form-group">
 | 
				
			||||||
 | 
										            <label for="password">Password</label>
 | 
				
			||||||
 | 
										            <input type="password" class="form-control" name="password" required>
 | 
				
			||||||
 | 
										        </div>
 | 
				
			||||||
 | 
										        <div class="form-group">
 | 
				
			||||||
 | 
											        <label for="role">Role</label>
 | 
				
			||||||
 | 
											        <select name="role" class="form-control" required>
 | 
				
			||||||
 | 
											            <option value="ROLE_ADMIN">Admin</option>
 | 
				
			||||||
 | 
											            <option value="ROLE_USER">User</option>
 | 
				
			||||||
 | 
											            <option value="ROLE_LIMITED_API_USER">Limited API User</option>
 | 
				
			||||||
 | 
											            <option value="ROLE_WEB_ONLY_USER">Web Only User</option>
 | 
				
			||||||
 | 
											        </select>
 | 
				
			||||||
 | 
											    </div>
 | 
				
			||||||
 | 
										        
 | 
				
			||||||
 | 
										        <!-- Add other fields as required -->
 | 
				
			||||||
 | 
										        <button type="submit" class="btn btn-primary">Save User</button>
 | 
				
			||||||
 | 
										    </form>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div th:insert="~{fragments/footer.html :: footer}"></div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
@ -339,48 +339,10 @@
 | 
				
			|||||||
                  </div>
 | 
					                  </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                <hr>
 | 
					                <a href="account" target="_blank">
 | 
				
			||||||
                <h5 th:text="#{settings.userSettings}">User Settings</h5>
 | 
					                    <button type="button" class="btn btn-sm btn-outline-primary" th:text="#{settings.accountSettings}">Account Settings</button>
 | 
				
			||||||
 | 
					 | 
				
			||||||
                <form action="/change-username" method="post">
 | 
					 | 
				
			||||||
			        <div class="form-group">
 | 
					 | 
				
			||||||
			            <label for="newUsername" th:text="#{settings.changeUsername}">Change Username</label>
 | 
					 | 
				
			||||||
			            <input type="text" class="form-control" name="newUsername" id="newUsername" placeholder="New Username">
 | 
					 | 
				
			||||||
			        </div>
 | 
					 | 
				
			||||||
			        <div class="form-group">
 | 
					 | 
				
			||||||
			            <label for="password" th:text="#{settings.password}">Password</label>
 | 
					 | 
				
			||||||
			            <input type="password" class="form-control" name="password" id="password" placeholder="Password">
 | 
					 | 
				
			||||||
			        </div>
 | 
					 | 
				
			||||||
			        <div class="form-group">
 | 
					 | 
				
			||||||
			            <button type="submit" class="btn btn-primary" th:text="#{settings.changeUsernameButton}">Change Username</button>
 | 
					 | 
				
			||||||
			        </div>
 | 
					 | 
				
			||||||
			    </form>
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			    <!-- Change Password Form -->
 | 
					 | 
				
			||||||
			    <form action="/change-password" method="post">
 | 
					 | 
				
			||||||
			        <div class="form-group">
 | 
					 | 
				
			||||||
			            <label for="oldPassword" th:text="#{settings.oldPassword}">Old Password</label>
 | 
					 | 
				
			||||||
			            <input type="password" class="form-control" name="oldPassword" id="oldPassword" placeholder="Old Password">
 | 
					 | 
				
			||||||
			        </div>
 | 
					 | 
				
			||||||
			        <div class="form-group">
 | 
					 | 
				
			||||||
			            <label for="newPassword" th:text="#{settings.newPassword}">New Password</label>
 | 
					 | 
				
			||||||
			            <input type="password" class="form-control" name="newPassword" id="newPassword" placeholder="New Password">
 | 
					 | 
				
			||||||
			        </div>
 | 
					 | 
				
			||||||
			        <div class="form-group">
 | 
					 | 
				
			||||||
			            <label for="confirmNewPassword" th:text="#{settings.confirmNewPassword}">Confirm New Password</label>
 | 
					 | 
				
			||||||
			            <input type="password" class="form-control" name="confirmNewPassword" id="confirmNewPassword" placeholder="Confirm New Password">
 | 
					 | 
				
			||||||
			        </div>
 | 
					 | 
				
			||||||
			        <div class="form-group">
 | 
					 | 
				
			||||||
			            <button type="submit" class="btn btn-primary" th:text="#{settings.changePasswordButton}">Change Password</button>
 | 
					 | 
				
			||||||
			        </div>
 | 
					 | 
				
			||||||
			    </form>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- Sign Out Button -->
 | 
					 | 
				
			||||||
                <div class="form-group">
 | 
					 | 
				
			||||||
                    <a href="/logout">
 | 
					 | 
				
			||||||
                        <button type="button" class="btn btn-danger" th:text="#{settings.signOut}">Sign Out</button>
 | 
					 | 
				
			||||||
                </a>
 | 
					                </a>
 | 
				
			||||||
                </div>
 | 
					               
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div class="modal-footer">
 | 
					            <div class="modal-footer">
 | 
				
			||||||
 | 
				
			|||||||
@ -23,9 +23,6 @@
 | 
				
			|||||||
            <script src="js/homecard.js"></script>
 | 
					            <script src="js/homecard.js"></script>
 | 
				
			||||||
            	
 | 
					            	
 | 
				
			||||||
            <div class=" container">
 | 
					            <div class=" container">
 | 
				
			||||||
            <form th:if="${@loginEnabled == true}" action="#" th:action="@{/logout}" method="post">
 | 
					 | 
				
			||||||
			    <input type="submit" value="Logout" />
 | 
					 | 
				
			||||||
			</form>
 | 
					 | 
				
			||||||
            <input type="text" id="searchBar" onkeyup="filterCards()" placeholder="Search for features...">
 | 
					            <input type="text" id="searchBar" onkeyup="filterCards()" placeholder="Search for features...">
 | 
				
			||||||
            <div class="features-container ">
 | 
					            <div class="features-container ">
 | 
				
			||||||
            	
 | 
					            	
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user