diff --git a/README.md b/README.md index f236ed5..01d6cef 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,20 @@ Engineering Thesis For production environment, you need to have an oracle database. If you don't have a created an oracle database yet, run script `database/install.sql`. + ## Profiles -`swagger` - to run application with Swagger +`swagger` - to run application with Swagger +`dev` - to run application with H2 database (for offline development) ## Swagger To run swagger use `swagger` profile. Link to swagger documentation: http://localhost:8080/swagger-ui/index.html +## Offline development && Testing +To develop application offline please use `dev` profile - it uses h2 database instead of oracle database that is used on production. +img.png +No other configurations are needed. + ## Environment variables `DB_URL` - database url `DB_USERNAME` - database username diff --git a/backend/src/main/java/meowhub/backend/posts/services/impl/PostServiceImpl.java b/backend/src/main/java/meowhub/backend/posts/services/impl/PostServiceImpl.java index 7350d0a..f72d1cd 100644 --- a/backend/src/main/java/meowhub/backend/posts/services/impl/PostServiceImpl.java +++ b/backend/src/main/java/meowhub/backend/posts/services/impl/PostServiceImpl.java @@ -6,13 +6,12 @@ import meowhub.backend.posts.services.PostService; import meowhub.backend.shared.constants.AlertConstants; import meowhub.backend.users.dtos.BasicUserInfoDto; +import meowhub.backend.users.facades.UserPostServiceFacade; import meowhub.backend.users.models.User; import meowhub.backend.posts.repositories.PostRepository; -import meowhub.backend.users.repositories.UserRepository; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import org.webjars.NotFoundException; @@ -21,8 +20,8 @@ @Service @RequiredArgsConstructor public class PostServiceImpl implements PostService { + private final UserPostServiceFacade userPostServiceFacade; private final PostRepository postRepository; - private final UserRepository userRepository; @Override public Page getPosts(String requestedBy, int pageNo, int pageSize) { @@ -33,8 +32,7 @@ public Page getPosts(String requestedBy, int pageNo, int pageSize) { @Override public Page getPostsForUser(String login, String requestedBy, int pageNo, int pageSize) { Pageable pageable = PageRequest.of(pageNo, pageSize); - userRepository.findByLogin(login) - .orElseThrow(() -> new UsernameNotFoundException(String.format(AlertConstants.USER_WITH_LOGIN_NOT_FOUND, login))); + userPostServiceFacade.validateIfUserExists(login); if (login.equals(requestedBy)) { return postRepository.findOwn(login, pageable); @@ -45,8 +43,7 @@ public Page getPostsForUser(String login, String requestedBy, int pageN @Override public PostDto createPost(String login, String content) { - User postOwner = userRepository.findByLogin(login) - .orElseThrow(() -> new UsernameNotFoundException(String.format(AlertConstants.USER_WITH_LOGIN_NOT_FOUND, login))); + User postOwner = userPostServiceFacade.findUserByLogin(login); Post post = new Post(); post.setContentHtml(content); post.setUser(postOwner); @@ -69,8 +66,7 @@ public void deletePost(String login, String postId) { } private Post findUserPost(String login, String postId) { - userRepository.findByLogin(login) - .orElseThrow(() -> new UsernameNotFoundException(String.format(AlertConstants.USER_WITH_LOGIN_NOT_FOUND, login))); + userPostServiceFacade.validateIfUserExists(login); return postRepository.findByUserLoginAndId(login, postId) .orElseThrow(() -> new NotFoundException(String.format(AlertConstants.RESOURCE_NOT_FOUND, "post", "id", postId))); @@ -81,9 +77,7 @@ private PostDto convertToPostDto(Post post) { return null; } - String login = post.getUser().getLogin(); - BasicUserInfoDto author = userRepository.findBasicUserInfoByLogin(login) - .orElseThrow(() -> new UsernameNotFoundException(String.format(AlertConstants.USER_WITH_LOGIN_NOT_FOUND, login))); + BasicUserInfoDto author = userPostServiceFacade.getBasicUserInfo(post.getUser().getLogin()); return PostDto.builder() .id(post.getId()) diff --git a/backend/src/main/java/meowhub/backend/security/SecurityConfig.java b/backend/src/main/java/meowhub/backend/security/SecurityConfig.java index 5d07684..3779c13 100644 --- a/backend/src/main/java/meowhub/backend/security/SecurityConfig.java +++ b/backend/src/main/java/meowhub/backend/security/SecurityConfig.java @@ -1,16 +1,8 @@ package meowhub.backend.security; import meowhub.backend.constants.Genders; -import meowhub.backend.constants.PrivacySettings; import meowhub.backend.constants.Roles; -import meowhub.backend.users.models.Gender; -import meowhub.backend.users.models.PrivacySetting; -import meowhub.backend.users.models.Role; -import meowhub.backend.users.models.User; -import meowhub.backend.users.repositories.PrivacySettingRepository; -import meowhub.backend.users.repositories.GenderRepository; -import meowhub.backend.users.repositories.RoleRepository; -import meowhub.backend.users.repositories.UserRepository; +import meowhub.backend.users.facades.UserAuthServiceFacade; import meowhub.backend.security.jwt.AuthEntryPointJwt; import meowhub.backend.security.jwt.AuthTokenFilter; import org.springframework.boot.CommandLineRunner; @@ -28,7 +20,6 @@ import org.springframework.security.web.csrf.CookieCsrfTokenRepository; import java.time.LocalDate; -import java.time.LocalDateTime; @Configuration @EnableWebSecurity @@ -60,59 +51,16 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, AuthEntryPoint } @Bean - public CommandLineRunner initData(RoleRepository roleRepository, UserRepository userRepository, PasswordEncoder passwordEncoder, PrivacySettingRepository privacySettingRepository, GenderRepository genderRepository) { + public CommandLineRunner initData(UserAuthServiceFacade userAuthServiceFacade) { return args -> { - Role userRole = roleRepository.findByCode(Roles.ROLE_USER.name()) - .orElseGet(() -> roleRepository.save(new Role(Roles.ROLE_USER))); - Role adminRole = roleRepository.findByCode(Roles.ROLE_ADMIN.name()) - .orElseGet(() -> roleRepository.save(new Role(Roles.ROLE_ADMIN))); - - PrivacySetting publicSetting = privacySettingRepository.findByCode(PrivacySettings.PUBLIC.name()) - .orElseGet(() -> privacySettingRepository.save(new PrivacySetting(PrivacySettings.PUBLIC))); - - Gender female = genderRepository.findByCode(Genders.FEMALE.name()) - .orElseGet(() -> genderRepository.save(new Gender(Genders.FEMALE))); - - if (!userRepository.existsByLogin("user1")) { - User user1 = new User(); - user1.setLogin("user1"); - user1.setPassword(passwordEncoder.encode("password1")); - user1.setEmail("user1@example.com"); - user1.setName("Jan"); - user1.setSurname("Kos"); - user1.setSalt("salt"); - user1.setAccountNonLocked(false); - user1.setBirthdate(LocalDate.of(1990, 1, 1)); - user1.setCredentialsNonExpired(true); - user1.setCredentialsExpiryDate(LocalDateTime.now().plusYears(1)); - user1.setRole(userRole); - user1.setPostsPrivacy(publicSetting); - user1.setFriendsPrivacy(publicSetting); - user1.setProfilePrivacy(publicSetting); - user1.setGender(female); - userRepository.save(user1); + if (!userAuthServiceFacade.existsByLogin("user1")) { + userAuthServiceFacade.createUser("user1", "Gustaw", "Jeleń", "user1@gmail.com", "userPass", LocalDate.of(1970, 10, 14), Roles.ROLE_USER, Genders.MALE); } - if (!userRepository.existsByLogin("admin")) { - User admin = new User(); - admin.setLogin("admin"); - admin.setPassword(passwordEncoder.encode("adminPass")); - admin.setEmail("admin@example.com"); - admin.setName("Gustaw"); - admin.setSurname("Jeleń"); - admin.setSalt("salt"); - admin.setAccountNonLocked(false); - admin.setBirthdate(LocalDate.of(1980, 1, 1)); - admin.setCredentialsNonExpired(true); - admin.setCredentialsExpiryDate(LocalDateTime.now().plusYears(1)); - admin.setRole(adminRole); - admin.setPostsPrivacy(publicSetting); - admin.setFriendsPrivacy(publicSetting); - admin.setProfilePrivacy(publicSetting); - admin.setGender(female); - userRepository.save(admin); - } + if (!userAuthServiceFacade.existsByLogin("admin")) { + userAuthServiceFacade.createUser("admin", "Jan", "Kos", "admin@gmail.com", "adminPass", LocalDate.of(1979, 12, 11), Roles.ROLE_ADMIN, Genders.MALE); + } }; } diff --git a/backend/src/main/java/meowhub/backend/security/controllers/AdminController.java b/backend/src/main/java/meowhub/backend/security/controllers/AdminController.java index 6191f97..439bb70 100644 --- a/backend/src/main/java/meowhub/backend/security/controllers/AdminController.java +++ b/backend/src/main/java/meowhub/backend/security/controllers/AdminController.java @@ -2,10 +2,9 @@ import lombok.RequiredArgsConstructor; import meowhub.backend.dtos.UserDto; -import meowhub.backend.users.services.UserService; +import meowhub.backend.users.facades.UserAdminServiceFacade; import org.springframework.http.ResponseEntity; 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.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -17,24 +16,17 @@ @RequestMapping("/api/admin") @RequiredArgsConstructor public class AdminController { - private final UserService userService; + private final UserAdminServiceFacade userAdminServiceFacade; @GetMapping("/all-users") public ResponseEntity> getAllUsers() { - List allUsers = userService.getAllUsers(); + List allUsers = userAdminServiceFacade.getAllUsers(); return ResponseEntity.ok(allUsers); } @PostMapping("/user/change-role") public ResponseEntity changeUserRole(@RequestParam String userId, @RequestParam String roleName) { - userService.changeUserRole(userId, roleName); + userAdminServiceFacade.changeUserRole(userId, roleName); return ResponseEntity.ok("Role updated"); } - - @GetMapping("/user/{userId}") - public ResponseEntity getUserById(@PathVariable String userId) { - UserDto user = userService.getUserById(userId); - return ResponseEntity.ok(user); - } - } diff --git a/backend/src/main/java/meowhub/backend/security/services/impl/AuthServiceImpl.java b/backend/src/main/java/meowhub/backend/security/services/impl/AuthServiceImpl.java index d3d251a..c4c9a29 100644 --- a/backend/src/main/java/meowhub/backend/security/services/impl/AuthServiceImpl.java +++ b/backend/src/main/java/meowhub/backend/security/services/impl/AuthServiceImpl.java @@ -1,18 +1,10 @@ package meowhub.backend.security.services.impl; import lombok.RequiredArgsConstructor; -import meowhub.backend.constants.PrivacySettings; import meowhub.backend.constants.Roles; import meowhub.backend.shared.constants.AlertConstants; import meowhub.backend.shared.exceptions.NotUniqueObjectException; -import meowhub.backend.users.models.Gender; -import meowhub.backend.users.models.PrivacySetting; -import meowhub.backend.users.models.Role; -import meowhub.backend.users.models.User; -import meowhub.backend.users.repositories.GenderRepository; -import meowhub.backend.users.repositories.PrivacySettingRepository; -import meowhub.backend.users.repositories.RoleRepository; -import meowhub.backend.users.repositories.UserRepository; +import meowhub.backend.users.facades.UserAuthServiceFacade; import meowhub.backend.security.jwt.JwtUtils; import meowhub.backend.security.requests.LoginRequest; import meowhub.backend.security.requests.SignUpRequest; @@ -26,7 +18,6 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import java.util.List; @@ -36,11 +27,7 @@ public class AuthServiceImpl implements AuthService { private final JwtUtils jwtUtils; private final AuthenticationManager authenticationManager; - private final UserRepository userRepository; - private final RoleRepository roleRepository; - private final PrivacySettingRepository privacySettingRepository; - private final GenderRepository genderRepository; - private final PasswordEncoder passwordEncoder; + private final UserAuthServiceFacade userAuthServiceFacade; @Override public LoginResponse authenticateUser(LoginRequest request) { @@ -64,42 +51,16 @@ public LoginResponse authenticateUser(LoginRequest request) { @Override public void signUpUser(SignUpRequest request) { validateSignUpRequest(request); - - Role userRole = roleRepository.findByCode(Roles.ROLE_USER.name()) - .orElseGet(() -> roleRepository.save(new Role(Roles.ROLE_USER))); - - Gender gender = genderRepository.findByCode(request.getGender().name()) - .orElseThrow(() -> new IllegalArgumentException(String.format(AlertConstants.RESOURCE_NOT_FOUND, "gender", "gender.code", request.getGender()))); - - PrivacySetting publicSettings = privacySettingRepository.findByCode(PrivacySettings.PUBLIC.name()) - .orElseGet(() -> privacySettingRepository.save(new PrivacySetting(PrivacySettings.PUBLIC))); - - User user = User.builder() - .email(request.getEmail()) - .name(request.getName()) - .surname(request.getSurname()) - .login(request.getLogin()) - .birthdate(request.getBirthdate()) - .gender(gender) - .profilePrivacy(publicSettings) - .postsPrivacy(publicSettings) - .friendsPrivacy(publicSettings) - .birthdate(request.getBirthdate()) - .salt("salt") - .password(passwordEncoder.encode(request.getPassword())) - .role(userRole) - .build(); - - userRepository.save(user); + userAuthServiceFacade.createUser(request.getLogin(), request.getName(), request.getSurname(), request.getEmail(), request.getPassword(), request.getBirthdate(), Roles.ROLE_USER, request.getGender()); } private void validateSignUpRequest(SignUpRequest request) { - boolean isLoginNotUnique = userRepository.existsByLogin(request.getLogin()); + boolean isLoginNotUnique = userAuthServiceFacade.existsByLogin(request.getLogin()); if (isLoginNotUnique) { throw new NotUniqueObjectException(String.format(AlertConstants.NOT_UNIQUE_OBJECT, "login", request.getLogin())); } - boolean isEmailNotUnique = userRepository.existsByEmail(request.getEmail()); + boolean isEmailNotUnique = userAuthServiceFacade.existsByEmail(request.getEmail()); if (isEmailNotUnique) { throw new NotUniqueObjectException(String.format(AlertConstants.NOT_UNIQUE_OBJECT, "email", request.getEmail())); } diff --git a/backend/src/main/java/meowhub/backend/security/services/impl/UserDetailsServiceImpl.java b/backend/src/main/java/meowhub/backend/security/services/impl/UserDetailsServiceImpl.java index ea49689..a41cd3a 100644 --- a/backend/src/main/java/meowhub/backend/security/services/impl/UserDetailsServiceImpl.java +++ b/backend/src/main/java/meowhub/backend/security/services/impl/UserDetailsServiceImpl.java @@ -2,9 +2,8 @@ import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; -import meowhub.backend.shared.constants.AlertConstants; import meowhub.backend.users.models.User; -import meowhub.backend.users.repositories.UserRepository; +import meowhub.backend.users.services.UserQueryService; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -13,13 +12,12 @@ @Service @RequiredArgsConstructor public class UserDetailsServiceImpl implements UserDetailsService { - private final UserRepository userRepository; + private final UserQueryService userQueryService; @Override @Transactional public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - User user = userRepository.findByLogin(username) - .orElseThrow(() -> new UsernameNotFoundException(String.format(AlertConstants.USER_WITH_LOGIN_NOT_FOUND, username))); + User user = userQueryService.findUserByLogin(username); return UserDetailsImpl.build(user); } diff --git a/backend/src/main/java/meowhub/backend/users/controllers/UserController.java b/backend/src/main/java/meowhub/backend/users/controllers/UserController.java index c8a6d70..38da40a 100644 --- a/backend/src/main/java/meowhub/backend/users/controllers/UserController.java +++ b/backend/src/main/java/meowhub/backend/users/controllers/UserController.java @@ -2,7 +2,7 @@ import lombok.RequiredArgsConstructor; import meowhub.backend.users.dtos.BasicUserInfoDto; -import meowhub.backend.users.services.UserService; +import meowhub.backend.users.services.UserQueryService; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -12,10 +12,10 @@ @RequestMapping("/api/users") @RequiredArgsConstructor public class UserController { - private final UserService userService; + private final UserQueryService userQueryService; @GetMapping("basic-user-info") public ResponseEntity getBasicUserInfoTemp(String login) { - return ResponseEntity.ok(userService.getBasicUserInfo(login)); + return ResponseEntity.ok(userQueryService.getBasicUserInfo(login)); } } diff --git a/backend/src/main/java/meowhub/backend/users/facades/UserAdminServiceFacade.java b/backend/src/main/java/meowhub/backend/users/facades/UserAdminServiceFacade.java new file mode 100644 index 0000000..d79c877 --- /dev/null +++ b/backend/src/main/java/meowhub/backend/users/facades/UserAdminServiceFacade.java @@ -0,0 +1,11 @@ +package meowhub.backend.users.facades; + +import meowhub.backend.dtos.UserDto; + +import java.util.List; + +public interface UserAdminServiceFacade { + void changeUserRole(String userId, String roleName); + + List getAllUsers(); +} diff --git a/backend/src/main/java/meowhub/backend/users/facades/UserAuthServiceFacade.java b/backend/src/main/java/meowhub/backend/users/facades/UserAuthServiceFacade.java new file mode 100644 index 0000000..73fee01 --- /dev/null +++ b/backend/src/main/java/meowhub/backend/users/facades/UserAuthServiceFacade.java @@ -0,0 +1,15 @@ +package meowhub.backend.users.facades; + +import meowhub.backend.constants.Genders; +import meowhub.backend.constants.Roles; +import meowhub.backend.users.models.User; + +import java.time.LocalDate; + +public interface UserAuthServiceFacade { + User createUser(String login, String name, String surname, String email, String password, LocalDate birthdate, Roles role, Genders gender); + + boolean existsByLogin(String login); + + boolean existsByEmail(String email); +} diff --git a/backend/src/main/java/meowhub/backend/users/facades/UserPostServiceFacade.java b/backend/src/main/java/meowhub/backend/users/facades/UserPostServiceFacade.java new file mode 100644 index 0000000..828d9fe --- /dev/null +++ b/backend/src/main/java/meowhub/backend/users/facades/UserPostServiceFacade.java @@ -0,0 +1,12 @@ +package meowhub.backend.users.facades; + +import meowhub.backend.users.dtos.BasicUserInfoDto; +import meowhub.backend.users.models.User; + +public interface UserPostServiceFacade { + void validateIfUserExists(String login); + + User findUserByLogin(String login); + + BasicUserInfoDto getBasicUserInfo(String login); +} diff --git a/backend/src/main/java/meowhub/backend/users/facades/impl/UserAdminServiceFacadeImpl.java b/backend/src/main/java/meowhub/backend/users/facades/impl/UserAdminServiceFacadeImpl.java new file mode 100644 index 0000000..05b8abb --- /dev/null +++ b/backend/src/main/java/meowhub/backend/users/facades/impl/UserAdminServiceFacadeImpl.java @@ -0,0 +1,29 @@ +package meowhub.backend.users.facades.impl; + +import jakarta.annotation.security.RolesAllowed; +import lombok.RequiredArgsConstructor; +import meowhub.backend.dtos.UserDto; +import meowhub.backend.users.facades.UserAdminServiceFacade; +import meowhub.backend.users.services.UserQueryService; +import meowhub.backend.users.services.UserService; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RolesAllowed("ROLE_ADMIN") +@RequiredArgsConstructor +public class UserAdminServiceFacadeImpl implements UserAdminServiceFacade { + private final UserService userService; + private final UserQueryService userQueryService; + + @Override + public void changeUserRole(String userId, String roleName) { + userService.changeUserRole(userId, roleName); + } + + @Override + public List getAllUsers() { + return userQueryService.getAllUsers(); + } +} diff --git a/backend/src/main/java/meowhub/backend/users/facades/impl/UserAuthServiceFacadeImpl.java b/backend/src/main/java/meowhub/backend/users/facades/impl/UserAuthServiceFacadeImpl.java new file mode 100644 index 0000000..26d7e6c --- /dev/null +++ b/backend/src/main/java/meowhub/backend/users/facades/impl/UserAuthServiceFacadeImpl.java @@ -0,0 +1,35 @@ +package meowhub.backend.users.facades.impl; + +import lombok.RequiredArgsConstructor; +import meowhub.backend.constants.Genders; +import meowhub.backend.constants.Roles; +import meowhub.backend.users.facades.UserAuthServiceFacade; +import meowhub.backend.users.models.User; +import meowhub.backend.users.services.UserService; +import meowhub.backend.users.services.UserValidationService; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; + +@RequiredArgsConstructor +@Service +public class UserAuthServiceFacadeImpl implements UserAuthServiceFacade { + private final UserService userService; + private final UserValidationService userValidationService; + + @Override + public User createUser(String login, String name, String surname, String email, String password, LocalDate birthdate, Roles role, Genders gender) { + return userService.createUser(login, name, surname, email, password, birthdate, role, gender); + } + + @Override + public boolean existsByLogin(String login) { + return userValidationService.existsByLogin(login); + } + + + @Override + public boolean existsByEmail(String email) { + return userValidationService.existsByEmail(email); + } +} diff --git a/backend/src/main/java/meowhub/backend/users/facades/impl/UserPostServiceFacadeImpl.java b/backend/src/main/java/meowhub/backend/users/facades/impl/UserPostServiceFacadeImpl.java new file mode 100644 index 0000000..4f2b6e1 --- /dev/null +++ b/backend/src/main/java/meowhub/backend/users/facades/impl/UserPostServiceFacadeImpl.java @@ -0,0 +1,31 @@ +package meowhub.backend.users.facades.impl; + +import lombok.RequiredArgsConstructor; +import meowhub.backend.users.dtos.BasicUserInfoDto; +import meowhub.backend.users.facades.UserPostServiceFacade; +import meowhub.backend.users.models.User; +import meowhub.backend.users.services.UserQueryService; +import meowhub.backend.users.services.UserValidationService; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class UserPostServiceFacadeImpl implements UserPostServiceFacade { + private final UserValidationService userValidationService; + private final UserQueryService userQueryService; + + @Override + public void validateIfUserExists(String login) { + userValidationService.validateIfUserExists(login); + } + + @Override + public User findUserByLogin(String login) { + return userQueryService.findUserByLogin(login); + } + + @Override + public BasicUserInfoDto getBasicUserInfo(String login) { + return userQueryService.getBasicUserInfo(login); + } +} diff --git a/backend/src/main/java/meowhub/backend/users/models/HUser.java b/backend/src/main/java/meowhub/backend/users/models/HUser.java index 2ebf338..50430b7 100644 --- a/backend/src/main/java/meowhub/backend/users/models/HUser.java +++ b/backend/src/main/java/meowhub/backend/users/models/HUser.java @@ -61,11 +61,6 @@ public class HUser { @Column(name = "PASSWORD", nullable = false, length = 120) private String password; - @Size(max = 20) - @NotNull - @Column(name = "SALT", nullable = false, length = 20) - private String salt; - @NotNull @Column(name = "BIRTHDATE", nullable = false) private LocalDate birthdate; diff --git a/backend/src/main/java/meowhub/backend/users/models/User.java b/backend/src/main/java/meowhub/backend/users/models/User.java index d707127..86f6a25 100644 --- a/backend/src/main/java/meowhub/backend/users/models/User.java +++ b/backend/src/main/java/meowhub/backend/users/models/User.java @@ -77,11 +77,6 @@ public class User { @Column(name = "PASSWORD", nullable = false, length = 120) private String password; - @Size(max = 20) - @NotNull - @Column(name = "SALT", nullable = false, length = 20) - private String salt; - @NotNull @Column(name = "BIRTHDATE", nullable = false) private LocalDate birthdate; diff --git a/backend/src/main/java/meowhub/backend/users/services/UserQueryService.java b/backend/src/main/java/meowhub/backend/users/services/UserQueryService.java new file mode 100644 index 0000000..6fbed04 --- /dev/null +++ b/backend/src/main/java/meowhub/backend/users/services/UserQueryService.java @@ -0,0 +1,15 @@ +package meowhub.backend.users.services; + +import meowhub.backend.dtos.UserDto; +import meowhub.backend.users.dtos.BasicUserInfoDto; +import meowhub.backend.users.models.User; + +import java.util.List; + +public interface UserQueryService { + List getAllUsers(); + + BasicUserInfoDto getBasicUserInfo(String login); + + User findUserByLogin(String login); +} diff --git a/backend/src/main/java/meowhub/backend/users/services/UserService.java b/backend/src/main/java/meowhub/backend/users/services/UserService.java index d0b592b..9db999a 100644 --- a/backend/src/main/java/meowhub/backend/users/services/UserService.java +++ b/backend/src/main/java/meowhub/backend/users/services/UserService.java @@ -1,16 +1,13 @@ package meowhub.backend.users.services; -import meowhub.backend.dtos.UserDto; -import meowhub.backend.users.dtos.BasicUserInfoDto; +import meowhub.backend.constants.Genders; +import meowhub.backend.constants.Roles; +import meowhub.backend.users.models.User; -import java.util.List; +import java.time.LocalDate; public interface UserService { - List getAllUsers(); - - UserDto getUserById(String userId); - void changeUserRole(String userId, String roleName); - BasicUserInfoDto getBasicUserInfo(String login); + User createUser(String login, String name, String surname, String email, String password, LocalDate birthdate, Roles role, Genders gender); } diff --git a/backend/src/main/java/meowhub/backend/users/services/UserValidationService.java b/backend/src/main/java/meowhub/backend/users/services/UserValidationService.java new file mode 100644 index 0000000..68258a1 --- /dev/null +++ b/backend/src/main/java/meowhub/backend/users/services/UserValidationService.java @@ -0,0 +1,10 @@ +package meowhub.backend.users.services; + + +public interface UserValidationService { + boolean existsByEmail(String email); + + boolean existsByLogin(String login); + + void validateIfUserExists(String login); +} diff --git a/backend/src/main/java/meowhub/backend/users/services/impl/UserQueryServiceImpl.java b/backend/src/main/java/meowhub/backend/users/services/impl/UserQueryServiceImpl.java new file mode 100644 index 0000000..d1d37aa --- /dev/null +++ b/backend/src/main/java/meowhub/backend/users/services/impl/UserQueryServiceImpl.java @@ -0,0 +1,59 @@ +package meowhub.backend.users.services.impl; + +import jakarta.annotation.security.RolesAllowed; +import lombok.RequiredArgsConstructor; +import meowhub.backend.constants.Genders; +import meowhub.backend.constants.Roles; +import meowhub.backend.dtos.UserDto; +import meowhub.backend.shared.constants.AlertConstants; +import meowhub.backend.users.dtos.BasicUserInfoDto; +import meowhub.backend.users.models.User; +import meowhub.backend.users.repositories.UserRepository; +import meowhub.backend.users.services.UserQueryService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class UserQueryServiceImpl implements UserQueryService { + private final UserRepository userRepository; + + @Override + public BasicUserInfoDto getBasicUserInfo(String login) { + return userRepository.findBasicUserInfoByLogin(login) + .orElseThrow(() -> new UsernameNotFoundException(String.format(AlertConstants.USER_WITH_LOGIN_NOT_FOUND, login))); + } + + @RolesAllowed("ROLE_ADMIN") + @Override + public List getAllUsers() { + return userRepository.findAll().stream().map(this::mapToUserDto).toList(); + } + + @Override + public User findUserByLogin(String login) { + return userRepository.findByLogin(login) + .orElseThrow(() -> new UsernameNotFoundException(String.format(AlertConstants.USER_WITH_LOGIN_NOT_FOUND, login))); + } + + private UserDto mapToUserDto(User user) { + if (user == null) throw new NullPointerException(); + + Genders gender = Genders.valueOf(user.getGender().getCode()); + Roles role = Roles.valueOf(user.getRole().getCode()); + + return UserDto.builder() + .userId(user.getId()) + .login(user.getLogin()) + .email(user.getEmail()) + .name(user.getName()) + .surname(user.getSurname()) + .birthdate(user.getBirthdate()) + .gender(gender) + .createdAt(user.getCreatedAt()) + .role(role) + .build(); + } +} diff --git a/backend/src/main/java/meowhub/backend/users/services/impl/UserServiceImpl.java b/backend/src/main/java/meowhub/backend/users/services/impl/UserServiceImpl.java index 3bbfa3c..bc9af34 100644 --- a/backend/src/main/java/meowhub/backend/users/services/impl/UserServiceImpl.java +++ b/backend/src/main/java/meowhub/backend/users/services/impl/UserServiceImpl.java @@ -2,42 +2,35 @@ import lombok.RequiredArgsConstructor; import meowhub.backend.constants.Genders; +import meowhub.backend.constants.PrivacySettings; import meowhub.backend.constants.Roles; -import meowhub.backend.dtos.UserDto; -import meowhub.backend.shared.constants.AlertConstants; -import meowhub.backend.users.dtos.BasicUserInfoDto; +import meowhub.backend.users.models.Gender; +import meowhub.backend.users.models.PrivacySetting; import meowhub.backend.users.models.Role; import meowhub.backend.users.models.User; +import meowhub.backend.users.repositories.GenderRepository; +import meowhub.backend.users.repositories.PrivacySettingRepository; import meowhub.backend.users.repositories.RoleRepository; import meowhub.backend.users.repositories.UserRepository; import meowhub.backend.users.services.UserService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; -import java.util.List; +import java.time.LocalDate; +import java.time.LocalDateTime; @Service @RequiredArgsConstructor public class UserServiceImpl implements UserService { private final UserRepository userRepository; private final RoleRepository roleRepository; + private final PrivacySettingRepository privacySettingRepository; + private final GenderRepository genderRepository; + private final PasswordEncoder passwordEncoder; @Override - public List getAllUsers() { - return userRepository.findAll().stream().map(this::mapToUserDto).toList(); - } - - @Override - public UserDto getUserById(String userId) { - User user = userRepository.findById(userId) - .orElseThrow(); - - return mapToUserDto(user); - } - - @Override - public void changeUserRole(String userId, String roleCode) { - User user = userRepository.findById(userId) + public void changeUserRole(String login, String roleCode) { + User user = userRepository.findByLogin(login) .orElseThrow(); Role role = roleRepository.findByCode(roleCode) .orElseThrow(); @@ -46,28 +39,29 @@ public void changeUserRole(String userId, String roleCode) { userRepository.save(user); } - @Override - public BasicUserInfoDto getBasicUserInfo(String login) { - return userRepository.findBasicUserInfoByLogin(login) - .orElseThrow(() -> new UsernameNotFoundException(String.format(AlertConstants.USER_WITH_LOGIN_NOT_FOUND, login))); - } - - private UserDto mapToUserDto(User user) { - if (user == null) throw new NullPointerException(); - - Genders gender = Genders.valueOf(user.getGender().getCode()); - Roles role = Roles.valueOf(user.getRole().getCode()); - - return UserDto.builder() - .userId(user.getId()) - .login(user.getLogin()) - .email(user.getEmail()) - .name(user.getName()) - .surname(user.getSurname()) - .birthdate(user.getBirthdate()) - .gender(gender) - .createdAt(user.getCreatedAt()) - .role(role) - .build(); + public User createUser(String login, String name, String surname, String email, String password, LocalDate birthdate, Roles role, Genders gender) { + Role userRole = roleRepository.findByCode(role.name()) + .orElseGet(() -> roleRepository.save(new Role(role))); + PrivacySetting publicSetting = privacySettingRepository.findByCode(PrivacySettings.PUBLIC.name()) + .orElseGet(() -> privacySettingRepository.save(new PrivacySetting(PrivacySettings.PUBLIC))); + Gender userGender = genderRepository.findByCode(gender.name()) + .orElseGet(() -> genderRepository.save(new Gender(gender))); + User user = new User(); + user.setLogin(login); + user.setPassword(passwordEncoder.encode(password)); + user.setEmail(email); + user.setName(name); + user.setSurname(surname); + user.setAccountNonLocked(false); + user.setBirthdate(birthdate); + user.setCredentialsNonExpired(true); + user.setCredentialsExpiryDate(LocalDateTime.now().plusYears(1)); + user.setRole(userRole); + user.setPostsPrivacy(publicSetting); + user.setFriendsPrivacy(publicSetting); + user.setProfilePrivacy(publicSetting); + user.setGender(userGender); + userRepository.save(user); + return user; } } diff --git a/backend/src/main/java/meowhub/backend/users/services/impl/UserValidationServiceImpl.java b/backend/src/main/java/meowhub/backend/users/services/impl/UserValidationServiceImpl.java new file mode 100644 index 0000000..70b0ef3 --- /dev/null +++ b/backend/src/main/java/meowhub/backend/users/services/impl/UserValidationServiceImpl.java @@ -0,0 +1,28 @@ +package meowhub.backend.users.services.impl; + +import lombok.RequiredArgsConstructor; +import meowhub.backend.shared.constants.AlertConstants; +import meowhub.backend.users.repositories.UserRepository; +import meowhub.backend.users.services.UserValidationService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class UserValidationServiceImpl implements UserValidationService { + private final UserRepository userRepository; + @Override + public boolean existsByEmail (String email) { + return userRepository.existsByEmail(email); + } + @Override + public boolean existsByLogin(String login) { + return userRepository.existsByLogin(login); + } + @Override + public void validateIfUserExists(String login) { + userRepository.findByLogin(login).orElseThrow(() -> + new UsernameNotFoundException(String.format(AlertConstants.USER_WITH_LOGIN_NOT_FOUND, login)) + ); + } +} diff --git a/backend/src/main/resources/application-dev.properties b/backend/src/main/resources/application-dev.properties new file mode 100644 index 0000000..4115c6f --- /dev/null +++ b/backend/src/main/resources/application-dev.properties @@ -0,0 +1,19 @@ +spring.datasource.url=jdbc:h2:file:./data/devdb;DB_CLOSE_DELAY=-1;AUTO_SERVER=TRUE +spring.datasource.driver-class-name=org.h2.Driver + +spring.datasource.username=sa +spring.datasource.password= +spring.jpa.hibernate.ddl-auto=update +spring.h2.console.enabled=true +spring.h2.console.path=/h2-console + + +spring.sql.init.mode=always +spring.sql.init.schema-locations=classpath:data/create_schemas_h2.sql + +spring.app.jwtSecret=mySecretKey134567854345671693872934523452436235234523451412432342341534524352346 +spring.app.jwtExpirationMs=17280000 + +logging.level.org.hibernate.SQL=DEBUG +logging.level.org.springframework.jdbc=DEBUG +logging.level.org.springframework.orm.jpa=DEBUG diff --git a/backend/src/main/resources/data/create_schemas_h2.sql b/backend/src/main/resources/data/create_schemas_h2.sql new file mode 100644 index 0000000..a869565 --- /dev/null +++ b/backend/src/main/resources/data/create_schemas_h2.sql @@ -0,0 +1,7 @@ +CREATE SCHEMA IF NOT EXISTS MH_USERS; +CREATE SCHEMA IF NOT EXISTS MH_CHATS; +CREATE SCHEMA IF NOT EXISTS MH_MATCHING; +CREATE SCHEMA IF NOT EXISTS MH_POSTS; +CREATE SCHEMA IF NOT EXISTS MH_PROFILES; +CREATE SCHEMA IF NOT EXISTS MH_GROUPS; +CREATE SCHEMA IF NOT EXISTS MH_USER_RELATIONS; \ No newline at end of file diff --git a/backend/src/test/java/meowhub/backend/InitDataTestConfig.java b/backend/src/test/java/meowhub/backend/InitDataTestConfig.java index b2158f4..3242f18 100644 --- a/backend/src/test/java/meowhub/backend/InitDataTestConfig.java +++ b/backend/src/test/java/meowhub/backend/InitDataTestConfig.java @@ -93,7 +93,6 @@ private void initUser() { user1.setEmail("admin1@example.com"); user1.setName("Jan"); user1.setSurname("Kos"); - user1.setSalt("salt"); user1.setAccountNonLocked(false); user1.setBirthdate(LocalDate.of(1990, 1, 1)); user1.setCredentialsNonExpired(true); @@ -111,7 +110,6 @@ private void initUser() { user2.setEmail("user1@example.com"); user2.setName("Gustaw"); user2.setSurname("Jeleń"); - user2.setSalt("salt"); user2.setAccountNonLocked(false); user2.setBirthdate(LocalDate.of(1991, 11, 11)); user2.setCredentialsNonExpired(true); @@ -129,7 +127,6 @@ private void initUser() { user3.setEmail("grzegorz@example.com"); user3.setName("Grzegorz"); user3.setSurname("Saakaszwili"); - user3.setSalt("salt"); user3.setAccountNonLocked(false); user3.setBirthdate(LocalDate.of(1991, 11, 11)); user3.setCredentialsNonExpired(true); diff --git a/backend/src/test/java/meowhub/backend/posts/PostServiceImplTest.java b/backend/src/test/java/meowhub/backend/posts/PostServiceImplTest.java index 4ed84b9..7119247 100644 --- a/backend/src/test/java/meowhub/backend/posts/PostServiceImplTest.java +++ b/backend/src/test/java/meowhub/backend/posts/PostServiceImplTest.java @@ -5,8 +5,8 @@ import meowhub.backend.posts.repositories.PostRepository; import meowhub.backend.posts.services.impl.PostServiceImpl; import meowhub.backend.users.dtos.BasicUserInfoDto; +import meowhub.backend.users.facades.UserPostServiceFacade; import meowhub.backend.users.models.User; -import meowhub.backend.users.repositories.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -35,12 +35,12 @@ class PostServiceImplTest { @Mock private PostRepository postRepository; - @Mock - private UserRepository userRepository; - @InjectMocks private PostServiceImpl postService; + @Mock + private UserPostServiceFacade userPostServiceFacade; + private User user; private PostDto postDto; private Post post; @@ -85,7 +85,6 @@ void testGetPosts() { void testGetPostsForUser() { // Given Page mockPage = new PageImpl<>(List.of(postDto)); - when(userRepository.findByLogin("john_doe")).thenReturn(Optional.of(user)); when(postRepository.findByUserLoginIfPublicOrFriend(any(String.class), any(String.class), any(PageRequest.class))).thenReturn(mockPage); when(postRepository.findOwn(any(String.class), any(PageRequest.class))).thenReturn(mockPage); @@ -105,8 +104,8 @@ void testGetPostsForUser() { @Test void testCreatePost() { // Given - when(userRepository.findByLogin("john_doe")).thenReturn(Optional.of(user)); - when(userRepository.findBasicUserInfoByLogin("john_doe")).thenReturn(Optional.of(basicUserInfoDto)); + when(userPostServiceFacade.findUserByLogin("john_doe")).thenReturn(user); + when(userPostServiceFacade.getBasicUserInfo("john_doe")).thenReturn(basicUserInfoDto); when(postRepository.save(any(Post.class))).thenReturn(post); // When @@ -115,15 +114,13 @@ void testCreatePost() { // Then assertNotNull(result); assertEquals("Initial content", result.getContent()); - verify(userRepository, times(1)).findByLogin("john_doe"); + verify(userPostServiceFacade, times(1)).findUserByLogin("john_doe"); verify(postRepository, times(1)).save(any(Post.class)); } @Test void testUpdatePost() { // Given - when(userRepository.findByLogin("john_doe")).thenReturn(Optional.of(user)); - when(userRepository.findBasicUserInfoByLogin("john_doe")).thenReturn(Optional.of(basicUserInfoDto)); when(postRepository.save(any(Post.class))).thenReturn(post); when(postRepository.findByUserLoginAndId("john_doe", "post-id")).thenReturn(Optional.of(post)); @@ -139,7 +136,6 @@ void testUpdatePost() { @Test void testDeletePost() { // Given - when(userRepository.findByLogin("john_doe")).thenReturn(Optional.of(user)); when(postRepository.findByUserLoginAndId("john_doe", "post-id")).thenReturn(Optional.of(post)); // When @@ -152,12 +148,14 @@ void testDeletePost() { @Test void testCreatePost_UserNotFound() { // Given - when(userRepository.findByLogin("unknown_user")).thenReturn(Optional.empty()); + String unknownUser = "unknown_user"; + when(userPostServiceFacade.findUserByLogin(unknownUser)).thenThrow(UsernameNotFoundException.class); - // When & Then + // When assertThrows(UsernameNotFoundException.class, - () -> postService.createPost("unknown_user", "content")); - verify(userRepository, times(1)).findByLogin("unknown_user"); + () -> postService.createPost(unknownUser, "Sample content")); + + verify(userPostServiceFacade, times(1)).findUserByLogin(unknownUser); } } diff --git a/database/scripts/120_create_tables.sql b/database/scripts/120_create_tables.sql index 9c1423a..771686e 100644 --- a/database/scripts/120_create_tables.sql +++ b/database/scripts/120_create_tables.sql @@ -147,7 +147,6 @@ CREATE TABLE mh_users.Users login varchar2(20) NOT NULL, email varchar2(50) NOT NULL, password varchar2(120) NOT NULL, - salt varchar2(20) NOT NULL, birthdate date NOT NULL, gender_id varchar2(36) NOT NULL, profile_privacy_id varchar2(36) NOT NULL, diff --git a/database/scripts/121_create_h_tables.sql b/database/scripts/121_create_h_tables.sql index 9ebbf47..b3c3dd5 100644 --- a/database/scripts/121_create_h_tables.sql +++ b/database/scripts/121_create_h_tables.sql @@ -9,7 +9,6 @@ CREATE TABLE mh_users.h_users login varchar2(20) NOT NULL, email varchar2(50) NOT NULL, password varchar2(120) NOT NULL, - salt varchar2(20) NOT NULL, birthdate date NOT NULL, gender_id varchar2(36) NOT NULL, profile_privacy_id varchar2(36) NOT NULL, diff --git a/database/scripts/401_create_h_tables_triggers.sql b/database/scripts/401_create_h_tables_triggers.sql index 21135e7..3658afd 100644 --- a/database/scripts/401_create_h_tables_triggers.sql +++ b/database/scripts/401_create_h_tables_triggers.sql @@ -3,13 +3,13 @@ CREATE OR REPLACE TRIGGER mh_users.h_users_trg FOR EACH ROW BEGIN IF INSERTING THEN - INSERT INTO mh_users.h_users (user_id, operation_type, name, surname, login, email, password, salt, birthdate, gender_id, profile_privacy_id, posts_privacy_id, friends_privacy_id, role_id, account_non_locked, credentials_non_expired, credentials_expiry_date, created_at, created_by) - VALUES (:NEW.id,'INSERT', :NEW.name, :NEW.surname, :NEW.login, :NEW.email, :NEW.password, :NEW.salt, :NEW.birthdate, :NEW.gender_id, :NEW.profile_privacy_id, :NEW.posts_privacy_id, :NEW.friends_privacy_id, :NEW.role_id, :NEW.account_non_locked, :NEW.credentials_non_expired, :NEW.credentials_expiry_date, :NEW.created_at, :NEW.created_by); + INSERT INTO mh_users.h_users (user_id, operation_type, name, surname, login, email, password, birthdate, gender_id, profile_privacy_id, posts_privacy_id, friends_privacy_id, role_id, account_non_locked, credentials_non_expired, credentials_expiry_date, created_at, created_by) + VALUES (:NEW.id,'INSERT', :NEW.name, :NEW.surname, :NEW.login, :NEW.email, :NEW.password, :NEW.birthdate, :NEW.gender_id, :NEW.profile_privacy_id, :NEW.posts_privacy_id, :NEW.friends_privacy_id, :NEW.role_id, :NEW.account_non_locked, :NEW.credentials_non_expired, :NEW.credentials_expiry_date, :NEW.created_at, :NEW.created_by); ELSIF UPDATING THEN - INSERT INTO mh_users.h_users (user_id, operation_type, name, surname, login, email, password, salt, birthdate, gender_id, profile_privacy_id, posts_privacy_id, friends_privacy_id, role_id, account_non_locked, credentials_non_expired, credentials_expiry_date, created_at, created_by) - VALUES (:NEW.id, 'UPDATE', :NEW.name, :NEW.surname, :NEW.login, :NEW.email, :NEW.password, :NEW.salt, :NEW.birthdate, :NEW.gender_id, :NEW.profile_privacy_id, :NEW.posts_privacy_id, :NEW.friends_privacy_id, :NEW.role_id, :NEW.account_non_locked, :NEW.credentials_non_expired, :NEW.credentials_expiry_date, :NEW.created_at, :NEW.created_by); + INSERT INTO mh_users.h_users (user_id, operation_type, name, surname, login, email, password, birthdate, gender_id, profile_privacy_id, posts_privacy_id, friends_privacy_id, role_id, account_non_locked, credentials_non_expired, credentials_expiry_date, created_at, created_by) + VALUES (:NEW.id, 'UPDATE', :NEW.name, :NEW.surname, :NEW.login, :NEW.email, :NEW.password, :NEW.birthdate, :NEW.gender_id, :NEW.profile_privacy_id, :NEW.posts_privacy_id, :NEW.friends_privacy_id, :NEW.role_id, :NEW.account_non_locked, :NEW.credentials_non_expired, :NEW.credentials_expiry_date, :NEW.created_at, :NEW.created_by); ELSIF DELETING THEN - INSERT INTO mh_users.h_users (user_id, operation_type, name, surname, login, email, password, salt, birthdate, gender_id, profile_privacy_id, posts_privacy_id, friends_privacy_id, role_id, account_non_locked, credentials_non_expired, credentials_expiry_date, created_at, created_by) - VALUES (:OLD.id, 'DELETE', :OLD.name, :OLD.surname, :OLD.login, :OLD.email, :OLD.password, :OLD.salt, :OLD.birthdate, :OLD.gender_id, :OLD.profile_privacy_id, :OLD.posts_privacy_id, :OLD.friends_privacy_id, :OLD.role_id, :OLD.account_non_locked, :OLD.credentials_non_expired, :OLD.credentials_expiry_date, :OLD.created_at, :OLD.created_by); + INSERT INTO mh_users.h_users (user_id, operation_type, name, surname, login, email, password, birthdate, gender_id, profile_privacy_id, posts_privacy_id, friends_privacy_id, role_id, account_non_locked, credentials_non_expired, credentials_expiry_date, created_at, created_by) + VALUES (:OLD.id, 'DELETE', :OLD.name, :OLD.surname, :OLD.login, :OLD.email, :OLD.password, :OLD.birthdate, :OLD.gender_id, :OLD.profile_privacy_id, :OLD.posts_privacy_id, :OLD.friends_privacy_id, :OLD.role_id, :OLD.account_non_locked, :OLD.credentials_non_expired, :OLD.credentials_expiry_date, :OLD.created_at, :OLD.created_by); END IF; END; \ No newline at end of file diff --git a/img.png b/img.png new file mode 100644 index 0000000..90e05d9 Binary files /dev/null and b/img.png differ