Monday, April 12, 2010

ASP.Net: Referencing root-relative paths (~/) in javascript

The “~/” specifies a root-relative path in ASP.Net. ASP.Net uses the ResolveUrl and ResolveClientUrl to determine the logical path that a path containing the “~/” points to.

If you need to use a root relative path from within a JavaScript method running within an ASP.Net page, then here is what the code looks like:

<script type="text/javascript">
    function OnSuccess() {
              window.open('<%=Page.ResolveClientUrl("~/newWindow.aspx")%>', 
                'mywindow', 
                'width=400,height=200,toolbar=yes,location=yes,directories=yes,status=yes,menubar=yes,scrollbars=yes,copyhistory=yes,resizable=yes');
      }
</script>

And here is a useful site with information on ASP.Net Paths - http://www.west-wind.com/weblog/posts/132081.aspx

Give me parameterized SQL, or give me death

An old post by Jeff Atwood – but a timeless one that every developer should read over and over again until it becomes second nature.

http://www.codinghorror.com/blog/2005/04/give-me-parameterized-sql-or-give-me-death.html

Also read the sister post “Who needs stored procs, Anyways

Thursday, April 08, 2010

Impersonating a specific windows identity

Alternate Title: How to get a WCF web service to use an alternate fixed identity to access a resource (such as a database).

By default, WCF will ignore the “<identity impersonate="true" userName="domain\username" password="xxxxxxx" />”. This is because even though the WCF service and ASP.net web site are using the same web.config, the ASP.net process is using only the settings under the System.Web node for its configuration and the WCF service is using the System.ServiceModel node for its configuration.

So how do you get WCF to use a particular identity to access a resource such as a database?

The problem I had was that I had a public facing website which needed to access a database. The database allowed only for windows authentication. Everytime the website called the webservice, the connection.Open command would fail as the user accessing the database was invalid.

There are a few ways to get around this issue.

1. Use the correct Identity as the ASPNet app pool identity. I believe this is the identity that WCF will use when attempting to access any resource. Unfortunately, if you are using IIS under XP then you dont have access to the AppPool identity. (which was my case). Using the correct identity is the best method to enable the scenario we are working on here.

2. If (1) is not possible, then you can get WCF to accept your “identity impersonate” settings that have been set in the system.web node.

The first thing to do is add the following line under the <system.servicemodel> node of your web.config.

<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

As you can see, what we are specifying here is that WCF will have to play nice with ASP.Net. The next thing you need to do is, in each of your service implementations, you need to define the following attribute at the class level.

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

This lets WCF know that you are indeed alright with your service code running under ASP.Net compatibility mode. Now whenever your end up using a protected resource (like open a database connection that is set up to using integrated identity), it will use the identity specified in the “<identity impersonate="true" userName="domain\username" password="xxxxxxx" />” node.

I dont like this solution, because you are opening up WCF to ASP.Net, when in reality WCF is a technology that doesnt care about ASP.Net. So its a shame to make the 2 speak to each other if the only reason we are enabling it is so that we can use the settings in the identity node of the system.web node.

But some times you have got to do what you got to do and in those cases, the above might be a reasonable solution.

3. Use the LogonApi to set the “WindowsImpersonationContext” for the operation that needs to use a different identity to access the resource.

This is a nice solution too, except for the fact that you need to use PInvoke to access the LogonApi (which sits in the advapi32.dll). The reason this is nice is that if you need to use multiple identities for the different resources that you need to access, then the only way to do that is using this method. (Another bad part to this option is that you need to store the username password somewhere, so that you can use it to perform the logon – so be sure to think about encrypting this information).

The code needed is the following:

using System;
using System.Collections.Generic;
using System.Linq;

using System.Runtime.InteropServices;
using System.Security.Principal;

/// <summary>
/// Summary description for LogonAPI
/// </summary>
public class LogonAPI
{
    public const int LOGON32_LOGON_INTERACTIVE = 2;
    public const int LOGON32_PROVIDER_DEFAULT = 0;
    [DllImport("advapi32.dll")]
    public static extern int LogonUserA(String lpszUserName,
        String lpszDomain,
        String lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        ref IntPtr phToken);
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int DuplicateToken(IntPtr hToken,
        int impersonationLevel,
        ref IntPtr hNewToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool RevertToSelf();

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern bool CloseHandle(IntPtr handle);


    public static bool impersonateValidUser(String userName, String domain, String password, ref WindowsImpersonationContext impersonationContext)
    {
        WindowsIdentity tempWindowsIdentity;
        IntPtr token = IntPtr.Zero;
        IntPtr tokenDuplicate = IntPtr.Zero;

        if (RevertToSelf())
        {
            if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
                LOGON32_PROVIDER_DEFAULT, ref token) != 0)
            {
                if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                {
                    tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                    impersonationContext = tempWindowsIdentity.Impersonate();
                    if (impersonationContext != null)
                    {
                        CloseHandle(token);
                        CloseHandle(tokenDuplicate);
                        return true;
                    }
                }
            }
        }
        if (token != IntPtr.Zero)
            CloseHandle(token);
        if (tokenDuplicate != IntPtr.Zero)
            CloseHandle(tokenDuplicate);
        return false;
    }
    public static void undoImpersonation(WindowsImpersonationContext impersonationContext)
    {
        impersonationContext.Undo();
    }
}

And to use it

WindowsImpersonationContext impersonationContext = null;
if (LogonAPI.impersonateValidUser("username", "domain", "password", ref impersonationContext))
{
    //do work here
    LogonAPI.undoImpersonation(impersonationContext);
}
else
{
   //impersonation failed
}

Are there any other ways to do the above? Is there a better way to do it? (preferably through configuration?)

Design recommendation on using impersonation for multi tiered applications

As a common design recommendation, the further from the client, the less significant its identity should be. In a layered architecture, each layer should run underneath its own identity, authenticate its direct callers, and implicitly trust its calling layer to authenticate its original callers.

See “Trusted Subsystem Pattern” (Patterns & Practices)

The Web service acts as a trusted subsystem to access additional resources. It uses its own credentials instead of the user's credentials to access the resource. The Web service must perform appropriate authentication and authorization of all requests that enter the subsystem. Remote resources should also be able to verify that the midstream caller is a trusted subsystem and not an upstream user of the application that is trying to bypass access to the trusted subsystem.

Aa480587.ch4_trustsub_f01(en-us,MSDN.10).gif

See also:

http://blogs.msdn.com/securitytools/archive/2009/12/30/wcf-security-impersonation.aspx

Monday, April 05, 2010

Connecting to SSRS Web-Service from Visual Studio 2008 (part II)

In a previous post (Connecting to SSRS Web-Services from Visual Studio 2008), I had written about how to connect to SSRS from Visual Studio 2008. In that one I told you how to add a reference to the SSRS web-service using the “Add Web-Reference” option. That is basically a cop-out and even though an easy way of doing it, I wanted to figure out how to do it using VS2008 and WCF.

So basically I wanted to use a WCF client and connect to a ASMX web-service. (So this will work for any project that needs a similar configuration and needs to use the current user credentials for accessing the WCF client).

First add a reference to the SSRS service using the “Add Service Reference” option.

Now use the following code:

using (ReportingService2005SoapClient proxy = new ReportingService2005SoapClient("ReportingService2005Soap"))
{
    //tell WCF that you need the server to impersonate the current user on the server
    //an important piece to get this working is in the config file - where the security is setup
    //the security needs to be setup like so:
    /*
        <security mode="TransportCredentialOnly">
            <transport clientCredentialType="Ntlm" proxyCredentialType="None"
                realm="" />
            <message clientCredentialType="UserName" algorithmSuite="Default" />
        </security>
    */

    proxy.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;

    Property name = new Property();
    name.Name = "Name";

    Property description = new Property();
    description.Name = "Description";

    Property[] properties = new Property[2];
    properties[0] = name;
    properties[1] = description;

    try
    {
        Property[] returnProperties;
        
        //this method's signature is different from what you get if you were to use
        //Add Web Reference. The return value is now an out parameter.
        proxy.GetProperties(new ItemNamespaceHeader(), 
            "/reportfolder/reportname", properties, out returnProperties);

        foreach (Property p in returnProperties)
        {
            Console.WriteLine(p.Name + ": " + p.Value);
        }
    }

    catch (Exception e)
    {
        Console.WriteLine(e.Message);
    }


}

I have added comments regarding the important parts of the code.

Now the last thing you need to do is to update your config file so that your security node (part of the Binding element) looks like this:

<security mode="TransportCredentialOnly">
    <transport clientCredentialType="Ntlm" proxyCredentialType="None"
        realm="" />
    <message clientCredentialType="UserName" algorithmSuite="Default" />
</security>

Connecting to SSRS Web-Services from Visual Studio 2008

Or “How to add a web-service reference to a WinForm or a Console based application”.

Its easy to connect to the SSRS web-service from VS2005. Simply add a web-reference by right clicking on the references node and they enter the web-service end-point. (This is typically “http://serverName/ReportServer/ReportService2005.asmx” – note: the 2005 in the end is used even if you are attempting to connect to SSRS2008).

If you use the following code, it should all build correctly and also run.

SSRS.ReportingService2005 rs = new ReportingService2005();
rs.Credentials = System.Net.CredentialCache.DefaultCredentials;

Property name = new Property();
name.Name = "Name";

Property description = new Property();
description.Name = "Description";

Property[] properties = new Property[2];
properties[0] = name;
properties[1] = description;

try
{
    Property[] returnProperties = rs.GetProperties("/Reports/reportName", properties);

    foreach (Property p in returnProperties)
    {
        Console.WriteLine(p.Name + ": " + p.Value);
    }
}

catch (Exception e)
{
    Console.WriteLine(e.Message);
}

If you try to add a webservice reference is VS2008, all you get is an option to add a service reference and if you use the next dialog to connect to your SSRS webservice end-point, the sample code shown above will not compile. (you will get errors regarding no Credentials property available as well as a whole litany of others).
image

Instead here is what you need to do:

Click on the Add Service Reference Option.

Click on the Advanced Button.

The next dialog has the “Add Web Reference” button.

Enter the end-point address in the next dialog.

Now the code shown above should work.

So what is the difference between “Add Web Reference” and “Add Service Reference”? Turns out that you can use the Add Web Reference option only when you have a WSDL document. But if you need to use any WCF based service – not just WSDL based ones, you need to use the “Add Service Reference” option.

Saturday, April 03, 2010

ASP.Net – Editing items in a nested DataList

I had a complex business entity that was setup with a hierarchical relationship (i.e. the parent object contained a list of child objects). I wanted to find a way to display the data such that users could edit items in the contained list.

What I found was the DataList makes it easy to display hierarchically related data using the concept of nested data-lists (I am sure one will be able to use my technique to create any kind of nested databound table). There are quite a few examples of how create nested data-lists and data-grids, but what almost all the samples lacked was the how regarding the editing of the nested data. (the few that I found – just did not work).

First take a look at the implemented code: http://www.aggregatedintelligence.com/NestedDataList/CategoriesView.aspx

Here is how I got it to work:

First the sample data-objects:

image

Category is the parent object and it contains a list of Items.

The goal is to be able to display the data in the following format:

Category    
Name    
Description    
Item-Line1 Item-Line2 Amount
Item-Line1 Item-Line2 Amount
Category    
Name    
Description    
Item-Line1 Item-Line2 Amount
Item-Line1 Item-Line2 Amount

The first step is to drag a DataList onto the web-page. You then add an item template to display the fields from the parent object (Category in this case).

<ItemTemplate>
<div class="divCategoryInfo">
<div>
Category:
<asp:Label ID="lblFirstName" runat="server" Text='<%#Bind("CategoryName")%>'></asp:Label>
</div>
<div>
Description:
<asp:Label ID="lblLastName" runat="server" Text='<%# Bind("Description") %>'></asp:Label>
</div>
</div>
<div class="divCategoryItems">
<!-- insert items here -->
</div>
</ItemTemplate>

Notice the place holder for the category items, we will come back to that later.

For now, lets plumb up the DataList. In the code-behind, we will perform data-binding during the Page Load event. The databinding needs to be performed ONLY during direct page loads and not during a post-back.

Now for the nested DataList. We could insert a new DataList directly into the ItemTemplate shown above. While that option does work for displaying of the data, it does not allow the nested DataList to be edited. (What used to happen was the ItemEditing event used to fire, but the ItemUpdated never ever fired – which meant that I could never get my hands at the updated data values).

Instead, what I found was that if I put the DataList into a user-control and then dropped it into the parent data-list, it allowed all the events to fire properly. One complication with this method was how to provide the nested data to the inner DataList. Another complication was: during post-back, as data-binding does not occur automatically, how do we again get the correct inner data and manually databind it to the nested data-list?

So here are the basic parts that need to be setup on the DataList that is in the user-control:

First create the user-control.
Next drag a data-list on the user-control. Setup the datalist to display the data.

<asp:DataList ID="dlItems" runat="server" Width="100%" 
OnItemCommand="mySubListItemHandler"
onEditCommand="myListEditHandler"
onUpdateCommand="myListUpdateHandler"
onCancelCommand="myListCancelHandler" BorderStyle="Solid"
BorderWidth="1px" GridLines="Both" >
<HeaderTemplate>
<th></th><th>Line 1</th><th>Line 2</th><th>Amount</th><th>In Shopping Cart</th>
</HeaderTemplate>
<ItemTemplate>
<td>
<asp:LinkButton ID="Linkbutton3" runat="server" CommandName="AddToCart" Text='<%#GetCartTitle(Eval("InShoppingCart")) %>' />
<asp:LinkButton ID="Linkbutton1" runat="server" CommandName="Edit" Text="Edit" />
</td>
<td>
<asp:Label ID="lblFirstName" runat="server" Text='<%#Bind("Line1")%>'></asp:Label>
</td>
<td>
<asp:Label ID="Label1" runat="server" Text='<%#Bind("Line2")%>'></asp:Label>
</td>
<td align="right">
<asp:Label ID="Label2" runat="server" Text='<%#Bind("Amount")%>'></asp:Label>
</td>
<td align="center">
<asp:ImageButton ID="ImageButton1" runat="server" CommandName="AddToCart"
ImageUrl='<%#GetImageUrl(Eval("InShoppingCart")) %>' />
</td>
</ItemTemplate>
<EditItemTemplate>
<td>
<asp:LinkButton ID="Linkbutton1" runat="server" CommandName="Update" Text="Update" />
<asp:LinkButton ID="Linkbutton2" runat="server" CommandName="Cancel" Text="Cancel" />
</td>
<td>
<asp:Label ID="lblFirstName" runat="server" Text='<%#Bind("Line1")%>'></asp:Label>
</td>
<td>
<asp:Label ID="Label1" runat="server" Text='<%#Bind("Line2")%>'></asp:Label>
</td>
<td>
<asp:TextBox ID="txtAmount" runat="server" Text='<%#Bind("Amount")%>'></asp:TextBox>
</td>
<td align="center">
<asp:ImageButton ID="ImageButton1" runat="server" CommandName="AddToCart"
ImageUrl='<%#GetImageUrl(Eval("InShoppingCart")) %>' />
</td>
</EditItemTemplate>
</asp:DataList>

The first thing to realize is that I have implemented the handlers for OnItemCommand, OnEditCommand, OnUpdateCommand, OnCancelCommand. The OnItemCommand is strictly not needed, but I wanted to keep track of when a subitem had been clicked. The next thing to realize is that I have an edit template defined for this DataList. It has only one element setup to be edited. (the Amount column).

Now the code-behind. What I realized was that you can get the parent DataList’s item index using the following code:
((System.Web.UI.WebControls.DataListItem)(this.Parent)).ItemIndex;
Now all I needed to do is that when the main page got loaded, I had to store the data in the SessionState, this would give me access to the data in the nested user-control. In my case I stored in list of categories in “Session["list"]”. This meant that every time I needed to perform data-binding in the nested user-control, I had to get the ((System.Web.UI.WebControls.DataListItem)(this.Parent)).ItemIndex item from the Session[“list”] object.
As for when to data-bind: You need to databind in page-load event only if it is not a post-back event. Other times you need to data-bind are when any of the events on the data-list fire (onItemCommand, OnEditCommand, etc).

A little bit about the sample code. It is a sample. I created it quickly to make sure that I could do what I wanted it to do. So dont complain that I dont check for this and I dont check for that. This is not production quality code. The page_load of the categoriesview page is weird because I am using a single data-generation method. It is meant to mock data coming from a different page where I would be selecting the categories that I am interested in. Also, I added some extra handling to keep track of a shopping cart into which items were being added and removed. The items are loaded using a DataProvider which creates the data randomly. The page also uses the MS Ajax framework to make the experience of working with the cart smoother.

Some other things to note. The user-control has an event that the main page subscribes to, to get notified about when an item is added or removed to the cart. Also, the check-box that represent that cart state of an item are simple image-links. The url is dynamically changed during databinding.

You can download the sample code from: http://cid-fbe9049ba8229d5b.skydrive.live.com/self.aspx/Public/WebApp%2004-03-2010.zip

Quotes by the Dalai Lama

 

  1. All major religious traditions carry basically the same message, that is love, compassion and forgiveness the important thing is they should be part of our daily lives.
  2. Happiness is not something ready made. It comes from your own actions.
  3. If you have a particular faith or religion, that is good. But you can survive without it.
  4. If you want others to be happy, practice compassion. If you want to be happy, practice compassion.
  5. In the practice of tolerance, one's enemy is the best teacher.
  6. Love and compassion are necessities, not luxuries. Without them humanity cannot survive.
  7. My religion is very simple. My religion is kindness.
  8. Old friends pass away, new friends appear. It is just like the days. An old day passes, a new day arrives. The important thing is to make it meaningful: a meaningful friend - or a meaningful day.
  9. Our prime purpose in this life is to help others. And if you can't help them, at least don't hurt them.
  10. Sleep is the best meditation.
  11. Sometimes one creates a dynamic impression by saying something, and sometimes one creates as significant an impression by remaining silent.
  12. The ultimate authority must always rest with the individual's own reason and critical analysis.
  13. There is no need for temples, no need for complicated philosophies. My brain and my heart are my temples; my philosophy is kindness.
  14. This is my simple religion. There is no need for temples; no need for complicated philosophy. Our own brain, our own heart is our temple; the philosophy is kindness.
  15. Today, more than ever before, life must be characterized by a sense of Universal responsibility, not only nation to nation and human to human, but also human to other forms of life.
  16. We can live without religion and meditation, but we cannot survive without human affection.
  17. We can never obtain peace in the outer world until we make peace with ourselves.
  18. Where ignorance is our master, there is no possibility of real peace.
  19. Whether one believes in a religion or not, and whether one believes in rebirth or not, there isn't anyone who doesn't appreciate kindness and compassion.
  20. With realization of one's own potential and self-confidence in one's ability, one can build a better world.

Friday, April 02, 2010

Comcast - Speedtest

769988151[1]

C# – Generate a random string

Generating a random string is useful for unit tests (filling your objects with random garbage). Here is a simple snippet to generate random strings/number/symbols in any combination. Useful for suggesting passwords.

static Random random = new Random(DateTime.Now.Second);
private static string PASSWORD_CHARS_LCASE = "abcdefghijklmnopqrstuvwxyz";
private static string PASSWORD_CHARS_NUMERIC = "0123456789";
private static string PASSWORD_CHARS_SPECIAL = "*$-+?_&=!%{}/";
public static string RandomString(int size, bool allowNumbers, bool allowUpperCase, bool allowLowerCase, bool allowSpecialChars)
{
StringBuilder builder = new StringBuilder();

List<char> validCharList = new List<char>();
if (allowNumbers)
validCharList.AddRange(PASSWORD_CHARS_NUMERIC.ToCharArray());
if (allowLowerCase)
validCharList.AddRange(PASSWORD_CHARS_LCASE.ToCharArray());
if (allowUpperCase)
validCharList.AddRange(PASSWORD_CHARS_LCASE.ToUpper().ToCharArray());
if (allowSpecialChars)
validCharList.AddRange(PASSWORD_CHARS_SPECIAL.ToCharArray());

while (builder.Length < size)
{
builder.Append(validCharList[random.Next(0, validCharList.Count)]);
}

return builder.ToString();
}