November 2007 Entries

So we deployed an ADXSTUDIO site onto ADAM (see my earlier post) but the out-the-box permissions editor didn't support ADAM, only full AD. We managed however to override the default permissions editor with our own - more on how we did that with ADXSTUDIO later.

For now I'd like to take our experiences and do a short blog series on writing your own permissions editor for AD or ADAM by using ONLY the DirectoryServices namespace in .NET2. The Microsoft documentation is pretty poor on the DirectoryServices side of things and badly lacking in examples and explanations. There's plenty of tidbits in blogs here and there and even a couple of  books on the subject, but all of them are lacking in bringing together exactly what you need to know and why. So, by trial, error and sheer pigheadedness, I had to get to the bottom of it all. I'd like to share it with you and hopefully it will be of use.

To set expectations - I'm not going to plaster my blog with code samples. You're a coder - you write code. You can do that yourself. What I will do is explain what classes you need, why you need them and what you do with them. This will apply equally to doing most things with AD/ADAM permissions, not just writing a permissions editor.

So firstly let's do a quick glossary/terminology check. There's a lot of acronyms etc. that fly around when talking about permissions and you probably know some or most of them already, but let's summarise them so that we're all starting from the same point:

  • AD - Active Directory, Microsoft's LDAP implementation - I might call it 'full AD' by which I mean that it is a domain-wide AD implementation. AD is licensed by CAL and cost can be high
  • ADAM (or AD/AM) - Active Directory Application Mode - a FREE version of AD which runs as a standalone 'instance' as opposed to a domain. It is effectively an LDAP provider for use by an application rather than an enterprise. You can have multiple ADAM 'instances' per server, but an ADAM instance cannot talk to other 'instances' or join a full AD farm. Otherwise, it is identical to AD in terms of API calls.
  • CAL - Client Access License, a way of licensing per-user and typically how Windows Server 2003 / AD is licensed
  • ACE - Access Control Entry - something attached to an object which says that somebody can (or cannot) do something with it. ACE's can be applied to files, websites and AD objects but we're only bothered about ACE's on AD objects. A typical ACE might be something like 'User [John Doe] can [Read the contents] of the AD container [Computers]'
  • ACL - Access Control List - A list containing a number of ACE's. All objects in AD have an ACL which contains the ACE's that apply to that object.
  • DACL - Discretionary Access Control List - There are two ACL's on all AD objects and this is the main one - A strange name for the general permissions on who can do what on the object like read the object, write the object, create children of the object, delete the object etc.
  • SACL - System Access Control List - This is the second ACL on all AD objects and it's actually to do with writing/reading audit entries for the object. We won't be worrying about this one too much
  • LDAP - Lightweight Directory Access Protocol - a standards-defined way of storing information in a directory repository. Usually used just for users and domains but actually a very efficient way of storing many types of hierarchical data.
  • AD Schema - the definition of all the object types in the actual AD repository for a domain (or instance, if using ADAM)

That should be all we need to know for the moment... lets start thinking about what our .NET namespace is.

System.DirectoryServices

The key class that we'll be using as we go through these blog posts is the DirectoryEntry class. Every object within the AD repository is represented as a DirectoryEntry. How does this work when AD stores everything from computer definitions to users to documents? Well, the DirectoryEntry class has a property called 'SchemaClassName' which defines what sort of entry it is. This SchemaClassName will map to an object that you can look up in the AD schema. So the DirectoryEntry is really just a generic container for various types of data in AD.

A DirectoryEntry has lots of properties and methods, some of which we'll cover as we go, but for now suffice it to say that if you want to write some samples and tests then you can get a DirectoryEntry with literally one line of code using its constuctor, like so (replacing the options in square brackets with their values:

DirectoryEntry oEntry = new DirectoryEntry([LDAP path], [username], [password]);

e.g.

DirectoryEntry oEntry = new DirectoryEntry("LDAP://localhost/CN=SomeContainer,OU=mydomain,OU=com", "administrator", "password99");

Remember to add the 'using' statement to DirectoryServices and include any relevant DLL references when playing with examples.

Next post - Part 2 - How to read, filter and understand the ACL for a DirectoryEntry object


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

I thought I'd just blog a little interesting thing I came across whilst fitting a new CPU to my PC yesterday. I've got an Asus motherboard and wanted to swap my Socket-A Sempron for an Athlon XP (yes, old skool I know). When I fitted the new CPU and hit the power switch, the system fired up for 3 seconds and then shut down. Thinking that maybe I'd got a fried chip - it was 2nd hand after all - I swapped the old one back in. Same thing. Started, then halted.

Worried that I'd fried the motherboard somehow, I did the whole techie thing... stripped down to a bare board and tried another PSU. Same thing. Wouldn't boot. I was about to sling the board in the bin and go buy another when I had a thought. Asus have something called CPU Overheating Protection (COP) on many of their boards, including this one. I'd been testing with the heatsink+fan connected to the motherboard but not fitted on the CPU itself. I fitted them, and hey presto it all worked fine :)

Turns out that the Asus COP somehow detects the presence of the heatsink fitted on top of the CPU. I'm not sure how - maybe pressure? Maybe some kind of induction test inside the CPU core or Motherboard? If anyone can tell me I'd appreciate it - I'm curious.

So I should really have a bit more faith in Asus motherboards, and just be impressed at the clever little things that stop me doing stupid stuff :)


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

So you might have an MCMS site that has been kicking around for a few years... lots of content has been deleted. Site is getting sluggish. You go into Site Manager to empty the deleted items and everything you click takes 1 minute to respond. Emptying the deleted items is useless - it just crashes. The reason for this, I think, is that it retrieves XML from MCMS and when trying to manipulate the huge XML files in the XML DOM it just falls over. The DOM does have a maximum logical size of XML it can manipulate before performance degrades quickly.

What to do?

Well it's a nasty one as there is no API call to empty the deleted items. Microsoft - who I've logged support calls with about this - have taken over a year to come back with nothing. Basically, you're on your own.

Now the MCMS schema is a bit nasty and if you do anything to the MCMS database directly you may invalidate your Microsoft support. Having said that, it's the only way I could find to empty the deleted items without Site Manager.

Here is the script I wrote and used. Use it ENTIRELY at your own risk. I suggest you figure out what it's doing before you run it. Good luck!

-- properties (historical)

print 'deleting properties (historical)'

delete from NodeProperty where NodeId in
(select Id from Node where ArchiveSourceGUID in
(select FollowGUID from Node where DeletedWhen is not null and Type=16))

-- resources (historical)

print 'deleting resources (historical)'

delete from NodeResource where NodeId in
(select Id from Node where ArchiveSourceGUID in
(select FollowGUID from Node where DeletedWhen is not null and Type=16))

-- roles (historical)

print 'deleting roles (historical)'

delete from NodeRole where NodeId in
(select Id from Node where ArchiveSourceGUID in
(select FollowGUID from Node where DeletedWhen is not null and Type=16))

-- placeholder content (historical)

print 'deleting placeholder content (historical)'

delete from NodePlaceholderContent where NodeId in
(select Id from Node where ArchiveSourceGUID in
(select FollowGUID from Node where DeletedWhen is not null and Type=16))

-- placeholders (historical)

print 'deleting placeholders (historical)'

delete from NodePlaceholder where NodeId in
(select Id from Node where ArchiveSourceGUID in
(select FollowGUID from Node where DeletedWhen is not null and Type=16))

-- postings (historical)

print 'deleting postings (historical)'

delete from Node where ArchiveSourceGUID in
(select FollowGUID from Node where DeletedWhen is not null and Type=16)

-- properties (current)

print 'deleting properties (current)'

delete from NodeProperty where NodeId in
(select Id from Node where NodeGUID in
(select FollowGUID from Node where DeletedWhen is not null and Type=16))

-- resources (current)

print 'deleting resources (current)'

delete from NodeResource where NodeId in
(select Id from Node where NodeGUID in
(select FollowGUID from Node where DeletedWhen is not null and Type=16))

-- roles (current)

print 'deleting roles (current)'

delete from NodeRole where NodeId in
(select Id from Node where NodeGUID in
(select FollowGUID from Node where DeletedWhen is not null and Type=16))

-- placeholder content (current)

print 'deleting placeholder content (current)'

delete from NodePlaceholderContent where NodeId in
(select Id from Node where NodeGUID in
(select FollowGUID from Node where DeletedWhen is not null and Type=16))

-- placeholders (current)

print 'deleting placeholders (current)'

delete from NodePlaceholder where NodeId in
(select Id from Node where NodeGUID in
(select FollowGUID from Node where DeletedWhen is not null and Type=16))

-- postings (current)

print 'deleting postings (current)'

delete from Node where NodeGUID in
(select FollowGUID from Node where DeletedWhen is not null and Type=16)

-- pages

print 'deleting pages'

delete from Node where DeletedWhen is not null and Type=16

-- channel properties

print 'deleting channel properties'

delete from NodeProperty where NodeId in
(select Id from Node where DeletedWhen is not null and Type=4)

-- channel roles

print 'deleting channel roles'

delete from NodeRole where NodeId in
(select Id from Node where DeletedWhen is not null and Type=4)

-- channels

print 'deleting channels'

delete from Node where DeletedWhen is not null and Type=4

 
 


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

Imagine the MOSS search without the surrounding SharePoint gubbins. Imagine it free.

http://www.microsoft.com/enterprisesearch/serverproducts/searchserverexpress/default.aspx

 


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist