File Coverage

blib/lib/Mail/SpamAssassin/Conf/LDAP.pm
Criterion Covered Total %
statement 12 85 14.1
branch 0 18 0.0
condition 0 12 0.0
subroutine 4 10 40.0
pod 1 5 20.0
total 17 130 13.0


line stmt bran cond sub pod time code
1             # <@LICENSE>
2             # Licensed to the Apache Software Foundation (ASF) under one or more
3             # contributor license agreements. See the NOTICE file distributed with
4             # this work for additional information regarding copyright ownership.
5             # The ASF licenses this file to you under the Apache License, Version 2.0
6             # (the "License"); you may not use this file except in compliance with
7             # the License. You may obtain a copy of the License at:
8             #
9             # http://www.apache.org/licenses/LICENSE-2.0
10             #
11             # Unless required by applicable law or agreed to in writing, software
12             # distributed under the License is distributed on an "AS IS" BASIS,
13             # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14             # See the License for the specific language governing permissions and
15             # limitations under the License.
16             # </@LICENSE>
17              
18             =head1 NAME
19              
20             Mail::SpamAssassin::Conf::LDAP - load SpamAssassin scores from LDAP database
21              
22             =head1 SYNOPSIS
23              
24             (see Mail::SpamAssassin)
25              
26              
27             =head1 DESCRIPTION
28              
29             Mail::SpamAssassin is a module to identify spam using text analysis and
30             several internet-based realtime blacklists.
31              
32             This class is used internally by SpamAssassin to load scores from an LDAP
33             database. Please refer to the C<Mail::SpamAssassin> documentation for public
34             interfaces.
35              
36             =head1 METHODS
37              
38             =over 4
39              
40             =cut
41              
42              
43             use Mail::SpamAssassin::Logger;
44 41     41   266  
  41         78  
  41         2050  
45             use strict;
46 41     41   217 use warnings;
  41         73  
  41         680  
47 41     41   165 # use bytes;
  41         76  
  41         947  
48             use re 'taint';
49 41     41   181  
  41         77  
  41         30013  
50             our @ISA = qw();
51              
52             ###########################################################################
53              
54             my $class = shift;
55             $class = ref($class) || $class;
56 0     0 0   my ($main) = @_;
57 0   0        
58 0           my $self = {
59             'main' => $main
60 0           };
61              
62             bless ($self, $class);
63             $self;
64 0           }
65 0            
66             ###########################################################################
67              
68             dbg("ldap: loading Net::LDAP and URI");
69             eval {
70             require Net::LDAP; # actual server connection
71 0     0 0   require URI; # parse server connection dsn
72 0           };
73 0            
74 0           # do any other preloading that will speed up operation
75             }
76              
77             ###########################################################################
78              
79             =item $f->load ($username)
80              
81             Read configuration parameters from LDAP server and parse scores from it.
82              
83             =back
84              
85             =cut
86              
87             my ($self, $username) = @_;
88              
89             my $conf = $self->{main}->{conf};
90             my $url = $conf->{user_scores_dsn}; # an ldap URI
91 0     0 1   dbg("ldap: URL is $url");
92             if(!defined($url) || $url eq '') {
93 0           dbg("ldap: No URL defined; skipping LDAP");
94 0           return;
95 0           }
96 0 0 0        
97 0           eval {
98 0           # make sure we can see croak messages from DBI
99             local $SIG{'__DIE__'} = sub { warn "$_[0]"; };
100             require Net::LDAP;
101             require URI;
102             load_with_ldap($self, $username, $url);
103 0     0     1;
  0            
104 0           } or do {
105 0           my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat;
106 0           if ($conf->{user_scores_fail_to_global}) {
107 0           info("ldap: failed to load user (%s) scores from LDAP server, ".
108 0 0         "using a global default: %s", $username, $eval_stat);
109 0 0         return 1;
  0            
110 0 0         } else {
111 0           warn sprintf(
112             "ldap: failed to load user (%s) scores from LDAP server: %s\n",
113 0           $username, $eval_stat);
114             return 0;
115 0           }
116             };
117             }
118 0            
119             my ($self, $username, $url) = @_;
120              
121             # ldapurl = scheme "://" [hostport] ["/"
122             # [dn ["?" [attributes] ["?" [scope]
123             # ["?" [filter] ["?" extensions]]]]]]
124 0     0 0    
125             my $uri = URI->new("$url");
126              
127             my $host = $uri->host;
128             if (!defined($host) || $host eq '') {
129             dbg("ldap: No server specified, assuming localhost");
130 0           $host = "localhost";
131             }
132 0           my $port = $uri->port;
133 0 0 0       my $base = $uri->dn;
134 0           my @attr = $uri->attributes;
135 0           my $scope = $uri->scope;
136             my $filter = $uri->filter;
137 0           my $scheme = $uri->scheme;
138 0           my %extn = $uri->extensions; # unused
139 0            
140 0           $filter =~ s/__USERNAME__/$username/g;
141 0           dbg("ldap: host=$host, port=$port, base='$base', attr=${attr[0]}, scope=$scope, filter='$filter'");
142 0            
143 0           my $main = $self->{main};
144             my $conf = $main->{conf};
145 0           my $ldapuser = $conf->{user_scores_ldap_username};
146 0           my $ldappass = $conf->{user_scores_ldap_password};
147              
148 0           if(!$ldapuser) {
149 0           undef($ldapuser);
150 0           } else {
151 0           dbg("ldap: user='$ldapuser'");
152             }
153 0 0          
154 0           if(!$ldappass) {
155             undef($ldappass);
156 0           } else {
157             # don't log this to avoid leaking sensitive info
158             # dbg("ldap: pass='$ldappass'");
159 0 0         }
160 0            
161             my $f_attribute = $attr[0];
162              
163             my $ldap = Net::LDAP->new ("$host:$port",
164             onerror => "warn",
165             scheme => $scheme);
166 0            
167             if (!defined($ldapuser) && !defined($ldappass)) {
168 0           $ldap->bind;
169             } else {
170             $ldap->bind($ldapuser, password => $ldappass);
171             }
172 0 0 0        
173 0           my $result = $ldap->search( base => $base,
174             filter => $filter,
175 0           scope => $scope,
176             attrs => \@attr
177             );
178 0            
179             my $config_text = '';
180             foreach my $entry ($result->all_entries) {
181             my @v = $entry->get_value($f_attribute);
182             foreach my $v (@v) {
183             dbg("ldap: retrieving prefs for $username: $v");
184 0           $config_text .= $v."\n";
185 0           }
186 0           }
187 0           if ($config_text ne '') {
188 0           $conf->{main} = $main;
189 0           $conf->parse_scores_only($config_text);
190             delete $conf->{main};
191             }
192 0 0         return;
193 0           }
194 0            
195 0           ###########################################################################
196              
197 0            
198             ###########################################################################
199              
200             1;