Thursday, June 12, 2008

Adding OpenID to an existing ASP.NET application

[Updated 22:00 12 June 2008] To include improvements suggested by Andrew Arnott.

In this post I'll describe the steps I took to add OpenID support to an existing ASP.NET app that used forms authentication. The application originally used the users email address as their username. The OpenID login process therefore needs to provide an email address to avoid having to rewrite loads of code. Not all OpenID providers allow email addresses to be sent so new users might have to partially re-register the first time they use their OpenID.

  1. Downloaded the latest DotNetOpenID zip file from http://dotnetopenid.googlecode.com/files/
  2. Unziped the package
  3. Copied and renamed login.aspx and login.cs from \Samples\RelyingPartyPortal to my project's root folder, renaming them loginOpenID.aspx and loginOpenID.cs. Changed codebehind attribute in loginOpenID.aspx from CodeBehind="login.aspx.cs" to CodeBehind="loginOpenID.aspx.cs". I then added a link named "Login with OpenID" on my original login.aspx page pointing to the new loginOpenID.aspx page. I then changed some of the properties on the OpenIdLogin control as I wanted to ask the OpenID provider to supply the users FullName and Email address. Unfortunately some providers (eg Yahoo.com) do not allow the FullName & Email info to be sent so we'll have to deal with this in code later.

    <RP:OpenIdLogin ID="OpenIdLogin1"
    runat="server"
    RequestFullName="Require"
    RequestEmail="Require"
    RememberMeVisible="True"
    PolicyUrl="~/PrivacyPolicy.aspx"
    TabIndex="1"
    OnLoggedIn="OpenIdLogin1_LoggedIn"    
    OnCanceled="OpenIdLogin1_Canceled"
    OnFailed="OpenIdLogin1_Failed"
    OnSetupRequired="OpenIdLogin1_SetupRequired" RememberMe="True"
    />

  4. Copied \Samples\RelyingPartyPortal\Code\state.cs to my project's App_Code folder
  5. Copied \Samples\RelyingPartyPortal\xrds.aspx to my project's root folder. Modified this file to point to my new loginOpenID.aspx page changing

    <URI><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/login.aspx"))%></URI>

    to

    <URI><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/loginopenid.aspx"))%></URI>

  6. Copied \Samples\RelyingPartyPortal\privacypolicy.aspx to my project's root folder.
  7. I added the following to default.aspx (the default document for this domain).
    <%@ Register Assembly="DotNetOpenId" Namespace="DotNetOpenId" TagPrefix="openid" %>
    <openid:XrdsPublisher runat="server" XrdsUrl="~/xrds.aspx" />
    This was required to get myopenid.com accounts to work.
  8. Added a reference to the DotNetOpenID.dll from the \Samples\RelyingPartyPortal\bin folder
  9. I then added code to OpenIdLogin1_LoggedIn in loginOpenID.cs to ensure we have the user's real email address. If for whatever reason we cannot get their email address redirect them to the partially populated registration page.


     

    protected void OpenIdLogin1_LoggedIn (object sender, OpenIdEventArgs e)

        {

        State.ProfileFields = e.ProfileFields;

        

        //    Setup linq connection to SQL database                        

        DataClassesDataContext db = new DataClassesDataContext(ConfigurationManager.ConnectionStrings["DB_RW"].ConnectionString);

        People people = null;


     

        //    See if user has logged on using OpenID before                

        try

            {

            people = (from c in db.Peoples

                     where c.OpenID == e.ClaimedIdentifier.ToString()

                     select c).Single();

            }

        catch

            {

            }


     

        if (people == null)

            {

            //    This is the first time this OpenID identity has been used    

            if (e.ProfileFields.Email == null)

                {

                //    Force user to register as their OpenID provider did not send their email

                //    address (eg Yahoo.com) and this app needs their real email address.        

                e.Cancel = true;

                Response.Redirect("RegisterNewAccount.aspx?mode=OpenID_NoEmailSupplied");

                return;

                }

            else

                {

                //    See if user has created an account already                

                try

                    {

                    people = (from c in db.Peoples

                             where c.Email == e.ProfileFields.Email

                             select c).Single();

                    }

                catch

                    {

                    //    email address does not exist in our user table redirect user    

                    //    to registration page.                                            

                    e.Cancel = true;

                    Response.Redirect("RegisterNewAccount.aspx?mode=OpenID_UnknownEmail");

                    return;

                    }

                }

            }


     

        if (people.Status.StartsWith("Reject T&C"))

            {

            e.Cancel = true;

            loginFailedLabel.Text = "Your account has been suspended because you have rejected our T&C's.";

            loginFailedLabel.Visible = true;

            return;

            }


     

        if (people.Status != "Verified")

            {

            e.Cancel = true;

            loginFailedLabel.Text = "Your account has not been verified yet. Check your email for further instructions.";

            loginFailedLabel.Visible = true;

            return;

            }


     


     

        //    Remember OpenID identity for next time            

    people.OpenID = e.ClaimedIdentifier.ToString();
    people.LoginCount = (people.LoginCount ?? 0) + 1;

        db.SubmitChanges();


     

        //    I set some other Session variables here

    Session["UserEmail"] = people.Email;


     

    //    The openID code will now redirect to the requesting page    

    //    and set context.user.identity.name to the OpenID identity    

    //    eg http://andrew.jones.myopemid.com                            

    }

  10. The RegisterNewAccount.aspx pre-populates as much information as it can. Any additional user information sent by the OpenID provider is available in State.ProfileFields.

Thursday, June 05, 2008

DataGridViewRow SelectedRows selection order

When you use SelectedRows to get the selected rows from a DataGridView the order of items is dependent on the order they were selected. If you start at the top and select down the item list will be reversed. Selecting from the bottom row up is OK. Selecting random rows with CTRL pressed will leave the most recent item at the start of the list. The list therefore needs to be sorted based on the DataGridViewRow.Index property.

Here is a function to sort the list




// Create a generics list to hold selected rows so it can be sorted later
List<DataGridViewRow> SelectedRows = new List<DataGridViewRow>();


foreach (DataGridViewRow dgvr in yourDataGridViewControl.SelectedRows)
SelectedRows.Add(dgvr);

// Sort list based on DataGridViewRow.Index
SelectedRows.Sort (DataGridViewRowIndexCompare);




private static int DataGridViewRowIndexCompare (DataGridViewRow x, DataGridViewRow y)
{
if (x == null)
{
if (y == null)
{
// If x is null and y is null, they're
// equal.
return 0;
}
else
{
// If x is null and y is not null, y
// is greater.
return -1;
}
}
else
{
// If x is not null...
//
if (y == null)
// ...and y is null, x is greater.
{
return 1;
}
else
{
// ...and y is not null, compare the
// lengths of the two strings.
//
int retval = x.Index.CompareTo (y.Index);

if (retval != 0)
{
// If the strings are not of equal length,
// the longer string is greater.
//
return retval;
}
else
{
// If the strings are of equal length,
// sort them with ordinary string comparison.
//
return x.Index.CompareTo(y.Index);
}
}
}
}

Monday, June 02, 2008

TCP/IP Fundamentals

Microsoft have released a very useful (free) PDF book describing in details TCP/IP.

Download

Friday, May 09, 2008

LinkedIn BICSI RCDD group

I've just created a BICSI RCDD group on LinkedIn. If you are a BICSI RCDD click on the link below to join :-

http://www.linkedin.com/e/gis/101866/2861405DDA38

Tuesday, February 19, 2008

WPF Limit number of lines in multi line TextBox

WPF TextBox controls do not have a property to limit the number of lines of text that can be entered into a control. Here is a bit of C# to provide the functionality

XAML

<TextBox Height="138" Name="textBox_DeliverFrom" Width="257" AcceptsReturn="True" PreviewKeyDown="textBox_DeliverFrom_PreviewKeyDown" />


C#

private void textBox_DeliverFrom_PreviewKeyDown (object sender, KeyEventArgs e)
{
// Do not allow more than 8 Lines
if (e.Key == Key.Enter)
{
if (textBox_DeliverFrom.LineCount > 7)
e.Handled = true;
}
}

One needs to add a PreviewKeyDown handler and ignore then Enter key if the control has reached the required limit.

Tuesday, November 06, 2007

Installing msi on Vista without UAC prompts

I've been using VS2005 to create an .msi to install on Vista without UAC prompts. The installer is fairly simple and there is no real reason for a UAC prompt as the files are not being installed to any system folders however msi files always UAC prompt on Vista unless specifically told not to.

Msiinfo.exe allows you to modify a msi file. In the example below I'm setting a flag to indicate that the msi file does not need to UAC prompt. This flag is only read when the file is installed on Vista.

I've added the following PostBuildEvents to the VS2005 Deployment project.

rem ----------------------------------------------------------
rem First set flag to indicate no UAC on Vista
rem ----------------------------------------------------------
"C:\Program Files\Microsoft SDKs\Windows\v6.0\Bin\msiinfo" "$(BuiltOuputPath)" -w 10


rem ----------------------------------------------------------
rem Code sign the msi file
rem ----------------------------------------------------------
"C:\Program Files\Microsoft SDKs\Windows\v6.0\Bin\signtool.exe" sign /f "$(ProjectDir)CodeSignKey.pfx" /p mypassword /d "Product Name" /du "http://www.productURL" /t "http://timestamp.comodoca.com/authenticode" "$(BuiltOuputPath)"


rem ----------------------------------------------------------
rem Code sign the setup.exe file
rem ----------------------------------------------------------
"C:\Program Files\Microsoft SDKs\Windows\v6.0\Bin\signtool.exe" sign /f "$(ProjectDir)CodeSignKey.pfx" /p mypassword /d "Product Name" /du "http://www.productURL" /t "http://timestamp.comodoca.com/authenticode" "setup.exe"


msiinfo.exe is available in the Windows SDK Components for Windows Installer Developers.

signtool.exe is available in the Microsoft Visual Studio 2005/.NET Framework 2.0

CodeSignKey.pfx is a code signing certificate. I bought mine from http://www.instantssl.com/code-signing/code-signing.html. This file must be manually exported from Internet Explorer 7 if you used Vista to purchase a code signing certificate. In this example i've put the pfx file in the same folder as the project .vdproj file.

mypassword is the password used when exporting the CodeSignKey.pfx from Internet Explorer. When you buy a code signing certificate using Vista it gets automatically installed into your Personal Certificates store in Internet Explorer. This article describes how to export this certificate so it can be used by SignTool. http://www.tech-pro.net/export-to-pfx.html

product name is the name of your installer. This is displayed along with the company name when you execute setup.exe

http://www.productURL is a url that can be used to get more info on the product

http://timestamp.comodoca.com/authenticode is the url of the Comodo time server. Certificates are valid for one year so it's important to time stamp the file when it is created. This allows the file to be protected for ever. If the file was not time stamped it would stop running when the certificate expired.

When installing on Vista launch the .msi file without using setup.exe. Any program called setup.exe automatically UAC prompts on Vista wiping out all the good work above.

Once you have done all this you can launch the msi file and not have to worry about it UAC prompting. In a corporate enviromnent it is a real pain to have to enter an admin password whenever someone wants to install a program. Preventing UAC when it is not required makes the process much easier.

Sunday, October 14, 2007

Vista start-up crash solved

Ever since I installed Vista on my Sony VGN-AR21S it has randomly crashed about every third re-boot. Task Manager would show the 'system' process using 98-100% processor utilisation. After about 10 minutes all 2Gb of memory would get used up and Vista would crash with a BSOD, typically a kernel in page error. I partially solved this problem by hibernating rather than shutting down the machine. I think I have now solved the problem.

 
 

I downloaded the excellent Sysinternals Process Monitor (http://www.microsoft.com/technet/sysinternals/default.mspx) which provides a full display of the individual executables running as part of the system process. Process Monitor showed that ndis.sys!NdisInitializeWrapper was consuming all the processor time and memory. This information indicated a network driver problem.


 

The machine has an Intel Pro 100/VE Ethernet card and an Intel Wireless 3945ABG Wi-Fi card. I downloaded the latest network drivers from the Intel web site and installed them. I then used device manager to delete both network devices. Device manager then re-detected both network cards. Having done this the machine appears to start-up without any problems.


 

It's really annoying that neither the Microsoft nor Sony update sites offer these new network drivers.

Wednesday, August 22, 2007

3Com V3000 ATA Ringing on UK Phones

I've been configuring a 3Com V3000 VOIP PABX. If you want to connect a UK spec phone you need to ensure you use a "PSTN full master". "PABX Masters" or "Secondary adapters" will not allow the phone to ring.





A UK PSTN Full Master contains a capacitor, resistor and spark suppressor. If you use anything other than a PSTN Master you can use the phone to make calls but it will not ring.

There are many different PSTN Masters available with different pinouts. I have sucessfully tested the following Krone part:-

Krone P/N : 6536/1/601/11

3Com 4500 default admin password

I have been configuring a 3Com 3CR17571-91 POW switch this week. Again the default password is not listed in the manuals. The admin username is : admin and the default password is blank. Hope that saves you some time if you are setting up one of these.

So far I'm not impressed with the quality of documentation from 3Com.

3Com V3000 BRI Admin password

I could not find this anywhere in the 3Com manuals. The default admin password for a 3Com V3000 is :-

0000

(tha's 4 zeros)

3Com V3000 ATA to UK Phones

I've been configuring a 3Com V3000BRI PABX this week and have had some difficulty connecting the ATA ports to UK phones. First it's inportant to understand that UK approved phones use different pins to USA phones. This means you cannot connect a UK phone to a 3Com ATA using a RJ11 straight through lead. The diagram above shows how UK approved phones and UK phone cords work.





If you want to connect a UK approved Telephone to an item of USA equipment you need to make a RJ11 lead like this:-











Here is the full end to end pinout



It's really annoying that the 3Com documentation does not show the pinouts of the ATA RJ11 sockets.

Monday, April 30, 2007

Attaching databases to different Sql Servers

I often attach a SQL database created on my development machine to a production SQL server. When you do this you need to re-create the sql login accounts (SQL Server Management Studio -> Security -> Logins -> New Login...) on the target server and then run the following command to map the two accounts back together.

Open a query window from the attached database and run this command replacing DatabaseUsername and ServerUsername with your own values.

EXEC sp_change_users_login 'Update_One', 'DatabaseUsername', 'ServerUsername';

Wednesday, March 21, 2007

VSTO 2005 SE Publish Wizard

Warning : If you use the VSTS 2005 SE Publish Wizard it creates a set of files that cannot be moved from the published folder.

An easier way of deploying your Excel application to a small number of clients is to compile and then copy the *.xls and *.dll file from your application bin\release folder to a location on your network or client pc.

You then need to setup a .NET 2.0 trust for the folder containing your files. I prefer to use the graphical UI tool to do this. In order to get the "Microsoft .NET Framework 2.0 Configuration" tool in Control Panel->Admin Tools you need to install the .NET 2.0 SDK. Get it here http://www.microsoft.com/downloads/details.aspx?FamilyID=FE6F2099-B7B4-4F47-A244-C96D69C35DEC&displaylang=en 340Mb just to get one tool...ouch! When you install this you only need the tools, don't install the samples or documentation.

Once you have installed the SDK you can open "Microsoft .NET Framework 2.0 Configuration" from Control Panel->Admin tools to setup a trust for the folder holding your files. Open "Microsoft .NET Framework 2.0 Configuration" and expand my Computer -> Runtime Security Policy -> Machine -> Code Groups -> All_Code -> My_Computer_Zone, right click on My_Computer_Zone and select "New...". Name the item and press Next, change the Membership condition to URL and enter the path to your folder in this format:-

file://c:\FolderName\*

The "file://" is required as a prefix. "\*" is required at the end so the entire folder is trusted. Then on the Permission Set tab change to FullTrust. This sets up trust to allow anything in the c:\FolderName folder to execute. You may want to setup a more restrictive permission set. You may also want to uninstall the SDK once you have setup the trust. You can also use a command line tool to setup these trusts but I could not work out how to use it as you cannot see what it has changed.

If you are installing the files on a network folder you need to use LocalIntranet_Zone rather than My_Computer_Zone.

In order to change these settings you need local admin rights on the client pc.

Before you can install a VSTO SE App you need the following :-

Office 2003 Professional
SP2 is not required but well worth installing anyway

Microsoft .NET Framework 2.0
http://www.microsoft.com/downloads/details.aspx?familyid=0856EACB-4362-4B0D-8EDD-AAB15C5E04F5&displaylang=en

Microsoft .NET Framework 2.0 SDK
http://www.microsoft.com/downloads/details.aspx?FamilyID=FE6F2099-B7B4-4F47-A244-C96D69C35DEC&displaylang=en

Office 2003 Primary Interop Assemblies
http://www.microsoft.com/downloads/details.aspx?familyid=3c9a983a-ac14-4125-8ba0-d36d67e0f4ad&displaylang=en

VSTO 2005 SE runtime
http://www.microsoft.com/downloads/details.aspx?FamilyID=f5539a90-dc41-4792-8ef8-f4de62ff1e81&DisplayLang=en

Install in the order shown above.

Thursday, March 15, 2007

VSTO SE only works with Office 2003 Professional

I think I have finally established that VSTO SE only works with Office 2003 Professional (or any version of Office 2007) when using document level applications, Office 2003 Standard is not supported. Apparently you can install application level apps on Office 2003 Standard (although I have not tested this).

This Microsoft document http://msdn2.microsoft.com/en-us/library//2ac08ee2(vs.80).aspx which indicates that VSTO SE will install on Office 2003 Standard is therefore wrong, or at best incomplete, as in fact only application level apps are supported on Office 2003 Standard.

Wednesday, March 14, 2007

Deploying VSTO SE Excel applications

I've had real trouble attempting to deploy an Excel VSTO SE app today. I cannot believe how difficult they have made this. It's dead easy to write the application using Visual Studio 2005 but the deployment is an absolute nightmare.

I've eventually found a solution. Starting from an empty XP SP2 machine do this and it should work. I'm sure it's not the most secure configuration but at least it works.

1) Install .NET 2.0
2) Install .NET 2.0 SDK (Tools only, no need for samples or docs)
This is required so you get the .NET Framework 2.0 Configuration tool in control panel -> admin tools. Unfortunately it's no longer distributed as part of the framework as it was in V1.1.
3) Install Excel Professional 2003, use advanced customization and add under Excel ".NET Programability support" to be installed to disk
4) Install Office SP2
5) Install VSTO SE Runtime

Use the .NET Framework 2.0 Configuration tool and right click on My Computer-> Runtime Security Policy-> Machine-> All_Code-> LocalIntranet_Zone and select "New...".

On the first tab enter any name for your app.
On the second tab set the membership condition to be URL and enter the folder containing your published files followed by \* (eg file:////MyServer/my excel app\*).
On the third tab change Permission Set to Full Trust.

Once you have done this you should be able to open the Excel spreadsheet from the published location.

This assumes you have used the Publish Wizard in VS2005 to create the application folder which contains the .xls and .dll files together with the associated manifests and config files.

Friday, March 09, 2007

www.vodafone.net does not work with IE7 or FF2

Today I discovered that www.vodafone.net does not work with IE7 on Vista or FF2. It works OK in IE6. This is because the site uses 40 bit MD5 encryption which (along with 56 bit) has been discontinued in IE7 and FF2 because it is too insecure.

If you want to access the site you can use Firefox 2 and type about:config into the location bar and press enter. Find the setting security.ssl3.rsa_rc2_40_md5 and double-click on it to change its value to true.

I cannot find a solution for IE7 from the client end. Vodafone simply need to replace the totally insecure 40 bit encryption on their servers.

I cannot believe that Vodafone can run a 'secure' site that relies on obsolete 40 bit encryption.

Thursday, March 08, 2007

Sony W880i

I upgraded my phone to a Sony Ericsson W880i this week. It's a cool phone. The only thing it could do with is WiFi. Unfortunately I cannot get any 3G coverage so I cannot make any video calls.

Another problem is the lack of Vista support. The PC Sync software will only install in XP.

Wednesday, February 21, 2007

SQL 2005 Dump Transaction Log

I always forget how to dump a transaction log on SQL server. Here is the answer:-

BACKUP LOG {DatabaseName} WITH NO_LOG
GO
DBCC SHRINKDATABASE ({DatabaseName}, 0)
GO
DBCC SQLPERF(logspace)
GO

Friday, December 15, 2006

Fluke DTX 1200 & DTX 1800 not on Vista

I was trying to download some test results from a Fluke DTX 1200 and 1800 yesterday but discovered that the Fluke USB drivers do not work on Vista.

Fluke tech support have no idea when drivers will be available.

In addition the help system in LinkWare 2.6 is based on WinHlp32.exe and this is not available in Vista so you cannot view the help file.

Tuesday, December 12, 2006

Create .lnk file in C#

Here is some C# code to create a shortcut to a folder. These are actually .lnk files which are handled in a special way by Windows Explorer.

First include a reference to C:\Windows\System32\wshom.ocx

Second, include the following using statement :-

using IWshRuntimeLibrary;

Third, Here is the code :-


// This creates a Folder Shortcut

IWshShell wsh = new WshShellClass();
IWshShortcut shortcut = (IWshShortcut) wsh.CreateShortcut (shortcutpathfilename);

shortcut.TargetPath = targetdir;

shortcut.Save();

shortcutpathfilename is a path & filename of the .lnk file.

targetdir is the directory the link points to.