Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
best-practical
rt-extension-ldapimport
Commits
05f15db9
Commit
05f15db9
authored
May 24, 2012
by
Thomas Sibley
Browse files
Merge branch 'object-cf-values'
parents
05d216b4
95a1def8
Changes
5
Hide whitespace changes
Inline
Side-by-side
MANIFEST
View file @
05f15db9
...
...
@@ -13,6 +13,8 @@ inc/Module/Install/RTx.pm
inc/Module/Install/Substitute.pm
inc/Module/Install/Win32.pm
inc/Module/Install/WriteAll.pm
inc/Module/Install/ReadmeFromPod.pm
INSTALL
INSTALL.SKIP
lib/RT/Extension/LDAPImport.pm
Makefile.PL
...
...
@@ -27,3 +29,4 @@ t/pod-coverage.t
t/pod.t
t/user-import-privileged.t
t/user-import.t
t/user-import-cfs.t
META.yml
View file @
05f15db9
...
...
@@ -28,4 +28,4 @@ requires:
Test::More:
0
resources
:
license
:
http://dev.perl.org/licenses/
version
:
0.32_0
3
version
:
0.32_0
4
README
View file @
05f15db9
...
...
@@ -74,6 +74,22 @@ CONFIGURATION
The LDAP attribute can also be a subroutine reference that returns
either an arrayref or a list of attributes.
The keys in the mapping (i.e. the RT fields, the left hand side) may
be a user custom field name prefixed with "UserCF.", for example
"'UserCF.Employee Number' => 'employeeId'". Note that this only adds
values at the moment, which on single value CFs will remove any old
value first. Multiple value CFs may behave not quite how you expect.
If the attribute no longer exists on a user in LDAP, it will be
cleared on the RT side as well.
You may also prefix any RT custom field name with "CF." inside your
mapping to add available values to a Select custom field. This
effectively takes user attributes in LDAP and adds the values as
selectable options in a CF. It does not set a CF value on any RT
object (User, Ticket, Queue, etc). You might use this to populate a
ticket Location CF with all the locations of your users so that
tickets can be associated with the locations in use.
"Set($LDAPCreatePrivileged, 1);"
By default users are created as Unprivileged, but you can change
this by setting $LDAPCreatePrivileged to 1.
...
...
@@ -298,6 +314,15 @@ METHODS
This could probably use some caching.
update_object_custom_field_values
Adds CF values to an object (currently only users). The Custom Field
should already exist, otherwise this will throw an error and not import
any data.
Note that this code only adds values at the moment, which on single
value CFs will remove any old value first. Multiple value CFs may behave
not quite how you expect.
import_groups import => 1|0
Takes the results of the search from "run_group_search" and maps
attributes from LDAP into "RT::Group" attributes using
...
...
lib/RT/Extension/LDAPImport.pm
View file @
05f15db9
package
RT::Extension::
LDAPImport
;
# XXX TODO: For historical reasons, this should become 0.33 after 0.32_xx dev releases are done.
our
$VERSION
=
'
0.32_0
3
';
our
$VERSION
=
'
0.32_0
4
';
use
warnings
;
use
strict
;
...
...
@@ -102,6 +102,20 @@ which will be concatenated together with a space.
The LDAP attribute can also be a subroutine reference
that returns either an arrayref or a list of attributes.
The keys in the mapping (i.e. the RT fields, the left hand side) may be a user
custom field name prefixed with C<UserCF.>, for example C<< 'UserCF.Employee
Number' => 'employeeId' >>. Note that this only B<adds> values at the moment,
which on single value CFs will remove any old value first. Multiple value CFs
may behave not quite how you expect. If the attribute no longer exists on a
user in LDAP, it will be cleared on the RT side as well.
You may also prefix any RT custom field name with C<CF.> inside your mapping to
add available values to a Select custom field. This effectively takes user
attributes in LDAP and adds the values as selectable options in a CF. It does
B<not> set a CF value on any RT object (User, Ticket, Queue, etc). You might
use this to populate a ticket Location CF with all the locations of your users
so that tickets can be associated with the locations in use.
=item C<< Set($LDAPCreatePrivileged, 1); >>
By default users are created as Unprivileged, but you can change this by
...
...
@@ -449,6 +463,7 @@ sub _import_user {
$self
->
add_user_to_group
(
%args
);
$self
->
add_custom_field_value
(
%args
);
$self
->
update_object_custom_field_values
(
%args
,
object
=>
$args
{
user
}
);
return
1
;
}
...
...
@@ -542,7 +557,7 @@ exists in the returned object.
sub
_build_user_object
{
my
$self
=
shift
;
my
$user
=
$self
->
_build_object
(
skip
=>
qr/(?i)^CF\./
,
skip
=>
qr/(?i)^
(?:User)?
CF\./
,
mapping
=>
$
RT::
LDAPMapping
,
@
_
);
...
...
@@ -827,6 +842,52 @@ sub add_custom_field_value {
}
=head3 update_object_custom_field_values
Adds CF values to an object (currently only users). The Custom Field should
already exist, otherwise this will throw an error and not import any data.
Note that this code only B<adds> values at the moment, which on single value
CFs will remove any old value first. Multiple value CFs may behave not quite
how you expect.
=cut
sub
update_object_custom_field_values
{
my
$self
=
shift
;
my
%args
=
@_
;
my
$obj
=
$args
{
object
};
foreach
my
$rtfield
(
keys
%
{
$
RT::
LDAPMapping
}
)
{
# XXX TODO: accept GroupCF when we call this from group_import too
next
unless
$rtfield
=~
/^UserCF\.(.+)$/i
;
my
$cf_name
=
$
1
;
my
$ldap_attribute
=
$
RT::
LDAPMapping
->
{
$rtfield
};
my
@attributes
=
$self
->
_parse_ldap_mapping
(
$ldap_attribute
);
unless
(
@attributes
)
{
$self
->
_error
("
Invalid LDAP mapping for
$rtfield
"
.
Dumper
(
$ldap_attribute
));
next
;
}
my
$value
=
join
'
',
grep
{
defined
and
length
}
map
{
scalar
$args
{
ldap_entry
}
->
get_value
(
$_
)
}
@attributes
;
if
((
$obj
->
FirstCustomFieldValue
(
$cf_name
)
||
'')
eq
(
$value
||
''))
{
$self
->
_debug
(
$obj
->
Name
.
"
: Value '
$value
' is already set for '
$cf_name
'
");
next
;
}
$self
->
_debug
(
$obj
->
Name
.
"
: Adding object value '
$value
' for '
$cf_name
'
");
next
unless
$args
{
import
};
my
(
$ok
,
$msg
)
=
$obj
->
AddCustomFieldValue
(
Field
=>
$cf_name
,
Value
=>
$value
);
$self
->
_error
(
$obj
->
Name
.
"
: Couldn't add value '
$value
' for '
$cf_name
':
$msg
")
unless
$ok
;
}
}
=head2 import_groups import => 1|0
Takes the results of the search from C<run_group_search>
...
...
@@ -912,6 +973,7 @@ sub _import_group {
my
(
$group_obj
,
$created
)
=
$self
->
create_rt_group
(
%args
,
group
=>
$group
);
return
if
$args
{
import
}
and
not
$group_obj
;
$self
->
add_group_members
(
%args
,
name
=>
$group
->
{
Name
},
group
=>
$group_obj
,
ldap_entry
=>
$ldap_entry
,
new
=>
$created
);
# XXX TODO: support OCFVs for groups too
return
;
}
...
...
t/user-import-cfs.t
0 → 100644
View file @
05f15db9
use
strict
;
use
warnings
;
use
lib
'
t/lib
';
use
RT::Extension::LDAPImport::
Test
tests
=>
7
+
13
*
3
+
3
+
2
*
2
+
1
;
eval
{
require
Net::LDAP::Server::
Test
;
1
;
}
or
do
{
plan
skip_all
=>
'
Unable to test without Net::Server::LDAP::Test
';
};
use
Net::LDAP::
Entry
;
use
RT::
User
;
{
my
$cf
=
RT::
CustomField
->
new
(
RT
->
SystemUser
);
my
(
$ok
,
$msg
)
=
$cf
->
Create
(
Name
=>
'
Employee Number
',
LookupType
=>
'
RT::User
',
Type
=>
'
FreeformSingle
',
Disabled
=>
0
,
);
ok
$cf
->
Id
,
$msg
;
my
$ocf
=
RT::
ObjectCustomField
->
new
(
RT
->
SystemUser
);
(
$ok
,
$msg
)
=
$ocf
->
Create
(
CustomField
=>
$cf
->
Id
);
ok
$ocf
->
Id
,
$msg
;
}
my
$importer
=
RT::Extension::
LDAPImport
->
new
;
isa_ok
(
$importer
,'
RT::Extension::LDAPImport
');
my
$ldap_port
=
1024
+
int
rand
(
10000
)
+
$$
%
1024
;
ok
(
my
$server
=
Net::LDAP::Server::
Test
->
new
(
$ldap_port
,
auto_schema
=>
1
),
"
spawned test LDAP server on port
$ldap_port
");
my
$ldap
=
Net::
LDAP
->
new
("
localhost:
$ldap_port
");
$ldap
->
bind
();
my
@ldap_entries
;
for
(
1
..
13
)
{
my
$username
=
"
testuser
$_
";
my
$dn
=
"
uid=
$username
,ou=foo,dc=bestpractical,dc=com
";
my
$entry
=
{
cn
=>
"
Test User
$_
"
.
int
rand
(
200
),
mail
=>
"
$username
\@
invalid.tld
",
uid
=>
$username
,
employeeId
=>
$_
,
objectClass
=>
'
User
',
};
push
@ldap_entries
,
{
dn
=>
$dn
,
%$entry
};
$ldap
->
add
(
$dn
,
attr
=>
[
%$entry
]
);
}
RT
->
Config
->
Set
('
LDAPHost
',"
ldap://localhost:
$ldap_port
");
RT
->
Config
->
Set
('
LDAPMapping
',
{
Name
=>
'
uid
',
EmailAddress
=>
'
mail
',
RealName
=>
'
cn
',
'
UserCF.Employee Number
'
=>
'
employeeId
',});
RT
->
Config
->
Set
('
LDAPBase
','
ou=foo,dc=bestpractical,dc=com
');
RT
->
Config
->
Set
('
LDAPFilter
','
(objectClass=User)
');
$importer
->
screendebug
(
1
)
if
(
$ENV
{
TEST_VERBOSE
});
# check that we don't import
ok
(
$importer
->
import_users
());
{
my
$users
=
RT::
Users
->
new
(
$
RT::
SystemUser
);
for
my
$username
(
qw/RT_System root Nobody/
)
{
$users
->
Limit
(
FIELD
=>
'
Name
',
OPERATOR
=>
'
!=
',
VALUE
=>
$username
,
ENTRYAGGREGATOR
=>
'
AND
'
);
}
is
(
$users
->
Count
,
0
);
}
# check that we do import
ok
(
$importer
->
import_users
(
import
=>
1
));
for
my
$entry
(
@ldap_entries
)
{
my
$user
=
RT::
User
->
new
(
$
RT::
SystemUser
);
$user
->
LoadByCols
(
EmailAddress
=>
$entry
->
{
mail
},
Realname
=>
$entry
->
{
cn
},
Name
=>
$entry
->
{
uid
}
);
ok
(
$user
->
Id
,
"
Found
$entry
->{cn} as
"
.
$user
->
Id
);
ok
(
!
$user
->
Privileged
,
"
User created as Unprivileged
");
is
(
$user
->
FirstCustomFieldValue
('
Employee Number
'),
$entry
->
{
employeeId
},
"
cf is good
");
}
# import again, check that it was cleared
{
my
$delete
=
$ldap_entries
[
0
];
$ldap
->
modify
(
$delete
->
{
dn
},
delete
=>
['
employeeId
']
);
delete
$delete
->
{
employeeId
};
my
$update
=
$ldap_entries
[
1
];
$ldap
->
modify
(
$update
->
{
dn
},
replace
=>
['
employeeId
'
=>
42
]
);
$update
->
{
employeeId
}
=
42
;
ok
(
$importer
->
import_users
(
import
=>
1
));
for
my
$entry
(
@ldap_entries
[
0
,
1
])
{
my
$user
=
RT::
User
->
new
(
$
RT::
SystemUser
);
$user
->
LoadByCols
(
EmailAddress
=>
$entry
->
{
mail
},
Realname
=>
$entry
->
{
cn
},
Name
=>
$entry
->
{
uid
}
);
ok
(
$user
->
Id
,
"
Found
$entry
->{cn} as
"
.
$user
->
Id
);
is
(
$user
->
FirstCustomFieldValue
('
Employee Number
'),
$entry
->
{
employeeId
},
"
cf is updated
");
}
}
# can't unbind earlier or the server will die
$ldap
->
unbind
;
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment