feat: Make db-anon-role optional
Without db-anon-role, PostgREST will block any anonymous access without hitting the database. Resolves #1689, Ref #1823
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
db-anon-role = "required"
|
||||
|
||||
db-schema = "provided_through_alias"
|
||||
max-rows = 1000
|
||||
pre-request = "check_alias"
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
db-anon-role = "required"
|
||||
|
||||
db-channel-enabled = "1"
|
||||
db-prepared-statements = "0"
|
||||
jwt-secret-is-base64 = "2"
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
db-anon-role = "required"
|
||||
|
||||
db-channel-enabled = "true"
|
||||
db-prepared-statements = "FALSE"
|
||||
jwt-secret-is-base64 = "\"true\""
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
db-anon-role = "required"
|
||||
# Not the default, but only works with PG* variables, which are not set
|
||||
db-config = false
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
db-anon-role = "required"
|
||||
db-anon-role = ""
|
||||
db-channel = "pgrst"
|
||||
db-channel-enabled = true
|
||||
db-extra-search-path = "public"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
db-anon-role = "required"
|
||||
db-anon-role = ""
|
||||
db-channel = "pgrst"
|
||||
db-channel-enabled = true
|
||||
db-extra-search-path = "public"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
db-anon-role = "required"
|
||||
db-anon-role = ""
|
||||
db-channel = "pgrst"
|
||||
db-channel-enabled = true
|
||||
db-extra-search-path = "public"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
db-anon-role = "required"
|
||||
db-anon-role = ""
|
||||
db-channel = "pgrst"
|
||||
db-channel-enabled = true
|
||||
db-extra-search-path = "public"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
db-anon-role = "postgrest_test_anonymous"
|
||||
db-anon-role = "other"
|
||||
db-channel = "postgrest"
|
||||
db-channel-enabled = false
|
||||
db-extra-search-path = "public,extensions,other"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
db-anon-role = "postgrest_test_anonymous"
|
||||
db-anon-role = "anonymous"
|
||||
db-channel = "postgrest"
|
||||
db-channel-enabled = false
|
||||
db-extra-search-path = "public,extensions,private"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
db-anon-role = "required"
|
||||
db-anon-role = ""
|
||||
db-channel = "pgrst"
|
||||
db-channel-enabled = true
|
||||
db-extra-search-path = "public"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# tests how config options fall back with invalid types
|
||||
db-anon-role = "required"
|
||||
|
||||
# expects string
|
||||
app.settings.test = false
|
||||
|
||||
@@ -7,6 +7,7 @@ ALTER ROLE db_config_authenticator SET pgrst.raw_media_types = 'application/vnd.
|
||||
ALTER ROLE db_config_authenticator SET pgrst.jwt_secret = 'REALLY=REALLY=REALLY=REALLY=VERY=SAFE';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.jwt_secret_is_base64 = 'false';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.jwt_role_claim_key = '."a"."role"';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.db_anon_role = 'anonymous';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.db_tx_end = 'commit-allow-override';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.db_schemas = 'test, tenant1, tenant2';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.db_root_spec = 'root';
|
||||
@@ -29,7 +30,6 @@ ALTER ROLE db_config_authenticator SET pgrst.server_port = 'ignored';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.server_unix_socket = 'ignored';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.server_unix_socket_mode = 'ignored';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.log_level = 'ignored';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.db_anon_role = 'ignored';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.db_uri = 'postgresql://ignored';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.db_channel_enabled = 'ignored';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.db_channel = 'ignored';
|
||||
@@ -45,6 +45,7 @@ ALTER ROLE other_authenticator SET pgrst.raw_media_types = 'application/vnd.pgrs
|
||||
ALTER ROLE other_authenticator SET pgrst.jwt_secret = 'ODERREALLYREALLYREALLYREALLYVERYSAFE';
|
||||
ALTER ROLE other_authenticator SET pgrst.jwt_secret_is_base64 = 'true';
|
||||
ALTER ROLE other_authenticator SET pgrst.jwt_role_claim_key = '."other"."role"';
|
||||
ALTER ROLE other_authenticator SET pgrst.db_anon_role = 'other';
|
||||
ALTER ROLE other_authenticator SET pgrst.db_tx_end = 'rollback-allow-override';
|
||||
ALTER ROLE other_authenticator SET pgrst.db_schemas = 'test, other_tenant1, other_tenant2';
|
||||
ALTER ROLE other_authenticator SET pgrst.db_root_spec = 'other_root';
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
\ir db_config.sql
|
||||
|
||||
CREATE ROLE postgrest_test_anonymous;
|
||||
ALTER ROLE :USER SET pgrst.db_anon_role = 'postgrest_test_anonymous';
|
||||
|
||||
CREATE ROLE postgrest_test_author;
|
||||
|
||||
GRANT postgrest_test_anonymous, postgrest_test_author TO :USER;
|
||||
|
||||
@@ -23,10 +23,6 @@ cli:
|
||||
- name: invalid config file
|
||||
expect: error
|
||||
args: ['test/io-tests/configs/invalid.yaml']
|
||||
# failures: required config options
|
||||
- name: missing db-anon-role
|
||||
expect: error
|
||||
env:
|
||||
# failures: wrong config values
|
||||
- name: invalid server-unix-socket-mode not octal
|
||||
expect: error
|
||||
|
||||
+7
-4
@@ -92,8 +92,7 @@ def defaultenv():
|
||||
"PGDATABASE": os.environ["PGDATABASE"],
|
||||
"PGHOST": os.environ["PGHOST"],
|
||||
"PGUSER": os.environ["PGUSER"],
|
||||
"PGRST_DB_ANON_ROLE": os.environ["PGRST_DB_ANON_ROLE"],
|
||||
"PGRST_DB_CONFIG": "false",
|
||||
"PGRST_DB_CONFIG": "true",
|
||||
"PGRST_LOG_LEVEL": "info",
|
||||
}
|
||||
|
||||
@@ -384,6 +383,7 @@ def test_read_secret_from_file(secretpath, defaultenv):
|
||||
|
||||
with run(stdin=secret, env=env) as postgrest:
|
||||
response = postgrest.session.get("/authors_only", headers=headers)
|
||||
print(response.text)
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
@@ -600,6 +600,8 @@ def test_jwt_secret_external_file_reload(tmp_path, defaultenv):
|
||||
**defaultenv,
|
||||
"PGRST_JWT_SECRET": f"@{external_secret_file}",
|
||||
"PGRST_DB_CHANNEL_ENABLED": "true",
|
||||
"PGRST_DB_CONFIG": "false",
|
||||
"PGRST_DB_ANON_ROLE": "postgrest_test_anonymous", # required for NOTIFY
|
||||
}
|
||||
|
||||
with run(env=env) as postgrest:
|
||||
@@ -609,7 +611,7 @@ def test_jwt_secret_external_file_reload(tmp_path, defaultenv):
|
||||
# change external file
|
||||
external_secret_file.write_text(SECRET)
|
||||
|
||||
# SIGUSR1 doesn't reload external files
|
||||
# SIGUSR1 doesn't reload external files, at least when db-config=false
|
||||
postgrest.process.send_signal(signal.SIGUSR1)
|
||||
time.sleep(0.1)
|
||||
|
||||
@@ -627,7 +629,8 @@ def test_jwt_secret_external_file_reload(tmp_path, defaultenv):
|
||||
external_secret_file.write_text("invalid" * 5)
|
||||
|
||||
# reload config and external file with NOTIFY
|
||||
postgrest.session.post("/rpc/reload_pgrst_config")
|
||||
response = postgrest.session.post("/rpc/reload_pgrst_config")
|
||||
assert response.status_code == 200
|
||||
time.sleep(0.1)
|
||||
|
||||
response = postgrest.session.get("/authors_only", headers=headers)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
module Feature.NoAnonSpec where
|
||||
|
||||
import Network.Wai (Application)
|
||||
|
||||
import Network.HTTP.Types
|
||||
import Test.Hspec
|
||||
import Test.Hspec.Wai
|
||||
import Test.Hspec.Wai.JSON
|
||||
|
||||
import Protolude hiding (get)
|
||||
import SpecHelper
|
||||
|
||||
spec :: SpecWith ((), Application)
|
||||
spec = describe "server started without anonymous role" $ do
|
||||
it "behaves normally on attempted auth" $ do
|
||||
-- token body: { "role": "postgrest_test_author" }
|
||||
let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA"
|
||||
request methodGet "/authors_only"
|
||||
[auth]
|
||||
""
|
||||
`shouldRespondWith`
|
||||
200
|
||||
|
||||
it "responds with error when user does not attempt auth" $
|
||||
get "/items"
|
||||
`shouldRespondWith`
|
||||
[json|{"message":"Anonymous access is disabled"}|]
|
||||
{ matchStatus = 401
|
||||
, matchHeaders = ["WWW-Authenticate" <:> "Bearer"]
|
||||
}
|
||||
@@ -37,6 +37,7 @@ import qualified Feature.InsertSpec
|
||||
import qualified Feature.JsonOperatorSpec
|
||||
import qualified Feature.LegacyGucsSpec
|
||||
import qualified Feature.MultipleSchemaSpec
|
||||
import qualified Feature.NoAnonSpec
|
||||
import qualified Feature.NoJwtSpec
|
||||
import qualified Feature.NonexistentSchemaSpec
|
||||
import qualified Feature.OpenApiSpec
|
||||
@@ -96,6 +97,7 @@ main = do
|
||||
maxRowsApp = app testMaxRowsCfg
|
||||
disabledOpenApi = app testDisabledOpenApiCfg
|
||||
proxyApp = app testProxyCfg
|
||||
noAnonApp = app testCfgNoAnon
|
||||
noJwtApp = app testCfgNoJWT
|
||||
binaryJwtApp = app testCfgBinaryJWT
|
||||
audJwtApp = app testCfgAudienceJWT
|
||||
@@ -170,6 +172,10 @@ main = do
|
||||
parallel $ before proxyApp $
|
||||
describe "Feature.ProxySpec" Feature.ProxySpec.spec
|
||||
|
||||
-- this test runs without an anonymous role
|
||||
parallel $ before noAnonApp $
|
||||
describe "Feature.NoAnonSpec" Feature.NoAnonSpec.spec
|
||||
|
||||
-- this test runs without a JWT secret
|
||||
parallel $ before noJwtApp $
|
||||
describe "Feature.NoJwtSpec" Feature.NoJwtSpec.spec
|
||||
|
||||
@@ -76,7 +76,7 @@ baseCfg :: AppConfig
|
||||
baseCfg = let secret = Just $ encodeUtf8 "reallyreallyreallyreallyverysafe" in
|
||||
AppConfig {
|
||||
configAppSettings = [ ("app.settings.app_host", "localhost") , ("app.settings.external_api_secret", "0123456789abcdef") ]
|
||||
, configDbAnonRole = "postgrest_test_anonymous"
|
||||
, configDbAnonRole = Just "postgrest_test_anonymous"
|
||||
, configDbChannel = mempty
|
||||
, configDbChannelEnabled = True
|
||||
, configDbExtraSearchPath = []
|
||||
@@ -118,6 +118,9 @@ testCfgDisallowRollback = baseCfg { configDbTxAllowOverride = False, configDbTxR
|
||||
testCfgForceRollback :: AppConfig
|
||||
testCfgForceRollback = baseCfg { configDbTxAllowOverride = False, configDbTxRollbackAll = True }
|
||||
|
||||
testCfgNoAnon :: AppConfig
|
||||
testCfgNoAnon = baseCfg { configDbAnonRole = Nothing }
|
||||
|
||||
testCfgNoJWT :: AppConfig
|
||||
testCfgNoJWT = baseCfg { configJwtSecret = Nothing, configJWKS = Nothing }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user