-
Notifications
You must be signed in to change notification settings - Fork 32
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
Add unit tests for github auth provider #149
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
03f9c78
chore(github): add a couple decorator tests
vit-zikmund cbde637
chore(github): simplify caching decorators finish decorator tests
vit-zikmund 129ec03
chore(github): make user auth caching more robust + tests
vit-zikmund 5b875f0
chore(github): move config validation to schema
vit-zikmund 368eef8
chore(github): add auth provider tests
vit-zikmund 125de7c
chore(github): fix incorrect comment
vit-zikmund f6a7cbc
chore(github): improve lock typing
vit-zikmund d622b91
chore(github): use Flask means to extract org & repo
vit-zikmund File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ pytest-mypy | |
pytest-env | ||
pytest-cov | ||
pytest-vcr | ||
responses | ||
|
||
pytz | ||
types-pytz | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 entirely sure I understand the purpose of "authoritative".
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 related to caching and thread safety, in the boundaries of the current design.
The "problem" is in the way how this whole authentication is performed in
BaseView._is_authorized
, which first creates the authenticator,__call__
s it to get theIdentity
and only then it verifies the right permissions withIdentity.is_authorized
.These two consecutive steps have no locks, so they can get mixed up with other threads simultaneously doing the same thing. Because I'm authorizing the user via GitHub API calls, my goal is to prevent those from repeating not only by caching that for some TTL, but also by blocking concurrent identical auth requests from different threads, so only a single thread gets to do the API calls and other just sit blocking and get only the result.
Because I wanted to do all the API calls within the
GithubAuthenticator.__call__
method not to drag therequests
Session
object around (which is otherwise helpful in reusing a single TLS connection the to API), I'm storing only the resulting permissions on theGithubIdentity
object, which can only simply expire.With all this, and the fact all the caches not only have configurable TTL, but also a rather low maximum size, I was obsessed about a scenario where (likely) an attacker would DoS the permission cache by doing concurrent calls for the same user, but different org/repo. In that scenario, the cached entry that got stored in the
GithubAuthenticator.__call__
gets evicted sooner than it's being finally accessed viaIdentity.is_authorized
.Therefore I couldn't eventually resist to introduce a tiny in-between cache to remember the permissions for a user obtained in
__call__
till it's being successfully read inis_authorized
😮💨This
permissions
method is called in two contexts:__call__
to peek if a certain user's permissions are cachedis_authorized
, which isn't used internally, but mean the obtained permissions have been read by the core logic for good and that I'm free to finally forget it.The
authoritative
flag is just for that latter case, telling the logic the permissions have been properly read and now can sit and expire in the regular cache.I admit this design is covering a really niche scenario, but once I realized it's there, my "OCD" couldn't let it stay. If you think there's a chance to considerably simplify the design, I'm all ears, but since this works and it's already done... 😇
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 sounds good to me. My approach would just (if I'd noticed it at all, which I probably wouldn't because I wouldn't have tested high concurrency) have been to throw a mutex around the critical section and accept the performance hit. This seems much more amenable to many callers at once.