-
Notifications
You must be signed in to change notification settings - Fork 49
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
Change API to be able to assign roles to namespaces #607
Change API to be able to assign roles to namespaces #607
Conversation
✅ Deploy Preview for kaleidoscopic-dango-0cf31d canceled.
|
This comment was marked as outdated.
This comment was marked as outdated.
2c3ba98
to
9df4c35
Compare
9df4c35
to
6a2f985
Compare
...-server/conda_store_server/alembic/versions/46bdf428642d_change_namespacerolemapping_and_.py
Outdated
Show resolved
Hide resolved
6a2f985
to
e96a709
Compare
...-server/conda_store_server/alembic/versions/46bdf428642d_change_namespacerolemapping_and_.py
Outdated
Show resolved
Hide resolved
67c271e
to
0b19764
Compare
552dda1
to
bb3b131
Compare
6fa9c98
to
cee8495
Compare
ed7384c
to
9ac936b
Compare
3f5c744
to
09e6708
Compare
Otherwise, nothing is printed when `role_mappings_version` is set incorrectly. This ``` c.RBACAuthorizationBackend.role_mappings_version = 42 ``` now prints ``` c.RBACAuthorizationBackend.role_mappings_version: invalid role mappings version: 42, expected: (1, 2) ```
Migrating data from v1 to v2 role mappings will be done in a separate PR because it can cause issues during migration, which might require manual intervention. So a standalone function will be provided for those who need it. #681 |
@app.exception_handler(Exception) | ||
async def exception_handler(request, exc): | ||
print(exc) | ||
return await http_exception_handler(request, exc) |
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.
Added this to print an exception to the terminal when an invalid role_mappings_version
value is used. Previously, nothing was printed except "Invalid server error".
for row in raw_role_mappings: | ||
db_role_mappings[row["entity"]].add(row["role"]) | ||
|
||
return db_role_mappings |
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 code used to be in database_role_bindings
. I moved it to the start of the class to use it in _role_mappings_versions
.
@@ -140,36 +141,191 @@ def test_authorization(conda_store, entity_bindings, arn, permissions, authorize | |||
assert authorized == authorization.authorize(entity, arn, permissions) | |||
|
|||
|
|||
def test_end_to_end_auth_flow(conda_store): | |||
@pytest.mark.parametrize("role_mappings_version", [1, 2]) |
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.
Updated this test to match the newly added test_end_to_end_auth_flow_v2
. The v1 test calls HTTP APIs that use the v1 role mappings table. The v2 test does the same for the v2 table. Both tests check that auth works after inserting roles into the corresponding DB table.
response.raise_for_status() | ||
|
||
# Should fail again | ||
assert authorize() is False |
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.
Note that in the v2 table the relationship between namespaces is reversed compared to v1. That's why this performs API requests to other_namespace
and sets other_namespace
to namespace
in post
. This is intended.
The v1 table says: "I give myself access to this namespace"
The v2 table says: "I allow this namespace access"
Permissions.SETTING_READ, | ||
Permissions.SETTING_UPDATE, | ||
}, | ||
) |
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.
Nit: the above block should be indented.
I've reviewed the code, not planning to make any changes. Ready for review. |
For reviewers: this is a huge PR, I suggest you start with |
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.
The comments in this review are mostly stylistic and are not blocking for approval.
...-server/conda_store_server/alembic/versions/46bdf428642d_change_namespacerolemapping_and_.py
Outdated
Show resolved
Hide resolved
conda-store-server/conda_store_server/alembic/versions/771180018e1b_add_v2_role_mappings.py
Show resolved
Hide resolved
if namespace is None: | ||
raise ValueError(f"Namespace='{name}' not found") | ||
|
||
nrm = aliased(orm.NamespaceRoleMappingV2) |
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.
Can we please have more descriptive variable names here? Readability is part of maintainability.
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.
In general, I agree. But not here. I deliberately use short names so that queries don't get split up over multiple lines by the linter, which would be hard to read. All these DB functions are very small, so there's no concern of this clashing with something else causing confusion. I couldn't think of a short name for NamespaceRoleMapping other than using the first letters.
namespace = relationship(Namespace, foreign_keys=[namespace_id]) | ||
|
||
# ... for other namespace | ||
other_namespace_id = Column(Integer, ForeignKey("namespace.id"), nullable=False) |
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.
As mentioned above, these names don't make it clear which namespace has permissions to which.
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.
Addressed in another comment above.
One thought that occurred to me is that introducing the version to our routes actually is breaking if anyone has previously had naked routes. I think adding a router for naked routes that redirects to v1 of the route and documenting this would handle that issue. This could be a seperate issue/pr if that is simpler, but without it this will be a breaking change. |
Did you mean to reply to the router_ui thread? This PR doesn't make changes to existing routes. |
No, i meant in regards to the api. I thought that we had discussed making the default v1, but if this doesn't touch existing routes then you can disregard this comment. |
Yeah, no action is needed here. I'll mention this for whoever is reading these later. The API I'm using is already versioned: router_api = APIRouter(
tags=["api"],
prefix="/api/v1",
) |
@@ -71,6 +71,108 @@ def test_namespace_role_mapping(db): | |||
NamespaceRoleMapping(namespace=namespace, namespace_id=namespace.id, entity="*/*") | |||
|
|||
|
|||
def test_namespace_role_mapping_v2(db): |
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.
Like the detailed tests on this feature
}, | ||
primary_namespace=namespace, | ||
# No default roles | ||
role_bindings={}, | ||
) | ||
) | ||
|
||
token_model = authentication.authenticate(token) | ||
|
||
authorization = RBACAuthorizationBackend( |
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.
Pulling all this auth stuff that is setup for the auth tests into a helper function would help readability of these tests imo. This won't block approval, but would help maintainability I think.
other_namespace_name3 = "pytest-other-namespace3" | ||
|
||
# Starts with no namespaces | ||
assert len(api.list_namespaces(db).all()) == 0 |
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.
It might be simpler to just clear out the namespaces like you do in the auth tests than to assert here.
assert len(roles) == 1 | ||
roles = api.get_other_namespace_roles(db, namespace_name) | ||
db.commit() | ||
assert len(roles) == 0 |
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.
In general, relying on having a pure db and checking lengths has led to flaky tests for me in the past, especially if you ever try to run tests in parallel. I always prefer to check for concrete records than for lengths.
Fixes #491.