accountExpires in Active Directory

Jan 6, 2012 at 4:53 PM
Edited Jan 6, 2012 at 4:57 PM

I've compiled a list of things I need to be able to do with the app that will use LINQ to LDAP and I'm running through it at the mo.  I've managed to deal with single- and multi-valued attributes (strings) and now i'm working through other data types.

First, dates/times.  I've managed to map whenCreated/whenChanged succesfully.  They're system-only, so I only had to worry about reads, and are String(Generalized-Time) in AD so I just created them as DateTime and did a standard mapping.  That seems to work okay.

Then I tried accountExpires.  Microsoft defines this as "The date when the account expires. This value represents the number of 100-nanosecond intervals since January 1, 1601 (UTC). A value of 0 or 0x7FFFFFFFFFFFFFFF (9223372036854775807) indicates that the account never expires."  It's stored in the directory as an Interval which "Represents a time interval value. The actual time units depend on the attribute. This syntax is identical to the LargeInteger syntax, except the high and low values are unsigned integers."

I mapped this using:

Map(x => x.accountExpires).DateTimeFormat(null);

and

public DateTime accountExpires { get; set; }

It works in two situations but not in the third:

  • if accountExpires has a typical value, such as today's date, it works;
  • if accountExpires has a zero value, (indicating 'never expires'), it works;
  • if accountExpires has the 0x7FFFFFFFFFFFFFFF (9223372036854775807) value (also indicating 'never expires'), it throws an exception (see below).

The exception

Value 'System.DirectoryServices.Protocols.DirectoryAttribute' returned from directory cannot be converted to System.DateTime for 'accountExpires' on 'LinqToLdap_Updates.Entities.UserObject' - 'CN=King Arthur (ka1234),OU=Test,OU=Royals,OU=Camelot Users,OU=Camelot,DC=big,DC=wooden,DC=badger'

The problem is that 0x7FFFFFFFFFFFFFFF (9223372036854775807) is the default value for newly-created accounts, so it must be able to handle it.

I've been thinking about how this might be handled.  Is it possible to require accountExpires to be mapped as Nullable<DateTime> and map null to 0x7FFFFFFFFFFFFFFF for reads and writes?

Note: if you're investigating this, once you've set a value for accountsExpires, if you set it back to (never) using ADU&C, it sets it to 0, not 0x7FFFFFFFFFFFFFFF.

Coordinator
Jan 7, 2012 at 12:01 AM

I will create a ticket for this and see what I come up with over the weekend.  Thanks for your feedback!

Jan 18, 2012 at 10:31 AM
Edited Jan 18, 2012 at 10:33 AM

[Comment deleted because it was nonsense.]

Jan 28, 2012 at 6:55 AM

In the post on lastLogonTimestamp, you replied:

"However, after your post on accountExpires, I began thinking about supporting mapping values from the directory to other .Net valid values.  For instance, mapping accountExpires as a nullable DateTime and mapping 9223372036854775807 to be treated as null from the directory and likewise, null from .Net will be converted to 9223372036854775807 when sending it back to the directory."

I've been thinking about this as well.  Rather than anything specific for an attribute that's probably only used that way in one directory, is it possible to do anything general with interfaces or base classes so we can provide our own implementations or is that a ridiculous idea?

Coordinator
Jan 30, 2012 at 7:11 AM
Edited Jan 30, 2012 at 7:20 AM

Not at all.  This functionality is actually part of changset 11038.  I'm still making sure everything works, but the idea is you can implement how a property is converted from the directory, to the directory, and to a filter value.  Here's an example mapping accountExpires:

 

MapCustom(x => x.AccountExpires)
    .Named("accountExpires")
    .ConvertFromDirectoryUsing(directoryAttribute =>
                                    {
                                        string str;
                                        if (directoryAttribute != null && directoryAttribute.Count > 0 &&
                                            (str = directoryAttribute[0] as string) != null &&
                                            !string.IsNullOrWhiteSpace(str))
                                        {
                                            if (str != "9223372036854775807")
                                            {
                                                var dateTime = DateTime.FromFileTime(long.Parse(str));
                                                return dateTime;
                                            }
                                        }

                                        return DateTime.MinValue;
                                    })
    .ConvertToDirectoryUsing(dateTime => dateTime == DateTime.MinValue ? "9223372036854775807" : dateTime.ToFileTime().ToString())
    .ConvertToFilterUsing(dateTime => dateTime.ToFileTime().ToString());

 

How much you implement is based on how you plan to use the property,  At the minimum you need to map ConverFromDirectoryUsing.  If you plan to do updates then you have to map ConvertToDirectoryUsing.  And if you plan to query using the property then you have to implement ConvertToFilterUsing.  Currently this is only available via ClassMap.  There's just not a clean way to do this with attributes.

EDIT

I must also add that you have complete control of your POCOs so you can also just have a property called AccountExpires mapped as a long and one called AccountExpiresDate that wraps AccountExpires and gets / sets the appropriate value.