File Coverage

blib/lib/WE/Util/Htaccess.pm
Criterion Covered Total %
statement 6 98 6.1
branch 0 34 0.0
condition 0 12 0.0
subroutine 2 7 28.5
pod 0 1 0.0
total 8 152 5.2


line stmt bran cond sub pod time code
1             # -*- perl -*-
2              
3             #
4             # $Id: Htaccess.pm,v 1.7 2004/04/08 14:26:23 eserte Exp $
5             # Author: Slaven Rezic
6             #
7             # Copyright (C) 2002 Online Office Berlin. All rights reserved.
8             # Copyright (C) 2002, 2003 Slaven Rezic.
9             # This is free software; you can redistribute it and/or modify it under the
10             # terms of the GNU General Public License, see the file COPYING.
11              
12             #
13             # Mail: slaven@rezic.de
14             # WWW: http://we-framework.sourceforge.net
15             #
16              
17             package WE::Util::Htaccess;
18              
19 2     2   5109 use strict;
  2         6  
  2         72  
20 2     2   9 use vars qw($VERSION);
  2         3  
  2         3457  
21             $VERSION = sprintf("%d.%02d", q$Revision: 1.7 $ =~ /(\d+)\.(\d+)/);
22              
23             =head1 NAME
24              
25             WE::Util::Htaccess - create apache .htaccess files
26              
27             =head1 SYNOPSIS
28              
29             use WE::Util::Htaccess;
30             WE::Util::Htaccess::create("/var/www/htdocs/.htaccess", $obj_db
31             -authname => "sample",
32             -authuserfile => "/var/www/.htpasswd",
33             -authgroupfile => "/var/www/.htgroup",
34             -inherit => 1,
35             -add => "ErrorDocument 401 /index.html",
36             );
37              
38              
39             =head1 DESCRIPTION
40              
41             This module is used to create Apache C<.htaccess> files from a
42             C database. All objects in the database are traversed (or
43             restricted by a filter) and if the object contains a C
44             attribute, an entry for the C<.htaccess> file is created.
45              
46             The C attribute should be a string with the following syntax:
47              
48             "[user=|group=]id1,[user=|group=]id2..."
49              
50             If netither "user=" nor "group=" is specified, then a user id is
51             assumed. Example:
52              
53             "bla,group=foo,user=bar"
54              
55             means: the users C and C and the group C.
56              
57             The files C<.htpasswd>, C<.htgroup> and C<.htaccess> are always
58             protected from WWW access, so you can use these names for the
59             user/group files, if you have to store these files in a WWW readable
60             directory.
61              
62             The C function expects the following arguments:
63              
64             =over 4
65              
66             =item -authname => $name
67              
68             The name of the authorization realm. By default it is "WE Authentication".
69              
70             =item -authtype => $type
71              
72             The type of user authentication. By default it is "Basic".
73              
74             =item -authuserfile => $file
75              
76             The path to the .htpasswd file (see L). This is
77             required unless set by an entry in the global C.
78              
79             =item -authgroupfile => $file
80              
81             The path to the groups file (see L). This is
82             required if there are any group authentifications in the object
83             database and no entry from the global C can be used.
84              
85             =item -inherit => $bool
86              
87             If set to true (default) then inherit folder rights to their children
88             and subfolders.
89              
90             =item -filter => sub { my($obj) = @_; ... }
91              
92             A filter callback for restricting an object or sub-tree. The callback
93             will get the current object as parameter and should return a boolean
94             value. If the returned value is false, then the object is not
95             processed; if it is a folder then the descendants of the folder are
96             not processed either.
97              
98             =item -add => $string
99              
100             A C<$string> to be added to the .htaccess file. An example would be to
101             add an C directive (see
102             L).
103              
104             =item -addfile => $file
105              
106             Like C<-add>, but read the contents from the named file. It is
107             possible to use C<-add> and C<-addfile> together.
108              
109             =item -restrict => $restrict
110              
111             Alternative restriction scheme. If set, then no access to the
112             C database is done. C<-inherit> and C<-filter> are
113             ignored. The C<$restrict> string should be of the form:
114              
115             type1 value1 value2 value3; type2 value4 value5 ...
116              
117             where I is either C or C and I a group or
118             user name.
119              
120             =item -getaliases => sub { my($id) = @_; ... }
121              
122             This should be a code reference which receives the object id as
123             parameter and returns a list of alias names for this page (excluding
124             the supplied id).
125              
126             =cut
127              
128             sub create {
129 0     0 0   my($dest_file, $obj_db, %args) = @_;
130              
131 0           my $s = _create($obj_db, %args);
132              
133 0 0         open(D, ">$dest_file") or die "Can't write to $dest_file: $!";
134 0           print D $s;
135 0           close D;
136             }
137              
138             sub _create {
139 0     0     my($obj_db, %args) = @_;
140              
141 0   0       my $authname = $args{-authname} || "WE Authentication";
142 0   0       my $authtype = $args{-authtype} || "Basic";
143 0 0         my $inherit = defined $args{-inherit} ? $args{-inherit} : 1;
144 0           my $filter = delete $args{-filter};
145 0   0       my $restrict = delete $args{-restrict} || "";
146 0           my $get_aliases = delete $args{-getaliases};
147 0           my $add = "";
148 0 0         if (defined $args{-add}) {
149 0           $add .= "\n" . delete($args{-add}) . "\n";
150             }
151 0 0         if (defined $args{-addfile}) {
152 0 0         if (!open(ADDFILE, $args{-addfile})) {
153 0           warn "Can't open file specified in -addfile: $args{-addfile}: $!";
154             } else {
155 0           local $/ = undef;
156 0           $add .= "\n" . scalar() . "\n";
157 0           close ADDFILE;
158             }
159             }
160              
161             # get all objects with restrictions
162 0           my %restr_objs;
163 0 0         if ($restrict eq '') {
164             $obj_db->walk_preorder($obj_db->root_object, sub {
165 0     0     my($id) = @_;
166 0           my($obj) = $obj_db->get_object($id);
167              
168 0 0 0       return if ($filter && !$filter->($obj));
169              
170 0 0         if ($inherit) {
171 0           my(@parent_ids) = $obj_db->parent_ids($id);
172 0           foreach my $p_id (@parent_ids) {
173 0 0         if (exists $restr_objs{$p_id}) {
174 0           push @{ $restr_objs{$id} }, @{ $restr_objs{$p_id} };
  0            
  0            
175             }
176             }
177             }
178              
179 0 0 0       if (defined $obj->{WWWAuth} && $obj->{WWWAuth} ne "") {
180 0           my(@auth_token) = split /,/, $obj->{WWWAuth};
181 0           foreach my $auth_token (@auth_token) {
182 0 0         if ($auth_token =~ /^([^=]+)=(.*)$/) {
183 0           push @{ $restr_objs{$id} }, [$1, $2];
  0            
184             } else {
185 0           push @{ $restr_objs{$id} }, [user => $auth_token];
  0            
186             }
187             }
188             }
189 0           });
190             }
191              
192             # norm requirements so it is easier to collect requirements
193 0           my %restr_reqs; # require-string => [objid ...]
194 0           while(my($objid, $reqs) = each %restr_objs) {
195 0           my $require_string = _norm_requirements($objid, $reqs);
196 0           push @{ $restr_reqs{$require_string} }, $objid;
  0            
197             }
198              
199             # create auth/files sections
200 0           my $s = "";
201 0 0         if ($restrict ne '') {
202 0           $s .= <
203             AuthName "$authname"
204             AuthType $authtype
205             EOF
206 0 0         $s .= "AuthGroupFile $args{-authgroupfile}\n"
207             if $args{-authgroupfile};
208 0 0         $s .= "AuthUserFile $args{-authuserfile}\n"
209             if $args{-authuserfile};
210 0           my(@token1) = split /\s*;\s*/, $restrict;
211 0           for my $token (@token1) {
212 0           my($type, @val) = split /\s+/, $token;
213 0           $s .= "require $type @val\n";
214             }
215 0           $s .= "\n";
216             } else {
217 0           while(my($restr_reqs, $ids) = each %restr_reqs) {
218 0           $s .= "
219 0           my @ids = @$ids;
220 0           my(%aliases, @aliases);
221 0 0         if ($get_aliases) {
222 0           for my $id (@ids) {
223 0           my @new_aliases = $get_aliases->($id);
224 0           @aliases{@new_aliases} = (1) x @new_aliases;
225             }
226 0           @aliases = keys %aliases;
227             }
228 0           $s .= join("|", map { quotemeta($_) } @ids, @aliases);
  0            
229 0           $s .= ")\\.[^\\.]*\$\">\n";
230 0           $s .= "AuthName \"$authname\"\n";
231 0           $s .= "AuthType $authtype\n";
232 0 0         $s .= "AuthGroupFile $args{-authgroupfile}\n"
233             if $args{-authgroupfile};
234 0 0         $s .= "AuthUserFile $args{-authuserfile}\n"
235             if $args{-authuserfile};
236 0           $s .= $restr_reqs;
237 0           $s .= "\n\n";
238             }
239             }
240              
241 0           $s .= _protect_ourselves();
242              
243 0           $s .= $add;
244              
245 0           $s;
246             }
247              
248             sub _protect_ourselves {
249             <<'EOF'
250            
251             Order deny,allow
252             Deny from all
253             Satisfy All
254            
255              
256             EOF
257 0     0     }
258              
259             sub _norm_requirements {
260 0     0     my($objid, $reqs) = @_;
261 0           my %reqs_by_type;
262 0           foreach my $req (@$reqs) {
263 0           my($type, $name) = @$req;
264 0           push @{ $reqs_by_type{$type} }, $name;
  0            
265             }
266 0           my $require_string = "";
267 0           foreach my $type (sort keys %reqs_by_type) {
268             # make unique
269 0           my %values = map {($_=>1)} @{ $reqs_by_type{$type} };
  0            
  0            
270 0           $require_string .= "require $type " . join(" ", sort keys %values) . "\n";
271             }
272 0           $require_string;
273             }
274              
275             1;
276              
277             __END__