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: -- -- -- -- 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