ec2-signature 1.0 → 2.0
raw patch · 5 files changed
+165/−85 lines, 5 filesdep +QuickCheckdep +doctestdep +hspec
Dependencies added: QuickCheck, doctest, hspec
Files
- Network/HTTP/Rest/Signature/EC2.hs +1/−83
- Network/HTTP/Rest/Signature/EC2/Internal.hs +93/−0
- ec2-signature.cabal +27/−2
- test/Doctest.hs +9/−0
- test/Spec.hs +35/−0
Network/HTTP/Rest/Signature/EC2.hs view
@@ -16,86 +16,4 @@ , signature' ) where -import Data.ByteString (ByteString)-import qualified Data.ByteString as BS-import Data.ByteString.Char8 ()-import qualified Data.ByteString.Lazy as BL-import qualified Data.ByteString.Base64 as Base64-import qualified Data.Digest.Pure.SHA as SHA-import Data.List-import Data.Map (Map)-import qualified Data.Map as Map-import Data.Monoid-import qualified Network.HTTP.Types as HTTP---- | A query string for HTTP.------ > "param1=value1¶m2=value2"-data QueryString = QueryString { toString :: ByteString }- deriving (Eq)--type Method = ByteString-type Endpoint = ByteString-type Path = ByteString-type SecretKey = ByteString-data SignatureMethod = HmacSHA256---- | Convert a parameter list to 'QueryString'.------ ex:------ > queryString [("param1", "value1"), ("param2", "value2)] --> "param1=value1¶m2=value2"-queryString :: [(ByteString, ByteString)] -> QueryString-queryString = queryString' . sort---- | Convert a parameter map to 'QueryString'.-queryStringFromMap :: Map ByteString ByteString -> QueryString-queryStringFromMap = queryString' . Map.toList--queryString' :: [(ByteString, ByteString)] -> QueryString-queryString' = QueryString . BS.intercalate "&" . map concatWithEqual- where- concatWithEqual (key, val) = mconcat- [ key- , "="- , HTTP.urlEncode True val- ]---- | Make a string for making signature.------ ex:------ > GET--- > ec2.amazonaws.com--- > /--- > Action=DescribeInstances&AWSAccessKeyId=xxx&...-stringToSign :: Method -> Endpoint -> Path -> [(ByteString, ByteString)] -> ByteString-stringToSign method end path = stringToSign' method end path . queryString--stringToSign' :: Method -> Endpoint -> Path -> QueryString -> ByteString-stringToSign' method end path params = BS.intercalate "\n"- [ method- , end- , path- , toString params- ]---- | Make signature from a parameter list.-signature :: Method -> Endpoint -> Path -> SecretKey -> SignatureMethod -> [(ByteString, ByteString)] -> ByteString-signature method end path secret sigm- = signature' method end path secret sigm . queryString---- | Make signature from a 'QueryString'.-signature' :: Method -> Endpoint -> Path -> SecretKey -> SignatureMethod -> QueryString -> ByteString-signature' method end path secret sigm params- = HTTP.urlEncode True- $ Base64.encode- $ signedString sigm- where- strToSign = stringToSign' method end path params- signedString HmacSHA256- = mconcat- $ BL.toChunks- $ SHA.bytestringDigest- $ SHA.hmacSha256 (toL secret) (toL strToSign)- toL = BL.fromChunks . (:[])+import Network.HTTP.Rest.Signature.EC2.Internal
+ Network/HTTP/Rest/Signature/EC2/Internal.hs view
@@ -0,0 +1,93 @@+module Network.HTTP.Rest.Signature.EC2.Internal where++import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import Data.ByteString.Char8 ()+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Base64 as Base64+import qualified Data.Digest.Pure.SHA as SHA+import Data.List+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Monoid+import qualified Network.HTTP.Types as HTTP++-- | A query string for HTTP.+--+-- > "param1=value1¶m2=value2"+data QueryString =+ QueryString { rawData :: [(ByteString, ByteString)] }+ deriving (Eq)++instance Monoid QueryString where+ mempty = QueryString []+ mappend a b = QueryString $ rawData a `merge` rawData b++merge :: Ord a => [a] -> [a] -> [a]+merge [] bs = bs+merge as [] = as+merge (a:as) (b:bs)+ | a <= b = a:merge as (b:bs)+ | otherwise = b:merge (a:as) bs++type Method = ByteString+type Endpoint = ByteString+type Path = ByteString+type SecretKey = ByteString+data SignatureMethod = HmacSHA256++-- | Convert a parameter list to 'QueryString'.+--+-- >>> toString $ queryString [("param1", "value1"), ("param2", "value2")]+-- "param1=value1¶m2=value2"+queryString :: [(ByteString, ByteString)] -> QueryString+queryString = QueryString . sort++-- | Convert a parameter map to 'QueryString'.+queryStringFromMap :: Map ByteString ByteString -> QueryString+queryStringFromMap = queryString . Map.toList++toString :: QueryString -> ByteString+toString = BS.intercalate "&" . map concatWithEqual . rawData+ where+ concatWithEqual ("", _) = error "name is null."+ concatWithEqual (key, val) = mconcat+ [ HTTP.urlEncode True key+ , "="+ , HTTP.urlEncode True val+ ]++-- | Make a string for making signature.+--+-- >>> stringToSign "GET" "ec2.amazonaws.com" "/" [("key1", "value1"), ("key2", "value2")]+-- "GET\nec2.amazonaws.com\n/\nkey1=value1&key2=value2"+stringToSign :: Method -> Endpoint -> Path -> [(ByteString, ByteString)] -> ByteString+stringToSign method end path = stringToSign' method end path . queryString++stringToSign' :: Method -> Endpoint -> Path -> QueryString -> ByteString+stringToSign' method end path params = BS.intercalate "\n"+ [ method+ , end+ , path+ , toString params+ ]++-- | Make signature from a parameter list.+signature :: Method -> Endpoint -> Path -> SecretKey -> SignatureMethod -> [(ByteString, ByteString)] -> ByteString+signature method end path secret sigm+ = signature' method end path secret sigm . queryString++-- | Make signature from a 'QueryString'.+signature' :: Method -> Endpoint -> Path -> SecretKey -> SignatureMethod -> QueryString -> ByteString+signature' method end path secret sigm params+ = HTTP.urlEncode True+ $ Base64.encode+ $ signedString sigm+ where+ strToSign = stringToSign' method end path params+ signedString HmacSHA256+ = mconcat+ $ BL.toChunks+ $ SHA.bytestringDigest+ $ SHA.hmacSha256 (toL secret) (toL strToSign)+ toL = BL.fromChunks . (:[])
ec2-signature.cabal view
@@ -1,5 +1,5 @@ name: ec2-signature-version: 1.0+version: 2.0 synopsis: The Amazon EC2 style signature calculator. description: The Amazon EC2 style signature calculator. <http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html>@@ -15,6 +15,7 @@ library exposed-modules: Network.HTTP.Rest.Signature.EC2+ other-modules: Network.HTTP.Rest.Signature.EC2.Internal extensions: OverloadedStrings ghc-options: -Wall -fno-warn-unused-do-bind build-depends: base == 4.*@@ -24,6 +25,30 @@ , SHA , http-types +test-suite doctest+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Doctest.hs+ ghc-options: -Wall+ build-depends: base == 4.*+ , doctest++test-suite spec+ type: exitcode-stdio-1.0+ hs-source-dirs: ., test+ main-is: Spec.hs+ ghc-options: -Wall+ extensions: OverloadedStrings+ build-depends: base == 4.*+ , bytestring+ , base64-bytestring+ , containers+ , SHA+ , http-types+ , hspec+ , QuickCheck+ , bytestring+ source-repository head type: git- location: git://github.com/yunomu/ec2-signature.git+ location: git://github.com/worksap-ate/ec2-signature.git
+ test/Doctest.hs view
@@ -0,0 +1,9 @@+module Main where++import Test.DocTest++main :: IO ()+main = doctest+ [ "-XOverloadedStrings"+ , "Network/HTTP/Rest/Signature/EC2.hs"+ ]
+ test/Spec.hs view
@@ -0,0 +1,35 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}++import Control.Applicative+import Data.ByteString (ByteString)+import qualified Data.ByteString.Char8 as BC+import Data.Monoid+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck++import Network.HTTP.Rest.Signature.EC2.Internal++instance Arbitrary ByteString where+ arbitrary = BC.pack <$> arbitrary++data KeyString = Key { unpack :: ByteString }++instance Arbitrary KeyString where+ arbitrary = Key . BC.pack <$> ((:) <$> arbitrary <*> arbitrary)++instance Arbitrary QueryString where+ arbitrary = queryString . map f <$> arbitrary+ where+ f (a, b) = (unpack a, b)++instance Show QueryString where+ show = show . rawData++main :: IO ()+main = hspec $ do+ describe "QueryString" $ do+ prop "mappend" prop_mappend++prop_mappend :: QueryString -> QueryString -> Bool+prop_mappend a b = (a <> b) == queryString (rawData a ++ rawData b)