Exemple #1
0
def test_apps_session_cookies_expire():
    # Given that I have an app with a cookie store that immediately expires session cookies
    cookie_store = CookieStore(b"secret", cookie_ttl=0)

    def set_username(username: str, session: Session) -> None:
        session["username"] = username

    def get_username(session: Session) -> Optional[str]:
        return session.get("username")

    app = App(
        components=[
            SessionComponent(cookie_store),
        ],

        middleware=[
            SessionMiddleware(cookie_store),
            ResponseRendererMiddleware(),
        ],

        routes=[
            Route("/set-username/{username}", set_username),
            Route("/get-username", get_username),
        ],
    )

    # And a client for that app
    client = testing.TestClient(app)

    # When I make a request to a handler that stores session data
    response = client.get(app.reverse_uri("set_username", username="******"))

    # Then I should get back a successful response
    assert response.status_code == 200

    # And the response should contain my session cookie
    cookie = cookies.SimpleCookie()
    for data in response.headers.get_all("set-cookie"):
        cookie.load(data.replace("SameSite=Strict", ""))

    assert "__sess__" in cookie

    # When I make another request with that same cookie
    session_cookie = cookie.output(attrs=[], header="")
    response = client.get(app.reverse_uri("get_username"), headers={
        "cookie": session_cookie,
    })

    # Then I should get back nothing
    assert response.json() is None
Exemple #2
0
def test_openapi_can_render_fields_with_different_request_and_response_names():
    # Given that I have a schema that has different names based on whether it's in the request or response
    @schema
    class A:
        x: int = field(request_name="X", response_name="Y")

    def index(a: A) -> A:
        pass

    # And an app
    app = App(routes=[Route("/", index)])

    # When I generate a document
    document = generate_openapi_document(
        app, Metadata("example", "an example", "0.0.0"), [])

    # Then the schema should mark that field as writeOnly
    response_schema = document["components"]["schemas"][
        "tests.openapi.test_openapi.A"]
    assert response_schema["properties"] == {
        "X": {
            "type": "integer",
            "format": "int64",
            "writeOnly": True,
        },
        "Y": {
            "type": "integer",
            "format": "int64",
            "readOnly": True,
        },
    }
Exemple #3
0
def test_openapi_can_render_request_only_fields():
    # Given that I have a schema that has request-only fields
    @schema
    class A:
        x: int = field(request_only=True)

    def index(a: A) -> A:
        pass

    # And an app
    app = App(routes=[Route("/", index)])

    # When I generate a document
    document = generate_openapi_document(
        app, Metadata("example", "an example", "0.0.0"), [])

    # Then the schema should mark that field as writeOnly
    response_schema = document["components"]["schemas"][
        "tests.openapi.test_openapi.A"]
    assert response_schema["properties"] == {
        "x": {
            "type": "integer",
            "format": "int64",
            "writeOnly": True,
        },
    }
Exemple #4
0
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
Exemple #5
0
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()],
    )
Exemple #6
0
def test_apps_can_be_injected_into_singleton_components():
    # Given that I have a singleton component that requests the app
    class AClass:  # noqa
        def __init__(self, app):
            self.app = app

    class AComponent:
        is_cacheable = True
        is_singleton = True

        def can_handle_parameter(self, parameter):
            return parameter.annotation is AClass

        def resolve(self, app: BaseApp):
            return AClass(app)

    app = App(components=[AComponent()])

    # When I resolve that component
    def test(a_class: AClass):
        # Then its app property should be the app instance
        assert a_class.app is app

    resolver = app.injector.get_resolver()
    resolver.resolve(test)()
Exemple #7
0
def setup_app():
    get_schema = OpenAPIHandler(
        metadata=Metadata(
            title="Pet Store",
            description=__doc__,
            version="0.0.0",
        ),
        security_schemes=[HTTPSecurityScheme("Bearer", "bearer")],
        default_security_scheme="Bearer",
    )

    get_schema = annotate(no_auth=True)(get_schema)
    get_docs = annotate(no_auth=True)(OpenAPIUIHandler())

    return App(
        components=[
            DatabaseComponent(),
            categories.CategoryManagerComponent(),
            tags.TagManagerComponent(),
            pets.PetManagerComponent(),
        ],
        middleware=[
            ResponseRendererMiddleware(),
            auth_middleware,
        ],
        routes=[
            Include("/v1/categories", categories.routes),
            Include("/v1/pets", pets.routes),
            Include("/v1/tags", tags.routes),
            Route("/_docs", get_docs),
            Route("/_schema", get_schema),
        ],
    )
Exemple #8
0
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
Exemple #9
0
def test_openapi_can_render_api_key_security_schemes_correctly():
    # Given that I have an APIKeySecurityScheme
    security_scheme = APIKeySecurityScheme(
        name="api-key",
        param_name="x-api-key",
        in_="header",
    )

    # When I generate a document with that security scheme
    document = generate_openapi_document(
        App(),
        Metadata(
            "example",
            "an example",
            "0.0.0",
        ),
        security_schemes=[security_scheme],
        default_security_scheme="api-key",
    )

    # Then I should get back a valid, non-ambiguous, OpenAPI document
    assert document["components"] == {
        "schemas": {},
        "securitySchemes": {
            "api-key": {
                "name": "x-api-key",
                "in": "header",
                "type": "apiKey",
            },
        },
    }
Exemple #10
0
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
Exemple #11
0
def test_empty_app_can_return_openapi_document():
    # Given that I have an empty app
    app = App(routes=[
        Route("/schema.json",
              OpenAPIHandler(
                  Metadata(
                      title="empty application",
                      description="an application that doesn't do anything",
                      version="0.1.0",
                      contact=Contact(name="Jim Gordon", ),
                  )),
              name="schema"),
    ])

    # When I visit its schema uri
    response = testing.TestClient(app).get("/schema.json")

    # Then I should get back a successful response
    assert response.status_code == 200
    assert response.json() == {
        "openapi": "3.0.1",
        "info": {
            "title": "empty application",
            "description": "an application that doesn't do anything",
            "version": "0.1.0",
            "contact": {
                "name": "Jim Gordon",
            },
        },
        "paths": {
            "/schema.json": {
                "get": {
                    "tags": [],
                    "operationId": "schema",
                    "summary": "Generates an OpenAPI v3 document.",
                    "description": "",
                    "deprecated": False,
                    "parameters": [],
                    "responses": {
                        "200": {
                            "description": "A successful response.",
                            "content": {},
                        },
                    },
                }
            }
        },
        "components": {
            "schemas": {},
            "securitySchemes": {},
        },
    }
Exemple #12
0
def create_app(_components=None, _middleware=None, _routes=None):
    app = App(
        components=_components or components,
        middleware=_middleware or middleware,
        routes=_routes or routes,
        parsers=[
            JSONParser(),
            URLEncodingParser(),
            MultiPartParser(),
        ],
        renderers=[JSONRenderer()]
    )
    setup_db(app)
    return app
def test_app_can_render_mail_templates():
    mail = Mail(
        user="******",
        password="******",
        port=587,
        use_tls=True,
        suppress_send=True,
    )
    mail.send = MagicMock(return_value=None)

    def template_handler(mail_templates: MailTemplates) -> Response:
        mail.send_message(
            subject="Test email",
            html=mail_templates.render("test_template.html", name="Molten"),
            recipients=["*****@*****.**"],
        )
        return Response(HTTP_204, content="")

    app = App(
        components=[MailTemplatesComponent("./tests/mail_templates")],
        routes=[Route("/", template_handler, name="index")],
    )

    client = testing.TestClient(app)

    # Given that a handler will use templating
    # When constructing and email
    response = client.get(app.reverse_uri("index"))

    # Then I should get back a successfull response
    assert response.status_code == 204

    # and the handler should have called the Mail object
    # with the string value of the rendered template
    mail_msg = mail.send.call_args[0][0]
    assert "<th>Hey there Molten</th>" in mail_msg.html
Exemple #14
0
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
Exemple #15
0
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
Exemple #16
0
def test_openapi_can_render_lists_of_x(fields, expected):
    # Given that I have a schema that has a list of something in it
    A = type("A", (object, ), fields)
    A.__annotations__ = fields
    A = schema(A)

    def index() -> A:
        pass

    # And an app
    app = App(routes=[Route("/", index)])

    # When I generate a document
    document = generate_openapi_document(
        app, Metadata("example", "an example", "0.0.0"), [])

    # Then the return schema should have an array of that thing
    response_schema = document["components"]["schemas"][
        "tests.openapi.test_openapi.A"]
    assert response_schema["properties"] == expected
Exemple #17
0
def test_openapi_can_render_documents_with_method_handlers():
    # Given that I have a resource class
    @schema
    class User:
        username: str

    class Users:
        def get_users(self) -> User:
            pass

    # And an app that uses that an instance of that resource
    users = Users()
    app = App(routes=[Route("/users", users.get_users)])

    # When I generate a document
    document = generate_openapi_document(
        app, Metadata("example", "an example", "0.0.0"), [])

    # Then I should get back a valid document
    assert document
Exemple #18
0
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
Exemple #19
0
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
Exemple #20
0
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
Exemple #21
0
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
Exemple #22
0
def test_ws_middleware_validates_origin():
    # Given that I have an app instance whose ws middleware validates the incoming origin
    app = App(
        middleware=[
            ResponseRendererMiddleware(),
            WebsocketsMiddleware(re.compile("example.com")),
        ],
        routes=[Route("/echo", echo)],
    )

    # And a ws client for that app
    client = WebsocketsTestClient(app)

    # When I try to connect to its echo endopoint with an invalid origin
    # Then an error should occur
    with pytest.raises(ValueError):
        client.connect("/echo")

    # When I try to connect to its echo endopoint with a valid origin
    with client.connect("/echo", headers={"origin": "example.com"}) as sock:
        # Then my connection should succeed
        assert sock
Exemple #23
0
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
Exemple #24
0
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
Exemple #25
0
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
Exemple #26
0
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
Exemple #27
0
from molten import App, Route


def hello(name: str, age: int) -> str:
    return f"Hello {age} year old named {name}!"


app = App(routes=[Route("/hello/{name}/{age}", hello)])
Exemple #28
0
from molten import App, JSONRenderer, QueryParam, ResponseRendererMiddleware, Route
from molten.contrib.sessions import CookieStore, Session, SessionComponent, SessionMiddleware

cookie_store = CookieStore(b"ubersecret")


def set_username(username: QueryParam, session: Session) -> str:
    session["username"] = username
    return username


def get_username(session: Session) -> Optional[str]:
    return session.get("username")


app = App(
    components=[
        SessionComponent(cookie_store),
    ],
    middleware=[
        SessionMiddleware(cookie_store),
        ResponseRendererMiddleware([
            JSONRenderer(),
        ]),
    ],
    routes=[
        Route("/get-username", get_username),
        Route("/set-username", set_username),
    ],
)
Exemple #29
0
        "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)
Exemple #30
0
    title="Emeeting API",
    description="An API for managing your room meetings.",
    version="0.0.1",
))

get_docs = OpenAPIUIHandler()
"""
Add middlewares
"""

middlewares = [prometheus_middleware, ResponseRendererMiddleware()]
"""
Include or add routes
"""

routes = [
    Route("/", get_docs),
    Route("/schema", get_schema),
    Route("/metrics", expose_metrics),
    Include("/v1/rooms", routes=room.routes),
    Include("/v1/meetings", routes=meeting.routes),
]
"""
Start application
"""

app = App(routes=routes, middleware=middlewares)
app = CORS(app, headers="*", methods="*", origin="*", maxage="86400")

log.info("Start application successfully.")