1*43c1707eStholo#! @PERL@ 2*43c1707eStholo# -*-Perl-*- 3*43c1707eStholo# 4*43c1707eStholo# Access control lists for CVS. dgg@ksr.com (David G. Grubbs) 5*43c1707eStholo# 6*43c1707eStholo# CVS "commitinfo" for matching repository names, running the program it finds 7*43c1707eStholo# on the same line. More information is available in the CVS man pages. 8*43c1707eStholo# 9*43c1707eStholo# ==== INSTALLATION: 10*43c1707eStholo# 11*43c1707eStholo# To use this program as I intended, do the following four things: 12*43c1707eStholo# 13*43c1707eStholo# 0. Install PERL. :-) 14*43c1707eStholo# 15*43c1707eStholo# 1. Put one line, as the *only* non-comment line, in your commitinfo file: 16*43c1707eStholo# 17*43c1707eStholo# DEFAULT /usr/local/bin/cvs_acls 18*43c1707eStholo# 19*43c1707eStholo# 2. Install this file as /usr/local/bin/cvs_acls and make it executable. 20*43c1707eStholo# 21*43c1707eStholo# 3. Create a file named $CVSROOT/CVSROOT/avail. 22*43c1707eStholo# 23*43c1707eStholo# ==== FORMAT OF THE avail FILE: 24*43c1707eStholo# 25*43c1707eStholo# The avail file determines whether you may commit files. It contains lines 26*43c1707eStholo# read from top to bottom, keeping track of a single "bit". The "bit" 27*43c1707eStholo# defaults to "on". It can be turned "off" by "unavail" lines and "on" by 28*43c1707eStholo# "avail" lines. ==> Last one counts. 29*43c1707eStholo# 30*43c1707eStholo# Any line not beginning with "avail" or "unavail" is ignored. 31*43c1707eStholo# 32*43c1707eStholo# Lines beginning with "avail" or "unavail" are assumed to be '|'-separated 33*43c1707eStholo# triples: (All spaces and tabs are ignored in a line.) 34*43c1707eStholo# 35*43c1707eStholo# {avail.*,unavail.*} [| user,user,... [| repos,repos,...]] 36*43c1707eStholo# 37*43c1707eStholo# 1. String starting with "avail" or "unavail". 38*43c1707eStholo# 2. Optional, comma-separated list of usernames. 39*43c1707eStholo# 3. Optional, comma-separated list of repository pathnames. 40*43c1707eStholo# These are pathnames relative to $CVSROOT. They can be directories or 41*43c1707eStholo# filenames. A directory name allows access to all files and 42*43c1707eStholo# directories below it. 43*43c1707eStholo# 44*43c1707eStholo# Example: (Text from the ';;' rightward may not appear in the file.) 45*43c1707eStholo# 46*43c1707eStholo# unavail ;; Make whole repository unavailable. 47*43c1707eStholo# avail|dgg ;; Except for user "dgg". 48*43c1707eStholo# avail|fred, john|bin/ls ;; Except when "fred" or "john" commit to 49*43c1707eStholo# ;; the module whose repository is "bin/ls" 50*43c1707eStholo# 51*43c1707eStholo# PROGRAM LOGIC: 52*43c1707eStholo# 53*43c1707eStholo# CVS passes to @ARGV an absolute directory pathname (the repository 54*43c1707eStholo# appended to your $CVSROOT variable), followed by a list of filenames 55*43c1707eStholo# within that directory. 56*43c1707eStholo# 57*43c1707eStholo# We walk through the avail file looking for a line that matches both 58*43c1707eStholo# the username and repository. 59*43c1707eStholo# 60*43c1707eStholo# A username match is simply the user's name appearing in the second 61*43c1707eStholo# column of the avail line in a space-or-comma separate list. 62*43c1707eStholo# 63*43c1707eStholo# A repository match is either: 64*43c1707eStholo# - One element of the third column matches $ARGV[0], or some 65*43c1707eStholo# parent directory of $ARGV[0]. 66*43c1707eStholo# - Otherwise *all* file arguments ($ARGV[1..$#ARGV]) must be 67*43c1707eStholo# in the file list in one avail line. 68*43c1707eStholo# - In other words, using directory names in the third column of 69*43c1707eStholo# the avail file allows committing of any file (or group of 70*43c1707eStholo# files in a single commit) in the tree below that directory. 71*43c1707eStholo# - If individual file names are used in the third column of 72*43c1707eStholo# the avail file, then files must be committed individually or 73*43c1707eStholo# all files specified in a single commit must all appear in 74*43c1707eStholo# third column of a single avail line. 75*43c1707eStholo# 76*43c1707eStholo 77*43c1707eStholo$debug = 0; 78*43c1707eStholo$cvsroot = $ENV{'CVSROOT'}; 79*43c1707eStholo$availfile = $cvsroot . "/CVSROOT/avail"; 80*43c1707eStholo$myname = $ENV{"USER"} if !($myname = $ENV{"LOGNAME"}); 81*43c1707eStholo 82*43c1707eStholoeval "print STDERR \$die='Unknown parameter $1\n' if !defined \$$1; \$$1=\$';" 83*43c1707eStholo while ($ARGV[0] =~ /^(\w+)=/ && shift(@ARGV)); 84*43c1707eStholoexit 255 if $die; # process any variable=value switches 85*43c1707eStholo 86*43c1707eStholodie "Must set CVSROOT\n" if !$cvsroot; 87*43c1707eStholo($repos = shift) =~ s:^$cvsroot/::; 88*43c1707eSthologrep($_ = $repos . '/' . $_, @ARGV); 89*43c1707eStholo 90*43c1707eStholoprint "$$ Repos: $repos\n","$$ ==== ",join("\n$$ ==== ",@ARGV),"\n" if $debug; 91*43c1707eStholo 92*43c1707eStholo$exit_val = 0; # Good Exit value 93*43c1707eStholo 94*43c1707eStholo$universal_off = 0; 95*43c1707eStholoopen (AVAIL, $availfile) || exit(0); # It is ok for avail file not to exist 96*43c1707eStholowhile (<AVAIL>) { 97*43c1707eStholo chop; 98*43c1707eStholo next if /^\s*\#/; 99*43c1707eStholo next if /^\s*$/; 100*43c1707eStholo ($flagstr, $u, $m) = split(/[\s,]*\|[\s,]*/, $_); 101*43c1707eStholo 102*43c1707eStholo # Skip anything not starting with "avail" or "unavail" and complain. 103*43c1707eStholo (print "Bad avail line: $_\n"), next 104*43c1707eStholo if ($flagstr !~ /^avail/ && $flagstr !~ /^unavail/); 105*43c1707eStholo 106*43c1707eStholo # Set which bit we are playing with. ('0' is OK == Available). 107*43c1707eStholo $flag = (($& eq "avail") ? 0 : 1); 108*43c1707eStholo 109*43c1707eStholo # If we find a "universal off" flag (i.e. a simple "unavail") remember it 110*43c1707eStholo $universal_off = 1 if ($flag && !$u && !$m); 111*43c1707eStholo 112*43c1707eStholo # $myname considered "in user list" if actually in list or is NULL 113*43c1707eStholo $in_user = (!$u || grep ($_ eq $myname, split(/[\s,]+/,$u))); 114*43c1707eStholo print "$$ \$myname($myname) in user list: $_\n" if $debug && $in_user; 115*43c1707eStholo 116*43c1707eStholo # Module matches if it is a NULL module list in the avail line. If module 117*43c1707eStholo # list is not null, we check every argument combination. 118*43c1707eStholo if (!($in_repo = !$m)) { 119*43c1707eStholo @tmp = split(/[\s,]+/,$m); 120*43c1707eStholo for $j (@tmp) { 121*43c1707eStholo # If the repos from avail is a parent(or equal) dir of $repos, OK 122*43c1707eStholo $in_repo = 1, last if ($repos eq $j || $repos =~ /^$j\//); 123*43c1707eStholo } 124*43c1707eStholo if (!$in_repo) { 125*43c1707eStholo $in_repo = 1; 126*43c1707eStholo for $j (@ARGV) { 127*43c1707eStholo last if !($in_repo = grep ($_ eq $j, @tmp)); 128*43c1707eStholo } 129*43c1707eStholo } 130*43c1707eStholo } 131*43c1707eStholo print "$$ \$repos($repos) in repository list: $_\n" if $debug && $in_repo; 132*43c1707eStholo 133*43c1707eStholo $exit_val = $flag if ($in_user && $in_repo); 134*43c1707eStholo print "$$ ==== \$exit_val = $exit_val\n$$ ==== \$flag = $flag\n" if $debug; 135*43c1707eStholo} 136*43c1707eStholoclose(AVAIL); 137*43c1707eStholoprint "$$ ==== \$exit_val = $exit_val\n" if $debug; 138*43c1707eStholoprint "**** Access denied: Insufficient Karma ($myname|$repos)\n" if $exit_val; 139*43c1707eStholoprint "**** Access allowed: Personal Karma exceeds Environmental Karma.\n" 140*43c1707eStholo if $universal_off && !$exit_val; 141*43c1707eStholoexit($exit_val); 142