diff --git a/backend/Dockerfile b/backend/Dockerfile index f016d66..d83eca3 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,4 +1,3 @@ -# Stage 1: Сборка приложения FROM maven:3.8.3-openjdk-17 AS build WORKDIR /app COPY pom.xml . @@ -10,5 +9,4 @@ RUN mvn package -Dmaven.test.skip=true FROM openjdk:17 WORKDIR /app COPY --from=build /app/target/QuickProjectApp-0.0.1-SNAPSHOT.jar /app/QuickProjectApp.jar -CMD ["java", "-jar", "QuickProjectApp.jar"] - +CMD ["java", "-jar", "QuickProjectApp.jar"] \ No newline at end of file diff --git a/backend/QuickProject.postman_collection.json b/backend/QuickProject.postman_collection.json new file mode 100644 index 0000000..cf9b242 --- /dev/null +++ b/backend/QuickProject.postman_collection.json @@ -0,0 +1,116 @@ +{ + "info": { + "_postman_id": "71a90115-759d-4f6b-93e0-a28d0c86f1fd", + "name": "QuickProject", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "29891838" + }, + "item": [ + { + "name": "auth/register", + "request": { + "auth": { + "type": "noauth" + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"email\" : \"ela@mail.ru\",\r\n \"password\" : \"1\",\r\n \"created_at\": \"2024-03-22T18:00:00\",\r\n \"tz\" : \"+3\",\r\n \"enable\" : true\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8081/api/auth/register", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "api", + "auth", + "register" + ] + } + }, + "response": [] + }, + { + "name": "New Request", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"email\" : \"ela@mail.ru\",\r\n \"password\" : \"1\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8081/api/auth/authenticate", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "api", + "auth", + "authenticate" + ] + } + }, + "response": [] + }, + { + "name": "New Request", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:8081/api/users", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "api", + "users" + ] + } + }, + "response": [] + }, + { + "name": "New Request", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:8081/api/projects", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "api", + "projects" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/backend/docker-compose.yaml b/backend/docker-compose.yaml index 205e548..b0df597 100644 --- a/backend/docker-compose.yaml +++ b/backend/docker-compose.yaml @@ -2,21 +2,21 @@ version: '3.8' services: - dev_app: + pet_app: image: pet:dev - container_name: dev_app + container_name: pet_app build: context: . dockerfile: Dockerfile depends_on: - dev_db: + pet_db: condition: service_healthy links: - - dev_db + - pet_db environment: WAIT_HOSTS: dev_db:5432 SPRING_PROFILES_ACTIVE: dev - SPRING_DATASOURCE_URL: jdbc:postgresql://dev_db:5432/pet + SPRING_DATASOURCE_URL: jdbc:postgresql://pet_db:5432/pet SPRING_DATASOURCE_USERNAME: ${DB_USERNAME} SPRING_DATASOURCE_PASSWORD: ${DB_PASSWORD} SPRING_PARENT_DIRECTORY: / @@ -26,9 +26,9 @@ services: volumes: - ${HOST_PARENT_DIRECTORY}:/pet - dev_db: + pet_db: image: postgres - container_name: dev_db + container_name: pet_db environment: POSTGRES_USER: ${DB_USERNAME} POSTGRES_PASSWORD: ${DB_PASSWORD} diff --git a/backend/pom.xml b/backend/pom.xml index 79e229d..996728e 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -8,7 +8,8 @@ spring-boot-starter-parent 3.2.2 - + + com.QuickProject QuickProjectApp 0.0.1-SNAPSHOT @@ -33,7 +34,7 @@ org.springframework.boot - spring-boot-starter-data-rest + spring-boot-starter-security @@ -49,15 +50,27 @@ - org.flywaydb - flyway-core - 9.16.0 + io.jsonwebtoken + jjwt-api + 0.11.5 - org.postgresql - postgresql - runtime + io.jsonwebtoken + jjwt-impl + 0.11.5 + + + + io.jsonwebtoken + jjwt-jackson + 0.11.5 + + + + org.flywaydb + flyway-core + 9.16.0 @@ -69,11 +82,10 @@ spring-boot-starter-data-rest - org.postgresql postgresql - 42.7.1 + 42.7.2 diff --git a/backend/src/main/java/com/QuickProject/QuickProjectApp/auth/AuthenticationController.java b/backend/src/main/java/com/QuickProject/QuickProjectApp/auth/AuthenticationController.java new file mode 100644 index 0000000..8be048f --- /dev/null +++ b/backend/src/main/java/com/QuickProject/QuickProjectApp/auth/AuthenticationController.java @@ -0,0 +1,32 @@ +package com.QuickProject.QuickProjectApp.auth; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/auth") +@RequiredArgsConstructor +public class AuthenticationController { + + private final AuthenticationService service; + + @PostMapping("/register") + public ResponseEntity register( + @RequestBody RegisterRequest request + ) { + return ResponseEntity.ok(service.register(request)); + } + + + @PostMapping("/authenticate") + public ResponseEntity authenticate( + @RequestBody AuthenticationRequest request + ) { + return ResponseEntity.ok(service.authenticate(request)); + } + +} diff --git a/backend/src/main/java/com/QuickProject/QuickProjectApp/auth/AuthenticationRequest.java b/backend/src/main/java/com/QuickProject/QuickProjectApp/auth/AuthenticationRequest.java new file mode 100644 index 0000000..00a3ea1 --- /dev/null +++ b/backend/src/main/java/com/QuickProject/QuickProjectApp/auth/AuthenticationRequest.java @@ -0,0 +1,19 @@ +package com.QuickProject.QuickProjectApp.auth; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.UUID; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class AuthenticationRequest { + + private String email; + + private String password; +} diff --git a/backend/src/main/java/com/QuickProject/QuickProjectApp/auth/AuthenticationResponse.java b/backend/src/main/java/com/QuickProject/QuickProjectApp/auth/AuthenticationResponse.java new file mode 100644 index 0000000..8e9c215 --- /dev/null +++ b/backend/src/main/java/com/QuickProject/QuickProjectApp/auth/AuthenticationResponse.java @@ -0,0 +1,15 @@ +package com.QuickProject.QuickProjectApp.auth; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class AuthenticationResponse { + + private String token; +} diff --git a/backend/src/main/java/com/QuickProject/QuickProjectApp/auth/AuthenticationService.java b/backend/src/main/java/com/QuickProject/QuickProjectApp/auth/AuthenticationService.java new file mode 100644 index 0000000..148670e --- /dev/null +++ b/backend/src/main/java/com/QuickProject/QuickProjectApp/auth/AuthenticationService.java @@ -0,0 +1,54 @@ +package com.QuickProject.QuickProjectApp.auth; + +import com.QuickProject.QuickProjectApp.config.JwtService; +import com.QuickProject.QuickProjectApp.dao.UserRepository; +import com.QuickProject.QuickProjectApp.entity.user.Role; +import com.QuickProject.QuickProjectApp.entity.user.User; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class AuthenticationService { + + private final UserRepository repository; + + private final JwtService jwtService; + + private final PasswordEncoder passwordEncoder; + + private final AuthenticationManager authenticationManager; + + public AuthenticationResponse register(RegisterRequest request) { + var user = User.builder() + .email(request.getEmail()) + .password(passwordEncoder.encode(request.getPassword())) + .role(Role.USER) + .created_at(request.getCreated_at()) + .enable(request.isEnable()) + .tz(request.getTz()) + .build(); + repository.save(user); + var jwtToken = jwtService.generateToken(user); + return AuthenticationResponse.builder() + .token(jwtToken) + .build(); + } + + public AuthenticationResponse authenticate(AuthenticationRequest request) { + authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken( + request.getEmail(), + request.getPassword() + ) + ); + var user = repository.findByEmail(request.getEmail()).orElseThrow(); + var jwtToken = jwtService.generateToken(user); + return AuthenticationResponse.builder() + .token(jwtToken) + .build(); + } +} diff --git a/backend/src/main/java/com/QuickProject/QuickProjectApp/auth/RegisterRequest.java b/backend/src/main/java/com/QuickProject/QuickProjectApp/auth/RegisterRequest.java new file mode 100644 index 0000000..b25ebf1 --- /dev/null +++ b/backend/src/main/java/com/QuickProject/QuickProjectApp/auth/RegisterRequest.java @@ -0,0 +1,29 @@ +package com.QuickProject.QuickProjectApp.auth; + + +import com.QuickProject.QuickProjectApp.entity.user.Role; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class RegisterRequest { + + private String email; + + private String password; + + private LocalDateTime created_at; + + private boolean enable; + + private String tz; + + private final Role user_role = Role.USER; +} diff --git a/backend/src/main/java/com/QuickProject/QuickProjectApp/config/ApplicationConfig.java b/backend/src/main/java/com/QuickProject/QuickProjectApp/config/ApplicationConfig.java new file mode 100644 index 0000000..1d6aa7c --- /dev/null +++ b/backend/src/main/java/com/QuickProject/QuickProjectApp/config/ApplicationConfig.java @@ -0,0 +1,49 @@ +package com.QuickProject.QuickProjectApp.config; + +import com.QuickProject.QuickProjectApp.dao.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.ldap.LdapUserServiceBeanDefinitionParser; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +@RequiredArgsConstructor +public class ApplicationConfig { + + private final UserRepository repository; + + @Bean + public UserDetailsService userDetailsService() { + return username -> repository.findByEmail(username) + .orElseThrow(() -> new UsernameNotFoundException("User not found")); + } + + @Bean + public AuthenticationProvider authenticationProvider() { + DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + authProvider.setUserDetailsService(userDetailsService()); + authProvider.setPasswordEncoder(passwordEncoder()); + return authProvider; + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception { + return configuration.getAuthenticationManager(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/backend/src/main/java/com/QuickProject/QuickProjectApp/config/JwtAuthenticationFilter.java b/backend/src/main/java/com/QuickProject/QuickProjectApp/config/JwtAuthenticationFilter.java new file mode 100644 index 0000000..cb8d5fb --- /dev/null +++ b/backend/src/main/java/com/QuickProject/QuickProjectApp/config/JwtAuthenticationFilter.java @@ -0,0 +1,61 @@ +package com.QuickProject.QuickProjectApp.config; + +import jakarta.annotation.Nonnull; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +@Component +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtService jwtService; + + private final UserDetailsService userDetailsService; + + @Override + protected void doFilterInternal( + @Nonnull HttpServletRequest request, + @Nonnull HttpServletResponse response, + @Nonnull FilterChain filterChain + ) throws ServletException, IOException { + final String authHeader = request.getHeader("Authorization"); + final String jwt; + final String userEmail; + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + filterChain.doFilter(request, response); + return; + } + jwt = authHeader.substring(7); + userEmail = jwtService.extractUsername(jwt); + if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) { + UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail); + if (jwtService.isTokenValid(jwt, userDetails)) { + UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( + userDetails, + null, + userDetails.getAuthorities() + ); + + authToken.setDetails( + new WebAuthenticationDetailsSource().buildDetails(request) + ); + + SecurityContextHolder.getContext().setAuthentication(authToken); + } + } + filterChain.doFilter(request, response); + } +} diff --git a/backend/src/main/java/com/QuickProject/QuickProjectApp/config/JwtService.java b/backend/src/main/java/com/QuickProject/QuickProjectApp/config/JwtService.java new file mode 100644 index 0000000..ae82cc0 --- /dev/null +++ b/backend/src/main/java/com/QuickProject/QuickProjectApp/config/JwtService.java @@ -0,0 +1,74 @@ +package com.QuickProject.QuickProjectApp.config; + +import io.jsonwebtoken.Claims; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Service; + +import java.security.Key; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +@Service +public class JwtService { + private static final String SECRET_KEY = "96c8f1ee65af9fa52e8f7bf75bb308a866fbaca5db8ee702e911818224f40e77150fc6dcf5e6e03ececdf1631017f7194a2c7fb1fe58226e7bc743b8a7e8127c"; + + public String extractUsername(String token) { + return extractClaims(token, Claims::getSubject); + } + + public String generateToken( + Map extraClaims, + UserDetails userDetails + ) { + return Jwts + .builder() + .setClaims(extraClaims) + .setSubject(userDetails.getUsername()) + .setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 24)) + .signWith(getSignKey(), SignatureAlgorithm.HS256) + .compact(); + } + + public boolean isTokenValid(String token, UserDetails userDetails) { + final String username = extractUsername(token); + return username.equals(userDetails.getUsername()) && !isTokenExpired(token); + } + + private boolean isTokenExpired(String token) { + return extractExpiration(token).before(new Date());} + + private Date extractExpiration(String token) { + return extractClaims(token, Claims::getExpiration); + } + + public T extractClaims(String token, Function claimsResolver) { + final Claims claims = extractAllClaims(token); + return claimsResolver.apply(claims); + } + + public String generateToken(UserDetails userDetails) { + return generateToken(new HashMap<>(), userDetails); + } + + private Claims extractAllClaims(String token) { + return Jwts + .parserBuilder() + .setSigningKey(getSignKey()) + .build() + .parseClaimsJws(token) + .getBody(); + } + + private Key getSignKey() { + byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY); + return Keys.hmacShaKeyFor(keyBytes); + } +} diff --git a/backend/src/main/java/com/QuickProject/QuickProjectApp/config/SecurityConfiguration.java b/backend/src/main/java/com/QuickProject/QuickProjectApp/config/SecurityConfiguration.java new file mode 100644 index 0000000..7f8772d --- /dev/null +++ b/backend/src/main/java/com/QuickProject/QuickProjectApp/config/SecurityConfiguration.java @@ -0,0 +1,49 @@ +package com.QuickProject.QuickProjectApp.config; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationProvider; +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.configurers.AbstractHttpConfigurer; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfiguration { + + private final JwtAuthenticationFilter jwtAuthFilter; + private final AuthenticationProvider authenticationProvider; + + private static final String[] WHITE_LIST_URL = {"/api/auth/**", + "/api/users", + "/api/projects", + "/api/journal", + "/api/queue", + "/api/projects_roles"}; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + + http + .csrf(AbstractHttpConfigurer::disable) + .authorizeHttpRequests(req -> + req.requestMatchers(WHITE_LIST_URL) + .permitAll() + .anyRequest() + .authenticated()) + .sessionManagement(session -> session.sessionCreationPolicy(STATELESS)) + .authenticationProvider(authenticationProvider) + .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } +} + + + diff --git a/backend/src/main/java/com/QuickProject/QuickProjectApp/dao/UserRepository.java b/backend/src/main/java/com/QuickProject/QuickProjectApp/dao/UserRepository.java index c5c3b9f..bf9740d 100644 --- a/backend/src/main/java/com/QuickProject/QuickProjectApp/dao/UserRepository.java +++ b/backend/src/main/java/com/QuickProject/QuickProjectApp/dao/UserRepository.java @@ -1,13 +1,13 @@ package com.QuickProject.QuickProjectApp.dao; -import com.QuickProject.QuickProjectApp.entity.User; +import com.QuickProject.QuickProjectApp.entity.user.User; import org.springframework.dao.DataAccessException; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.rest.core.annotation.RepositoryRestResource; -import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.Optional; import java.util.UUID; /** @@ -16,7 +16,5 @@ @RepositoryRestResource(path="users") public interface UserRepository extends JpaRepository { @Transactional(readOnly = true) - List findByEmail(String email) throws DataAccessException; - - + Optional findByEmail(String email) throws DataAccessException; } diff --git a/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/Journal.java b/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/Journal.java index c05dd91..59ac76e 100644 --- a/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/Journal.java +++ b/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/Journal.java @@ -3,6 +3,7 @@ import java.time.LocalDateTime; import java.util.UUID; +import com.QuickProject.QuickProjectApp.entity.user.User; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/Project.java b/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/Project.java index fa50c8a..d90304a 100644 --- a/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/Project.java +++ b/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/Project.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.UUID; +import com.QuickProject.QuickProjectApp.entity.user.User; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/Queue.java b/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/Queue.java index 90c7871..fce1e45 100644 --- a/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/Queue.java +++ b/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/Queue.java @@ -2,6 +2,7 @@ import java.util.UUID; +import com.QuickProject.QuickProjectApp.entity.user.User; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/user/Role.java b/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/user/Role.java new file mode 100644 index 0000000..219ebb6 --- /dev/null +++ b/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/user/Role.java @@ -0,0 +1,6 @@ +package com.QuickProject.QuickProjectApp.entity.user; + +public enum Role { + USER, + ADMIN +} diff --git a/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/User.java b/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/user/User.java similarity index 51% rename from backend/src/main/java/com/QuickProject/QuickProjectApp/entity/User.java rename to backend/src/main/java/com/QuickProject/QuickProjectApp/entity/user/User.java index de6be1d..60fa02c 100644 --- a/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/User.java +++ b/backend/src/main/java/com/QuickProject/QuickProjectApp/entity/user/User.java @@ -1,30 +1,38 @@ -package com.QuickProject.QuickProjectApp.entity; +package com.QuickProject.QuickProjectApp.entity.user; +import com.QuickProject.QuickProjectApp.entity.Journal; +import com.QuickProject.QuickProjectApp.entity.Project; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.hibernate.annotations.UuidGenerator; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.UUID; /** * Класс для работы с сущностью "user" -*/ + */ @Builder @AllArgsConstructor @NoArgsConstructor @Entity @Data @Table(name = "users") -public class User { +public class User implements UserDetails { @Id @Column(name = "id") - private UUID id = UUID.randomUUID(); + @UuidGenerator + private UUID id; @Column(name = "nickname") private String nickname; @@ -41,6 +49,11 @@ public class User { @Column(name = "password") private String password; + @Column(name = "user_role") + @Enumerated(EnumType.STRING) + @Builder.Default + Role role = Role.USER; + @Column(name = "created_at") private LocalDateTime created_at; @@ -66,4 +79,33 @@ public class User { @OneToOne(mappedBy = "creator") private Project project; + @Override + public Collection getAuthorities() { + return List.of(new SimpleGrantedAuthority(role.name())); + } + + @Override + public String getUsername() { + return email; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return enable; + } } diff --git a/backend/src/main/resources/db/migration/V1__Init.sql b/backend/src/main/resources/db/migration/V1__Init.sql index fe858bb..24f23c1 100644 --- a/backend/src/main/resources/db/migration/V1__Init.sql +++ b/backend/src/main/resources/db/migration/V1__Init.sql @@ -1,3 +1,5 @@ +CREATE TYPE role AS ENUM ('USER', 'ADMIN'); + create table if not exists users ( id uuid not null primary key, @@ -5,10 +7,11 @@ create table if not exists users email varchar(100) not null unique, telegram varchar(100) unique, phone varchar(15) unique, - password varchar(30) not null, + password varchar(80) not null, created_at timestamp not null, enable boolean not null default true, project_role varchar(300), + user_role role not null, photo bytea ); diff --git a/backend/src/main/resources/db/migration/V3__Insert_Users.sql b/backend/src/main/resources/db/migration/V3__Insert_Users.sql index 05a4c12..86bb57f 100644 --- a/backend/src/main/resources/db/migration/V3__Insert_Users.sql +++ b/backend/src/main/resources/db/migration/V3__Insert_Users.sql @@ -1,7 +1,7 @@ -INSERT INTO users (id, email, password, created_at, project_role) +INSERT INTO users (id, email, password, created_at, project_role, user_role) VALUES - ('223e4567-e89b-12d3-a456-426614174000', 'test@test.com', '12345678', NOW(), 'Программист'), - ('223e4567-e89b-12d3-a456-426614174001', 'designer@test.com', '123', NOW(), 'Дизайнер'); + ('223e4567-e89b-12d3-a456-426614174000', 'test@test.com', '123', NOW(), 'Программист', 'USER'), + ('223e4567-e89b-12d3-a456-426614174001', 'designer@test.com', '123', NOW(), 'Дизайнер', 'USER'); INSERT INTO projects (id, creator_id, name, description, enable, created_at) VALUES diff --git a/backend/src/test/java/com/QuickProject/QuickProjectApp/ProjectRoleRepositoyTest.java b/backend/src/test/java/com/QuickProject/QuickProjectApp/ProjectRoleRepositoyTest.java index 4679cb4..3536042 100644 --- a/backend/src/test/java/com/QuickProject/QuickProjectApp/ProjectRoleRepositoyTest.java +++ b/backend/src/test/java/com/QuickProject/QuickProjectApp/ProjectRoleRepositoyTest.java @@ -5,7 +5,7 @@ import com.QuickProject.QuickProjectApp.dao.UserRepository; import com.QuickProject.QuickProjectApp.entity.Project; import com.QuickProject.QuickProjectApp.entity.ProjectRole; -import com.QuickProject.QuickProjectApp.entity.User; +import com.QuickProject.QuickProjectApp.entity.user.User; import io.zonky.test.db.AutoConfigureEmbeddedDatabase; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; diff --git a/backend/src/test/java/com/QuickProject/QuickProjectApp/ProjectsRepositoyTest.java b/backend/src/test/java/com/QuickProject/QuickProjectApp/ProjectsRepositoyTest.java index 3591751..7a75a61 100644 --- a/backend/src/test/java/com/QuickProject/QuickProjectApp/ProjectsRepositoyTest.java +++ b/backend/src/test/java/com/QuickProject/QuickProjectApp/ProjectsRepositoyTest.java @@ -3,16 +3,16 @@ import com.QuickProject.QuickProjectApp.dao.ProjectRepository; import com.QuickProject.QuickProjectApp.dao.UserRepository; import com.QuickProject.QuickProjectApp.entity.Project; -import com.QuickProject.QuickProjectApp.entity.User; +import com.QuickProject.QuickProjectApp.entity.user.User; import io.zonky.test.db.AutoConfigureEmbeddedDatabase; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import java.time.LocalDateTime; -import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; + @DataJpaTest @AutoConfigureEmbeddedDatabase(provider = AutoConfigureEmbeddedDatabase.DatabaseProvider.ZONKY) public class ProjectsRepositoyTest { diff --git a/backend/src/test/java/com/QuickProject/QuickProjectApp/UserRepositoyTest.java b/backend/src/test/java/com/QuickProject/QuickProjectApp/UserRepositoyTest.java index 8ed6dad..bac54eb 100644 --- a/backend/src/test/java/com/QuickProject/QuickProjectApp/UserRepositoyTest.java +++ b/backend/src/test/java/com/QuickProject/QuickProjectApp/UserRepositoyTest.java @@ -1,7 +1,7 @@ package com.QuickProject.QuickProjectApp; import com.QuickProject.QuickProjectApp.dao.UserRepository; -import com.QuickProject.QuickProjectApp.entity.User; +import com.QuickProject.QuickProjectApp.entity.user.User; import io.zonky.test.db.AutoConfigureEmbeddedDatabase; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -29,6 +29,6 @@ void saveProject(){ user.setTz("+3"); repository.saveAndFlush(user); - assertThat(repository.findByEmail("test_user@test.com").get(0).getEmail()).isEqualTo("test_user@test.com"); + assertThat(repository.findByEmail("test_user@test.com").get().getEmail()).isEqualTo("test_user@test.com"); } }