4. Permissions [voting story] – Mastering Plone 6 development – Roundtrip [The voting story] frontend, backend, and REST

Permissions [voting story]

4. Permissions [voting story]#

In this part you will:

  • Add and use permissions

Topics covered:

  • permissions, roles

Adding permissions#

Permissions have a long history, there are two types of permissions.

In Zope2, a permission was just a string.

In ZTK, a permission is an object that gets registered as a Utility.

We must support both, in some cases we have to reference the permission by their Zope2 version, in some by their ZTK Version.

Luckily, there is a zcml statement to register a permission both ways in one step.

See also

The configuration registry was meant to solve a problem, but we will now stumble over a problem that did not get resolved properly.

Our permission is a utility. Our browser views declare this permission as a requirement for viewing them.

When our browser views get registered, the permissions must exist already. If you try to register the permissions after the views, Zope won't start because it doesn't know about the permissions.

Let's add a file permissions.zcml and define three permissions for viewing, editing and clearing votes.

 2  xmlns="http://namespaces.zope.org/zope"
 3  xmlns:zcml="http://namespaces.zope.org/zcml"
 4  i18n_domain="plone">
 6  <configure zcml:condition="installed AccessControl.security">
 8    <permission
 9        id="training.votable.view_vote"
10        title="training.votable: View Votes"
11        />
13    <permission
14        id="training.votable.can_vote"
15        title="training.votable: Can Vote"
16        />
18    <permission
19        id="training.votable.clear_votes"
20        title="training.votable: Clear Votes"
21        />
23  </configure>

In some places we have to reference the Zope 2 permission strings. It is best practice to provide a static variable for this.

We provide this in __init__.py

1ViewVotesPermission = "training.votable: View Votes"
2CanVotePermission = "training.votable: Can Vote"
3ClearVotesPermission = "training.votable: Clear Votes"

Using our permissions#

We can add now restriction on accessing the @votes endpoint POST service in /src/training/votable/api/configure.zcml

 2  xmlns="http://namespaces.zope.org/zope"
 3  xmlns:browser="http://namespaces.zope.org/browser"
 4  i18n_domain="training.votable">
 6  <plone:service
 7    method="POST"
 8    for="training.votable.behaviors.votable.IVotableMarker"
 9    factory=".voting.VotingPost"
10    name="@votes"
11    permission="training.votable.can_vote"
12    />

And we can add a permission check inside Python code on the current user on the context by

 1class VotingDelete(Service):
 2    """Unlock an object"""
 4    def reply(self):
 5        alsoProvides(self.request, IDisableCSRFProtection)
 6        can_clear_votes = api.user.has_permission(
 7            ClearVotesPermission, obj=self.context
 8        )
 9        if not can_clear_votes:
10            raise Unauthorized("User not authorized to clear votes.")
11        voting = IVotable(self.context)
12        voting.clear()
13        return vote_info(self.context, self.request)

Provide defaults#

After protecting services we do now want to assign the permissions to roles. For example the users with role "Reviewer" should be able to vote.

The persistent configuration is managed in profiles/default/rolemap.xml

 1<?xml version="1.0"?>
 3  <permissions>
 5    <permission name="training.votable: View Votes" acquire="True">
 6      <role name="Authenticated"/>
 7      <role name="Site Administrator"/>
 8      <role name="Manager"/>
 9    </permission>
10    <permission name="training.votable: Can Vote" acquire="True">
11      <role name="Reviewer"/>
12    </permission>
13    <permission name="training.votable: Clear Votes" acquire="True">
14      <role name="Site Administrator"/>
15      <role name="Manager"/>
16    </permission>
18  </permissions>