diff options
-rw-r--r-- | css/style.css | 4 | ||||
-rw-r--r-- | lib/FPanel/Interface.pm | 221 | ||||
-rw-r--r-- | template/list-domains.html | 2 | ||||
-rw-r--r-- | template/list-locals.html | 31 |
4 files changed, 163 insertions, 95 deletions
diff --git a/css/style.css b/css/style.css index 465a7b1..b4925da 100644 --- a/css/style.css +++ b/css/style.css @@ -176,3 +176,7 @@ div.editform { font-size: 11pt; margin: 10pt; } +.dunno { + font-weight: bolder; + color: #888888; +} diff --git a/lib/FPanel/Interface.pm b/lib/FPanel/Interface.pm index f73b5de..79e36b5 100644 --- a/lib/FPanel/Interface.pm +++ b/lib/FPanel/Interface.pm @@ -12,9 +12,9 @@ use base 'FPanel::Login'; # inherits the configuration from the super class. sub cgiapp_init { my $self = shift; - + $self->SUPER::cgiapp_init; - + # Every single Run Mode here is protected $self->authen->protected_runmodes( ':all' ); } @@ -26,23 +26,26 @@ sub ListDomains : StartRunmode { 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 $domains = $ldap->search( base => $suffix , scope => 'one' , filter => 'objectClass=FripostVirtualDomain' , deref => 'never' - , attrs => [ qw/fvd description fripostIsStatusActive - fripostCanCreateAlias fripostCanCreateList - fripostOwner fripostPostmaster/ ] + , attrs => [ qw/fvd description + fripostIsStatusActive + fripostCanCreateAlias + fripostCanCreateList + fripostOwner + fripostPostmaster/ ] ); die $domains->error if $domains->code; $ldap->unbind; - - + + my $template = $self->load_tmpl( 'list-domains.html', cache => 1, utf8 => 1 , loop_context_vars => 1 , global_vars => 1 ); @@ -50,12 +53,13 @@ sub ListDomains : StartRunmode { $template->param( USER_LOCALPART => $l, USER_DOMAINPART => $d); $template->param( DOMAINS => [ map { { DOMAIN => $_->get_value('fvd') - , PERMS => &list_perms($_, $authzDN) + , 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' ? + 1 : 0 }; } - $domains->sorted('fvd') + $domains->sorted('fvd') ]); return $template->output; } @@ -67,34 +71,31 @@ sub ListLocals : 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 $domainname = (split /\//, $ENV{PATH_INFO}, 3)[1]; - - my $answer = $ldap->search( base => "fvd=$domainname,$suffix" - , scope => 'base' - , filter => 'objectClass=FripostVirtualDomain' - , deref => 'never' - , attrs => [ qw/fvd description fripostIsStatusActive - fripostOptionalMaildrop - fripostOwner fripostPostmaster/ ] - ); - die $answer->error if $answer->code; - my $domain = $answer->entry('0'); - - my $canedit = 0; - foreach (qw/fripostOwner fripostPostmaster/) { - my $x = $domain->get_value($_, asref=>1); - if (defined $x and grep { $_ eq $authzDN} @$x) { - $canedit = 1; - last; - } - } + # Query *the* matching domain + my $domains = $ldap->search( base => "fvd=$domainname,$suffix" + , scope => 'base' + , filter => 'objectClass=FripostVirtualDomain' + , deref => 'never' + , attrs => [ qw/fvd description + fripostIsStatusActive + fripostOptionalMaildrop + fripostCanCreateAlias + fripostCanCreateList + 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; + # Query the mailboxes under the given domain my $mailboxes = $ldap->search( base => "fvd=$domainname,$suffix" , scope => 'one' , filter => 'objectClass=FripostVirtualMailbox' @@ -103,9 +104,10 @@ sub ListLocals : Runmode { fripostIsStatusActive fripostOptionalMaildrop fripostMailboxQuota/ ] - ); + ); die $mailboxes->error if $mailboxes->code; + # Query the aliases under the given domain my $aliases = $ldap->search( base => "fvd=$domainname,$suffix" , scope => 'one' , filter => 'objectClass=FripostVirtualAlias' @@ -117,6 +119,7 @@ sub ListLocals : Runmode { ); die $aliases->error if $aliases->code; + # Query the lists under the given domain my $lists = $ldap->search( base => "fvd=$domainname,$suffix" , scope => 'one' , filter => 'objectClass=FripostVirtualList' @@ -129,42 +132,79 @@ sub ListLocals : Runmode { die $lists->error if $lists->code; $ldap->unbind; - + + my $domain = $domains->entry('0'); + my $perms = &list_perms($domain, $authzDN); + my $template = $self->load_tmpl( 'list-locals.html', cache => 1, utf8 => 1 , loop_context_vars => 1 , global_vars => 1 ); + $template->param( URL => $self->query->url ); $template->param( DOMAIN => $domainname ); $template->param( USER_LOCALPART => $l, USER_DOMAINPART => $d); - $template->param( DESCRIPTION => join ("\n", $domain->get_value('description')) ); - $template->param( ISACTIVE => $domain->get_value('fripostIsStatusActive') eq 'TRUE' ? 1 : 0 ); - $template->param( CANEDIT => $canedit ); + $template->param( DESCRIPTION => + join ("\n", $domain->get_value('description')) ); + $template->param( ISACTIVE => + $domain->get_value('fripostIsStatusActive') eq 'TRUE' ? 1 : 0 ); + # Can the user edit the domain (change description, toggle + # activation, modify catchalls?) + $template->param( CANEDIT => $perms =~ /[op]/ ); + + # Can the user add mailboxes? + $template->param( CANADDMAILBOX => $perms =~ /p/ ); + # Should we list mailboxes? + $template->param( LISTMAILBOXES => $mailboxes->count || $perms =~ /p/ ); $template->param( MAILBOXES => [ map { { USER => $_->get_value('fvu') , DESCRIPTION => join ("\n", $_->get_value('description')) - , ISACTIVE => $_->get_value('fripostIsStatusActive') eq 'TRUE' ? 1 : 0 - , FORWARDS => [ map { {FORWARD => $_} } $_->get_value('fripostOptionalMaildrop') ] + , ISACTIVE => $_->get_value('fripostIsStatusActive') eq 'TRUE' ? + 1 : 0 + , FORWARDS => [ map { {FORWARD => $_} } + $_->get_value('fripostOptionalMaildrop') ] , QUOTA => $_->get_value('fripostMailboxQuota') // '' }; } - $mailboxes->sorted('fvu') + $mailboxes->sorted('fvu') ]); + + # Can the user add aliases? + $template->param( CANADDALIAS => $perms =~ /[aop]/ ); + # Should we list aliases? + $template->param( LISTMAILBOXES => $mailboxes->count || $perms =~ /p/ ); + $template->param( LISTALIASES => $aliases->count || $perms =~ /[aop]/ ); $template->param( ALIASES => [ map { { ALIAS => $_->get_value('fva') , DESCRIPTION => join ("\n", $_->get_value('description')) - , ISACTIVE => $_->get_value('fripostIsStatusActive') eq 'TRUE' ? 1 : 0 - , OWNERS => [ map { {OWNER => &dn2email($_)} } $_->get_value('fripostOwner') ] - , DESTINATIONS => [ map { {DESTINATION => $_} } $_->get_value('fripostMaildrop') ] + , ISACTIVE => $_->get_value('fripostIsStatusActive') eq 'TRUE' ? + 1 : 0 + , OWNERS => [ map { {OWNER => &dn2email($_)} } + $_->get_value('fripostOwner') ] + , DESTINATIONS => [ map { {DESTINATION => $_} } + $_->get_value('fripostMaildrop') ] }; } $aliases->sorted('fva') ]); - $template->param( CATCHALLS => [ map { {CATCHALL => $_} } $domain->get_value('fripostOptionalMaildrop') ] ); + $template->param( CATCHALLS => [ map { {CATCHALL => $_} } + $domain->get_value('fripostOptionalMaildrop') ] + , OWNERS => [ ( map { {OWNER => &dn2email($_)} } + $domain->get_value('fripostOwner') ) + , ( map { {OWNER => &dn2email($_)} } + $domain->get_value('fripostPostmaster') ) ] + , CAODD => $aliases->count % 2 ); + + # Can the user add lists? + $template->param( CANADDLIST => $perms =~ /[lop]/ ); + # Should we list lists? + $template->param( LISTLISTS => $lists->count || $perms =~ /[lop]/ ); $template->param( LISTS => [ map { { LIST => $_->get_value('fvl') , DESCRIPTION => join ("\n", $_->get_value('description')) - , ISACTIVE => $_->get_value('fripostIsStatusActive') eq 'TRUE' ? 1 : 0 - , OWNERS => [ map { {OWNER => &dn2email($_)} } $_->get_value('fripostOwner') ] + , ISACTIVE => $_->get_value('fripostIsStatusActive') eq 'TRUE' ? + 1 : 0 + , OWNERS => [ map { {OWNER => &dn2email($_)} } + $_->get_value('fripostOwner') ] , TRANSPORT => $_->get_value('fripostListManager') }; } @@ -179,11 +219,11 @@ sub EditDomain : 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 $domainname = (split /\//, $ENV{PATH_INFO}, 3)[1]; if (defined $self->query->param('submit') or @@ -209,7 +249,8 @@ sub EditDomain : Runmode { $changes{fripostOptionalMaildrop} = [ @maildrop ]; } - my $mesg = $ldap->modify( "fvd=$domainname,$suffix", replace => \%changes ); + my $mesg = $ldap->modify( "fvd=$domainname,$suffix", + replace => \%changes ); die $mesg->error if $mesg->code; } @@ -228,44 +269,26 @@ sub EditDomain : Runmode { $template->param( URL => $self->query->url ); $template->param( USER_LOCALPART => $l, USER_DOMAINPART => $d); $template->param( DOMAIN => $domainname ); - $template->param( DESCRIPTION => join ("\n",$answer->get_value('description')) ); - $template->param( MAILDROP => join ("\n",$answer->get_value('fripostOptionalMaildrop')) ); - $template->param( ISACTIVE => $answer->get_value('fripostIsStatusActive') eq 'TRUE' ? 1 : 0 ); + $template->param( DESCRIPTION => + join ("\n",$answer->get_value('description')) ); + $template->param( MAILDROP => + join ("\n",$answer->get_value('fripostOptionalMaildrop')) ); + $template->param( ISACTIVE => $answer->get_value('fripostIsStatusActive') eq 'TRUE' ? + 1 : 0 ); $template->param( NEWCHANGES => defined $self->query->param('submit') ); return $template->output; } -# This subroutine displays the access that the given DN has on the entry. -# Possible values are : +# This subroutine displays the access that the given DN has on the entry +# (long version). Possible values are : # - "can create aliases" (a) # - "can create lists" (l) # - "can create aliases & lists" (al) # - "owner" (o) # - "postmaster" (p) -sub list_perms { - my ($entry, $dn) = @_; - my $perms = ''; - - my $canCreateAlias = $entry->get_value ('fripostCanCreateAlias', asref => 1); - $perms .= 'a' - if defined $canCreateAlias and - grep { $dn eq $_ or (split /,/,$dn,2)[1] eq $_ } - @{$canCreateAlias}; - - my $canCreateList = $entry->get_value ('fripostCanCreateList', asref => 1); - $perms .= 'l' - if defined $canCreateList and - grep { $dn eq $_ or (split /,/,$dn,2)[1] eq $_ } - @{$canCreateList}; - - my $owner = $entry->get_value ('fripostOwner', asref => 1); - $perms = 'o' - if defined $owner and grep { $dn eq $_ } @{$owner}; - - my $postmaster = $entry->get_value ('fripostPostmaster', asref => 1); - $perms = 'p' - if defined $postmaster and grep { $dn eq $_ } @{$postmaster}; +sub list_perms_long { + my $perms = &list_perms(@_); if ( $perms =~ /a/) { return 'can create aliases & lists' if ( $perms =~ /l/); @@ -282,6 +305,29 @@ sub list_perms { } } +# This subroutine displays the access that the given DN has on the entry +# (short version). +sub list_perms { + my ($entry, $dn) = @_; + my $perms = ''; + + $perms .= 'a' + if grep { $dn eq $_ or (split /,/,$dn,2)[1] eq $_ } + $entry->get_value ('fripostCanCreateAlias'); + + $perms .= 'l' + if grep { $dn eq $_ or (split /,/,$dn,2)[1] eq $_ } + $entry->get_value ('fripostCanCreateList'); + + $perms = 'o' + if grep { $dn eq $_ } $entry->get_value('fripostOwner'); + + $perms = 'p' + if grep { $dn eq $_ } $entry->get_value('fripostPostmaster'); + + return $perms; +} + # This method SASL binds the web application and uses the provided # authorization DN. @@ -289,18 +335,21 @@ sub ldap_from_auth_user { my $self = shift; my $authzDN = shift; - my $ldap = Net::LDAP->new( $self->cfg('ldap_uri'), async => 1, onerror => 'die' ); - my $sasl = Authen::SASL->new( mechanism => 'DIGEST-MD5' - , callback => { user => $self->cfg('ldap_authcID') - , pass => $self->cfg('ldap_authcPW') - , authname => "dn:$authzDN" } - ); - my $mesg = $ldap->bind( sasl => $sasl ) ; + my $ldap = Net::LDAP->new( $self->cfg('ldap_uri'), + async => 1, onerror => 'die' ); + my $sasl = Authen::SASL->new( + mechanism => 'DIGEST-MD5', + callback => { user => $self->cfg('ldap_authcID') + , pass => $self->cfg('ldap_authcPW') + , authname => "dn:$authzDN" } + ); + my $mesg = $ldap->bind( sasl => $sasl ); die $mesg->error if $mesg->code; return $ldap; } +# Converts a DN into an email. sub dn2email { my $dn = shift; $dn =~ /^fv[ual]=([^,]+),fvd=([^,]+),/ or return ''; diff --git a/template/list-domains.html b/template/list-domains.html index b933167..f57d448 100644 --- a/template/list-domains.html +++ b/template/list-domains.html @@ -19,7 +19,7 @@ </div> <hr/> - <h1>Manage domains<span class="add">[<a href="<TMPL_VAR NAME=URL>/?a=AddDomain">add domain</a>]<span></h1> + <h1>Manage domains<span class="add">[<a href="<TMPL_VAR NAME=URL>/?a=AddDomain">add</a>]<span></h1> <table class="list"> <thead> diff --git a/template/list-locals.html b/template/list-locals.html index 76b86ca..60d4581 100644 --- a/template/list-locals.html +++ b/template/list-locals.html @@ -27,7 +27,10 @@ <p>Domain status: <b><TMPL_IF NAME=ISACTIVE><span class="active">Active</span><TMPL_ELSE><span class="inactive">Inactive</span></TMPL_IF></b><p> - <h3>Mailboxes<span class="add">[<a href="<TMPL_VAR NAME=URL>/<TMPL_VAR NAME=DOMAIN>/?a=AddUser">add</a>]</span></h3> + <TMPL_IF NAME=LISTMAILBOXES> + <h3>Mailboxes<TMPL_IF NAME=CANADDMAILBOX + ><span class="add">[<a href="<TMPL_VAR NAME=URL>/<TMPL_VAR NAME=DOMAIN>/?a=AddUser">add</a>]</span + ></TMPL_IF></h3> <table class="list"> <thead> @@ -53,11 +56,15 @@ </TMPL_LOOP> </tbody> </table> + </TMPL_IF> <br/> - <h3>Aliases<span class="add">[<a href="<TMPL_VAR NAME=URL>/<TMPL_VAR NAME=DOMAIN>/?a=Addlias">add</a>]</span></h3> + <TMPL_IF NAME=LISTALIASES> + <h3>Alias<TMPL_IF NAME=CANADDALIAS + ><span class="add">[<a href="<TMPL_VAR NAME=URL>/<TMPL_VAR NAME=DOMAIN>/?a=AddAlias">add</a>]</span + ></TMPL_IF></h3> <table class="list"> <thead> @@ -82,19 +89,26 @@ </tr> </TMPL_LOOP> <TMPL_IF NAME=CATCHALLS> - <td>*</td> - <td>Catch-all alias(es) for domain <tt><TMPL_VAR NAME=DOMAIN></tt>.</td> - <td><TMPL_IF NAME=ISACTIVE><span class="active">✔</span><TMPL_ELSE><span class="inactive">✘</span></TMPL_IF></td> - <td></td> - <td><TMPL_LOOP NAME=CATCHALLS><tt><TMPL_VAR NAME=CATCHALL></tt><TMPL_UNLESS NAME=__last__>, </TMPL_UNLESS></TMPL_LOOP></td> + <TMPL_IF NAME=CAODD><tr class="odd"><TMPL_ELSE><tr></TMPL_IF> + <td>*</td> + <td>Catch-all alias(es) for domain <tt><TMPL_VAR NAME=DOMAIN></tt>.</td> + <td><span class="dunno">—</span></td> + <td><TMPL_UNLESS NAME=OWNERS><span class="none">(none)</span></TMPL_UNLESS> + <TMPL_LOOP NAME=OWNERS><tt><TMPL_VAR NAME=OWNER></tt><TMPL_UNLESS NAME=__last__>, </TMPL_UNLESS></TMPL_LOOP></td> + <td><TMPL_LOOP NAME=CATCHALLS><tt><TMPL_VAR NAME=CATCHALL></tt><TMPL_UNLESS NAME=__last__>, </TMPL_UNLESS></TMPL_LOOP></td> + <tr> </TMPL_IF> </tbody> </table> + </TMPL_IF> <br/> - <h3>Lists<span class="add">[<a href="<TMPL_VAR NAME=URL>/<TMPL_VAR NAME=DOMAIN>/?a=Addlist">add</a>]</span></h3> + <TMPL_IF NAME=LISTLISTS> + <h3>Lists<TMPL_IF NAME=CANADDLIST + ><span class="add">[<a href="<TMPL_VAR NAME=URL>/<TMPL_VAR NAME=DOMAIN>/?a=AddList">add</a>]</span + ></TMPL_IF></h3> <table class="list"> <thead> @@ -119,6 +133,7 @@ </TMPL_LOOP> </tbody> </table> + </TMPL_IF> </body> |