Cornices/cornice

Route_prefix breaks the cors check

Opened this issue · 5 comments

fizyk commented

I'm having an issue with route_prefix and cors matching a service through url.

We're running the application in several setups.

  • There's a frontend UI application
  • There's also pyramid application using cornice, we're trying to introduce an api prefix to all routes.

We're running it either behind a single nginx that serves both: UI on the root path and pyramid app behind am api prefix or in more like dev environment on different ports. While the single host works great, the separate ports deployment causes an issue in that endpoints are not passing CORS check correctly from the UI. If I remove the route_prefix, everything works correctly and I'm not sure what's happening here.

fizyk commented

Maybe I should add, that it happens on views where we define custom options methods.

Natim commented

Hello @fizyk thank you for reaching out.
Could you create a PR with a test that reproduce the issue (a view with a route_prefix that doesn't handle CORS properly?)

You can copy an existing CORS tests from here: https://github.com/Cornices/cornice/blob/master/tests/test_cors.py

fizyk commented

Thanks @Natim

I'm not sure why, I'm having issues with identifying what and where to initialize in tests to actually achive the results we have.

What I can defnitely tell from my debugging is that the register_service_views have two ways of adding services to it's internal map:

    if existing_route:
        route_name = existing_route
        services[prefix + '__cornice' + existing_route] = service
    else:
        services[prefix + service.path] = service

(We do create pyramid routes first)
Then we do define routes with custom options and require authentication for them, for that, we had to append get_cors_preflight_view on said views ourselves withink forbidden_views if the views with our own options got called and we use cornice.util.current_service to get service related to service. However having route_prefix defined it returns None, as it internally checks always for services stored under these keys:

service = services.get(pattern, services.get('__cornice' + name))

either pattern, or route name prefixed with cornice, and with route_prefix there's the prefix added, which isn't used in this flow.

This MIGHT be the underlying cause of our current issue given the workarounds we've aded earlier 🤔but it might not. At the moment, I'm not able to reproduce the same issue in cornice tests though.

I released a new version with your changes. Let us know if it fixed the issue on your side. We can rollout a bug fix release anytime

fizyk commented

This fixed the main issue we've had :)
I think I know what underlying issue was/is that we had to rely on manually accessing that API... whenever we implement our own OPTIONS to present to the logged user available OPTIONS on the endpoints, we loose the ability to do a preflight check so we've got a bit of code to handle that in our forbidden view:

from typing import Any, Dict, Optional

from cornice import current_service
from cornice.cors import get_cors_preflight_view
from pyramid.request import Request
from pyramid.view import forbidden_view_config


@forbidden_view_config(renderer="json")
def forbidden_view(request: Request) -> Optional[Dict[str, Any]]:
    """Set proper response status code."""
    # Preflight request for endpoint with options method defined
    # and requiring authentication
    if not request.authorization and request.method == "OPTIONS":
        service = current_service(request)
        return get_cors_preflight_view(service)(request)

I wonder it that would be something that cornice could handle automatically...