Authentication cleanup

This commit is contained in:
aaron levin 2015-05-12 11:01:23 -04:00 committed by Arian van Putten
parent 1cacf850bf
commit 17885bc50f
2 changed files with 33 additions and 13 deletions

View file

@ -73,8 +73,15 @@ type Server layout = ServerT layout (ExceptT ServantErr IO)
-- | A type-indexed class to encapsulate Basic authentication handling. -- | A type-indexed class to encapsulate Basic authentication handling.
-- Authentication handling is indexed by the lookup type. -- Authentication handling is indexed by the lookup type.
class BasicAuthLookup lookup a | lookup -> a where --
basicAuthLookup :: Proxy lookup -> B.ByteString -> B.ByteString -> IO (Maybe a) -- > data ExampleAuthDB
-- > data ExampleUser
-- > instance BasicAuthLookup ExampleAuthDB where
-- > type BasicAuthVal = ExampleUser
-- > basicAuthLookup _ _ _ = return Nothing
class BasicAuthLookup lookup where
type BasicAuthVal
basicAuthLookup :: Proxy lookup -> B.ByteString -> B.ByteString -> IO (Maybe BasicAuthVal)
-- * Instances -- * Instances
@ -244,31 +251,44 @@ instance
#if MIN_VERSION_base(4,8,0) #if MIN_VERSION_base(4,8,0)
{-# OVERLAPPABLE #-} {-# OVERLAPPABLE #-}
#endif #endif
(HasServer sublayout, BasicAuthLookup lookup authVal) => HasServer (BasicAuth realm lookup authVal :> sublayout) where ( HasServer sublayout
type ServerT (BasicAuth realm lookup authVal :> sublayout) m = authVal -> ServerT sublayout m , BasicAuthLookup lookup
route proxy action request response = , KnownSymbol realm
)
=> HasServer (BasicAuth realm lookup :> sublayout) where
type ServerT (BasicAuth realm lookup :> sublayout) m
= BasicAuthVal -> ServerT sublayout m
route _ action request respond =
case lookup "Authorization" (requestHeaders request) of case lookup "Authorization" (requestHeaders request) of
Nothing -> error "handle no authorization header" -- 401 Nothing -> respond . succeedWith $ authFailure401
Just authBs -> Just authBs ->
-- ripped from: https://hackage.haskell.org/package/wai-extra-1.3.4.5/docs/src/Network-Wai-Middleware-HttpAuth.html#basicAuth -- ripped from: https://hackage.haskell.org/package/wai-extra-1.3.4.5/docs/src/Network-Wai-Middleware-HttpAuth.html#basicAuth
let (x,y) = B.break isSpace authBs in let (x,y) = B.break isSpace authBs in
if B.map toLower x == "basic" if B.map toLower x == "basic"
then checkB64 (B.dropWhile isSpace y) -- check base64-encoded password
else error "not basic authentication" -- 401 then checkB64AndRespond (B.dropWhile isSpace y)
-- Authenticaiton header is not Basic, fail with 401.
else respond . succeedWith $ authFailure401
where where
checkB64 encoded = realmBytes = (fromString . symbolVal) (Proxy :: Proxy realm)
headerBytes = "Basic realm=\"" <> realmBytes <> "\""
authFailure401 = responseLBS status401 [("WWW-Authenticate", headerBytes)] ""
checkB64AndRespond encoded =
case B.uncons passwordWithColonAtHead of case B.uncons passwordWithColonAtHead of
Just (_, password) -> do Just (_, password) -> do
-- let's check these credentials using the user-provided lookup method -- let's check these credentials using the user-provided lookup method
maybeAuthData <- basicAuthLookup (Proxy :: Proxy lookup) username password maybeAuthData <- basicAuthLookup (Proxy :: Proxy lookup) username password
case maybeAuthData of case maybeAuthData of
Nothing -> error "bad password" -- 403 Nothing -> respond . succeedWith $ authFailure403
(Just authData) -> (Just authData) ->
route (Proxy :: Proxy sublayout) (action authData) request response route (Proxy :: Proxy sublayout) (action authData) request respond
-- no username:password present -- no username:password present
Nothing -> error "No password" -- 403 Nothing -> respond . succeedWith $ authFailure401
where where
authFailure403 = responseLBS status403 [] ""
raw = decodeLenient encoded raw = decodeLenient encoded
-- split username and password at the colon ':' char. -- split username and password at the colon ':' char.
(username, passwordWithColonAtHead) = B.breakByte _colon raw (username, passwordWithColonAtHead) = B.breakByte _colon raw

View file

@ -12,7 +12,7 @@ import GHC.TypeLits (Symbol)
-- --
-- Example: -- Example:
-- >>> type MyApi = BasicAuth "book-realm" DB :> "books" :> Get '[JSON] [Book] -- >>> type MyApi = BasicAuth "book-realm" DB :> "books" :> Get '[JSON] [Book]
data BasicAuth (realm :: Symbol) lookup a data BasicAuth (realm :: Symbol) lookup
deriving (Typeable) deriving (Typeable)
-- $setup -- $setup