From cb9790fdd68d37b3c9081452f70a59a7716c51a1 Mon Sep 17 00:00:00 2001 From: iRedTea Date: Mon, 29 Aug 2022 00:05:38 +0300 Subject: [PATCH] Added auth system, file upload system, file upload tests --- build.gradle | 5 +- .../easystartupcore/config/AppConfig.java | 12 ++++ .../easystartupcore/config/MvcConfig.java | 12 ++++ .../config/WebSecurityConfig.java | 45 ++++++++++++ .../controller/RegistrationController.java | 45 ++++++++++++ .../easystartupcore/domain/Role.java | 5 ++ .../easystartupcore/domain/User.java | 24 +++++++ .../easystartupcore/repos/UserRepo.java | 8 +++ src/main/resources/application.properties | 7 +- src/main/resources/static/logo/logo_black.png | Bin 0 -> 18483 bytes src/main/resources/templates/login.html | 65 +++++++++++++++++ .../resources/templates/registration.html | 42 +++++++++++ .../easystartupcore/FileUploadTests.java | 68 ++++++++++++++++++ 13 files changed, 332 insertions(+), 6 deletions(-) create mode 100644 src/main/java/site/easystartup/easystartupcore/config/MvcConfig.java create mode 100644 src/main/java/site/easystartup/easystartupcore/config/WebSecurityConfig.java create mode 100644 src/main/java/site/easystartup/easystartupcore/controller/RegistrationController.java create mode 100644 src/main/java/site/easystartup/easystartupcore/domain/Role.java create mode 100644 src/main/java/site/easystartup/easystartupcore/domain/User.java create mode 100644 src/main/java/site/easystartup/easystartupcore/repos/UserRepo.java create mode 100644 src/main/resources/static/logo/logo_black.png create mode 100644 src/main/resources/templates/login.html create mode 100644 src/main/resources/templates/registration.html create mode 100644 src/test/java/site/easystartup/easystartupcore/FileUploadTests.java diff --git a/build.gradle b/build.gradle index d734261..9b3c9ec 100644 --- a/build.gradle +++ b/build.gradle @@ -20,13 +20,16 @@ repositories { } dependencies { + implementation 'org.springframework.data:spring-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' - runtimeOnly 'mysql:mysql-connector-java' + runtimeOnly 'org.postgresql:postgresql' + implementation 'com.zaxxer:HikariCP:5.0.1' annotationProcessor 'org.projectlombok:lombok' providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/src/main/java/site/easystartup/easystartupcore/config/AppConfig.java b/src/main/java/site/easystartup/easystartupcore/config/AppConfig.java index 72de475..4abc860 100644 --- a/src/main/java/site/easystartup/easystartupcore/config/AppConfig.java +++ b/src/main/java/site/easystartup/easystartupcore/config/AppConfig.java @@ -1,4 +1,16 @@ package site.easystartup.easystartupcore.config; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +@ComponentScan(basePackages = {"site.easystartup.easystartupcore"}) public class AppConfig { + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } } diff --git a/src/main/java/site/easystartup/easystartupcore/config/MvcConfig.java b/src/main/java/site/easystartup/easystartupcore/config/MvcConfig.java new file mode 100644 index 0000000..d1736f6 --- /dev/null +++ b/src/main/java/site/easystartup/easystartupcore/config/MvcConfig.java @@ -0,0 +1,12 @@ +package site.easystartup.easystartupcore.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class MvcConfig implements WebMvcConfigurer { + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/login").setViewName("login"); + } +} diff --git a/src/main/java/site/easystartup/easystartupcore/config/WebSecurityConfig.java b/src/main/java/site/easystartup/easystartupcore/config/WebSecurityConfig.java new file mode 100644 index 0000000..ff5810d --- /dev/null +++ b/src/main/java/site/easystartup/easystartupcore/config/WebSecurityConfig.java @@ -0,0 +1,45 @@ +package site.easystartup.easystartupcore.config; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +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.WebSecurityConfigurerAdapter; +import org.springframework.security.crypto.password.PasswordEncoder; + +import javax.sql.DataSource; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + private final DataSource dataSource; + + private final PasswordEncoder passwordEncoder; + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .antMatchers("/", "/home", "/registration").permitAll() + .anyRequest().authenticated() + .and() + .formLogin() + .loginPage("/login") + .permitAll() + .and() + .logout() + .permitAll(); + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.jdbcAuthentication() + .dataSource(dataSource) + .passwordEncoder(passwordEncoder) + .usersByUsernameQuery("SELECT `username`, `password`, `active` FROM usr WHERE `username` = ?;") + .authoritiesByUsernameQuery("SELECT u.username, ur.roles FROM usr u INNER JOIN user_role ur ON u.id = ur.user_id WHERE username = ?;"); + } +} diff --git a/src/main/java/site/easystartup/easystartupcore/controller/RegistrationController.java b/src/main/java/site/easystartup/easystartupcore/controller/RegistrationController.java new file mode 100644 index 0000000..4048f45 --- /dev/null +++ b/src/main/java/site/easystartup/easystartupcore/controller/RegistrationController.java @@ -0,0 +1,45 @@ +package site.easystartup.easystartupcore.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import site.easystartup.easystartupcore.domain.Role; +import site.easystartup.easystartupcore.domain.User; +import site.easystartup.easystartupcore.repos.UserRepo; + +import java.util.Collections; + +@Controller +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class RegistrationController { + private final PasswordEncoder passwordEncoder; + + private final UserRepo userRepo; + + @GetMapping("/registration") + public String registration() { + return "registration"; + } + + @PostMapping("/registration") + public String addUser(User user, Model model) { + User userFromDb = userRepo.findByUsername(user.getUsername()); + + if(userFromDb != null) { + model.addAttribute("message", "Пользователь уже существует!"); + return "registration"; + } + + user.setActive(true); + user.setRoles(Collections.singleton(Role.USER)); + user.setPassword(passwordEncoder.encode(user.getPassword())); + + userRepo.save(user); + + return "redirect:/login"; + } +} diff --git a/src/main/java/site/easystartup/easystartupcore/domain/Role.java b/src/main/java/site/easystartup/easystartupcore/domain/Role.java new file mode 100644 index 0000000..1f8a7ef --- /dev/null +++ b/src/main/java/site/easystartup/easystartupcore/domain/Role.java @@ -0,0 +1,5 @@ +package site.easystartup.easystartupcore.domain; + +public enum Role { + USER, PREMIUM, MODER, ADMIN; +} diff --git a/src/main/java/site/easystartup/easystartupcore/domain/User.java b/src/main/java/site/easystartup/easystartupcore/domain/User.java new file mode 100644 index 0000000..6d8fd49 --- /dev/null +++ b/src/main/java/site/easystartup/easystartupcore/domain/User.java @@ -0,0 +1,24 @@ +package site.easystartup.easystartupcore.domain; + +import lombok.*; + +import javax.persistence.*; +import java.util.Set; + +@Data +@Entity +@Table(name = "usr") +public class User { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private long id; + private String username; + private String password; + private boolean active; + + @ElementCollection(targetClass = Role.class, fetch = FetchType.EAGER) + @CollectionTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id")) + @Enumerated(EnumType.STRING) + private Set roles; + +} diff --git a/src/main/java/site/easystartup/easystartupcore/repos/UserRepo.java b/src/main/java/site/easystartup/easystartupcore/repos/UserRepo.java new file mode 100644 index 0000000..d716224 --- /dev/null +++ b/src/main/java/site/easystartup/easystartupcore/repos/UserRepo.java @@ -0,0 +1,8 @@ +package site.easystartup.easystartupcore.repos; + +import org.springframework.data.jpa.repository.JpaRepository; +import site.easystartup.easystartupcore.domain.User; + +public interface UserRepo extends JpaRepository { + User findByUsername(String username); +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 949105b..96ccdf5 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,14 +1,11 @@ server.port=80 -spring.main.banner-mode=off -logging.level.org.springframework=ERROR - -spring.jpa.hibernate.ddl-auto=none +spring.jpa.hibernate.ddl-auto=create spring.sql.init.mode=always + spring.sql.init.platform=postgres spring.datasource.url=jdbc:postgresql://localhost:5432/testdb spring.datasource.username=postgres spring.datasource.password=root -spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true diff --git a/src/main/resources/static/logo/logo_black.png b/src/main/resources/static/logo/logo_black.png new file mode 100644 index 0000000000000000000000000000000000000000..01e76af68753298f4ca6c7cd4b7c338fe0650953 GIT binary patch literal 18483 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 0000000..5e73206 --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,65 @@ + + + + + Вход + + + + + + + + +
+ +
+

Пожалуйста, войдите

+ +
+ + +
+
+ + +
+ +
+ +
+ + + +
+ Неправильный логин или пароль. +
+
+ Вы вышли из аккаунта. +
+ +

© 2022

+
+ Зарегистрироваться +
+ + + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/registration.html b/src/main/resources/templates/registration.html new file mode 100644 index 0000000..3ce7d41 --- /dev/null +++ b/src/main/resources/templates/registration.html @@ -0,0 +1,42 @@ + + + + Регистрация + + + + + + + + + + +
+ +
+ +

Пожалуйста, зарегистрируйтесь

+ +
+ + +
+
+ + +
+ + +
+

+
+

© 2022

+
+
+ + + + \ No newline at end of file diff --git a/src/test/java/site/easystartup/easystartupcore/FileUploadTests.java b/src/test/java/site/easystartup/easystartupcore/FileUploadTests.java new file mode 100644 index 0000000..2600fed --- /dev/null +++ b/src/test/java/site/easystartup/easystartupcore/FileUploadTests.java @@ -0,0 +1,68 @@ +package site.easystartup.easystartupcore; + +import java.nio.file.Paths; +import java.util.stream.Stream; + +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.web.servlet.MockMvc; +import site.easystartup.easystartupcore.storage.StorageFileNotFoundException; +import site.easystartup.easystartupcore.storage.StorageService; + +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + + +@AutoConfigureMockMvc +@SpringBootTest +public class FileUploadTests { + + @Autowired + private MockMvc mvc; + + @MockBean + private StorageService storageService; + + @Test + public void shouldListAllFiles() throws Exception { + given(this.storageService.loadAll()) + .willReturn(Stream.of(Paths.get("first.txt"), Paths.get("second.txt"))); + + this.mvc.perform(get("/")).andExpect(status().isOk()) + .andExpect(model().attribute("files", + Matchers.contains("http://localhost/files/first.txt", + "http://localhost/files/second.txt"))); + } + + @Test + public void shouldSaveUploadedFile() throws Exception { + MockMultipartFile multipartFile = new MockMultipartFile("file", "test.txt", + "text/plain", "Spring Framework".getBytes()); + this.mvc.perform(multipart("/").file(multipartFile)) + .andExpect(status().isFound()) + .andExpect(header().string("Location", "/")); + + then(this.storageService).should().save(multipartFile); + } + + @SuppressWarnings("unchecked") + @Test + public void should404WhenMissingFile() throws Exception { + given(this.storageService.load("test.txt")) + .willThrow(StorageFileNotFoundException.class); + + this.mvc.perform(get("/files/test.txt")).andExpect(status().isNotFound()); + } + +} \ No newline at end of file