среда, 31 октября 2012 г.

Быстрая авторизация на pyramid при помощи persona (Перевод)


Несколько дней назад, когда вышла первая бета persona, я подумал, что было бы неплохо использовать этот механизм аутентификации для моего следующего проекта. По поводу pyramid документация persona отсылает к записи в блоге под названием: Painless Authentication with Pyramid and BrowserID, которая описывает как использовать pyramid_whoauth с repoze.who.plugins.browserid для использования persona в pyramid.
К сожалению, этот метод всего лишь предоставляет 403-ю страницу с кнопкой входа и нет никакого очевидного способа разместить эту кнопку где-то ещё. Быстрый взгляд на исходники показывает, что сделать это не так уж и просто, так как большая часть работы выполняется в пределах wsgi приложения. Чтобы получить кнопку для входа мне бы пришлось переписать javascript, который взаимодействует с persona api, и, возможно, большую часть кода для входа для сохранения csrf верификации.
Так что вместо того, чтобы переписывать половину и пытаться работать с существующей реализацией, я решил написать код с нуля и, как мне кажется, неплохо было бы сделать это в виде библиотеки. Я назвал его pyramid_persona и он доступен на pypi. README объясняет как его использовать, но тут я хотел бы привести более наглядную демонстрацию.

Запретный вид

Во-первых, давайте покажем как обрабатывать аутентификацию. Возьмём небольшое приложение с представлением, которое приветствует нас если мы залогинились и возвращает 403 в противном случае.
from waitress import serve
from pyramid.config import Configurator
from pyramid.response import Response
from pyramid.security import authenticated_userid
from pyramid.exceptions import Forbidden

def hello_world(request):
    userid = authenticated_userid(request)
    if userid is None:
        raise Forbidden()
    return Response('Hello %s!' % (userid,))


if __name__ == '__main__':
    config = Configurator()
    config.add_route('hello', '/')
    config.add_view(hello_world, route_name='hello')
    app = config.make_wsgi_app()
    serve(app, host='0.0.0.0')
И, как мы и ожидаем, мы видим сообщение об ошибке:

Теперь давайте добавим pyramid_persona и некоторые настройки. secret используется для того, чтобы пометить куки, а audience - это одна из фишек для безопасности у persona, не давая возможности атакующему зайти на твой сайт используя вход на другом сайте.
settings = {
    'persona.secret': 'some secret',
    'persona.audiences': 'http://localhost:8080'
}
config = Configurator(settings=settings)
config.include('pyramid_persona')
Теперь у нас есть кнопка входа на 403-ей странице и вход работает так, как мы и ожидаем:
Нажав на кнопку входа мы попадаем на форму для входа persona (тут на французском - по вине автора статьи).
images/pyramid-persona/persona.png
После того, как мы залогинемся, страница перезагрузится и всё будет работать ровно так, как мы и ожидаем:
images/pyramid-persona/logged_in.png

Кнопка входа

Итак, мы только что получили прекрасную, практически свободную систему для входа. Ещё лучше было бы иметь кнопочку для входа на любой странице, на которой мы захотим.
Это не тяжело. Тут участвует немного html, так что давайте создадим для этого шаблон. Мы изменим отображение и конфигурацию:
def hello_world(request):
    userid = authenticated_userid(request)
    return {'user': userid}

# ...

    settings = {
        'persona.secret': 'some secret',
        'persona.audiences': 'http://localhost:8080',
        'mako.directories': '.',  # Where to find the template file
    }

# ...

config.add_view(hello_world, route_name='hello', renderer='hello.mako')
И мы создадим файл hello.mako в той же директории:
<html>
<head>
    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
    <script src="https://login.persona.org/include.js" type="text/javascript"></script>
    <script type="text/javascript">${request.persona_js}</script>
</head>
<body>
<h1>Persona test page</h1>
Hello ${user}
${request.persona_button}
</body>
</html>
Нам надо подключить persona api, jquery, и добавить немного javascript, нужного чтобы persona работала (он добавляется при помощи request.persona_js). Мы используем request.persona_button предоставляющий простую кнопку входа/выхода, в зависимости от вашего состояния. Вот результат:
images/pyramid-persona/button_out.png images/pyramid-persona/button_in.png
Кнопку, понятно, можно настроить, ровно как и изменить javascript, если Вы хотите чего-то большего, чем просто перезагрузить страницу. Более подробно - смотрите README.

Заключение

pyramid_persona предоставляет быстрый способ настроить аутентификацию, который можно изменить в зависимости от вашего проекта. Он доступен на pypi, так что его можно установить при помощи pip. Можно ознакомиться с readme и исходниками на github. И, как понятно, сообщения об ошибках и предложения только приветствуются.

Источник

Комментариев нет:

Отправить комментарий