Я действительно плохо понимаю, как работает ACL. Я знаю, что это круто и может сэкономить мне много времени и боли. Но сейчас я немного потерялся. Во всех примерах пирамиды используется обход. Я использую исключительно URL-рассылку. Я не уверен, что понимаю, как я могу построить древовидную структуру ресурсов.
Вот пример кода:
class QuestionFactory(object):
def __init__(self, request):
self.__acl__ = default[:]
self.uid = authenticated_userid(request)
self.qid = request.matchdict.get('id')
if self.qid:
self.question = request.db.questions.find_one({'_id': ObjectId(self.qid)})
if str(self.question.get('owner')) == self.uid:
self.__acl__.append((Allow, userid, 'view'))
Дело в том, что он работает. Но мне нужно определить новую фабрику для каждого типа ресурсов. Я не уверен, как я должен узнать, к какому ресурсу я пытаюсь получить доступ через URL-адрес Dispatch и Factory. Я бы увидел что-то подобное
/accounts/{account} //Owners only but viewable by anyone
/messages/{message} //Owners only
/configs/{config} //Admin only
/pages/{page} //Admins only but viewable by anyone
Но здесь у меня была бы такая структура
Root -\
+-- account
+-- message
+-- config
+-- page
У каждой из этих фабрик есть свой особый acl. Другое дело, что / accounts - это главная страница. У него нет идентификатора или чего-то подобного. Также / accounts / new - тоже особый случай. Это не идентификатор, а представление для создания нового элемента.
Я использую спокойный стиль с требованием GET / PUT / DELETE / POST. Я не совсем уверен, как я должен автоматически сопоставить URL-адрес с ресурсом и с правильным ACL. Если я определю в своем корне специальную фабрику, как указано выше, проблем не будет.
править
Я заставил его работать, за исключением некоторых вещей. Я наконец думаю, что понимаю, в чем цель траверса. Например, у нас есть URL: / comments / 9494f0eda / new, / comments / {comment} / new
У нас может быть узел в нашем дереве ресурсов или даже 3 узла.
Сначала проверяется RootFactory, а затем в соответствии с нашим обходом. Он получит атрибут комментариев RootFactory, затем «comment» фабрики комментариев и «новый» CommentFactory или самого объекта
. Я не использую Factory как dict, как в примере Майкла
. Выглядит красиво. примерно так:
class RessourceFactory(object):
def __init__(self, parent, name):
self.__acl__ = []
self.__name__ = name
self.__parent__ = parent
self.uid = parent.uid
self.locale = parent.locale
self.db = parent.db
self.req = parent.req
Это мой базовый объект-ресурс. На каждом шаге он копирует информацию от родителя к новому дочернему элементу ... Я, конечно, мог бы всплыть в моем атрибуте ... контексте. parent ._ parent _. Uid, но это не так уж и здорово.
Причина, по которой я не использую атрибут dict. Я добавляю, чтобы заставить его работать с
/ comments
По некоторым причинам он действительно создал мою фабрику комментариев, но не вернул ее, так как не было необходимости в ключе.
Итак, моя корневая Factory примерно так выглядит:
class RootFactory(object):
def __init__(self, request):
self.__acl__ = default[:]
self.req = request
self.db = request.db
self.uid = authenticated_userid(request)
self.locale = request.params.get('locale', 'en')
def __getitem__(self, key):
if key == 'questions':
return QuestionFactory(self, 'questions')
elif key == 'pages':
return PageFactory(self, 'pages')
elif key == 'configs':
return ConfigFactory(self, 'configs')
elif key == 'accounts':
return AccountFactory(self, 'accounts')
return self
если элемент не найден, RootFactory возвращает себя, если нет, он возвращает новую Factory. Поскольку я основываю свой код на коде Майкла, есть второй параметр для конструктора Factory. Я не уверен, что сохраню его, поскольку QuestionFactory хорошо знает, как обрабатывать «вопросы», поэтому здесь нет необходимости называть фабрику. Он уже должен знать свое название.
class QuestionFactory(RessourceFactory):
def __init__(self, parent, name):
RessourceFactory.__init__(self, parent, name)
self.__acl__.append((Allow, 'g:admin', 'view'))
self.__acl__.append((Allow, 'g:admin', 'edit'))
self.__acl__.append((Allow, 'g:admin', 'create'))
self.__acl__.append((Allow, 'g:admin', 'delete'))
self.__acl__.append((Allow, Everyone, 'create'))
def __getitem__(self, key):
if key=='read':
return self
self.qid = key
self.question = self.db.questions.find_one({'_id': ObjectId(self.qid)})
if str(self.question.get('owner')) == self.uid:
log.info('Allowd user %s' % self.uid)
self.__acl__.append((Allow, self.uid, 'view'))
self.__acl__.append((Allow, self.uid, 'edit'))
self.__acl__.append((Allow, self.uid, 'delete'))
return self
Вот куда и пойдет почти вся логика. В init я установил acl, который будет работать для / questions в getitem, он будет работать для / questions / {id} / *
Поскольку я возвращаю себя, любой getitem после этого RessourceFactory будет указывать на себя если я не верну новую Фабрику в каком-то особом случае. Причина этого в том, что мой контекст - это не просто объект в базе данных или объект.
Мой контекст обрабатывает множество вещей, таких как идентификатор пользователя, локаль и так далее ... когда acl завершен, у меня есть новый объект контекста, готовый к использованию. Это удаляет большую часть логики в представлениях.
Я, вероятно, мог бы установить события для запроса locale и uid, но здесь это действительно подходит. Если мне нужно что-то новое, мне просто нужно отредактировать свои RootFactory и RessourceFactory, чтобы скопировать их в дочернюю фабрику.
Таким образом, если что-то должно измениться во всех представлениях, не будет никакой избыточности.