# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::ACL::DB::ACL;
use strict;
use warnings;
use Kernel::Language qw(Translatable);
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Cache',
'Kernel::System::DB',
'Kernel::System::Log',
'Kernel::System::Main',
'Kernel::System::User',
'Kernel::System::YAML',
);
=head1 NAME
Kernel::System::ACL::DB::ACL
=head1 DESCRIPTION
ACL DB ACL backend
=head1 PUBLIC INTERFACE
=head2 new()
create a ACL object. Do not use it directly, instead use:
my $ACLObject = $Kernel::OM->Get('Kernel::System::ACL::DB::ACL');
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# get the cache TTL (in seconds)
$Self->{CacheTTL} = int( $Kernel::OM->Get('Kernel::Config')->Get('ACL::CacheTTL') || 3600 );
# set lower if database is case sensitive
$Self->{Lower} = '';
if ( $Kernel::OM->Get('Kernel::System::DB')->GetDatabaseFunction('CaseSensitive') ) {
$Self->{Lower} = 'LOWER';
}
return $Self;
}
=head2 ACLAdd()
add new ACL
returns the id of the created ACL if success or undef otherwise
my $ID = $ACL->ACLAdd(
Name => 'NameOfACL' # mandatory
Comment => 'Comment', # optional
Description => 'Description', # optional
StopAfterMatch => 1, # optional
ConfigMatch => $ConfigMatchHashRef, # optional
ConfigChange => $ConfigChangeHashRef, # optional
ValidID => 1, # mandatory
UserID => 123, # mandatory
);
Returns:
$ID = 567;
=cut
sub ACLAdd {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Key (qw(Name ValidID UserID)) {
if ( !$Param{$Key} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Key!",
);
return;
}
}
# get yaml object
my $YAMLObject = $Kernel::OM->Get('Kernel::System::YAML');
# define Description field if not present
$Param{Description} //= '';
my $ConfigMatch = '';
my $ConfigChange = '';
if ( $Param{ConfigMatch} ) {
if ( !IsHashRefWithData( $Param{ConfigMatch} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "ConfigMatch needs to be a valid hash with data!",
);
return;
}
$ConfigMatch = $YAMLObject->Dump( Data => $Param{ConfigMatch} );
utf8::upgrade($ConfigMatch);
}
if ( $Param{ConfigChange} ) {
if ( !IsHashRefWithData( $Param{ConfigChange} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "ConfigChange needs to be a valid hash with data!",
);
return;
}
$ConfigChange = $YAMLObject->Dump( Data => $Param{ConfigChange} );
utf8::upgrade($ConfigChange);
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# check if ACL with this name already exists
return if !$DBObject->Prepare(
SQL => "
SELECT id
FROM acl
WHERE $Self->{Lower}(name) = $Self->{Lower}(?)",
Bind => [ \$Param{Name} ],
Limit => 1,
);
my $ACLExists;
while ( my @Data = $DBObject->FetchrowArray() ) {
$ACLExists = 1;
}
if ($ACLExists) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "An ACL with the name '$Param{Name}' already exists.",
);
return;
}
# SQL
return if !$DBObject->Do(
SQL => '
INSERT INTO acl ( name, comments, description, stop_after_match, config_match,
config_change, valid_id, create_time, create_by, change_time, change_by )
VALUES (?, ?, ?, ?, ?, ?, ?, current_timestamp, ?, current_timestamp, ?)',
Bind => [
\$Param{Name}, \$Param{Comment}, \$Param{Description}, \$Param{StopAfterMatch},
\$ConfigMatch, \$ConfigChange, \$Param{ValidID},
\$Param{UserID}, \$Param{UserID},
],
);
return if !$DBObject->Prepare(
SQL => 'SELECT id FROM acl WHERE name = ?',
Bind => [ \$Param{Name} ],
);
my $ID;
while ( my @Row = $DBObject->FetchrowArray() ) {
$ID = $Row[0];
}
return if !$ID;
# delete cache
$Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
Type => 'ACLEditor_ACL',
);
return if !$DBObject->Do(
SQL => '
INSERT INTO acl_sync ( acl_id, sync_state, create_time, change_time )
VALUES (?, ?, current_timestamp, current_timestamp)',
Bind => [ \$ID, \'not_sync' ],
);
return $ID;
}
=head2 ACLDelete()
delete an ACL
returns 1 if success or undef otherwise
my $Success = $ACLObject->ACLDelete(
ID => 123,
UserID => 123,
);
=cut
sub ACLDelete {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Key (qw(ID UserID)) {
if ( !$Param{$Key} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Key!"
);
return;
}
}
# check if exists
my $ACL = $Self->ACLGet(
ID => $Param{ID},
UserID => 1,
);
return if !IsHashRefWithData($ACL);
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# delete ACL
return if !$DBObject->Do(
SQL => 'DELETE FROM acl WHERE id = ?',
Bind => [ \$Param{ID} ],
);
# delete cache
$Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
Type => 'ACLEditor_ACL',
);
return if !$DBObject->Do(
SQL => '
INSERT INTO acl_sync ( acl_id, sync_state, create_time, change_time )
VALUES (?, ?, current_timestamp, current_timestamp)',
Bind => [ \$Param{ID}, \'deleted' ],
);
return 1;
}
=head2 ACLGet()
get ACL attributes
my $ACL = $ACLObject->ACLGet(
ID => 123, # ID or name is needed
Name => 'ACL1',
UserID => 123, # mandatory
);
Returns:
$ACL = {
ID => 123,
Name => 'some name',
Comment => 'Comment',
Description => 'Description',
StopAfterMatch => 1,
ConfigMatch => $ConfigMatchHashRef,
ConfigChange => $ConfigChangeHashRef,
ValidID => 1,
CreateTime => '2012-07-04 15:08:00',
ChangeTime => '2012-07-04 15:08:00',
CreateBy => 'user_login',
ChangeBy => 'user_login',
};
=cut
sub ACLGet {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{ID} && !$Param{Name} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need ID or Name!'
);
return;
}
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need UserID!',
);
return;
}
# check cache
my $CacheKey;
if ( $Param{ID} ) {
$CacheKey = 'ACLGet::ID::' . $Param{ID};
}
else {
$CacheKey = 'ACLGet::Name::' . $Param{Name};
}
# get cache object
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
my $Cache = $CacheObject->Get(
Type => 'ACLEditor_ACL',
Key => $CacheKey,
);
return $Cache if $Cache;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# SQL
if ( $Param{ID} ) {
return if !$DBObject->Prepare(
SQL => '
SELECT id, name, comments, description, stop_after_match, valid_id, config_match,
config_change, create_time, change_time, create_by, change_by
FROM acl
WHERE id = ?',
Bind => [ \$Param{ID} ],
Limit => 1,
);
}
else {
return if !$DBObject->Prepare(
SQL => '
SELECT id, name, comments, description, stop_after_match, valid_id, config_match,
config_change, create_time, change_time, create_by, change_by
FROM acl
WHERE name = ?',
Bind => [ \$Param{Name} ],
Limit => 1,
);
}
# get yaml object
my $YAMLObject = $Kernel::OM->Get('Kernel::System::YAML');
my %Data;
while ( my @Data = $DBObject->FetchrowArray() ) {
my $ConfigMatch = '';
if ( $Data[6] ) {
$ConfigMatch = $YAMLObject->Load( Data => $Data[6] );
}
my $ConfigChange = '';
if ( $Data[7] ) {
$ConfigChange = $YAMLObject->Load( Data => $Data[7] );
}
%Data = (
ID => $Data[0],
Name => $Data[1],
Comment => $Data[2],
Description => $Data[3] || '',
StopAfterMatch => $Data[4] || 0,
ValidID => $Data[5],
ConfigMatch => $ConfigMatch,
ConfigChange => $ConfigChange,
CreateTime => $Data[8],
ChangeTime => $Data[9],
CreateBy => $Data[10],
ChangeBy => $Data[11],
);
}
return if !$Data{ID};
# get user object
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
# convert UserIDs outside of fetchrowArray, otherwise UserLooukup will rise some warnings
my $CreateUser = $UserObject->UserLookup( UserID => $Data{CreateBy} );
my $ChangeUser = $UserObject->UserLookup( UserID => $Data{ChangeBy} );
$Data{CreateBy} = $CreateUser;
$Data{ChangeBy} = $ChangeUser;
# set cache
$CacheObject->Set(
Type => 'ACLEditor_ACL',
Key => $CacheKey,
Value => \%Data,
TTL => $Self->{CacheTTL},
);
return \%Data;
}
=head2 ACLUpdate()
update ACL attributes
returns 1 if success or undef otherwise
my $Success = $ACLObject->ACLUpdate(
ID => 123, # mandatory
Name => 'NameOfACL', # mandatory
Comment => 'Comment', # optional
Description => 'Description', # optional
StopAfterMatch => 1, # optional
ValidID => 'ValidID', # mandatory
ConfigMatch => $ConfigMatchHashRef, # optional
ConfigChange => $ConfigChangeHashRef, # optional
UserID => 123, # mandatory
);
=cut
sub ACLUpdate {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Key (qw(ID Name ValidID UserID)) {
if ( !$Param{$Key} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Key!"
);
return;
}
}
# define Description field if not present
$Param{Description} //= '';
my $ConfigMatch = '';
my $ConfigChange = '';
for my $Key (qw(ConfigMatch ConfigChange)) {
if ( $Param{$Key} && !IsHashRefWithData( $Param{$Key} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "$Key needs to be a valid hash with data!",
);
return;
}
}
# get yaml object
my $YAMLObject = $Kernel::OM->Get('Kernel::System::YAML');
if ( $Param{ConfigMatch} && IsHashRefWithData( $Param{ConfigMatch} ) ) {
$ConfigMatch = $YAMLObject->Dump( Data => $Param{ConfigMatch} );
utf8::upgrade($ConfigMatch);
}
if ( $Param{ConfigChange} && IsHashRefWithData( $Param{ConfigChange} ) ) {
$ConfigChange = $YAMLObject->Dump( Data => $Param{ConfigChange} );
utf8::upgrade($ConfigChange);
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# check if Name already exists
return if !$DBObject->Prepare(
SQL => "
SELECT id FROM acl
WHERE $Self->{Lower}(name) = $Self->{Lower}(?)
AND id != ?",
Bind => [ \$Param{Name}, \$Param{ID} ],
LIMIT => 1,
);
my $ACLExists;
while ( my @Data = $DBObject->FetchrowArray() ) {
$ACLExists = 1;
}
if ($ACLExists) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "An ACL with the name '$Param{Name}' already exists.",
);
return;
}
# check if need to update db
return if !$DBObject->Prepare(
SQL => '
SELECT name, comments, description, stop_after_match, valid_id, config_match,
config_change
FROM acl
WHERE id = ?',
Bind => [ \$Param{ID} ],
Limit => 1,
);
my $CurrentName;
my $CurrentComment;
my $CurrentDescription;
my $CurrentStopAfterMatch;
my $CurrentValidID;
my $CurrentConfigMatch;
my $CurrentConfigChange;
while ( my @Data = $DBObject->FetchrowArray() ) {
$CurrentName = $Data[0];
$CurrentComment = $Data[1];
$CurrentDescription = $Data[2] || '';
$CurrentStopAfterMatch = $Data[3] || 0;
$CurrentValidID = $Data[4];
$CurrentConfigMatch = $Data[5];
$CurrentConfigChange = $Data[6];
}
if (
$CurrentName
&& $CurrentName eq $Param{Name}
&& $CurrentComment eq $Param{Comment}
&& $CurrentDescription eq $Param{Description}
&& $CurrentStopAfterMatch eq $Param{StopAfterMatch}
&& $CurrentValidID eq $Param{ValidID}
&& $CurrentConfigMatch eq $Param{ConfigMatch}
&& $CurrentConfigChange eq $Param{ConfigChange}
)
{
return 1;
}
# SQL
return if !$DBObject->Do(
SQL => '
UPDATE acl
SET name = ?, comments = ?, description = ?, stop_after_match = ?, valid_id = ?,
config_match = ?, config_change = ?, change_time = current_timestamp, change_by = ?
WHERE id = ?',
Bind => [
\$Param{Name}, \$Param{Comment}, \$Param{Description}, \$Param{StopAfterMatch},
\$Param{ValidID}, \$ConfigMatch, \$ConfigChange,
\$Param{UserID}, \$Param{ID},
],
);
# delete cache
$Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
Type => 'ACLEditor_ACL',
);
return if !$DBObject->Do(
SQL => '
INSERT INTO acl_sync ( acl_id, sync_state, create_time, change_time )
VALUES (?, ?, current_timestamp, current_timestamp)',
Bind => [ \$Param{ID}, \'not_sync' ],
);
return 1;
}
=head2 ACLList()
get an ACL list
my $List = $ACLObject->ACLList(
ValidIDs => ['1','2'], # optional, to filter ACLs that match listed valid IDs
UserID => 1,
);
Returns:
$List = {
1 => 'NameOfACL',
}
=cut
sub ACLList {
my ( $Self, %Param ) = @_;
# check needed
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need UserID!"
);
return;
}
my $ValidIDsStrg;
if ( !IsArrayRefWithData( $Param{ValidIDs} ) ) {
$ValidIDsStrg = 'ALL';
}
else {
$ValidIDsStrg = join ',', @{ $Param{ValidIDs} };
}
# get cache object
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
# check cache
my $CacheKey = 'ACLList::ValidIDs::' . $ValidIDsStrg;
my $Cache = $CacheObject->Get(
Type => 'ACLEditor_ACL',
Key => $CacheKey,
);
return $Cache if ref $Cache;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
my $SQL = '
SELECT id, name
FROM acl ';
if ( $ValidIDsStrg ne 'ALL' ) {
my $ValidIDsStrgDB = join ',', map { $DBObject->Quote( $_, 'Integer' ) }
@{ $Param{ValidIDs} };
$SQL .= "WHERE valid_id IN ($ValidIDsStrgDB)";
}
return if !$DBObject->Prepare( SQL => $SQL );
my %Data;
while ( my @Row = $DBObject->FetchrowArray() ) {
$Data{ $Row[0] } = $Row[1];
}
# set cache
$CacheObject->Set(
Type => 'ACLEditor_ACL',
Key => $CacheKey,
Value => \%Data,
TTL => $Self->{CacheTTL},
);
return \%Data;
}
=head2 ACLListGet()
get an ACL list with all ACL details
my $List = $ACLObject->ACLListGet(
UserID => 1,
ValidIDs => ['1','2'], # optional, to filter ACLs that match listed valid IDs
);
Returns:
$List = [
{
ID => 123,
Name => 'some name',
Comment => 'Comment',
Description => 'Description',
ValidID => 1,
ConfigMatch => $ConfigMatchHashRef,
ConfigChange => $ConfigChangeHashRef,
CreateTime => '2012-07-04 15:08:00',
ChangeTime => '2012-07-04 15:08:00',
},
{
ID => 123,
Name => 'some name',
Comment => 'Comment',
Description => 'Description',
ValidID => 1,
ConfigMatch => $ConfigMatchHashRef,
ConfigChange => $ConfigChangeHashRef,
CreateTime => '2012-07-04 15:08:00',
ChangeTime => '2012-07-04 15:08:00',
},
];
=cut
sub ACLListGet {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need UserID!',
);
return;
}
my $ValidIDsStrg;
if ( !IsArrayRefWithData( $Param{ValidIDs} ) ) {
$ValidIDsStrg = 'ALL';
}
else {
$ValidIDsStrg = join ',', @{ $Param{ValidIDs} };
}
# get cache object
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
# check cache
my $CacheKey = 'ACLListGet::ValidIDs::' . $ValidIDsStrg;
my $Cache = $CacheObject->Get(
Type => 'ACLEditor_ACL',
Key => $CacheKey,
);
return $Cache if $Cache;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
my $SQL = '
SELECT id
FROM acl ';
if ( $ValidIDsStrg ne 'ALL' ) {
my $ValidIDsStrgDB = join ',', map { $DBObject->Quote( $_, 'Integer' ) } @{ $Param{ValidIDs} };
$SQL .= "WHERE valid_id IN ($ValidIDsStrgDB)";
}
$SQL .= 'ORDER BY id';
# SQL
return if !$DBObject->Prepare(
SQL => $SQL,
);
my @ACLIDs;
while ( my @Row = $DBObject->FetchrowArray() ) {
push @ACLIDs, $Row[0];
}
my @Data;
for my $ItemID (@ACLIDs) {
my $ACLData = $Self->ACLGet(
ID => $ItemID,
UserID => 1,
);
push @Data, $ACLData;
}
# set cache
$CacheObject->Set(
Type => 'ACLEditor_ACL',
Key => $CacheKey,
Value => \@Data,
TTL => $Self->{CacheTTL},
);
return \@Data;
}
=head2 ACLsNeedSync()
Check if there are ACLs that are not yet deployed
my $SyncCount = $ACLObject->ACLsNeedSync();
Returns:
$SyncCount = 0 || Number of ALCs that need to be synced
=cut
sub ACLsNeedSync {
my ( $Self, %Param ) = @_;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
my $SQL = '
SELECT COUNT(*)
FROM acl_sync';
return if !$DBObject->Prepare( SQL => $SQL );
my $NeedSync = 0;
while ( my @Row = $DBObject->FetchrowArray() ) {
$NeedSync = $Row[0];
}
return $NeedSync;
}
=head2 ACLsNeedSyncReset()
Reset synchronization information for ACLs.
=cut
sub ACLsNeedSyncReset {
my ( $Self, %Param ) = @_;
return if !$Kernel::OM->Get('Kernel::System::DB')->Do( SQL => 'DELETE FROM acl_sync' );
return 1;
}
=head2 ACLDump()
gets a complete ACL information dump from the DB
my $ACLDump = $ACLObject->ACLDump(
ResultType => 'SCALAR' # 'SCALAR' || 'HASH' || 'FILE'
Location => '/opt/otrs/var/myfile.txt' # mandatory for ResultType = 'FILE'
UserID => 1,
);
Returns:
$ACLDump = '/opt/otrs/var/myfile.txt'; # or undef if can't write the file
=cut
sub ACLDump {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need UserID!',
);
return;
}
if ( !defined $Param{ResultType} ) {
$Param{ResultType} = 'FILE';
}
if ( $Param{ResultType} eq 'FILE' ) {
if ( !$Param{Location} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Location for ResultType \'FILE\'!',
);
}
}
# get valid ACLs
my $ACLList = $Self->ACLListGet(
UserID => 1,
ValidIDs => [1],
);
my %ACLDump;
ACL:
for my $ACLData ( @{$ACLList} ) {
next ACL if !IsHashRefWithData($ACLData);
my $Properties;
my $PropertiesDatabase;
if ( IsHashRefWithData( $ACLData->{ConfigMatch} ) ) {
$Properties = $ACLData->{ConfigMatch}->{Properties};
$PropertiesDatabase = $ACLData->{ConfigMatch}->{PropertiesDatabase};
}
my $Possible;
my $PossibleAdd;
my $PossibleNot;
if ( IsHashRefWithData( $ACLData->{ConfigChange} ) ) {
$Possible = $ACLData->{ConfigChange}->{Possible};
$PossibleAdd = $ACLData->{ConfigChange}->{PossibleAdd};
$PossibleNot = $ACLData->{ConfigChange}->{PossibleNot};
}
$ACLDump{ $ACLData->{Name} } = {
CreateTime => $ACLData->{CreateTime},
ChangeTime => $ACLData->{ChangeTime},
CreateBy => $ACLData->{CreateBy},
ChangeBy => $ACLData->{ChangeBy},
Comment => $ACLData->{Comment},
Values => {
StopAfterMatch => $ACLData->{StopAfterMatch} || 0,
Properties => $Properties || {},
PropertiesDatabase => $PropertiesDatabase || {},
Possible => $Possible || {},
PossibleAdd => $PossibleAdd || {},
PossibleNot => $PossibleNot || {},
},
};
}
# delete cache
$Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
Type => 'ACLEditor_ACL',
);
my $Output = '';
for my $ACLName ( sort keys %ACLDump ) {
# create output
$Output .= $Self->_ACLItemOutput(
Key => $ACLName,
Value => $ACLDump{$ACLName}{Values},
Comment => $ACLDump{$ACLName}{Comment},
CreateTime => $ACLDump{$ACLName}{CreateTime},
ChangeTime => $ACLDump{$ACLName}{ChangeTime},
CreateBy => $ACLDump{$ACLName}{CreateBy},
ChangeBy => $ACLDump{$ACLName}{ChangeBy},
);
}
# get user data of the current user to use for the file comment
my %User = $Kernel::OM->Get('Kernel::System::User')->GetUserData(
UserID => $Param{UserID},
);
# remove home from location path to show in file comment
my $Home = $Kernel::OM->Get('Kernel::Config')->Get('Home');
my $Location = $Param{Location};
$Location =~ s{$Home\/}{}xmsg;
# build comment (therefore we need to trick out the filter)
my $FileStart = <<'EOF';
# OTRS config file (automatically generated)
# VERSION:1.1
package Kernel::Config::Files::ZZZACL;
use strict;
use warnings;
no warnings 'redefine'; ## no critic
use utf8;
sub Load {
my ($File, $Self) = @_;
EOF
my $FileEnd = <<'EOF';
return;
}
1;
EOF
$Output = $FileStart . $Output . $FileEnd;
my $FileLocation = $Kernel::OM->Get('Kernel::System::Main')->FileWrite(
Location => $Param{Location},
Content => \$Output,
Mode => 'utf8',
Type => 'Local',
);
return $FileLocation;
}
=head2 ACLImport()
import an ACL YAML file/content
my $ACLImport = $ACLObject->ACLImport(
Content => $YAMLContent, # mandatory, YAML format
OverwriteExistingEntities => 0, # 0 || 1
UserID => 1, # mandatory
);
Returns:
$ACLImport = {
Success => 1, # 1 if success or undef if operation could not
# be performed
Message => 'The Message to show.', # error message
AddedACLs => 'ACL1, ACL2', # list of ACLs correctly added
UpdatedACLs => 'ACL3, ACL4', # list of ACLs correctly updated
ACLErrors => 'ACL5', # list of ACLs that could not be added or updated
};
=cut
sub ACLImport {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Content UserID)) {
# check needed stuff
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return {
Success => 0,
Message => "$Needed is missing can not continue.",
};
}
}
my $ACLData = $Kernel::OM->Get('Kernel::System::YAML')->Load( Data => $Param{Content} );
if ( ref $ACLData ne 'ARRAY' ) {
return {
Success => 0,
Message => Translatable("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';
$ACL = $Self->_ACLMigrateFrom33(
ACL => $ACL,
UserID => $Param{UserID},
);
my @ExistingACLs = @{ $Self->ACLListGet( UserID => $Param{UserID} ) || [] };
@ExistingACLs = grep { $_->{Name} eq $ACL->{Name} } @ExistingACLs;
if ( $Param{OverwriteExistingEntities} && $ExistingACLs[0] ) {
my $Success = $Self->ACLUpdate(
%{ $ExistingACLs[0] },
Name => $ACL->{Name},
Comment => $ACL->{Comment},
Description => $ACL->{Description} || '',
StopAfterMatch => $ACL->{StopAfterMatch} || 0,
ConfigMatch => $ACL->{ConfigMatch} || undef,
ConfigChange => $ACL->{ConfigChange} || undef,
ValidID => $ACL->{ValidID} || 1,
UserID => $Param{UserID},
);
if ($Success) {
push @UpdatedACLs, $ACL->{Name};
}
else {
push @ACLErrors, $ACL->{Name};
}
}
else {
# now add the ACL
my $Success = $Self->ACLAdd(
Name => $ACL->{Name},
Comment => $ACL->{Comment},
Description => $ACL->{Description} || '',
ConfigMatch => $ACL->{ConfigMatch} || undef,
ConfigChange => $ACL->{ConfigChange} || undef,
StopAfterMatch => $ACL->{StopAfterMatch},
ValidID => $ACL->{ValidID} || 1,
UserID => $Param{UserID},
);
if ($Success) {
push @AddedACLs, $ACL->{Name};
}
else {
push @ACLErrors, $ACL->{Name};
}
}
}
return {
Success => 1,
AddedACLs => join( ', ', @AddedACLs ) || '',
UpdatedACLs => join( ', ', @UpdatedACLs ) || '',
ACLErrors => join( ', ', @ACLErrors ) || '',
};
}
=begin Internal:
=cut
=head2 _ACLItemOutput()
converts an ACL structure to perl code suitable to be saved on a perl file.
my $Output = $ACLObject->_ACLItemOutput (
Key => 'some ACL name',
Value => {
Properties => {
Ticket => {
Priority => [ 'some priority' ],
Queue => [ 'some queue' ],
},
},
PropertiesDatabase => { }, # similar to Properties or empty hash ref
Possible => {
Ticket => {
Queue => [ 'some other queue' ],
},
PossibleNot => { }, # similar to Possible or empty hash ref
PossibleAdd => { }, # similar to Possible or empty hash ref
StopAfterMatch => 0, # 0 or 1
},
Comment => 'some comment',
CreateTime => '2014-06-03 19:03:57',
ChangeTime => '2014-06-03 19:51:17',
CreateBy => 'some user login',
ChangeBy => 'some user login',
);
returns:
$Output = '
# Created: 2014-06-03 19:03:57 (some user login)
# Changed: 2014-06-03 19:51:17 (some user login)
# Comment: some comment
$Self->{TicketAcl}->{"100-Example-ACL"} = {
\\'Possible\\' => {
\\'Ticket\\' => {
\\'Queue\\' => [
\\'some other queue\\'
]
}
},
\\'PossibleAdd\\' => {},
\\'PossibleNot\\' => {},
\\'Properties\\' => {
\\'Ticket\\' => {
\\'Priority\\' => [
\\'some priority\\'
],
\\'Queue\\' => [
\\'some queue\\'
]
}
},
\\'PropertiesDatabase\\' => {},
\\'StopAfterMatch\\' => 0
};
';
=cut
sub _ACLItemOutput {
my ( $Self, %Param ) = @_;
my $Output = "# Created: $Param{CreateTime} ($Param{CreateBy})\n";
$Output .= "# Changed: $Param{ChangeTime} ($Param{ChangeBy})\n";
if ( $Param{Comment} ) {
$Output .= "# Comment: $Param{Comment}\n";
}
$Output .= $Kernel::OM->Get('Kernel::System::Main')->Dump(
$Param{Value},
);
# replace "[empty]" by ''
$Output =~ s{\[empty\]}{}xmsg;
my $Name = $Param{Key};
$Name =~ s{\'}{\\'}xmsg;
my $Key = '$Self->{TicketAcl}->{\'' . $Name . '\'}';
$Output =~ s{\$VAR1}{$Key}mxs;
return $Output . "\n";
}
=head2 _ACLMigrateFrom33()
Updates ACLs structure my changing the Possible->Action hash ref to a PossibleNot->Action array ref
with just the elements that where set to 0 in the original ACL:
my $ACL = $ACLObject->_ACLMigrateFrom33 (
$ACL => {
ID => 123,
Name => 'some name',
Description => '',
Comment => 'Comment',
ConfigMatch => {
Properties' => {},
},
ConfigChange => {
Possible => {}
Action => {
AgentTicketPhone => 1,
AgentTicketPrint => 0,
AgentTicketZoom => 1,
AgentTicketCLose => 0,
AgentTicketCompose => 0,
},
},
PossibleNot => {},
},
StopAfterMatch => 1,
ValidID => 1,
CreateTime => '2013-09-20 11:56:05',
CreateBy => 'root@localhost',
ChangeTime => '2014-06-16 11:31:55',
ChangeBy => 'root@localhost',
};
UserID => 123,
)
Returns:
$ACL = {
ID => 123,
Name => 'some name',
Description => '',
Comment => 'Comment',
ConfigMatch => {
Properties' => {},
},
ConfigChange => {
Possible => {},
PossibleNot => {
Action => [
'AgentTicketCLose',
'AgentTicketCompose',
'AgentTicketPrint'
],
},
}
StopAfterMatch => 1,
ValidID => 1,
CreateBy => 'root@localhost',
CreateTime => '2013-09-20 11:56:05',
ChangeTime => '2014-06-16 11:31:55',
ChangeBy => 'root@localhost',
};
=cut
sub _ACLMigrateFrom33 {
my ( $Self, %Param ) = @_;
my $ACL = $Param{ACL};
return $ACL if !ref $ACL->{ConfigChange};
return $ACL if !$ACL->{ConfigChange}->{Possible}->{Action};
return $ACL if ref $ACL->{ConfigChange}->{Possible}->{Action} ne 'HASH';
# convert old hash into an array using only the keys set to 0, and skip those that are set
# to 1, set them as PossibleNot and delete the Possible->Action section from the ACL.
my @NewAction = grep { $ACL->{ConfigChange}->{Possible}->{Action}->{$_} == 0 }
sort keys %{ $ACL->{ConfigChange}->{Possible}->{Action} };
delete $ACL->{ConfigChange}->{Possible}->{Action};
$ACL->{ConfigChange}->{PossibleNot}->{Action} = \@NewAction;
return $ACL;
}
1;
=end Internal:
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L.
=cut
1;