= Custom Permission Policies [[PageOutline(2-5,Contents,pullout)]] Permission policies were introduced on the [TracFineGrainedPermissions#PermissionPolicies TracFineGrainedPermissions] page. Custom policies can often be implemented with a short plugin. Some custom permission policy examples are given on this page. == Restrict a Workflow Action to the Ticket Owner This permissions policy can be used to restrict a workflow action to the ticket owner that possesses `TICKET_CHANGE_STATE`. User with `TICKET_ADMIN` can perform the action even if they are not the owner. To install and activate the plugin: 1. Create a [TracDev/PluginDevelopment#Singlefileplugins single file plugin] that implements [wiki:TracDev/PluginDevelopment/ExtensionPoints/trac.perm.IPermissionPolicy IPermissionPolicy] and [wiki:TracDev/PluginDevelopment/ExtensionPoints/trac.perm.IPermissionRequestor IPermissionRequestor]: {{{#!python # -*- coding: utf-8 -*- # # Copyright (C) 2014-2017 Edgewall Software # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. from trac.core import * from trac.perm import IPermissionPolicy, IPermissionRequestor from trac.resource import ResourceNotFound from trac.ticket.model import Ticket class RestrictTicketActionsPolicy(Component): """Provides a permission for restricting ticket actions to the ticket owner. """ implements(IPermissionPolicy, IPermissionRequestor) _field = 'owner' # IPermissionRequestor methods def get_permission_actions(self): return ['TICKET_CHANGE_STATE'] # IPermissionPolicy methods def check_permission(self, action, username, resource, perm): if action == 'TICKET_CHANGE_STATE' and \ resource is not None and \ resource.realm == 'ticket' and \ resource.id is not None: try: ticket = Ticket(self.env, resource.id) except ResourceNotFound: pass else: if ticket[self._field] != username: return 'TICKET_ADMIN' in perm }}} 1. Edit the `permission_policies` option in the [TracIni#trac-section "[trac]"] section of trac.ini, adding the `RestrictTicketActionsPolicy` component ''before'' the default [TracPermissions permission] policy: {{{#!ini [trac] permission_policies = RestrictTicketActionsPolicy, ... }}} 1. Require `TICKET_CHANGE_STATE` for one or more workflow actions. For example, the [TracWorkflow#Environmentscreatedwith0.11 default workflow] could be modified so that only the ticket owner can assign tickets: {{{#!diff -reassign.permissions = TICKET_MODIFY +reassign.permissions = TICKET_CHANGE_STATE }}} 1. Grant the `TICKET_CHANGE_STATE` permission to your users. The permission policy can be applied to the ticket reporter rather than the ticket owner by changing the `_field` class attribute from `'owner'` to `'reporter'`. == Grant a permission to the Ticket Owner This permissions policy can be used to grant permissions to the ticket's owner. To install and activate the plugin: 1. Create a [TracDev/PluginDevelopment#Singlefileplugins single file plugin] that implements [wiki:TracDev/PluginDevelopment/ExtensionPoints/trac.perm.IPermissionPolicy IPermissionPolicy]: {{{#!python # -*- coding: utf-8 -*- # # Copyright (C) 2014-2017 Edgewall Software # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. from trac.core import * from trac.perm import IPermissionPolicy from trac.resource import ResourceNotFound from trac.ticket.model import Ticket class GrantTicketOwnerPermissionsPolicy(Component): """Grants permissions to the ticket owner.""" implements(IPermissionPolicy) allowed_actions = ( 'TICKET_CHGPROP', 'TICKET_EDIT_CC', 'TICKET_EDIT_DESCRIPTION', 'TICKET_EDIT_COMMENT') # IPermissionPolicy methods def check_permission(self, action, username, resource, perm): if action in self.allowed_actions and \ resource is not None and \ resource.realm == 'ticket' and \ resource.id is not None: try: ticket = Ticket(self.env, resource.id) except ResourceNotFound: pass else: if ticket['owner'] == username: return True }}} 1. Edit the `permission_policies` option in the [TracIni#trac-section "[trac]"] section of trac.ini, adding the `GrantTicketOwnerPermissionsPolicy` component ''before'' the default [TracPermissions permission] policy: {{{#!ini [trac] permission_policies = GrantTicketOwnerPermissionsPolicy, ... }}} ==== Variations * Remove permissions from the `allowed_actions` or add others. * Change `ticket['owner']` to `ticket['reporter']` to grant the permissions to the Ticket Reporter instead. == Support Desk Policy This permission policy allows users to view only tickets they have reported. To install and activate the plugin: 1. Create a [TracDev/PluginDevelopment#Singlefileplugins single file plugin] that implements [wiki:TracDev/PluginDevelopment/ExtensionPoints/trac.perm.IPermissionPolicy IPermissionPolicy]: {{{#!python # -*- coding: utf-8 -*- # # Copyright (C) 2017 Edgewall Software # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. from trac.core import * from trac.perm import IPermissionPolicy, IPermissionRequestor from trac.resource import ResourceNotFound from trac.ticket.model import Ticket class SupportDeskPolicy(Component): """Provides a permission for restricting ticket actions to the ticket owner. """ implements(IPermissionPolicy, IPermissionRequestor) # IPermissionRequestor methods def get_permission_actions(self): return ['TICKET_VIEW_REPORTED'] # IPermissionPolicy methods def check_permission(self, action, username, resource, perm): if username != 'anonymous' and \ action == 'TICKET_VIEW' and \ 'TICKET_ADMIN' not in perm: if 'TICKET_VIEW_REPORTED' in perm: if resource is None or \ resource.realm == 'ticket' and \ resource.id is None: return True elif resource.realm == 'ticket' and \ resource.id is not None: try: ticket = Ticket(self.env, resource.id) except ResourceNotFound: pass else: return username in (ticket['reporter'], ticket['owner']) }}} 1. Edit the `permission_policies` option in the [TracIni#trac-section "[trac]"] section of trac.ini, adding the `SupportDeskPolicy` component ''before'' the default [TracPermissions permission] policy: {{{#!ini [trac] permission_policies = SupportDeskPolicy, ... }}} 1. Revoke `TICKET_VIEW` and grant `TICKET_VIEW_REPORTED` for users that should only view tickets they reported. Users with `TICKET_VIEW` can view all tickets, provided they don't also have `TICKET_VIEW_REPORTED`. 1. Grant other permissions such as `TICKET_CHGPROP`, `TICKET_APPEND` or `TICKET_MODIFY`. Users can only change tickets they can view, therefore you'll only be granting these permissions for tickets the user reported. == Restrict Tickets Per Milestone Milestones access can be restricted per-milestone using [TracFineGrainedPermissions#AuthzPolicy AuthzPolicy]. This permission policy further allows users to only view tickets that are assigned to viewable milestones. To install and activate the plugin: 1. Create a [TracDev/PluginDevelopment#Singlefileplugins single file plugin] that implements [wiki:TracDev/PluginDevelopment/ExtensionPoints/trac.perm.IPermissionPolicy IPermissionPolicy]: {{{#!python # -*- coding: utf-8 -*- # # Copyright (C) 2019 Edgewall Software # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at https://trac.edgewall.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at https://trac.edgewall.org/log/. from trac.core import * from trac.perm import IPermissionPolicy from trac.resource import ResourceNotFound from trac.ticket.model import Ticket class TicketMilestonePolicy(Component): """Provides a permission for restricting ticket access to viewable milestones. """ implements(IPermissionPolicy) # IPermissionPolicy methods def check_permission(self, action, username, resource, perm): if action == 'TICKET_VIEW': if resource is None or \ resource.realm == 'ticket' and \ resource.id is None: return True elif resource.realm == 'ticket' and \ resource.id is not None: try: ticket = Ticket(self.env, resource.id) except ResourceNotFound: pass else: return 'MILESTONE_VIEW' in perm('milestone', ticket['milestone']) }}} 1. Edit [[TracIni#trac-permission_policies-option|[trac] permission_policies]] in trac.ini, adding the `TicketMilestonePolicy` and `AuthzPolicy` components ''before'' the default [TracPermissions permission] policy: {{{#!ini [trac] permission_policies = TicketMilestonePolicy, AuthzPolicy, ... }}} 1. Revoke the coarse-grained `MILESTONE_VIEW` and configure [TracFineGrainedPermissions#AuthzPolicy AuthzPolicy] with a policy that grants `MILESTONE_VIEW` per milestone-resource: {{{#!ini [milestone:milestone1] user1 = MILESTONE_VIEW [milestone:milestone2] user2 = MILESTONE_VIEW }}} 1. Grant `TICKET_VIEW` to any users that you wish to view some (but not necessarily all) tickets. The `TicketMilestonePolicy` will deny access to a ticket if the users isn't allowed to view the associated milestone for the ticket, even though the user has coarse-grained `TICKET_VIEW`. 1. Grant other permissions such as `TICKET_CHGPROP`, `TICKET_APPEND` or `TICKET_MODIFY`. Users can only change tickets they can view, therefore you'll only be granting these permissions for tickets assigned to milestones they can view. == Restrict Changing Ticket Milestone With the default permission policies enabled, a user with `TICKET_CHGPROP` and `MILESTONE_VIEW` can change the milestone of a ticket. Since 1.3.2 the behavior is implemented in the permission policy `DefaultTicketPolicy`. The action `TICKET_CHG_MILESTONE` is used to grant the privilege, however this permission is not defined through an `IPermissionRequestor` implementation and therefore not available to be assigned to users. The action can be defined using ExtraPermissionsProvider, and with no other changes to the permission policy, changing the milestone property will be restricted to users with the `TICKET_CHG_MILESTONE` permission. {{{#!ini [extra-permissions] _perms = TICKET_CHG_MILESTONE }}} See also: #8778. ---- See also: [CookBook/Configuration/SignedTickets#Readonlylogic ReadonlySignedTickets policy], [gmessage:trac-users:0XDa9f9fAos/PFsiA32GLJAJ mailing list discussion] about !RestrictTicketActionsPolicy