mirror of
https://github.com/neondatabase/neon.git
synced 2026-01-13 08:22:55 +00:00
We used to generate the ID, if the caller didn't specify it. That's bad practice, however, because network is never fully reliable, so it's possible we create a new tenant but the caller doesn't know about it, and because it doesn't know the tenant ID, it has no way of retrying or checking if it succeeded. To discourage that, make it mandatory. The web control plane has not relied on the auto-generation for a long time.
152 lines
5.6 KiB
Python
152 lines
5.6 KiB
Python
from contextlib import closing
|
|
|
|
import pytest
|
|
from fixtures.neon_fixtures import NeonEnvBuilder, PgProtocol
|
|
from fixtures.pageserver.http import PageserverApiException
|
|
from fixtures.types import TenantId, TimelineId
|
|
|
|
|
|
def test_pageserver_auth(neon_env_builder: NeonEnvBuilder):
|
|
neon_env_builder.auth_enabled = True
|
|
env = neon_env_builder.init_start()
|
|
|
|
ps = env.pageserver
|
|
|
|
tenant_token = env.auth_keys.generate_tenant_token(env.initial_tenant)
|
|
tenant_http_client = env.pageserver.http_client(tenant_token)
|
|
invalid_tenant_token = env.auth_keys.generate_tenant_token(TenantId.generate())
|
|
invalid_tenant_http_client = env.pageserver.http_client(invalid_tenant_token)
|
|
|
|
pageserver_token = env.auth_keys.generate_pageserver_token()
|
|
pageserver_http_client = env.pageserver.http_client(pageserver_token)
|
|
|
|
# this does not invoke auth check and only decodes jwt and checks it for validity
|
|
# check both tokens
|
|
ps.safe_psql("set FOO", password=tenant_token)
|
|
ps.safe_psql("set FOO", password=pageserver_token)
|
|
|
|
# tenant can create branches
|
|
tenant_http_client.timeline_create(
|
|
pg_version=env.pg_version,
|
|
tenant_id=env.initial_tenant,
|
|
new_timeline_id=TimelineId.generate(),
|
|
ancestor_timeline_id=env.initial_timeline,
|
|
)
|
|
# console can create branches for tenant
|
|
pageserver_http_client.timeline_create(
|
|
pg_version=env.pg_version,
|
|
tenant_id=env.initial_tenant,
|
|
new_timeline_id=TimelineId.generate(),
|
|
ancestor_timeline_id=env.initial_timeline,
|
|
)
|
|
|
|
# fail to create branch using token with different tenant_id
|
|
with pytest.raises(
|
|
PageserverApiException, match="Forbidden: Tenant id mismatch. Permission denied"
|
|
):
|
|
invalid_tenant_http_client.timeline_create(
|
|
pg_version=env.pg_version,
|
|
tenant_id=env.initial_tenant,
|
|
new_timeline_id=TimelineId.generate(),
|
|
ancestor_timeline_id=env.initial_timeline,
|
|
)
|
|
|
|
# create tenant using management token
|
|
pageserver_http_client.tenant_create(TenantId.generate())
|
|
|
|
# fail to create tenant using tenant token
|
|
with pytest.raises(
|
|
PageserverApiException,
|
|
match="Forbidden: Attempt to access management api with tenant scope. Permission denied",
|
|
):
|
|
tenant_http_client.tenant_create(TenantId.generate())
|
|
|
|
|
|
def test_compute_auth_to_pageserver(neon_env_builder: NeonEnvBuilder):
|
|
neon_env_builder.auth_enabled = True
|
|
neon_env_builder.num_safekeepers = 3
|
|
env = neon_env_builder.init_start()
|
|
|
|
branch = "test_compute_auth_to_pageserver"
|
|
env.neon_cli.create_branch(branch)
|
|
endpoint = env.endpoints.create_start(branch)
|
|
|
|
with closing(endpoint.connect()) as conn:
|
|
with conn.cursor() as cur:
|
|
# we rely upon autocommit after each statement
|
|
# as waiting for acceptors happens there
|
|
cur.execute("CREATE TABLE t(key int primary key, value text)")
|
|
cur.execute("INSERT INTO t SELECT generate_series(1,100000), 'payload'")
|
|
cur.execute("SELECT sum(key) FROM t")
|
|
assert cur.fetchone() == (5000050000,)
|
|
|
|
|
|
@pytest.mark.parametrize("auth_enabled", [False, True])
|
|
def test_auth_failures(neon_env_builder: NeonEnvBuilder, auth_enabled: bool):
|
|
neon_env_builder.auth_enabled = auth_enabled
|
|
env = neon_env_builder.init_start()
|
|
|
|
branch = f"test_auth_failures_auth_enabled_{auth_enabled}"
|
|
timeline_id = env.neon_cli.create_branch(branch)
|
|
env.endpoints.create_start(branch)
|
|
|
|
tenant_token = env.auth_keys.generate_tenant_token(env.initial_tenant)
|
|
invalid_tenant_token = env.auth_keys.generate_tenant_token(TenantId.generate())
|
|
pageserver_token = env.auth_keys.generate_pageserver_token()
|
|
safekeeper_token = env.auth_keys.generate_safekeeper_token()
|
|
|
|
def check_connection(
|
|
pg_protocol: PgProtocol, command: str, expect_success: bool, **conn_kwargs
|
|
):
|
|
def op():
|
|
with closing(pg_protocol.connect(**conn_kwargs)) as conn:
|
|
with conn.cursor() as cur:
|
|
cur.execute(command)
|
|
|
|
if expect_success:
|
|
op()
|
|
else:
|
|
with pytest.raises(Exception):
|
|
op()
|
|
|
|
def check_pageserver(expect_success: bool, **conn_kwargs):
|
|
check_connection(
|
|
env.pageserver,
|
|
f"get_last_record_rlsn {env.initial_tenant} {timeline_id}",
|
|
expect_success,
|
|
**conn_kwargs,
|
|
)
|
|
|
|
check_pageserver(not auth_enabled)
|
|
if auth_enabled:
|
|
check_pageserver(True, password=tenant_token)
|
|
|
|
env.pageserver.allowed_errors.append(".*Tenant id mismatch. Permission denied.*")
|
|
check_pageserver(False, password=invalid_tenant_token)
|
|
|
|
check_pageserver(True, password=pageserver_token)
|
|
|
|
env.pageserver.allowed_errors.append(
|
|
".*SafekeeperData scope makes no sense for Pageserver.*"
|
|
)
|
|
check_pageserver(False, password=safekeeper_token)
|
|
|
|
def check_safekeeper(expect_success: bool, **conn_kwargs):
|
|
check_connection(
|
|
PgProtocol(
|
|
host="localhost",
|
|
port=env.safekeepers[0].port.pg,
|
|
options=f"ztenantid={env.initial_tenant} ztimelineid={timeline_id}",
|
|
),
|
|
"IDENTIFY_SYSTEM",
|
|
expect_success,
|
|
**conn_kwargs,
|
|
)
|
|
|
|
check_safekeeper(not auth_enabled)
|
|
if auth_enabled:
|
|
check_safekeeper(True, password=tenant_token)
|
|
check_safekeeper(False, password=invalid_tenant_token)
|
|
check_safekeeper(False, password=pageserver_token)
|
|
check_safekeeper(True, password=safekeeper_token)
|