1175261Sobrien<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2175261Sobrien<html xmlns="http://www.w3.org/1999/xhtml">
3175261Sobrien<head>
4175261Sobrien<title>cvs_acls</title>
5175261Sobrien<link rev="made" href="mailto:root@localhost" />
6175261Sobrien</head>
7175261Sobrien
8175261Sobrien<body style="background-color: white">
9175261Sobrien
10175261Sobrien<p><a name="__index__"></a></p>
11175261Sobrien<!-- INDEX BEGIN -->
12175261Sobrien
13175261Sobrien<ul>
14175261Sobrien
15175261Sobrien	<li><a href="#name">Name</a></li>
16175261Sobrien	<li><a href="#synopsis">Synopsis</a></li>
17175261Sobrien	<li><a href="#licensing">Licensing</a></li>
18175261Sobrien	<li><a href="#description">Description</a></li>
19175261Sobrien	<li><a href="#enhancements">Enhancements</a></li>
20175261Sobrien	<ul>
21175261Sobrien
22175261Sobrien		<li><a href="#fixed_bugs">Fixed Bugs</a></li>
23175261Sobrien		<li><a href="#enhancements">Enhancements</a></li>
24175261Sobrien		<li><a href="#todos">ToDoS</a></li>
25175261Sobrien	</ul>
26175261Sobrien
27175261Sobrien	<li><a href="#version_information">Version Information</a></li>
28175261Sobrien	<li><a href="#installation">Installation</a></li>
29175261Sobrien	<li><a href="#format_of_the_cvsacl_file">Format of the cvsacl file</a></li>
30175261Sobrien	<li><a href="#program_logic">Program Logic</a></li>
31175261Sobrien	<ul>
32175261Sobrien
33175261Sobrien		<li><a href="#pseudocode">Pseudocode</a></li>
34175261Sobrien		<li><a href="#sanity_check">Sanity Check</a></li>
35175261Sobrien	</ul>
36175261Sobrien
37175261Sobrien</ul>
38175261Sobrien<!-- INDEX END -->
39175261Sobrien
40175261Sobrien<hr />
41175261Sobrien<p>
42175261Sobrien</p>
43175261Sobrien<h1><a name="name">Name</a></h1>
44175261Sobrien<p>cvs_acls - Access Control List for CVS</p>
45175261Sobrien<p>
46175261Sobrien</p>
47175261Sobrien<hr />
48175261Sobrien<h1><a name="synopsis">Synopsis</a></h1>
49175261Sobrien<p>In 'commitinfo':</p>
50175261Sobrien<pre>
51175261Sobrien  repository/path/to/restrict $CVSROOT/CVSROOT/cvs_acls [-d][-u $USER][-f &lt;logfile&gt;]</pre>
52175261Sobrien<p>where:</p>
53175261Sobrien<pre>
54175261Sobrien  -d  turns on debug information
55175261Sobrien  -u  passes the client-side userId to the cvs_acls script
56175261Sobrien  -f  specifies an alternate filename for the restrict_log file</pre>
57175261Sobrien<p>In 'cvsacl':</p>
58175261Sobrien<pre>
59175261Sobrien  {allow.*,deny.*} [|user,user,... [|repos,repos,... [|branch,branch,...]]]</pre>
60175261Sobrien<p>where:</p>
61175261Sobrien<pre>
62175261Sobrien  allow|deny - allow: commits are allowed; deny: prohibited
63175261Sobrien  user          - userId to be allowed or restricted
64175261Sobrien  repos         - file or directory to be allowed or restricted
65175261Sobrien  branch        - branch to be allowed or restricted</pre>
66175261Sobrien<p>See below for examples.</p>
67175261Sobrien<p>
68175261Sobrien</p>
69175261Sobrien<hr />
70175261Sobrien<h1><a name="licensing">Licensing</a></h1>
71175261Sobrien<p>cvs_acls - provides access control list functionality for CVS
72175261Sobrien</p>
73175261Sobrien<pre>
74175261Sobrien
75175261SobrienCopyright (c) 2004 by Peter Connolly &lt;peter.connolly@cnet.com&gt;  
76175261SobrienAll rights reserved.</pre>
77175261Sobrien<p>This program is free software; you can redistribute it and/or modify  
78175261Sobrienit under the terms of the GNU General Public License as published by  
79175261Sobrienthe Free Software Foundation; either version 2 of the License, or  
80175261Sobrien(at your option) any later version.</p>
81175261Sobrien<p>This program is distributed in the hope that it will be useful,  
82175261Sobrienbut WITHOUT ANY WARRANTY; without even the implied warranty of  
83175261SobrienMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  
84175261SobrienGNU General Public License for more details.</p>
85175261Sobrien<p>You should have received a copy of the GNU General Public License  
86175261Sobrienalong with this program; if not, write to the Free Software  
87175261SobrienFoundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA</p>
88175261Sobrien<p>
89175261Sobrien</p>
90175261Sobrien<hr />
91175261Sobrien<h1><a name="description">Description</a></h1>
92175261Sobrien<p>This script--cvs_acls--is invoked once for each directory within a 
93175261Sobrien``cvs commit''. The set of files being committed for that directory as 
94175261Sobrienwell as the directory itself, are passed to this script.  This script 
95175261Sobrienchecks its 'cvsacl' file to see if any of the files being committed 
96175261Sobrienare on the 'cvsacl' file's restricted list.  If any of the files are
97175261Sobrienrestricted, then the cvs_acls script passes back an exit code of 1
98175261Sobrienwhich disallows the commits for that directory.</p>
99175261Sobrien<p>Messages are returned to the committer indicating the <a href="#item_file"><code>file(s)</code></a> that 
100175261Sobrienhe/she are not allowed to committ.  Additionally, a site-specific 
101175261Sobrienset of messages (e.g., contact information) can be included in these 
102175261Sobrienmessages.</p>
103175261Sobrien<p>When a commit is prohibited, log messages are written to a restrict_log
104175261Sobrienfile in $CVSROOT/CVSROOT.  This default file can be redirected to 
105175261Sobrienanother destination.</p>
106175261Sobrien<p>The script is triggered from the 'commitinfo' file in $CVSROOT/CVSROOT/.</p>
107175261Sobrien<p>
108175261Sobrien</p>
109175261Sobrien<hr />
110175261Sobrien<h1><a name="enhancements">Enhancements</a></h1>
111175261Sobrien<p>This section lists the bug fixes and enhancements added to cvs_acls
112175261Sobrienthat make up the current cvs_acls.</p>
113175261Sobrien<p>
114175261Sobrien</p>
115175261Sobrien<h2><a name="fixed_bugs">Fixed Bugs</a></h2>
116175261Sobrien<p>This version attempts to get rid the following bugs from the
117175261Sobrienoriginal version of cvs_acls:</p>
118175261Sobrien<ul>
119175261Sobrien<li><strong><a name="item_files">Multiple entries on an 'cvsacl' line will be matched individually, 
120175261Sobrieninstead of requiring that all commit files *exactly* match all 
121175261Sobrien'cvsacl' entries. Commiting a file not in the 'cvsacl' list would
122175261Sobrienallow *all* files (including a restricted file) to be committed.</a></strong><br />
123175261Sobrien</li>
124175261Sobrien[IMO, this basically made the original script unuseable for our 
125175261Sobriensituation since any arbitrary combination of committed files could 
126175261Sobrienavoid matching the 'cvsacl's entries.]
127175261Sobrien<p></p>
128175261Sobrien<li><strong><a name="item_handle_specific_filename_restrictions_2e_cvs_acls_">Handle specific filename restrictions. cvs_acls didn't restrict
129175261Sobrienindividual files specified in 'cvsacl'.</a></strong><br />
130175261Sobrien</li>
131175261Sobrien<li><strong><a name="item_correctly_handle_multiple_2c_specific_filename_res">Correctly handle multiple, specific filename restrictions</a></strong><br />
132175261Sobrien</li>
133175261Sobrien<li><strong><a name="item_prohibit_mix_of_dirs_and_files_on_a_single__27cvsa">Prohibit mix of dirs and files on a single 'cvsacl' line
134175261Sobrien[To simplify the logic and because this would be normal usage.]</a></strong><br />
135175261Sobrien</li>
136175261Sobrien<li><strong><a name="item_correctly_handle_a_mixture_of_branch_restrictions_">Correctly handle a mixture of branch restrictions within one work
137175261Sobriendirectory</a></strong><br />
138175261Sobrien</li>
139175261Sobrien<li><strong><a name="item__24cvsroot_existence_is_checked_too_late">$CVSROOT existence is checked too late</a></strong><br />
140175261Sobrien</li>
141175261Sobrien<li><strong><a name="item_option">Correctly handle the CVSROOT=:local:/... option (useful for 
142175261Sobrieninteractive testing)</a></strong><br />
143175261Sobrien</li>
144175261Sobrien<li><strong><a name="item_logic">Replacing shoddy ``$universal_off'' logic 
145175261Sobrien(Thanks to Karl-Konig Konigsson for pointing this out.)</a></strong><br />
146175261Sobrien</li>
147175261Sobrien</ul>
148175261Sobrien<p>
149175261Sobrien</p>
150175261Sobrien<h2><a name="enhancements">Enhancements</a></h2>
151175261Sobrien<ul>
152175261Sobrien<li><strong><a name="item_checks_modules_in_the__27cvsacl_27_file_for_valid_">Checks modules in the 'cvsacl' file for valid files and directories</a></strong><br />
153175261Sobrien</li>
154175261Sobrien<li><strong><a name="item_accurately_report_restricted_entries_and_their_mat">Accurately report restricted entries and their matching patterns</a></strong><br />
155175261Sobrien</li>
156175261Sobrien<li><strong><a name="item_simplified_and_commented_overly_complex_perl_regex">Simplified and commented overly complex PERL REGEXPs for readability 
157175261Sobrienand maintainability</a></strong><br />
158175261Sobrien</li>
159175261Sobrien<li><strong><a name="item_skip_the_rest_of_processing_if_a_mismatch_on_porti">Skip the rest of processing if a mismatch on portion of the 'cvsacl' line</a></strong><br />
160175261Sobrien</li>
161175261Sobrien<li><strong><a name="item_file">Get rid of opaque ``karma'' messages in favor of user-friendly messages
162175261Sobrienthat describe which user, <code>file(s)</code> and <code>branch(es)</code> were disallowed.</a></strong><br />
163175261Sobrien</li>
164175261Sobrien<li><strong><a name="item_add_optional__27restrict_msg_27_file_for_additiona">Add optional 'restrict_msg' file for additional, site-specific 
165175261Sobrienrestriction messages.</a></strong><br />
166175261Sobrien</li>
167175261Sobrien<li><strong><a name="item_userid">Take a ``-u'' parameter for $USER from commit_prep so that the script
168175261Sobriencan do restrictions based on the client-side userId rather than the
169175261Sobrienserver-side userId (usually 'cvs').</a></strong><br />
170175261Sobrien</li>
171175261Sobrien(See discussion below on ``Admin Setup'' for more on this point.)
172175261Sobrien<p></p>
173175261Sobrien<li><strong><a name="item_added_a_lot_more_debug_trace">Added a lot more debug trace</a></strong><br />
174175261Sobrien</li>
175175261Sobrien<li><strong><a name="item_tested_these_restrictions_with_concurrent_use_of_p">Tested these restrictions with concurrent use of pserver and SSH
176175261Sobrienaccess to model our transition from pserver to ext access.</a></strong><br />
177175261Sobrien</li>
178175261Sobrien<li><strong><a name="item_added_logging_of_restricted_commit_attempts_2e_res">Added logging of restricted commit attempts.
179175261SobrienRestricted commits can be sent to a default file:
180175261Sobrien$CVSROOT/CVSROOT/restrictlog or to one passed to the script
181175261Sobrienvia the -f command parameter.</a></strong><br />
182175261Sobrien</li>
183175261Sobrien</ul>
184175261Sobrien<p>
185175261Sobrien</p>
186175261Sobrien<h2><a name="todos">ToDoS</a></h2>
187175261Sobrien<ul>
188175261Sobrien<li><strong><a name="item_need_to_deal_with_pserver_2fssh_transition_with_co">Need to deal with pserver/SSH transition with conflicting umasks?</a></strong><br />
189175261Sobrien</li>
190175261Sobrien<li><strong><a name="item_use_a_cpan_module_to_handle_command_parameters_2e">Use a CPAN module to handle command parameters.</a></strong><br />
191175261Sobrien</li>
192175261Sobrien<li><strong><a name="item_use_a_cpan_module_to_clone_data_structures_2e">Use a CPAN module to clone data structures.</a></strong><br />
193175261Sobrien</li>
194175261Sobrien</ul>
195175261Sobrien<p>
196175261Sobrien</p>
197175261Sobrien<hr />
198175261Sobrien<h1><a name="version_information">Version Information</a></h1>
199175261Sobrien<p>This is not offered as a fix to the original 'cvs_acls' script since it 
200175261Sobriendiffers substantially in goals and methods from the original and there 
201175261Sobrienare probably a significant number of people out there that still require 
202175261Sobrienthe original version's functionality.</p>
203175261Sobrien<p>The 'cvsacl' file flags of 'allow' and 'deny' were intentionally 
204175261Sobrienchanged to 'allow' and 'deny' because there are enough differences 
205175261Sobrienbetween the original script's behavior and this one's that we wanted to
206175261Sobrienmake sure that users will rethink their 'cvsacl' file formats before
207175261Sobrienplugging in this newer script.</p>
208175261Sobrien<p>Please note that there has been very limited cross-platform testing of 
209175261Sobrienthis script!!! (We did not have the time or resources to do exhaustive
210175261Sobriencross-platform testing.)</p>
211175261Sobrien<p>It was developed and tested under Red Hat Linux 9.0 using PERL 5.8.0.
212175261SobrienAdditionally, it was built and tested under Red Hat Linux 7.3 using 
213175261SobrienPERL 5.6.1.</p>
214175261Sobrien<p>$Id: cvs_acls.html,v 1.1.2.2 2005/09/01 13:44:49 dprice Exp $</p>
215175261Sobrien<p>This version is based on the 1.11.13 version of cvs_acls
216175261Sobrien<a href="mailto:peter.connolly@cnet.com">peter.connolly@cnet.com</a> (Peter Connolly)</p>
217175261Sobrien<pre>
218175261Sobrien  Access control lists for CVS.  dgg@ksr.com (David G. Grubbs)
219175261Sobrien  Branch specific controls added by voisine@bytemobile.com (Aaron Voisine)</pre>
220175261Sobrien<p>
221175261Sobrien</p>
222175261Sobrien<hr />
223175261Sobrien<h1><a name="installation">Installation</a></h1>
224175261Sobrien<p>To use this program, do the following four things:</p>
225175261Sobrien<p>0. Install PERL, version 5.6.1 or 5.8.0.</p>
226175261Sobrien<p>1. Admin Setup:</p>
227175261Sobrien<pre>
228175261Sobrien   There are two choices here.</pre>
229175261Sobrien<pre>
230175261Sobrien   a) The first option is to use the $ENV{&quot;USER&quot;}, server-side userId
231175261Sobrien      (from the third column of your pserver 'passwd' file) as the basis for 
232175261Sobrien      your restrictions.  In this case, you will (at a minimum) want to set
233175261Sobrien      up a new &quot;cvsadmin&quot; userId and group on the pserver machine.  
234175261Sobrien      CVS administrators will then set up their 'passwd' file entries to
235175261Sobrien      run either as &quot;cvs&quot; (for regular users) or as &quot;cvsadmin&quot; (for power 
236175261Sobrien      users).  Correspondingly, your 'cvsacl' file will only list 'cvs'
237175261Sobrien      and 'cvsadmin' as the userIds in the second column.</pre>
238175261Sobrien<pre>
239175261Sobrien      Commentary: A potential weakness of this is that the xinetd 
240175261Sobrien      cvspserver process will need to run as 'root' in order to switch 
241175261Sobrien      between the 'cvs' and the 'cvsadmin' userIds.  Some sysadmins don't
242175261Sobrien      like situations like this and may want to chroot the process.
243175261Sobrien      Talk to them about this point...</pre>
244175261Sobrien<pre>
245175261Sobrien   b) The second option is to use the client-side userId as the basis for
246175261Sobrien      your restrictions.  In this case, all the xinetd cvspserver processes 
247175261Sobrien      can run as userId 'cvs' and no 'root' userId is required.  If you have
248175261Sobrien      a 'passwd' file that lists 'cvs' as the effective run-time userId for
249175261Sobrien      all your users, then no changes to this file are needed.  Your 'cvsacl'
250175261Sobrien      file will use the individual, client-side userIds in its 2nd column.</pre>
251175261Sobrien<pre>
252175261Sobrien      As long as the userIds in pserver's 'passwd' file match those userIds 
253175261Sobrien      that your Linux server know about, this approach is ideal if you are 
254175261Sobrien      planning to move from pserver to SSH access at some later point in time.
255175261Sobrien      Just by switching the CVSROOT var from CVSROOT=:pserver:&lt;userId&gt;... to 
256175261Sobrien      CVSROOT=:ext:&lt;userId&gt;..., users can switch over to SSH access without
257175261Sobrien      any other administrative changes.  When all users have switched over to
258175261Sobrien      SSH, the inherently insecure xinetd cvspserver process can be disabled.
259175261Sobrien      [<a href="http://ximbiot.com/cvs/manual/cvs-1.11.17/cvs_2.html#SEC32">http://ximbiot.com/cvs/manual/cvs-1.11.17/cvs_2.html#SEC32</a>]</pre>
260175261Sobrien<pre>
261175261Sobrien      :TODO: The only potential glitch with the SSH approach is the possibility 
262175261Sobrien      that each user can have differing umasks that might interfere with one 
263175261Sobrien      another, especially during a transition from pserver to SSH.  As noted
264175261Sobrien      in the ToDo section, this needs a good strategy and set of tests for that 
265175261Sobrien      yet...</pre>
266175261Sobrien<p>2. Put two lines, as the *only* non-comment lines, in your commitinfo file:</p>
267175261Sobrien<pre>
268175261Sobrien   ALL $CVSROOT/CVSROOT/commit_prep 
269175261Sobrien   ALL $CVSROOT/CVSROOT/cvs_acls [-d][-u $USER ][-f &lt;logfilename&gt;]</pre>
270175261Sobrien<pre>
271175261Sobrien   where &quot;-d&quot; turns on debug trace
272175261Sobrien         &quot;-u $USER&quot; passes the client-side userId to cvs_acls 
273175261Sobrien         &quot;-f &lt;logfilename&quot;&gt; overrides the default filename used to log
274175261Sobrien                            restricted commit attempts.</pre>
275175261Sobrien<pre>
276175261Sobrien   (These are handled in the processArgs() subroutine.)</pre>
277175261Sobrien<p>If you are using client-side userIds to restrict access to your 
278175261Sobrienrepository, make sure that they are in this order since the commit_prep 
279175261Sobrienscript is required in order to pass the $USER parameter.</p>
280175261Sobrien<p>A final note about the repository matching pattern.  The example above
281175261Sobrienuses ``ALL'' but note that this means that the cvs_acls script will run
282175261Sobrienfor each and every commit in your repository.  Obviously, in a large
283175261Sobrienrepository this adds up to a lot of overhead that may not be necesary. 
284175261SobrienA better strategy is to use a repository pattern that is more specific 
285175261Sobriento the areas that you wish to secure.</p>
286175261Sobrien<p>3. Install this file as $CVSROOT/CVSROOT/cvs_acls and make it executable.</p>
287175261Sobrien<p>4. Create a file named CVSROOT/cvsacl and optionally add it to
288175261Sobrien   CVSROOT/checkoutlist and check it in.  See the CVS manual's
289175261Sobrien   administrative files section about checkoutlist.  Typically:</p>
290175261Sobrien<pre>
291175261Sobrien   $ cvs checkout CVSROOT
292175261Sobrien   $ cd CVSROOT
293175261Sobrien   [ create the cvsacl file, include 'commitinfo' line ]
294175261Sobrien   [ add cvsacl to checkoutlist ]
295175261Sobrien   $ cvs add cvsacl
296175261Sobrien   $ cvs commit -m 'Added cvsacl for use with cvs_acls.' cvsacl checkoutlist</pre>
297175261Sobrien<p>Note: The format of the 'cvsacl' file is described in detail immediately 
298175261Sobrienbelow but here is an important set up point:</p>
299175261Sobrien<pre>
300175261Sobrien   Make sure to include a line like the following:</pre>
301175261Sobrien<pre>
302175261Sobrien     deny||CVSROOT/commitinfo CVSROOT/cvsacl
303175261Sobrien     allow|cvsadmin|CVSROOT/commitinfo CVSROOT/cvsacl</pre>
304175261Sobrien<pre>
305175261Sobrien   that restricts access to commitinfo and cvsacl since this would be one of
306175261Sobrien   the easiest &quot;end runs&quot; around this ACL approach. ('commitinfo' has the 
307175261Sobrien   line that executes the cvs_acls script and, of course, all the 
308175261Sobrien   restrictions are in 'cvsacl'.)</pre>
309175261Sobrien<p>5. (Optional) Create a 'restrict_msg' file in the $CVSROOT/CVSROOT directory.
310175261Sobrien   Whenever there is a restricted file or dir message, cvs_acls will look 
311175261Sobrien   for this file and, if it exists, print its contents as part of the 
312175261Sobrien   commit-denial message.  This gives you a chance to print any site-specific
313175261Sobrien   information (e.g., who to call, what procedures to look up,...) whenever
314175261Sobrien   a commit is denied.</p>
315175261Sobrien<p>
316175261Sobrien</p>
317175261Sobrien<hr />
318175261Sobrien<h1><a name="format_of_the_cvsacl_file">Format of the cvsacl file</a></h1>
319175261Sobrien<p>The 'cvsacl' file determines whether you may commit files.  It contains lines
320175261Sobrienread from top to bottom, keeping track of whether a given user, repository
321175261Sobrienand branch combination is ``allowed'' or ``denied.''  The script will assume 
322175261Sobrien``allowed'' on all repository paths until 'allow' and 'deny' rules change 
323175261Sobrienthat default.</p>
324175261Sobrien<p>The normal pattern is to specify an 'deny' rule to turn off
325175261Sobrienaccess to ALL users, then follow it with a matching 'allow' rule that will 
326175261Sobrienturn on access for a select set of users.  In the case of multiple rules for
327175261Sobrienthe same user, repository and branch, the last one takes precedence.</p>
328175261Sobrien<p>Blank lines and lines with only comments are ignored.  Any other lines not 
329175261Sobrienbeginning with ``allow'' or ``deny'' are logged to the restrict_log file.</p>
330175261Sobrien<p>Lines beginning with ``allow'' or ``deny'' are assumed to be '|'-separated
331175261Sobrientriples: (All spaces and tabs are ignored in a line.)</p>
332175261Sobrien<pre>
333175261Sobrien  {allow.*,deny.*} [|user,user,... [|repos,repos,... [|branch,branch,...]]]</pre>
334175261Sobrien<pre>
335175261Sobrien   1. String starting with &quot;allow&quot; or &quot;deny&quot;.
336175261Sobrien   2. Optional, comma-separated list of usernames.
337175261Sobrien   3. Optional, comma-separated list of repository pathnames.
338175261Sobrien      These are pathnames relative to $CVSROOT.  They can be directories or
339175261Sobrien      filenames.  A directory name allows or restricts access to all files and
340175261Sobrien      directories below it. One line can have either directories or filenames
341175261Sobrien      but not both.
342175261Sobrien   4. Optional, comma-separated list of branch tags.
343175261Sobrien      If not specified, all branches are assumed. Use HEAD to reference the
344175261Sobrien      main branch.</pre>
345175261Sobrien<p>Example:  (Note: No in-line comments.)</p>
346175261Sobrien<pre>
347175261Sobrien   # ----- Make whole repository unavailable.
348175261Sobrien   deny</pre>
349175261Sobrien<pre>
350175261Sobrien   # ----- Except for user &quot;dgg&quot;.
351175261Sobrien   allow|dgg</pre>
352175261Sobrien<pre>
353175261Sobrien   # ----- Except when &quot;fred&quot; or &quot;john&quot; commit to the 
354175261Sobrien   #       module whose repository is &quot;bin/ls&quot;
355175261Sobrien   allow|fred, john|bin/ls</pre>
356175261Sobrien<pre>
357175261Sobrien   # ----- Except when &quot;ed&quot; commits to the &quot;stable&quot; 
358175261Sobrien   #       branch of the &quot;bin/ls&quot; repository
359175261Sobrien   allow|ed|/bin/ls|stable</pre>
360175261Sobrien<p>
361175261Sobrien</p>
362175261Sobrien<hr />
363175261Sobrien<h1><a name="program_logic">Program Logic</a></h1>
364175261Sobrien<p>CVS passes to @ARGV an absolute directory pathname (the repository
365175261Sobrienappended to your $CVSROOT variable), followed by a list of filenames
366175261Sobrienwithin that directory that are to be committed.</p>
367175261Sobrien<p>The script walks through the 'cvsacl' file looking for matches on 
368175261Sobrienthe username, repository and branch.</p>
369175261Sobrien<p>A username match is simply the user's name appearing in the second
370175261Sobriencolumn of the cvsacl line in a space-or-comma separate list. If
371175261Sobrienblank, then any user will match.</p>
372175261Sobrien<p>A repository match:</p>
373175261Sobrien<ul>
374175261Sobrien<li><strong><a name="item_each_entry_in_the_modules_section_of_the_current__">Each entry in the modules section of the current 'cvsacl' line is 
375175261Sobrienexamined to see if it is a dir or a file. The line must have 
376175261Sobrieneither files or dirs, but not both. (To simplify the logic.)</a></strong><br />
377175261Sobrien</li>
378175261Sobrien<li><strong><a name="item_if_neither_2c_then_assume_the__27cvsacl_27_file_wa">If neither, then assume the 'cvsacl' file was set up in error and
379175261Sobrienskip that 'allow' line.</a></strong><br />
380175261Sobrien</li>
381175261Sobrien<li><strong><a name="item_if_a_dir_2c_then_each_dir_pattern_is_matched_separ">If a dir, then each dir pattern is matched separately against the 
382175261Sobrienbeginning of each of the committed files in @ARGV.</a></strong><br />
383175261Sobrien</li>
384175261Sobrien<li><strong><a name="item_if_a_file_2c_then_each_file_pattern_is_matched_exa">If a file, then each file pattern is matched exactly against each
385175261Sobrienof the files to be committed in @ARGV.</a></strong><br />
386175261Sobrien</li>
387175261Sobrien<li><strong><a name="item_repository_and_branch_must_both_match_together_2e_">Repository and branch must BOTH match together. This is to cover
388175261Sobrienthe use case where a user has multiple branches checked out in
389175261Sobriena single work directory. Commit files can be from different
390175261Sobrienbranches.</a></strong><br />
391175261Sobrien</li>
392175261SobrienA branch match is either:
393175261Sobrien<ul>
394175261Sobrien<li><strong><a name="item_when_no_branches_are_listed_in_the_fourth_column_2">When no branches are listed in the fourth column. (``Match any.'')</a></strong><br />
395175261Sobrien</li>
396175261Sobrien<li><strong><a name="item_all_elements_from_the_fourth_column_are_matched_ag">All elements from the fourth column are matched against each of 
397175261Sobrienthe tag names for $ARGV[1..$#ARGV] found in the %branches file.</a></strong><br />
398175261Sobrien</li>
399175261Sobrien</ul>
400175261Sobrien<li><strong><a name="item__27allow_27_match_remove_that_match_from_the_tally">'allow' match remove that match from the tally map.</a></strong><br />
401175261Sobrien</li>
402175261Sobrien<li><strong><a name="item_restricted">Restricted ('deny') matches are saved in the %repository_matches 
403175261Sobrientable.</a></strong><br />
404175261Sobrien</li>
405175261Sobrien<li><strong><a name="item_if_there_is_a_match_on_user_2c_repository_and_bran">If there is a match on user, repository and branch:</a></strong><br />
406175261Sobrien</li>
407175261Sobrien<pre>
408175261Sobrien  If repository, branch and user match
409175261Sobrien    if 'deny'
410175261Sobrien      add %repository_matches entries to %restricted_entries
411175261Sobrien    else if 'allow'
412175261Sobrien      remove %repository_matches entries from %restricted_entries</pre>
413175261Sobrien<li><strong><a name="item_at_the_end_of_all_the__27cvsacl_27_line_checks_2c_">At the end of all the 'cvsacl' line checks, check to see if there
414175261Sobrienare any entries in the %restricted_entries.  If so, then deny the
415175261Sobriencommit.</a></strong><br />
416175261Sobrien</li>
417175261Sobrien</ul>
418175261Sobrien<p>
419175261Sobrien</p>
420175261Sobrien<h2><a name="pseudocode">Pseudocode</a></h2>
421175261Sobrien<pre>
422175261Sobrien     read CVS/Entries file and create branch{file}-&gt;{branch} hash table
423175261Sobrien   + for each 'allow' and 'deny' line in the 'cvsacl' file:
424175261Sobrien   |   user match?   
425175261Sobrien   |     - Yes: set $user_match       = 1;
426175261Sobrien   |   repository and branch match?
427175261Sobrien   |     - Yes: add to %repository_matches;
428175261Sobrien   |   did user, repository match?
429175261Sobrien   |     - Yes: if 'deny' then 
430175261Sobrien   |                add %repository_matches -&gt; %restricted_entries
431175261Sobrien   |            if 'allow'   then 
432175261Sobrien   |                remove %repository_matches &lt;- %restricted_entries
433175261Sobrien   + end for loop
434175261Sobrien     any saved restrictions?
435175261Sobrien       no:  exit, 
436175261Sobrien            set exit code allowing commits and exit
437175261Sobrien       yes: report restrictions, 
438175261Sobrien            set exit code prohibiting commits and exit</pre>
439175261Sobrien<p>
440175261Sobrien</p>
441175261Sobrien<h2><a name="sanity_check">Sanity Check</a></h2>
442175261Sobrien<pre>
443175261Sobrien  1) file allow trumps a dir deny
444175261Sobrien     deny||java/lib
445175261Sobrien     allow||java/lib/README
446175261Sobrien  2) dir allow can undo a file deny
447175261Sobrien     deny||java/lib/README
448175261Sobrien     allow||java/lib
449175261Sobrien  3) file deny trumps a dir allow
450175261Sobrien     allow||java/lib
451175261Sobrien     deny||java/lib/README
452175261Sobrien  4) dir deny trumps a file allow
453175261Sobrien     allow||java/lib/README
454175261Sobrien     deny||java/lib
455175261Sobrien  ... so last match always takes precedence</pre>
456175261Sobrien
457175261Sobrien</body>
458175261Sobrien
459175261Sobrien</html>
460