From 12553661ba19bf19222797dedeb5e695a8041624 Mon Sep 17 00:00:00 2001 From: RodinRoman Date: Fri, 5 Jan 2024 16:49:43 +0300 Subject: [PATCH 1/4] fix of parent pom --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 2a2b5785..bcbc4e5f 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,7 @@ module08 module09 module10 + module11 module12 pom From 31ad79a3d126cd29700465de6e5cb231f17acd00 Mon Sep 17 00:00:00 2001 From: RodinRoman Date: Sat, 13 Jan 2024 09:03:15 +0300 Subject: [PATCH 2/4] added module13 --- module13/.gitignore | 38 +++++++++++++++++++ module13/README.md | 9 +++++ module13/pom.xml | 19 ++++++++++ .../src/main/java/ru/sberbank/edu/App.java | 13 +++++++ .../test/java/ru/sberbank/edu/AppTest.java | 38 +++++++++++++++++++ pom.xml | 1 + 6 files changed, 118 insertions(+) create mode 100644 module13/.gitignore create mode 100644 module13/README.md create mode 100644 module13/pom.xml create mode 100644 module13/src/main/java/ru/sberbank/edu/App.java create mode 100644 module13/src/test/java/ru/sberbank/edu/AppTest.java diff --git a/module13/.gitignore b/module13/.gitignore new file mode 100644 index 00000000..5ff6309b --- /dev/null +++ b/module13/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/module13/README.md b/module13/README.md new file mode 100644 index 00000000..8c663473 --- /dev/null +++ b/module13/README.md @@ -0,0 +1,9 @@ +# Module 13. Docker + +### Требования: +Доработать ДЗ 12, добавить возможность запускать приложение в контейнере. + +## Критерии приемки: + +1. Для запуска приложения использовать Docker (docker-compose.yml). +2. В качестве бд использовать PostgreSQL (предпочтение) или любую другую бд. \ No newline at end of file diff --git a/module13/pom.xml b/module13/pom.xml new file mode 100644 index 00000000..03e95137 --- /dev/null +++ b/module13/pom.xml @@ -0,0 +1,19 @@ + + + homework-javareboot-2023-group-06 + ru.sberbank.edu + 1.0-SNAPSHOT + + 4.0.0 + + module13 + jar + + module13 + + + UTF-8 + + + diff --git a/module13/src/main/java/ru/sberbank/edu/App.java b/module13/src/main/java/ru/sberbank/edu/App.java new file mode 100644 index 00000000..5419c026 --- /dev/null +++ b/module13/src/main/java/ru/sberbank/edu/App.java @@ -0,0 +1,13 @@ +package ru.sberbank.edu; + +/** + * Hello world! + * + */ +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello World!" ); + } +} diff --git a/module13/src/test/java/ru/sberbank/edu/AppTest.java b/module13/src/test/java/ru/sberbank/edu/AppTest.java new file mode 100644 index 00000000..895d735c --- /dev/null +++ b/module13/src/test/java/ru/sberbank/edu/AppTest.java @@ -0,0 +1,38 @@ +package ru.sberbank.edu; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +} diff --git a/pom.xml b/pom.xml index bcbc4e5f..f978a54f 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ module10 module11 module12 + module13 pom From fa45f4e66f70bb190e3d36ed8fe9f72456e2f9f2 Mon Sep 17 00:00:00 2001 From: Radium Date: Sat, 20 Jan 2024 14:52:11 +0300 Subject: [PATCH 3/4] feat: h2 changed to postgres --- module13/pom.xml | 70 +++++++++-- .../java/ru/edu/module13/Application.java | 13 ++ .../edu/module13/config/SecurityConfig.java | 60 ++++++++++ .../controller/UsersRestController.java | 49 ++++++++ .../java/ru/edu/module13/entity/Auth.java | 36 ++++++ .../java/ru/edu/module13/entity/MyUser.java | 21 ++++ .../java/ru/edu/module13/entity/Role.java | 23 ++++ .../module13/repository/AuthRepository.java | 10 ++ .../module13/repository/MyUserRepository.java | 10 ++ .../module13/repository/RoleRepository.java | 10 ++ .../src/main/java/ru/sberbank/edu/App.java | 13 -- module13/src/main/resources/application.yaml | 25 ++++ module13/src/main/resources/data.sql | 10 ++ .../src/test/java/SecurityConfigTest.java | 71 +++++++++++ .../test/java/UsersRestControllerTest.java | 113 ++++++++++++++++++ .../test/java/ru/sberbank/edu/AppTest.java | 38 ------ 16 files changed, 509 insertions(+), 63 deletions(-) create mode 100644 module13/src/main/java/ru/edu/module13/Application.java create mode 100644 module13/src/main/java/ru/edu/module13/config/SecurityConfig.java create mode 100644 module13/src/main/java/ru/edu/module13/controller/UsersRestController.java create mode 100644 module13/src/main/java/ru/edu/module13/entity/Auth.java create mode 100644 module13/src/main/java/ru/edu/module13/entity/MyUser.java create mode 100644 module13/src/main/java/ru/edu/module13/entity/Role.java create mode 100644 module13/src/main/java/ru/edu/module13/repository/AuthRepository.java create mode 100644 module13/src/main/java/ru/edu/module13/repository/MyUserRepository.java create mode 100644 module13/src/main/java/ru/edu/module13/repository/RoleRepository.java delete mode 100644 module13/src/main/java/ru/sberbank/edu/App.java create mode 100644 module13/src/main/resources/application.yaml create mode 100644 module13/src/main/resources/data.sql create mode 100644 module13/src/test/java/SecurityConfigTest.java create mode 100644 module13/src/test/java/UsersRestControllerTest.java delete mode 100644 module13/src/test/java/ru/sberbank/edu/AppTest.java diff --git a/module13/pom.xml b/module13/pom.xml index 03e95137..e43225be 100644 --- a/module13/pom.xml +++ b/module13/pom.xml @@ -1,19 +1,65 @@ + + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - homework-javareboot-2023-group-06 - ru.sberbank.edu - 1.0-SNAPSHOT + org.springframework.boot + spring-boot-starter-parent + 3.2.0 + - 4.0.0 - - module13 - jar - - module13 - + ru.edu + module12 + 0.0.1-SNAPSHOT + module12 + module12 - UTF-8 + 17 + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-security + + + + org.postgresql + postgresql + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + org.projectlombok + lombok + RELEASE + compile + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + diff --git a/module13/src/main/java/ru/edu/module13/Application.java b/module13/src/main/java/ru/edu/module13/Application.java new file mode 100644 index 00000000..106b56b5 --- /dev/null +++ b/module13/src/main/java/ru/edu/module13/Application.java @@ -0,0 +1,13 @@ +package ru.edu.module13; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/module13/src/main/java/ru/edu/module13/config/SecurityConfig.java b/module13/src/main/java/ru/edu/module13/config/SecurityConfig.java new file mode 100644 index 00000000..f3a98e80 --- /dev/null +++ b/module13/src/main/java/ru/edu/module13/config/SecurityConfig.java @@ -0,0 +1,60 @@ +package ru.edu.module13.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import ru.edu.module13.entity.Auth; +import ru.edu.module13.repository.AuthRepository; + +import java.util.stream.Collectors; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + @Autowired + private AuthRepository authRepository; + + @Bean + public UserDetailsService userDetailsService() { + return username -> { + Auth auth = authRepository.findByUsername(username); + if (auth == null) { + throw new UsernameNotFoundException("Пользователь " + username + " не найден."); + } + return new org.springframework.security.core.userdetails.User( + auth.getUsername(), + auth.getPassword(), + auth.getRoles().stream() + .map(role -> new SimpleGrantedAuthority(role.getName())) + .collect(Collectors.toList())); + }; + } + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf((csrf) -> csrf.disable()) + .authorizeHttpRequests((authorize) -> authorize + .requestMatchers(HttpMethod.GET).hasAuthority("ROLE_USER") + .requestMatchers(HttpMethod.POST).hasAuthority("ROLE_ADMIN") + .anyRequest().authenticated()) + .httpBasic(Customizer.withDefaults()); + + return http.build(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/module13/src/main/java/ru/edu/module13/controller/UsersRestController.java b/module13/src/main/java/ru/edu/module13/controller/UsersRestController.java new file mode 100644 index 00000000..11ed15ca --- /dev/null +++ b/module13/src/main/java/ru/edu/module13/controller/UsersRestController.java @@ -0,0 +1,49 @@ +package ru.edu.module13.controller; + +import org.springframework.web.bind.annotation.*; +import ru.edu.module13.entity.MyUser; +import ru.edu.module13.repository.MyUserRepository; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/users") +public class UsersRestController { + private MyUserRepository myUserRepository; + public UsersRestController(MyUserRepository myUserRepository) { + this.myUserRepository = myUserRepository; + } + + @GetMapping + public List getUsers() { + return myUserRepository.findAll(); + } + @PostMapping + public MyUser createUser(@RequestBody MyUser user) { + return myUserRepository.save(user); + } + @PutMapping("/{id}") + public MyUser updateUser(@PathVariable Long id, @RequestBody MyUser myUserDetails) { + MyUser myUser = myUserRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found with id " + id)); + + myUser.setName(myUserDetails.getName()); + myUser.setAge(myUserDetails.getAge()); + return myUserRepository.save(myUser); + } + @DeleteMapping("/{id}") + public Map deleteUser(@PathVariable Long id) { + MyUser myUser = myUserRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found with id " + id)); + + myUserRepository.delete(myUser); + Map response = new HashMap<>(); + response.put("deleted", Boolean.TRUE); + return response; + } + @GetMapping("/{id}") + public MyUser getUserById(@PathVariable Long id) { + return myUserRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found with id " + id)); + } + +} diff --git a/module13/src/main/java/ru/edu/module13/entity/Auth.java b/module13/src/main/java/ru/edu/module13/entity/Auth.java new file mode 100644 index 00000000..4496f45c --- /dev/null +++ b/module13/src/main/java/ru/edu/module13/entity/Auth.java @@ -0,0 +1,36 @@ +package ru.edu.module13.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.HashSet; +import java.util.Set; + +@Entity +@Table(name = "auth") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class Auth { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String username; + private String password; + + @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) + @JoinTable( + name = "auth_roles", + joinColumns = @JoinColumn(name = "auth_id"), + inverseJoinColumns = @JoinColumn(name = "role_id") + ) + private Set roles = new HashSet<>(); + + +} diff --git a/module13/src/main/java/ru/edu/module13/entity/MyUser.java b/module13/src/main/java/ru/edu/module13/entity/MyUser.java new file mode 100644 index 00000000..c9ab9022 --- /dev/null +++ b/module13/src/main/java/ru/edu/module13/entity/MyUser.java @@ -0,0 +1,21 @@ +package ru.edu.module13.entity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class MyUser { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + private Integer age; +} \ No newline at end of file diff --git a/module13/src/main/java/ru/edu/module13/entity/Role.java b/module13/src/main/java/ru/edu/module13/entity/Role.java new file mode 100644 index 00000000..b33402b8 --- /dev/null +++ b/module13/src/main/java/ru/edu/module13/entity/Role.java @@ -0,0 +1,23 @@ +package ru.edu.module13.entity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Table(name = "roles") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class Role { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + +} diff --git a/module13/src/main/java/ru/edu/module13/repository/AuthRepository.java b/module13/src/main/java/ru/edu/module13/repository/AuthRepository.java new file mode 100644 index 00000000..80cd756a --- /dev/null +++ b/module13/src/main/java/ru/edu/module13/repository/AuthRepository.java @@ -0,0 +1,10 @@ +package ru.edu.module13.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import ru.edu.module13.entity.Auth; + +@Repository +public interface AuthRepository extends JpaRepository { + Auth findByUsername(String username); +} \ No newline at end of file diff --git a/module13/src/main/java/ru/edu/module13/repository/MyUserRepository.java b/module13/src/main/java/ru/edu/module13/repository/MyUserRepository.java new file mode 100644 index 00000000..4cd17489 --- /dev/null +++ b/module13/src/main/java/ru/edu/module13/repository/MyUserRepository.java @@ -0,0 +1,10 @@ +package ru.edu.module13.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import ru.edu.module13.entity.MyUser; + +@Repository +public interface MyUserRepository extends JpaRepository { + +} diff --git a/module13/src/main/java/ru/edu/module13/repository/RoleRepository.java b/module13/src/main/java/ru/edu/module13/repository/RoleRepository.java new file mode 100644 index 00000000..6346bd00 --- /dev/null +++ b/module13/src/main/java/ru/edu/module13/repository/RoleRepository.java @@ -0,0 +1,10 @@ +package ru.edu.module13.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import ru.edu.module13.entity.Role; + +@Repository +public interface RoleRepository extends JpaRepository { + Role findByName(String name); +} diff --git a/module13/src/main/java/ru/sberbank/edu/App.java b/module13/src/main/java/ru/sberbank/edu/App.java deleted file mode 100644 index 5419c026..00000000 --- a/module13/src/main/java/ru/sberbank/edu/App.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.sberbank.edu; - -/** - * Hello world! - * - */ -public class App -{ - public static void main( String[] args ) - { - System.out.println( "Hello World!" ); - } -} diff --git a/module13/src/main/resources/application.yaml b/module13/src/main/resources/application.yaml new file mode 100644 index 00000000..ffd45ddb --- /dev/null +++ b/module13/src/main/resources/application.yaml @@ -0,0 +1,25 @@ +logging: + level: + root: WARN + +server: + port: 8080 + address: localhost + +spring: + datasource: + url: jdbc:postgresql://localhost:5432/postgres + driverClassName: org.postgresql.Driver + username: postgres + password: mypassword + jpa: + defer-datasource-initialization: true + hibernate: + ddl-auto: create-drop + properties: + hibernate: + show_sql: true + format_sql: true + sql: + init: + mode: always diff --git a/module13/src/main/resources/data.sql b/module13/src/main/resources/data.sql new file mode 100644 index 00000000..23bbfdfd --- /dev/null +++ b/module13/src/main/resources/data.sql @@ -0,0 +1,10 @@ +-- drop table if exists auth; +-- drop table if exists auth_roles; +-- drop table if exists my_user; +-- drop table if exists roles; +INSERT INTO roles (name) VALUES ('ROLE_ADMIN'); +INSERT INTO roles (name) VALUES ('ROLE_USER'); +INSERT INTO auth (username, password) VALUES ('admin', '$2a$10$rnxHUAo/gTfmUm2Rmf7lfeyIwMKcVOiU9cdnGWIrFDXivSGOsCvde'); +INSERT INTO auth_roles (auth_id, role_id) SELECT (SELECT id FROM auth WHERE username = 'admin'), (SELECT id FROM roles WHERE name = 'ROLE_ADMIN'); +INSERT INTO auth (username, password) VALUES ('user', '$2a$10$rnxHUAo/gTfmUm2Rmf7lfeyIwMKcVOiU9cdnGWIrFDXivSGOsCvde'); +INSERT INTO auth_roles (auth_id, role_id) SELECT (SELECT id FROM auth WHERE username = 'user'), (SELECT id FROM roles WHERE name = 'ROLE_USER'); \ No newline at end of file diff --git a/module13/src/test/java/SecurityConfigTest.java b/module13/src/test/java/SecurityConfigTest.java new file mode 100644 index 00000000..c84b43cb --- /dev/null +++ b/module13/src/test/java/SecurityConfigTest.java @@ -0,0 +1,71 @@ +import org.junit.jupiter.api.BeforeEach; +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.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import ru.edu.module13.Application; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + + +@SpringBootTest(classes = Application.class) +@AutoConfigureMockMvc +@WithMockUser +public class SecurityConfigTest { + private MockMvc mvc; + + @Autowired + private WebApplicationContext wac; + + @BeforeEach + public void setup() { + this.mvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + } + + @WithMockUser(username = "admin", roles = "ADMIN") + @Test + public void shouldAllowAccessToUserRole() throws Exception { + // Поставка POST-запроса с CSRF и пользователем с ролью ADMIN + mvc.perform(get("/users") + .with(csrf())) // CSRF disabled globally, but we can still use it in tests) + .andExpect(status().isOk()); + } + + @WithMockUser(username = "admin", roles = "ADMIN") + @Test + public void shouldAllowAccessToAdminRole() throws Exception { + // Поставка POST-запроса с CSRF и пользователем с ролью ADMIN + mvc.perform(post("/users") + .with(csrf()) // CSRF disabled globally, but we can still use it in tests + .contentType("application/json") + .content("{\"id\": 2, \"name\": \"Petr\", \"age\": \"12\"}")) + .andExpect(status().isOk()); + } + + @WithMockUser(username = "user", roles = "USER") + @Test + public void shouldDenyAccessToNonAdminRole() throws Exception { + // Пользователь с ролью USER не должен иметь доступ к POST /users + mvc.perform(post("/users") + .with(csrf()) + .contentType("application/json") + .content("{\"id\": 2, \"name\": \"Petr\", \"age\": \"12\"}")) + .andExpect(status().isForbidden()); + } + + @Test + public void shouldDenyAccessToUnauthenticatedUsers() throws Exception { + // Неаутентифицированные пользователи не должны иметь доступ к POST /users + mvc.perform(post("/users") + .contentType("application/json") + .content("{\"id\": 2, \"name\": \"Petr\", \"age\": \"12\"}")) + .andExpect(status().isUnauthorized()); + } +} \ No newline at end of file diff --git a/module13/src/test/java/UsersRestControllerTest.java b/module13/src/test/java/UsersRestControllerTest.java new file mode 100644 index 00000000..6e84c770 --- /dev/null +++ b/module13/src/test/java/UsersRestControllerTest.java @@ -0,0 +1,113 @@ +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import ru.edu.module13.controller.UsersRestController; +import ru.edu.module13.entity.MyUser; +import ru.edu.module13.repository.MyUserRepository; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.doNothing; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; + +@ExtendWith(MockitoExtension.class) +@SpringBootConfiguration +public class UsersRestControllerTest { + + @Mock + private MyUserRepository myUserRepository; + + @InjectMocks + private UsersRestController usersRestController; + + private MockMvc mockMvc; + + @BeforeEach + public void setup() { + mockMvc = standaloneSetup(usersRestController).build(); + } + + @Test + public void testGetUsers() throws Exception { + List users = Arrays.asList(new MyUser(1L, "Alice", 30), new MyUser(2L, "Bob", 25)); + given(myUserRepository.findAll()).willReturn(users); + + mockMvc.perform(get("/users")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].name").value("Alice")) + .andExpect(jsonPath("$[1].name").value("Bob")); + } + + @Test + public void testCreateUser() throws Exception { + MyUser newUser = new MyUser(null, "Charlie", 35); + MyUser savedUser = new MyUser(3L, "Charlie", 35); + given(myUserRepository.save(any(MyUser.class))).willReturn(savedUser); + + ObjectMapper objectMapper = new ObjectMapper(); + String userJson = objectMapper.writeValueAsString(newUser); + + mockMvc.perform(post("/users") + .contentType(MediaType.APPLICATION_JSON) + .content(userJson)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.name").value("Charlie")) + .andExpect(jsonPath("$.age").value(35)); + } + + @Test + public void testUpdateUser() throws Exception { + Long userId = 1L; + MyUser existingUser = new MyUser(userId, "Alice", 30); + MyUser updatedUser = new MyUser(userId, "Alice Updated", 35); + given(myUserRepository.findById(userId)).willReturn(Optional.of(existingUser)); + given(myUserRepository.save(existingUser)).willReturn(updatedUser); + + ObjectMapper objectMapper = new ObjectMapper(); + String userJson = objectMapper.writeValueAsString(updatedUser); + + mockMvc.perform(put("/users/{id}", userId) + .contentType(MediaType.APPLICATION_JSON) + .content(userJson)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.name").value("Alice Updated")) + .andExpect(jsonPath("$.age").value(35)); + } + + @Test + public void testDeleteUser() throws Exception { + Long userId = 1L; + MyUser existingUser = new MyUser(userId, "Alice", 30); + given(myUserRepository.findById(userId)).willReturn(Optional.of(existingUser)); + doNothing().when(myUserRepository).delete(existingUser); + + mockMvc.perform(delete("/users/{id}", userId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.deleted").value(true)); + } + + @Test + public void testGetUserById() throws Exception { + Long userId = 1L; + MyUser user = new MyUser(userId, "Alice", 30); + given(myUserRepository.findById(userId)).willReturn(Optional.of(user)); + + mockMvc.perform(get("/users/{id}", userId)) + .andExpect(status().isOk()).andExpect(jsonPath("$.name").value("Alice")) + .andExpect(jsonPath("$.age").value(30)); + } +} \ No newline at end of file diff --git a/module13/src/test/java/ru/sberbank/edu/AppTest.java b/module13/src/test/java/ru/sberbank/edu/AppTest.java deleted file mode 100644 index 895d735c..00000000 --- a/module13/src/test/java/ru/sberbank/edu/AppTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package ru.sberbank.edu; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -/** - * Unit test for simple App. - */ -public class AppTest - extends TestCase -{ - /** - * Create the test case - * - * @param testName name of the test case - */ - public AppTest( String testName ) - { - super( testName ); - } - - /** - * @return the suite of tests being tested - */ - public static Test suite() - { - return new TestSuite( AppTest.class ); - } - - /** - * Rigourous Test :-) - */ - public void testApp() - { - assertTrue( true ); - } -} From ecd3fcbda6b3ebe815b53faeb88dd7318ab7021b Mon Sep 17 00:00:00 2001 From: Radium Date: Sat, 20 Jan 2024 17:38:38 +0300 Subject: [PATCH 4/4] feat: docker compose added to project --- .../test/java/ru/sberbank/edu/AppTest.java | 38 ------ module13/docker-compose.yml | 27 +++++ module13/dockerfile | 4 + module13/pom.xml | 6 +- module13/src/main/resources/application.yaml | 2 +- module13/src/main/resources/data.sql | 4 - .../src/test/java/SecurityConfigTest.java | 71 ----------- .../test/java/UsersRestControllerTest.java | 113 ------------------ 8 files changed, 35 insertions(+), 230 deletions(-) delete mode 100644 module09/src/test/java/ru/sberbank/edu/AppTest.java create mode 100644 module13/docker-compose.yml create mode 100644 module13/dockerfile delete mode 100644 module13/src/test/java/SecurityConfigTest.java delete mode 100644 module13/src/test/java/UsersRestControllerTest.java diff --git a/module09/src/test/java/ru/sberbank/edu/AppTest.java b/module09/src/test/java/ru/sberbank/edu/AppTest.java deleted file mode 100644 index 895d735c..00000000 --- a/module09/src/test/java/ru/sberbank/edu/AppTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package ru.sberbank.edu; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -/** - * Unit test for simple App. - */ -public class AppTest - extends TestCase -{ - /** - * Create the test case - * - * @param testName name of the test case - */ - public AppTest( String testName ) - { - super( testName ); - } - - /** - * @return the suite of tests being tested - */ - public static Test suite() - { - return new TestSuite( AppTest.class ); - } - - /** - * Rigourous Test :-) - */ - public void testApp() - { - assertTrue( true ); - } -} diff --git a/module13/docker-compose.yml b/module13/docker-compose.yml new file mode 100644 index 00000000..de348b7d --- /dev/null +++ b/module13/docker-compose.yml @@ -0,0 +1,27 @@ +version: '3.8' + +services: + app: + build: . + ports: + - "8080:8080" + environment: + - SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/postgres + - SPRING_DATASOURCE_USERNAME=postgres + - SPRING_DATASOURCE_PASSWORD=mypassword + depends_on: + - db + + db: + image: postgres:latest + environment: + - POSTGRES_DB=postgres + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=mypassword + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + +volumes: + postgres_data: \ No newline at end of file diff --git a/module13/dockerfile b/module13/dockerfile new file mode 100644 index 00000000..838255fe --- /dev/null +++ b/module13/dockerfile @@ -0,0 +1,4 @@ +FROM openjdk:17-slim +COPY target/module13-0.0.1-SNAPSHOT.jar app.jar +EXPOSE 8080 +ENTRYPOINT ["java", "-jar", "/app.jar"] \ No newline at end of file diff --git a/module13/pom.xml b/module13/pom.xml index e43225be..fc1a80c0 100644 --- a/module13/pom.xml +++ b/module13/pom.xml @@ -9,10 +9,10 @@ ru.edu - module12 + module13 0.0.1-SNAPSHOT - module12 - module12 + module13 + module13 17 diff --git a/module13/src/main/resources/application.yaml b/module13/src/main/resources/application.yaml index ffd45ddb..eea6b4e6 100644 --- a/module13/src/main/resources/application.yaml +++ b/module13/src/main/resources/application.yaml @@ -4,7 +4,7 @@ logging: server: port: 8080 - address: localhost + address: 0.0.0.0 spring: datasource: diff --git a/module13/src/main/resources/data.sql b/module13/src/main/resources/data.sql index 23bbfdfd..ccab0b85 100644 --- a/module13/src/main/resources/data.sql +++ b/module13/src/main/resources/data.sql @@ -1,7 +1,3 @@ --- drop table if exists auth; --- drop table if exists auth_roles; --- drop table if exists my_user; --- drop table if exists roles; INSERT INTO roles (name) VALUES ('ROLE_ADMIN'); INSERT INTO roles (name) VALUES ('ROLE_USER'); INSERT INTO auth (username, password) VALUES ('admin', '$2a$10$rnxHUAo/gTfmUm2Rmf7lfeyIwMKcVOiU9cdnGWIrFDXivSGOsCvde'); diff --git a/module13/src/test/java/SecurityConfigTest.java b/module13/src/test/java/SecurityConfigTest.java deleted file mode 100644 index c84b43cb..00000000 --- a/module13/src/test/java/SecurityConfigTest.java +++ /dev/null @@ -1,71 +0,0 @@ -import org.junit.jupiter.api.BeforeEach; -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.security.test.context.support.WithMockUser; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; -import ru.edu.module13.Application; - -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - - -@SpringBootTest(classes = Application.class) -@AutoConfigureMockMvc -@WithMockUser -public class SecurityConfigTest { - private MockMvc mvc; - - @Autowired - private WebApplicationContext wac; - - @BeforeEach - public void setup() { - this.mvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); - } - - @WithMockUser(username = "admin", roles = "ADMIN") - @Test - public void shouldAllowAccessToUserRole() throws Exception { - // Поставка POST-запроса с CSRF и пользователем с ролью ADMIN - mvc.perform(get("/users") - .with(csrf())) // CSRF disabled globally, but we can still use it in tests) - .andExpect(status().isOk()); - } - - @WithMockUser(username = "admin", roles = "ADMIN") - @Test - public void shouldAllowAccessToAdminRole() throws Exception { - // Поставка POST-запроса с CSRF и пользователем с ролью ADMIN - mvc.perform(post("/users") - .with(csrf()) // CSRF disabled globally, but we can still use it in tests - .contentType("application/json") - .content("{\"id\": 2, \"name\": \"Petr\", \"age\": \"12\"}")) - .andExpect(status().isOk()); - } - - @WithMockUser(username = "user", roles = "USER") - @Test - public void shouldDenyAccessToNonAdminRole() throws Exception { - // Пользователь с ролью USER не должен иметь доступ к POST /users - mvc.perform(post("/users") - .with(csrf()) - .contentType("application/json") - .content("{\"id\": 2, \"name\": \"Petr\", \"age\": \"12\"}")) - .andExpect(status().isForbidden()); - } - - @Test - public void shouldDenyAccessToUnauthenticatedUsers() throws Exception { - // Неаутентифицированные пользователи не должны иметь доступ к POST /users - mvc.perform(post("/users") - .contentType("application/json") - .content("{\"id\": 2, \"name\": \"Petr\", \"age\": \"12\"}")) - .andExpect(status().isUnauthorized()); - } -} \ No newline at end of file diff --git a/module13/src/test/java/UsersRestControllerTest.java b/module13/src/test/java/UsersRestControllerTest.java deleted file mode 100644 index 6e84c770..00000000 --- a/module13/src/test/java/UsersRestControllerTest.java +++ /dev/null @@ -1,113 +0,0 @@ -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; -import ru.edu.module13.controller.UsersRestController; -import ru.edu.module13.entity.MyUser; -import ru.edu.module13.repository.MyUserRepository; - -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.doNothing; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; - -@ExtendWith(MockitoExtension.class) -@SpringBootConfiguration -public class UsersRestControllerTest { - - @Mock - private MyUserRepository myUserRepository; - - @InjectMocks - private UsersRestController usersRestController; - - private MockMvc mockMvc; - - @BeforeEach - public void setup() { - mockMvc = standaloneSetup(usersRestController).build(); - } - - @Test - public void testGetUsers() throws Exception { - List users = Arrays.asList(new MyUser(1L, "Alice", 30), new MyUser(2L, "Bob", 25)); - given(myUserRepository.findAll()).willReturn(users); - - mockMvc.perform(get("/users")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$[0].name").value("Alice")) - .andExpect(jsonPath("$[1].name").value("Bob")); - } - - @Test - public void testCreateUser() throws Exception { - MyUser newUser = new MyUser(null, "Charlie", 35); - MyUser savedUser = new MyUser(3L, "Charlie", 35); - given(myUserRepository.save(any(MyUser.class))).willReturn(savedUser); - - ObjectMapper objectMapper = new ObjectMapper(); - String userJson = objectMapper.writeValueAsString(newUser); - - mockMvc.perform(post("/users") - .contentType(MediaType.APPLICATION_JSON) - .content(userJson)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.name").value("Charlie")) - .andExpect(jsonPath("$.age").value(35)); - } - - @Test - public void testUpdateUser() throws Exception { - Long userId = 1L; - MyUser existingUser = new MyUser(userId, "Alice", 30); - MyUser updatedUser = new MyUser(userId, "Alice Updated", 35); - given(myUserRepository.findById(userId)).willReturn(Optional.of(existingUser)); - given(myUserRepository.save(existingUser)).willReturn(updatedUser); - - ObjectMapper objectMapper = new ObjectMapper(); - String userJson = objectMapper.writeValueAsString(updatedUser); - - mockMvc.perform(put("/users/{id}", userId) - .contentType(MediaType.APPLICATION_JSON) - .content(userJson)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.name").value("Alice Updated")) - .andExpect(jsonPath("$.age").value(35)); - } - - @Test - public void testDeleteUser() throws Exception { - Long userId = 1L; - MyUser existingUser = new MyUser(userId, "Alice", 30); - given(myUserRepository.findById(userId)).willReturn(Optional.of(existingUser)); - doNothing().when(myUserRepository).delete(existingUser); - - mockMvc.perform(delete("/users/{id}", userId)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.deleted").value(true)); - } - - @Test - public void testGetUserById() throws Exception { - Long userId = 1L; - MyUser user = new MyUser(userId, "Alice", 30); - given(myUserRepository.findById(userId)).willReturn(Optional.of(user)); - - mockMvc.perform(get("/users/{id}", userId)) - .andExpect(status().isOk()).andExpect(jsonPath("$.name").value("Alice")) - .andExpect(jsonPath("$.age").value(30)); - } -} \ No newline at end of file