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