Проверить, есть ли у текущего пользователя разрешение на службу пирамиды?

Я использую Cornice и Pyramid с ACL Auth. Это дубликат старого вопроса который я повторно спрашиваю, поскольку Пирамида изменилась.

Текущие документы скажем, что pyramid.security.has_permission заменено на request.has_permission, у которого есть необязательный аргумент context. Я пытаюсь использовать has_permission в цикле по всем службам, чтобы увидеть, к каким службам имеет доступ текущий пользователь (request).

Конечной целью является динамическое сканирование всех служб Cornice (т.е. просмотр файлов с помощью декоратора Cornice @resource), чтобы увидеть, какие из них авторизованы для данного разрешения (т.е. 'view') для текущего пользователя. Я готов использовать другой способ сделать это, помимо has_permission.

Вариант использования этих знаний — предоставить документ Swagger Spec JSON, который документирует только конечные точки API, доступные текущему пользователю.

Я ожидаю, что код будет выглядеть примерно так:

from cornice import service
# Get our list of services
services = service.get_services()
# Assume we have an authenticated user logged in, thus attaching auth info to request
for svc in services:
    context = magic_context_function(svc)
    if request.has_permission('view', context) == False:
        # Code will go here to hide endpoint documentation for this endpoint

person hamx0r    schedule 15.06.2015    source источник


Ответы (1)


Кажется, ответ должен заключаться в использовании view_execution_permitted(context, request, name=''), но я не могу заставить его работать с произвольным именем представления, так как аргумент name не соответствует значению cornice.service.name.

Однако вот полурешение из Pyramid issue на Github . Вам понадобится несколько импортов, чтобы связанное решение работало (лучше). Вот полный код

from pyramid.security import _get_registry, Allowed
from pyramid.interfaces import IRouteRequest, IRequest, IViewClassifier, ISecuredView, IView
from zope.interface import providedBy
def route_view_execution_permitted(context, request, route_name, name=''):
    reg = _get_registry(request)
    context_iface = providedBy(context)
    request_iface = reg.queryUtility(
        IRouteRequest,
        name=route_name,
        default=IRequest)
    provides = (IViewClassifier, request_iface, context_iface)

    view = reg.adapters.lookup(provides, ISecuredView, name=name)
    if view is None:
        view = reg.adapters.lookup(provides, IView, name=name)
        if view is None:
            raise TypeError('No registered view satisfies the constraints. '
                            'It would not make sense to claim that this view '
                            '"is" or "is not" permitted.')
        return Allowed(
            'Allowed: view name %r in context %r for route %r (no permission defined)' %
            (name, context, route_name))
    return view.__permitted__(context, request)

Можно использовать приведенную выше функцию, чтобы определить, может ли текущий пользователь (определенный из объекта request) получить доступ к службе (по имени) следующим образом:

from cornice import service
services = service.get_services()
for svc in services:
  view_permitted = route_view_execution_permitted(request.context, request, svc.name)
  if view_permitted == True:
      # Do something interesting...

Я обнаружил, что приведенное выше решение имеет два недостатка:

  1. Это медленно, потому что каждая итерация цикла svc по какой-то причине открывает новое соединение с API.
  2. Он возвращает ошибочные результаты (например, говорит, что у одного есть разрешение на услуги, которых у него нет, и наоборот)

Возможно, кто-то увидит способ улучшить ответ выше. А пока вот решение, использующее ACL, прикрепленные к каждой службе, а затем определяющее, соответствует ли текущий request.effective_principals.

# Now see if current user meets ACL requirements for any permission
is_permitted = None  # set our default.
for ace in acl:
    for principal in request.effective_principals:
        if ace[1] == principal:
            is_permitted = True if ace[0] == Allow else False
            break
    if is_permitted is not None:
        break

if is_permitted is True:
    # Do something interesting...

Слабыми сторонами здесь являются:

  1. Это медленно по той же причине, что и предыдущее решение
  2. В реализованном виде он рассматривает только @resource классы обслуживания, а не методы @view, которые могут иметь собственные разрешения или списки управления доступом.

Это можно исправить с помощью чего-то вроде:

for method, view, args in service.definitions:
    if 'permission' in args:
        # Now start looking at permission to see if they match what's given by the parent ACL in the resource class
        # Also, the special "__no_permission_required__" value means we should not have a Security Requirement Object
        if args['permission'] == NO_PERMISSION_REQUIRED :
            # Interesting....
person hamx0r    schedule 16.06.2015