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 @@ -3,6 +3,7 @@
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

@Schema(description = "컬렉션 문서 추가 요청")
public record AddDocumentRequest(
@Schema(description = "추가할 문서 ID") @NotNull Long documentId
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;

@Schema(description = "컬렉션 생성 요청")
public record CreateCollectionRequest(
@Schema(description = "컬렉션 이름") @NotBlank String name,
@Schema(description = "컬렉션 설명") String description,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import io.swagger.v3.oas.annotations.media.Schema;

@Schema(description = "컬렉션 문서 추가 결과")
public record CollectionDocumentResponse(
@Schema(description = "컬렉션 ID") Long collectionId,
@Schema(description = "문서 ID") Long documentId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import io.swagger.v3.oas.annotations.media.Schema;

@Schema(description = "컬렉션 정보")
public record CollectionResponse(
@Schema(description = "컬렉션 ID") Long collectionId,
@Schema(description = "컬렉션 이름") String name,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package com.opensource.docgrid.domain.collection.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

import com.opensource.docgrid.domain.collection.entity.CollectionDocument;

public interface CollectionDocumentRepository extends JpaRepository<CollectionDocument, Long> {

boolean existsByCollectionIdAndDocumentId(Long collectionId, Long documentId);

List<CollectionDocument> findAllByCollectionId(Long collectionId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.opensource.docgrid.domain.permission.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


import com.opensource.docgrid.domain.auth.annotation.CurrentUser;
import com.opensource.docgrid.domain.permission.dto.request.GrantPermissionRequest;
import com.opensource.docgrid.domain.permission.dto.response.CollectionPermissionResponse;
import com.opensource.docgrid.domain.permission.dto.response.DocumentPermissionResponse;
import com.opensource.docgrid.domain.permission.service.command.CollectionPermissionCommandService;
import com.opensource.docgrid.domain.permission.service.command.DocumentPermissionCommandService;
import com.opensource.docgrid.global.common.response.ApiResponse;
import com.opensource.docgrid.global.common.response.ResponseUtils;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@Tag(name = "Permission", description = "권한 관련 API")
@RestController
@RequestMapping("/permissions")
@RequiredArgsConstructor
public class PermissionController {

private final CollectionPermissionCommandService collectionPermissionCommandService;
private final DocumentPermissionCommandService documentPermissionCommandService;

@Operation(
summary = "컬렉션 권한 부여",
description = "컬렉션에 USER/ROLE/DEPARTMENT 단위로 권한을 부여합니다. 컬렉션 소유자(owner)만 가능합니다. " +
"targetType에 맞는 ID 필드(userId/roleId/departmentId) 하나만 입력해야 합니다. " +
"USER 대상인 경우 컬렉션 내 문서에 대한 접근 캐시가 즉시 갱신됩니다."
)
@PostMapping("/collections/{collectionId}")
public ResponseEntity<ApiResponse<CollectionPermissionResponse>> grantCollectionPermission(
@PathVariable Long collectionId,
@Parameter(hidden = true) @CurrentUser Long userId,
@RequestBody @Valid GrantPermissionRequest request) {
return ResponseUtils.created(
collectionPermissionCommandService.grantPermission(collectionId, userId, request));
}

@Operation(
summary = "컬렉션 권한 회수",
description = "부여된 컬렉션 권한을 회수합니다. 컬렉션 소유자(owner)만 가능합니다. " +
"USER 대상 권한이었다면 접근 캐시도 즉시 무효화됩니다."
)
@DeleteMapping("/collections/{collectionId}/{permissionId}")
public ResponseEntity<ApiResponse<Void>> revokeCollectionPermission(
@PathVariable Long collectionId,
@PathVariable Long permissionId,
@Parameter(hidden = true) @CurrentUser Long userId) {
collectionPermissionCommandService.revokePermission(collectionId, permissionId, userId);
return ResponseUtils.noContent();
}

@Operation(
summary = "문서 예외 권한 부여",
description = "특정 문서 하나에만 적용되는 예외 권한을 부여합니다. 문서 소유자(owner)만 가능합니다. " +
"기본 권한은 collection_permissions로 관리하고, 이 API는 예외 케이스에만 최소한으로 사용하세요. " +
"USER 대상인 경우 해당 문서의 접근 캐시가 즉시 갱신됩니다."
)
@PostMapping("/documents/{documentId}")
public ResponseEntity<ApiResponse<DocumentPermissionResponse>> grantDocumentPermission(
@PathVariable Long documentId,
@Parameter(hidden = true) @CurrentUser Long userId,
@RequestBody @Valid GrantPermissionRequest request) {
return ResponseUtils.created(
documentPermissionCommandService.grantPermission(documentId, userId, request));
}

@Operation(
summary = "문서 예외 권한 회수",
description = "부여된 문서 예외 권한을 회수합니다. 문서 소유자(owner)만 가능합니다. " +
"USER 대상 권한이었다면 해당 문서의 접근 캐시도 즉시 무효화됩니다."
)
@DeleteMapping("/documents/{documentId}/{permissionId}")
public ResponseEntity<ApiResponse<Void>> revokeDocumentPermission(
@PathVariable Long documentId,
@PathVariable Long permissionId,
@Parameter(hidden = true) @CurrentUser Long userId) {
documentPermissionCommandService.revokePermission(documentId, permissionId, userId);
return ResponseUtils.noContent();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.opensource.docgrid.domain.permission.converter;

import org.springframework.stereotype.Component;

import com.opensource.docgrid.domain.permission.dto.response.CollectionPermissionResponse;
import com.opensource.docgrid.domain.permission.dto.response.DocumentPermissionResponse;
import com.opensource.docgrid.domain.permission.entity.CollectionPermission;
import com.opensource.docgrid.domain.permission.entity.DocumentPermission;

@Component
public class PermissionConverter {

public CollectionPermissionResponse toCollectionPermissionResponse(CollectionPermission p) {
return new CollectionPermissionResponse(
p.getId(),
p.getCollection().getId(),
p.getTargetType(),
p.getUser() != null ? p.getUser().getId() : null,
p.getRole() != null ? p.getRole().getId() : null,
p.getDepartment() != null ? p.getDepartment().getId() : null,
p.getPermissionType(),
p.isCanRead(),
p.isCanWrite(),
p.isCanAdmin(),
p.getGrantedBy() != null ? p.getGrantedBy().getId() : null,
p.getGrantedAt(),
p.getExpiresAt()
);
}

public DocumentPermissionResponse toDocumentPermissionResponse(DocumentPermission p) {
return new DocumentPermissionResponse(
p.getId(),
p.getDocument().getId(),
p.getTargetType(),
p.getUser() != null ? p.getUser().getId() : null,
p.getRole() != null ? p.getRole().getId() : null,
p.getDepartment() != null ? p.getDepartment().getId() : null,
p.getPermissionType(),
p.isCanRead(),
p.isCanWrite(),
p.isCanAdmin(),
p.getGrantedBy() != null ? p.getGrantedBy().getId() : null,
p.getGrantedAt(),
p.getExpiresAt()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.opensource.docgrid.domain.permission.dto.request;

import java.time.LocalDateTime;

import com.opensource.docgrid.domain.permission.enums.PermissionTargetType;
import com.opensource.docgrid.domain.permission.enums.PermissionType;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

@Schema(description = "권한 부여 요청")
public record GrantPermissionRequest(
@Schema(description = "권한 부여 대상 타입 (USER / ROLE / DEPARTMENT)") @NotNull PermissionTargetType targetType,
@Schema(description = "대상 사용자 ID (targetType=USER일 때만 입력)") Long userId,
@Schema(description = "대상 역할 ID (targetType=ROLE일 때만 입력)") Long roleId,
@Schema(description = "대상 부서 ID (targetType=DEPARTMENT일 때만 입력)") Long departmentId,
@Schema(description = "권한 종류 (READ / WRITE / ADMIN)") @NotNull PermissionType permissionType,
@Schema(description = "권한 만료 시각 (null이면 만료 없음)") LocalDateTime expiresAt
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.opensource.docgrid.domain.permission.dto.response;

import java.time.LocalDateTime;

import com.opensource.docgrid.domain.permission.enums.PermissionTargetType;
import com.opensource.docgrid.domain.permission.enums.PermissionType;

import io.swagger.v3.oas.annotations.media.Schema;

@Schema(description = "컬렉션 권한 부여 결과")
public record CollectionPermissionResponse(
@Schema(description = "권한 ID") Long permissionId,
@Schema(description = "컬렉션 ID") Long collectionId,
@Schema(description = "권한 대상 타입") PermissionTargetType targetType,
@Schema(description = "대상 사용자 ID") Long userId,
@Schema(description = "대상 역할 ID") Long roleId,
@Schema(description = "대상 부서 ID") Long departmentId,
@Schema(description = "권한 종류") PermissionType permissionType,
@Schema(description = "읽기 권한") boolean canRead,
@Schema(description = "쓰기 권한") boolean canWrite,
@Schema(description = "관리 권한") boolean canAdmin,
@Schema(description = "부여한 사용자 ID") Long grantedBy,
@Schema(description = "부여 시각") LocalDateTime grantedAt,
@Schema(description = "만료 시각") LocalDateTime expiresAt
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.opensource.docgrid.domain.permission.dto.response;

import java.time.LocalDateTime;

import com.opensource.docgrid.domain.permission.enums.PermissionTargetType;
import com.opensource.docgrid.domain.permission.enums.PermissionType;

import io.swagger.v3.oas.annotations.media.Schema;

@Schema(description = "문서 권한 부여 결과")
public record DocumentPermissionResponse(
@Schema(description = "권한 ID") Long permissionId,
@Schema(description = "문서 ID") Long documentId,
@Schema(description = "권한 대상 타입") PermissionTargetType targetType,
@Schema(description = "대상 사용자 ID") Long userId,
@Schema(description = "대상 역할 ID") Long roleId,
@Schema(description = "대상 부서 ID") Long departmentId,
@Schema(description = "권한 종류") PermissionType permissionType,
@Schema(description = "읽기 권한") boolean canRead,
@Schema(description = "쓰기 권한") boolean canWrite,
@Schema(description = "관리 권한") boolean canAdmin,
@Schema(description = "부여한 사용자 ID") Long grantedBy,
@Schema(description = "부여 시각") LocalDateTime grantedAt,
@Schema(description = "만료 시각") LocalDateTime expiresAt
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,17 @@ public UserDocumentAccessCache(User user, Document document, boolean canRead, bo
this.invalidatedAt = invalidatedAt;
this.expiresAt = expiresAt;
}

public void grant(boolean canRead, boolean canWrite, boolean canAdmin, LocalDateTime expiresAt) {
this.canRead = canRead;
this.canWrite = canWrite;
this.canAdmin = canAdmin;
this.invalidatedAt = null;
this.computedAt = LocalDateTime.now();
this.expiresAt = expiresAt;
}

public void invalidate() {
this.invalidatedAt = LocalDateTime.now();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.opensource.docgrid.domain.permission.repository;

import org.springframework.data.jpa.repository.JpaRepository;

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

public interface CollectionPermissionRepository extends JpaRepository<CollectionPermission, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.opensource.docgrid.domain.permission.repository;

import org.springframework.data.jpa.repository.JpaRepository;

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

public interface DocumentPermissionRepository extends JpaRepository<DocumentPermission, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.opensource.docgrid.domain.permission.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;

import com.opensource.docgrid.domain.permission.entity.UserDocumentAccessCache;
import com.opensource.docgrid.domain.permission.enums.AccessSourceType;

public interface UserDocumentAccessCacheRepository extends JpaRepository<UserDocumentAccessCache, Long> {

Optional<UserDocumentAccessCache> findByUserIdAndDocumentIdAndSourceTypeAndSourceId(
Long userId, Long documentId, AccessSourceType sourceType, Long sourceId);
}
Loading