feat: add openapi-mode config (#1881)
* openapi-mode="follow-acl"(default): follows access control for the JWT role. * openapi-mode="ignore-acl": ignores access control for the JWT role. * openapi-mode="disabled": disables OpenAPI output, the root endpoint replies with 404 Not Found.
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
module Feature.DisabledOpenApiSpec where
|
||||
|
||||
import Network.HTTP.Types
|
||||
import Network.Wai (Application)
|
||||
|
||||
import Test.Hspec hiding (pendingWith)
|
||||
import Test.Hspec.Wai
|
||||
|
||||
import Protolude
|
||||
|
||||
spec :: SpecWith ((), Application)
|
||||
spec =
|
||||
describe "Disabled OpenApi" $ do
|
||||
it "does not accept application/openapi+json and responds with 415" $
|
||||
request methodGet "/"
|
||||
[("Accept","application/openapi+json")] "" `shouldRespondWith` 415
|
||||
|
||||
it "accepts application/json and responds with 404" $
|
||||
request methodGet "/"
|
||||
[("Accept","application/json")] "" `shouldRespondWith` 404
|
||||
@@ -0,0 +1,43 @@
|
||||
module Feature.IgnoreAclOpenApiSpec where
|
||||
|
||||
import Control.Lens ((^?))
|
||||
|
||||
import Data.Aeson.Lens
|
||||
import Data.Aeson.QQ
|
||||
|
||||
import Network.HTTP.Types
|
||||
import Network.Wai (Application)
|
||||
import Network.Wai.Test (SResponse (..))
|
||||
|
||||
import Test.Hspec hiding (pendingWith)
|
||||
import Test.Hspec.Wai
|
||||
|
||||
import Protolude hiding (get)
|
||||
import SpecHelper
|
||||
|
||||
spec :: SpecWith ((), Application)
|
||||
spec = describe "OpenAPI Ignore ACL" $ do
|
||||
it "root path returns a valid openapi spec" $ do
|
||||
validateOpenApiResponse [("Accept", "application/openapi+json")]
|
||||
request methodHead "/" (acceptHdrs "application/openapi+json") ""
|
||||
`shouldRespondWith` "" { matchStatus = 200 }
|
||||
|
||||
describe "table" $ do
|
||||
|
||||
it "includes privileged table even if user does not have permission" $ do
|
||||
r <- simpleBody <$> get "/"
|
||||
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 privileged function even if user does not have permission" $ do
|
||||
r <- simpleBody <$> get "/"
|
||||
let funcTag = r ^? key "paths" . key "/rpc/privileged_hello"
|
||||
. key "post" . key "tags"
|
||||
. nth 0
|
||||
|
||||
liftIO $ funcTag `shouldBe` Just [aesonQQ|"(rpc) privileged_hello"|]
|
||||
@@ -28,9 +28,11 @@ import qualified Feature.BinaryJwtSecretSpec
|
||||
import qualified Feature.ConcurrentSpec
|
||||
import qualified Feature.CorsSpec
|
||||
import qualified Feature.DeleteSpec
|
||||
import qualified Feature.DisabledOpenApiSpec
|
||||
import qualified Feature.EmbedDisambiguationSpec
|
||||
import qualified Feature.ExtraSearchPathSpec
|
||||
import qualified Feature.HtmlRawOutputSpec
|
||||
import qualified Feature.IgnoreAclOpenApiSpec
|
||||
import qualified Feature.InsertSpec
|
||||
import qualified Feature.JsonOperatorSpec
|
||||
import qualified Feature.MultipleSchemaSpec
|
||||
@@ -92,6 +94,8 @@ main = do
|
||||
|
||||
let withApp = app testCfg
|
||||
maxRowsApp = app testMaxRowsCfg
|
||||
disabledOpenApi = app testDisabledOpenApiCfg
|
||||
ignoreAclOpenApi = app testIgnoreAclOpenApiCfg
|
||||
proxyApp = app testProxyCfg
|
||||
noJwtApp = app testCfgNoJWT
|
||||
binaryJwtApp = app testCfgBinaryJWT
|
||||
@@ -152,6 +156,14 @@ main = do
|
||||
parallel $ before unicodeApp $
|
||||
describe "Feature.UnicodeSpec" Feature.UnicodeSpec.spec
|
||||
|
||||
-- this test runs with openapi-mode set to disabled
|
||||
parallel $ before disabledOpenApi $
|
||||
describe "Feature.DisabledOpenApiSpec" Feature.DisabledOpenApiSpec.spec
|
||||
|
||||
-- this test runs with openapi-mode set to ignore-acl
|
||||
parallel $ before ignoreAclOpenApi $
|
||||
describe "Feature.IgnoreAclOpenApiSpec" Feature.IgnoreAclOpenApiSpec.spec
|
||||
|
||||
-- this test runs with a proxy
|
||||
parallel $ before proxyApp $
|
||||
describe "Feature.ProxySpec" Feature.ProxySpec.spec
|
||||
|
||||
+10
-1
@@ -24,7 +24,9 @@ import Text.Heredoc
|
||||
|
||||
import PostgREST.Config (AppConfig (..),
|
||||
JSPathExp (..),
|
||||
LogLevel (..), parseSecret)
|
||||
LogLevel (..),
|
||||
OpenAPIMode (..),
|
||||
parseSecret)
|
||||
import PostgREST.DbStructure.Identifiers (QualifiedIdentifier (..))
|
||||
import Protolude hiding (toS)
|
||||
import Protolude.Conv (toS)
|
||||
@@ -94,6 +96,7 @@ _baseCfg = let secret = Just $ encodeUtf8 "reallyreallyreallyreallyverysafe" in
|
||||
, configJwtSecret = secret
|
||||
, configJwtSecretIsBase64 = False
|
||||
, configLogLevel = LogCrit
|
||||
, configOpenApiMode = OAFollowACL
|
||||
, configOpenApiServerProxyUri = Nothing
|
||||
, configRawMediaTypes = []
|
||||
, configServerHost = "localhost"
|
||||
@@ -122,6 +125,12 @@ testUnicodeCfg testDbConn = (testCfg testDbConn) { configDbSchemas = fromList ["
|
||||
testMaxRowsCfg :: Text -> AppConfig
|
||||
testMaxRowsCfg testDbConn = (testCfg testDbConn) { configDbMaxRows = Just 2 }
|
||||
|
||||
testDisabledOpenApiCfg :: Text -> AppConfig
|
||||
testDisabledOpenApiCfg testDbConn = (testCfg testDbConn) { configOpenApiMode = OADisabled }
|
||||
|
||||
testIgnoreAclOpenApiCfg :: Text -> AppConfig
|
||||
testIgnoreAclOpenApiCfg testDbConn = (testCfg testDbConn) { configOpenApiMode = OAIgnoreACL }
|
||||
|
||||
testProxyCfg :: Text -> AppConfig
|
||||
testProxyCfg testDbConn = (testCfg testDbConn) { configOpenApiServerProxyUri = Just "https://postgrest.com/openapi.json" }
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ jwt-role-claim-key = ".\"aliased\""
|
||||
jwt-secret = ""
|
||||
jwt-secret-is-base64 = true
|
||||
log-level = "error"
|
||||
openapi-mode = "follow-acl"
|
||||
openapi-server-proxy-uri = ""
|
||||
raw-media-types = ""
|
||||
server-host = "!4"
|
||||
|
||||
@@ -17,6 +17,7 @@ jwt-role-claim-key = ".\"role\""
|
||||
jwt-secret = ""
|
||||
jwt-secret-is-base64 = true
|
||||
log-level = "error"
|
||||
openapi-mode = "follow-acl"
|
||||
openapi-server-proxy-uri = ""
|
||||
raw-media-types = ""
|
||||
server-host = "!4"
|
||||
|
||||
@@ -17,6 +17,7 @@ jwt-role-claim-key = ".\"role\""
|
||||
jwt-secret = ""
|
||||
jwt-secret-is-base64 = true
|
||||
log-level = "error"
|
||||
openapi-mode = "follow-acl"
|
||||
openapi-server-proxy-uri = ""
|
||||
raw-media-types = ""
|
||||
server-host = "!4"
|
||||
|
||||
@@ -17,6 +17,7 @@ jwt-role-claim-key = ".\"role\""
|
||||
jwt-secret = ""
|
||||
jwt-secret-is-base64 = false
|
||||
log-level = "error"
|
||||
openapi-mode = "follow-acl"
|
||||
openapi-server-proxy-uri = ""
|
||||
raw-media-types = ""
|
||||
server-host = "!4"
|
||||
|
||||
@@ -17,6 +17,7 @@ jwt-role-claim-key = ".\"other\".\"role\""
|
||||
jwt-secret = "ODERREALLYREALLYREALLYREALLYVERYSAFE"
|
||||
jwt-secret-is-base64 = true
|
||||
log-level = "info"
|
||||
openapi-mode = "ignore-acl"
|
||||
openapi-server-proxy-uri = "https://otherexample.org/api"
|
||||
raw-media-types = "application/vnd.pgrst.other-db-config"
|
||||
server-host = "0.0.0.0"
|
||||
|
||||
@@ -17,6 +17,7 @@ jwt-role-claim-key = ".\"a\".\"role\""
|
||||
jwt-secret = "OVERRIDEREALLYREALLYREALLYREALLYVERYSAFE"
|
||||
jwt-secret-is-base64 = true
|
||||
log-level = "info"
|
||||
openapi-mode = "ignore-acl"
|
||||
openapi-server-proxy-uri = "https://example.org/api"
|
||||
raw-media-types = "application/vnd.pgrst.db-config"
|
||||
server-host = "0.0.0.0"
|
||||
|
||||
@@ -17,6 +17,7 @@ jwt-role-claim-key = ".\"user\"[0].\"real-role\""
|
||||
jwt-secret = "c2VjdXJpdHl0aHJvdWdob2JzY3VyaXR5"
|
||||
jwt-secret-is-base64 = true
|
||||
log-level = "info"
|
||||
openapi-mode = "ignore-acl"
|
||||
openapi-server-proxy-uri = "https://postgrest.org"
|
||||
raw-media-types = "application/vnd.pgrst.config"
|
||||
server-host = "0.0.0.0"
|
||||
|
||||
@@ -17,6 +17,7 @@ jwt-role-claim-key = ".\"role\""
|
||||
jwt-secret = ""
|
||||
jwt-secret-is-base64 = false
|
||||
log-level = "error"
|
||||
openapi-mode = "follow-acl"
|
||||
openapi-server-proxy-uri = ""
|
||||
raw-media-types = ""
|
||||
server-host = "!4"
|
||||
|
||||
@@ -19,6 +19,7 @@ PGRST_JWT_ROLE_CLAIM_KEY: '.user[0]."real-role"'
|
||||
PGRST_JWT_SECRET: c2VjdXJpdHl0aHJvdWdob2JzY3VyaXR5
|
||||
PGRST_JWT_SECRET_IS_BASE64: true
|
||||
PGRST_LOG_LEVEL: info
|
||||
PGRST_OPENAPI_MODE: 'ignore-acl'
|
||||
PGRST_OPENAPI_SERVER_PROXY_URI: 'https://postgrest.org'
|
||||
PGRST_RAW_MEDIA_TYPES: application/vnd.pgrst.config
|
||||
PGRST_SERVER_HOST: 0.0.0.0
|
||||
|
||||
@@ -17,6 +17,7 @@ jwt-role-claim-key = ".user[0].\"real-role\""
|
||||
jwt-secret = "c2VjdXJpdHl0aHJvdWdob2JzY3VyaXR5"
|
||||
jwt-secret-is-base64 = true
|
||||
log-level = "info"
|
||||
openapi-mode = "ignore-acl"
|
||||
openapi-server-proxy-uri = "https://postgrest.org"
|
||||
raw-media-types = "application/vnd.pgrst.config"
|
||||
server-host = "0.0.0.0"
|
||||
|
||||
Reference in New Issue
Block a user