aws-sns-verify-0.0.0.2: library/Amazon/SNS/Verify.hs
module Amazon.SNS.Verify
( verifySNSMessage
, verifySNSMessageEither
, verifySNSMessageJSON
, verifySNSMessageJSONEither
, SNSNotificationValidationError(..)
) where
import Amazon.SNS.Verify.Prelude
import Amazon.SNS.Verify.Payload
import Amazon.SNS.Verify.Validate
import Control.Error (hoistEither, runExceptT)
import Data.Aeson (FromJSON, Value, eitherDecode)
import Data.Aeson.Types (Result(Error, Success), fromJSON)
import Data.Bifunctor (first)
import Data.ByteString.Lazy (fromStrict)
import Data.Text.Encoding (encodeUtf8)
-- | Decode and verify an SNS message as JSON
--
-- The same as 'verifySNSMessage', but decodes the message as `JSON`.
--
verifySNSMessageJSON :: (FromJSON a, MonadIO m) => Value -> m a
verifySNSMessageJSON = unTryIO id <=< verifySNSMessageJSONEither
verifySNSMessageJSONEither
:: (FromJSON a, MonadIO m)
=> Value
-> m (Either SNSNotificationValidationError a)
verifySNSMessageJSONEither value =
join
. traverse (first BadJSONParse . eitherDecode . fromStrict . encodeUtf8)
<$> verifySNSMessageEither value
-- | Decode and verify an SNS message
--
-- This function follows the process outlined in the following:
--
-- <https://docs.aws.amazon.com/sns/latest/dg/sns-verify-signature-of-message.html>
--
-- A `JSON` payload is:
--
-- 1. Parsed as an SNS message type 'Notification', `SubscriptionConfirmation`,
-- or `UnsubscribeConfirmation`.
-- 2. Verified against its signature.
-- 3. And in the case of subscription events responded to.
--
verifySNSMessage :: MonadIO m => Value -> m Text
verifySNSMessage = unTryIO id <=< verifySNSMessageEither
verifySNSMessageEither
:: MonadIO m => Value -> m (Either SNSNotificationValidationError Text)
verifySNSMessageEither value = runExceptT $ do
payload <- hoistEither $ parseSNSPayload value
verified <- hoistEither =<< validateSnsMessage payload
hoistEither =<< handleSubscription verified
parseSNSPayload :: Value -> Either SNSNotificationValidationError SNSPayload
parseSNSPayload = first BadJSONParse . fromResult . fromJSON
fromResult :: Result a -> Either String a
fromResult = \case
Success a -> Right a
Error str -> Left str