init
This commit is contained in:
@@ -10,11 +10,13 @@ import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import stirling.software.SPDF.utils.GeneralUtils;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableWebSecurity(debug = true)
|
||||
//@EnableScheduling
|
||||
public class SPdfApplication {
|
||||
|
||||
|
||||
@@ -5,6 +5,17 @@ import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class AppConfig {
|
||||
|
||||
|
||||
@Bean(name = "loginEnabled")
|
||||
public boolean loginEnabled() {
|
||||
String appName = System.getProperty("login.enabled");
|
||||
if (appName == null)
|
||||
appName = System.getenv("login.enabled");
|
||||
|
||||
return (appName != null) ? Boolean.valueOf(appName) : false;
|
||||
}
|
||||
|
||||
@Bean(name = "appName")
|
||||
public String appName() {
|
||||
String appName = System.getProperty("APP_HOME_NAME");
|
||||
|
||||
@@ -24,8 +24,9 @@ import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
public class CleanUrlInterceptor implements HandlerInterceptor {
|
||||
|
||||
private static final List<String> ALLOWED_PARAMS = Arrays.asList("lang", "endpoint", "endpoints");
|
||||
private static final List<String> ALLOWED_PARAMS = Arrays.asList("lang", "endpoint", "endpoints", "logout", "error");
|
||||
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws Exception {
|
||||
@@ -39,9 +40,12 @@ public class CleanUrlInterceptor implements HandlerInterceptor {
|
||||
String[] queryParameters = queryString.split("&");
|
||||
for (String param : queryParameters) {
|
||||
String[] keyValue = param.split("=");
|
||||
System.out.print("astirli " + keyValue[0]);
|
||||
if (keyValue.length != 2) {
|
||||
continue;
|
||||
}
|
||||
System.out.print("astirli2 " + keyValue[0]);
|
||||
|
||||
if (ALLOWED_PARAMS.contains(keyValue[0])) {
|
||||
parameters.put(keyValue[0], keyValue[1]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package stirling.software.SPDF.controller.api;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import stirling.software.SPDF.config.security.UserService;
|
||||
|
||||
@Controller
|
||||
public class UserController {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@PostMapping("/register")
|
||||
public String register(@RequestParam String username, @RequestParam String password, Model model) {
|
||||
if(userService.usernameExists(username)) {
|
||||
model.addAttribute("error", "Username already exists");
|
||||
return "register";
|
||||
}
|
||||
|
||||
userService.saveUser(username, password);
|
||||
return "redirect:/login?registered=true";
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.ResourcePatternUtils;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -24,10 +25,30 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
@Controller
|
||||
@Tag(name = "General", description = "General APIs")
|
||||
public class GeneralWebController {
|
||||
|
||||
|
||||
@GetMapping("/login")
|
||||
public String login(HttpServletRequest request, Model model, Authentication authentication) {
|
||||
if (authentication != null && authentication.isAuthenticated()) {
|
||||
return "redirect:/";
|
||||
}
|
||||
|
||||
if (request.getParameter("error") != null) {
|
||||
|
||||
model.addAttribute("error", request.getParameter("error"));
|
||||
}
|
||||
if (request.getParameter("logout") != null) {
|
||||
|
||||
model.addAttribute("logoutMessage", "You have been logged out.");
|
||||
}
|
||||
|
||||
return "login";
|
||||
}
|
||||
|
||||
@GetMapping("/pipeline")
|
||||
@Hidden
|
||||
public String pipelineForm(Model model) {
|
||||
|
||||
52
src/main/java/stirling/software/SPDF/model/Authority.java
Normal file
52
src/main/java/stirling/software/SPDF/model/Authority.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package stirling.software.SPDF.model;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "authorities")
|
||||
public class Authority {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "authority")
|
||||
private String authority;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "username")
|
||||
private User user;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getAuthority() {
|
||||
return authority;
|
||||
}
|
||||
|
||||
public void setAuthority(String authority) {
|
||||
this.authority = authority;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
62
src/main/java/stirling/software/SPDF/model/User.java
Normal file
62
src/main/java/stirling/software/SPDF/model/User.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package stirling.software.SPDF.model;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
public class User {
|
||||
|
||||
@Id
|
||||
@Column(name = "username")
|
||||
private String username;
|
||||
|
||||
@Column(name = "password")
|
||||
private String password;
|
||||
|
||||
@Column(name = "enabled")
|
||||
private boolean enabled;
|
||||
|
||||
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user")
|
||||
private Set<Authority> authorities;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public Set<Authority> getAuthorities() {
|
||||
return authorities;
|
||||
}
|
||||
|
||||
public void setAuthorities(Set<Authority> authorities) {
|
||||
this.authorities = authorities;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package stirling.software.SPDF.repository;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import stirling.software.SPDF.model.Authority;
|
||||
|
||||
public interface AuthorityRepository extends JpaRepository<Authority, Long> {
|
||||
//Set<Authority> findByUsername(String username);
|
||||
Set<Authority> findByUser_Username(String username);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package stirling.software.SPDF.repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import stirling.software.SPDF.model.User;
|
||||
|
||||
public interface UserRepository extends JpaRepository<User, String> {
|
||||
Optional<User> findByUsername(String username);
|
||||
}
|
||||
|
||||
@@ -16,8 +16,11 @@ server.error.include-stacktrace=always
|
||||
server.error.include-exception=true
|
||||
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.security=DEBUG
|
||||
|
||||
login.enabled=true
|
||||
|
||||
server.servlet.session.tracking-modes=cookie
|
||||
server.servlet.context-path=${APP_ROOT_PATH:/}
|
||||
@@ -32,4 +35,12 @@ spring.mvc.async.request-timeout=${ASYNC_CONNECTION_TIMEOUT:300000}
|
||||
|
||||
spring.resources.static-locations=file:customFiles/static/
|
||||
#spring.thymeleaf.prefix=file:/customFiles/templates/,classpath:/templates/
|
||||
#spring.thymeleaf.cache=false
|
||||
#spring.thymeleaf.cache=false
|
||||
|
||||
|
||||
spring.datasource.url=jdbc:h2:file:./mydatabase;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
|
||||
spring.datasource.driver-class-name=org.h2.Driver
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=
|
||||
spring.h2.console.enabled=true
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
|
||||
@@ -55,6 +55,15 @@ settings.downloadOption.2=Open in new window
|
||||
settings.downloadOption.3=Download file
|
||||
settings.zipThreshold=Zip files when the number of downloaded files exceeds
|
||||
|
||||
settings.userSettings=User Settings
|
||||
settings.changeUsername=New Username
|
||||
settings.changeUsernameButton=Change Username
|
||||
settings.password=Password
|
||||
settings.oldPassword=Old password
|
||||
settings.newPassword=New Password
|
||||
settings.changePasswordButton=Change Password
|
||||
settings.confirmNewPassword=Confirm New Password
|
||||
settings.signOut=Sign Out
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
#############
|
||||
|
||||
@@ -338,6 +338,49 @@
|
||||
<label class="custom-control-label" for="boredWaiting" th:text="#{bored}"></label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<h5 th:text="#{settings.userSettings}">User Settings</h5>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
<script src="js/homecard.js"></script>
|
||||
|
||||
<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...">
|
||||
<div class="features-container ">
|
||||
|
||||
|
||||
58
src/main/resources/templates/login.html
Normal file
58
src/main/resources/templates/login.html
Normal file
@@ -0,0 +1,58 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<title>Login</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center align-items-center" style="height:100vh;">
|
||||
<div class="col-md-4">
|
||||
<div class="login-container card">
|
||||
<div class="card-header text-center">
|
||||
<img src="favicon.svg" alt="Logo" class="img-fluid" style="max-width: 100px;"> <!-- Adjust path and style as needed -->
|
||||
<h4>Stirling-PDF Login</h4>
|
||||
</div>
|
||||
|
||||
|
||||
<div th:if="${logoutMessage}" class="alert alert-success" th:text="${logoutMessage}"></div>
|
||||
|
||||
|
||||
|
||||
<div class="card-body">
|
||||
<form th:action="@{/login}" method="post">
|
||||
<div class="form-group">
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" id="username" name="username" class="form-control" required="required" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" class="form-control" required="required" />
|
||||
</div>
|
||||
<div class="form-group text-center">
|
||||
<input type="submit" value="Login" class="btn btn-primary" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer text-danger text-center">
|
||||
<div th:if="${error == 'badcredentials'}">
|
||||
Invalid username or password.
|
||||
</div>
|
||||
<div th:if="${error == 'locked'}">
|
||||
Your account has been locked.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user