fix(message_bizppurio): webhook 미들웨어 self-gate 전환 + 발송 시나리오 scale 면제
라우트 파일에서 직접 부착하던 webhook IP 화이트리스트를 plugin.php getMiddleware self-gate 선언으로 전환한다(코어 확장 미들웨어 규정). 테스트 게이트 활성 헬퍼를 이식해 webhook 보안 테스트를 복구하고, 단건 발송 Job 시나리오에 scale 축 면제를 명시한다(볼륨 무관 상수 메모리). webhook API 문서에 self-gate 부착 방식을 함께 보강한다.
This commit is contained in:
@@ -78,7 +78,7 @@ Content-Type: application/json
|
||||
|
||||
비즈뿌리오가 문자·알림톡 발송 결과를 URL PUSH 로 통보하는 리포트 수신 엔드포인트다. 운영자가 이 주소를 비즈뿌리오에 등록하면(환경설정 화면의 리포트 수신 주소), 발송 후 결과가 이 엔드포인트로 전송된다.
|
||||
|
||||
- **인증**: 코어 토큰/IDV 미들웨어를 라우트 레벨에서 제외하고, 인증을 IP 화이트리스트로 대체한다. 화이트리스트 밖 IP 는 403.
|
||||
- **인증**: 코어 토큰/IDV 미들웨어를 라우트 레벨에서 제외하고, 인증을 IP 화이트리스트로 대체한다. 화이트리스트 밖 IP 는 403. IP 화이트리스트는 `plugin.php::getMiddleware()` 에서 이 라우트명(`api.plugins.sirsoft-message_bizppurio.webhook`)으로 self-gate 선언하며, 코어 게이트가 요청 시점에 부착한다(라우트 파일 직접 부착 아님).
|
||||
- **처리**: `REFKEY` 로 발송 이력을 조회한다. 없으면(위조/미매칭) 200 으로 흡수한다. 이미 리포트가 반영된 이력(`reported_at` 존재)이면 replay 로 판정해 멱등 처리한다. 그 외에는 `RESULT` 코드를 분류(성공/실패/잔액부족)해 상태를 전이하고 `media`·`fallback_status`·`raw_payload`·`reported_at` 을 기록한다.
|
||||
- **잔액부족**: `RESULT` 가 9070(문자)/7436(알림톡)이면 이력을 실패로 뒤집고 관리자에게 자체 알림을 1회 발송한다.
|
||||
- 응답은 항상 200 이다(비즈뿌리오가 실패 응답을 재전송하지 않도록). replay 멱등이 중복 처리를 막는다.
|
||||
|
||||
@@ -12,6 +12,7 @@ use App\Models\NotificationDefinition;
|
||||
use Database\Seeders\NotificationDefinitionSeeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Plugins\Sirsoft\MessageBizppurio\Http\Middleware\BizppurioWebhookIpWhitelist;
|
||||
use Plugins\Sirsoft\MessageBizppurio\Listeners\GuestPhoneExtractListener;
|
||||
use Plugins\Sirsoft\MessageBizppurio\Listeners\LinkNotificationLogListener;
|
||||
use Plugins\Sirsoft\MessageBizppurio\Listeners\RegisterNotificationChannelsListener;
|
||||
@@ -450,4 +451,26 @@ class Plugin extends AbstractPlugin
|
||||
ValidateBizppurioSettingsListener::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 확장 미들웨어 선언 (self-gate)
|
||||
*
|
||||
* webhook(URL PUSH) 리포트 수신 엔드포인트에만 IP 화이트리스트를 부착한다.
|
||||
* 라우트 파일에서 직접 부착하지 않고 코어 게이트(ExtensionMiddlewareGate)가
|
||||
* 요청 시점에 라우트 이름을 대조해 매칭될 때만 실행한다.
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function getMiddleware(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'class' => BizppurioWebhookIpWhitelist::class,
|
||||
'groups' => ['api'],
|
||||
'targets' => [
|
||||
'api.plugins.sirsoft-message_bizppurio.webhook',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ use Plugins\Sirsoft\MessageBizppurio\Controllers\Admin\AlimtalkTemplateControlle
|
||||
use Plugins\Sirsoft\MessageBizppurio\Controllers\Admin\DispatchResultController;
|
||||
use Plugins\Sirsoft\MessageBizppurio\Controllers\Admin\NotificationBindingController;
|
||||
use Plugins\Sirsoft\MessageBizppurio\Controllers\BizppurioWebhookController;
|
||||
use Plugins\Sirsoft\MessageBizppurio\Http\Middleware\BizppurioWebhookIpWhitelist;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -24,9 +23,9 @@ use Plugins\Sirsoft\MessageBizppurio\Http\Middleware\BizppurioWebhookIpWhitelist
|
||||
//
|
||||
// api 그룹에 appendToGroup 된 토큰/IDV 미들웨어를 라우트 레벨에서 제외(코어 무수정)하고,
|
||||
// 인증은 IP 화이트리스트로 대체한다(계획서 D13). 항상 200 응답(멱등).
|
||||
// IP 화이트리스트는 plugin.php::getMiddleware() 에서 이 라우트명으로 self-gate 선언한다.
|
||||
Route::post('/webhook', [BizppurioWebhookController::class, 'handle'])
|
||||
->withoutMiddleware([EnforceIdentityPolicy::class, RefreshTokenExpiration::class])
|
||||
->middleware(BizppurioWebhookIpWhitelist::class)
|
||||
->name('webhook');
|
||||
|
||||
Route::prefix('admin')->name('admin.')->middleware(['auth:sanctum', 'admin'])->group(function () {
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
|
||||
namespace Plugins\Sirsoft\MessageBizppurio\Tests;
|
||||
|
||||
use App\Enums\ExtensionStatus;
|
||||
use App\Extension\ExtensionMiddlewareRegistry;
|
||||
use App\Extension\HookListenerRegistrar;
|
||||
use App\Extension\HookManager;
|
||||
use App\Extension\PluginManager;
|
||||
use App\Models\Plugin;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@@ -54,6 +58,38 @@ abstract class PluginTestCase extends TestCase
|
||||
$this->isolatePluginStorage();
|
||||
$this->snapshotHookManager();
|
||||
$this->snapshotHookListenerRegistrar();
|
||||
$this->activateSelfForMiddlewareGate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 자기 플러그인을 미들웨어 게이트 인덱스에 활성 등록한다.
|
||||
*
|
||||
* getMiddleware() self-gate 선언(webhook IP 화이트리스트)은 코어 게이트가
|
||||
* 활성 플러그인 registry 를 대조해 실행하므로, 테스트 환경에서도 plugins 테이블
|
||||
* 활성 행 + PluginManager 인스턴스 등록 + 인덱스 무효화가 있어야 매칭된다.
|
||||
* 누락 시 게이트가 미들웨어를 부착하지 못해 차단 IP 도 통과한다.
|
||||
*/
|
||||
protected function activateSelfForMiddlewareGate(): void
|
||||
{
|
||||
Plugin::query()->updateOrCreate(
|
||||
['identifier' => 'sirsoft-message_bizppurio'],
|
||||
[
|
||||
'vendor' => 'sirsoft',
|
||||
'name' => json_encode(['ko' => 'sirsoft-message_bizppurio', 'en' => 'sirsoft-message_bizppurio']),
|
||||
'version' => '1.0.0',
|
||||
'status' => ExtensionStatus::Active->value,
|
||||
]
|
||||
);
|
||||
PluginManager::invalidatePluginStatusCache();
|
||||
|
||||
$pluginManager = $this->app->make(PluginManager::class);
|
||||
$property = new \ReflectionProperty($pluginManager, 'plugins');
|
||||
$property->setAccessible(true);
|
||||
$plugins = $property->getValue($pluginManager);
|
||||
$plugins['sirsoft-message_bizppurio'] = new \Plugins\Sirsoft\MessageBizppurio\Plugin;
|
||||
$property->setValue($pluginManager, $plugins);
|
||||
|
||||
ExtensionMiddlewareRegistry::flush();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
# audit:allow scenario-scale-axis-required reason: SendMessageJob 은 알림 1건당 1 dispatch(payload 1건·refkey 1건)로 볼륨 무관 상수 메모리다. 루프/chunk/대량 적재가 없어 #79 형 붕괴 구조가 아니므로 scale 축이 무의미하다.
|
||||
# audit:allow test-scenario-coverage reason: |
|
||||
# 발송 결과 기록·webhook 수신·결과코드 해석의 axes cross product 는 PHPUnit
|
||||
# (BizppurioWebhookTest·ResultCodeResolverTest·SmsChannelDriverTest·SendMessageJobTest·
|
||||
|
||||
Reference in New Issue
Block a user