Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,16 @@ data class ReadingRecordResponse private constructor(
val createdAt: String,

@Schema(description = "수정 일시", example = "2023-01-01T12:00:00")
val updatedAt: String
val updatedAt: String,

@Schema(description = "도서 제목", example = "클린 코드")
val bookTitle: String?,

@Schema(description = "출판사", example = "인사이트")
val bookPublisher: String?,

@Schema(description = "도서 썸네일 URL", example = "https://example.com/book-cover.jpg")
val bookCoverImageUrl: String?
) {
companion object {
private val dateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME
Expand All @@ -44,7 +53,10 @@ data class ReadingRecordResponse private constructor(
review = readingRecordInfoVO.review.value,
emotionTags = readingRecordInfoVO.emotionTags,
createdAt = readingRecordInfoVO.createdAt.format(dateTimeFormatter),
updatedAt = readingRecordInfoVO.updatedAt.format(dateTimeFormatter)
updatedAt = readingRecordInfoVO.updatedAt.format(dateTimeFormatter),
bookTitle = readingRecordInfoVO.bookTitle,
bookPublisher = readingRecordInfoVO.bookPublisher,
bookCoverImageUrl = readingRecordInfoVO.bookCoverImageUrl
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ import org.yapp.domain.readingrecordtag.ReadingRecordTag
import org.yapp.domain.readingrecordtag.ReadingRecordTagRepository
import org.yapp.domain.tag.Tag
import org.yapp.domain.tag.TagRepository
import org.yapp.domain.userbook.UserBookRepository
import org.yapp.globalutils.annotation.DomainService
import java.util.UUID

@DomainService
class ReadingRecordDomainService(
private val readingRecordRepository: ReadingRecordRepository,
private val tagRepository: TagRepository,
private val readingRecordTagRepository: ReadingRecordTagRepository
private val readingRecordTagRepository: ReadingRecordTagRepository,
private val userBookRepository: UserBookRepository
) {

fun createReadingRecord(
Expand Down Expand Up @@ -46,7 +48,15 @@ class ReadingRecordDomainService(
}
readingRecordTagRepository.saveAll(readingRecordTags)

return ReadingRecordInfoVO.newInstance(savedReadingRecord, tags.map { it.name })
val userBook = userBookRepository.findById(userBookId)

return ReadingRecordInfoVO.newInstance(
readingRecord = savedReadingRecord,
emotionTags = tags.map { it.name },
bookTitle = userBook?.title,
bookPublisher = userBook?.publisher,
bookCoverImageUrl = userBook?.coverImageUrl
)
}

fun findReadingRecordsByDynamicCondition(
Expand All @@ -55,11 +65,21 @@ class ReadingRecordDomainService(
pageable: Pageable
): Page<ReadingRecordInfoVO> {
val page = readingRecordRepository.findReadingRecordsByDynamicCondition(userBookId, sort, pageable)

// Get the UserBook entity to get the book thumbnail, title, and publisher
val userBook = userBookRepository.findById(userBookId)

return page.map { readingRecord ->
val readingRecordTags = readingRecordTagRepository.findByReadingRecordId(readingRecord.id.value)
val tagIds = readingRecordTags.map { it.tagId.value }
val tags = tagRepository.findByIds(tagIds)
ReadingRecordInfoVO.newInstance(readingRecord, tags.map { it.name })
ReadingRecordInfoVO.newInstance(
readingRecord = readingRecord,
emotionTags = tags.map { it.name },
bookTitle = userBook?.title,
bookPublisher = userBook?.publisher,
bookCoverImageUrl = userBook?.coverImageUrl
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ data class ReadingRecordInfoVO private constructor(
val review: ReadingRecord.Review,
val emotionTags: List<String>,
val createdAt: LocalDateTime,
val updatedAt: LocalDateTime
val updatedAt: LocalDateTime,
val bookTitle: String? = null,
val bookPublisher: String? = null,
val bookCoverImageUrl: String? = null
) {
init {
require(emotionTags.size <= 3) { "Maximum 3 emotion tags are allowed" }
Expand All @@ -23,7 +26,10 @@ data class ReadingRecordInfoVO private constructor(
companion object {
fun newInstance(
readingRecord: ReadingRecord,
emotionTags: List<String>
emotionTags: List<String>,
bookTitle: String? = null,
bookPublisher: String? = null,
bookCoverImageUrl: String? = null
): ReadingRecordInfoVO {
return ReadingRecordInfoVO(
id = readingRecord.id,
Expand All @@ -33,7 +39,10 @@ data class ReadingRecordInfoVO private constructor(
review = readingRecord.review,
emotionTags = emotionTags,
createdAt = readingRecord.createdAt ?: throw IllegalStateException("createdAt은 null일 수 없습니다."),
updatedAt = readingRecord.updatedAt ?: throw IllegalStateException("updatedAt은 null일 수 없습니다.")
updatedAt = readingRecord.updatedAt ?: throw IllegalStateException("updatedAt은 null일 수 없습니다."),
bookTitle = bookTitle,
bookPublisher = bookPublisher,
bookCoverImageUrl = bookCoverImageUrl
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface UserBookRepository {
fun findByUserIdAndBookIsbn(userId: UUID, isbn: String): UserBook?
fun findByBookIdAndUserId(bookId: UUID, userId: UUID): UserBook?
fun findByIdAndUserId(id: UUID, userId: UUID): UserBook?
fun findById(id: UUID): UserBook?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

보안 취약점: 사용자 인증 우회 가능성

새로 추가된 findById(id: UUID) 메서드는 사용자 ID 검증을 생략하여 다른 사용자의 UserBook에 접근할 수 있는 보안 취약점을 생성합니다. 기존 메서드들(findByIdAndUserId, findByBookIdAndUserId 등)은 모두 userId를 통한 접근 제어를 수행하고 있습니다.

사용자 스코프를 유지하도록 다음과 같이 수정하는 것을 권장합니다:

-    fun findById(id: UUID): UserBook?
+    // 이 메서드 대신 기존의 findByIdAndUserId를 사용하거나,
+    // 반드시 필요한 경우 명시적으로 internal용임을 표시
🤖 Prompt for AI Agents
In domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt at line
13, the findById(id: UUID) method lacks user ID verification, allowing
unauthorized access to other users' UserBook data. Modify this method to include
userId as a parameter and enforce access control by querying with both id and
userId, similar to existing methods like findByIdAndUserId, to maintain user
scope and prevent authentication bypass.


fun save(userBook: UserBook): UserBook

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ class AladinApi(

fun lookupBook(request: AladinBookLookupRequest): Result<AladinBookDetailResponse> {
return runCatching {
val aladinApiParams = request.toMap()
val aladinApiParams = request.toMap().toMutableMap()
aladinApiParams["Cover"] = "Big"
aladinRestClient.itemLookUp(ttbKey, aladinApiParams) // Map으로 전달
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ class UserBookRepositoryImpl(
return jpaUserBookRepository.findByIdAndUserId(id, userId)?.toDomain()
}

override fun findById(id: UUID): UserBook? {
return jpaUserBookRepository.findById(id).orElse(null)?.toDomain()
}

override fun save(userBook: UserBook): UserBook {
val savedEntity = jpaUserBookRepository.saveAndFlush(UserBookEntity.fromDomain(userBook))
return savedEntity.toDomain()
Expand Down