1275 lines
34 KiB
Perl
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;
|