-
Notifications
You must be signed in to change notification settings - Fork 9
🐛 Fix FASTAPI_CLOUD_TOKEN always overrides user token (solution with Context)
#174
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
Closed
Closed
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
721b10f
Add `prefer_auth_mode` param to `Identity`
YuriiMotov 83738d1
Add `Context` to tell wich token to use in each command
YuriiMotov f109a3b
Add tests for sentry init with deployment token only
YuriiMotov aec339c
Initialize token in app callback
YuriiMotov 90e662b
Add test for login when deploy token is set
YuriiMotov f59a0c6
Notify when deploy token is used
YuriiMotov 737215a
Refactor `Identity` to store deploy token even if unused
YuriiMotov b3c1452
Remove callback for top level app (doesn't work), add init to top lev…
YuriiMotov 092f407
Warn on login command when deploy token is set
YuriiMotov 1d3293a
🎨 Auto format
github-actions[bot] 5a15e8d
Fix typing issues
YuriiMotov 0dcf573
Improve the output of `whoami` command
YuriiMotov 0a0cde4
Add `unset_env_vars` fxture to ignore `FASTAPI_CLOUD_TOKEN` breaking …
YuriiMotov 730a42f
Configure test to run with `deploy` and `cloud deploy` (for coverage)
YuriiMotov a2752e6
Fix coverage
YuriiMotov c737d50
🎨 Auto format
github-actions[bot] 12a7d06
Address review comments from Copilot
YuriiMotov 653d704
Fix changed behavior for `fastapi cloud` command without subcommand
YuriiMotov 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import logging | ||
| from typing import Literal | ||
|
|
||
| from fastapi_cloud_cli.utils.auth import Identity | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| class Context: | ||
| def __init__(self) -> None: | ||
| self._is_initialized = False | ||
|
|
||
| def initialize(self, prefer_auth_mode: Literal["token", "user"] = "user") -> None: | ||
| logger.debug("Initializing context with prefer_auth_mode: %s", prefer_auth_mode) | ||
| self.prefer_auth_mode = prefer_auth_mode | ||
| self._is_initialized = True | ||
|
|
||
| def get_identity(self) -> Identity: | ||
| if not self._is_initialized: # pragma: no cover | ||
| raise RuntimeError("Context must be initialized before use") | ||
| return Identity(prefer_auth_mode=self.prefer_auth_mode) | ||
|
|
||
|
|
||
| ctx = Context() |
This file contains hidden or 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 hidden or 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 |
|---|---|---|
|
|
@@ -109,34 +109,57 @@ def _is_jwt_expired(token: str) -> bool: | |
|
|
||
|
|
||
| class Identity: | ||
| auth_mode: Literal["token", "user"] | ||
|
|
||
| def __init__(self) -> None: | ||
| self.token = _get_auth_token() | ||
| self.auth_mode = "user" | ||
| def __init__(self, prefer_auth_mode: Literal["token", "user"] = "user") -> None: | ||
| self._user_token = _get_auth_token() | ||
| self._auth_mode: Literal["token", "user"] = "user" | ||
| self._deploy_token: str | None = None | ||
|
|
||
| # users using `FASTAPI_CLOUD_TOKEN` | ||
| if env_token := self._get_token_from_env(): | ||
| self.token = env_token | ||
| self.auth_mode = "token" | ||
| logger.debug("Reading token from FASTAPI_CLOUD_TOKEN environment variable") | ||
| self._deploy_token = env_token | ||
| if prefer_auth_mode == "token": | ||
| self._auth_mode = "token" | ||
| logger.debug("Using `token` auth mode") | ||
|
|
||
| def _get_token_from_env(self) -> str | None: | ||
| return os.environ.get("FASTAPI_CLOUD_TOKEN") | ||
|
|
||
| def is_expired(self) -> bool: | ||
| if not self.token: | ||
| if self._auth_mode != "user": # pragma: no cover # Should never happen | ||
|
Contributor
Author
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. We can add unit tests for this class and cover this case there |
||
| raise RuntimeError("Expiration check is only applicable for user tokens") | ||
|
|
||
| if not self.user_token: | ||
| return True | ||
|
|
||
| return _is_jwt_expired(self.token) | ||
| return _is_jwt_expired(self.user_token) | ||
|
|
||
| def is_logged_in(self) -> bool: | ||
| if self.token is None: | ||
| logger.debug("Login status: False (no token)") | ||
| return False | ||
|
|
||
| if self.auth_mode == "user" and self.is_expired(): | ||
| if self._auth_mode == "user" and self.is_expired(): | ||
| logger.debug("Login status: False (token expired)") | ||
| return False | ||
|
|
||
| logger.debug("Login status: True") | ||
| return True | ||
|
|
||
| @property | ||
| def auth_mode(self) -> Literal["token", "user"]: | ||
| return self._auth_mode | ||
|
|
||
| @property | ||
| def token(self) -> str | None: | ||
| if self._auth_mode == "token": | ||
| return self.deploy_token or self.user_token | ||
| return self.user_token | ||
|
|
||
| @property | ||
| def user_token(self) -> str | None: | ||
| return self._user_token | ||
|
|
||
| @property | ||
| def deploy_token(self) -> str | None: | ||
| return self._deploy_token | ||
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.
For
fastapi cloud ...commands context will be initialized by callback, but it doesn't work forfastapi ...commands (can't use callback when app is added without name).To fix this we need to duplicate this initialization in commands that are used on top level (
fastapi loginandfastapi deploy).Not sure if we can handle this in a better way...