X-Git-Url: http://git.cielonegro.org/gitweb.cgi?p=Lucu.git;a=blobdiff_plain;f=Network%2FHTTP%2FLucu%2FAuthentication.hs;h=a63419cea4b6b03e814120f82afeede81869d8cc;hp=495c931604696c9f8b7a4741bcba861a335254f0;hb=90fca0675b1694e69b8e431c989343855cbd125d;hpb=cc074d0ce3f7df2544bc2baddca4e7730ecdf0a0 diff --git a/Network/HTTP/Lucu/Authentication.hs b/Network/HTTP/Lucu/Authentication.hs index 495c931..a63419c 100644 --- a/Network/HTTP/Lucu/Authentication.hs +++ b/Network/HTTP/Lucu/Authentication.hs @@ -1,5 +1,9 @@ {-# LANGUAGE - OverloadedStrings + FlexibleInstances + , MultiParamTypeClasses + , OverloadedStrings + , TemplateHaskell + , TypeSynonymInstances , UnicodeSyntax #-} -- |An internal module for HTTP authentication. @@ -9,17 +13,18 @@ module Network.HTTP.Lucu.Authentication , Realm , UserID , Password - - , printAuthChallenge - , authCredential ) where import Control.Monad -import Data.Ascii (Ascii) -import qualified Data.Ascii as A +import Data.Ascii (Ascii, AsciiBuilder) +import Data.Attempt import Data.Attoparsec.Char8 import qualified Data.ByteString.Base64 as B64 import qualified Data.ByteString.Char8 as C8 +import Data.Convertible.Base +import Data.Convertible.Instances.Ascii () +import Data.Convertible.Utils +import Data.Default import Data.Monoid.Unicode import Network.HTTP.Lucu.Parser.Http import Network.HTTP.Lucu.Utils @@ -27,7 +32,7 @@ import Prelude.Unicode -- |Authentication challenge to be sent to clients with -- \"WWW-Authenticate\" header field. See --- 'Network.HTTP.Lucu.Resource.setWWWAuthenticate'. +-- 'Network.HTTP.Lucu.setWWWAuthenticate'. data AuthChallenge = BasicAuthChallenge !Realm deriving (Eq) @@ -36,8 +41,7 @@ data AuthChallenge type Realm = Ascii -- |Authorization credential to be sent by client with --- \"Authorization\" header. See --- 'Network.HTTP.Lucu.Resource.getAuthorization'. +-- \"Authorization\" header. See 'Network.HTTP.Lucu.getAuthorization'. data AuthCredential = BasicAuthCredential !UserID !Password deriving (Show, Eq) @@ -48,31 +52,37 @@ type UserID = Ascii -- |'Password' is just an 'Ascii' string. type Password = Ascii --- |Convert an 'AuthChallenge' to 'Ascii'. -printAuthChallenge ∷ AuthChallenge → Ascii -printAuthChallenge (BasicAuthChallenge realm) - = A.fromAsciiBuilder $ - A.toAsciiBuilder "Basic realm=" ⊕ quoteStr realm +instance ConvertSuccess AuthChallenge Ascii where + {-# INLINE convertSuccess #-} + convertSuccess = convertSuccessVia ((⊥) ∷ AsciiBuilder) --- |'Parser' for an 'AuthCredential'. -authCredential ∷ Parser AuthCredential -authCredential - = do void $ string "Basic" - skipMany1 lws - b64 ← takeWhile1 base64 - case C8.break (≡ ':') (B64.decodeLenient b64) of - (user, cPassword) - | C8.null cPassword - → fail "no colons in the basic auth credential" - | otherwise - → do u ← asc user - p ← asc (C8.tail cPassword) - return (BasicAuthCredential u p) - where - base64 ∷ Char → Bool - base64 = inClass "a-zA-Z0-9+/=" +instance ConvertSuccess AuthChallenge AsciiBuilder where + {-# INLINE convertSuccess #-} + convertSuccess (BasicAuthChallenge realm) + = cs ("Basic realm=" ∷ Ascii) ⊕ quoteStr realm + +deriveAttempts [ ([t| AuthChallenge |], [t| Ascii |]) + , ([t| AuthChallenge |], [t| AsciiBuilder |]) + ] + +instance Default (Parser AuthCredential) where + def = do void $ string "Basic" + skipMany1 lws + b64 ← takeWhile1 base64 + case C8.break (≡ ':') (B64.decodeLenient b64) of + (user, cPassword) + | C8.null cPassword + → fail "no colons in the basic auth credential" + | otherwise + → do u ← asc user + p ← asc (C8.tail cPassword) + return (BasicAuthCredential u p) + where + base64 ∷ Char → Bool + base64 = inClass "a-zA-Z0-9+/=" - asc ∷ C8.ByteString → Parser Ascii - asc bs = case A.fromByteString bs of - Just as → return as - Nothing → fail "Non-ascii character in auth credential" + asc ∷ C8.ByteString → Parser Ascii + asc bs + = case ca bs of + Success as → return as + Failure _ → fail "Non-ascii character in auth credential"