Commit 9c6523ef39f49340ca3efd6bd5ddd868473baed3
1 parent
023f8cd9
Exists in
master
Support for OTRS 6.0.1
Showing
11 changed files
with
3774 additions
and
0 deletions
Show diff stats
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +dist/*.opm | ... | ... |
| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<projectDescription> | |
| 3 | + <name>otrs-cas</name> | |
| 4 | + <comment></comment> | |
| 5 | + <projects> | |
| 6 | + <project>otrs</project> | |
| 7 | + </projects> | |
| 8 | + <buildSpec> | |
| 9 | + <buildCommand> | |
| 10 | + <name>org.epic.perleditor.perlbuilder</name> | |
| 11 | + <arguments> | |
| 12 | + </arguments> | |
| 13 | + </buildCommand> | |
| 14 | + </buildSpec> | |
| 15 | + <natures> | |
| 16 | + <nature>org.epic.perleditor.perlnature</nature> | |
| 17 | + </natures> | |
| 18 | +</projectDescription> | ... | ... |
| ... | ... | @@ -0,0 +1,56 @@ |
| 1 | +<?xml version="1.0" encoding="utf-8" ?> | |
| 2 | +<otrs_package version="1.0"> | |
| 3 | + <Name>CASAuthentication</Name> | |
| 4 | + <Version>1.3.0</Version> | |
| 5 | + <Framework>6.0.1</Framework> | |
| 6 | + <Vendor>Rodrigo Gonçalves (rodrigo@goncalves.pro.br)</Vendor> | |
| 7 | + <URL>http://www.goncalves.pro.br/</URL> | |
| 8 | + <License>AGPL</License> | |
| 9 | + | |
| 10 | + <ChangeLog Version="1.0.0" Date="2014-06-01">First Version</ChangeLog> | |
| 11 | + <ChangeLog Version="1.1.0" Date="2014-01-05">Support for OTRS 4</ChangeLog> | |
| 12 | + <ChangeLog Version="1.1.0" Date="2014-01-05">Bug in customer authentication for OTRS 4</ChangeLog> | |
| 13 | + <ChangeLog Version="1.1.8" Date="2014-02-03">Support for users not in database (custom redirect)</ChangeLog> | |
| 14 | + <ChangeLog Version="1.1.9" Date="2015-11-16">Fix for authentication with redirect URL</ChangeLog> | |
| 15 | + <ChangeLog Version="1.2.0" Date="2016-01-18">Support for OTRS 5.0.x and fix for agent redirection on first link</ChangeLog> | |
| 16 | + <ChangeLog Version="1.2.0" Date="2017-12-14">Support for OTRS 6.0.1</ChangeLog> | |
| 17 | + | |
| 18 | + <Description>CAS Authentication (Jasig) Module</Description> | |
| 19 | + | |
| 20 | + <BuildDate>?</BuildDate> | |
| 21 | + <BuildHost>?</BuildHost> | |
| 22 | + | |
| 23 | + <IntroInstall Type="post" Title="Thank you"><![CDATA[ | |
| 24 | + Module installed successfully!<BR/><BR/> | |
| 25 | + | |
| 26 | + In order to active the module, the following lines should be included/changed in Kernel/Config.pm:<BR/><BR/> | |
| 27 | + | |
| 28 | + $Self->{'AuthModule'} = 'Kernel::System::Auth::CAS';<BR/> | |
| 29 | + $Self->{'AuthModule::CAS::Gateway'} = 0;<BR/> | |
| 30 | + $Self->{'AuthModule::CAS::ServiceUrl'} = 'URL FOR OTRS AGENT INDEX (ex: https://host.com/otrs/index.pl)';<BR/> | |
| 31 | + $Self->{'AuthModule::CAS::CASUrl'} = 'URL FOR CAS SERVICE (WITHOUT TRAILING /) - ex: https://cas.systems.com';<BR/> | |
| 32 | +<BR/> | |
| 33 | + $Self->{'Customer::AuthModule'} = 'Kernel::System::CustomerAuth::CAS';<BR/> | |
| 34 | + $Self->{'Customer::AuthModule::CAS::Gateway'} = 0;<BR/> | |
| 35 | + $Self->{'Customer::AuthModule::CAS::ServiceUrl'} = 'URL FOR OTRS CUSTOMER INDEX (ex: https://host.com/otrs/customer.pl)';<BR/> | |
| 36 | + $Self->{'Customer::AuthModule::CAS::CASUrl'} = 'URL FOR CAS SERVICE (WITHOUT TRAILING /) - ex: https://cas.systems.com';<BR/> | |
| 37 | + | |
| 38 | + ]]></IntroInstall> | |
| 39 | + | |
| 40 | + <ModuleRequired>AuthCAS</ModuleRequired> | |
| 41 | + | |
| 42 | + <Filelist> | |
| 43 | + <File Permission="644" Location="Kernel/System/Auth/CAS.pm"></File> | |
| 44 | + <File Permission="644" Location="Kernel/System/CustomerAuth/CAS.pm"></File> | |
| 45 | + <File Permission="644" Location="Custom/Kernel/System/Web/InterfaceAgent.pm"></File> | |
| 46 | + <File Permission="644" Location="Custom/Kernel/System/Web/InterfaceCustomer.pm"></File> | |
| 47 | + <File Permission="644" Location="Custom/Kernel/System/Web/Request.pm"></File> | |
| 48 | + </Filelist> | |
| 49 | + | |
| 50 | + <DatabaseInstall> | |
| 51 | + <TableCreate Name="cas_session"> | |
| 52 | + <Column Name="UserLogin" Required="true" Size="100" Type="VARCHAR" /> | |
| 53 | + <Column Name="Ticket" Required="true" Size="250" Type="VARCHAR" /> | |
| 54 | + </TableCreate> | |
| 55 | + </DatabaseInstall> | |
| 56 | +</otrs_package> | ... | ... |
| ... | ... | @@ -0,0 +1,1242 @@ |
| 1 | +# -- | |
| 2 | +# Copyright (C) 2001-2017 OTRS AG, http://otrs.com/ | |
| 3 | +# -- | |
| 4 | +# This software comes with ABSOLUTELY NO WARRANTY. For details, see | |
| 5 | +# the enclosed file COPYING for license information (AGPL). If you | |
| 6 | +# did not receive this file, see http://www.gnu.org/licenses/agpl.txt. | |
| 7 | +# | |
| 8 | +# | |
| 9 | +# Custom version for CAS authentication - rodrigo@goncalves.pro.br | |
| 10 | +# | |
| 11 | +# Version 18/01/2016 - RG - Version for OTRS 5.0.6 | |
| 12 | +# Version 2017-12-07 - RG - Version for OTRS 6.0.1 | |
| 13 | +# | |
| 14 | +# -- | |
| 15 | + | |
| 16 | +package Kernel::System::Web::InterfaceAgent; | |
| 17 | + | |
| 18 | +use strict; | |
| 19 | +use warnings; | |
| 20 | + | |
| 21 | +# CAS Custom | |
| 22 | +use CGI; | |
| 23 | +use URI::Escape; | |
| 24 | +# CAS Custom | |
| 25 | + | |
| 26 | +use Kernel::Language qw(Translatable); | |
| 27 | +use Kernel::System::DateTime; | |
| 28 | + | |
| 29 | +our @ObjectDependencies = ( | |
| 30 | + 'Kernel::Config', | |
| 31 | + 'Kernel::Output::HTML::Layout', | |
| 32 | + 'Kernel::System::Auth', | |
| 33 | + 'Kernel::System::AuthSession', | |
| 34 | + 'Kernel::System::DB', | |
| 35 | + 'Kernel::System::Email', | |
| 36 | + 'Kernel::System::Group', | |
| 37 | + 'Kernel::System::Log', | |
| 38 | + 'Kernel::System::Main', | |
| 39 | + 'Kernel::System::Scheduler', | |
| 40 | + 'Kernel::System::DateTime', | |
| 41 | + 'Kernel::System::User', | |
| 42 | + 'Kernel::System::Web::Request', | |
| 43 | + 'Kernel::System::Valid', | |
| 44 | +); | |
| 45 | + | |
| 46 | +=head1 NAME | |
| 47 | + | |
| 48 | +Kernel::System::Web::InterfaceAgent - the agent web interface | |
| 49 | + | |
| 50 | +=head1 DESCRIPTION | |
| 51 | + | |
| 52 | +the global agent web interface (authentication, session handling, ...) | |
| 53 | + | |
| 54 | +=head1 PUBLIC INTERFACE | |
| 55 | + | |
| 56 | +=head2 new() | |
| 57 | + | |
| 58 | +create agent web interface object. Do not use it directly, instead use: | |
| 59 | + | |
| 60 | + use Kernel::System::ObjectManager; | |
| 61 | + my $Debug = 0, | |
| 62 | + local $Kernel::OM = Kernel::System::ObjectManager->new( | |
| 63 | + 'Kernel::System::Web::InterfaceAgent' => { | |
| 64 | + Debug => 0, | |
| 65 | + WebRequest => CGI::Fast->new(), # optional, e. g. if fast cgi is used, | |
| 66 | + # the CGI object is already provided | |
| 67 | + } | |
| 68 | + ); | |
| 69 | + my $InterfaceAgent = $Kernel::OM->Get('Kernel::System::Web::InterfaceAgent'); | |
| 70 | + | |
| 71 | +=cut | |
| 72 | + | |
| 73 | +sub new { | |
| 74 | + my ( $Type, %Param ) = @_; | |
| 75 | + my $Self = {}; | |
| 76 | + bless( $Self, $Type ); | |
| 77 | + | |
| 78 | + # Performance log | |
| 79 | + $Self->{PerformanceLogStart} = time(); | |
| 80 | + | |
| 81 | + # get debug level | |
| 82 | + $Self->{Debug} = $Param{Debug} || 0; | |
| 83 | + | |
| 84 | + $Kernel::OM->ObjectParamAdd( | |
| 85 | + 'Kernel::System::Log' => { | |
| 86 | + LogPrefix => $Kernel::OM->Get('Kernel::Config')->Get('CGILogPrefix'), | |
| 87 | + }, | |
| 88 | + 'Kernel::System::Web::Request' => { | |
| 89 | + WebRequest => $Param{WebRequest} || 0, | |
| 90 | + }, | |
| 91 | + ); | |
| 92 | + | |
| 93 | + # debug info | |
| 94 | + if ( $Self->{Debug} ) { | |
| 95 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 96 | + Priority => 'debug', | |
| 97 | + Message => 'Global handle started...', | |
| 98 | + ); | |
| 99 | + } | |
| 100 | + | |
| 101 | + return $Self; | |
| 102 | +} | |
| 103 | + | |
| 104 | +=head2 Run() | |
| 105 | + | |
| 106 | +execute the object | |
| 107 | + | |
| 108 | + $InterfaceAgent->Run(); | |
| 109 | + | |
| 110 | +=cut | |
| 111 | + | |
| 112 | +sub Run { | |
| 113 | + my $Self = shift; | |
| 114 | + | |
| 115 | + # CAS Custom | |
| 116 | + if ($Self->CASLogout()) { | |
| 117 | + return; | |
| 118 | + } | |
| 119 | + # CAS Custom | |
| 120 | + | |
| 121 | + my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); | |
| 122 | + | |
| 123 | + my $QueryString = $ENV{QUERY_STRING} || ''; | |
| 124 | + | |
| 125 | + # Check if https forcing is active, and redirect if needed. | |
| 126 | + if ( $ConfigObject->Get('HTTPSForceRedirect') ) { | |
| 127 | + | |
| 128 | + # Some web servers do not set HTTPS environment variable, so it's not possible to easily know if we are using | |
| 129 | + # https protocol. Look also for similarly named keys in environment hash, since this should prevent loops in | |
| 130 | + # certain cases. | |
| 131 | + if ( | |
| 132 | + ( | |
| 133 | + !defined $ENV{HTTPS} | |
| 134 | + && !grep {/^HTTPS(?:_|$)/} keys %ENV | |
| 135 | + ) | |
| 136 | + || $ENV{HTTPS} ne 'on' | |
| 137 | + ) | |
| 138 | + { | |
| 139 | + my $Host = $ENV{HTTP_HOST} || $ConfigObject->Get('FQDN'); | |
| 140 | + | |
| 141 | + # Redirect with 301 code. Add two new lines at the end, so HTTP headers are validated correctly. | |
| 142 | + print "Status: 301 Moved Permanently\nLocation: https://$Host$ENV{REQUEST_URI}\n\n"; | |
| 143 | + return; | |
| 144 | + } | |
| 145 | + } | |
| 146 | + | |
| 147 | + my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); | |
| 148 | + | |
| 149 | + my %Param; | |
| 150 | + | |
| 151 | + # get session id | |
| 152 | + $Param{SessionName} = $ConfigObject->Get('SessionName') || 'SessionID'; | |
| 153 | + $Param{SessionID} = $ParamObject->GetParam( Param => $Param{SessionName} ) || ''; | |
| 154 | + | |
| 155 | + # drop old session id (if exists) | |
| 156 | + $QueryString =~ s/(\?|&|;|)$Param{SessionName}(=&|=;|=.+?&|=.+?$)/;/g; | |
| 157 | + | |
| 158 | + # define framework params | |
| 159 | + my $FrameworkParams = { | |
| 160 | + Lang => '', | |
| 161 | + Action => '', | |
| 162 | + Subaction => '', | |
| 163 | + RequestedURL => $QueryString, | |
| 164 | + }; | |
| 165 | + for my $Key ( sort keys %{$FrameworkParams} ) { | |
| 166 | + $Param{$Key} = $ParamObject->GetParam( Param => $Key ) | |
| 167 | + || $FrameworkParams->{$Key}; | |
| 168 | + } | |
| 169 | + | |
| 170 | + # validate language | |
| 171 | + if ( $Param{Lang} && $Param{Lang} !~ m{\A[a-z]{2}(?:_[A-Z]{2})?\z}xms ) { | |
| 172 | + delete $Param{Lang}; | |
| 173 | + } | |
| 174 | + | |
| 175 | + # check if the browser sends the SessionID cookie and set the SessionID-cookie | |
| 176 | + # as SessionID! GET or POST SessionID have the lowest priority. | |
| 177 | + my $BrowserHasCookie = 0; | |
| 178 | + if ( $ConfigObject->Get('SessionUseCookie') ) { | |
| 179 | + $Param{SessionIDCookie} = $ParamObject->GetCookie( Key => $Param{SessionName} ); | |
| 180 | + if ( $Param{SessionIDCookie} ) { | |
| 181 | + $Param{SessionID} = $Param{SessionIDCookie}; | |
| 182 | + } | |
| 183 | + } | |
| 184 | + | |
| 185 | + $Kernel::OM->ObjectParamAdd( | |
| 186 | + 'Kernel::Output::HTML::Layout' => { | |
| 187 | + Lang => $Param{Lang}, | |
| 188 | + UserLanguage => $Param{Lang}, | |
| 189 | + }, | |
| 190 | + 'Kernel::Language' => { | |
| 191 | + UserLanguage => $Param{Lang} | |
| 192 | + }, | |
| 193 | + ); | |
| 194 | + | |
| 195 | + my $CookieSecureAttribute; | |
| 196 | + if ( $ConfigObject->Get('HttpType') eq 'https' ) { | |
| 197 | + | |
| 198 | + # Restrict Cookie to HTTPS if it is used. | |
| 199 | + $CookieSecureAttribute = 1; | |
| 200 | + } | |
| 201 | + | |
| 202 | + my $DBCanConnect = $Kernel::OM->Get('Kernel::System::DB')->Connect(); | |
| 203 | + | |
| 204 | + if ( !$DBCanConnect || $ParamObject->Error() ) { | |
| 205 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 206 | + if ( !$DBCanConnect ) { | |
| 207 | + $LayoutObject->FatalError( | |
| 208 | + Comment => Translatable('Please contact the administrator.'), | |
| 209 | + ); | |
| 210 | + return; | |
| 211 | + } | |
| 212 | + if ( $ParamObject->Error() ) { | |
| 213 | + $LayoutObject->FatalError( | |
| 214 | + Message => $ParamObject->Error(), | |
| 215 | + Comment => Translatable('Please contact the administrator.'), | |
| 216 | + ); | |
| 217 | + return; | |
| 218 | + } | |
| 219 | + } | |
| 220 | + | |
| 221 | + # get common application and add-on application params | |
| 222 | + my %CommonObjectParam = %{ $ConfigObject->Get('Frontend::CommonParam') }; | |
| 223 | + for my $Key ( sort keys %CommonObjectParam ) { | |
| 224 | + $Param{$Key} = $ParamObject->GetParam( Param => $Key ) || $CommonObjectParam{$Key}; | |
| 225 | + } | |
| 226 | + | |
| 227 | + # security check Action Param (replace non word chars) | |
| 228 | + $Param{Action} =~ s/\W//g; | |
| 229 | + | |
| 230 | + my $SessionObject = $Kernel::OM->Get('Kernel::System::AuthSession'); | |
| 231 | + my $UserObject = $Kernel::OM->Get('Kernel::System::User'); | |
| 232 | + | |
| 233 | + # check request type | |
| 234 | + if ( $Param{Action} eq 'PreLogin' ) { | |
| 235 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 236 | + $Param{RequestedURL} = $Param{RequestedURL} || "Action=AgentDashboard"; | |
| 237 | + | |
| 238 | + # login screen | |
| 239 | + $LayoutObject->Print( | |
| 240 | + Output => \$LayoutObject->Login( | |
| 241 | + Title => 'Login', | |
| 242 | + Mode => 'PreLogin', | |
| 243 | + %Param, | |
| 244 | + ), | |
| 245 | + ); | |
| 246 | + | |
| 247 | + return; | |
| 248 | + } | |
| 249 | + elsif ( $Param{Action} eq 'Login' ) { | |
| 250 | + | |
| 251 | + # get params | |
| 252 | + my $PostUser = $ParamObject->GetParam( Param => 'User' ) || ''; | |
| 253 | + my $PostPw = $ParamObject->GetParam( | |
| 254 | + Param => 'Password', | |
| 255 | + Raw => 1 | |
| 256 | + ) || ''; | |
| 257 | + my $PostTwoFactorToken = $ParamObject->GetParam( | |
| 258 | + Param => 'TwoFactorToken', | |
| 259 | + Raw => 1 | |
| 260 | + ) || ''; | |
| 261 | + | |
| 262 | + # create AuthObject | |
| 263 | + my $AuthObject = $Kernel::OM->Get('Kernel::System::Auth'); | |
| 264 | + | |
| 265 | + # check submitted data | |
| 266 | + my $User = $AuthObject->Auth( | |
| 267 | + User => $PostUser, | |
| 268 | + Pw => $PostPw, | |
| 269 | + TwoFactorToken => $PostTwoFactorToken, | |
| 270 | + RequestedURL => $Param{RequestedURL}, | |
| 271 | + ); | |
| 272 | + | |
| 273 | + # login is invalid | |
| 274 | + if ( !$User ) { | |
| 275 | + | |
| 276 | + # CAS Custom | |
| 277 | + # Fixes OTRS Layout bug when redirecting | |
| 278 | + my $cgi = new CGI(); | |
| 279 | + print $cgi->redirect( -URL => $ConfigObject->Get("AuthModule::CAS::CASUrl") . "/login?service=" . $ConfigObject->Get("AuthModule::CAS::ServiceUrl") . "?" . $Param{RequestedURL}); | |
| 280 | + return; | |
| 281 | + # CAS Custom | |
| 282 | + | |
| 283 | + my $Expires = '+' . $ConfigObject->Get('SessionMaxTime') . 's'; | |
| 284 | + if ( !$ConfigObject->Get('SessionUseCookieAfterBrowserClose') ) { | |
| 285 | + $Expires = ''; | |
| 286 | + } | |
| 287 | + | |
| 288 | + $Kernel::OM->ObjectParamAdd( | |
| 289 | + 'Kernel::Output::HTML::Layout' => { | |
| 290 | + SetCookies => { | |
| 291 | + OTRSBrowserHasCookie => $ParamObject->SetCookie( | |
| 292 | + Key => 'OTRSBrowserHasCookie', | |
| 293 | + Value => 1, | |
| 294 | + Expires => $Expires, | |
| 295 | + Path => $ConfigObject->Get('ScriptAlias'), | |
| 296 | + Secure => $CookieSecureAttribute, | |
| 297 | + HTTPOnly => 1, | |
| 298 | + ), | |
| 299 | + }, | |
| 300 | + } | |
| 301 | + ); | |
| 302 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 303 | + | |
| 304 | + # redirect to alternate login | |
| 305 | + if ( $ConfigObject->Get('LoginURL') ) { | |
| 306 | + $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); | |
| 307 | + print $LayoutObject->Redirect( | |
| 308 | + ExtURL => $ConfigObject->Get('LoginURL') | |
| 309 | + . "?Reason=LoginFailed&RequestedURL=$Param{RequestedURL}", | |
| 310 | + ); | |
| 311 | + return; | |
| 312 | + } | |
| 313 | + | |
| 314 | + # show normal login | |
| 315 | + $LayoutObject->Print( | |
| 316 | + Output => \$LayoutObject->Login( | |
| 317 | + Title => 'Login', | |
| 318 | + Message => $Kernel::OM->Get('Kernel::System::Log')->GetLogEntry( | |
| 319 | + Type => 'Info', | |
| 320 | + What => 'Message', | |
| 321 | + ) | |
| 322 | + || $LayoutObject->{LanguageObject}->Translate( $AuthObject->GetLastErrorMessage() ) | |
| 323 | + || Translatable('Login failed! Your user name or password was entered incorrectly.'), | |
| 324 | + LoginFailed => 1, | |
| 325 | + MessageType => 'Error', | |
| 326 | + User => $User, | |
| 327 | + %Param, | |
| 328 | + ), | |
| 329 | + ); | |
| 330 | + return; | |
| 331 | + } | |
| 332 | + | |
| 333 | + # login is successful | |
| 334 | + my %UserData = $UserObject->GetUserData( | |
| 335 | + User => $User, | |
| 336 | + Valid => 1 | |
| 337 | + ); | |
| 338 | + | |
| 339 | + # check if the browser supports cookies | |
| 340 | + if ( $ParamObject->GetCookie( Key => 'OTRSBrowserHasCookie' ) ) { | |
| 341 | + $Kernel::OM->ObjectParamAdd( | |
| 342 | + 'Kernel::Output::HTML::Layout' => { | |
| 343 | + BrowserHasCookie => 1, | |
| 344 | + }, | |
| 345 | + ); | |
| 346 | + } | |
| 347 | + | |
| 348 | + # check needed data | |
| 349 | + if ( !$UserData{UserID} || !$UserData{UserLogin} ) { | |
| 350 | + | |
| 351 | + # redirect to alternate login | |
| 352 | + if ( $ConfigObject->Get('LoginURL') ) { | |
| 353 | + print $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Redirect( | |
| 354 | + ExtURL => $ConfigObject->Get('LoginURL') . '?Reason=SystemError', | |
| 355 | + ); | |
| 356 | + return; | |
| 357 | + } | |
| 358 | + | |
| 359 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 360 | + | |
| 361 | + # show need user data error message | |
| 362 | + $LayoutObject->Print( | |
| 363 | + Output => \$LayoutObject->Login( | |
| 364 | + Title => 'Error', | |
| 365 | + Message => | |
| 366 | + Translatable( | |
| 367 | + 'Authentication succeeded, but no user data record is found in the database. Please contact the administrator.' | |
| 368 | + ), | |
| 369 | + %Param, | |
| 370 | + MessageType => 'Error', | |
| 371 | + ), | |
| 372 | + ); | |
| 373 | + return; | |
| 374 | + } | |
| 375 | + | |
| 376 | + my $DateTimeObj = $Kernel::OM->Create('Kernel::System::DateTime'); | |
| 377 | + | |
| 378 | + # create new session id | |
| 379 | + my $NewSessionID = $SessionObject->CreateSessionID( | |
| 380 | + %UserData, | |
| 381 | + UserLastRequest => $DateTimeObj->ToEpoch(), | |
| 382 | + UserType => 'User', | |
| 383 | + SessionSource => 'AgentInterface', | |
| 384 | + ); | |
| 385 | + | |
| 386 | + # show error message if no session id has been created | |
| 387 | + if ( !$NewSessionID ) { | |
| 388 | + | |
| 389 | + # get error message | |
| 390 | + my $Error = $SessionObject->SessionIDErrorMessage() || ''; | |
| 391 | + | |
| 392 | + # output error message | |
| 393 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 394 | + $LayoutObject->Print( | |
| 395 | + Output => \$LayoutObject->Login( | |
| 396 | + Title => 'Login', | |
| 397 | + Message => $Error, | |
| 398 | + MessageType => 'Error', | |
| 399 | + %Param, | |
| 400 | + ), | |
| 401 | + ); | |
| 402 | + return; | |
| 403 | + } | |
| 404 | + | |
| 405 | + # execution in 20 seconds | |
| 406 | + my $ExecutionTimeObj = $DateTimeObj->Clone(); | |
| 407 | + $ExecutionTimeObj->Add( Seconds => 20 ); | |
| 408 | + my $ExecutionTime = $ExecutionTimeObj->ToString(); | |
| 409 | + | |
| 410 | + # add a asychronous executor scheduler task to count the concurrent user | |
| 411 | + $Kernel::OM->Get('Kernel::System::Scheduler')->TaskAdd( | |
| 412 | + ExecutionTime => $ExecutionTime, | |
| 413 | + Type => 'AsynchronousExecutor', | |
| 414 | + Name => 'PluginAsynchronous::ConcurrentUser', | |
| 415 | + MaximumParallelInstances => 1, | |
| 416 | + Data => { | |
| 417 | + Object => 'Kernel::System::SupportDataCollector::PluginAsynchronous::OTRS::ConcurrentUsers', | |
| 418 | + Function => 'RunAsynchronous', | |
| 419 | + }, | |
| 420 | + ); | |
| 421 | + | |
| 422 | + # get time zone | |
| 423 | + my $UserTimeZone = $UserData{UserTimeZone} || Kernel::System::DateTime->UserDefaultTimeZoneGet(); | |
| 424 | + $SessionObject->UpdateSessionID( | |
| 425 | + SessionID => $NewSessionID, | |
| 426 | + Key => 'UserTimeZone', | |
| 427 | + Value => $UserTimeZone, | |
| 428 | + ); | |
| 429 | + | |
| 430 | + # check if the time zone offset reported by the user's browser differs from that | |
| 431 | + # of the OTRS user's time zone offset | |
| 432 | + my $DateTimeObject = $Kernel::OM->Create( | |
| 433 | + 'Kernel::System::DateTime', | |
| 434 | + ObjectParams => { | |
| 435 | + TimeZone => $UserTimeZone, | |
| 436 | + }, | |
| 437 | + ); | |
| 438 | + my $OTRSUserTimeZoneOffset = $DateTimeObject->Format( Format => '%{offset}' ) / 60; | |
| 439 | + my $BrowserTimeZoneOffset = ( $ParamObject->GetParam( Param => 'TimeZoneOffset' ) || 0 ) * -1; | |
| 440 | + | |
| 441 | + # TimeZoneOffsetDifference contains the difference of the time zone offset between | |
| 442 | + # the user's OTRS time zone setting and the one reported by the user's browser. | |
| 443 | + # If there is a difference it can be evaluated later to e. g. show a message | |
| 444 | + # for the user to check his OTRS time zone setting. | |
| 445 | + my $UserTimeZoneOffsetDifference = abs( $OTRSUserTimeZoneOffset - $BrowserTimeZoneOffset ); | |
| 446 | + $SessionObject->UpdateSessionID( | |
| 447 | + SessionID => $NewSessionID, | |
| 448 | + Key => 'UserTimeZoneOffsetDifference', | |
| 449 | + Value => $UserTimeZoneOffsetDifference, | |
| 450 | + ); | |
| 451 | + | |
| 452 | + # create a new LayoutObject with SessionIDCookie | |
| 453 | + my $Expires = '+' . $ConfigObject->Get('SessionMaxTime') . 's'; | |
| 454 | + if ( !$ConfigObject->Get('SessionUseCookieAfterBrowserClose') ) { | |
| 455 | + $Expires = ''; | |
| 456 | + } | |
| 457 | + | |
| 458 | + my $SecureAttribute; | |
| 459 | + if ( $ConfigObject->Get('HttpType') eq 'https' ) { | |
| 460 | + | |
| 461 | + # Restrict Cookie to HTTPS if it is used. | |
| 462 | + $SecureAttribute = 1; | |
| 463 | + } | |
| 464 | + | |
| 465 | + $Kernel::OM->ObjectParamAdd( | |
| 466 | + 'Kernel::Output::HTML::Layout' => { | |
| 467 | + SetCookies => { | |
| 468 | + SessionIDCookie => $ParamObject->SetCookie( | |
| 469 | + Key => $Param{SessionName}, | |
| 470 | + Value => $NewSessionID, | |
| 471 | + Expires => $Expires, | |
| 472 | + Path => $ConfigObject->Get('ScriptAlias'), | |
| 473 | + Secure => scalar $CookieSecureAttribute, | |
| 474 | + HTTPOnly => 1, | |
| 475 | + ), | |
| 476 | + OTRSBrowserHasCookie => $ParamObject->SetCookie( | |
| 477 | + Key => 'OTRSBrowserHasCookie', | |
| 478 | + Value => '', | |
| 479 | + Expires => '-1y', | |
| 480 | + Path => $ConfigObject->Get('ScriptAlias'), | |
| 481 | + Secure => $CookieSecureAttribute, | |
| 482 | + HTTPOnly => 1, | |
| 483 | + ), | |
| 484 | + }, | |
| 485 | + SessionID => $NewSessionID, | |
| 486 | + SessionName => $Param{SessionName}, | |
| 487 | + }, | |
| 488 | + ); | |
| 489 | + | |
| 490 | + # Check if Chat is active | |
| 491 | + if ( $Kernel::OM->Get('Kernel::Config')->Get('ChatEngine::Active') ) { | |
| 492 | + my $ChatReceivingAgentsGroup | |
| 493 | + = $Kernel::OM->Get('Kernel::Config')->Get('ChatEngine::PermissionGroup::ChatReceivingAgents'); | |
| 494 | + | |
| 495 | + my $ChatReceivingAgentsGroupPermission = $Kernel::OM->Get('Kernel::System::Group')->PermissionCheck( | |
| 496 | + UserID => $UserData{UserID}, | |
| 497 | + GroupName => $ChatReceivingAgentsGroup, | |
| 498 | + Type => 'rw', | |
| 499 | + ); | |
| 500 | + | |
| 501 | + if ( | |
| 502 | + $UserData{UserID} != -1 | |
| 503 | + && $ChatReceivingAgentsGroup | |
| 504 | + && $ChatReceivingAgentsGroupPermission | |
| 505 | + && $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Agent::UnavailableForExternalChatsOnLogin') | |
| 506 | + ) | |
| 507 | + { | |
| 508 | + # Get user preferences | |
| 509 | + my %Preferences = $Kernel::OM->Get('Kernel::System::User')->GetPreferences( | |
| 510 | + UserID => $UserData{UserID}, | |
| 511 | + ); | |
| 512 | + | |
| 513 | + if ( $Preferences{ChatAvailability} && $Preferences{ChatAvailability} == 2 ) { | |
| 514 | + | |
| 515 | + # User is available for external chats. Set his availability to internal only. | |
| 516 | + $Kernel::OM->Get('Kernel::System::User')->SetPreferences( | |
| 517 | + Key => 'ChatAvailability', | |
| 518 | + Value => '1', | |
| 519 | + UserID => $UserData{UserID}, | |
| 520 | + ); | |
| 521 | + | |
| 522 | + # Set ChatAvailabilityNotification to display notification in agent interface (only once) | |
| 523 | + $Kernel::OM->Get('Kernel::System::User')->SetPreferences( | |
| 524 | + Key => 'ChatAvailabilityNotification', | |
| 525 | + Value => '1', | |
| 526 | + UserID => $UserData{UserID}, | |
| 527 | + ); | |
| 528 | + } | |
| 529 | + } | |
| 530 | + } | |
| 531 | + | |
| 532 | + # redirect with new session id and old params | |
| 533 | + # prepare old redirect URL -- do not redirect to Login or Logout (loop)! | |
| 534 | + if ( $Param{RequestedURL} =~ /Action=(Logout|Login|LostPassword|PreLogin)/ ) { | |
| 535 | + $Param{RequestedURL} = ''; | |
| 536 | + } | |
| 537 | + | |
| 538 | + # redirect with new session id | |
| 539 | + print $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Redirect( | |
| 540 | + OP => $Param{RequestedURL}, | |
| 541 | + Login => 1, | |
| 542 | + ); | |
| 543 | + return 1; | |
| 544 | + } | |
| 545 | + | |
| 546 | + # logout | |
| 547 | + elsif ( $Param{Action} eq 'Logout' ) { | |
| 548 | + | |
| 549 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 550 | + | |
| 551 | + # check session id | |
| 552 | + if ( !$SessionObject->CheckSessionID( SessionID => $Param{SessionID} ) ) { | |
| 553 | + | |
| 554 | + # redirect to alternate login | |
| 555 | + if ( $ConfigObject->Get('LoginURL') ) { | |
| 556 | + $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); | |
| 557 | + print $LayoutObject->Redirect( | |
| 558 | + ExtURL => $ConfigObject->Get('LoginURL') | |
| 559 | + . "?Reason=InvalidSessionID&RequestedURL=$Param{RequestedURL}", | |
| 560 | + ); | |
| 561 | + return; | |
| 562 | + } | |
| 563 | + | |
| 564 | + # show login screen | |
| 565 | + $LayoutObject->Print( | |
| 566 | + Output => \$LayoutObject->Login( | |
| 567 | + Title => 'Logout', | |
| 568 | + %Param, | |
| 569 | + ), | |
| 570 | + ); | |
| 571 | + return; | |
| 572 | + } | |
| 573 | + | |
| 574 | + # get session data | |
| 575 | + my %UserData = $SessionObject->GetSessionIDData( | |
| 576 | + SessionID => $Param{SessionID}, | |
| 577 | + ); | |
| 578 | + | |
| 579 | + # create a new LayoutObject with %UserData | |
| 580 | + $Kernel::OM->ObjectParamAdd( | |
| 581 | + 'Kernel::Output::HTML::Layout' => { | |
| 582 | + SetCookies => { | |
| 583 | + SessionIDCookie => $ParamObject->SetCookie( | |
| 584 | + Key => $Param{SessionName}, | |
| 585 | + Value => '', | |
| 586 | + Expires => '-1y', | |
| 587 | + Path => $ConfigObject->Get('ScriptAlias'), | |
| 588 | + Secure => scalar $CookieSecureAttribute, | |
| 589 | + HTTPOnly => 1, | |
| 590 | + ), | |
| 591 | + }, | |
| 592 | + %UserData, | |
| 593 | + }, | |
| 594 | + ); | |
| 595 | + $Kernel::OM->ObjectsDiscard( Objects => ['Kernel::Output::HTML::Layout'] ); | |
| 596 | + $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 597 | + | |
| 598 | + # Prevent CSRF attacks | |
| 599 | + $LayoutObject->ChallengeTokenCheck(); | |
| 600 | + | |
| 601 | + # remove session id | |
| 602 | + if ( !$SessionObject->RemoveSessionID( SessionID => $Param{SessionID} ) ) { | |
| 603 | + $LayoutObject->FatalError( | |
| 604 | + Message => Translatable('Can`t remove SessionID.'), | |
| 605 | + Comment => Translatable('Please contact the administrator.'), | |
| 606 | + ); | |
| 607 | + return; | |
| 608 | + } | |
| 609 | + | |
| 610 | + # redirect to alternate login | |
| 611 | + if ( $ConfigObject->Get('LogoutURL') ) { | |
| 612 | + print $LayoutObject->Redirect( | |
| 613 | + ExtURL => $ConfigObject->Get('LogoutURL') . "?Reason=Logout", | |
| 614 | + ); | |
| 615 | + return 1; | |
| 616 | + } | |
| 617 | + | |
| 618 | + # show logout screen | |
| 619 | + my $LogoutMessage = $LayoutObject->{LanguageObject}->Translate('Logout successful.'); | |
| 620 | + | |
| 621 | + $LayoutObject->Print( | |
| 622 | + Output => \$LayoutObject->Login( | |
| 623 | + Title => 'Logout', | |
| 624 | + Message => $LogoutMessage, | |
| 625 | + MessageType => 'Success', | |
| 626 | + %Param, | |
| 627 | + ), | |
| 628 | + ); | |
| 629 | + return 1; | |
| 630 | + } | |
| 631 | + | |
| 632 | + # user lost password | |
| 633 | + elsif ( $Param{Action} eq 'LostPassword' ) { | |
| 634 | + | |
| 635 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 636 | + | |
| 637 | + # check feature | |
| 638 | + if ( !$ConfigObject->Get('LostPassword') ) { | |
| 639 | + | |
| 640 | + # show normal login | |
| 641 | + $LayoutObject->Print( | |
| 642 | + Output => \$LayoutObject->Login( | |
| 643 | + Title => 'Login', | |
| 644 | + Message => Translatable('Feature not active!'), | |
| 645 | + MessageType => 'Error', | |
| 646 | + ), | |
| 647 | + ); | |
| 648 | + return; | |
| 649 | + } | |
| 650 | + | |
| 651 | + # get params | |
| 652 | + my $User = $ParamObject->GetParam( Param => 'User' ) || ''; | |
| 653 | + my $Token = $ParamObject->GetParam( Param => 'Token' ) || ''; | |
| 654 | + | |
| 655 | + # get user login by token | |
| 656 | + if ( !$User && $Token ) { | |
| 657 | + my %UserList = $UserObject->SearchPreferences( | |
| 658 | + Key => 'UserToken', | |
| 659 | + Value => $Token, | |
| 660 | + ); | |
| 661 | + USERS: | |
| 662 | + for my $UserID ( sort keys %UserList ) { | |
| 663 | + my %UserData = $UserObject->GetUserData( | |
| 664 | + UserID => $UserID, | |
| 665 | + Valid => 1, | |
| 666 | + ); | |
| 667 | + if (%UserData) { | |
| 668 | + $User = $UserData{UserLogin}; | |
| 669 | + last USERS; | |
| 670 | + } | |
| 671 | + } | |
| 672 | + } | |
| 673 | + | |
| 674 | + # get user data | |
| 675 | + my %UserData = $UserObject->GetUserData( | |
| 676 | + User => $User, | |
| 677 | + Valid => 1 | |
| 678 | + ); | |
| 679 | + | |
| 680 | + # verify user is valid when requesting password reset | |
| 681 | + my @ValidIDs = $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet(); | |
| 682 | + my $UserIsValid = grep { $UserData{ValidID} && $UserData{ValidID} == $_ } @ValidIDs; | |
| 683 | + if ( !$UserData{UserID} || !$UserIsValid ) { | |
| 684 | + | |
| 685 | + # Security: pretend that password reset instructions were actually sent to | |
| 686 | + # make sure that users cannot find out valid usernames by | |
| 687 | + # just trying and checking the result message. | |
| 688 | + $LayoutObject->Print( | |
| 689 | + Output => \$LayoutObject->Login( | |
| 690 | + Title => 'Login', | |
| 691 | + Message => Translatable('Sent password reset instructions. Please check your email.'), | |
| 692 | + MessageType => 'Success', | |
| 693 | + %Param, | |
| 694 | + ), | |
| 695 | + ); | |
| 696 | + return; | |
| 697 | + } | |
| 698 | + | |
| 699 | + # create email object | |
| 700 | + my $EmailObject = $Kernel::OM->Get('Kernel::System::Email'); | |
| 701 | + | |
| 702 | + # send password reset token | |
| 703 | + if ( !$Token ) { | |
| 704 | + | |
| 705 | + # generate token | |
| 706 | + $UserData{Token} = $UserObject->TokenGenerate( | |
| 707 | + UserID => $UserData{UserID}, | |
| 708 | + ); | |
| 709 | + | |
| 710 | + # send token notify email with link | |
| 711 | + my $Body = $ConfigObject->Get('NotificationBodyLostPasswordToken') | |
| 712 | + || 'ERROR: NotificationBodyLostPasswordToken is missing!'; | |
| 713 | + my $Subject = $ConfigObject->Get('NotificationSubjectLostPasswordToken') | |
| 714 | + || 'ERROR: NotificationSubjectLostPasswordToken is missing!'; | |
| 715 | + for ( sort keys %UserData ) { | |
| 716 | + $Body =~ s/<OTRS_$_>/$UserData{$_}/gi; | |
| 717 | + } | |
| 718 | + my $Sent = $EmailObject->Send( | |
| 719 | + To => $UserData{UserEmail}, | |
| 720 | + Subject => $Subject, | |
| 721 | + Charset => $LayoutObject->{UserCharset}, | |
| 722 | + MimeType => 'text/plain', | |
| 723 | + Body => $Body | |
| 724 | + ); | |
| 725 | + if ( !$Sent->{Success} ) { | |
| 726 | + $LayoutObject->FatalError( | |
| 727 | + Comment => Translatable('Please contact the administrator.'), | |
| 728 | + ); | |
| 729 | + return; | |
| 730 | + } | |
| 731 | + $LayoutObject->Print( | |
| 732 | + Output => \$LayoutObject->Login( | |
| 733 | + Title => 'Login', | |
| 734 | + Message => Translatable('Sent password reset instructions. Please check your email.'), | |
| 735 | + MessageType => 'Success', | |
| 736 | + %Param, | |
| 737 | + ), | |
| 738 | + ); | |
| 739 | + return 1; | |
| 740 | + } | |
| 741 | + | |
| 742 | + # reset password | |
| 743 | + # check if token is valid | |
| 744 | + my $TokenValid = $UserObject->TokenCheck( | |
| 745 | + Token => $Token, | |
| 746 | + UserID => $UserData{UserID}, | |
| 747 | + ); | |
| 748 | + if ( !$TokenValid ) { | |
| 749 | + $LayoutObject->Print( | |
| 750 | + Output => \$LayoutObject->Login( | |
| 751 | + Title => 'Login', | |
| 752 | + Message => Translatable('Invalid Token!'), | |
| 753 | + MessageType => 'Error', | |
| 754 | + %Param, | |
| 755 | + ), | |
| 756 | + ); | |
| 757 | + return; | |
| 758 | + } | |
| 759 | + | |
| 760 | + # get new password | |
| 761 | + $UserData{NewPW} = $UserObject->GenerateRandomPassword(); | |
| 762 | + | |
| 763 | + # update new password | |
| 764 | + $UserObject->SetPassword( | |
| 765 | + UserLogin => $User, | |
| 766 | + PW => $UserData{NewPW} | |
| 767 | + ); | |
| 768 | + | |
| 769 | + # send notify email | |
| 770 | + my $Body = $ConfigObject->Get('NotificationBodyLostPassword') | |
| 771 | + || 'New Password is: <OTRS_NEWPW>'; | |
| 772 | + my $Subject = $ConfigObject->Get('NotificationSubjectLostPassword') | |
| 773 | + || 'New Password!'; | |
| 774 | + for ( sort keys %UserData ) { | |
| 775 | + $Body =~ s/<OTRS_$_>/$UserData{$_}/gi; | |
| 776 | + } | |
| 777 | + my $Sent = $EmailObject->Send( | |
| 778 | + To => $UserData{UserEmail}, | |
| 779 | + Subject => $Subject, | |
| 780 | + Charset => $LayoutObject->{UserCharset}, | |
| 781 | + MimeType => 'text/plain', | |
| 782 | + Body => $Body | |
| 783 | + ); | |
| 784 | + | |
| 785 | + if ( !$Sent->{Success} ) { | |
| 786 | + $LayoutObject->FatalError( | |
| 787 | + Comment => Translatable('Please contact the administrator.'), | |
| 788 | + ); | |
| 789 | + return; | |
| 790 | + } | |
| 791 | + my $Message = $LayoutObject->{LanguageObject}->Translate( | |
| 792 | + 'Sent new password to %s. Please check your email.', | |
| 793 | + $UserData{UserEmail}, | |
| 794 | + ); | |
| 795 | + $LayoutObject->Print( | |
| 796 | + Output => \$LayoutObject->Login( | |
| 797 | + Title => 'Login', | |
| 798 | + Message => $Message, | |
| 799 | + User => $User, | |
| 800 | + MessageType => 'Success', | |
| 801 | + %Param, | |
| 802 | + ), | |
| 803 | + ); | |
| 804 | + return 1; | |
| 805 | + } | |
| 806 | + | |
| 807 | + # show login site | |
| 808 | + elsif ( !$Param{SessionID} ) { | |
| 809 | + | |
| 810 | + # create AuthObject | |
| 811 | + my $AuthObject = $Kernel::OM->Get('Kernel::System::Auth'); | |
| 812 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 813 | + if ( $AuthObject->GetOption( What => 'PreAuth' ) ) { | |
| 814 | + | |
| 815 | + # automatic login | |
| 816 | + $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); | |
| 817 | + print $LayoutObject->Redirect( | |
| 818 | + OP => "Action=PreLogin&RequestedURL=$Param{RequestedURL}", | |
| 819 | + ); | |
| 820 | + return; | |
| 821 | + } | |
| 822 | + elsif ( $ConfigObject->Get('LoginURL') ) { | |
| 823 | + | |
| 824 | + # redirect to alternate login | |
| 825 | + $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); | |
| 826 | + print $LayoutObject->Redirect( | |
| 827 | + ExtURL => $ConfigObject->Get('LoginURL') | |
| 828 | + . "?RequestedURL=$Param{RequestedURL}", | |
| 829 | + ); | |
| 830 | + return; | |
| 831 | + } | |
| 832 | + | |
| 833 | + # login screen | |
| 834 | + $LayoutObject->Print( | |
| 835 | + Output => \$LayoutObject->Login( | |
| 836 | + Title => 'Login', | |
| 837 | + %Param, | |
| 838 | + ), | |
| 839 | + ); | |
| 840 | + return; | |
| 841 | + } | |
| 842 | + | |
| 843 | + # run modules if a version value exists | |
| 844 | + elsif ( $Kernel::OM->Get('Kernel::System::Main')->Require("Kernel::Modules::$Param{Action}") ) { | |
| 845 | + | |
| 846 | + # check session id | |
| 847 | + if ( !$SessionObject->CheckSessionID( SessionID => $Param{SessionID} ) ) { | |
| 848 | + | |
| 849 | + # put '%Param' into LayoutObject | |
| 850 | + $Kernel::OM->ObjectParamAdd( | |
| 851 | + 'Kernel::Output::HTML::Layout' => { | |
| 852 | + SetCookies => { | |
| 853 | + SessionIDCookie => $ParamObject->SetCookie( | |
| 854 | + Key => $Param{SessionName}, | |
| 855 | + Value => '', | |
| 856 | + Expires => '-1y', | |
| 857 | + Path => $ConfigObject->Get('ScriptAlias'), | |
| 858 | + Secure => scalar $CookieSecureAttribute, | |
| 859 | + HTTPOnly => 1, | |
| 860 | + ), | |
| 861 | + }, | |
| 862 | + %Param, | |
| 863 | + }, | |
| 864 | + ); | |
| 865 | + | |
| 866 | + $Kernel::OM->ObjectsDiscard( Objects => ['Kernel::Output::HTML::Layout'] ); | |
| 867 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 868 | + | |
| 869 | + # create AuthObject | |
| 870 | + my $AuthObject = $Kernel::OM->Get('Kernel::System::Auth'); | |
| 871 | + if ( $AuthObject->GetOption( What => 'PreAuth' ) ) { | |
| 872 | + | |
| 873 | + # automatic re-login | |
| 874 | + $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); | |
| 875 | + print $LayoutObject->Redirect( | |
| 876 | + OP => "?Action=PreLogin&RequestedURL=$Param{RequestedURL}", | |
| 877 | + ); | |
| 878 | + return; | |
| 879 | + } | |
| 880 | + elsif ( $ConfigObject->Get('LoginURL') ) { | |
| 881 | + | |
| 882 | + # redirect to alternate login | |
| 883 | + $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); | |
| 884 | + print $LayoutObject->Redirect( | |
| 885 | + ExtURL => $ConfigObject->Get('LoginURL') | |
| 886 | + . "?Reason=InvalidSessionID&RequestedURL=$Param{RequestedURL}", | |
| 887 | + ); | |
| 888 | + return; | |
| 889 | + } | |
| 890 | + | |
| 891 | + # show login | |
| 892 | + $LayoutObject->Print( | |
| 893 | + Output => \$LayoutObject->Login( | |
| 894 | + Title => 'Login', | |
| 895 | + Message => | |
| 896 | + $LayoutObject->{LanguageObject}->Translate( $SessionObject->SessionIDErrorMessage() ), | |
| 897 | + MessageType => 'Error', | |
| 898 | + %Param, | |
| 899 | + ), | |
| 900 | + ); | |
| 901 | + return; | |
| 902 | + } | |
| 903 | + | |
| 904 | + # get session data | |
| 905 | + my %UserData = $SessionObject->GetSessionIDData( | |
| 906 | + SessionID => $Param{SessionID}, | |
| 907 | + ); | |
| 908 | + | |
| 909 | + # check needed data | |
| 910 | + if ( !$UserData{UserID} || !$UserData{UserLogin} || $UserData{UserType} ne 'User' ) { | |
| 911 | + | |
| 912 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 913 | + | |
| 914 | + # redirect to alternate login | |
| 915 | + if ( $ConfigObject->Get('LoginURL') ) { | |
| 916 | + print $LayoutObject->Redirect( | |
| 917 | + ExtURL => $ConfigObject->Get('LoginURL') . '?Reason=SystemError', | |
| 918 | + ); | |
| 919 | + return; | |
| 920 | + } | |
| 921 | + | |
| 922 | + # show login screen | |
| 923 | + $LayoutObject->Print( | |
| 924 | + Output => \$LayoutObject->Login( | |
| 925 | + Title => 'Error', | |
| 926 | + Message => Translatable('Error: invalid session.'), | |
| 927 | + MessageType => 'Error', | |
| 928 | + %Param, | |
| 929 | + ), | |
| 930 | + ); | |
| 931 | + return; | |
| 932 | + } | |
| 933 | + | |
| 934 | + # check module registry | |
| 935 | + my $ModuleReg = $ConfigObject->Get('Frontend::Module')->{ $Param{Action} }; | |
| 936 | + if ( !$ModuleReg ) { | |
| 937 | + | |
| 938 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 939 | + Priority => 'error', | |
| 940 | + Message => | |
| 941 | + "Module Kernel::Modules::$Param{Action} not registered in Kernel/Config.pm!", | |
| 942 | + ); | |
| 943 | + $Kernel::OM->Get('Kernel::Output::HTML::Layout')->FatalError( | |
| 944 | + Comment => Translatable('Please contact the administrator.'), | |
| 945 | + ); | |
| 946 | + return; | |
| 947 | + } | |
| 948 | + | |
| 949 | + # module permisson check | |
| 950 | + if ( | |
| 951 | + ref $ModuleReg->{GroupRo} eq 'ARRAY' | |
| 952 | + && !scalar @{ $ModuleReg->{GroupRo} } | |
| 953 | + && ref $ModuleReg->{Group} eq 'ARRAY' | |
| 954 | + && !scalar @{ $ModuleReg->{Group} } | |
| 955 | + ) | |
| 956 | + { | |
| 957 | + $Param{AccessRo} = 1; | |
| 958 | + $Param{AccessRw} = 1; | |
| 959 | + } | |
| 960 | + else { | |
| 961 | + my $GroupObject = $Kernel::OM->Get('Kernel::System::Group'); | |
| 962 | + | |
| 963 | + PERMISSION: | |
| 964 | + for my $Permission (qw(GroupRo Group)) { | |
| 965 | + my $AccessOk = 0; | |
| 966 | + my $Group = $ModuleReg->{$Permission}; | |
| 967 | + next PERMISSION if !$Group; | |
| 968 | + if ( ref $Group eq 'ARRAY' ) { | |
| 969 | + INNER: | |
| 970 | + for my $GroupName ( @{$Group} ) { | |
| 971 | + next INNER if !$GroupName; | |
| 972 | + next INNER if !$GroupObject->PermissionCheck( | |
| 973 | + UserID => $UserData{UserID}, | |
| 974 | + GroupName => $GroupName, | |
| 975 | + Type => $Permission eq 'GroupRo' ? 'ro' : 'rw', | |
| 976 | + | |
| 977 | + ); | |
| 978 | + $AccessOk = 1; | |
| 979 | + last INNER; | |
| 980 | + } | |
| 981 | + } | |
| 982 | + else { | |
| 983 | + my $HasPermission = $GroupObject->PermissionCheck( | |
| 984 | + UserID => $UserData{UserID}, | |
| 985 | + GroupName => $Group, | |
| 986 | + Type => $Permission eq 'GroupRo' ? 'ro' : 'rw', | |
| 987 | + | |
| 988 | + ); | |
| 989 | + if ($HasPermission) { | |
| 990 | + $AccessOk = 1; | |
| 991 | + } | |
| 992 | + } | |
| 993 | + if ( $Permission eq 'Group' && $AccessOk ) { | |
| 994 | + $Param{AccessRo} = 1; | |
| 995 | + $Param{AccessRw} = 1; | |
| 996 | + } | |
| 997 | + elsif ( $Permission eq 'GroupRo' && $AccessOk ) { | |
| 998 | + $Param{AccessRo} = 1; | |
| 999 | + } | |
| 1000 | + } | |
| 1001 | + if ( !$Param{AccessRo} && !$Param{AccessRw} || !$Param{AccessRo} && $Param{AccessRw} ) { | |
| 1002 | + | |
| 1003 | + print $Kernel::OM->Get('Kernel::Output::HTML::Layout')->NoPermission( | |
| 1004 | + Message => Translatable('No Permission to use this frontend module!') | |
| 1005 | + ); | |
| 1006 | + return; | |
| 1007 | + } | |
| 1008 | + } | |
| 1009 | + | |
| 1010 | + # put '%Param' and '%UserData' into LayoutObject | |
| 1011 | + $Kernel::OM->ObjectParamAdd( | |
| 1012 | + 'Kernel::Output::HTML::Layout' => { | |
| 1013 | + %Param, | |
| 1014 | + %UserData, | |
| 1015 | + ModuleReg => $ModuleReg, | |
| 1016 | + }, | |
| 1017 | + ); | |
| 1018 | + $Kernel::OM->ObjectsDiscard( Objects => ['Kernel::Output::HTML::Layout'] ); | |
| 1019 | + | |
| 1020 | + # update last request time | |
| 1021 | + if ( | |
| 1022 | + !$ParamObject->IsAJAXRequest() | |
| 1023 | + || $Param{Action} eq 'AgentVideoChat' | |
| 1024 | + || | |
| 1025 | + ( | |
| 1026 | + $Param{Action} eq 'AgentChat' | |
| 1027 | + && | |
| 1028 | + $Param{Subaction} ne 'ChatGetOpenRequests' && | |
| 1029 | + $Param{Subaction} ne 'ChatMonitorCheck' | |
| 1030 | + ) | |
| 1031 | + ) | |
| 1032 | + { | |
| 1033 | + my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); | |
| 1034 | + | |
| 1035 | + $SessionObject->UpdateSessionID( | |
| 1036 | + SessionID => $Param{SessionID}, | |
| 1037 | + Key => 'UserLastRequest', | |
| 1038 | + Value => $DateTimeObject->ToEpoch(), | |
| 1039 | + ); | |
| 1040 | + } | |
| 1041 | + | |
| 1042 | + # Override user settings. | |
| 1043 | + my $Home = $ConfigObject->Get('Home'); | |
| 1044 | + my $File = "$Home/Kernel/Config/Files/User/$UserData{UserID}.pm"; | |
| 1045 | + if ( -e $File ) { | |
| 1046 | + if ( !require $File ) { | |
| 1047 | + die "ERROR: $!\n"; | |
| 1048 | + } | |
| 1049 | + | |
| 1050 | + # prepare file | |
| 1051 | + $File =~ s/\Q$Home\E//g; | |
| 1052 | + $File =~ s/^\///g; | |
| 1053 | + $File =~ s/\/\//\//g; | |
| 1054 | + $File =~ s/\//::/g; | |
| 1055 | + $File =~ s/\.pm$//g; | |
| 1056 | + $File->Load($ConfigObject); | |
| 1057 | + } | |
| 1058 | + | |
| 1059 | + # pre application module | |
| 1060 | + my $PreModule = $ConfigObject->Get('PreApplicationModule'); | |
| 1061 | + if ($PreModule) { | |
| 1062 | + my %PreModuleList; | |
| 1063 | + if ( ref $PreModule eq 'HASH' ) { | |
| 1064 | + %PreModuleList = %{$PreModule}; | |
| 1065 | + } | |
| 1066 | + else { | |
| 1067 | + $PreModuleList{Init} = $PreModule; | |
| 1068 | + } | |
| 1069 | + | |
| 1070 | + MODULE: | |
| 1071 | + for my $PreModuleKey ( sort keys %PreModuleList ) { | |
| 1072 | + my $PreModule = $PreModuleList{$PreModuleKey}; | |
| 1073 | + next MODULE if !$PreModule; | |
| 1074 | + next MODULE if !$Kernel::OM->Get('Kernel::System::Main')->Require($PreModule); | |
| 1075 | + | |
| 1076 | + # debug info | |
| 1077 | + if ( $Self->{Debug} ) { | |
| 1078 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 1079 | + Priority => 'debug', | |
| 1080 | + Message => "PreApplication module $PreModule is used.", | |
| 1081 | + ); | |
| 1082 | + } | |
| 1083 | + | |
| 1084 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 1085 | + | |
| 1086 | + # use module | |
| 1087 | + my $PreModuleObject = $PreModule->new( | |
| 1088 | + %Param, | |
| 1089 | + %UserData, | |
| 1090 | + ModuleReg => $ModuleReg, | |
| 1091 | + ); | |
| 1092 | + my $Output = $PreModuleObject->PreRun(); | |
| 1093 | + if ($Output) { | |
| 1094 | + $LayoutObject->Print( Output => \$Output ); | |
| 1095 | + return 1; | |
| 1096 | + } | |
| 1097 | + } | |
| 1098 | + } | |
| 1099 | + | |
| 1100 | + # debug info | |
| 1101 | + if ( $Self->{Debug} ) { | |
| 1102 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 1103 | + Priority => 'debug', | |
| 1104 | + Message => 'Kernel::Modules::' . $Param{Action} . '->new', | |
| 1105 | + ); | |
| 1106 | + } | |
| 1107 | + | |
| 1108 | + my $FrontendObject = ( 'Kernel::Modules::' . $Param{Action} )->new( | |
| 1109 | + %Param, | |
| 1110 | + %UserData, | |
| 1111 | + ModuleReg => $ModuleReg, | |
| 1112 | + Debug => $Self->{Debug}, | |
| 1113 | + ); | |
| 1114 | + | |
| 1115 | + # debug info | |
| 1116 | + if ( $Self->{Debug} ) { | |
| 1117 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 1118 | + Priority => 'debug', | |
| 1119 | + Message => 'Kernel::Modules::' . $Param{Action} . '->run', | |
| 1120 | + ); | |
| 1121 | + } | |
| 1122 | + | |
| 1123 | + # ->Run $Action with $FrontendObject | |
| 1124 | + $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Print( Output => \$FrontendObject->Run() ); | |
| 1125 | + | |
| 1126 | + # log request time | |
| 1127 | + if ( $ConfigObject->Get('PerformanceLog') ) { | |
| 1128 | + if ( ( !$QueryString && $Param{Action} ) || $QueryString !~ /Action=/ ) { | |
| 1129 | + $QueryString = 'Action=' . $Param{Action} . '&Subaction=' . $Param{Subaction}; | |
| 1130 | + } | |
| 1131 | + my $File = $ConfigObject->Get('PerformanceLog::File'); | |
| 1132 | + ## no critic | |
| 1133 | + if ( open my $Out, '>>', $File ) { | |
| 1134 | + ## use critic | |
| 1135 | + print $Out time() | |
| 1136 | + . '::Agent::' | |
| 1137 | + . ( time() - $Self->{PerformanceLogStart} ) | |
| 1138 | + . "::$UserData{UserLogin}::$QueryString\n"; | |
| 1139 | + close $Out; | |
| 1140 | + | |
| 1141 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 1142 | + Priority => 'debug', | |
| 1143 | + Message => "Response::Agent: " | |
| 1144 | + . ( time() - $Self->{PerformanceLogStart} ) | |
| 1145 | + . "s taken (URL:$QueryString:$UserData{UserLogin})", | |
| 1146 | + ); | |
| 1147 | + } | |
| 1148 | + else { | |
| 1149 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 1150 | + Priority => 'error', | |
| 1151 | + Message => "Can't write $File: $!", | |
| 1152 | + ); | |
| 1153 | + } | |
| 1154 | + } | |
| 1155 | + return 1; | |
| 1156 | + } | |
| 1157 | + | |
| 1158 | + # print an error screen | |
| 1159 | + my %Data = $SessionObject->GetSessionIDData( | |
| 1160 | + SessionID => $Param{SessionID}, | |
| 1161 | + ); | |
| 1162 | + $Kernel::OM->ObjectParamAdd( | |
| 1163 | + 'Kernel::Output::HTML::Layout' => { | |
| 1164 | + %Param, | |
| 1165 | + %Data, | |
| 1166 | + }, | |
| 1167 | + ); | |
| 1168 | + $Kernel::OM->Get('Kernel::Output::HTML::Layout')->FatalError( | |
| 1169 | + Comment => Translatable('Please contact the administrator.'), | |
| 1170 | + ); | |
| 1171 | + return; | |
| 1172 | +} | |
| 1173 | + | |
| 1174 | +sub DESTROY { | |
| 1175 | + my $Self = shift; | |
| 1176 | + | |
| 1177 | + # debug info | |
| 1178 | + if ( $Self->{Debug} ) { | |
| 1179 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 1180 | + Priority => 'debug', | |
| 1181 | + Message => 'Global handle stopped.', | |
| 1182 | + ); | |
| 1183 | + } | |
| 1184 | + | |
| 1185 | + return 1; | |
| 1186 | +} | |
| 1187 | + | |
| 1188 | +# CAS Custom | |
| 1189 | +=item | |
| 1190 | + Check if it is a CAS logout - if true, logs out user for the session ticket | |
| 1191 | +=cut | |
| 1192 | +sub CASLogout { | |
| 1193 | + my $Self = shift; | |
| 1194 | + | |
| 1195 | + # get post data | |
| 1196 | + my $cgi = new CGI; | |
| 1197 | + my $request = uri_unescape($cgi->query_string()); | |
| 1198 | + | |
| 1199 | + # check if it is CAS logout | |
| 1200 | + if ($request =~ /(logoutRequest=<samlp:LogoutRequest)/) { | |
| 1201 | + | |
| 1202 | + (my $ticket) = ($request =~ /<samlp:SessionIndex>([^<]+)/); | |
| 1203 | + if (! $ticket) { | |
| 1204 | + return 1; | |
| 1205 | + } | |
| 1206 | + | |
| 1207 | + my $DBObject = $Kernel::OM->Get("Kernel::System::DB"); | |
| 1208 | + | |
| 1209 | + # get login for session | |
| 1210 | + my $sqlLogin = 'SELECT UserLogin from cas_session where Ticket=?'; | |
| 1211 | + $DBObject->Prepare(SQL => $sqlLogin, Bind => [\$ticket]); | |
| 1212 | + | |
| 1213 | + my $login; | |
| 1214 | + while (my @Row = $DBObject->FetchrowArray()) { | |
| 1215 | + $login = $Row[0]; | |
| 1216 | + } | |
| 1217 | + | |
| 1218 | + # if a login was found, kill the user session | |
| 1219 | + if ($login) { | |
| 1220 | + $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)", | |
| 1221 | + Bind => [\$login]); | |
| 1222 | + } else { | |
| 1223 | + } | |
| 1224 | + return 1; | |
| 1225 | + } else { | |
| 1226 | + return 0; | |
| 1227 | + } | |
| 1228 | + | |
| 1229 | +} | |
| 1230 | +# CAS Custom | |
| 1231 | + | |
| 1232 | +1; | |
| 1233 | + | |
| 1234 | +=head1 TERMS AND CONDITIONS | |
| 1235 | + | |
| 1236 | +This software is part of the OTRS project (L<http://otrs.org/>). | |
| 1237 | + | |
| 1238 | +This software comes with ABSOLUTELY NO WARRANTY. For details, see | |
| 1239 | +the enclosed file COPYING for license information (AGPL). If you | |
| 1240 | +did not receive this file, see L<http://www.gnu.org/licenses/agpl.txt>. | |
| 1241 | + | |
| 1242 | +=cut | ... | ... |
| ... | ... | @@ -0,0 +1,1512 @@ |
| 1 | +# -- | |
| 2 | +# Copyright (C) 2001-2017 OTRS AG, http://otrs.com/ | |
| 3 | +# -- | |
| 4 | +# This software comes with ABSOLUTELY NO WARRANTY. For details, see | |
| 5 | +# the enclosed file COPYING for license information (AGPL). If you | |
| 6 | +# did not receive this file, see http://www.gnu.org/licenses/agpl.txt. | |
| 7 | +# | |
| 8 | +# | |
| 9 | +# Custom version for CAS authentication - rodrigo@goncalves.pro.br | |
| 10 | +# | |
| 11 | +# Version 2016-01-18 - RG - Version for OTRS 5.0.6 | |
| 12 | +# Version 2017-12-07 - RG - Version for OTRS 6.0.1 | |
| 13 | +# | |
| 14 | +# -- | |
| 15 | + | |
| 16 | +package Kernel::System::Web::InterfaceCustomer; | |
| 17 | + | |
| 18 | +use strict; | |
| 19 | +use warnings; | |
| 20 | + | |
| 21 | +# CAS Custom | |
| 22 | +use CGI; | |
| 23 | +use URI::Escape; | |
| 24 | +# CAS Custom | |
| 25 | + | |
| 26 | +use Kernel::System::DateTime; | |
| 27 | +use Kernel::System::Email; | |
| 28 | +use Kernel::System::VariableCheck qw(IsArrayRefWithData IsHashRefWithData); | |
| 29 | +use Kernel::Language qw(Translatable); | |
| 30 | + | |
| 31 | +our @ObjectDependencies = ( | |
| 32 | + 'Kernel::Config', | |
| 33 | + 'Kernel::Output::HTML::Layout', | |
| 34 | + 'Kernel::System::AuthSession', | |
| 35 | + 'Kernel::System::CustomerAuth', | |
| 36 | + 'Kernel::System::CustomerGroup', | |
| 37 | + 'Kernel::System::CustomerUser', | |
| 38 | + 'Kernel::System::DB', | |
| 39 | + 'Kernel::System::Group', | |
| 40 | + 'Kernel::System::Log', | |
| 41 | + 'Kernel::System::Main', | |
| 42 | + 'Kernel::System::Scheduler', | |
| 43 | + 'Kernel::System::DateTime', | |
| 44 | + 'Kernel::System::Web::Request', | |
| 45 | + 'Kernel::System::Valid', | |
| 46 | +); | |
| 47 | + | |
| 48 | +=head1 NAME | |
| 49 | + | |
| 50 | +Kernel::System::Web::InterfaceCustomer - the customer web interface | |
| 51 | + | |
| 52 | +=head1 DESCRIPTION | |
| 53 | + | |
| 54 | +the global customer web interface (authentication, session handling, ...) | |
| 55 | + | |
| 56 | +=head1 PUBLIC INTERFACE | |
| 57 | + | |
| 58 | +=head2 new() | |
| 59 | + | |
| 60 | +create customer web interface object | |
| 61 | + | |
| 62 | + use Kernel::System::Web::InterfaceCustomer; | |
| 63 | + | |
| 64 | + my $Debug = 0; | |
| 65 | + my $InterfaceCustomer = Kernel::System::Web::InterfaceCustomer->new( | |
| 66 | + Debug => $Debug, | |
| 67 | + WebRequest => CGI::Fast->new(), # optional, e. g. if fast cgi is used, the CGI object is already provided | |
| 68 | + ); | |
| 69 | + | |
| 70 | +=cut | |
| 71 | + | |
| 72 | +sub new { | |
| 73 | + my ( $Type, %Param ) = @_; | |
| 74 | + | |
| 75 | + # allocate new hash for object | |
| 76 | + my $Self = {}; | |
| 77 | + bless( $Self, $Type ); | |
| 78 | + | |
| 79 | + # get debug level | |
| 80 | + $Self->{Debug} = $Param{Debug} || 0; | |
| 81 | + | |
| 82 | + # performance log | |
| 83 | + $Self->{PerformanceLogStart} = time(); | |
| 84 | + | |
| 85 | + $Kernel::OM->ObjectParamAdd( | |
| 86 | + 'Kernel::System::Log' => { | |
| 87 | + LogPrefix => $Kernel::OM->Get('Kernel::Config')->Get('CGILogPrefix'), | |
| 88 | + }, | |
| 89 | + 'Kernel::System::Web::Request' => { | |
| 90 | + WebRequest => $Param{WebRequest} || 0, | |
| 91 | + }, | |
| 92 | + ); | |
| 93 | + | |
| 94 | + # debug info | |
| 95 | + if ( $Self->{Debug} ) { | |
| 96 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 97 | + Priority => 'debug', | |
| 98 | + Message => 'Global handle started...', | |
| 99 | + ); | |
| 100 | + } | |
| 101 | + | |
| 102 | + return $Self; | |
| 103 | +} | |
| 104 | + | |
| 105 | +=head2 Run() | |
| 106 | + | |
| 107 | +execute the object | |
| 108 | + | |
| 109 | + $InterfaceCustomer->Run(); | |
| 110 | + | |
| 111 | +=cut | |
| 112 | + | |
| 113 | +sub Run { | |
| 114 | + my $Self = shift; | |
| 115 | + | |
| 116 | + # CAS Custom | |
| 117 | + if ($Self->CASLogout()) { | |
| 118 | + return; | |
| 119 | + } | |
| 120 | + # CAS Custom | |
| 121 | + | |
| 122 | + my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); | |
| 123 | + | |
| 124 | + my $QueryString = $ENV{QUERY_STRING} || ''; | |
| 125 | + | |
| 126 | + # Check if https forcing is active, and redirect if needed. | |
| 127 | + if ( $ConfigObject->Get('HTTPSForceRedirect') ) { | |
| 128 | + | |
| 129 | + # Some web servers do not set HTTPS environment variable, so it's not possible to easily know if we are using | |
| 130 | + # https protocol. Look also for similarly named keys in environment hash, since this should prevent loops in | |
| 131 | + # certain cases. | |
| 132 | + if ( | |
| 133 | + ( | |
| 134 | + !defined $ENV{HTTPS} | |
| 135 | + && !grep {/^HTTPS(?:_|$)/} keys %ENV | |
| 136 | + ) | |
| 137 | + || $ENV{HTTPS} ne 'on' | |
| 138 | + ) | |
| 139 | + { | |
| 140 | + my $Host = $ENV{HTTP_HOST} || $ConfigObject->Get('FQDN'); | |
| 141 | + | |
| 142 | + # Redirect with 301 code. Add two new lines at the end, so HTTP headers are validated correctly. | |
| 143 | + print "Status: 301 Moved Permanently\nLocation: https://$Host$ENV{REQUEST_URI}\n\n"; | |
| 144 | + return; | |
| 145 | + } | |
| 146 | + } | |
| 147 | + | |
| 148 | + my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); | |
| 149 | + | |
| 150 | + my %Param; | |
| 151 | + | |
| 152 | + # get session id | |
| 153 | + $Param{SessionName} = $ConfigObject->Get('CustomerPanelSessionName') || 'CSID'; | |
| 154 | + $Param{SessionID} = $ParamObject->GetParam( Param => $Param{SessionName} ) || ''; | |
| 155 | + | |
| 156 | + # drop old session id (if exists) | |
| 157 | + $QueryString =~ s/(\?|&|;|)$Param{SessionName}(=&|=;|=.+?&|=.+?$)/;/g; | |
| 158 | + | |
| 159 | + # define framework params | |
| 160 | + my $FrameworkParams = { | |
| 161 | + Lang => '', | |
| 162 | + Action => '', | |
| 163 | + Subaction => '', | |
| 164 | + RequestedURL => $QueryString, | |
| 165 | + }; | |
| 166 | + for my $Key ( sort keys %{$FrameworkParams} ) { | |
| 167 | + $Param{$Key} = $ParamObject->GetParam( Param => $Key ) | |
| 168 | + || $FrameworkParams->{$Key}; | |
| 169 | + } | |
| 170 | + | |
| 171 | + # validate language | |
| 172 | + if ( $Param{Lang} && $Param{Lang} !~ m{\A[a-z]{2}(?:_[A-Z]{2})?\z}xms ) { | |
| 173 | + delete $Param{Lang}; | |
| 174 | + } | |
| 175 | + | |
| 176 | + my $BrowserHasCookie = 0; | |
| 177 | + | |
| 178 | + # Check if the browser sends the SessionID cookie and set the SessionID-cookie | |
| 179 | + # as SessionID! GET or POST SessionID have the lowest priority. | |
| 180 | + if ( $ConfigObject->Get('SessionUseCookie') ) { | |
| 181 | + $Param{SessionIDCookie} = $ParamObject->GetCookie( Key => $Param{SessionName} ); | |
| 182 | + if ( $Param{SessionIDCookie} ) { | |
| 183 | + $Param{SessionID} = $Param{SessionIDCookie}; | |
| 184 | + } | |
| 185 | + } | |
| 186 | + | |
| 187 | + my $CookieSecureAttribute; | |
| 188 | + if ( $ConfigObject->Get('HttpType') eq 'https' ) { | |
| 189 | + | |
| 190 | + # Restrict Cookie to HTTPS if it is used. | |
| 191 | + $CookieSecureAttribute = 1; | |
| 192 | + } | |
| 193 | + | |
| 194 | + $Kernel::OM->ObjectParamAdd( | |
| 195 | + 'Kernel::Output::HTML::Layout' => { | |
| 196 | + Lang => $Param{Lang}, | |
| 197 | + }, | |
| 198 | + 'Kernel::Language' => { | |
| 199 | + UserLanguage => $Param{Lang}, | |
| 200 | + }, | |
| 201 | + ); | |
| 202 | + | |
| 203 | + my $DBCanConnect = $Kernel::OM->Get('Kernel::System::DB')->Connect(); | |
| 204 | + | |
| 205 | + if ( !$DBCanConnect || $ParamObject->Error() ) { | |
| 206 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 207 | + if ( !$DBCanConnect ) { | |
| 208 | + $LayoutObject->CustomerFatalError( | |
| 209 | + Comment => Translatable('Please contact the administrator.'), | |
| 210 | + ); | |
| 211 | + return; | |
| 212 | + } | |
| 213 | + if ( $ParamObject->Error() ) { | |
| 214 | + $LayoutObject->CustomerFatalError( | |
| 215 | + Message => $ParamObject->Error(), | |
| 216 | + Comment => Translatable('Please contact the administrator.'), | |
| 217 | + ); | |
| 218 | + return; | |
| 219 | + } | |
| 220 | + } | |
| 221 | + | |
| 222 | + my $UserObject = $Kernel::OM->Get('Kernel::System::CustomerUser'); | |
| 223 | + my $SessionObject = $Kernel::OM->Get('Kernel::System::AuthSession'); | |
| 224 | + | |
| 225 | + # get common application and add on application params | |
| 226 | + my %CommonObjectParam = %{ $ConfigObject->Get('CustomerFrontend::CommonParam') }; | |
| 227 | + for my $Key ( sort keys %CommonObjectParam ) { | |
| 228 | + $Param{$Key} = $ParamObject->GetParam( Param => $Key ) || $CommonObjectParam{$Key}; | |
| 229 | + } | |
| 230 | + | |
| 231 | + # security check Action Param (replace non-word chars) | |
| 232 | + $Param{Action} =~ s/\W//g; | |
| 233 | + | |
| 234 | + # check request type | |
| 235 | + if ( $Param{Action} eq 'PreLogin' ) { | |
| 236 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 237 | + | |
| 238 | + # login screen | |
| 239 | + $LayoutObject->Print( | |
| 240 | + Output => \$LayoutObject->CustomerLogin( | |
| 241 | + Title => 'Login', | |
| 242 | + Mode => 'PreLogin', | |
| 243 | + %Param, | |
| 244 | + ), | |
| 245 | + ); | |
| 246 | + | |
| 247 | + return; | |
| 248 | + } | |
| 249 | + elsif ( $Param{Action} eq 'Login' ) { | |
| 250 | + | |
| 251 | + # get params | |
| 252 | + my $PostUser = $ParamObject->GetParam( Param => 'User' ) || ''; | |
| 253 | + my $PostPw = $ParamObject->GetParam( | |
| 254 | + Param => 'Password', | |
| 255 | + Raw => 1 | |
| 256 | + ) || ''; | |
| 257 | + my $PostTwoFactorToken = $ParamObject->GetParam( | |
| 258 | + Param => 'TwoFactorToken', | |
| 259 | + Raw => 1 | |
| 260 | + ) || ''; | |
| 261 | + | |
| 262 | + # create AuthObject | |
| 263 | + my $AuthObject = $Kernel::OM->Get('Kernel::System::CustomerAuth'); | |
| 264 | + | |
| 265 | + # check submitted data | |
| 266 | + my $User = $AuthObject->Auth( | |
| 267 | + User => $PostUser, | |
| 268 | + Pw => $PostPw, | |
| 269 | + TwoFactorToken => $PostTwoFactorToken, | |
| 270 | + RequestedURL => $Param{RequestedURL}, | |
| 271 | + ); | |
| 272 | + | |
| 273 | + my $Expires = '+' . $ConfigObject->Get('SessionMaxTime') . 's'; | |
| 274 | + if ( !$ConfigObject->Get('SessionUseCookieAfterBrowserClose') ) { | |
| 275 | + $Expires = ''; | |
| 276 | + } | |
| 277 | + | |
| 278 | + # login is invalid | |
| 279 | + if ( !$User ) { | |
| 280 | + | |
| 281 | + # CAS Custom | |
| 282 | + # Fixes OTRS Layout bug when redirecting | |
| 283 | + my $cgi = new CGI(); | |
| 284 | + print $cgi->redirect( -URL => $ConfigObject->Get("Customer::AuthModule::CAS::CASUrl") . "/login?service=" . $ConfigObject->Get("Customer::AuthModule::CAS::ServiceUrl") . "?" . $Param{RequestedURL}); | |
| 285 | + return; | |
| 286 | + # CAS Custom | |
| 287 | + | |
| 288 | + $Kernel::OM->ObjectParamAdd( | |
| 289 | + 'Kernel::Output::HTML::Layout' => { | |
| 290 | + SetCookies => { | |
| 291 | + OTRSBrowserHasCookie => $ParamObject->SetCookie( | |
| 292 | + Key => 'OTRSBrowserHasCookie', | |
| 293 | + Value => 1, | |
| 294 | + Expires => $Expires, | |
| 295 | + Path => $ConfigObject->Get('ScriptAlias'), | |
| 296 | + Secure => $CookieSecureAttribute, | |
| 297 | + HTTPOnly => 1, | |
| 298 | + ), | |
| 299 | + }, | |
| 300 | + }, | |
| 301 | + ); | |
| 302 | + | |
| 303 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 304 | + | |
| 305 | + # redirect to alternate login | |
| 306 | + if ( $ConfigObject->Get('CustomerPanelLoginURL') ) { | |
| 307 | + $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); | |
| 308 | + print $LayoutObject->Redirect( | |
| 309 | + ExtURL => $ConfigObject->Get('CustomerPanelLoginURL') | |
| 310 | + . "?Reason=LoginFailed;RequestedURL=$Param{RequestedURL}", | |
| 311 | + ); | |
| 312 | + return; | |
| 313 | + } | |
| 314 | + | |
| 315 | + # show normal login | |
| 316 | + $LayoutObject->Print( | |
| 317 | + Output => \$LayoutObject->CustomerLogin( | |
| 318 | + Title => 'Login', | |
| 319 | + Message => $Kernel::OM->Get('Kernel::System::Log')->GetLogEntry( | |
| 320 | + Type => 'Info', | |
| 321 | + What => 'Message', | |
| 322 | + ) | |
| 323 | + || $AuthObject->GetLastErrorMessage() | |
| 324 | + || Translatable('Login failed! Your user name or password was entered incorrectly.'), | |
| 325 | + User => $PostUser, | |
| 326 | + LoginFailed => 1, | |
| 327 | + %Param, | |
| 328 | + ), | |
| 329 | + ); | |
| 330 | + return; | |
| 331 | + } | |
| 332 | + | |
| 333 | + # login is successful | |
| 334 | + my %UserData = $UserObject->CustomerUserDataGet( | |
| 335 | + User => $User, | |
| 336 | + Valid => 1 | |
| 337 | + ); | |
| 338 | + | |
| 339 | + # check if the browser supports cookies | |
| 340 | + if ( $ParamObject->GetCookie( Key => 'OTRSBrowserHasCookie' ) ) { | |
| 341 | + $Kernel::OM->ObjectParamAdd( | |
| 342 | + 'Kernel::Output::HTML::Layout' => { | |
| 343 | + BrowserHasCookie => 1, | |
| 344 | + }, | |
| 345 | + ); | |
| 346 | + } | |
| 347 | + | |
| 348 | + # check needed data | |
| 349 | + if ( !$UserData{UserID} || !$UserData{UserLogin} ) { | |
| 350 | + | |
| 351 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 352 | + | |
| 353 | + # CAS Custom | |
| 354 | + my $url = $Kernel::OM->Get("Kernel::Config")->Get('Customer::AuthModule::CAS::InvalidUserURL'); | |
| 355 | + | |
| 356 | + if ($url) { | |
| 357 | + print $LayoutObject->Redirect( ExtURL => $url, ); | |
| 358 | + return; | |
| 359 | + } | |
| 360 | + # CAS Custom | |
| 361 | + | |
| 362 | + # redirect to alternate login | |
| 363 | + if ( $ConfigObject->Get('CustomerPanelLoginURL') ) { | |
| 364 | + print $LayoutObject->Redirect( | |
| 365 | + ExtURL => $ConfigObject->Get('CustomerPanelLoginURL') | |
| 366 | + . '?Reason=SystemError', | |
| 367 | + ); | |
| 368 | + return; | |
| 369 | + } | |
| 370 | + | |
| 371 | + # show need user data error message | |
| 372 | + $LayoutObject->Print( | |
| 373 | + Output => \$LayoutObject->CustomerLogin( | |
| 374 | + Title => 'Error', | |
| 375 | + Message => Translatable( | |
| 376 | + 'Authentication succeeded, but no customer record is found in the customer backend. Please contact the administrator.' | |
| 377 | + ), | |
| 378 | + %Param, | |
| 379 | + ), | |
| 380 | + ); | |
| 381 | + return; | |
| 382 | + } | |
| 383 | + | |
| 384 | + # create datetime object | |
| 385 | + my $SessionDTObject = $Kernel::OM->Create('Kernel::System::DateTime'); | |
| 386 | + | |
| 387 | + # create new session id | |
| 388 | + my $NewSessionID = $SessionObject->CreateSessionID( | |
| 389 | + %UserData, | |
| 390 | + UserLastRequest => $SessionDTObject->ToEpoch(), | |
| 391 | + UserType => 'Customer', | |
| 392 | + SessionSource => 'CustomerInterface', | |
| 393 | + ); | |
| 394 | + | |
| 395 | + # show error message if no session id has been created | |
| 396 | + if ( !$NewSessionID ) { | |
| 397 | + | |
| 398 | + # get error message | |
| 399 | + my $Error = $SessionObject->SessionIDErrorMessage() || ''; | |
| 400 | + | |
| 401 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 402 | + | |
| 403 | + # output error message | |
| 404 | + $LayoutObject->Print( | |
| 405 | + Output => \$LayoutObject->CustomerLogin( | |
| 406 | + Title => 'Login', | |
| 407 | + Message => $Error, | |
| 408 | + %Param, | |
| 409 | + ), | |
| 410 | + ); | |
| 411 | + return; | |
| 412 | + } | |
| 413 | + | |
| 414 | + # execution in 20 seconds | |
| 415 | + my $ExecutionTimeObj = $Kernel::OM->Create('Kernel::System::DateTime'); | |
| 416 | + $ExecutionTimeObj->Add( Seconds => 20 ); | |
| 417 | + | |
| 418 | + # add a asynchronous executor scheduler task to count the concurrent user | |
| 419 | + $Kernel::OM->Get('Kernel::System::Scheduler')->TaskAdd( | |
| 420 | + ExecutionTime => $ExecutionTimeObj->ToString(), | |
| 421 | + Type => 'AsynchronousExecutor', | |
| 422 | + Name => 'PluginAsynchronous::ConcurrentUser', | |
| 423 | + MaximumParallelInstances => 1, | |
| 424 | + Data => { | |
| 425 | + Object => 'Kernel::System::SupportDataCollector::PluginAsynchronous::OTRS::ConcurrentUsers', | |
| 426 | + Function => 'RunAsynchronous', | |
| 427 | + }, | |
| 428 | + ); | |
| 429 | + | |
| 430 | + # get time zone | |
| 431 | + my $UserTimeZone = $UserData{UserTimeZone} || Kernel::System::DateTime->UserDefaultTimeZoneGet(); | |
| 432 | + $SessionObject->UpdateSessionID( | |
| 433 | + SessionID => $NewSessionID, | |
| 434 | + Key => 'UserTimeZone', | |
| 435 | + Value => $UserTimeZone, | |
| 436 | + ); | |
| 437 | + | |
| 438 | + # check if the time zone offset reported by the user's browser differs from that | |
| 439 | + # of the OTRS user's time zone offset | |
| 440 | + my $DateTimeObject = $Kernel::OM->Create( | |
| 441 | + 'Kernel::System::DateTime', | |
| 442 | + ObjectParams => { | |
| 443 | + TimeZone => $UserTimeZone, | |
| 444 | + }, | |
| 445 | + ); | |
| 446 | + my $OTRSUserTimeZoneOffset = $DateTimeObject->Format( Format => '%{offset}' ) / 60; | |
| 447 | + my $BrowserTimeZoneOffset = ( $ParamObject->GetParam( Param => 'TimeZoneOffset' ) || 0 ) * -1; | |
| 448 | + | |
| 449 | + # TimeZoneOffsetDifference contains the difference of the time zone offset between | |
| 450 | + # the user's OTRS time zone setting and the one reported by the user's browser. | |
| 451 | + # If there is a difference it can be evaluated later to e. g. show a message | |
| 452 | + # for the user to check his OTRS time zone setting. | |
| 453 | + my $UserTimeZoneOffsetDifference = abs( $OTRSUserTimeZoneOffset - $BrowserTimeZoneOffset ); | |
| 454 | + $SessionObject->UpdateSessionID( | |
| 455 | + SessionID => $NewSessionID, | |
| 456 | + Key => 'UserTimeZoneOffsetDifference', | |
| 457 | + Value => $UserTimeZoneOffsetDifference, | |
| 458 | + ); | |
| 459 | + | |
| 460 | + $Kernel::OM->ObjectParamAdd( | |
| 461 | + 'Kernel::Output::HTML::Layout' => { | |
| 462 | + SetCookies => { | |
| 463 | + SessionIDCookie => $ParamObject->SetCookie( | |
| 464 | + Key => $Param{SessionName}, | |
| 465 | + Value => $NewSessionID, | |
| 466 | + Expires => $Expires, | |
| 467 | + Path => $ConfigObject->Get('ScriptAlias'), | |
| 468 | + Secure => scalar $CookieSecureAttribute, | |
| 469 | + HTTPOnly => 1, | |
| 470 | + ), | |
| 471 | + OTRSBrowserHasCookie => $ParamObject->SetCookie( | |
| 472 | + Key => 'OTRSBrowserHasCookie', | |
| 473 | + Value => '', | |
| 474 | + Expires => '-1y', | |
| 475 | + Path => $ConfigObject->Get('ScriptAlias'), | |
| 476 | + Secure => $CookieSecureAttribute, | |
| 477 | + HTTPOnly => 1, | |
| 478 | + ), | |
| 479 | + | |
| 480 | + }, | |
| 481 | + SessionID => $NewSessionID, | |
| 482 | + SessionName => $Param{SessionName}, | |
| 483 | + }, | |
| 484 | + ); | |
| 485 | + | |
| 486 | + # redirect with new session id and old params | |
| 487 | + # prepare old redirect URL -- do not redirect to Login or Logout (loop)! | |
| 488 | + if ( $Param{RequestedURL} =~ /Action=(Logout|Login|LostPassword|PreLogin)/ ) { | |
| 489 | + $Param{RequestedURL} = ''; | |
| 490 | + } | |
| 491 | + | |
| 492 | + # redirect with new session id | |
| 493 | + print $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Redirect( | |
| 494 | + OP => $Param{RequestedURL}, | |
| 495 | + Login => 1, | |
| 496 | + ); | |
| 497 | + return 1; | |
| 498 | + } | |
| 499 | + | |
| 500 | + # logout | |
| 501 | + elsif ( $Param{Action} eq 'Logout' ) { | |
| 502 | + | |
| 503 | + # check session id | |
| 504 | + if ( !$SessionObject->CheckSessionID( SessionID => $Param{SessionID} ) ) { | |
| 505 | + | |
| 506 | + # new layout object | |
| 507 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 508 | + | |
| 509 | + # redirect to alternate login | |
| 510 | + if ( $ConfigObject->Get('CustomerPanelLoginURL') ) { | |
| 511 | + $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); | |
| 512 | + print $LayoutObject->Redirect( | |
| 513 | + ExtURL => $ConfigObject->Get('CustomerPanelLoginURL') | |
| 514 | + . "?Reason=InvalidSessionID;RequestedURL=$Param{RequestedURL}", | |
| 515 | + ); | |
| 516 | + } | |
| 517 | + | |
| 518 | + # show login screen | |
| 519 | + print $LayoutObject->CustomerLogin( | |
| 520 | + Title => 'Logout', | |
| 521 | + Message => Translatable('Session invalid. Please log in again.'), | |
| 522 | + %Param, | |
| 523 | + ); | |
| 524 | + return; | |
| 525 | + } | |
| 526 | + | |
| 527 | + # get session data | |
| 528 | + my %UserData = $SessionObject->GetSessionIDData( | |
| 529 | + SessionID => $Param{SessionID}, | |
| 530 | + ); | |
| 531 | + | |
| 532 | + # create new LayoutObject with new '%Param' and '%UserData' | |
| 533 | + $Kernel::OM->ObjectParamAdd( | |
| 534 | + 'Kernel::Output::HTML::Layout' => { | |
| 535 | + SetCookies => { | |
| 536 | + SessionIDCookie => $ParamObject->SetCookie( | |
| 537 | + Key => $Param{SessionName}, | |
| 538 | + Value => '', | |
| 539 | + Expires => '-1y', | |
| 540 | + Path => $ConfigObject->Get('ScriptAlias'), | |
| 541 | + Secure => scalar $CookieSecureAttribute, | |
| 542 | + HTTPOnly => 1, | |
| 543 | + ), | |
| 544 | + }, | |
| 545 | + %Param, | |
| 546 | + %UserData, | |
| 547 | + }, | |
| 548 | + ); | |
| 549 | + | |
| 550 | + $Kernel::OM->ObjectsDiscard( Objects => ['Kernel::Output::HTML::Layout'] ); | |
| 551 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 552 | + | |
| 553 | + # remove session id | |
| 554 | + if ( !$SessionObject->RemoveSessionID( SessionID => $Param{SessionID} ) ) { | |
| 555 | + $LayoutObject->CustomerFatalError( | |
| 556 | + Comment => Translatable('Please contact the administrator.') | |
| 557 | + ); | |
| 558 | + return; | |
| 559 | + } | |
| 560 | + | |
| 561 | + # redirect to alternate login | |
| 562 | + if ( $ConfigObject->Get('CustomerPanelLogoutURL') ) { | |
| 563 | + print $LayoutObject->Redirect( | |
| 564 | + ExtURL => $ConfigObject->Get('CustomerPanelLogoutURL') | |
| 565 | + . "?Reason=Logout", | |
| 566 | + ); | |
| 567 | + } | |
| 568 | + | |
| 569 | + # show logout screen | |
| 570 | + my $LogoutMessage = $LayoutObject->{LanguageObject}->Translate('Logout successful.'); | |
| 571 | + | |
| 572 | + $LayoutObject->Print( | |
| 573 | + Output => \$LayoutObject->CustomerLogin( | |
| 574 | + Title => 'Logout', | |
| 575 | + Message => $LogoutMessage, | |
| 576 | + MessageType => 'Success', | |
| 577 | + %Param, | |
| 578 | + ), | |
| 579 | + ); | |
| 580 | + return 1; | |
| 581 | + } | |
| 582 | + | |
| 583 | + # CustomerLostPassword | |
| 584 | + elsif ( $Param{Action} eq 'CustomerLostPassword' ) { | |
| 585 | + | |
| 586 | + # new layout object | |
| 587 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 588 | + | |
| 589 | + # check feature | |
| 590 | + if ( !$ConfigObject->Get('CustomerPanelLostPassword') ) { | |
| 591 | + | |
| 592 | + # show normal login | |
| 593 | + $LayoutObject->Print( | |
| 594 | + Output => \$LayoutObject->CustomerLogin( | |
| 595 | + Title => 'Login', | |
| 596 | + Message => Translatable('Feature not active!'), | |
| 597 | + ), | |
| 598 | + ); | |
| 599 | + return; | |
| 600 | + } | |
| 601 | + | |
| 602 | + # get params | |
| 603 | + my $User = $ParamObject->GetParam( Param => 'User' ) || ''; | |
| 604 | + my $Token = $ParamObject->GetParam( Param => 'Token' ) || ''; | |
| 605 | + | |
| 606 | + # get user login by token | |
| 607 | + if ( !$User && $Token ) { | |
| 608 | + my %UserList = $UserObject->SearchPreferences( | |
| 609 | + Key => 'UserToken', | |
| 610 | + Value => $Token, | |
| 611 | + ); | |
| 612 | + USER_ID: | |
| 613 | + for my $UserID ( sort keys %UserList ) { | |
| 614 | + my %UserData = $UserObject->CustomerUserDataGet( | |
| 615 | + User => $UserID, | |
| 616 | + Valid => 1, | |
| 617 | + ); | |
| 618 | + if (%UserData) { | |
| 619 | + $User = $UserData{UserLogin}; | |
| 620 | + last USER_ID; | |
| 621 | + } | |
| 622 | + } | |
| 623 | + } | |
| 624 | + | |
| 625 | + # get user data | |
| 626 | + my %UserData = $UserObject->CustomerUserDataGet( User => $User ); | |
| 627 | + | |
| 628 | + # verify customer user is valid when requesting password reset | |
| 629 | + my @ValidIDs = $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet(); | |
| 630 | + my $UserIsValid = grep { $UserData{ValidID} && $UserData{ValidID} == $_ } @ValidIDs; | |
| 631 | + if ( !$UserData{UserID} || !$UserIsValid ) { | |
| 632 | + | |
| 633 | + # Security: pretend that password reset instructions were actually sent to | |
| 634 | + # make sure that users cannot find out valid usernames by | |
| 635 | + # just trying and checking the result message. | |
| 636 | + $LayoutObject->Print( | |
| 637 | + Output => \$LayoutObject->CustomerLogin( | |
| 638 | + Title => 'Login', | |
| 639 | + Message => Translatable('Sent password reset instructions. Please check your email.'), | |
| 640 | + MessageType => 'Success', | |
| 641 | + ), | |
| 642 | + ); | |
| 643 | + return; | |
| 644 | + } | |
| 645 | + | |
| 646 | + # create email object | |
| 647 | + my $EmailObject = Kernel::System::Email->new( %{$Self} ); | |
| 648 | + | |
| 649 | + # send password reset token | |
| 650 | + if ( !$Token ) { | |
| 651 | + | |
| 652 | + # generate token | |
| 653 | + $UserData{Token} = $UserObject->TokenGenerate( | |
| 654 | + UserID => $UserData{UserID}, | |
| 655 | + ); | |
| 656 | + | |
| 657 | + # send token notify email with link | |
| 658 | + my $Body = $ConfigObject->Get('CustomerPanelBodyLostPasswordToken') | |
| 659 | + || 'ERROR: CustomerPanelBodyLostPasswordToken is missing!'; | |
| 660 | + my $Subject = $ConfigObject->Get('CustomerPanelSubjectLostPasswordToken') | |
| 661 | + || 'ERROR: CustomerPanelSubjectLostPasswordToken is missing!'; | |
| 662 | + for ( sort keys %UserData ) { | |
| 663 | + $Body =~ s/<OTRS_$_>/$UserData{$_}/gi; | |
| 664 | + } | |
| 665 | + my $Sent = $EmailObject->Send( | |
| 666 | + To => $UserData{UserEmail}, | |
| 667 | + Subject => $Subject, | |
| 668 | + Charset => $LayoutObject->{UserCharset}, | |
| 669 | + MimeType => 'text/plain', | |
| 670 | + Body => $Body | |
| 671 | + ); | |
| 672 | + if ( !$Sent->{Success} ) { | |
| 673 | + $LayoutObject->FatalError( | |
| 674 | + Comment => Translatable('Please contact the administrator.'), | |
| 675 | + ); | |
| 676 | + return; | |
| 677 | + } | |
| 678 | + $LayoutObject->Print( | |
| 679 | + Output => \$LayoutObject->CustomerLogin( | |
| 680 | + Title => 'Login', | |
| 681 | + Message => Translatable('Sent password reset instructions. Please check your email.'), | |
| 682 | + %Param, | |
| 683 | + MessageType => 'Success', | |
| 684 | + ), | |
| 685 | + ); | |
| 686 | + return 1; | |
| 687 | + | |
| 688 | + } | |
| 689 | + | |
| 690 | + # reset password | |
| 691 | + # check if token is valid | |
| 692 | + my $TokenValid = $UserObject->TokenCheck( | |
| 693 | + Token => $Token, | |
| 694 | + UserID => $UserData{UserID}, | |
| 695 | + ); | |
| 696 | + if ( !$TokenValid ) { | |
| 697 | + $LayoutObject->Print( | |
| 698 | + Output => \$LayoutObject->CustomerLogin( | |
| 699 | + Title => 'Login', | |
| 700 | + Message => Translatable('Invalid Token!'), | |
| 701 | + %Param, | |
| 702 | + ), | |
| 703 | + ); | |
| 704 | + return; | |
| 705 | + } | |
| 706 | + | |
| 707 | + # get new password | |
| 708 | + $UserData{NewPW} = $UserObject->GenerateRandomPassword(); | |
| 709 | + | |
| 710 | + # update new password | |
| 711 | + my $Success = $UserObject->SetPassword( | |
| 712 | + UserLogin => $User, | |
| 713 | + PW => $UserData{NewPW} | |
| 714 | + ); | |
| 715 | + | |
| 716 | + if ( !$Success ) { | |
| 717 | + $LayoutObject->Print( | |
| 718 | + Output => \$LayoutObject->CustomerLogin( | |
| 719 | + Title => 'Login', | |
| 720 | + Message => Translatable('Reset password unsuccessful. Please contact the administrator.'), | |
| 721 | + User => $User, | |
| 722 | + ), | |
| 723 | + ); | |
| 724 | + return; | |
| 725 | + } | |
| 726 | + | |
| 727 | + # send notify email | |
| 728 | + my $Body = $ConfigObject->Get('CustomerPanelBodyLostPassword') | |
| 729 | + || 'New Password is: <OTRS_NEWPW>'; | |
| 730 | + my $Subject = $ConfigObject->Get('CustomerPanelSubjectLostPassword') | |
| 731 | + || 'New Password!'; | |
| 732 | + for ( sort keys %UserData ) { | |
| 733 | + $Body =~ s/<OTRS_$_>/$UserData{$_}/gi; | |
| 734 | + } | |
| 735 | + my $Sent = $EmailObject->Send( | |
| 736 | + To => $UserData{UserEmail}, | |
| 737 | + Subject => $Subject, | |
| 738 | + Charset => $LayoutObject->{UserCharset}, | |
| 739 | + MimeType => 'text/plain', | |
| 740 | + Body => $Body | |
| 741 | + ); | |
| 742 | + if ( !$Sent->{Success} ) { | |
| 743 | + $LayoutObject->CustomerFatalError( | |
| 744 | + Comment => Translatable('Please contact the administrator.') | |
| 745 | + ); | |
| 746 | + return; | |
| 747 | + } | |
| 748 | + my $Message = $LayoutObject->{LanguageObject}->Translate( | |
| 749 | + 'Sent new password to %s. Please check your email.', | |
| 750 | + $UserData{UserEmail}, | |
| 751 | + ); | |
| 752 | + $LayoutObject->Print( | |
| 753 | + Output => \$LayoutObject->CustomerLogin( | |
| 754 | + Title => 'Login', | |
| 755 | + Message => $Message, | |
| 756 | + User => $User, | |
| 757 | + MessageType => 'Success', | |
| 758 | + ), | |
| 759 | + ); | |
| 760 | + return 1; | |
| 761 | + } | |
| 762 | + | |
| 763 | + # create new customer account | |
| 764 | + elsif ( $Param{Action} eq 'CustomerCreateAccount' ) { | |
| 765 | + | |
| 766 | + # new layout object | |
| 767 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 768 | + | |
| 769 | + # check feature | |
| 770 | + if ( !$ConfigObject->Get('CustomerPanelCreateAccount') ) { | |
| 771 | + | |
| 772 | + # show normal login | |
| 773 | + $LayoutObject->Print( | |
| 774 | + Output => \$LayoutObject->CustomerLogin( | |
| 775 | + Title => 'Login', | |
| 776 | + Message => Translatable('Feature not active!'), | |
| 777 | + ), | |
| 778 | + ); | |
| 779 | + return; | |
| 780 | + } | |
| 781 | + | |
| 782 | + # get params | |
| 783 | + my %GetParams; | |
| 784 | + for my $Entry ( @{ $ConfigObject->Get('CustomerUser')->{Map} } ) { | |
| 785 | + $GetParams{ $Entry->[0] } = $ParamObject->GetParam( Param => $Entry->[1] ) | |
| 786 | + || ''; | |
| 787 | + } | |
| 788 | + $GetParams{ValidID} = 1; | |
| 789 | + | |
| 790 | + # check needed params | |
| 791 | + if ( !$GetParams{UserCustomerID} ) { | |
| 792 | + $GetParams{UserCustomerID} = $GetParams{UserEmail}; | |
| 793 | + } | |
| 794 | + if ( !$GetParams{UserLogin} ) { | |
| 795 | + $GetParams{UserLogin} = $GetParams{UserEmail}; | |
| 796 | + } | |
| 797 | + | |
| 798 | + # get new password | |
| 799 | + $GetParams{UserPassword} = $UserObject->GenerateRandomPassword(); | |
| 800 | + | |
| 801 | + # get user data | |
| 802 | + my %UserData = $UserObject->CustomerUserDataGet( User => $GetParams{UserLogin} ); | |
| 803 | + if ( $UserData{UserID} || !$GetParams{UserLogin} ) { | |
| 804 | + | |
| 805 | + # send data to JS | |
| 806 | + $LayoutObject->AddJSData( | |
| 807 | + Key => 'SignupError', | |
| 808 | + Value => 1, | |
| 809 | + ); | |
| 810 | + | |
| 811 | + $LayoutObject->Print( | |
| 812 | + Output => \$LayoutObject->CustomerLogin( | |
| 813 | + Title => 'Login', | |
| 814 | + Message => | |
| 815 | + Translatable('This e-mail address already exists. Please log in or reset your password.'), | |
| 816 | + UserTitle => $GetParams{UserTitle}, | |
| 817 | + UserFirstname => $GetParams{UserFirstname}, | |
| 818 | + UserLastname => $GetParams{UserLastname}, | |
| 819 | + UserEmail => $GetParams{UserEmail}, | |
| 820 | + ), | |
| 821 | + ); | |
| 822 | + return; | |
| 823 | + } | |
| 824 | + | |
| 825 | + # check for mail address restrictions | |
| 826 | + my @Whitelist = @{ | |
| 827 | + $ConfigObject->Get('CustomerPanelCreateAccount::MailRestrictions::Whitelist') // [] | |
| 828 | + }; | |
| 829 | + my @Blacklist = @{ | |
| 830 | + $ConfigObject->Get('CustomerPanelCreateAccount::MailRestrictions::Blacklist') // [] | |
| 831 | + }; | |
| 832 | + | |
| 833 | + my $WhitelistMatched; | |
| 834 | + for my $WhitelistEntry (@Whitelist) { | |
| 835 | + my $Regex = eval {qr/$WhitelistEntry/i}; | |
| 836 | + if ($@) { | |
| 837 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 838 | + Priority => 'error', | |
| 839 | + Message => | |
| 840 | + $LayoutObject->{LanguageObject}->Translate( | |
| 841 | + 'The customer panel mail address whitelist contains the invalid regular expression $WhitelistEntry, please check and correct it.' | |
| 842 | + ), | |
| 843 | + ); | |
| 844 | + } | |
| 845 | + elsif ( $GetParams{UserEmail} =~ $Regex ) { | |
| 846 | + $WhitelistMatched++; | |
| 847 | + } | |
| 848 | + } | |
| 849 | + my $BlacklistMatched; | |
| 850 | + for my $BlacklistEntry (@Blacklist) { | |
| 851 | + my $Regex = eval {qr/$BlacklistEntry/i}; | |
| 852 | + if ($@) { | |
| 853 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 854 | + Priority => 'error', | |
| 855 | + Message => | |
| 856 | + $LayoutObject->{LanguageObject}->Translate( | |
| 857 | + 'The customer panel mail address blacklist contains the invalid regular expression $BlacklistEntry, please check and correct it.' | |
| 858 | + ), | |
| 859 | + ); | |
| 860 | + } | |
| 861 | + elsif ( $GetParams{UserEmail} =~ $Regex ) { | |
| 862 | + $BlacklistMatched++; | |
| 863 | + } | |
| 864 | + } | |
| 865 | + | |
| 866 | + if ( ( @Whitelist && !$WhitelistMatched ) || ( @Blacklist && $BlacklistMatched ) ) { | |
| 867 | + | |
| 868 | + # send data to JS | |
| 869 | + $LayoutObject->AddJSData( | |
| 870 | + Key => 'SignupError', | |
| 871 | + Value => 1, | |
| 872 | + ); | |
| 873 | + | |
| 874 | + $LayoutObject->Print( | |
| 875 | + Output => \$LayoutObject->CustomerLogin( | |
| 876 | + Title => 'Login', | |
| 877 | + Message => | |
| 878 | + Translatable('This email address is not allowed to register. Please contact support staff.'), | |
| 879 | + UserTitle => $GetParams{UserTitle}, | |
| 880 | + UserFirstname => $GetParams{UserFirstname}, | |
| 881 | + UserLastname => $GetParams{UserLastname}, | |
| 882 | + UserEmail => $GetParams{UserEmail}, | |
| 883 | + ), | |
| 884 | + ); | |
| 885 | + | |
| 886 | + return; | |
| 887 | + } | |
| 888 | + | |
| 889 | + # create account | |
| 890 | + my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); | |
| 891 | + | |
| 892 | + my $Now = $DateTimeObject->ToString(); | |
| 893 | + | |
| 894 | + my $Add = $UserObject->CustomerUserAdd( | |
| 895 | + %GetParams, | |
| 896 | + Comment => $LayoutObject->{LanguageObject}->Translate( 'Added via Customer Panel (%s)', $Now ), | |
| 897 | + ValidID => 1, | |
| 898 | + UserID => $ConfigObject->Get('CustomerPanelUserID'), | |
| 899 | + ); | |
| 900 | + if ( !$Add ) { | |
| 901 | + | |
| 902 | + # send data to JS | |
| 903 | + $LayoutObject->AddJSData( | |
| 904 | + Key => 'SignupError', | |
| 905 | + Value => 1, | |
| 906 | + ); | |
| 907 | + | |
| 908 | + $LayoutObject->Print( | |
| 909 | + Output => \$LayoutObject->CustomerLogin( | |
| 910 | + Title => 'Login', | |
| 911 | + Message => Translatable('Customer user can\'t be added!'), | |
| 912 | + UserTitle => $GetParams{UserTitle}, | |
| 913 | + UserFirstname => $GetParams{UserFirstname}, | |
| 914 | + UserLastname => $GetParams{UserLastname}, | |
| 915 | + UserEmail => $GetParams{UserEmail}, | |
| 916 | + ), | |
| 917 | + ); | |
| 918 | + return; | |
| 919 | + } | |
| 920 | + | |
| 921 | + # send notify email | |
| 922 | + my $EmailObject = Kernel::System::Email->new( %{$Self} ); | |
| 923 | + my $Body = $ConfigObject->Get('CustomerPanelBodyNewAccount') | |
| 924 | + || 'No Config Option found!'; | |
| 925 | + my $Subject = $ConfigObject->Get('CustomerPanelSubjectNewAccount') | |
| 926 | + || 'New OTRS Account!'; | |
| 927 | + for ( sort keys %GetParams ) { | |
| 928 | + $Body =~ s/<OTRS_$_>/$GetParams{$_}/gi; | |
| 929 | + } | |
| 930 | + | |
| 931 | + # send account info | |
| 932 | + my $Sent = $EmailObject->Send( | |
| 933 | + To => $GetParams{UserEmail}, | |
| 934 | + Subject => $Subject, | |
| 935 | + Charset => $LayoutObject->{UserCharset}, | |
| 936 | + MimeType => 'text/plain', | |
| 937 | + Body => $Body | |
| 938 | + ); | |
| 939 | + if ( !$Sent->{Success} ) { | |
| 940 | + my $Output = $LayoutObject->CustomerHeader( | |
| 941 | + Area => 'Core', | |
| 942 | + Title => 'Error' | |
| 943 | + ); | |
| 944 | + $Output .= $LayoutObject->CustomerWarning( | |
| 945 | + Comment => Translatable('Can\'t send account info!') | |
| 946 | + ); | |
| 947 | + $Output .= $LayoutObject->CustomerFooter(); | |
| 948 | + $LayoutObject->Print( Output => \$Output ); | |
| 949 | + return; | |
| 950 | + } | |
| 951 | + | |
| 952 | + # show sent account info | |
| 953 | + if ( $ConfigObject->Get('CustomerPanelLoginURL') ) { | |
| 954 | + | |
| 955 | + # redirect to alternate login | |
| 956 | + $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); | |
| 957 | + print $LayoutObject->Redirect( | |
| 958 | + ExtURL => $ConfigObject->Get('CustomerPanelLoginURL') | |
| 959 | + . "?RequestedURL=$Param{RequestedURL};User=$GetParams{UserLogin};" | |
| 960 | + . "Email=$GetParams{UserEmail};Reason=NewAccountCreated", | |
| 961 | + ); | |
| 962 | + return 1; | |
| 963 | + } | |
| 964 | + | |
| 965 | + my $AccountCreatedMessage = $LayoutObject->{LanguageObject}->Translate( | |
| 966 | + 'New account created. Sent login information to %s. Please check your email.', | |
| 967 | + $GetParams{UserEmail}, | |
| 968 | + ); | |
| 969 | + | |
| 970 | + # login screen | |
| 971 | + $LayoutObject->Print( | |
| 972 | + Output => \$LayoutObject->CustomerLogin( | |
| 973 | + Title => 'Login', | |
| 974 | + Message => $AccountCreatedMessage, | |
| 975 | + User => $GetParams{UserLogin}, | |
| 976 | + MessageType => 'Success', | |
| 977 | + ), | |
| 978 | + ); | |
| 979 | + return 1; | |
| 980 | + } | |
| 981 | + | |
| 982 | + # show login site | |
| 983 | + elsif ( !$Param{SessionID} ) { | |
| 984 | + | |
| 985 | + # new layout object | |
| 986 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 987 | + | |
| 988 | + # create AuthObject | |
| 989 | + my $AuthObject = $Kernel::OM->Get('Kernel::System::CustomerAuth'); | |
| 990 | + if ( $AuthObject->GetOption( What => 'PreAuth' ) ) { | |
| 991 | + | |
| 992 | + # automatic login | |
| 993 | + $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); | |
| 994 | + print $LayoutObject->Redirect( | |
| 995 | + OP => "Action=PreLogin;RequestedURL=$Param{RequestedURL}", | |
| 996 | + ); | |
| 997 | + return; | |
| 998 | + } | |
| 999 | + elsif ( $ConfigObject->Get('CustomerPanelLoginURL') ) { | |
| 1000 | + | |
| 1001 | + # redirect to alternate login | |
| 1002 | + $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); | |
| 1003 | + print $LayoutObject->Redirect( | |
| 1004 | + ExtURL => $ConfigObject->Get('CustomerPanelLoginURL') | |
| 1005 | + . "?RequestedURL=$Param{RequestedURL}", | |
| 1006 | + ); | |
| 1007 | + return; | |
| 1008 | + } | |
| 1009 | + | |
| 1010 | + # login screen | |
| 1011 | + $LayoutObject->Print( | |
| 1012 | + Output => \$LayoutObject->CustomerLogin( | |
| 1013 | + Title => 'Login', | |
| 1014 | + %Param, | |
| 1015 | + ), | |
| 1016 | + ); | |
| 1017 | + return 1; | |
| 1018 | + } | |
| 1019 | + | |
| 1020 | + # run modules if a version value exists | |
| 1021 | + elsif ( $Kernel::OM->Get('Kernel::System::Main')->Require("Kernel::Modules::$Param{Action}") ) { | |
| 1022 | + | |
| 1023 | + # check session id | |
| 1024 | + if ( !$SessionObject->CheckSessionID( SessionID => $Param{SessionID} ) ) { | |
| 1025 | + | |
| 1026 | + # create new LayoutObject with new '%Param' | |
| 1027 | + $Kernel::OM->ObjectParamAdd( | |
| 1028 | + 'Kernel::Output::HTML::Layout' => { | |
| 1029 | + SetCookies => { | |
| 1030 | + SessionIDCookie => $ParamObject->SetCookie( | |
| 1031 | + Key => $Param{SessionName}, | |
| 1032 | + Value => '', | |
| 1033 | + Expires => '-1y', | |
| 1034 | + Path => $ConfigObject->Get('ScriptAlias'), | |
| 1035 | + Secure => scalar $CookieSecureAttribute, | |
| 1036 | + HTTPOnly => 1, | |
| 1037 | + ), | |
| 1038 | + }, | |
| 1039 | + %Param, | |
| 1040 | + } | |
| 1041 | + ); | |
| 1042 | + | |
| 1043 | + $Kernel::OM->ObjectsDiscard( Objects => ['Kernel::Output::HTML::Layout'] ); | |
| 1044 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 1045 | + | |
| 1046 | + # create AuthObject | |
| 1047 | + my $AuthObject = $Kernel::OM->Get('Kernel::System::CustomerAuth'); | |
| 1048 | + if ( $AuthObject->GetOption( What => 'PreAuth' ) ) { | |
| 1049 | + | |
| 1050 | + # automatic re-login | |
| 1051 | + $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); | |
| 1052 | + print $LayoutObject->Redirect( | |
| 1053 | + OP => "?Action=PreLogin&RequestedURL=$Param{RequestedURL}", | |
| 1054 | + ); | |
| 1055 | + return; | |
| 1056 | + } | |
| 1057 | + | |
| 1058 | + # redirect to alternate login | |
| 1059 | + elsif ( $ConfigObject->Get('CustomerPanelLoginURL') ) { | |
| 1060 | + | |
| 1061 | + # redirect to alternate login | |
| 1062 | + $Param{RequestedURL} = $LayoutObject->LinkEncode( $Param{RequestedURL} ); | |
| 1063 | + print $LayoutObject->Redirect( | |
| 1064 | + ExtURL => $ConfigObject->Get('CustomerPanelLoginURL') | |
| 1065 | + . "?Reason=InvalidSessionID;RequestedURL=$Param{RequestedURL}", | |
| 1066 | + ); | |
| 1067 | + return; | |
| 1068 | + } | |
| 1069 | + | |
| 1070 | + # show login | |
| 1071 | + $LayoutObject->Print( | |
| 1072 | + Output => \$LayoutObject->CustomerLogin( | |
| 1073 | + Title => 'Login', | |
| 1074 | + Message => | |
| 1075 | + $LayoutObject->{LanguageObject}->Translate( $SessionObject->SessionIDErrorMessage() ), | |
| 1076 | + %Param, | |
| 1077 | + ), | |
| 1078 | + ); | |
| 1079 | + return; | |
| 1080 | + } | |
| 1081 | + | |
| 1082 | + # get session data | |
| 1083 | + my %UserData = $SessionObject->GetSessionIDData( | |
| 1084 | + SessionID => $Param{SessionID}, | |
| 1085 | + ); | |
| 1086 | + | |
| 1087 | + # check needed data | |
| 1088 | + if ( !$UserData{UserID} || !$UserData{UserLogin} || $UserData{UserType} ne 'Customer' ) { | |
| 1089 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 1090 | + | |
| 1091 | + # redirect to alternate login | |
| 1092 | + if ( $ConfigObject->Get('CustomerPanelLoginURL') ) { | |
| 1093 | + print $LayoutObject->Redirect( | |
| 1094 | + ExtURL => $ConfigObject->Get('CustomerPanelLoginURL') | |
| 1095 | + . "?Reason=SystemError", | |
| 1096 | + ); | |
| 1097 | + return; | |
| 1098 | + } | |
| 1099 | + | |
| 1100 | + # show login screen | |
| 1101 | + $LayoutObject->Print( | |
| 1102 | + Output => \$LayoutObject->CustomerLogin( | |
| 1103 | + Title => 'Error', | |
| 1104 | + Message => Translatable('Error: invalid session.'), | |
| 1105 | + %Param, | |
| 1106 | + ), | |
| 1107 | + ); | |
| 1108 | + return; | |
| 1109 | + } | |
| 1110 | + | |
| 1111 | + # module registry | |
| 1112 | + my $ModuleReg = $ConfigObject->Get('CustomerFrontend::Module')->{ $Param{Action} }; | |
| 1113 | + if ( !$ModuleReg ) { | |
| 1114 | + | |
| 1115 | + # new layout object | |
| 1116 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 1117 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 1118 | + Priority => 'error', | |
| 1119 | + Message => | |
| 1120 | + "Module Kernel::Modules::$Param{Action} not registered in Kernel/Config.pm!", | |
| 1121 | + ); | |
| 1122 | + $LayoutObject->CustomerFatalError( | |
| 1123 | + Comment => Translatable('Please contact the administrator.'), | |
| 1124 | + ); | |
| 1125 | + return; | |
| 1126 | + } | |
| 1127 | + | |
| 1128 | + # module permission check for action | |
| 1129 | + if ( | |
| 1130 | + ref $ModuleReg->{GroupRo} eq 'ARRAY' | |
| 1131 | + && !scalar @{ $ModuleReg->{GroupRo} } | |
| 1132 | + && ref $ModuleReg->{Group} eq 'ARRAY' | |
| 1133 | + && !scalar @{ $ModuleReg->{Group} } | |
| 1134 | + ) | |
| 1135 | + { | |
| 1136 | + $Param{AccessRo} = 1; | |
| 1137 | + $Param{AccessRw} = 1; | |
| 1138 | + } | |
| 1139 | + else { | |
| 1140 | + | |
| 1141 | + ( $Param{AccessRo}, $Param{AccessRw} ) = $Self->_CheckModulePermission( | |
| 1142 | + ModuleReg => $ModuleReg, | |
| 1143 | + %UserData, | |
| 1144 | + ); | |
| 1145 | + | |
| 1146 | + if ( !$Param{AccessRo} ) { | |
| 1147 | + | |
| 1148 | + # new layout object | |
| 1149 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 1150 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 1151 | + Priority => 'error', | |
| 1152 | + Message => 'No Permission to use this frontend action module!' | |
| 1153 | + ); | |
| 1154 | + $LayoutObject->CustomerFatalError( | |
| 1155 | + Comment => Translatable('Please contact the administrator.'), | |
| 1156 | + ); | |
| 1157 | + return; | |
| 1158 | + } | |
| 1159 | + | |
| 1160 | + } | |
| 1161 | + | |
| 1162 | + my $NavigationConfig = $ConfigObject->Get('CustomerFrontend::Navigation')->{ $Param{Action} }; | |
| 1163 | + | |
| 1164 | + # module permission check for submenu item | |
| 1165 | + if ( IsHashRefWithData($NavigationConfig) ) { | |
| 1166 | + | |
| 1167 | + KEY: | |
| 1168 | + for my $Key ( sort keys %{$NavigationConfig} ) { | |
| 1169 | + next KEY if $Key !~ m/^\d+/i; | |
| 1170 | + next KEY if $Param{RequestedURL} !~ m/Subaction/i; | |
| 1171 | + | |
| 1172 | + my @ModuleNavigationConfigs; | |
| 1173 | + | |
| 1174 | + # FIXME: Support both old (HASH) and new (ARRAY of HASH) navigation configurations, for reasons of | |
| 1175 | + # backwards compatibility. Once we are sure everything has been migrated correctly, support for | |
| 1176 | + # HASH-only configuration can be dropped in future major release. | |
| 1177 | + if ( IsHashRefWithData( $NavigationConfig->{$Key} ) ) { | |
| 1178 | + push @ModuleNavigationConfigs, $NavigationConfig->{$Key}; | |
| 1179 | + } | |
| 1180 | + elsif ( IsArrayRefWithData( $NavigationConfig->{$Key} ) ) { | |
| 1181 | + push @ModuleNavigationConfigs, @{ $NavigationConfig->{$Key} }; | |
| 1182 | + } | |
| 1183 | + | |
| 1184 | + # Skip incompatible configuration. | |
| 1185 | + else { | |
| 1186 | + next KEY; | |
| 1187 | + } | |
| 1188 | + | |
| 1189 | + ITEM: | |
| 1190 | + for my $Item (@ModuleNavigationConfigs) { | |
| 1191 | + if ( | |
| 1192 | + $Item->{Link} =~ m/Subaction=/i | |
| 1193 | + && $Item->{Link} !~ m/$Param{Subaction}/i | |
| 1194 | + ) | |
| 1195 | + { | |
| 1196 | + next ITEM; | |
| 1197 | + } | |
| 1198 | + $Param{AccessRo} = 0; | |
| 1199 | + $Param{AccessRw} = 0; | |
| 1200 | + | |
| 1201 | + # module permission check for submenu item | |
| 1202 | + if ( | |
| 1203 | + ref $Item->{GroupRo} eq 'ARRAY' | |
| 1204 | + && !scalar @{ $Item->{GroupRo} } | |
| 1205 | + && ref $Item->{Group} eq 'ARRAY' | |
| 1206 | + && !scalar @{ $Item->{Group} } | |
| 1207 | + ) | |
| 1208 | + { | |
| 1209 | + $Param{AccessRo} = 1; | |
| 1210 | + $Param{AccessRw} = 1; | |
| 1211 | + } | |
| 1212 | + else { | |
| 1213 | + | |
| 1214 | + ( $Param{AccessRo}, $Param{AccessRw} ) = $Self->_CheckModulePermission( | |
| 1215 | + ModuleReg => $Item, | |
| 1216 | + %UserData, | |
| 1217 | + ); | |
| 1218 | + | |
| 1219 | + if ( !$Param{AccessRo} ) { | |
| 1220 | + | |
| 1221 | + # new layout object | |
| 1222 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 1223 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 1224 | + Priority => 'error', | |
| 1225 | + Message => 'No Permission to use this frontend subaction module!' | |
| 1226 | + ); | |
| 1227 | + $LayoutObject->CustomerFatalError( | |
| 1228 | + Comment => Translatable('Please contact the administrator.') | |
| 1229 | + ); | |
| 1230 | + return; | |
| 1231 | + } | |
| 1232 | + } | |
| 1233 | + } | |
| 1234 | + } | |
| 1235 | + } | |
| 1236 | + | |
| 1237 | + # create new LayoutObject with new '%Param' and '%UserData' | |
| 1238 | + $Kernel::OM->ObjectParamAdd( | |
| 1239 | + 'Kernel::Output::HTML::Layout' => { | |
| 1240 | + %Param, | |
| 1241 | + %UserData, | |
| 1242 | + ModuleReg => $ModuleReg, | |
| 1243 | + }, | |
| 1244 | + ); | |
| 1245 | + | |
| 1246 | + $Kernel::OM->ObjectsDiscard( Objects => ['Kernel::Output::HTML::Layout'] ); | |
| 1247 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 1248 | + | |
| 1249 | + # update last request time | |
| 1250 | + if ( | |
| 1251 | + !$ParamObject->IsAJAXRequest() | |
| 1252 | + || $Param{Action} eq 'CustomerVideoChat' | |
| 1253 | + ) | |
| 1254 | + { | |
| 1255 | + my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); | |
| 1256 | + | |
| 1257 | + $SessionObject->UpdateSessionID( | |
| 1258 | + SessionID => $Param{SessionID}, | |
| 1259 | + Key => 'UserLastRequest', | |
| 1260 | + Value => $DateTimeObject->ToEpoch(), | |
| 1261 | + ); | |
| 1262 | + } | |
| 1263 | + | |
| 1264 | + # pre application module | |
| 1265 | + my $PreModule = $ConfigObject->Get('CustomerPanelPreApplicationModule'); | |
| 1266 | + if ($PreModule) { | |
| 1267 | + my %PreModuleList; | |
| 1268 | + if ( ref $PreModule eq 'HASH' ) { | |
| 1269 | + %PreModuleList = %{$PreModule}; | |
| 1270 | + } | |
| 1271 | + else { | |
| 1272 | + $PreModuleList{Init} = $PreModule; | |
| 1273 | + } | |
| 1274 | + | |
| 1275 | + MODULE: | |
| 1276 | + for my $PreModuleKey ( sort keys %PreModuleList ) { | |
| 1277 | + my $PreModule = $PreModuleList{$PreModuleKey}; | |
| 1278 | + next MODULE if !$PreModule; | |
| 1279 | + next MODULE if !$Kernel::OM->Get('Kernel::System::Main')->Require($PreModule); | |
| 1280 | + | |
| 1281 | + # debug info | |
| 1282 | + if ( $Self->{Debug} ) { | |
| 1283 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 1284 | + Priority => 'debug', | |
| 1285 | + Message => "CustomerPanelPreApplication module $PreModule is used.", | |
| 1286 | + ); | |
| 1287 | + } | |
| 1288 | + | |
| 1289 | + # use module | |
| 1290 | + my $PreModuleObject = $PreModule->new( | |
| 1291 | + %Param, | |
| 1292 | + %UserData, | |
| 1293 | + | |
| 1294 | + ); | |
| 1295 | + my $Output = $PreModuleObject->PreRun(); | |
| 1296 | + if ($Output) { | |
| 1297 | + $LayoutObject->Print( Output => \$Output ); | |
| 1298 | + return 1; | |
| 1299 | + } | |
| 1300 | + } | |
| 1301 | + } | |
| 1302 | + | |
| 1303 | + # debug info | |
| 1304 | + if ( $Self->{Debug} ) { | |
| 1305 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 1306 | + Priority => 'debug', | |
| 1307 | + Message => 'Kernel::Modules::' . $Param{Action} . '->new', | |
| 1308 | + ); | |
| 1309 | + } | |
| 1310 | + | |
| 1311 | + my $FrontendObject = ( 'Kernel::Modules::' . $Param{Action} )->new( | |
| 1312 | + %Param, | |
| 1313 | + %UserData, | |
| 1314 | + ModuleReg => $ModuleReg, | |
| 1315 | + Debug => $Self->{Debug}, | |
| 1316 | + ); | |
| 1317 | + | |
| 1318 | + # debug info | |
| 1319 | + if ( $Self->{Debug} ) { | |
| 1320 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 1321 | + Priority => 'debug', | |
| 1322 | + Message => 'Kernel::Modules::' . $Param{Action} . '->run', | |
| 1323 | + ); | |
| 1324 | + } | |
| 1325 | + | |
| 1326 | + # ->Run $Action with $FrontendObject | |
| 1327 | + $LayoutObject->Print( Output => \$FrontendObject->Run() ); | |
| 1328 | + | |
| 1329 | + # log request time | |
| 1330 | + if ( $ConfigObject->Get('PerformanceLog') ) { | |
| 1331 | + if ( ( !$QueryString && $Param{Action} ) || $QueryString !~ /Action=/ ) { | |
| 1332 | + $QueryString = 'Action=' . $Param{Action} . ';Subaction=' . $Param{Subaction}; | |
| 1333 | + } | |
| 1334 | + my $File = $ConfigObject->Get('PerformanceLog::File'); | |
| 1335 | + ## no critic | |
| 1336 | + if ( open my $Out, '>>', $File ) { | |
| 1337 | + ## use critic | |
| 1338 | + print $Out time() | |
| 1339 | + . '::Customer::' | |
| 1340 | + . ( time() - $Self->{PerformanceLogStart} ) | |
| 1341 | + . "::$UserData{UserLogin}::$QueryString\n"; | |
| 1342 | + close $Out; | |
| 1343 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 1344 | + Priority => 'debug', | |
| 1345 | + Message => 'Response::Customer: ' | |
| 1346 | + . ( time() - $Self->{PerformanceLogStart} ) | |
| 1347 | + . "s taken (URL:$QueryString:$UserData{UserLogin})", | |
| 1348 | + ); | |
| 1349 | + } | |
| 1350 | + else { | |
| 1351 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 1352 | + Priority => 'error', | |
| 1353 | + Message => "Can't write $File: $!", | |
| 1354 | + ); | |
| 1355 | + } | |
| 1356 | + } | |
| 1357 | + return 1; | |
| 1358 | + } | |
| 1359 | + | |
| 1360 | + # print an error screen | |
| 1361 | + my %Data = $SessionObject->GetSessionIDData( | |
| 1362 | + SessionID => $Param{SessionID}, | |
| 1363 | + ); | |
| 1364 | + $Kernel::OM->ObjectParamAdd( | |
| 1365 | + 'Kernel::Output::HTML::Layout' => { | |
| 1366 | + %Param, | |
| 1367 | + %Data, | |
| 1368 | + }, | |
| 1369 | + ); | |
| 1370 | + my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); | |
| 1371 | + $LayoutObject->CustomerFatalError( | |
| 1372 | + Comment => Translatable('Please contact the administrator.'), | |
| 1373 | + ); | |
| 1374 | + return; | |
| 1375 | +} | |
| 1376 | + | |
| 1377 | +=begin Internal: | |
| 1378 | + | |
| 1379 | +=head2 _CheckModulePermission() | |
| 1380 | + | |
| 1381 | +module permission check | |
| 1382 | + | |
| 1383 | + ($AccessRo, $AccessRw = $AutoResponseObject->_CheckModulePermission( | |
| 1384 | + ModuleReg => $ModuleReg, | |
| 1385 | + %UserData, | |
| 1386 | + ); | |
| 1387 | + | |
| 1388 | +=cut | |
| 1389 | + | |
| 1390 | +sub _CheckModulePermission { | |
| 1391 | + my ( $Self, %Param ) = @_; | |
| 1392 | + | |
| 1393 | + my $AccessRo = 0; | |
| 1394 | + my $AccessRw = 0; | |
| 1395 | + | |
| 1396 | + PERMISSION: | |
| 1397 | + for my $Permission (qw(GroupRo Group)) { | |
| 1398 | + my $AccessOk = 0; | |
| 1399 | + my $Group = $Param{ModuleReg}->{$Permission}; | |
| 1400 | + | |
| 1401 | + next PERMISSION if !$Group; | |
| 1402 | + | |
| 1403 | + my $GroupObject = $Kernel::OM->Get('Kernel::System::CustomerGroup'); | |
| 1404 | + | |
| 1405 | + if ( IsArrayRefWithData($Group) ) { | |
| 1406 | + GROUP: | |
| 1407 | + for my $Item ( @{$Group} ) { | |
| 1408 | + next GROUP if !$Item; | |
| 1409 | + next GROUP if !$GroupObject->PermissionCheck( | |
| 1410 | + UserID => $Param{UserID}, | |
| 1411 | + GroupName => $Item, | |
| 1412 | + Type => $Permission eq 'GroupRo' ? 'ro' : 'rw', | |
| 1413 | + ); | |
| 1414 | + | |
| 1415 | + $AccessOk = 1; | |
| 1416 | + last GROUP; | |
| 1417 | + } | |
| 1418 | + } | |
| 1419 | + else { | |
| 1420 | + my $HasPermission = $GroupObject->PermissionCheck( | |
| 1421 | + UserID => $Param{UserID}, | |
| 1422 | + GroupName => $Group, | |
| 1423 | + Type => $Permission eq 'GroupRo' ? 'ro' : 'rw', | |
| 1424 | + ); | |
| 1425 | + if ($HasPermission) { | |
| 1426 | + $AccessOk = 1; | |
| 1427 | + } | |
| 1428 | + } | |
| 1429 | + if ( $Permission eq 'Group' && $AccessOk ) { | |
| 1430 | + $AccessRo = 1; | |
| 1431 | + $AccessRw = 1; | |
| 1432 | + } | |
| 1433 | + elsif ( $Permission eq 'GroupRo' && $AccessOk ) { | |
| 1434 | + $AccessRo = 1; | |
| 1435 | + } | |
| 1436 | + } | |
| 1437 | + | |
| 1438 | + return ( $AccessRo, $AccessRw ); | |
| 1439 | +} | |
| 1440 | + | |
| 1441 | +=end Internal: | |
| 1442 | + | |
| 1443 | +=cut | |
| 1444 | + | |
| 1445 | +sub DESTROY { | |
| 1446 | + my $Self = shift; | |
| 1447 | + | |
| 1448 | + # debug info | |
| 1449 | + if ( $Self->{Debug} ) { | |
| 1450 | + $Kernel::OM->Get('Kernel::System::Log')->Log( | |
| 1451 | + Priority => 'debug', | |
| 1452 | + Message => 'Global handle stopped.', | |
| 1453 | + ); | |
| 1454 | + } | |
| 1455 | + | |
| 1456 | + return 1; | |
| 1457 | +} | |
| 1458 | + | |
| 1459 | +# CAS Custom | |
| 1460 | +=item | |
| 1461 | + Check if it is a CAS logout - if true, logs out user for the session ticket | |
| 1462 | +=cut | |
| 1463 | +sub CASLogout { | |
| 1464 | + my $Self = shift; | |
| 1465 | + | |
| 1466 | + # get post data | |
| 1467 | + my $cgi = new CGI; | |
| 1468 | + my $request = uri_unescape($cgi->query_string()); | |
| 1469 | + | |
| 1470 | + # check if it is CAS logout | |
| 1471 | + if ($request =~ /(logoutRequest=<samlp:LogoutRequest)/) { | |
| 1472 | + | |
| 1473 | + (my $ticket) = ($request =~ /<samlp:SessionIndex>([^<]+)/); | |
| 1474 | + if (! $ticket) { | |
| 1475 | + return 1; | |
| 1476 | + } else { | |
| 1477 | + } | |
| 1478 | + | |
| 1479 | + my $DBObject = $Kernel::OM->Get("Kernel::System::DB"); | |
| 1480 | + | |
| 1481 | + # get login for session | |
| 1482 | + my $sqlLogin = 'SELECT UserLogin from cas_session where Ticket=?'; | |
| 1483 | + $DBObject->Prepare(SQL => $sqlLogin, Bind => [\$ticket]); | |
| 1484 | + my $login; | |
| 1485 | + while (my @Row = $DBObject->FetchrowArray()) { | |
| 1486 | + $login = $Row[0]; | |
| 1487 | + } | |
| 1488 | + | |
| 1489 | + # if a login was found, kill the user session | |
| 1490 | + if ($login) { | |
| 1491 | + $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)", | |
| 1492 | + Bind => [\$login]); | |
| 1493 | + } else { | |
| 1494 | + } | |
| 1495 | + return 1; | |
| 1496 | + } else { | |
| 1497 | + return 0; | |
| 1498 | + } | |
| 1499 | +} | |
| 1500 | +# CAS Custom | |
| 1501 | + | |
| 1502 | +1; | |
| 1503 | + | |
| 1504 | +=head1 TERMS AND CONDITIONS | |
| 1505 | + | |
| 1506 | +This software is part of the OTRS project (L<http://otrs.org/>). | |
| 1507 | + | |
| 1508 | +This software comes with ABSOLUTELY NO WARRANTY. For details, see | |
| 1509 | +the enclosed file COPYING for license information (AGPL). If you | |
| 1510 | +did not receive this file, see L<http://www.gnu.org/licenses/agpl.txt>. | |
| 1511 | + | |
| 1512 | +=cut | ... | ... |
| ... | ... | @@ -0,0 +1,582 @@ |
| 1 | +# -- | |
| 2 | +# Copyright (C) 2001-2017 OTRS AG, http://otrs.com/ | |
| 3 | +# -- | |
| 4 | +# This software comes with ABSOLUTELY NO WARRANTY. For details, see | |
| 5 | +# the enclosed file COPYING for license information (AGPL). If you | |
| 6 | +# did not receive this file, see http://www.gnu.org/licenses/agpl.txt. | |
| 7 | +# | |
| 8 | +# Custom version for CAS authentication - rodrigo@goncalves.pro.br | |
| 9 | +# | |
| 10 | +# Version 2016-01-18 - RG - Version for OTRS 5.0.6 | |
| 11 | +# Version 2017-12-07 - RG - Version for OTRS 6.0.1 | |
| 12 | +# | |
| 13 | +# -- | |
| 14 | + | |
| 15 | +package Kernel::System::Web::Request; | |
| 16 | + | |
| 17 | +use strict; | |
| 18 | +use warnings; | |
| 19 | + | |
| 20 | +use CGI (); | |
| 21 | +use CGI::Carp; | |
| 22 | +use File::Path qw(); | |
| 23 | + | |
| 24 | +use Kernel::System::VariableCheck qw(:all); | |
| 25 | + | |
| 26 | +our @ObjectDependencies = ( | |
| 27 | + 'Kernel::Config', | |
| 28 | + 'Kernel::System::CheckItem', | |
| 29 | + 'Kernel::System::Encode', | |
| 30 | + 'Kernel::System::Web::UploadCache', | |
| 31 | + 'Kernel::System::FormDraft', | |
| 32 | +); | |
| 33 | + | |
| 34 | +=head1 NAME | |
| 35 | + | |
| 36 | +Kernel::System::Web::Request - global CGI interface | |
| 37 | + | |
| 38 | +=head1 DESCRIPTION | |
| 39 | + | |
| 40 | +All cgi param functions. | |
| 41 | + | |
| 42 | +=head1 PUBLIC INTERFACE | |
| 43 | + | |
| 44 | +=head2 new() | |
| 45 | + | |
| 46 | +create param object. Do not use it directly, instead use: | |
| 47 | + | |
| 48 | + use Kernel::System::ObjectManager; | |
| 49 | + local $Kernel::OM = Kernel::System::ObjectManager->new( | |
| 50 | + 'Kernel::System::Web::Request' => { | |
| 51 | + WebRequest => CGI::Fast->new(), # optional, e. g. if fast cgi is used | |
| 52 | + } | |
| 53 | + ); | |
| 54 | + my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); | |
| 55 | + | |
| 56 | +If Kernel::System::Web::Request is instantiated several times, they will share the | |
| 57 | +same CGI data (this can be helpful in filters which do not have access to the | |
| 58 | +ParamObject, for example. | |
| 59 | + | |
| 60 | +If you need to reset the CGI data before creating a new instance, use | |
| 61 | + | |
| 62 | + CGI::initialize_globals(); | |
| 63 | + | |
| 64 | +before calling Kernel::System::Web::Request->new(); | |
| 65 | + | |
| 66 | +=cut | |
| 67 | + | |
| 68 | +sub new { | |
| 69 | + my ( $Type, %Param ) = @_; | |
| 70 | + | |
| 71 | + # allocate new hash for object | |
| 72 | + my $Self = {}; | |
| 73 | + bless( $Self, $Type ); | |
| 74 | + | |
| 75 | + # get config object | |
| 76 | + my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); | |
| 77 | + | |
| 78 | + # max 5 MB posts | |
| 79 | + $CGI::POST_MAX = $ConfigObject->Get('WebMaxFileUpload') || 1024 * 1024 * 5; ## no critic | |
| 80 | + | |
| 81 | + # query object (in case use already existing WebRequest, e. g. fast cgi) | |
| 82 | + $Self->{Query} = $Param{WebRequest} || CGI->new(); | |
| 83 | + | |
| 84 | + return $Self; | |
| 85 | +} | |
| 86 | + | |
| 87 | +=head2 Error() | |
| 88 | + | |
| 89 | +to get the error back | |
| 90 | + | |
| 91 | + if ( $ParamObject->Error() ) { | |
| 92 | + print STDERR $ParamObject->Error() . "\n"; | |
| 93 | + } | |
| 94 | + | |
| 95 | +=cut | |
| 96 | + | |
| 97 | +sub Error { | |
| 98 | + my ( $Self, %Param ) = @_; | |
| 99 | + | |
| 100 | + # Workaround, do not check cgi_error() with perlex, CGI module is not | |
| 101 | + # working with perlex. | |
| 102 | + if ( $ENV{'GATEWAY_INTERFACE'} && $ENV{'GATEWAY_INTERFACE'} =~ /^CGI-PerlEx/ ) { | |
| 103 | + return; | |
| 104 | + } | |
| 105 | + | |
| 106 | + return if !$Self->{Query}->cgi_error(); | |
| 107 | + ## no critic | |
| 108 | + return $Self->{Query}->cgi_error() . ' - POST_MAX=' . ( $CGI::POST_MAX / 1024 ) . 'KB'; | |
| 109 | + ## use critic | |
| 110 | +} | |
| 111 | + | |
| 112 | +=head2 GetParam() | |
| 113 | + | |
| 114 | +to get single request parameters. By default, trimming is performed on the data. | |
| 115 | + | |
| 116 | + my $Param = $ParamObject->GetParam( | |
| 117 | + Param => 'ID', | |
| 118 | + Raw => 1, # optional, input data is not changed | |
| 119 | + ); | |
| 120 | + | |
| 121 | +=cut | |
| 122 | + | |
| 123 | +sub GetParam { | |
| 124 | + my ( $Self, %Param ) = @_; | |
| 125 | + | |
| 126 | + my $Value = $Self->{Query}->param( $Param{Param} ); | |
| 127 | + | |
| 128 | + # Fallback to query string for mixed requests. | |
| 129 | + my $RequestMethod = $Self->{Query}->request_method() // ''; | |
| 130 | + if ( $RequestMethod eq 'POST' && !defined $Value ) { | |
| 131 | + $Value = $Self->{Query}->url_param( $Param{Param} ); | |
| 132 | + } | |
| 133 | + | |
| 134 | + $Kernel::OM->Get('Kernel::System::Encode')->EncodeInput( \$Value ); | |
| 135 | + | |
| 136 | + my $Raw = defined $Param{Raw} ? $Param{Raw} : 0; | |
| 137 | + | |
| 138 | + if ( !$Raw ) { | |
| 139 | + | |
| 140 | + # If it is a plain string, perform trimming | |
| 141 | + if ( ref \$Value eq 'SCALAR' ) { | |
| 142 | + $Kernel::OM->Get('Kernel::System::CheckItem')->StringClean( | |
| 143 | + StringRef => \$Value, | |
| 144 | + TrimLeft => 1, | |
| 145 | + TrimRight => 1, | |
| 146 | + ); | |
| 147 | + } | |
| 148 | + } | |
| 149 | + | |
| 150 | + return $Value; | |
| 151 | +} | |
| 152 | + | |
| 153 | +=head2 GetParamNames() | |
| 154 | + | |
| 155 | +to get names of all parameters passed to the script. | |
| 156 | + | |
| 157 | + my @ParamNames = $ParamObject->GetParamNames(); | |
| 158 | + | |
| 159 | +Example: | |
| 160 | + | |
| 161 | +Called URL: index.pl?Action=AdminSystemConfiguration;Subaction=Save;Name=Config::Option::Valid | |
| 162 | + | |
| 163 | + my @ParamNames = $ParamObject->GetParamNames(); | |
| 164 | + print join " :: ", @ParamNames; | |
| 165 | + #prints Action :: Subaction :: Name | |
| 166 | + | |
| 167 | +=cut | |
| 168 | + | |
| 169 | +sub GetParamNames { | |
| 170 | + my $Self = shift; | |
| 171 | + | |
| 172 | + # fetch all names | |
| 173 | + my @ParamNames = $Self->{Query}->param(); | |
| 174 | + | |
| 175 | + # Fallback to query string for mixed requests. | |
| 176 | + my $RequestMethod = $Self->{Query}->request_method() // ''; | |
| 177 | + if ( $RequestMethod eq 'POST' ) { | |
| 178 | + my %POSTNames; | |
| 179 | + @POSTNames{@ParamNames} = @ParamNames; | |
| 180 | + my @GetNames = $Self->{Query}->url_param(); | |
| 181 | + GETNAME: | |
| 182 | + for my $GetName (@GetNames) { | |
| 183 | + next GETNAME if !defined $GetName; | |
| 184 | + push @ParamNames, $GetName if !exists $POSTNames{$GetName}; | |
| 185 | + } | |
| 186 | + } | |
| 187 | + | |
| 188 | + for my $Name (@ParamNames) { | |
| 189 | + $Kernel::OM->Get('Kernel::System::Encode')->EncodeInput( \$Name ); | |
| 190 | + } | |
| 191 | + | |
| 192 | + return @ParamNames; | |
| 193 | +} | |
| 194 | + | |
| 195 | +=head2 GetArray() | |
| 196 | + | |
| 197 | +to get array request parameters. | |
| 198 | +By default, trimming is performed on the data. | |
| 199 | + | |
| 200 | + my @Param = $ParamObject->GetArray( | |
| 201 | + Param => 'ID', | |
| 202 | + Raw => 1, # optional, input data is not changed | |
| 203 | + ); | |
| 204 | + | |
| 205 | +=cut | |
| 206 | + | |
| 207 | +sub GetArray { | |
| 208 | + my ( $Self, %Param ) = @_; | |
| 209 | + | |
| 210 | + my @Values = $Self->{Query}->multi_param( $Param{Param} ); | |
| 211 | + | |
| 212 | + # Fallback to query string for mixed requests. | |
| 213 | + my $RequestMethod = $Self->{Query}->request_method() // ''; | |
| 214 | + if ( $RequestMethod eq 'POST' && !@Values ) { | |
| 215 | + @Values = $Self->{Query}->url_param( $Param{Param} ); | |
| 216 | + } | |
| 217 | + | |
| 218 | + $Kernel::OM->Get('Kernel::System::Encode')->EncodeInput( \@Values ); | |
| 219 | + | |
| 220 | + my $Raw = defined $Param{Raw} ? $Param{Raw} : 0; | |
| 221 | + | |
| 222 | + if ( !$Raw ) { | |
| 223 | + | |
| 224 | + # get check item object | |
| 225 | + my $CheckItemObject = $Kernel::OM->Get('Kernel::System::CheckItem'); | |
| 226 | + | |
| 227 | + VALUE: | |
| 228 | + for my $Value (@Values) { | |
| 229 | + | |
| 230 | + # don't validate CGI::File::Temp objects from file uploads | |
| 231 | + next VALUE if !$Value || ref \$Value ne 'SCALAR'; | |
| 232 | + | |
| 233 | + $CheckItemObject->StringClean( | |
| 234 | + StringRef => \$Value, | |
| 235 | + TrimLeft => 1, | |
| 236 | + TrimRight => 1, | |
| 237 | + ); | |
| 238 | + } | |
| 239 | + } | |
| 240 | + | |
| 241 | + return @Values; | |
| 242 | +} | |
| 243 | + | |
| 244 | +=head2 GetUploadAll() | |
| 245 | + | |
| 246 | +gets file upload data. | |
| 247 | + | |
| 248 | + my %File = $ParamObject->GetUploadAll( | |
| 249 | + Param => 'FileParam', # the name of the request parameter containing the file data | |
| 250 | + ); | |
| 251 | + | |
| 252 | + returns ( | |
| 253 | + Filename => 'abc.txt', | |
| 254 | + ContentType => 'text/plain', | |
| 255 | + Content => 'Some text', | |
| 256 | + ); | |
| 257 | + | |
| 258 | +=cut | |
| 259 | + | |
| 260 | +sub GetUploadAll { | |
| 261 | + my ( $Self, %Param ) = @_; | |
| 262 | + | |
| 263 | + # get upload | |
| 264 | + my $Upload = $Self->{Query}->upload( $Param{Param} ); | |
| 265 | + return if !$Upload; | |
| 266 | + | |
| 267 | + # get real file name | |
| 268 | + my $UploadFilenameOrig = $Self->GetParam( Param => $Param{Param} ) || 'unknown'; | |
| 269 | + | |
| 270 | + my $NewFileName = "$UploadFilenameOrig"; # use "" to get filename of anony. object | |
| 271 | + $Kernel::OM->Get('Kernel::System::Encode')->EncodeInput( \$NewFileName ); | |
| 272 | + | |
| 273 | + # replace all devices like c: or d: and dirs for IE! | |
| 274 | + $NewFileName =~ s/.:\\(.*)/$1/g; | |
| 275 | + $NewFileName =~ s/.*\\(.+?)/$1/g; | |
| 276 | + | |
| 277 | + # return a string | |
| 278 | + my $Content = ''; | |
| 279 | + while (<$Upload>) { | |
| 280 | + $Content .= $_; | |
| 281 | + } | |
| 282 | + close $Upload; | |
| 283 | + | |
| 284 | + my $ContentType = $Self->_GetUploadInfo( | |
| 285 | + Filename => $UploadFilenameOrig, | |
| 286 | + Header => 'Content-Type', | |
| 287 | + ); | |
| 288 | + | |
| 289 | + return ( | |
| 290 | + Filename => $NewFileName, | |
| 291 | + Content => $Content, | |
| 292 | + ContentType => $ContentType, | |
| 293 | + ); | |
| 294 | +} | |
| 295 | + | |
| 296 | +sub _GetUploadInfo { | |
| 297 | + my ( $Self, %Param ) = @_; | |
| 298 | + | |
| 299 | + # get file upload info | |
| 300 | + my $FileInfo = $Self->{Query}->uploadInfo( $Param{Filename} ); | |
| 301 | + | |
| 302 | + # return if no upload info exists | |
| 303 | + return 'application/octet-stream' if !$FileInfo; | |
| 304 | + | |
| 305 | + # return if no content type of upload info exists | |
| 306 | + return 'application/octet-stream' if !$FileInfo->{ $Param{Header} }; | |
| 307 | + | |
| 308 | + # return content type of upload info | |
| 309 | + return $FileInfo->{ $Param{Header} }; | |
| 310 | +} | |
| 311 | + | |
| 312 | +=head2 SetCookie() | |
| 313 | + | |
| 314 | +set a cookie | |
| 315 | + | |
| 316 | + $ParamObject->SetCookie( | |
| 317 | + Key => ID, | |
| 318 | + Value => 123456, | |
| 319 | + Expires => '+3660s', | |
| 320 | + Path => 'otrs/', # optional, only allow cookie for given path | |
| 321 | + Secure => 1, # optional, set secure attribute to disable cookie on HTTP (HTTPS only) | |
| 322 | + HTTPOnly => 1, # optional, sets HttpOnly attribute of cookie to prevent access via JavaScript | |
| 323 | + ); | |
| 324 | + | |
| 325 | +=cut | |
| 326 | + | |
| 327 | +sub SetCookie { | |
| 328 | + my ( $Self, %Param ) = @_; | |
| 329 | + | |
| 330 | + $Param{Path} ||= ''; | |
| 331 | + | |
| 332 | + return $Self->{Query}->cookie( | |
| 333 | + -name => $Param{Key}, | |
| 334 | + -value => $Param{Value}, | |
| 335 | + -expires => $Param{Expires}, | |
| 336 | + -secure => $Param{Secure} || '', | |
| 337 | + -httponly => $Param{HTTPOnly} || '', | |
| 338 | + -path => '/' . $Param{Path}, | |
| 339 | + ); | |
| 340 | +} | |
| 341 | + | |
| 342 | +=head2 GetCookie() | |
| 343 | + | |
| 344 | +get a cookie | |
| 345 | + | |
| 346 | + my $String = $ParamObject->GetCookie( | |
| 347 | + Key => ID, | |
| 348 | + ); | |
| 349 | + | |
| 350 | +=cut | |
| 351 | + | |
| 352 | +sub GetCookie { | |
| 353 | + my ( $Self, %Param ) = @_; | |
| 354 | + | |
| 355 | + return $Self->{Query}->cookie( $Param{Key} ); | |
| 356 | +} | |
| 357 | + | |
| 358 | +=head2 IsAJAXRequest() | |
| 359 | + | |
| 360 | +checks if the current request was sent by AJAX | |
| 361 | + | |
| 362 | + my $IsAJAXRequest = $ParamObject->IsAJAXRequest(); | |
| 363 | + | |
| 364 | +=cut | |
| 365 | + | |
| 366 | +sub IsAJAXRequest { | |
| 367 | + my ( $Self, %Param ) = @_; | |
| 368 | + | |
| 369 | + return ( $Self->{Query}->http('X-Requested-With') // '' ) eq 'XMLHttpRequest' ? 1 : 0; | |
| 370 | +} | |
| 371 | + | |
| 372 | +=head2 LoadFormDraft() | |
| 373 | + | |
| 374 | +Load specified draft. | |
| 375 | +This will read stored draft data and inject it into the param object | |
| 376 | +for transparent use by frontend module. | |
| 377 | + | |
| 378 | + my $FormDraftID = $ParamObject->LoadFormDraft( | |
| 379 | + FormDraftID => 123, | |
| 380 | + UserID => 1, | |
| 381 | + ); | |
| 382 | + | |
| 383 | +=cut | |
| 384 | + | |
| 385 | +sub LoadFormDraft { | |
| 386 | + my ( $Self, %Param ) = @_; | |
| 387 | + | |
| 388 | + return if !$Param{FormDraftID} || !$Param{UserID}; | |
| 389 | + | |
| 390 | + # get draft data | |
| 391 | + my $FormDraft = $Kernel::OM->Get('Kernel::System::FormDraft')->FormDraftGet( | |
| 392 | + FormDraftID => $Param{FormDraftID}, | |
| 393 | + UserID => $Param{UserID}, | |
| 394 | + ); | |
| 395 | + return if !IsHashRefWithData($FormDraft); | |
| 396 | + | |
| 397 | + # Verify action. | |
| 398 | + my $Action = $Self->GetParam( Param => 'Action' ); | |
| 399 | + return if $FormDraft->{Action} ne $Action; | |
| 400 | + | |
| 401 | + # add draft name to form data | |
| 402 | + $FormDraft->{FormData}->{FormDraftTitle} = $FormDraft->{Title}; | |
| 403 | + | |
| 404 | + # create FormID and add to form data | |
| 405 | + my $FormID = $Kernel::OM->Get('Kernel::System::Web::UploadCache')->FormIDCreate(); | |
| 406 | + $FormDraft->{FormData}->{FormID} = $FormID; | |
| 407 | + | |
| 408 | + # set form data to param object, depending on type | |
| 409 | + KEY: | |
| 410 | + for my $Key ( sort keys %{ $FormDraft->{FormData} } ) { | |
| 411 | + my $Value = $FormDraft->{FormData}->{$Key} // ''; | |
| 412 | + | |
| 413 | + # array value | |
| 414 | + if ( IsArrayRefWithData($Value) ) { | |
| 415 | + $Self->{Query}->param( | |
| 416 | + -name => $Key, | |
| 417 | + -values => $Value, | |
| 418 | + ); | |
| 419 | + next KEY; | |
| 420 | + } | |
| 421 | + | |
| 422 | + # scalar value | |
| 423 | + $Self->{Query}->param( | |
| 424 | + -name => $Key, | |
| 425 | + -value => $Value, | |
| 426 | + ); | |
| 427 | + } | |
| 428 | + | |
| 429 | + # add UploadCache data | |
| 430 | + my $UploadCacheObject = $Kernel::OM->Get('Kernel::System::Web::UploadCache'); | |
| 431 | + for my $File ( @{ $FormDraft->{FileData} } ) { | |
| 432 | + return if !$UploadCacheObject->FormIDAddFile( | |
| 433 | + %{$File}, | |
| 434 | + FormID => $FormID, | |
| 435 | + ); | |
| 436 | + } | |
| 437 | + | |
| 438 | + return $Param{FormDraftID}; | |
| 439 | +} | |
| 440 | + | |
| 441 | +=head2 SaveFormDraft() | |
| 442 | + | |
| 443 | +Create or replace draft using data from param object and upload cache. | |
| 444 | +Specified params can be overwritten if necessary. | |
| 445 | + | |
| 446 | + my $FormDraftID = $ParamObject->SaveFormDraft( | |
| 447 | + UserID => 1 | |
| 448 | + ObjectType => 'Ticket', | |
| 449 | + ObjectID => 123, | |
| 450 | + OverrideParams => { # optional, can contain strings and array references | |
| 451 | + Subaction => undef, | |
| 452 | + UserID => 1, | |
| 453 | + CustomParam => [ 1, 2, 3, ], | |
| 454 | + ... | |
| 455 | + }, | |
| 456 | + ); | |
| 457 | + | |
| 458 | +=cut | |
| 459 | + | |
| 460 | +sub SaveFormDraft { | |
| 461 | + my ( $Self, %Param ) = @_; | |
| 462 | + | |
| 463 | + # check params | |
| 464 | + return if !$Param{UserID} || !$Param{ObjectType} || !IsInteger( $Param{ObjectID} ); | |
| 465 | + | |
| 466 | + # gather necessary data for backend | |
| 467 | + my %MetaParams; | |
| 468 | + for my $Param (qw(Action FormDraftID FormDraftTitle FormID)) { | |
| 469 | + $MetaParams{$Param} = $Self->GetParam( | |
| 470 | + Param => $Param, | |
| 471 | + ); | |
| 472 | + } | |
| 473 | + return if !$MetaParams{Action}; | |
| 474 | + | |
| 475 | + # determine session name param (SessionUseCookie = 0) for exclusion | |
| 476 | + my $SessionName = $Kernel::OM->Get('Kernel::Config')->Get('SessionName') || 'SessionID'; | |
| 477 | + | |
| 478 | + # compile override list | |
| 479 | + my %OverrideParams = IsHashRefWithData( $Param{OverrideParams} ) ? %{ $Param{OverrideParams} } : (); | |
| 480 | + | |
| 481 | + # these params must always be excluded for safety, they take precedence | |
| 482 | + for my $Name ( | |
| 483 | + qw(Action ChallengeToken FormID FormDraftID FormDraftTitle FormDraftAction LoadFormDraftID), | |
| 484 | + $SessionName | |
| 485 | + ) | |
| 486 | + { | |
| 487 | + $OverrideParams{$Name} = undef; | |
| 488 | + } | |
| 489 | + | |
| 490 | + # Gather all params. | |
| 491 | + # Exclude, add or override by using OverrideParams if necessary. | |
| 492 | + my @ParamNames = $Self->GetParamNames(); | |
| 493 | + my %ParamSeen; | |
| 494 | + my %FormData; | |
| 495 | + PARAM: | |
| 496 | + for my $Param ( @ParamNames, sort keys %OverrideParams ) { | |
| 497 | + next PARAM if $ParamSeen{$Param}++; | |
| 498 | + my $Value; | |
| 499 | + | |
| 500 | + # check for overrides first | |
| 501 | + if ( exists $OverrideParams{$Param} ) { | |
| 502 | + | |
| 503 | + # allow only strings and array references as value | |
| 504 | + if ( | |
| 505 | + IsStringWithData( $OverrideParams{$Param} ) | |
| 506 | + || IsArrayRefWithData( $OverrideParams{$Param} ) | |
| 507 | + ) | |
| 508 | + { | |
| 509 | + $Value = $OverrideParams{$Param}; | |
| 510 | + } | |
| 511 | + | |
| 512 | + # skip all other parameters (including those specified to be excluded by using 'undef') | |
| 513 | + else { | |
| 514 | + next PARAM; | |
| 515 | + } | |
| 516 | + } | |
| 517 | + | |
| 518 | + # get other values from param object | |
| 519 | + if ( !defined $Value ) { | |
| 520 | + my @Values = $Self->GetArray( Param => $Param ); | |
| 521 | + next PARAM if !IsArrayRefWithData( \@Values ); | |
| 522 | + | |
| 523 | + # store single occurances as string | |
| 524 | + if ( scalar @Values == 1 ) { | |
| 525 | + $Value = $Values[0]; | |
| 526 | + } | |
| 527 | + | |
| 528 | + # store multiple occurances as array reference | |
| 529 | + else { | |
| 530 | + $Value = \@Values; | |
| 531 | + } | |
| 532 | + } | |
| 533 | + | |
| 534 | + $FormData{$Param} = $Value; | |
| 535 | + } | |
| 536 | + | |
| 537 | + # get files from upload cache | |
| 538 | + my @FileData = $Kernel::OM->Get('Kernel::System::Web::UploadCache')->FormIDGetAllFilesData( | |
| 539 | + FormID => $MetaParams{FormID}, | |
| 540 | + ); | |
| 541 | + | |
| 542 | + # prepare data to add or update draft | |
| 543 | + my %FormDraft = ( | |
| 544 | + FormData => \%FormData, | |
| 545 | + FileData => \@FileData, | |
| 546 | + FormDraftID => $MetaParams{FormDraftID}, | |
| 547 | + ObjectType => $Param{ObjectType}, | |
| 548 | + ObjectID => $Param{ObjectID}, | |
| 549 | + Action => $MetaParams{Action}, | |
| 550 | + Title => $MetaParams{FormDraftTitle}, | |
| 551 | + UserID => $Param{UserID}, | |
| 552 | + ); | |
| 553 | + | |
| 554 | + # update draft | |
| 555 | + if ( $MetaParams{FormDraftID} ) { | |
| 556 | + return if !$Kernel::OM->Get('Kernel::System::FormDraft')->FormDraftUpdate(%FormDraft); | |
| 557 | + return 1; | |
| 558 | + } | |
| 559 | + | |
| 560 | + # create new draft | |
| 561 | + return if !$Kernel::OM->Get('Kernel::System::FormDraft')->FormDraftAdd(%FormDraft); | |
| 562 | + return 1; | |
| 563 | +} | |
| 564 | + | |
| 565 | +# CAS Custom | |
| 566 | +sub GetMethod { | |
| 567 | + my ( $Self, %Param ) = @_; | |
| 568 | + return $Self->{Query}->request_method(); | |
| 569 | +} | |
| 570 | +# CAS Custom | |
| 571 | + | |
| 572 | +1; | |
| 573 | + | |
| 574 | +=head1 TERMS AND CONDITIONS | |
| 575 | + | |
| 576 | +This software is part of the OTRS project (L<http://otrs.org/>). | |
| 577 | + | |
| 578 | +This software comes with ABSOLUTELY NO WARRANTY. For details, see | |
| 579 | +the enclosed file COPYING for license information (AGPL). If you | |
| 580 | +did not receive this file, see L<http://www.gnu.org/licenses/agpl.txt>. | |
| 581 | + | |
| 582 | +=cut | ... | ... |
| ... | ... | @@ -0,0 +1,154 @@ |
| 1 | +# -- | |
| 2 | +# Kernel/System/Auth/CAS.pm - provides the CAS authentication through Jasig | |
| 3 | +# | |
| 4 | +# Copyright (C) 2015-2017 - Rodrigo Gonçalves - rodrigo@goncalves.pro.br | |
| 5 | +# -- | |
| 6 | +# $Id: CAS.pm,v 2.0 2015/01/05 15:16:05 mb Exp $ | |
| 7 | +# | |
| 8 | +# Version 2015/01/15 - RG - Adjusts for OTRS4 | |
| 9 | +# Version 2016-01-18 - RG - Fixes for OTRS 5.0.6 | |
| 10 | +# Version 2017-12-07 - RG - Fixes for OTRS 6.0.1 | |
| 11 | +# | |
| 12 | +# | |
| 13 | +# -- | |
| 14 | +# This software comes with ABSOLUTELY NO WARRANTY. For details, see | |
| 15 | +# the enclosed file COPYING for license information (AGPL). If you | |
| 16 | +# did not receive this file, see http://www.gnu.org/licenses/agpl.txt. | |
| 17 | +# -- | |
| 18 | +# Note: | |
| 19 | +# | |
| 20 | +# If you use this module, you should use as fallback the following config settings: | |
| 21 | +# | |
| 22 | +# If use isn't login through apache ($ENV{REMOTE_USER} or $ENV{HTTP_REMOTE_USER}) | |
| 23 | +# $Self->{CustomerPanelLoginURL} = 'http://host.example.com/not-authorised-for-otrs.html'; | |
| 24 | +# | |
| 25 | +# $Self->{CustomerPanelLogoutURL} = 'http://host.example.com/thanks-for-using-otrs.html'; | |
| 26 | +# | |
| 27 | +# -- | |
| 28 | +package Kernel::System::Auth::CAS; | |
| 29 | + | |
| 30 | +use strict; | |
| 31 | +use warnings; | |
| 32 | +use CGI; | |
| 33 | +use AuthCAS; | |
| 34 | +use Data::Dumper; | |
| 35 | +use CGI::Carp qw( fatalsToBrowser ); | |
| 36 | +use URI::Escape; | |
| 37 | + | |
| 38 | +our @ObjectDependencies = ( "Kernel::Config", "Kernel::System::Log", "Kernel::System::DB" ); | |
| 39 | + | |
| 40 | +sub new { | |
| 41 | + my ( $Type, %Param ) = @_; | |
| 42 | + | |
| 43 | + # allocate new hash for object | |
| 44 | + my $Self = {}; | |
| 45 | + bless( $Self, $Type ); | |
| 46 | + | |
| 47 | + # Debug 0=off 1=on | |
| 48 | + $Self->{Debug} = 1; | |
| 49 | + $Self->{Count} = $Param{Count} || ''; | |
| 50 | + | |
| 51 | + return $Self; | |
| 52 | +} | |
| 53 | + | |
| 54 | +sub GetOption { | |
| 55 | + my ( $Self, %Param ) = @_; | |
| 56 | + | |
| 57 | + # check needed stuff | |
| 58 | + if ( !$Param{What} ) { | |
| 59 | + $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need What!" ); | |
| 60 | + return; | |
| 61 | + } | |
| 62 | + | |
| 63 | + # module options | |
| 64 | + my %Option = ( PreAuth => 1, ); | |
| 65 | + | |
| 66 | + # return option | |
| 67 | + return $Option{ $Param{What} }; | |
| 68 | +} | |
| 69 | + | |
| 70 | +sub Auth { | |
| 71 | + my ( $Self, %Param ) = @_; | |
| 72 | + | |
| 73 | + my $QueryString = $ENV{"HTTP_REFERER"} || ''; | |
| 74 | + | |
| 75 | + my $ConfigObject = $Kernel::OM->Get("Kernel::Config"); | |
| 76 | + | |
| 77 | + my $cas = new AuthCAS( casUrl => $ConfigObject->Get('AuthModule::CAS::CASUrl') ); | |
| 78 | + my $app_url = $ConfigObject->Get('AuthModule::CAS::ServiceUrl'); | |
| 79 | + my $Gateway = $ConfigObject->Get('AuthModule::CAS::Gateway'); | |
| 80 | + my $User = ''; | |
| 81 | + | |
| 82 | + if ( $Gateway == 1 ) { | |
| 83 | + | |
| 84 | + # TEST MODE | |
| 85 | + if ( $QueryString =~ /ticket/ ) { | |
| 86 | + $QueryString =~ /ticket%3D([^&]+)/; | |
| 87 | + my $ST = $1; | |
| 88 | + my $User = $cas->validateST( $app_url, $ST ); | |
| 89 | + return $User; | |
| 90 | + } | |
| 91 | + | |
| 92 | + if ( $QueryString =~ /checked_cas/ ) { | |
| 93 | + return ''; | |
| 94 | + } | |
| 95 | + | |
| 96 | + my $login_url = $cas->getServerLoginGatewayURL( $app_url . '?checked_cas=1' ); | |
| 97 | + my $q = CGI->new(); | |
| 98 | + print $q->redirect( -URL => $login_url ); | |
| 99 | + } | |
| 100 | + else { | |
| 101 | + $Self->Debug("Autenticando: " . $QueryString); | |
| 102 | + | |
| 103 | + # If no ticket passed, redirect to CAS to authenticate/get token | |
| 104 | + unless ( $QueryString =~ /ticket=/ || $QueryString =~ /ticket%3D/ ) { | |
| 105 | + my $redurl = $app_url . "?" . $Param{RequestedURL}; | |
| 106 | + $redurl = uri_escape($redurl); | |
| 107 | + my $login_url = $cas->getServerLoginURL( $redurl ); | |
| 108 | + my $q = CGI->new(); | |
| 109 | + print $q->redirect( -URL => $login_url ); | |
| 110 | + } | |
| 111 | + else { | |
| 112 | + $Self->Debug("Recebida URL com ticket: " . $QueryString); | |
| 113 | + | |
| 114 | + # CAS session created - record id | |
| 115 | + $QueryString =~ /ticket=([^&]+)/; | |
| 116 | + my $ST = $1; | |
| 117 | + if (! $ST) { | |
| 118 | + $QueryString =~ /ticket%3D([^&]+)/; | |
| 119 | + $ST = $1; | |
| 120 | + } | |
| 121 | + | |
| 122 | + my $requrl = $Param{RequestedURL}; | |
| 123 | + my $substring = substr($requrl, 0, index($requrl, "&ticket=ST")); | |
| 124 | + | |
| 125 | + my $redurl = $app_url . "?" . $substring; | |
| 126 | + $redurl = uri_escape($redurl); | |
| 127 | + | |
| 128 | + $Self->Debug("Validando URL $redurl com ticket $ST"); | |
| 129 | + $User = $cas->validateST( $redurl, $ST ); | |
| 130 | + | |
| 131 | + $Self->Debug("Autenticou... $User"); | |
| 132 | + if ($User) { | |
| 133 | + $Kernel::OM->Get("Kernel::System::DB")->Do( | |
| 134 | + SQL => 'DELETE FROM cas_session WHERE UserLogin=?', | |
| 135 | + Bind => [ \$User ], | |
| 136 | + ); | |
| 137 | + | |
| 138 | + $Kernel::OM->Get("Kernel::System::DB")->Do( | |
| 139 | + SQL => 'INSERT INTO cas_session (UserLogin,Ticket) VALUES (?, ?)', | |
| 140 | + Bind => [ \$User, \$ST, ], | |
| 141 | + ); | |
| 142 | + } | |
| 143 | + } | |
| 144 | + } | |
| 145 | + return $User; | |
| 146 | +} | |
| 147 | + | |
| 148 | +sub Debug { | |
| 149 | + my $Self = shift; | |
| 150 | + my $msg = shift; | |
| 151 | + $Kernel::OM->Get("Kernel::System::Log")->Log( Priority => 'debug', Message => $msg ); | |
| 152 | +} | |
| 153 | + | |
| 154 | +1; | ... | ... |
| ... | ... | @@ -0,0 +1,154 @@ |
| 1 | +# -- | |
| 2 | +# Kernel/System/CustomerAuth/CAS.pm - provides the CAS authentication through Jasig | |
| 3 | +# | |
| 4 | +# Copyright (C) 2015-2017 - Rodrigo Gonçalves - rodrigo@goncalves.pro.br | |
| 5 | +# -- | |
| 6 | +# $Id: CAS.pm,v 2.0 2015/01/05 15:16:05 mb Exp $ | |
| 7 | +# | |
| 8 | +# Version 2015/01/15 - RG - Adjusts for OTRS4 | |
| 9 | +# Version 2016-01-18 - RG - Fixes for OTRS 5.0.6 | |
| 10 | +# Version 2017-12-07 - RG - Fixes for OTRS 6.0.1 | |
| 11 | +# | |
| 12 | +# -- | |
| 13 | +# This software comes with ABSOLUTELY NO WARRANTY. For details, see | |
| 14 | +# the enclosed file COPYING for license information (AGPL). If you | |
| 15 | +# did not receive this file, see http://www.gnu.org/licenses/agpl.txt. | |
| 16 | +# -- | |
| 17 | +# Note: | |
| 18 | +# | |
| 19 | +# If you use this module, you should use as fallback the following config settings: | |
| 20 | +# | |
| 21 | +# If use isn't login through apache ($ENV{REMOTE_USER} or $ENV{HTTP_REMOTE_USER}) | |
| 22 | +# $Self->{CustomerPanelLoginURL} = 'http://host.example.com/not-authorised-for-otrs.html'; | |
| 23 | +# | |
| 24 | +# $Self->{CustomerPanelLogoutURL} = 'http://host.example.com/thanks-for-using-otrs.html'; | |
| 25 | +# | |
| 26 | +# -- | |
| 27 | +package Kernel::System::CustomerAuth::CAS; | |
| 28 | + | |
| 29 | +use strict; | |
| 30 | +use warnings; | |
| 31 | +use CGI; | |
| 32 | +use AuthCAS; | |
| 33 | +use Data::Dumper; | |
| 34 | +use CGI::Carp qw( fatalsToBrowser ); | |
| 35 | +use URI::Escape; | |
| 36 | + | |
| 37 | +our @ObjectDependencies = ( "Kernel::Config", "Kernel::System::Log", "Kernel::System::DB" ); | |
| 38 | + | |
| 39 | +sub new { | |
| 40 | + my ( $Type, %Param ) = @_; | |
| 41 | + | |
| 42 | + # allocate new hash for object | |
| 43 | + my $Self = {}; | |
| 44 | + bless( $Self, $Type ); | |
| 45 | + | |
| 46 | + # Debug 0=off 1=on | |
| 47 | + $Self->{Debug} = 0; | |
| 48 | + $Self->{Count} = $Param{Count} || ''; | |
| 49 | + | |
| 50 | + return $Self; | |
| 51 | +} | |
| 52 | + | |
| 53 | +sub GetOption { | |
| 54 | + my ( $Self, %Param ) = @_; | |
| 55 | + | |
| 56 | + # check needed stuff | |
| 57 | + if ( !$Param{What} ) { | |
| 58 | + $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need What!" ); | |
| 59 | + return; | |
| 60 | + } | |
| 61 | + | |
| 62 | + # module options | |
| 63 | + my %Option = ( PreAuth => 1, ); | |
| 64 | + | |
| 65 | + # return option | |
| 66 | + return $Option{ $Param{What} }; | |
| 67 | +} | |
| 68 | + | |
| 69 | +sub Auth { | |
| 70 | + my ( $Self, %Param ) = @_; | |
| 71 | + | |
| 72 | + my $QueryString = $ENV{"HTTP_REFERER"} || ''; | |
| 73 | + | |
| 74 | + my $ConfigObject = $Kernel::OM->Get("Kernel::Config"); | |
| 75 | + | |
| 76 | + my $cas = new AuthCAS( casUrl => $ConfigObject->Get('Customer::AuthModule::CAS::CASUrl') ); | |
| 77 | + my $app_url = $ConfigObject->Get('Customer::AuthModule::CAS::ServiceUrl'); | |
| 78 | + my $Gateway = $ConfigObject->Get('Customer::AuthModule::CAS::Gateway'); | |
| 79 | + my $User = ''; | |
| 80 | + | |
| 81 | + if ( $Gateway == 1 ) { | |
| 82 | + | |
| 83 | + # TEST MODE | |
| 84 | + if ( $QueryString =~ /ticket/ ) { | |
| 85 | + $QueryString =~ /ticket%3D([^&]+)/; | |
| 86 | + my $ST = $1; | |
| 87 | + my $User = $cas->validateST( $app_url, $ST ); | |
| 88 | + return $User; | |
| 89 | + } | |
| 90 | + | |
| 91 | + if ( $QueryString =~ /checked_cas/ ) { | |
| 92 | + return ''; | |
| 93 | + } | |
| 94 | + | |
| 95 | + my $login_url = $cas->getServerLoginGatewayURL( $app_url . '?checked_cas=1' ); | |
| 96 | + my $q = CGI->new(); | |
| 97 | + print $q->redirect( -URL => $login_url ); | |
| 98 | + } | |
| 99 | + else { | |
| 100 | + $Self->Debug("Autenticando: " . $QueryString); | |
| 101 | + | |
| 102 | + # If no ticket passed, redirect to CAS to authenticate/get token | |
| 103 | + unless ( $QueryString =~ /ticket=/ || $QueryString =~ /ticket%3D/ ) { | |
| 104 | + my $redurl = $app_url . "?" . $Param{RequestedURL}; | |
| 105 | + $redurl = uri_escape($redurl); | |
| 106 | + my $login_url = $cas->getServerLoginURL( $redurl ); | |
| 107 | + my $q = CGI->new(); | |
| 108 | + print $q->redirect( -URL => $login_url ); | |
| 109 | + } | |
| 110 | + else { | |
| 111 | + $Self->Debug("Recebida URL com ticket: " . $QueryString); | |
| 112 | + | |
| 113 | + # CAS session created - record id | |
| 114 | + $QueryString =~ /ticket=([^&]+)/; | |
| 115 | + my $ST = $1; | |
| 116 | + if (! $ST) { | |
| 117 | + $QueryString =~ /ticket%3D([^&]+)/; | |
| 118 | + $ST = $1; | |
| 119 | + } | |
| 120 | + | |
| 121 | + my $requrl = $Param{RequestedURL}; | |
| 122 | + my $substring = substr($requrl, 0, index($requrl, "&ticket=ST")); | |
| 123 | + | |
| 124 | + my $redurl = $app_url . "?" . $substring; | |
| 125 | + $redurl = uri_escape($redurl); | |
| 126 | + | |
| 127 | + $Self->Debug("Validando URL $redurl com ticket $ST"); | |
| 128 | + $User = $cas->validateST( $redurl, $ST ); | |
| 129 | + | |
| 130 | + $Self->Debug("Autenticou... $User"); | |
| 131 | + if ($User) { | |
| 132 | + $Kernel::OM->Get("Kernel::System::DB")->Do( | |
| 133 | + SQL => 'DELETE FROM cas_session WHERE UserLogin=?', | |
| 134 | + Bind => [ \$User ], | |
| 135 | + ); | |
| 136 | + | |
| 137 | + $Kernel::OM->Get("Kernel::System::DB")->Do( | |
| 138 | + SQL => 'INSERT INTO cas_session (UserLogin,Ticket) VALUES (?, ?)', | |
| 139 | + Bind => [ \$User, \$ST, ], | |
| 140 | + ); | |
| 141 | + } | |
| 142 | + } | |
| 143 | + } | |
| 144 | + | |
| 145 | + return $User; | |
| 146 | +} | |
| 147 | + | |
| 148 | +sub Debug { | |
| 149 | + my $Self = shift; | |
| 150 | + my $msg = shift; | |
| 151 | + $Kernel::OM->Get("Kernel::System::Log")->Log( Priority => 'debug', Message => $msg ); | |
| 152 | +} | |
| 153 | + | |
| 154 | +1; | ... | ... |
| ... | ... | @@ -0,0 +1,41 @@ |
| 1 | +CAS Authentication (Jasig-based) for OTRS | |
| 2 | +========================================= | |
| 3 | + | |
| 4 | + | |
| 5 | +Required PERL Packages: | |
| 6 | +----------------------- | |
| 7 | + | |
| 8 | +* AuthCAS (`apt install libauthcas-perl` on Ubuntu) | |
| 9 | + | |
| 10 | + | |
| 11 | +Config required: | |
| 12 | +---------------- | |
| 13 | + | |
| 14 | +*Kernel/Config.pm:* | |
| 15 | + | |
| 16 | + $Self->{'AuthModule'} = 'Kernel::System::Auth::CAS'; | |
| 17 | + $Self->{'AuthModule::CAS::Gateway'} = 0; | |
| 18 | + $Self->{'AuthModule::CAS::ServiceUrl'} = 'URL FOR OTRS AGENT INDEX (ex: https://host.com/otrs/index.pl)'; | |
| 19 | + $Self->{'AuthModule::CAS::CASUrl'} = 'URL FOR CAS SERVICE (WITHOUT TRAILING /) - ex: https://cas.systems.com'; | |
| 20 | + | |
| 21 | + $Self->{'Customer::AuthModule'} = 'Kernel::System::CustomerAuth::CAS'; | |
| 22 | + $Self->{'Customer::AuthModule::CAS::Gateway'} = 0; | |
| 23 | + $Self->{'Customer::AuthModule::CAS::ServiceUrl'} = 'URL FOR OTRS CUSTOMER INDEX (ex: https://host.com/otrs/customer.pl)'; | |
| 24 | + $Self->{'Customer::AuthModule::CAS::CASUrl'} = 'URL FOR CAS SERVICE (WITHOUT TRAILING /) - ex: https://cas.systems.com'; | |
| 25 | + | |
| 26 | + | |
| 27 | +Package building: | |
| 28 | +----------------- | |
| 29 | + | |
| 30 | +To build the package, do the following: | |
| 31 | + | |
| 32 | + cd dist | |
| 33 | + ./CreateOpm.sh | |
| 34 | + | |
| 35 | +The generated package will be on the same `dist` directory | |
| 36 | + | |
| 37 | + | |
| 38 | +Issues: | |
| 39 | +------- | |
| 40 | + | |
| 41 | +This module was developed in-house and thus is provided without warranty or support. If you have a problem, please open an issue on github. | |
| 0 | 42 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | +#!/bin/bash | |
| 2 | +LOCAL="$PWD" | |
| 3 | + | |
| 4 | +echo Build package otrs-cas-authentication; | |
| 5 | +echo Package will be generated on the current path... | |
| 6 | +cd /opt/otrs | |
| 7 | +bin/otrs.Console.pl Dev::Package::Build "$LOCAL"/../CASAuthentication.sopm "$LOCAL"/ | |
| 8 | +cd $LOCAL | |
| 0 | 9 | \ No newline at end of file | ... | ... |