diff options
-rw-r--r-- | INSTALL | 27 | ||||
-rw-r--r-- | README | 20 | ||||
-rw-r--r-- | TODO.org | 8 | ||||
-rw-r--r-- | css/style.css | 14 | ||||
-rw-r--r-- | lib/Fripost/Panel/Interface.pm | 22 | ||||
-rw-r--r-- | lib/Fripost/Panel/Login.pm | 16 | ||||
-rw-r--r-- | lib/Fripost/Schema/List.pm | 27 | ||||
-rw-r--r-- | lib/Fripost/Schema/Misc.pm | 1 | ||||
-rw-r--r-- | template/add-list.html | 26 | ||||
-rw-r--r-- | template/error_http.html | 4 | ||||
-rw-r--r-- | template/list-locals.html | 3 | ||||
-rw-r--r-- | template/login.html | 4 |
12 files changed, 140 insertions, 32 deletions
@@ -1,5 +1,6 @@ # You need several CPAN modules to use the administrator panel. -# If you use Debian GNU/Linux, you can install them like so: +# If you use Debian GNU/Linux, you can install them like so (tested on +# Debian 7.0): # Web panel apt-get install libcgi-application-perl \ @@ -7,13 +8,35 @@ apt-get install libcgi-application-perl \ libcgi-application-plugin-autorunmode-perl \ libcgi-application-plugin-configauto-perl \ libcgi-application-plugin-session-perl \ + libcgi-fast-perl \ libfreezethaw-perl \ libhtml-template-perl + # Fripost Schema apt-get install libnet-ldap-perl \ libauthen-sasl-perl \ libemail-valid-perl \ libdigest-perl \ libstring-mkpasswd-perl \ - libnet-idn-encode-perl + libnet-idn-encode-perl \ + libmail-gnupg-perl + + +# Configuration +mkdir -p /etc/fripost-panel/gnupg +chown www-data:www-data /etc/fripost-panel/gnupg && chmod 0700 /etc/fripost-panel/gnupg +cp ./config.in /etc/fripost-panel/ +chown www-data:www-data /etc/fripost-panel/config.in && chmod 0600 /etc/fripost-panel/config.in +# Edit /etc/fripost-panel/config.in + + +# Create a GPG key +sudo -u www-data gpg --homedir /etc/fripost-panel/gnupg/ --gen-key +# (1) RSA and RSA (default) +# What keysize do you want? (2048) 4096 +# Real name: Fripost Admin Panel +# Email address: AdminWebPanel@fripost.org +# Comment: +# You selected this USER-ID: +# "Fripost Admin Panel <AdminWebPanel@fripost.org>" @@ -14,7 +14,7 @@ Read installation file INSTALL and follow those instructions. ** LDAP The panel, or rather the Fripost::Schema library itself, requires -Fripost's LDAP schema, and base directory. See our other repository +Fripost's LDAP schema, and base directory. See our other repository git clone gitolite@git.fripost.org:fripost-admin.git @@ -28,15 +28,27 @@ custom modifications. Both files are equal separated (e.g., key=value) configuration file. Comments (prefixed with a hash #) and blank/empty lines are ignored. -** Web server +** Web server (nginx) -TODO: instructions for Apache and Nginx. + location = / { + rewrite ^ /cgi-bin/ permanent; + } + location ^~ /cgi-bin/ { + fastcgi_split_path_info ^(/cgi-bin)(/.*)$; + include fastcgi/params; + fastcgi_pass unix:/var/run/fcgi/fripost-panel.socket; + } + location ^~ /img/ { } + location ^~ /css/ { } + location ^~ / { return 404; } + +Start the FastCGI process with './bin/fripost-panel start'. ** Development For testing purposes, the developers may want to install HTTP::Server::Simple and use our custom clone of -CGI::Application::Server. +CGI::Application::Server. ./dev/server.pl will start a server listening to localhost:8080. Visit http://127.0.0.1:8080/cgi-bin/ to log in and browse the panel. @@ -12,14 +12,14 @@ domains/emails to Punycode internally? * TODO Check for cycles when creating new aliases? (It is impossible since the authenticated user may not have full read access on the graph) -Actually Postfix checks it and warns the administrator with a +Actually Postfix checks it and warns the administrator with a "unreasonable virtual_alias_maps map nesting for test-loop1@fripost.org" in the logs. So it's fine to do a partial check here. * TODO Write a script to check every runmode against the W3 validator. - -* TODO Use FastCGI. References -- http://search.cpan.org/~naoya/CGI-Application-FastCGI-0.02/lib/CGI/Application/FastCGI.pm + +* DONE Use FastCGI. References +- http://www.cgi-app.org/index.cgi?FastCGI - http://stackoverflow.com/questions/11771564/nginx-fastcgi-configuration-for-cgiapplication-app * TODO Use HTML::Template::Pro. Not sure it's really worth it, though. diff --git a/css/style.css b/css/style.css index 277ff52..fa2f407 100644 --- a/css/style.css +++ b/css/style.css @@ -21,6 +21,13 @@ h1, h2, h3 { span.domain, span.email, span.alias, span.mailbox, span.list { font-family: Inconsolata, "Lucida Console", "Droid Sans Mono", Cousine, monospace; } +.errorhttp { + font-family: serif, times; + margin: 25pt auto; + text-align: center; + font-size: 25pt; + font-weight: bold; +} /* Login form */ @@ -128,11 +135,16 @@ table.list thead th { font-size: 11pt; margin: 10pt; } +a.external { + text-decoration: none; + color:#ff0000; + color: #1a1a1a; +} /* Edit modes */ .help { - font-size: 8pt; + font-size: 8pt; text-align: justify; color: #6A6A6A; padding: 0 50pt; diff --git a/lib/Fripost/Panel/Interface.pm b/lib/Fripost/Panel/Interface.pm index 46d4058..e253ca0 100644 --- a/lib/Fripost/Panel/Interface.pm +++ b/lib/Fripost/Panel/Interface.pm @@ -37,7 +37,7 @@ sub ListDomains : StartRunmode { my ($ul,$ud) = split /\@/, email_to_unicode($self->authen->username), 2; - my $fp = Fripost::Schema->SASLauth( $self->authen->username, %CFG ); + my $fp = Fripost::Schema::->SASLauth( $self->authen->username, %CFG ); my @domains = $fp->domain->search( -concat => "\n", -die => 403); $fp->done; @@ -139,6 +139,7 @@ sub ListLocals : Runmode { , description => join ("\n", @{$_->{description}}) , isactive => $_->{isactive} , transport => $_->{transport} + , listurl => $CFG{'listurl_'.$_->{transport}}.$_->{list}.'@'.$d }; } @lists @@ -360,6 +361,7 @@ sub AddLocal : Runmode { if (defined $q->param('submit')) { # Changes have been submitted: process them my %entry; + my %rest; if ($t eq 'mailbox') { $entry{user} = $q->param('user').'@'.$du; $entry{forwards} = $q->param('forwards'); @@ -383,6 +385,22 @@ sub AddLocal : Runmode { elsif ($t eq 'list') { $entry{list} = $q->param('list').'@'.$du; $entry{transport} = $q->param('transport'); + if ($q->param('password') ne $q->param('password2')) { + $error = "Passwords do not match"; + } + elsif (length $q->param('password') < $CFG{password_min_length}) { + $error = "Password should be at least " + .$CFG{password_min_length} + ." characters long."; + } + else { + $rest{gpg} = { use_agent => 0 + , keydir => $CFG{gpghome} + , key => $CFG{gpg_private_key_id} + , passphrase => $CFG{gpg_private_key_passphrase} + }; + $entry{password} = $q->param('password'); + } } else { # Unknown type @@ -393,7 +411,7 @@ sub AddLocal : Runmode { unless ($error) { my $fp = Fripost::Schema::->SASLauth( $self->authen->username, %CFG ); - $error = $fp->$t->add( \%entry, -concat => "(\n|\x{0D}\x{0A})"); + $error = $fp->$t->add( \%entry, -concat => "(\n|\x{0D}\x{0A})", %rest); $fp->done; return $self->redirect($q->url.'/'.$d.'/') unless $error; } diff --git a/lib/Fripost/Panel/Login.pm b/lib/Fripost/Panel/Login.pm index a147371..1483e06 100644 --- a/lib/Fripost/Panel/Login.pm +++ b/lib/Fripost/Panel/Login.pm @@ -20,7 +20,6 @@ use CGI::Application::Plugin::Redirect; use CGI::Application::Plugin::ConfigAuto 'cfg'; use Fripost::Schema; -use File::Spec::Functions qw/catfile catdir/; use HTML::Entities; use Net::IDN::Encode qw/email_to_ascii/; @@ -92,12 +91,11 @@ sub setup { my $self = shift; $self->header_props( -charset=>'utf-8' ); - $self->tmpl_path( catdir ( $self->cfg('pwd'), $self->cfg('tmpl_path') ) ); + $self->tmpl_path( $self->cfg('tmpl_path') ); $self->mode_param( sub { my $self = shift; my $q = $self->query; - print STDERR $ENV{PATH_INFO} . '?' . $q->query_string, "\n"; # The user just logged in return 'okay' if defined $q->param('login'); @@ -135,9 +133,9 @@ sub setup { # wanted to visit. sub okay : Runmode { my $self = shift; - my $destination = $self->query->param('destination') // - $self->query->url; - return $self->redirect($destination); + my $redirect = $self->query->param('redirect') // + $self->query->url; + return $self->redirect($redirect); } @@ -154,8 +152,8 @@ sub login : Runmode { $self->query->param('a') eq 'login'; # Where the users wants to go - $self->query->param( destination => $self->query->self_url) - unless defined $self->query->param('destination'); + $self->query->param( redirect => $self->query->self_url) + unless defined $self->query->param('redirect'); return $self->login_box; } @@ -167,7 +165,7 @@ sub login_box { my $template = $self->load_tmpl( 'login.html', cache => 1, utf8 => 1 ); $template->param( error => $self->authen->login_attempts ); - $template->param( destination => $self->query->param('destination') ); + $template->param( redirect => $self->query->param('redirect') ); return $template->output; } diff --git a/lib/Fripost/Schema/List.pm b/lib/Fripost/Schema/List.pm index 69317b1..edf9d24 100644 --- a/lib/Fripost/Schema/List.pm +++ b/lib/Fripost/Schema/List.pm @@ -20,6 +20,8 @@ use parent 'Fripost::Schema'; use Fripost::Schema::Misc qw/concat explode must_attrs email_valid/; use Net::IDN::Encode qw/domain_to_ascii email_to_ascii email_to_unicode/; +use Mail::GnuPG; +use MIME::Entity; =head1 METHODS @@ -105,6 +107,7 @@ sub add { my $l = shift; my %options = @_; + my $lname = $l->{list}; $l->{description} = explode ($options{'-concat'}, $l->{description}) if defined $l->{description}; @@ -114,7 +117,7 @@ sub add { die "Missing list name\n" if $l eq ''; must_attrs( $l, 'transport' ); &_is_valid($l); - die "‘".$l->{list}."‘ alread exists\n" + die "‘".$l->{list}."‘ already exists\n" if $self->local->exists($l->{list},%options); my %attrs = ( objectClass => 'FripostVirtualList' @@ -134,8 +137,24 @@ sub add { die $mesg->error."\n"; } }; - return $@; - # TODO: send email to mklist-$transport to finalize + return $@ if $@; + + # Ask the list manager to create the list now. + my $member = $self->whoami; + $member =~ s/^fvu=([^,]+),fvd=([^,]+),.*$/$1\@$2/; + my $mail = MIME::Entity::->build( + From => 'Fripost Admin Panel <AdminWebPanel@fripost.org>', + To => 'mklist+'.$l->{transport}.'@fripost.org', + Subject => "New ".$l->{transport}." list", + Encoding => 'quoted-printable', + Charset => 'utf-8', + Data => [ map { $_ . "\n"} ($lname, $member, $l->{password}) ] + ); + my $gpg = Mail::GnuPG::->new( %{$options{gpg}} ); + my $ret = $gpg->mime_sign( $mail ); + return join ("\n", @{$gpg->{last_message}}) if $ret; + $mail->send; + return 0; } @@ -166,7 +185,7 @@ sub is_pending { die "Error: Multiple matching entries found." if $mesg->count > 1; my $list = $mesg->pop_entry; - + die "Error: No matching entry found." unless defined $list; my $r = $list->get_value('fripostIsStatusPending'); return (defined $r and $r eq 'TRUE'); diff --git a/lib/Fripost/Schema/Misc.pm b/lib/Fripost/Schema/Misc.pm index 39fa3b7..745a20b 100644 --- a/lib/Fripost/Schema/Misc.pm +++ b/lib/Fripost/Schema/Misc.pm @@ -10,7 +10,6 @@ use 5.010_000; use strict; use warnings; use utf8; -use feature "unicode_strings"; use Exporter 'import'; our @EXPORT_OK = qw /concat get_perms explode diff --git a/template/add-list.html b/template/add-list.html index 00c94ac..ef99497 100644 --- a/template/add-list.html +++ b/template/add-list.html @@ -52,6 +52,26 @@ <hr/> + <h4 class="label">Password</h4> + + <table class="loginform"> + <tr> + <td class="label">Password</td> + <td><input type="password" name="password" size="20" /></td> + </tr> + <tr> + <td class="label">Repeat password</td> + <td><input type="password" name="password2" size="20" /></td> + </tr> + </table> + <div class="help"> + This is the admin password for the web interface of the chosen list manager. + (The user name being yourself + <span class="email"><TMPL_VAR NAME=user_localpart>@<TMPL_VAR NAME=user_domainpart></span>.) + </div> + + <hr/> + <h4 class="label">Description</h4> <textarea name="description" cols="50" rows="3" ><TMPL_VAR NAME=description></textarea> <div class="help"> @@ -79,6 +99,12 @@ <input type="submit" name="cancel" value="Cancel" /> <input type="submit" name="submit" value="Submit" /> + <div class="help"> + <i>Note</i>: No confirmation email will be sent. It may take a while for the list to be created (especially + for the Schleuder list manager, as it requires a GPG key creation); Once the list has succefully been created, + it will be visible under the + <a href="<TMPL_VAR NAME=url>/<TMPL_VAR NAME=domain>/">management page for <span class="domain"><TMPL_VAR NAME=domain></span></a>. + </div> </div> </form> </div> diff --git a/template/error_http.html b/template/error_http.html index 7cf4f50..c6ab50a 100644 --- a/template/error_http.html +++ b/template/error_http.html @@ -2,10 +2,10 @@ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> - <title><TMPL_IF NAME=message><TMPL_VAR NAME=message><TMPL_ELSE>Error</TMPL_IF></title> + <title><TMPL_VAR NAME=code> <TMPL_IF NAME=message><TMPL_VAR NAME=message><TMPL_ELSE>Error</TMPL_IF></title> <link href="/css/style.css" media="all" rel="stylesheet" type="text/css" /> </head> <body> - <h1><TMPL_VAR NAME=code> <TMPL_VAR NAME=message></h1> + <div class="errorhttp"><TMPL_VAR NAME=code> <TMPL_VAR NAME=message></div> </body> </html> diff --git a/template/list-locals.html b/template/list-locals.html index 1e3a799..f9d8535 100644 --- a/template/list-locals.html +++ b/template/list-locals.html @@ -122,7 +122,8 @@ <tbody> <TMPL_LOOP NAME=lists> <TMPL_IF NAME=__even__><tr class="odd"><TMPL_ELSE><tr></TMPL_IF> - <td><span class="list"><a href="<TMPL_VAR NAME=url>/<TMPL_VAR NAME=domain>/<TMPL_VAR NAME=list>/"><TMPL_VAR NAME=list></a></span></td> + <td><span class="list"><a href="<TMPL_VAR NAME=url>/<TMPL_VAR NAME=domain>/<TMPL_VAR NAME=list>/"><TMPL_VAR NAME=list></a></span + > <a class="external" target="_blank" href="<TMPL_VAR NAME=listurl>">➠</a></td> <td><TMPL_IF NAME=description><TMPL_VAR NAME=description><TMPL_ELSE><span class="none">(none)</span></TMPL_IF></td> <td><TMPL_IF NAME=isactive><span class="active">✔</span><TMPL_ELSE><span class="inactive">✘</span></TMPL_IF></td> <td><TMPL_VAR NAME=transport></td> diff --git a/template/login.html b/template/login.html index 766dd7d..ed10d20 100644 --- a/template/login.html +++ b/template/login.html @@ -2,7 +2,7 @@ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> - <title>Log In | Fripost's Administrator panel</title> + <title>Log In | Fripost Administrator Panel</title> <link href="/css/style.css" media="all" rel="stylesheet" type="text/css" /> </head> <body class="loginform" onload="document.loginform.authen_username.focus();"> @@ -30,7 +30,7 @@ </tr> </table> <div> - <input type="hidden" name="destination" value="<TMPL_VAR NAME=destination>" /> + <input type="hidden" name="redirect" value="<TMPL_VAR NAME=redirect>" /> <input type="submit" name="login" value="Log in" /> </div> </form> |