Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Roles #15

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ repoze.workflow Changelog
1.2 (unreleased)
----------------

- support for role guards in transitions
- TBD

1.1 (2020-07-01)
Expand Down
23 changes: 23 additions & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ workflow.
permission="moderate"
/>

<transition
name="private_to_public_by_role"
from_state="private"
to_state="public">
<role name="admin"/>
<role name="editor"/>
</transition>

</workflow>

</configure>
Expand Down Expand Up @@ -164,6 +172,14 @@ attributes:
the current user implied by the request has the permission in the
``context``, ``False`` otherwise.

``roles_checker``

A Python dotted-name referring to a permission checking function.
This function should accept three arguments: ``roles`` (a list of
string), ``context`` and ``request``. It should return ``True`` if
the current user implied by the request has at least one of the roles
in the ``context``, ``False`` otherwise.

A ``workflow`` tag may contain ``transition`` and ``state`` tags. A
workflow declared via ZCML is unique amongst all workflows defined if
the combination of its ``type``, its ``content_types`` and its
Expand Down Expand Up @@ -323,6 +339,13 @@ The ``alias`` tag may only be used within a ``state`` tag. The
aliases*, it will be considered to be in that state, according to
e.g. ``workflow.state_of``, etc.

The ``role`` Tag
----------------
The ``role`` tag which may occur within the ``transition`` tag allows
to specify role guards. The only attribute available is ``name``.
The roles specified within a ``transition`` are passed as a list of strings
to the ``roles_checker`` which can be specified for the workflow.

.. _callbacks:

Callbacks
Expand Down
8 changes: 4 additions & 4 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ use some of these APIs you need a "request" object. This object is
available in :mod:`repoze.bfg` views as the "request" parameter to the
view. Your web framework may have another kind of request object
obtained from another place. If none of your workflows use a
``permission_checker``, you can pass ``None`` as the request object.
``permission_checker`` or ``roles_checker``, you can pass ``None`` as the request object.

Here is how you initialize a piece of content to the initial workflow
state:
Expand Down Expand Up @@ -180,7 +180,7 @@ current
transitions

A sequence of transition dictionaries; if any of the transitions is
not allowed due to a permission violation, it will not show up in
not allowed due to a permission violation or insuficient roles, it will not show up in
this list.

You can also obtain state information about a nonexistent object
Expand All @@ -193,9 +193,9 @@ content object) using ``state_info``:
state_info = workflow.state_info(None, request, context=someotherobject)

This will return the same list of dictionaries, except the ``current``
flag will always be false. Permissions used to compute the allowed
flag will always be false. Permissions and roles used to compute the allowed
transitions will be computed against the ``context`` (the ``context``
will be passed to the permission checker instead of any particular
will be passed to the permission checker resp. the roles checker instead of any particular
content object).

You can obtain transition information for a piece of content using the
Expand Down
8 changes: 8 additions & 0 deletions repoze/workflow/meta.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@
handler="repoze.workflow.zcml.guard_function"
/>

<meta:directive
name="role"
namespace="http://namespaces.repoze.org/bfg"
usedIn="repoze.workflow.zcml.ITransitionDirective"
schema="repoze.workflow.zcml.IRoleDirective"
handler="repoze.workflow.zcml.role"
/>

<meta:groupingDirective
name="state"
namespace="http://namespaces.repoze.org/bfg"
Expand Down
113 changes: 62 additions & 51 deletions repoze/workflow/tests/fixtures/configure.zcml
Original file line number Diff line number Diff line change
@@ -1,54 +1,65 @@
<configure xmlns="http://namespaces.repoze.org/bfg">

<include package="repoze.workflow" file="meta.zcml"/>

<workflow
type="security"
name="the workflow"
description="The workflow which is of the testing fixtures package"
state_attr="state"
initial_state="private"
content_types=".dummy.IContent .dummy.IContent2"
elector=".dummy.elector"
permission_checker=".dummy.has_permission"
>

<state name="private"
callback=".dummy.callback" title="Private">
<key name="description" value="Nobody can see it"/>
<alias name="supersecret"/>
</state>

<state name="public"
callback=".dummy.callback" title="Public">
<key name="description" value="Everybody can see it"/>
</state>

<transition
name="public_to_private"
callback=".dummy.callback"
from_state="public"
to_state="private"
permission="moderate"
/>

<transition
name="unavailable_public_to_private"
callback=".dummy.callback"
from_state="public"
to_state="private"
permission="moderate">
<guard function=".dummy.never" />
</transition>

<transition
name="private_to_public"
callback=".dummy.callback"
from_state="private"
to_state="public"
permission="moderate"
/>

</workflow>

<include package="repoze.workflow" file="meta.zcml"/>

<workflow
type="security"
name="the workflow"
description="The workflow which is of the testing fixtures package"
state_attr="state"
initial_state="private"
content_types=".dummy.IContent .dummy.IContent2"
elector=".dummy.elector"
permission_checker=".dummy.has_permission"
roles_checker=".dummy.has_role"
>

<state name="private"
callback=".dummy.callback" title="Private">
<key name="description" value="Nobody can see it"/>
<alias name="supersecret"/>
</state>

<state name="public"
callback=".dummy.callback" title="Public">
<key name="description" value="Everybody can see it"/>
</state>

<transition
name="public_to_private"
callback=".dummy.callback"
from_state="public"
to_state="private"
permission="moderate"
/>

<transition
name="unavailable_public_to_private"
callback=".dummy.callback"
from_state="public"
to_state="private"
permission="moderate">
<guard function=".dummy.never"/>
</transition>

<transition
name="private_to_public"
callback=".dummy.callback"
callback_after=".dummy.callback_after"
from_state="private"
to_state="public"
permission="moderate"
/>

<transition
name="private_to_public_by_role"
from_state="private"
to_state="public"
>
<role name="admin"/>
<role name="editor"/>
</transition>

</workflow>

</configure>
6 changes: 6 additions & 0 deletions repoze/workflow/tests/fixtures/dummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,16 @@ class Content(object):
def callback(context, transition):
""" """

def callback_after(context, transition):
""""""

def never(context, transition): # pragma: NO COVER
raise WorkflowError("This is never allowed")

def elector(context): return True

def has_permission(permission, context, request):
""" """

def has_role(roles, context, request):
""""""
Loading