fastapi-azure-auth
fastapi-azure-auth copied to clipboard
[Docs] Recipe to mock a logged-in user/Active Directory roles for testing
We are using this to keep the unit tests small and simple and I thought it may be helpful for others. Based on the username and the assigned ActiveDirectory role users have different permissions in the system. For a convenient way of testing this we can now mock any logged in user per-request:
Example test:
def test_readonly_user_may_not_write(client):
with mockuser(user_name="[email protected]", roles=["foo.read"]):
response = client.post("/somemodel", json={})
assert response.status_code == 403
The setup:
in the routes:
def prepare_user(request: Request, db: Session = Depends(get_db)):
"""
On each request the azure authenticated user is used for local user lookup.
When not found it will be created. The db user object is then stored in `request.state` for easy access.
"""
if not hasattr(request.state, "user"):
raise HTTPException(403, detail="No user information found in request object.")
email: EmailStr = request.state.user.claims["preferred_username"] # logged in azure user
request.state.user_obj = UserRepo.get_or_create(db, email)
router = APIRouter(dependencies=[Security(azure_scheme), Depends(prepare_user)])
conftest.py
class mockuser(ContextDecorator):
"""
Mock any username and role(s) for api requests as if they would come directly from the real
Azure Auth integration.
"""
def __init__(self, user_name="[email protected]", roles=["Bar.readonly"]):
self.user_name = user_name
self.roles = roles
def __enter__(self):
def mock_prepare_user(request: fastapi.Request, db: Session = fastapi.Depends(get_db)):
request.state.user = User(
claims={"preferred_username": self.user_name}, roles=self.roles,
aud="aud", tid="tid", access_token="123"
)
return prepare_user(request, db)
fastapi_app.dependency_overrides[prepare_user] = mock_prepare_user
fastapi_app_internal.dependency_overrides[prepare_user] = mock_prepare_user
def __exit__(self, type, value, traceback):
del fastapi_app.dependency_overrides[prepare_user]
del fastapi_app_internal.dependency_overrides[prepare_user]
class staffuser(mockuser):
"""Mock a staff user sending an api request."""
def __init__(self):
super().__init__(user_name="[email protected] roles=["yourcompany.superuser"])
Hi! This is an awesome example - thank you so much. If we skip the DB part of it, it could definitely be added to the docs under Usage and FAQ
(with a link to this issue for an extended example). Would you like to contribute with a PR? 😊
I've also done a variation of this in @Intility/templates. I'll try to publish some docs during the summer if you don't beat me to it 😊