python - what's the correct way to find out flask's endpoint by function name? -
i have flask demo, , needs access control. access control base on function name, , wrote decorator it.
the decorator define is:
def permission(fn): @wraps(fn) def wraped(*args, **kwargs): if _do_validate(fn.__name__): # todo check user has privileges here return fn(*args, **kwargs) else: abort(401) return wraped usage example follow:
@app.route('/index') @permission def index(): return 'hello world' this work fine if user don't specify endpoint, because flask's default endpoint fn.__name__, in example endpoint='index'.
but when user specify endpoint, or use blueprint, endpoint changed. example:
bl = blueprint('admin', __name__) @bl.route('hello') @permission def hello(): return 'hello world in blueprint' the endpoint changed admin.hello.
i don't want specify arg in @permission, write new permission decorator follow:
def permission(fn): @wraps(fn) def wraped(*args, **kwargs): m = fn.__module__.split('.') if len(m) <= 2: # app must define in root module, if user not use blueprint, juse use fn.__name__ endpoint reg_f = fn.__name__ else: # blue print must define in submodule reg_f = '{0}.{1}'.format(m[1], fn.__name__) if _do_validate(ref_f): # todo check user has privileges here return fn(*args, **kwargs) else: abort(401) return wraped the problem solved, think it's not elegant.
can give me better one? thx.
your solution not valid in case of blueprint's name not equal module's __name__.
i can propose next solution:
from collections import defaultdict registered = defaultdict(int) def permission(fn): registered[fn.__name__] += 1 fn.permission_token = "%s.%s" % (fn.__name__, registered[fn.__name__]) @wraps(fn) def wraped(*args, **kwargs): if _do_validate(fn.permission_token): return fn(*args, **kwargs) else: abort(401) return wraped @permission def foo(): pass @permission def bar(): pass def _do_validate(permission_token): return {foo.permission_token: true, bar.permission_token: false}.get(permission_token, false) the main drawback of have import routes in _do_validate's module create such "access control list".
upd: ok, because of want use endpoint values check user access, next solution allows find endpoint view function:
from collections import defaultdict flask import current_app def find_endpoint(fn): # loop can optimized maintaining # {permission_token => endpoint} dict. endpoint, view_func in current_app.view_functions.items(): if getattr(view_func, 'permission_token', none) == fn.permission_token: return endpoint return none registered = defaultdict(int) def permission(fn): registered[fn.__name__] += 1 @wraps(fn) def wrapped(*args, **kwargs): endpoint = find_endpoint(wrapped) assert endpoint not none if _do_validate(endpoint): return fn(*args, **kwargs) else: abort(401) # work because flask's route decorator returns # original view function, not wrapped one. wrapped.permission_token = "%s.%s" % (fn.__name__, registered[fn.__name__]) return wrapped def _do_validate(endpoint): # or search in db return {'index': true, 'bp.index': false}.get(endpoint, false) however, checking access endpoint not idea, because code change during development process lead undesired losing (or worse - retrieving!) access. so, opinion using action (or resource) argument permission decorator reasonable. such parameter can freely modify endpoints/view functions without undesired side effects.
upd2: actually, more simple way find endpoint value wrapper function use request.endpoint.
Comments
Post a Comment