Add a new Validator service, and EmailAddress validators.
The service runs a chain of one or more validators, each one being fed the
normalized output of the previous one.
The return from each validator should be a hash of valid (0 or 1), the new
normalized output (the untouched input if invalid or nothing needed to be
changed), and if invalid an error string. Optionally, a validator can also
include an "additionals" hash of extra information to be included in the
final response.
The complete list of validators included is:
OpenSRF::Application::Validator::Base
The base validator. Always returns valid.
OpenSRF::Application::Validator::Invalid
Always returns invalid for testing purposes.
OpenSRF::Application::Validator::EmailAddress::Regex
Does a very basic regular expression check on email addresses.
OpenSRF::Application::Validator::EmailAddress::DNS
Uses Net::DNS to look up the domain on an email address
Signed-off-by: Thomas Berezansky <tsbere@mvlc.org>
Signed-off-by: Dan Scott <dscott@laurentian.ca>
<max_spare_children>5</max_spare_children>
</unix_config>
</opensrf.settings>
+
+ <opensrf.validator>
+ <keepalive>1</keepalive>
+ <stateless>1</stateless>
+ <language>perl</language>
+ <implementation>OpenSRF::Application::Validator</implementation>
+ <max_requests>17</max_requests>
+ <unix_config>
+ <unix_sock>opensrf.validator_unix.sock</unix_sock>
+ <unix_pid>opensrf.validator_unix.pid</unix_pid>
+ <max_requests>1000</max_requests>
+ <unix_log>opensrf.validator_unix.log</unix_log>
+ <min_children>5</min_children>
+ <max_children>15</max_children>
+ <min_spare_children>3</min_spare_children>
+ <max_spare_children>5</max_spare_children>
+ </unix_config>
+ <app_settings>
+ <validators>
+ <emailaddress>
+ <modules>
+ <a_regex>
+ <implementation>OpenSRF::Application::Validator::EmailAddress::Regex</implementation>
+ </a_regex>
+ <b_dns>
+ <implementation>OpenSRF::Application::Validator::EmailAddress::DNS</implementation>
+ <check_mx_a>1</check_mx_a>
+ <!-- Change this to a 1 to check for IPV6 records as well as IPV4 -->
+ <check_aaaa>0</check_aaaa>
+ <!-- Uncomment this to specify a resolve.conf-like config file for DNS lookups -->
+ <!--<config_file>/path/to/file</config_file>-->
+ <!-- A set of IPs to ignore - Useful when your DNS provider intercepts NXDOMAIN (say, OpenDNS) -->
+ <ignore_ips>127.0.0.1,67.215.65.132</ignore_ips>
+ </b_dns>
+ </modules>
+ </emailaddress>
+ </validators>
+ </app_settings>
+ </opensrf.validator>
</apps>
</default>
<appname>opensrf.settings</appname>
<appname>opensrf.math</appname>
<appname>opensrf.dbmath</appname>
+ <appname>opensrf.validator</appname>
</activeapps>
<apps>
libgdbm-dev \
liblog-log4perl-perl\
libmodule-build-perl\
+ libnet-dns-perl\
libnet-jabber-perl\
libperl-dev\
libreadline5-dev\
perl-Log-Log4perl \
perl-Memcached-libmemcached \
perl-Module-Build \
+ perl-Net-DNS \
perl-Net-Server \
perl-Template-Toolkit \
perl-Test-Pod \
perl-libwww-perl \
perl-Log-Log4perl \
perl-Module-Build \
+ perl-Net-DNS \
perl-Net-Jabber \
perl-Net-Server \
perl-RPC-XML \
--- /dev/null
+package OpenSRF::Application::Validator;
+use base qw/OpenSRF::Application/;
+use OpenSRF::Application;
+use OpenSRF::Utils::SettingsClient;
+use OpenSRF::Utils::Logger;
+use Module::Load;
+
+my $logger = OpenSRF::Utils::Logger;
+my %modules;
+
+sub initialize {
+ my $sc = OpenSRF::Utils::SettingsClient->new;
+ my $validators = $sc->config_value( apps => 'opensrf.validator' => app_settings => 'validators' );
+ while(my $module = each %$validators ) {
+ __PACKAGE__->register_method(
+ api_name => "opensrf.validator.$module.validate",
+ method => 'do_validate',
+ argc => 1,
+ validator_name => $module
+ );
+ $modules{$module} = $validators->{$module};
+ }
+}
+
+sub do_validate {
+ my $self = shift;
+ my $client = shift;
+ my $input = shift;
+ my $return = { 'valid' => 1, 'normalized' => $input }; # Default return
+ my $validators = $modules{$self->{validator_name}}->{modules};
+ my @validator_names = sort keys %$validators;
+ my $additionals = ();
+
+ my $submodulename, $submodule;
+ while($return->{valid} && ($submodulename = shift @validator_names)) {
+ $submodule = $validators->{$submodulename};
+ my $implementation = $submodule->{implementation};
+ $logger->debug("Running request through $submodulename ($implementation)");
+ load $implementation;
+ my $result = $implementation->validate($return->{normalized}, $submodule);
+ if($result) {
+ $return = $result;
+ $additionals = {%$additionals, %{$return->{additionals}}} if $return->{additionals};
+ }
+ }
+ $return->{additionals} = $additionals;
+ return $return;
+}
+
+1;
--- /dev/null
+package OpenSRF::Application::Validator::Base;
+use strict;
+use warnings;
+
+sub validate {
+ my $self = shift;
+ my $input = shift;
+ my $settings = shift;
+ return { 'valid' => 1, 'normalized' => $input };
+}
+
+1;
--- /dev/null
+package OpenSRF::Application::Validator::EmailAddress::DNS;
+use base qw/OpenSRF::Application::Validator::Base/;
+
+use OpenSRF::Application::Validator::Base;
+use Net::DNS;
+
+use strict;
+use warnings;
+
+sub validate {
+ my $self = shift;
+ my $input = shift;
+ my $settings = shift;
+
+ my $return = { 'valid' => 0, 'normalized' => $input };
+
+ if(!$input->{emailaddress}) {
+ return { 'valid' => 0, 'normalized' => $input, 'error' => 'No Address' };
+ } elsif ($input->{emailaddress} !~ /@([^@]+)$/) {
+ return { 'valid' => 0, 'normalized' => $input, 'error' => 'Bad Address - Regex Check Failed' };
+ }
+ my $domain = $1;
+ my $checkMXA = $settings->{check_mx_a};
+ my $checkAAAA = $settings->{check_aaaa};
+ my $config_file = $settings->{config_file};
+ my @badAddrs;
+ if($settings->{ignore_ips}) {
+ @badAddrs = split(',', $settings->{ignore_ips});
+ }
+ my $res;
+ $res = Net::DNS::Resolver->new(config_file => $config_file, defnames => 0) if $config_file;
+ $res = Net::DNS::Resolver->new(defnames => 0) if !$config_file;
+ my @arecords;
+ # Look for MX records first
+ my $answer = $res->send($domain, 'MX');
+ foreach($answer->answer) {
+ if($_->type eq 'MX') {
+ push(@arecords, $_->exchange);
+ }
+ }
+ if(@arecords) {
+ if($checkMXA) {
+ OUTER: foreach my $checkdomain (@arecords) {
+ $answer = $res->send($checkdomain, 'A');
+ foreach my $record ($answer->answer) {
+ last if $record->type eq 'CNAME' || $record->type eq 'DNAME';
+ if($record->type eq 'A') {
+ next if grep { $_ eq $record->address } @badAddrs;
+ $return->{valid} = 1;
+ last OUTER;
+ }
+ }
+ if($checkAAAA) {
+ $answer = $res->send($checkdomain, 'AAAA');
+ foreach my $record ($answer->answer) {
+ last if $record->type eq 'CNAME' || $record->type eq 'DNAME';
+ if($record->type eq 'AAAA') {
+ next if grep { $_ eq $record->address } @badAddrs;
+ $return->{valid} = 1;
+ last OUTER;
+ }
+ }
+ }
+ }
+ $return->{error} = "MX Records Invalid" if(!$return->{valid});
+ } else {
+ $return->{valid} = 1;
+ }
+ } else {
+ $answer = $res->send($domain,'A');
+ foreach my $record ($answer->answer) {
+ last if $record->type eq 'CNAME' || $record->type eq 'DNAME';
+ if($record->type eq 'A') {
+ next if grep { $_ eq $record->address } @badAddrs;
+ $return->{valid} = 1;
+ last;
+ }
+ }
+ if(!$return->{valid} && $checkAAAA) {
+ $answer = $res->send($domain, 'AAAA');
+ foreach my $record ($answer->answer) {
+ last if $record->type eq 'CNAME' || $record->type eq 'DNAME';
+ if($record->type eq 'AAAA') {
+ next if grep { $_ eq $record->address } @badAddrs;
+ $return->{valid} = 1;
+ last;
+ }
+ }
+ }
+ $return->{error} = "No A Records Found" if(!$return->{valid});
+ }
+ $return->{normalized}->{emailaddress} = lc($return->{normalized}->{emailaddress}) if($return->{valid});
+ return $return;
+}
+
+1;
--- /dev/null
+package OpenSRF::Application::Validator::EmailAddress::Regex;
+use base qw/OpenSRF::Application::Validator::Base/;
+
+use OpenSRF::Application::Validator::Base;
+
+use strict;
+use warnings;
+
+sub validate {
+ my $self = shift;
+ my $input = shift;
+ my $settings = shift;
+
+ if(!$input->{emailaddress}) {
+ return { 'valid' => 0, 'normalized' => $input, 'error' => 'No Address' };
+ } elsif ($input->{emailaddress} !~ /^.+@[^@]+\.[^@]{2,}$/) {
+ return { 'valid' => 0, 'normalized' => $input, 'error' => 'Bad Address - Regex Check Failed' };
+ }
+ $input->{emailaddress} = lc($input->{emailaddress});
+ return { 'valid' => 1, 'normalized' => $input };
+}
+
+1;
--- /dev/null
+package OpenSRF::Application::Validator::Invalid;
+use base qw/OpenSRF::Application::Validator::Base/;
+
+use OpenSRF::Application::Validator::Base;
+
+use strict;
+use warnings;
+
+sub validate {
+ my $self = shift;
+ my $input = shift;
+ my $settings = shift;
+ return { 'valid' => 0, 'normalized' => $input, 'error' => 'Forced Invalid' };
+}
+
+1;