Proposed Contrib: External (LDAP) user info attr mapping

Hello!

I apologize in advance for the length of this. I hate long email but I
value the advice of this list when approaching something as complex as
I’m describing below … and if I can’t adequately explain it here, I
feel like I shouldn’t do it at all. So you get to read a long email.
Sorry. =\

I want to implement the mapping of LDAP attr → RT field mentioned in
Philip Cole’s code comments at
Request Tracker Wiki and, in order to avoid synchronization issues, I want information which is gotten from LDAP to be immutable in all RT interfaces (web and CLI). I have the additional challenge of five valid FQDNs by which you can reach folks here using some various combos of unix login, first-initial-last-name, and first.last left of the @ … but there’s no one LHS which is universal to all five RHS.

I should mention that I have no control of, and little influence in, how
this all works, so cleaning it up isn’t an option for me. I’m also in a
hurry (I need to finish this by Friday), so waiting for the coming
cleanup isn’t an option, either.

Fun, eh?

I’m considering doing this like so:

  • Add %RT::LdapAttrMap as Philip Cole suggests, with some way of
    indicating user-based custom fields as well. Perhaps:

    %RT::LdapAttrMap = ( Name=> ‘uid’,
    RealName=> ‘cn’,
    EmailAdress=> ‘mail’,
    CF::ExternalObjectID=> ‘dn’
    …);

  • Add $RT::LdapUserInfoEnable and $RT::LdapUserInfoTTL (in minutes)
    to control whether user info is acquired from LDAP and, if so, how
    long it’s considered valid before rechecking LDAP.

  • Add user-based CFs for:

    • preferred email (Freeform single, set when receiving user email)
    • valid email addrs (Freeform multiple, cached from LDAP)
    • ExternalObjectId (Freeform single, cached from LDAP)
    • last LDAP update (Freeform single, set while getting LDAP info);
      alternately, this could just use the existing “LastUpdate”
      RT::User attr … though I was thinking that forcing a cache
      refresh could be as easy as blanking this CF and clicking
      “Save Preferences”.
  • Refactor Philip Cole’s RT::User::LookupExternalUserInfo() to accept
    ExternalObjectId or ExternalUserName (for use with the LDAP auth
    params), as well as the current key/value scheme.

  • Add a method, RT::User->RefreshFromExternalUserInfo(), which calls
    LookupExternalUserInfo() then uses the mapping defined in
    %RT::LdapAttrMap to set the current objects attrs appropriately.

  • Any time user info is loaded RT::User->Load(), check that the cached
    data hasn’t outlived its expiration and, if it has, call
    RefreshFromExternalInfo(). Failure of this refresh generates a
    warning (info? crit?) but is not fatal.

So where are the holes I’ve missed?

Thanks for reading this far!

–j

p.s. if ever you want to be sure to understand what you intend, compose
your plans in an email to people smarter than yourself and in front of
whom you’d like not to make a fool of yourself. It’s worked for me. =]

p.p.s. A minor digression: if I were implementing directly in RT rather
than as a contrib, I’d extend the Users table with ExternalObjectId and
LastExternalUpdate columns, both of which would only be seen on the
/Admin/Users/Modify page. I’d also add an EmailAddresses table with id,
ObjectId (users or groups, right?), EmailAddress, and Preferred
(boolean) columns.

I’d also work to better integrate the external autho/info methods
(RT::ExternalInfo.pm? $RT::ExternalAuthMethod? Dunno =). It might be fun
to leverage this to groups, too (manage RT group membership in LDAP;
give groups a distinct email address and leave off emailing individuals;
etc.)

If someone cares to comment on this approach, I’d enjoy the benefit of
their knowledge.
Jim Meyer, Geek at Large purp@acm.org

Hello!On Tue, 2005-11-29 at 21:35, Jim Meyer wrote:

p.p.s. A minor digression: if I were implementing directly in RT rather
than as a contrib, I’d extend the Users table with ExternalObjectId and
LastExternalUpdate columns, both of which would only be seen on the
/Admin/Users/Modify page. I’d also add an EmailAddresses table with id,
ObjectId (users or groups, right?), EmailAddress, and Preferred
(boolean) columns.

Or maybe “ExternalContactInfoId” and “ExternalAuthInfoId”. And perhaps
I’d add columns to track what system is used to get that info, something
like “ContactInfoSystem” and “AuthSystem”. Yeah, that sounds smart!

I should have dug just a touch deeper. Color me mortified. =\

However, now that I’m digging, these don’t seem to be used in RT code,
nor are they mentioned on the wiki. Googling through the mail archives
finds only three-year-old unanswered questions. Is there code somewhere
(in RTFM or RTIR?) which shows their preferred use, or just there for
use by folks who know about them in ways they divine or devise?

Thanks!

–j
Jim Meyer, Geek at Large purp@acm.org

Or maybe “ExternalContactInfoId” and “ExternalAuthInfoId”. And perhaps
I’d add columns to track what system is used to get that info, something
like “ContactInfoSystem” and “AuthSystem”. Yeah, that sounds smart!

I should have dug just a touch deeper. Color me mortified. =\

However, now that I’m digging, these don’t seem to be used in RT code,
nor are they mentioned on the wiki. Googling through the mail archives
finds only three-year-old unanswered questions. Is there code somewhere
(in RTFM or RTIR?) which shows their preferred use, or just there for
use by folks who know about them in ways they divine or devise?

It was part of the RT 2.0 design. I expected to want to support external
autentication systems. Those properties were reserved for future
expansion. It sounds like you’re doing some expansion.

Hello!

Other than Jesse’s comment, this has been Warnocked so I’m going forward
on the hope that it means no one sees a fatal flaw.

Cheers!

–jOn Tue, 2005-11-29 at 21:35, Jim Meyer wrote:

Hello!

I apologize in advance for the length of this. I hate long email but I
value the advice of this list when approaching something as complex as
I’m describing below … and if I can’t adequately explain it here, I
feel like I shouldn’t do it at all. So you get to read a long email.
Sorry. =\

I want to implement the mapping of LDAP attr → RT field mentioned in
Philip Cole’s code comments at
http://wiki.bestpractical.com/index.cgi?AutoCreateAndCanonicalizeUserInfo and, in order to avoid synchronization issues, I want information which is gotten from LDAP to be immutable in all RT interfaces (web and CLI). I have the additional challenge of five valid FQDNs by which you can reach folks here using some various combos of unix login, first-initial-last-name, and first.last left of the @ … but there’s no one LHS which is universal to all five RHS.

I should mention that I have no control of, and little influence in, how
this all works, so cleaning it up isn’t an option for me. I’m also in a
hurry (I need to finish this by Friday), so waiting for the coming
cleanup isn’t an option, either.

Fun, eh?

I’m considering doing this like so:

  • Add %RT::LdapAttrMap as Philip Cole suggests, with some way of
    indicating user-based custom fields as well. Perhaps:

    %RT::LdapAttrMap = ( Name=> ‘uid’,
    RealName=> ‘cn’,
    EmailAdress=> ‘mail’,
    CF::ExternalObjectID=> ‘dn’
    …);

  • Add $RT::LdapUserInfoEnable and $RT::LdapUserInfoTTL (in minutes)
    to control whether user info is acquired from LDAP and, if so, how
    long it’s considered valid before rechecking LDAP.

  • Add user-based CFs for:

    • preferred email (Freeform single, set when receiving user email)
    • valid email addrs (Freeform multiple, cached from LDAP)
    • ExternalObjectId (Freeform single, cached from LDAP)
    • last LDAP update (Freeform single, set while getting LDAP info);
      alternately, this could just use the existing “LastUpdate”
      RT::User attr … though I was thinking that forcing a cache
      refresh could be as easy as blanking this CF and clicking
      “Save Preferences”.
  • Refactor Philip Cole’s RT::User::LookupExternalUserInfo() to accept
    ExternalObjectId or ExternalUserName (for use with the LDAP auth
    params), as well as the current key/value scheme.

  • Add a method, RT::User->RefreshFromExternalUserInfo(), which calls
    LookupExternalUserInfo() then uses the mapping defined in
    %RT::LdapAttrMap to set the current objects attrs appropriately.

  • Any time user info is loaded RT::User->Load(), check that the cached
    data hasn’t outlived its expiration and, if it has, call
    RefreshFromExternalInfo(). Failure of this refresh generates a
    warning (info? crit?) but is not fatal.

So where are the holes I’ve missed?

Thanks for reading this far!

–j

p.s. if ever you want to be sure to understand what you intend, compose
your plans in an email to people smarter than yourself and in front of
whom you’d like not to make a fool of yourself. It’s worked for me. =]

p.p.s. A minor digression: if I were implementing directly in RT rather
than as a contrib, I’d extend the Users table with ExternalObjectId and
LastExternalUpdate columns, both of which would only be seen on the
/Admin/Users/Modify page. I’d also add an EmailAddresses table with id,
ObjectId (users or groups, right?), EmailAddress, and Preferred
(boolean) columns.

I’d also work to better integrate the external autho/info methods
(RT::ExternalInfo.pm? $RT::ExternalAuthMethod? Dunno =). It might be fun
to leverage this to groups, too (manage RT group membership in LDAP;
give groups a distinct email address and leave off emailing individuals;
etc.)

If someone cares to comment on this approach, I’d enjoy the benefit of
their knowledge.
Jim Meyer, Geek at Large purp@acm.org

Hello!

I apologize in advance for the length of this. I hate long email but I
value the advice of this list when approaching something as complex as
I’m describing below … and if I can’t adequately explain it here, I
feel like I shouldn’t do it at all. So you get to read a long email.
Sorry. =\

I want to implement the mapping of LDAP attr → RT field mentioned in
Philip Cole’s code comments at
Request Tracker Wiki and, in order to avoid synchronization issues, I want information which is gotten from LDAP to be immutable in all RT interfaces (web and CLI). I have the additional challenge of five valid FQDNs by which you can reach folks here using some various combos of unix login, first-initial-last-name, and first.last left of the @ … but there’s no one LHS which is universal to all five RHS.

I should mention that I have no control of, and little influence in, how
this all works, so cleaning it up isn’t an option for me. I’m also in a
hurry (I need to finish this by Friday), so waiting for the coming
cleanup isn’t an option, either.

Fun, eh?

I’m considering doing this like so:
Ok. I never tried external auth systems to use with RT, but try to comment this.

  • Add %RT::LdapAttrMap as Philip Cole suggests, with some way of
    indicating user-based custom fields as well. Perhaps:

    %RT::LdapAttrMap = ( Name=> ‘uid’,
    RealName=> ‘cn’,
    EmailAdress=> ‘mail’,
    CF::ExternalObjectID=> ‘dn’
    …);
    There is also other auth systems and really is better to implement
    into the RT core code that would allow you to define name of the
    module (for example RT::Auth::LDAP). This would allow you to subclass
    module and implement own mapping or something like this.

  • Add $RT::LdapUserInfoEnable and $RT::LdapUserInfoTTL (in minutes)
    to control whether user info is acquired from LDAP and, if so, how
    long it’s considered valid before rechecking LDAP.
  1. $RT::LdapUserInfoEnable should be named ExternalUserInfo or
    something like this and RT should just call method in the module of
    the chosen auth system.
  2. $RT::LdapUserInfoTTL - I think external script should check it, RT
    can fetch data on the first request, but then external script can be
    ran from cron to check if some records require update.
  • Add user-based CFs for:
    • preferred email (Freeform single, set when receiving user email)
    • valid email addrs (Freeform multiple, cached from LDAP)
      Don’t know if it should be CF, but anyway “multiple emails” is good feature.
  • ExternalObjectId (Freeform single, cached from LDAP)
    It should be Attribute of the user’s object.
  • last LDAP update (Freeform single, set while getting LDAP info);
    alternately, this could just use the existing “LastUpdate”
    RT::User attr … though I was thinking that forcing a cache
    refresh could be as easy as blanking this CF and clicking
    “Save Preferences”.
    Again attribute instead of CF.
  • Refactor Philip Cole’s RT::User::LookupExternalUserInfo() to accept
    ExternalObjectId or ExternalUserName (for use with the LDAP auth
    params), as well as the current key/value scheme.

  • Add a method, RT::User->RefreshFromExternalUserInfo(), which calls
    LookupExternalUserInfo() then uses the mapping defined in
    %RT::LdapAttrMap to set the current objects attrs appropriately.
    As said before, move to plugable solution, refactor it a bit. I’m not
    good in genrating names, but there is could be something like
    RT::User::External::LDAP, RT::User::External::NTLM, PAM or
    MyCompanyDBWithUsers that has all that methods: LookupUser,
    UpdateUserInfo and so on.

  • Any time user info is loaded RT::User->Load(), check that the cached
    data hasn’t outlived its expiration and, if it has, call
    RefreshFromExternalInfo(). Failure of this refresh generates a
    warning (info? crit?) but is not fatal.
    I really don’t like it, do this updates in external script.

So where are the holes I’ve missed?

Thanks for reading this far!

–j

p.s. if ever you want to be sure to understand what you intend, compose
your plans in an email to people smarter than yourself and in front of
whom you’d like not to make a fool of yourself. It’s worked for me. =]

p.p.s. A minor digression: if I were implementing directly in RT rather
than as a contrib, I’d extend the Users table with ExternalObjectId and
LastExternalUpdate columns, both of which would only be seen on the
/Admin/Users/Modify page. I’d also add an EmailAddresses table with id,
ObjectId (users or groups, right?), EmailAddress, and Preferred
(boolean) columns.
Now we have Attributes table that allows us to stick info to any
record in the DB, but if this feature will slowdown things a lot then
it could be splitted into own tables and fields.

I’d also work to better integrate the external autho/info methods
(RT::ExternalInfo.pm? $RT::ExternalAuthMethod? Dunno =). It might be fun
to leverage this to groups, too (manage RT group membership in LDAP;
give groups a distinct email address and leave off emailing individuals;
etc.)
manage groups membership from external source is good idea, but I
think it’s hard to implement as also group’s email addresses.

If someone cares to comment on this approach, I’d enjoy the benefit of
their knowledge.

Jim Meyer, Geek at Large purp@acm.org


Rt-devel mailing list
Rt-devel@lists.bestpractical.com
The rt-devel Archives

Best regards, Ruslan.