1# -- 2# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/ 3# -- 4# This software comes with ABSOLUTELY NO WARRANTY. For details, see 5# the enclosed file COPYING for license information (GPL). If you 6# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt. 7# -- 8 9package Kernel::System::Ticket::TicketACL; 10 11use strict; 12use warnings; 13 14use Kernel::System::VariableCheck qw(:all); 15 16our $ObjectManagerDisabled = 1; 17 18=head1 NAME 19 20Kernel::System::Ticket::TicketACL - ticket ACL lib 21 22=head1 DESCRIPTION 23 24All ticket ACL functions. 25 26 27=head2 TicketAcl() 28 29Restricts the Data parameter sent to a subset of it, depending on a group of user defied rules 30called ACLs. The reduced subset can be access from TicketACLData() if ReturnType parameter is set 31to: Ticket, Process or ActivityDialog, or in TicketACLActionData(), if ReturnType Action is used. 32 33Each ACL can contain different restrictions for different objects the ReturnType parameter defines 34which object is considered for this restrictions, in the case of the Ticket object a second 35parameter called ReturnSubtype is needed, to specify the ticket attribute to be restricted, like: 36Queue, State, Owner, etc. While for the rest of the objects a "-" value must be set. The ReturnType 37and ReturnSubType must be set according to the Data parameter sent. 38 39The rest of the attributes define the matching options for the ACL rules. 40 41Example to restrict ticket actions: 42 43 my $Success = $TicketObject->TicketAcl( 44 Data => { # Values to restrict 45 1 => AgentTicketZoom, 46 # ... 47 }, 48 49 Action => 'AgentTicketZoom', # Optional 50 TicketID => 123, # Optional 51 DynamicField => { # Optional 52 DynamicField_NameX => 123, 53 DynamicField_NameZ => 'some value', 54 }, 55 56 QueueID => 123, # Optional 57 Queue => 'some queue name', # Optional 58 NewQueueID => 123, # Optional, QueueID or NewQueueID can be 59 # used and they both refers to QueueID 60 61 ServiceID => 123, # Optional 62 Service => 'some service name', # Optional 63 64 TypeID => 123, 65 Type => 'some ticket type name', # Optional 66 67 PriorityID => 123, # Optional 68 NewPriorityID => 123, # Optional, PriorityID or NewPriorityID can be 69 # used and they both refers to PriorityID 70 Priority => 'some priority name', # Optional 71 72 SLAID => 123, 73 SLA => 'some SLA name', # Optional 74 75 StateID => 123, # Optional 76 NextStateID => 123, # Optional, StateID or NextStateID can be 77 # used and they both refers to StateID 78 State => 'some ticket state name', # Optional 79 80 OwnerID => 123, # Optional 81 NewOwnerID => 123, # Optional, OwnerID or NewOwnerID can be 82 # used and they both refers to OwnerID 83 Owner => 'some user login' # Optional 84 85 ResponsibleID => 123, # Optional 86 NewResponsibleID => 123, # Optional, ResponsibleID or NewResposibleID 87 # can be used and they both refers to 88 # ResponsibleID 89 Responsible => 'some user login' # Optional 90 91 ReturnType => 'Action', # To match Possible, PossibleAdd or 92 # PossibleNot key in ACL 93 ReturnSubType => '-', # To match Possible, PossibleAdd or 94 # PossibleNot sub-key in ACL 95 96 UserID => 123, # UserID => 1 is not affected by this function 97 CustomerUserID => 'customer login', # UserID or CustomerUserID are mandatory 98 99 # Process Management Parameters 100 ProcessEntityID => 123, # Optional 101 ActivityEntityID => 123, # Optional 102 ActivityDialogEntityID => 123, # Optional 103 ); 104 105or to restrict ticket states: 106 107 $Success = $TicketObject->TicketAcl( 108 Data => { 109 1 => 'new', 110 2 => 'open', 111 # ... 112 }, 113 ReturnType => 'Ticket', 114 ReturnSubType => 'State', 115 UserID => 123, 116 ); 117 118returns: 119 $Success = 1, # if an ACL matches, or false otherwise. 120 121If ACL modules are configured in the C<Ticket::Acl::Module> config key, they are invoked 122during the call to C<TicketAcl>. The configuration of a module looks like this: 123 124 $ConfigObject->{'Ticket::Acl::Module'}->{'TheName'} = { 125 Module => 'Kernel::System::Ticket::Acl::TheAclModule', 126 Checks => ['Owner', 'Queue', 'SLA', 'Ticket'], 127 ReturnType => 'Ticket', 128 ReturnSubType => ['State', 'Service'], 129 }; 130 131Each time the C<ReturnType> and one of the C<ReturnSubType> entries is identical to the same 132arguments passed to C<TicketAcl>, the module of the name in C<Module> is loaded, the C<new> method 133is called on it, and then the C<Run> method is called. 134 135The C<Checks> array reference in the configuration controls what arguments are passed. to the 136C<Run> method. 137Valid keys are C<CustomerUser>, C<DynamicField>, C<Frontend>, C<Owner>, C<Priority>, C<Process>, 138C<Queue>, C<Responsible>, C<Service>, C<SLA>, C<State>, C<Ticket> and C<Type>. If any of those are 139present, the C<Checks> argument passed to C<Run> contains an entry with the same name, and as a 140value the associated data. 141 142The C<Run> method can add entries to the C<Acl> param hash, which are then evaluated along with all 143other ACL. It should only add entries whose conditionals can be checked with the data specified in 144the C<Checks> configuration entry. 145 146The return value of the C<Run> method is ignored. 147 148=cut 149 150sub TicketAcl { 151 my ( $Self, %Param ) = @_; 152 153 # check needed stuff 154 if ( !$Param{UserID} && !$Param{CustomerUserID} ) { 155 $Kernel::OM->Get('Kernel::System::Log')->Log( 156 Priority => 'error', 157 Message => 'Need UserID or CustomerUserID!', 158 ); 159 return; 160 } 161 162 # check needed stuff 163 for my $Needed (qw(ReturnSubType ReturnType Data)) { 164 if ( !$Param{$Needed} ) { 165 $Kernel::OM->Get('Kernel::System::Log')->Log( 166 Priority => 'error', 167 Message => "Need $Needed!" 168 ); 169 return; 170 } 171 } 172 173 # do not execute ACLs if UserID 1 is used 174 return if $Param{UserID} && $Param{UserID} == 1; 175 176 # get config object 177 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 178 179 my $ACLs = $ConfigObject->Get('TicketAcl'); 180 my $AclModules = $ConfigObject->Get('Ticket::Acl::Module'); 181 182 # only execute ACLs if ACL or ACL module is configured 183 if ( !$ACLs && !$AclModules ) { 184 return; 185 } 186 187 # find out which data we actually need 188 my %ApplicableAclModules; 189 my %RequiredChecks; 190 my $CheckAll = 0; 191 192 MODULENAME: 193 for my $ModuleName ( sort keys %{ $AclModules // {} } ) { 194 my $Module = $AclModules->{$ModuleName}; 195 if ( $Module->{ReturnType} && $Module->{ReturnType} ne $Param{ReturnType} ) { 196 next MODULENAME; 197 } 198 if ( $Module->{ReturnSubType} ) { 199 if ( ref( $Module->{ReturnSubType} ) eq 'HASH' ) { 200 next MODULENAME if !grep { $Param{ReturnSubType} eq $_ } 201 @{ $Module->{ReturnSubType} }; 202 } 203 else { 204 205 # a scalar, we hope 206 next MODULENAME if !$Module->{ReturnSubType} eq $Param{ReturnSubType}; 207 } 208 } 209 210 # here only modules applicable to this ACL invocation remain 211 $ApplicableAclModules{$ModuleName} = $Module; 212 213 if ( $Module->{Checks} && ref( $Module->{Checks} ) eq 'ARRAY' ) { 214 $RequiredChecks{$_} = 1 for @{ $Module->{Checks} }; 215 } 216 elsif ( $Module->{Checks} ) { 217 $RequiredChecks{ $Module->{Checks} } = 1; 218 } 219 else { 220 $CheckAll = 1; 221 } 222 } 223 224 return if !%ApplicableAclModules && !$ACLs && !$CheckAll; 225 226 for my $ACL ( values %{ $ACLs // {} } ) { 227 for my $Source (qw/ Properties PropertiesDatabase/) { 228 for my $Check ( sort keys %{ $ACL->{$Source} } ) { 229 my $CleanedUp = $Check; 230 $CleanedUp =~ s/(?:ID|Name|Login)$//; 231 $CleanedUp =~ s/^(?:Next|New|Old)//; 232 $RequiredChecks{$CleanedUp} = 1; 233 if ( $Check eq 'Ticket' ) { 234 if ( ref( $ACL->{Properties}{$Check} ) eq 'HASH' ) { 235 for my $InnerCheck ( sort keys %{ $ACL->{$Source}{$Check} } ) { 236 $InnerCheck =~ s/(?:ID|Name|Login)$//; 237 $InnerCheck =~ s/^(?:Next|New|Old)//; 238 $RequiredChecks{$InnerCheck} = 1; 239 } 240 } 241 } 242 } 243 } 244 } 245 246 # gather all required data to be compared against the ACLs 247 my $CheckResult = $Self->_GetChecks( 248 %Param, 249 CheckAll => $CheckAll, 250 RequiredChecks => \%RequiredChecks, 251 ); 252 my %Checks = %{ $CheckResult->{Checks} || {} }; 253 my %ChecksDatabase = %{ $CheckResult->{ChecksDatabase} || {} }; 254 255 # check ACL configuration 256 my %Acls; 257 if ( $ConfigObject->Get('TicketAcl') ) { 258 %Acls = %{ $ConfigObject->Get('TicketAcl') }; 259 } 260 261 # check ACL module 262 MODULE: 263 for my $ModuleName ( sort keys %ApplicableAclModules ) { 264 265 my $Module = $ApplicableAclModules{$ModuleName}; 266 267 next MODULE if !$Kernel::OM->Get('Kernel::System::Main')->Require( $Module->{Module} ); 268 269 my $Generic = $Module->{Module}->new(); 270 271 $Generic->Run( 272 %Param, 273 Acl => \%Acls, 274 Checks => \%Checks, 275 Config => $Module, 276 ); 277 } 278 279 # get used data 280 my %Data; 281 if ( ref $Param{Data} ) { 282 %Data = %{ $Param{Data} }; 283 } 284 285 my %NewData; 286 my $UseNewMasterParams = 0; 287 288 my %NewDefaultActionData; 289 290 if ( $Param{ReturnType} eq 'Action' ) { 291 292 if ( !IsHashRefWithData( $Param{Data} ) ) { 293 294 # use Data if is a string and it is not '-' 295 if ( IsStringWithData( $Param{Data} ) && $Param{Data} ne '-' ) { 296 %Data = ( 1 => $Param{Data} ); 297 } 298 299 # otherwise use the param Action 300 elsif ( IsStringWithData( $Param{Action} ) ) { 301 %Data = ( 1 => $Param{Action} ); 302 } 303 } 304 305 my %NewActionData = %Data; 306 307 # calculate default ticket action ACL data 308 my @ActionsToDelete; 309 my $DefaultActionData = $ConfigObject->Get('TicketACL::Default::Action') || {}; 310 311 if ( IsHashRefWithData($DefaultActionData) ) { 312 313 for my $Index ( sort keys %NewActionData ) { 314 315 my $Action = $NewActionData{$Index}; 316 if ( !$DefaultActionData->{$Index} ) { 317 push @ActionsToDelete, $Action; 318 } 319 } 320 } 321 322 $Self->{DefaultTicketAclActionData} = \%NewActionData; 323 324 for my $Action (@ActionsToDelete) { 325 delete $Self->{DefaultTicketAclActionData}->{$Action}; 326 } 327 } 328 329 # set NewTmpData after Possible Data recalculation on ReturnType Action 330 my %NewTmpData = %Data; 331 332 # get the debug parameters 333 $Self->{ACLDebug} = $ConfigObject->Get('TicketACL::Debug::Enabled') || 0; 334 $Self->{ACLDebugLogPriority} = $ConfigObject->Get('TicketACL::Debug::LogPriority') || 'debug'; 335 336 my $ACLDebugConfigFilters = $ConfigObject->Get('TicketACL::Debug::Filter') || {}; 337 for my $FilterName ( sort keys %{$ACLDebugConfigFilters} ) { 338 my %Filter = %{ $ACLDebugConfigFilters->{$FilterName} }; 339 for my $FilterItem ( sort keys %Filter ) { 340 $Self->{ACLDebugFilters}->{$FilterItem} = $Filter{$FilterItem}; 341 } 342 } 343 344 # check if debug filters apply (ticket) 345 if ( $Self->{ACLDebug} ) { 346 347 DEBUGFILTER: 348 for my $DebugFilter ( sort keys %{ $Self->{ACLDebugFilters} } ) { 349 next DEBUGFILTER if $DebugFilter eq 'ACLName'; 350 next DEBUGFILTER if !$Self->{ACLDebugFilters}->{$DebugFilter}; 351 352 if ( $DebugFilter =~ m{<OTRS_TICKET_([^>]+)>}msx ) { 353 my $TicketParam = $1; 354 355 if ( 356 defined $ChecksDatabase{Ticket}->{$TicketParam} 357 && $ChecksDatabase{Ticket}->{$TicketParam} 358 && $Self->{ACLDebugFilters}->{$DebugFilter} ne 359 $ChecksDatabase{Ticket}->{$TicketParam} 360 ) 361 { 362 $Self->{ACLDebug} = 0; 363 last DEBUGFILTER; 364 } 365 } 366 } 367 } 368 369 # remember last ACLDebug state (before ACLs loop) 370 $Self->{ACLDebugRecovery} = $Self->{ACLDebug}; 371 372 ACLRULES: 373 for my $Acl ( sort keys %Acls ) { 374 375 # check if debug filters apply (ACL) (only if ACLDebug is active) 376 if ( 377 $Self->{ACLDebugRecovery} 378 && defined $Self->{ACLDebugFilters}->{'ACLName'} 379 && $Self->{ACLDebugFilters}->{'ACLName'} 380 ) 381 { 382 # if not match current ACL disable ACLDebug 383 if ( $Self->{ACLDebugFilters}->{'ACLName'} ne $Acl ) { 384 $Self->{ACLDebug} = 0; 385 } 386 387 # reenable otherwise (we are sure it was enabled before) 388 else { 389 $Self->{ACLDebug} = 1; 390 } 391 } 392 393 my %Step = %{ $Acls{$Acl} }; 394 395 # check force match 396 my $ForceMatch; 397 if ( 398 !IsHashRefWithData( $Step{Properties} ) 399 && !IsHashRefWithData( $Step{PropertiesDatabase} ) 400 ) 401 { 402 $ForceMatch = 1; 403 } 404 405 my $PropertiesMatch; 406 my $PropertiesMatchTry; 407 my $PropertiesDatabaseMatch; 408 my $PropertiesDatabaseMatchTry; 409 my $UseNewParams = 0; 410 411 for my $PropertiesHash (qw(Properties PropertiesDatabase)) { 412 413 my %UsedChecks = %Checks; 414 if ( $PropertiesHash eq 'PropertiesDatabase' ) { 415 %UsedChecks = %ChecksDatabase; 416 } 417 418 # set match params 419 my $Match = 1; 420 my $MatchTry = 0; 421 for my $Key ( sort keys %{ $Step{$PropertiesHash} } ) { 422 for my $Data ( sort keys %{ $Step{$PropertiesHash}->{$Key} } ) { 423 my $MatchProperty = 0; 424 for my $Item ( @{ $Step{$PropertiesHash}->{$Key}->{$Data} } ) { 425 if ( ref $UsedChecks{$Key}->{$Data} eq 'ARRAY' ) { 426 my $MatchItem = 0; 427 if ( substr( $Item, 0, length '[Not' ) eq '[Not' ) { 428 $MatchItem = 1; 429 } 430 my $MatchedArrayDataItem; 431 ARRAYDATAITEM: 432 for my $ArrayDataItem ( @{ $UsedChecks{$Key}->{$Data} } ) { 433 $MatchedArrayDataItem = $ArrayDataItem; 434 my $LoopMatchResult = $Self->_CompareMatchWithData( 435 Match => $Item, 436 Data => $ArrayDataItem, 437 SingleItem => 0, 438 ); 439 if ( !$LoopMatchResult->{Skip} ) 440 { 441 $MatchItem = $LoopMatchResult->{Match}; 442 last ARRAYDATAITEM; 443 } 444 } 445 if ($MatchItem) { 446 $MatchProperty = 1; 447 448 # debug log 449 if ( $Self->{ACLDebug} ) { 450 $Kernel::OM->Get('Kernel::System::Log')->Log( 451 Priority => $Self->{ACLDebugLogPriority}, 452 Message => 453 "TicketACL '$Acl' $PropertiesHash:'$Key->$Data' MatchedARRAY ($Item eq $MatchedArrayDataItem)", 454 ); 455 } 456 } 457 } 458 elsif ( defined $UsedChecks{$Key}->{$Data} ) { 459 460 my $DataItem = $UsedChecks{$Key}->{$Data}; 461 my $MatchResult = $Self->_CompareMatchWithData( 462 Match => $Item, 463 Data => $DataItem, 464 SingleItem => 1 465 ); 466 467 if ( $MatchResult->{Match} ) { 468 $MatchProperty = 1; 469 470 # debug 471 if ( $Self->{ACLDebug} ) { 472 $Kernel::OM->Get('Kernel::System::Log')->Log( 473 Priority => $Self->{ACLDebugLogPriority}, 474 Message => 475 "TicketACL '$Acl' $PropertiesHash:'$Key->$Data' Matched ($Item eq $UsedChecks{$Key}->{$Data})", 476 ); 477 } 478 } 479 } 480 } 481 if ( !$MatchProperty ) { 482 $Match = 0; 483 } 484 $MatchTry = 1; 485 } 486 } 487 488 # check force option 489 if ($ForceMatch) { 490 $Match = 1; 491 $MatchTry = 1; 492 } 493 494 if ( $PropertiesHash eq 'Properties' ) { 495 $PropertiesMatch = $Match; 496 $PropertiesMatchTry = $MatchTry; 497 } 498 else { 499 $PropertiesDatabaseMatch = $Match; 500 $PropertiesDatabaseMatchTry = $MatchTry; 501 } 502 503 # check if properties is missing 504 if ( !IsHashRefWithData( $Step{Properties} ) ) { 505 $PropertiesMatch = $PropertiesDatabaseMatch; 506 $PropertiesMatchTry = $PropertiesDatabaseMatchTry; 507 } 508 509 # check if properties database is missing 510 if ( !IsHashRefWithData( $Step{PropertiesDatabase} ) ) { 511 $PropertiesDatabaseMatch = $PropertiesMatch; 512 $PropertiesDatabaseMatchTry = $PropertiesMatchTry; 513 } 514 } 515 516 # the following logic should be applied to calculate if an ACL matches: 517 # if both Properties and PropertiesDatabase match => match 518 # if Properties matches, and PropertiesDatabase does not match => no match 519 # if PropertiesDatabase matches, but Properties does not match => no match 520 # if PropertiesDatabase matches, and Properties is missing => match 521 # if Properties matches, and PropertiesDatabase is missing => match. 522 my $Match; 523 if ( $PropertiesMatch && $PropertiesDatabaseMatch ) { 524 $Match = 1; 525 } 526 527 my $MatchTry; 528 if ( $PropertiesMatchTry && $PropertiesDatabaseMatchTry ) { 529 $MatchTry = 1; 530 } 531 532 # debug log 533 if ( $Match && $MatchTry ) { 534 if ( $Self->{ACLDebug} ) { 535 $Kernel::OM->Get('Kernel::System::Log')->Log( 536 Priority => $Self->{ACLDebugLogPriority}, 537 Message => 538 "TicketACL '$Acl' Matched for return data:'$Param{ReturnType}:$Param{ReturnSubType}'", 539 ); 540 } 541 } 542 543 my %SpecialReturnTypes = ( 544 Action => 1, 545 Process => 1, 546 ActivityDialog => 1, 547 ); 548 549 if ( $SpecialReturnTypes{ $Param{ReturnType} } ) { 550 551 # build new Special ReturnType data hash (ProcessManagement) 552 # for Special ReturnType Step{Possible} 553 if ( 554 ( %Checks || %ChecksDatabase ) 555 && $Match 556 && $MatchTry 557 && $Step{Possible}->{ $Param{ReturnType} } 558 && IsArrayRefWithData( $Step{Possible}->{ $Param{ReturnType} } ) 559 ) 560 { 561 $UseNewParams = 1; 562 563 # reset return data as it will be filled with just the Possible Items excluded the 564 # ones that are not in the possible section, this is the same as remove all 565 # missing items from the original data 566 %NewTmpData = (); 567 568 # debug log 569 if ( $Self->{ADLDebug} ) { 570 $Kernel::OM->Get('Kernel::System::Log')->Log( 571 Priority => $Self->{ACLDebugLogPriority}, 572 Message => 573 "TicketACL '$Acl' Used with Possible:'$Param{ReturnType}:$Param{ReturnSubType}'", 574 ); 575 $Kernel::OM->Get('Kernel::System::Log')->Log( 576 Priority => $Self->{ACLDebugLogPriority}, 577 Message => 578 "TicketACL '$Acl' Reset return data:'$Param{ReturnType}:$Param{ReturnSubType}''", 579 ); 580 } 581 582 # possible list 583 for my $ID ( sort keys %Data ) { 584 585 for my $New ( @{ $Step{Possible}->{ $Param{ReturnType} } } ) { 586 my $MatchResult = $Self->_CompareMatchWithData( 587 Match => $New, 588 Data => $Data{$ID}, 589 SingleItem => 1 590 ); 591 if ( $MatchResult->{Match} ) { 592 $NewTmpData{$ID} = $Data{$ID}; 593 if ( $Self->{ACLDebug} ) { 594 $Kernel::OM->Get('Kernel::System::Log')->Log( 595 Priority => $Self->{ACLDebugLogPriority}, 596 Message => 597 "TicketACL '$Acl' Possible param '$Data{$ID}' added to return data:'$Param{ReturnType}:$Param{ReturnSubType}'", 598 ); 599 } 600 } 601 else { 602 if ( $Self->{ACLDebug} ) { 603 $Kernel::OM->Get('Kernel::System::Log')->Log( 604 Priority => $Self->{ACLDebugLogPriority}, 605 Message => 606 "TicketACL '$Acl' Possible param '$Data{$ID}' skipped from return data:'$Param{ReturnType}:$Param{ReturnSubType}'", 607 ); 608 } 609 } 610 } 611 } 612 } 613 614 # for Special ReturnType Step{PossibleAdd} 615 if ( 616 ( %Checks || %ChecksDatabase ) 617 && $Match 618 && $MatchTry 619 && $Step{PossibleAdd}->{ $Param{ReturnType} } 620 && IsArrayRefWithData( $Step{PossibleAdd}->{ $Param{ReturnType} } ) 621 ) 622 { 623 624 $UseNewParams = 1; 625 626 # debug log 627 if ( $Self->{ACLDebug} ) { 628 $Kernel::OM->Get('Kernel::System::Log')->Log( 629 Priority => $Self->{ACLDebugLogPriority}, 630 Message => 631 "TicketACL '$Acl' Used with PossibleAdd:'$Param{ReturnType}:$Param{ReturnSubType}'", 632 ); 633 } 634 635 # possible add list 636 for my $ID ( sort keys %Data ) { 637 638 for my $New ( @{ $Step{PossibleAdd}->{ $Param{ReturnType} } } ) { 639 my $MatchResult = $Self->_CompareMatchWithData( 640 Match => $New, 641 Data => $Data{$ID}, 642 SingleItem => 1 643 ); 644 if ( $MatchResult->{Match} ) { 645 $NewTmpData{$ID} = $Data{$ID}; 646 if ( $Self->{ACLDebug} ) { 647 $Kernel::OM->Get('Kernel::System::Log')->Log( 648 Priority => $Self->{ACLDebugLogPriority}, 649 Message => 650 "TicketACL '$Acl' PossibleAdd param '$Data{$ID}' added to return data:'$Param{ReturnType}:$Param{ReturnSubType}'", 651 ); 652 } 653 } 654 else { 655 if ( $Self->{ACLDebug} ) { 656 $Kernel::OM->Get('Kernel::System::Log')->Log( 657 Priority => $Self->{ACLDebugLogPriority}, 658 Message => 659 "TicketACL '$Acl' PossibleAdd param '$Data{$ID}' skipped from return data:'$Param{ReturnType}:$Param{ReturnSubType}'", 660 ); 661 } 662 } 663 } 664 } 665 } 666 667 # for Special Step{PossibleNot} 668 if ( 669 ( %Checks || %ChecksDatabase ) 670 && $Match 671 && $MatchTry 672 && $Step{PossibleNot}->{ $Param{ReturnType} } 673 && IsArrayRefWithData( $Step{PossibleNot}->{ $Param{ReturnType} } ) 674 ) 675 { 676 677 $UseNewParams = 1; 678 679 # debug log 680 if ( $Self->{ACLDebug} ) { 681 $Kernel::OM->Get('Kernel::System::Log')->Log( 682 Priority => $Self->{ACLDebugLogPriority}, 683 Message => 684 "TicketACL '$Acl' Used with PossibleNot:'$Param{ReturnType}:$Param{ReturnSubType}'", 685 ); 686 } 687 688 # not possible list 689 for my $ID ( sort keys %Data ) { 690 my $Match = 1; 691 for my $New ( @{ $Step{PossibleNot}->{ $Param{ReturnType} } } ) { 692 my $LoopMatchResult = $Self->_CompareMatchWithData( 693 Match => $New, 694 Data => $Data{$ID}, 695 SingleItem => 1 696 ); 697 if ( $LoopMatchResult->{Match} ) { 698 $Match = 0; 699 } 700 } 701 if ( !$Match ) { 702 if ( $Self->{ACLDebug} ) { 703 $Kernel::OM->Get('Kernel::System::Log')->Log( 704 Priority => $Self->{ACLDebugLogPriority}, 705 Message => 706 "TicketACL '$Acl' PossibleNot param '$Data{$ID}' removed from return data:'$Param{ReturnType}:$Param{ReturnSubType}'", 707 ); 708 } 709 if ( $NewTmpData{$ID} ) { 710 delete $NewTmpData{$ID}; 711 } 712 } 713 else { 714 if ( $Self->{ACLDebug} ) { 715 $Kernel::OM->Get('Kernel::System::Log')->Log( 716 Priority => $Self->{ACLDebugLogPriority}, 717 Message => 718 "TicketACL '$Acl' PossibleNot param '$Data{$ID}' leaved for return data:'$Param{ReturnType}:$Param{ReturnSubType}'", 719 ); 720 } 721 } 722 } 723 } 724 } 725 726 elsif ( $Param{ReturnType} eq 'Ticket' ) { 727 728 # build new ticket data hash 729 # Step Ticket Possible (Resets White list) 730 if ( 731 ( %Checks || %ChecksDatabase ) 732 && $Match 733 && $MatchTry 734 && $Step{Possible}->{Ticket}->{ $Param{ReturnSubType} } 735 ) 736 { 737 $UseNewParams = 1; 738 739 # reset return data as it will be filled with just the Possible Items excluded the ones 740 # that are not in the possible section, this is the same as remove all missing items from 741 # the original data 742 %NewTmpData = (); 743 744 # debug log 745 if ( $Self->{ACLDebug} ) { 746 $Kernel::OM->Get('Kernel::System::Log')->Log( 747 Priority => $Self->{ACLDebugLogPriority}, 748 Message => 749 "TicketACL '$Acl' Used with Possible:'$Param{ReturnType}:$Param{ReturnSubType}'", 750 ); 751 $Kernel::OM->Get('Kernel::System::Log')->Log( 752 Priority => $Self->{ACLDebugLogPriority}, 753 Message => 754 "TicketACL '$Acl' Reset return data:'$Param{ReturnType}:$Param{ReturnSubType}''", 755 ); 756 } 757 758 # possible list 759 for my $ID ( sort keys %Data ) { 760 761 for my $New ( @{ $Step{Possible}->{Ticket}->{ $Param{ReturnSubType} } } ) { 762 my $MatchResult = $Self->_CompareMatchWithData( 763 Match => $New, 764 Data => $Data{$ID}, 765 SingleItem => 1 766 ); 767 if ( $MatchResult->{Match} ) { 768 $NewTmpData{$ID} = $Data{$ID}; 769 if ( $Self->{ACLDebug} ) { 770 $Kernel::OM->Get('Kernel::System::Log')->Log( 771 Priority => $Self->{ACLDebugLogPriority}, 772 Message => 773 "TicketACL '$Acl' Possible param '$Data{$ID}' added to return data:'$Param{ReturnType}:$Param{ReturnSubType}'", 774 ); 775 } 776 } 777 else { 778 if ( $Self->{ACLDebug} ) { 779 $Kernel::OM->Get('Kernel::System::Log')->Log( 780 Priority => $Self->{ACLDebugLogPriority}, 781 Message => 782 "TicketACL '$Acl' Possible param '$Data{$ID}' skipped from return data:'$Param{ReturnType}:$Param{ReturnSubType}'", 783 ); 784 } 785 } 786 } 787 } 788 } 789 790 # Step Ticket PossibleAdd (Add new options to the white list) 791 if ( 792 ( %Checks || %ChecksDatabase ) 793 && $Match 794 && $MatchTry 795 && $Step{PossibleAdd}->{Ticket}->{ $Param{ReturnSubType} } 796 ) 797 { 798 $UseNewParams = 1; 799 800 # debug log 801 if ( $Self->{ACLDebug} ) { 802 $Kernel::OM->Get('Kernel::System::Log')->Log( 803 Priority => $Self->{ACLDebugLogPriority}, 804 Message => 805 "TicketACL '$Acl' Used with PossibleAdd:'$Param{ReturnType}:$Param{ReturnSubType}'", 806 ); 807 } 808 809 # possible add list 810 for my $ID ( sort keys %Data ) { 811 812 for my $New ( @{ $Step{PossibleAdd}->{Ticket}->{ $Param{ReturnSubType} } } ) { 813 my $MatchResult = $Self->_CompareMatchWithData( 814 Match => $New, 815 Data => $Data{$ID}, 816 SingleItem => 1 817 ); 818 if ( $MatchResult->{Match} ) { 819 $NewTmpData{$ID} = $Data{$ID}; 820 if ( $Self->{ACLDebug} ) { 821 $Kernel::OM->Get('Kernel::System::Log')->Log( 822 Priority => $Self->{ACLDebugLogPriority}, 823 Message => 824 "TicketACL '$Acl' PossibleAdd param '$Data{$ID}' added to return data:'$Param{ReturnType}:$Param{ReturnSubType}'", 825 ); 826 } 827 } 828 else { 829 if ( $Self->{ACLDebug} ) { 830 $Kernel::OM->Get('Kernel::System::Log')->Log( 831 Priority => $Self->{ACLDebugLogPriority}, 832 Message => 833 "TicketACL '$Acl' PossibleAdd param '$Data{$ID}' skipped from return data:'$Param{ReturnType}:$Param{ReturnSubType}'", 834 ); 835 } 836 } 837 } 838 } 839 } 840 841 # Step Ticket PossibleNot (removes options from white list) 842 if ( 843 ( %Checks || %ChecksDatabase ) 844 && $Match 845 && $MatchTry 846 && $Step{PossibleNot}->{Ticket}->{ $Param{ReturnSubType} } 847 ) 848 { 849 $UseNewParams = 1; 850 851 # debug log 852 if ( $Self->{ACLDebug} ) { 853 $Kernel::OM->Get('Kernel::System::Log')->Log( 854 Priority => $Self->{ACLDebugLogPriority}, 855 Message => 856 "TicketACL '$Acl' Used with PossibleNot:'$Param{ReturnType}:$Param{ReturnSubType}'", 857 ); 858 } 859 860 # not possible list 861 for my $ID ( sort keys %Data ) { 862 my $Match = 1; 863 for my $New ( @{ $Step{PossibleNot}->{Ticket}->{ $Param{ReturnSubType} } } ) { 864 my $LoopMatchResult = $Self->_CompareMatchWithData( 865 Match => $New, 866 Data => $Data{$ID}, 867 SingleItem => 1 868 ); 869 if ( $LoopMatchResult->{Match} ) { 870 $Match = 0; 871 } 872 } 873 if ( !$Match ) { 874 if ( $Self->{ACLDebug} ) { 875 $Kernel::OM->Get('Kernel::System::Log')->Log( 876 Priority => $Self->{ACLDebugLogPriority}, 877 Message => 878 "TicketACL '$Acl' PossibleNot param '$Data{$ID}' removed from return data:'$Param{ReturnType}:$Param{ReturnSubType}'", 879 ); 880 } 881 if ( $NewTmpData{$ID} ) { 882 delete $NewTmpData{$ID}; 883 } 884 } 885 else { 886 if ( $Self->{ACLDebug} ) { 887 $Kernel::OM->Get('Kernel::System::Log')->Log( 888 Priority => $Self->{ACLDebugLogPriority}, 889 Message => 890 "TicketACL '$Acl' PossibleNot param '$Data{$ID}' leaved for return data:'$Param{ReturnType}:$Param{ReturnSubType}'", 891 ); 892 } 893 } 894 } 895 } 896 } 897 898 # remember to new params if given 899 if ($UseNewParams) { 900 %NewData = %NewTmpData; 901 $UseNewMasterParams = 1; 902 } 903 904 # return new params if stop after this step 905 if ( $UseNewParams && $Step{StopAfterMatch} ) { 906 $Self->{TicketAclData} = \%NewData; 907 908 # if we stop after the first match 909 # exit the ACLRULES loop 910 last ACLRULES; 911 } 912 } 913 914 # return if no new param exists 915 return if !$UseNewMasterParams; 916 917 $Self->{TicketAclData} = \%NewData; 918 919 return 1; 920} 921 922=head2 TicketAclData() 923 924return the current ACL data hash after TicketAcl() 925 926 my %Acl = $TicketObject->TicketAclData(); 927 928=cut 929 930sub TicketAclData { 931 my ( $Self, %Param ) = @_; 932 933 return %{ $Self->{TicketAclData} || {} }; 934} 935 936=head2 TicketAclActionData() 937 938return the current ACL action data hash after TicketAcl() 939 940 my %AclAction = $TicketObject->TicketAclActionData(); 941 942=cut 943 944sub TicketAclActionData { 945 my ( $Self, %Param ) = @_; 946 947 if ( $Self->{TicketAclData} ) { 948 return %{ $Self->{TicketAclData} }; 949 } 950 return %{ $Self->{DefaultTicketActionData} || {} }; 951} 952 953=begin Internal: 954 955=cut 956 957=head2 _GetChecks() 958 959creates two check hashes (one for current data updatable via AJAX refreshes and another for 960static ticket data stored in the DB) with the required data to use as a basis to match the ACLs 961 962 my $ChecskResult = $TicketObject->_GetChecks( 963 CheckAll => '1', # Optional 964 RequiredChecks => $RequiredCheckHashRef, # Optional a hash reference with the 965 # attributes to gather: 966 # e. g. User => 1, will fetch all user 967 # information from the database, this data 968 # will be tried to match with current ACLs 969 Action => 'AgentTicketZoom', # Optional 970 TicketID => 123, # Optional 971 DynamicField => { # Optional 972 DynamicField_NameX => 123, 973 DynamicField_NameZ => 'some value', 974 }, 975 976 QueueID => 123, # Optional 977 Queue => 'some queue name', # Optional 978 979 ServiceID => 123, # Optional 980 Service => 'some service name', # Optional 981 982 TypeID => 123, 983 Type => 'some ticket type name', # Optional 984 985 PriorityID => 123, # Optional 986 NewPriorityID => 123, # Optional, PriorityID or NewPriorityID can be 987 # used and they both refers to PriorityID 988 Priority => 'some priority name', # Optional 989 990 SLAID => 123, 991 SLA => 'some SLA name', # Optional 992 993 StateID => 123, # Optional 994 NextStateID => 123, # Optional, StateID or NextStateID can be 995 # used and they both refers to StateID 996 State => 'some ticket state name', # Optional 997 998 OwnerID => 123, # Optional 999 NewOwnerID => 123, # Optional, OwnerID or NewOwnerID can be 1000 # used and they both refers to OwnerID 1001 Owner => 'some user login' # Optional 1002 1003 ResponsibleID => 123, # Optional 1004 NewResponsibleID => 123, # Optional, ResponsibleID or NewResposibleID 1005 # can be used and they both refers to 1006 # ResponsibleID 1007 Responsible => 'some user login' # Optional 1008 1009 UserID => 123, # UserID => 1 is not affected by this function 1010 CustomerUserID => 'customer login', # UserID or CustomerUserID are mandatory 1011 1012 # Process Management Parameters 1013 ProcessEntityID => 123, # Optional 1014 ActivityEntityID => 123, # Optional 1015 ActivityDialogEntityID => 123, # Optional 1016 ); 1017 1018returns: 1019 $ChecksResult = { 1020 Checks => { 1021 # ... 1022 Ticket => { 1023 TicketID => 123, 1024 # ... 1025 Queue => 'some queue name', 1026 QueueID => '123', 1027 # ... 1028 }, 1029 Queue => { 1030 Name => 'some queue name', 1031 # ... 1032 }, 1033 # ... 1034 }, 1035 ChecksDatabase => 1036 # ... 1037 Ticket => { 1038 TicketID => 123, 1039 # ... 1040 Queue => 'original queue name', 1041 QueueID => '456', 1042 # ... 1043 }, 1044 Queue => { 1045 Name => 'original queue name', 1046 # ... 1047 }, 1048 # ... 1049 }, 1050 }; 1051 1052=cut 1053 1054sub _GetChecks { 1055 my ( $Self, %Param ) = @_; 1056 1057 my $CheckAll = $Param{CheckAll}; 1058 my %RequiredChecks = %{ $Param{RequiredChecks} }; 1059 1060 # get used interface for process management checks 1061 my $Interface = 'AgentInterface'; 1062 if ( !$Param{UserID} ) { 1063 $Interface = 'CustomerInterface'; 1064 } 1065 1066 my %Checks; 1067 my %ChecksDatabase; 1068 1069 if ( $Param{Action} ) { 1070 $Checks{Frontend} = { 1071 Action => $Param{Action}, 1072 }; 1073 $ChecksDatabase{Frontend} = { 1074 Action => $Param{Action}, 1075 }; 1076 } 1077 1078 # get config object 1079 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 1080 1081 # use ticket data if ticket id is given 1082 # do that always, even if $RequiredChecks{Ticket} is not that 1083 # (because too much stuff depends on it) 1084 if ( $Param{TicketID} ) { 1085 my %Ticket = $Self->TicketGet( 1086 %Param, 1087 DynamicFields => 1, 1088 ); 1089 $Checks{Ticket} = \%Ticket; 1090 1091 # keep database ticket data separated since the reference is affected below 1092 my %TicketDatabase = %Ticket; 1093 $ChecksDatabase{Ticket} = \%TicketDatabase; 1094 1095 # get used dynamic fields where Activity and Process Entities IDs are Stored 1096 # (ProcessManagement) 1097 my $ActivityEntityIDField = $ConfigObject->Get("Process::DynamicFieldProcessManagementActivityID"); 1098 my $ProcessEntityIDField = $ConfigObject->Get("Process::DynamicFieldProcessManagementProcessID"); 1099 1100 # check for ActivityEntityID 1101 if ( $Ticket{ 'DynamicField_' . $ActivityEntityIDField } ) { 1102 $ChecksDatabase{Process}->{ActivityEntityID} = $Ticket{ 'DynamicField_' . $ActivityEntityIDField }; 1103 } 1104 1105 # check for ProcessEntityID 1106 if ( $Ticket{ 'DynamicField_' . $ProcessEntityIDField } ) { 1107 $ChecksDatabase{Process}->{ProcessEntityID} = $Ticket{ 'DynamicField_' . $ProcessEntityIDField }; 1108 } 1109 1110 # take over the ChecksDatabase to the Checks hash as basis 1111 if ( $ChecksDatabase{Process} && %{ $ChecksDatabase{Process} } ) { 1112 my %ProcessDatabase = %{ $ChecksDatabase{Process} }; 1113 $Checks{Process} = \%ProcessDatabase; 1114 } 1115 } 1116 1117 # check for ProcessEntityID if set as parameter (ProcessManagement) 1118 if ( ( $CheckAll || $RequiredChecks{Process} ) && $Param{ProcessEntityID} ) { 1119 $Checks{Process}->{ProcessEntityID} = $Param{ProcessEntityID}; 1120 } 1121 1122 # check for ActivityDialogEntityID if set as parameter (ProcessManagement) 1123 if ( ( $CheckAll || $RequiredChecks{Process} ) && $Param{ActivityDialogEntityID} ) { 1124 1125 my $ActivityDialog = $Kernel::OM->Get('Kernel::System::ProcessManagement::ActivityDialog')->ActivityDialogGet( 1126 ActivityDialogEntityID => $Param{ActivityDialogEntityID}, 1127 Interface => $Interface, 1128 ); 1129 1130 if ( IsHashRefWithData($ActivityDialog) ) { 1131 $Checks{Process}->{ActivityDialogEntityID} = $Param{ActivityDialogEntityID}; 1132 } 1133 } 1134 1135 # Check for ActivityEntityID if set as parameter (ProcessManagement). 1136 if ( ( $CheckAll || $RequiredChecks{Process} ) && $Param{ActivityEntityID} ) { 1137 1138 my $Activity = $Kernel::OM->Get('Kernel::System::ProcessManagement::Activity')->ActivityGet( 1139 ActivityEntityID => $Param{ActivityEntityID}, 1140 Interface => [$Interface], 1141 ); 1142 1143 if ( IsHashRefWithData($Activity) ) { 1144 $Checks{Process}->{ActivityEntityID} = $Param{ActivityEntityID}; 1145 } 1146 } 1147 1148 # check for dynamic fields 1149 if ( IsHashRefWithData( $Param{DynamicField} ) ) { 1150 $Checks{DynamicField} = $Param{DynamicField}; 1151 1152 # update or add dynamic fields information to the ticket check 1153 for my $DynamicFieldName ( sort keys %{ $Param{DynamicField} } ) { 1154 $Checks{Ticket}->{$DynamicFieldName} = $Param{DynamicField}->{$DynamicFieldName}; 1155 } 1156 } 1157 1158 # always get info from ticket too and set it to the Dynamic Field check hash if the info is 1159 # different. this can be done because in the previous step ticket info was updated. but maybe 1160 # ticket has more information stored than in the DynamicField parameter. 1161 TICKETATTRIBUTE: 1162 for my $TicketAttribute ( sort keys %{ $Checks{Ticket} // {} } ) { 1163 next TICKETATTRIBUTE if !$TicketAttribute; 1164 1165 # check if is a dynamic field with data 1166 next TICKETATTRIBUTE if $TicketAttribute !~ m{ \A DynamicField_ }smx; 1167 next TICKETATTRIBUTE if !defined $Checks{Ticket}->{$TicketAttribute}; 1168 next TICKETATTRIBUTE if !length $Checks{Ticket}->{$TicketAttribute}; 1169 1170 if ( 1171 ref $Checks{Ticket}->{$TicketAttribute} eq 'ARRAY' 1172 && !IsArrayRefWithData( $Checks{Ticket}->{$TicketAttribute} ) 1173 ) 1174 { 1175 next TICKETATTRIBUTE; 1176 } 1177 1178 # compare if data is different and skip on same data 1179 if ( 1180 $Checks{DynamicField}->{$TicketAttribute} 1181 && !DataIsDifferent( 1182 Data1 => $Checks{Ticket}->{$TicketAttribute}, 1183 Data2 => $Checks{DynamicField}->{$TicketAttribute}, 1184 ) 1185 ) 1186 { 1187 next TICKETATTRIBUTE; 1188 } 1189 1190 $Checks{DynamicField}->{$TicketAttribute} = $Checks{Ticket}->{$TicketAttribute}; 1191 } 1192 1193 # also copy the database information to the appropriate hash 1194 TICKETATTRIBUTE: 1195 for my $TicketAttribute ( sort keys %{ $ChecksDatabase{Ticket} } ) { 1196 next TICKETATTRIBUTE if !$TicketAttribute; 1197 1198 # check if is a dynamic field with data 1199 next TICKETATTRIBUTE if $TicketAttribute !~ m{ \A DynamicField_ }smx; 1200 next TICKETATTRIBUTE if !defined $ChecksDatabase{Ticket}->{$TicketAttribute}; 1201 next TICKETATTRIBUTE if !length $ChecksDatabase{Ticket}->{$TicketAttribute}; 1202 1203 if ( 1204 ref $ChecksDatabase{Ticket}->{$TicketAttribute} eq 'ARRAY' 1205 && !IsArrayRefWithData( $ChecksDatabase{Ticket}->{$TicketAttribute} ) 1206 ) 1207 { 1208 next TICKETATTRIBUTE; 1209 } 1210 1211 $ChecksDatabase{DynamicField}->{$TicketAttribute} = $ChecksDatabase{Ticket}->{$TicketAttribute}; 1212 } 1213 1214 # use user data 1215 if ( ( $CheckAll || $RequiredChecks{User} ) && $Param{UserID} ) { 1216 1217 my %User = $Kernel::OM->Get('Kernel::System::User')->GetUserData( 1218 UserID => $Param{UserID}, 1219 ); 1220 1221 # get group object 1222 my $GroupObject = $Kernel::OM->Get('Kernel::System::Group'); 1223 1224 for my $Type ( @{ $ConfigObject->Get('System::Permission') } ) { 1225 1226 my %Groups = $GroupObject->PermissionUserGet( 1227 UserID => $Param{UserID}, 1228 Type => $Type, 1229 ); 1230 1231 my @GroupNames = sort values %Groups; 1232 1233 $User{"Group_$Type"} = \@GroupNames; 1234 } 1235 1236 my %RoleIDs = $GroupObject->PermissionUserRoleGet( 1237 UserID => $Param{UserID}, 1238 ); 1239 1240 my @RoleNames = sort values %RoleIDs; 1241 1242 $User{Role} = \@RoleNames; 1243 $Checks{User} = \%User; 1244 $ChecksDatabase{User} = \%User; 1245 } 1246 1247 # get customer user objects 1248 my $CustomerGroupObject = $Kernel::OM->Get('Kernel::System::CustomerGroup'); 1249 my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser'); 1250 1251 # use customer user data 1252 if ( ( $CheckAll || $RequiredChecks{CustomerUser} ) && $Param{CustomerUserID} ) { 1253 1254 my %CustomerUser = $CustomerUserObject->CustomerUserDataGet( 1255 User => $Param{CustomerUserID}, 1256 ); 1257 1258 for my $Type ( @{ $ConfigObject->Get('System::Customer::Permission') } ) { 1259 1260 my @Groups = $CustomerGroupObject->GroupMemberList( 1261 UserID => $Param{CustomerUserID}, 1262 Result => 'Name', 1263 Type => $Type, 1264 ); 1265 1266 $CustomerUser{"Group_$Type"} = \@Groups; 1267 } 1268 1269 $Checks{CustomerUser} = \%CustomerUser; 1270 1271 # update or add customer information to the ticket check 1272 $Checks{Ticket}->{CustomerUserID} = $Checks{CustomerUser}->{UserLogin}; 1273 $Checks{Ticket}->{CustomerID} = $Checks{CustomerUser}->{UserCustomerID}; 1274 } 1275 else { 1276 if ( IsStringWithData( $Checks{Ticket}->{CustomerUserID} ) ) { 1277 1278 # get customer data from the ticket 1279 my %CustomerUser = $CustomerUserObject->CustomerUserDataGet( 1280 User => $Checks{Ticket}->{CustomerUserID}, 1281 ); 1282 1283 for my $Type ( @{ $ConfigObject->Get('System::Customer::Permission') } ) { 1284 1285 my @Groups = $CustomerGroupObject->GroupMemberList( 1286 UserID => $Checks{Ticket}->{CustomerUserID}, 1287 Result => 'Name', 1288 Type => $Type, 1289 ); 1290 1291 $CustomerUser{"Group_$Type"} = \@Groups; 1292 } 1293 1294 $Checks{CustomerUser} = \%CustomerUser; 1295 } 1296 } 1297 1298 # create hash with the ticket information stored in the database 1299 if ( 1300 ( $CheckAll || $RequiredChecks{CustomerUser} ) 1301 && IsStringWithData( $ChecksDatabase{Ticket}->{CustomerUserID} ) 1302 ) 1303 { 1304 1305 # check if database data matches current data (performance) 1306 if ( 1307 defined $Checks{CustomerUser}->{UserLogin} 1308 && $ChecksDatabase{Ticket}->{CustomerUserID} eq $Checks{CustomerUser}->{UserLogin} 1309 ) 1310 { 1311 $ChecksDatabase{CustomerUser} = $Checks{CustomerUser}; 1312 } 1313 1314 # otherwise complete the data querying the database again 1315 else { 1316 1317 my %CustomerUser = $CustomerUserObject->CustomerUserDataGet( 1318 User => $ChecksDatabase{Ticket}->{CustomerUserID}, 1319 ); 1320 1321 for my $Type ( @{ $ConfigObject->Get('System::Customer::Permission') } ) { 1322 1323 my @Groups = $CustomerGroupObject->GroupMemberList( 1324 UserID => $ChecksDatabase{Ticket}->{CustomerUserID}, 1325 Result => 'Name', 1326 Type => $Type, 1327 ); 1328 1329 $CustomerUser{"Group_$Type"} = \@Groups; 1330 } 1331 1332 $ChecksDatabase{CustomerUser} = \%CustomerUser; 1333 } 1334 } 1335 1336 # use queue data (if given) 1337 if ( $CheckAll || $RequiredChecks{Queue} ) { 1338 1339 # get queue object 1340 my $QueueObject = $Kernel::OM->Get('Kernel::System::Queue'); 1341 1342 if ( $Param{NewQueueID} && !$Param{QueueID} ) { 1343 $Param{QueueID} = $Param{NewQueueID}; 1344 } 1345 1346 if ( $Param{QueueID} ) { 1347 my %Queue = $QueueObject->QueueGet( ID => $Param{QueueID} ); 1348 $Checks{Queue} = \%Queue; 1349 1350 # update or add queue information to the ticket check 1351 $Checks{Ticket}->{Queue} = $Checks{Queue}->{Name}; 1352 $Checks{Ticket}->{QueueID} = $Checks{Queue}->{QueueID}; 1353 } 1354 elsif ( $Param{Queue} ) { 1355 my %Queue = $QueueObject->QueueGet( Name => $Param{Queue} ); 1356 $Checks{Queue} = \%Queue; 1357 1358 # update or add queue information to the ticket check 1359 $Checks{Ticket}->{Queue} = $Checks{Queue}->{Name}; 1360 $Checks{Ticket}->{QueueID} = $Checks{Queue}->{QueueID}; 1361 } 1362 elsif ( !$Param{QueueID} && !$Param{Queue} ) { 1363 if ( IsPositiveInteger( $Checks{Ticket}->{QueueID} ) ) { 1364 1365 # get queue data from the ticket 1366 my %Queue = $QueueObject->QueueGet( ID => $Checks{Ticket}->{QueueID} ); 1367 $Checks{Queue} = \%Queue; 1368 } 1369 } 1370 } 1371 1372 # create hash with the ticket information stored in the database 1373 if ( 1374 ( $CheckAll || $RequiredChecks{Queue} ) 1375 && IsPositiveInteger( $ChecksDatabase{Ticket}->{QueueID} ) 1376 ) 1377 { 1378 1379 # check if database data matches current data (performance) 1380 if ( 1381 defined $Checks{Queue}->{QueueID} 1382 && $ChecksDatabase{Ticket}->{QueueID} eq $Checks{Queue}->{QueueID} 1383 ) 1384 { 1385 $ChecksDatabase{Queue} = $Checks{Queue}; 1386 } 1387 1388 # otherwise complete the data querying the database again 1389 else { 1390 1391 # get queue object 1392 my $QueueObject = $Kernel::OM->Get('Kernel::System::Queue'); 1393 1394 my %Queue = $QueueObject->QueueGet( 1395 ID => $ChecksDatabase{Ticket}->{QueueID}, 1396 ); 1397 1398 $ChecksDatabase{Queue} = \%Queue; 1399 } 1400 } 1401 1402 # use service data (if given) 1403 if ( $CheckAll || $RequiredChecks{Service} ) { 1404 1405 # get service object 1406 my $ServiceObject = $Kernel::OM->Get('Kernel::System::Service'); 1407 1408 if ( $Param{ServiceID} ) { 1409 my %Service = $ServiceObject->ServiceGet( 1410 ServiceID => $Param{ServiceID}, 1411 UserID => 1, 1412 ); 1413 $Checks{Service} = \%Service; 1414 1415 # update or add service information to the ticket check 1416 $Checks{Ticket}->{Service} = $Checks{Service}->{Name}; 1417 $Checks{Ticket}->{ServiceID} = $Checks{Service}->{ServiceID}; 1418 } 1419 elsif ( $Param{Service} ) { 1420 my %Service = $ServiceObject->ServiceGet( 1421 Name => $Param{Service}, 1422 UserID => 1, 1423 ); 1424 $Checks{Service} = \%Service; 1425 1426 # update or add service information to the ticket check 1427 $Checks{Ticket}->{Service} = $Checks{Service}->{Name}; 1428 $Checks{Ticket}->{ServiceID} = $Checks{Service}->{ServiceID}; 1429 } 1430 elsif ( !$Param{ServiceID} && !$Param{Service} ) { 1431 if ( IsPositiveInteger( $Checks{Ticket}->{ServiceID} ) ) { 1432 1433 # get service data from the ticket 1434 my %Service = $ServiceObject->ServiceGet( 1435 ServiceID => $Checks{Ticket}->{ServiceID}, 1436 UserID => 1, 1437 ); 1438 $Checks{Service} = \%Service; 1439 } 1440 } 1441 1442 # create hash with the ticket information stored in the database 1443 if ( IsPositiveInteger( $ChecksDatabase{Ticket}->{ServiceID} ) ) { 1444 1445 # check if database data matches current data (performance) 1446 if ( 1447 defined $Checks{Queue}->{QueueID} 1448 && $ChecksDatabase{Ticket}->{ServiceID} eq $Checks{Service}->{ServiceID} 1449 ) 1450 { 1451 $ChecksDatabase{Service} = $Checks{Service}; 1452 } 1453 1454 # otherwise complete the data querying the database again 1455 else { 1456 my %Service = $ServiceObject->ServiceGet( 1457 ServiceID => $ChecksDatabase{Ticket}->{ServiceID}, 1458 UserID => 1, 1459 ); 1460 $ChecksDatabase{Service} = \%Service; 1461 } 1462 } 1463 } 1464 1465 # use type data (if given) 1466 if ( $CheckAll || $RequiredChecks{Type} ) { 1467 1468 # get type object 1469 my $TypeObject = $Kernel::OM->Get('Kernel::System::Type'); 1470 1471 if ( $Param{TypeID} ) { 1472 my %Type = $TypeObject->TypeGet( 1473 ID => $Param{TypeID}, 1474 UserID => 1, 1475 ); 1476 $Checks{Type} = \%Type; 1477 1478 # update or add ticket type information to the ticket check 1479 $Checks{Ticket}->{Type} = $Checks{Type}->{Name}; 1480 $Checks{Ticket}->{TypeID} = $Checks{Type}->{ID}; 1481 } 1482 elsif ( $Param{Type} ) { 1483 1484 # TODO Attention! 1485 # 1486 # The parameter type can contain not only the wanted ticket type, because also 1487 # some other functions in Kernel/System/Ticket.pm use a type parameter, for example 1488 # MoveList() etc... These functions could be rewritten to not 1489 # use a Type parameter, or the functions that call TicketAcl() could be modified to 1490 # not just pass the complete Param-Hash, but instead a new parameter, like FrontEndParameter. 1491 # 1492 # As a workaround we lookup the TypeList first, and compare if the type parameter 1493 # is found in the list, so we can be more sure that it is the type that we want here. 1494 1495 # lookup the type list (workaround for described problem) 1496 my %TypeList = reverse $TypeObject->TypeList(); 1497 1498 # check if type is in the type list (workaround for described problem) 1499 if ( $TypeList{ $Param{Type} } ) { 1500 my %Type = $TypeObject->TypeGet( 1501 Name => $Param{Type}, 1502 UserID => 1, 1503 ); 1504 $Checks{Type} = \%Type; 1505 1506 # update or add ticket type information to the ticket check 1507 $Checks{Ticket}->{Type} = $Checks{Type}->{Name}; 1508 $Checks{Ticket}->{TypeID} = $Checks{Type}->{ID}; 1509 } 1510 } 1511 elsif ( !$Param{TypeID} && !$Param{Type} ) { 1512 if ( IsPositiveInteger( $Checks{Ticket}->{TypeID} ) ) { 1513 1514 # get type data from the ticket 1515 my %Type = $TypeObject->TypeGet( 1516 ID => $Checks{Ticket}->{TypeID}, 1517 UserID => 1, 1518 ); 1519 $Checks{Type} = \%Type; 1520 } 1521 } 1522 1523 # create hash with the ticket information stored in the database 1524 if ( IsPositiveInteger( $ChecksDatabase{Ticket}->{TypeID} ) ) { 1525 1526 # check if database data matches current data (performance) 1527 if ( 1528 defined $Checks{Type}->{ID} 1529 && $ChecksDatabase{Ticket}->{TypeID} eq $Checks{Type}->{ID} 1530 ) 1531 { 1532 $ChecksDatabase{Type} = $Checks{Type}; 1533 } 1534 1535 # otherwise complete the data querying the database again 1536 else { 1537 my %Type = $TypeObject->TypeGet( 1538 ID => $ChecksDatabase{Ticket}->{TypeID}, 1539 UserID => 1, 1540 ); 1541 $ChecksDatabase{Type} = \%Type; 1542 } 1543 } 1544 } 1545 1546 if ( $CheckAll || $RequiredChecks{Priority} ) { 1547 1548 # get priority object 1549 my $PriorityObject = $Kernel::OM->Get('Kernel::System::Priority'); 1550 1551 # use priority data (if given) 1552 if ( $Param{NewPriorityID} && !$Param{PriorityID} ) { 1553 $Param{PriorityID} = $Param{NewPriorityID}; 1554 } 1555 1556 if ( $Param{PriorityID} ) { 1557 my %Priority = $PriorityObject->PriorityGet( 1558 PriorityID => $Param{PriorityID}, 1559 UserID => 1, 1560 ); 1561 $Checks{Priority} = \%Priority; 1562 1563 # update or add priority information to the ticket check 1564 $Checks{Ticket}->{Priority} = $Checks{Priority}->{Name}; 1565 $Checks{Ticket}->{PriorityID} = $Checks{Priority}->{ID}; 1566 } 1567 elsif ( $Param{Priority} ) { 1568 my $PriorityID = $PriorityObject->PriorityLookup( 1569 Priority => $Param{Priority}, 1570 ); 1571 my %Priority = $PriorityObject->PriorityGet( 1572 PriorityID => $PriorityID, 1573 UserID => 1, 1574 ); 1575 $Checks{Priority} = \%Priority; 1576 1577 # update or add priority information to the ticket check 1578 $Checks{Ticket}->{Priority} = $Checks{Priority}->{Name}; 1579 $Checks{Ticket}->{PriorityID} = $Checks{Priority}->{ID}; 1580 } 1581 elsif ( !$Param{PriorityID} && !$Param{Priority} ) { 1582 if ( IsPositiveInteger( $Checks{Ticket}->{PriorityID} ) ) { 1583 1584 # get priority data from the ticket 1585 my %Priority = $PriorityObject->PriorityGet( 1586 PriorityID => $Checks{Ticket}->{PriorityID}, 1587 UserID => 1, 1588 ); 1589 $Checks{Priority} = \%Priority; 1590 } 1591 } 1592 } 1593 1594 # create hash with the ticket information stored in the database 1595 if ( IsPositiveInteger( $ChecksDatabase{Ticket}->{PriorityID} ) ) { 1596 1597 # check if database data matches current data (performance) 1598 if ( 1599 defined $Checks{Priority}->{ID} 1600 && $ChecksDatabase{Ticket}->{PriorityID} eq $Checks{Priority}->{ID} 1601 ) 1602 { 1603 $ChecksDatabase{Priority} = $Checks{Priority}; 1604 } 1605 1606 # otherwise complete the data querying the database again 1607 else { 1608 1609 # get priority object 1610 my $PriorityObject = $Kernel::OM->Get('Kernel::System::Priority'); 1611 1612 # get priority data from the ticket 1613 my %Priority = $PriorityObject->PriorityGet( 1614 PriorityID => $ChecksDatabase{Ticket}->{PriorityID}, 1615 UserID => 1, 1616 ); 1617 $ChecksDatabase{Priority} = \%Priority; 1618 } 1619 } 1620 1621 # use SLA data (if given) 1622 if ( $CheckAll || $RequiredChecks{SLA} ) { 1623 1624 # get sla object 1625 my $SLAObject = $Kernel::OM->Get('Kernel::System::SLA'); 1626 1627 if ( $Param{SLAID} ) { 1628 my %SLA = $SLAObject->SLAGet( 1629 SLAID => $Param{SLAID}, 1630 UserID => 1, 1631 ); 1632 $Checks{SLA} = \%SLA; 1633 1634 # update or add SLA information to the ticket check 1635 $Checks{Ticket}->{SLA} = $Checks{SLA}->{Name}; 1636 $Checks{Ticket}->{SLAID} = $Checks{SLA}->{SLAID}; 1637 } 1638 elsif ( $Param{SLA} ) { 1639 my $SLAID = $SLAObject->SLALookup( 1640 Name => $Param{SLA}, 1641 ); 1642 my %SLA = $SLAObject->SLAGet( 1643 SLAID => $SLAID, 1644 UserID => 1, 1645 ); 1646 $Checks{SLA} = \%SLA; 1647 1648 # update or add SLA information to the ticket check 1649 $Checks{Ticket}->{SLA} = $Checks{SLA}->{Name}; 1650 $Checks{Ticket}->{SLAID} = $Checks{SLA}->{SLAID}; 1651 } 1652 elsif ( !$Param{SLAID} && !$Param{SLA} ) { 1653 1654 if ( IsPositiveInteger( $Checks{Ticket}->{SLAID} ) ) { 1655 1656 # get SLA data from the ticket 1657 my %SLA = $SLAObject->SLAGet( 1658 SLAID => $Checks{Ticket}->{SLAID}, 1659 UserID => 1, 1660 ); 1661 $Checks{SLA} = \%SLA; 1662 } 1663 } 1664 } 1665 1666 # create hash with the ticket information stored in the database 1667 if ( IsPositiveInteger( $ChecksDatabase{Ticket}->{SLAID} ) ) { 1668 1669 # check if database data matches current data (performance) 1670 if ( 1671 defined $Checks{SLA}->{SLAID} 1672 && $ChecksDatabase{Ticket}->{SLAID} eq $Checks{SLA}->{SLAID} 1673 ) 1674 { 1675 $ChecksDatabase{SLA} = $Checks{SLA}; 1676 } 1677 1678 # otherwise complete the data querying the database again 1679 else { 1680 1681 my %SLA = $Kernel::OM->Get('Kernel::System::SLA')->SLAGet( 1682 SLAID => $ChecksDatabase{Ticket}->{SLAID}, 1683 UserID => 1, 1684 ); 1685 $ChecksDatabase{SLA} = \%SLA; 1686 } 1687 } 1688 1689 # get state object 1690 my $StateObject = $Kernel::OM->Get('Kernel::System::State'); 1691 1692 # use state data (if given) 1693 if ( $CheckAll || $RequiredChecks{State} ) { 1694 if ( !$Param{StateID} ) { 1695 $Param{StateID} = $Param{NextStateID} || $Param{NewStateID}; 1696 } 1697 if ( $Param{StateID} ) { 1698 my %State = $StateObject->StateGet( 1699 ID => $Param{StateID}, 1700 UserID => 1, 1701 ); 1702 $Checks{State} = \%State; 1703 1704 # update or add state information to the ticket check 1705 $Checks{Ticket}->{State} = $Checks{State}->{Name}; 1706 $Checks{Ticket}->{StateID} = $Checks{State}->{ID}; 1707 $Checks{Ticket}->{StateType} = $Checks{State}->{TypeName}; 1708 } 1709 elsif ( $Param{State} ) { 1710 my %State = $StateObject->StateGet( 1711 Name => $Param{State}, 1712 UserID => 1, 1713 ); 1714 $Checks{State} = \%State; 1715 1716 # update or add state information to the ticket check 1717 $Checks{Ticket}->{State} = $Checks{State}->{Name}; 1718 $Checks{Ticket}->{StateID} = $Checks{State}->{ID}; 1719 $Checks{Ticket}->{StateType} = $Checks{State}->{TypeName}; 1720 } 1721 elsif ( !$Param{StateID} && !$Param{State} ) { 1722 if ( IsPositiveInteger( $Checks{Ticket}->{StateID} ) ) { 1723 1724 # get state data from the ticket 1725 my %State = $StateObject->StateGet( 1726 ID => $Checks{Ticket}->{StateID}, 1727 UserID => 1, 1728 ); 1729 $Checks{State} = \%State; 1730 } 1731 } 1732 } 1733 1734 # create hash with the ticket information stored in the database 1735 if ( IsPositiveInteger( $ChecksDatabase{Ticket}->{StateID} ) ) { 1736 1737 # check if database data matches current data (performance) 1738 if ( 1739 defined $Checks{State}->{ID} 1740 && $ChecksDatabase{Ticket}->{StateID} eq $Checks{State}->{ID} 1741 ) 1742 { 1743 $ChecksDatabase{State} = $Checks{State}; 1744 } 1745 1746 # otherwise complete the data querying the database again 1747 else { 1748 my %State = $StateObject->StateGet( 1749 ID => $ChecksDatabase{Ticket}->{StateID}, 1750 UserID => 1, 1751 ); 1752 $ChecksDatabase{State} = \%State; 1753 } 1754 } 1755 1756 # get needed objects 1757 my $GroupObject = $Kernel::OM->Get('Kernel::System::Group'); 1758 my $UserObject = $Kernel::OM->Get('Kernel::System::User'); 1759 1760 # use owner data (if given) 1761 if ( $CheckAll || $RequiredChecks{Owner} ) { 1762 if ( 1763 $Param{NewOwnerID} 1764 && !$Param{OwnerID} 1765 && defined $Param{NewOwnerType} 1766 && $Param{NewOwnerType} eq 'New' 1767 ) 1768 { 1769 $Param{OwnerID} = $Param{NewOwnerID}; 1770 } 1771 elsif ( 1772 $Param{OldOwnerID} 1773 && !$Param{OwnerID} 1774 && defined $Param{NewOwnerType} 1775 && $Param{NewOwnerType} eq 'Old' 1776 ) 1777 { 1778 $Param{OwnerID} = $Param{OldOwnerID}; 1779 } 1780 1781 if ( $Param{OwnerID} ) { 1782 1783 my %Owner = $UserObject->GetUserData( 1784 UserID => $Param{OwnerID}, 1785 ); 1786 for my $Type ( @{ $ConfigObject->Get('System::Permission') } ) { 1787 1788 my %Groups = $GroupObject->PermissionUserGet( 1789 UserID => $Param{OwnerID}, 1790 Type => $Type, 1791 ); 1792 1793 my @GroupNames = sort values %Groups; 1794 1795 $Owner{"Group_$Type"} = \@GroupNames; 1796 } 1797 1798 my %RoleIDs = $GroupObject->PermissionUserRoleGet( 1799 UserID => $Param{OwnerID}, 1800 ); 1801 1802 my @RoleNames = sort values %RoleIDs; 1803 1804 $Owner{Role} = \@RoleNames; 1805 $Checks{Owner} = \%Owner; 1806 1807 # update or add owner information to the ticket check 1808 $Checks{Ticket}->{Owner} = $Checks{Owner}->{UserLogin}; 1809 $Checks{Ticket}->{OwnerID} = $Checks{Owner}->{UserID}; 1810 } 1811 elsif ( $Param{Owner} ) { 1812 my $OwnerID = $UserObject->UserLookup( 1813 UserLogin => $Param{Owner}, 1814 ); 1815 my %Owner = $UserObject->GetUserData( 1816 UserID => $OwnerID, 1817 ); 1818 for my $Type ( @{ $ConfigObject->Get('System::Permission') } ) { 1819 1820 my %Groups = $GroupObject->PermissionUserGet( 1821 UserID => $OwnerID, 1822 Type => $Type, 1823 ); 1824 1825 my @GroupNames = sort values %Groups; 1826 1827 $Owner{"Group_$Type"} = \@GroupNames; 1828 } 1829 1830 my %RoleIDs = $GroupObject->PermissionUserRoleGet( 1831 UserID => $OwnerID, 1832 ); 1833 1834 my @RoleNames = sort values %RoleIDs; 1835 1836 $Owner{Role} = \@RoleNames; 1837 $Checks{Owner} = \%Owner; 1838 1839 # update or add owner information to the ticket check 1840 $Checks{Ticket}->{Owner} = $Checks{Owner}->{UserLogin}; 1841 $Checks{Ticket}->{OwnerID} = $Checks{Owner}->{UserID}; 1842 } 1843 elsif ( !$Param{OwnerID} && !$Param{Owner} ) { 1844 if ( IsPositiveInteger( $Checks{Ticket}->{OwnerID} ) ) { 1845 1846 # get responsible data from the ticket 1847 my %Owner = $UserObject->GetUserData( 1848 UserID => $Checks{Ticket}->{OwnerID}, 1849 ); 1850 for my $Type ( @{ $ConfigObject->Get('System::Permission') } ) { 1851 1852 my %Groups = $GroupObject->PermissionUserGet( 1853 UserID => $Checks{Ticket}->{OwnerID}, 1854 Type => $Type, 1855 ); 1856 1857 my @GroupNames = sort values %Groups; 1858 1859 $Owner{"Group_$Type"} = \@GroupNames; 1860 } 1861 1862 my %RoleIDs = $GroupObject->PermissionUserRoleGet( 1863 UserID => $Checks{Ticket}->{OwnerID}, 1864 ); 1865 1866 my @RoleNames = sort values %RoleIDs; 1867 1868 $Owner{Role} = \@RoleNames; 1869 $Checks{Owner} = \%Owner; 1870 } 1871 } 1872 } 1873 1874 # create hash with the ticket information stored in the database 1875 if ( IsPositiveInteger( $ChecksDatabase{Ticket}->{OwnerID} ) ) { 1876 1877 # check if database data matches current data (performance) 1878 if ( 1879 defined $Checks{Owner}->{UserID} 1880 && $ChecksDatabase{Ticket}->{OwnerID} eq $Checks{Owner}->{UserID} 1881 ) 1882 { 1883 $ChecksDatabase{Owner} = $Checks{Owner}; 1884 } 1885 1886 # otherwise complete the data querying the database again 1887 else { 1888 my %Owner = $UserObject->GetUserData( 1889 UserID => $ChecksDatabase{Ticket}->{OwnerID}, 1890 ); 1891 for my $Type ( @{ $ConfigObject->Get('System::Permission') } ) { 1892 1893 my %Groups = $GroupObject->PermissionUserGet( 1894 UserID => $ChecksDatabase{Ticket}->{OwnerID}, 1895 Type => $Type, 1896 ); 1897 1898 my @GroupNames = sort values %Groups; 1899 1900 $Owner{"Group_$Type"} = \@GroupNames; 1901 } 1902 1903 my %RoleIDs = $GroupObject->PermissionUserRoleGet( 1904 UserID => $ChecksDatabase{Ticket}->{OwnerID}, 1905 ); 1906 1907 my @RoleNames = sort values %RoleIDs; 1908 1909 $Owner{Role} = \@RoleNames; 1910 $ChecksDatabase{Owner} = \%Owner; 1911 } 1912 } 1913 1914 # use responsible data (if given) 1915 $Param{ResponsibleID} ||= $Param{NewResponsibleID}; 1916 1917 if ( $CheckAll || $RequiredChecks{Responsible} ) { 1918 if ( $Param{ResponsibleID} ) { 1919 my %Responsible = $UserObject->GetUserData( 1920 UserID => $Param{ResponsibleID}, 1921 ); 1922 for my $Type ( @{ $ConfigObject->Get('System::Permission') } ) { 1923 1924 my %Groups = $GroupObject->PermissionUserGet( 1925 UserID => $Param{ResponsibleID}, 1926 Type => $Type, 1927 ); 1928 1929 my @GroupNames = sort values %Groups; 1930 1931 $Responsible{"Group_$Type"} = \@GroupNames; 1932 } 1933 1934 my %RoleIDs = $GroupObject->PermissionUserRoleGet( 1935 UserID => $Param{ResponsibleID}, 1936 ); 1937 1938 my @RoleNames = sort values %RoleIDs; 1939 1940 $Responsible{Role} = \@RoleNames; 1941 $Checks{Responsible} = \%Responsible; 1942 1943 # update or add responsible information to the ticket check 1944 $Checks{Ticket}->{Responsible} = $Checks{Responsible}->{UserLogin}; 1945 $Checks{Ticket}->{ResponsibleID} = $Checks{Responsible}->{UserID}; 1946 } 1947 elsif ( $Param{Responsible} ) { 1948 my $ResponsibleID = $UserObject->UserLookup( 1949 UserLogin => $Param{Responsible}, 1950 ); 1951 my %Responsible = $UserObject->GetUserData( 1952 UserID => $ResponsibleID, 1953 ); 1954 for my $Type ( @{ $ConfigObject->Get('System::Permission') } ) { 1955 1956 my %Groups = $GroupObject->PermissionUserGet( 1957 UserID => $ResponsibleID, 1958 Type => $Type, 1959 ); 1960 1961 my @GroupNames = sort values %Groups; 1962 1963 $Responsible{"Group_$Type"} = \@GroupNames; 1964 } 1965 1966 my %RoleIDs = $GroupObject->PermissionUserRoleGet( 1967 UserID => $ResponsibleID, 1968 ); 1969 1970 my @RoleNames = sort values %RoleIDs; 1971 1972 $Responsible{Role} = \@RoleNames; 1973 $Checks{Responsible} = \%Responsible; 1974 1975 # update or add responsible information to the ticket check 1976 $Checks{Ticket}->{Responsible} = $Checks{Responsible}->{UserLogin}; 1977 $Checks{Ticket}->{ResponsibleID} = $Checks{Responsible}->{UserID}; 1978 } 1979 elsif ( !$Param{ResponsibleID} && !$Param{Responsible} ) { 1980 if ( IsPositiveInteger( $Checks{Ticket}->{ResponsibleID} ) ) { 1981 1982 # get responsible data from the ticket 1983 my %Responsible = $UserObject->GetUserData( 1984 UserID => $Checks{Ticket}->{ResponsibleID}, 1985 ); 1986 for my $Type ( @{ $ConfigObject->Get('System::Permission') } ) { 1987 1988 my %Groups = $GroupObject->PermissionUserGet( 1989 UserID => $Checks{Ticket}->{ResponsibleID}, 1990 Type => $Type, 1991 ); 1992 1993 my @GroupNames = sort values %Groups; 1994 1995 $Responsible{"Group_$Type"} = \@GroupNames; 1996 } 1997 1998 my %RoleIDs = $GroupObject->PermissionUserRoleGet( 1999 UserID => $Checks{Ticket}->{ResponsibleID}, 2000 ); 2001 2002 my @RoleNames = sort values %RoleIDs; 2003 2004 $Responsible{Role} = \@RoleNames; 2005 $Checks{Responsible} = \%Responsible; 2006 } 2007 } 2008 } 2009 2010 # create hash with the ticket information stored in the database 2011 if ( IsPositiveInteger( $ChecksDatabase{Ticket}->{ResponsibleID} ) ) { 2012 2013 # check if database data matches current data (performance) 2014 if ( 2015 defined $Checks{Owner}->{UserID} 2016 && defined $Checks{Responsible}->{UserID} 2017 && $ChecksDatabase{Ticket}->{ResponsibleID} eq $Checks{Responsible}->{UserID} 2018 ) 2019 { 2020 $ChecksDatabase{Responsible} = $Checks{Responsible}; 2021 } 2022 2023 # otherwise complete the data querying the database again 2024 else { 2025 2026 my %Responsible = $UserObject->GetUserData( 2027 UserID => $ChecksDatabase{Ticket}->{ResponsibleID}, 2028 ); 2029 2030 for my $Type ( @{ $ConfigObject->Get('System::Permission') } ) { 2031 2032 my %Groups = $GroupObject->PermissionUserGet( 2033 UserID => $ChecksDatabase{Ticket}->{ResponsibleID}, 2034 Type => $Type, 2035 ); 2036 2037 my @GroupNames = sort values %Groups; 2038 2039 $Responsible{"Group_$Type"} = \@GroupNames; 2040 } 2041 2042 my %RoleIDs = $GroupObject->PermissionUserRoleGet( 2043 UserID => $ChecksDatabase{Ticket}->{ResponsibleID}, 2044 ); 2045 2046 my @RoleNames = sort values %RoleIDs; 2047 2048 $Responsible{Role} = \@RoleNames; 2049 $ChecksDatabase{Responsible} = \%Responsible; 2050 } 2051 } 2052 2053 # within this function %Param is modified by replacements like: 2054 # $Param{PriorityID} = $Param{NewPriorityID} 2055 # apparently this changes are not longer needed outside this function and it is not necessary 2056 # to return such replacements 2057 2058 return { 2059 Checks => \%Checks, 2060 ChecksDatabase => \%ChecksDatabase, 2061 }; 2062} 2063 2064=head2 _CompareMatchWithData() 2065 2066Compares a properties element with the data sent to the ACL, the compare results varies on how the 2067ACL properties where defined including normal, negated, regular expression and negated regular 2068expression comparisons. 2069 2070 my $Result = $TicketObject->_CompareMatchWithData( 2071 Match => 'a value', # or '[Not]a value', or '[RegExp]val' or '[NotRegExp]val' 2072 # or '[Notregexp]val' or '[Notregexp]' 2073 Data => 'a value', 2074 SingleItem => 1, # or 0, optional, default 0 2075 ); 2076 2077Returns: 2078 2079 $Result = { 2080 Success => 1, # or false 2081 Match => 1, # or false 2082 Skip => 1, # or false (in certain cases where SingleItem is set) 2083 }; 2084 2085=cut 2086 2087sub _CompareMatchWithData { 2088 my ( $Self, %Param ) = @_; 2089 2090 # check needed stuff 2091 for my $Needed (qw(Match Data)) { 2092 if ( !defined $Param{$Needed} ) { 2093 $Kernel::OM->Get('Kernel::System::Log')->Log( 2094 Priority => 'error', 2095 Message => "Need $Needed!", 2096 ); 2097 return { 2098 Success => 0, 2099 }; 2100 } 2101 } 2102 2103 my $Match = $Param{Match}; 2104 my $Data = $Param{Data}; 2105 2106 # Negated matches requires a different logic. 2107 if ( substr( $Match, 0, length '[Not' ) eq '[Not' ) { 2108 2109 # Not equal match 2110 if ( substr( $Match, 0, length '[Not]' ) eq '[Not]' ) { 2111 my $NotValue = substr $Match, length '[Not]'; 2112 if ( $NotValue eq $Data ) { 2113 return { 2114 Success => 1, 2115 Match => 0, 2116 }; 2117 } 2118 } 2119 2120 # Not reg-exp match case-sensitive. 2121 elsif ( substr( $Match, 0, length '[NotRegExp]' ) eq '[NotRegExp]' ) { 2122 my $RegExp = substr $Match, length '[NotRegExp]'; 2123 if ( $Data =~ /$RegExp/ ) { 2124 return { 2125 Success => 1, 2126 Match => 0, 2127 }; 2128 } 2129 } 2130 2131 # Not reg-exp match case-insensitive. 2132 elsif ( substr( $Match, 0, length '[Notregexp]' ) eq '[Notregexp]' ) { 2133 my $RegExp = substr $Match, length '[Notregexp]'; 2134 if ( $Data =~ /$RegExp/i ) { 2135 return { 2136 Success => 1, 2137 Match => 0, 2138 }; 2139 } 2140 } 2141 2142 if ( $Param{SingleItem} ) { 2143 return { 2144 Success => 1, 2145 Match => 1, 2146 Skip => 0, 2147 }; 2148 } 2149 else { 2150 return { 2151 Success => 1, 2152 Match => 1, 2153 Skip => 1, 2154 }; 2155 } 2156 } 2157 else { 2158 2159 # Equal match. 2160 if ( $Match eq $Data ) { 2161 return { 2162 Success => 1, 2163 Match => 1, 2164 }; 2165 } 2166 2167 # Reg-exp match case-sensitive. 2168 elsif ( substr( $Match, 0, length '[RegExp]' ) eq '[RegExp]' ) { 2169 my $RegExp = substr $Match, length '[RegExp]'; 2170 if ( $Data =~ /$RegExp/ ) { 2171 return { 2172 Success => 1, 2173 Match => 1, 2174 }; 2175 } 2176 } 2177 2178 # Reg-exp match case-insensitive. 2179 elsif ( substr( $Match, 0, length '[regexp]' ) eq '[regexp]' ) { 2180 my $RegExp = substr $Match, length '[regexp]'; 2181 if ( $Data =~ /$RegExp/i ) { 2182 return { 2183 Success => 1, 2184 Match => 1, 2185 }; 2186 } 2187 } 2188 2189 if ( $Param{SingleItem} ) { 2190 return { 2191 Success => 1, 2192 Match => 0, 2193 Skip => 0, 2194 }; 2195 } 2196 else { 2197 return { 2198 Success => 1, 2199 Match => 0, 2200 Skip => 1, 2201 }; 2202 } 2203 } 2204} 2205 22061; 2207 2208=end Internal: 2209 2210=head1 TERMS AND CONDITIONS 2211 2212This software is part of the OTRS project (L<https://otrs.org/>). 2213 2214This software comes with ABSOLUTELY NO WARRANTY. For details, see 2215the enclosed file COPYING for license information (GPL). If you 2216did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>. 2217 2218=cut 2219