Skip to content
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

[Docs] Recipe to mock a logged-in user/Active Directory roles for testing #81

Closed
martin-greentrax opened this issue May 25, 2022 · 2 comments
Labels
documentation Improvements or additions to documentation enhancement New feature or request

Comments

@martin-greentrax
Copy link

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="foo@bar.de", 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="foo@bar.com", 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="foo@yourcompany.com roles=["yourcompany.superuser"])

@martin-greentrax martin-greentrax added the enhancement New feature or request label May 25, 2022
@JonasKs
Copy link
Member

JonasKs commented May 28, 2022

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? 😊

@JonasKs
Copy link
Member

JonasKs commented Jul 1, 2022

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 😊

@JonasKs JonasKs added the documentation Improvements or additions to documentation label Sep 11, 2022
JonasKs added a commit that referenced this issue Feb 26, 2023
nikstuckenbrock pushed a commit to nikstuckenbrock/fastapi-azure-auth that referenced this issue Oct 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants