diff --git a/build.gradle b/build.gradle index cda1630..647ba77 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,11 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.hibernate.validator:hibernate-validator' testImplementation 'org.springframework.boot:spring-boot-starter-test' + + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'com.auth0:java-jwt:4.4.0' + implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6' + annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" } tasks.named('test') { diff --git a/src/main/java/ru/ulstu/is/sbapp/Repository/IAlbumRepository.java b/src/main/java/ru/ulstu/is/sbapp/Repository/IAlbumRepository.java index bfd41fe..26d2714 100644 --- a/src/main/java/ru/ulstu/is/sbapp/Repository/IAlbumRepository.java +++ b/src/main/java/ru/ulstu/is/sbapp/Repository/IAlbumRepository.java @@ -12,4 +12,7 @@ public interface IAlbumRepository extends JpaRepository { "join a.songs s " + "group by a.id, a.albumName, s.songName") List getAll(); + + @Query("SELECT a FROM Album a WHERE a.albumName = :name") + List getAlbumsByName(String name); } diff --git a/src/main/java/ru/ulstu/is/sbapp/Repository/IArtistRepository.java b/src/main/java/ru/ulstu/is/sbapp/Repository/IArtistRepository.java index 9aa5127..fff1a00 100644 --- a/src/main/java/ru/ulstu/is/sbapp/Repository/IArtistRepository.java +++ b/src/main/java/ru/ulstu/is/sbapp/Repository/IArtistRepository.java @@ -11,4 +11,7 @@ import java.util.List; public interface IArtistRepository extends JpaRepository { @Query(value = "SELECT * FROM artist_album", nativeQuery = true) List getAllArtistAlbum(); + + @Query("SELECT a FROM Artist a WHERE a.artistName = :name") + List getArtistsByName(String name); } diff --git a/src/main/java/ru/ulstu/is/sbapp/Repository/ISongRepository.java b/src/main/java/ru/ulstu/is/sbapp/Repository/ISongRepository.java index a32a689..36332cf 100644 --- a/src/main/java/ru/ulstu/is/sbapp/Repository/ISongRepository.java +++ b/src/main/java/ru/ulstu/is/sbapp/Repository/ISongRepository.java @@ -10,4 +10,7 @@ import java.util.List; public interface ISongRepository extends JpaRepository { @Query("SELECT a.songs FROM Album a WHERE :song MEMBER OF a.songs") List findSongsInAlbum(@Param("song") Song song); + + @Query("SELECT s FROM Song s WHERE s.songName = :name") + List getSongsByName(String name); } diff --git a/src/main/java/ru/ulstu/is/sbapp/Repository/IUserRepository.java b/src/main/java/ru/ulstu/is/sbapp/Repository/IUserRepository.java new file mode 100644 index 0000000..038ad52 --- /dev/null +++ b/src/main/java/ru/ulstu/is/sbapp/Repository/IUserRepository.java @@ -0,0 +1,8 @@ +package ru.ulstu.is.sbapp.Repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.ulstu.is.sbapp.database.model.User; + +public interface IUserRepository extends JpaRepository { + User findOneByLoginIgnoreCase(String login); +} diff --git a/src/main/java/ru/ulstu/is/sbapp/configuration/PasswordEncoderConfiguration.java b/src/main/java/ru/ulstu/is/sbapp/configuration/PasswordEncoderConfiguration.java new file mode 100644 index 0000000..29034d1 --- /dev/null +++ b/src/main/java/ru/ulstu/is/sbapp/configuration/PasswordEncoderConfiguration.java @@ -0,0 +1,14 @@ +package ru.ulstu.is.sbapp.configuration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class PasswordEncoderConfiguration { + @Bean + public PasswordEncoder createPasswordEncoder() { + return new BCryptPasswordEncoder(); + } +} \ No newline at end of file diff --git a/src/main/java/ru/ulstu/is/sbapp/configuration/SecurityConfiguration.java b/src/main/java/ru/ulstu/is/sbapp/configuration/SecurityConfiguration.java new file mode 100644 index 0000000..fa19030 --- /dev/null +++ b/src/main/java/ru/ulstu/is/sbapp/configuration/SecurityConfiguration.java @@ -0,0 +1,67 @@ +package ru.ulstu.is.sbapp.configuration; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; +import org.springframework.security.web.SecurityFilterChain; +import ru.ulstu.is.sbapp.controllers.UserSignUpMvcController; +import ru.ulstu.is.sbapp.database.model.UserRole; +import ru.ulstu.is.sbapp.database.service.UserService; + +@Configuration +@EnableWebSecurity +@EnableMethodSecurity( + securedEnabled = true +) +public class SecurityConfiguration { + private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class); + private static final String LOGIN_URL = "/login"; + private final UserService userService; + + public SecurityConfiguration(UserService userService) { + this.userService = userService; + createAdminOnStartup(); + } + + private void createAdminOnStartup() { + final String admin = "admin"; + if (userService.findByLogin(admin) == null) { + log.info("Admin user successfully created"); + userService.createUser(admin, admin, admin, UserRole.ADMIN); + } + } + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.headers().frameOptions().sameOrigin().and() + .cors().and() + .csrf().disable() + .authorizeHttpRequests() + .requestMatchers(UserSignUpMvcController.SIGNUP_URL).permitAll() + .requestMatchers(HttpMethod.GET, LOGIN_URL).permitAll() + .anyRequest().authenticated() + .and() + .formLogin() + .loginPage(LOGIN_URL).permitAll() + .defaultSuccessUrl("/artist", true) + .and() + .logout().permitAll(); + return http.userDetailsService(userService).build(); + } + + + @Bean + public WebSecurityCustomizer webSecurityCustomizer() { + return (web) -> web.ignoring() + .requestMatchers("/css/**") + .requestMatchers("/js/**") + .requestMatchers("/templates/**") + .requestMatchers("/webjars/**"); + } +} \ No newline at end of file diff --git a/src/main/java/ru/ulstu/is/sbapp/WebConfiguration.java b/src/main/java/ru/ulstu/is/sbapp/configuration/WebConfiguration.java similarity index 88% rename from src/main/java/ru/ulstu/is/sbapp/WebConfiguration.java rename to src/main/java/ru/ulstu/is/sbapp/configuration/WebConfiguration.java index 8592909..c8c278f 100644 --- a/src/main/java/ru/ulstu/is/sbapp/WebConfiguration.java +++ b/src/main/java/ru/ulstu/is/sbapp/configuration/WebConfiguration.java @@ -1,4 +1,4 @@ -package ru.ulstu.is.sbapp; +package ru.ulstu.is.sbapp.configuration; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; @@ -15,6 +15,6 @@ public class WebConfiguration implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { WebMvcConfigurer.super.addViewControllers(registry); - registry.addViewController("artist"); + registry.addViewController("login"); } } \ No newline at end of file diff --git a/src/main/java/ru/ulstu/is/sbapp/controllers/AlbumController.java b/src/main/java/ru/ulstu/is/sbapp/controllers/AlbumController.java index 7269cdc..29c0aee 100644 --- a/src/main/java/ru/ulstu/is/sbapp/controllers/AlbumController.java +++ b/src/main/java/ru/ulstu/is/sbapp/controllers/AlbumController.java @@ -3,7 +3,7 @@ package ru.ulstu.is.sbapp.controllers; import jakarta.validation.Valid; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import ru.ulstu.is.sbapp.WebConfiguration; +import ru.ulstu.is.sbapp.configuration.WebConfiguration; import ru.ulstu.is.sbapp.database.model.Artist; import ru.ulstu.is.sbapp.database.model.Song; import ru.ulstu.is.sbapp.database.service.AlbumService; diff --git a/src/main/java/ru/ulstu/is/sbapp/controllers/AlbumMvcController.java b/src/main/java/ru/ulstu/is/sbapp/controllers/AlbumMvcController.java index f74e6f9..6034250 100644 --- a/src/main/java/ru/ulstu/is/sbapp/controllers/AlbumMvcController.java +++ b/src/main/java/ru/ulstu/is/sbapp/controllers/AlbumMvcController.java @@ -1,6 +1,7 @@ package ru.ulstu.is.sbapp.controllers; import jakarta.validation.Valid; +import org.springframework.security.core.Authentication; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; @@ -27,19 +28,25 @@ public class AlbumMvcController { } @GetMapping - public String getAlbums(Model model) { + public String getAlbums(Model model, Authentication authentication) { model.addAttribute("albums", albumService.findAllAlbums().stream() .map(AlbumDTO::new) .toList()); + boolean isAdmin = authentication.getAuthorities().stream() + .anyMatch(authority -> authority.getAuthority().equals("ROLE_ADMIN")); + model.addAttribute("isAdmin", isAdmin); return "album"; } @GetMapping(value = {"/edit", "/edit/{id}"}) public String editAlbum(@PathVariable(required = false) Long id, - Model model) { + Model model, Authentication authentication) { if (id == null || id <= 0) { model.addAttribute("albumDTO", new AlbumDTO()); + boolean isAdmin = authentication.getAuthorities().stream() + .anyMatch(authority -> authority.getAuthority().equals("ROLE_ADMIN")); + model.addAttribute("isAdmin", isAdmin); } else { model.addAttribute("albumId", id); model.addAttribute("albumDTO", new AlbumDTO(albumService.findAlbum(id))); @@ -136,19 +143,4 @@ public class AlbumMvcController { Map> report = albumService.getAll(); return "report"; } - -// @GetMapping("/addArtistToAlbum/{id}") -// public String addArtistToAlbumForm(@PathVariable Long id, Model model) { -// model.addAttribute("albumDTO", new AlbumDTO(albumService.findAlbum(id))); -// model.addAttribute("albumId", id); -// model.addAttribute("artists", artistService.findAllArtists()); -// return "add-artist-to-album"; -// } -// -// @PostMapping("/addArtistToAlbum/{id}") -// public String addArtistToAlbum(@PathVariable Long id, -// @RequestParam("artistId") List artistIds) { -// albumService.addArtistToAlbum(id, artistIds); -// return "redirect:/album"; -// } } diff --git a/src/main/java/ru/ulstu/is/sbapp/controllers/ArtistController.java b/src/main/java/ru/ulstu/is/sbapp/controllers/ArtistController.java index a4cdd20..4e6d853 100644 --- a/src/main/java/ru/ulstu/is/sbapp/controllers/ArtistController.java +++ b/src/main/java/ru/ulstu/is/sbapp/controllers/ArtistController.java @@ -2,7 +2,7 @@ package ru.ulstu.is.sbapp.controllers; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.*; -import ru.ulstu.is.sbapp.WebConfiguration; +import ru.ulstu.is.sbapp.configuration.WebConfiguration; import ru.ulstu.is.sbapp.database.service.ArtistService; import java.util.List; @@ -49,7 +49,7 @@ public class ArtistController { } @PostMapping("/{id}/addArtistToAlbum") - public void addArtistToAlbum(@PathVariable Long id, @RequestBody @Valid List groupsIds){ - artistService.addArtistToAlbum(id, groupsIds); + public void addArtistToAlbum(@PathVariable Long id, @RequestBody @Valid List albumsIds){ + artistService.addArtistToAlbum(id, albumsIds); } } diff --git a/src/main/java/ru/ulstu/is/sbapp/controllers/ArtistMvcController.java b/src/main/java/ru/ulstu/is/sbapp/controllers/ArtistMvcController.java index 017f43a..364340b 100644 --- a/src/main/java/ru/ulstu/is/sbapp/controllers/ArtistMvcController.java +++ b/src/main/java/ru/ulstu/is/sbapp/controllers/ArtistMvcController.java @@ -1,6 +1,7 @@ package ru.ulstu.is.sbapp.controllers; import jakarta.validation.Valid; +import org.springframework.security.core.Authentication; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; @@ -24,19 +25,25 @@ public class ArtistMvcController { } @GetMapping - public String getArtists(Model model) { + public String getArtists(Model model, Authentication authentication) { model.addAttribute("artists", artistService.findAllArtists().stream() .map(ArtistDTO::new) .toList()); + boolean isAdmin = authentication.getAuthorities().stream() + .anyMatch(authority -> authority.getAuthority().equals("ROLE_ADMIN")); + model.addAttribute("isAdmin", isAdmin); return "artist"; } @GetMapping(value = {"/edit", "/edit/{id}"}) public String editArtist(@PathVariable(required = false) Long id, - Model model) { + Model model, Authentication authentication) { if (id == null || id <= 0) { model.addAttribute("artistDTO", new ArtistDTO()); + boolean isAdmin = authentication.getAuthorities().stream() + .anyMatch(authority -> authority.getAuthority().equals("ROLE_ADMIN")); + model.addAttribute("isAdmin", isAdmin); } else { model.addAttribute("artistId", id); model.addAttribute("artistDTO", new ArtistDTO(artistService.findArtist(id))); diff --git a/src/main/java/ru/ulstu/is/sbapp/controllers/FindController.java b/src/main/java/ru/ulstu/is/sbapp/controllers/FindController.java new file mode 100644 index 0000000..942cc1b --- /dev/null +++ b/src/main/java/ru/ulstu/is/sbapp/controllers/FindController.java @@ -0,0 +1,23 @@ +package ru.ulstu.is.sbapp.controllers; + +import org.springframework.web.bind.annotation.*; +import ru.ulstu.is.sbapp.configuration.WebConfiguration; +import ru.ulstu.is.sbapp.database.service.FindByNameService; + +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping(WebConfiguration.REST_API + "/find") +public class FindController { + private final FindByNameService findService; + + public FindController(FindByNameService findService) { + this.findService = findService; + } + + @GetMapping("/get/{name}") + public Map> getByName(@PathVariable(required = false) String name){ + return findService.GetByName(name); + } +} \ No newline at end of file diff --git a/src/main/java/ru/ulstu/is/sbapp/controllers/FindMvcController.java b/src/main/java/ru/ulstu/is/sbapp/controllers/FindMvcController.java new file mode 100644 index 0000000..98f6097 --- /dev/null +++ b/src/main/java/ru/ulstu/is/sbapp/controllers/FindMvcController.java @@ -0,0 +1,42 @@ +package ru.ulstu.is.sbapp.controllers; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import ru.ulstu.is.sbapp.database.service.FindByNameService; + +import java.util.List; +import java.util.Map; + +@Controller +@RequestMapping("/find") +public class FindMvcController { + + private final FindByNameService findService; + + public FindMvcController(FindByNameService findService) { + this.findService = findService; + } + + @GetMapping("/get/{name}") + public String getByName(@PathVariable(required = false) String name, Model model) { + Map> searchResult = findService.GetByName(name); + //model.addAttribute("name", name); + model.addAttribute("searchResult", searchResult != null); + model.addAttribute("name", name); + if (searchResult != null) { + model.addAttribute("songs", searchResult.get("songs")); + model.addAttribute("albums", searchResult.get("albums")); + model.addAttribute("artists", searchResult.get("artists")); + } + return "find"; + } + + @GetMapping("/get/") + public String getFind() { + return "find"; + } +} diff --git a/src/main/java/ru/ulstu/is/sbapp/controllers/SongController.java b/src/main/java/ru/ulstu/is/sbapp/controllers/SongController.java index 6405747..89f0074 100644 --- a/src/main/java/ru/ulstu/is/sbapp/controllers/SongController.java +++ b/src/main/java/ru/ulstu/is/sbapp/controllers/SongController.java @@ -2,7 +2,7 @@ package ru.ulstu.is.sbapp.controllers; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.*; -import ru.ulstu.is.sbapp.WebConfiguration; +import ru.ulstu.is.sbapp.configuration.WebConfiguration; import ru.ulstu.is.sbapp.database.service.AlbumService; import ru.ulstu.is.sbapp.database.service.SongService; diff --git a/src/main/java/ru/ulstu/is/sbapp/controllers/SongMvcController.java b/src/main/java/ru/ulstu/is/sbapp/controllers/SongMvcController.java index ed1b3e5..024f9af 100644 --- a/src/main/java/ru/ulstu/is/sbapp/controllers/SongMvcController.java +++ b/src/main/java/ru/ulstu/is/sbapp/controllers/SongMvcController.java @@ -1,6 +1,7 @@ package ru.ulstu.is.sbapp.controllers; import jakarta.validation.Valid; +import org.springframework.security.core.Authentication; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; @@ -21,20 +22,26 @@ public class SongMvcController { } @GetMapping - public String getSongs(Model model) { + public String getSongs(Model model, Authentication authentication) { model.addAttribute("songs", songService.findAllSongs().stream() .map(SongDTO::new) .toList()); + boolean isAdmin = authentication.getAuthorities().stream() + .anyMatch(authority -> authority.getAuthority().equals("ROLE_ADMIN")); + model.addAttribute("isAdmin", isAdmin); return "song"; } @GetMapping(value = {"/edit", "/edit/{id}"}) public String editSong(@PathVariable(required = false) Long id, - Model model) { + Model model, Authentication authentication) { model.addAttribute("Albums", albumService.findAllAlbums()); if (id == null || id <= 0) { model.addAttribute("songDTO", new SongDTO()); + boolean isAdmin = authentication.getAuthorities().stream() + .anyMatch(authority -> authority.getAuthority().equals("ROLE_ADMIN")); + model.addAttribute("isAdmin", isAdmin); } else { model.addAttribute("songId", id); model.addAttribute("songDTO", new SongDTO(songService.findSong(id))); diff --git a/src/main/java/ru/ulstu/is/sbapp/controllers/UserDTO.java b/src/main/java/ru/ulstu/is/sbapp/controllers/UserDTO.java new file mode 100644 index 0000000..c8dba09 --- /dev/null +++ b/src/main/java/ru/ulstu/is/sbapp/controllers/UserDTO.java @@ -0,0 +1,28 @@ +package ru.ulstu.is.sbapp.controllers; + +import ru.ulstu.is.sbapp.database.model.User; +import ru.ulstu.is.sbapp.database.model.UserRole; + +public class UserDTO { + private final long id; + private final String login; + private final UserRole role; + + public UserDTO(User user) { + this.id = user.getId(); + this.login = user.getLogin(); + this.role = user.getRole(); + } + + public long getId() { + return id; + } + + public String getLogin() { + return login; + } + + public UserRole getRole() { + return role; + } +} diff --git a/src/main/java/ru/ulstu/is/sbapp/controllers/UserMvcController.java b/src/main/java/ru/ulstu/is/sbapp/controllers/UserMvcController.java new file mode 100644 index 0000000..bcd432f --- /dev/null +++ b/src/main/java/ru/ulstu/is/sbapp/controllers/UserMvcController.java @@ -0,0 +1,41 @@ +package ru.ulstu.is.sbapp.controllers; + +import org.springframework.data.domain.Page; +import org.springframework.security.access.annotation.Secured; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import ru.ulstu.is.sbapp.database.model.UserRole; +import ru.ulstu.is.sbapp.database.service.UserService; + +import java.util.List; +import java.util.stream.IntStream; + +@Controller +@RequestMapping("/users") +public class UserMvcController { + private final UserService userService; + + public UserMvcController(UserService userService) { + this.userService = userService; + } + + @GetMapping + @Secured({UserRole.AsString.ADMIN}) + public String getUsers(@RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "5") int size, + Model model) { + final Page users = userService.findAllPages(page, size) + .map(UserDTO::new); + model.addAttribute("users", users); + final int totalPages = users.getTotalPages(); + final List pageNumbers = IntStream.rangeClosed(1, totalPages) + .boxed() + .toList(); + model.addAttribute("pages", pageNumbers); + model.addAttribute("totalPages", totalPages); + return "users"; + } +} diff --git a/src/main/java/ru/ulstu/is/sbapp/controllers/UserSignUpDTO.java b/src/main/java/ru/ulstu/is/sbapp/controllers/UserSignUpDTO.java new file mode 100644 index 0000000..78aa46f --- /dev/null +++ b/src/main/java/ru/ulstu/is/sbapp/controllers/UserSignUpDTO.java @@ -0,0 +1,40 @@ +package ru.ulstu.is.sbapp.controllers; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public class UserSignUpDTO { + @NotBlank + @Size(min = 3, max = 64) + private String login; + @NotBlank + @Size(min = 6, max = 64) + private String password; + @NotBlank + @Size(min = 6, max = 64) + private String passwordConfirm; + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getPasswordConfirm() { + return passwordConfirm; + } + + public void setPasswordConfirm(String passwordConfirm) { + this.passwordConfirm = passwordConfirm; + } +} diff --git a/src/main/java/ru/ulstu/is/sbapp/controllers/UserSignUpMvcController.java b/src/main/java/ru/ulstu/is/sbapp/controllers/UserSignUpMvcController.java new file mode 100644 index 0000000..fb6d722 --- /dev/null +++ b/src/main/java/ru/ulstu/is/sbapp/controllers/UserSignUpMvcController.java @@ -0,0 +1,47 @@ +package ru.ulstu.is.sbapp.controllers; + +import jakarta.validation.Valid; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import ru.ulstu.is.sbapp.database.model.User; +import ru.ulstu.is.sbapp.database.service.UserService; +import ru.ulstu.is.sbapp.database.util.validation.ValidationException; + +@Controller +@RequestMapping(UserSignUpMvcController.SIGNUP_URL) +public class UserSignUpMvcController { + public static final String SIGNUP_URL = "/signup"; + private final UserService userService; + + public UserSignUpMvcController(UserService userService) { + this.userService = userService; + } + + @GetMapping + public String showSignupForm(Model model) { + model.addAttribute("UserDTO", new UserSignUpDTO()); + return "signup"; + } + + @PostMapping + public String signup(@ModelAttribute("UserDTO") @Valid UserSignUpDTO userSignupDto, + BindingResult bindingResult, + Model model) { + if (bindingResult.hasErrors()) { + model.addAttribute("errors", bindingResult.getAllErrors()); + return "signup"; + } + try { + final User user = userService.createUser(userSignupDto.getLogin(), userSignupDto.getPassword(), userSignupDto.getPasswordConfirm()); + return "redirect:/login?created=" + user.getLogin(); + } catch (ValidationException e) { + model.addAttribute("errors", e.getMessage()); + return "signup"; + } + } +} diff --git a/src/main/java/ru/ulstu/is/sbapp/database/model/Album.java b/src/main/java/ru/ulstu/is/sbapp/database/model/Album.java index b4b2adb..5c71a29 100644 --- a/src/main/java/ru/ulstu/is/sbapp/database/model/Album.java +++ b/src/main/java/ru/ulstu/is/sbapp/database/model/Album.java @@ -26,6 +26,9 @@ public class Album { inverseJoinColumns = {@JoinColumn(name = "artist_id")}) private List artists; + @ManyToOne + @JoinColumn(name= "user_id", nullable = false) + private User user; public Album() { this.songs = new ArrayList<>(); } @@ -39,6 +42,12 @@ public class Album { public String getAlbumName() { return albumName; } public void setAlbumName(String name) { this.albumName = name; } + public User getUser() { + return user; + } + public void setUser(User user) { + this.user = user; + } @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/ru/ulstu/is/sbapp/database/model/Artist.java b/src/main/java/ru/ulstu/is/sbapp/database/model/Artist.java index 8bf0bfe..1d6668d 100644 --- a/src/main/java/ru/ulstu/is/sbapp/database/model/Artist.java +++ b/src/main/java/ru/ulstu/is/sbapp/database/model/Artist.java @@ -23,6 +23,10 @@ public class Artist { inverseJoinColumns = {@JoinColumn(name = "album_id")}) private List albums = new ArrayList<>(); + @ManyToOne + @JoinColumn(name= "user_id", nullable = false) + private User user; + public Artist() { } @@ -37,6 +41,13 @@ public class Artist { public String getGenre() { return genre; } public void setGenre(String genre) { this.genre = genre; } + public User getUser() { + return user; + } + public void setUser(User user) { + this.user = user; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/ru/ulstu/is/sbapp/database/model/Song.java b/src/main/java/ru/ulstu/is/sbapp/database/model/Song.java index 0e0c3bd..ec94ee3 100644 --- a/src/main/java/ru/ulstu/is/sbapp/database/model/Song.java +++ b/src/main/java/ru/ulstu/is/sbapp/database/model/Song.java @@ -21,6 +21,10 @@ public class Song { @JoinColumn(name = "album_id", nullable = true) private Album album; + @ManyToOne + @JoinColumn(name= "user_id", nullable = false) + private User user; + public Song() { } @@ -35,6 +39,13 @@ public class Song { public Double getDuration() { return duration;} public void setDuration(Double duration) { this.duration = duration; } + public User getUser() { + return user; + } + public void setUser(User user) { + this.user = user; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/ru/ulstu/is/sbapp/database/model/User.java b/src/main/java/ru/ulstu/is/sbapp/database/model/User.java new file mode 100644 index 0000000..e4cef53 --- /dev/null +++ b/src/main/java/ru/ulstu/is/sbapp/database/model/User.java @@ -0,0 +1,83 @@ +package ru.ulstu.is.sbapp.database.model; + +import jakarta.persistence.*; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +import java.util.Objects; + +@Entity +@Table(name = "users") +public class User { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + @Column(nullable = false, unique = true, length = 64) + @NotBlank + @Size(min = 3, max = 64) + private String login; + @Column(nullable = false, length = 64) + @NotBlank + @Size(min = 6, max = 64) + private String password; + private UserRole role; + + public User() { + } + + public User(String login, String password) { + this(login, password, UserRole.USER); + } + + public User(String login, String password, UserRole role) { + this.login = login; + this.password = password; + this.role = role; + } + + public Long getId() { + return id; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public UserRole getRole() { + return role; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + User user = (User) o; + return Objects.equals(id, user.id) && Objects.equals(login, user.login); + } + + @Override + public int hashCode() { + return Objects.hash(id, login); + } + @Override + public String toString() { + return "User{" + + "id=" + id + + ", login='" + login + '\'' + + ", password='" + password + '\'' + + ", role='" + role + '\'' + + '}'; + } +} diff --git a/src/main/java/ru/ulstu/is/sbapp/database/model/UserRole.java b/src/main/java/ru/ulstu/is/sbapp/database/model/UserRole.java new file mode 100644 index 0000000..aec9ac3 --- /dev/null +++ b/src/main/java/ru/ulstu/is/sbapp/database/model/UserRole.java @@ -0,0 +1,20 @@ +package ru.ulstu.is.sbapp.database.model; + +import org.springframework.security.core.GrantedAuthority; + +public enum UserRole implements GrantedAuthority { + ADMIN, + USER; + + private static final String PREFIX = "ROLE_"; + + @Override + public String getAuthority() { + return PREFIX + this.name(); + } + + public static final class AsString { + public static final String ADMIN = PREFIX + "ADMIN"; + public static final String USER = PREFIX + "USER"; + } +} diff --git a/src/main/java/ru/ulstu/is/sbapp/database/service/AlbumService.java b/src/main/java/ru/ulstu/is/sbapp/database/service/AlbumService.java index c6c5b25..3d62e8b 100644 --- a/src/main/java/ru/ulstu/is/sbapp/database/service/AlbumService.java +++ b/src/main/java/ru/ulstu/is/sbapp/database/service/AlbumService.java @@ -1,13 +1,13 @@ package ru.ulstu.is.sbapp.database.service; import org.springframework.context.annotation.Lazy; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import ru.ulstu.is.sbapp.Repository.IAlbumRepository; -import ru.ulstu.is.sbapp.database.model.Album; -import ru.ulstu.is.sbapp.database.model.Artist; -import ru.ulstu.is.sbapp.database.model.Song; +import ru.ulstu.is.sbapp.database.model.*; import java.util.ArrayList; import java.util.List; @@ -21,11 +21,13 @@ public class AlbumService { private final SongService songService; private final ArtistService artistService; + private final UserService userService; - public AlbumService(IAlbumRepository albumRepository, SongService songService, @Lazy ArtistService artistService) { + public AlbumService(IAlbumRepository albumRepository, SongService songService, @Lazy ArtistService artistService, UserService userService) { this.albumRepository = albumRepository; this.songService = songService; this.artistService = artistService; + this.userService = userService; } @Transactional @@ -34,6 +36,14 @@ public class AlbumService { throw new IllegalArgumentException("Album name is null or empty"); } final Album album = new Album(name); + Object currentUser = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + if(currentUser instanceof UserDetails){ + String username = ((UserDetails)currentUser).getUsername(); + User user = userService.findByLogin(username); + if(user.getRole() == UserRole.ADMIN){ + album.setUser(user); + } + } return albumRepository.save(album); } diff --git a/src/main/java/ru/ulstu/is/sbapp/database/service/ArtistService.java b/src/main/java/ru/ulstu/is/sbapp/database/service/ArtistService.java index 0f4dda2..1d85791 100644 --- a/src/main/java/ru/ulstu/is/sbapp/database/service/ArtistService.java +++ b/src/main/java/ru/ulstu/is/sbapp/database/service/ArtistService.java @@ -1,10 +1,12 @@ package ru.ulstu.is.sbapp.database.service; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import ru.ulstu.is.sbapp.Repository.IArtistRepository; -import ru.ulstu.is.sbapp.database.model.Artist; +import ru.ulstu.is.sbapp.database.model.*; import java.util.ArrayList; import java.util.List; @@ -14,10 +16,12 @@ import java.util.Optional; public class ArtistService { private final IArtistRepository artistRepository; private final AlbumService albumService; + private final UserService userService; - public ArtistService(IArtistRepository artistRepository, AlbumService albumService) { + public ArtistService(IArtistRepository artistRepository, AlbumService albumService, UserService userService) { this.artistRepository = artistRepository; this.albumService = albumService; + this.userService = userService; } @Transactional @@ -26,6 +30,14 @@ public class ArtistService { throw new IllegalArgumentException("Artist name or genre is null or empty"); } final Artist artist = new Artist(artistName, genre); + Object currentUser = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + if(currentUser instanceof UserDetails){ + String username = ((UserDetails)currentUser).getUsername(); + User user = userService.findByLogin(username); + if(user.getRole() == UserRole.ADMIN){ + artist.setUser(user); + } + } return artistRepository.save(artist); } diff --git a/src/main/java/ru/ulstu/is/sbapp/database/service/FindByNameService.java b/src/main/java/ru/ulstu/is/sbapp/database/service/FindByNameService.java new file mode 100644 index 0000000..925f066 --- /dev/null +++ b/src/main/java/ru/ulstu/is/sbapp/database/service/FindByNameService.java @@ -0,0 +1,49 @@ +package ru.ulstu.is.sbapp.database.service; + +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.ulstu.is.sbapp.Repository.IAlbumRepository; +import ru.ulstu.is.sbapp.Repository.IArtistRepository; +import ru.ulstu.is.sbapp.Repository.ISongRepository; +import ru.ulstu.is.sbapp.database.model.Album; +import ru.ulstu.is.sbapp.database.model.Artist; +import ru.ulstu.is.sbapp.database.model.Song; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class FindByNameService { + private final IAlbumRepository albumRepository; + private final ISongRepository songRepository; + private final IArtistRepository artistRepository; + + + public FindByNameService(@Lazy IAlbumRepository albumRepository, @Lazy ISongRepository songRepository, @Lazy IArtistRepository artistRepository) { + this.albumRepository = albumRepository; + this.songRepository = songRepository; + this.artistRepository = artistRepository; + } + + @Transactional + public Map> GetByName(String name) { + Map> resultMap = new HashMap<>(); + + List songs = songRepository.getSongsByName(name).stream().toList(); + List songsResult = new ArrayList<>(songs); + resultMap.put("songs", songsResult); + + List albums = albumRepository.getAlbumsByName(name).stream().toList(); + List albumsResult = new ArrayList<>(albums); + resultMap.put("albums", albumsResult); + + List artists = artistRepository.getArtistsByName(name).stream().toList(); + List artistsResult = new ArrayList<>(artists); + resultMap.put("artists", artistsResult); + + return resultMap; + } +} \ No newline at end of file diff --git a/src/main/java/ru/ulstu/is/sbapp/database/service/SongService.java b/src/main/java/ru/ulstu/is/sbapp/database/service/SongService.java index e8cf050..52ec061 100644 --- a/src/main/java/ru/ulstu/is/sbapp/database/service/SongService.java +++ b/src/main/java/ru/ulstu/is/sbapp/database/service/SongService.java @@ -1,11 +1,13 @@ package ru.ulstu.is.sbapp.database.service; import org.springframework.context.annotation.Lazy; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import ru.ulstu.is.sbapp.Repository.ISongRepository; -import ru.ulstu.is.sbapp.database.model.Song; +import ru.ulstu.is.sbapp.database.model.*; import java.util.List; import java.util.Optional; @@ -15,10 +17,12 @@ public class SongService { private final ISongRepository songRepository; private final AlbumService albumService; + private final UserService userService; - public SongService(ISongRepository songRepository, @Lazy AlbumService albumService) { + public SongService(ISongRepository songRepository, @Lazy AlbumService albumService, UserService userService) { this.songRepository = songRepository; this.albumService = albumService; + this.userService = userService; } @Transactional @@ -27,6 +31,14 @@ public class SongService { throw new IllegalArgumentException("Song name or duration is null or empty"); } final Song song = new Song(songName, duration); + Object currentUser = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + if(currentUser instanceof UserDetails){ + String username = ((UserDetails)currentUser).getUsername(); + User user = userService.findByLogin(username); + if(user.getRole() == UserRole.ADMIN){ + song.setUser(user); + } + } return songRepository.save(song); } diff --git a/src/main/java/ru/ulstu/is/sbapp/database/service/UserService.java b/src/main/java/ru/ulstu/is/sbapp/database/service/UserService.java new file mode 100644 index 0000000..009c22a --- /dev/null +++ b/src/main/java/ru/ulstu/is/sbapp/database/service/UserService.java @@ -0,0 +1,59 @@ +package ru.ulstu.is.sbapp.database.service; + +import jakarta.validation.ValidationException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +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.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import ru.ulstu.is.sbapp.Repository.IUserRepository; +import ru.ulstu.is.sbapp.database.model.User; +import ru.ulstu.is.sbapp.database.model.UserRole; + + +import java.util.Collections; +import java.util.Objects; + +@Service +public class UserService implements UserDetailsService { + private final IUserRepository userRepository; + private final PasswordEncoder passwordEncoder; + + public UserService(IUserRepository userRepository, + PasswordEncoder passwordEncoder) { + this.userRepository = userRepository; + this.passwordEncoder = passwordEncoder; + } + public Page findAllPages(int page, int size) { + return userRepository.findAll(PageRequest.of(page - 1, size, Sort.by("id").ascending())); + } + public User findByLogin(String login) { + return userRepository.findOneByLoginIgnoreCase(login); + } + public User createUser(String login, String password, String passwordConfirm) { + return createUser(login, password, passwordConfirm, UserRole.USER); + } + public User createUser(String login, String password, String passwordConfirm, UserRole role) { + if (findByLogin(login) != null) { + throw new ValidationException(String.format("User '%s' already exists", login)); + } + final User user = new User(login, passwordEncoder.encode(password), role); + if (!Objects.equals(password, passwordConfirm)) { + throw new ValidationException("Passwords not equals"); + } + return userRepository.save(user); + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + final User userEntity = findByLogin(username); + if (userEntity == null) { + throw new UsernameNotFoundException(username); + } + return new org.springframework.security.core.userdetails.User( + userEntity.getLogin(), userEntity.getPassword(), Collections.singleton(userEntity.getRole())); + } +} diff --git a/src/main/resources/templates/album.html b/src/main/resources/templates/album.html index 8f06af4..6217636 100644 --- a/src/main/resources/templates/album.html +++ b/src/main/resources/templates/album.html @@ -7,7 +7,7 @@

Альбомы

@@ -23,13 +23,13 @@ -
+
+ th:href="@{/album/edit/{id}(id=${album.id})}" th:if="${isAdmin}"> Изменить Посмотреть исполнителей
-
+ diff --git a/src/main/resources/templates/artist.html b/src/main/resources/templates/artist.html index 83daf0a..5b54dd1 100644 --- a/src/main/resources/templates/artist.html +++ b/src/main/resources/templates/artist.html @@ -7,7 +7,7 @@

Исполнители

@@ -27,15 +27,15 @@
+ th:href="@{/artist/edit/{id}(id=${artist.id})}" th:if="${isAdmin}"> Изменить
- + diff --git a/src/main/resources/templates/default.html b/src/main/resources/templates/default.html index 4a56bc1..015eed6 100644 --- a/src/main/resources/templates/default.html +++ b/src/main/resources/templates/default.html @@ -10,7 +10,6 @@ -
diff --git a/src/main/resources/templates/error.html b/src/main/resources/templates/error.html index f960e68..8d83d70 100644 --- a/src/main/resources/templates/error.html +++ b/src/main/resources/templates/error.html @@ -2,12 +2,12 @@ - - -
-
- На главную + \ No newline at end of file diff --git a/src/main/resources/templates/find.html b/src/main/resources/templates/find.html new file mode 100644 index 0000000..4a51965 --- /dev/null +++ b/src/main/resources/templates/find.html @@ -0,0 +1,39 @@ + + + +
+

Поиск

+ + +
+ Поиск + +
+
+

Результаты поиска

+
+

Песни

+
    +
  • + +
  • +
+

Альбомы

+
    +
  • + +
  • +
+

Исполнители

+
    +
  • + +
  • +
+
+
+
+ + diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 0000000..e8b17ca --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,42 @@ + + + +
+
+ User not found +
+
+ Logout success +
+
+ User '' was successfully created +
+
+
+

Login

+ +
+
+

Password

+ +
+
+ +
+
+

+ Not a member yet? + Sign Up here +

+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/signup.html b/src/main/resources/templates/signup.html new file mode 100644 index 0000000..9e4308f --- /dev/null +++ b/src/main/resources/templates/signup.html @@ -0,0 +1,29 @@ + + + +
+
+
+
+ +
+
+ +
+
+ +
+
+ + Назад +
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/song.html b/src/main/resources/templates/song.html index b4c89a0..2240ea4 100644 --- a/src/main/resources/templates/song.html +++ b/src/main/resources/templates/song.html @@ -9,7 +9,7 @@

Песни

@@ -31,15 +31,15 @@
+ th:href="@{/song/edit/{id}(id=${song.id})}" th:if="${isAdmin}"> Изменить
-
+ diff --git a/src/main/resources/templates/users.html b/src/main/resources/templates/users.html new file mode 100644 index 0000000..40b7f21 --- /dev/null +++ b/src/main/resources/templates/users.html @@ -0,0 +1,38 @@ + + + +
+
+ + + + + + + + + + + + + + + + + +
#IDЛогинРоль
+
+ +
+ + \ No newline at end of file