feat: IP 화이트리스트 관리 API 구현 (#50)
All checks were successful
SPMS_API/pipeline/head This commit looks good
All checks were successful
SPMS_API/pipeline/head This commit looks good
Reviewed-on: https://git.ipstein.myds.me/SPMS/SPMS_API/pulls/51
This commit is contained in:
commit
9762052dd6
|
|
@ -125,4 +125,52 @@ public class ServiceController : ControllerBase
|
||||||
var result = await _serviceManagementService.GetCredentialsAsync(serviceCode);
|
var result = await _serviceManagementService.GetCredentialsAsync(serviceCode);
|
||||||
return Ok(ApiResponse<CredentialsResponseDto>.Success(result));
|
return Ok(ApiResponse<CredentialsResponseDto>.Success(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("{serviceCode}/ip/list")]
|
||||||
|
[SwaggerOperation(
|
||||||
|
Summary = "IP 화이트리스트 조회",
|
||||||
|
Description = "서비스에 등록된 IP 화이트리스트 목록을 조회합니다.")]
|
||||||
|
[SwaggerResponse(200, "조회 성공", typeof(ApiResponse<IpListResponseDto>))]
|
||||||
|
[SwaggerResponse(401, "인증되지 않은 요청")]
|
||||||
|
[SwaggerResponse(403, "권한 없음")]
|
||||||
|
[SwaggerResponse(404, "서비스를 찾을 수 없음")]
|
||||||
|
public async Task<IActionResult> GetIpListAsync([FromRoute] string serviceCode)
|
||||||
|
{
|
||||||
|
var result = await _serviceManagementService.GetIpListAsync(serviceCode);
|
||||||
|
return Ok(ApiResponse<IpListResponseDto>.Success(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("{serviceCode}/ip/add")]
|
||||||
|
[SwaggerOperation(
|
||||||
|
Summary = "IP 추가",
|
||||||
|
Description = "서비스의 IP 화이트리스트에 새 IP를 추가합니다. IPv4 형식만 지원합니다.")]
|
||||||
|
[SwaggerResponse(200, "추가 성공", typeof(ApiResponse<ServiceIpDto>))]
|
||||||
|
[SwaggerResponse(400, "잘못된 요청 (유효하지 않은 IP 형식)")]
|
||||||
|
[SwaggerResponse(401, "인증되지 않은 요청")]
|
||||||
|
[SwaggerResponse(403, "권한 없음")]
|
||||||
|
[SwaggerResponse(404, "서비스를 찾을 수 없음")]
|
||||||
|
[SwaggerResponse(409, "이미 등록된 IP")]
|
||||||
|
public async Task<IActionResult> AddIpAsync(
|
||||||
|
[FromRoute] string serviceCode,
|
||||||
|
[FromBody] AddIpRequestDto request)
|
||||||
|
{
|
||||||
|
var result = await _serviceManagementService.AddIpAsync(serviceCode, request);
|
||||||
|
return Ok(ApiResponse<ServiceIpDto>.Success(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("{serviceCode}/ip/delete")]
|
||||||
|
[SwaggerOperation(
|
||||||
|
Summary = "IP 삭제",
|
||||||
|
Description = "서비스의 IP 화이트리스트에서 IP를 삭제합니다.")]
|
||||||
|
[SwaggerResponse(200, "삭제 성공", typeof(ApiResponse))]
|
||||||
|
[SwaggerResponse(401, "인증되지 않은 요청")]
|
||||||
|
[SwaggerResponse(403, "권한 없음")]
|
||||||
|
[SwaggerResponse(404, "서비스 또는 IP를 찾을 수 없음")]
|
||||||
|
public async Task<IActionResult> DeleteIpAsync(
|
||||||
|
[FromRoute] string serviceCode,
|
||||||
|
[FromBody] DeleteIpRequestDto request)
|
||||||
|
{
|
||||||
|
await _serviceManagementService.DeleteIpAsync(serviceCode, request);
|
||||||
|
return Ok(ApiResponse.Success());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
SPMS.Application/DTOs/Service/AddIpRequestDto.cs
Normal file
12
SPMS.Application/DTOs/Service/AddIpRequestDto.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace SPMS.Application.DTOs.Service;
|
||||||
|
|
||||||
|
public class AddIpRequestDto
|
||||||
|
{
|
||||||
|
[Required(ErrorMessage = "IP 주소는 필수입니다.")]
|
||||||
|
[RegularExpression(
|
||||||
|
@"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
|
||||||
|
ErrorMessage = "유효한 IPv4 주소 형식이 아닙니다.")]
|
||||||
|
public string IpAddress { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
9
SPMS.Application/DTOs/Service/DeleteIpRequestDto.cs
Normal file
9
SPMS.Application/DTOs/Service/DeleteIpRequestDto.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace SPMS.Application.DTOs.Service;
|
||||||
|
|
||||||
|
public class DeleteIpRequestDto
|
||||||
|
{
|
||||||
|
[Required(ErrorMessage = "IP ID는 필수입니다.")]
|
||||||
|
public long IpId { get; set; }
|
||||||
|
}
|
||||||
14
SPMS.Application/DTOs/Service/IpListResponseDto.cs
Normal file
14
SPMS.Application/DTOs/Service/IpListResponseDto.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
namespace SPMS.Application.DTOs.Service;
|
||||||
|
|
||||||
|
public class IpListResponseDto
|
||||||
|
{
|
||||||
|
public string ServiceCode { get; set; } = string.Empty;
|
||||||
|
public List<ServiceIpDto> Items { get; set; } = new();
|
||||||
|
public int TotalCount { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ServiceIpDto
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
public string IpAddress { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
@ -11,4 +11,9 @@ public interface IServiceManagementService
|
||||||
Task RegisterApnsCredentialsAsync(string serviceCode, ApnsCredentialsRequestDto request);
|
Task RegisterApnsCredentialsAsync(string serviceCode, ApnsCredentialsRequestDto request);
|
||||||
Task RegisterFcmCredentialsAsync(string serviceCode, FcmCredentialsRequestDto request);
|
Task RegisterFcmCredentialsAsync(string serviceCode, FcmCredentialsRequestDto request);
|
||||||
Task<CredentialsResponseDto> GetCredentialsAsync(string serviceCode);
|
Task<CredentialsResponseDto> GetCredentialsAsync(string serviceCode);
|
||||||
|
|
||||||
|
// IP Whitelist
|
||||||
|
Task<IpListResponseDto> GetIpListAsync(string serviceCode);
|
||||||
|
Task<ServiceIpDto> AddIpAsync(string serviceCode, AddIpRequestDto request);
|
||||||
|
Task DeleteIpAsync(string serviceCode, DeleteIpRequestDto request);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -279,6 +279,90 @@ public class ServiceManagementService : IServiceManagementService
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IpListResponseDto> GetIpListAsync(string serviceCode)
|
||||||
|
{
|
||||||
|
var service = await _serviceRepository.GetByServiceCodeWithIpsAsync(serviceCode);
|
||||||
|
if (service is null)
|
||||||
|
{
|
||||||
|
throw new SpmsException(
|
||||||
|
ErrorCodes.NotFound,
|
||||||
|
"서비스를 찾을 수 없습니다.",
|
||||||
|
404);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new IpListResponseDto
|
||||||
|
{
|
||||||
|
ServiceCode = service.ServiceCode,
|
||||||
|
Items = service.ServiceIps.Select(ip => new ServiceIpDto
|
||||||
|
{
|
||||||
|
Id = ip.Id,
|
||||||
|
IpAddress = ip.IpAddress
|
||||||
|
}).ToList(),
|
||||||
|
TotalCount = service.ServiceIps.Count
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ServiceIpDto> AddIpAsync(string serviceCode, AddIpRequestDto request)
|
||||||
|
{
|
||||||
|
var service = await _serviceRepository.GetByServiceCodeAsync(serviceCode);
|
||||||
|
if (service is null)
|
||||||
|
{
|
||||||
|
throw new SpmsException(
|
||||||
|
ErrorCodes.NotFound,
|
||||||
|
"서비스를 찾을 수 없습니다.",
|
||||||
|
404);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for duplicate IP
|
||||||
|
var exists = await _serviceRepository.ServiceIpExistsAsync(service.Id, request.IpAddress);
|
||||||
|
if (exists)
|
||||||
|
{
|
||||||
|
throw new SpmsException(
|
||||||
|
ErrorCodes.Conflict,
|
||||||
|
"이미 등록된 IP 주소입니다.",
|
||||||
|
409);
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceIp = new ServiceIp
|
||||||
|
{
|
||||||
|
ServiceId = service.Id,
|
||||||
|
IpAddress = request.IpAddress
|
||||||
|
};
|
||||||
|
|
||||||
|
await _serviceRepository.AddServiceIpAsync(serviceIp);
|
||||||
|
await _unitOfWork.SaveChangesAsync();
|
||||||
|
|
||||||
|
return new ServiceIpDto
|
||||||
|
{
|
||||||
|
Id = serviceIp.Id,
|
||||||
|
IpAddress = serviceIp.IpAddress
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteIpAsync(string serviceCode, DeleteIpRequestDto request)
|
||||||
|
{
|
||||||
|
var service = await _serviceRepository.GetByServiceCodeAsync(serviceCode);
|
||||||
|
if (service is null)
|
||||||
|
{
|
||||||
|
throw new SpmsException(
|
||||||
|
ErrorCodes.NotFound,
|
||||||
|
"서비스를 찾을 수 없습니다.",
|
||||||
|
404);
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceIp = await _serviceRepository.GetServiceIpByIdAsync(request.IpId);
|
||||||
|
if (serviceIp is null || serviceIp.ServiceId != service.Id)
|
||||||
|
{
|
||||||
|
throw new SpmsException(
|
||||||
|
ErrorCodes.NotFound,
|
||||||
|
"IP 주소를 찾을 수 없습니다.",
|
||||||
|
404);
|
||||||
|
}
|
||||||
|
|
||||||
|
_serviceRepository.DeleteServiceIp(serviceIp);
|
||||||
|
await _unitOfWork.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
private static ServiceSummaryDto MapToSummaryDto(Service service)
|
private static ServiceSummaryDto MapToSummaryDto(Service service)
|
||||||
{
|
{
|
||||||
return new ServiceSummaryDto
|
return new ServiceSummaryDto
|
||||||
|
|
|
||||||
|
|
@ -8,5 +8,12 @@ public interface IServiceRepository : IRepository<Service>
|
||||||
Task<Service?> GetByServiceCodeAsync(string serviceCode);
|
Task<Service?> GetByServiceCodeAsync(string serviceCode);
|
||||||
Task<Service?> GetByApiKeyAsync(string apiKey);
|
Task<Service?> GetByApiKeyAsync(string apiKey);
|
||||||
Task<Service?> GetByIdWithIpsAsync(long id);
|
Task<Service?> GetByIdWithIpsAsync(long id);
|
||||||
|
Task<Service?> GetByServiceCodeWithIpsAsync(string serviceCode);
|
||||||
Task<IReadOnlyList<Service>> GetByStatusAsync(ServiceStatus status);
|
Task<IReadOnlyList<Service>> GetByStatusAsync(ServiceStatus status);
|
||||||
|
|
||||||
|
// ServiceIp methods
|
||||||
|
Task<ServiceIp?> GetServiceIpByIdAsync(long ipId);
|
||||||
|
Task<bool> ServiceIpExistsAsync(long serviceId, string ipAddress);
|
||||||
|
Task AddServiceIpAsync(ServiceIp serviceIp);
|
||||||
|
void DeleteServiceIp(ServiceIp serviceIp);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,25 @@ public class ServiceRepository : Repository<Service>, IServiceRepository
|
||||||
.Include(s => s.ServiceIps)
|
.Include(s => s.ServiceIps)
|
||||||
.FirstOrDefaultAsync(s => s.Id == id);
|
.FirstOrDefaultAsync(s => s.Id == id);
|
||||||
|
|
||||||
|
public async Task<Service?> GetByServiceCodeWithIpsAsync(string serviceCode)
|
||||||
|
=> await _dbSet
|
||||||
|
.Include(s => s.ServiceIps)
|
||||||
|
.FirstOrDefaultAsync(s => s.ServiceCode == serviceCode);
|
||||||
|
|
||||||
public async Task<IReadOnlyList<Service>> GetByStatusAsync(ServiceStatus status)
|
public async Task<IReadOnlyList<Service>> GetByStatusAsync(ServiceStatus status)
|
||||||
=> await _dbSet.Where(s => s.Status == status).ToListAsync();
|
=> await _dbSet.Where(s => s.Status == status).ToListAsync();
|
||||||
|
|
||||||
|
// ServiceIp methods
|
||||||
|
public async Task<ServiceIp?> GetServiceIpByIdAsync(long ipId)
|
||||||
|
=> await _context.Set<ServiceIp>().FirstOrDefaultAsync(ip => ip.Id == ipId);
|
||||||
|
|
||||||
|
public async Task<bool> ServiceIpExistsAsync(long serviceId, string ipAddress)
|
||||||
|
=> await _context.Set<ServiceIp>()
|
||||||
|
.AnyAsync(ip => ip.ServiceId == serviceId && ip.IpAddress == ipAddress);
|
||||||
|
|
||||||
|
public async Task AddServiceIpAsync(ServiceIp serviceIp)
|
||||||
|
=> await _context.Set<ServiceIp>().AddAsync(serviceIp);
|
||||||
|
|
||||||
|
public void DeleteServiceIp(ServiceIp serviceIp)
|
||||||
|
=> _context.Set<ServiceIp>().Remove(serviceIp);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user