diff options
author | Jannis M. Hoffmann <jannis@fehcom.de> | 2023-09-10 15:39:25 +0200 |
---|---|---|
committer | Jannis M. Hoffmann <jannis@fehcom.de> | 2023-09-10 15:39:25 +0200 |
commit | 278b76fabf31abe8fc4fbe6ca1c0ad6af830fcb7 (patch) | |
tree | a3ca8baafd3c26b6d8df3840fe92f5e2f2dd0c34 | |
parent | 84186e77461ddeb867fa2944dcbf45217b41b80e (diff) |
added test pam to replace MockMaildir ReadMail implementation
-rw-r--r-- | MANIFEST | 3 | ||||
-rw-r--r-- | MANIFEST.BIN.m4 | 3 | ||||
-rwxr-xr-x | actions | 2 | ||||
-rwxr-xr-x | configure | 13 | ||||
-rw-r--r-- | jwebmail.development.toml | 8 | ||||
-rw-r--r-- | jwebmail.production.toml | 10 | ||||
-rw-r--r-- | jwebmail.service | 1 | ||||
-rw-r--r-- | lib/JWebmail.pm | 20 | ||||
-rw-r--r-- | lib/JWebmail/Config.pm.in | 4 | ||||
-rw-r--r-- | lib/JWebmail/Model/ReadMails/MockJSON.pm | 137 | ||||
-rw-r--r-- | lib/JWebmail/Model/ReadMails/MockMaildir.pm | 59 | ||||
-rw-r--r-- | lib/JWebmail/Model/ReadMails/QMailAuthuser.pm | 23 | ||||
-rw-r--r-- | lib/JWebmail/Model/WriteMails.pm | 28 | ||||
-rwxr-xr-x | script/testauthenticator.pl | 53 | ||||
-rw-r--r-- | t/Extract.t | 4 | ||||
-rw-r--r-- | t/I18N2.t | 2 | ||||
-rw-r--r-- | t/INI.t | 2 | ||||
-rw-r--r-- | t/Pagination.t | 2 | ||||
-rw-r--r-- | t/ViewWebmail.t | 2 | ||||
-rw-r--r-- | t/Webmail.t | 5 |
20 files changed, 109 insertions, 272 deletions
@@ -4,12 +4,11 @@ script/jwebmail script/mojocookiecheck.pl script/qmauth.pl script/qmauth.py +script/testauthenticator.pl lib/JWebmail/Controller/Webmail.pm lib/JWebmail/I18N/de.pm lib/JWebmail/I18N/en.pm -lib/JWebmail/Model/ReadMails/MockJSON.pm -lib/JWebmail/Model/ReadMails/MockMaildir.pm lib/JWebmail/Model/ReadMails/QMailAuthuser.pm lib/JWebmail/Model/ReadMails/Role.pm lib/JWebmail/Model/WriteMails.pm diff --git a/MANIFEST.BIN.m4 b/MANIFEST.BIN.m4 index fcacc01..4b87f31 100644 --- a/MANIFEST.BIN.m4 +++ b/MANIFEST.BIN.m4 @@ -5,12 +5,11 @@ ifelse(MAILDIR_EXTRACTOR_NAME, `extractpy', `script/qmauth.py') ifelse(MAILDIR_EXTRACTOR_NAME, `extractpl', `script/qmauth.pl') script/jwebmail +script/testauthenticator.pl lib/JWebmail/Controller/Webmail.pm lib/JWebmail/I18N/de.pm lib/JWebmail/I18N/en.pm -lib/JWebmail/Model/ReadMails/MockJSON.pm -lib/JWebmail/Model/ReadMails/MockMaildir.pm lib/JWebmail/Model/ReadMails/QMailAuthuser.pm lib/JWebmail/Model/ReadMails/Role.pm lib/JWebmail/Model/WriteMails.pm @@ -6,7 +6,7 @@ help_text= help_text="$help_text build\t[]\t\n" dev_config () { - ./configure -e extractrs -n cat + ./configure -e extractrs } help_text="$help_text unconfigure\t[]\tresets the build\n" @@ -41,13 +41,10 @@ check_ninja_version () { } allargs="$*" -while getopts 'c:e:hm:n:r:u:' opt +while getopts 'c:e:hm:' opt do case $opt in (e) JWM_MAILDIR_EXTRACTOR="$OPTARG";; - (r) JWM_READ_MODEL="$OPTARG";; (m) JWM_MODE="$OPTARG";; - (n) JWM_SENDMAIL="$OPTARG";; - (u) JWM_USER="$OPTARG";; (c) JWM_LOGIN_SCHEME="$OPTARG";; (h) t="$(printf '\t')" @@ -57,11 +54,8 @@ usage: $0 configure [options...] OPTIONS FLAG ARGUMENT DEFAULT VALUES DESCRIPTION -e MAILDIR_EXTRACTOR extractpy {extractpy,extractpl,extractrs} - -r PERL_MODULE JWebmail::Model::ReadMails::MockMaildir The read model implementation -m MODE development {development,production} - -u USERNAME $USER $t The server runs as this user -c LOGIN_SCHEME plain {plain,cram_md5} The login scheme (don't use plain unless over TLS) - -n PATH sendmail The path to a sendmail executable END exit ;; @@ -71,11 +65,8 @@ done shift $((OPTIND-1)) : "${JWM_MAILDIR_EXTRACTOR=extractpy}" && echo "$JWM_MAILDIR_EXTRACTOR" | grep -Eq '^extract(pl|py|rs)$' -: "${JWM_READ_MODEL=JWebmail::Model::ReadMails::MockMaildir}" : "${JWM_MODE=development}" && [ "$JWM_MODE" = development -o "$JWM_MODE" = production ] -: "${JWM_USER=$USER}" && id "$JWM_USER" >/dev/null : "${JWM_LOGIN_SCHEME=plain}" && [ "$JWM_LOGIN_SCHEME" = plain -o "$JWM_LOGIN_SCHEME" = cram_md5 ] -: "${JWM_SENDMAIL=sendmail}" && [ -x "$(command -v "$JWM_SENDMAIL")" ] PATH="$(pwd)/node_modules/.bin${PATH:+:$PATH}" @@ -120,13 +111,11 @@ case "$JWM_MAILDIR_EXTRACTOR" in ;; esac -export SENDMAIL="$JWM_SENDMAIL" export LOGIN_SCHEME="$JWM_LOGIN_SCHEME" export CONFARGS="$allargs" export MAILDIR_EXTRACTOR_NAME="$JWM_MAILDIR_EXTRACTOR" export MAILDIR_EXTRACTOR_BIN export JWM_EXTRACTRS_DIR -export JWM_READ_MODEL export JWM_MODE perl -pe 's/@(\w+)@/$ENV{$1}/ga' lib/JWebmail/Config.pm.in >|lib/JWebmail/Config.pm diff --git a/jwebmail.development.toml b/jwebmail.development.toml index f8b670b..1d5033f 100644 --- a/jwebmail.development.toml +++ b/jwebmail.development.toml @@ -1,8 +1,12 @@ -admin_mail = "me@example.com" # for complaints / support +admin_mail = 'me@example.com' # for complaints / support [i18n] -default_language = "en" +default_language = 'en' [model.read] virtual_user = 'jmhoffmann' mailbox_path = 't/testdata' +authenticator = 'script/testauthenticator.pl' + +[model.write] +sendmail = 'cat' diff --git a/jwebmail.production.toml b/jwebmail.production.toml index b18fbaf..7834958 100644 --- a/jwebmail.production.toml +++ b/jwebmail.production.toml @@ -6,10 +6,14 @@ admin_mail = "test@example.org" default_language = "en" [model.read] -virtual_user = netfehcom -mailbox_path = /home/netfehcom/users +virtual_user = "netfehcom" +mailbox_path = "/home/netfehcom/users" +authenticator = "/var/qmail/bin/qmail-authuser" + +[model.write] +sendmail = "/var/qmail/bin/sendmail" [hypnotoad] proxy = 1 -pid_file = /run/jwebmail/hypnotoad.pid +pid_file = "/run/jwebmail/hypnotoad.pid" listen = ["http://127.0.0.1:8081"] diff --git a/jwebmail.service b/jwebmail.service index 9c52ac1..44169db 100644 --- a/jwebmail.service +++ b/jwebmail.service @@ -8,6 +8,7 @@ ExecStart=/usr/bin/hypnotoad -f jwebmail ExecReload=/usr/bin/hypnotoad -f jwebmail PIDFile=/run/jwebmail/hypnotoad.pid Environment=MOJO_LOG_SHORT=1 +DynamicUser=yes [Install] WantedBy=multi-user.target diff --git a/lib/JWebmail.pm b/lib/JWebmail.pm index 993ad59..b829191 100644 --- a/lib/JWebmail.pm +++ b/lib/JWebmail.pm @@ -4,11 +4,10 @@ use Mojo::Base Mojolicious; use Module::Load 'load'; +use JWebmail::Config 'LOGIN_SCHEME'; use JWebmail::Controller::Webmail; -use JWebmail::Model::ReadMails::Role; use JWebmail::Model::ReadMails::QMailAuthuser; use JWebmail::Model::WriteMails; -use JWebmail::Config qw'LOGIN_SCHEME MAILDIR_READER'; sub validateConf { @@ -26,6 +25,9 @@ sub validateConf { getpwnam $conf->{model}{read}{virtual_user} or die; exists $conf->{model}{read}{mailbox_path} or die; -d $conf->{model}{read}{mailbox_path} or die; + exists $conf->{model}{read}{authenticator} or die; + + exists $conf->{model}{write}{sendmail} or die; return 1; } @@ -42,7 +44,7 @@ sub startup { $self->plugin('TOMLConfig'); $self->validateConf; - if (fc LOGIN_SCHEME eq fc 's3d') { + if (fc LOGIN_SCHEME eq fc 'plain') { $self->plugin('ServerSideSessionData'); } $self->plugin('Paginate'); @@ -52,15 +54,11 @@ sub startup { delete $self->config->{secret}; # initialize models - my $read_mails = do { - my $cls = MAILDIR_READER; - eval { load $cls; 1 } || die "Issue for module $cls with: $@"; - $cls->new(($self->config->{model}{read} // {})->%*) - }; - die "given class @{[ ref $read_mails ]} does not ReadMails" - unless $read_mails->DOES('JWebmail::Model::ReadMails::Role'); + my $read_mails = JWebmail::Model::ReadMails::QMailAuthuser->new($self->config->{model}{read}); $self->helper(users => sub { $read_mails }); - $self->helper(send_mail => sub { my ($c, $mail) = @_; JWebmail::Model::WriteMails::sendmail($mail) }); + + my $write = JWebmail::Model::WriteMails->new($self->config->{model}{write}); + $self->helper(send_mail => sub { $write->sendmail($_[1]) }); $self->validator->add_check(mail_line => \&_mail_line); $self->validator->add_filter(non_empty_ul => \&_filter_empty_upload); diff --git a/lib/JWebmail/Config.pm.in b/lib/JWebmail/Config.pm.in index e91f933..a899024 100644 --- a/lib/JWebmail/Config.pm.in +++ b/lib/JWebmail/Config.pm.in @@ -5,12 +5,10 @@ use warnings; use utf8; use Exporter 'import'; -our @EXPORT_OK = qw(MAILDIR_READER MAILDIR_EXTRACTOR SENDMAIL LOGIN_SCHEME); +our @EXPORT_OK = qw(MAILDIR_EXTRACTOR LOGIN_SCHEME); use constant { - MAILDIR_READER => '@JWM_READ_MODEL@', MAILDIR_EXTRACTOR => '@MAILDIR_EXTRACTOR_BIN@', - SENDMAIL => '@SENDMAIL@', LOGIN_SCHEME => fc '@LOGIN_SCHEME@', }; diff --git a/lib/JWebmail/Model/ReadMails/MockJSON.pm b/lib/JWebmail/Model/ReadMails/MockJSON.pm deleted file mode 100644 index 70daf8f..0000000 --- a/lib/JWebmail/Model/ReadMails/MockJSON.pm +++ /dev/null @@ -1,137 +0,0 @@ -package JWebmail::Model::ReadMails::MockJSON; - -use v5.24; -use warnings; -use utf8; -use autodie; - -use List::Util 'sum'; -use JSON::PP 'decode_json'; - -use JWebmail::Config 'LOGIN_SCHEME'; - -if (LOGIN_SCHEME eq fc 'cram_md5') { - require Digest::HMAC_MD5; - Digest::HMAC_MD5->import('hmac_md5_hex'); -} - -use Role::Tiny::With; - -use namespace::clean; - -use constant { - VALID_USER => 'mockjson@example.org', - VALID_PW => '12345', -}; - -with 'JWebmail::Model::ReadMails::Role'; - - -sub new { bless {%$_[1]}, shift } - -sub _read_json_file { - my ($self, $file_name) = @_; - - open my $body_file, '<', $self->{mailbox_path} . '/' . $file_name; - local $/; - my $body = <$body_file>; - close $body_file; - - return decode_json($body); -} - -sub list_reply { - my $self = shift; - state $init = _read_json_file($self, 'msgs.json'); -} - -sub read_reply { - my $self = shift; - state $init = { - 'SC-ORD-MAIL54526c63b751646618a793be3f8329cca@sc-ord-mail5' => _read_json_file($self, 'msg2.json'), - 'example' => _read_json_file($self, 'msg.json'), - }; -} - - -sub verify_user { - my $self = shift; - my $auth = shift; - - if ($auth->{challenge}) { - my $res = hmac_md5_hex($auth->{challenge}, VALID_PW); - return $auth->{user} eq VALID_USER && $auth->{password} eq $res; - } - return $auth->{user} eq VALID_USER && $auth->{password} eq VALID_PW; -} - -sub read_headers_for { - my $self = shift; - my $auth = shift; - my %args = @_; - - my ($start, $end, $sort, $folder) = @args{qw(start end sort folder)}; - - unless ($sort) { - return [@{ $self->list_reply }[$start..$end]]; - } - if ($folder eq 'test') { - return []; - } - my $s = sub { - my $sort_by = $sort; - my $rev = $sort_by !~ m/^!/ ? 1 : -1; - $sort_by =~ s/^!//; - return (($a->{$sort_by}||$a->{head}{$sort_by}) cmp ($b->{$sort_by}||$b->{head}{$sort_by})) * $rev; - }; - return [sort { &$s } @{ $self->list_reply }[$start..$end]]; -} - -sub count { - my $self = shift; - my $auth = shift; - my $_folder = shift; - - return ( - sum(map {$_->{size}} @{ $self->list_reply }), # size - scalar(@{ $self->list_reply }), # count - 0, # new - ); -} - -sub show { - my $self = shift; - my $auth = shift; - my $mid = shift; - - my $mail = $self->read_reply->{$mid}; - if ($mail) { - return $mail; - } - else { - die 'unknown mail-id'; - } -} - -sub folders { ['', qw(cur test devel debug)] } - -sub raw { - my $self = shift; - my ($auth, $folder, $mid, $path) = @_; - - ... -} - -sub search { ... } -sub move { ... } - - -1 - -__END__ - -=head1 NAME - -Mock - Simple file based mock for the L<JWebmail::Model::ReadMails> module. - -=cut diff --git a/lib/JWebmail/Model/ReadMails/MockMaildir.pm b/lib/JWebmail/Model/ReadMails/MockMaildir.pm deleted file mode 100644 index f9d530f..0000000 --- a/lib/JWebmail/Model/ReadMails/MockMaildir.pm +++ /dev/null @@ -1,59 +0,0 @@ -package JWebmail::Model::ReadMails::MockMaildir; - -use Mojo::Base 'JWebmail::Model::ReadMails::QMailAuthuser'; - -use Mojo::JSON 'decode_json'; - -use JWebmail::Config 'LOGIN_SCHEME'; - -if (LOGIN_SCHEME eq fc 'cram_md5') { - require Digest::HMAC_MD5; - Digest::HMAC_MD5->import('hmac_md5_hex'); -} - -use constant { - VALID_USER => 'mockmaildir@example.org', - VALID_PW => '12345', -}; - - -sub new { - my $cls = shift; - my %args = @_ == 1 ? %$_[0] : @_; - - my $self = bless {%args}, ref $cls || $cls; - - return $self->next::method(); -} - - -sub verify_user { - my $self = shift; - my $auth = shift; - - my $passwd = $auth->{password}->show_password; - - if ($auth->{challenge}) { - return $auth->{user} eq VALID_USER && - $passwd eq hmac_md5_hex($auth->{challenge}, VALID_PW); - } - else { - return $auth->{user} eq VALID_USER && $passwd eq VALID_PW; - } -} - -sub start_qmauth { - my $self = shift; - my ($auth, $mode, $args) = @_; - - my $mail_user = 'maildir'; - my @exec = ($self->{prog}, $self->{mailbox_path}, $self->{virtual_user}, $mail_user, $mode, @$args); - - my $pid = open(my $reader, '-|', @exec) - or die "failed to create subprocess: $!"; - - return $pid, $reader; -} - - -1 diff --git a/lib/JWebmail/Model/ReadMails/QMailAuthuser.pm b/lib/JWebmail/Model/ReadMails/QMailAuthuser.pm index b2015aa..5190e26 100644 --- a/lib/JWebmail/Model/ReadMails/QMailAuthuser.pm +++ b/lib/JWebmail/Model/ReadMails/QMailAuthuser.pm @@ -4,6 +4,8 @@ use v5.24; use warnings; use utf8; +use JWebmail::Config 'MAILDIR_EXTRACTOR'; + use IPC::Open2; use JSON::PP 'decode_json'; use Params::Check 'check'; @@ -11,7 +13,6 @@ use Role::Tiny::With; use Scalar::Util 'blessed'; use namespace::clean; -use JWebmail::Config 'MAILDIR_EXTRACTOR'; with 'JWebmail::Model::ReadMails::Role'; @@ -74,9 +75,9 @@ package JWebmail::Model::ReadMails::QMailAuthuser::Error { my $QMailAuthuserCheck = { - virtual_user => {required => 1}, - mailbox_path => {required => 1}, - qmail_dir => {default => '/var/qmail/'}, + virtual_user => {required => 1}, + mailbox_path => {required => 1}, + authenticator => {required => 1}, }; sub new { @@ -174,12 +175,12 @@ sub build_arg { my $self = shift; my ($user_mail_addr, $mode, $args) = @_; - return $self->{qmail_dir} . "/bin/qmail-authuser true 3<&0" + return $self->{authenticator} . ' true 3<&0' if $mode eq 'auth'; my ($user_name) = $user_mail_addr =~ /(\w*)@/; - return $self->{qmail_dir}.'/bin/qmail-authuser ' + return $self->{authenticator} . ' ' . join(' ', map { my $x = s/(['\\])/\\$1/gr; "'$x'" } ($self->{prog}, $self->{mailbox_path}, $self->{virtual_user}, $user_name, $mode, @$args)) . ' 3<&0'; } @@ -210,11 +211,6 @@ sub read_qmauth { my $rs; if (eof $reader) { - # for regular open - close $reader - or warn "closing read pipe failed: $!"; - $rs = $?; - # for IPC::Open2 if (waitpid($pid, 0) == $pid) { $rs = $?; @@ -244,18 +240,19 @@ sub read_qmauth { }; } elsif ($rs == 3 << 8 || $rs == 0) { + $rc = $rs >> 8; eval { $resp = decode_json $input if $input; 1 } or do { $resp = { info => "error decoding response", response => $input, cause => $@, - return_code => $rs >> 8, + return_code => $rc, }; - $rc = 3; }; } else { + $rc = $rs >> 8; $resp = { info => "got unsuccessful return code by qmail-authuser", return_code => $rc, diff --git a/lib/JWebmail/Model/WriteMails.pm b/lib/JWebmail/Model/WriteMails.pm index 751192a..330f709 100644 --- a/lib/JWebmail/Model/WriteMails.pm +++ b/lib/JWebmail/Model/WriteMails.pm @@ -1,21 +1,15 @@ package JWebmail::Model::WriteMails; -use v5.24; -use warnings; -use utf8; - -use JWebmail::Config 'SENDMAIL'; - -use Exporter 'import'; -our @EXPORT_OK = qw(sendmail); - use Email::MIME; +use namespace::clean; +use Mojo::Base -base; -our $Block_Writes = 0; +has 'sendmail_bin'; sub _build_mail { + my $self = shift; my $mail = shift; my $text_part = Email::MIME->create( @@ -51,9 +45,9 @@ sub _build_mail { sub _send { - my ($mime, @recipients) = @_; + my ($self, $mime, @recipients) = @_; - open(my $m, '|-', SENDMAIL, '-i', @recipients) + open(my $m, '|-', $self->sendmail_bin, '-i', @recipients) or die 'Connecting to sendmail failed. Is it in your PATH?'; $m->print($mime->as_string()); close($m); @@ -62,21 +56,17 @@ sub _send { sub sendmail { + my $self = shift; my $mail = shift; - my $mime = _build_mail($mail); + my $mime = $self->_build_mail($mail); my @recipients; push @recipients, @{ $mail->{to} } if $mail->{to}; push @recipients, @{ $mail->{cc} } if $mail->{cc}; push @recipients, @{ $mail->{bcc} } if $mail->{bcc}; - if ($Block_Writes) { - say $mime; - return 1; - } - - return _send($mime, @recipients); + return $self->_send($mime, @recipients); } diff --git a/script/testauthenticator.pl b/script/testauthenticator.pl new file mode 100755 index 0000000..2e04573 --- /dev/null +++ b/script/testauthenticator.pl @@ -0,0 +1,53 @@ +#!/usr/bin/perl + +use v5.24; +use warnings; +use utf8; +use autodie; + +use constant HMAC_MD5 => eval { require Digest::HMAC_MD5; Digest::HMAC_MD5->import('hmac_md5_hex'); 1 }; + +use constant { + VALID_USER => 'mockmaildir@example.org', + VALID_PW => '12345', +}; + + +sub main { + open my $authfd, '<&=3'; + + my $inp = <$authfd>; + close $authfd; + + my ($u, $p, $c, @r) = split "\0", $inp; + if (@r) { + warn 'too many fields!'; + exit 2; + } + + if ($c) { + if (!HMAC_MD5) { exit 111 } + if ($u eq VALID_USER && $p eq hmac_md5_hex($c, VALID_PW)) { + exec @ARGV; + } + } + else { + if ($u eq VALID_USER && $p eq VALID_PW) { + exec @ARGV; + } + } + + exit 1; +} + +main unless caller; + +1 + +__END__ + +=encoding utf-8 + +=head1 + +testauthenicator.pl diff --git a/t/Extract.t b/t/Extract.t index 3869b39..b4c0283 100644 --- a/t/Extract.t +++ b/t/Extract.t @@ -1,4 +1,4 @@ -use v5.22; +use v5.24; use warnings; use utf8; @@ -13,7 +13,7 @@ use JWebmail::Config 'MAILDIR_EXTRACTOR'; my $EXTRACT = MAILDIR_EXTRACTOR; my $MAILDIR = 't/testdata'; my $SYS_USER = $ENV{USER}; -my $MAIL_USER = 'maildir'; +my $MAIL_USER = 'mockmaildir'; my $PROG = "$EXTRACT $MAILDIR $SYS_USER $MAIL_USER"; @@ -1,4 +1,4 @@ -use v5.22; +use v5.24; use warnings; use utf8; @@ -1,6 +1,6 @@ package JWebmail::Test::INI; -use v5.22; +use v5.24; use warnings; use utf8; diff --git a/t/Pagination.t b/t/Pagination.t index e06df03..3602490 100644 --- a/t/Pagination.t +++ b/t/Pagination.t @@ -1,4 +1,4 @@ -use v5.22; +use v5.24; use warnings; use strict; use utf8; diff --git a/t/ViewWebmail.t b/t/ViewWebmail.t index 0708523..59a02a5 100644 --- a/t/ViewWebmail.t +++ b/t/ViewWebmail.t @@ -1,4 +1,4 @@ -use v5.22; +use v5.24; use warnings; use strict; use utf8; diff --git a/t/Webmail.t b/t/Webmail.t index c88b31a..5346d2c 100644 --- a/t/Webmail.t +++ b/t/Webmail.t @@ -1,4 +1,4 @@ -use v5.22; +use v5.24; use warnings; use utf8; @@ -14,7 +14,8 @@ my $pw = JWebmail::Model::ReadMails::MockJSON::VALID_PW; my $t = Test::Mojo->new('JWebmail', { - model => { read => { virtual_user => $ENV{USER}, mailbox_path => 't/testdata/json' }}, + model => { read => { virtual_user => $ENV{USER}, mailbox_path => 't/testdata', authenticator => 'script/testauthenticator.pl' }, + write => { sendmail => 'cat' }}, i18n => { default_language => DEFAULT_LANGUAGE }, admin_mail => 'test@example.org', }); |