Friday, August 30, 2013

Configuring ASP.NET Application, IIS and Infrastructure to Utilize Username in Application

Hi folks, here is again an article about a thing from the miraculous world of web software development that drives me crazy.

Background

Currently I am writing a database ASP.NET application to be used within our company's intranet. I want to restrict the data from the database accessible by my users, so I need the user name to perform user-specific selects on the database. In Visual Studio debugging mode, everything works works fine: the user name is available by using the variable:
System.Security.Principal.WindowsIdentity.GetCurrent().Name
Unfortunately, the situation changed as soon as I deployed my application on my IIS 7.5 web server. There nothing was available in the first place.
Further environment characteristics:

  • OS (development & webserver): Windows 7 Enterprise
  • IDE: Visual Studio Express for Web Development 2012
  • IIS: 7.5 installed on my development machine (local admin required)
  • Database: SQL Server 2008 R2 (not important here, but for completeness)

Starting Point

As described in the Introduction, I started having a working application, which even worked in the sense of providing a user name in debugging mode. The minimal web.config provided as standard by Visual Studio looks like this:
<?xml version="1.0" encoding="utf-8"?>

<configuration>
    <system.web>
      <compilation debug="true" targetFramework="4.5" />
      <httpRuntime targetFramework="4.5" />
    </system.web>
</configuration>

Note: this simple configuration is only provided if you choose the "Empty ASP.NET Web Application". It can as well be more complex. However, running this simple application from within Visual Studio in debugging mode produces the following output:


Deploying on my IIS and running the same thing resulted in this (note: the greyed out area is the URL to the PC, the IIS is running on):



This is where I began searching the web for possible solutions for my problem. One source I found very promising was the following forum thread where the solution to the initial post seemed to fit my needs quite well:
How to get Windows user name when identity impersonate=“true” in asp.net?

Setting Up Web.Config and IIS

According to the stackoverflow-thread above, the problem can be cured by adjusting the web.config file of my development project and by adjusting the settings in IIS.
Following the advices of stackoverflow, I added a line <authentication mode="Windows"> to my web config, which looked like this:
<?xml version="1.0" encoding="utf-8"?>

<configuration>
    <system.web>
      <compilation debug="true" targetFramework="4.5" />
      <httpRuntime targetFramework="4.5" />
      <authentication mode="Windows" />
    </system.web>
</configuration>
If you do this, please do not forget to re-publish your site to IIS.
To change the IIS properties, first launch IIS (internet information services) in admin mode. Select your site you want to adjust (in my case TestApp) and double click on the "Authentication" Button. In a screenshot this looks like this (please forgive my German speaking screen ;)):


In the "Authentication" section, switch of everything but Windows-authentication:


Then I re-started my application and ... ran into the next problem. Now a login popup window asked me to provide a user and a password. I tried my normal MyDomain\MyUser and normal password without success. I guess I had to create dedicated user on my local PC, which I didn't want as I didn't want to put the login burdon on my users.

Login Popup Problem

As described above, now I had this s***ty lobin popup coming up again and again and it took me about 2 days to find a solution to this. Before I come to this, I'd like to tell what I checked:
  • WebMatrix.*.dll in BIN diectory of my app according to a stackoverflow thread. This was actually a nobrainer as my app was not based on MVC3.
  • Combinations of impersonation in IIS and in web.config, like in this thread
  • I checked all the settings about security zones (added my site to trusted sites) and authentication settings in IE advanced options
  • Some more stuff I don't remember. Nothing worked...
The breakthrough came through an msdn blog: "Things to check when Kerberos authentication fails using IIS/IE…". Somewhere in the lower part, they mention "Are the client and server on the same box?" and guess what - I tested all the time on my local machine where as well my webserver sits. So finally I switched to another machine - configuration of web.config and IIS as described in the previous chapters - and the thing worked. Actually one thing I had to do was to delete my application completely from the server and re-publish it again and afterwards it worked.
My test application yields the following result:

Conclusion: The required user information is available via:

  • Page.User.Identity.Name
  • HttpContext.Current.User.Identity.Name

Test Application

To have a handy look at the variables, I programmed a little test application with output for the parameters interesting for me. This Application has one screen:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="TestApp.WebForm1" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        System.Environment.UserName:<br />
        <asp:Label ID="Label_Environment" runat="server" Text="Label" Width="300px"></asp:Label>
        <br />
        Page.User.Identity.Name<br />
        <asp:Label ID="Label_PageIdentity" runat="server" Text="Label" Width="300px"></asp:Label>
        <br />
        System.Security.Principal.WindowsIdentity:<br />
        <asp:Label ID="Label_Principal" runat="server" Text="Label" Width="300px"></asp:Label>    
        <br />
        HttpContext.Current.User.Identity.Name:<br />
        <asp:Label ID="Label_HttpContext" runat="server" Text="Label" Width="300px"></asp:Label>  
        <br />
        System.Threading.Thread.CurrentPrincipal.Identity:<br />
        <asp:Label ID="Label_Threading" runat="server" Text="Label" Width="300px"></asp:Label> 
    </div>
    </form>
</body>
</html>

The three Label_* fields are filled in the Page_Load method:
protected void Page_Load(object sender, EventArgs e)
{
    Label_Environment.Text =     System.Environment.UserName.ToString();
    Label_PageIdentity.Text = Page.User.Identity.Name;
    Label_Principal.Text =       System.Security.Principal.WindowsIdentity.GetCurrent().Name.ToString();
    Label_HttpContext.Text = HttpContext.Current.User.Identity.Name;
    Label_Threading.Text = System.Threading.Thread.CurrentPrincipal.Identity.ToString();
}

That was it, I hope you can get something out of my findings
Cheers

Wednesday, August 28, 2013

ASP.NET - Mapping of Date Information from DB to UI

Background

I'm currently working on a database-based ASP.NET application where I have 3 date fields on the screen. I use TextBox controls for the Date fields and the column format in the database is "date". 
Saving to and reading from the database was no problem as long as the input type of the TextBox was datettime, but I ran into a major issue when I wanted to have the TextBox of input type "date" - the code could be compiled but when executing, the program crashed.

Wrong Turn

My first attempt to map from the TextBox to the database looked something like this:


DataTable DB_Table = new DataTable //I'm doing everything in the code with DataTables reflecting the input parameters of stored procedures
[... create table structure, etc. ...] 
DB_Table["DateColumn"] = TextBox_Date.Text // This worked fine as long as this was single line or datetime
[... update table in DB ...] 

Solution

After a bit of searching the web and try and error I came up with the following solution, which works, but which still has the disadvantge of hard coded date format. I believe, someone more clever then me could do this in a more generic way:

Mapping DB date format to TextBox date format

As one can see, the mapper inputs and outputs strings, which have to have the right format. As I am living in Germany, I chose the date format to be dd.MM.yyyy:
private String convertDBDateToTextBoxDate(String inputDateString) 
{    
    String outputDateString;
    String inputFormat = "dd.MM.yyyy HH:mm:ss";
    String outputFormat = "{0:dd.MM.yyyy}";
    DateTime closedDate = 
             DateTime.ParseExact(inputDateString,
                                inputFormat, CultureInfo.InvariantCulture);
                              // This line is actually the main point!!!
    outputDateString =
                                String.Format(outputFormat, closedDate);
    return outputDateString;
}

Mapping TextBox date format to DB date format

The approach above can be used as well vice versa:
private String convertTextBoxDateToDBDate(String inputDateString)
{
    String outputDateString;
    String outputFormat = "{0:dd.MM.yyyy HH:mm:ss}";
    String inputFormat = "dd.MM.yyyy";
    DateTime closedDate =
                                DateTime.ParseExact(inputDateString,
                               inputFormat, CultureInfo.InvariantCulture);
    outputDateString = String.Format(outputFormat,
                                closedDate);
    return outputDateString;
}

Surrrounding Code - Procedures to Update DB

First the code to map screen values to DB:
private DataRow updateValuesFromScreen(DataRow ActionItem)
{
     if (TB_Date.Text != "") {
          theDataRow["MeetingDate"] =
                                        convertTextBoxDateToDBDate(TB_Date.Text);
           }
    [... code filling all the other fields ...]
    return theDataRow;
}
Then the code of the ButtonSave_Click event:
protected void ButtonSave_Click(object sender, EventArgs e)
{
    DataRow theDataRow;
    classDataHandler theDataHandler =
                                            new classDataHandler();
                                           // The data handler is a class where I do the processing of data from / to DB
    theDataRow =
                        theDataHandler.getSingleRow(<keyValuesInDB>);
                       // This is required, because theDataRow is not filled due to the REST paradigm.
    theDataRow = updateValuesFromScreen(theDataRow);   
          theDataHandler.updateActionItem(theDataRow);
}

Surrrounding Code - Procedures to Update UI

The method getActionItem() updates the screen-fields by 1) fetching data from DB and 2) calling the conversion method convertDBDateToTextBoxDate (see above)
private void getActionItems()
{
       classDataHandler theDataHandler =
                                           new classDataHandler();
       tableWithRows =
                                          theDataHandler.getRows(<db keys>);
       [... some more code ...]     
       setScreenTexts(tableWithRows);
}

private void setScreenTexts(DataTable theDataTable)
{
       DataRow theDataRow =
                              theDataTable.Rows[<index>];
       if (theDataRow["MyDate"].ToString() != "")
       {
             TB_Date.Text = 
convertDBDateToTextBoxDate(theDataRow["MyDate"].ToString());
        }
        else { TB_Date.Text = ""; }
        [... other code ...]
             }

I hope this helps.

Cheers
W

Friday, August 9, 2013

CITRIX Online Plug-In: Fixing the Error 2320 (AllowHotkey)

Background

This article refers to a CITRIX client running on a windows 7 machine. The corresponding CITRIX online plug-in has the version 12.0.3.8. You can check this by launching the system control --> software

One note: if this blog post helps resolving your problem, it would be nice if you clicked on the ads banner to the right or at the bottom. Does not cost you anything and gives me some cents of revenue ;)

Process

Start the local CITRIX online plug-in on your PC:
Start --> CITRIX --> Online Plug-In

Then start a CITRIX client session
In the taks bar --> left-click on online plug-in and the requested client configuration

Result
You will get an error:


Error numer 2320: "No value could be found for (Allowhotkey) that satisfies all lockdown requeriments"

Fixing this Error

1) Open the registry: start --> search for regedit.exe
2) In the registry path HKEY_CURRENT_USER\Software\Citrix\ICA Client\Lockdown Profiles\All Regions:
Double click on the registry key "EnableLockdown":


And change the value from 1 to 0:


3) You need to stop and restart the online plug-in to make changes to take effect.

I hope that helps, cheers W