From b2476795224a322688397e906567a7a9cc044d75 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 2 Sep 2025 06:41:46 +0000
Subject: [PATCH 1/4] Initial plan
From e85ab3a63f31100bf14e653a2d278886c2a5b2e2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 2 Sep 2025 07:01:05 +0000
Subject: [PATCH 2/4] Complete REST API implementation with JWT authentication
Co-authored-by: yus04 <49590084+yus04@users.noreply.github.com>
---
pom.xml | 93 +
.../example/userapi/UserApiApplication.java | 11 +
.../example/userapi/config/DataLoader.java | 26 +
.../userapi/controller/UserController.java | 34 +
.../com/example/userapi/dto/UserResponse.java | 51 +
.../java/com/example/userapi/entity/User.java | 69 +
.../exception/GlobalExceptionHandler.java | 49 +
.../exception/UserNotFoundException.java | 7 +
.../userapi/repository/UserRepository.java | 9 +
.../security/JwtAuthenticationFilter.java | 49 +
.../com/example/userapi/security/JwtUtil.java | 68 +
.../userapi/security/SecurityConfig.java | 39 +
.../example/userapi/util/TokenGenerator.java | 27 +
src/main/resources/application.properties | 29 +
.../userapi/UserApiApplicationTest.java | 15 +
.../controller/UserControllerTest.java | 114 ++
.../example/userapi/security/JwtUtilTest.java | 68 +
.../resources/application-test.properties | 25 +
target/classes/application.properties | 29 +
.../example/userapi/UserApiApplication.class | Bin 0 -> 748 bytes
.../example/userapi/config/DataLoader.class | Bin 0 -> 1308 bytes
.../userapi/controller/UserController.class | Bin 0 -> 2944 bytes
.../example/userapi/dto/UserResponse.class | Bin 0 -> 1468 bytes
.../com/example/userapi/entity/User.class | Bin 0 -> 1976 bytes
.../exception/GlobalExceptionHandler.class | Bin 0 -> 3468 bytes
.../exception/UserNotFoundException.class | Bin 0 -> 941 bytes
.../userapi/repository/UserRepository.class | Bin 0 -> 434 bytes
.../security/JwtAuthenticationFilter.class | Bin 0 -> 3147 bytes
.../example/userapi/security/JwtUtil.class | Bin 0 -> 3165 bytes
.../userapi/security/SecurityConfig.class | Bin 0 -> 6484 bytes
.../example/userapi/util/TokenGenerator.class | Bin 0 -> 1403 bytes
.../compile/default-compile/createdFiles.lst | 12 +
.../compile/default-compile/inputFiles.lst | 12 +
.../default-testCompile/createdFiles.lst | 3 +
.../default-testCompile/inputFiles.lst | 3 +
...example.userapi.UserApiApplicationTest.xml | 392 ++++
....userapi.controller.UserControllerTest.xml | 1724 +++++++++++++++++
...m.example.userapi.security.JwtUtilTest.xml | 1724 +++++++++++++++++
...example.userapi.UserApiApplicationTest.txt | 116 ++
....userapi.controller.UserControllerTest.txt | 564 ++++++
...m.example.userapi.security.JwtUtilTest.txt | 564 ++++++
.../test-classes/application-test.properties | 22 +
.../userapi/UserApiApplicationTest.class | Bin 0 -> 622 bytes
.../controller/UserControllerTest.class | Bin 0 -> 6811 bytes
.../userapi/security/JwtUtilTest.class | Bin 0 -> 2338 bytes
45 files changed, 5948 insertions(+)
create mode 100644 pom.xml
create mode 100644 src/main/java/com/example/userapi/UserApiApplication.java
create mode 100644 src/main/java/com/example/userapi/config/DataLoader.java
create mode 100644 src/main/java/com/example/userapi/controller/UserController.java
create mode 100644 src/main/java/com/example/userapi/dto/UserResponse.java
create mode 100644 src/main/java/com/example/userapi/entity/User.java
create mode 100644 src/main/java/com/example/userapi/exception/GlobalExceptionHandler.java
create mode 100644 src/main/java/com/example/userapi/exception/UserNotFoundException.java
create mode 100644 src/main/java/com/example/userapi/repository/UserRepository.java
create mode 100644 src/main/java/com/example/userapi/security/JwtAuthenticationFilter.java
create mode 100644 src/main/java/com/example/userapi/security/JwtUtil.java
create mode 100644 src/main/java/com/example/userapi/security/SecurityConfig.java
create mode 100644 src/main/java/com/example/userapi/util/TokenGenerator.java
create mode 100644 src/main/resources/application.properties
create mode 100644 src/test/java/com/example/userapi/UserApiApplicationTest.java
create mode 100644 src/test/java/com/example/userapi/controller/UserControllerTest.java
create mode 100644 src/test/java/com/example/userapi/security/JwtUtilTest.java
create mode 100644 src/test/resources/application-test.properties
create mode 100644 target/classes/application.properties
create mode 100644 target/classes/com/example/userapi/UserApiApplication.class
create mode 100644 target/classes/com/example/userapi/config/DataLoader.class
create mode 100644 target/classes/com/example/userapi/controller/UserController.class
create mode 100644 target/classes/com/example/userapi/dto/UserResponse.class
create mode 100644 target/classes/com/example/userapi/entity/User.class
create mode 100644 target/classes/com/example/userapi/exception/GlobalExceptionHandler.class
create mode 100644 target/classes/com/example/userapi/exception/UserNotFoundException.class
create mode 100644 target/classes/com/example/userapi/repository/UserRepository.class
create mode 100644 target/classes/com/example/userapi/security/JwtAuthenticationFilter.class
create mode 100644 target/classes/com/example/userapi/security/JwtUtil.class
create mode 100644 target/classes/com/example/userapi/security/SecurityConfig.class
create mode 100644 target/classes/com/example/userapi/util/TokenGenerator.class
create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
create mode 100644 target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst
create mode 100644 target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst
create mode 100644 target/surefire-reports/TEST-com.example.userapi.UserApiApplicationTest.xml
create mode 100644 target/surefire-reports/TEST-com.example.userapi.controller.UserControllerTest.xml
create mode 100644 target/surefire-reports/TEST-com.example.userapi.security.JwtUtilTest.xml
create mode 100644 target/surefire-reports/com.example.userapi.UserApiApplicationTest.txt
create mode 100644 target/surefire-reports/com.example.userapi.controller.UserControllerTest.txt
create mode 100644 target/surefire-reports/com.example.userapi.security.JwtUtilTest.txt
create mode 100644 target/test-classes/application-test.properties
create mode 100644 target/test-classes/com/example/userapi/UserApiApplicationTest.class
create mode 100644 target/test-classes/com/example/userapi/controller/UserControllerTest.class
create mode 100644 target/test-classes/com/example/userapi/security/JwtUtilTest.class
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..ba8696b
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,93 @@
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.1.5
+
+
+
+ com.example
+ user-api
+ 1.0.0
+ jar
+
+ User API
+ REST API for user management with JWT authentication
+
+
+ 17
+ 0.11.5
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+
+
+ io.jsonwebtoken
+ jjwt-api
+ ${jwt.version}
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ ${jwt.version}
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ ${jwt.version}
+ runtime
+
+
+
+
+ com.h2database
+ h2
+ runtime
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/example/userapi/UserApiApplication.java b/src/main/java/com/example/userapi/UserApiApplication.java
new file mode 100644
index 0000000..d11cecc
--- /dev/null
+++ b/src/main/java/com/example/userapi/UserApiApplication.java
@@ -0,0 +1,11 @@
+package com.example.userapi;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class UserApiApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(UserApiApplication.class, args);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/userapi/config/DataLoader.java b/src/main/java/com/example/userapi/config/DataLoader.java
new file mode 100644
index 0000000..e7ce084
--- /dev/null
+++ b/src/main/java/com/example/userapi/config/DataLoader.java
@@ -0,0 +1,26 @@
+package com.example.userapi.config;
+
+import com.example.userapi.entity.User;
+import com.example.userapi.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Component;
+
+@Component
+@Profile("!test")
+public class DataLoader implements CommandLineRunner {
+
+ @Autowired
+ private UserRepository userRepository;
+
+ @Override
+ public void run(String... args) throws Exception {
+ // Only load data if the repository is empty
+ if (userRepository.count() == 0) {
+ userRepository.save(new User("Admin User", "admin@example.com", "ADMIN"));
+ userRepository.save(new User("Regular User", "user@example.com", "USER"));
+ userRepository.save(new User("Test User", "test@example.com", "USER"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/userapi/controller/UserController.java b/src/main/java/com/example/userapi/controller/UserController.java
new file mode 100644
index 0000000..6671ec6
--- /dev/null
+++ b/src/main/java/com/example/userapi/controller/UserController.java
@@ -0,0 +1,34 @@
+package com.example.userapi.controller;
+
+import com.example.userapi.dto.UserResponse;
+import com.example.userapi.entity.User;
+import com.example.userapi.exception.UserNotFoundException;
+import com.example.userapi.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/api/users")
+public class UserController {
+
+ @Autowired
+ private UserRepository userRepository;
+
+ @GetMapping("/{id}")
+ @PreAuthorize("hasRole('ADMIN')")
+ public ResponseEntity getUserById(@PathVariable Long id) {
+ User user = userRepository.findById(id)
+ .orElseThrow(() -> new UserNotFoundException(id));
+
+ UserResponse response = new UserResponse(
+ user.getId(),
+ user.getName(),
+ user.getEmail(),
+ user.getRole()
+ );
+
+ return ResponseEntity.ok(response);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/userapi/dto/UserResponse.java b/src/main/java/com/example/userapi/dto/UserResponse.java
new file mode 100644
index 0000000..b0dc4b5
--- /dev/null
+++ b/src/main/java/com/example/userapi/dto/UserResponse.java
@@ -0,0 +1,51 @@
+package com.example.userapi.dto;
+
+public class UserResponse {
+ private Long id;
+ private String name;
+ private String email;
+ private String role;
+
+ // Constructors
+ public UserResponse() {}
+
+ public UserResponse(Long id, String name, String email, String role) {
+ this.id = id;
+ this.name = name;
+ this.email = email;
+ this.role = role;
+ }
+
+ // Getters and Setters
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public void setRole(String role) {
+ this.role = role;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/userapi/entity/User.java b/src/main/java/com/example/userapi/entity/User.java
new file mode 100644
index 0000000..008a576
--- /dev/null
+++ b/src/main/java/com/example/userapi/entity/User.java
@@ -0,0 +1,69 @@
+package com.example.userapi.entity;
+
+import jakarta.persistence.*;
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+@Entity
+@Table(name = "users")
+public class User {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @NotBlank
+ @Column(nullable = false)
+ private String name;
+
+ @Email
+ @NotBlank
+ @Column(nullable = false, unique = true)
+ private String email;
+
+ @NotBlank
+ @Column(nullable = false)
+ private String role;
+
+ // Constructors
+ public User() {}
+
+ public User(String name, String email, String role) {
+ this.name = name;
+ this.email = email;
+ this.role = role;
+ }
+
+ // Getters and Setters
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public void setRole(String role) {
+ this.role = role;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/userapi/exception/GlobalExceptionHandler.java b/src/main/java/com/example/userapi/exception/GlobalExceptionHandler.java
new file mode 100644
index 0000000..10d42cc
--- /dev/null
+++ b/src/main/java/com/example/userapi/exception/GlobalExceptionHandler.java
@@ -0,0 +1,49 @@
+package com.example.userapi.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+ @ExceptionHandler(UserNotFoundException.class)
+ public ResponseEntity