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 @@ | @@ -0,0 +1 @@ | ||
| 1 | +dist/*.opm |
| @@ -0,0 +1,18 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 | \ No newline at end of file | 42 | \ No newline at end of file |
| @@ -0,0 +1,8 @@ | @@ -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 | \ No newline at end of file | 9 | \ No newline at end of file |