# -- # Copyright (C) 2001-2017 OTRS AG, http://otrs.com/ # -- # This software comes with ABSOLUTELY NO WARRANTY. For details, see # the enclosed file COPYING for license information (AGPL). If you # did not receive this file, see http://www.gnu.org/licenses/agpl.txt. # # # Custom version for CAS authentication - rodrigo@goncalves.pro.br # # Version 2016-01-18 - RG - Version for OTRS 5.0.6 # Version 2017-12-07 - RG - Version for OTRS 6.0.1 # # -- package Kernel::System::Web::InterfaceCustomer; use strict; use warnings; # CAS Custom use CGI; use URI::Escape; # CAS Custom use Kernel::System::DateTime; use Kernel::System::Email; use Kernel::System::VariableCheck qw(IsArrayRefWithData IsHashRefWithData); use Kernel::Language qw(Translatable); our @ObjectDependencies = ( 'Kernel::Config', 'Kernel::Output::HTML::Layout', 'Kernel::System::AuthSession', 'Kernel::System::CustomerAuth', 'Kernel::System::CustomerGroup', 'Kernel::System::CustomerUser', 'Kernel::System::DB', 'Kernel::System::Group', 'Kernel::System::Log', 'Kernel::System::Main', 'Kernel::System::Scheduler', 'Kernel::System::DateTime', 'Kernel::System::Web::Request', 'Kernel::System::Valid', ); =head1 NAME Kernel::System::Web::InterfaceCustomer - the customer web interface =head1 DESCRIPTION the global customer web interface (authentication, session handling, ...) =head1 PUBLIC INTERFACE =head2 new() create customer web interface object use Kernel::System::Web::InterfaceCustomer; my $Debug = 0; my $InterfaceCustomer = Kernel::System::Web::InterfaceCustomer->new( Debug => $Debug, WebRequest => CGI::Fast->new(), # optional, e. g. if fast cgi is used, the CGI object is already provided ); =cut sub new { my ( $Type, %Param ) = @_; # allocate new hash for object my $Self = {}; bless( $Self, $Type ); # get debug level $Self->{Debug} = $Param{Debug} || 0; # performance log $Self->{PerformanceLogStart} = time(); $Kernel::OM->ObjectParamAdd( 'Kernel::System::Log' => { LogPrefix => $Kernel::OM->Get('Kernel::Config')->Get('CGILogPrefix'), }, 'Kernel::System::Web::Request' => { WebRequest => $Param{WebRequest} || 0, }, ); # debug info if ( $Self->{Debug} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'debug', Message => 'Global handle started...', ); } return $Self; } =head2 Run() execute the object $InterfaceCustomer->Run(); =cut sub Run { my $Self = shift; # CAS Custom if ($Self->CASLogout()) { return; } # CAS Custom my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); my $QueryString = $ENV{QUERY_STRING} || ''; # Check if https forcing is active, and redirect if needed. if ( $ConfigObject->Get('HTTPSForceRedirect') ) { # Some web servers do not set HTTPS environment variable, so it's not possible to easily know if we are using # https protocol. Look also for similarly named keys in environment hash, since this should prevent loops in # certain cases. if ( ( !defined $ENV{HTTPS} && !grep {/^HTTPS(?:_|$)/} keys %ENV ) || $ENV{HTTPS} ne 'on' ) { my $Host = $ENV{HTTP_HOST} || $ConfigObject->Get('FQDN'); # Redirect with 301 code. Add two new lines at the end, so HTTP headers are validated correctly. print "Status: 301 Moved Permanently\nLocation: https://$Host$ENV{REQUEST_URI}\n\n"; return; } } my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); my %Param; # get session id $Param{SessionName} = $ConfigObject->Get('CustomerPanelSessionName') || 'CSID'; $Param{SessionID} = $ParamObject->GetParam( Param => $Param{SessionName} ) || ''; # drop old session id (if exists) $QueryString =~ s/(\?|&|;|)$Param{SessionName}(=&|=;|=.+?&|=.+?$)/;/g; # define framework params my $FrameworkParams = { Lang => '', Action => '', Subaction => '', RequestedURL => $QueryString, }; for my $Key ( sort keys %{$FrameworkParams} ) { $Param{$Key} = $ParamObject->GetParam( Param => $Key ) || $FrameworkParams->{$Key}; } # validate language if ( $Param{Lang} && $Param{Lang} !~ m{\A[a-z]{2}(?:_[A-Z]{2})?\z}xms ) { delete $Param{Lang}; } my $BrowserHasCookie = 0; # Check if the browser sends the SessionID cookie and set the SessionID-cookie # as SessionID! GET or POST SessionID have the lowest priority. if ( $ConfigObject->Get('SessionUseCookie') ) { $Param{SessionIDCookie} = $ParamObject->GetCookie( Key => $Param{SessionName} ); if ( $Param{SessionIDCookie} ) { $Param{SessionID} = $Param{SessionIDCookie}; } } my $CookieSecureAttribute; if ( $ConfigObject->Get('HttpType') eq 'https' ) { # Restrict Cookie to HTTPS if it is used. $CookieSecureAttribute = 1; } $Kernel::OM->ObjectParamAdd( 'Kernel::Output::HTML::Layout' => { Lang => $Param{Lang}, }, 'Kernel::Language' => { UserLanguage => $Param{Lang}, }, ); my $DBCanConnect = $Kernel::OM->Get('Kernel::System::DB')->Connect(); if ( !$DBCanConnect || $ParamObject->Error() ) { my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); if ( !$DBCanConnect ) { $LayoutObject->CustomerFatalError( Comment => Translatable('Please contact the administrator.'), ); return; } if ( $ParamObject->Error() ) { $LayoutObject->CustomerFatalError( Message => $ParamObject->Error(), Comment => Translatable('Please contact the administrator.'), ); return; } } my $UserObject = $Kernel::OM->Get('Kernel::System::CustomerUser'); my $SessionObject = $Kernel::OM->Get('Kernel::System::AuthSession'); # get common application and add on application params my %CommonObjectParam = %{ $ConfigObject->Get('CustomerFrontend::CommonParam') }; for my $Key ( sort keys %CommonObjectParam ) { $Param{$Key} = $ParamObject->GetParam( Param => $Key ) || $CommonObjectParam{$Key}; } # security check Action Param (replace non-word chars) $Param{Action} =~ s/\W//g; # check request type if ( $Param{Action} eq 'PreLogin' ) { my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # login screen $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Login', Mode => 'PreLogin', %Param, ), ); return; } elsif ( $Param{Action} eq 'Login' ) { # get params my $PostUser = $ParamObject->GetParam( Param => 'User' ) || ''; my $PostPw = $ParamObject->GetParam( Param => 'Password', Raw => 1 ) || ''; my $PostTwoFactorToken = $ParamObject->GetParam( Param => 'TwoFactorToken', Raw => 1 ) || ''; # create AuthObject my $AuthObject = $Kernel::OM->Get('Kernel::System::CustomerAuth'); # check submitted data my $User = $AuthObject->Auth( User => $PostUser, Pw => $PostPw, TwoFactorToken => $PostTwoFactorToken, RequestedURL => $Param{RequestedURL}, ); my $Expires = '+' . $ConfigObject->Get('SessionMaxTime') . 's'; if ( !$ConfigObject->Get('SessionUseCookieAfterBrowserClose') ) { $Expires = ''; } # login is invalid if ( !$User ) { # CAS Custom # Fixes OTRS Layout bug when redirecting my $cgi = new CGI(); print $cgi->redirect( -URL => $ConfigObject->Get("Customer::AuthModule::CAS::CASUrl") . "/login?service=" . $ConfigObject->Get("Customer::AuthModule::CAS::ServiceUrl") . "?" . $Param{RequestedURL}); return; # CAS Custom $Kernel::OM->ObjectParamAdd( 'Kernel::Output::HTML::Layout' => { SetCookies => { OTRSBrowserHasCookie => $ParamObject->SetCookie( Key => 'OTRSBrowserHasCookie', Value => 1, Expires => $Expires, Path => $ConfigObject->Get('ScriptAlias'), Secure => $CookieSecureAttribute, HTTPOnly => 1, ), }, }, ); my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # redirect to alternate login if ( $ConfigObject->Get('CustomerPanelLoginURL') ) { $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); print $LayoutObject->Redirect( ExtURL => $ConfigObject->Get('CustomerPanelLoginURL') . "?Reason=LoginFailed;RequestedURL=$Param{RequestedURL}", ); return; } # show normal login $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Login', Message => $Kernel::OM->Get('Kernel::System::Log')->GetLogEntry( Type => 'Info', What => 'Message', ) || $AuthObject->GetLastErrorMessage() || Translatable('Login failed! Your user name or password was entered incorrectly.'), User => $PostUser, LoginFailed => 1, %Param, ), ); return; } # login is successful my %UserData = $UserObject->CustomerUserDataGet( User => $User, Valid => 1 ); # check if the browser supports cookies if ( $ParamObject->GetCookie( Key => 'OTRSBrowserHasCookie' ) ) { $Kernel::OM->ObjectParamAdd( 'Kernel::Output::HTML::Layout' => { BrowserHasCookie => 1, }, ); } # check needed data if ( !$UserData{UserID} || !$UserData{UserLogin} ) { my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # CAS Custom my $url = $Kernel::OM->Get("Kernel::Config")->Get('Customer::AuthModule::CAS::InvalidUserURL'); if ($url) { print $LayoutObject->Redirect( ExtURL => $url, ); return; } # CAS Custom # redirect to alternate login if ( $ConfigObject->Get('CustomerPanelLoginURL') ) { print $LayoutObject->Redirect( ExtURL => $ConfigObject->Get('CustomerPanelLoginURL') . '?Reason=SystemError', ); return; } # show need user data error message $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Error', Message => Translatable( 'Authentication succeeded, but no customer record is found in the customer backend. Please contact the administrator.' ), %Param, ), ); return; } # create datetime object my $SessionDTObject = $Kernel::OM->Create('Kernel::System::DateTime'); # create new session id my $NewSessionID = $SessionObject->CreateSessionID( %UserData, UserLastRequest => $SessionDTObject->ToEpoch(), UserType => 'Customer', SessionSource => 'CustomerInterface', ); # show error message if no session id has been created if ( !$NewSessionID ) { # get error message my $Error = $SessionObject->SessionIDErrorMessage() || ''; my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # output error message $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Login', Message => $Error, %Param, ), ); return; } # execution in 20 seconds my $ExecutionTimeObj = $Kernel::OM->Create('Kernel::System::DateTime'); $ExecutionTimeObj->Add( Seconds => 20 ); # add a asynchronous executor scheduler task to count the concurrent user $Kernel::OM->Get('Kernel::System::Scheduler')->TaskAdd( ExecutionTime => $ExecutionTimeObj->ToString(), Type => 'AsynchronousExecutor', Name => 'PluginAsynchronous::ConcurrentUser', MaximumParallelInstances => 1, Data => { Object => 'Kernel::System::SupportDataCollector::PluginAsynchronous::OTRS::ConcurrentUsers', Function => 'RunAsynchronous', }, ); # get time zone my $UserTimeZone = $UserData{UserTimeZone} || Kernel::System::DateTime->UserDefaultTimeZoneGet(); $SessionObject->UpdateSessionID( SessionID => $NewSessionID, Key => 'UserTimeZone', Value => $UserTimeZone, ); # check if the time zone offset reported by the user's browser differs from that # of the OTRS user's time zone offset my $DateTimeObject = $Kernel::OM->Create( 'Kernel::System::DateTime', ObjectParams => { TimeZone => $UserTimeZone, }, ); my $OTRSUserTimeZoneOffset = $DateTimeObject->Format( Format => '%{offset}' ) / 60; my $BrowserTimeZoneOffset = ( $ParamObject->GetParam( Param => 'TimeZoneOffset' ) || 0 ) * -1; # TimeZoneOffsetDifference contains the difference of the time zone offset between # the user's OTRS time zone setting and the one reported by the user's browser. # If there is a difference it can be evaluated later to e. g. show a message # for the user to check his OTRS time zone setting. my $UserTimeZoneOffsetDifference = abs( $OTRSUserTimeZoneOffset - $BrowserTimeZoneOffset ); $SessionObject->UpdateSessionID( SessionID => $NewSessionID, Key => 'UserTimeZoneOffsetDifference', Value => $UserTimeZoneOffsetDifference, ); $Kernel::OM->ObjectParamAdd( 'Kernel::Output::HTML::Layout' => { SetCookies => { SessionIDCookie => $ParamObject->SetCookie( Key => $Param{SessionName}, Value => $NewSessionID, Expires => $Expires, Path => $ConfigObject->Get('ScriptAlias'), Secure => scalar $CookieSecureAttribute, HTTPOnly => 1, ), OTRSBrowserHasCookie => $ParamObject->SetCookie( Key => 'OTRSBrowserHasCookie', Value => '', Expires => '-1y', Path => $ConfigObject->Get('ScriptAlias'), Secure => $CookieSecureAttribute, HTTPOnly => 1, ), }, SessionID => $NewSessionID, SessionName => $Param{SessionName}, }, ); # redirect with new session id and old params # prepare old redirect URL -- do not redirect to Login or Logout (loop)! if ( $Param{RequestedURL} =~ /Action=(Logout|Login|LostPassword|PreLogin)/ ) { $Param{RequestedURL} = ''; } # redirect with new session id print $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Redirect( OP => $Param{RequestedURL}, Login => 1, ); return 1; } # logout elsif ( $Param{Action} eq 'Logout' ) { # check session id if ( !$SessionObject->CheckSessionID( SessionID => $Param{SessionID} ) ) { # new layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # redirect to alternate login if ( $ConfigObject->Get('CustomerPanelLoginURL') ) { $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); print $LayoutObject->Redirect( ExtURL => $ConfigObject->Get('CustomerPanelLoginURL') . "?Reason=InvalidSessionID;RequestedURL=$Param{RequestedURL}", ); } # show login screen print $LayoutObject->CustomerLogin( Title => 'Logout', Message => Translatable('Session invalid. Please log in again.'), %Param, ); return; } # get session data my %UserData = $SessionObject->GetSessionIDData( SessionID => $Param{SessionID}, ); # create new LayoutObject with new '%Param' and '%UserData' $Kernel::OM->ObjectParamAdd( 'Kernel::Output::HTML::Layout' => { SetCookies => { SessionIDCookie => $ParamObject->SetCookie( Key => $Param{SessionName}, Value => '', Expires => '-1y', Path => $ConfigObject->Get('ScriptAlias'), Secure => scalar $CookieSecureAttribute, HTTPOnly => 1, ), }, %Param, %UserData, }, ); $Kernel::OM->ObjectsDiscard( Objects => ['Kernel::Output::HTML::Layout'] ); my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # remove session id if ( !$SessionObject->RemoveSessionID( SessionID => $Param{SessionID} ) ) { $LayoutObject->CustomerFatalError( Comment => Translatable('Please contact the administrator.') ); return; } # redirect to alternate login if ( $ConfigObject->Get('CustomerPanelLogoutURL') ) { print $LayoutObject->Redirect( ExtURL => $ConfigObject->Get('CustomerPanelLogoutURL') . "?Reason=Logout", ); } # show logout screen my $LogoutMessage = $LayoutObject->{LanguageObject}->Translate('Logout successful.'); $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Logout', Message => $LogoutMessage, MessageType => 'Success', %Param, ), ); return 1; } # CustomerLostPassword elsif ( $Param{Action} eq 'CustomerLostPassword' ) { # new layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # check feature if ( !$ConfigObject->Get('CustomerPanelLostPassword') ) { # show normal login $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Login', Message => Translatable('Feature not active!'), ), ); return; } # get params my $User = $ParamObject->GetParam( Param => 'User' ) || ''; my $Token = $ParamObject->GetParam( Param => 'Token' ) || ''; # get user login by token if ( !$User && $Token ) { my %UserList = $UserObject->SearchPreferences( Key => 'UserToken', Value => $Token, ); USER_ID: for my $UserID ( sort keys %UserList ) { my %UserData = $UserObject->CustomerUserDataGet( User => $UserID, Valid => 1, ); if (%UserData) { $User = $UserData{UserLogin}; last USER_ID; } } } # get user data my %UserData = $UserObject->CustomerUserDataGet( User => $User ); # verify customer user is valid when requesting password reset my @ValidIDs = $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet(); my $UserIsValid = grep { $UserData{ValidID} && $UserData{ValidID} == $_ } @ValidIDs; if ( !$UserData{UserID} || !$UserIsValid ) { # Security: pretend that password reset instructions were actually sent to # make sure that users cannot find out valid usernames by # just trying and checking the result message. $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Login', Message => Translatable('Sent password reset instructions. Please check your email.'), MessageType => 'Success', ), ); return; } # create email object my $EmailObject = Kernel::System::Email->new( %{$Self} ); # send password reset token if ( !$Token ) { # generate token $UserData{Token} = $UserObject->TokenGenerate( UserID => $UserData{UserID}, ); # send token notify email with link my $Body = $ConfigObject->Get('CustomerPanelBodyLostPasswordToken') || 'ERROR: CustomerPanelBodyLostPasswordToken is missing!'; my $Subject = $ConfigObject->Get('CustomerPanelSubjectLostPasswordToken') || 'ERROR: CustomerPanelSubjectLostPasswordToken is missing!'; for ( sort keys %UserData ) { $Body =~ s//$UserData{$_}/gi; } my $Sent = $EmailObject->Send( To => $UserData{UserEmail}, Subject => $Subject, Charset => $LayoutObject->{UserCharset}, MimeType => 'text/plain', Body => $Body ); if ( !$Sent->{Success} ) { $LayoutObject->FatalError( Comment => Translatable('Please contact the administrator.'), ); return; } $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Login', Message => Translatable('Sent password reset instructions. Please check your email.'), %Param, MessageType => 'Success', ), ); return 1; } # reset password # check if token is valid my $TokenValid = $UserObject->TokenCheck( Token => $Token, UserID => $UserData{UserID}, ); if ( !$TokenValid ) { $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Login', Message => Translatable('Invalid Token!'), %Param, ), ); return; } # get new password $UserData{NewPW} = $UserObject->GenerateRandomPassword(); # update new password my $Success = $UserObject->SetPassword( UserLogin => $User, PW => $UserData{NewPW} ); if ( !$Success ) { $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Login', Message => Translatable('Reset password unsuccessful. Please contact the administrator.'), User => $User, ), ); return; } # send notify email my $Body = $ConfigObject->Get('CustomerPanelBodyLostPassword') || 'New Password is: '; my $Subject = $ConfigObject->Get('CustomerPanelSubjectLostPassword') || 'New Password!'; for ( sort keys %UserData ) { $Body =~ s//$UserData{$_}/gi; } my $Sent = $EmailObject->Send( To => $UserData{UserEmail}, Subject => $Subject, Charset => $LayoutObject->{UserCharset}, MimeType => 'text/plain', Body => $Body ); if ( !$Sent->{Success} ) { $LayoutObject->CustomerFatalError( Comment => Translatable('Please contact the administrator.') ); return; } my $Message = $LayoutObject->{LanguageObject}->Translate( 'Sent new password to %s. Please check your email.', $UserData{UserEmail}, ); $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Login', Message => $Message, User => $User, MessageType => 'Success', ), ); return 1; } # create new customer account elsif ( $Param{Action} eq 'CustomerCreateAccount' ) { # new layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # check feature if ( !$ConfigObject->Get('CustomerPanelCreateAccount') ) { # show normal login $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Login', Message => Translatable('Feature not active!'), ), ); return; } # get params my %GetParams; for my $Entry ( @{ $ConfigObject->Get('CustomerUser')->{Map} } ) { $GetParams{ $Entry->[0] } = $ParamObject->GetParam( Param => $Entry->[1] ) || ''; } $GetParams{ValidID} = 1; # check needed params if ( !$GetParams{UserCustomerID} ) { $GetParams{UserCustomerID} = $GetParams{UserEmail}; } if ( !$GetParams{UserLogin} ) { $GetParams{UserLogin} = $GetParams{UserEmail}; } # get new password $GetParams{UserPassword} = $UserObject->GenerateRandomPassword(); # get user data my %UserData = $UserObject->CustomerUserDataGet( User => $GetParams{UserLogin} ); if ( $UserData{UserID} || !$GetParams{UserLogin} ) { # send data to JS $LayoutObject->AddJSData( Key => 'SignupError', Value => 1, ); $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Login', Message => Translatable('This e-mail address already exists. Please log in or reset your password.'), UserTitle => $GetParams{UserTitle}, UserFirstname => $GetParams{UserFirstname}, UserLastname => $GetParams{UserLastname}, UserEmail => $GetParams{UserEmail}, ), ); return; } # check for mail address restrictions my @Whitelist = @{ $ConfigObject->Get('CustomerPanelCreateAccount::MailRestrictions::Whitelist') // [] }; my @Blacklist = @{ $ConfigObject->Get('CustomerPanelCreateAccount::MailRestrictions::Blacklist') // [] }; my $WhitelistMatched; for my $WhitelistEntry (@Whitelist) { my $Regex = eval {qr/$WhitelistEntry/i}; if ($@) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => $LayoutObject->{LanguageObject}->Translate( 'The customer panel mail address whitelist contains the invalid regular expression $WhitelistEntry, please check and correct it.' ), ); } elsif ( $GetParams{UserEmail} =~ $Regex ) { $WhitelistMatched++; } } my $BlacklistMatched; for my $BlacklistEntry (@Blacklist) { my $Regex = eval {qr/$BlacklistEntry/i}; if ($@) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => $LayoutObject->{LanguageObject}->Translate( 'The customer panel mail address blacklist contains the invalid regular expression $BlacklistEntry, please check and correct it.' ), ); } elsif ( $GetParams{UserEmail} =~ $Regex ) { $BlacklistMatched++; } } if ( ( @Whitelist && !$WhitelistMatched ) || ( @Blacklist && $BlacklistMatched ) ) { # send data to JS $LayoutObject->AddJSData( Key => 'SignupError', Value => 1, ); $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Login', Message => Translatable('This email address is not allowed to register. Please contact support staff.'), UserTitle => $GetParams{UserTitle}, UserFirstname => $GetParams{UserFirstname}, UserLastname => $GetParams{UserLastname}, UserEmail => $GetParams{UserEmail}, ), ); return; } # create account my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); my $Now = $DateTimeObject->ToString(); my $Add = $UserObject->CustomerUserAdd( %GetParams, Comment => $LayoutObject->{LanguageObject}->Translate( 'Added via Customer Panel (%s)', $Now ), ValidID => 1, UserID => $ConfigObject->Get('CustomerPanelUserID'), ); if ( !$Add ) { # send data to JS $LayoutObject->AddJSData( Key => 'SignupError', Value => 1, ); $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Login', Message => Translatable('Customer user can\'t be added!'), UserTitle => $GetParams{UserTitle}, UserFirstname => $GetParams{UserFirstname}, UserLastname => $GetParams{UserLastname}, UserEmail => $GetParams{UserEmail}, ), ); return; } # send notify email my $EmailObject = Kernel::System::Email->new( %{$Self} ); my $Body = $ConfigObject->Get('CustomerPanelBodyNewAccount') || 'No Config Option found!'; my $Subject = $ConfigObject->Get('CustomerPanelSubjectNewAccount') || 'New OTRS Account!'; for ( sort keys %GetParams ) { $Body =~ s//$GetParams{$_}/gi; } # send account info my $Sent = $EmailObject->Send( To => $GetParams{UserEmail}, Subject => $Subject, Charset => $LayoutObject->{UserCharset}, MimeType => 'text/plain', Body => $Body ); if ( !$Sent->{Success} ) { my $Output = $LayoutObject->CustomerHeader( Area => 'Core', Title => 'Error' ); $Output .= $LayoutObject->CustomerWarning( Comment => Translatable('Can\'t send account info!') ); $Output .= $LayoutObject->CustomerFooter(); $LayoutObject->Print( Output => \$Output ); return; } # show sent account info if ( $ConfigObject->Get('CustomerPanelLoginURL') ) { # redirect to alternate login $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); print $LayoutObject->Redirect( ExtURL => $ConfigObject->Get('CustomerPanelLoginURL') . "?RequestedURL=$Param{RequestedURL};User=$GetParams{UserLogin};" . "Email=$GetParams{UserEmail};Reason=NewAccountCreated", ); return 1; } my $AccountCreatedMessage = $LayoutObject->{LanguageObject}->Translate( 'New account created. Sent login information to %s. Please check your email.', $GetParams{UserEmail}, ); # login screen $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Login', Message => $AccountCreatedMessage, User => $GetParams{UserLogin}, MessageType => 'Success', ), ); return 1; } # show login site elsif ( !$Param{SessionID} ) { # new layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # create AuthObject my $AuthObject = $Kernel::OM->Get('Kernel::System::CustomerAuth'); if ( $AuthObject->GetOption( What => 'PreAuth' ) ) { # automatic login $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); print $LayoutObject->Redirect( OP => "Action=PreLogin;RequestedURL=$Param{RequestedURL}", ); return; } elsif ( $ConfigObject->Get('CustomerPanelLoginURL') ) { # redirect to alternate login $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); print $LayoutObject->Redirect( ExtURL => $ConfigObject->Get('CustomerPanelLoginURL') . "?RequestedURL=$Param{RequestedURL}", ); return; } # login screen $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Login', %Param, ), ); return 1; } # run modules if a version value exists elsif ( $Kernel::OM->Get('Kernel::System::Main')->Require("Kernel::Modules::$Param{Action}") ) { # check session id if ( !$SessionObject->CheckSessionID( SessionID => $Param{SessionID} ) ) { # create new LayoutObject with new '%Param' $Kernel::OM->ObjectParamAdd( 'Kernel::Output::HTML::Layout' => { SetCookies => { SessionIDCookie => $ParamObject->SetCookie( Key => $Param{SessionName}, Value => '', Expires => '-1y', Path => $ConfigObject->Get('ScriptAlias'), Secure => scalar $CookieSecureAttribute, HTTPOnly => 1, ), }, %Param, } ); $Kernel::OM->ObjectsDiscard( Objects => ['Kernel::Output::HTML::Layout'] ); my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # create AuthObject my $AuthObject = $Kernel::OM->Get('Kernel::System::CustomerAuth'); if ( $AuthObject->GetOption( What => 'PreAuth' ) ) { # automatic re-login $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); print $LayoutObject->Redirect( OP => "?Action=PreLogin&RequestedURL=$Param{RequestedURL}", ); return; } # redirect to alternate login elsif ( $ConfigObject->Get('CustomerPanelLoginURL') ) { # redirect to alternate login $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); print $LayoutObject->Redirect( ExtURL => $ConfigObject->Get('CustomerPanelLoginURL') . "?Reason=InvalidSessionID;RequestedURL=$Param{RequestedURL}", ); return; } # show login $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Login', Message => $LayoutObject->{LanguageObject}->Translate( $SessionObject->SessionIDErrorMessage() ), %Param, ), ); return; } # get session data my %UserData = $SessionObject->GetSessionIDData( SessionID => $Param{SessionID}, ); # check needed data if ( !$UserData{UserID} || !$UserData{UserLogin} || $UserData{UserType} ne 'Customer' ) { my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # redirect to alternate login if ( $ConfigObject->Get('CustomerPanelLoginURL') ) { print $LayoutObject->Redirect( ExtURL => $ConfigObject->Get('CustomerPanelLoginURL') . "?Reason=SystemError", ); return; } # show login screen $LayoutObject->Print( Output => \$LayoutObject->CustomerLogin( Title => 'Error', Message => Translatable('Error: invalid session.'), %Param, ), ); return; } # module registry my $ModuleReg = $ConfigObject->Get('CustomerFrontend::Module')->{ $Param{Action} }; if ( !$ModuleReg ) { # new layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Module Kernel::Modules::$Param{Action} not registered in Kernel/Config.pm!", ); $LayoutObject->CustomerFatalError( Comment => Translatable('Please contact the administrator.'), ); return; } # module permission check for action if ( ref $ModuleReg->{GroupRo} eq 'ARRAY' && !scalar @{ $ModuleReg->{GroupRo} } && ref $ModuleReg->{Group} eq 'ARRAY' && !scalar @{ $ModuleReg->{Group} } ) { $Param{AccessRo} = 1; $Param{AccessRw} = 1; } else { ( $Param{AccessRo}, $Param{AccessRw} ) = $Self->_CheckModulePermission( ModuleReg => $ModuleReg, %UserData, ); if ( !$Param{AccessRo} ) { # new layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => 'No Permission to use this frontend action module!' ); $LayoutObject->CustomerFatalError( Comment => Translatable('Please contact the administrator.'), ); return; } } my $NavigationConfig = $ConfigObject->Get('CustomerFrontend::Navigation')->{ $Param{Action} }; # module permission check for submenu item if ( IsHashRefWithData($NavigationConfig) ) { KEY: for my $Key ( sort keys %{$NavigationConfig} ) { next KEY if $Key !~ m/^\d+/i; next KEY if $Param{RequestedURL} !~ m/Subaction/i; my @ModuleNavigationConfigs; # FIXME: Support both old (HASH) and new (ARRAY of HASH) navigation configurations, for reasons of # backwards compatibility. Once we are sure everything has been migrated correctly, support for # HASH-only configuration can be dropped in future major release. if ( IsHashRefWithData( $NavigationConfig->{$Key} ) ) { push @ModuleNavigationConfigs, $NavigationConfig->{$Key}; } elsif ( IsArrayRefWithData( $NavigationConfig->{$Key} ) ) { push @ModuleNavigationConfigs, @{ $NavigationConfig->{$Key} }; } # Skip incompatible configuration. else { next KEY; } ITEM: for my $Item (@ModuleNavigationConfigs) { if ( $Item->{Link} =~ m/Subaction=/i && $Item->{Link} !~ m/$Param{Subaction}/i ) { next ITEM; } $Param{AccessRo} = 0; $Param{AccessRw} = 0; # module permission check for submenu item if ( ref $Item->{GroupRo} eq 'ARRAY' && !scalar @{ $Item->{GroupRo} } && ref $Item->{Group} eq 'ARRAY' && !scalar @{ $Item->{Group} } ) { $Param{AccessRo} = 1; $Param{AccessRw} = 1; } else { ( $Param{AccessRo}, $Param{AccessRw} ) = $Self->_CheckModulePermission( ModuleReg => $Item, %UserData, ); if ( !$Param{AccessRo} ) { # new layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => 'No Permission to use this frontend subaction module!' ); $LayoutObject->CustomerFatalError( Comment => Translatable('Please contact the administrator.') ); return; } } } } } # create new LayoutObject with new '%Param' and '%UserData' $Kernel::OM->ObjectParamAdd( 'Kernel::Output::HTML::Layout' => { %Param, %UserData, ModuleReg => $ModuleReg, }, ); $Kernel::OM->ObjectsDiscard( Objects => ['Kernel::Output::HTML::Layout'] ); my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # update last request time if ( !$ParamObject->IsAJAXRequest() || $Param{Action} eq 'CustomerVideoChat' ) { my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); $SessionObject->UpdateSessionID( SessionID => $Param{SessionID}, Key => 'UserLastRequest', Value => $DateTimeObject->ToEpoch(), ); } # pre application module my $PreModule = $ConfigObject->Get('CustomerPanelPreApplicationModule'); if ($PreModule) { my %PreModuleList; if ( ref $PreModule eq 'HASH' ) { %PreModuleList = %{$PreModule}; } else { $PreModuleList{Init} = $PreModule; } MODULE: for my $PreModuleKey ( sort keys %PreModuleList ) { my $PreModule = $PreModuleList{$PreModuleKey}; next MODULE if !$PreModule; next MODULE if !$Kernel::OM->Get('Kernel::System::Main')->Require($PreModule); # debug info if ( $Self->{Debug} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'debug', Message => "CustomerPanelPreApplication module $PreModule is used.", ); } # use module my $PreModuleObject = $PreModule->new( %Param, %UserData, ); my $Output = $PreModuleObject->PreRun(); if ($Output) { $LayoutObject->Print( Output => \$Output ); return 1; } } } # debug info if ( $Self->{Debug} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'debug', Message => 'Kernel::Modules::' . $Param{Action} . '->new', ); } my $FrontendObject = ( 'Kernel::Modules::' . $Param{Action} )->new( %Param, %UserData, ModuleReg => $ModuleReg, Debug => $Self->{Debug}, ); # debug info if ( $Self->{Debug} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'debug', Message => 'Kernel::Modules::' . $Param{Action} . '->run', ); } # ->Run $Action with $FrontendObject $LayoutObject->Print( Output => \$FrontendObject->Run() ); # log request time if ( $ConfigObject->Get('PerformanceLog') ) { if ( ( !$QueryString && $Param{Action} ) || $QueryString !~ /Action=/ ) { $QueryString = 'Action=' . $Param{Action} . ';Subaction=' . $Param{Subaction}; } my $File = $ConfigObject->Get('PerformanceLog::File'); ## no critic if ( open my $Out, '>>', $File ) { ## use critic print $Out time() . '::Customer::' . ( time() - $Self->{PerformanceLogStart} ) . "::$UserData{UserLogin}::$QueryString\n"; close $Out; $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'debug', Message => 'Response::Customer: ' . ( time() - $Self->{PerformanceLogStart} ) . "s taken (URL:$QueryString:$UserData{UserLogin})", ); } else { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Can't write $File: $!", ); } } return 1; } # print an error screen my %Data = $SessionObject->GetSessionIDData( SessionID => $Param{SessionID}, ); $Kernel::OM->ObjectParamAdd( 'Kernel::Output::HTML::Layout' => { %Param, %Data, }, ); my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); $LayoutObject->CustomerFatalError( Comment => Translatable('Please contact the administrator.'), ); return; } =begin Internal: =head2 _CheckModulePermission() module permission check ($AccessRo, $AccessRw = $AutoResponseObject->_CheckModulePermission( ModuleReg => $ModuleReg, %UserData, ); =cut sub _CheckModulePermission { my ( $Self, %Param ) = @_; my $AccessRo = 0; my $AccessRw = 0; PERMISSION: for my $Permission (qw(GroupRo Group)) { my $AccessOk = 0; my $Group = $Param{ModuleReg}->{$Permission}; next PERMISSION if !$Group; my $GroupObject = $Kernel::OM->Get('Kernel::System::CustomerGroup'); if ( IsArrayRefWithData($Group) ) { GROUP: for my $Item ( @{$Group} ) { next GROUP if !$Item; next GROUP if !$GroupObject->PermissionCheck( UserID => $Param{UserID}, GroupName => $Item, Type => $Permission eq 'GroupRo' ? 'ro' : 'rw', ); $AccessOk = 1; last GROUP; } } else { my $HasPermission = $GroupObject->PermissionCheck( UserID => $Param{UserID}, GroupName => $Group, Type => $Permission eq 'GroupRo' ? 'ro' : 'rw', ); if ($HasPermission) { $AccessOk = 1; } } if ( $Permission eq 'Group' && $AccessOk ) { $AccessRo = 1; $AccessRw = 1; } elsif ( $Permission eq 'GroupRo' && $AccessOk ) { $AccessRo = 1; } } return ( $AccessRo, $AccessRw ); } =end Internal: =cut sub DESTROY { my $Self = shift; # debug info if ( $Self->{Debug} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'debug', Message => 'Global handle stopped.', ); } return 1; } # CAS Custom =item Check if it is a CAS logout - if true, logs out user for the session ticket =cut sub CASLogout { my $Self = shift; # get post data my $cgi = new CGI; my $request = uri_unescape($cgi->query_string()); # check if it is CAS logout if ($request =~ /(logoutRequest=([^<]+)/); if (! $ticket) { return 1; } else { } my $DBObject = $Kernel::OM->Get("Kernel::System::DB"); # get login for session my $sqlLogin = 'SELECT UserLogin from cas_session where Ticket=?'; $DBObject->Prepare(SQL => $sqlLogin, Bind => [\$ticket]); my $login; while (my @Row = $DBObject->FetchrowArray()) { $login = $Row[0]; } # if a login was found, kill the user session if ($login) { $DBObject->Do(SQL => "DELETE FROM sessions WHERE session_id IN (select session_id from (select session_id FROM sessions WHERE data_key='UserLogin' AND data_value=?) as x)", Bind => [\$login]); } else { } return 1; } else { return 0; } } # CAS Custom 1; =head1 TERMS AND CONDITIONS This software is part of the OTRS project (L). This software comes with ABSOLUTELY NO WARRANTY. For details, see the enclosed file COPYING for license information (AGPL). If you did not receive this file, see L. =cut