Re-enable authentication

This commit is contained in:
Joe Nelson
2014-12-06 17:42:22 -08:00
parent 13bc9a4510
commit a45718e992
6 changed files with 65 additions and 57 deletions
+1 -1
View File
@@ -96,7 +96,7 @@ app req =
Nothing -> return $ responseLBS status400 [jsonH] $ Nothing -> return $ responseLBS status400 [jsonH] $
encode . object $ [("error", String "Failed to parse user.")] encode . object $ [("error", String "Failed to parse user.")]
Just u -> do Just u -> do
_ <- liftIO $ addUser (cs $ userId u) _ <- addUser (cs $ userId u)
(cs $ userPass u) (cs $ userRole u) (cs $ userPass u) (cs $ userRole u)
return $ responseLBS status201 return $ responseLBS status201
[ jsonH [ jsonH
+10 -7
View File
@@ -1,20 +1,23 @@
{-# LANGUAGE QuasiQuotes, ScopedTypeVariables #-} {-# LANGUAGE QuasiQuotes, ScopedTypeVariables, OverloadedStrings #-}
module Auth where module Auth where
import Data.Aeson import Data.Aeson
import Control.Monad (mzero) import Control.Monad (mzero)
import Control.Applicative ( (<*>), (<$>) ) import Control.Applicative ( (<*>), (<$>) )
import Control.Monad.IO.Class (liftIO)
import Crypto.BCrypt import Crypto.BCrypt
import Data.Text import Data.Text
import Data.Monoid
import qualified Hasql as H import qualified Hasql as H
import qualified Hasql.Postgres as H import qualified Hasql.Postgres as H
import Data.String.Conversions (cs) import Data.String.Conversions (cs)
import PgQuery (pgFmtLit)
data AuthUser = AuthUser { data AuthUser = AuthUser {
userId :: String userId :: String
, userPass :: String , userPass :: String
, userRole :: String , userRole :: String
} } deriving (Show)
instance FromJSON AuthUser where instance FromJSON AuthUser where
parseJSON (Object v) = AuthUser <$> parseJSON (Object v) = AuthUser <$>
@@ -42,17 +45,17 @@ checkPass :: Text -> Text -> Bool
checkPass = (. cs) . validatePassword . cs checkPass = (. cs) . validatePassword . cs
setRole :: Text -> H.Tx H.Postgres s () setRole :: Text -> H.Tx H.Postgres s ()
setRole role = H.unit $ [H.q| set role ?|] role setRole role = H.unit ("set role " <> cs (pgFmtLit role), [], True)
resetRole :: H.Tx H.Postgres s () resetRole :: H.Tx H.Postgres s ()
resetRole = H.unit [H.q|reset role|] resetRole = H.unit [H.q|reset role|]
addUser :: Text -> Text -> Text -> IO(H.Tx H.Postgres s ()) addUser :: Text -> Text -> Text -> H.Session H.Postgres IO ()
addUser identity pass role = do addUser identity pass role = do
Just hashed <- hashPasswordUsingPolicy fastBcryptHashingPolicy (cs pass) Just hashed <- liftIO $ hashPasswordUsingPolicy fastBcryptHashingPolicy (cs pass)
return $ H.unit $ H.tx Nothing $ H.unit $
[H.q|insert into dbapi.auth (id, pass, rolname) values (?, ?, ?)|] [H.q|insert into dbapi.auth (id, pass, rolname) values (?, ?, ?)|]
identity hashed role identity (cs hashed :: Text) role
signInRole :: Text -> Text -> H.Tx H.Postgres s LoginAttempt signInRole :: Text -> Text -> H.Tx H.Postgres s LoginAttempt
signInRole user pass = do signInRole user pass = do
+3 -2
View File
@@ -3,7 +3,6 @@ module Main where
import Paths_dbapi (version) import Paths_dbapi (version)
import App import App
--import Auth
import Middleware import Middleware
import Control.Monad (unless) import Control.Monad (unless)
@@ -50,7 +49,9 @@ main = do
H.session pgSettings sessSettings $ do H.session pgSettings sessSettings $ do
session' <- flip runReaderT <$> ask session' <- flip runReaderT <$> ask
let runApp req respond = let runApp req respond =
respond =<< catchJust isSqlError (session' $ app req) sqlErrHandler respond =<< catchJust isSqlError
(session' $ authenticated (cs $ configAnonRole conf) app req)
sqlErrHandler
liftIO $ runSettings appSettings $ middle runApp liftIO $ runSettings appSettings $ middle runApp
-- . authenticated (cs $ configAnonRole conf) $ app -- . authenticated (cs $ configAnonRole conf) $ app
+37 -30
View File
@@ -3,23 +3,25 @@
module Middleware where module Middleware where
--import Data.Aeson ((.=), toJSON, ToJSON, object, encode) --import Data.Aeson ((.=), toJSON, ToJSON, object, encode)
-- import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Monoid (mconcat) import Data.Monoid (mconcat)
import Data.Text
-- import Data.Pool(withResource, Pool) -- import Data.Pool(withResource, Pool)
import qualified Hasql as H import qualified Hasql as H
import qualified Hasql.Postgres as H
import Data.String.Conversions(cs) import Data.String.Conversions(cs)
--import qualified Data.ByteString.Char8 as BS
import Control.Exception (catchJust) import Control.Exception (catchJust)
import Network.HTTP.Types.Header (hLocation, hContentType) import Network.HTTP.Types.Header (hLocation, hContentType, hAuthorization)
import Network.HTTP.Types.Status (status400, status301) import Network.HTTP.Types (RequestHeaders)
import Network.HTTP.Types.Status (status400, status401, status301)
import Network.Wai (Application, requestHeaders, responseLBS, rawPathInfo, import Network.Wai (Application, requestHeaders, responseLBS, rawPathInfo,
rawQueryString, isSecure) rawQueryString, isSecure, Request(..), Response)
import Network.URI (URI(..), parseURI) import Network.URI (URI(..), parseURI)
-- import Auth (LoginAttempt(..), signInRole, setRole, resetRole) import Auth (LoginAttempt(..), signInRole, setRole, resetRole)
-- import Codec.Binary.Base64.String (decode) import Codec.Binary.Base64.String (decode)
import Debug.Trace import Debug.Trace
@@ -36,30 +38,35 @@ import Debug.Trace
-- else Database.PostgreSQL.Simple.withSavepoint conn go -- else Database.PostgreSQL.Simple.withSavepoint conn go
-- where go = app conn req respond -- where go = app conn req respond
-- authenticated :: BS.ByteString -> (Connection -> Application) -> authenticated :: Text -> (Request -> H.Session H.Postgres IO Response) ->
-- Connection -> Application Request -> H.Session H.Postgres IO Response
-- authenticated anon app conn req respond = do authenticated anon app req = do
-- attempt <- httpRequesterRole (requestHeaders req) attempt <- httpRequesterRole (requestHeaders req)
-- case attempt of case attempt of
-- MalformedAuth -> MalformedAuth ->
-- respond $ responseLBS status400 [] "Malformed basic auth header" return $ responseLBS status400 [] "Malformed basic auth header"
-- LoginFailed -> LoginFailed ->
-- respond $ responseLBS status401 [] "Invalid username or password" return $ responseLBS status401 [] "Invalid username or password"
-- LoginSuccess role -> LoginSuccess role -> runInRole role
-- bracket_ (setRole conn role) (resetRole conn) $ app conn req respond NoCredentials -> runInRole anon
-- NoCredentials ->
-- bracket_ (setRole conn anon) (resetRole conn) $ app conn req respond
-- where where
-- httpRequesterRole :: RequestHeaders -> IO LoginAttempt httpRequesterRole :: RequestHeaders -> H.Session H.Postgres IO LoginAttempt
-- httpRequesterRole hdrs = do httpRequesterRole hdrs = do
-- let auth = fromMaybe "" $ lookup hAuthorization hdrs let auth = fromMaybe "" $ lookup hAuthorization hdrs
-- case BS.split ' ' (cs auth) of case split (==' ') (cs auth) of
-- ("Basic" : b64 : _) -> ("Basic" : b64 : _) ->
-- case BS.split ':' $ cs (decode $ cs b64) of case split (==':') (cs . decode . cs $ b64) of
-- (u:p:_) -> signInRole conn u p (u:p:_) -> H.tx Nothing $ signInRole u p
-- _ -> return MalformedAuth _ -> return MalformedAuth
-- _ -> return NoCredentials _ -> return NoCredentials
runInRole :: Text -> H.Session H.Postgres IO Response
runInRole r = do
H.tx Nothing $ setRole r
resp <- app req
H.tx Nothing resetRole
return resp
-- instance ToJSON SqlError where -- instance ToJSON SqlError where
-- toJSON t = object [ -- toJSON t = object [
+7 -12
View File
@@ -8,21 +8,17 @@ import Test.Hspec.Wai.JSON
import SpecHelper import SpecHelper
import Network.HTTP.Types import Network.HTTP.Types
import Codec.Binary.Base64.String (encode)
import Data.Monoid ((<>))
import Data.String.Conversions (cs)
spec :: Spec spec :: Spec
spec = around withApp $ do spec = around withApp $ do
let uName = "a user"
uPass = "nobody can ever know"
describe "GET /" $ describe "GET /" $
it "lists views in schema" $ it "lists views in schema" $ do
request methodGet "/" _ <- post "/dbapi/users" [json| { "id":"jdoe", "pass": "1234", "role": "dbapi_test_author" } |]
[("Authorization", "Basic "<>(uName<>":"<>uPass))] "" let auth = authHeader "jdoe" "1234"
request methodGet "/" [auth] ""
`shouldRespondWith` [json| [ `shouldRespondWith` [json| [
{"schema":"1","name":"authors_only","insertable":true} {"schema":"1","name":"auto_incrementing_pk","insertable":true}
, {"schema":"1","name":"auto_incrementing_pk","insertable":true}
, {"schema":"1","name":"compound_pk","insertable":true} , {"schema":"1","name":"compound_pk","insertable":true}
, {"schema":"1","name":"has_fk","insertable":true} , {"schema":"1","name":"has_fk","insertable":true}
, {"schema":"1","name":"items","insertable":true} , {"schema":"1","name":"items","insertable":true}
@@ -136,8 +132,7 @@ spec = around withApp $ do
|] |]
it "includes foreign key data" $ it "includes foreign key data" $
request methodOptions "/has_fk" request methodOptions "/has_fk" [] ""
[("Authorization", "Basic "<>(cs.encode $ cs uName<>":"<>cs uPass))] ""
`shouldRespondWith` [json| `shouldRespondWith` [json|
{ {
"pkey": ["id"], "pkey": ["id"],
+6 -4
View File
@@ -27,15 +27,16 @@ import Network.Wai.Middleware.Cors (cors)
import System.Process (readProcess) import System.Process (readProcess)
import App (app, sqlErrHandler, isSqlError) import App (app, sqlErrHandler, isSqlError)
import Config (corsPolicy) import Config (AppConfig(..), corsPolicy)
import Middleware
-- import Auth (addUser) -- import Auth (addUser)
isLeft :: Either a b -> Bool isLeft :: Either a b -> Bool
isLeft (Left _ ) = True isLeft (Left _ ) = True
isLeft _ = False isLeft _ = False
-- cfg :: AppConfig cfg :: AppConfig
-- cfg = AppConfig "postgres://dbapi_test:@localhost:5432/dbapi_test" 9000 "dbapi_anonymous" False 10 cfg = AppConfig "postgres://dbapi_test:@localhost:5432/dbapi_test" 9000 "dbapi_anonymous" False 10
testSettings :: SessionSettings testSettings :: SessionSettings
testSettings = fromMaybe (error "bad settings") $ H.sessionSettings 1 30 testSettings = fromMaybe (error "bad settings") $ H.sessionSettings 1 30
@@ -48,7 +49,8 @@ withApp perform =
perform $ middle $ \req resp -> perform $ middle $ \req resp ->
H.session pgSettings testSettings $ do H.session pgSettings testSettings $ do
session' <- flip runReaderT <$> ask session' <- flip runReaderT <$> ask
liftIO $ resp =<< catchJust isSqlError (session' $ app req) liftIO $ resp =<< catchJust isSqlError
(session' $ authenticated (cs $ configAnonRole cfg) app req)
sqlErrHandler sqlErrHandler
where middle = cors corsPolicy where middle = cors corsPolicy