<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;"># --
# Kernel/Modules/AdminACL.pm - ACL administration
# Copyright (C) 2001-2014 OTRS AG, http://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (AGPL). If you
# did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
# --

package Kernel::Modules::AdminACL;

use strict;
use warnings;

use Kernel::System::YAML;
use Kernel::System::Valid;
use Kernel::System::JSON;
use Kernel::System::DynamicField;
use Kernel::System::ACL::DB::ACL;

use Kernel::System::VariableCheck qw(:all);

sub new {
    my ( $Type, %Param ) = @_;

    # allocate new hash for object
    my $Self = {%Param};
    bless( $Self, $Type );

    # check all needed objects
    for my $Needed (
        qw(ParamObject DBObject LayoutObject ConfigObject LogObject MainObject EncodeObject)
        )
    {
        if ( !$Self-&gt;{$Needed} ) {
            $Self-&gt;{LayoutObject}-&gt;FatalError( Message =&gt; "Got no $Needed!" );
        }
    }

    # create additional objects
    $Self-&gt;{ValidObject}        = Kernel::System::Valid-&gt;new(%Param);
    $Self-&gt;{DynamicFieldObject} = Kernel::System::DynamicField-&gt;new(%Param);
    $Self-&gt;{JSONObject}         = Kernel::System::JSON-&gt;new( %{$Self} );
    $Self-&gt;{YAMLObject}         = Kernel::System::YAML-&gt;new( %{$Self} );
    $Self-&gt;{ACLObject}          = Kernel::System::ACL::DB::ACL-&gt;new( %{$Self} );

    return $Self;
}

sub Run {
    my ( $Self, %Param ) = @_;

    $Self-&gt;{Subaction} = $Self-&gt;{ParamObject}-&gt;GetParam( Param =&gt; 'Subaction' ) || '';

    my $ACLID = $Self-&gt;{ParamObject}-&gt;GetParam( Param =&gt; 'ID' ) || '';

    my $SynchronizeMessage
        = 'ACL information from database is not in sync with the system configuration, please deploy all ACLs.';

    my $SynchronizedMessageVisible = 0;
    if ( $Self-&gt;{ACLObject}-&gt;ACLsNeedSync() ) {

        # create a notification if system is not up to date
        $Param{NotifyData} = [
            {
                Info =&gt; $SynchronizeMessage,
            },
        ];
        $SynchronizedMessageVisible = 1;
    }

    # ------------------------------------------------------------ #
    # ACLImport
    # ------------------------------------------------------------ #
    if ( $Self-&gt;{Subaction} eq 'ACLImport' ) {

        # challenge token check for write action
        $Self-&gt;{LayoutObject}-&gt;ChallengeTokenCheck();

        my $FormID = $Self-&gt;{ParamObject}-&gt;GetParam( Param =&gt; 'FormID' ) || '';
        my %UploadStuff = $Self-&gt;{ParamObject}-&gt;GetUploadAll(
            Param  =&gt; 'FileUpload',
            Source =&gt; 'string',
        );

        my $OverwriteExistingEntities
            = $Self-&gt;{ParamObject}-&gt;GetParam( Param =&gt; 'OverwriteExistingEntities' ) || '';

        my $ACLData = $Self-&gt;{YAMLObject}-&gt;Load( Data =&gt; $UploadStuff{Content} );

        if ( ref $ACLData ne 'ARRAY' ) {
            return $Self-&gt;{LayoutObject}-&gt;ErrorScreen(
                Message =&gt;
                    "Couldn't read ACL configuration file. Please make sure the file is valid.",
            );
        }

        my @UpdatedACLs;
        my @AddedACLs;
        my @ACLErrors;

        ACL:
        for my $ACL ( @{$ACLData} ) {

            next ACL if !$ACL;
            next ACL if ref $ACL ne 'HASH';

            my @ExistingACLs
                = @{ $Self-&gt;{ACLObject}-&gt;ACLListGet( UserID =&gt; $Self-&gt;{UserID} ) || [] };
            @ExistingACLs = grep { $_-&gt;{Name} eq $ACL-&gt;{Name} } @ExistingACLs;

            if ( $OverwriteExistingEntities &amp;&amp; $ExistingACLs[0] ) {
                my $Success = $Self-&gt;{ACLObject}-&gt;ACLUpdate(
                    %{ $ExistingACLs[0] },
                    Name           =&gt; $ACL-&gt;{Name},
                    Comment        =&gt; $ACL-&gt;{Comment},
                    Description    =&gt; $ACL-&gt;{Description} || '',
                    StopAfterMatch =&gt; $ACL-&gt;{StopAfterMatch} || 0,
                    ConfigMatch    =&gt; $ACL-&gt;{ConfigMatch} || undef,
                    ConfigChange   =&gt; $ACL-&gt;{ConfigChange} || undef,
                    ValidID        =&gt; $ACL-&gt;{ValidID} || 1,
                    UserID         =&gt; $Self-&gt;{UserID},
                );

                if ($Success) {
                    push @UpdatedACLs, $ACL-&gt;{Name};
                }
                else {
                    push @ACLErrors, $ACL-&gt;{Name};
                }

            }
            else {

                # now add the ACL
                my $Success = $Self-&gt;{ACLObject}-&gt;ACLAdd(
                    Name           =&gt; $ACL-&gt;{Name},
                    Comment        =&gt; $ACL-&gt;{Comment},
                    Description    =&gt; $ACL-&gt;{Description} || '',
                    ConfigMatch    =&gt; $ACL-&gt;{ConfigMatch} || undef,
                    ConfigChange   =&gt; $ACL-&gt;{ConfigChange} || undef,
                    StopAfterMatch =&gt; $ACL-&gt;{StopAfterMatch},
                    ValidID        =&gt; $ACL-&gt;{ValidID} || 1,
                    UserID         =&gt; $Self-&gt;{UserID},
                );

                if ($Success) {
                    push @AddedACLs, $ACL-&gt;{Name};
                }
                else {
                    push @ACLErrors, $ACL-&gt;{Name};
                }
            }
        }

        my $UpdatedACLsStrg = join( ', ', @UpdatedACLs ) || '';
        my $AddedACLsStrg   = join( ', ', @AddedACLs )   || '';
        my $ACLErrorsStrg   = join( ', ', @ACLErrors )   || '';

        if ($AddedACLsStrg) {
            push @{ $Param{NotifyData} }, {
                Info =&gt; 'The following ACLs have been added successfully: ' . $AddedACLsStrg,
            };
        }
        if ($UpdatedACLsStrg) {
            push @{ $Param{NotifyData} }, {
                Info =&gt; 'The following ACLs have been updated successfully: ' . $UpdatedACLsStrg,
            };
        }
        if ($ACLErrorsStrg) {
            push @{ $Param{NotifyData} }, {
                Priority =&gt; 'Error',
                Info     =&gt; 'There where errors adding/updating the following ACLs: '
                    . $ACLErrorsStrg
                    . '. Please check the log file for more information.',
            };
        }

        if ( ( $UpdatedACLsStrg || $AddedACLsStrg ) &amp;&amp; !$SynchronizedMessageVisible ) {

            $Param{NotifyData} = [
                @{ $Param{NotifyData} || [] },
                {
                    Info =&gt; $SynchronizeMessage,
                },
            ];
        }

        return $Self-&gt;_ShowOverview(
            %Param,
        );
    }

    # ------------------------------------------------------------ #
    # ACLNew
    # ------------------------------------------------------------ #
    elsif ( $Self-&gt;{Subaction} eq 'ACLNew' ) {

        return $Self-&gt;_ShowEdit(
            %Param,
            Action =&gt; 'New',
        );
    }

    # ------------------------------------------------------------ #
    # ACLNewAction
    # ------------------------------------------------------------ #
    elsif ( $Self-&gt;{Subaction} eq 'ACLNewAction' ) {

        # challenge token check for write action
        $Self-&gt;{LayoutObject}-&gt;ChallengeTokenCheck();

        # get ACL data
        my $ACLData;

        # get parameter from web browser
        my $GetParam = $Self-&gt;_GetParams();

        # set new confguration
        $ACLData-&gt;{Name}           = $GetParam-&gt;{Name};
        $ACLData-&gt;{Comment}        = $GetParam-&gt;{Comment};
        $ACLData-&gt;{Description}    = $GetParam-&gt;{Description};
        $ACLData-&gt;{StopAfterMatch} = $GetParam-&gt;{StopAfterMatch} || 0;
        $ACLData-&gt;{ValidID}        = $GetParam-&gt;{ValidID};

        # check required parameters
        my %Error;
        if ( !$GetParam-&gt;{Name} ) {

            # add server error error class
            $Error{NameServerError}        = 'ServerError';
            $Error{NameServerErrorMessage} = 'This field is required';
        }

        if ( !$GetParam-&gt;{ValidID} ) {

            # add server error error class
            $Error{ValidIDServerError}        = 'ServerError';
            $Error{ValidIDServerErrorMessage} = 'This field is required';
        }

        # if there is an error return to edit screen
        if ( IsHashRefWithData( \%Error ) ) {
            return $Self-&gt;_ShowEdit(
                %Error,
                %Param,
                ACLData =&gt; $ACLData,
                Action  =&gt; 'New',
            );
        }

        # otherwise save configuration and return to overview screen
        my $ACLID = $Self-&gt;{ACLObject}-&gt;ACLAdd(
            Name           =&gt; $ACLData-&gt;{Name},
            Comment        =&gt; $ACLData-&gt;{Comment},
            Description    =&gt; $ACLData-&gt;{Description},
            StopAfterMatch =&gt; $ACLData-&gt;{StopAfterMatch},
            ValidID        =&gt; $ACLData-&gt;{ValidID},
            UserID         =&gt; $Self-&gt;{UserID},
        );

        # show error if can't create
        if ( !$ACLID ) {
            return $Self-&gt;{LayoutObject}-&gt;ErrorScreen(
                Message =&gt; "There was an error creating the ACL",
            );
        }

        # redirect to edit screen
        return $Self-&gt;{LayoutObject}
            -&gt;Redirect( OP =&gt; "Action=$Self-&gt;{Action};Subaction=ACLEdit;ID=$ACLID" );
    }

    # ------------------------------------------------------------ #
    # ACLEdit
    # ------------------------------------------------------------ #
    elsif ( $Self-&gt;{Subaction} eq 'ACLEdit' ) {

        # check for ACLID
        if ( !$ACLID ) {
            return $Self-&gt;{LayoutObject}-&gt;ErrorScreen(
                Message =&gt; "Need ACLID!",
            );
        }

        # get ACL data
        my $ACLData = $Self-&gt;{ACLObject}-&gt;ACLGet(
            ID     =&gt; $ACLID,
            UserID =&gt; $Self-&gt;{UserID},
        );

        # check for valid ACL data
        if ( !IsHashRefWithData($ACLData) ) {
            return $Self-&gt;{LayoutObject}-&gt;ErrorScreen(
                Message =&gt; "Could not get data for ACLID $ACLID",
            );
        }

        if ( $ACLData-&gt;{ConfigMatch} ) {
            $ACLData-&gt;{ConfigMatch} = $Self-&gt;{JSONObject}-&gt;Encode(
                Data =&gt; $ACLData-&gt;{ConfigMatch},
            );
        }

        if ( $ACLData-&gt;{ConfigChange} ) {
            $ACLData-&gt;{ConfigChange} = $Self-&gt;{JSONObject}-&gt;Encode(
                Data =&gt; $ACLData-&gt;{ConfigChange},
            );
        }

        return $Self-&gt;_ShowEdit(
            %Param,
            ACLID   =&gt; $ACLID,
            ACLData =&gt; $ACLData,
            Action  =&gt; 'Edit',
        );
    }

    # ------------------------------------------------------------ #
    # ACLEditAction
    # ------------------------------------------------------------ #
    elsif ( $Self-&gt;{Subaction} eq 'ACLEditAction' ) {

        # challenge token check for write action
        $Self-&gt;{LayoutObject}-&gt;ChallengeTokenCheck();

        # get webserice configuration
        my $ACLData;

        # get parameter from web browser
        my $GetParam = $Self-&gt;_GetParams();

        # set new confguration
        $ACLData-&gt;{Name}           = $GetParam-&gt;{Name};
        $ACLData-&gt;{Comment}        = $GetParam-&gt;{Comment};
        $ACLData-&gt;{Description}    = $GetParam-&gt;{Description};
        $ACLData-&gt;{StopAfterMatch} = $GetParam-&gt;{StopAfterMatch} || 0;
        $ACLData-&gt;{ValidID}        = $GetParam-&gt;{ValidID};
        $ACLData-&gt;{ConfigMatch}    = $GetParam-&gt;{ConfigMatch} || '';
        $ACLData-&gt;{ConfigChange}   = $GetParam-&gt;{ConfigChange} || '';

        # check required parameters
        my %Error;
        if ( !$GetParam-&gt;{Name} ) {

            # add server error error class
            $Error{NameServerError}        = 'ServerError';
            $Error{NameServerErrorMessage} = 'This field is required';
        }

        if ( !$GetParam-&gt;{ValidID} ) {

            # add server error error class
            $Error{ValidIDServerError}        = 'ServerError';
            $Error{ValidIDServerErrorMessage} = 'This field is required';
        }

        # if there is an error return to edit screen
        if ( IsHashRefWithData( \%Error ) ) {
            return $Self-&gt;_ShowEdit(
                %Error,
                %Param,
                ACLData =&gt; $ACLData,
                Action  =&gt; 'Edit',
            );
        }

        # otherwise save configuration and return to overview screen
        my $Success = $Self-&gt;{ACLObject}-&gt;ACLUpdate(
            ID             =&gt; $ACLID,
            Name           =&gt; $ACLData-&gt;{Name},
            Comment        =&gt; $ACLData-&gt;{Comment},
            Description    =&gt; $ACLData-&gt;{Description},
            StopAfterMatch =&gt; $ACLData-&gt;{StopAfterMatch} || 0,
            ValidID        =&gt; $ACLData-&gt;{ValidID},
            ConfigMatch    =&gt; $ACLData-&gt;{ConfigMatch} || '',
            ConfigChange   =&gt; $ACLData-&gt;{ConfigChange} || '',
            UserID         =&gt; $Self-&gt;{UserID},
        );

        # show error if can't update
        if ( !$Success ) {
            return $Self-&gt;{LayoutObject}-&gt;ErrorScreen(
                Message =&gt; "There was an error updating the ACL",
            );
        }

        if ( $Self-&gt;{ParamObject}-&gt;GetParam( Param =&gt; 'ContinueAfterSave' ) eq '1' ) {

            # if the user would like to continue editing the ACL, just redirect to the edit screen
            return $Self-&gt;{LayoutObject}-&gt;Redirect(
                OP =&gt;
                    "Action=AdminACL;Subaction=ACLEdit;ID=$ACLID"
            );
        }
        else {

            # otherwise return to overview
            return $Self-&gt;{LayoutObject}-&gt;Redirect( OP =&gt; "Action=$Self-&gt;{Action}" );
        }
    }

    # ------------------------------------------------------------ #
    # Sync
    # ------------------------------------------------------------ #
    elsif ( $Self-&gt;{Subaction} eq 'ACLDeploy' ) {

        my $Location
            = $Self-&gt;{ConfigObject}-&gt;Get('Home') . '/Kernel/Config/Files/ZZZACL.pm';

        my $ACLDump = $Self-&gt;{ACLObject}-&gt;ACLDump(
            ResultType =&gt; 'FILE',
            Location   =&gt; $Location,
            UserID     =&gt; $Self-&gt;{UserID},
        );

        if ($ACLDump) {

            my $Success = $Self-&gt;{ACLObject}-&gt;ACLsNeedSyncReset();

            if ($Success) {
                return $Self-&gt;{LayoutObject}-&gt;Redirect( OP =&gt; "Action=$Self-&gt;{Action}" );
            }
            else {

                # show error if can't set state
                return $Self-&gt;{LayoutObject}-&gt;ErrorScreen(
                    Message =&gt; "There was an error setting the entity sync status.",
                );
            }
        }
        else {

            # show error if can't synch
            return $Self-&gt;{LayoutObject}-&gt;ErrorScreen(
                Message =&gt; "There was an error synchronizing the ACLs.",
            );
        }
    }

    # ------------------------------------------------------------ #
    # ACLDelete AJAX
    # ------------------------------------------------------------ #
    elsif ( $Self-&gt;{Subaction} eq 'ACLDelete' ) {

        # challenge token check for write action
        $Self-&gt;{LayoutObject}-&gt;ChallengeTokenCheck();

        # check for ACLID
        return if !$ACLID;

        my $CheckResult = $Self-&gt;_CheckACLDelete( ID =&gt; $ACLID );

        my $JSON;
        if ( $CheckResult-&gt;{Success} ) {

            my $Success = $Self-&gt;{ACLObject}-&gt;ACLDelete(
                ID     =&gt; $ACLID,
                UserID =&gt; $Self-&gt;{UserID},
            );

            my %DeleteResult = (
                Success =&gt; $Success,
            );

            if ( !$Success ) {
                $DeleteResult{Message} = 'ACL $ACLID could not be deleted';
            }

            # build JSON output
            $JSON = $Self-&gt;{LayoutObject}-&gt;JSONEncode(
                Data =&gt; {
                    %DeleteResult,
                },
            );
        }
        else {

            # build JSON output
            $JSON = $Self-&gt;{LayoutObject}-&gt;JSONEncode(
                Data =&gt; {
                    %{$CheckResult},
                },
            );
        }

        # send JSON response
        return $Self-&gt;{LayoutObject}-&gt;Attachment(
            ContentType =&gt; 'application/json; charset=' . $Self-&gt;{LayoutObject}-&gt;{Charset},
            Content     =&gt; $JSON,
            Type        =&gt; 'inline',
            NoCache     =&gt; 1,
        );
    }

    # ------------------------------------------------------------ #
    # ACLExport
    # ------------------------------------------------------------ #
    elsif ( $Self-&gt;{Subaction} eq 'ACLExport' ) {

        my $ACLID = $Self-&gt;{ParamObject}-&gt;GetParam( Param =&gt; 'ID' ) || '';
        my $ACLData;
        my $ACLSingleData;
        my $Filename = 'Export_ACL.yml';

        if ($ACLID) {

            $ACLSingleData = $Self-&gt;{ACLObject}-&gt;ACLGet(
                ID     =&gt; $ACLID,
                UserID =&gt; 1,
            );

            if ( !$ACLSingleData || !IsHashRefWithData($ACLSingleData) ) {
                return $Self-&gt;{LayoutObject}-&gt;ErrorScreen(
                    Message =&gt; "There was an error getting data for ACL with ID " . $ACLID,
                );
            }

            my $ACLName = $ACLSingleData-&gt;{Name};
            $ACLName =~ s{[^a-zA-Z0-9-_]}{_}xmsg;    # cleanup name for saving

            $Filename = 'Export_ACL_' . $ACLName . '.yml';
            $ACLData  = [$ACLSingleData];
        }
        else {

            $ACLData = $Self-&gt;{ACLObject}-&gt;ACLListGet(
                UserID =&gt; 1,
                ValidIDs =&gt; [ '1', '2' ],
            );
        }

        # convert the ACL data hash to string
        my $ACLDataYAML = $Self-&gt;{YAMLObject}-&gt;Dump( Data =&gt; $ACLData );

        # send the result to the browser
        return $Self-&gt;{LayoutObject}-&gt;Attachment(
            ContentType =&gt; 'text/html; charset=' . $Self-&gt;{LayoutObject}-&gt;{Charset},
            Content     =&gt; $ACLDataYAML,
            Type        =&gt; 'attachment',
            Filename    =&gt; $Filename,
            NoCache     =&gt; 1,
        );
    }

    # ------------------------------------------------------------ #
    # ACLCopy
    # ------------------------------------------------------------ #
    elsif ( $Self-&gt;{Subaction} eq 'ACLCopy' ) {

        # challenge token check for write action
        $Self-&gt;{LayoutObject}-&gt;ChallengeTokenCheck();

        # get ACL data
        my $ACLData = $Self-&gt;{ACLObject}-&gt;ACLGet(
            ID     =&gt; $ACLID,
            UserID =&gt; $Self-&gt;{UserID},
        );
        if ( !$ACLData ) {
            return $Self-&gt;{LayoutObject}-&gt;ErrorScreen(
                Message =&gt; "Unknown ACL $ACLID!",
            );
        }

        # create new ACL name
        my $ACLName =
            $ACLData-&gt;{Name}
            . ' ('
            . $Self-&gt;{LayoutObject}-&gt;{LanguageObject}-&gt;Get('Copy')
            . ')';

        # otherwise save configuration and return to overview screen
        my $ACLID = $Self-&gt;{ACLObject}-&gt;ACLAdd(
            Name           =&gt; $ACLName,
            Comment        =&gt; $ACLData-&gt;{Comment},
            Description    =&gt; $ACLData-&gt;{Description},
            ConfigMatch    =&gt; $ACLData-&gt;{ConfigMatch} || '',
            ConfigChange   =&gt; $ACLData-&gt;{ConfigChange} || '',
            StopAfterMatch =&gt; $ACLData-&gt;{StopAfterMatch} || 0,
            ValidID        =&gt; '1',
            UserID         =&gt; $Self-&gt;{UserID},
        );

        # show error if can't create
        if ( !$ACLID ) {
            return $Self-&gt;{LayoutObject}-&gt;ErrorScreen(
                Message =&gt; "There was an error creating the ACL",
            );
        }

        # return to overview
        return $Self-&gt;{LayoutObject}-&gt;Redirect( OP =&gt; "Action=$Self-&gt;{Action}" );
    }

    # ------------------------------------------------------------ #
    # Overview
    # ------------------------------------------------------------ #
    else {
        return $Self-&gt;_ShowOverview(
            %Param,
        );
    }
}

sub _ShowOverview {
    my ( $Self, %Param ) = @_;

    my $Output = $Self-&gt;{LayoutObject}-&gt;Header();
    $Output .= $Self-&gt;{LayoutObject}-&gt;NavigationBar();

    # show notifications if any
    if ( $Param{NotifyData} ) {
        for my $Notification ( @{ $Param{NotifyData} } ) {
            $Output .= $Self-&gt;{LayoutObject}-&gt;Notify(
                %{$Notification},
            );
        }
    }

    # get ACL list
    my $ACLList = $Self-&gt;{ACLObject}-&gt;ACLList( UserID =&gt; $Self-&gt;{UserID} );

    if ( IsHashRefWithData($ACLList) ) {

        # sort data numerically
        my @ACLIDsSorted = sort { $ACLList-&gt;{$a} cmp $ACLList-&gt;{$b} } keys %{$ACLList};

        # get each ACLs data
        for my $ACLID (@ACLIDsSorted) {

            my $ACLData = $Self-&gt;{ACLObject}-&gt;ACLGet(
                ID     =&gt; $ACLID,
                UserID =&gt; $Self-&gt;{UserID},
            );

            # set the valid state
            $ACLData-&gt;{ValidID}
                = $Self-&gt;{ValidObject}-&gt;ValidLookup( ValidID =&gt; $ACLData-&gt;{ValidID} );

            # print each ACL in overview table
            $Self-&gt;{LayoutObject}-&gt;Block(
                Name =&gt; 'ACLRow',
                Data =&gt; {
                    %{$ACLData},
                },
            );
        }
    }
    else {

        # print no data found message
        $Self-&gt;{LayoutObject}-&gt;Block(
            Name =&gt; 'ACLNoDataRow',
            Data =&gt; {},
        );
    }

    $Output .= $Self-&gt;{LayoutObject}-&gt;Output(
        TemplateFile =&gt; 'AdminACL',
        Data         =&gt; \%Param,
    );
    $Output .= $Self-&gt;{LayoutObject}-&gt;Footer();

    return $Output;
}

sub _ShowEdit {
    my ( $Self, %Param ) = @_;

    # get ACL information
    my $ACLData = $Param{ACLData} || {};

    # decide wether to show delete button
    if ( $Param{Action} eq 'Edit' &amp;&amp; $ACLData &amp;&amp; $ACLData-&gt;{ValidID} ne '1' ) {
        $Self-&gt;{LayoutObject}-&gt;Block(
            Name =&gt; 'ACLDeleteAction',
            Data =&gt; {
                %{$ACLData},
            },
        );
    }

    # get valid list
    my %ValidList = $Self-&gt;{ValidObject}-&gt;ValidList();

    $Param{ValidOption} = $Self-&gt;{LayoutObject}-&gt;BuildSelection(
        Data       =&gt; \%ValidList,
        Name       =&gt; 'ValidID',
        SelectedID =&gt; $ACLData-&gt;{ValidID} || $ValidList{valid},
        Class      =&gt; 'Validate_Required ' . ( $Param{Errors}-&gt;{'ValidIDInvalid'} || '' ),
    );

    my $ACLKeysLevel1Match = $Self-&gt;{ConfigObject}-&gt;Get('ACLKeysLevel1Match') || {};
    $Param{ACLKeysLevel1Match} = $Self-&gt;{LayoutObject}-&gt;BuildSelection(
        Data         =&gt; $ACLKeysLevel1Match,
        Name         =&gt; 'ItemAdd',
        Class        =&gt; 'ItemAdd ItemAddLevel1',
        ID           =&gt; 'ItemAddLevel1Match',
        TreeView     =&gt; 1,
        PossibleNone =&gt; 1,
        Translation  =&gt; 0,
    );

    my $ACLKeysLevel1Change = $Self-&gt;{ConfigObject}-&gt;Get('ACLKeysLevel1Change') || {};
    $Param{ACLKeysLevel1Change} = $Self-&gt;{LayoutObject}-&gt;BuildSelection(
        Data         =&gt; $ACLKeysLevel1Change,
        Name         =&gt; 'ItemAdd',
        Class        =&gt; 'ItemAdd ItemAddLevel1',
        ID           =&gt; 'ItemAddLevel1Change',
        TreeView     =&gt; 1,
        PossibleNone =&gt; 1,
        Translation  =&gt; 0,
    );

    my $ACLKeysLevel2Possible = $Self-&gt;{ConfigObject}-&gt;Get('ACLKeysLevel2::Possible') || {};
    $Param{ACLKeysLevel2Possible} = $Self-&gt;{LayoutObject}-&gt;BuildSelection(
        Data         =&gt; $ACLKeysLevel2Possible,
        Name         =&gt; 'ItemAdd',
        ID           =&gt; 'Possible',
        Class        =&gt; 'ItemAdd LevelToBeAdded',
        Translation  =&gt; 0,
        PossibleNone =&gt; 1,
    );

    my $ACLKeysLevel2PossibleNot = $Self-&gt;{ConfigObject}-&gt;Get('ACLKeysLevel2::PossibleNot') || {};
    $Param{ACLKeysLevel2PossibleNot} = $Self-&gt;{LayoutObject}-&gt;BuildSelection(
        Data         =&gt; $ACLKeysLevel2PossibleNot,
        Name         =&gt; 'ItemAdd',
        ID           =&gt; 'PossibleNot',
        Class        =&gt; 'ItemAdd LevelToBeAdded',
        Translation  =&gt; 0,
        PossibleNone =&gt; 1,
    );

    my $ACLKeysLevel2Properties = $Self-&gt;{ConfigObject}-&gt;Get('ACLKeysLevel2::Properties') || {};
    $Param{ACLKeysLevel2Properties} = $Self-&gt;{LayoutObject}-&gt;BuildSelection(
        Data         =&gt; $ACLKeysLevel2Properties,
        Name         =&gt; 'ItemAdd',
        ID           =&gt; 'Properties',
        Class        =&gt; 'ItemAdd LevelToBeAdded',
        Translation  =&gt; 0,
        PossibleNone =&gt; 1,
    );

    my $ACLKeysLevel2PropertiesDatabase
        = $Self-&gt;{ConfigObject}-&gt;Get('ACLKeysLevel2::PropertiesDatabase') || {};
    $Param{ACLKeysLevel2PropertiesDatabase} = $Self-&gt;{LayoutObject}-&gt;BuildSelection(
        Data         =&gt; $ACLKeysLevel2PropertiesDatabase,
        Name         =&gt; 'ItemAdd',
        ID           =&gt; 'PropertiesDatabase',
        Class        =&gt; 'ItemAdd LevelToBeAdded',
        Translation  =&gt; 0,
        PossibleNone =&gt; 1,
    );

    $Param{ACLKeysLevel4Prefixes} = $Self-&gt;{LayoutObject}-&gt;BuildSelection(
        Data =&gt; {
            ''         =&gt; 'Standard',
            '[RegExp]' =&gt; 'Regex',
            '[regexp]' =&gt; 'Regex (ignore case)',
        },
        Name           =&gt; 'ItemPrefix',
        Class          =&gt; 'ItemPrefix',
        ID             =&gt; 'Prefixes',
        Sort           =&gt; 'IndividualKey',
        SortIndividual =&gt; [ '', '[RegExp]', '[regexp]' ],
        Translation    =&gt; 0,
    );

    # get list of all possible dynamic fields
    my $DynamicFieldList = $Self-&gt;{DynamicFieldObject}-&gt;DynamicFieldList(
        ObjectType =&gt; 'Ticket',
        ResultType =&gt; 'HASH',
    );
    my %DynamicFieldNames = reverse %{$DynamicFieldList};
    my %DynamicFields;
    for my $DynamicFieldName ( sort keys %DynamicFieldNames ) {
        $DynamicFields{ 'DynamicField_' . $DynamicFieldName } = $DynamicFieldName;
    }
    $Param{ACLKeysLevel3DynamicFields} = $Self-&gt;{LayoutObject}-&gt;BuildSelection(
        Data         =&gt; \%DynamicFields,
        Name         =&gt; 'NewDataKeyDropdown',
        Class        =&gt; 'NewDataKeyDropdown',
        ID           =&gt; 'DynamicField',
        Translation  =&gt; 0,
        PossibleNone =&gt; 1,
    );

    # get list of all possible actions
    my @PossibleActionsList;
    my $ACLKeysLevel3Actions
        = $Self-&gt;{ConfigObject}-&gt;Get('ACLKeysLevel3::Actions') || [];

    for my $Key ( sort keys %{$ACLKeysLevel3Actions} ) {
        push @PossibleActionsList, @{ $ACLKeysLevel3Actions-&gt;{$Key} };
    }

    $Param{ACLKeysLevel3Actions} = $Self-&gt;{LayoutObject}-&gt;BuildSelection(
        Data         =&gt; \@PossibleActionsList,
        Name         =&gt; 'NewDataKeyDropdown',
        Class        =&gt; 'NewDataKeyDropdown Boolean',
        ID           =&gt; 'Action',
        Translation  =&gt; 0,
        PossibleNone =&gt; 1,
    );

    if ( defined $ACLData-&gt;{StopAfterMatch} &amp;&amp; $ACLData-&gt;{StopAfterMatch} == 1 ) {
        $Param{Checked} = 'checked="checked"';
    }

    my $Output = $Self-&gt;{LayoutObject}-&gt;Header();
    $Output .= $Self-&gt;{LayoutObject}-&gt;NavigationBar();

    # show notifications if any
    if ( $Param{NotifyData} ) {
        for my $Notification ( @{ $Param{NotifyData} } ) {
            $Output .= $Self-&gt;{LayoutObject}-&gt;Notify(
                %{$Notification},
            );
        }
    }

    $Output .= $Self-&gt;{LayoutObject}-&gt;Output(
        TemplateFile =&gt; "AdminACL$Param{Action}",
        Data         =&gt; {
            %Param,
            %{$ACLData},
        },
    );

    $Output .= $Self-&gt;{LayoutObject}-&gt;Footer();

    return $Output;
}

sub _GetParams {
    my ( $Self, %Param ) = @_;

    my $GetParam;

    # get parameters from web browser
    for my $ParamName (
        qw( Name EntityID Comment Description StopAfterMatch ValidID ConfigMatch ConfigChange )
        )
    {
        $GetParam-&gt;{$ParamName} = $Self-&gt;{ParamObject}-&gt;GetParam( Param =&gt; $ParamName ) || '';
    }

    if ( $GetParam-&gt;{ConfigMatch} ) {
        $GetParam-&gt;{ConfigMatch} = $Self-&gt;{JSONObject}-&gt;Decode(
            Data =&gt; $GetParam-&gt;{ConfigMatch},
        );
        if ( !IsHashRefWithData( $GetParam-&gt;{ConfigMatch} ) ) {
            $GetParam-&gt;{ConfigMatch} = undef;
        }
    }

    if ( $GetParam-&gt;{ConfigChange} ) {
        $GetParam-&gt;{ConfigChange} = $Self-&gt;{JSONObject}-&gt;Decode(
            Data =&gt; $GetParam-&gt;{ConfigChange},
        );
        if ( !IsHashRefWithData( $GetParam-&gt;{ConfigChange} ) ) {
            $GetParam-&gt;{ConfigChange} = undef;
        }
    }

    return $GetParam;
}

sub _CheckACLDelete {
    my ( $Self, %Param ) = @_;

    # get ACL data
    my $ACLData = $Self-&gt;{ACLObject}-&gt;ACLGet(
        ID     =&gt; $Param{ID},
        UserID =&gt; $Self-&gt;{UserID},
    );

    # check for valid ACL data
    if ( !IsHashRefWithData($ACLData) ) {
        return {
            Success =&gt; 0,
            Message =&gt; "Could not get data for ACLID $Param{ID}",
        };
    }

    return {
        Success =&gt; 1,
        ACLData =&gt; $ACLData,
    };
}

1;
</pre></body></html>