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