From 8e107e5b24f5a3c56bd96e90ac474248ec50834c Mon Sep 17 00:00:00 2001 From: calebmer Date: Wed, 7 Oct 2015 20:01:46 -0400 Subject: [PATCH] Tolerate missing role in user creation --- .gitignore | 1 + CHANGELOG.md | 3 +++ src/PostgREST/App.hs | 2 +- src/PostgREST/Auth.hs | 19 ++++++++++++------- test/Feature/AuthSpec.hs | 25 ++++++++++++++++++------- test/fixtures/schema.sql | 2 +- 6 files changed, 36 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index bdca21e1b..ef5079161 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.DS_Store db dist .cabal-sandbox diff --git a/CHANGELOG.md b/CHANGELOG.md index fab28a50f..8badef4d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Filter columns, e.g. `?select=col1,col2` - @ruslantalpa - Does not execute the count total if header "Prefer: count=none" - @diogob +### Fixed +- Tolerate a missing role in user creation - @calebmer + ## [0.2.11.1] - 2015-09-01 ### Fixed diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index 8dc758f48..f9044832d 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -141,7 +141,7 @@ app dbstructure conf reqBody dbrole req = encode . object $ [("message", String "Failed to parse user.")] Just u -> do _ <- addUser (cs $ userId u) - (cs $ userPass u) (cs $ userRole u) + (cs $ userPass u) (cs <$> userRole u) return $ responseLBS status201 [ jsonH , (hLocation, "/postgrest/users?id=eq." <> cs (userId u)) diff --git a/src/PostgREST/Auth.hs b/src/PostgREST/Auth.hs index 2e60448c5..edcf86cf9 100644 --- a/src/PostgREST/Auth.hs +++ b/src/PostgREST/Auth.hs @@ -8,6 +8,7 @@ import Data.Map import Data.Monoid import Data.String.Conversions (cs) import Data.Text +import Data.Maybe (isNothing) import qualified Data.Vector as V import qualified Hasql as H import qualified Hasql.Backend as B @@ -21,14 +22,14 @@ import System.IO.Unsafe data AuthUser = AuthUser { userId :: String , userPass :: String - , userRole :: String + , userRole :: Maybe String } deriving (Show) instance FromJSON AuthUser where parseJSON (Object v) = AuthUser <$> v .: "id" <*> v .: "pass" <*> - v .:? "role" .!= "" + v .:? "role" parseJSON _ = mzero instance ToJSON AuthUser where @@ -62,12 +63,16 @@ setUserId uid = resetUserId :: H.Tx P.Postgres s () resetUserId = H.unitEx [H.stmt|reset user_vars.user_id|] -addUser :: Text -> Text -> Text -> H.Tx P.Postgres s () -addUser identity pass role = do - let Just hashed = unsafePerformIO $ hashPasswordUsingPolicy fastBcryptHashingPolicy (cs pass) +addUser :: Text -> Text -> Maybe Text -> H.Tx P.Postgres s () +addUser identity pass role = H.unitEx $ - [H.stmt|insert into postgrest.auth (id, pass, rolname) values (?, ?, ?)|] - identity (cs hashed :: Text) role + if isNothing role + then [H.stmt|insert into postgrest.auth (id, pass) values (?, ?)|] + identity hashedText + else [H.stmt|insert into postgrest.auth (id, pass, rolname) values (?, ?, ?)|] + identity hashedText role + where Just hashed = unsafePerformIO $ hashPasswordUsingPolicy fastBcryptHashingPolicy (cs pass) + hashedText = cs hashed :: Text signInRole :: Text -> Text -> H.Tx P.Postgres s LoginAttempt signInRole user pass = do diff --git a/test/Feature/AuthSpec.hs b/test/Feature/AuthSpec.hs index 915526f06..7362e7a15 100644 --- a/test/Feature/AuthSpec.hs +++ b/test/Feature/AuthSpec.hs @@ -24,21 +24,32 @@ spec = beforeAll `shouldRespondWith` 401 it "allows users with permissions to see their tables (BasicAuth)" $ do - _ <- post "/postgrest/users" [json| { "id":"jdoe", "pass": "1234", "role": "postgrest_test_author" } |] + _ <- post "/postgrest/users" [json| { "id": "jdoe", "pass": "1234", "role": "postgrest_test_author" } |] + let auth = authHeaderBasic "jdoe" "1234" + request methodGet "/authors_only" [auth] "" + `shouldRespondWith` 200 + + it "respects database constraints for role" $ + post "/postgrest/users" [json| { "id": "ssmith", "pass": "1234", "role": "SUPER_ADMIN_TRUNCATE_POWERS" } |] + `shouldRespondWith` 400 + + it "does not send a value when no role is provided" $ do + post "/postgrest/users" [json| { "id": "bdeey", "pass": "1234" } |] + `shouldRespondWith` 201 let auth = authHeaderBasic "jdoe" "1234" request methodGet "/authors_only" [auth] "" `shouldRespondWith` 200 it "recovers after 400 error with logged in user" $ do - _ <- post "/postgrest/users" [json| { "id":"jdoe", "pass": "1234", "role": "postgrest_test_author" } |] + _ <- post "/postgrest/users" [json| { "id": "jdoe", "pass": "1234", "role": "postgrest_test_author" } |] let auth = authHeaderBasic "jdoe" "1234" _ <- request methodPost "/rpc/problem" [auth] "" request methodGet "/authors_only" [auth] "" `shouldRespondWith` 200 it "allows users to login (JWT)" $ do - _ <- post "/postgrest/users" [json| { "id":"jdoe", "pass": "1234", "role": "postgrest_test_author" } |] - post "/postgrest/tokens" [json| { "id":"jdoe", "pass": "1234" } |] + _ <- post "/postgrest/users" [json| { "id": "jdoe", "pass": "1234", "role": "postgrest_test_author" } |] + post "/postgrest/tokens" [json| { "id": "jdoe", "pass": "1234" } |] `shouldRespondWith` ResponseMatcher { matchBody = Just [json| {"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWQiOiJqZG9lIn0.y4vZuu1dDdwAl0-S00MCRWRYMlJ5YAMSir6Es6WtWx0"} |] , matchStatus = 201 @@ -46,8 +57,8 @@ spec = beforeAll } it "indicates login failure (JWT)" $ do - _ <- post "/postgrest/users" [json| { "id":"jdoe", "pass": "1234", "role": "postgrest_test_author" } |] - post "/postgrest/tokens" [json| { "id":"jdoe", "pass": "NOPE" } |] + _ <- post "/postgrest/users" [json| { "id": "jdoe", "pass": "1234", "role": "postgrest_test_author" } |] + post "/postgrest/tokens" [json| { "id": "jdoe", "pass": "NOPE" } |] `shouldRespondWith` ResponseMatcher { matchBody = Just [json| {"message":"Failed authentication."} |] , matchStatus = 401 @@ -55,7 +66,7 @@ spec = beforeAll } it "allows users with permissions to see their tables (JWT)" $ do - _ <- post "/postgrest/users" [json| { "id":"jdoe", "pass": "1234", "role": "postgrest_test_author" } |] + _ <- post "/postgrest/users" [json| { "id": "jdoe", "pass": "1234", "role": "postgrest_test_author" } |] let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWQiOiJqZG9lIn0.y4vZuu1dDdwAl0-S00MCRWRYMlJ5YAMSir6Es6WtWx0" request methodGet "/authors_only" [auth] "" `shouldRespondWith` 200 diff --git a/test/fixtures/schema.sql b/test/fixtures/schema.sql index d66ce05a4..b69e45d38 100755 --- a/test/fixtures/schema.sql +++ b/test/fixtures/schema.sql @@ -372,7 +372,7 @@ SET search_path = postgrest, pg_catalog; CREATE TABLE auth ( id character varying NOT NULL, - rolname name NOT NULL, + rolname name NOT NULL DEFAULT 'postgrest_test_author', pass character(60) NOT NULL );