aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Fripost/Schema
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem.moulin@fripost.org>2013-01-29 02:37:58 +0100
committerGuilhem Moulin <guilhem.moulin@fripost.org>2013-01-29 02:37:58 +0100
commit7b81775603b8208c995cd1c4a15cd2a287009404 (patch)
treef8946c7eff5b1c8de2e3b6ee7944f8e5180891cc /lib/Fripost/Schema
parentae6b8a2905bfc7905030479e06f3490f2c901099 (diff)
Bug fixes.
Diffstat (limited to 'lib/Fripost/Schema')
-rw-r--r--lib/Fripost/Schema/Auth.pm5
-rw-r--r--lib/Fripost/Schema/Domain.pm58
-rw-r--r--lib/Fripost/Schema/Util.pm79
3 files changed, 94 insertions, 48 deletions
diff --git a/lib/Fripost/Schema/Auth.pm b/lib/Fripost/Schema/Auth.pm
index c6325b8..d9c0267 100644
--- a/lib/Fripost/Schema/Auth.pm
+++ b/lib/Fripost/Schema/Auth.pm
@@ -253,10 +253,7 @@ converted to ASCII.
sub mail2dn {
my $self = shift;
- my $user = shift // return;
-
- $user =~ s/^([^\@]+)$/\@$1/;
- my ($l,$d) = split_addr($user, -encode => 'ascii');
+ my ($l,$d) = split_addr(shift, -encode => 'ascii') or return;
my @dn = ({fvd => $d}, @{$self->suffix});
unshift @dn, {fvl => $l} if $l;
diff --git a/lib/Fripost/Schema/Domain.pm b/lib/Fripost/Schema/Domain.pm
index 3caffb5..4cc12e4 100644
--- a/lib/Fripost/Schema/Domain.pm
+++ b/lib/Fripost/Schema/Domain.pm
@@ -18,11 +18,12 @@ use utf8;
use parent 'Fripost::Schema';
use Fripost::Schema::Util qw/softdie dn2mail email_valid domain_valid
- canonical_dn ldap_explode_dn ldap_error/;
+ canonical_dn ldap_explode_dn ldap_error
+ ldap_assert_absent/;
use Fripost::Schema::Mail;
use Net::IDN::Encode qw/domain_to_ascii domain_to_unicode email_to_unicode/;
use URI::Escape::XS 'encodeURIComponent';
-use Encode;
+use Encode ();
use Net::Domain::TLD 'tld_exists';
use Net::DNS::Dig;
use Net::Whois::Parser 'parse_whois';
@@ -280,7 +281,7 @@ sub _entries_to_domains {
if not @$keys or grep { $_ eq 'description' } @$keys;
}
else {
- die "Missing translation for domain attribute ‘".$attr."’.";
+ die "Missing translation for domain attribute ‘".$attr."’";
}
}
@@ -327,7 +328,7 @@ sub _keys_to_attrs {
);
my %attrs;
foreach my $k (@_) {
- die "Missing translation for key ‘".$k."’."
+ die "Missing translation for key ‘".$k."’"
unless exists $map{$k};
if (ref $map{$k} eq 'ARRAY') {
$attrs{$_} = 1 for @{$map{$k}};
@@ -410,7 +411,7 @@ sub list_owner_emails {
my $domain;
until ( tld_exists($tld) ) {
- softdie ("‘".$domainname."’ has an Invalid TLD.", %options) // return
+ softdie ("‘".$domainname."’ has an Invalid TLD", %options) // return
unless $tld =~ /\./;
$domain = $tld;
@@ -495,25 +496,33 @@ sub add {
my $domainname = $domain->{name};
# Check domain validity.
- &_is_valid($domain, %options) // return;
- my $dn = $self->mail2dn( $domain->{name} );
-
- # Search for an existing domain with the same name.
- my $count = $self->search($domain->{name}, %options, '-count' => 1);
- softdie ( "Domain ‘".$domainname."’ already exists.", %options ) // return
- if not $options{'-append'} and $count;
- # Stop here in dry-run mode.
- return 1 if $options{'-dry-run'};
+ &_assert_valid($domain, %options) // return;
+
+ my $exists;
+ if ($options{'-dry-run'} or $options{'-append'}) {
+ # Search for an existing domain with the same name. We can't
+ # use our previously defined method here, since the current user
+ # may not have read access to the entry. There is a race
+ # condition since someone could modify the directory between
+ # this check and the actual insertion, but then the insertion
+ # would fail.
+ $exists = ldap_assert_absent( $self, $domain->{name}, undef, %options)
+ // return;
+ return 1 if $options{'-dry-run'};
+ }
# Convert the domain into a LDAP entry, and remove keys to empty values.
my %attrs = $self->_domain_to_entry (%$domain);
- Fripost::Schema::Util::clean_ldap_entry( \%attrs );
+ Fripost::Schema::Util::ldap_clean_entry( \%attrs );
my ($mesg, $token);
- if ($options{'-append'} and $count) {
+ my $dn = $self->mail2dn( $domain->{name} );
+ if ($options{'-append'} and $exists) {
# Replace single valued attributes; Add other attributes.
- my %unique = ( fripostIsStatusActive => $attrs{fripostIsStatusActive} );
- delete $attrs{$_} for (keys %unique);
+ my %unique;
+ foreach (qw/fripostIsStatusActive/) {
+ $unique{$_} = delete $attrs{$_} if exists $attrs{$_};
+ }
$mesg = $self->ldap->modify( $dn, replace => \%unique, add => \%attrs );
}
else {
@@ -528,6 +537,10 @@ sub add {
}
# The default owner is the current user.
$attrs{fripostOwner} //= [ $self->whoami ];
+ $options{'-die'} = { Net::LDAP::Constant::LDAP_ALREADY_EXISTS =>
+ "‘".$domainname."’ exists"
+ , Net::LDAP::Constant::LDAP_SUCCESS => 0 }
+ unless exists $options{'-die'};
$mesg = $self->ldap->add( $dn, attrs => [ %attrs ] );
}
@@ -594,7 +607,7 @@ sub _domain_to_entry {
[ map { $self->mail2dn($_) } @{$domain{postmaster}} ];
}
else {
- die "Missing translation for domain key ‘".$key."’.";
+ die "Missing translation for domain key ‘".$key."’";
}
}
return %entry;
@@ -681,7 +694,7 @@ sub replace {
return if $options{'-error'} && ${$options{'-error'}};
# Check domain validity.
- &_is_valid($domain, %options) // return;
+ &_assert_valid($domain, %options, -replace => 1) // return;
return 1 if $options{'-dry-run'};
my %entry = $self->_domain_to_entry (%$domain);
@@ -693,11 +706,12 @@ sub replace {
# Ensure that the given domain is valid.
-sub _is_valid {
+sub _assert_valid {
my $d = shift;
my %options = @_;
eval {
- Fripost::Schema::Util::must_attrs( $d, qw/name isActive/ );
+ Fripost::Schema::Util::must_attrs( $d, qw/name isActive/ )
+ unless $options{'-append'} or $options{'-replace'};
$d->{name} = domain_valid( domain_to_ascii ($d->{name}) );
$d->{catchAll} = [ map { email_valid($_, -prefix => 'fake') }
@{$d->{catchAll}} ]
diff --git a/lib/Fripost/Schema/Util.pm b/lib/Fripost/Schema/Util.pm
index 59d724f..b3439cc 100644
--- a/lib/Fripost/Schema/Util.pm
+++ b/lib/Fripost/Schema/Util.pm
@@ -15,10 +15,11 @@ use Exporter 'import';
our @EXPORT_OK = qw /concat get_perms explode
must_attrs domain_valid email_valid split_addr dn2mail
canonical_dn ldap_explode_dn ldap_error ldap_and_filter
- clean_ldap_entry
- assert softdie/;
+ ldap_clean_entry
+ assert ldap_assert_absent softdie/;
use Email::Valid;
-use Net::IDN::Encode qw/domain_to_unicode email_to_unicode/;
+use Net::IDN::Encode qw/domain_to_unicode email_to_unicode
+ domain_to_ascii email_to_ascii/;
use Net::LDAP::Util;
use Encode;
@@ -89,7 +90,7 @@ sub get_perms {
sub must_attrs {
my $h = shift;
foreach (@_) {
- die 'Missing attribute: ‘'.$_."’\n"
+ die 'Missing value: ‘'.$_."’\n"
unless defined $h->{$_} and
(ref $h->{$_} eq 'ARRAY' ? @{$h->{$_}} : $h->{$_} ne '')
}
@@ -110,14 +111,17 @@ sub email_valid {
$in = $options{'-prefix'}.$i if defined $options{'-prefix'};
Encode::_utf8_on($in);
Encode::_utf8_on($i);
- $in = Net::IDN::Encode::email_to_ascii($in);
-
- my $addr = Email::Valid::->address( -address => $in,
- -tldcheck => 1,
- -fqdn => 1 );
- my $match = defined $addr;
- $match &&= $addr eq $in if $options{'-exact'};
- unless ($match) {
+
+ my ($addr, $match);
+ eval {
+ $in = Net::IDN::Encode::email_to_ascii($in);
+ $addr = Email::Valid::->address( -address => $in,
+ -tldcheck => 1,
+ -fqdn => 1 );
+ $match = defined $addr;
+ $match &&= $addr eq $in if $options{'-exact'};
+ };
+ if ($@ || !$match) {
return if $options{'-nodie'};
die $mesg." ‘".$i."’\n";
}
@@ -157,24 +161,33 @@ sub split_addr {
my $addr = shift // return;
my %options = @_;
- $addr =~ /^(.*)\@([^\@]+)$/s;
- my ($l,$d) = ($1, $2);
-
+ Encode::_utf8_on($addr);
+ my $mesg = $addr =~ /\@/ ? "Invalid e-mail ‘".$addr."’" :
+ "Invalid domain ‘".$addr."’";
+ my $ret;
if (defined $options{'-encode'}) {
my $e = $options{'-encode'};
if ($e eq 'ascii') {
- Encode::_utf8_on($d);
- $d = Net::IDN::Encode::domain_to_ascii($d);
+ eval { $addr = $addr =~ /\@/ ? email_to_ascii($addr)
+ : domain_to_ascii($addr);
+ };
+ $ret = $@;
}
elsif ($e eq 'unicode') {
- $d = Net::IDN::Encode::domain_to_unicode($d);
+ eval { $addr = $addr =~ /\@/ ? email_to_unicode($addr)
+ : domain_to_unicode($addr);
+ };
+ $ret = $@;
}
else {
- softdie ("Unknown encoding: ". $e, %options);
- return;
+ die "Unknown encoding ‘".$e."’";
}
}
- return ($l,$d);
+ softdie ($mesg, %options) // return if $ret;
+ return ('',$addr) unless $addr =~ /\@/;
+
+ $addr =~ /^(.*)\@([^\@]+)$/;
+ return ($1,$2);
}
sub ldap_error {
@@ -252,7 +265,7 @@ sub ldap_and_filter {
}
-sub clean_ldap_entry {
+sub ldap_clean_entry {
my $attrs = shift;
foreach (keys %$attrs) {
@@ -270,6 +283,28 @@ sub clean_ldap_entry {
}
}
+sub ldap_assert_absent {
+ my $self = shift;
+ my $name = shift;
+ my $found = shift // "‘".$name."’ exists";
+ my %options = @_;
+
+ my $mesg = $self->ldap->search( base => $self->mail2dn( $name )
+ , scope => 'base'
+ , deref => 'never'
+ , filter => '(objectClass=*)'
+ , attrs => [ '1.1' ]
+ );
+ $options{'-die'} = { Net::LDAP::Constant::LDAP_NO_SUCH_OBJECT => 0
+ , Net::LDAP::Constant::LDAP_SUCCESS =>
+ $options{'-append'} ? 0 : $found
+ };
+ ldap_error($mesg, %options) // return;
+ return $mesg->code eq Net::LDAP::Constant::LDAP_SUCCESS ? 1 :
+ $mesg->code eq Net::LDAP::Constant::LDAP_NO_SUCH_OBJECT ? 0 :
+ undef
+}
+
=head1 AUTHOR
Guilhem Moulin C<< <guilhem at fripost.org> >>