def get_botx_wsgi_api( messages: List[APIMessage], requests: List[APIRequest], errors: Dict[Type[BotXMethod], Tuple[int, Any]], ) -> App: """Generate BotX API mock. Arguments: messages: list of message that were sent from bot and should be extended. requests: all requests that were sent from bot. errors: errors to be generated by mocked API. Returns: Generated BotX API mock for using with httpx. """ return App( components=[ SettingsComponent( Settings(messages=messages, requests=requests, errors=errors), ), ], routes=list(_create_molten_routes()), middleware=[error_middleware], parsers=[JSONParser()], )
def test_middleware_white_listing(app_settings, testing_token): def test_handler(jwt_identity: JWTIdentity): if jwt_identity is None: return "No user token present" return jwt_identity.id routes = [Route("/whitelisted", method="GET", handler=test_handler)] components = [ SettingsComponent({ **app_settings, "JWT_AUTH_WHITELIST": ["test_handler"] }), JWTComponent(), JWTIdentityComponent(), ] middleware = [ResponseRendererMiddleware(), JWTAuthMiddleware()] app = App(routes=routes, components=components, middleware=middleware) client = testing.TestClient(app) response = client.get("/whitelisted", headers={"Authorization": f"Bearer {testing_token}"}) unauthenticated = client.get("/whitelisted") assert 200 == response.status_code assert "1234567890" in response.data assert 200 == unauthenticated.status_code assert "No user token present" in unauthenticated.data
def test_middleware_token_validation_raises_error_on_token_exp(app_settings): def test_handler(jwt_identity: JWTIdentity): if jwt_identity is None: return "No user token present" return jwt_identity.id jwt = JWT(key="keepthissafe", alg="HS256") iat = dt.datetime.now() + dt.timedelta(seconds=-10) exp = iat + dt.timedelta(seconds=5) payload = {"sub": "1234567890", "name": "John Doe", "iat": iat, "exp": exp} token = jwt.encode(payload) routes = [Route("/auth-required", method="GET", handler=test_handler)] components = [ SettingsComponent(app_settings), JWTComponent(), JWTIdentityComponent(), ] middleware = [ResponseRendererMiddleware(), JWTAuthMiddleware()] app = App(routes=routes, components=components, middleware=middleware) client = testing.TestClient(app) response = client.get("/auth-required", headers={"Authorization": f"Bearer {token}"}) assert 401 == response.status_code content = response.json() assert "error_message" in content assert content.get("status") == 401
def setup_app(): setup_logging() cookie_store = CookieStore(**settings.strict_get("sessions")) get_schema = OpenAPIHandler( Metadata( title="Chat", description="A simple chat app.", version="0.0.0", ), ) get_docs = OpenAPIUIHandler() app = App( components=[ AccountManagerComponent(), ChatHandlerFactoryComponent(), ChatroomListenerComponent(), ChatroomRegistryComponent(), CurrentAccountComponent(), PasswordHasherComponent(), RedisComponent(), SQLAlchemyEngineComponent(), SQLAlchemySessionComponent(), SessionComponent(cookie_store), SettingsComponent(settings), TemplatesComponent(path_to("templates")), ], middleware=[ RequestIdMiddleware(), SessionMiddleware(cookie_store), ResponseRendererMiddleware(), WebsocketsMiddleware(), SQLAlchemyMiddleware(), ], routes=[ Route("/_schema", get_schema), Route("/_docs", get_docs), Route("/", index), Route("/login", login), Route("/register", register), Include("/v1", [ Include("/accounts", accounts.routes, namespace="accounts"), Include("/chat", chat.routes, namespace="chat"), Include("/sessions", sessions.routes, namespace="sessions"), ], namespace="v1"), ], ) decorated_app = WhiteNoise(app, **settings.strict_get("whitenoise")) return decorated_app, app
def test_app(): settings = Settings({ 'database_engine_dsn': 'sqlite://', 'identity_server': 'http://localhost', 'secret_key': 'fake_secret', }) app = create_app( settings=settings, components=[ SettingsComponent(settings), SQLAlchemyEngineComponent(), SQLAlchemySessionComponent(), # RequestSessionComponent(), MockProvider(AuthProviderComponent), ], ) yield app
def test_claims_required_attaches_values(app_settings): @claims_required({"admin": True}) def test_handler(): return "Handler called" routes = [Route("/claims", method="GET", handler=test_handler)] components = [SettingsComponent(app_settings)] middleware = [ResponseRendererMiddleware()] app = App(routes=routes, components=components, middleware=middleware) client = testing.TestClient(app) response = client.get("/claims") assert 200 == response.status_code assert "admin" in test_handler.claims assert test_handler.claims.get("admin") is True assert "Handler called" in response.data
def test_middleware_raises_401_error(app_settings): def test_handler(): return "Handler called" routes = [Route("/auth-required", method="GET", handler=test_handler)] components = [ SettingsComponent(app_settings), JWTComponent(), JWTIdentityComponent(), ] middleware = [ResponseRendererMiddleware(), JWTAuthMiddleware()] app = App(routes=routes, components=components, middleware=middleware) client = testing.TestClient(app) response = client.get("/auth-required") assert 401 == response.status_code
def test_JWT_claims_options_raises_error(app_settings, testing_token): def test_handler(jwt_identity: JWTIdentity): if jwt_identity is None: return "No user token present" return jwt_identity.id jwt = JWT(key=app_settings.get("JWT_SECRET_KEY"), alg="HS256") mod_token = jwt.encode({ **jwt.decode(testing_token), **{ "iss": "https://molten.com" } }) routes = [Route("/claim_options", method="GET", handler=test_handler)] components = [ SettingsComponent({ **app_settings, "JWT_CLAIMS_OPTIONS": { "iss": { "essential": True, "values": ["https://example.com", "https://example.org"], } }, }), JWTComponent(), JWTIdentityComponent(), ] middleware = [ResponseRendererMiddleware(), JWTAuthMiddleware()] app = App(routes=routes, components=components, middleware=middleware) client = testing.TestClient(app) missing_claim_response = client.get( "/claim_options", headers={"Authorization": f"Bearer {testing_token}"}) wrong_claim_value_response = client.get( "/claim_options", headers={"Authorization": f"Bearer {mod_token}"}) assert 401 == missing_claim_response.status_code assert 401 == wrong_claim_value_response.status_code
def test_claims_required_raises_error(app_settings, testing_token): @claims_required({"admin": True}) def test_handler(): return "Handler called" routes = [Route("/claims", method="GET", handler=test_handler)] components = [ SettingsComponent(app_settings), JWTComponent(), JWTIdentityComponent(), ] middleware = [ResponseRendererMiddleware(), JWTAuthMiddleware()] app = App(routes=routes, components=components, middleware=middleware) client = testing.TestClient(app) response = client.get("/claims", headers={"Authorization": f"Bearer {testing_token}"}) assert 403 == response.status_code
def test_middleware_anonymous_user_support(app_settings): @allow_anonymous def test_handler(): return "Handler called" routes = [Route("/auth-maybe", method="GET", handler=test_handler)] components = [ SettingsComponent(app_settings), JWTComponent(), JWTIdentityComponent(), ] middleware = [ResponseRendererMiddleware(), JWTAuthMiddleware()] app = App(routes=routes, components=components, middleware=middleware) client = testing.TestClient(app) response = client.get("/auth-maybe") assert 200 == response.status_code assert "Handler called" in response.data
def create_app(middleware=None, components=None, settings=None): if settings is None: settings = Settings({ 'database_engine_dsn': os.environ['SQLALCHEMY_URI'], 'identity_server': os.environ['IDENTITY_SERVER'], 'secret_key': os.environ['SECRET_KEY'], }) if middleware is None: middleware = [ ResponseRendererMiddleware(), SQLAlchemyMiddleware(), auth_middleware, ] if components is None: components = [ SettingsComponent(settings), SQLAlchemyEngineComponent(), SQLAlchemySessionComponent(), RequestSessionComponent(), AuthProviderComponent(), ManagerComponent(ChoreInstanceManager), ManagerComponent(ChoreDefinitionManager), UserProviderComponent(), ] app = App( routes=[ Route('/login', login, method='POST', name='login'), Include( '/api', routes, namespace='api', ) ], middleware=middleware, components=components, ) return app
def test_identity_extract_jwt_from_cookie(app_settings): def test_auth(jwt: JWT): cookie_name = "molten_auth_cookie" cookie_value = jwt.encode({"sub": 123456, "name": "spiderman"}) auth_response = Response(HTTP_200) auth_response.set_cookie(Cookie(cookie_name, cookie_value)) return auth_response def test_cookie(jwt_identity: JWTIdentity): if jwt_identity is None: return "Didn't work" return f"Hello {jwt_identity.name} your sub id is {jwt_identity.sub}" routes = [ Route("/auth", method="POST", handler=test_auth), Route("/cookie", method="GET", handler=test_cookie), ] components = [ SettingsComponent({ **app_settings, **{ "JWT_AUTH_COOKIE": "molten_auth_cookie" } }), JWTComponent(), JWTIdentityComponent(), ] app = App(routes=routes, components=components) client = testing.TestClient(app) auth_response = client.post("/auth") cookie_value = auth_response.headers.get_all("set-cookie")[0] assert "molten_auth_cookie" in cookie_value cookie_response = client.get("/cookie", headers={"cookie": cookie_value}) assert "123456" in cookie_response.data assert "spiderman" in cookie_response.data
def test_middleware_token_validation_passes(app_settings, testing_token): def test_handler(jwt_identity: JWTIdentity): if jwt_identity is None: return "No user token present" return jwt_identity.id routes = [Route("/auth-required", method="GET", handler=test_handler)] components = [ SettingsComponent(app_settings), JWTComponent(), JWTIdentityComponent(), ] middleware = [ResponseRendererMiddleware(), JWTAuthMiddleware()] app = App(routes=routes, components=components, middleware=middleware) client = testing.TestClient(app) response = client.get("/auth-required", headers={"Authorization": f"Bearer {testing_token}"}) assert 200 == response.status_code assert "1234567890" in response.data
def setup_app(): setup_logging() get_docs = OpenAPIUIHandler() get_schema = OpenAPIHandler( metadata=Metadata( title="Pets", description="", version="0.0.0", ), ) app = App( components=[ ManagerComponent(PetManager), SQLAlchemyEngineComponent(), SQLAlchemySessionComponent(), SettingsComponent(settings), TemplatesComponent(path_to("templates")), ], middleware=[ RequestIdMiddleware(), ResponseRendererMiddleware(), SQLAlchemyMiddleware(), ], routes=[ Route("/_docs", get_docs), Route("/_schema", get_schema), Route("/", index), Include("/v1/pets", pets.routes, namespace="pets"), ], ) decorated_app = WhiteNoise(app, **settings.strict_get("whitenoise")) return decorated_app, app
def test_missing_auth_cookie(app_settings): def test_cookie(jwt_identity: JWTIdentity): if jwt_identity is None: return "Didn't work" return f"Hello {jwt_identity.name} your sub id is {jwt_identity.sub}" routes = [Route("/cookie", method="GET", handler=test_cookie)] components = [ SettingsComponent({ **app_settings, **{ "JWT_AUTH_COOKIE": "molten_auth_cookie" } }), JWTComponent(), JWTIdentityComponent(), ] app = App(routes=routes, components=components) client = testing.TestClient(app) cookie_response = client.get("/cookie") assert "Didn't work" in cookie_response.data
class Signup: email: str first_name: str def send_async_email(mail: Mail, msg: Message) -> None: mail.send(msg) def sign_up(signup: Signup, mail: Mail, templates: MailTemplates): """Handler that simulates a basic async sending of an html templated welcome email""" msg = Message( subject="Welcome to Molten!", body="This is a body that gets shown if html can't", html=templates.render("welcome_mail.html", name=signup.first_name), recipients=[signup.email], ) Thread(target=send_async_email, args=(mail, msg)).start() return Response(HTTP_204, content="") routes = [Route("/", sign_up, "POST")] components = [ SettingsComponent(settings), MailComponent(), MailTemplatesComponent("templates"), ] app = App(routes=routes, components=components)
from molten import App, Route, Settings, SettingsComponent, testing def index(settings: Settings) -> dict: return settings app = App( components=[SettingsComponent(Settings({"database_dsn": "sqlite://"}))], routes=[Route("/", index)], ) client = testing.TestClient(app) def test_apps_can_load_settings(): # Given that I have an app that uses settings # When I make a request to that handler response = client.get(app.reverse_uri("index")) # Then I should get back a successful response assert response.status_code == 200 # And the response should contain the settings assert response.json() == { "database_dsn": "sqlite://", }
metadata=Metadata( title="Molten-Boilerplate API", description="An API for Molten-Boilerplate.", version="0.0.0", ), security_schemes=[ HTTPSecurityScheme("default", "bearer"), ], default_security_scheme="default", ) components: List[Component] = [ # TOMLSettingsComponent(), SQLAlchemyEngineComponent(), SQLAlchemySessionComponent(), SettingsComponent(settings.SETTINGS) ] middleware: List[Middleware] = [ ResponseRendererMiddleware(), SQLAlchemyMiddleware(), ] routes: List[Union[Route, Include]] = [ Include("/api/v1/users", [ Route("", list_users), Route("", create_user, method="POST"), Route("/{user_id}", delete_user, method="DELETE"), Route("/{user_id}", get_user), ]),
"MAIL_PASSWORD": "******", "MAIL_PORT": 587, "MAIL_USE_TLS": True, "MAIL_DEFAULT_SENDER": "*****@*****.**", } ) def send_message(params: QueryParams, mail: Mail): """Emails an email address provided in the query string""" addresses = params.get_all("email") if not addresses: return Response( HTTP_400, content="Provide emails in the query params to send a welcome message", ) msg = Message( subject="Welcome to Molten!", body="Welcome to Molten! Glad to have you here.", recipients=addresses, ) mail.send(msg) return Response(HTTP_204, content="") routes = [Route("/", send_message, "POST")] components = [SettingsComponent(settings), MailComponent()] app = App(routes=routes, components=components)