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:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,24 +101,21 @@ 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
|
||||||
|
|
||||||
accessibleProcs :: H.Query Schema (M.HashMap Text ProcDescription)
|
decodeProcs :: HD.Result (M.HashMap Text ProcDescription)
|
||||||
accessibleProcs =
|
decodeProcs =
|
||||||
H.statement sql (HE.value HE.text)
|
M.fromList . map addName <$> HD.rowsList tblRow
|
||||||
(M.fromList . map addName <$>
|
where
|
||||||
HD.rowsList (
|
tblRow = ProcDescription
|
||||||
ProcDescription <$> HD.value HD.text
|
<$> HD.value HD.text
|
||||||
<*> HD.nullableValue HD.text
|
<*> HD.nullableValue HD.text
|
||||||
<*> (parseArgs <$> HD.value HD.text)
|
<*> (parseArgs <$> HD.value HD.text)
|
||||||
<*> (parseRetType <$>
|
<*> (parseRetType
|
||||||
HD.value HD.text <*>
|
<$> HD.value HD.text
|
||||||
HD.value HD.text <*>
|
<*> HD.value HD.text
|
||||||
HD.value HD.bool <*>
|
<*> HD.value HD.bool
|
||||||
HD.value HD.char)
|
<*> HD.value HD.char)
|
||||||
<*> (parseVolatility <$>
|
<*> (parseVolatility <$> HD.value HD.char)
|
||||||
HD.value HD.char)
|
|
||||||
)
|
|
||||||
) True
|
|
||||||
where
|
|
||||||
addName :: ProcDescription -> (Text, ProcDescription)
|
addName :: ProcDescription -> (Text, ProcDescription)
|
||||||
addName pd = (pdName pd, pd)
|
addName pd = (pdName pd, pd)
|
||||||
|
|
||||||
@@ -147,12 +145,20 @@ accessibleProcs =
|
|||||||
_ -> Scalar qi -- 'b'ase, 'd'omain, 'e'num, 'r'ange
|
_ -> Scalar qi -- 'b'ase, 'd'omain, 'e'num, 'r'ange
|
||||||
|
|
||||||
parseVolatility :: Char -> ProcVolatility
|
parseVolatility :: Char -> ProcVolatility
|
||||||
parseVolatility 'i' = Immutable
|
parseVolatility v | v == 'i' = Immutable
|
||||||
parseVolatility 's' = Stable
|
| v == 's' = Stable
|
||||||
parseVolatility 'v' = Volatile
|
| otherwise = Volatile -- only 'v' can happen here
|
||||||
parseVolatility _ = Volatile -- should not happen, but be pessimistic
|
|
||||||
|
|
||||||
sql = [q|
|
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.statement (toS sql) (HE.value HE.text) decodeProcs True
|
||||||
|
where
|
||||||
|
sql = procsSqlQuery <> " AND has_function_privilege(p.oid, 'execute')"
|
||||||
|
|
||||||
|
procsSqlQuery :: SqlQuery
|
||||||
|
procsSqlQuery = [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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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" } |]
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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 "/"
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
@@ -46,6 +47,7 @@ main = do
|
|||||||
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,6 +77,10 @@ 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)
|
||||||
@@ -88,4 +94,5 @@ main = do
|
|||||||
, ("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)
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Vendored
+3
@@ -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;
|
||||||
|
|||||||
Vendored
+3
@@ -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
|
||||||
--
|
--
|
||||||
|
|||||||
Reference in New Issue
Block a user