-
Notifications
You must be signed in to change notification settings - Fork 1
feat(breadbox): Added checks for ensuring that group entries always match full email or email domain. #315
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
base: master
Are you sure you want to change the base?
Changes from all commits
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 |
|---|---|---|
|
|
@@ -23,6 +23,14 @@ def user_has_access_to_group( | |
| if user in settings.admin_users: | ||
| return True | ||
|
|
||
| # adding a special case for special groups, to avoid running into the assertion below | ||
| # that all email entries must start with a "@" if group_entry.exact_match == False | ||
| # We should clean up that entry because I think it'd be better if it doesn't exist -- but for now, let's avoid the data migration. | ||
| # This means that only the public group which is possible to be viewable by _all_ users. Similarly all users need to be | ||
| # able to access the transient group. | ||
| if group.id in [PUBLIC_GROUP_ID, TRANSIENT_GROUP_ID] and write_access is False: | ||
| return True | ||
|
Member
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. You make a good point about the public and transient groups being a special case. As a heads up, the updates you made to the
I'll also just mention, when we do clean up those entries, the
Contributor
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. I don't think that the current updates made in the api layer for I agree that we should remove those special entries for transient and public groups since they don't seem necessary and we'd no longer need to consider a case where the email could be |
||
|
|
||
| # Check if user is in group's group entries | ||
| if write_access: | ||
| group_entries = [ | ||
|
|
@@ -37,10 +45,21 @@ def user_has_access_to_group( | |
| group_entries = group.group_entries | ||
|
|
||
| for group_entry in group_entries: | ||
| if group_entry.exact_match and group_entry.email == user: | ||
| return True | ||
| elif not group_entry.exact_match and user.endswith(group_entry.email): | ||
| return True | ||
| if group_entry.exact_match: | ||
| if group_entry.email == user: | ||
| return True | ||
| else: | ||
| # I'm adding this check because I'm concerned that it is too easy to accidentally add a group entry which isn't specific enough. | ||
| # For example, if `group_entry.email == ""` then all users would match that. Or similarly, if someone | ||
| # added an entry with `group_entry.email == "apple.com"` then the email address `[email protected]` would | ||
| # also return true. To avoid these potential problems require that the suffix always starts with "@". | ||
| # This is checked when adding an entry, but assert that's the case just as a last sanity test | ||
| assert group_entry.email.startswith( | ||
| "@" | ||
| ), f"If group_entry.exact_match is False, we require the email address field contain the full domain name, starting with @ but the value was {repr(group_entry.email)}" | ||
| if user.endswith(group_entry.email): | ||
| return True | ||
|
|
||
| return False | ||
|
|
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -166,7 +166,7 @@ def test_add_group_entry(self, client: TestClient, settings: Settings, minimal_d | |
| json={ | ||
| "email": "[email protected]", | ||
| "access_type": "read", | ||
| "exact_match": False, | ||
| "exact_match": True, | ||
| }, | ||
| headers=admin_headers, | ||
| ) | ||
|
|
@@ -176,12 +176,28 @@ def test_add_group_entry(self, client: TestClient, settings: Settings, minimal_d | |
| json={ | ||
| "email": "[email protected]", | ||
| "access_type": "read", | ||
| "exact_match": False, | ||
| "exact_match": True, | ||
| }, | ||
| headers=admin_headers, | ||
| ) | ||
| assert_status_ok(post_entry_1_copy) | ||
|
|
||
| # this should fail because the email doesn't start with '@' | ||
| post_suffix_match_entry = client.post( | ||
| f"/groups/{owner_group['id']}/addAccess", | ||
| json={"email": "email.com", "access_type": "read", "exact_match": False,}, | ||
| headers=admin_headers, | ||
| ) | ||
| assert_status_not_ok(post_suffix_match_entry) | ||
|
|
||
| # this should succeed because now that email starts with '@' | ||
| post_suffix_match_entry = client.post( | ||
| f"/groups/{owner_group['id']}/addAccess", | ||
| json={"email": "@email.com", "access_type": "read", "exact_match": False,}, | ||
| headers=admin_headers, | ||
| ) | ||
| assert_status_ok(post_suffix_match_entry) | ||
|
|
||
|
|
||
| class TestDelete: | ||
| def test_delete_group( | ||
|
|
@@ -299,10 +315,11 @@ def test_delete_entry(self, client: TestClient, settings: Settings, minimal_db): | |
| json={ | ||
| "email": "[email protected]", | ||
| "access_type": "read", | ||
| "exact_match": False, | ||
| "exact_match": True, | ||
| }, | ||
| headers=admin_headers, | ||
| ) | ||
| assert_status_ok(post_read_entry) | ||
| read_only_user_entry = post_read_entry.json() | ||
| group = client.get(f"/groups/{owner_group['id']}", headers=admin_headers).json() | ||
| assert len(group["group_entries"]) == 2 | ||
|
|
@@ -313,10 +330,11 @@ def test_delete_entry(self, client: TestClient, settings: Settings, minimal_db): | |
| json={ | ||
| "email": "[email protected]", | ||
| "access_type": "write", | ||
| "exact_match": False, | ||
| "exact_match": True, | ||
| }, | ||
| headers=admin_headers, | ||
| ) | ||
| assert_status_ok(post_write_entry) | ||
| write_only_user_entry = post_write_entry.json() | ||
| group = client.get(f"/groups/{owner_group['id']}", headers=admin_headers).json() | ||
| assert len(group["group_entries"]) == 3 | ||
|
|
@@ -359,7 +377,7 @@ def read_only_delete(): | |
| json={ | ||
| "email": "[email protected]", | ||
| "access_type": "owner", | ||
| "exact_match": False, | ||
| "exact_match": True, | ||
| }, | ||
| headers=admin_headers, | ||
| ) | ||
|
|
||
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 is just a personal preference but for validations related to the request body, I tend to put them in the request body's Pydantic model as a validator. This has the added benefit that it will return an error earlier if the request body is malformed
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: Pydantic has a special type
EmailStrwhich I thought we could use for theGroupEntryInmodel but it requires that the email be formatted as[name]@[domain]. However, this makes me realize that perhaps there should be more specific rules to think about such as proper formatting of the domain to include only one@and.symbol. I don't think we need a super stringent check for now since we currently control groups but something to think about in the future