refactor: 각 어댑터터별 리펙토링 진행
This commit is contained in:
@@ -26,8 +26,12 @@ dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.Notification
|
||||
dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.NotificationPlatformMode
|
||||
dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.NotificationPlatformSettings
|
||||
dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.NotificationProviderAssembly
|
||||
dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.NotificationSecretRequirements
|
||||
dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.NotificationSmtpProviderConfig
|
||||
dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.NotificationSmtpSettings
|
||||
dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.ProviderRuntimeAssembler
|
||||
dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.ProviderType
|
||||
dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.SmtpProviderRuntimeAssembler
|
||||
dev.caskeleton.adapter.outbound.notification.platform.dispatch.AttemptPermit
|
||||
dev.caskeleton.adapter.outbound.notification.platform.dispatch.CapabilityReconciliationGateway
|
||||
dev.caskeleton.adapter.outbound.notification.platform.dispatch.ConfiguredProfileCatalog
|
||||
@@ -56,6 +60,7 @@ dev.caskeleton.adapter.outbound.notification.platform.observation.LoggingNotific
|
||||
dev.caskeleton.adapter.outbound.notification.platform.observation.NotificationHealthReporter
|
||||
dev.caskeleton.adapter.outbound.notification.platform.observation.NotificationHealthSnapshot
|
||||
dev.caskeleton.adapter.outbound.notification.platform.observation.NotificationServingThresholds
|
||||
dev.caskeleton.adapter.outbound.notification.platform.provider.EmailAttachments
|
||||
dev.caskeleton.adapter.outbound.notification.platform.provider.ProviderResults
|
||||
dev.caskeleton.adapter.outbound.notification.platform.provider.UnconfiguredAttachmentResolver
|
||||
dev.caskeleton.adapter.outbound.notification.platform.provider.apns.ApnsFailureClassifier
|
||||
@@ -89,6 +94,7 @@ dev.caskeleton.adapter.outbound.notification.platform.provider.ses.SesRequestMap
|
||||
dev.caskeleton.adapter.outbound.notification.platform.provider.ses.SesSuppressionUpdater
|
||||
dev.caskeleton.adapter.outbound.notification.platform.provider.ses.SnsCertificateProvider
|
||||
dev.caskeleton.adapter.outbound.notification.platform.provider.ses.SnsSignatureVerifier
|
||||
dev.caskeleton.adapter.outbound.notification.platform.provider.smtp.JavaMailSenderSmtpDispatch
|
||||
dev.caskeleton.adapter.outbound.notification.platform.provider.smtp.SmtpDispatch
|
||||
dev.caskeleton.adapter.outbound.notification.platform.provider.smtp.SmtpDispatchException
|
||||
dev.caskeleton.adapter.outbound.notification.platform.provider.smtp.SmtpFailureClassifier
|
||||
@@ -122,6 +128,7 @@ dev.caskeleton.adapter.outbound.notification.platform.reactor.ReactorContextBrid
|
||||
dev.caskeleton.adapter.outbound.notification.platform.reactor.ReactorNotificationOrchestrator
|
||||
dev.caskeleton.adapter.outbound.notification.platform.security.AesGcmCallbackPayloadProtection
|
||||
dev.caskeleton.adapter.outbound.notification.platform.security.AesGcmContactPointProtector
|
||||
dev.caskeleton.adapter.outbound.notification.platform.security.AesGcmNotificationPayloadProtection
|
||||
dev.caskeleton.adapter.outbound.notification.platform.security.CredentialGeneration
|
||||
dev.caskeleton.adapter.outbound.notification.platform.security.HmacProviderRequestIdHasher
|
||||
dev.caskeleton.adapter.outbound.notification.platform.security.ProviderCredentialManager
|
||||
@@ -137,6 +144,7 @@ dev.caskeleton.adapter.outbound.notification.platform.template.NotificationTempl
|
||||
dev.caskeleton.adapter.outbound.notification.platform.template.PlaceholderTemplateEngine
|
||||
dev.caskeleton.adapter.outbound.notification.platform.template.Sha256MessageDigestAdapter
|
||||
dev.caskeleton.adapter.outbound.notification.platform.template.TemplateSlotMode
|
||||
dev.caskeleton.adapter.outbound.notification.platform.template.TemplateSlotPolicy
|
||||
dev.caskeleton.adapter.outbound.notification.platform.template.ThymeleafNotificationRenderer
|
||||
dev.caskeleton.adapter.outbound.notification.platform.template.ThymeleafStringTemplateEngine
|
||||
dev.caskeleton.adapter.outbound.notification.provider.AttemptCorrelationId
|
||||
@@ -384,6 +392,7 @@ dev.caskeleton.application.notification.platform.callback.ProviderEventProjector
|
||||
dev.caskeleton.application.notification.platform.callback.ProviderEventRecord
|
||||
dev.caskeleton.application.notification.platform.callback.ProviderEventRecordId
|
||||
dev.caskeleton.application.notification.platform.callback.ProviderEventSource
|
||||
dev.caskeleton.application.notification.platform.callback.ProviderRequestIdHash
|
||||
dev.caskeleton.application.notification.platform.callback.StandardDeliveryProjector
|
||||
dev.caskeleton.application.notification.platform.callback.SuppressionFacts
|
||||
dev.caskeleton.application.notification.platform.callback.VerifiedCallback
|
||||
@@ -400,6 +409,7 @@ dev.caskeleton.application.notification.platform.contact.LegacyFcmRegistrationTo
|
||||
dev.caskeleton.application.notification.platform.contact.MobilePushTarget
|
||||
dev.caskeleton.application.notification.platform.contact.PhoneNumber
|
||||
dev.caskeleton.application.notification.platform.contact.WebPushSubscriptionValue
|
||||
dev.caskeleton.application.notification.platform.dispatch.AcceptNotificationApplicationUseCase
|
||||
dev.caskeleton.application.notification.platform.dispatch.ApplicationReceiptServiceImpl
|
||||
dev.caskeleton.application.notification.platform.dispatch.AttachmentIntegrityGuard
|
||||
dev.caskeleton.application.notification.platform.dispatch.CancelNotificationApplicationUseCase
|
||||
@@ -430,6 +440,7 @@ dev.caskeleton.application.notification.platform.dispatch.PolicyRoutePlanner
|
||||
dev.caskeleton.application.notification.platform.dispatch.ProviderDispatchGatewayPort
|
||||
dev.caskeleton.application.notification.platform.dispatch.ProviderProfileCatalogPort
|
||||
dev.caskeleton.application.notification.platform.dispatch.ProviderRequestIdHasherPort
|
||||
dev.caskeleton.application.notification.platform.dispatch.PublishNotificationTemplateApplicationUseCase
|
||||
dev.caskeleton.application.notification.platform.dispatch.RecipientDeliveryRecord
|
||||
dev.caskeleton.application.notification.platform.dispatch.RecipientDeliveryStorePort
|
||||
dev.caskeleton.application.notification.platform.dispatch.RecipientLease
|
||||
@@ -499,12 +510,16 @@ dev.caskeleton.application.notification.platform.policy.SuppressionReason
|
||||
dev.caskeleton.application.notification.platform.policy.SuppressionScope
|
||||
dev.caskeleton.application.notification.platform.policy.SuppressionSource
|
||||
dev.caskeleton.application.notification.platform.policy.SuppressionStorePort
|
||||
dev.caskeleton.application.notification.platform.port.in.AcceptNotificationCommand
|
||||
dev.caskeleton.application.notification.platform.port.in.AcceptNotificationUseCase
|
||||
dev.caskeleton.application.notification.platform.port.in.CancelNotificationCommand
|
||||
dev.caskeleton.application.notification.platform.port.in.CancelNotificationUseCase
|
||||
dev.caskeleton.application.notification.platform.port.in.GetNotificationQuery
|
||||
dev.caskeleton.application.notification.platform.port.in.GetNotificationUseCase
|
||||
dev.caskeleton.application.notification.platform.port.in.IngestProviderCallbackCommand
|
||||
dev.caskeleton.application.notification.platform.port.in.IngestProviderCallbackUseCase
|
||||
dev.caskeleton.application.notification.platform.port.in.PublishNotificationTemplateCommand
|
||||
dev.caskeleton.application.notification.platform.port.in.PublishNotificationTemplateUseCase
|
||||
dev.caskeleton.application.notification.platform.port.in.ScheduleNotificationCommand
|
||||
dev.caskeleton.application.notification.platform.port.in.ScheduleNotificationUseCase
|
||||
dev.caskeleton.application.notification.platform.port.in.SubmitNotificationCommand
|
||||
@@ -539,6 +554,8 @@ dev.caskeleton.application.notification.platform.push.ReceiptKind
|
||||
dev.caskeleton.application.notification.platform.push.ReceiptResult
|
||||
dev.caskeleton.application.notification.platform.security.AccessContext
|
||||
dev.caskeleton.application.notification.platform.security.ContactPointProtector
|
||||
dev.caskeleton.application.notification.platform.security.NotificationPayloadProtection
|
||||
dev.caskeleton.application.notification.platform.security.NotificationPayloadUnreadableException
|
||||
dev.caskeleton.application.notification.platform.security.NotificationRedactor
|
||||
dev.caskeleton.application.notification.platform.security.ProtectedContactPoint
|
||||
dev.caskeleton.application.notification.platform.security.SafeDiagnosticContext
|
||||
|
||||
@@ -41,6 +41,12 @@ still waiting on gets claimed by a second worker, and the recipient receives the
|
||||
| `callbacks.enabled` | `APP_NOTIFICATION_PLATFORM_CALLBACKS_ENABLED` | `false` | boolean |
|
||||
| `callbacks.max-body-bytes` | `APP_NOTIFICATION_PLATFORM_CALLBACK_MAX_BODY_BYTES` | `65508` | 1..65508 |
|
||||
| `callbacks.replay-skew` | `APP_NOTIFICATION_PLATFORM_CALLBACK_REPLAY_SKEW` | `5m` | positive |
|
||||
| `callbacks.trusted-proxies` | `APP_NOTIFICATION_PLATFORM_CALLBACK_TRUSTED_PROXIES` | *(empty)* | CSV of peer addresses |
|
||||
|
||||
여러 provider가 요청 URL에 서명하므로, 그 URL을 잘못 재구성하면 정상 webhook이 전부 서명 실패가 된다.
|
||||
`trusted-proxies`가 비어 있으면 forwarded 헤더를 **믿지 않고** 컨테이너가 관측한 값을 쓴다. 무조건 믿으면
|
||||
아무 호출자나 자기 서명이 검증될 URL을 고를 수 있어 서명 자체가 무의미해진다. 로드밸런서 뒤에 있는 배포는
|
||||
그 peer를 명시한다.
|
||||
|
||||
65508 is not a round number by accident: it is the ciphertext column's 65536 bytes minus the AES-GCM
|
||||
nonce and tag. A larger configured value would pass every check above the database and fail the
|
||||
@@ -69,6 +75,43 @@ them, because the keys are deployment-chosen; supply them as YAML or as
|
||||
A profile pins provider type, environment, credential profile, timeouts, concurrency and rate limit.
|
||||
Sender identity and credential profile are separate concerns.
|
||||
|
||||
## SMTP relay
|
||||
|
||||
The one provider profile the template ships, off. A deployment that wants the common case sets
|
||||
`APP_NOTIFICATION_PLATFORM_SMTP_ENABLED=true` and the relay address; one that wants a different
|
||||
profile id or a second family declares it in its own YAML instead.
|
||||
|
||||
The profile and the relay are separate tables below because they answer different questions. The
|
||||
profile says *which* provider serves EMAIL and under what limits; the relay says *what the transport
|
||||
is*. Host, port and credentials are not here at all — they stay `spring.mail.*`, because Spring
|
||||
already owns them and a second spelling would be a second thing to keep in step.
|
||||
|
||||
| Property | Environment variable | Default | Bound |
|
||||
|---|---|---|---|
|
||||
| `providers.smtp.enabled` | `APP_NOTIFICATION_PLATFORM_SMTP_ENABLED` | `false` | boolean |
|
||||
| `providers.smtp.primary-for-channel` | `APP_NOTIFICATION_PLATFORM_SMTP_PRIMARY` | `true` | boolean; exactly one primary per channel |
|
||||
| `providers.smtp.environment` | `APP_NOTIFICATION_PLATFORM_SMTP_ENVIRONMENT` | `local` | required when enabled |
|
||||
| `providers.smtp.credential-profile` | `APP_NOTIFICATION_PLATFORM_SMTP_CREDENTIAL_PROFILE` | `default` | resolved through `SecretMaterialProvider`, never inline material |
|
||||
| `providers.smtp.timeout` | `APP_NOTIFICATION_PLATFORM_SMTP_TIMEOUT` | `10s` | positive, finite |
|
||||
| `providers.smtp.max-concurrency` | `APP_NOTIFICATION_PLATFORM_SMTP_MAX_CONCURRENCY` | `4` | positive |
|
||||
| `providers.smtp.rate-per-second` | `APP_NOTIFICATION_PLATFORM_SMTP_RATE_PER_SECOND` | `10` | positive |
|
||||
|
||||
| Property | Environment variable | Default | Bound |
|
||||
|---|---|---|---|
|
||||
| `smtp.tls-mode` | `APP_NOTIFICATION_PLATFORM_SMTP_TLS_MODE` | `STARTTLS_REQUIRED` | `STARTTLS_REQUIRED` or `IMPLICIT_TLS` |
|
||||
| `smtp.sender-identity` | `APP_NOTIFICATION_PLATFORM_SMTP_SENDER_IDENTITY` | `no-reply@example.invalid` | address |
|
||||
| `smtp.connect-timeout` | `APP_NOTIFICATION_PLATFORM_SMTP_CONNECT_TIMEOUT` | `5s` | positive, finite |
|
||||
| `smtp.read-timeout` | `APP_NOTIFICATION_PLATFORM_SMTP_READ_TIMEOUT` | `10s` | positive, finite |
|
||||
| `smtp.write-timeout` | `APP_NOTIFICATION_PLATFORM_SMTP_WRITE_TIMEOUT` | `10s` | positive, finite |
|
||||
| `smtp.max-concurrency` | `APP_NOTIFICATION_PLATFORM_SMTP_DISPATCH_CONCURRENCY` | `4` | positive |
|
||||
|
||||
The TLS mode enum has no plaintext member. An unencrypted relay is refused by construction rather
|
||||
than by a validator somebody has to remember to run.
|
||||
|
||||
The default sender is an RFC 2606 reserved domain that resolves nowhere, so a deployment that forgot
|
||||
to set one produces a traceable bounce instead of mail apparently sent from an address it does not
|
||||
own.
|
||||
|
||||
## Startup failures
|
||||
|
||||
Startup fails rather than degrading when:
|
||||
@@ -91,6 +134,33 @@ All key material arrives through `SecretMaterialProvider`. Nothing is read from
|
||||
committed file, or from a plaintext log. Contact point encryption and lookup HMAC keys must be
|
||||
distinct, and the encryption key must be exactly 256 bits.
|
||||
|
||||
Eight purposes, eight keys. Each is base64 of at least 32 bytes and each must differ from every
|
||||
other; the platform decodes them at startup and refuses to boot if one is blank, short or shared. A
|
||||
blank value used to be skipped, which meant the platform started without the key and found out on
|
||||
the first contact point — in production, on a recipient's notification.
|
||||
|
||||
Every default below is **unset**, deliberately. Supply the values out of band, per environment. Do
|
||||
not write one into this table, into `application.yml`, into an `.env` file that is tracked, or into
|
||||
any example: a value that appears in the repository is a value that has been disclosed.
|
||||
|
||||
| Purpose | Key material | Active key id |
|
||||
|---|---|---|
|
||||
| Contact point encryption | `APP_NOTIFICATION_PLATFORM_CONTACT_ENCRYPTION_KEY` | `APP_NOTIFICATION_PLATFORM_CONTACT_ENCRYPTION_KEY_ID` |
|
||||
| Contact point lookup HMAC | `APP_NOTIFICATION_PLATFORM_CONTACT_LOOKUP_HMAC_KEY` | `APP_NOTIFICATION_PLATFORM_CONTACT_LOOKUP_HMAC_KEY_ID` |
|
||||
| Callback signing | `APP_NOTIFICATION_PLATFORM_CALLBACK_SIGNING_KEY` | `APP_NOTIFICATION_PLATFORM_CALLBACK_SIGNING_KEY_ID` |
|
||||
| Callback fingerprint HMAC | `APP_NOTIFICATION_PLATFORM_CALLBACK_FINGERPRINT_HMAC_KEY` | `APP_NOTIFICATION_PLATFORM_CALLBACK_FINGERPRINT_HMAC_KEY_ID` |
|
||||
| Provider credential encryption | `APP_NOTIFICATION_PLATFORM_PROVIDER_CREDENTIAL_KEY` | `APP_NOTIFICATION_PLATFORM_PROVIDER_CREDENTIAL_KEY_ID` |
|
||||
| Provider request lookup HMAC | `APP_NOTIFICATION_PLATFORM_PROVIDER_REQUEST_LOOKUP_HMAC_KEY` | `APP_NOTIFICATION_PLATFORM_PROVIDER_REQUEST_LOOKUP_HMAC_KEY_ID` |
|
||||
| Payload encryption | `APP_NOTIFICATION_PLATFORM_PAYLOAD_ENCRYPTION_KEY` | `APP_NOTIFICATION_PLATFORM_PAYLOAD_ENCRYPTION_KEY_ID` |
|
||||
| Web Push VAPID signing | `APP_NOTIFICATION_PLATFORM_VAPID_SIGNING_KEY` | `APP_NOTIFICATION_PLATFORM_VAPID_SIGNING_KEY_ID` |
|
||||
|
||||
A key id is not secret — an id identifies key material without revealing it — but it is required,
|
||||
and it has no default on purpose. A constant id makes a rotation indistinguishable from the key it
|
||||
replaced, so nothing could decrypt what was written before it. Change the id in the same deployment
|
||||
that changes the material, and keep the superseded key readable under its old id until the data it
|
||||
wrote has been re-encrypted. The rotation sequence is in
|
||||
[at-rest-threat-model.md](at-rest-threat-model.md).
|
||||
|
||||
## Readiness
|
||||
|
||||
The platform contributes a `notifications` actuator endpoint and a health indicator. It reports DOWN
|
||||
|
||||
Reference in New Issue
Block a user