github-webhook-handler (empty) → 0.0.1
raw patch · 4 files changed
+149/−0 lines, 4 filesdep +aesondep +basedep +bytestringsetup-changed
Dependencies added: aeson, base, bytestring, cryptohash, github-types, text, transformers, vector
Files
- LICENSE +20/−0
- Setup.hs +2/−0
- github-webhook-handler.cabal +37/−0
- src/GitHub/WebHook/Handler.hs +90/−0
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2015 Tomas Carnecky++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ github-webhook-handler.cabal view
@@ -0,0 +1,37 @@+name: github-webhook-handler+version: 0.0.1++synopsis: GitHub WebHook Handler+description:+ Generic definition of a GitHub Webhook Handler. Specialized version for Snap+ can be found in the 'github-webhook-handler-snap' package.++license: MIT+license-file: LICENSE++author: Tomas Carnecky+maintainer: tomas.carnecky@gmail.com++category: GitHub++build-type: Simple+cabal-version: >=1.10+++library+ hs-source-dirs: src+ default-language: Haskell2010+ ghc-options: -Wall++ build-depends:+ base >=4.8 && <4.9+ , bytestring+ , github-types+ , transformers+ , aeson+ , text+ , vector+ , cryptohash++ exposed-modules:+ GitHub.WebHook.Handler
+ src/GitHub/WebHook/Handler.hs view
@@ -0,0 +1,90 @@+{-# LANGUAGE OverloadedStrings #-}++module GitHub.WebHook.Handler+ ( Handler(..)+ , Error(..)+ , runHandler+ ) where+++import Crypto.Hash++import Data.Aeson (eitherDecodeStrict')+import Data.Aeson.Types (parseEither)++import Data.Monoid++import Data.Text+import qualified Data.Text as T+import Data.Text.Encoding++import Data.ByteString+import qualified Data.ByteString.Char8 as BC8++import GitHub.Types++++data Handler m = Handler+ { hSecretKey :: Maybe String+ -- ^ Optional key which is used to authenticate the incoming request.++ , hBody :: m ByteString+ -- ^ Action which is used to read the request body.++ , hHeader :: ByteString -> m (Maybe ByteString)+ -- ^ Action which is used to retrieve a particular header from the+ -- request.++ , hAction :: Either Error Event -> m ()+ -- ^ Action which is executed once we've processed all the information+ -- in the request.+ }+++data Error+ = InvalidRequest+ | ParseError !Text+ | UnsignedRequest+ | InvalidSignature++toParseError :: String -> Either Error Event+toParseError = Left . ParseError . T.pack++runHandler :: Monad m => Handler m -> m ()+runHandler h = do+ res <- do+ rawBody <- hBody h+ mbSignature <- hHeader h "X-Hub-Signature"++ authenticatedBody <- return $ case (hSecretKey h, mbSignature) of++ -- No secret key and no signature. Pass along the body unverified.+ (Nothing, Nothing) -> Right rawBody++ -- Signature is available but no secret key to verify it. This is+ -- not a fatal error, we can still process the event.+ (Nothing, Just _) -> Right rawBody++ -- Secret token is available but the request is not signed. Reject+ -- the request.+ (Just _, Nothing) -> Left UnsignedRequest++ -- Both the signature and secret token are available. Verify the+ -- signature and reject the request if that fails.+ (Just sc, Just sig) -> do+ let mac = hmac (BC8.pack sc) rawBody :: HMAC SHA1+ if sig == ("sha1=" <> digestToHexByteString (hmacGetDigest mac))+ then Right rawBody+ else Left InvalidSignature++ mbEventName <- hHeader h "X-GitHub-Event"+ return $ do+ eventName <- maybe (Left InvalidRequest) Right mbEventName+ body <- authenticatedBody+ case eitherDecodeStrict' body of+ Left e -> toParseError e+ Right value -> either toParseError Right $+ parseEither (eventParser $ decodeUtf8 eventName) value++ hAction h res