From e652956aa8dd6160b078f396e3f89157c15294af Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Tue, 4 Sep 2012 22:00:19 +0200 Subject: Edition of mailboxes, aliases and lists. --- lib/FPanel/Interface.pm | 240 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 203 insertions(+), 37 deletions(-) (limited to 'lib/FPanel/Interface.pm') diff --git a/lib/FPanel/Interface.pm b/lib/FPanel/Interface.pm index 79e36b5..aa5b554 100644 --- a/lib/FPanel/Interface.pm +++ b/lib/FPanel/Interface.pm @@ -42,7 +42,7 @@ sub ListDomains : StartRunmode { fripostOwner fripostPostmaster/ ] ); - die $domains->error if $domains->code; + die '403' if $domains->code; $ldap->unbind; @@ -53,10 +53,10 @@ sub ListDomains : StartRunmode { $template->param( USER_LOCALPART => $l, USER_DOMAINPART => $d); $template->param( DOMAINS => [ map { { DOMAIN => $_->get_value('fvd') + # TODO: do we really want to list the permissions? , PERMS => &list_perms_long($_, $authzDN) , DESCRIPTION => join ("\n", $_->get_value('description')) - , ISACTIVE => $_->get_value('fripostIsStatusActive') eq 'TRUE' ? - 1 : 0 + , ISACTIVE => $_->get_value('fripostIsStatusActive') eq 'TRUE' }; } $domains->sorted('fvd') @@ -65,7 +65,7 @@ sub ListDomains : StartRunmode { } -# This Run Mode lists the known aliases, users and lists in the current +# This Run Mode lists the known mailboxes, aliases and lists in the current # domain. sub ListLocals : Runmode { my $self = shift; @@ -91,9 +91,11 @@ sub ListLocals : Runmode { fripostOwner fripostPostmaster/ ] ); - die $domains->error if $domains->code; - die "Error: Domains not found" unless $domains->count; - die "Error: Multiple domains found" if $domains->count > 1; + die '404' if $domains->code; + # The following is not supposed to happen. + die "Error: Multible matching entries found." if $domains->count > 1; + my $domain = $domains->pop_entry or die '404'; + # Query the mailboxes under the given domain my $mailboxes = $ldap->search( base => "fvd=$domainname,$suffix" @@ -105,6 +107,7 @@ sub ListLocals : Runmode { fripostOptionalMaildrop fripostMailboxQuota/ ] ); + # We don't return 403 or 404 here, since it's not supposed to crash. die $mailboxes->error if $mailboxes->code; # Query the aliases under the given domain @@ -117,6 +120,7 @@ sub ListLocals : Runmode { fripostOwner fripostMaildrop/ ] ); + # We don't return 403 or 404 here, since it's not supposed to crash. die $aliases->error if $aliases->code; # Query the lists under the given domain @@ -129,11 +133,14 @@ sub ListLocals : Runmode { fripostOwner fripostListManager/ ] ); + # We don't return 403 or 404 here, since it's not supposed to crash. die $lists->error if $lists->code; $ldap->unbind; - my $domain = $domains->entry('0'); + # Get the perms of the autenticated user, so that we know where + # should put "add" and "edit" links (the LDAP ACLs back that up + # eventually, anyway). my $perms = &list_perms($domain, $authzDN); my $template = $self->load_tmpl( 'list-locals.html', cache => 1, utf8 => 1 @@ -146,7 +153,7 @@ sub ListLocals : Runmode { $template->param( DESCRIPTION => join ("\n", $domain->get_value('description')) ); $template->param( ISACTIVE => - $domain->get_value('fripostIsStatusActive') eq 'TRUE' ? 1 : 0 ); + $domain->get_value('fripostIsStatusActive') eq 'TRUE' ); # Can the user edit the domain (change description, toggle # activation, modify catchalls?) $template->param( CANEDIT => $perms =~ /[op]/ ); @@ -158,8 +165,7 @@ sub ListLocals : Runmode { $template->param( MAILBOXES => [ map { { USER => $_->get_value('fvu') , DESCRIPTION => join ("\n", $_->get_value('description')) - , ISACTIVE => $_->get_value('fripostIsStatusActive') eq 'TRUE' ? - 1 : 0 + , ISACTIVE => $_->get_value('fripostIsStatusActive') eq 'TRUE' , FORWARDS => [ map { {FORWARD => $_} } $_->get_value('fripostOptionalMaildrop') ] , QUOTA => $_->get_value('fripostMailboxQuota') // '' @@ -176,8 +182,8 @@ sub ListLocals : Runmode { $template->param( ALIASES => [ map { { ALIAS => $_->get_value('fva') , DESCRIPTION => join ("\n", $_->get_value('description')) - , ISACTIVE => $_->get_value('fripostIsStatusActive') eq 'TRUE' ? - 1 : 0 + , ISACTIVE => $_->get_value('fripostIsStatusActive') eq 'TRUE' + # TODO: do we really want to list the owners? , OWNERS => [ map { {OWNER => &dn2email($_)} } $_->get_value('fripostOwner') ] , DESTINATIONS => [ map { {DESTINATION => $_} } @@ -188,6 +194,7 @@ sub ListLocals : Runmode { ]); $template->param( CATCHALLS => [ map { {CATCHALL => $_} } $domain->get_value('fripostOptionalMaildrop') ] + # TODO: do we really want to list the owners? , OWNERS => [ ( map { {OWNER => &dn2email($_)} } $domain->get_value('fripostOwner') ) , ( map { {OWNER => &dn2email($_)} } @@ -201,8 +208,8 @@ sub ListLocals : Runmode { $template->param( LISTS => [ map { { LIST => $_->get_value('fvl') , DESCRIPTION => join ("\n", $_->get_value('description')) - , ISACTIVE => $_->get_value('fripostIsStatusActive') eq 'TRUE' ? - 1 : 0 + , ISACTIVE => $_->get_value('fripostIsStatusActive') eq 'TRUE' + # TODO: do we really want to list the owners? , OWNERS => [ map { {OWNER => &dn2email($_)} } $_->get_value('fripostOwner') ] , TRANSPORT => $_->get_value('fripostListManager') @@ -214,7 +221,8 @@ sub ListLocals : Runmode { } -# This is the page used to edit domains. +# In this Run Mode authenticated users can edit the domain description +# and catchall, and toggle activation (if they have the permission). sub EditDomain : Runmode { my $self = shift; my %CFG = $self->cfg; @@ -226,24 +234,26 @@ sub EditDomain : Runmode { my $domainname = (split /\//, $ENV{PATH_INFO}, 3)[1]; - if (defined $self->query->param('submit') or - defined $self->query->param('active')) { + my $error; # Tells wether the change submission has fails. + if (defined $self->query->param('submit')) { # Changes have been submitted: process them my %changes; - if (defined $self->query->param('active')) { - $changes{fripostIsStatusActive} = $self->query->param('active'); + my $q = $self->query; + if (defined $q->param('status')) { + $changes{fripostIsStatusActive} = $q->param('status') eq 'active' ? + 'TRUE' : 'FALSE'; } - if (defined $self->query->param('description')) { + if (defined $q->param('description')) { my @desc; - foreach my $d (split /\n/, $self->query->param('description')) { + foreach my $d (split /\n/, $q->param('description')) { push @desc, $d; } $changes{description} = [ @desc ]; } - if (defined $self->query->param('maildrop')) { + if (defined $q->param('maildrop')) { my @maildrop; - foreach my $d (split /\n/, $self->query->param('maildrop')) { - $d =~ s/\s//g; + foreach my $d (split /\n/, $q->param('maildrop')) { + $d =~ s/\s//g; # lowercase and strip out the spaces push @maildrop, (lc $d) unless $d =~ /^$/; } $changes{fripostOptionalMaildrop} = [ @maildrop ]; @@ -251,16 +261,19 @@ sub EditDomain : Runmode { my $mesg = $ldap->modify( "fvd=$domainname,$suffix", replace => \%changes ); - die $mesg->error if $mesg->code; + $error = $mesg->error if $mesg->code; } - my $answer = $ldap->search( base => "fvd=$domainname,$suffix" - , scope => 'base' - , filter => 'objectClass=FripostVirtualDomain' - , deref => 'never' - ); - die $answer->error if $answer->code; - $answer = $answer->entry('0'); + my $domains = $ldap->search( base => "fvd=$domainname,$suffix" + , scope => 'base' + , filter => 'objectClass=FripostVirtualDomain' + , deref => 'never' + ); + die '404' if $domains->code; + # The following is not supposed to happen. + die "Error: Multible matching entries found." if $domains->count > 1; + my $domain = $domains->pop_entry or die '404'; + $ldap->unbind; my $template = $self->load_tmpl( 'edit-domain.html', cache => 1, utf8 => 1 @@ -270,12 +283,165 @@ sub EditDomain : Runmode { $template->param( USER_LOCALPART => $l, USER_DOMAINPART => $d); $template->param( DOMAIN => $domainname ); $template->param( DESCRIPTION => - join ("\n",$answer->get_value('description')) ); + join ("\n",$domain->get_value('description')) ); $template->param( MAILDROP => - join ("\n",$answer->get_value('fripostOptionalMaildrop')) ); - $template->param( ISACTIVE => $answer->get_value('fripostIsStatusActive') eq 'TRUE' ? - 1 : 0 ); + join ("\n",$domain->get_value('fripostOptionalMaildrop')) ); + $template->param( ISACTIVE => $domain->get_value('fripostIsStatusActive') eq 'TRUE' ); + $template->param( NEWCHANGES => defined $self->query->param('submit') ); + $template->param( ERROR => $error ); + return $template->output; +} + + +# In this Run Mode authenticated users can edit the entry (if they have +# the permission). +sub EditLocal : Runmode { + my $self = shift; + my %CFG = $self->cfg; + my $suffix = join ',', @{$CFG{ldap_suffix}}; + + my ($l,$d) = split /@/, $self->authen->username, 2; + my $authzDN = "fvu=$l,fvd=$d,". $suffix; + my $ldap = $self->ldap_from_auth_user($authzDN); + + my ($null,$domainname,$localname,$crap) = (split /\//, $ENV{PATH_INFO}, 4); + + my $error; # Tells wether the change submission has fails. + if (defined $self->query->param('submit')) { + # Changes have been submitted: process them + my (%changes, $t2); + my $q = $self->query; + my $t = lc $q->param('t') // die "Error: Unknown type"; + + if ($t eq 'mailbox') { + $t2 = 'fvu'; + if ($q->param('oldpassword') ne '' or + $q->param('newpassword') ne '' or + $q->param('newpassword2') ne '') { + # If the user tries to change the password, we make her + # bind first, to prevent an attacker from setting a + # custom password and accessing the emails. + + if ($q->param('newpassword') eq $q->param('newpassword2')) { + my $ldap2 = Net::LDAP->new( $CFG{ldap_uri} ); + my $mesg = $ldap2->bind ( "fvu=$l,fvd=$d,$suffix", + password => $q->param('oldpassword') ); + if ($mesg->code) { + $error = "Wrong password (for ".$self->authen->username.")."; + } + else { + my $pw = $q->param('newpassword'); + # TODO: hash it. + $mesg = $ldap2->modify( "fvu=$localname,fvd=$domainname,$suffix", + replace => { userPassword => $pw } ); + $error = $mesg->error if $mesg->code; + } + $ldap2->unbind; + } + else { + $error = "Password don't match."; + } + } + + if (defined $q->param('maildrop')) { + my @maildrop; + foreach my $d (split /\n/, $q->param('maildrop')) { + $d =~ s/\s//g; # lowercase and strip out the spaces + push @maildrop, (lc $d) unless $d =~ /^$/; + } + $changes{fripostOptionalMaildrop} = [ @maildrop ]; + } + } + + elsif ($t eq 'alias') { + $t2 = 'fva'; + if (defined $q->param('maildrop')) { + my @maildrop; + foreach my $d (split /\n/, $q->param('maildrop')) { + $d =~ s/\s//g; # lowercase and strip out the spaces + push @maildrop, (lc $d) unless $d =~ /^$/; + } + $changes{fripostMaildrop} = [ @maildrop ]; + } + } + + elsif ($t eq 'list') { + $t2 = 'fvl'; + } + + else { + die "Error: Unknown type"; + } + + # Global parameters + if (defined $q->param('status')) { + $changes{fripostIsStatusActive} = $q->param('status') eq 'active' ? + 'TRUE' : 'FALSE'; + } + if (defined $q->param('description')) { + my @desc; + foreach my $d (split /\n/, $q->param('description')) { + push @desc, $d; + } + $changes{description} = [ @desc ]; + } + + unless (defined $error) { + my $mesg = $ldap->modify( "$t2=$localname,fvd=$domainname,$suffix", + replace => \%changes ); + $error = $mesg->error if $mesg->code; + } + } + + # Query *the* matching mailbox, alias or list. + my $locals = $ldap->search( base => "fvd=$domainname,$suffix" + , scope => 'one' + , filter => "(|(&(objectClass=FripostVirtualMailbox) + (fvu=$localname)) + (&(objectClass=FripostVirtualAlias) + (fva=$localname)) + (&(objectClass=FripostVirtualList) + (fvl=$localname)) + )" + , deref => 'never' + , attrs => [ qw/fvu description + fripostIsStatusActive + fripostOptionalMaildrop + fripostMailboxQuota + fva fripostMaildrop + fvl fripostListManager/ ] + ); + die '404' if $locals->code; + # The following is not supposed to happen. + die "Error: Multible matching entries found." if $locals->count > 1; + my $local = $locals->pop_entry or die '404'; + + my $template; + if ($local->dn =~ /^fvu=/) { + $template = $self->load_tmpl( 'edit-mailbox.html', cache => 1, utf8 => 1 ); + $template->param( MAILBOX => $local->get_value('fvu') ); + $template->param( MAILDROP => + join ("\n",$local->get_value('fripostOptionalMaildrop')) ); + } + elsif ($local->dn =~ /^fva=/) { + $template = $self->load_tmpl( 'edit-alias.html', cache => 1, utf8 => 1 ); + $template->param( ALIAS => $local->get_value('fva') ); + $template->param( MAILDROP => + join ("\n",$local->get_value('fripostMaildrop')) ); + } + elsif ($local->dn =~ /^fvl=/) { + $template = $self->load_tmpl( 'edit-list.html', cache => 1, utf8 => 1 ); + $template->param( LIST => $local->get_value('fvl') ); + } + + $template->param( URL => $self->query->url ); + $template->param( DOMAIN => $domainname ); + $template->param( USER_LOCALPART => $l, USER_DOMAINPART => $d); + $template->param( DESCRIPTION => + join ("\n",$local->get_value('description')) ); + $template->param( ISACTIVE => $local->get_value('fripostIsStatusActive') eq 'TRUE' ); $template->param( NEWCHANGES => defined $self->query->param('submit') ); + $template->param( ERROR => $error ); return $template->output; } -- cgit v1.2.3