Files
2024-10-14 00:08:40 +02:00

1275 lines
34 KiB
Perl

# --
# 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<https://otrs.org/>).
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<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut
1;