-
Notifications
You must be signed in to change notification settings - Fork 0
Feature : 코드 크래딧 추가 #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e7dc7b1
f2a0326
c1da9dd
b7a683d
055f2e0
5eaaa99
4e83965
327fa2c
f2d8a32
8aa4c24
0cb7930
0986638
105857f
c82bd41
e29a9fb
c280132
d4048d4
e6ea4a2
9ad211b
41cdc92
f48c908
cb84fbb
511bf25
3d399a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,6 +3,8 @@ | |||||||||||||||||||||||||||||||||||||||
| using ProjectVG.Application.Models.Character; | ||||||||||||||||||||||||||||||||||||||||
| using ProjectVG.Api.Models.Character.Request; | ||||||||||||||||||||||||||||||||||||||||
| using ProjectVG.Api.Models.Character.Response; | ||||||||||||||||||||||||||||||||||||||||
| using ProjectVG.Api.Filters; | ||||||||||||||||||||||||||||||||||||||||
| using System.Security.Claims; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| namespace ProjectVG.Api.Controllers | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -19,6 +21,16 @@ public CharacterController(ICharacterService characterService, ILogger<Character | |||||||||||||||||||||||||||||||||||||||
| _logger = logger; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| private Guid? GetCurrentUserId() | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
| var userIdClaim = User.FindFirst("user_id")?.Value; | ||||||||||||||||||||||||||||||||||||||||
| if (Guid.TryParse(userIdClaim, out var userId)) | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
| return userId; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| [HttpGet] | ||||||||||||||||||||||||||||||||||||||||
| public async Task<ActionResult<IEnumerable<CharacterResponse>>> GetAllCharacters() | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -35,29 +47,81 @@ public async Task<ActionResult<CharacterResponse>> GetCharacterById(Guid id) | |||||||||||||||||||||||||||||||||||||||
| return Ok(response); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| [HttpPost] | ||||||||||||||||||||||||||||||||||||||||
| public async Task<ActionResult<CharacterResponse>> CreateCharacter([FromBody] CreateCharacterRequest request) | ||||||||||||||||||||||||||||||||||||||||
| [HttpPost("individual")] | ||||||||||||||||||||||||||||||||||||||||
| [JwtAuthentication] | ||||||||||||||||||||||||||||||||||||||||
| public async Task<ActionResult<CharacterResponse>> CreateCharacterWithFields([FromBody] CreateCharacterWithFieldsRequest request) | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
| var command = request.ToCreateCharacterCommand(); | ||||||||||||||||||||||||||||||||||||||||
| var characterDto = await _characterService.CreateCharacterAsync(command); | ||||||||||||||||||||||||||||||||||||||||
| var userId = GetCurrentUserId(); | ||||||||||||||||||||||||||||||||||||||||
| var command = request.ToCommand(userId); | ||||||||||||||||||||||||||||||||||||||||
| var characterDto = await _characterService.CreateCharacterWithFieldsAsync(command); | ||||||||||||||||||||||||||||||||||||||||
| var response = CharacterResponse.ToResponseDto(characterDto); | ||||||||||||||||||||||||||||||||||||||||
| return CreatedAtAction(nameof(GetCharacterById), new { id = response.Id }, response); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| [HttpPut("{id}")] | ||||||||||||||||||||||||||||||||||||||||
| public async Task<ActionResult<CharacterResponse>> UpdateCharacter(Guid id, [FromBody] UpdateCharacterRequest request) | ||||||||||||||||||||||||||||||||||||||||
| [HttpPost("systemprompt")] | ||||||||||||||||||||||||||||||||||||||||
| [JwtAuthentication] | ||||||||||||||||||||||||||||||||||||||||
| public async Task<ActionResult<CharacterResponse>> CreateCharacterWithSystemPrompt([FromBody] CreateCharacterWithSystemPromptRequest request) | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
| var userId = GetCurrentUserId(); | ||||||||||||||||||||||||||||||||||||||||
| var command = request.ToCommand(userId); | ||||||||||||||||||||||||||||||||||||||||
| var characterDto = await _characterService.CreateCharacterWithSystemPromptAsync(command); | ||||||||||||||||||||||||||||||||||||||||
| var response = CharacterResponse.ToResponseDto(characterDto); | ||||||||||||||||||||||||||||||||||||||||
| return CreatedAtAction(nameof(GetCharacterById), new { id = response.Id }, response); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| [HttpPut("{id}/individual")] | ||||||||||||||||||||||||||||||||||||||||
| public async Task<ActionResult<CharacterResponse>> UpdateCharacterToIndividual(Guid id, [FromBody] UpdateCharacterToIndividualRequest request) | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
| var command = request.ToCommand(id); | ||||||||||||||||||||||||||||||||||||||||
| var characterDto = await _characterService.UpdateCharacterToIndividualAsync(command); | ||||||||||||||||||||||||||||||||||||||||
| var response = CharacterResponse.ToResponseDto(characterDto); | ||||||||||||||||||||||||||||||||||||||||
| return Ok(response); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+72
to
+79
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 업데이트 API에 인증/권한 검증 누락 두 엔드포인트 모두 인증 특성과 소유자 검증이 없습니다. 최소한 컨트롤러에서 차단하고, 궁극적으로 서비스 계층에서 강제하세요. -[HttpPut("{id}/individual")]
-public async Task<ActionResult<CharacterResponse>> UpdateCharacterToIndividual(Guid id, [FromBody] UpdateCharacterToIndividualRequest request)
+[HttpPut("{id}/individual")]
+[JwtAuthentication]
+public async Task<ActionResult<CharacterResponse>> UpdateCharacterToIndividual(Guid id, [FromBody] UpdateCharacterToIndividualRequest request)
{
- var command = request.ToCommand(id);
- var characterDto = await _characterService.UpdateCharacterToIndividualAsync(command);
+ var userId = GetCurrentUserId();
+ if (!userId.HasValue) return Unauthorized();
+ var current = await _characterService.GetCharacterByIdAsync(id);
+ if (current.CreatedByUserId != userId.Value) return Forbid();
+ var command = request.ToCommand(id);
+ var characterDto = await _characterService.UpdateCharacterToIndividualAsync(command/* , userId.Value */);
...
}
-[HttpPut("{id}/systemprompt")]
-public async Task<ActionResult<CharacterResponse>> UpdateCharacterToSystemPrompt(Guid id, [FromBody] UpdateCharacterToSystemPromptRequest request)
+[HttpPut("{id}/systemprompt")]
+[JwtAuthentication]
+public async Task<ActionResult<CharacterResponse>> UpdateCharacterToSystemPrompt(Guid id, [FromBody] UpdateCharacterToSystemPromptRequest request)
{
- var command = request.ToCommand(id);
- var characterDto = await _characterService.UpdateCharacterToSystemPromptAsync(command);
+ var userId = GetCurrentUserId();
+ if (!userId.HasValue) return Unauthorized();
+ var current = await _characterService.GetCharacterByIdAsync(id);
+ if (current.CreatedByUserId != userId.Value) return Forbid();
+ var command = request.ToCommand(id);
+ var characterDto = await _characterService.UpdateCharacterToSystemPromptAsync(command/* , userId.Value */);
...
}추가로 TOCTOU 방지를 위해 서비스 메서드에 Also applies to: 81-88 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| [HttpPut("{id}/systemprompt")] | ||||||||||||||||||||||||||||||||||||||||
| public async Task<ActionResult<CharacterResponse>> UpdateCharacterToSystemPrompt(Guid id, [FromBody] UpdateCharacterToSystemPromptRequest request) | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
| var command = request.ToUpdateCharacterCommand(); | ||||||||||||||||||||||||||||||||||||||||
| var characterDto = await _characterService.UpdateCharacterAsync(id, command); | ||||||||||||||||||||||||||||||||||||||||
| var command = request.ToCommand(id); | ||||||||||||||||||||||||||||||||||||||||
| var characterDto = await _characterService.UpdateCharacterToSystemPromptAsync(command); | ||||||||||||||||||||||||||||||||||||||||
| var response = CharacterResponse.ToResponseDto(characterDto); | ||||||||||||||||||||||||||||||||||||||||
| return Ok(response); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+81
to
88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SystemPrompt 수정 API에도 인증/권한 검증 추가. -[HttpPut("{id}/systemprompt")]
+[HttpPut("{id}/systemprompt")]
+[JwtAuthentication]
public async Task<ActionResult<CharacterResponse>> UpdateCharacterToSystemPrompt(Guid id, [FromBody] UpdateCharacterToSystemPromptRequest request)
{📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| [HttpDelete("{id}")] | ||||||||||||||||||||||||||||||||||||||||
| [JwtAuthentication] | ||||||||||||||||||||||||||||||||||||||||
| public async Task<ActionResult> DeleteCharacter(Guid id) | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
| await _characterService.DeleteCharacterAsync(id); | ||||||||||||||||||||||||||||||||||||||||
| var userId = GetCurrentUserId(); | ||||||||||||||||||||||||||||||||||||||||
| if (!userId.HasValue) | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
| return Unauthorized(); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| await _characterService.DeleteCharacterAsync(id, userId.Value); | ||||||||||||||||||||||||||||||||||||||||
| return NoContent(); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| [HttpGet("my")] | ||||||||||||||||||||||||||||||||||||||||
| [JwtAuthentication] | ||||||||||||||||||||||||||||||||||||||||
| public async Task<ActionResult<IEnumerable<CharacterResponse>>> GetMyCharacters([FromQuery] string orderBy = "latest") | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
| var userId = GetCurrentUserId(); | ||||||||||||||||||||||||||||||||||||||||
| if (!userId.HasValue) | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
| return Unauthorized(); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| var characterDtos = await _characterService.GetMyCharactersAsync(userId.Value, orderBy); | ||||||||||||||||||||||||||||||||||||||||
| var responses = characterDtos.Select(CharacterResponse.ToResponseDto); | ||||||||||||||||||||||||||||||||||||||||
| return Ok(responses); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| [HttpGet("public")] | ||||||||||||||||||||||||||||||||||||||||
| public async Task<ActionResult<IEnumerable<CharacterResponse>>> GetPublicCharacters([FromQuery] string orderBy = "latest") | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
| var characterDtos = await _characterService.GetPublicCharactersAsync(orderBy); | ||||||||||||||||||||||||||||||||||||||||
| var responses = characterDtos.Select(CharacterResponse.ToResponseDto); | ||||||||||||||||||||||||||||||||||||||||
| return Ok(responses); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
클레임 키 불일치로 사용자 ID를 읽지 못함 (user_id vs NameIdentifier).
테스트/다른 컨트롤러는 ClaimTypes.NameIdentifier를 사용합니다. 현재 구현은 user_id만 읽어 인증이 항상 실패할 수 있습니다. 호환 검색으로 보완하세요.
📝 Committable suggestion
🤖 Prompt for AI Agents