Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Maven
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties

# IDE
.idea/
*.iws
*.iml
*.ipr
.vscode/
.eclipse/

# OS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# Logs
*.log
logs/

# Temporary files
*.tmp
*.temp
94 changes: 93 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,93 @@
# coding-agent-output-comparison
# User Management API

Java REST APIユーザー管理システムです。管理者がユーザー情報を参照できる機能を提供します。

## 機能

- 管理者権限でのユーザー情報取得
- ユーザーIDによる特定ユーザー情報の取得
- 認証機能(Basic認証)
- エラーハンドリング(存在しないユーザーの場合)

## API仕様

### GET /api/users/{id}

指定されたIDのユーザー情報を取得します。

**認証**: Basic認証(管理者権限必須)

**パラメータ**:
- `id` (path): ユーザーID(数値)

**レスポンス**:
- 成功時 (200): ユーザー情報のJSON
- ユーザーが存在しない場合 (404): Not Found
- 認証失敗 (401): Unauthorized
- 権限不足 (403): Forbidden

**レスポンス例**:
```json
{
"id": 1,
"name": "田中太郎",
"email": "tanaka@example.com",
"phone": "090-1234-5678",
"department": "開発部"
}
```

## 使用方法

### アプリケーションの起動

```bash
mvn spring-boot:run
```

### APIの呼び出し例

```bash
# 正常なケース(ユーザーID 1の情報を取得)
curl -u admin:admin123 http://localhost:8080/api/users/1

# 存在しないユーザー(404エラー)
curl -u admin:admin123 http://localhost:8080/api/users/999

# 認証なし(401エラー)
curl http://localhost:8080/api/users/1
```

### 認証情報

- ユーザー名: `admin`
- パスワード: `admin123`
- ロール: `ADMIN`

## テストの実行

```bash
mvn test
```

## 技術仕様

- Java 17
- Spring Boot 3.1.5
- Spring Security (Basic認証)
- Spring Data JPA
- H2データベース(インメモリ)
- Maven

## データベース

開発用にH2インメモリデータベースを使用しています。
初期データとして5件のサンプルユーザーが登録されています。

### サンプルデータ

1. 田中太郎 (tanaka@example.com, 開発部)
2. 佐藤花子 (sato@example.com, 営業部)
3. 鈴木一郎 (suzuki@example.com, マーケティング部)
4. 高橋美穂 (takahashi@example.com, 人事部)
5. 山田次郎 (yamada@example.com, 総務部)
63 changes: 63 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version>
<relativePath/>
</parent>

<groupId>com.example</groupId>
<artifactId>user-management-api</artifactId>
<version>1.0.0</version>
<name>User Management API</name>
<description>REST API for user management with admin authentication</description>

<properties>
<java.version>17</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.example.usermanagement;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class UserManagementApplication {

public static void main(String[] args) {
SpringApplication.run(UserManagementApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.example.usermanagement.config;

import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers(new AntPathRequestMatcher("/h2-console/**")).permitAll()
.requestMatchers(new AntPathRequestMatcher("/api/users/**")).hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(basic -> {})
.csrf(csrf -> csrf
.ignoringRequestMatchers(new AntPathRequestMatcher("/h2-console/**"))
)
.headers(headers -> headers.frameOptions().disable());

return http.build();
}

@Bean
public UserDetailsService userDetailsService() {
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder().encode("admin123"))
.roles("ADMIN")
.build();

return new InMemoryUserDetailsManager(admin);
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.example.usermanagement.controller;

import com.example.usermanagement.model.User;
import com.example.usermanagement.service.UserService;
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.*;

import java.util.Optional;

@RestController
@RequestMapping("/api/users")
public class UserController {

@Autowired
private UserService userService;

@GetMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
Optional<User> user = userService.getUserById(id);

if (user.isPresent()) {
return ResponseEntity.ok(user.get());
} else {
return ResponseEntity.notFound().build();
}
}
}
76 changes: 76 additions & 0 deletions src/main/java/com/example/usermanagement/model/User.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.example.usermanagement.model;

import jakarta.persistence.*;

@Entity
@Table(name = "users")
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
private String name;

@Column(nullable = false, unique = true)
private String email;

@Column
private String phone;

@Column
private String department;

// Default constructor
public User() {}

// Constructor
public User(String name, String email, String phone, String department) {
this.name = name;
this.email = email;
this.phone = phone;
this.department = department;
}

// 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 getPhone() {
return phone;
}

public void setPhone(String phone) {
this.phone = phone;
}

public String getDepartment() {
return department;
}

public void setDepartment(String department) {
this.department = department;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example.usermanagement.repository;

import com.example.usermanagement.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
Loading