diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2221589..20a3ced 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -15,11 +15,11 @@ jobs:
steps:
- uses: actions/checkout@v4
- - name: Set up JDK 8
+ - name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
- java-version: '8'
+ java-version: '17'
cache: maven
- name: Build and run tests
diff --git a/UserFront/pom.xml b/UserFront/pom.xml
index bdc2944..eb1245d 100644
--- a/UserFront/pom.xml
+++ b/UserFront/pom.xml
@@ -14,14 +14,14 @@
org.springframework.boot
spring-boot-starter-parent
- 1.5.4.RELEASE
+ 3.3.5
UTF-8
UTF-8
- 1.8
+ 17
@@ -46,8 +46,8 @@
- mysql
- mysql-connector-java
+ com.mysql
+ mysql-connector-j
@@ -72,6 +72,13 @@
spring-security-test
test
+
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ test
+
diff --git a/UserFront/src/main/java/com/userFront/config/PasswordEncoderConfig.java b/UserFront/src/main/java/com/userFront/config/PasswordEncoderConfig.java
new file mode 100644
index 0000000..93683f4
--- /dev/null
+++ b/UserFront/src/main/java/com/userFront/config/PasswordEncoderConfig.java
@@ -0,0 +1,14 @@
+package com.userFront.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+@Configuration
+public class PasswordEncoderConfig {
+
+ @Bean
+ public BCryptPasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+}
diff --git a/UserFront/src/main/java/com/userFront/config/RequestFilter.java b/UserFront/src/main/java/com/userFront/config/RequestFilter.java
deleted file mode 100644
index 65b14ce..0000000
--- a/UserFront/src/main/java/com/userFront/config/RequestFilter.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.userFront.config;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.core.Ordered;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Component;
-
-@Component
-@Order(Ordered.HIGHEST_PRECEDENCE)
-public class RequestFilter implements Filter {
-
- private static final Logger LOG = LoggerFactory.getLogger(RequestFilter.class);
-
- public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
- HttpServletResponse response = (HttpServletResponse) res;
- HttpServletRequest request = (HttpServletRequest) req;
-
- response.setHeader("Access-Control-Allow-Origin", "http://localhost:4200"); //8081
- response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
- response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
- response.setHeader("Access-Control-Max-Age", "3600");
- response.setHeader("Access-Control-Allow-Credentials", "true");
-
- if (!(request.getMethod().equalsIgnoreCase("OPTIONS"))) {
- try {
- chain.doFilter(req, res);
- } catch(Exception e) {
- LOG.error("Error while processing request in RequestFilter", e);
- }
- } else {
- LOG.debug("Handling CORS pre-flight request");
- response.setHeader("Access-Control-Allow-Methods", "POST,GET,DELETE");
- response.setHeader("Access-Control-Max-Age", "3600");
- response.setHeader("Access-Control-Allow-Headers", "authorization, content-type," +
- "access-control-request-headers,access-control-request-method,accept,origin,authorization,x-requested-with");
- response.setStatus(HttpServletResponse.SC_OK);
- }
-
- }
-
- public void init(FilterConfig filterConfig) {}
-
- public void destroy() {}
-
-}
diff --git a/UserFront/src/main/java/com/userFront/config/SecurityConfig.java b/UserFront/src/main/java/com/userFront/config/SecurityConfig.java
index c74bfa0..025dbf8 100644
--- a/UserFront/src/main/java/com/userFront/config/SecurityConfig.java
+++ b/UserFront/src/main/java/com/userFront/config/SecurityConfig.java
@@ -1,34 +1,33 @@
package com.userFront.config;
+import java.util.Arrays;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.core.env.Environment;
-import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
-import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import com.userFront.service.UserServiceImpl.UserSecurityService;
@Configuration
@EnableWebSecurity
-@EnableGlobalMethodSecurity(prePostEnabled=true)
-public class SecurityConfig extends WebSecurityConfigurerAdapter {
-
- @Autowired
- private Environment env;
+@EnableMethodSecurity(prePostEnabled = true)
+public class SecurityConfig {
@Autowired
private UserSecurityService userSecurityService;
- @Bean
- public BCryptPasswordEncoder passwordEncoder() {
- return new BCryptPasswordEncoder();
- }
+ @Autowired
+ private BCryptPasswordEncoder passwordEncoder;
private static final String[] PUBLIC_MATCHERS = {
"/webjars/**",
@@ -38,35 +37,59 @@ public BCryptPasswordEncoder passwordEncoder() {
"/",
"/about/**",
"/contact/**",
- "/error/**/*",
+ "/error/**",
"/console/**",
"/signup"
};
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http
- .authorizeRequests().
-// antMatchers("/**").
- antMatchers(PUBLIC_MATCHERS).
- permitAll().anyRequest().authenticated();
-
- http
- .csrf().disable().cors().disable()
- .formLogin().failureUrl("/index?error").defaultSuccessUrl("/userFront").loginPage("/index").permitAll()
- .and()
- .logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/index?logout").deleteCookies("remember-me").permitAll()
- .and()
- .rememberMe();
+ @Bean
+ public DaoAuthenticationProvider authenticationProvider() {
+ DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
+ provider.setUserDetailsService(userSecurityService);
+ provider.setPasswordEncoder(passwordEncoder);
+ return provider;
}
+ @Bean
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+ http
+ .cors(cors -> cors.configurationSource(corsConfigurationSource()))
+ // CSRF is enabled (Spring Security default). The server-rendered
+ // Thymeleaf forms receive the token automatically; the cross-origin
+ // JSON API consumed by the Angular SPA is exempted and relies on the
+ // CORS allowlist plus the authenticated session. Full SPA CSRF token
+ // handling is addressed with the frontend rewrite (Phase 7).
+ .csrf(csrf -> csrf.ignoringRequestMatchers("/api/**"))
+ .authorizeHttpRequests(auth -> auth
+ .requestMatchers(PUBLIC_MATCHERS).permitAll()
+ .anyRequest().authenticated())
+ .formLogin(form -> form
+ .loginPage("/index")
+ .failureUrl("/index?error")
+ .defaultSuccessUrl("/userFront")
+ .permitAll())
+ .logout(logout -> logout
+ .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
+ .logoutSuccessUrl("/index?logout")
+ .deleteCookies("remember-me")
+ .permitAll())
+ .rememberMe(remember -> {
+ });
-
- @Autowired
- public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
-// auth.inMemoryAuthentication().withUser("user").password("password").roles("USER"); //This is in-memory authentication
- auth.userDetailsService(userSecurityService).passwordEncoder(passwordEncoder());
+ return http.build();
}
+ @Bean
+ public CorsConfigurationSource corsConfigurationSource() {
+ CorsConfiguration configuration = new CorsConfiguration();
+ configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
+ configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
+ configuration.setAllowedHeaders(Arrays.asList("*"));
+ configuration.setAllowCredentials(true);
+ configuration.setMaxAge(3600L);
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ source.registerCorsConfiguration("/**", configuration);
+ return source;
+ }
}
diff --git a/UserFront/src/main/java/com/userFront/domain/Appointment.java b/UserFront/src/main/java/com/userFront/domain/Appointment.java
index 1d6623e..7fd9560 100644
--- a/UserFront/src/main/java/com/userFront/domain/Appointment.java
+++ b/UserFront/src/main/java/com/userFront/domain/Appointment.java
@@ -2,12 +2,12 @@
import java.util.Date;
-import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.JoinColumn;
-import javax.persistence.ManyToOne;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
@Entity
public class Appointment {
diff --git a/UserFront/src/main/java/com/userFront/domain/PrimaryAccount.java b/UserFront/src/main/java/com/userFront/domain/PrimaryAccount.java
index 0354bfd..d93cd89 100644
--- a/UserFront/src/main/java/com/userFront/domain/PrimaryAccount.java
+++ b/UserFront/src/main/java/com/userFront/domain/PrimaryAccount.java
@@ -3,13 +3,13 @@
import java.math.BigDecimal;
import java.util.List;
-import javax.persistence.CascadeType;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.OneToMany;
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.OneToMany;
import com.fasterxml.jackson.annotation.JsonIgnore;
diff --git a/UserFront/src/main/java/com/userFront/domain/PrimaryTransaction.java b/UserFront/src/main/java/com/userFront/domain/PrimaryTransaction.java
index f0d2395..e8792b2 100644
--- a/UserFront/src/main/java/com/userFront/domain/PrimaryTransaction.java
+++ b/UserFront/src/main/java/com/userFront/domain/PrimaryTransaction.java
@@ -3,12 +3,12 @@
import java.math.BigDecimal;
import java.util.Date;
-import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.JoinColumn;
-import javax.persistence.ManyToOne;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
@Entity
public class PrimaryTransaction {
diff --git a/UserFront/src/main/java/com/userFront/domain/Recipient.java b/UserFront/src/main/java/com/userFront/domain/Recipient.java
index 9ec1f70..80a6a37 100644
--- a/UserFront/src/main/java/com/userFront/domain/Recipient.java
+++ b/UserFront/src/main/java/com/userFront/domain/Recipient.java
@@ -1,11 +1,11 @@
package com.userFront.domain;
-import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.JoinColumn;
-import javax.persistence.ManyToOne;
+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 com.fasterxml.jackson.annotation.JsonIgnore;
diff --git a/UserFront/src/main/java/com/userFront/domain/SavingsAccount.java b/UserFront/src/main/java/com/userFront/domain/SavingsAccount.java
index 922a806..c064912 100644
--- a/UserFront/src/main/java/com/userFront/domain/SavingsAccount.java
+++ b/UserFront/src/main/java/com/userFront/domain/SavingsAccount.java
@@ -3,13 +3,13 @@
import java.math.BigDecimal;
import java.util.List;
-import javax.persistence.CascadeType;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.OneToMany;
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.OneToMany;
import com.fasterxml.jackson.annotation.JsonIgnore;
diff --git a/UserFront/src/main/java/com/userFront/domain/SavingsTransaction.java b/UserFront/src/main/java/com/userFront/domain/SavingsTransaction.java
index 8a5ac55..f19a3bc 100644
--- a/UserFront/src/main/java/com/userFront/domain/SavingsTransaction.java
+++ b/UserFront/src/main/java/com/userFront/domain/SavingsTransaction.java
@@ -3,12 +3,12 @@
import java.math.BigDecimal;
import java.util.Date;
-import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.JoinColumn;
-import javax.persistence.ManyToOne;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
@Entity
public class SavingsTransaction {
diff --git a/UserFront/src/main/java/com/userFront/domain/User.java b/UserFront/src/main/java/com/userFront/domain/User.java
index 3b14e71..89c0222 100644
--- a/UserFront/src/main/java/com/userFront/domain/User.java
+++ b/UserFront/src/main/java/com/userFront/domain/User.java
@@ -5,15 +5,15 @@
import java.util.List;
import java.util.Set;
-import javax.persistence.CascadeType;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.OneToMany;
-import javax.persistence.OneToOne;
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.OneToMany;
+import jakarta.persistence.OneToOne;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
diff --git a/UserFront/src/main/java/com/userFront/domain/security/Role.java b/UserFront/src/main/java/com/userFront/domain/security/Role.java
index 45aa369..65e1ec0 100644
--- a/UserFront/src/main/java/com/userFront/domain/security/Role.java
+++ b/UserFront/src/main/java/com/userFront/domain/security/Role.java
@@ -1,6 +1,6 @@
package com.userFront.domain.security;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.util.HashSet;
import java.util.Set;
diff --git a/UserFront/src/main/java/com/userFront/domain/security/UserRole.java b/UserFront/src/main/java/com/userFront/domain/security/UserRole.java
index a72a53d..48e5f07 100644
--- a/UserFront/src/main/java/com/userFront/domain/security/UserRole.java
+++ b/UserFront/src/main/java/com/userFront/domain/security/UserRole.java
@@ -2,7 +2,7 @@
import com.userFront.domain.User;
-import javax.persistence.*;
+import jakarta.persistence.*;
@Entity
diff --git a/UserFront/src/main/java/com/userFront/service/UserServiceImpl/AppointmentServiceImpl.java b/UserFront/src/main/java/com/userFront/service/UserServiceImpl/AppointmentServiceImpl.java
index 838ad22..0d62b84 100644
--- a/UserFront/src/main/java/com/userFront/service/UserServiceImpl/AppointmentServiceImpl.java
+++ b/UserFront/src/main/java/com/userFront/service/UserServiceImpl/AppointmentServiceImpl.java
@@ -24,7 +24,7 @@ public List findAll() {
}
public Appointment findAppointment(Long id) {
- return appointmentDao.findOne(id);
+ return appointmentDao.findById(id).orElse(null);
}
public void confirmAppointment(Long id) {
diff --git a/UserFront/src/main/resources/application.properties b/UserFront/src/main/resources/application.properties
index 94c53c4..ee307e9 100644
--- a/UserFront/src/main/resources/application.properties
+++ b/UserFront/src/main/resources/application.properties
@@ -1,3 +1,8 @@
+# The service layer contains mutual dependencies between UserService,
+# AccountService and TransactionService. Spring Boot 2.6+ prohibits circular
+# references by default; allow them to preserve the existing wiring.
+spring.main.allow-circular-references = true
+
# ===============================
# = DATA SOURCE
# ===============================
@@ -33,5 +38,5 @@ spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = update
# Allows Hibernate to generate SQL optimized for a particular DBMS
-spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
+spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect
diff --git a/UserFront/src/test/resources/application.properties b/UserFront/src/test/resources/application.properties
index 1084b77..bf8ae0d 100644
--- a/UserFront/src/test/resources/application.properties
+++ b/UserFront/src/test/resources/application.properties
@@ -4,7 +4,11 @@
# Integration tests run against an in-memory H2 database so no external
# MySQL instance is required in CI.
-spring.datasource.url = jdbc:h2:mem:onlinebanking;DB_CLOSE_DELAY=-1;MODE=MySQL
+# Mirror the production setting: allow the mutual service-layer dependencies
+# that Spring Boot 2.6+ rejects by default.
+spring.main.allow-circular-references = true
+
+spring.datasource.url = jdbc:h2:mem:onlinebanking;DB_CLOSE_DELAY=-1;MODE=MySQL;NON_KEYWORDS=USER
spring.datasource.username = sa
spring.datasource.password =
spring.datasource.driver-class-name = org.h2.Driver