summaryrefslogtreecommitdiffstats
path: root/contrib/cvs/contrib/cvs_acls.in
blob: 990f2076c712a2b967090f95d7921fa11acfcf6d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#! @PERL@
# -*-Perl-*-
#
# Access control lists for CVS.  dgg@ksr.com (David G. Grubbs)
# Branch specific controls added by voisine@bytemobile.com (Aaron Voisine)
#
# CVS "commitinfo" for matching repository names, running the program it finds
# on the same line.  More information is available in the CVS man pages.
#
# ==== INSTALLATION:
#
# To use this program as I intended, do the following four things:
#
# 0. Install PERL.  :-)
#
# 1. Put one line, as the *only* non-comment line, in your commitinfo file:
#
#	DEFAULT		/usr/local/bin/cvs_acls
#
# 2. Install this file as /usr/local/bin/cvs_acls and make it executable.
#
# 3. Create a file named CVSROOT/avail and optionally add it to
#    CVSROOT/checkoutlist and check it in.  See the CVS manual's
#    administrative files section about checkoutlist.  Typically:
#
#    $ cvs checkout CVSROOT
#    $ cd CVSROOT
#    [ create the avail file ]
#    [ add avail to checkoutlist ]
#    $ cvs add avail
#    $ cvs commit -m 'Added avail for use with cvs_acls.' avail checkoutlist
#
# ==== FORMAT OF THE avail FILE:
#
# The avail file determines whether you may commit files.  It contains lines
# read from top to bottom, keeping track of a single "bit".  The "bit"
# defaults to "on".  It can be turned "off" by "unavail" lines and "on" by
# "avail" lines.  ==> Last one counts.
#
# Any line not beginning with "avail" or "unavail" is ignored.
#
# Lines beginning with "avail" or "unavail" are assumed to be '|'-separated
# triples: (All spaces and tabs are ignored in a line.)
#
# {avail.*,unavail.*} [|user,user,... [|repos,repos,... [|branch,branch,...]]]
#
#    1. String starting with "avail" or "unavail".
#    2. Optional, comma-separated list of usernames.
#    3. Optional, comma-separated list of repository pathnames.
#	These are pathnames relative to $CVSROOT.  They can be directories or
#	filenames.  A directory name allows access to all files and
#	directories below it.
#    4. Optional, comma-separated list of branch tags.
#	If not specified, all branches are assumed. Use HEAD to reference the
#	main branch.
#
# Example:  (Text from the ';;' rightward may not appear in the file.)
#
#	unavail			;; Make whole repository unavailable.
#	avail|dgg		;; Except for user "dgg".
#	avail|fred, john|bin/ls	;; Except when "fred" or "john" commit to
#				;; the module whose repository is "bin/ls"
#	avail|ed|/bin/ls|stable ;; Except when "ed" commits to the "stable"
#				;; branch of the "bin/ls" repository 
#
# PROGRAM LOGIC:
#
#	CVS passes to @ARGV an absolute directory pathname (the repository
#	appended to your $CVSROOT variable), followed by a list of filenames
#	within that directory.
#
#	We walk through the avail file looking for a line that matches the
#	username, repository and branch.
#
#	A username match is simply the user's name appearing in the second
#	column of the avail line in a space-or-comma separate list.
#
#	A repository match is either:
#		- One element of the third column matches $ARGV[0], or some
#		  parent directory of $ARGV[0].
#		- Otherwise *all* file arguments ($ARGV[1..$#ARGV]) must be
#		  in the file list in one avail line.
#	    - In other words, using directory names in the third column of
#	      the avail file allows committing of any file (or group of
#	      files in a single commit) in the tree below that directory.
#	    - If individual file names are used in the third column of
#	      the avail file, then files must be committed individually or
#	      all files specified in a single commit must all appear in
#	      third column of a single avail line.
#
#	A branch match is either:
#		- When no branches are listed in the fourth column.
#		- One element from the fourth column matches each of the tag
#		  names for $ARGV[1..$#ARGV] found in the CVS/Entries file.
#		- HEAD specified in the fourth column will match if there
#		  is no tag listed in the CVS/Entries file.
#

$debug = 0;
$cvsroot = $ENV{'CVSROOT'};
$availfile = $cvsroot . "/CVSROOT/avail";
$entries = "CVS/Entries";
$myname = $ENV{"USER"} if !($myname = $ENV{"LOGNAME"});

eval "print STDERR \$die='Unknown parameter $1\n' if !defined \$$1; \$$1=\$';"
    while ($ARGV[0] =~ /^(\w+)=/ && shift(@ARGV));
exit 255 if $die;		# process any variable=value switches

die "Must set CVSROOT\n" if !$cvsroot;
($repos = shift) =~ s:^$cvsroot/::;
grep($_ = $repos . '/' . $_, @ARGV);

print "$$ Repos: $repos\n","$$ ==== ",join("\n$$ ==== ",@ARGV),"\n" if $debug;

$exit_val = 0;				# Good Exit value

$universal_off = 0;

my %branch;
my $f;

open(ENTRIES, $entries) || die("Cannot open $entries.\n");
while(<ENTRIES>) {
    chop;
    next if /^\s*$/;
    if(m|^[^/]*/([^/]*)/(?:[^/]*/)*[^/]?([^/]*)$|) {
	$branch{$repos . '/' . $1} = ($2) ? $2 : "HEAD"; 
	print "$$ $1/$2\n" if $debug;
    }
}
close(ENTRIES);

open (AVAIL, $availfile) || exit(0);	# It is ok for avail file not to exist
while (<AVAIL>) {
    chop;
    next if /^\s*\#/;
    next if /^\s*$/;
    ($flagstr, $u, $m, $b) = split(/[\s,]*\|[\s,]*/, $_);

    # Skip anything not starting with "avail" or "unavail" and complain.
    (print "Bad avail line: $_\n"), next
	if ($flagstr !~ /^avail/ && $flagstr !~ /^unavail/);

    # Set which bit we are playing with. ('0' is OK == Available).
    $flag = (($& eq "avail") ? 0 : 1);

    # If we find a "universal off" flag (i.e. a simple "unavail") remember it
    $universal_off = 1 if ($flag && !$u && !$m && !$b);

    # $myname considered "in user list" if actually in list or is NULL
    $in_user = (!$u || grep ($_ eq $myname, split(/[\s,]+/,$u)));
    print "$$ \$myname($myname) in user list: $_\n" if $debug && $in_user;

    # Module matches if it is a NULL module list in the avail line.  If module
    # list is not null, we check every argument combination.
    if (!($in_repo = !$m)) {
	my @tmp = split(/[\s,]+/,$m);
	for $j (@tmp) {
	    # If the repos from avail is a parent(or equal) dir of $repos, OK
	    $in_repo = 1, last if ($repos eq $j || $repos =~ /^$j\//);
	}
	if (!$in_repo) {
	    $in_repo = 1;
	    for $j (@ARGV) {
		last if !($in_repo = grep ($_ eq $j, @tmp));
	    }
	}
    }
    print "$$ \$repos($repos) in repository list: $_\n" if $debug && $in_repo;

    # Branch matches if it is in the branch list in the avail line, the branch
    # list is NULL, or there is no branch and HEAD is in the branch list.
    if(!($in_branch = !$b)) {
	@bls = split (/[\s,]+/,$b);

	for $j (@ARGV) {
	   $f = $j;
	   last if !($in_branch = grep($_ eq $branch{$j}, @bls)); 
	}
    }
    print "$$ \$branch($branch{$f}) in branch list: $_\n"
	if $debug && $in_branch;

    $exit_val = $flag if ($in_user && $in_repo && $in_branch);
    print "$$ ==== \$exit_val = $exit_val\n$$ ==== \$flag = $flag\n" if $debug;
}
close(AVAIL);
print "$$ ==== \$exit_val = $exit_val\n" if $debug;
print "**** Access denied: Insufficient Karma ($myname|$repos|$branch{$f})\n"
	if $exit_val;
print "**** Access allowed: Personal Karma exceeds Environmental Karma.\n"
	if $universal_off && !$exit_val;
exit($exit_val);
OpenPOWER on IntegriCloud