| 1 | = Fine grained permissions |
| 2 | [[PageOutline(2-5, Contents, floated)]] |
| 3 | [[TracGuideToc]] |
| 4 | |
| 5 | There is a general mechanism in place that allows custom **permission policies** to grant or deny any action on any Trac resource, or even specific versions of a resource. |
| 6 | |
| 7 | That mechanism is `AuthzPolicy`, an optional component in `tracopt.perm.authz_policy.*` which is not activated by default. It can be activated via the //Plugins// panel in the Trac administration module. |
| 8 | |
| 9 | See TracPermissions for a more general introduction to Trac permissions and permission policies. |
| 10 | |
| 11 | == Permission Policies |
| 12 | |
| 13 | A great diversity of permission policies can be implemented and Trac comes with a few examples. |
| 14 | |
| 15 | The active policies are determined by a [TracIni#trac-permission_policies-option configuration setting]: |
| 16 | |
| 17 | {{{#!ini |
| 18 | [trac] |
| 19 | permission_policies = DefaultWikiPolicy, |
| 20 | DefaultTicketPolicy, |
| 21 | DefaultPermissionPolicy, |
| 22 | LegacyAttachmentPolicy |
| 23 | }}} |
| 24 | |
| 25 | * [#DefaultWikiPolicyandDefaultTicketPolicy DefaultWikiPolicy] controls readonly access to wiki pages. |
| 26 | * [#DefaultWikiPolicyandDefaultTicketPolicy DefaultTicketPolicy] provides elevated privileges in the ticket system for authenticated users. |
| 27 | * !DefaultPermissionPolicy checks for the traditional coarse-grained permissions described in TracPermissions. |
| 28 | * !LegacyAttachmentPolicy uses the coarse-grained permissions to check permissions on attachments. |
| 29 | |
| 30 | Among the optional choices, there is [#AuthzPolicy], a very generic permission policy, based on an Authz-style system. See |
| 31 | [trac:source:branches/1.4-stable/tracopt/perm/authz_policy.py authz_policy.py] for details. |
| 32 | |
| 33 | Another permission policy [#AuthzSourcePolicy], uses the [http://svnbook.red-bean.com/nightly/en/svn.serverconfig.pathbasedauthz.html path-based authorization] defined by Subversion to enforce permissions on the version control system. |
| 34 | |
| 35 | See also [trac:source:branches/1.4-stable/sample-plugins/permissions sample-plugins/permissions] for more examples. |
| 36 | |
| 37 | === !AuthzPolicy |
| 38 | ==== Configuration |
| 39 | * Put an empty conf file (`authzpolicy.conf`) in a secure location on the server, not readable by users other than the webuser. If the file contains non-ASCII characters, the UTF-8 encoding should be used. |
| 40 | * Update your `trac.ini`: |
| 41 | 1. modify the [TracIni#trac-permission_policies-option permission_policies] option in the `[trac]` section: |
| 42 | {{{#!ini |
| 43 | [trac] |
| 44 | permission_policies = AuthzPolicy, DefaultWikiPolicy, DefaultTicketPolicy, DefaultPermissionPolicy, LegacyAttachmentPolicy |
| 45 | }}} |
| 46 | 1. add a new `[authz_policy]` section and point the `authz_file` option to the conf file: |
| 47 | {{{#!ini |
| 48 | [authz_policy] |
| 49 | authz_file = /some/trac/env/conf/authzpolicy.conf |
| 50 | }}} |
| 51 | 1. enable the plugin through [/admin/general/plugin WebAdmin] or by editing the `[components]` section: |
| 52 | {{{#!ini |
| 53 | [components] |
| 54 | tracopt.perm.authz_policy.* = enabled |
| 55 | }}} |
| 56 | |
| 57 | ==== Usage Notes |
| 58 | |
| 59 | Note the order in which permission policies are specified: policies are implemented in the sequence provided and therefore may override earlier policy specifications. |
| 60 | |
| 61 | A policy will return either `True`, `False` or `None` for a given permission check. `True` is returned if the policy explicitly grants the permission. `False` is returned if the policy explicitly denies the permission. `None` is returned if the policy is unable to either grant or deny the permission. |
| 62 | |
| 63 | NOTE: Only if the return value is `None` will the ''next'' permission policy be consulted. If none of the policies explicitly grants the permission, the final result will be `False`, i.e. permission denied. |
| 64 | |
| 65 | The `authzpolicy.conf` file is a `.ini` style configuration file: |
| 66 | {{{#!ini |
| 67 | [wiki:PrivatePage@*] |
| 68 | john = WIKI_VIEW, !WIKI_MODIFY |
| 69 | jack = WIKI_VIEW |
| 70 | * = |
| 71 | }}} |
| 72 | * Each section of the config is a glob pattern used to match against a Trac resource descriptor. These descriptors are in the form: |
| 73 | {{{ |
| 74 | <realm>:<id>@<version>[/<realm>:<id>@<version> ...] |
| 75 | }}} |
| 76 | |
| 77 | Resources are ordered left to right, from parent to child. If any component is inapplicable, `*` is substituted. If the version pattern is not specified explicitly, all versions (`@*`) is added implicitly. Example: Match the WikiStart page: |
| 78 | {{{#!ini |
| 79 | [wiki:*] |
| 80 | [wiki:WikiStart*] |
| 81 | [wiki:WikiStart@*] |
| 82 | [wiki:WikiStart] |
| 83 | }}} |
| 84 | |
| 85 | Example: Match the attachment `wiki:WikiStart@117/attachment:FOO.JPG@*` on WikiStart: |
| 86 | {{{#!ini |
| 87 | [wiki:*] |
| 88 | [wiki:WikiStart*] |
| 89 | [wiki:WikiStart@*] |
| 90 | [wiki:WikiStart@*/attachment:*] |
| 91 | [wiki:WikiStart@117/attachment:FOO.JPG] |
| 92 | }}} |
| 93 | |
| 94 | * Sections are checked against the current Trac resource descriptor '''IN ORDER''' of appearance in the configuration file. '''ORDER IS CRITICAL'''. |
| 95 | |
| 96 | * Once a section matches, the current username is matched against the keys (usernames) of the section, '''IN ORDER'''. |
| 97 | * If a key (username) is prefixed with a `@`, it is treated as a group. |
| 98 | * If a value (permission) is prefixed with a `!`, the permission is denied rather than granted. |
| 99 | |
| 100 | The username will match any of 'anonymous', 'authenticated', <username> or '*', using normal Trac permission rules. |
| 101 | |
| 102 | || '''Note:''' Other groups which are created by user (e.g. by 'adding subjects to groups' on web interface page //Admin / Permissions//) cannot be used. See [trac:#5648] for details about this missing feature. || |
| 103 | |
| 104 | For example, if the `authz_file` contains: |
| 105 | {{{#!ini |
| 106 | [wiki:WikiStart@*] |
| 107 | * = WIKI_VIEW |
| 108 | |
| 109 | [wiki:PrivatePage@*] |
| 110 | john = WIKI_VIEW |
| 111 | * = !WIKI_VIEW |
| 112 | }}} |
| 113 | and the default permissions are set like this: |
| 114 | {{{ |
| 115 | john WIKI_VIEW |
| 116 | jack WIKI_VIEW |
| 117 | # anonymous has no WIKI_VIEW |
| 118 | }}} |
| 119 | |
| 120 | Then: |
| 121 | * All versions of WikiStart will be viewable by everybody, including anonymous |
| 122 | * !PrivatePage will be viewable only by john |
| 123 | * other pages will be viewable only by john and jack |
| 124 | |
| 125 | Groups: |
| 126 | {{{#!ini |
| 127 | [groups] |
| 128 | admins = john, jack |
| 129 | devs = alice, bob |
| 130 | |
| 131 | [wiki:Dev@*] |
| 132 | @admins = TRAC_ADMIN |
| 133 | @devs = WIKI_VIEW |
| 134 | * = |
| 135 | |
| 136 | [*] |
| 137 | @admins = TRAC_ADMIN |
| 138 | * = |
| 139 | }}} |
| 140 | |
| 141 | Then: |
| 142 | - everything is blocked (whitelist approach), but |
| 143 | - admins get all TRAC_ADMIN everywhere and |
| 144 | - devs can view wiki pages. |
| 145 | |
| 146 | Some repository examples (Browse Source specific): |
| 147 | {{{#!ini |
| 148 | # A single repository: |
| 149 | [repository:test_repo@*] |
| 150 | john = BROWSER_VIEW, FILE_VIEW |
| 151 | # John has BROWSER_VIEW and FILE_VIEW for the entire test_repo |
| 152 | |
| 153 | # The default repository (requires Trac 1.0.2 or later): |
| 154 | [repository:@*] |
| 155 | john = BROWSER_VIEW, FILE_VIEW |
| 156 | # John has BROWSER_VIEW and FILE_VIEW for the entire default repository |
| 157 | |
| 158 | # All repositories: |
| 159 | [repository:*@*] |
| 160 | jack = BROWSER_VIEW, FILE_VIEW |
| 161 | # Jack has BROWSER_VIEW and FILE_VIEW for all repositories |
| 162 | }}} |
| 163 | |
| 164 | Very granular repository access: |
| 165 | {{{#!ini |
| 166 | # John has BROWSER_VIEW and FILE_VIEW access to trunk/src/some/location/ only |
| 167 | [repository:test_repo@*/source:trunk/src/some/location/*@*] |
| 168 | john = BROWSER_VIEW, FILE_VIEW |
| 169 | |
| 170 | # John has BROWSER_VIEW and FILE_VIEW access to only revision 1 of all files at trunk/src/some/location only |
| 171 | [repository:test_repo@*/source:trunk/src/some/location/*@1] |
| 172 | john = BROWSER_VIEW, FILE_VIEW |
| 173 | |
| 174 | # John has BROWSER_VIEW and FILE_VIEW access to all revisions of 'somefile' at trunk/src/some/location only |
| 175 | [repository:test_repo@*/source:trunk/src/some/location/somefile@*] |
| 176 | john = BROWSER_VIEW, FILE_VIEW |
| 177 | |
| 178 | # John has BROWSER_VIEW and FILE_VIEW access to only revision 1 of 'somefile' at trunk/src/some/location only |
| 179 | [repository:test_repo@*/source:trunk/src/some/location/somefile@1] |
| 180 | john = BROWSER_VIEW, FILE_VIEW |
| 181 | }}} |
| 182 | |
| 183 | Note: In order for Timeline to work/visible for John, we must add CHANGESET_VIEW to the above permission list. |
| 184 | |
| 185 | ==== Missing Features |
| 186 | Although possible with the !DefaultPermissionPolicy handling (see Admin panel), fine-grained permissions still miss those grouping features (see [trac:#9573], [trac:#5648]). Patches are partially available, see authz_policy.2.patch, part of [trac:ticket:6680 #6680]. |
| 187 | |
| 188 | You cannot do the following: |
| 189 | {{{#!ini |
| 190 | [groups] |
| 191 | team1 = a, b, c |
| 192 | team2 = d, e, f |
| 193 | team3 = g, h, i |
| 194 | departmentA = team1, team2 |
| 195 | }}} |
| 196 | |
| 197 | Permission groups are not supported either, so you cannot do the following: |
| 198 | {{{#!ini |
| 199 | [groups] |
| 200 | permission_level_1 = WIKI_VIEW, TICKET_VIEW |
| 201 | permission_level_2 = permission_level_1, WIKI_MODIFY, TICKET_MODIFY |
| 202 | [*] |
| 203 | @team1 = permission_level_1 |
| 204 | @team2 = permission_level_2 |
| 205 | @team3 = permission_level_2, TICKET_CREATE |
| 206 | }}} |
| 207 | |
| 208 | === !AuthzSourcePolicy (`mod_authz_svn`-like permission policy) #AuthzSourcePolicy |
| 209 | |
| 210 | `AuthzSourcePolicy` can be used for restricting access to the repository. Granular permission control needs a definition file, which is the one used by Subversion's `mod_authz_svn`. |
| 211 | More information about this file format and about its usage in Subversion is available in the [http://svnbook.red-bean.com/en/1.7/svn.serverconfig.pathbasedauthz.html Path-Based Authorization] section in the Server Configuration chapter of the svn book. |
| 212 | |
| 213 | Example: |
| 214 | {{{#!ini |
| 215 | [/] |
| 216 | * = r |
| 217 | |
| 218 | [/branches/calc/bug-142] |
| 219 | harry = rw |
| 220 | sally = r |
| 221 | |
| 222 | [/branches/calc/bug-142/secret] |
| 223 | harry = |
| 224 | }}} |
| 225 | |
| 226 | * '''/''' = ''Everyone has read access by default'' |
| 227 | * '''/branches/calc/bug-142''' = ''harry has read/write access, sally read only'' |
| 228 | * '''/branches/calc/bug-142/secret''' = ''harry has no access, sally has read access (inherited as a sub folder permission)'' |
| 229 | |
| 230 | ==== Trac Configuration |
| 231 | |
| 232 | To activate granular permissions you __must__ specify the {{{authz_file}}} option in the `[svn]` section of trac.ini. If this option is set to null or not specified, the permissions will not be used. |
| 233 | |
| 234 | {{{#!ini |
| 235 | [svn] |
| 236 | authz_file = /path/to/svnaccessfile |
| 237 | }}} |
| 238 | |
| 239 | If you want to support the use of the `[`''modulename''`:/`''some''`/`''path''`]` syntax within the `authz_file`, add: |
| 240 | |
| 241 | {{{#!ini |
| 242 | authz_module_name = modulename |
| 243 | }}} |
| 244 | |
| 245 | where ''modulename'' refers to the same repository indicated by the `<name>.dir` entry in the `[repositories]` section. As an example, if the `somemodule.dir` entry in the `[repositories]` section is `/srv/active/svn/somemodule`, that would yield the following: |
| 246 | |
| 247 | {{{ #!ini |
| 248 | [svn] |
| 249 | authz_file = /path/to/svnaccessfile |
| 250 | authz_module_name = somemodule |
| 251 | ... |
| 252 | [repositories] |
| 253 | somemodule.dir = /srv/active/svn/somemodule |
| 254 | }}} |
| 255 | |
| 256 | where the svn access file, {{{/path/to/svnaccessfile}}}, contains entries such as {{{[somemodule:/some/path]}}}. |
| 257 | |
| 258 | '''Note:''' Usernames inside the Authz file __must__ be the same as those used inside trac. |
| 259 | |
| 260 | Make sure you have ''!AuthzSourcePolicy'' included in the permission_policies list in trac.ini, otherwise the authz permissions file will be ignored. |
| 261 | |
| 262 | {{{#!ini |
| 263 | [trac] |
| 264 | permission_policies = AuthzSourcePolicy, DefaultWikiPolicy, DefaultTicketPolicy, DefaultPermissionPolicy, LegacyAttachmentPolicy |
| 265 | }}} |
| 266 | |
| 267 | ==== Subversion Configuration |
| 268 | |
| 269 | The same access file is typically applied to the corresponding Subversion repository using an Apache directive like this: |
| 270 | {{{#!apache |
| 271 | <Location /repos> |
| 272 | DAV svn |
| 273 | SVNParentPath /usr/local/svn |
| 274 | |
| 275 | # our access control policy |
| 276 | AuthzSVNAccessFile /path/to/svnaccessfile |
| 277 | </Location> |
| 278 | }}} |
| 279 | |
| 280 | For information about how to restrict access to entire projects in a multiple project environment see [trac:wiki:TracMultipleProjectsSVNAccess]. |
| 281 | |
| 282 | === !DefaultWikiPolicy and !DefaultTicketPolicy |
| 283 | |
| 284 | Since 1.1.2, the read-only attribute of wiki pages is enabled and enforced when `DefaultWikiPolicy` is in the list of active permission policies (`DefaultWikiPolicy` was named `ReadonlyWikiPolicy` from Trac 1.1.2 to 1.3.1). The default for new Trac installations in 1.3.2 and later is: |
| 285 | {{{#!ini |
| 286 | [trac] |
| 287 | permission_policies = DefaultWikiPolicy, |
| 288 | DefaultTicketPolicy, |
| 289 | DefaultPermissionPolicy, |
| 290 | LegacyAttachmentPolicy |
| 291 | }}} |
| 292 | |
| 293 | `DefaultWikiPolicy` returns `False` to deny modify, delete and rename actions on wiki pages when the page has the read-only attribute set and the user does not have `WIKI_ADMIN`, regardless of `WIKI_MODIFY`, `WIKI_DELETE` and `WIKI_RENAME` permissions. It returns `None` for all other cases, which causes the next permission policy in the list to be consulted. |
| 294 | |
| 295 | Since 1.3.2 `DefaultTicketPolicy` implements the following behaviors: |
| 296 | * Authenticated user can edit their own comments. |
| 297 | * Authenticated user with `TICKET_APPEND` or `TICKET_CHGPROP` can modify the description of a ticket they reported. |
| 298 | * User with `MILESTONE_VIEW` can change the ticket milestone. |
| 299 | |
| 300 | The wiki- and ticket-specific behaviors are implemented in permission policies so they can be easily replaced in case other behavior is desired. |
| 301 | |
| 302 | When upgrading from earlier versions of Trac, `DefaultWikiPolicy, DefaultTicketPolicy` will be appended to the list of `permission_policies` when upgrading the environment, provided that `permission_policies` has the default value (`ReadonlyWikiPolicy, DefaultPermissionPolicy, LegacyAttachmentPolicy` if upgrading from Trac 1.1.2 or later). If any non-default `permission_polices` are active, `DefaultWikiPolicy, DefaultTicketPolicy` **will need to be manually added** to the list. A message will be echoed to the console when upgrading the environment, indicating if any action needs to be taken. |
| 303 | |
| 304 | **!DefaultWikiPolicy and !DefaultTicketPolicy must be listed //before// !DefaultPermissionPolicy**. The latter returns `True` to allow modify, delete or rename actions when the user has the respective `WIKI_*` permission, without consideration for the read-only attribute. Similarly, some of the behaviors implemented in `DefaultTicketPolicy` won't be considered if `DefaultPermissionPolicy` is executed first. |
| 305 | |
| 306 | When active, the [#AuthzPolicy] should therefore come before `DefaultWikiPolicy, DefaultTicketPolicy`, allowing it to grant or deny the actions on individual resources, which is the usual ordering for `AuthzPolicy` in the `permission_policies` list. |
| 307 | {{{#!ini |
| 308 | [trac] |
| 309 | permission_policies = AuthzPolicy, |
| 310 | DefaultWikiPolicy, |
| 311 | DefaultTicketPolicy, |
| 312 | DefaultPermissionPolicy, |
| 313 | LegacyAttachmentPolicy |
| 314 | }}} |
| 315 | |
| 316 | The placement of [#AuthzSourcePolicy] relative to `DefaultWikiPolicy, DefaultTicketPolicy` does not matter since they don't perform checks on the same realms. |
| 317 | |
| 318 | For all other permission policies, the user will need to decide the proper ordering. Generally, if the permission policy should be capable of overriding the checks performed by `DefaultWikiPolicy` or `DefaultTicketPolicy`, it should come before the policy it overrides. If `DefaultWikiPolicy` or `DefaultTicketPolicy` should override the check performed by another permission policy, as is the case for those policies relative to `DefaultPermissionPolicy`, then the overriding policy should come first. |
| 319 | |
| 320 | == Debugging permissions |
| 321 | In trac.ini set: |
| 322 | {{{#!ini |
| 323 | [logging] |
| 324 | log_file = trac.log |
| 325 | log_level = DEBUG |
| 326 | log_type = file |
| 327 | }}} |
| 328 | |
| 329 | Display the trac.log to understand what checks are being performed: |
| 330 | {{{#!sh |
| 331 | tail -n 0 -f log/trac.log | egrep '\[perm\]|\[authz_policy\]' |
| 332 | }}} |
| 333 | |
| 334 | See the sourced documentation of the plugin for more info. |
| 335 | |
| 336 | ---- |
| 337 | See also: TracPermissions, |
| 338 | [https://trac-hacks.org/wiki/FineGrainedPageAuthzEditorPlugin FineGrainedPageAuthzEditorPlugin] for a simple editor. |