Compare commits
10 Commits
dimas_time
...
dimas_inte
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d90cf83d98 | ||
|
|
ccf43a50e8 | ||
|
|
0fcacc3e88 | ||
|
|
71448a6c21 | ||
|
|
fddd02b9ce | ||
|
|
3bf3f92551 | ||
|
|
fa99d42bee | ||
|
|
c45cd0549f | ||
|
|
07c9ed63ee | ||
|
|
d03e256425 |
6
pom.xml
6
pom.xml
@@ -78,6 +78,12 @@
|
|||||||
<artifactId>spring-security-web</artifactId>
|
<artifactId>spring-security-web</artifactId>
|
||||||
<version>6.1.5</version>
|
<version>6.1.5</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-math3 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-math3</artifactId>
|
||||||
|
<version>3.6.1</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<defaultGoal>package</defaultGoal>
|
<defaultGoal>package</defaultGoal>
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package com.faf223.expensetrackerfaf.config;
|
package com.faf223.expensetrackerfaf.config;
|
||||||
|
|
||||||
|
import com.faf223.expensetrackerfaf.repository.CredentialRepository;
|
||||||
|
import com.faf223.expensetrackerfaf.repository.UserRepository;
|
||||||
import com.faf223.expensetrackerfaf.security.PersonDetails;
|
import com.faf223.expensetrackerfaf.security.PersonDetails;
|
||||||
import com.faf223.expensetrackerfaf.service.CredentialService;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||||
@@ -18,11 +20,11 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class ApplicationConfig {
|
public class ApplicationConfig {
|
||||||
|
|
||||||
private final CredentialService credentialService;
|
private final CredentialRepository credentialRepository;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public UserDetailsService userDetailsService() {
|
public UserDetailsService userDetailsService() {
|
||||||
return username -> new PersonDetails(credentialService.findByEmail(username).orElseThrow((() -> new UsernameNotFoundException("User not found"))));
|
return username -> new PersonDetails(credentialRepository.findByEmail(username).orElseThrow(() -> new UsernameNotFoundException("User not found")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -42,4 +44,10 @@ public class ApplicationConfig {
|
|||||||
public PasswordEncoder passwordEncoder() {
|
public PasswordEncoder passwordEncoder() {
|
||||||
return new BCryptPasswordEncoder();
|
return new BCryptPasswordEncoder();
|
||||||
}
|
}
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
public JwtAuthenticationFilter customJwtAuthenticationFilter(JwtService jwtService, UserDetailsService userDetailsService) {
|
||||||
|
return new JwtAuthenticationFilter(jwtService, userDetailsService);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
response.setContentType("application/json");
|
response.setContentType("application/json");
|
||||||
|
|
||||||
ErrorResponse errorResponse = new ErrorResponse("Your session has expired. Refresh your token.");
|
ErrorResponse errorResponse = new ErrorResponse("Your session has expired. Refresh your token.");
|
||||||
ObjectMapper objectMapper = new ObjectMapper(); // You may need to import ObjectMapper
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
response.getWriter().write(objectMapper.writeValueAsString(errorResponse));
|
response.getWriter().write(objectMapper.writeValueAsString(errorResponse));
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import io.jsonwebtoken.Jwts;
|
|||||||
import io.jsonwebtoken.SignatureAlgorithm;
|
import io.jsonwebtoken.SignatureAlgorithm;
|
||||||
import io.jsonwebtoken.io.Decoders;
|
import io.jsonwebtoken.io.Decoders;
|
||||||
import io.jsonwebtoken.security.Keys;
|
import io.jsonwebtoken.security.Keys;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -16,6 +17,7 @@ import java.util.Map;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class JwtService {
|
public class JwtService {
|
||||||
|
|
||||||
@Value("${application.security.jwt.secret-key}")
|
@Value("${application.security.jwt.secret-key}")
|
||||||
@@ -25,6 +27,7 @@ public class JwtService {
|
|||||||
@Value("${application.security.jwt.refresh-token.expiration}")
|
@Value("${application.security.jwt.refresh-token.expiration}")
|
||||||
private long refreshExpiration;
|
private long refreshExpiration;
|
||||||
|
|
||||||
|
|
||||||
public String extractUsername(String token) {
|
public String extractUsername(String token) {
|
||||||
return extractClaim(token, Claims::getSubject);
|
return extractClaim(token, Claims::getSubject);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,43 @@
|
|||||||
package com.faf223.expensetrackerfaf.config;
|
package com.faf223.expensetrackerfaf.config;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import com.faf223.expensetrackerfaf.controller.auth.JwtAuthenticationSuccessHandler;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
import org.springframework.security.config.Customizer;
|
import org.springframework.security.config.Customizer;
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
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.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||||
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
import org.springframework.web.cors.CorsConfigurationSource;
|
import org.springframework.web.cors.CorsConfigurationSource;
|
||||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static org.springframework.security.config.Customizer.withDefaults;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@RequiredArgsConstructor
|
|
||||||
@EnableMethodSecurity
|
@EnableMethodSecurity
|
||||||
public class SecurityConfiguration {
|
public class SecurityConfiguration {
|
||||||
|
|
||||||
private final JwtAuthenticationFilter jwtAuthFilter;
|
private final JwtAuthenticationFilter jwtAuthFilter;
|
||||||
private final AuthenticationProvider authenticationProvider;
|
private final AuthenticationProvider authenticationProvider;
|
||||||
|
private final ClientRegistrationRepository clientRegistrationRepository;
|
||||||
|
|
||||||
|
public SecurityConfiguration(JwtAuthenticationFilter jwtAuthFilter,
|
||||||
|
AuthenticationProvider authenticationProvider,
|
||||||
|
ClientRegistrationRepository clientRegistrationRepository) {
|
||||||
|
this.jwtAuthFilter = jwtAuthFilter;
|
||||||
|
this.authenticationProvider = authenticationProvider;
|
||||||
|
this.clientRegistrationRepository = clientRegistrationRepository;
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
@@ -33,15 +45,17 @@ public class SecurityConfiguration {
|
|||||||
.cors(Customizer.withDefaults())
|
.cors(Customizer.withDefaults())
|
||||||
.csrf(AbstractHttpConfigurer::disable)
|
.csrf(AbstractHttpConfigurer::disable)
|
||||||
.authorizeHttpRequests(auth -> auth
|
.authorizeHttpRequests(auth -> auth
|
||||||
.requestMatchers("/api/v1/auth/**").permitAll()
|
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
)
|
)
|
||||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
.oauth2Login(withDefaults());
|
||||||
.authenticationProvider(authenticationProvider)
|
|
||||||
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); // will be executed before UsernamePasswordAuthenticationFilter
|
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler() {
|
||||||
|
return new JwtAuthenticationSuccessHandler();
|
||||||
|
}
|
||||||
@Bean
|
@Bean
|
||||||
public CorsConfigurationSource corsConfigurationSource() {
|
public CorsConfigurationSource corsConfigurationSource() {
|
||||||
CorsConfiguration configuration = new CorsConfiguration();
|
CorsConfiguration configuration = new CorsConfiguration();
|
||||||
@@ -54,4 +68,8 @@ public class SecurityConfiguration {
|
|||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AuthenticationEntryPoint authenticationEntryPoint() {
|
||||||
|
return new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,9 @@ import com.faf223.expensetrackerfaf.model.User;
|
|||||||
import com.faf223.expensetrackerfaf.service.ExpenseCategoryService;
|
import com.faf223.expensetrackerfaf.service.ExpenseCategoryService;
|
||||||
import com.faf223.expensetrackerfaf.service.ExpenseService;
|
import com.faf223.expensetrackerfaf.service.ExpenseService;
|
||||||
import com.faf223.expensetrackerfaf.service.UserService;
|
import com.faf223.expensetrackerfaf.service.UserService;
|
||||||
|
import com.faf223.expensetrackerfaf.util.DataExtender;
|
||||||
import com.faf223.expensetrackerfaf.util.errors.ErrorResponse;
|
import com.faf223.expensetrackerfaf.util.errors.ErrorResponse;
|
||||||
import com.faf223.expensetrackerfaf.util.exceptions.TransactionDoesNotBelongToTheUserException;
|
import com.faf223.expensetrackerfaf.util.exceptions.*;
|
||||||
import com.faf223.expensetrackerfaf.util.exceptions.TransactionNotCreatedException;
|
|
||||||
import com.faf223.expensetrackerfaf.util.exceptions.TransactionNotUpdatedException;
|
|
||||||
import com.faf223.expensetrackerfaf.util.exceptions.TransactionsNotFoundException;
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
@@ -26,6 +24,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.Month;
|
import java.time.Month;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -134,6 +133,32 @@ public class ExpenseController {
|
|||||||
throw new TransactionsNotFoundException("The expenses have not been found");
|
throw new TransactionsNotFoundException("The expenses have not been found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/extend-data")
|
||||||
|
public ResponseEntity<List<BigDecimal>> extendData(@RequestParam Optional<String> extendValue,
|
||||||
|
@RequestParam Optional<Integer> extrapolationCount) {
|
||||||
|
if(extendValue.isEmpty())
|
||||||
|
throw new RequiredParamMissingException("Extend value has not been specified");
|
||||||
|
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
User user;
|
||||||
|
|
||||||
|
if (authentication != null && authentication.getPrincipal() instanceof UserDetails userDetails)
|
||||||
|
user = userService.getUserByEmail(userDetails.getUsername());
|
||||||
|
else
|
||||||
|
user = null;
|
||||||
|
|
||||||
|
if(user == null)
|
||||||
|
throw new UserNotFoundException("User with the specified UUID has not been found");
|
||||||
|
|
||||||
|
if(extendValue.get().equalsIgnoreCase("i"))
|
||||||
|
return ResponseEntity.ok(DataExtender.interpolate(user.getExpenses()));
|
||||||
|
|
||||||
|
if(extendValue.get().equalsIgnoreCase("e"))
|
||||||
|
return extrapolationCount.map(integer -> ResponseEntity.ok(DataExtender.extrapolate(user.getExpenses(), integer))).orElseGet(() -> ResponseEntity.ok(DataExtender.extrapolate(user.getExpenses(), 10)));
|
||||||
|
|
||||||
|
throw new WrongParamValueException("Wrong extend value has been specified (use either \"i\" or \"e\")");
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/categories")
|
@GetMapping("/categories")
|
||||||
public ResponseEntity<List<ExpenseCategory>> getAllCategories() {
|
public ResponseEntity<List<ExpenseCategory>> getAllCategories() {
|
||||||
List<ExpenseCategory> categories = expenseCategoryService.getAllCategories();
|
List<ExpenseCategory> categories = expenseCategoryService.getAllCategories();
|
||||||
|
|||||||
@@ -12,72 +12,47 @@ public class GlobalExceptionHandler {
|
|||||||
|
|
||||||
@ExceptionHandler
|
@ExceptionHandler
|
||||||
private ResponseEntity<ErrorResponse> handleDoesNotBelongException(TransactionDoesNotBelongToTheUserException e) {
|
private ResponseEntity<ErrorResponse> handleDoesNotBelongException(TransactionDoesNotBelongToTheUserException e) {
|
||||||
ErrorResponse response = new ErrorResponse(
|
return new ResponseEntity<>(new ErrorResponse(e.getMessage()), HttpStatus.FORBIDDEN);
|
||||||
e.getMessage(),
|
|
||||||
System.currentTimeMillis()
|
|
||||||
);
|
|
||||||
|
|
||||||
return new ResponseEntity<>(response, HttpStatus.FORBIDDEN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExceptionHandler
|
@ExceptionHandler
|
||||||
private ResponseEntity<ErrorResponse> handleTransactionNotCreatedException(TransactionNotCreatedException e) {
|
private ResponseEntity<ErrorResponse> handleTransactionNotCreatedException(TransactionNotCreatedException e) {
|
||||||
ErrorResponse response = new ErrorResponse(
|
return new ResponseEntity<>(new ErrorResponse(e.getMessage()), HttpStatus.NOT_MODIFIED);
|
||||||
e.getMessage(),
|
|
||||||
System.currentTimeMillis()
|
|
||||||
);
|
|
||||||
|
|
||||||
return new ResponseEntity<>(response, HttpStatus.NOT_MODIFIED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExceptionHandler
|
@ExceptionHandler
|
||||||
private ResponseEntity<ErrorResponse> handleTransactionsNotFoundException(TransactionsNotFoundException e) {
|
private ResponseEntity<ErrorResponse> handleTransactionsNotFoundException(TransactionsNotFoundException e) {
|
||||||
ErrorResponse response = new ErrorResponse(
|
return new ResponseEntity<>(new ErrorResponse(e.getMessage()), HttpStatus.NOT_FOUND);
|
||||||
e.getMessage(),
|
|
||||||
System.currentTimeMillis()
|
|
||||||
);
|
|
||||||
|
|
||||||
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExceptionHandler
|
@ExceptionHandler
|
||||||
private ResponseEntity<ErrorResponse> handleUserNotFoundException(UserNotFoundException e) {
|
private ResponseEntity<ErrorResponse> handleUserNotFoundException(UserNotFoundException e) {
|
||||||
ErrorResponse response = new ErrorResponse(
|
return new ResponseEntity<>(new ErrorResponse(e.getMessage()), HttpStatus.NOT_FOUND);
|
||||||
e.getMessage(),
|
|
||||||
System.currentTimeMillis()
|
|
||||||
);
|
|
||||||
|
|
||||||
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExceptionHandler
|
@ExceptionHandler
|
||||||
private ResponseEntity<ErrorResponse> handleTransactionNotUpdatedException(TransactionNotUpdatedException e) {
|
private ResponseEntity<ErrorResponse> handleTransactionNotUpdatedException(TransactionNotUpdatedException e) {
|
||||||
ErrorResponse response = new ErrorResponse(
|
return new ResponseEntity<>(new ErrorResponse(e.getMessage()), HttpStatus.NOT_MODIFIED);
|
||||||
e.getMessage(),
|
|
||||||
System.currentTimeMillis()
|
|
||||||
);
|
|
||||||
|
|
||||||
return new ResponseEntity<>(response, HttpStatus.NOT_MODIFIED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExceptionHandler
|
@ExceptionHandler
|
||||||
private ResponseEntity<ErrorResponse> handleUserNotAuthenticatedException(UserNotAuthenticatedException e) {
|
private ResponseEntity<ErrorResponse> handleUserNotAuthenticatedException(UserNotAuthenticatedException e) {
|
||||||
ErrorResponse response = new ErrorResponse(
|
return new ResponseEntity<>(new ErrorResponse(e.getMessage()), HttpStatus.FORBIDDEN);
|
||||||
e.getMessage(),
|
|
||||||
System.currentTimeMillis()
|
|
||||||
);
|
|
||||||
|
|
||||||
return new ResponseEntity<>(response, HttpStatus.FORBIDDEN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExceptionHandler
|
@ExceptionHandler
|
||||||
private ResponseEntity<ErrorResponse> handleUserNotCreatedException(UserNotCreatedException e) {
|
private ResponseEntity<ErrorResponse> handleUserNotCreatedException(UserNotCreatedException e) {
|
||||||
ErrorResponse response = new ErrorResponse(
|
return new ResponseEntity<>(new ErrorResponse(e.getMessage()), HttpStatus.NOT_MODIFIED);
|
||||||
e.getMessage(),
|
}
|
||||||
System.currentTimeMillis()
|
|
||||||
);
|
|
||||||
|
|
||||||
return new ResponseEntity<>(response, HttpStatus.NOT_MODIFIED);
|
@ExceptionHandler
|
||||||
|
private ResponseEntity<ErrorResponse> handleRequiredParamMissingException(RequiredParamMissingException e) {
|
||||||
|
return new ResponseEntity<>(new ErrorResponse(e.getMessage()), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler
|
||||||
|
private ResponseEntity<ErrorResponse> handleWrongParamValueException(WrongParamValueException e) {
|
||||||
|
return new ResponseEntity<>(new ErrorResponse(e.getMessage()), HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,9 @@ import com.faf223.expensetrackerfaf.model.User;
|
|||||||
import com.faf223.expensetrackerfaf.service.IncomeCategoryService;
|
import com.faf223.expensetrackerfaf.service.IncomeCategoryService;
|
||||||
import com.faf223.expensetrackerfaf.service.IncomeService;
|
import com.faf223.expensetrackerfaf.service.IncomeService;
|
||||||
import com.faf223.expensetrackerfaf.service.UserService;
|
import com.faf223.expensetrackerfaf.service.UserService;
|
||||||
|
import com.faf223.expensetrackerfaf.util.DataExtender;
|
||||||
import com.faf223.expensetrackerfaf.util.errors.ErrorResponse;
|
import com.faf223.expensetrackerfaf.util.errors.ErrorResponse;
|
||||||
import com.faf223.expensetrackerfaf.util.exceptions.TransactionDoesNotBelongToTheUserException;
|
import com.faf223.expensetrackerfaf.util.exceptions.*;
|
||||||
import com.faf223.expensetrackerfaf.util.exceptions.TransactionNotCreatedException;
|
|
||||||
import com.faf223.expensetrackerfaf.util.exceptions.TransactionNotUpdatedException;
|
|
||||||
import com.faf223.expensetrackerfaf.util.exceptions.TransactionsNotFoundException;
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
@@ -26,6 +24,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.Month;
|
import java.time.Month;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -134,6 +133,32 @@ public class IncomeController {
|
|||||||
throw new TransactionsNotFoundException("The expenses have not been found");
|
throw new TransactionsNotFoundException("The expenses have not been found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/extend-data")
|
||||||
|
public ResponseEntity<List<BigDecimal>> extendData(@RequestParam Optional<String> extendValue,
|
||||||
|
@RequestParam Optional<Integer> extrapolationCount) {
|
||||||
|
if(extendValue.isEmpty())
|
||||||
|
throw new RequiredParamMissingException("Extend value has not been specified");
|
||||||
|
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
User user;
|
||||||
|
|
||||||
|
if (authentication != null && authentication.getPrincipal() instanceof UserDetails userDetails)
|
||||||
|
user = userService.getUserByEmail(userDetails.getUsername());
|
||||||
|
else
|
||||||
|
user = null;
|
||||||
|
|
||||||
|
if(user == null)
|
||||||
|
throw new UserNotFoundException("User with the specified UUID has not been found");
|
||||||
|
|
||||||
|
if(extendValue.get().equalsIgnoreCase("i"))
|
||||||
|
return ResponseEntity.ok(DataExtender.interpolate(user.getIncomes()));
|
||||||
|
|
||||||
|
if(extendValue.get().equalsIgnoreCase("e"))
|
||||||
|
return extrapolationCount.map(integer -> ResponseEntity.ok(DataExtender.extrapolate(user.getIncomes(), integer))).orElseGet(() -> ResponseEntity.ok(DataExtender.extrapolate(user.getIncomes(), 10)));
|
||||||
|
|
||||||
|
throw new WrongParamValueException("Wrong extend value has been specified (use either \"i\" or \"e\")");
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/categories")
|
@GetMapping("/categories")
|
||||||
public ResponseEntity<List<IncomeCategory>> getAllCategories() {
|
public ResponseEntity<List<IncomeCategory>> getAllCategories() {
|
||||||
List<IncomeCategory> categories = incomeCategoryService.getAllCategories();
|
List<IncomeCategory> categories = incomeCategoryService.getAllCategories();
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.faf223.expensetrackerfaf.controller.auth;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||||
|
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class JwtAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
|
||||||
|
|
||||||
|
super.onAuthenticationSuccess(request, response, authentication);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.faf223.expensetrackerfaf.controller.auth;
|
||||||
|
|
||||||
|
import com.faf223.expensetrackerfaf.service.AuthenticationService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
|
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class OAuth2SuccessController {
|
||||||
|
|
||||||
|
private final AuthenticationService jwtService;
|
||||||
|
|
||||||
|
@GetMapping()
|
||||||
|
public AuthenticationResponse getUser(@AuthenticationPrincipal OAuth2User oAuth2User) {
|
||||||
|
|
||||||
|
AuthenticationResponse response = jwtService.register(oAuth2User);
|
||||||
|
System.out.println("Response: " + response);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ import org.springframework.security.authentication.AuthenticationManager;
|
|||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -30,6 +31,7 @@ public class AuthenticationService {
|
|||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
private final JwtService jwtService;
|
private final JwtService jwtService;
|
||||||
private final AuthenticationManager authenticationManager;
|
private final AuthenticationManager authenticationManager;
|
||||||
|
private final PasswordGenerator passwordGenerator;
|
||||||
|
|
||||||
public AuthenticationResponse register(RegisterRequest request) {
|
public AuthenticationResponse register(RegisterRequest request) {
|
||||||
|
|
||||||
@@ -53,6 +55,56 @@ public class AuthenticationService {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AuthenticationResponse register(OAuth2User oAuth2User) {
|
||||||
|
String userEmail = oAuth2User.getAttribute("email");
|
||||||
|
|
||||||
|
// Check if the user is already registered
|
||||||
|
Optional<Credential> existingCredential = credentialRepository.findByEmail(userEmail);
|
||||||
|
if (existingCredential.isPresent()) {
|
||||||
|
UserDetails userDetails = new PersonDetails(existingCredential.get());
|
||||||
|
String jwtToken = jwtService.generateToken(userDetails);
|
||||||
|
String refreshToken = jwtService.generateRefreshToken(userDetails);
|
||||||
|
|
||||||
|
return AuthenticationResponse.builder()
|
||||||
|
.accessToken(jwtToken)
|
||||||
|
.refreshToken(refreshToken)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String givenName = oAuth2User.getAttribute("given_name");
|
||||||
|
String familyName = oAuth2User.getAttribute("family_name");
|
||||||
|
String email = oAuth2User.getAttribute("email");
|
||||||
|
|
||||||
|
User user = User.builder()
|
||||||
|
.firstName(givenName)
|
||||||
|
.lastName(familyName)
|
||||||
|
.username(email)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
String randomPassword = passwordGenerator.generateRandomPassword(8);
|
||||||
|
|
||||||
|
user.setPassword(passwordEncoder.encode(randomPassword));
|
||||||
|
|
||||||
|
userRepository.save(user);
|
||||||
|
Credential credential = new Credential(user, email, passwordEncoder.encode(randomPassword));
|
||||||
|
credentialRepository.save(credential);
|
||||||
|
|
||||||
|
UserDetails userDetails = new PersonDetails(credential);
|
||||||
|
String jwtToken = jwtService.generateToken(userDetails);
|
||||||
|
String refreshToken = jwtService.generateRefreshToken(userDetails);
|
||||||
|
|
||||||
|
System.out.println("New user: " + user);
|
||||||
|
System.out.println("New credentials: " + credential);
|
||||||
|
|
||||||
|
return AuthenticationResponse.builder()
|
||||||
|
.accessToken(jwtToken)
|
||||||
|
.refreshToken(refreshToken)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public AuthenticationResponse authenticate(AuthenticationRequest request) {
|
public AuthenticationResponse authenticate(AuthenticationRequest request) {
|
||||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword()));
|
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword()));
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.faf223.expensetrackerfaf.service;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class PasswordGenerator {
|
||||||
|
|
||||||
|
private final SecureRandom secureRandom = new SecureRandom();
|
||||||
|
|
||||||
|
public String generateRandomPassword(int length) {
|
||||||
|
byte[] randomBytes = new byte[length];
|
||||||
|
secureRandom.nextBytes(randomBytes);
|
||||||
|
return Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package com.faf223.expensetrackerfaf.util;
|
||||||
|
|
||||||
|
import com.faf223.expensetrackerfaf.model.IMoneyTransaction;
|
||||||
|
import org.apache.commons.math3.analysis.interpolation.LinearInterpolator;
|
||||||
|
import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DataExtender {
|
||||||
|
|
||||||
|
public static List<BigDecimal> interpolate(List<? extends IMoneyTransaction> transactions) {
|
||||||
|
List<BigDecimal> values = new ArrayList<>();
|
||||||
|
|
||||||
|
// Perform linear interpolation on the amount field
|
||||||
|
if (transactions.size() > 1) {
|
||||||
|
LinearInterpolator interpolator = new LinearInterpolator();
|
||||||
|
|
||||||
|
double[] xValues = new double[transactions.size()];
|
||||||
|
double[] yValues = new double[transactions.size()];
|
||||||
|
|
||||||
|
for (int i = 0; i < transactions.size(); i++) {
|
||||||
|
xValues[i] = i;
|
||||||
|
yValues[i] = transactions.get(i).getAmount().doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
PolynomialSplineFunction splineFunction = interpolator.interpolate(xValues, yValues);
|
||||||
|
|
||||||
|
// Interpolate values between the first and last data points
|
||||||
|
for (int i = 1; i < transactions.size() - 1; i++)
|
||||||
|
values.add(BigDecimal.valueOf(splineFunction.value(i + 0.5))); // Interpolate at the midpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<BigDecimal> extrapolate(List<? extends IMoneyTransaction> transactions, int count) {
|
||||||
|
List<BigDecimal> values = new ArrayList<>();
|
||||||
|
|
||||||
|
// Perform linear extrapolation on the amount field
|
||||||
|
if (transactions.size() > 1) {
|
||||||
|
LinearInterpolator interpolator = new LinearInterpolator();
|
||||||
|
|
||||||
|
double[] xValues = new double[transactions.size()];
|
||||||
|
double[] yValues = new double[transactions.size()];
|
||||||
|
|
||||||
|
for (int i = 0; i < transactions.size(); i++) {
|
||||||
|
xValues[i] = i;
|
||||||
|
yValues[i] = transactions.get(i).getAmount().doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
PolynomialSplineFunction splineFunction = interpolator.interpolate(xValues, yValues);
|
||||||
|
|
||||||
|
// Extrapolate values beyond the last data point
|
||||||
|
for (int i = transactions.size(); i < transactions.size() + count; i++)
|
||||||
|
values.add(BigDecimal.valueOf(splineFunction.value(i))); // Extrapolate by extending the x-axis
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.faf223.expensetrackerfaf.util.exceptions;
|
||||||
|
|
||||||
|
public class RequiredParamMissingException extends RuntimeException {
|
||||||
|
public RequiredParamMissingException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.faf223.expensetrackerfaf.util.exceptions;
|
||||||
|
|
||||||
|
public class WrongParamValueException extends RuntimeException {
|
||||||
|
public WrongParamValueException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user