Re-enable authentication
This commit is contained in:
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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 [
|
||||||
|
|||||||
@@ -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"],
|
||||||
|
|||||||
+7
-5
@@ -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,8 +49,9 @@ 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
|
||||||
sqlErrHandler
|
(session' $ authenticated (cs $ configAnonRole cfg) app req)
|
||||||
|
sqlErrHandler
|
||||||
|
|
||||||
where middle = cors corsPolicy
|
where middle = cors corsPolicy
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user