emmett-framework / emmett

The web framework for inventors
Other
1.04k stars 70 forks source link

await error_handler() #318

Closed josejachuf closed 3 years ago

josejachuf commented 3 years ago

Hi @gi0baro

I'm passing an app from Weppy to Emmett and I have this problem when trying to use signature in the URL

The creation of the signature and the validation of this works fine, but it gives me a problem when I modify the URL (abort function) When the validation of the signature fails, abort is executed


    async def pipe(self, next_pipe, **kwargs):
        if self.valid_url():
            return await next_pipe(**kwargs)
        abort(404)

await error_handler(), TypeError: object str can't be used in 'await' expression

The error occurs when I add the following code

@app.on_error(404)
def not_found():
    return app.render_template("404.html")

All the code of the Pipe and Injector

from emmett import Pipe, Injector, request, abort
from emmett.routing.urls import HttpUrlBuilder, url
from itsdangerous import Signer

KEY = 'e733071b-798b-435a-a95b-7a530563036e'

class SignInjector(Injector):
    namespace = "signature"

    @staticmethod
    def sign(path, args, params, anchor, scheme, host, language):

        s = Signer(KEY)
        builder = HttpUrlBuilder([url(path)])
        _url = builder.url(None, None, language, args, params, None)

        p = _url.find('_signature')
        if p > 0:
            _url = _url[:p-1] + _url[p+38:]

        _url = str.encode(_url)
        signature = s.get_signature(_url).decode("utf-8")

        return signature

class SignPipe(Pipe):

    async def pipe(self, next_pipe, **kwargs):
        if self.valid_url():
            return await next_pipe(**kwargs)
        abort(404)

    def valid_url(self):
        signature = request.query_params._signature or ''

        builder = HttpUrlBuilder([request.path])
        _url = builder.url(None, None,
                           request.language, [],
                           {key: request.query_params[key] for key in request.query_params.keys() if key != '_signature'},
                           None)

        s = Signer(KEY)
        sig = str.encode(signature)
        value = str.encode(_url)
        return s.verify_signature(value=value, sig=sig)
gi0baro commented 3 years ago

@josejachuf the objects you decorate with on_error in Emmett should be coroutines, change your code as:

@app.on_error(404)
async def not_found():
    return app.render_template("404.html")
josejachuf commented 3 years ago

This works fine. Thanks @gi0baro