Fix some OpenAPI issues (#970)

* Fix #933, update externals docs url to current version

* Fix #962, openApi don't err on nonexistent schema

* Fix #954, make OpenAPI rpc output dependent on user privileges
This commit is contained in:
Steve Chávez
2017-09-25 23:09:52 -07:00
committed by Joe Nelson
parent cf19ad0369
commit 4ba27d84a4
13 changed files with 176 additions and 82 deletions
+3
View File
@@ -13,6 +13,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Fixed ### Fixed
- #968, Treat blank proxy uri as missing - @begriffs - #968, Treat blank proxy uri as missing - @begriffs
- #933, OpenAPI externals docs url to current version - @steve-chavez
- #962, OpenAPI don't err on nonexistent schema - @steve-chavez
- #954, make OpenAPI rpc output dependent on user privileges - @steve-chavez
## [0.4.3.0] - 2017-09-06 ## [0.4.3.0] - 2017-09-06
+1
View File
@@ -127,6 +127,7 @@ Test-Suite spec
, Feature.UnicodeSpec , Feature.UnicodeSpec
, Feature.AndOrParamsSpec , Feature.AndOrParamsSpec
, Feature.RpcSpec , Feature.RpcSpec
, Feature.NonexistentSchemaSpec
, SpecHelper , SpecHelper
, TestTypes , TestTypes
Build-Depends: aeson Build-Depends: aeson
+2 -2
View File
@@ -268,8 +268,8 @@ app dbStructure conf apiRequest =
uri Nothing = ("http", host, port, "/") uri Nothing = ("http", host, port, "/")
uri (Just Proxy { proxyScheme = s, proxyHost = h, proxyPort = p, proxyPath = b }) = (s, h, p, b) uri (Just Proxy { proxyScheme = s, proxyHost = h, proxyPort = p, proxyPath = b }) = (s, h, p, b)
uri' = uri proxy uri' = uri proxy
encodeApi ti sd = encodeOpenAPI (M.elems allProcs) (toTableInfo ti) uri' sd (dbPrimaryKeys dbStructure) encodeApi ti sd procs = encodeOpenAPI (M.elems procs) (toTableInfo ti) uri' sd (dbPrimaryKeys dbStructure)
body <- encodeApi <$> H.query schema accessibleTables <*> H.query schema schemaDescription body <- encodeApi <$> H.query schema accessibleTables <*> H.query schema schemaDescription <*> H.query schema accessibleProcs
return $ responseLBS status200 [toHeader CTOpenAPI] $ toS body return $ responseLBS status200 [toHeader CTOpenAPI] $ toS body
_ -> return notFound _ -> return notFound
+6 -1
View File
@@ -14,6 +14,7 @@ turned in configurable behaviour if needed.
Other hardcoded options such as the minimum version number also belong here. Other hardcoded options such as the minimum version number also belong here.
-} -}
module PostgREST.Config ( prettyVersion module PostgREST.Config ( prettyVersion
, docsVersion
, readOptions , readOptions
, corsPolicy , corsPolicy
, minimumPgVersion , minimumPgVersion
@@ -33,7 +34,7 @@ import Data.Configurator.Types (Value(..))
import Data.List (lookup) import Data.List (lookup)
import Data.Monoid import Data.Monoid
import Data.Scientific (floatingOrInteger) import Data.Scientific (floatingOrInteger)
import Data.Text (strip, intercalate, lines) import Data.Text (strip, intercalate, lines, dropAround)
import Data.Text.Encoding (encodeUtf8) import Data.Text.Encoding (encodeUtf8)
import Data.Text.IO (hPutStrLn) import Data.Text.IO (hPutStrLn)
import Data.Version (versionBranch) import Data.Version (versionBranch)
@@ -92,6 +93,10 @@ corsPolicy req = case lookup "origin" headers of
prettyVersion :: Text prettyVersion :: Text
prettyVersion = intercalate "." $ map show $ versionBranch version prettyVersion = intercalate "." $ map show $ versionBranch version
-- | Version number used in docs
docsVersion :: Text
docsVersion = "v" <> dropAround (== '.') (dropAround (/= '.') prettyVersion)
-- | Function to read and parse options from the command line -- | Function to read and parse options from the command line
readOptions :: IO AppConfig readOptions :: IO AppConfig
readOptions = do readOptions = do
+61 -54
View File
@@ -6,6 +6,7 @@
module PostgREST.DbStructure ( module PostgREST.DbStructure (
getDbStructure getDbStructure
, accessibleTables , accessibleTables
, accessibleProcs
, schemaDescription , schemaDescription
) where ) where
@@ -35,7 +36,7 @@ getDbStructure schema = do
syns <- H.query () $ allSynonyms cols syns <- H.query () $ allSynonyms cols
rels <- H.query () $ allRelations tabs cols rels <- H.query () $ allRelations tabs cols
keys <- H.query () $ allPrimaryKeys tabs keys <- H.query () $ allPrimaryKeys tabs
procs <- H.query schema accessibleProcs procs <- H.query schema allProcs
let rels' = (addManyToManyRelations . raiseRelations schema syns . addParentRelations . addSynonymousRelations syns) rels let rels' = (addManyToManyRelations . raiseRelations schema syns . addParentRelations . addSynonymousRelations syns) rels
cols' = addForeignKeys rels' cols cols' = addForeignKeys rels' cols
@@ -100,59 +101,64 @@ decodeSynonyms cols =
<*> HD.value HD.text <*> HD.value HD.text <*> HD.value HD.text <*> HD.value HD.text
<*> HD.value HD.text <*> HD.value HD.text <*> HD.value HD.text <*> HD.value HD.text
decodeProcs :: HD.Result (M.HashMap Text ProcDescription)
decodeProcs =
M.fromList . map addName <$> HD.rowsList tblRow
where
tblRow = ProcDescription
<$> HD.value HD.text
<*> HD.nullableValue HD.text
<*> (parseArgs <$> HD.value HD.text)
<*> (parseRetType
<$> HD.value HD.text
<*> HD.value HD.text
<*> HD.value HD.bool
<*> HD.value HD.char)
<*> (parseVolatility <$> HD.value HD.char)
addName :: ProcDescription -> (Text, ProcDescription)
addName pd = (pdName pd, pd)
parseArgs :: Text -> [PgArg]
parseArgs = mapMaybe (parseArg . strip) . split (==',')
parseArg :: Text -> Maybe PgArg
parseArg a =
let (body, def) = breakOn " DEFAULT " a
(name, typ) = breakOn " " body in
if T.null typ
then Nothing
else Just $
PgArg (dropAround (== '"') name) (strip typ) (T.null def)
parseRetType :: Text -> Text -> Bool -> Char -> RetType
parseRetType schema name isSetOf typ
| isSetOf = SetOf pgType
| otherwise = Single pgType
where
qi = QualifiedIdentifier schema name
pgType = case typ of
'c' -> Composite qi
'p' -> if name == "record" -- Only pg pseudo type that is a row type is 'record'
then Composite qi
else Scalar qi
_ -> Scalar qi -- 'b'ase, 'd'omain, 'e'num, 'r'ange
parseVolatility :: Char -> ProcVolatility
parseVolatility v | v == 'i' = Immutable
| v == 's' = Stable
| otherwise = Volatile -- only 'v' can happen here
allProcs :: H.Query Schema (M.HashMap Text ProcDescription)
allProcs = H.statement (toS procsSqlQuery) (HE.value HE.text) decodeProcs True
accessibleProcs :: H.Query Schema (M.HashMap Text ProcDescription) accessibleProcs :: H.Query Schema (M.HashMap Text ProcDescription)
accessibleProcs = accessibleProcs = H.statement (toS sql) (HE.value HE.text) decodeProcs True
H.statement sql (HE.value HE.text) where
(M.fromList . map addName <$> sql = procsSqlQuery <> " AND has_function_privilege(p.oid, 'execute')"
HD.rowsList (
ProcDescription <$> HD.value HD.text
<*> HD.nullableValue HD.text
<*> (parseArgs <$> HD.value HD.text)
<*> (parseRetType <$>
HD.value HD.text <*>
HD.value HD.text <*>
HD.value HD.bool <*>
HD.value HD.char)
<*> (parseVolatility <$>
HD.value HD.char)
)
) True
where
addName :: ProcDescription -> (Text, ProcDescription)
addName pd = (pdName pd, pd)
parseArgs :: Text -> [PgArg] procsSqlQuery :: SqlQuery
parseArgs = mapMaybe (parseArg . strip) . split (==',') procsSqlQuery = [q|
parseArg :: Text -> Maybe PgArg
parseArg a =
let (body, def) = breakOn " DEFAULT " a
(name, typ) = breakOn " " body in
if T.null typ
then Nothing
else Just $
PgArg (dropAround (== '"') name) (strip typ) (T.null def)
parseRetType :: Text -> Text -> Bool -> Char -> RetType
parseRetType schema name isSetOf typ
| isSetOf = SetOf pgType
| otherwise = Single pgType
where
qi = QualifiedIdentifier schema name
pgType = case typ of
'c' -> Composite qi
'p' -> if name == "record" -- Only pg pseudo type that is a row type is 'record'
then Composite qi
else Scalar qi
_ -> Scalar qi -- 'b'ase, 'd'omain, 'e'num, 'r'ange
parseVolatility :: Char -> ProcVolatility
parseVolatility 'i' = Immutable
parseVolatility 's' = Stable
parseVolatility 'v' = Volatile
parseVolatility _ = Volatile -- should not happen, but be pessimistic
sql = [q|
SELECT p.proname as "proc_name", SELECT p.proname as "proc_name",
d.description as "proc_description", d.description as "proc_description",
pg_get_function_arguments(p.oid) as "args", pg_get_function_arguments(p.oid) as "args",
@@ -167,11 +173,12 @@ accessibleProcs =
JOIN pg_namespace tn ON tn.oid = t.typnamespace JOIN pg_namespace tn ON tn.oid = t.typnamespace
LEFT JOIN pg_class comp ON comp.oid = t.typrelid LEFT JOIN pg_class comp ON comp.oid = t.typrelid
LEFT JOIN pg_catalog.pg_description as d on d.objoid = p.oid LEFT JOIN pg_catalog.pg_description as d on d.objoid = p.oid
WHERE pn.nspname = $1|] WHERE pn.nspname = $1
|]
schemaDescription :: H.Query Schema (Maybe Text) schemaDescription :: H.Query Schema (Maybe Text)
schemaDescription = schemaDescription =
H.statement sql (HE.value HE.text) (HD.singleRow $ HD.nullableValue HD.text) True H.statement sql (HE.value HE.text) (join <$> HD.maybeRow (HD.nullableValue HD.text)) True
where where
sql = [q| sql = [q|
select select
+2 -2
View File
@@ -22,7 +22,7 @@ import Protolude hiding ((&), Proxy, get, intercalate, dr
import Data.Swagger import Data.Swagger
import PostgREST.ApiRequest (ContentType(..)) import PostgREST.ApiRequest (ContentType(..))
import PostgREST.Config (prettyVersion) import PostgREST.Config (prettyVersion, docsVersion)
import PostgREST.Types (Table(..), Column(..), PgArg(..), ForeignKey(..), import PostgREST.Types (Table(..), Column(..), PgArg(..), ForeignKey(..),
PrimaryKey(..), Proxy(..), ProcDescription(..), toMime) PrimaryKey(..), Proxy(..), ProcDescription(..), toMime)
@@ -260,7 +260,7 @@ postgrestSpec pds ti (s, h, p, b) sd pks = (mempty :: Swagger)
& description ?~ d) & description ?~ d)
& externalDocs ?~ ((mempty :: ExternalDocs) & externalDocs ?~ ((mempty :: ExternalDocs)
& description ?~ "PostgREST Documentation" & description ?~ "PostgREST Documentation"
& url .~ URL "https://postgrest.com/en/latest/api.html") & url .~ URL ("https://postgrest.com/en/" <> docsVersion <> "/api.html"))
& host .~ h' & host .~ h'
& definitions .~ fromList (map (makeTableDef pks) ti) & definitions .~ fromList (map (makeTableDef pks) ti)
& parameters .~ fromList (makeParamDefs ti) & parameters .~ fromList (makeParamDefs ti)
+11 -2
View File
@@ -1,6 +1,5 @@
module Feature.AuthSpec where module Feature.AuthSpec where
-- {{{ Imports
import Text.Heredoc import Text.Heredoc
import Test.Hspec import Test.Hspec
import Test.Hspec.Wai import Test.Hspec.Wai
@@ -11,7 +10,6 @@ import SpecHelper
import Network.Wai (Application) import Network.Wai (Application)
import Protolude hiding (get) import Protolude hiding (get)
-- }}}
spec :: SpecWith Application spec :: SpecWith Application
spec = describe "authorization" $ do spec = describe "authorization" $ do
@@ -39,6 +37,17 @@ spec = describe "authorization" $ do
, matchHeaders = [] , matchHeaders = []
} }
it "denies execution on functions that anonymous does not own" $
post "/rpc/privileged_hello" [json|{"name": "anonymous"}|] `shouldRespondWith` 401
it "allows execution on a function that postgrest_test_author owns" $
let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA" in
request methodPost "/rpc/privileged_hello" [auth] [json|{"name": "jdoe"}|]
`shouldRespondWith` [json|"Privileged hello to jdoe"|]
{ matchStatus = 200
, matchHeaders = [matchContentTypeJson]
}
it "returns jwt functions as jwt tokens" $ it "returns jwt functions as jwt tokens" $
request methodPost "/rpc/login" [single] request methodPost "/rpc/login" [single]
[json| { "id": "jdoe", "pass": "1234" } |] [json| { "id": "jdoe", "pass": "1234" } |]
+15
View File
@@ -0,0 +1,15 @@
module Feature.NonexistentSchemaSpec where
import Network.Wai (Application)
import Protolude hiding (get)
import Test.Hspec
import Test.Hspec.Wai
spec :: SpecWith Application
spec =
describe "Non existent api schema" $ do
it "succeeds when requesting root path" $
get "/" `shouldRespondWith` 200
it "gives 404 when requesting a nonexistent table in this nonexistent schema" $
get "/nonexistent_table" `shouldRespondWith` 404
+41 -3
View File
@@ -4,7 +4,9 @@ import Test.Hspec hiding (pendingWith)
import Test.Hspec.Wai import Test.Hspec.Wai
import Network.HTTP.Types import Network.HTTP.Types
import PostgREST.Config (docsVersion)
import Control.Lens ((^?)) import Control.Lens ((^?))
import Data.Aeson.Types (Value (..))
import Data.Aeson.Lens import Data.Aeson.Lens
import Data.Aeson.QQ import Data.Aeson.QQ
@@ -27,7 +29,14 @@ spec = do
(acceptHdrs "application/openapi+json") "" (acceptHdrs "application/openapi+json") ""
`shouldRespondWith` 415 `shouldRespondWith` 415
describe "table" $ it "includes postgrest.com current version api docs" $ do
r <- simpleBody <$> get "/"
let docsUrl = r ^? key "externalDocs" . key "url"
liftIO $ docsUrl `shouldBe` Just (String ("https://postgrest.com/en/" <> docsVersion <> "/api.html"))
describe "table" $ do
it "includes paths to tables" $ do it "includes paths to tables" $ do
r <- simpleBody <$> get "/" r <- simpleBody <$> get "/"
@@ -76,7 +85,7 @@ spec = do
deleteResponse `shouldBe` Just "No Content" deleteResponse `shouldBe` Just "No Content"
it "includes definitions to tables" $ do it "includes definitions to tables" $ do
r <- simpleBody <$> get "/" r <- simpleBody <$> get "/"
let def = r ^? key "definitions" . key "child_entities" let def = r ^? key "definitions" . key "child_entities"
@@ -108,7 +117,21 @@ spec = do
} }
|] |]
describe "RPC" $ it "doesn't include privileged table for anonymous" $ do
r <- simpleBody <$> get "/"
let tablePath = r ^? key "paths" . key "/authors_only"
liftIO $ tablePath `shouldBe` Nothing
it "includes table if user has permission" $ do
let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA"
r <- simpleBody <$> request methodGet "/" [auth] ""
let tableTag = r ^? key "paths" . key "/authors_only"
. key "post" . key "tags"
. nth 0
liftIO $ tableTag `shouldBe` Just [aesonQQ|"authors_only"|]
describe "RPC" $ do
it "includes body schema for arguments" $ do it "includes body schema for arguments" $ do
r <- simpleBody <$> get "/" r <- simpleBody <$> get "/"
@@ -162,6 +185,21 @@ spec = do
} }
|] |]
it "doesn't include privileged function for anonymous" $ do
r <- simpleBody <$> get "/"
let funcPath = r ^? key "paths" . key "/rpc/privileged_hello"
liftIO $ funcPath `shouldBe` Nothing
it "includes function if user has permission" $ do
let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA"
r <- simpleBody <$> request methodGet "/" [auth] ""
let funcTag = r ^? key "paths" . key "/rpc/privileged_hello"
. key "post" . key "tags"
. nth 0
liftIO $ funcTag `shouldBe` Just [aesonQQ|"(rpc) privileged_hello"|]
describe "Allow header" $ do describe "Allow header" $ do
it "includes read/write verbs for writeable table" $ do it "includes read/write verbs for writeable table" $ do
+25 -18
View File
@@ -27,6 +27,7 @@ import qualified Feature.UnicodeSpec
import qualified Feature.ProxySpec import qualified Feature.ProxySpec
import qualified Feature.AndOrParamsSpec import qualified Feature.AndOrParamsSpec
import qualified Feature.RpcSpec import qualified Feature.RpcSpec
import qualified Feature.NonexistentSchemaSpec
import Protolude import Protolude
@@ -39,13 +40,14 @@ main = do
result <- P.use pool $ getDbStructure "test" result <- P.use pool $ getDbStructure "test"
refDbStructure <- newIORef $ Just $ either (panic.show) id result refDbStructure <- newIORef $ Just $ either (panic.show) id result
let withApp = return $ postgrest (testCfg testDbConn) refDbStructure pool $ pure () let withApp = return $ postgrest (testCfg testDbConn) refDbStructure pool $ pure ()
ltdApp = return $ postgrest (testLtdRowsCfg testDbConn) refDbStructure pool $ pure () ltdApp = return $ postgrest (testLtdRowsCfg testDbConn) refDbStructure pool $ pure ()
unicodeApp = return $ postgrest (testUnicodeCfg testDbConn) refDbStructure pool $ pure () unicodeApp = return $ postgrest (testUnicodeCfg testDbConn) refDbStructure pool $ pure ()
proxyApp = return $ postgrest (testProxyCfg testDbConn) refDbStructure pool $ pure () proxyApp = return $ postgrest (testProxyCfg testDbConn) refDbStructure pool $ pure ()
noJwtApp = return $ postgrest (testCfgNoJWT testDbConn) refDbStructure pool $ pure () noJwtApp = return $ postgrest (testCfgNoJWT testDbConn) refDbStructure pool $ pure ()
binaryJwtApp = return $ postgrest (testCfgBinaryJWT testDbConn) refDbStructure pool $ pure () binaryJwtApp = return $ postgrest (testCfgBinaryJWT testDbConn) refDbStructure pool $ pure ()
asymJwkApp = return $ postgrest (testCfgAsymJWK testDbConn) refDbStructure pool $ pure () asymJwkApp = return $ postgrest (testCfgAsymJWK testDbConn) refDbStructure pool $ pure ()
nonexistentSchemaApp = return $ postgrest (testNonexistentSchemaCfg testDbConn) refDbStructure pool $ pure ()
let reset = resetDb testDbConn let reset = resetDb testDbConn
hspec $ do hspec $ do
@@ -75,17 +77,22 @@ main = do
beforeAll_ reset . before asymJwkApp $ beforeAll_ reset . before asymJwkApp $
describe "Feature.AsymmetricJwtSpec" Feature.AsymmetricJwtSpec.spec describe "Feature.AsymmetricJwtSpec" Feature.AsymmetricJwtSpec.spec
-- this test runs with a nonexistent db-schema
beforeAll_ reset . before nonexistentSchemaApp $
describe "Feature.NonexistentSchemaSpec" Feature.NonexistentSchemaSpec.spec
where where
specs = map (uncurry describe) [ specs = map (uncurry describe) [
("Feature.AuthSpec" , Feature.AuthSpec.spec) ("Feature.AuthSpec" , Feature.AuthSpec.spec)
, ("Feature.ConcurrentSpec" , Feature.ConcurrentSpec.spec) , ("Feature.ConcurrentSpec" , Feature.ConcurrentSpec.spec)
, ("Feature.CorsSpec" , Feature.CorsSpec.spec) , ("Feature.CorsSpec" , Feature.CorsSpec.spec)
, ("Feature.DeleteSpec" , Feature.DeleteSpec.spec) , ("Feature.DeleteSpec" , Feature.DeleteSpec.spec)
, ("Feature.InsertSpec" , Feature.InsertSpec.spec) , ("Feature.InsertSpec" , Feature.InsertSpec.spec)
, ("Feature.QuerySpec" , Feature.QuerySpec.spec) , ("Feature.QuerySpec" , Feature.QuerySpec.spec)
, ("Feature.RpcSpec" , Feature.RpcSpec.spec) , ("Feature.RpcSpec" , Feature.RpcSpec.spec)
, ("Feature.RangeSpec" , Feature.RangeSpec.spec) , ("Feature.RangeSpec" , Feature.RangeSpec.spec)
, ("Feature.SingularSpec" , Feature.SingularSpec.spec) , ("Feature.SingularSpec" , Feature.SingularSpec.spec)
, ("Feature.StructureSpec" , Feature.StructureSpec.spec) , ("Feature.StructureSpec" , Feature.StructureSpec.spec)
, ("Feature.AndOrParamsSpec" , Feature.AndOrParamsSpec.spec) , ("Feature.AndOrParamsSpec" , Feature.AndOrParamsSpec.spec)
, ("Feature.NonexistentSchemaSpec" , Feature.NonexistentSchemaSpec.spec)
] ]
+3
View File
@@ -101,6 +101,9 @@ testCfgAsymJWK testDbConn = (testCfg testDbConn) {
[str|{"alg":"RS256","e":"AQAB","key_ops":["verify"],"kty":"RSA","n":"0etQ2Tg187jb04MWfpuogYGV75IFrQQBxQaGH75eq_FpbkyoLcEpRUEWSbECP2eeFya2yZ9vIO5ScD-lPmovePk4Aa4SzZ8jdjhmAbNykleRPCxMg0481kz6PQhnHRUv3nF5WP479CnObJKqTVdEagVL66oxnX9VhZG9IZA7k0Th5PfKQwrKGyUeTGczpOjaPqbxlunP73j9AfnAt4XCS8epa-n3WGz1j-wfpr_ys57Aq-zBCfqP67UYzNpeI1AoXsJhD9xSDOzvJgFRvc3vm2wjAW4LEMwi48rCplamOpZToIHEPIaPzpveYQwDnB1HFTR1ove9bpKJsHmi-e2uzQ","use":"sig"}|] [str|{"alg":"RS256","e":"AQAB","key_ops":["verify"],"kty":"RSA","n":"0etQ2Tg187jb04MWfpuogYGV75IFrQQBxQaGH75eq_FpbkyoLcEpRUEWSbECP2eeFya2yZ9vIO5ScD-lPmovePk4Aa4SzZ8jdjhmAbNykleRPCxMg0481kz6PQhnHRUv3nF5WP479CnObJKqTVdEagVL66oxnX9VhZG9IZA7k0Th5PfKQwrKGyUeTGczpOjaPqbxlunP73j9AfnAt4XCS8epa-n3WGz1j-wfpr_ys57Aq-zBCfqP67UYzNpeI1AoXsJhD9xSDOzvJgFRvc3vm2wjAW4LEMwi48rCplamOpZToIHEPIaPzpveYQwDnB1HFTR1ove9bpKJsHmi-e2uzQ","use":"sig"}|]
} }
testNonexistentSchemaCfg :: Text -> AppConfig
testNonexistentSchemaCfg testDbConn = (testCfg testDbConn) { configSchema = "nonexistent" }
setupDb :: Text -> IO () setupDb :: Text -> IO ()
setupDb dbConn = do setupDb dbConn = do
loadFixture dbConn "database" loadFixture dbConn "database"
+3
View File
@@ -74,3 +74,6 @@ GRANT ALL ON TABLE authors_only TO postgrest_test_author;
GRANT SELECT (article_id, user_id) ON TABLE limited_article_stars TO postgrest_test_anonymous; GRANT SELECT (article_id, user_id) ON TABLE limited_article_stars TO postgrest_test_anonymous;
GRANT INSERT (article_id, user_id) ON TABLE limited_article_stars TO postgrest_test_anonymous; GRANT INSERT (article_id, user_id) ON TABLE limited_article_stars TO postgrest_test_anonymous;
GRANT UPDATE (article_id, user_id) ON TABLE limited_article_stars TO postgrest_test_anonymous; GRANT UPDATE (article_id, user_id) ON TABLE limited_article_stars TO postgrest_test_anonymous;
REVOKE EXECUTE ON FUNCTION privileged_hello(text) FROM PUBLIC; -- All functions are available to every role(PUBLIC) by default
GRANT EXECUTE ON FUNCTION privileged_hello(text) TO postgrest_test_author;
+3
View File
@@ -1247,6 +1247,9 @@ create function test.test() returns table(test text, value int) as $$
values ('hello', 1); values ('hello', 1);
$$ language sql; $$ language sql;
create function test.privileged_hello(name text) returns text as $$
select 'Privileged hello to ' || $1;
$$ language sql;
-- --
-- PostgreSQL database dump complete -- PostgreSQL database dump complete
-- --