File Coverage

blib/lib/SVN/Notify/Filter/AuthZMail.pm
Criterion Covered Total %
statement 18 84 21.4
branch 0 26 0.0
condition n/a
subroutine 6 10 60.0
pod 2 2 100.0
total 26 122 21.3


line stmt bran cond sub pod time code
1             package SVN::Notify::Filter::AuthZMail;
2              
3              
4             # $Id: AuthZMail.pm 19 2008-07-17 06:14:46Z jborlik $
5              
6 2     2   661155 use warnings;
  2         6  
  2         95  
7 2     2   13 use strict;
  2         4  
  2         72  
8 2     2   3961 use SVN::Notify;
  2         95164  
  2         71  
9 2     2   2411 use SVN::Access;
  2         89664  
  2         79  
10 2     2   25 use File::Basename;
  2         3  
  2         203  
11 2     2   11 use Carp;
  2         5  
  2         2317  
12              
13             =head1 NAME
14              
15             SVN::Notify::Filter::AuthZMail - Determines Subversion accounts to receive the email, via the AuthZSVNAccess file
16              
17             =head1 VERSION
18              
19             Version 1.01
20              
21             =cut
22              
23             our $VERSION = '1.01';
24              
25             my $acl; # Access control list, via SVN::Access
26             my $debugflag=0; # 1=debug output, not for general use
27              
28             SVN::Notify->register_attributes(
29             authz_file => 'authz_file=s',
30             authz_module => 'authz_module=s',
31             );
32              
33             =head1 SYNOPSIS
34              
35             This is intended to work with SVN::Notify, as part of a subversion post-commit hook.
36              
37             svnnotify --repos-path "$1" --revision "$2" ..etc.. \
38             --filter AuthZMail \
39             --authz_file /x/x/x/authz \
40             --authz_module yyy
41              
42              
43             =head1 DESCRIPTION
44              
45             This module is a filter for SVN::Notify, intended to assist with the maintenance of
46             access control lists with Subversion repositories. This module removes the need to
47             maintain a separate list of people to send email notification messages to (via
48             svnnotify --to arguments), from the AuthZSVNAccessFile.
49              
50             Based upon the Subversion revision, it finds the files that were modified in the
51             commit, determines the union of people that access to those files (via the AuthZ
52             file), and passes those account names into the SVN::Notify. Hopefully, this module
53             follows Subversion's rules for determining access.
54              
55             This module works well with SVN::Notify::Filter::EmailFlatFileDB. If this filter
56             is put first in the svnnotify argument list, it will add to the usernames to
57             SVN::Notify's list of recipient names, and then the EmailFlatFileDB filter will
58             convert those usernames into email addresses.
59              
60             (Note that for SVN::Notify versions less than 2.76, you may need to include a
61             --to option line in order to bypass some of SVN::Notify's checking.)
62              
63              
64             =head1 DEPENDENCIES
65              
66             This module depends upon SVN::Notify, by David Wheeler. It also depends upon
67             SVN::Access, by Michael Gregorowicz, to parse the AuthZ file.
68              
69              
70              
71             =head1 FUNCTIONS
72              
73             =head2 from
74              
75             This SVN::Notify callback function is not used.
76              
77             =cut
78              
79             # SVN::Notify filter callback function for --from
80             # The first argument is the SVN::Notify object
81             # The second argument is the sender address, passed in
82             # as the committer account name.
83             sub from {
84 0     0 1   my ($notifier, $from) = @_;
85              
86 0 0         if ($debugflag) { print "AuthZEmail from=$from\n"; }
  0            
87              
88 0           return $from;
89             }
90              
91              
92             =head2 pre_prepare
93              
94             This SVN::Notify callback function adds the usernames to the list,
95             based on the contents of the authz file. This is executed
96             automatically by the SVN::Notify framework. It will add to the
97             list of recipients. Note that any other SVN::Notify::Filter
98             that manipulates the list of recipients should be specified
99             after this filter.
100              
101             =cut
102              
103             # SVN::Notify filter callback function for --to
104             # The first argument is the SVN::Notify object
105             # The second argument is an array reference to the
106             # recipient email addresses.
107              
108             # This will add usernames to the list, based on
109             # the contents of the authz file.
110              
111             sub pre_prepare {
112 0     0 1   my ($notifier) = @_;
113              
114 0           my @recip = $notifier->to;
115            
116 0           my @files;
117 0           my $svnrev = $notifier->revision;
118 0           my $svnlook = $notifier->svnlook;
119 0           my $path_to_repo = $notifier->repos_path;
120 0           my $authz_module = $notifier->authz_module;
121 0           my $authz_file = $notifier->authz_file;
122              
123             # initialize the AuthZ file reader
124 0           $acl = SVN::Access->new(acl_file => $authz_file);
125              
126            
127 0 0         if ($svnrev > 0) {
128             # add additional files to the list by parsing the output of svnlook
129             # for the given rev number
130 0           my $cmd = "$svnlook changed $path_to_repo -r $svnrev";
131 0 0         open (SVNIN, "$cmd |") or croak "SVN::Notify::Filter::AuthZMail can't open svnlook while doing 'to' list: $cmd";
132              
133 0           while () {
134 0           chomp;
135 0           /^\w+\s+(.+)/;
136 0           push(@files,$1);
137              
138             }
139              
140 0           close SVNIN;
141             }
142              
143 0 0         if ($debugflag) { print 'AuthZMail numfiles=' . $#files . ': ' . join('|', @files) . "\n"; }
  0            
144              
145             # Now process the files. Note that we will not clear out the hash,
146             # so it will be the union of all users that have access to any file.
147 0           my %users = ();
148 0           for my $thisfile (@files) {
149 0           _listUsersThatCanAccessFile($authz_module,$thisfile,\%users);
150             }
151 0 0         if ($debugflag) { _writePerms("all files", \%users); }
  0            
152              
153 0           push @recip, keys(%users);
154              
155 0           $notifier->to(@recip);
156             }
157              
158              
159              
160             =head1 AUTHOR
161              
162             Jeffrey Borlik, C<< >>
163              
164             =head1 BUGS
165              
166             Please report any bugs or feature requests to C, or through
167             the web interface at L. I will be notified, and then you'll
168             automatically be notified of progress on your bug as I make changes.
169              
170              
171              
172              
173             =head1 SUPPORT
174              
175             You can find documentation for this module with the perldoc command.
176              
177             perldoc SVN::Notify::Filter::AuthZMail
178              
179              
180             You can also look for information at:
181              
182             =over 4
183              
184             =item * RT: CPAN's request tracker
185              
186             L
187              
188             =item * AnnoCPAN: Annotated CPAN documentation
189              
190             L
191              
192             =item * CPAN Ratings
193              
194             L
195              
196             =item * Search CPAN
197              
198             L
199              
200             =back
201              
202              
203             =head1 ACKNOWLEDGEMENTS
204              
205             Thanks to David Wheeler for his SVN::Notify Perl module. Also, thanks to Michael Gregorowicz
206             for SVN::Access.
207              
208             =head1 COPYRIGHT & LICENSE
209              
210             Copyright 2008 Jeffrey Borlik, all rights reserved.
211              
212             This program is free software; you can redistribute it and/or modify it
213             under the same terms as Perl itself.
214              
215              
216             =cut
217              
218              
219             #----------------------------------------------------------
220              
221              
222             # This function will return a hash of users and permissions,
223             # for a given file in the SVN authz file.
224             #
225             # Note that it requires a global variable $acl, and
226             # arguments of the repository name and filename
227              
228             sub _listUsersThatCanAccessFile {
229              
230 0     0     my ($repos,$file,$users) = @_;
231              
232             # keys are the usernames, values are the permissions (r, rw)
233              
234 0           my ($file_name, $file_path, $file_suff) = fileparse($file);
235 0           $file_path = '/' . $file_path;
236              
237              
238             # First, develop a list of "resources" that apply to this file.
239 0           my @applic_res = ();
240              
241             #iterate through all possible paths
242             # (Another option would be to iterate through all known resources)
243 0           my @allpaths = split('/',$file_path);
244             #print '>' . join('|',@allpaths) . "<\n";
245              
246 0           for (@allpaths) {
247             # All repositories
248 0           my $gotit = $acl->resource('/' . $_);
249 0 0         if ($gotit) {
250 0           push(@applic_res, $gotit);
251             }
252             # Specified repository
253 0           $gotit = $acl->resource($repos . ':/' . $_);
254 0 0         if ($gotit) {
255 0           push(@applic_res, $gotit);
256             }
257             }
258              
259             # Sort them
260 0           @applic_res = sort { $b->name cmp $a->name } @applic_res;
  0            
261             #print "Sorted applicable resources:\n";
262             #for (@applic_res) { print $_->name . '|'; }
263             #print "\n";
264              
265             # Now, iterate through the list, adding users/rights to the map.
266             # Groups are immediately "expanded".
267             # The longest resources are the "most local", so apply them first
268             # Subsequent user entries will be ignored. This is what the
269             # SVN Red Book says: Most-specific path wins, and within the
270             # each path, the highest user listing.
271              
272 0           foreach my $this_res (@applic_res) {
273 0           while (my ($user, $perms) = each(%{$this_res->authorized})) {
  0            
274 0 0         if ($user eq '*') {
    0          
275             # anonymous access... we will just ignore this
276             } elsif (substr($user,0,1) eq '@') {
277             # group
278 0           my $groupname = substr($user,1);
279 0           my $res_group = $acl->group($groupname);
280 0 0         if (not $res_group) {
281 0           print "Can't find group $groupname!!\n";
282             } else {
283             # iterate through all of the group members
284 0           foreach my $member ($res_group->members) {
285 0 0         if (not exists $$users{$member}) {
286 0           $$users{$member} = $perms;
287             # print "Adding user (group member): $member\n";
288             }
289             }
290             }
291             } else {
292             # Non-group was specified
293 0 0         if (not exists $$users{$user}) {
294 0           $$users{$user} = $perms;
295             # print "Adding user: $user\n";
296             }
297             }
298             }
299             }
300              
301             # Next, we will remove users that do not have valid permissions
302 0           foreach my $this_user (keys(%$users)) {
303 0 0         if ($$users{$this_user} eq '') {
304 0           delete $$users{$this_user};
305             }
306             }
307              
308             }
309              
310              
311             #----------------------------------------------------------
312             # Write-out routines, for debugging/testing
313              
314             sub _writePerms {
315 0     0     my $testfile = shift;
316 0           my %users = %{shift(@_)};
  0            
317            
318 0           print "AuthZMail for $testfile.........\n";
319 0           for my $user (keys(%users)) {
320 0           print " $user = " . $users{$user} . "\n";
321             }
322             }
323              
324              
325             1; # End of SVN::Notify::Filter::AuthZMail