-
-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(admin-auth): Add TTL cache for admin roles #4358
Changes from all commits
51f8261
6c9461b
11c2c0f
ece3330
d0c1aab
375a8d1
f78fa66
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,5 +55,6 @@ | |
(6, "config"), | ||
(7, "dlq"), | ||
(8, "optimize"), | ||
(9, "admin_auth"), | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ | |
from snuba.migrations.policies import MigrationPolicy | ||
from snuba.migrations.runner import MigrationKey, Runner | ||
from snuba.migrations.status import Status | ||
from snuba.redis import RedisClientKey, get_redis_client | ||
|
||
|
||
@pytest.fixture | ||
|
@@ -31,6 +32,7 @@ def admin_api() -> FlaskClient: | |
return application.test_client() | ||
|
||
|
||
@pytest.mark.redis_db | ||
@pytest.mark.clickhouse_db | ||
def test_migration_groups(admin_api: FlaskClient) -> None: | ||
runner = Runner() | ||
|
@@ -76,6 +78,7 @@ def get_migration_ids( | |
] | ||
|
||
|
||
@pytest.mark.redis_db | ||
@pytest.mark.clickhouse_db | ||
def test_list_migration_status(admin_api: FlaskClient) -> None: | ||
with patch( | ||
|
@@ -137,6 +140,7 @@ def sort_by_migration_id(migration: Any) -> Any: | |
assert sorted_response == sorted_expected_json | ||
|
||
|
||
@pytest.mark.redis_db | ||
@pytest.mark.clickhouse_db | ||
@pytest.mark.parametrize("action", ["run", "reverse"]) | ||
def test_run_reverse_migrations(admin_api: FlaskClient, action: str) -> None: | ||
|
@@ -281,6 +285,7 @@ def print_something(*args: Any, **kwargs: Any) -> None: | |
assert mock_run_migration.call_count == 1 | ||
|
||
|
||
@pytest.mark.redis_db | ||
def test_get_iam_roles(caplog: Any) -> None: | ||
system_role = generate_migration_test_role("system", "all") | ||
tool_role = generate_tool_test_role("snql-to-sql") | ||
|
@@ -359,6 +364,8 @@ def test_get_iam_roles(caplog: Any) -> None: | |
tool_role, | ||
] | ||
|
||
iam_file.close() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test is very large and should be split up into multiple tests. You've also tested the happy path but the unhappy path is not tested. Example: what if the redis connection fails, does that mean nobody can access admin? Also, is |
||
|
||
with patch( | ||
"snuba.admin.auth.settings.ADMIN_IAM_POLICY_FILE", "file_not_exists.json" | ||
): | ||
|
@@ -369,3 +376,122 @@ def test_get_iam_roles(caplog: Any) -> None: | |
assert "IAM policy file not found file_not_exists.json" in str( | ||
log.calls | ||
) | ||
|
||
|
||
@pytest.mark.redis_db | ||
def test_get_iam_roles_cache() -> None: | ||
system_role = generate_migration_test_role("system", "all") | ||
tool_role = generate_tool_test_role("snql-to-sql") | ||
with patch( | ||
"snuba.admin.auth.DEFAULT_ROLES", | ||
[system_role, tool_role], | ||
): | ||
iam_file = tempfile.NamedTemporaryFile() | ||
iam_file.write( | ||
json.dumps( | ||
{ | ||
"bindings": [ | ||
{ | ||
"members": [ | ||
"group:team-sns@sentry.io", | ||
"user:test_user1@sentry.io", | ||
], | ||
"role": "roles/NonBlockingMigrationsExecutor", | ||
}, | ||
{ | ||
"members": [ | ||
"group:team-sns@sentry.io", | ||
"user:test_user1@sentry.io", | ||
"user:test_user2@sentry.io", | ||
], | ||
"role": "roles/TestMigrationsExecutor", | ||
}, | ||
{ | ||
"members": [ | ||
"group:team-sns@sentry.io", | ||
"user:test_user1@sentry.io", | ||
"user:test_user2@sentry.io", | ||
], | ||
"role": "roles/owner", | ||
}, | ||
{ | ||
"members": [ | ||
"group:team-sns@sentry.io", | ||
"user:test_user1@sentry.io", | ||
], | ||
"role": "roles/AllTools", | ||
}, | ||
] | ||
} | ||
).encode("utf-8") | ||
) | ||
|
||
iam_file.flush() | ||
with patch("snuba.admin.auth.settings.ADMIN_IAM_POLICY_FILE", iam_file.name): | ||
|
||
user1 = AdminUser(email="test_user1@sentry.io", id="unknown") | ||
_set_roles(user1) | ||
|
||
assert user1.roles == [ | ||
ROLES["NonBlockingMigrationsExecutor"], | ||
ROLES["TestMigrationsExecutor"], | ||
ROLES["AllTools"], | ||
system_role, | ||
tool_role, | ||
] | ||
|
||
iam_file = tempfile.NamedTemporaryFile() | ||
iam_file.write(json.dumps({"bindings": []}).encode("utf-8")) | ||
iam_file.flush() | ||
|
||
with patch("snuba.admin.auth.settings.ADMIN_IAM_POLICY_FILE", iam_file.name): | ||
_set_roles(user1) | ||
|
||
assert user1.roles == [ | ||
ROLES["NonBlockingMigrationsExecutor"], | ||
ROLES["TestMigrationsExecutor"], | ||
ROLES["AllTools"], | ||
system_role, | ||
tool_role, | ||
] | ||
|
||
redis_client = get_redis_client(RedisClientKey.ADMIN_AUTH) | ||
redis_client.delete(f"roles-{user1.email}") | ||
_set_roles(user1) | ||
|
||
assert user1.roles == [ | ||
system_role, | ||
tool_role, | ||
] | ||
|
||
|
||
@pytest.mark.redis_db | ||
@patch("redis.Redis") | ||
def test_get_iam_roles_cache_fail(mock_redis: Any) -> None: | ||
mock_redis.get.side_effect = Exception("Test exception") | ||
mock_redis.set.side_effect = Exception("Test exception") | ||
system_role = generate_migration_test_role("system", "all") | ||
tool_role = generate_tool_test_role("snql-to-sql") | ||
with patch( | ||
"snuba.admin.auth.DEFAULT_ROLES", | ||
[system_role, tool_role], | ||
): | ||
iam_file = tempfile.NamedTemporaryFile() | ||
iam_file.write(json.dumps({"bindings": []}).encode("utf-8")) | ||
iam_file.flush() | ||
|
||
with patch("snuba.admin.auth.settings.ADMIN_IAM_POLICY_FILE", iam_file.name): | ||
user1 = AdminUser(email="test_user1@sentry.io", id="unknown") | ||
_set_roles(user1) | ||
|
||
assert user1.roles == [ | ||
system_role, | ||
tool_role, | ||
] | ||
|
||
_set_roles(user1) | ||
|
||
assert user1.roles == [ | ||
system_role, | ||
tool_role, | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a PR to add
admin_auth
to the prod settings as well?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not yet, I'll make a PR right now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That will need to go out before this can be merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this stuff is super inconvenient. I wonder if we can make it so that the prod_settings file can override individual keys of the default settings file, and does not have to redefine the entire variable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@untitaker I made a draft PR to do this here.