# Tuesday, 13 February 2007

One obvious thing you might want to do with an authorization system is to get a list of all the operations a given user is granted access to.  This is particularly useful for building up UI elements such as navigation menus.  Nobody likes navigation links that result in Access Denied errors, so it's best to only include in your navigation hierarchy those pages a user can actually execute successfully. 

Here's an example of how to do this with ADAM and AzMan.  It's a little weird, since you have to switch back and forth between an operation's string name, and its integer ID, hence the lookup dictionaries. 

 

   1:  public string[] AvailableOperations(string userId)
   2:  {
   3:      IAuthenticationStore authenticate = AuthenticationStoreFactory.Create();
   4:      UserInfo userInfo = authenticate.LookupUser(userId);
   5:      IAzClientContext2 client = setupClientContext(userId, userInfo);
   6:   
   7:      try
   8:      {
   9:          Dictionary<string, int> opToId = 
  10:      new Dictionary<string, int>(azApp.Operations.Count);
  11:          Dictionary<int, string> idToOp =
  12:      new Dictionary<int, string>(azApp.Operations.Count);
  13:          foreach (IAzOperation op in azApp.Operations)
  14:          {
  15:              opToId.Add(op.Name, op.OperationID);
  16:              idToOp.Add(op.OperationID, op.Name);
  17:              Marshal.ReleaseComObject(op);
  18:          }
  19:   
  20:          List<string> availableOps = new List<string>();
  21:   
  22:          object[] scope = new object[] { (object)"" };
  23:   
  24:          object[] operations = new object[idToOp.Count];
  25:          int i = 0;
  26:          try
  27:          {
  28:              foreach (int opId in idToOp.Keys)
  29:              {
  30:                  operations[i] = (object)opId;
  31:                  i++;
  32:              }
  33:          }
  34:          catch (COMException ex)
  35:          {
  36:              throw new InvalidOperationException("Failed", ex);
  37:          }
  38:          object[] results = (object[])client.AccessCheck("User authentication", scope,
  39:               operations, null, null, null, null, null);
  40:   
  41:          for (int j = 0; j < results.Length; j++)
  42:          {
  43:              if ((int)results[j] == 0)
  44:              {
  45:                  availableOps.Add(idToOp[(int)operations[j]]);
  46:              }
  47:          }
  48:          return availableOps.ToArray();            
  49:      }
  50:      finally
  51:      {
  52:          Marshal.ReleaseComObject(client);
  53:      }
  54:   
  55:  }

I talked about the LookupUser and setupClientContext methods yesterday.  This method returns a handy list of all the operations that the given user can access.  In practice, you probably want to cache the results, although so far it seems to perform very well.  It might prove particularly valuable to cache the ID->Name and Name->ID lookup tables, as they aren't likely to change very often.

Tuesday, 13 February 2007 09:34:22 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, 12 February 2007

I'm working on an authentication and authorization solution using ADAM and AzMan, and starting to write the code to integrate the two.  For those who might be looking to do such a thing, here's an example.  This method takes two pieces of information, a user's ID (which in this case corresponds to a userPrincipalName in ADAM), and the name of the operation to check access to...

 

   1:  public bool CheckAccess(string operation, string userId)
   2:  {
   3:      log.Verbose("Starting CheckAccess");
   4:      bool result = false;
   5:      IAuthenticationStore authenticate = AuthenticationStoreFactory.Create();
   6:      UserInfo userInfo = authenticate.LookupUser(userId);
   7:      log.Verbose("LookupUser {0} succeeded", userId);
   8:      IAzClientContext2 client = setupClientContext(userId, userInfo);
   9:   
  10:      object[] scope = new object[] {(object)""};
  11:   
  12:      object[] operations = null;
  13:      try
  14:      {
  15:          IAzOperation op = azApp.OpenOperation(operation, null);
  16:          operations = new object[] { (Int32)op.OperationID };
  17:          Marshal.ReleaseComObject(op);
  18:      }
  19:      catch (COMException ex)
  20:      {
  21:          log.Error("No such operation: {0}", operation);
  22:          if (ex.ErrorCode == AzManConstants.INT_NO_SUCH_OBJECT)
  23:              throw new UnknownOperationException(operation);
  24:          else
  25:              throw;
  26:      }
  27:      object[] results = (object[])client.AccessCheck("User authentication", scope, 
  28:                      operations, null, null, null, null, null);
  29:      if ((int)results[0] == 0)
  30:      {
  31:          result = true;
  32:          log.Verbose("Access granted to {0} for user {1}", operation, userId);
  33:      }
  34:      else
  35:      {
  36:          log.Verbose("Access denied to {0} for user {1}", operation, userId);
  37:      }
  38:      Marshal.ReleaseComObject(client);
  39:      log.Verbose("Leaving CheckAccess");
  40:      return result;
  41:  }
 

There are a couple things here that need explaining.  The LookupUser method finds the specified user in ADAM by searching for the userPrincipalName using the DirectorySearcher class.  It returns a structure containing the user's SID, all of the SIDs for groups they belong to, and some other stuff specific to our application.  There's a great example of how to retrieve this info in the paper Developing Applications Using Windows Authorization Manager, available on MSDN. 

The other method of interest here is the setupClientContext method, which looks like this...

 

   1:  private IAzClientContext2 setupClientContext(string userId, UserInfo userInfo)
   2:  {
   3:      log.Verbose("Setting up client context");
   4:      IAzClientContext2 client = azApp.InitializeClientContext2(userId, null);
   5:      log.Verbose("User sid = {0}", userInfo.SecurityId);
   6:      object[] userSids = null;
   7:      if (userInfo.GroupIds != null)
   8:      {
   9:          userSids = new object[userInfo.GroupIds.Count + 1];
  10:          userSids[0] = userInfo.SecurityId;
  11:          int i = 1;
  12:          foreach (string sid in userInfo.GroupIds)
  13:          {
  14:              log.Verbose("Adding group sid {0}", sid);
  15:              userSids[i] = sid;
  16:              i++;
  17:          }
  18:      }
  19:      else
  20:      {
  21:          log.Verbose("No group IDs returned, using only user SID");
  22:          userSids = new object[1];
  23:          userSids[0] = (object)userInfo.SecurityId;
  24:      }
  25:      client.AddStringSids(userSids);
  26:      log.Verbose("Finished setting up client context");
  27:      return client;
  28:  }
 
 

This is the part that only works on Windows 2003 Server or Vista. On XP, the IAzClientContext2 interface is not available, and so you can't add the additional group SIDs to make this work.

The end result is that the user's role membership will be used by AzMan to determine whether or not the specified user can access the specified operation, and return a boolean value back to the caller.  Easy to use by the client, and as far as I can tell, pretty performant.

Monday, 12 February 2007 11:21:02 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, 31 January 2007

If you are working with AzMan at all, the best resource I've found so far that you'll want to check out is Developing Applications Using Windows Authorization Manager from MSDN.  It includes a very comprehensive set of info on working with AzMan using Windows, ADAM, or custom principals, on using AzMan from ASP.NET, and general info on authorization strategy and policy.

There are lots of code samples included in the document, including code for doing access checks using ADAM principals, and some for writing AzMan "BizRules" in managed code.

Good stuff.

AzMan | Work
Wednesday, 31 January 2007 15:05:23 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, 29 January 2007

Yesterday we had a party for the 10th Anniversary of the CERT program here in Hillsboro.  We had many more people than I had expected show up, to whom we provided free spiffs, literature on disaster preparedness, and lot's of nifty door prizes including tools, hats, etc.  Plus safety related games for the kids.  And there was cake.

A good time was had by all.  Here's to 10 more...

Monday, 29 January 2007 15:08:44 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, 25 January 2007

Not so good. 

Update:  I did get WMP to work, after installing the "Media Services" for 2003 server from the "Add Windows Components" dialog.  Still no go with Rhapsody.  Interestingly enough, QuickTime doesn't work either...

I've recently had to upgrade my development box to Windows 2003 Server, because some of the work I'm doing with ADAM and AzMan requires 2003 or Vista (and I'm not quite ready to go there, not is our IT department).  I can't get the Rhapsody client to install at all on 2003 server.  I suspect it has something to do with the fact that there's no Windows Media Player installed.  There doesn't seem to be a Windows Media Player for 2003 Server, which probably isn't unreasonable.  The Rhapsody client install fails while trying to set up some DRM stuff, which is what makes me suspect WMP. 

Despite that, the new web based Rhapsody client works just fine in FireFox, so I'll have to limp along with that once again.

Sigh.

Thursday, 25 January 2007 10:04:20 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [6]  | 
# Wednesday, 24 January 2007

I'll post some code later on, but I wanted to make some quick points about integrating ADAM and AzMan.  I'm in the midst of building an authentication/authorization system using the two technologies together, and have some across some stumbling blocks.  There's not much documentation, particularly around AzMan, and the COM interfaces for AzMan can be a bit cumbersome.

  • Storing users in ADAM and authorizing them using ADAM requires Windows 2003 Server or Vista.  There's no decent way to make this work on Windows XP.  The necessary AzMan interface, IAzClientContext2, doesn't exist on XP.  It's required for using a collection of user and group SIDs from ADAM to do access checks against AzMan.  I'll post some code later...
    • IAzClientContext2 is also available on Vista, so Vista is also a viable dev platform.
  • There are some confusing interactions between the AzMan UI and the programmatic API.  If you create a Role in the AzMan UI, but don't create a RoleAssignment, the programmatic call to IAzApplication2.OpenRole will fail.  If you create the role assignment, but don't actually assign any users or groups to it, OpenRole succeeds.  Conversely, if you call the programmatic IAzApplication2.CreateRole method and assign operations and users to the role in code, the RoleAssignment shows up in the UI, but not the Role itself. 
  • If you assign an ADAM user to be a member of an AzMan group, it won't show up in the AzMan UI, but if you assign them directly to a Role, the ADAM user's SID will show up (as "unknown SID") under the RoleAssignment.  Either way, the call to AccessCheck works correctly.
  • You must pass the complete list of group SIDs from ADAM, but fetching the user's "tokenGroups" property.  Don't use "memberOf" because it doesn't take into account groups which belong to other groups.

More detail to come...

Wednesday, 24 January 2007 10:08:12 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [2]  | 
# Thursday, 04 January 2007

I've read a bunch of these over the last few weeks, and thought perhaps I could duck, but Scott tagged me today, so I guess I should come clean.  Most of these arne't exactly secret, but you may not have heard me hold forth on them before :-)

  • My degree (a B.A.) is in East Asian Studies.  I spent my 4 years studying up on Buddhism, Japanese Art, and Chinese Communism.  My work study job was retrieving term papers off of floppies that had beer spilled on them, one precious sector at a time.  The first Computer Science class I ever took was at PCC after working at Intel for several years.  C++ I think it was.  Or possibly data structures.  I dropped out of a class on assembler, 'cause it was boring. 
  • I love trashy food.  I was raised on 70's hippy vegetarian food, and to this day I have a deep and abiding love of chicken fried steak, chipped beef on toast, and fried SPAM. 
  • I have a "simian crease" on my right hand.  Most people have two or three lines running horizontally across their palm (heart line, life line, etc).  I only have one the runs all the way across.  Common among chimpanzees and people with Down syndrome, less so among others.
  • On weekends, I tend to dress like a Viking.  My whole family are long-term participants in the Society for Creative Anochronism.  I used to dress up in armor and hit people with (actually mostly be hit with) wooden swords.  I gave that part up since I'm not 18 anymore, and it hurts.
  • My very first job was as an apprentice house painter.  I mostly dug ditches and drove ladders around Seattle for $6/hr.  I started the job by lowering the bottom of the boss' dirt floored basement by about 6 feet.  8 hour days of digging dirt into 5 gallon buckets, and hauling them two at a time up a flight of stairs.  I gained about 10 pounds of pure biceps. 

Not exactly shocking revalations, I admit.  Not compared to Scott's fashion obsession anyway.  I knew he was a snappy dresser, but...

I can't think of too many people I know have haven't already been tagged, so I'm not sure I'll make 5.  I nominate Jason, Stuart Celarier, John Batdorf,  Jeff B., and Don Smith.

Thursday, 04 January 2007 15:04:31 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, 18 December 2006

Late last week, Rhapsody finally released a new desktop client that doesn't crash in the presence of IE7.  Hurray!  I've been gimping along with the web-based client, which is cool, but not nearly as full-featured as the desktop version.  I've been running it for several days now, and not one crash, so I'm hopeful at this point.  Just in time to listen to all that Christmas music that I'd never shell out to buy full time...

Update: I may have spoken too soon.  It works fine on my desktop at work, but crashes constantly on my laptop at home.  Sigh.

Monday, 18 December 2006 12:41:57 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 

Since time immemorial (or since Marconi, anyhow) those wishing to be licensed as Radio Amateurs in the US have had to pass a Morse code test.  Relatively recently, there has been an entry level license class (Technician) that doesn't require passing the code test, but which (therefore) comes with no privileges on the HF bands (for long-distance communications). 

The code requirement has long been a hotly contested issue among amateurs.  Many have maintained that learning Morse code meant that you were "serious" about amateur radio, and would therefore be a skilled and considerate radio operator.  The problem is, since so few people actually use Morse code on the radio anymore, it has become (in my opinion, and that of many others) an artificial hoop that had to be jumped through before you could get into the high priesthood of amateur radio.  Most of the General or Extra class licensees I've talked to have never ditted or dahed once since passing the test. 

What this means for me personally is that I can finally hope to upgrade to a General class license.  I've studied all the material, and am pretty sure that I could pass the exam, but given the way the rest of my life works, I've been unable (or unwilling) to devote the time it would take to learn Morse code, so I've never taken the test. 

Of course, if I did pass the test, I'd want to get an HF-capable radio, but that's a whole different problem. :-)

CERT | Radio
Monday, 18 December 2006 12:38:55 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 

I spent about a day and a half this week trying to get a CC.NET server working with a complex combination of MSBuild (running the build script) TypeMock (the solution we're using for "mock objects") and NCover (for code coverage analysis) that proved tricky to get right.

In a typical build script, you'd create a task to run your unit tests that depends on your compile task.  And you might want to run a "clean" task first so you know you're starting from ground zero. 

Bringing a profiler into the mix changes the equation a bit.  Or in this case, two profilers that have to play nice together.  A "profiler" in this case means something that registers itself as a .NET profiler, meaning that it uses the underlying COM API in the CLR to get under the covers of the runtime.  This was originally envisioned as enabling profilers that track things like performance or memory usage that have to run outside the context of the CLR.  NCover uses this capability to do code coverage analysis, so that we can get reports of which lines of code in our platform are not being touched by our unit tests.

TypeMock is also a profiler, which causes some interesting interaction.  TypeMock uses the profiler API to insert itself between calling code and the ultimate target of that call in order to "mock" or pretend to be the target.  We use this to reduce the dependencies that our unit test code requires.  Rather than installing SQL Server on our build box, we can use TypeMock to "mock" the calls to the database, and have them return the results we expect without actually calling SQL. 

So...the problem all this interaction led to re: my build was that in order to make everything work, the profiler(s) have to be active before any of the assemblies you want to test/profile are loaded.  I had my UnitTest target set up like this:

 

<Target Name="UnitTestCoverage" DependsOnTargets="Compile;RemoveTestResults">

 

    <CreateItem Include="$(BuildDir)\\**\*.MBUnit.dll" >

      <Output

              TaskParameter="Include"

              ItemName="TargetFiles"/>

    </CreateItem>

 

    <TypeMockStart Link="NCover" />

 

    <Exec Command="&quot;$(NCoverConsole)&quot; $(MBUnitConsole) @(TargetFiles->'%(FullPath)', ' ') /rt:xml /rf:$(TestResultsDir) /fc:unit //l $(TestResultsDir)\NCover.log //x $(TestResultsDir)\NCoverage.xml //a $(AssembliesToProfile)" ContinueOnError="true"/>

 

    <TypeMockStop/>

    ...

</Target>

with the dependencies set to that the compile and "RemoveTestResults" targets would be evaluated first.  Unfortunately, this caused the whole build to hang for upwards of an hour when this target started, but after the compile and "remove" targets had run.  I'm theorizing (but haven't confirmed) that is is because the compiler loads the assemblies in question during the build process, and they don't get unloaded by the time we get to starting TypeMock.  That apparently means a whole bunch of overhead to attach the profilers (or something).  What finally ended up working was moving the compile target inside the bounds of the TypeMock activation, using CallTarget instead of the DependsOnTargets attribute.

 

    <TypeMockStart Link="NCover" />

    <CallTarget Targets="CleanCompile;RemoveTestResults"/>

 

    <Exec Command="&quot;$(NCoverConsole)&quot; $(MBUnitConsole) @(TargetFiles->'%(FullPath)', ' ') /rt:xml /rf:$(TestResultsDir) /fc:unit //l $(TestResultsDir)\NCover.log //x $(TestResultsDir)\NCoverage.xml //a $(AssembliesToProfile)" ContinueOnError="true"/>

 

    <TypeMockStop/>

This works just fine, and doesn't cause the 1 hour delay, which makes things much easier. :-)
Monday, 18 December 2006 12:27:20 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |