packages feed

yesod-auth-basic (empty) → 0.1.0.0

raw patch · 7 files changed

+318/−0 lines, 7 filesdep +basedep +base64-bytestringdep +bytestringsetup-changed

Dependencies added: base, base64-bytestring, bytestring, exceptions, hlint, hspec, text, wai, word8, yesod, yesod-auth-basic, yesod-test

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015, Christopher Reichert++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Christopher Reichert nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ Yesod/Auth/Http/Basic.hs view
@@ -0,0 +1,164 @@+-- | A Yesod middleware for <<http://tools.ietf.org/html/rfc1945#section-11.1 HTTP Basic Authentication>>+--+-- This middleware performs a single authentication lookup+-- per request and uses the Yesod request-local caching+-- mechanisms to store valid auth credentials found in the+-- Authorization header.+--+--+-- The recommended way to use this module is to override the+-- @maybeAuthId@ to @defaultMaybeBasicAuthId@ and supply a+-- lookup function.+--+-- @+-- instance YesodAuth App where+--     type AuthId App = Text+--     getAuthId = return . Just . credsIdent+--     maybeAuthId = defaultMaybeBasicAuthId checkCreds+--       where+--         checkCreds = \k s -> return $ (k == "user")+--                                    && (s == "secret")+-- @+--+--+-- WWW-Authenticate challenges are currently not implemented.+-- The current workaround is to override the error handler:+--+-- @+-- instance Yesod App where+--   errorHandler NotAuthenticated = selectRep $+--       provideRep $ do+--         addHeader "WWW-Authenticate" $ T.concat+--               [ "RedirectJSON realm=\"Realm\", param=\"myurl.com\"" ]+--         -- send error response here+--         ...+--   errorHandler e = defaultErrorHandler e+--   ...+-- @+--+--+-- Proper response status on failed authentication is not implemented.+-- The current workaround is to override the 'Yesod' typeclass+-- @isAuthorized@ function to handle required auth routes. e.g.+--+-- @+-- instance Yesod App where+--   isAuthorized SecureR _   =+--     maybeAuthId >>= return . maybe AuthenticationRequired (const Authorized)+--   isAuthorized _ _         = Authorized+-- @+--++{-# LANGUAGE DeriveDataTypeable    #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE RecordWildCards       #-}++module Yesod.Auth.Http.Basic+       (+         -- * Drop in replace for maybeAuthId.+         defaultMaybeBasicAuthId++       -- The AuthSettings will not be exported until+       -- features are implemented which actually uses+       -- them+       --+       -- , AuthSettings+       -- , authRealm+       -- , defaultAuthSettings+       ) where++import           Control.Applicative+import           Control.Monad.Catch    (MonadThrow)+import           Data.ByteString        (ByteString)+import qualified Data.ByteString        as BS+import           Data.ByteString.Base64 (decodeLenient)+import           Data.Text              (Text)+import qualified Data.Text.Encoding     as T+import           Data.Typeable+import           Data.Word8             (isSpace, toLower, _colon)+import           Network.Wai+import           Yesod                  hiding (Header)+++-- | Authentication Settings+data AuthSettings = AuthSettings+    {+      authRealm :: Text+    }++-- | ready-to-go 'AuthSettings' which can be used+defaultAuthSettings :: AuthSettings+defaultAuthSettings = AuthSettings { authRealm = "Realm" }+++-- | Cachable basic authentication credentials+newtype CachedBasicAuthId a+  = CachedBasicAuthId { unCached :: Maybe a }+    deriving Typeable+++-- | A function to verify user credentials+type CheckCreds = ByteString+                  -> ByteString+                  -> IO Bool+++-- | Retrieve the 'AuthId' using Authorization header.+--+-- If valid credentials are found and authorized the+-- auth id is cached.+--+-- TODO use more general type than Text to represent+-- the auth id+defaultMaybeBasicAuthId+  :: (MonadIO m, MonadThrow m, MonadBaseControl IO m)+     => CheckCreds+     -> AuthSettings+     -> HandlerT site m (Maybe Text)+defaultMaybeBasicAuthId auth cfg =+    cachedAuth $ waiRequest >>= maybeBasicAuthId auth cfg+++-- | Cached Authentication credentials+cachedAuth+  :: (MonadIO m, MonadThrow m, MonadBaseControl IO m)+     => HandlerT site m (Maybe Text)+     -> HandlerT site m (Maybe Text)+cachedAuth = fmap unCached . cached . fmap CachedBasicAuthId+++-- | Use the HTTP Basic _Authorization_ header to retrieve+-- the AuthId of request+--+-- This function uses yesod 'cachedAuth' to cache the result of+-- the first succesful header lookup.+--+-- Subsequent calls to 'maybeAuthId' do not require the 'CheckCreds'+-- function to be run again.+maybeBasicAuthId+  :: MonadIO m+     => CheckCreds+     -> AuthSettings+     -> Request+     -> m (Maybe Text)+maybeBasicAuthId checkCreds AuthSettings{..} req =+    case authorization of+      Just (strategy, userpass)+        | BS.map toLower strategy == "basic" ->+              authorizeCredentials $ BS.dropWhile isSpace userpass+        | otherwise -> return Nothing+      _ -> return Nothing+  where+    authorization = BS.break isSpace+                    <$> lookup "Authorization" (requestHeaders req)+    authorizeCredentials encoded =+      let (username, password') = BS.breakByte _colon $ decodeLenient encoded+      in case BS.uncons password' of+          Nothing -> return Nothing+          Just (_,password) -> do+            authorized <- liftIO $ checkCreds username password+            return $ if authorized+                       then Just $ T.decodeUtf8 username+                       else Nothing
+ test/HLint.hs view
@@ -0,0 +1,19 @@++module Main (main) where++import           Language.Haskell.HLint (hlint)+import           System.Exit            (exitFailure, exitSuccess)+++main :: IO ()+main = do+    hints <- hlint arguments+    print hints+    if null hints+       then exitSuccess+       else exitFailure+  where+    arguments = [+                  "Yesod"+                , "test"+                ]
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ test/YesodAuthHttpBasicSpec.hs view
@@ -0,0 +1,41 @@++{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# OPTIONS_GHC -fno-warn-orphans  #-}++module YesodAuthHttpBasicSpec where++import           Data.Monoid ((<>))+import           Test.Hspec+import           Yesod       hiding (get)+import           Yesod.Test+++-- TODO create real tests++spec :: SpecWith ()+spec = describe "Yesod HTTP Basic Authentication" $+  yesodSpec app $+    ydescribe "Yesod HTTP Basic Authentication" $+      ydescribe "tests1" $ do+        yit "Has Basic Auth" $ do+           -- addRequestHeader (hUserAgent, "Chrome/41.0.2228.0")+           get $ LiteAppRoute []+           statusIs 200+        yit "Denies Failed Basic Auth" $ do+          get $ LiteAppRoute []+          statusIs 200+          -- statusIs 403+++instance RenderMessage LiteApp FormMessage where+  renderMessage _ _ = defaultFormMessage+++app :: LiteApp+app = liteApp $+  dispatchTo $ do+    mfoo <- lookupGetParam "foo"+    case mfoo of+     Nothing -> return "Hello world!"+     Just foo -> return $ "foo=" <> foo
+ yesod-auth-basic.cabal view
@@ -0,0 +1,61 @@++name:                yesod-auth-basic+version:             0.1.0.0+license:             BSD3+license-file:        LICENSE+author:              Christopher Reichert+maintainer:          creichert07@gmail.com+copyright:           (c) 2015, Christopher Reichert+category:            Web+build-type:          Simple+cabal-version:       >=1.10+synopsis:            Yesod Middleware for HTTP Basic Authentication+description:+ An efficient Yesod middleware middleware for HTTP Basic+ Authentication.+ .+ Utilizes Yesod request-local caching mechanisms to store valid auth+ credentials found in the Authorization header.+++source-repository head+  type:     git+  location: git://github.com/creichert/yesod-auth-basic.git+++library+  exposed-modules:     Yesod.Auth.Http.Basic+  ghc-options:         -Wall+  default-language:    Haskell2010+  build-depends:       base              == 4.*+                     , base64-bytestring+                     , bytestring+                     , exceptions+                     , text+                     , yesod+                     , wai+                     , word8+++test-suite spec+  type:                exitcode-stdio-1.0+  main-is:             Spec.hs+  hs-source-dirs:      test/+  other-modules:       YesodAuthHttpBasicSpec+  ghc-options:         -Wall -Werror -threaded+  default-language:    Haskell2010+  build-depends:       base                 == 4.*+                     , yesod+                     , yesod-auth-basic+                     , yesod-test+                     , hspec                >= 1.3+                     , text+++test-suite hlint+    main-is:          test/HLint.hs+    ghc-options:      -Wall -Werror -threaded+    type:             exitcode-stdio-1.0+    default-language: Haskell2010+    build-depends:    base+                    , hlint == 1.8.*