Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
7fde3c0
build: open feign 의존성 추가
juanxiu Oct 14, 2025
ac7d8f2
feat: 애플리케이션에서 openfeign 어노테이션 등록
juanxiu Oct 14, 2025
4822282
feat: SampleClient 구현
juanxiu Oct 14, 2025
f9bb453
feat: vpc, subnet, sg requestDto
juanxiu Oct 14, 2025
0999889
feat: CloudProviderService interface 구현
juanxiu Oct 14, 2025
77ca004
feat: SampleCloudProviderService 구현체
juanxiu Oct 14, 2025
46fade2
feat: 테넌트 격리 이벤트와 리스너 구현
juanxiu Oct 14, 2025
41b7420
feat: TenantIsolationService 테넌트 격리 정책 서비스
juanxiu Oct 14, 2025
c551784
feat: CloudResourceResult Dto 구현
juanxiu Oct 14, 2025
8a71bcf
Merge branch 'develop' of github.com:CCCloudPlatform/AgenticCP-Core i…
juanxiu Oct 14, 2025
793869e
Merge
juanxiu Oct 15, 2025
9dfbe0b
feat: SampleCloudProviderService 보안그룹 메서드
juanxiu Oct 15, 2025
a7459f5
feat: 격리 결과와 상태용 DTO
juanxiu Oct 15, 2025
35db24e
feat: 테넌트 격리 정책 인터페이스
juanxiu Oct 15, 2025
e08f2c8
feat: 테넌트 격리 정책 인터페이스 생성하는 팩토리
juanxiu Oct 15, 2025
0188b52
feat: TenantIsolationService에 격리 정책 적용 메서드까지 구현
juanxiu Oct 15, 2025
4143d47
feat: 불필요한 파일 삭제
juanxiu Oct 24, 2025
3fa5631
refactor: 파일 구조 변경
juanxiu Oct 24, 2025
3d41db7
feat: 리소스 생성 인터페이스, 요청, 샘플 구현체
juanxiu Oct 24, 2025
613e924
feat: Isolationstrategy 인터페이스와 팩토리
juanxiu Oct 24, 2025
8e0e05f
feat: SharedIsolationStrategy 구현체
juanxiu Oct 24, 2025
b7d9c29
feat: AwstenantIsolationAdapter 컴포넌트
juanxiu Oct 24, 2025
38c9af4
feat: AwsCloudResourceCreator 샘플 리소스 생성 구현체
juanxiu Oct 24, 2025
2694e7e
feat: IsolationStrategyFactory 맵 조회 로직 수정
juanxiu Oct 24, 2025
a571027
Merge branch 'develop' of github.com:CCCloudPlatform/AgenticCP-Core i…
juanxiu Oct 24, 2025
d5e6826
refactor: 파일 구조 변경
juanxiu Oct 24, 2025
28ed9d6
Merge branch 'develop' of github.com:CCCloudPlatform/AgenticCP-Core i…
juanxiu Oct 24, 2025
f37f976
Merge branch 'develop' of github.com:CCCloudPlatform/AgenticCP-Core i…
juanxiu Nov 8, 2025
f6c971f
Merge branch 'develop' of github.com:CCCloudPlatform/AgenticCP-Core i…
juanxiu Nov 23, 2025
e7e5366
refactor: Organization-Tenant 관계를 1:N에서 1:1로 변경
juanxiu Nov 24, 2025
42952f3
feat: TenantIsolationService 재작성 및 Repository 추가
juanxiu Nov 24, 2025
16a1614
feat: 리소스 소유권 관리 엔티티 및 서비스 추가
juanxiu Nov 24, 2025
f472da1
feat: 리소스 접근 제어 서비스 및 CloudResourceService 통합
juanxiu Nov 24, 2025
3c6a1e7
test: 테넌트 격리 수준 정책 관련 서비스 테스트 추가
juanxiu Nov 24, 2025
104e621
fix: AwsTenantIsolationAdapter 및 AwsCloudResourceCreator 수정
juanxiu Nov 24, 2025
7be9b4d
docs: 테넌트 격리 수준 정책 구현 문서 추가
juanxiu Nov 24, 2025
04d755e
fix: FeatureFlagAuditServiceTest requestPath 누락 오류 해결 - AuditEventBui…
juanxiu Nov 24, 2025
8f647fd
Revert "fix: FeatureFlagAuditServiceTest requestPath 누락 오류 해결 - Audit…
juanxiu Nov 24, 2025
739d4ed
Merge branch 'develop' of github.com:CCCloudPlatform/AgenticCP-Core i…
juanxiu Nov 24, 2025
1dec198
Merge branch 'develop' of github.com:CCCloudPlatform/AgenticCP-Core i…
juanxiu Dec 7, 2025
fd9e787
Merge: 충돌 해결 완료
juanxiu Dec 16, 2025
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
1,243 changes: 1,243 additions & 0 deletions docs/TENANT_ISOLATION_POLICY_IMPLEMENTATION.md

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,14 @@
<scope>test</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>4.1.3</version>
</dependency>

<!-- Awaitility for async testing -->
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableJpaRepositories
@EnableAsync
@EnableScheduling
public class AgenticCpCoreApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.agenticcp.core.common.util;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

/**
* SecurityContext 유틸리티
*
* @author AgenticCP Team
* @version 1.0.0
*/
public class SecurityContextUtils {

/**
* 현재 인증된 사용자명 조회
*
* @return 사용자명, 없으면 null
*/
public static String getCurrentUsername() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated()) {
return authentication.getName();
}
return null;
}

/**
* 현재 인증된 사용자명 조회 (null 체크 포함)
*
* @return 사용자명
* @throws IllegalStateException 인증 정보가 없는 경우
*/
public static String getCurrentUsernameOrThrow() {
String username = getCurrentUsername();
if (username == null) {
throw new IllegalStateException("현재 인증된 사용자 정보가 없습니다");
}
return username;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.agenticcp.core.domain.cloud.entity;

import com.agenticcp.core.common.entity.BaseEntity;
import com.agenticcp.core.domain.tenant.entity.Tenant;
import com.agenticcp.core.domain.user.entity.User;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* 리소스 소유자 엔티티
*
* <p>리소스와 사용자 간의 소유권 관계를 관리하는 중간 테이블입니다.
* DEDICATED 격리 모드에서 리소스 접근 권한을 제어하는데 사용됩니다.</p>
*
* @author AgenticCP Team
* @version 1.0.0
*/
@Entity
@Table(name = "resource_owners",
uniqueConstraints = @UniqueConstraint(
name = "uk_resource_user_deleted",
columnNames = {"resource_id", "user_id", "is_deleted"}
),
indexes = {
@Index(name = "idx_resource_owner_user_tenant", columnList = "user_id, tenant_id, is_deleted"),
@Index(name = "idx_resource_owner_resource_tenant", columnList = "resource_id, tenant_id, is_deleted")
})
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ResourceOwner extends BaseEntity {

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "resource_id", nullable = false)
private CloudResource resource;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "tenant_id", nullable = false)
private Tenant tenant;

@Enumerated(EnumType.STRING)
@Column(name = "access_type", nullable = false, length = 20)
@Builder.Default
private AccessType accessType = AccessType.OWNER;

/**
* 접근 타입 열거형
*/
public enum AccessType {
/** 소유자 (리소스 생성자) */
OWNER,
/** 공유 접근 (SHARED 모드에서 자동 추가, 향후 확장용) */
SHARED,
/** 읽기 전용 (향후 확장용) */
READ_ONLY
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package com.agenticcp.core.domain.cloud.repository;

import com.agenticcp.core.domain.cloud.entity.CloudResource;
import com.agenticcp.core.domain.cloud.entity.ResourceOwner;
import com.agenticcp.core.domain.tenant.entity.Tenant;
import com.agenticcp.core.domain.user.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

/**
* 리소스 소유자 Repository
*
* @author AgenticCP Team
* @version 1.0.0
*/
@Repository
public interface ResourceOwnerRepository extends JpaRepository<ResourceOwner, Long> {

/**
* 사용자가 소유한 리소스 목록 조회 (JOIN 최적화)
*
* @param user 사용자
* @param tenant 테넌트
* @return 소유한 리소스 목록
*/
@Query("SELECT DISTINCT ro.resource FROM ResourceOwner ro " +
"LEFT JOIN FETCH ro.resource.provider " +
"LEFT JOIN FETCH ro.resource.region " +
"LEFT JOIN FETCH ro.resource.service " +
"LEFT JOIN FETCH ro.resource.tenant " +
"WHERE ro.user = :user AND ro.tenant = :tenant AND ro.isDeleted = false " +
"AND ro.resource.isDeleted = false")
List<CloudResource> findResourcesByOwner(@Param("user") User user,
@Param("tenant") Tenant tenant);

/**
* 리소스 소유권 확인
*
* @param resource 리소스
* @param user 사용자
* @return 소유권 존재 여부
*/
boolean existsByResourceAndUserAndIsDeletedFalse(CloudResource resource, User user);

/**
* 리소스 소유권 조회
*
* @param resource 리소스
* @param user 사용자
* @return 리소스 소유권 정보
*/
Optional<ResourceOwner> findByResourceAndUserAndIsDeletedFalse(CloudResource resource, User user);

/**
* 리소스의 모든 소유자 조회
*
* @param resource 리소스
* @return 소유자 목록
*/
List<ResourceOwner> findByResourceAndIsDeletedFalse(CloudResource resource);

/**
* 사용자와 테넌트로 소유권 목록 조회
*
* @param user 사용자
* @param tenant 테넌트
* @return 소유권 목록
*/
List<ResourceOwner> findByUserAndTenantAndIsDeletedFalse(User user, Tenant tenant);

/**
* 사용자가 소유한 리소스 ID 목록 조회 (배치 최적화)
*
* @param user 사용자
* @param tenant 테넌트
* @return 소유한 리소스 ID 목록
*/
@Query("SELECT ro.resource.id FROM ResourceOwner ro " +
"WHERE ro.user = :user AND ro.tenant = :tenant AND ro.isDeleted = false")
List<Long> findResourceIdsByOwner(@Param("user") User user,
@Param("tenant") Tenant tenant);

/**
* 여러 리소스에 대한 사용자 소유권 일괄 확인 (배치 최적화)
*
* @param user 사용자
* @param resourceIds 리소스 ID 목록
* @param tenant 테넌트
* @return 소유한 리소스 ID 목록
*/
@Query("SELECT ro.resource.id FROM ResourceOwner ro " +
"WHERE ro.user = :user " +
"AND ro.resource.id IN :resourceIds " +
"AND ro.tenant = :tenant " +
"AND ro.isDeleted = false")
List<Long> findOwnedResourceIds(@Param("user") User user,
@Param("resourceIds") List<Long> resourceIds,
@Param("tenant") Tenant tenant);
}

Loading
Loading