Monday, February 14, 2011

Kerberos Security Checklists

Security ala Microsoft

Security – authentication and authorization – is a basic need of intranet applications and a great amount of infrastructure is geared toward enabling security features seamlessly.

Denizens of the planet Microsoft store their intranet user accounts and security groups in Active Directory. Then they configure their web applications for Windows Integrated Security, a.k.a. Single Sign On, whereby the user’s NT username – already authenticated when logging into the client machine – is passed to the web server along with the Http Request and used to authenticate and authorize the user for the application, by means of Kerberos authentication.

The Dreaded Double-Hop Error

One particularly nefarious error looks as follows:

Unauthorized (null)

or

Anonymous logon (null)

Causes include:

  • Bad server configuration
  • Bad network configuration (Service Principal Name - SPN; speak to your network admin)
  • Bad client browser configuration
  • Client login ticket has expired

Solution Checklists

Checklist: Server

  • Connection Pool of site runs under Network Service identity
  • Windows Authentication enabled for site
  • Impersonation enabled for site

Checklist: Client

  • Windows Integrated Authentication enabled (Tools > Internet Options > Advanced)
  • Check "Bypass proxy server for local addresses" (Tools > Internet Options > Connections > LAN Settings)
  • Allow "Automatic logon only in Intranet zone" (Tools > Internet Options > Connections > Security > Local Intranet > Custom Level)
  • Add intranet domain (*.mydomain.com) to list "web sites" (Tools > Internet Options > Connections > Security > Local Intranet > Sites > Advanced > Web Sites )

If the user has successfully accessed the site in the past, it may be a matter of refreshing the login token:

  • Close-reopen browser (IE7+)
  • Right click browser icon > Run as... > enter credentials (IE6)
  • When all else fails... REBOOT

Wednesday, February 9, 2011

Synchronous Ajax with Microsoft

Ajax Without the A

Microsoft Ajax makes it easy for us to call Web Service methods and code-behind Page Methods from our client JavaScript code. However, the process is always asynchronous (the A in Ajax) and requires the use of callback functions.
What about synchronous calls? Sometimes, you want to halt execution until the results arrive – for purposes of data validation, for instance.
The bad news: synchronous calls are not supported out-of-the-box.
The good news: you can work synchronously without having to work with XmlHttpRequest or switch to jQuery.

Synchronous Steps

Step 1
Add scripts to your page for the XmlHttpSyncExecutor and ExecuteSynchronously wrapper method provided thanks to Ricardo Peres.
Step 2
Make sure your Web Service has the right attributes:
[WebService]
    [System.Web.Script.Services.ScriptService(ResponseFormat = ResponseFormat.Json)]
    public class MyWebService: System.Web.Services.WebService
    {
        
        [WebMethod]
        [System.Web.Script.Services.ScriptMethod]
        public string MyWebMethod(int id)
        {
            return "test:" + id.ToString();
        }
    }
Step 3
Call the web service using the ExecuteSynchronously wrapper function in your client-side JavaScript:
var res = ExecuteSynchronously('<%=Request.ApplicationPath %>/WebService/MyWebService.asmx', 
                               'MyWebMethod',
                               { 'id': 100 });
alert(res.d);
Note two things:
  • Pass parameters as a JSON object (third parameter of the ExecuteSynchronously function)
  • The JSON result is stored in property d of the return value

Thursday, December 30, 2010

Integrated Windows Authentication and FireFox

Your manager decides to support FireFox in addition to IE for all intranet applications.

"That should be easy. You develop cross-browser anyway. Right?"
Well, yes and no.

Your Html, Css and JavaScript are only part of the picture.

Browser settings are another creature altogether, and when you're working with Kerberos authentication, browser settings are critical to:

  •  pass the users credentials without prompting him or her to sign in (single sign-on) and

  • allow impersonation and delegation of users credentials from the web server to the database server (double hop).



FireFox Settings - Cheat Sheet

There are 5 settings you need to change, all accessible when you type about:config in the address bar:

setting: network.negotiate-auth.delegation-uris

value: mySite.com,myotherSite.com

setting: network.negotiate-auth.trusted-uris

value: mySite.com,myotherSite.com

setting: network.automatic-ntlm-auth.trusted-uris

value: mySite.com,myotherSite.com

setting: network.automatic-ntlm-auth.allow-proxies

value:true

setting: network.negotiate-auth.allow-proxies

value: true

Piece of cake.

Now for the hard part: figuring out how to change the FireFox settings for your thousands of users...

Wednesday, October 6, 2010

Outlook 2010 Add-In Issue: Closing “Send Using Email” Window

Scenario:

Add-In for Outlook performs an action in the Application_ItemSend handler for outgoing email.

Add-In works for Outlook 2007, but in Outlook 2010 a strange side-effect occurs. When using the “Send Using Email” feature of Word 2010, the email sends but the modal email window stays open! The user has to close it manually.

More good news: Calling the Close(..) method of the inspector throws an error.

Solution:

There is no elegant solution; you have to resort to DLL Imports.

Add the DLL Imports for finding a window and sending it a message:

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool PostMessage(IntPtr hWnd, uint wMsg, IntPtr wParam, IntPtr lParam);
const int WM_CLOSE = 0x10;

Then, in call this in your code, making sure to clean up any COM object references that need to be released (but NOT the EmailItem reference):

void Application_ItemSend(object Item, ref bool Cancel)
{
    
    if (Item is Outlook.MailItem)
{ Outlook.MailItem mail = Item as Outlook.MailItem; Outlook.Inspector inspector = null; if (null != mail) { > try { inspector = mail.GetInspector; if (null != inspector) { //TODO: your add-in magic! } } finally { if (null != inspector) { IntPtr _inspHwnd = FindWindow("rctrl_renwnd32", inspector.Caption); bool retVal = PostMessage(_inspHwnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); Marshal.ReleaseComObject(inspector); } } } } }

 

Thanks to Ken Slovak and Andrei Smolin for their valuable input…

Monday, July 12, 2010

SSIS and WCF – The Devil in the Wsdl

I want to elaborate on a fix that wasn't so obvious to me...

The plan seemed simple enough. Current Application requires email reports to sent, some from the web GUI, others according to certain conditions and schedules. Instead of duplicating the report logic in multiple places, let's place it all in one WCF Service application (sending the reports offline via MSMQ) and then call the service from both the web GUI and an SSIS job that runs according to the conditions and schedules.

Not too much to ask, right?

Enter SSIS.

Web Service Task

SSIS offers a task for calling web services - you guessed it: the Web Service Task - which claims to support WCF too so long as the WCF uses Basic Http Binding.

So you add the Web Service Task, configure the Wsdl url and then...error:

Item has already been added. Key in dictionary: 'anyType' Key being added: 'anyType'

Error

Some friendly souls encountered this error before and offered sage advice here and here but the solution requires some spelling out.

The Wsdl needs to contain type definitions for the service methods and data contracts etc. These are split over a number of xsd files, all referenced in the Wsdl:

Wsdl file with Xsd references

Here’s the catch: Wcf generates Wsdl that creates circular references within the xsd files.

Here’s the content of Xsd2:

Xsd2

Note that Xsd2 refers to Xsd3 and Xsd1 even though they are already referenced in the Wsdl.

The result: SSIS in Visual Studio blindly builds a list of valid types based on these Xsd files and thus tries to add some types twice – and fails on validation.

This validation is usually a good thing, but flags Wcf-generated Wsdl content as a false positive.

The Solution

The way out of this mess?

  1. Look through the Xsd references in your Wsdl
  2. Peek at the Xsd files too (copy the urls to your browser of choice)
  3. Eliminate from the Wsdl file all references to Xsd files already referenced in earlier Xsd files.

In my case this meant deleting all the Xsd references from the Wsdl except for the first one.

[The irony, of course, is that I encountered a row of other issues – setting the Input and Output parameters for the Task, for starters – and decided to ditch the Web Service Task in favor of a Execute Script Task, which uses an old-fashioned web service reference in the script editor.

So much for that…]

Monday, May 24, 2010

JavaScript: Search an Array of Objects

The Object of your Desire

Ajax aficionados often find themselves with heaps of Json data, often arrays of data, fresh from the server, which they then magic into Html client-side.

A number of options exist for searching for a specific item in an array:

But what if you have an array of objects?

You can use JavaScript’s built-in indexOf(obj) method for arrays to compare for object identity:

arr.indexOf(obj);

But this requires the entire hotly-sought object and, apparently, this may not work in some browsers, such as IE6, without a creative work-around.

A Generic (and Perhaps Obvious) Solution

Recently, I wrote the following function:

function ArrayIndexOf(a, fnc) {
if (!fnc || typeof (fnc) != 'function') {
return -1;
}
if (!a || !a.length || a.length < 1) return -1;
for (var i = 0; i < a.length; i++) {
if (fnc(a[i])) return i;
}
return -1;
}

ArrayIndexOf(a, fnc) takes two parameters:

  • the array
  • a function of signature: bool f(item) {}

ArrayIndexOf compares each array item with, well, whatever you want, and returns the index of the item in the array, or -1 if there is no match. This approach gives you the flexibility to search for whatever specific field, or fields, you desire.

Sample usage:

var i = ArrayIndexOf(arrMovies, function(obj) {
return obj.MovieTitle == 'The Matrix';
});
if (-1 != i) {
alert('Found it! (Psst: take the blue pill!)');
}

Disclaimer:

  • I haven’t tested this code for performance, so if your arrays are immense, test it out
  • Handle with care. JavaScript’s type-insensitivity means you better be sure the object properties you test against exist if you don’t want to crash and burn.

Thursday, April 22, 2010

Iron Speed: To Generate or Not to Generate?

The Virtues of Laziness

If necessity is the mother of all invention, then laziness is a doting aunt, especially where developers are concerned.
Our desire to do as little work as possible has spawned, paradoxically, impressive feats of ingenuity.
We replaced our spaghetti-code subroutines with re-usable, elegant objects (OOP) figuring why do many times what can be done once; we abandoned our hard-coded data-layer drudgery in favor of drag-and-drop ORMs (ala Linq To Sql and Entity Framework); we even fled from manual GUI code (perish the thought!) and found refuge in the warm welcoming arms of template-driven web pages (Dynamic Data); we leverage Http Modules and fancy new Design Patterns whenever humanly possible (do I hear Inversion of Control?).
All-in-all, our laziness has paid off. Our code is more modular, maintainable and we get to use the battle cry DRY (don’t repeat yourself) when nobody’s looking.
The next evolutionary step, one might think, is code generation. Why write code at all when someone (or something) can do it for you?


Code Generation

For our purposes code generation refers to:
any tool that generates a fully-functional web application based on a set of database objects.
The generator creates web pages based on database tables and uses foreign key relationships between tables to create links between the web pages.




logo
Iron Speed is one such code generator for ASP .NET.
The generation process involves 5 steps:
  1. Select a design theme (menu above or at the side, color scheme)
  2. Select the page types you require -  record display, table display, reports, emails, workflows, etc. – and select the relevant database tables or views
    Wizard-PagesAndDB
  3. Select a few configuration settings (code language, Sql or stored procedures, etc)
  4. Let Iron Speed generate the web pages, data and business layer code in under a minute or two. LivewPreview
    Each page involves 5 files:
    - an html layout file
    - settings stored as xml
    These are used to generate the other files:
    - an aspx file
    - a code behind file (page.cs for C#)
    - a code file with classes for each Iron Speed control on the page (page.Controls.cs)
  5. Customize! (Rarely will Iron Speed guess correctly which fields you want to display.)
The last step is the most time-consuming. Customization involves two activities:
  • Using the Iron Speed designer – this makes changes to the html and xml files. When you re-build the site, the code files will also change.
  • Change page behavior by overriding methods in the code files in the designated sections. These changes will not be overwritten when you next build the site.

Pros and Cons

The advantages are obvious:
  • Quick development time
  • Rich GUI applications: paging, export to Excel etc., menu’s, reports
  • Object-oriented code design
  • Data layer can be configured to use either straight SQL or Stored Procedures
  • Online forums for troubleshooting
  • Simple if limited authentication-authorization infrastructure for forms or Active Directory-based security.
Immediate downsides include:
  • Expensive – licenses are in the thousands of dollars
  • Restrictive licensing policy – licenses are connected to specific users so forget about transferring a license from one team member to another even if the former has moved to Timbuktu.
But the more important disadvantages emerge only further down the road.




Iron Speed Gotchas

  • Customizations made via the Designer are lost when even minor changes are made. This can be a serious setback for application maintenance of customization-heavy pages. A minor tweak becomes a start-from-scratch task.
  • IronSpeed version upgrades often break code and require recoding.
  • Stored procedures are overwritten, not altered, and thus will lose their permissions properties each build. The procedures are based on dynamic queries and thus you’ll need to grant permissions to the actual tables in addition to the procedures.
  • GUI design is limited. Forget about moving your menus from below the logo to above – you’ll have to wade through pages of “theme” code – partly in XSL – which is littered with misleading obsolete code files. You may resort to very complex customizations and workarounds that defeat the purpose of code generation. Good luck!
    ”Why, o why,” your users will whine, '”can’t you just move the button to the other side?”
  • Simultaneous development is a major headache. The designer makes seemingly random changes that make code merges a nightmare if not practically impossible. The end result: features that “disappear” after a merge.
  • Users with minimal privileges may have empty table columns where buttons and values have been hidden. “Why can’t you just hide the damn table column?” is another familiar user complaint.

Conclusions

For small applications that follow the database table structure closely and require little updating, Iron Speed is a pleasure.
However, when it comes to a monster application delivered to demanding users and implemented by a multi-member Agile development team over multiple phases – think twice. The time you save on your first iteration will be devoured by your second, third and so on.
You may want to roll your own site or even tinker with Dynamic Data, which, being a template infrastructure as opposed to code generator, will offer the flexibility and predictability you seek, at the cost of fewer bells and whistles out the box.
Bottom line: laziness has a dark side too.