I really have little idea how an ACL works. I know this is pretty cool and can save a lot of time and pain. But for now, I'm a little lost. All examples for the pyramid use a bypass. I exclusively use the URL manager. I am not sure to understand how I can build a ressource tree structure.
Here is a sample code:
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'))
The fact is that it works. But I need to define a new factory for each type of ressource. I'm not sure how I should know which ressource I'm trying to access the Dispatch and Factory URLs. I would see something like this
/accounts/{account} //Owners only but viewable by anyone /messages/{message} //Owners only /configs/{config} //Admin only /pages/{page} //Admins only but viewable by anyone
Here I would call such a structure
Root -\ +-- account +-- message +-- config +-- page
Each of these factory has its own acl. Another thing is that / accounts is the main page. It does not have an identifier or anything else. Also / account / new is also a special case. This is not an identifier, but an idea of creating a new element.
I use a calm style with a GET / PUT / DELETE / POST requirement. I am not sure how I should automatically match the url with ressource and with the correct acl. If I define a special factory in my root as described above, there are no problems.
change
I managed to work, except for some things. I finally think I understand what the purpose of the passage is. For example, we have this url: / comments / 9494f0eda / new, / Comments / {comment} / new
We may need Node in our registry tree or even in three nodes.
RootFactory will be checked first, and then, according to our workaround. It will receive the comment attribute RootFactory, then the “comment” of the factory comment and the “new” CommentFactory or the object itself
I do not use factory as a dict, as in Michael's example
It looks something like this:
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
This is my base ressource object. At each step, it copies information from the parent to the new child .. I could, of course, create my own attribute .. context. parent ._ parent_.uid, but that's just not so good.
The reason I am not using the dict attribute. I add that it works with
/ comments
For some reason, he created my CommentFactory, but did not return it, since there was no need for a key.
So my factory root looks something like this:
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
If the item is not found, RootFactory returns itself; if not, it returns a new Factory. Since I base my code on Michael's code, there is a second parameter for the factory constructor. I'm not sure it should be like QuestionFactory, well versed in "questions", so there is no need to call factory here. He must know his name.
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
So almost all logic will go. In init, I install acl, which will work for / questions in getitem, it will work for / questions / {id} / *
Since I am returning myself, any getitem past this RessourceFactory will point to itself unless I return a new factory for some special case. The reason is because my context is not just an object in a database or object.
My context handles several elements, such as user id, locale, etc .... when acl is done, I have a new context object ready to use. It removes most of the logic in the views.
I could probably set events for the locale and uid request, but it really fits here. If I need something new, I just need to edit RootFactory and RessourceFactory to copy them to a child Factory.
Thus, if something should change in all respects, there is no redundancy at all.