См.также
Видео:
См.также
В пирамиде система безопасности поделена на 2 части. Первая это аутентификация, которая производит идентификацию пользователя, его проверку (например что он есть в БД и он не заблокирован) и определяет какими правами он наделен. Второе это авторизация, система которая проверяет имеет ли этот пользователь доступ к запрошенному ресурсу.
Примечание
Фреймворк Repoze.bfg имеет расширение repoze.who, которое отвечает за идентификацию и аутентификацию пользователя.
Who? т.е. Кто? ты.
Для авторизации используется расширение repoze.what, которое проверяет какие ресурсы тебе доступны.
What? т.е. Что? доступно тебе.
Несмотря на то, что фреймворк Pyramid это по сути переименованный repoze.bfg, в нем есть собственный механизм авторизации и аутентификации из коробки.
Определение текущего пользователя при поступлении HTTP запроса, это задача аутентификации (authentication policy). Производится она в 3 этапа:
Идентифицируем пользователя путем проверки токенов/заголовков/итд в HTTP
запросе. (см. pyramid.request.Request.unauthenticated_userid
)
Например: ищем auth_token
в куках запроса, проверяем что токен правильно
подписан, и возвращаем id
пользователя.
Подтверждаем статус идентифицированного пользователя. (authenticated_userid
)
Например: проверяем что id
этого пользователя все еще в базе данных и
пользователь еще активен. Пользователя могли удалить из БД, но при этом
в куках браузера хранится валидный токен auth_token
.
Ищем группы (principal) которые принадлежат пользователю и добавляем
их в список. (effective_principals
)
Например: берем из БД группы пользователя и добавляем в список. Для текущего идентифицированного пользователя это может быть: «vasya», «user_admin», «editor».
Каждый ресурс пирамиды может быть защищен правами доступа (permission). Задача авторизации определение того, какие пользователи имеют доступ к ресурсам.
После аутентификации создается список групп пользователя (principal). Политика авторизации (authorization policy) запрещает или разрешает доступ к ресурсу на основании этого списка групп, сверяя его с правами ресурса.
В пирамиде по умолчанию авторизация отключена. Все представления (views
)
полностью доступны анонимным пользователям. Для того чтобы их защитить нужно
добавить в настройки политику безопасности.
Для включения политики авторизации используется метод конфигуратора
pyramid.config.Configurator.set_authorization_policy()
. Для
аутентификации pyramid.config.Configurator.set_authentication_policy()
соответственно. Так-как авторизация не может существовать без аутентификации,
необходимо указывать обе политики в проекте.
from pyramid.config import Configurator
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
authn_policy = AuthTktAuthenticationPolicy('seekrit', hashalg='sha512')
authz_policy = ACLAuthorizationPolicy()
config = Configurator()
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(authz_policy)
Здесь pyramid.authentication.AuthTktAuthenticationPolicy
это механизм
аутентификации пользователя, который ищет его из «auth ticket» cookie.
pyramid.authorization.ACLAuthorizationPolicy
механизм авторизации
по аксесс листам (ACL).
Императивно:
config.add_view('mypackage.views.blog_entry_add_view',
name='add_entry.html',
permission='add')
Декларативно:
from pyramid.view import view_config
from resources import Blog
@view_config(name='add_entry.html', permission='add')
def blog_entry_add_view(request):
""" Add blog entry code goes here """
# ...
Если ресурсу не присвоены права доступа, то используются права по умолчанию.
В пирамиде права по умолчанию (pyramid.security.NO_PERMISSION_REQUIRED
)
подразумевают что ресурсы доступны всем, даже анонимным пользователям.
Это поведение возможно изменить при помощи метода
pyramid.config.Configurator.set_default_permission()
.
config.set_default_permission('my_default_permission')
См.также
Access Control List или ACL — список контроля доступа, который определяет, кто или что может получать доступ к конкретному объекту, и какие именно операции разрешено или запрещено этому субъекту проводить над объектом.
В пирамиде аксесс лист это список содержащий записи, определяющие права индивидуального пользователя или группы на ресурсы проекта. Элементы ACL также еще называют Access Control Entry или ACE.
Например:
from pyramid.security import Allow, Deny
from pyramid.security import Everyone
__acl__ = [
(Deny, 'vasya', 'move'),
(Deny, 'group:blacklist', ('add', 'delete', 'edit')),
(Allow, Everyone, 'view'),
(Allow, 'group:editors', ('add', 'edit')),
(Allow, 'group:editors', 'move'),
(Allow, 'group:deleter', 'delete'),
]
__acl__
из примера выше, это список контроля доступа (ACL).
(Allow, Everyone, 'delete')
это ACE, т.е. запись в ACL.
pyramid.security.Allow
и pyramid.security.Deny
.Также существую специальные группы (principal):
pyramid.security.Everyone
- для всех.pyramid.security.Authenticated
- для аутентифицированнных
пользователей.Если мы захотим запретить все, кроме тех ACE которые в списке, мы можем написать это так:
from pyramid.security import Allow
from pyramid.security import ALL_PERMISSIONS
__acl__ = [(Allow, 'fred', 'view'),
(Deny, Everyone, ALL_PERMISSIONS)]
или воспользоваться встроенным в пирамиду ACE:
from pyramid.security import Allow
from pyramid.security import DENY_ALL
__acl__ = [(Allow, 'fred', 'view'),
DENY_ALL]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.security import Allow
from pyramid.security import Everyone
class HelloFactory(object):
def __init__(self, request):
self.__acl__ = [
(Allow, Everyone, 'view'),
(Allow, 'group:editors', 'add'),
(Allow, 'group:editors', 'edit'),
]
def hello_world(request):
return Response('Hello %(name)s!' % request.matchdict)
if __name__ == '__main__':
authn_policy = AuthTktAuthenticationPolicy('seekrit', hashalg='sha512')
authz_policy = ACLAuthorizationPolicy()
config = Configurator()
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(authz_policy)
config.add_route('hello', '/hello/{name}',
factory=HelloFactory)
config.add_view(hello_world,
route_name='hello',
permission='view')
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 8080, app)
server.serve_forever() |
См.также
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.security import Allow
from pyramid.security import Everyone
class HelloFactory(object):
def __init__(self, request):
self.__acl__ = [
(Allow, Everyone, 'view'),
(Allow, 'group:editors', 'add'),
(Allow, 'group:editors', 'edit'),
]
def hello_world(request):
return Response('Hello %(name)s!' % request.matchdict)
if __name__ == '__main__':
authn_policy = AuthTktAuthenticationPolicy('seekrit', hashalg='sha512')
authz_policy = ACLAuthorizationPolicy()
config = Configurator(root_factory=HelloFactory)
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(authz_policy)
config.add_route('hello', '/hello/{name}')
config.add_view(hello_world,
route_name='hello',
permission='view')
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 8080, app)
server.serve_forever() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | from wsgiref.simple_server import make_server
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.config import Configurator
from pyramid.httpexceptions import HTTPFound
from pyramid.response import Response
from pyramid.security import Allow, forget, remember
class HelloFactory(object):
def __init__(self, request):
self.__acl__ = [
(Allow, 'vasya', 'view'),
(Allow, 'group:editors', 'add'),
(Allow, 'group:editors', 'edit'),
]
def hello_world(request):
return Response('Hello %(name)s!' % request.matchdict)
def login(request):
headers = remember(request, 'vasya')
return HTTPFound(location=request.route_url('hello', name='vasya'),
headers=headers)
def logout(request):
headers = forget(request)
return HTTPFound(location=request.route_url('hello', name='log out!!!'),
headers=headers)
if __name__ == '__main__':
authn_policy = AuthTktAuthenticationPolicy('seekrit', hashalg='sha512')
authz_policy = ACLAuthorizationPolicy()
config = Configurator(root_factory=HelloFactory)
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(authz_policy)
config.add_route('hello', '/hello/{name}')
config.add_view(hello_world,
route_name='hello',
permission='view')
# login form
config.add_route('login', '/login')
config.add_route('logout', '/logout')
config.add_view(login, route_name='login')
config.add_view(logout, route_name='logout')
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 8080, app)
server.serve_forever() |
from __future__ import absolute_import
from waitress import serve
from pyramid.config import Configurator
from pyramid.response import Response
from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
from pyramid.security import Authenticated, Allow, Everyone
from pyramid.authorization import ACLAuthorizationPolicy
class Root(object):
__acl__ = [
(Allow, Authenticated, 'view'),
]
def __init__(self, request):
self.request = request
def checkauth(username, password):
return username == 'pyramid' and password == 'aliens'
class BasicAuthenticationPolicy(object):
def authenticated_userid(self, request):
authorization = AUTHORIZATION(request.environ)
if not authorization:
return None
(authmeth, auth) = authorization.split(' ', 1)
if 'basic' != authmeth.lower():
return None
auth = auth.strip().decode('base64')
username, password = auth.split(':', 1)
if not checkauth(username, password):
return None
return username
def effective_principals(self, request):
ep = [Everyone]
username = self.authenticated_userid(request)
if username is not None:
ep.append(Authenticated)
ep.append(username)
ep.append('g:admin')
return ep
def unauthenticated_userid(self, request):
authorization = AUTHORIZATION(request.environ)
if not authorization:
return None
(authmeth, auth) = authorization.split(' ', 1)
if 'basic' != authmeth.lower():
return None
auth = auth.strip().decode('base64')
username, password = auth.split(':', 1)
return username
def remember(self, request, principal, **kw):
return []
def forget(self, request):
return []
def forbidden_view(request):
head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % 'fnord')
return Response('Not Authorized', status='401 Not Authorized', headers=head)
def hello_world(request):
return Response('Hello {!r}!'.format(request.effective_principals))
if __name__ == '__main__':
config = Configurator(root_factory=Root)
config.add_route('hello', '/hello')
config.add_view(hello_world, route_name='hello', permission='view')
config.set_authentication_policy(BasicAuthenticationPolicy())
config.set_authorization_policy(ACLAuthorizationPolicy())
config.add_forbidden_view(forbidden_view)
app = config.make_wsgi_app()
serve(app, host='0.0.0.0', port=8080)