AuthorizeRequest.pm 8.48 KB
Newer Older
1
2
3
4
5
6
7
8
9
# -*- indent-tabs-mode: nil; -*-
# vim:ft=perl:et:sw=4
# $Id$

# Sympa - SYsteme de Multi-Postage Automatique
#
# Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel
# Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
# 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites
10
# Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017 GIP RENATER
11
12
# Copyright 2017, 2018, 2021 The Sympa Community. See the
# AUTHORS.md file at the top-level directory of this distribution and at
13
# <https://github.com/sympa-community/sympa.git>.
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

package Sympa::Spindle::AuthorizeRequest;

use strict;
use warnings;
use Time::HiRes qw();

use Sympa;
use Sympa::Log;
use Sympa::Request;
use Sympa::Scenario;

use base qw(Sympa::Spindle);

sikeda's avatar
sikeda committed
41
my $log = Sympa::Log->instance;
42
43
44
45
46
47

sub _twist {
    my $self    = shift;
    my $request = shift;

    # Skip authorization unless specific scenario is defined.
Luc Didry's avatar
Luc Didry committed
48
49
    if (   $request->{error}
        or not $request->handler->action_scenario
50
        or ($self->{scenario_context} and $self->{scenario_context}{skip})) {
51
52
53
        return ['Sympa::Spindle::DispatchRequest'];
    }

54
55
    my $scenario      = $request->handler->action_scenario;
    my $action_regexp = $request->handler->action_regexp;
56

57
    my $sender = $request->{sender};
58

59
    # Check if required context (known list or robot) is given.
60
61
    if (defined $request->handler->context_class
        and $request->handler->context_class ne ref $request->{context}) {
62
63
64
65
66
        $request->{error} = 'unknown_list';
        return ['Sympa::Spindle::DispatchRequest'];
    }
    my $that = $request->{context};

67
68
    my $context = $self->{scenario_context}
        or die 'bug in logic. Ask developer';
IKEDA Soji's avatar
IKEDA Soji committed
69
    # Add target email kept only in request to context.
70
71
72
    # FIXME: $request and $context would be merged in some future
    $context = {email => $request->{email}, %$context}
        if exists $request->{email};
73

74
75
    # Call scenario: auth_method MD5 do not have any sense in
    # scenario because auth is performed by AUTH command.
sikeda's avatar
sikeda committed
76

77
78
79
    my $action;
    my $result;

80
    # The order of the following 3 lines is important! SMIME > DKIM > SMTP.
sikeda's avatar
sikeda committed
81
82
83
84
85
    my $auth_method =
          $request->{smime_signed} ? 'smime'
        : $request->{md5_check}    ? 'md5'
        : $request->{dkim_pass}    ? 'dkim'
        :                            'smtp';
86

IKEDA Soji's avatar
IKEDA Soji committed
87
88
    $result =
        Sympa::Scenario->new($that, $scenario)->authz($auth_method, $context);
89
90
91
92
93
94
95
96
97
98
99
100
101
102
    $action = $result->{'action'} if ref $result eq 'HASH';

    unless (defined $action and $action =~ /\A(?:$action_regexp)\b/) {
        $log->syslog(
            'info',
            '%s for %s from %s aborted, unknown requested action "%s" in scenario "%s"',
            uc $request->{action},
            $that,
            $sender,
            $action,
            $scenario
        );
        my $error = sprintf 'Unknown requested action in scenario: %s',
            ($action || '');
103
104
105
106
107
108
109
110
111
        Sympa::send_notify_to_listmaster(
            $request->{context},
            'mail_intern_error',
            {   error  => $error,
                who    => $sender,
                action => 'Command process',
            }
        );
        $self->add_stash($request, 'intern');
112
113
114
        return undef;
    }

115
116
117
118
    # Special cases for "subscribe" and "signoff" actions.
    if ($action =~ /\Areject\b/i) {
        ;
    } elsif (
119
        $sender ne ($request->{email} // '')
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
        and
        ($request->{action} eq 'subscribe' or $request->{action} eq 'signoff')
    ) {
        # Request by an other/anonymous user:
        # If membership is unsatisfactory, fake successful response to prevent
        # sniffing users.
        my $is_list_member = $that->get_list_member($request->{email});
        if (   $request->{action} eq 'subscribe' and $is_list_member
            or $request->{action} eq 'signoff' and not $is_list_member) {
            $log->syslog(
                'info',
                '%s %s for %s from %s is in vain (omitted)',
                uc $request->{action},
                $request->{email}, $that, $sender
            );
            # Notify target address.
            Sympa::send_notify_to_user($that, 'vain_request_by_other',
                $request->{email}, {request => $request});
            # Fake succsssful result.
            if ($action =~ /\Arequest_auth\b/i) {
                $self->add_stash($request, 'notice', 'sent_to_user',
                    {email => $request->{email}});
            } elsif ($action =~ /\Aowner\b/i) {
                $self->add_stash($request, 'notice', 'sent_to_owner');
            }
            # Abort processing request.
            return 1;
147
        }
148
149
150
151
152
153

        # Otherwise, confirmation should be sent to target email instead of
        # sender.  This fixup is necessary for subscribe.* scenarios bundled
        # in 6.2.34 or earlier.
        $action =~
            s/\Arequest_auth(?![(][[]email[]][)])/request_auth([email])/;
154
155
156
157
158
159
    }

    if ($action =~ /\Ado_it\b/i) {
        $request->{quiet} ||= ($action =~ /,\s*quiet\b/i);    # Overwrite.
        $request->{notify} = ($action =~ /,\s*notify\b/i);
        return ['Sympa::Spindle::DispatchRequest'];
160
161
162
    } elsif ($action =~ /\Alistmaster\b/i) {
        # Special case for move_list and create_list.
        $request->{pending} = 'pending';
Luc Didry's avatar
Luc Didry committed
163
        $request->{notify}  = ($action =~ /,\s*notify\b/i);
164
        return ['Sympa::Spindle::DispatchRequest'];
165
166
    } elsif (
        $action =~ /\Arequest_auth\b(?:\s*[(]\s*[[]\s*(\S+)\s*[]]\s*[)])?/i) {
167
168
        my $to = $1;
        if ($to and $to eq 'email') {
169
            $request->{sender_to_confirm} = $request->{email};
170
        }
171
        return ['Sympa::Spindle::ToAuth'];
172
    } elsif ($action =~ /\Aowner\b/i and ref $that eq 'Sympa::List') {
173
        # Special case for subscribe and signoff.
174
        $request->{quiet} ||= ($action =~ /,\s*quiet\b/i);
175
        return ['Sympa::Spindle::ToAuthOwner'];
176
    } elsif ($action =~ /\Areject\b/i) {
sikeda's avatar
sikeda committed
177
178
        $self->add_stash($request, 'auth', $result->{'reason'},
            {template => $result->{'tt2'}});
179
180
181
182
183
184
        $log->syslog(
            'info',
            '%s for %s from %s refused (not allowed)',
            uc $request->{action},
            $that, $sender
        );
185
        return undef;
186
    }
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208

    $log->syslog(
        'info',
        '%s for %s from %s aborted, unknown requested action "%s" in scenario "%s"',
        uc $request->{action},
        $that,
        $sender,
        $action,
        $scenario
    );
    my $error = sprintf 'Unknown requested action in scenario: %s',
        $request->{action};
    Sympa::send_notify_to_listmaster(
        $request->{context},
        'mail_intern_error',
        {   error  => $error,
            who    => $sender,
            action => 'Command process',
        }
    );
    $self->add_stash($request, 'intern');
    return undef;
209
210
211
212
213
214
}

# Checks the authentication and return method
# used if authentication not failed.
# Returns 'smime', 'md5', 'dkim' or 'smtp' if authentication OK, undef else.
# Old name: Sympa::Commands::get_auth_method().
215
216
# DEPRECATED.  Use Sympa::Request::Handler::auth module to authorize requests.
#sub _get_auth_method;
217
218

1;
sikeda's avatar
sikeda committed
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
__END__

=encoding utf-8

=head1 NAME

Sympa::Spindle::AuthorizeRequest -
Workflow to authorize requests in command messages

=head1 DESCRIPTION

L<Sympa::Spindle::AuthorizeRequest> authorizes requests and stores them
into request spool or dispatch them.

TBD

=head2 Public methods

See also L<Sympa::Spindle/"Public methods">.

=over

=item new ( key =E<gt> value, ... )

In most cases, L<Sympa::Spindle::ProcessMessage>
244
splices messages to this class.  This method is not used in ordinal case.
sikeda's avatar
sikeda committed
245
246
247
248
249
250
251
252
253
254
255

=item spin ( )

Not implemented.

=back

=head1 SEE ALSO

L<Sympa::Request>, L<Sympa::Scenario>, L<Sympa::Spindle::DispatchRequest>,
L<Sympa::Spindle::ProcessMessage>, L<Sympa::Spindle::ProcessRequest>,
256
L<Sympa::Spindle::ToAuth>, L<Sympa::Spindle::ToAuthOwner>.
sikeda's avatar
sikeda committed
257
258
259
260
261
262

=head1 HISTORY

L<Sympa::Spindle::AuthorizeRequest> appeared on Sympa 6.2.13.

=cut