Results tagged “Coffee Breaks”

Here's one that ate up way too much of my time. Imagine this scenario: You're trying to use ADFS to transform a security token from a foreign domain into the one that your app is in. To do this, you have ADFS setup as an active RP-STS and you present it with a SAML assertion that you got from the IP-STS in the other domain. This means you have created a claims provider and relying party trust in ADFS. Now, imagine you can get the assertion from the IP-STS just fine, but you always get this error in ADFS's trace log when you send it over to it:

Source : Microsoft.IdentityModel
EventId : 1
Data :
<TraceRecord xmlns="http://schemas.microsoft.com/2009/10/IdentityModel/TraceRecord" Severity="Warning"><Description>RequestFailed: TrustNamespace=http://docs.oasis-open.org/ws-sx/ws-trust/200512, Action=http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue, Exception=Microsoft.IdentityModel.SecurityTokenService.RequestFailedException: ID4007: The symmetric key inside the requested security token must be encrypted. To fix this, either override the SecurityTokenService.GetScope() method to assign appropriate value to Scope.EncryptingCredentials or set Scope.SymmetricKeyEncryptionRequired to false.
   at Microsoft.IdentityModel.Threading.AsyncResult.End(IAsyncResult result)
   at Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceContract.ProcessCoreAsyncResult.End(IAsyncResult ar)
   at Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceContract.EndProcessCore(IAsyncResult ar, String requestAction, String responseAction, String trustNamespace)</Description><AppDomain>Microsoft.IdentityServer.ServiceHost.exe</AppDomain></TraceRecord>

ProcessId : 3156
ThreadId : 11
When I read that, I thought it meant that the symmetric key sent in my RST to ADFS wasn't encrypted. I spent hours examining HTTP traces of my requests, pulling out SHA-1 thumbprints from KeyIdentifiers in SubjectConfirmation elements, decoding/encoding them, and confirming that my IP-STS had encrypted the symmetric key w/ ADFS's encryption cert. I almost lost my mind till Yang Yu pointed out to me that the message wasn't about the symmetric key presented to ADFS; it meant that ADFS wouldn't send back a different symmetric key to my app because it couldn't encrypt the payload of the RSTR. The fix was simple: In the relying party trust, add an encryption cert.

OMG, that took like a 100 coffee breaks to figure out :(

My friend and former coworker, Cassiano Durand, went to Code Camp Portland last month and told me about a session he attended where the speaker, Steve Evans, mentioned a new directory-related API that was introduced in .NET 3.5. After reviewing the slides and code from the presentation, my colleague and I started using this new API this week. We got snared by a serious gotcha that cost us our coffee breaks.

   

I'll cut to the chase: If you create a user with ADSI Edit, you have to set the object's msDS-UserAccountDisabled property to false and userPrincipalName to match the name of the new account. If you do not do this, the ValidateCredentials method on PrincipalContext objects will always inexplicably return false. To see what I mean, perform the following steps and run the code below.

 

  1. Start by opening ADSI Edit and connecting to an AD LDS instance using settings such as these:

       

       

  2. Once connected, drill down into the OU where you want to create the user objects.

       

       

  3. Create a new object of type user and specify a name.

       

       

       

  4. After finishing the wizard, reset the new user's password via the Reset Password option in the object's context menu.

       

    OK. This is the important bit. Stop skimming and read the following!

       

  5. Open the user object's Properties dialog box.
  6. In this windows, scroll down to msDS-UserAccountDisabled and double click it. In the Boolean Attribute Editor, set the value to False and click OK.

       

       

  7. Scroll down to userPrincipalName and double click it. In the resulting String Attribute Editor dialog box, type the name that you used in step 3 above.

       

       

If you don't perform the last two steps, the new .NET 3.5 System.DirectoryServices.AccountManagement API will not work. Every time you call the ValidateCredentials method on a PrincipalContext object, it will return false. This method seems to check against the User Principal Name (UPN) rather than the name entered when creating the account in ADSI Edit. To see this in action, you can create a simple console application with the following code:

static void Main()

{

    var host = "localhost";

    var port = 50000;

    var userName = "test1";

    var password = "Password1";

    var machineName = string.Format("{0}:{1}", host, port);

    var container = "OU=Users,C=Tenant";

 

    var context = new PrincipalContext(ContextType.ApplicationDirectory, machineName, container);

 

    var authenticated = context.ValidateCredentials(userName, password, ContextOptions.SimpleBind);

 

    if (!authenticated)

    {

        authenticated = context.ValidateCredentials(userName, password, ContextOptions.Negotiate);

 

        if (!authenticated)

            throw new AuthenticationException();

    }

}

Try running it with a user that hasn't had these properties set, an account that has just one of them set, and one that has both the msDS-UserAccountDisabled and userPrincipalName properties updated.  You'll see that creating a user account by simply following the steps on the GUI's wizard will result in an inoperable user account.

If only we had the time back that we wasted figuring this out.  We all could have enjoyed a nice coffee.  As it was, however, we had to work right through our breaks.  Very lame :-(

Lately, I've been doing a lot of work with WS-Trust, WS-Security, WS-Federation, and Information Cards using the Geneva framework and Cardspace Geneva.  It's been really fun and challenging.  Yesterday, however, I ran into a weird issue with the first beta that cost me and three other team members our coffee breaks.

We needed to create a passive STS that allowed users to authenticate using multiple types of credentials.  As our starting point, we used the "Web Application with Multiple Sign In Methods" sample that is included with the Geneva framework SDK, but altered it to use passive redirect.  To do this, we changed the Web.config of the Relying Party (RP) to include the FormsAuthenticationModule (FAM), removed the superfluous ASP.NET Web control from the RP's Web form, and a few other minor tweaks.  (See the claims-aware Web application getting started sample for a complete example of an RP that uses passive redirect.)

After being sent to the STS, the user was presented with a page where they could select their authentication type -- Web forms or info card -- a switchboard if you will.  Each supported type had a button with an event handler that would redirect the user (again) to another page that dealt only with that type of authentication.  The query string was also tacked on before shipping them off. 

In the page that handled Web forms authentication, there was an event handler that fired after logging in.  It redirected the user (dizzying isn't it?) to another page that contained a FederatedPassiveTokenService ASP.NET Web control.  This control is used to surface the custom STS and should do the following:

  1. Detect the presence of a cookie with a certain name and value and,
  2. Redirects the caller back to the RP with the necessary RSTR.
The problem was that it wasn't doing that.  For some reason the cookie wasn't being created, the control didn't find it, and silently did nothing.  The configuration of the IIS application prevented unauthorized access, and sent the user back to the login page.  After submitting the user name and password again, the event handler fired, authentication was successful, and the caller was once again redirected to the page with the FederatedPassiveTokenService control on it.  The second time, however, an error page was displayed saying that access was denied due to the server configuration (error number 401.2).  Of course, the user wasn't allowed to see anything, but they should not have needed to; they should have been sent back to the RP with the cookie.

There in lies the rub.  The cookie wasn't being created.  If it was, the custom STS would have seen it and used the info in the query string to send the authenticated caller on his way.  The workaround: force the cookie to be created explicitly in the event handler of the login form rather than relying on the framework to do it.  This was achieved using this code:

    3 protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)

    4     {

    5         if (FormsAuthentication.Authenticate(Login1.UserName, Login1.Password))

    6         {

    7             string redirectUrl = FormsAuthentication.GetRedirectUrl(Login1.UserName, false);

    8             HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName,

    9                 FormsAuthentication.Encrypt(new FormsAuthenticationTicket(Login1.UserName, false, 20)));

   10 

   11             Response.Cookies.Add(cookie);

   12             Response.Redirect(redirectUrl + "?" + Request.QueryString);

   13         }

   14     }


Here's the weird thing.  The cookie actually was created sometimes without this explicit code.  We only found the problem after deleting the cookie, so that we could work on logging in using Carspace.  After some tweaking, we got it to create the cookie automatically again.  Then we deleted the cookie, and, as before, subsequent authentication attempts resulted in no cookie and an error.  It wasn't until we used the code above to force the cookie creation that it started to work consistently. The only reason that we could find for this inconsistency was the beta nature of the framework.  This oddity cost four of us our coffee breaks trying to diagnose the problem and work around it.  That's the price you pay for living on the bleeding edge I guess.

A couple of weeks ago, I blogged about a problem I was having that took more time to solve then expected; it was a task that I thought would be fast but turned out not to be. I jokingly described it as the sort that costs you time you would have otherwise spent on a coffee break. Well, unfortunately, I ran into another such dilemma this week, so I thought I would collect the descriptions of these time sinks into an ongoing series which I'll call Coffee Breaks. In it, I'll present the time consuming problems that I bump into, and how I solved them. To kick it off, I give you the second in this new series:

This week, I needed to write a unit test that would isolate a LINQ to SQL DataContext, allowing me to call the methods that mapped my stored procedures and return known data instead of information in a database. This would allow me to test my data access logic without having to establish a connection to a back-end server. To separate my DataContext, I wanted to use TypeMock Isolator 5.1. This seemly simple problem ended up taking a while to solve because A) I'm new to TypeMock Isolator and wasn't familiar with its API, and B) because the SPROC wrappers returned an ISingleResult<T> which I couldn't simply replace with an array or something.

Learning a new library just takes time. While learning Typemock's and applying it to this problem, however, I ran into a couple of obstacles that protracted the effort and eventually cost me my coffee break (so to speak). The issues where these:

  • In order to substitute my known list of results for those returned by the SPROC wrapper method, I had to create a dynamic mock and figure out how to inject my data into it.
  • If I tried to swap in an array when GetEnumerable was called on the dynamic mock, I kept getting a StackOverflowException when executed inside Resharper's test runner.

To elaborate a bit, let me begin by showing the DataContext definition that was generated when I drug the stored procedure from the Server Explorer to the ORM designer:

    1 public partial class MyGoodDataContext : DataContext

    2 {

    3     [Function(Name = "dbo.MyGoodStoredProcedure")]

    4     public ISingleResult<MyGoodStoredProcedureResult> MyGoodStoredProcedure()

    5     {

    6         IExecuteResult result = this.ExecuteMethodCall(this,

    7             ((MethodInfo)(MethodInfo.GetCurrentMethod())));

    8         return ((ISingleResult<MyGoodStoredProcedureResult>)(result.ReturnValue));

    9     }

   10 }

Given this, I had to figure out a way to replace ISingleResult<T> with some object that contained my test data. The only concrete implementation of ISingleResult<T> in the .NET framework is internal, so I tried to create a fake one and replace the original one with my test double using TypeMock Isolator's SwapNextInstance method like this:

var fake = Isolate.Fake.Instance<ISingleResult<MyGoodStoredProcedureResult>>();

            Isolate.SwapNextInstance<ISingleResult<MyGoodStoredProcedureResult>>().With(fake);

This produced a run-time error telling me to use the MockObject method on the MockManager class to create a dynamic mock instead (perfect error message BTW!).

After some fiddling, I figured out this class's API, and the necessity to initialize it via attributes or by an explicate call to its Init method. Afterward, I was able to create a dynamic mock object of type IResultSet<T>. Now I had to somehow cause my canned info to be returned when this object was enumerated. To this end, I replaced the return value of the mock object's GetEnumerable method with my data like this:

results.ExpectAndReturn("GetEnumerator", expected.GetEnumerator(), 1);

At first, the variable "expected" was an anonymous array of MyGoodStoredProcedureResult objects. However, every time that I enumerated this collection in the code under test, a StackOverflowException was through and my test runner would crash. I replaced it with a object of type List, and it worked (bug I reckon).

With those issues resolved, I was able to fake my DataContext and isolate it from the database using this unit test:

    1 [Test, VerifyMocks, Isolated]

    2 public void Test()

    3 {

    4     // Arrange

    5     var dataContext = Isolate.Fake.Instance<MyGoodDataContext>(Members.ReturnRecursiveFakes);

    6     var results = MockManager.MockObject<ISingleResult<MyGoodStoredProcedureResult>>();

    7     var expected = GetExpectedResults();

    8 

    9     results.ExpectAndReturn("GetEnumerator", expected.GetEnumerator(), 1);

   10     Isolate.SwapNextInstance<MyGoodDataContext>().With(dataContext);

   11     Isolate.WhenCalled(() => dataContext.MyGoodStoredProcedure()).WillReturn(results.Object);

   12 

   13     // Act

   14     var actual = MyGoodRepository.MethodThatInTurnCallsMyGoodStoredProcedure();

   15 

   16     // Assert

   17     for (var i = 0; i < actual.Count; i++)

   18         Assert.That(actual[i], Is.EqualTo(expected[i]));

   19 }


Problem solved, but not before coffee time was over :-(

This week I need to do something relatively simple that should have taken no time but ended up taking the better part of a morning: I needed to write an XSLT template that transformed a node value using a regular expression. I learned that XSLT 2 supports regular expressions; however, there is no support for XSLT 2 in .NET 3.5. So, I added an extension function to my template that the XPath engine could use to apply the regular expression substitution. Using this custom function required me to qualify it with a namespace prefix. This introduced a problem because I was creating the template dynamically at run-time using LINQ to XML. This technology tries to hide all namespace-related tasks from you (which is helpful in some applications), so I struggled to find a way to add the namespace and the corresponding prefix to the XDocument that I was building up in memory. Eventually, I ended up with something along these lines:

    1 class Program

    2 {

    3     static void Main()

    4     {

    5         var outputXml = new StringBuilder();

    6         var inputXml = @"<r><phoneNumber>555-555-5555</phoneNumber><o>Foo</o></r>";          

    7         var transform = new XslCompiledTransform();

    8         var templates = GetTemplates();

    9         var stylesheet =                                                                  

   10             new XDocument(new XElement(xsltNs + "stylesheet",

   11                 new XAttribute("version", "1.0"),

   12                 new XAttribute(XNamespace.Xmlns + extNsPrefix, extNs),                      

   13                 templates));

   14 

   15         using (var reader = stylesheet.CreateReader())

   16             transform.Load(reader);

   17 

   18         using (var reader = XmlReader.Create(new StringReader(inputXml)))

   19         using (var writer = XmlWriter.Create(outputXml))

   20         {

   21             var arguments = new XsltArgumentList();

   22 

   23             arguments.AddExtensionObject(extNs.ToString(), new XPathExtensions());          

   24             transform.Transform(reader, arguments, writer);                                  

   25         }

   26 

   27         Trace.WriteLine(outputXml);

   28     }

   29 

   30     private static IEnumerable<XElement> GetTemplates()

   31     {

   32         var templates = new List<XElement>();

   33         var pattern = "\\d";

   34         var replacement = "X";

   35         var matchValue = "//phoneNumber/text()";

   36 

   37         templates.Add(XElement.Parse(

   38             @"<template match='@*|node()' xmlns='http://www.w3.org/1999/XSL/Transform'>

   39               <copy>

   40                 <apply-templates select='@*|node()'/>

   41               </copy>

   42             </template>"));

   43         templates.Add(

   44             new XElement(xsltNs + "template",

   45                 new XAttribute("match", matchValue),

   46                 new XElement(xsltNs + "value-of",

   47                     new XAttribute("select", string.Format("{0}:Replace(., '{1}', '{2}')",

   48                         extNsPrefix, pattern, replacement)))));

   49 

   50         return templates;

   51     }

   52 

   53     private class XPathExtensions

   54     {

   55         public static string Replace(string input, string pattern, string replacement)

   56         {

   57             return Regex.Replace(input, pattern, replacement);

   58         }

   59     }

   60 

   61     private static readonly XNamespace xsltNs = "http://www.w3.org/1999/XSL/Transform";

   62     private static readonly XNamespace extNs = "urn:foo";

   63     private const string extNsPrefix = "ext";

   64 }


This code isn't advanced, but there are a few details that can take a bit of time to figure out. So you're able to implement this sort of thing before your morning slips away and you miss your coffeebreak, I'll hightlight them here:



  • The XSLT stylesheet is created dynamically at run-time using LINQ to XML.

  • The subclass, XPathExtensions, has a public, static method called Replace that applies a regex to a given string and returns the result.

  • This class's method is made available to the XSLT engine by associating the namespace, urn:foo, with an instance of my XPathExtensions nested class and passed to the XslCompiledTransform object's Transform methods on lines 21 - 24.

  • The namespace and the prefix (ext) are explicitly added to the XSLT document that is created with LINQ to XML by adding the XAttribute on line 12.

  • This extension is called in the select statement on line 48 by prefixing the XPath function with ext and specifying the name of the public, static method that is in the XPathExtensions object that was passed into the engine via the XsltArgumentList.

(Of all these details, the one that took me the longest to figure out was how to explicitly add a namespace with a given prefix when using LINQ to XML.)

I hope this helps you solve your programming problems, and that you enjoy your coffee break!

1