Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
<mapstruct.version>1.6.3</mapstruct.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
<maven.checkstyle.plugin.configLocation>checkstyle.xml</maven.checkstyle.plugin.configLocation>
<jjwt.version>0.12.6</jjwt.version>
</properties>
<dependencies>
<dependency>
Expand Down Expand Up @@ -89,6 +90,23 @@
<version>2.3.232</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${jjwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${jjwt.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>${jjwt.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
33 changes: 31 additions & 2 deletions src/main/java/com/senkiv/carsharing/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,29 +1,58 @@
package com.senkiv.carsharing.config;

import com.senkiv.carsharing.security.JwtAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final UserDetailsService userDetailsService;
private final JwtAuthenticationFilter authenticationFilter;

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.cors(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> {
auth.requestMatchers("/cars", "/cars/**").permitAll();
auth.requestMatchers("/cars", "/cars/**", "/auth/**", "/register", "/login",
"/error",
"/v3/api-docs/**",
"/swagger-ui/**").permitAll()
.anyRequest().authenticated();
})
.userDetailsService(userDetailsService)
.addFilterBefore(authenticationFilter, UsernamePasswordAuthenticationFilter.class)
.httpBasic(Customizer.withDefaults())
.sessionManagement(
session -> session.sessionCreationPolicy(
SessionCreationPolicy.STATELESS))
.build();
}

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration)
throws Exception {
return configuration.getAuthenticationManager();
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.senkiv.carsharing.controller;

import com.senkiv.carsharing.dto.UserLoginRequestDto;
import com.senkiv.carsharing.dto.UserLoginResponseDto;
import com.senkiv.carsharing.dto.UserRegistrationRequestDto;
import com.senkiv.carsharing.dto.UserResponseDto;
import com.senkiv.carsharing.service.AuthenticationService;
import com.senkiv.carsharing.service.UserService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class AuthenticationController {
private final AuthenticationService authenticationService;
private final UserService userService;

@PostMapping("/register")
public UserResponseDto register(@RequestBody @Valid UserRegistrationRequestDto dto) {
return userService.register(dto);
}

@PostMapping("/login")
public UserLoginResponseDto authenticate(@RequestBody @Valid UserLoginRequestDto dto) {
return authenticationService.authenticate(dto);
}
}
54 changes: 54 additions & 0 deletions src/main/java/com/senkiv/carsharing/controller/CarController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.senkiv.carsharing.controller;

import com.senkiv.carsharing.dto.CarRequestDto;
import com.senkiv.carsharing.dto.CarResponseDto;
import com.senkiv.carsharing.service.CarService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/cars")
@RequiredArgsConstructor
public class CarController {
private final CarService carService;

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public CarResponseDto createCar(@RequestBody @Valid CarRequestDto requestDto) {
return carService.create(requestDto);
}

@GetMapping
public Page<CarResponseDto> getAllCars(Pageable pageable) {
return carService.getAll(pageable);
}

@GetMapping("/{id}")
public CarResponseDto getCarById(@PathVariable Long id) {
return carService.getById(id);
}

@PutMapping("/{id}")
public CarResponseDto updateCar(@PathVariable Long id,
@RequestBody @Valid CarRequestDto requestDto) {
return carService.update(id, requestDto);
}

@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteCar(@PathVariable Long id) {
carService.delete(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.senkiv.carsharing.controller;

import jakarta.persistence.EntityNotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;

@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(EntityNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public void handleEntityNotFoundException() {
// Just return the status code
}
}
47 changes: 47 additions & 0 deletions src/main/java/com/senkiv/carsharing/controller/UserController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.senkiv.carsharing.controller;

import com.senkiv.carsharing.dto.UserProfileUpdateRequestDto;
import com.senkiv.carsharing.dto.UserResponseDto;
import com.senkiv.carsharing.dto.UserRoleUpdateRequestDto;
import com.senkiv.carsharing.service.UserService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;

@PutMapping("/{id}/role")
@PreAuthorize("hasRole('ROLE_MANAGER')")
public UserResponseDto updateRole(@PathVariable Long id,
@RequestBody @Valid UserRoleUpdateRequestDto requestDto) {
return userService.updateRole(id, requestDto);
}

@GetMapping("/me")
public UserResponseDto getMyProfile() {
return userService.getCurrentUser();
}

@PutMapping("/me")
public UserResponseDto updateProfile(
@RequestBody @Valid UserProfileUpdateRequestDto requestDto) {
return userService.updateProfile(requestDto);
}

@PatchMapping("/me")
public UserResponseDto patchProfile(
@RequestBody @Valid UserProfileUpdateRequestDto requestDto) {
return userService.updateProfile(requestDto);
}
}
24 changes: 24 additions & 0 deletions src/main/java/com/senkiv/carsharing/dto/CarRequestDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.senkiv.carsharing.dto;

import com.senkiv.carsharing.model.CarType;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import java.math.BigDecimal;

public record CarRequestDto(
@NotBlank
String model,
@NotBlank
String brand,
@NotNull
CarType type,
@NotNull
@Min(0)
Integer inventory,
@NotNull
@Positive
BigDecimal dailyFee
) {
}
14 changes: 14 additions & 0 deletions src/main/java/com/senkiv/carsharing/dto/CarResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.senkiv.carsharing.dto;

import com.senkiv.carsharing.model.CarType;
import java.math.BigDecimal;

public record CarResponseDto(
Long id,
String model,
String brand,
CarType type,
Integer inventory,
BigDecimal dailyFee
) {
}
14 changes: 14 additions & 0 deletions src/main/java/com/senkiv/carsharing/dto/UserLoginRequestDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.senkiv.carsharing.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public record UserLoginRequestDto(
@Email
@NotNull
String email,
@NotBlank
String password
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.senkiv.carsharing.dto;

public record UserLoginResponseDto(
String token
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.senkiv.carsharing.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public record UserProfileUpdateRequestDto(
@NotBlank
String firstName,
@NotBlank
String lastName,
@Email
@NotNull
String email
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.senkiv.carsharing.dto;

import com.senkiv.carsharing.validation.FieldMatch;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

@FieldMatch(first = "password", second = "repeatedPassword")
public record UserRegistrationRequestDto(
@NotBlank
String firstName,
@NotBlank
String lastName,
@Email
@NotNull
String email,
@NotBlank
String password,
@NotBlank
String repeatedPassword
) {

}
13 changes: 13 additions & 0 deletions src/main/java/com/senkiv/carsharing/dto/UserResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.senkiv.carsharing.dto;

import com.senkiv.carsharing.model.Role.RoleName;
import java.util.Set;

public record UserResponseDto(
Long id,
String firstName,
String lastName,
String email,
Set<RoleName> roles
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.senkiv.carsharing.dto;

import com.senkiv.carsharing.model.Role.RoleName;
import jakarta.validation.constraints.NotNull;

public record UserRoleUpdateRequestDto(
@NotNull
RoleName roleName
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.senkiv.carsharing.exception;

public class RegistrationException extends RuntimeException {
public RegistrationException(String message) {
super(message);
}
}
17 changes: 17 additions & 0 deletions src/main/java/com/senkiv/carsharing/mapper/CarMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.senkiv.carsharing.mapper;

import com.senkiv.carsharing.config.MapperConfig;
import com.senkiv.carsharing.dto.CarRequestDto;
import com.senkiv.carsharing.dto.CarResponseDto;
import com.senkiv.carsharing.model.Car;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;

@Mapper(config = MapperConfig.class)
public interface CarMapper {
Car toModel(CarRequestDto dto);

CarResponseDto toDto(Car car);

Car update(CarRequestDto carRequestDto, @MappingTarget Car car);
}
Loading