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 :: forall a (m :: * -> *). (FromJSON a, MonadIO m) => Value -> m a
verifySNSMessageJSON = forall (m :: * -> *) e a b.
(MonadIO m, Exception e) =>
(a -> e) -> Either a b -> m b
unTryIO forall a. a -> a
id forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< forall a (m :: * -> *).
(FromJSON a, MonadIO m) =>
Value -> m (Either SNSNotificationValidationError a)
verifySNSMessageJSONEither

verifySNSMessageJSONEither
  :: (FromJSON a, MonadIO m)
  => Value
  -> m (Either SNSNotificationValidationError a)
verifySNSMessageJSONEither :: forall a (m :: * -> *).
(FromJSON a, MonadIO m) =>
Value -> m (Either SNSNotificationValidationError a)
verifySNSMessageJSONEither Value
value =
  forall (m :: * -> *) a. Monad m => m (m a) -> m a
join
    forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse (forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first String -> SNSNotificationValidationError
BadJSONParse forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. FromJSON a => ByteString -> Either String a
eitherDecode forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
fromStrict forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
encodeUtf8)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *).
MonadIO m =>
Value -> m (Either SNSNotificationValidationError Text)
verifySNSMessageEither Value
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 :: forall (m :: * -> *). MonadIO m => Value -> m Text
verifySNSMessage = forall (m :: * -> *) e a b.
(MonadIO m, Exception e) =>
(a -> e) -> Either a b -> m b
unTryIO forall a. a -> a
id forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< forall (m :: * -> *).
MonadIO m =>
Value -> m (Either SNSNotificationValidationError Text)
verifySNSMessageEither

verifySNSMessageEither
  :: MonadIO m => Value -> m (Either SNSNotificationValidationError Text)
verifySNSMessageEither :: forall (m :: * -> *).
MonadIO m =>
Value -> m (Either SNSNotificationValidationError Text)
verifySNSMessageEither Value
value = forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT forall a b. (a -> b) -> a -> b
$ do
  SNSPayload
payload <- forall (m :: * -> *) e a. Monad m => Either e a -> ExceptT e m a
hoistEither forall a b. (a -> b) -> a -> b
$ Value -> Either SNSNotificationValidationError SNSPayload
parseSNSPayload Value
value
  ValidSNSMessage
verified <- forall (m :: * -> *) e a. Monad m => Either e a -> ExceptT e m a
hoistEither forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall (m :: * -> *).
MonadIO m =>
SNSPayload
-> m (Either SNSNotificationValidationError ValidSNSMessage)
validateSnsMessage SNSPayload
payload
  forall (m :: * -> *) e a. Monad m => Either e a -> ExceptT e m a
hoistEither forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall (m :: * -> *).
MonadIO m =>
ValidSNSMessage -> m (Either SNSNotificationValidationError Text)
handleSubscription ValidSNSMessage
verified

parseSNSPayload :: Value -> Either SNSNotificationValidationError SNSPayload
parseSNSPayload :: Value -> Either SNSNotificationValidationError SNSPayload
parseSNSPayload = forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first String -> SNSNotificationValidationError
BadJSONParse forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Result a -> Either String a
fromResult forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. FromJSON a => Value -> Result a
fromJSON

fromResult :: Result a -> Either String a
fromResult :: forall a. Result a -> Either String a
fromResult = \case
  Success a
a -> forall a b. b -> Either a b
Right a
a
  Error String
str -> forall a b. a -> Either a b
Left String
str