Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.transaction.annotation.Transactional;

import com.opensource.docgrid.domain.collection.converter.CollectionConverter;
import com.opensource.docgrid.domain.permission.service.query.PermissionQueryService;
import com.opensource.docgrid.domain.collection.dto.request.AddDocumentRequest;
import com.opensource.docgrid.domain.collection.dto.request.CreateCollectionRequest;
import com.opensource.docgrid.domain.collection.dto.response.CollectionDocumentResponse;
Expand Down Expand Up @@ -35,6 +36,7 @@ public class CollectionCommandService {
private final DocumentRepository documentRepository;
private final UserRepository userRepository;
private final CollectionConverter collectionConverter;
private final PermissionQueryService permissionQueryService;

// 폴더 생성
public CollectionResponse createCollection(Long userId, CreateCollectionRequest request) {
Expand Down Expand Up @@ -66,8 +68,7 @@ public CollectionDocumentResponse addDocument(Long collectionId, Long userId, Ad
DocumentCollection collection = collectionRepository.findById(collectionId)
.orElseThrow(() -> new DocGridException(ErrorCode.COLLECTION_NOT_FOUND));

// TODO: Issue 3 PermissionService 완성 후 canWriteDocument() 로 대체
if (!collection.getOwner().getId().equals(userId)) {
if (!permissionQueryService.canWriteCollection(userId, collectionId)) {
throw new DocGridException(ErrorCode.PERMISSION_DENIED);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,158 @@
package com.opensource.docgrid.domain.permission.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.opensource.docgrid.domain.permission.entity.CollectionPermission;

public interface CollectionPermissionRepository extends JpaRepository<CollectionPermission, Long> {

// ROLE live — 사용자 역할 기반 컬렉션→문서 읽기 권한 존재 여부
@Query("""
SELECT COUNT(cp) > 0 FROM CollectionPermission cp
JOIN CollectionDocument cd ON cd.collection = cp.collection
JOIN UserRole ur ON ur.role = cp.role
WHERE cd.document.id = :documentId
AND cp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.ROLE
AND ur.user.id = :userId
AND cp.canRead = true
AND (cp.expiresAt IS NULL OR cp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsRoleReadPermissionForDocument(@Param("userId") Long userId, @Param("documentId") Long documentId);

// ROLE live — 사용자 역할 기반 컬렉션→문서 쓰기 권한 존재 여부
@Query("""
SELECT COUNT(cp) > 0 FROM CollectionPermission cp
JOIN CollectionDocument cd ON cd.collection = cp.collection
JOIN UserRole ur ON ur.role = cp.role
WHERE cd.document.id = :documentId
AND cp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.ROLE
AND ur.user.id = :userId
AND cp.canWrite = true
AND (cp.expiresAt IS NULL OR cp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsRoleWritePermissionForDocument(@Param("userId") Long userId, @Param("documentId") Long documentId);

// ROLE live — 사용자 역할 기반 컬렉션→문서 관리 권한 존재 여부
@Query("""
SELECT COUNT(cp) > 0 FROM CollectionPermission cp
JOIN CollectionDocument cd ON cd.collection = cp.collection
JOIN UserRole ur ON ur.role = cp.role
WHERE cd.document.id = :documentId
AND cp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.ROLE
AND ur.user.id = :userId
AND cp.canAdmin = true
AND (cp.expiresAt IS NULL OR cp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsRoleAdminPermissionForDocument(@Param("userId") Long userId, @Param("documentId") Long documentId);

// DEPARTMENT live — 사용자 부서 기반 컬렉션→문서 읽기 권한 존재 여부
@Query("""
SELECT COUNT(cp) > 0 FROM CollectionPermission cp
JOIN CollectionDocument cd ON cd.collection = cp.collection
JOIN User u ON u.department = cp.department
WHERE cd.document.id = :documentId
AND cp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.DEPARTMENT
AND u.id = :userId
AND cp.canRead = true
AND (cp.expiresAt IS NULL OR cp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsDeptReadPermissionForDocument(@Param("userId") Long userId, @Param("documentId") Long documentId);

// DEPARTMENT live — 사용자 부서 기반 컬렉션→문서 쓰기 권한 존재 여부
@Query("""
SELECT COUNT(cp) > 0 FROM CollectionPermission cp
JOIN CollectionDocument cd ON cd.collection = cp.collection
JOIN User u ON u.department = cp.department
WHERE cd.document.id = :documentId
AND cp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.DEPARTMENT
AND u.id = :userId
AND cp.canWrite = true
AND (cp.expiresAt IS NULL OR cp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsDeptWritePermissionForDocument(@Param("userId") Long userId, @Param("documentId") Long documentId);

// DEPARTMENT live — 사용자 부서 기반 컬렉션→문서 관리 권한 존재 여부
@Query("""
SELECT COUNT(cp) > 0 FROM CollectionPermission cp
JOIN CollectionDocument cd ON cd.collection = cp.collection
JOIN User u ON u.department = cp.department
WHERE cd.document.id = :documentId
AND cp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.DEPARTMENT
AND u.id = :userId
AND cp.canAdmin = true
AND (cp.expiresAt IS NULL OR cp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsDeptAdminPermissionForDocument(@Param("userId") Long userId, @Param("documentId") Long documentId);

// USER 직접 권한 — 컬렉션에 쓰기 권한이 있는지 (canWriteCollection 판단용)
@Query("""
SELECT COUNT(cp) > 0 FROM CollectionPermission cp
WHERE cp.collection.id = :collectionId
AND cp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.USER
AND cp.user.id = :userId
AND cp.canWrite = true
AND (cp.expiresAt IS NULL OR cp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsUserWritePermission(@Param("userId") Long userId, @Param("collectionId") Long collectionId);

// USER 직접 권한 — 컬렉션에 관리 권한이 있는지 (canAdminCollection 판단용)
@Query("""
SELECT COUNT(cp) > 0 FROM CollectionPermission cp
WHERE cp.collection.id = :collectionId
AND cp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.USER
AND cp.user.id = :userId
AND cp.canAdmin = true
AND (cp.expiresAt IS NULL OR cp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsUserAdminPermission(@Param("userId") Long userId, @Param("collectionId") Long collectionId);

// ROLE live — 사용자 역할 기반 컬렉션 쓰기 권한 존재 여부 (canWriteCollection 판단용)
@Query("""
SELECT COUNT(cp) > 0 FROM CollectionPermission cp
JOIN UserRole ur ON ur.role = cp.role
WHERE cp.collection.id = :collectionId
AND cp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.ROLE
AND ur.user.id = :userId
AND cp.canWrite = true
AND (cp.expiresAt IS NULL OR cp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsRoleWritePermissionForCollection(@Param("userId") Long userId, @Param("collectionId") Long collectionId);

// ROLE live — 사용자 역할 기반 컬렉션 관리 권한 존재 여부 (canAdminCollection 판단용)
@Query("""
SELECT COUNT(cp) > 0 FROM CollectionPermission cp
JOIN UserRole ur ON ur.role = cp.role
WHERE cp.collection.id = :collectionId
AND cp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.ROLE
AND ur.user.id = :userId
AND cp.canAdmin = true
AND (cp.expiresAt IS NULL OR cp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsRoleAdminPermissionForCollection(@Param("userId") Long userId, @Param("collectionId") Long collectionId);

// DEPARTMENT live — 사용자 부서 기반 컬렉션 쓰기 권한 존재 여부 (canWriteCollection 판단용)
@Query("""
SELECT COUNT(cp) > 0 FROM CollectionPermission cp
JOIN User u ON u.department = cp.department
WHERE cp.collection.id = :collectionId
AND cp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.DEPARTMENT
AND u.id = :userId
AND cp.canWrite = true
AND (cp.expiresAt IS NULL OR cp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsDeptWritePermissionForCollection(@Param("userId") Long userId, @Param("collectionId") Long collectionId);

// DEPARTMENT live — 사용자 부서 기반 컬렉션 관리 권한 존재 여부 (canAdminCollection 판단용)
@Query("""
SELECT COUNT(cp) > 0 FROM CollectionPermission cp
JOIN User u ON u.department = cp.department
WHERE cp.collection.id = :collectionId
AND cp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.DEPARTMENT
AND u.id = :userId
AND cp.canAdmin = true
AND (cp.expiresAt IS NULL OR cp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsDeptAdminPermissionForCollection(@Param("userId") Long userId, @Param("collectionId") Long collectionId);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,82 @@
package com.opensource.docgrid.domain.permission.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.opensource.docgrid.domain.permission.entity.DocumentPermission;

public interface DocumentPermissionRepository extends JpaRepository<DocumentPermission, Long> {

// ROLE live — 사용자 역할 기반 문서 읽기 권한 존재 여부
@Query("""
SELECT COUNT(dp) > 0 FROM DocumentPermission dp
JOIN UserRole ur ON ur.role = dp.role
WHERE dp.document.id = :documentId
AND dp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.ROLE
AND ur.user.id = :userId
AND dp.canRead = true
AND (dp.expiresAt IS NULL OR dp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsRoleReadPermission(@Param("userId") Long userId, @Param("documentId") Long documentId);

// ROLE live — 사용자 역할 기반 문서 쓰기 권한 존재 여부
@Query("""
SELECT COUNT(dp) > 0 FROM DocumentPermission dp
JOIN UserRole ur ON ur.role = dp.role
WHERE dp.document.id = :documentId
AND dp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.ROLE
AND ur.user.id = :userId
AND dp.canWrite = true
AND (dp.expiresAt IS NULL OR dp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsRoleWritePermission(@Param("userId") Long userId, @Param("documentId") Long documentId);

// ROLE live — 사용자 역할 기반 문서 관리 권한 존재 여부
@Query("""
SELECT COUNT(dp) > 0 FROM DocumentPermission dp
JOIN UserRole ur ON ur.role = dp.role
WHERE dp.document.id = :documentId
AND dp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.ROLE
AND ur.user.id = :userId
AND dp.canAdmin = true
AND (dp.expiresAt IS NULL OR dp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsRoleAdminPermission(@Param("userId") Long userId, @Param("documentId") Long documentId);

// DEPARTMENT live — 사용자 부서 기반 문서 읽기 권한 존재 여부
@Query("""
SELECT COUNT(dp) > 0 FROM DocumentPermission dp
JOIN User u ON u.department = dp.department
WHERE dp.document.id = :documentId
AND dp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.DEPARTMENT
AND u.id = :userId
AND dp.canRead = true
AND (dp.expiresAt IS NULL OR dp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsDeptReadPermission(@Param("userId") Long userId, @Param("documentId") Long documentId);

// DEPARTMENT live — 사용자 부서 기반 문서 쓰기 권한 존재 여부
@Query("""
SELECT COUNT(dp) > 0 FROM DocumentPermission dp
JOIN User u ON u.department = dp.department
WHERE dp.document.id = :documentId
AND dp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.DEPARTMENT
AND u.id = :userId
AND dp.canWrite = true
AND (dp.expiresAt IS NULL OR dp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsDeptWritePermission(@Param("userId") Long userId, @Param("documentId") Long documentId);

// DEPARTMENT live — 사용자 부서 기반 문서 관리 권한 존재 여부
@Query("""
SELECT COUNT(dp) > 0 FROM DocumentPermission dp
JOIN User u ON u.department = dp.department
WHERE dp.document.id = :documentId
AND dp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.DEPARTMENT
AND u.id = :userId
AND dp.canAdmin = true
AND (dp.expiresAt IS NULL OR dp.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsDeptAdminPermission(@Param("userId") Long userId, @Param("documentId") Long documentId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@

public interface UserDocumentAccessCacheRepository extends JpaRepository<UserDocumentAccessCache, Long> {

// 특정 권한 출처(sourceType + sourceId)의 캐시 단건 조회
Optional<UserDocumentAccessCache> findByUserIdAndDocumentIdAndSourceTypeAndSourceId(
Long userId, Long documentId, AccessSourceType sourceType, Long sourceId);

// 특정 권한 출처에서 파생된 캐시 전체 무효화 (invalidated_at 일괄 설정)
@Modifying(clearAutomatically = true)
@Query("""
UPDATE UserDocumentAccessCache c
Expand All @@ -26,6 +28,7 @@ Optional<UserDocumentAccessCache> findByUserIdAndDocumentIdAndSourceTypeAndSourc
int bulkInvalidateBySource(@Param("sourceType") AccessSourceType sourceType,
@Param("sourceId") Long sourceId);

// 특정 권한 출처에서 파생된 캐시 전체 권한 갱신
@Modifying(clearAutomatically = true)
@Query("""
UPDATE UserDocumentAccessCache c
Expand All @@ -41,6 +44,7 @@ int bulkUpdateBySource(@Param("userId") Long userId,
@Param("canAdmin") boolean canAdmin,
@Param("expiresAt") LocalDateTime expiresAt);

// 특정 권한 출처에서 이미 캐시된 문서 ID 목록 조회 (배치 INSERT 시 중복 방지용)
@Query("""
SELECT c.document.id FROM UserDocumentAccessCache c
WHERE c.user.id = :userId AND c.sourceType = :sourceType AND c.sourceId = :sourceId
Expand All @@ -49,4 +53,34 @@ List<Long> findDocumentIdsByUserIdAndSourceTypeAndSourceId(
@Param("userId") Long userId,
@Param("sourceType") AccessSourceType sourceType,
@Param("sourceId") Long sourceId);

// 유효한 읽기 캐시 존재 여부 (invalidated_at IS NULL, 만료 미포함)
@Query("""
SELECT COUNT(c) > 0 FROM UserDocumentAccessCache c
WHERE c.user.id = :userId AND c.document.id = :documentId
AND c.canRead = true
AND c.invalidatedAt IS NULL
AND (c.expiresAt IS NULL OR c.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsValidReadCache(@Param("userId") Long userId, @Param("documentId") Long documentId);

// 유효한 쓰기 캐시 존재 여부
@Query("""
SELECT COUNT(c) > 0 FROM UserDocumentAccessCache c
WHERE c.user.id = :userId AND c.document.id = :documentId
AND c.canWrite = true
AND c.invalidatedAt IS NULL
AND (c.expiresAt IS NULL OR c.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsValidWriteCache(@Param("userId") Long userId, @Param("documentId") Long documentId);

// 유효한 관리 캐시 존재 여부
@Query("""
SELECT COUNT(c) > 0 FROM UserDocumentAccessCache c
WHERE c.user.id = :userId AND c.document.id = :documentId
AND c.canAdmin = true
AND c.invalidatedAt IS NULL
AND (c.expiresAt IS NULL OR c.expiresAt > CURRENT_TIMESTAMP)
""")
boolean existsValidAdminCache(@Param("userId") Long userId, @Param("documentId") Long documentId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.opensource.docgrid.domain.collection.repository.CollectionRepository;
import com.opensource.docgrid.domain.document.entity.Document;
import com.opensource.docgrid.domain.permission.converter.PermissionConverter;
import com.opensource.docgrid.domain.permission.service.query.PermissionQueryService;
import com.opensource.docgrid.domain.permission.dto.request.GrantPermissionRequest;
import com.opensource.docgrid.domain.permission.dto.response.CollectionPermissionResponse;
import com.opensource.docgrid.domain.permission.entity.CollectionPermission;
Expand Down Expand Up @@ -43,15 +44,15 @@ public class CollectionPermissionCommandService {
private final RoleRepository roleRepository;
private final DepartmentRepository departmentRepository;
private final PermissionConverter permissionConverter;
private final PermissionQueryService permissionQueryService;

// 컬렉션 권한 부여
public CollectionPermissionResponse grantPermission(Long collectionId, Long grantorId,
GrantPermissionRequest request) {
DocumentCollection collection = collectionRepository.findById(collectionId)
.orElseThrow(() -> new DocGridException(ErrorCode.COLLECTION_NOT_FOUND));

// TODO: Issue 3 PermissionService 완성 후 canAdminDocument() 로 대체
if (!collection.getOwner().getId().equals(grantorId)) {
if (!permissionQueryService.canAdminCollection(grantorId, collectionId)) {
throw new DocGridException(ErrorCode.PERMISSION_DENIED);
}

Expand Down Expand Up @@ -109,8 +110,7 @@ public void revokePermission(Long collectionId, Long permissionId, Long revokerI
throw new DocGridException(ErrorCode.COLLECTION_PERMISSION_NOT_FOUND);
}

// TODO: Issue 3 PermissionService 완성 후 canAdminDocument() 로 대체
if (!collection.getOwner().getId().equals(revokerId)) {
if (!permissionQueryService.canAdminCollection(revokerId, collectionId)) {
throw new DocGridException(ErrorCode.PERMISSION_DENIED);
}

Expand Down
Loading