1# --
2# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/
3# --
4# This software comes with ABSOLUTELY NO WARRANTY. For details, see
5# the enclosed file COPYING for license information (GPL). If you
6# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
7# --
8
9package Kernel::System::Web::InterfaceCustomer;
10
11use strict;
12use warnings;
13
14use Kernel::System::DateTime;
15use Kernel::System::Email;
16use Kernel::System::VariableCheck qw(IsArrayRefWithData IsHashRefWithData);
17use Kernel::Language qw(Translatable);
18
19our @ObjectDependencies = (
20    'Kernel::Config',
21    'Kernel::Output::HTML::Layout',
22    'Kernel::System::AuthSession',
23    'Kernel::System::CustomerAuth',
24    'Kernel::System::CustomerGroup',
25    'Kernel::System::CustomerUser',
26    'Kernel::System::DB',
27    'Kernel::System::Group',
28    'Kernel::System::Log',
29    'Kernel::System::Main',
30    'Kernel::System::Scheduler',
31    'Kernel::System::DateTime',
32    'Kernel::System::Web::Request',
33    'Kernel::System::Valid',
34);
35
36=head1 NAME
37
38Kernel::System::Web::InterfaceCustomer - the customer web interface
39
40=head1 DESCRIPTION
41
42the global customer web interface (authentication, session handling, ...)
43
44=head1 PUBLIC INTERFACE
45
46=head2 new()
47
48create customer web interface object
49
50    use Kernel::System::Web::InterfaceCustomer;
51
52    my $Debug = 0;
53    my $InterfaceCustomer = Kernel::System::Web::InterfaceCustomer->new(
54        Debug      => $Debug,
55        WebRequest => CGI::Fast->new(), # optional, e. g. if fast cgi is used, the CGI object is already provided
56    );
57
58=cut
59
60sub new {
61    my ( $Type, %Param ) = @_;
62
63    # allocate new hash for object
64    my $Self = {};
65    bless( $Self, $Type );
66
67    # get debug level
68    $Self->{Debug} = $Param{Debug} || 0;
69
70    # performance log
71    $Self->{PerformanceLogStart} = time();
72
73    $Kernel::OM->ObjectParamAdd(
74        'Kernel::System::Log' => {
75            LogPrefix => $Kernel::OM->Get('Kernel::Config')->Get('CGILogPrefix'),
76        },
77        'Kernel::System::Web::Request' => {
78            WebRequest => $Param{WebRequest} || 0,
79        },
80    );
81
82    # debug info
83    if ( $Self->{Debug} ) {
84        $Kernel::OM->Get('Kernel::System::Log')->Log(
85            Priority => 'debug',
86            Message  => 'Global handle started...',
87        );
88    }
89
90    return $Self;
91}
92
93=head2 Run()
94
95execute the object
96
97    $InterfaceCustomer->Run();
98
99=cut
100
101sub Run {
102    my $Self = shift;
103
104    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
105
106    my $QueryString = $ENV{QUERY_STRING} || '';
107
108    # Check if https forcing is active, and redirect if needed.
109    if ( $ConfigObject->Get('HTTPSForceRedirect') ) {
110
111        # Some web servers do not set HTTPS environment variable, so it's not possible to easily know if we are using
112        #   https protocol. Look also for similarly named keys in environment hash, since this should prevent loops in
113        #   certain cases.
114        if (
115            (
116                !defined $ENV{HTTPS}
117                && !grep {/^HTTPS(?:_|$)/} keys %ENV
118            )
119            || $ENV{HTTPS} ne 'on'
120            )
121        {
122            my $Host = $ENV{HTTP_HOST} || $ConfigObject->Get('FQDN');
123
124            # Redirect with 301 code. Add two new lines at the end, so HTTP headers are validated correctly.
125            print "Status: 301 Moved Permanently\nLocation: https://$Host$ENV{REQUEST_URI}\n\n";
126            return;
127        }
128    }
129
130    my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
131
132    my %Param;
133
134    # get session id
135    $Param{SessionName} = $ConfigObject->Get('CustomerPanelSessionName')         || 'CSID';
136    $Param{SessionID}   = $ParamObject->GetParam( Param => $Param{SessionName} ) || '';
137
138    # drop old session id (if exists)
139    $QueryString =~ s/(\?|&|;|)$Param{SessionName}(=&|=;|=.+?&|=.+?$)/;/g;
140
141    # define framework params
142    my $FrameworkParams = {
143        Lang         => '',
144        Action       => '',
145        Subaction    => '',
146        RequestedURL => $QueryString,
147    };
148    for my $Key ( sort keys %{$FrameworkParams} ) {
149        $Param{$Key} = $ParamObject->GetParam( Param => $Key )
150            || $FrameworkParams->{$Key};
151    }
152
153    # validate language
154    if ( $Param{Lang} && $Param{Lang} !~ m{\A[a-z]{2}(?:_[A-Z]{2})?\z}xms ) {
155        delete $Param{Lang};
156    }
157
158    my $BrowserHasCookie = 0;
159
160    # Check if the browser sends the SessionID cookie and set the SessionID-cookie
161    # as SessionID! GET or POST SessionID have the lowest priority.
162    if ( $ConfigObject->Get('SessionUseCookie') ) {
163        $Param{SessionIDCookie} = $ParamObject->GetCookie( Key => $Param{SessionName} );
164        if ( $Param{SessionIDCookie} ) {
165            $Param{SessionID} = $Param{SessionIDCookie};
166        }
167    }
168
169    my $CookieSecureAttribute;
170    if ( $ConfigObject->Get('HttpType') eq 'https' ) {
171
172        # Restrict Cookie to HTTPS if it is used.
173        $CookieSecureAttribute = 1;
174    }
175
176    $Kernel::OM->ObjectParamAdd(
177        'Kernel::Output::HTML::Layout' => {
178            Lang => $Param{Lang},
179        },
180        'Kernel::Language' => {
181            UserLanguage => $Param{Lang},
182        },
183    );
184
185    my $DBCanConnect = $Kernel::OM->Get('Kernel::System::DB')->Connect();
186
187    if ( !$DBCanConnect || $ParamObject->Error() ) {
188        my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
189        if ( !$DBCanConnect ) {
190            $LayoutObject->CustomerFatalError(
191                Comment => Translatable('Please contact the administrator.'),
192            );
193            return;
194        }
195        if ( $ParamObject->Error() ) {
196            $LayoutObject->CustomerFatalError(
197                Message => $ParamObject->Error(),
198                Comment => Translatable('Please contact the administrator.'),
199            );
200            return;
201        }
202    }
203
204    my $UserObject    = $Kernel::OM->Get('Kernel::System::CustomerUser');
205    my $SessionObject = $Kernel::OM->Get('Kernel::System::AuthSession');
206
207    # get common application and add on application params
208    my %CommonObjectParam = %{ $ConfigObject->Get('CustomerFrontend::CommonParam') };
209    for my $Key ( sort keys %CommonObjectParam ) {
210        $Param{$Key} = $ParamObject->GetParam( Param => $Key ) || $CommonObjectParam{$Key};
211    }
212
213    # security check Action Param (replace non-word chars)
214    $Param{Action} =~ s/\W//g;
215
216    # check request type
217    if ( $Param{Action} eq 'PreLogin' ) {
218        my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
219
220        # login screen
221        $LayoutObject->Print(
222            Output => \$LayoutObject->CustomerLogin(
223                Title => 'Login',
224                Mode  => 'PreLogin',
225                %Param,
226            ),
227        );
228
229        return;
230    }
231    elsif ( $Param{Action} eq 'Login' ) {
232
233        # get params
234        my $PostUser = $ParamObject->GetParam( Param => 'User' ) || '';
235        my $PostPw   = $ParamObject->GetParam(
236            Param => 'Password',
237            Raw   => 1
238        ) || '';
239        my $PostTwoFactorToken = $ParamObject->GetParam(
240            Param => 'TwoFactorToken',
241            Raw   => 1
242        ) || '';
243
244        # create AuthObject
245        my $AuthObject = $Kernel::OM->Get('Kernel::System::CustomerAuth');
246
247        # check submitted data
248        my $User = $AuthObject->Auth(
249            User           => $PostUser,
250            Pw             => $PostPw,
251            TwoFactorToken => $PostTwoFactorToken,
252        );
253
254        my $Expires = '+' . $ConfigObject->Get('SessionMaxTime') . 's';
255        if ( !$ConfigObject->Get('SessionUseCookieAfterBrowserClose') ) {
256            $Expires = '';
257        }
258
259        # login is invalid
260        if ( !$User ) {
261            $Kernel::OM->ObjectParamAdd(
262                'Kernel::Output::HTML::Layout' => {
263                    SetCookies => {
264                        OTRSBrowserHasCookie => $ParamObject->SetCookie(
265                            Key      => 'OTRSBrowserHasCookie',
266                            Value    => 1,
267                            Expires  => $Expires,
268                            Path     => $ConfigObject->Get('ScriptAlias'),
269                            Secure   => $CookieSecureAttribute,
270                            HTTPOnly => 1,
271                        ),
272                    },
273                },
274            );
275
276            my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
277
278            # redirect to alternate login
279            if ( $ConfigObject->Get('CustomerPanelLoginURL') ) {
280                $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} );
281                print $LayoutObject->Redirect(
282                    ExtURL => $ConfigObject->Get('CustomerPanelLoginURL')
283                        . "?Reason=LoginFailed;RequestedURL=$Param{RequestedURL}",
284                );
285                return;
286            }
287
288            # show normal login
289            $LayoutObject->Print(
290                Output => \$LayoutObject->CustomerLogin(
291                    Title   => 'Login',
292                    Message => $Kernel::OM->Get('Kernel::System::Log')->GetLogEntry(
293                        Type => 'Info',
294                        What => 'Message',
295                        )
296                        || $AuthObject->GetLastErrorMessage()
297                        || Translatable('Login failed! Your user name or password was entered incorrectly.'),
298                    User        => $PostUser,
299                    LoginFailed => 1,
300                    %Param,
301                ),
302            );
303            return;
304        }
305
306        # login is successful
307        my %UserData = $UserObject->CustomerUserDataGet(
308            User  => $User,
309            Valid => 1
310        );
311
312        # check if the browser supports cookies
313        if ( $ParamObject->GetCookie( Key => 'OTRSBrowserHasCookie' ) ) {
314            $Kernel::OM->ObjectParamAdd(
315                'Kernel::Output::HTML::Layout' => {
316                    BrowserHasCookie => 1,
317                },
318            );
319        }
320
321        # check needed data
322        if ( !$UserData{UserID} || !$UserData{UserLogin} ) {
323
324            my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
325
326            # redirect to alternate login
327            if ( $ConfigObject->Get('CustomerPanelLoginURL') ) {
328                print $LayoutObject->Redirect(
329                    ExtURL => $ConfigObject->Get('CustomerPanelLoginURL')
330                        . '?Reason=SystemError',
331                );
332                return;
333            }
334
335            # show need user data error message
336            $LayoutObject->Print(
337                Output => \$LayoutObject->CustomerLogin(
338                    Title   => 'Error',
339                    Message => Translatable(
340                        'Authentication succeeded, but no customer record is found in the customer backend. Please contact the administrator.'
341                    ),
342                    %Param,
343                ),
344            );
345            return;
346        }
347
348        # create datetime object
349        my $SessionDTObject = $Kernel::OM->Create('Kernel::System::DateTime');
350
351        # Remove certain user attributes that are not needed to be stored in the session.
352        #   - SMIME Certificate could be in binary format, if session backend in DB (default)
353        #   it wont be possible to be saved in certain databases (see bug#14405).
354        my %UserSessionData = %UserData;
355        delete $UserSessionData{UserSMIMECertificate};
356
357        # create new session id
358        my $NewSessionID = $SessionObject->CreateSessionID(
359            %UserSessionData,
360            UserLastRequest => $SessionDTObject->ToEpoch(),
361            UserType        => 'Customer',
362            SessionSource   => 'CustomerInterface',
363        );
364
365        # show error message if no session id has been created
366        if ( !$NewSessionID ) {
367
368            # get error message
369            my $Error = $SessionObject->SessionIDErrorMessage() || '';
370
371            my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
372
373            # output error message
374            $LayoutObject->Print(
375                Output => \$LayoutObject->CustomerLogin(
376                    Title   => 'Login',
377                    Message => $Error,
378                    %Param,
379                ),
380            );
381            return;
382        }
383
384        # execution in 20 seconds
385        my $ExecutionTimeObj = $Kernel::OM->Create('Kernel::System::DateTime');
386        $ExecutionTimeObj->Add( Seconds => 20 );
387
388        # add a asynchronous executor scheduler task to count the concurrent user
389        $Kernel::OM->Get('Kernel::System::Scheduler')->TaskAdd(
390            ExecutionTime            => $ExecutionTimeObj->ToString(),
391            Type                     => 'AsynchronousExecutor',
392            Name                     => 'PluginAsynchronous::ConcurrentUser',
393            MaximumParallelInstances => 1,
394            Data                     => {
395                Object   => 'Kernel::System::SupportDataCollector::PluginAsynchronous::OTRS::ConcurrentUsers',
396                Function => 'RunAsynchronous',
397            },
398        );
399
400        my $UserTimeZone = $Self->_UserTimeZoneGet(%UserData);
401
402        $SessionObject->UpdateSessionID(
403            SessionID => $NewSessionID,
404            Key       => 'UserTimeZone',
405            Value     => $UserTimeZone,
406        );
407
408        # check if the time zone offset reported by the user's browser differs from that
409        # of the OTRS user's time zone offset
410        my $DateTimeObject = $Kernel::OM->Create(
411            'Kernel::System::DateTime',
412            ObjectParams => {
413                TimeZone => $UserTimeZone,
414            },
415        );
416        my $OTRSUserTimeZoneOffset = $DateTimeObject->Format( Format => '%{offset}' ) / 60;
417        my $BrowserTimeZoneOffset  = ( $ParamObject->GetParam( Param => 'TimeZoneOffset' ) || 0 ) * -1;
418
419        # TimeZoneOffsetDifference contains the difference of the time zone offset between
420        # the user's OTRS time zone setting and the one reported by the user's browser.
421        # If there is a difference it can be evaluated later to e. g. show a message
422        # for the user to check his OTRS time zone setting.
423        my $UserTimeZoneOffsetDifference = abs( $OTRSUserTimeZoneOffset - $BrowserTimeZoneOffset );
424        $SessionObject->UpdateSessionID(
425            SessionID => $NewSessionID,
426            Key       => 'UserTimeZoneOffsetDifference',
427            Value     => $UserTimeZoneOffsetDifference,
428        );
429
430        $Kernel::OM->ObjectParamAdd(
431            'Kernel::Output::HTML::Layout' => {
432                SetCookies => {
433                    SessionIDCookie => $ParamObject->SetCookie(
434                        Key      => $Param{SessionName},
435                        Value    => $NewSessionID,
436                        Expires  => $Expires,
437                        Path     => $ConfigObject->Get('ScriptAlias'),
438                        Secure   => scalar $CookieSecureAttribute,
439                        HTTPOnly => 1,
440                    ),
441                    OTRSBrowserHasCookie => $ParamObject->SetCookie(
442                        Key      => 'OTRSBrowserHasCookie',
443                        Value    => '',
444                        Expires  => '-1y',
445                        Path     => $ConfigObject->Get('ScriptAlias'),
446                        Secure   => $CookieSecureAttribute,
447                        HTTPOnly => 1,
448                    ),
449
450                },
451                SessionID   => $NewSessionID,
452                SessionName => $Param{SessionName},
453            },
454        );
455
456        # redirect with new session id and old params
457        # prepare old redirect URL -- do not redirect to Login or Logout (loop)!
458        if ( $Param{RequestedURL} =~ /Action=(Logout|Login|LostPassword|PreLogin)/ ) {
459            $Param{RequestedURL} = '';
460        }
461
462        # redirect with new session id
463        print $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Redirect(
464            OP    => $Param{RequestedURL},
465            Login => 1,
466        );
467        return 1;
468    }
469
470    # logout
471    elsif ( $Param{Action} eq 'Logout' ) {
472
473        # check session id
474        if ( !$SessionObject->CheckSessionID( SessionID => $Param{SessionID} ) ) {
475
476            # new layout object
477            my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
478
479            # redirect to alternate login
480            if ( $ConfigObject->Get('CustomerPanelLoginURL') ) {
481                $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} );
482                print $LayoutObject->Redirect(
483                    ExtURL => $ConfigObject->Get('CustomerPanelLoginURL')
484                        . "?Reason=InvalidSessionID;RequestedURL=$Param{RequestedURL}",
485                );
486            }
487
488            # show login screen
489            print $LayoutObject->CustomerLogin(
490                Title   => 'Logout',
491                Message => Translatable('Session invalid. Please log in again.'),
492                %Param,
493            );
494            return;
495        }
496
497        # get session data
498        my %UserData = $SessionObject->GetSessionIDData(
499            SessionID => $Param{SessionID},
500        );
501
502        $UserData{UserTimeZone} = $Self->_UserTimeZoneGet(%UserData);
503
504        # create new LayoutObject with new '%Param' and '%UserData'
505        $Kernel::OM->ObjectParamAdd(
506            'Kernel::Output::HTML::Layout' => {
507                SetCookies => {
508                    SessionIDCookie => $ParamObject->SetCookie(
509                        Key      => $Param{SessionName},
510                        Value    => '',
511                        Expires  => '-1y',
512                        Path     => $ConfigObject->Get('ScriptAlias'),
513                        Secure   => scalar $CookieSecureAttribute,
514                        HTTPOnly => 1,
515                    ),
516                },
517                %Param,
518                %UserData,
519            },
520        );
521
522        $Kernel::OM->ObjectsDiscard( Objects => ['Kernel::Output::HTML::Layout'] );
523        my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
524
525        # remove session id
526        if ( !$SessionObject->RemoveSessionID( SessionID => $Param{SessionID} ) ) {
527            $LayoutObject->CustomerFatalError(
528                Comment => Translatable('Please contact the administrator.')
529            );
530            return;
531        }
532
533        # redirect to alternate login
534        if ( $ConfigObject->Get('CustomerPanelLogoutURL') ) {
535            print $LayoutObject->Redirect(
536                ExtURL => $ConfigObject->Get('CustomerPanelLogoutURL'),
537            );
538        }
539
540        # show logout screen
541        my $LogoutMessage = $LayoutObject->{LanguageObject}->Translate('Logout successful.');
542
543        $LayoutObject->Print(
544            Output => \$LayoutObject->CustomerLogin(
545                Title       => 'Logout',
546                Message     => $LogoutMessage,
547                MessageType => 'Success',
548                %Param,
549            ),
550        );
551        return 1;
552    }
553
554    # CustomerLostPassword
555    elsif ( $Param{Action} eq 'CustomerLostPassword' ) {
556
557        # new layout object
558        my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
559
560        # check feature
561        if ( !$ConfigObject->Get('CustomerPanelLostPassword') ) {
562
563            # show normal login
564            $LayoutObject->Print(
565                Output => \$LayoutObject->CustomerLogin(
566                    Title   => 'Login',
567                    Message => Translatable('Feature not active!'),
568                ),
569            );
570            return;
571        }
572
573        # get params
574        my $User  = $ParamObject->GetParam( Param => 'User' )  || '';
575        my $Token = $ParamObject->GetParam( Param => 'Token' ) || '';
576
577        # get user login by token
578        if ( !$User && $Token ) {
579
580            # Prevent extracting password reset token character-by-character via wildcard injection
581            # The wild card characters "%" and "_" could be used to match arbitrary character.
582            if ( $Token !~ m{\A (?: [a-zA-Z] | \d )+ \z}xms ) {
583
584                # Security: pretend that password reset instructions were actually sent to
585                #   make sure that users cannot find out valid usernames by
586                #   just trying and checking the result message.
587                $LayoutObject->Print(
588                    Output => \$LayoutObject->Login(
589                        Title       => 'Login',
590                        Message     => Translatable('Sent password reset instructions. Please check your email.'),
591                        MessageType => 'Success',
592                        %Param,
593                    ),
594                );
595                return;
596            }
597
598            my %UserList = $UserObject->SearchPreferences(
599                Key   => 'UserToken',
600                Value => $Token,
601            );
602            USER_ID:
603            for my $UserID ( sort keys %UserList ) {
604                my %UserData = $UserObject->CustomerUserDataGet(
605                    User  => $UserID,
606                    Valid => 1,
607                );
608                if (%UserData) {
609                    $User = $UserData{UserLogin};
610                    last USER_ID;
611                }
612            }
613        }
614
615        # get user data
616        my %UserData = $UserObject->CustomerUserDataGet( User => $User );
617
618        # verify customer user is valid when requesting password reset
619        my @ValidIDs    = $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet();
620        my $UserIsValid = grep { $UserData{ValidID} && $UserData{ValidID} == $_ } @ValidIDs;
621        if ( !$UserData{UserID} || !$UserIsValid ) {
622
623            # Security: pretend that password reset instructions were actually sent to
624            #   make sure that users cannot find out valid usernames by
625            #   just trying and checking the result message.
626            $LayoutObject->Print(
627                Output => \$LayoutObject->CustomerLogin(
628                    Title       => 'Login',
629                    Message     => Translatable('Sent password reset instructions. Please check your email.'),
630                    MessageType => 'Success',
631                ),
632            );
633            return;
634        }
635
636        # create email object
637        my $EmailObject = Kernel::System::Email->new( %{$Self} );
638
639        # send password reset token
640        if ( !$Token ) {
641
642            # generate token
643            $UserData{Token} = $UserObject->TokenGenerate(
644                UserID => $UserData{UserID},
645            );
646
647            # send token notify email with link
648            my $Body = $ConfigObject->Get('CustomerPanelBodyLostPasswordToken')
649                || 'ERROR: CustomerPanelBodyLostPasswordToken is missing!';
650            my $Subject = $ConfigObject->Get('CustomerPanelSubjectLostPasswordToken')
651                || 'ERROR: CustomerPanelSubjectLostPasswordToken is missing!';
652            for ( sort keys %UserData ) {
653                $Body =~ s/<OTRS_$_>/$UserData{$_}/gi;
654            }
655            my $Sent = $EmailObject->Send(
656                To       => $UserData{UserEmail},
657                Subject  => $Subject,
658                Charset  => $LayoutObject->{UserCharset},
659                MimeType => 'text/plain',
660                Body     => $Body
661            );
662            if ( !$Sent->{Success} ) {
663                $LayoutObject->FatalError(
664                    Comment => Translatable('Please contact the administrator.'),
665                );
666                return;
667            }
668            $LayoutObject->Print(
669                Output => \$LayoutObject->CustomerLogin(
670                    Title   => 'Login',
671                    Message => Translatable('Sent password reset instructions. Please check your email.'),
672                    %Param,
673                    MessageType => 'Success',
674                ),
675            );
676            return 1;
677
678        }
679
680        # reset password
681        # check if token is valid
682        my $TokenValid = $UserObject->TokenCheck(
683            Token  => $Token,
684            UserID => $UserData{UserID},
685        );
686        if ( !$TokenValid ) {
687            $LayoutObject->Print(
688                Output => \$LayoutObject->CustomerLogin(
689                    Title   => 'Login',
690                    Message => Translatable('Invalid Token!'),
691                    %Param,
692                ),
693            );
694            return;
695        }
696
697        # get new password
698        $UserData{NewPW} = $UserObject->GenerateRandomPassword();
699
700        # update new password
701        my $Success = $UserObject->SetPassword(
702            UserLogin => $User,
703            PW        => $UserData{NewPW}
704        );
705
706        if ( !$Success ) {
707            $LayoutObject->Print(
708                Output => \$LayoutObject->CustomerLogin(
709                    Title   => 'Login',
710                    Message => Translatable('Reset password unsuccessful. Please contact the administrator.'),
711                    User    => $User,
712                ),
713            );
714            return;
715        }
716
717        # send notify email
718        my $Body = $ConfigObject->Get('CustomerPanelBodyLostPassword')
719            || 'New Password is: <OTRS_NEWPW>';
720        my $Subject = $ConfigObject->Get('CustomerPanelSubjectLostPassword')
721            || 'New Password!';
722        for ( sort keys %UserData ) {
723            $Body =~ s/<OTRS_$_>/$UserData{$_}/gi;
724        }
725        my $Sent = $EmailObject->Send(
726            To       => $UserData{UserEmail},
727            Subject  => $Subject,
728            Charset  => $LayoutObject->{UserCharset},
729            MimeType => 'text/plain',
730            Body     => $Body
731        );
732        if ( !$Sent->{Success} ) {
733            $LayoutObject->CustomerFatalError(
734                Comment => Translatable('Please contact the administrator.')
735            );
736            return;
737        }
738        my $Message = $LayoutObject->{LanguageObject}->Translate(
739            'Sent new password to %s. Please check your email.',
740            $UserData{UserEmail},
741        );
742        $LayoutObject->Print(
743            Output => \$LayoutObject->CustomerLogin(
744                Title       => 'Login',
745                Message     => $Message,
746                User        => $User,
747                MessageType => 'Success',
748            ),
749        );
750        return 1;
751    }
752
753    # create new customer account
754    elsif ( $Param{Action} eq 'CustomerCreateAccount' ) {
755
756        # new layout object
757        my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
758
759        # check feature
760        if ( !$ConfigObject->Get('CustomerPanelCreateAccount') ) {
761
762            # show normal login
763            $LayoutObject->Print(
764                Output => \$LayoutObject->CustomerLogin(
765                    Title   => 'Login',
766                    Message => Translatable('Feature not active!'),
767                ),
768            );
769            return;
770        }
771
772        # get params
773        my %GetParams;
774        for my $Entry ( @{ $ConfigObject->Get('CustomerUser')->{Map} } ) {
775            $GetParams{ $Entry->[0] } = $ParamObject->GetParam( Param => $Entry->[1] )
776                || '';
777        }
778        $GetParams{ValidID} = 1;
779
780        # check needed params
781        if ( !$GetParams{UserCustomerID} ) {
782            $GetParams{UserCustomerID} = $GetParams{UserEmail};
783        }
784        if ( !$GetParams{UserLogin} ) {
785            $GetParams{UserLogin} = $GetParams{UserEmail};
786        }
787
788        # get new password
789        $GetParams{UserPassword} = $UserObject->GenerateRandomPassword();
790
791        # get user data
792        my %UserData = $UserObject->CustomerUserDataGet( User => $GetParams{UserLogin} );
793        if ( $UserData{UserID} || !$GetParams{UserLogin} ) {
794
795            # send data to JS
796            $LayoutObject->AddJSData(
797                Key   => 'SignupError',
798                Value => 1,
799            );
800
801            $LayoutObject->Print(
802                Output => \$LayoutObject->CustomerLogin(
803                    Title => 'Login',
804                    Message =>
805                        Translatable('This e-mail address already exists. Please log in or reset your password.'),
806                    UserTitle     => $GetParams{UserTitle},
807                    UserFirstname => $GetParams{UserFirstname},
808                    UserLastname  => $GetParams{UserLastname},
809                    UserEmail     => $GetParams{UserEmail},
810                ),
811            );
812            return;
813        }
814
815        # check for mail address restrictions
816        my @Whitelist = @{
817            $ConfigObject->Get('CustomerPanelCreateAccount::MailRestrictions::Whitelist') // []
818        };
819        my @Blacklist = @{
820            $ConfigObject->Get('CustomerPanelCreateAccount::MailRestrictions::Blacklist') // []
821        };
822
823        my $WhitelistMatched;
824        for my $WhitelistEntry (@Whitelist) {
825            my $Regex = eval {qr/$WhitelistEntry/i};
826            if ($@) {
827                $Kernel::OM->Get('Kernel::System::Log')->Log(
828                    Priority => 'error',
829                    Message =>
830                        $LayoutObject->{LanguageObject}->Translate(
831                        'The customer panel mail address whitelist contains the invalid regular expression $WhitelistEntry, please check and correct it.'
832                        ),
833                );
834            }
835            elsif ( $GetParams{UserEmail} =~ $Regex ) {
836                $WhitelistMatched++;
837            }
838        }
839        my $BlacklistMatched;
840        for my $BlacklistEntry (@Blacklist) {
841            my $Regex = eval {qr/$BlacklistEntry/i};
842            if ($@) {
843                $Kernel::OM->Get('Kernel::System::Log')->Log(
844                    Priority => 'error',
845                    Message =>
846                        $LayoutObject->{LanguageObject}->Translate(
847                        'The customer panel mail address blacklist contains the invalid regular expression $BlacklistEntry, please check and correct it.'
848                        ),
849                );
850            }
851            elsif ( $GetParams{UserEmail} =~ $Regex ) {
852                $BlacklistMatched++;
853            }
854        }
855
856        if ( ( @Whitelist && !$WhitelistMatched ) || ( @Blacklist && $BlacklistMatched ) ) {
857
858            # send data to JS
859            $LayoutObject->AddJSData(
860                Key   => 'SignupError',
861                Value => 1,
862            );
863
864            $LayoutObject->Print(
865                Output => \$LayoutObject->CustomerLogin(
866                    Title => 'Login',
867                    Message =>
868                        Translatable('This email address is not allowed to register. Please contact support staff.'),
869                    UserTitle     => $GetParams{UserTitle},
870                    UserFirstname => $GetParams{UserFirstname},
871                    UserLastname  => $GetParams{UserLastname},
872                    UserEmail     => $GetParams{UserEmail},
873                ),
874            );
875
876            return;
877        }
878
879        # create account
880        my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
881
882        my $Now = $DateTimeObject->ToString();
883
884        my $Add = $UserObject->CustomerUserAdd(
885            %GetParams,
886            Comment => $LayoutObject->{LanguageObject}->Translate( 'Added via Customer Panel (%s)', $Now ),
887            ValidID => 1,
888            UserID  => $ConfigObject->Get('CustomerPanelUserID'),
889        );
890        if ( !$Add ) {
891
892            # send data to JS
893            $LayoutObject->AddJSData(
894                Key   => 'SignupError',
895                Value => 1,
896            );
897
898            $LayoutObject->Print(
899                Output => \$LayoutObject->CustomerLogin(
900                    Title         => 'Login',
901                    Message       => Translatable('Customer user can\'t be added!'),
902                    UserTitle     => $GetParams{UserTitle},
903                    UserFirstname => $GetParams{UserFirstname},
904                    UserLastname  => $GetParams{UserLastname},
905                    UserEmail     => $GetParams{UserEmail},
906                ),
907            );
908            return;
909        }
910
911        # send notify email
912        my $EmailObject = Kernel::System::Email->new( %{$Self} );
913        my $Body        = $ConfigObject->Get('CustomerPanelBodyNewAccount')
914            || 'No Config Option found!';
915        my $Subject = $ConfigObject->Get('CustomerPanelSubjectNewAccount')
916            || 'New OTRS Account!';
917        for ( sort keys %GetParams ) {
918            $Body =~ s/<OTRS_$_>/$GetParams{$_}/gi;
919        }
920
921        # send account info
922        my $Sent = $EmailObject->Send(
923            To       => $GetParams{UserEmail},
924            Subject  => $Subject,
925            Charset  => $LayoutObject->{UserCharset},
926            MimeType => 'text/plain',
927            Body     => $Body
928        );
929        if ( !$Sent->{Success} ) {
930            my $Output = $LayoutObject->CustomerHeader(
931                Area  => 'Core',
932                Title => 'Error'
933            );
934            $Output .= $LayoutObject->CustomerWarning(
935                Comment => Translatable('Can\'t send account info!')
936            );
937            $Output .= $LayoutObject->CustomerFooter();
938            $LayoutObject->Print( Output => \$Output );
939            return;
940        }
941
942        # show sent account info
943        if ( $ConfigObject->Get('CustomerPanelLoginURL') ) {
944
945            # redirect to alternate login
946            $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} );
947            print $LayoutObject->Redirect(
948                ExtURL => $ConfigObject->Get('CustomerPanelLoginURL')
949                    . "?RequestedURL=$Param{RequestedURL};User=$GetParams{UserLogin};"
950                    . "Email=$GetParams{UserEmail};Reason=NewAccountCreated",
951            );
952            return 1;
953        }
954
955        my $AccountCreatedMessage = $LayoutObject->{LanguageObject}->Translate(
956            'New account created. Sent login information to %s. Please check your email.',
957            $GetParams{UserEmail},
958        );
959
960        # login screen
961        $LayoutObject->Print(
962            Output => \$LayoutObject->CustomerLogin(
963                Title       => 'Login',
964                Message     => $AccountCreatedMessage,
965                User        => $GetParams{UserLogin},
966                MessageType => 'Success',
967            ),
968        );
969        return 1;
970    }
971
972    # show login site
973    elsif ( !$Param{SessionID} ) {
974
975        # new layout object
976        my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
977
978        # create AuthObject
979        my $AuthObject = $Kernel::OM->Get('Kernel::System::CustomerAuth');
980        if ( $AuthObject->GetOption( What => 'PreAuth' ) ) {
981
982            # automatic login
983            $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} );
984            print $LayoutObject->Redirect(
985                OP => "Action=PreLogin;RequestedURL=$Param{RequestedURL}",
986            );
987            return;
988        }
989        elsif ( $ConfigObject->Get('CustomerPanelLoginURL') ) {
990
991            # redirect to alternate login
992            $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} );
993            print $LayoutObject->Redirect(
994                ExtURL => $ConfigObject->Get('CustomerPanelLoginURL')
995                    . "?RequestedURL=$Param{RequestedURL}",
996            );
997            return;
998        }
999
1000        # login screen
1001        $LayoutObject->Print(
1002            Output => \$LayoutObject->CustomerLogin(
1003                Title => 'Login',
1004                %Param,
1005            ),
1006        );
1007        return 1;
1008    }
1009
1010    # run modules if a version value exists
1011    elsif ( $Kernel::OM->Get('Kernel::System::Main')->Require("Kernel::Modules::$Param{Action}") ) {
1012
1013        # check session id
1014        if ( !$SessionObject->CheckSessionID( SessionID => $Param{SessionID} ) ) {
1015
1016            # create new LayoutObject with new '%Param'
1017            $Kernel::OM->ObjectParamAdd(
1018                'Kernel::Output::HTML::Layout' => {
1019                    SetCookies => {
1020                        SessionIDCookie => $ParamObject->SetCookie(
1021                            Key      => $Param{SessionName},
1022                            Value    => '',
1023                            Expires  => '-1y',
1024                            Path     => $ConfigObject->Get('ScriptAlias'),
1025                            Secure   => scalar $CookieSecureAttribute,
1026                            HTTPOnly => 1,
1027                        ),
1028                    },
1029                    %Param,
1030                }
1031            );
1032
1033            $Kernel::OM->ObjectsDiscard( Objects => ['Kernel::Output::HTML::Layout'] );
1034            my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
1035
1036            # create AuthObject
1037            my $AuthObject = $Kernel::OM->Get('Kernel::System::CustomerAuth');
1038            if ( $AuthObject->GetOption( What => 'PreAuth' ) ) {
1039
1040                # automatic re-login
1041                $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} );
1042                print $LayoutObject->Redirect(
1043                    OP => "?Action=PreLogin&RequestedURL=$Param{RequestedURL}",
1044                );
1045                return;
1046            }
1047
1048            # redirect to alternate login
1049            elsif ( $ConfigObject->Get('CustomerPanelLoginURL') ) {
1050
1051                # redirect to alternate login
1052                $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} );
1053                print $LayoutObject->Redirect(
1054                    ExtURL => $ConfigObject->Get('CustomerPanelLoginURL')
1055                        . "?Reason=InvalidSessionID;RequestedURL=$Param{RequestedURL}",
1056                );
1057                return;
1058            }
1059
1060            # show login
1061            $LayoutObject->Print(
1062                Output => \$LayoutObject->CustomerLogin(
1063                    Title => 'Login',
1064                    Message =>
1065                        $LayoutObject->{LanguageObject}->Translate( $SessionObject->SessionIDErrorMessage() ),
1066                    %Param,
1067                ),
1068            );
1069            return;
1070        }
1071
1072        # get session data
1073        my %UserData = $SessionObject->GetSessionIDData(
1074            SessionID => $Param{SessionID},
1075        );
1076
1077        $UserData{UserTimeZone} = $Self->_UserTimeZoneGet(%UserData);
1078
1079        # check needed data
1080        if ( !$UserData{UserID} || !$UserData{UserLogin} || $UserData{UserType} ne 'Customer' ) {
1081            my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
1082
1083            # redirect to alternate login
1084            if ( $ConfigObject->Get('CustomerPanelLoginURL') ) {
1085                print $LayoutObject->Redirect(
1086                    ExtURL => $ConfigObject->Get('CustomerPanelLoginURL')
1087                        . "?Reason=SystemError",
1088                );
1089                return;
1090            }
1091
1092            # show login screen
1093            $LayoutObject->Print(
1094                Output => \$LayoutObject->CustomerLogin(
1095                    Title   => 'Error',
1096                    Message => Translatable('Error: invalid session.'),
1097                    %Param,
1098                ),
1099            );
1100            return;
1101        }
1102
1103        # module registry
1104        my $ModuleReg = $ConfigObject->Get('CustomerFrontend::Module')->{ $Param{Action} };
1105        if ( !$ModuleReg ) {
1106
1107            # new layout object
1108            my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
1109            $Kernel::OM->Get('Kernel::System::Log')->Log(
1110                Priority => 'error',
1111                Message =>
1112                    "Module Kernel::Modules::$Param{Action} not registered in Kernel/Config.pm!",
1113            );
1114            $LayoutObject->CustomerFatalError(
1115                Comment => Translatable('Please contact the administrator.'),
1116            );
1117            return;
1118        }
1119
1120        # module permission check for action
1121        if (
1122            ref $ModuleReg->{GroupRo} eq 'ARRAY'
1123            && !scalar @{ $ModuleReg->{GroupRo} }
1124            && ref $ModuleReg->{Group} eq 'ARRAY'
1125            && !scalar @{ $ModuleReg->{Group} }
1126            )
1127        {
1128            $Param{AccessRo} = 1;
1129            $Param{AccessRw} = 1;
1130        }
1131        else {
1132
1133            ( $Param{AccessRo}, $Param{AccessRw} ) = $Self->_CheckModulePermission(
1134                ModuleReg => $ModuleReg,
1135                %UserData,
1136            );
1137
1138            if ( !$Param{AccessRo} ) {
1139
1140                # new layout object
1141                my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
1142                $Kernel::OM->Get('Kernel::System::Log')->Log(
1143                    Priority => 'error',
1144                    Message  => 'No Permission to use this frontend action module!'
1145                );
1146                $LayoutObject->CustomerFatalError(
1147                    Comment => Translatable('Please contact the administrator.'),
1148                );
1149                return;
1150            }
1151
1152        }
1153
1154        my $NavigationConfig = $ConfigObject->Get('CustomerFrontend::Navigation')->{ $Param{Action} };
1155
1156        # module permission check for submenu item
1157        if ( IsHashRefWithData($NavigationConfig) ) {
1158
1159            KEY:
1160            for my $Key ( sort keys %{$NavigationConfig} ) {
1161                next KEY if $Key                 !~ m/^\d+/i;
1162                next KEY if $Param{RequestedURL} !~ m/Subaction/i;
1163
1164                my @ModuleNavigationConfigs;
1165
1166                # FIXME: Support both old (HASH) and new (ARRAY of HASH) navigation configurations, for reasons of
1167                #   backwards compatibility. Once we are sure everything has been migrated correctly, support for
1168                #   HASH-only configuration can be dropped in future major release.
1169                if ( IsHashRefWithData( $NavigationConfig->{$Key} ) ) {
1170                    push @ModuleNavigationConfigs, $NavigationConfig->{$Key};
1171                }
1172                elsif ( IsArrayRefWithData( $NavigationConfig->{$Key} ) ) {
1173                    push @ModuleNavigationConfigs, @{ $NavigationConfig->{$Key} };
1174                }
1175
1176                # Skip incompatible configuration.
1177                else {
1178                    next KEY;
1179                }
1180
1181                ITEM:
1182                for my $Item (@ModuleNavigationConfigs) {
1183                    if (
1184                        $Item->{Link} =~ m/Subaction=/i
1185                        && $Item->{Link} !~ m/$Param{Subaction}/i
1186                        )
1187                    {
1188                        next ITEM;
1189                    }
1190                    $Param{AccessRo} = 0;
1191                    $Param{AccessRw} = 0;
1192
1193                    # module permission check for submenu item
1194                    if (
1195                        ref $Item->{GroupRo} eq 'ARRAY'
1196                        && !scalar @{ $Item->{GroupRo} }
1197                        && ref $Item->{Group} eq 'ARRAY'
1198                        && !scalar @{ $Item->{Group} }
1199                        )
1200                    {
1201                        $Param{AccessRo} = 1;
1202                        $Param{AccessRw} = 1;
1203                    }
1204                    else {
1205
1206                        ( $Param{AccessRo}, $Param{AccessRw} ) = $Self->_CheckModulePermission(
1207                            ModuleReg => $Item,
1208                            %UserData,
1209                        );
1210
1211                        if ( !$Param{AccessRo} ) {
1212
1213                            # new layout object
1214                            my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
1215                            $Kernel::OM->Get('Kernel::System::Log')->Log(
1216                                Priority => 'error',
1217                                Message  => 'No Permission to use this frontend subaction module!'
1218                            );
1219                            $LayoutObject->CustomerFatalError(
1220                                Comment => Translatable('Please contact the administrator.')
1221                            );
1222                            return;
1223                        }
1224                    }
1225                }
1226            }
1227        }
1228
1229        # create new LayoutObject with new '%Param' and '%UserData'
1230        $Kernel::OM->ObjectParamAdd(
1231            'Kernel::Output::HTML::Layout' => {
1232                %Param,
1233                %UserData,
1234                ModuleReg => $ModuleReg,
1235            },
1236        );
1237
1238        $Kernel::OM->ObjectsDiscard( Objects => ['Kernel::Output::HTML::Layout'] );
1239        my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
1240
1241        # update last request time
1242        if (
1243            !$ParamObject->IsAJAXRequest()
1244            || $Param{Action} eq 'CustomerVideoChat'
1245            )
1246        {
1247            my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
1248
1249            $SessionObject->UpdateSessionID(
1250                SessionID => $Param{SessionID},
1251                Key       => 'UserLastRequest',
1252                Value     => $DateTimeObject->ToEpoch(),
1253            );
1254        }
1255
1256        # pre application module
1257        my $PreModule = $ConfigObject->Get('CustomerPanelPreApplicationModule');
1258        if ($PreModule) {
1259            my %PreModuleList;
1260            if ( ref $PreModule eq 'HASH' ) {
1261                %PreModuleList = %{$PreModule};
1262            }
1263            else {
1264                $PreModuleList{Init} = $PreModule;
1265            }
1266
1267            MODULE:
1268            for my $PreModuleKey ( sort keys %PreModuleList ) {
1269                my $PreModule = $PreModuleList{$PreModuleKey};
1270                next MODULE if !$PreModule;
1271                next MODULE if !$Kernel::OM->Get('Kernel::System::Main')->Require($PreModule);
1272
1273                # debug info
1274                if ( $Self->{Debug} ) {
1275                    $Kernel::OM->Get('Kernel::System::Log')->Log(
1276                        Priority => 'debug',
1277                        Message  => "CustomerPanelPreApplication module $PreModule is used.",
1278                    );
1279                }
1280
1281                # use module
1282                my $PreModuleObject = $PreModule->new(
1283                    %Param,
1284                    %UserData,
1285
1286                );
1287                my $Output = $PreModuleObject->PreRun();
1288                if ($Output) {
1289                    $LayoutObject->Print( Output => \$Output );
1290                    return 1;
1291                }
1292            }
1293        }
1294
1295        # debug info
1296        if ( $Self->{Debug} ) {
1297            $Kernel::OM->Get('Kernel::System::Log')->Log(
1298                Priority => 'debug',
1299                Message  => 'Kernel::Modules::' . $Param{Action} . '->new',
1300            );
1301        }
1302
1303        my $FrontendObject = ( 'Kernel::Modules::' . $Param{Action} )->new(
1304            %Param,
1305            %UserData,
1306            ModuleReg => $ModuleReg,
1307            Debug     => $Self->{Debug},
1308        );
1309
1310        # debug info
1311        if ( $Self->{Debug} ) {
1312            $Kernel::OM->Get('Kernel::System::Log')->Log(
1313                Priority => 'debug',
1314                Message  => 'Kernel::Modules::' . $Param{Action} . '->run',
1315            );
1316        }
1317
1318        # ->Run $Action with $FrontendObject
1319        $LayoutObject->Print( Output => \$FrontendObject->Run() );
1320
1321        # log request time
1322        if ( $ConfigObject->Get('PerformanceLog') ) {
1323            if ( ( !$QueryString && $Param{Action} ) || $QueryString !~ /Action=/ ) {
1324                $QueryString = 'Action=' . $Param{Action} . ';Subaction=' . $Param{Subaction};
1325            }
1326            my $File = $ConfigObject->Get('PerformanceLog::File');
1327            ## no critic
1328            if ( open my $Out, '>>', $File ) {
1329                ## use critic
1330                print $Out time()
1331                    . '::Customer::'
1332                    . ( time() - $Self->{PerformanceLogStart} )
1333                    . "::$UserData{UserLogin}::$QueryString\n";
1334                close $Out;
1335                $Kernel::OM->Get('Kernel::System::Log')->Log(
1336                    Priority => 'debug',
1337                    Message  => 'Response::Customer: '
1338                        . ( time() - $Self->{PerformanceLogStart} )
1339                        . "s taken (URL:$QueryString:$UserData{UserLogin})",
1340                );
1341            }
1342            else {
1343                $Kernel::OM->Get('Kernel::System::Log')->Log(
1344                    Priority => 'error',
1345                    Message  => "Can't write $File: $!",
1346                );
1347            }
1348        }
1349        return 1;
1350    }
1351
1352    # print an error screen
1353    my %Data = $SessionObject->GetSessionIDData(
1354        SessionID => $Param{SessionID},
1355    );
1356    $Data{UserTimeZone} = $Self->_UserTimeZoneGet(%Data);
1357    $Kernel::OM->ObjectParamAdd(
1358        'Kernel::Output::HTML::Layout' => {
1359            %Param,
1360            %Data,
1361        },
1362    );
1363    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
1364    $LayoutObject->CustomerFatalError(
1365        Comment => Translatable('Please contact the administrator.'),
1366    );
1367    return;
1368}
1369
1370=begin Internal:
1371
1372=head2 _CheckModulePermission()
1373
1374module permission check
1375
1376    ($AccessRo, $AccessRw = $AutoResponseObject->_CheckModulePermission(
1377        ModuleReg => $ModuleReg,
1378        %UserData,
1379    );
1380
1381=cut
1382
1383sub _CheckModulePermission {
1384    my ( $Self, %Param ) = @_;
1385
1386    my $AccessRo = 0;
1387    my $AccessRw = 0;
1388
1389    PERMISSION:
1390    for my $Permission (qw(GroupRo Group)) {
1391        my $AccessOk = 0;
1392        my $Group    = $Param{ModuleReg}->{$Permission};
1393
1394        next PERMISSION if !$Group;
1395
1396        my $GroupObject = $Kernel::OM->Get('Kernel::System::CustomerGroup');
1397
1398        if ( IsArrayRefWithData($Group) ) {
1399            GROUP:
1400            for my $Item ( @{$Group} ) {
1401                next GROUP if !$Item;
1402                next GROUP if !$GroupObject->PermissionCheck(
1403                    UserID    => $Param{UserID},
1404                    GroupName => $Item,
1405                    Type      => $Permission eq 'GroupRo' ? 'ro' : 'rw',
1406                );
1407
1408                $AccessOk = 1;
1409                last GROUP;
1410            }
1411        }
1412        else {
1413            my $HasPermission = $GroupObject->PermissionCheck(
1414                UserID    => $Param{UserID},
1415                GroupName => $Group,
1416                Type      => $Permission eq 'GroupRo' ? 'ro' : 'rw',
1417            );
1418            if ($HasPermission) {
1419                $AccessOk = 1;
1420            }
1421        }
1422        if ( $Permission eq 'Group' && $AccessOk ) {
1423            $AccessRo = 1;
1424            $AccessRw = 1;
1425        }
1426        elsif ( $Permission eq 'GroupRo' && $AccessOk ) {
1427            $AccessRo = 1;
1428        }
1429    }
1430
1431    return ( $AccessRo, $AccessRw );
1432}
1433
1434=head2 _UserTimeZoneGet()
1435
1436Get time zone for the current user. This function will validate passed time zone parameter and return default user time
1437zone if it's not valid.
1438
1439    my $UserTimeZone = $Self->_UserTimeZoneGet(
1440        UserTimeZone => 'Europe/Berlin',
1441    );
1442
1443=cut
1444
1445sub _UserTimeZoneGet {
1446    my ( $Self, %Param ) = @_;
1447
1448    my $UserTimeZone;
1449
1450    # Return passed time zone only if it's valid. It can happen that user preferences or session store an old-style
1451    #   offset which is not valid anymore. In this case, return the default value.
1452    #   Please see bug#13374 for more information.
1453    if (
1454        $Param{UserTimeZone}
1455        && Kernel::System::DateTime->IsTimeZoneValid( TimeZone => $Param{UserTimeZone} )
1456        )
1457    {
1458        $UserTimeZone = $Param{UserTimeZone};
1459    }
1460
1461    $UserTimeZone ||= Kernel::System::DateTime->UserDefaultTimeZoneGet();
1462
1463    return $UserTimeZone;
1464}
1465
1466=end Internal:
1467
1468=cut
1469
1470sub DESTROY {
1471    my $Self = shift;
1472
1473    # debug info
1474    if ( $Self->{Debug} ) {
1475        $Kernel::OM->Get('Kernel::System::Log')->Log(
1476            Priority => 'debug',
1477            Message  => 'Global handle stopped.',
1478        );
1479    }
1480
1481    return 1;
1482}
1483
14841;
1485
1486=head1 TERMS AND CONDITIONS
1487
1488This software is part of the OTRS project (L<https://otrs.org/>).
1489
1490This software comes with ABSOLUTELY NO WARRANTY. For details, see
1491the enclosed file COPYING for license information (GPL). If you
1492did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
1493
1494=cut
1495