def get_test_app(include_admin_routes: Optional[bool] = None, openapi_url: Optional[str] = "/openapi.json") -> FastAPI: include_admin_routes = include_admin_routes or get_api_settings().include_admin_routes auth_settings = get_auth_settings() router_builder = AuthRouterBuilder(auth_settings) app = get_auth_app(router_builder, include_admin_routes, openapi_url=openapi_url) setup_openapi(app) return app
def test_login_password_fails(self) -> None: admin_username = get_auth_settings().first_superuser assert admin_username is not None bad_password = "******" message = self._get_login_response(admin_username, bad_password, APIMessage, HTTP_401_UNAUTHORIZED) assert message.detail == "Incorrect password"
def test_read_self(self, non_admin_access_headers: Dict[str, str]) -> None: url = self.debug_auth_app.url_path_for(AuthEndpointName.read_self) self_user = self.request("GET", url, AuthUser, headers=non_admin_access_headers) assert self_user.username == get_auth_settings().first_superuser
def test_remove_expired_tokens(self, monkeypatch: MonkeyPatch) -> None: auth_settings = get_auth_settings() def real_get_epoch() -> int: return timegm( datetime.utcnow().utctimetuple()) # seconds since epoch assert abs(get_epoch() - real_get_epoch()) <= 1 def refresh_not_expired_epoch() -> int: return real_get_epoch( ) + auth_settings.refresh_token_expire_seconds - 100 def refresh_expired_epoch() -> int: return real_get_epoch( ) + auth_settings.refresh_token_expire_seconds + 100 with context_session() as session: assert len(session.query(RefreshToken).all()) == 3 assert remove_expired_tokens(db=session) == 0 assert len(session.query(RefreshToken).all()) == 3 monkeypatch.setattr(fastapi_auth.auth_app, "get_epoch", refresh_not_expired_epoch) assert remove_expired_tokens(db=session) == 0 assert len(session.query(RefreshToken).all()) == 3 monkeypatch.setattr(fastapi_auth.auth_app, "get_epoch", refresh_expired_epoch) assert remove_expired_tokens(db=session) == 3 assert len(session.query(RefreshToken).all()) == 0
def test_auth_settings() -> None: default_checker = get_auth_settings().password_checker new_checker = PasswordChecker([BCryptPasswordHasher()]) assert default_checker is not new_checker get_auth_settings().password_checker = new_checker assert get_auth_settings().password_checker is not default_checker assert get_auth_settings().password_checker is new_checker with pytest.raises(ValidationError) as exc_info: get_auth_settings().password_checker = 1 # type: ignore[assignment] assert exc_info.value.errors() == [ { "ctx": {"expected_arbitrary_type": "PasswordChecker"}, "loc": ("password_checker",), "msg": "instance of PasswordChecker expected", "type": "type_error.arbitrary_type", } ]
def _get_admin_tokens(self, admin_scope: bool = False) -> AuthTokens: auth_settings = get_auth_settings() admin_username = auth_settings.first_superuser admin_password = auth_settings.first_superuser_password assert admin_username is not None assert admin_password is not None return self._get_login_response(admin_username, admin_password, AuthTokens, admin_scope=admin_scope)
def get_reusable_oauth2() -> OAuth2RefreshPasswordBearer: settings = get_auth_settings() url_base = settings.api_prefix token_url = url_base + settings.token_url refresh_url = url_base + settings.refresh_url scopes = settings.expected_scopes return OAuth2RefreshPasswordBearer(token_url=token_url, refresh_url=refresh_url, scopes=scopes, auto_error=True)
def test_setup_auth_idempotent(debug_auth_app: FastAPI, caplog: LogCaptureFixture) -> None: logger_name = auth_app.__name__ caplog.set_level(logging.INFO, logger=logger_name) auth_settings = get_auth_settings() AuthRouterBuilder(auth_settings).setup_first_superuser(get_engine()) name, level, message = caplog.record_tuples[-1] assert name == logger_name assert level == logging.INFO assert message == "First superuser already exists."
def require_superuser() -> Sequence[params.Depends]: """ Returns a list of dependencies containing one that will require superuser access, *UNLESS* api_settings indicates otherwise (for development purposes). (In that case, no dependencies are added but a warning is emitted.) """ if get_api_settings().disable_superuser_dependency: logger.warning(f"*** require_superuser is DISABLED ***") return [] return [ params.Security(get_jwt_user, scopes=[get_auth_settings().superuser_scope]) ]
def get_test_app_fixture(include_admin_routes: Optional[bool] = None) -> Iterator[FastAPI]: test_db_path = Path("./test.db") if test_db_path.exists(): test_db_path.unlink() app = get_test_app(include_admin_routes=include_admin_routes) engine = get_engine() metadata = get_configured_metadata(app) metadata.create_all(bind=engine) initialize_database(engine) auth_settings = get_auth_settings() AuthRouterBuilder(auth_settings).setup_first_superuser(engine) yield app if test_db_path.exists(): test_db_path.unlink()
def generate_tokens(*, user_id: UserID, is_superuser: bool, scopes: List[str]) -> TokenPair: auth_settings = get_auth_settings() expected_scopes = auth_settings.expected_scopes for scope in scopes: if scope not in expected_scopes: raise_auth_error(detail=f"Unrecognized scope: {scope!r}") if auth_settings.superuser_scope in scopes and not is_superuser: raise_permissions_error() expires_in = auth_settings.access_token_expire_seconds access = _generate_token(user_id=user_id, expires_delta=timedelta(seconds=expires_in), scopes=scopes) refresh = _generate_token( user_id=user_id, expires_delta=timedelta( seconds=auth_settings.refresh_token_expire_seconds), scopes=scopes) if auth_settings.include_expires_in_with_tokens: return TokenPair(access=access, refresh=refresh, expires_in=expires_in) else: return TokenPair(access=access, refresh=refresh)
def parse_signed_encoding(encoded: str) -> Dict[str, Any]: auth_settings = get_auth_settings() return jwt.decode(encoded, auth_settings.secret_key, verify=True, algorithms=auth_settings.decoding_algorithms)
def generate_signed_encoding(claims: Dict[str, Any], expires_delta: Optional[timedelta]) -> str: expiration_claim: Dict[str, int] = {} if expires_delta is None else get_expiration_claim(expires_delta).dict() claims.update(expiration_claim) auth_settings = get_auth_settings() encoded = jwt.encode(claims, auth_settings.secret_key, algorithm=auth_settings.encoding_algorithm) return encoded.decode()
def prod_auth_app(prod_environment: None) -> Iterator[FastAPI]: assert get_auth_settings().include_expires_in_with_tokens is True assert get_api_settings().debug is False # Try re-adding the middleware yield from get_test_app_fixture()
def debug_auth_app(debug_environment: None) -> Iterator[FastAPI]: assert get_auth_settings().include_expires_in_with_tokens is False yield from get_test_app_fixture()
def test_default_password() -> None: checker = get_auth_settings().password_checker password = RawPassword("password") hashed = checker.make_sync(password) assert checker.check_sync(password, hashed).success assert not checker.check_sync(RawPassword("not password"), hashed).success