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

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

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

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

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