How to restrict access rights based on the identifier of one page in the URL?

I am trying to implement Pyramid security features on my website, but I am having some problems with how to use it.

I read this tutorial and this example , as well as Pyramid docs, and I cannot figure out how to implement an authorization policy for single page identifiers.

For example, I have the following URL scheme:

/pages /pages/12 

/pages explicitly lists the available pages, and /pages/:id is the place where you can read / comment on the page.

The documentation / examples I read showed that you can implement group level ACS by providing a groupfinder with a list of groups. For example, editor , admin , etc.

How can I not use the group for permissions, but instead rights based on the page identifier?

In my URL scheme above, when the user browses /pages , they must be logged in. When they browse /pages/:id , they should be granted access to view this particular identifier. Or they must be the owner of this page.

Same as comments. On the page /page/:id they can be granted access to view the page, but not comment on it.

+6
source share
2 answers

The basic principle is that Pyramid security checks the ACL for the current context. In this case, your page will be the logical context to use. The first step is to set up the factory context for the page. Assuming you are using SQLAlchemy and sending URLs, this is easy to do. Register your route as follows:

 config.add_route('page', '/pages/{id:\d+}', factory=page_factory) 

There should be a number in the path for the route that causes the pyramid to check the page identifier, so you do not need to check it yourself. Pay attention to the link to the method * page_factory *. Let's now define this:

 def page_factory(request): return DBSession.query(Page).get(int(request.matchdict['id'])) 

This infers the page id from the route and uses it to search for the page in your database. Please note that we do not check if the identifier can be converted to an integer here: we can avoid this, since the route already checks this directly.

The next step is to configure the ACL on the page. The easiest way is to add the acl property for the Page class:

 from pyramid import security class Page(BaseObject): @property def __acl__(self): return [(security.Allow, self.userid, 'view')] 

This ACL tells the pyramid that only the user with the identifier stored on the .userid page is allowed to view this page. It is important to understand that the ACL is different for each page: it is created for each page separately based on the information in your database; in this case using self.userid.

Now you can use view resolution on your view:

 @view_config(route_name='page', context=Page, permission='view') def page_view(context, request): return 'I can see!' 

This example has a minimum ACL for the page, but you can expand it to fit your needs.

Also pay attention to the context = page parameter for view_config: this tells the pyramid that this view should be used only by context - this is the page. If the factory context (page_factory in this example) did not find a suitable page, it will return None instead of the page instance, so this view will not be used by the pyramid. As a result, the pyramid will automatically produce an error not found.

+6
source

For the purpose of this discussion, I'm going to suggest that you are using SQLAlchemy to interact with your database.

If you have config.add_route('pages', '/pages/{id}') in __init__.py , you can add a custom factory to replace / add the default ACL. For instance:

The current ACL might look like this:

 class RootFactory(object): __acl__ = [ (Allow, Everyone, 'view'), (Allow, Authenticated, 'auth'), ] def __init__(self, request): self.request = request 

This will allow Authenticated users to access any view with the permission of "auth" and anyone who visits your site to access any view with the permission of "view".

Using a custom factory , you can either bypass RootFactory or add it.

To work around , change the original config.add_route file to β†’ config.add_route('pages', '/pages/{id}', factory=PageFactory) and create the PageFactory class as follows:

 class PageFactory(object): __acl__ = [ (Allow, Everyone, 'view'), (Allow, Authenticated, 'auth'), ] def __init__(self, request): self.request = request from pyramid.security import authenticated_userid user_id = authenticated_userid(self.request) thispage = DBSession.query(Page).filter(Page.id==self.request.matchdict['id']).first() if thispage.user_id == user_id: ## Pyramid allows Everyone, Authenticated, and authenticated_userid ## (each of these is known as a Principal) to be in the second ## position of the ACL tuple acl.append((Allow, user_id, 'edit')) 

This assumes your view has permission='edit' as one of its parameters.

Now, if you want to use RootFactory and the add- on with your custom factory , so you don’t need to repeat yourself, just leave you RootFactory, as I showed at the beginning of this post, and inherit from the RootFactory class as such:

 class PageFactory(RootFactory): @property def __acl__(self): acl = super(PageFactory, self).__acl__[:] ##[:] creates a copy from pyramid.security import authenticated_userid user_id = authenticated_userid(self.request) thispage = DBSession.query(Page).filter(Page.id==self.request.matchdict['id']).first() if thispage.user_id == user_id: acl.append((Allow, user_id, 'edit')) return acl 

groupfinder is very useful, by the way, because then you can just place users in groups, such as "admin", and everything in the admin group can access the views using permission='whatever' or permission='whateverelse' that you may need, and Factory is not required, only a groupfinder that returns a list of groups for the current user. Alas, I digress, because this is not what you wanted to do. Hope this answers your question.

+3
source

Source: https://habr.com/ru/post/913835/


All Articles