Thursday, February 28, 2013

Setting Up WCF with Windows Authentication

Introduction
Configuring WCF to do anything for the first time is very time consuming, frustrating and just generally a hassle. My coworker and I mucked through this wonderful WCF configuration experience last night and now I am going to share our tale of torment and learning.

Useless and Unhelpful Errors
If you have been having trouble setting this up, then chances are you have seen one of two or both of these page errors that are served to you on a steaming hot yellow screen of death:

Useless Error 1
Security settings for this service require Windows Authentication but it is not enabled for the IIS application that hosts this service.

Useless Error 2
Security settings for this service require 'Anonymous' Authentication but it is not enabled for the IIS application that hosts this service.

Why is this happening?
Well you may have guessed it already, you didn't configure something correctly. The short answer is that you have to enable Anonymous and Windows Authentication for your application in IIS for this to begin to work. However there is more configuration that needs to be done which is described further down below.

The Not So Obvious Fix to the Stupid Problem
I can't promise that what worked for me and my needs will work for you and your needs. I can however promise that this will work if the instructions are followed exactly. There are a lot of moving parts here and unfortunately most of what is happening here is behind the scenes magic (Windows Authentication, Kerberos, NTLM, Negotiate, etc...). Therefore I have to assume that you have IIS7 setup correctly and that you have successfully enabled Windows Authentication for other applications, just not WCF yet.

Configuring IIS
  1. Open IIS manager > Click on your Application > Make sure Features View is selected
  2. In the IIS section of Features View double click on "Authentication" (Figure 1)
  3. Enable Anonymous Authentication (Figure 2)
  4. Enable Windows Authentication (Figure 2)
    1. On the left hand side of the window look at the Actions section.
    2. Click on "Advanced Settings..." make sure Extended Protection is "OFF" and check mark the check box labeled "Enable Kernel-mode authentication" (checked="True")**. (Figure 3)
    3. Click on "Providers..." and make sure the Enabled Providers are only "Negotiate" and "NTLM", do not add anything else here. (Figure 4)
  5. Restart IIS using CMD > iisreset /restart
  6. You are finished with configuring IIS, time to configure WCF.
**Kernel-mode Authentication
You want this check marked especially if you are using a custom UN/PW for your Application Pool Identity.

Figure 1
Figure 2
Figure 3
Figure 4
Configuring WCF
As usual, 99% of the configuration for WCF takes place in the web.config/app.config. In this particular case it is a web.config because I am assuming you are using a Web Application as your WCF host. Therefore there are two main pieces at work here:
  • Enabling Windows Authentication for the Web Application
  • Enabling Windows Authentication for the WCF Service
These two points of interest are actually mutually exclusive. Just because you have Windows Authentication enabled for your Web Application doesn't mean I can't still hit your WCF Service. Therefore it must be enabled for both.

Configuring Windows Authentication for the Web Application
Put the following authentication section into the <system.web> section. In this example I am using Active Directory group names to filter out who has access. You can use the user names directly if you want.
<authentication mode="Windows" />
 <authorization>
  <allow roles="ADGroup1,ADGroup2" />
  <deny users="*" />
 </authorization>
<identity impersonate="false" />

Configuring Windows Authentication for the WCF Service
Put the following secruity section into your <binding> section.
<security mode="TransportCredentialOnly">
 <transport clientCredentialType="Windows" />
</security> 

Full Web.Config Example For Reference
<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0">
    </compilation>
    <httpRuntime
      executionTimeout="600"
      maxRequestLength="16384" />
    <authentication mode="Windows" />
    <authorization>
      <allow roles="ADGroup1,ADGroup2" />
      <deny users="*" />
    </authorization>
    <identity impersonate="false" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="AuthWCFService.AuthService" behaviorConfiguration="AuthWCFService.AuthServiceBehavior">
        <endpoint address=""
                  binding="basicHttpBinding"
                  bindingConfiguration="basicHttpBindingConfig"
                  contract="AuthWCFService.IAuthService" />
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      </service>
    </services>
    <bindings>
      <basicHttpBinding>
        <binding name="basicHttpBindingConfig"
                 openTimeout="00:10:00"
                 receiveTimeout="00:10:00"
                 sendTimeout="00:10:00"
                 closeTimeout="00:10:00"
                 maxReceivedMessageSize="2147483647"
                 maxBufferSize="2147483647"
                 maxBufferPoolSize="2147483647"
                 messageEncoding="Text"
                 transferMode="Buffered" >
          <security mode="TransportCredentialOnly">
            <transport clientCredentialType="Windows"/>
          </security>
          <readerQuotas maxDepth="32"
                        maxStringContentLength="20000000"
                        maxArrayLength="20000000"
                        maxBytesPerRead="4096"
                        maxNameTableCharCount="16384" />
        </binding>
      </basicHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="AuthWCFService.AuthServiceBehavior">
          <!-- To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="True" />
          <!-- To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
</configuration>

Conclusion
I think Microsoft needs to make this easier to figure out... Why the configuration has to be such a mystery that requires countless hours of plug and chug is beyond me. I have tried reading the white papers that are provided, but they are as useful as college books or technical manuals when you aren't really sure where to begin. As I like to call it FLUFF, lots and lots of FLUFF - no practicality - just lots of meaningless words strung into sentences.

Sources



Thursday, February 14, 2013

Things to know before buying the Nexus 4

Introduction
I bought the Nexus 4 recently and I have to say I am quite satisfied with my purchase, however I was pretty disappointed and borderline pissed off about a few things that aren't apparent until after you purchase the phone. However I would like to say that this phone is a MAJOR improvement over my old T-Mobile myTouch Q LGC800 which is a sad excuse for a phone when put next to the Nexus 4 which is also produced by LG. I am going to blame T-Mobile for the LGC800's initial total lack of performance, but after rooting the phone and freezing all of the T-Mobile bloatware the phone still had some serious issues with Bluetooth and other unexplained problems that made the phone an overall drag to use. I am mentioning this because unfortunately some of those problems have carried over to the Nexus 4 it seems. Well off to the list.

LG Product Page
The Nexus 4 is also known as the LGE960. You can view it's page here. You can download the user manual here as it does not ship with the phone; which is fine because it is something you would rarely look at anyhow. This phone is rather intuitive to use.

The Purchase Price is a Lie
Google Play advertises the phone for either 300 or 350 dollars. The price difference being that the 8GB phone is cheaper than the 16GB version. Well yes it is clearly cheaper by 50 dollars but since this phone does not allow me to use an SD card, I guess I am getting the 16GB version. Time to purchase the phone...

$350 my ass
Cadillac Shipping
Okay so I know there was going to be shipping costs involved and I know the $350 was just the cost of the phone, but holy crap. I feel cheated here. You are NOT given a choice on a cheaper form of shipping, I was willing to wait another 2 weeks for free shipping or at least cheaper shipping. I am going to assume the shipping included product insurance.

$13.99 Two Day Shipping -> 4% added cost

County Sales Taxes
All I have to say is... wow. I guess they had to add taxes, but holy shit.

$24.43 Taxes -> 7% added to cost (I live in Miami-Dade County taxes are 7%)

Total Overhead from Google
$38.42 - Yay I just paid 9.91% more than I felt like paying. Almost 10% higher than advertised!

Sub Total: $387.42

New Micro SIM Card
I had a regular sized SIM Card, so I had to purchase a new Micro SIM Card because apparently T-Mobile no longer changes them for free like they used to. I have been told you can cut the older regular sized ones down to size, but I don't trust that completely. I'm not that adventurous, I just need things to work right now - too busy for experiments.

$21.20 -> 5.47% more on top of the current total

New Belt Case for Phone
This phone is flat, skinny (small height), but wide in both length and width. I needed a new case with belt loops so I could keep my phone on my person. I am not one of those people who can just slip it into their pocket and not feel that it is going to be shattered in my pocket or left somewhere because I forgot to put it back. I bought this case: Krusell Hector 3XL Premium Leather Universal Case with Beltloops for SmartPhones with 4.3 / 4.8-inch Screen-Black.

$19.99 -> 5.16% more on top of current total

Total Overhead from Unexpected Costs
$41.19 - Paid another 10.63% on top of the $387.42, this is not Google's fault - but T-Mobile not switching out my SIM for free is not very fair and I cannot operate without having a pouch for my case so that is my preference.

Final Total: $428.61
$79.61 More than $349.00 -> 18.57% higher than expected

The Silver-lining
This was still cheaper than buying the phone anywhere else.

The Storage Space is a Lie
So I bought the phone for its 16GB and well I expected 16GB of free space. The phone has 16GB alright, but what they don't tell you is the OS has already used up 4GB of it. So you have 12GB of free space. Not the end of the world, but not very fair either.

Problems with the Phone
There are problems with Bluetooth and WiFi. I have had to restart my phone already several times because either Bluetooth stopped working or WiFi stopped working or both. There are reports about this all over the place and Google/LG has already started rolling out updates to fix the problem. I am tempted to root the phone and just use CyanogenMod. I am going to blame LG for this because these problems are far too reminiscent of what I had to deal with - with my old LGC800.

Minor Complaints
I don't care for the layout of the phone all that much. I don't like how there is a Google bar that is permanently affixed to the top of my screen. It cannot be removed. Adding widgets to your screen is a bit of a lengthy task since you have to look through your app drawer for them and scrolling through them is not quick. I am also concerned about the battery life of the phone, when it comes time to replace that battery what are we facing here? Can I do it myself?

Conclusion
This is by far the best phone I have owned so far. It is very fast, it does not lag which is my favorite part and it is very usable. The quality of this phone has offset the overall hit to the wallet which was a hard one, but I hope to hold on to this phone for as long as possible.

Get Authorized Roles from Web.Config

Introduction
To avoid hard coding the names of AD Groups, it is best to just pull them from a place where they already exist. They usually exist in the Authorization section of your web.config: configuration\system.web\authorization
So I started looking up how to do this and to my surpise it was more difficuilt than it had to be as usual when it comes to any xml file. Some solutions online just say to read in your web.config as an XmlDocument and I always think that is a last resort. If there is an API for it, use the API - don't reinvent the wheel as it is confusing for anyone that has to read your code later and you just wasted time rewriting something that existed already.

How to get the Authorization Roles from the Web.config
I don't have much to explain about this process other than it was hard to figure out, but now that is finished I don't want to have to think about it again. The code pretty much explains it all. Unfortunately the AuthorizationRuleCollection does not support any Linq operations, so this was uglier than I would have liked it to be. I am using a loop which I am not fond of, but I couldn't figure out a safer way to do this. The only good news is usually there are only a hand full of rules to loop through.

//Import the following
using System.Configuration;

//Get the web.config configuration for THIS web.config (hence the ~)
Configuration config = WebConfigurationManager.OpenWebConfiguration("~");

//Get the Authorization section which has the allowed roles in it (AD Groups)
AuthorizationSection auth = (AuthorizationSection)config.GetSection("system.web/authorization");

string[] arrRoles = new string[0];

//TODO: Find a better way to query the rules
//Loop through each rule
foreach(AuthorizationRule obj in auth.Rules)
{
 //Find the Allow Rule
 if (obj.Action == AuthorizationRuleAction.Allow)
 {
  //Resize the array
  arrRoles = new string[obj.Roles.Count];

  //Copy the roles from the StringCollection to an array of string
  obj.Roles.CopyTo(arrRoles, 0);

  //Exit loop after found
  break;
 }
}

Resources
http://stackoverflow.com/questions/213784/in-my-codebehind-class-how-do-i-retrieve-the-authorized-roles
http://msdn.microsoft.com/en-us/library/system.collections.specialized.stringcollection.copyto.aspx