-
Notifications
You must be signed in to change notification settings - Fork 21
PoC: fastapi-users
#363
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
PoC: fastapi-users
#363
Changes from all commits
587d9ae
01b6570
c831920
8c47587
b68ee12
6209bad
51ed081
faf2d00
a51e911
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 |
---|---|---|
|
@@ -37,11 +37,18 @@ | |
User, | ||
UserGroup, | ||
UserProfile, | ||
TestUser, | ||
UserCreate, | ||
UserRead, | ||
UserUpdate, | ||
Password, | ||
get_model_from_kind | ||
) | ||
from .paginator_models import PageModel | ||
from .pubsub import PubSub, Subscription | ||
from beanie import init_beanie | ||
from .auth import fastapi_users_instance, auth_backend | ||
|
||
|
||
app = FastAPI() | ||
db = Database(service=(os.getenv('MONGO_SERVICE') or 'mongodb://db:27017')) | ||
|
@@ -63,6 +70,15 @@ async def create_indexes(): | |
"""Startup event handler to create database indexes""" | ||
await db.create_indexes() | ||
|
||
@app.on_event('startup') | ||
async def fastapi_users_beanie_init(): | ||
"""Startup event handler for beanie""" | ||
await init_beanie( | ||
database=db.db, | ||
document_models=[ | ||
TestUser, | ||
], | ||
) | ||
Comment on lines
+76
to
+81
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. Maybe this call could be moved to 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. The reason I kept this call in |
||
|
||
@app.exception_handler(ValueError) | ||
async def value_error_exception_handler(request: Request, exc: ValueError): | ||
|
@@ -630,6 +646,38 @@ async def put_regression(regression_id: str, regression: Regression, | |
return obj | ||
|
||
|
||
app.include_router( | ||
fastapi_users_instance.get_auth_router(auth_backend, requires_verification=True), | ||
prefix="/auth/jwt", | ||
tags=["auth"] | ||
) | ||
app.include_router( | ||
fastapi_users_instance.get_register_router(UserRead, UserCreate), | ||
prefix="/auth", | ||
tags=["auth"], | ||
) | ||
app.include_router( | ||
fastapi_users_instance.get_reset_password_router(), | ||
prefix="/auth", | ||
tags=["auth"], | ||
) | ||
app.include_router( | ||
fastapi_users_instance.get_verify_router(UserRead), | ||
prefix="/auth", | ||
tags=["auth"], | ||
) | ||
app.include_router( | ||
fastapi_users_instance.get_users_router(UserRead, UserUpdate, requires_verification=True), | ||
prefix="/users", | ||
tags=["users"], | ||
) | ||
|
||
current_active_user = fastapi_users_instance.current_user(active=True) | ||
|
||
@app.get("/authenticated-route") | ||
async def authenticated_route(user: TestUser = Depends(current_active_user)): | ||
return {"message": f"Hello {user.username}!"} | ||
|
||
app = VersionedFastAPI( | ||
app, | ||
version_format='{major}', | ||
|
@@ -639,6 +687,7 @@ async def put_regression(regression_id: str, regression: Regression, | |
on_startup=[ | ||
pubsub_startup, | ||
create_indexes, | ||
fastapi_users_beanie_init, | ||
] | ||
) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,11 @@ | |
FileUrl, | ||
SecretStr, | ||
) | ||
from beanie import Indexed, Document | ||
from fastapi_users.db import BeanieBaseUser | ||
from fastapi_users import schemas | ||
from beanie import PydanticObjectId, Indexed | ||
from typing import Optional | ||
Comment on lines
+27
to
+31
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 wonder if these should rather be in a separate module to avoid "polluting" the whole 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. Yes, we should have a separate model file just like we have for pagination related models. |
||
|
||
|
||
class PyObjectId(ObjectId): | ||
|
@@ -148,6 +153,24 @@ def create_indexes(cls, collection): | |
collection.create_index("profile.username", unique=True) | ||
|
||
|
||
class TestUser(BeanieBaseUser, Document): | ||
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. Is the class called 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. Yes, exactly. |
||
"""Test user""" | ||
username: Indexed(str, unique=True) | ||
|
||
|
||
class UserRead(schemas.BaseUser[PydanticObjectId]): | ||
username: Indexed(str, unique=True) | ||
|
||
|
||
class UserCreate(schemas.BaseUserCreate): | ||
username: Indexed(str, unique=True) | ||
|
||
|
||
class UserUpdate(schemas.BaseUserUpdate): | ||
username: Optional[Indexed(str, unique=True)] | ||
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. Out of interest, why is this one optional whereas it's not in the other models? 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. That's to allow users to only send fields that are updating. If I remove the
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. Right, I think this brings us back to the idea I mentioned a while ago about having a different model for the API and for the database. |
||
|
||
|
||
|
||
class KernelVersion(BaseModel): | ||
"""Linux kernel version model""" | ||
version: int = Field( | ||
|
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.
So I guess this is where an email would be sent with the reset token in a URL, and then the user would then come back via the
on_after_request_verify
method?We could probably test this on the command line using an API token initially although this shouldn't be allowed in production as any leaked token should not enable changing the user's password imho. One issue of course is it's not possible to invalidate a JWT token, it just has a built-in expiry date. I wonder what's the standard way to deal with this in other APIs.
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.
Yes, that is true.