= Introduction = This document is a User's Guide for setting the protection table in Perforce. Read this if you need to (indirectly) run 'p4 protect'. = Copyrights and License = Copyright (c) 2000-2006, Tensilica Inc. All rights reserved. Redistribution and use, with or without modification, are permitted provided that the following conditions are met: * Redistributions must retain the above copyright notice, this list of conditions, and the following disclaimer. * Modified software must be plainly marked as such, so as not to be misrepresented as being the original software. * Neither the names of the copyright holders or their contributors, nor any of their trademarks, may be used to endorse or promote products or services derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. == Executive Summary == Instead of using 'p4 protect' directly, Perforce depot protections are now managed by editing simplified protection tables stored in multiple regular Perforce files under '''//depot/meta/prot/''' . Upon successful submission of any of these files, 'p4 protect' is executed automatically by the '''p4protect''' trigger, resulting in a composite protection table that is Correct By Construction. Only the 'p4admin' user may run 'p4 protect' directly. All other "privileged" users must use the above mechanism. (Normally, p4 admins also invoke 'p4 protect' via the '''p4protect''' trigger.) == Outline == This section gives an overview of how the new protection flow works. If you need to change protections on a file or tree (and are allowed to do so), follow these steps: # 'p4 edit' one or more protection file(s) that you have permission to access, under '''//depot/meta/prot/'''. You will generally not be able to put protection files in edit state if you can't submit them (a policy decision, not hardcoded in the trigger). # Make your edits to the protection file(s). The form and content is somewhat similar to that presented in the 'p4 protect' edit window. # Submit the protection file(s). The p4protect trigger reads the file contents and checks it against all the rules described in this document. If everything checks out, the trigger accepts your submission, computes a complete new protection table, and executes 'p4 protect' on your behalf.
If there is any error in the contents, your submit will fail with an appropriate error message, and the protections will not be updated. Just fix the errors and submit again. '''NOTE:''' Due to a bug in Perforce handling of content triggers, a failing submission leaves your files read-only even though they are still opened for edit (or add). You have to chmod the opened file(s) to be able to edit them before submitting again (e.g. chmod u+w prot_*). Take care to only do this on files that are opened for edit or add (Perforce only submits opened files; modifying revision-controlled files without telling Perforce about it is generally asking for confusion and unexpected behavior). == Goals == Switching from direct execution of 'p4 protect' to this trigger-based method gives us the following advantages. * ''Simplified 'p4 protect' syntax'' - It allows us to control the syntax. The syntax described in this document is designed to: ** Make protection tables easier to read. ** Make it easier to avoid mistakes in protection tables that give too much or too little access. * ''Partitioning'' - Protection information is split among several files. This has several benefits: ** It allows us to specify who can make protection statements for specific portions of the Perforce depot. For example, perhaps only "release" can change protections on release trees. ** It reduces the amount of protection information you have to look at when updating protections. ** It makes it less likely that you'll have to resolve a protection file. ** No more than one protection file can make statements about any specific user and depot path pair. This helps avoid ambiguity as to which protection file to use for a given protection statement. * ''Protection table error checking'' - The p4protect trigger will always check the following items. If any are present, submission of the protection file(s) will be rejected and protections will not be updated: ** ''NOP protection statements'' - Any protection statement that could be removed with no effect on protection. ** ''Out-of-bounds protections'' - Each protection file can only make statements about specific combinations of Perforce users and depot paths. Any protection statement found that violates these bounds is flagged as an error. ** ''Conflicting protections'' - Two protection statements that specify different permissions for the same users+path combination, except when the first of the two statements is explicitly marked to allow override by subsequent statements and the second statement is in the first one's ''scope'' (more on this in later sections). * '' 'p4 protect' race conditions'' - Races caused by multiple people trying to modify protection are resolved with the standard Perforce resolve command. * ''File lock/unlock status'' - Regular users can be allowed to read some of the protection files. Therefore, they can easily determine whether their files have been unlocked. = Protection Files = == What They Do == Protection files specify who has what access to which Perforce paths, for a given set(s) of users and paths (e.g. //depot/foo/...). All protection files taken together define the protection that will be used to synthesize input to 'p4 protect' (with some additional information from the ''protection metafile'', described later). Comparing the syntax of protection files with 'p4 protect', we can say that protection files: * Strip out and simplify the presentation. * Make it easy to insert comments. * Make it easy to repeat parts of the last line. * Provide a ''scoping'' function to specify exactly where protection statements may and may not overlap. This makes it much easier to scan the file and see what protections it provides. == Syntax == === Overall Syntax === Protection files consist of a series of protection statements. Each statement consists of three fields in one line: Protection statements may be grouped to limit the scope of ''dependent'' statements (see below) and/or to specify a prefix for paths within the block: begin [with ] : end === Comments === On any line, all text after '#' is ignored. Note that an informative block comment is present at the beginning of the file. Do not edit it. Your submit will fail if you do (or at least it ought to ;-). === Protection Statements === Protection statements have the following syntax: Examples: LOCK user1,user2 //depot/pathA/... DENY? G_group1 //depot/pathB/... * '''' - Specifies the access given to the specified user/group for the specified depot path. Other protection statements are not allowed to contradict this statement unless the '?' modifier suffix is given (more on that below). The following privileges are supported: ** DENY - Zero access. This is equivalent to using exclusionary (-) protections in 'p4 protect'. ** RO - Read-only access. User cannot put files in edit state. This is equivalent to '''read''' access in 'p4 protect'. ** LOCK - Read-write access. User can edit (but not submit) the files. This is equivalent to '''open''' access in 'p4 protect'. ** SUBMIT - Read-write-submit access. User can edit and submit the files. This is equivalent to '''write''' access in 'p4 protect'.
** ^ - Specifies the same privilege as the previous protection statement. Note that you cannot directly use any of {list, read, open, write, review, admin, or super}. You cannot directly use exclusionary mappings. Review privileges will be automatically given on any files with access levels higher than DENY. * - Specifies the user(s) or group(s) to which the privileges apply, and is one of: ** A valid Perforce user name. ** A valid Perforce group name; all Perforce groups start with '''G_''' . ** A valid protection file group name (described later); all protection file groups start with '''g_''' . ** A comma-separated list of user names, Perforce group names, and protection file group names, without any spaces. ** A single character * which refers to all users about which this protection file can make statements (for the path specified on this line). NOTE: this generally does ''not'' refer to all Perforce users. ** A single character ^ which specifies the same user(s)/group(s) as the previous protection statement. * - Specifies the tree or file(s) to which the protections will apply: ** The syntax is generally identical to that used in 'p4 protect'. ** Paths with spaces must be surrounded by double quotes. ** A single character ^ specifies the same depot path as the previous protection statement. Generally, protection statements must not contradict each other, ie. they must not overlap (cover a common subset of users and depot paths) unless they specify the same privilege. The '''p4protect''' trigger rejects a submission containing conflicting protection statements. There is no ordering among such independent protection statements. For example, the following protection statements are valid together: DENY user_1 //A/... SUBMIT user_2 //B/... SUBMIT user_1,user_2 //B/foo/... However, one will often want to specify a certain privilege for a wide set of users and/or depot paths, and specify a different privilege for a narrower subset of either or both. In this case, the wider protection statement is partially modified by the narrower protection statement(s). To allow this, the wider statement must be explicitly marked ''dependent'' (i.e. as 'modifiable') by suffixing the privilege with a question mark '?'. For example, the question mark is required here: DENY? user_1,user_2 //A/... SUBMIT user_1 //A/bar/... Statements marked ''dependent'' with '?' can only be 'modified' by protection statements in its scope, i.e. by statements that ''follow'' it and are contained within the same begin/end block (or a nested block). This helps limit how far you have to look in the protection file for statements that modify a ''dependent'' statement. It is best to avoid the question mark modifier whenever possible. Statements that don't have the modifier are ''independent'', i.e. they hold true (if their submission is accepted) regardless of other statements in this or any other protection file. Protection statements that 'modify' a ''dependent'' statement completely override the privilege for the specified combination of users and depot path. That is, unlike the traditional 'p4 protect', the privilege given to a specific user for a specific path is determined by the ''last'' statement that covers that user/path combination regardless of previous statements. For example, if a dependent statement gives SUBMIT privilege to a path, and a subsequent statement gives RO privilege to a subset of that path (for the same set of users), the resulting privilege for that path subset is RO, not SUBMIT. So, unlike the traditional 'p4 protect', you do ''not'' have to first deny access before lowering privilege to a given user/path combination. The '''p4protect''' trigger deals with this automatically. For example, given the following protection statements (where user_1 is in group G_coreusers): SUBMIT G_coreusers //A/... LOCK user_1 //A/... where the second statement overrides the first with a lower privilege, the '''p4protect''' trigger generates the following series of 'p4 protect' lines: write group G_coreusers * //A/... list user user_1 * -//A/... open user user_1 * //A/... We think this is less confusing than the behavior provided by 'p4 protect'. It is not necessary to use a DENY in the example, and doing so will be flagged as an error (NOP statement). === Protection File Groups === As a convenience, you may define groups within a protection file. The syntax is: group Examples: group g_wizards user1,user2,user3 group g_1 user4 These ''protection file groups'' are distinct from ''Perforce groups''. The former must have names that start with '''g_'''; the latter must have names that start with '''G_'''. The p4protect trigger script expands protection file groups into N lines, one per user in the group. Note that you may freely intermix users, Perforce groups, and protection file groups any time that it is appropriate to specify a user. === Begin/End Blocks === You can group protection statements together in a block using the 'begin' and 'end' keywords, each on its own line. Such blocks may nest. Any protection statements outside of begin-end act as an outermost scope (i.e., as though the entire file is contained within an outer begin-end block). Any ''dependent'' protection statement (i.e. where the privilege is suffixed with '?') within this block can only be modified by subsequent statements in the same block (or by statements in subsequent nested blocks within the same block). Here is a simple block (note the closing ''end'') that locks //depot/pathA for everyone in G_group1, and then allows SUBMIT for user1. Protection statements that follow the closing ''end'' are not allowed to modify (i.e. overlap with) the LOCK? statement. begin LOCK? G_group1 //depot/pathA/... SUBMIT user1 ^ end You can also group statements for readability by adding the '''with''' clause on the '''begin''' line. The '''with''' clause allows you to group statements with a common path prefix, specify that prefix once, and drop the prefix from all paths within the block. Suppose you have these protection statements: LOCK G_group1 //depot/long/prefix/... SUBMIT user1 //depot/long/prefix/... SUBMIT user2 //depot/long/prefix/foo/... SUBMIT user3 //depot/long/prefix/bar/baz/... Using a '''with''' clause, these become: begin with //depot/long/prefix LOCK G_group1 ... SUBMIT user1 ... SUBMIT user2 foo/... SUBMIT user3 bar/baz/... end = Master Protection File (prot_meta) = == What It Does == The Master Protection File performs these functions: * It controls access to protection files, including itself. This is done using a syntax identical to that used in other protection files. * It controls the depot paths and users/groups that can be referenced in each protection file. The master protection file seldom needs to change. Most people need not be concerned with its special syntax. Currently, only p4admins and Bea can modify it. == Syntax == The master (or ''meta'') protection file is located in '''//depot/meta/prot/prot_meta''' . The syntax is similar to that used in protection files. However, the master protection file has an additional ''meta'' section because it performs two different functions as just discussed. The overall syntax of this file is: meta # new syntax described below end # same syntax as protection files Here is an example master protection file: # Specify what statements can be made in each protection file # meta prot_foo G_coreusers //depot/foo/... prot_meta G_coreusers //depot/meta/... end # Specify who can access each protection file and how # RO G_coreusers //depot/meta/prot/prot_rel SUBMIT peter,mary ^ === Meta Block === This block defines what protection files exist, and what statements each of them can make. Each entry in this block indicates that a specific protection file can make statements about a given combination of users and depot path. An entry consists of a line containing three fields as follows: * '''' - The name of the protection file. No path is allowed; a path prefix of '''//depot/meta/prot/''' is assumed. * '''' - A list of users and groups, much in the same syntax as protection statements. If a group is specified, the protection file is allowed to individually reference members of that group. The ^ (repeat) character is not supported. The * character refers to ''all'' Perforce users. * '''' - A Perforce path with the same syntax as protection statements, except that the ^ (repeat) character is not supported. Each entry in the meta block must specify unique combinations of users and depot paths. There must be no specific user and path combination that matches more than one entry. Thus, each protection file manages a distinct set of user/path combinations that does not overlap with any other protection file's managed set. Protection files can only make statements about stated combinations. Meta entries do not mix. Example: prot_foo User_1 //A/... prot_foo User_2 //B/... This means that prot_foo may give User_1 access to all or part of //A/..., and give User_2 access to all or part of //B/... . However, prot_foo cannot give User_1 access to //B/... or give User_2 access to //A/... . === Protection Statements === The master protection file can (and does) contain protection statements like any other protection file. These protection statements typically define who has what access to each protection file. Typically a small number of "admin" people will have SUBMIT access to a given protection file, while a large number of people will be allowed to read the file to determine what is locked. Some protection files can only be read by a small number of users (e.g., prot_meta). = Example Files = Please see '''//depot/meta/prot/prot_*''' for the actual protection files. = P4 Protect Generation = The '''p4protect''' trigger does the following when someone submits any set of protection files under '''//depot/meta/prot/...''': * Verify that only files under '''//depot/meta/prot/...''' are being submitted. * Process the meta protection file ('''prot_meta'''). This provides an updated list of protection files. * Process all other protection files. Any protection file not contained in the submission is read from the depot (unless the submission deletes the file). The submission is rejected if any specified file is not found. When processing protection files, the following checks are performed: * Verify that syntax is legal. * Verify that all user, group, and requested privileges are legal. Note that you must use existing Perforce groups. * Verify that users, groups, and depot paths are allowed for this protection file. The allowed combinations of users and paths are defined in the master protection file ('''prot_meta'''). * Check for conflicts among protection statements. A conflict occurs when two statements specify different permissions for the same users+path combination, except when the first of the two statements is marked as ''dependent'' ('?' suffix) and the second statement is in the first one's ''scope'' (in the same block). * Check for NOP statements. A NOP statement is a statement whose removal causes no change in protection. * Check for out-of-bounds protections. Each protection file can only make statements about specific combinations of Perforce users and depot paths. * There are probably other sundry sanity checks not mentioned here (e.g. verifying that group names defined in Perforce are consistently prefixed with '''G_''', etc). If everything passes the above checks, each protection file undergoes the following steps to construct the corresponding portion of the p4 protections: * First, DENY statements are generated for every depot path / user combination that the protection file is allowed to control. In other words, an empty protection file provides zero access to its protection space. This means that you mainly need worry only about ''adding'' access. Note that these DENY statements are actually added ''before'' (''after''???) the check for NOP statements described above. * All statements are converted in order using the following rules: {| border=1 |+ Table of protection conversion rules |- ! Input Protection Keyword !! 'p4 protect' Keyword !! Comments |- | DENY || list || minus (-) added in front of depot path |- | RO || read & review || |- | LOCK || open & review || |- | SUBMIT || write & review || |} = TestPlan = [[/TestPlan]] = Miscellaneous Issues = * ''Force all paths to be existing paths.'' But allow users to put a comment in their checkin message that says:
NEW_PATH: //depot/foo/bar/...
which turns off the check for path existence. This is put in the checkin of the prot file. * ''Consider adding an optional timeout field with syntax nH, nD, nW to specify the number of hours, days, or weeks before an entry is removed. Since we can't modify the contents of the protection files, this seems to require writing out a file with the entry and the absolute date at which the entry should be deleted. Then the entry gets deleted the next time someone changes any protection file.''