xref: /openbsd/gnu/usr.bin/cvs/contrib/cvs_acls.in (revision 43c1707e)
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