November 2006 - Posts

ASP.NET 2.0 - Playing with ActiveDirectory (check whether user exists, etc)

It had been a wonderful day of my programming life. I was given an assignment to look into active directory, and test out the web development authentication with it. It was really a challenging one. Just imagine I have zero active directory experiences but I am still able to cope in 2 days.

 

This is my scenario:

 

*I configured my 1st active directory and domain controller + DNS. Cool :P Now I understand a lot on OU, CN, LDAP by configuring. Never like infra stuff in the past, but I enjoyed this time.

 

You have an active directory in a domain controller (under this domain community.com). You have a web server which is connected to this domain. The member’s profile of the system is stored in the database. There is a column in the member’s table that consists of the active directory account. So it means that certain user has access to the active directory and some cannot. The column must be null by default. The reason why there is a profile in the database as we are working on a huge system. Within the huge system, there is already existing application utilizing the database for member’s profile. There are around 10,000 members within this private system and I don’t think it is a good idea to move to Active Directory, then ask each of the members to change password. There are more design considerations on this.

 

User A: PC connected to the domain

A user runs this web application; he will be auto authenticated and able to access certain resources on the site

 

User B: PC not connected to the domain

A user runs this web application. He will be prompted to enter username, domain and password to authenticate. If fail, will get a 401.1 error.

 

Looks simple right.

 

So at first, when I heard it was related to ActiveDirectory I tried to look into System.DirectoryServices and ActiveDirectoryMembershipProvider. Trust me that it might looks very easy from the outside, but it was not a simple task to work on the API. You need to understand ActiveDirectory and LDAP first before taking this adventure ride. It was really a pain to me working on this.

 

I tried a number of ways:

 

a) To force to use System.DirectoryServices, in the directory security I assign the process to be a domain user or admin account. By doing this, all your web application has the access to this specific account, which sounds kind of stupid. Coz I will have so many members and each of them must be authenticated by itself.

 

Mission Failed.

 

b) I try to look into ActiveDirectoryMembershipProvider. Looks cool. You have to store a specific user account inside the web.config itself which did not make any sense to me. Just like point a. Try not to place a domain admin account inside here. You need to specify the account name and password.

 

Mission Failed.

 

c) How about doing some impersonation? Then get the logon user name by “Environment.UserName” or “System.Web.HttpContext.Current.User.Identity.Name”. Then pass this windows logon id for DirectoryEntry + DirectorySearch query. Sigh … it does not work. But if you do it manually, you will work. Kind of tough to explain on this one. You must set impersonation here or else you will not get accurate information. Furthermore, maybe I did a badly job on my DirectorySearch query. Oops L

 

d) Then I thought it could be authorization issues. But thinking twice, it should not be.

 

Life moves on and on… still not able to solve it. Suddenly something hit my mind :D

 

Anyway you will be surprised that I do not use any codes that involve System.DirectoryServices or ActiveDirectoryMembershipProvider on this one. I did not use the LogonUser API as well. So how did I solve this problem?

 

The answer is there is a solution for everything. When someone is being assigned a task, try to think if there is a simpler way of doing thing. Learn a lot this time.

 

With this implementation, I do not need to configure anything in the IIS, which is the best part. So anonymous user will be prompted to login. Hehe :D

 

        if (!Page.IsPostBack)

        {

            string test = System.Web.HttpContext.Current.Request.ServerVariables["LOGON_USER"].ToString();

 

            if (System.Web.HttpContext.Current.Request.ServerVariables["LOGON_USER"] == "")

            {

                System.Web.HttpContext.Current.Response.StatusCode = 401;

                System.Web.HttpContext.Current.Response.End();

            }

            else

            {

                // Request.ServerVariables["LOGON_USER"] has a valid domain user now!

 

                string changeDomainName = (Environment.UserDomainName).Substring(0, Environment.UserDomainName.Length - 1) + ".com";

                string account = Environment.UserName + "@" + changeDomainName;

                enObj.sADAccount = account;

 

               

 

                Response.Write(brObj.GetFullName(enObj));

            }

        }

 

 The rule is simple here. If you are authenticated, you will query the database and get the full name of the member’s profile. If not, you will be prompted login screen (max 3 times). If failed, then you will get “you are not authorized error”. If you manage to login, you still can get the full name. Sound easy right J

 

Anyway the term to find here is single sign on. I never thought of that till 5 minutes ago – 10.55 p.m.

Posted by chuawenching with 3 comment(s)

CommunityServer 2.1 SP 1 - Critical Error: SiteUrls.Config

“Critical Error: SiteUrls.Config

The file containing the SiteUrl Data could not be loaded.

Please contact your CommunityServer administrator. A technical explanation of why this error is caused is below. (quite a long listing)”

 

This is definitely an interesting problem. I was not able to sort this out. I tried to enable files / folders permission manually for IIS_WPG and Network Service, but I could not able to resolve this problem. I tried to set the application pool as a default one, yet failed to work. It was a recommended way in the internet. I looked into my database and also made sure my connection string was correct. However, I was not able to solve this problem.

 

Take note, this problem only works when you have a Windows Server 2003 which happens to be a Domain Controller itself (Active Directory will be installed and all local accounts will be removed). If you do not perform this action, you will not face this problem.

 

So how do you solve this problem? After my personal research, I found out a way to overcome this problem.

 

Open up your Visual Studio 2005 command prompt and type this command as below:

 

aspnet_regiis –ga Community0\IIS_WPG

 

Community0 is my current domain name.

 

Test it and you will not see the problem as mentioned above. Good luck then.

 

Updated: Sometimes it works and not. Funny. I got a Thread was being aborted error. Have to look into this further.

Posted by chuawenching with no comments

This article is interesting "Nine Things Developers Want More Than Money"

Been a very busy weeks on projects. Hardly find a time to blog :D

Anyway I found out a new article which was very interesting. Check it out here.

http://www.softwarebyrob.com/articles/Nine_Things_Developers_Want_More_Than_Money.aspx

I definitely agree with some of the points here. I am a developer too, and money is not about everything in my career.

Cheers.

Posted by chuawenching with 1 comment(s)

ASP.NET 2.0 DropDownList - FindByText / FindByValue

Assuming you want to achieve this:

 

1 GridView with 2 columns of databoundfield. There is 1 buttonfield called Edit with a command “UpdateThis”. When you click on Edit, it will open an input panel and shows 2 dropdown list controls. The 1st dropdown list has 5 items inside. The 2nd one has 10 items inside. From the gridview’s values selected, you need to dynamic pass the values into the dropdownlist while maintaining the state of it. How can you do that?

 

If you try to do this, you will not achieve the desire results:

 

ddlProgramOwnerDivisionName.SelectedItem.Text = dropdownValue;

 

By doing that, it will overwrite existing items. Not a good idea at all. Try this instead; you can either set FindByText or FindByValue.

 

ddlList1.SelectedIndex = ddlList1.Items.IndexOf(ddlList1.Items.FindByText(dropdownText));

 

Or

 

ddlList1.SelectedIndex = ddlList1.Items.IndexOf(ddlList1.Items.FindByValue(dropdownValue));

 

Have fun.

Posted by chuawenching with 1 comment(s)

ASP.NET 2.0 GridView Tips - Keyword like "Update" and BoundField Visible=False

1) Tip #1

Assuming you have this code:

<asp:ButtonField Text="Edit" HeaderText="Action" CommandName="Update" />

Then you have an event to get that command and process the necessary update to your field as below:

protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)

{

                if (e.CommandName == "Update")

                {

                                // process your update codes here

                }

}

You will be surprised that you will not get it to work. It will prompt you that you do not handle the RowUpdating/RowUpdated event as well.

So the tip here is, does not use keyword like Update. Named it something else likes this “UpdateModuleA” or something meaningful.

2) Tip #2

Assuming you have this code:

<asp:BoundField DataField="Remarks" Visible="false" HeaderText="Remarks" SortExpression="ExpRemarks">

 

Assuming you have a gridview that will only show 5 columns of data bound values. You will call a stored procedure which actually passes 6 columns of values. One of them is actually an invisible column, just like above.

 

When you try to get values from the GridView by passing the code as below:

 

grvExpenditure.Rows[index].Cells[ColumnIndex].Text

 

You will not find the value you want. But when you try to set the column to visible=true, you can see the value.

Posted by chuawenching with no comments

ASP.NET 2.0 CompareValidator Part 2 - ControlToValidate/ControlToCompare do not work in MasterPage

This is a part 2 of the earlier one. Why there is a part 2? You will know it soon. Before you read this, I will recommend you to read my 1st post first http://community.sgdotnet.org/blogs/chuawenching/archive/2006/10/30/ASP.NET-2.0-CompareValidator-_2D00_-Compare-Dates-through-custom-user-controls.aspx

I will like to say thanks to Alvin (http://alvinzc.blogspot.com/) for helping me out to figure out this one. Something that I don't really know how to fix. Cool buddy :)

Like I said earlier you need to pass a unique id to the parent web form's compare validator in order to get it work. But there is one big problem.

What if you have a web form which uses a Master Page? I can tell you this; it will not work at all using my earlier technique. Not for master pages.

There are 2 issues here, the 1st one is the common and the 2nd one happens to migrated .NET 1.0/1.1 to 2.0 user controls:

Common 

i) Master Page

Without Master Page, it will give you this:

uclCalendar$textBox 

With Master Page, it will give you this:

_ctl$ContentPlaceHolder$UclABC_GeneralInformation$uclABC_GeneralInformationDetail$

uclCalendar$txtDateValue

Why should I know this? How does it make any differences in this scenario? You should read this link http://www.odetocode.com/Articles/450.aspx under "FindControl, Javascript and Naming Containers"

Extracted from that link "FindControl only searches inside the current naming container. Using the FindControl method on the Page reference means we won’t be searching inside of MasterPage control. "

By using this way, you will get error like this "Unable to find control id ..."

Uncommon

ii) I had this user control ported by my ex colleague. It was a calendar control with javascript/vbscript. Complicated control which I didn't really understand the code. J

However I try to compare the codes with .NET 2.0 user controls. Looks okay, like inherit from the same Web.UI.UserControl. No special code, just extra html/javascript rendering and calling vbscripts.

When you try to do like the first way (read my 1st post on this) by passing the unique id, instead of giving you this:

*If you not sure about INamingContainer, you can read it at Alvin's blog or the internet

Container$UserControl$TextBox

But it gives you this:

Container:UserControl:TextBox

Don't ask me why? Coz I really not sure what happen? It is the same code implementation like a normal .NET 2.0 web user control.

I had tried a number of solutions:

a) CompareValidator.ControlToValidate = (uclCalendarStartDate.FindControl("txtDateValue") as TextBox).UniqueID;

b) I tried to create a property and using string.replace the character ‘:’ to ‘$’. But still failed.

c) I tried to create instance of ContentPane, and slowly from container down to specific controls

In order to solve this, try this code then. Take note; only use this code if you are facing similar problems like me as I am using String.Format. But it will work in Master Pages for sure. J

///////////////////////////////////////////

// Functional Code

// *Thanks to Alvin

 

TextBox startTextBox = uclCalendarStartDate.FindControl("txtDateValue") as TextBox;

cmpDate.ControlToCompare = String.Format("{0}${1}", uclCalendarStartDate.ID, startTextBox.ID);

 

TextBox endTextBox = uclCalendarEndDate.FindControl("txtDateValue") as TextBox;

cmpDate.ControlToValidate = String.Format("{0}${1}", uclCalendarEndDate.ID, endTextBox.ID);

 

//

///////////////////////////////////////////

If you are not facing the uncommon problem just do this instead:

cmpDate.ControlToCompare = uclCalendarStartDate.ID + “$” +  startTextBox.ID);

Yes my problem is solved now. Cool J Just to share with you all if you face this problem.

Posted by chuawenching with 2 comment(s)

Wee Hyong on Channel 9

Wow... I can't believe my eyes

http://channel9.msdn.com/Showpost.aspx?postid=252457

Just curious, how can a person being interviewed by Channel9?

Anyway congrats Wee Hyong.

Posted by chuawenching with 1 comment(s)

ASP.NET 2.0 - Response.Redirect error "Unable to evaluate expression ..."

Look at this:

Response.Redirect("~/ABC/Form/frmABC_GeneralInfo.aspx");

Does it looks like it will give you an error? You will be suprised on this one. Once a while, I am not sure why it happens once a while, you will get an exception like this:

ex = {Unable to evaluate expression because the code is optimized or a native frame is on top of the call stack.}

You will start scratching your head, what happened?

Just take it easy :) After investigating further online, I found out here http://support.microsoft.com/kb/312629/EN-US/

Thanks god it saved my life. What you should do or advised to do are ...

Response.Redirect("~/ABC/Form/frmABC_GeneralInfo.aspx", false); // Set false, as the 2nd argument

It worked fine now. It applies to Respond.End and Server.Transfer.

Thanks.

Posted by chuawenching with 1 comment(s)

ASP.NET 2.0 GridView DataFormatString for Amounts (SqlMoney)

I might have discussed this in my blog earlier. However I will like to share my thoughts again.

Check this:

http://community.sgdotnet.org/photos/chuawenching/picture44026.aspx

As you can see, if I don't set the DataFormatString (don't forget to set HtmlEncode="false"), I will get the 1st picture. Why 0.0000 instead of 0.00? Read my earlier posts :)

Since Amount should display the currency values like $0.00 instead of 0.0000. So you should format it as {0:C} or {0:c}. Take note, there is no use if you try this {0:C2}.

You will get your results as you want. Assuming it is based on en-US okay :)

But there are times when customer asks you to place the currency itself that gridview column HeaderText like this "Amount (RM)". RM represents the currency in Malaysia. So there is no point display $0.00.

For DataFormatString on currency, I can't see a way to remove the dollar sign '$'. So to do that, you have to format it to decimal like in 3rd picture using {0:#,##0.00}

Like this, it looks more reasonable and customer will be happy.

Posted by chuawenching with 3 comment(s)

ASP.NET 2.0 ValidationSummary Display

Do you know that in ASP.NET 2.0 ValidationSummary control, you can set it to be display directly in the form or appear as a message box?

See the 2 pictures below:

a) rendered in the form

<asp:ValidationSummary ID="ValidationSummary1" runat="server" HeaderText="These are my errors" ShowMessageBox="False" ShowSummary="True" />

http://community.sgdotnet.org/photos/chuawenching/picture43784.aspx 

b) display as a message box

<asp:ValidationSummary ID="ValidationSummary1" runat="server" HeaderText="These are my errors" ShowMessageBox="True" ShowSummary="False" />

 http://community.sgdotnet.org/photos/chuawenching/picture43783.aspx

There are more things you can do in this ValidationSummary control. Just have to tweak it a bit :P

Posted by chuawenching with 2 comment(s)

My First Swensen's Regular Earthquake Ice Cream

First of all, I like to say Swensen's ice cream is really quite nice. Less sweeter than Baskins but very easily to melt. Less flavours than Baskin, but quite okay. Just don't try the yummy raisins, I dislike it. Same to my best friend Nick. Suprisingly ladies like to eat it :) Wonder why.

http://community.sgdotnet.org/photos/chuawenching/picture43697.aspx

I tried the regular earthquake yesterday, 8 scoops of different flavours. With 4 or 8 toppings. Not sure which one?

One more thing, Swensen's food meals (like the one I ordered Gigantic Mega Burger). I didn't know it was that huge. So many layerss (hams, beef, eggs, vege, cheese, more hams). You know, marketing ... so normally if the brochure says huge, but always ended up small size. But Swensen's food is really different. Wow! Nick ordered some chicken barbeque thing... He thought he would be expecting a quarter chicken, but ended up half a chicken. Wow.. only costed him RM 17.00. Fairly cheap for western food in Malaysia (compared to TGIF, American Chillies, etc).

Try every Tuesday on cheaper discounts for ice cream. Hmm, I think is 50%.  

Plus there are a lot of ice cream vendors near my office. I think I will get fatter very soon. Hehe.

Posted by chuawenching with no comments