Backing up and fail-safing your ADAM / LDS instance

This could potentially be very long post, but I am going to stick to the high-level objectives only. Leave a comment if you want more details and I will reply to you.

OBJECTIVE: Your users are stored in an ADAM database and your ASP.net website sits on top of this utilizing the ASP.Net membership framework to interface with ADAM to authenticate users. You want to make sure that you are covered in case of any disaster scenarios (like disk corruption, ADAM server blowing up, and manually unintended corruption by your system admins)

PROBLEM: If you notice carefully, we are talking two different things here.

  • Hardware failures – i.e Poof!!!! and your ADAM instance just disappears. Panic your website is down!!!
  • Manual data corruption – So, if your sysadmin does something foolish and say he updates all users with the same last name using a vbs script or something. This is more insidious because your website is not down, ADAM is not down but your user data is corrupt.

SOLUTION: So although we have two distinct ways of getting into trouble, the end result is the same and the solutions are also the same. But first let us talk about what is required at the minimum to recover from a failure scenario.

ADAM Replication – Fortunately, for us, ADAM (or LDS) comes out of the box with support for replication. What this means, is once your main ADAM instance is up and running you can install multiple ADAM instances on other servers as “replicated instances” and all these servers magically know how to talk to each other and keep their data in sync with each other.

Plus, it gives you the flexibility of turning on “two way” replication. i.e you change data on the replicated instance and the main ADAM server reflects these changes. You have the option of staggered replication. i.e the replicated instances will receive deltas from the main server only after XX minutes or hours. Any light bulbs yet on how you will use this to recover from bad things happening?

Windows Backups – I know, I know. Nobody uses Windows Backup and Restore. But this is the perfect place to use it. You simply setup a backup job which backs up your ADAM directory to a file server. We have done it where I work and this gives us nightly backups for every day going back to last 60 days. Also, ADAM has a lock on the files on disk, but Windows Backup used Volume Shadow Copy which takes care backing up files even if they are locked by a process. We use the append option, so backups are not overwritten every night, but are appended (Keep an eye on that backup file though…it can grow pretty fast !!!)

So, now that we know the proper way to protect us from bad things. Here is how you will apply it to various situations:

  1. Hardware failures – If your main ADAM server blows up, you could simply point your website to the replicated instance since it has the latest and greatest data.
  2. Data Corruption by SysAdmin – So, if your sysadmin writes a script which updates every one’s SSN to 000-00-0000 for example, you could either restore the ADAM data from last night’s backup (believe me it is amazing simple to overwrite ADAM data from backup and get up and running in no time). Or, if you had staggered replication setup (i.e replicated instances receive change deltas only after 1 hour and your sysadmin reports the data corruption to you in time, you can shutdown the main ADAM instance and point your website to the replicated instances because they still have good data.

Bottom-line, you can use replicated instances for instantly recovering from the failure scenario, while you are busy building the main instance from the backups. If you have two way replication setup then when you bring the main instance online, the replicated instances will send their deltas back to the main instance (so for example 100 users signed up before you could restore the main instance from backups), these 100 users are only in the replicated instance now. But the moment you bring back the main ADAM instance up, replication will send these users back to the main instance and you will be in sync.

Control ctl00_ContentMain_ xxxxx of type ‘GridView’ must be placed inside a form tag with runat=server

Recently I wrote some simple code to Export the contents of a GridView to Excel. You know, the basic stuff like changing the type to application/ms-excel content and removing Images from the GridView (so that they don’t appear as missing images in the Excel sheet) and converting Hyperlinks to Labels.

The Problem : But when I ran my code, I got the dreaded error :Control ctl00_ContentMain_ xxxxx of type ‘GridView’ must be placed inside a form tag with runat=server

The Solution :  Just place this code in your codebehind. And that will fix ya! :

public override void VerifyRenderingInServerForm(Control control) {}

The Explanation :
Asp.net calls VerifyRenderingInServerForm to ensure that every single server control is being rendered inside a <form runat=”server”> tag. Which makes sense for a web page.  But it doesn’t make sense for an Excel sheet. Since, we just want to send the Gridview contents to the client and not the Form tag.

All we are doing here is overriding this Method and falsely reporting to Asp.Net that our Control is actually being rendered inside a <Form Runat=’server’> tag.

Hope it helps…

An enhanced UpdateProgress control, which shows an animation at the point where mouse was clicked

Also Check Out: My latest venture. http://loqly.me – a way for you to ask questions and get answers about local businesses around you. iTunes link:http://bit.ly/e5u4jv (only available in US for now)

WHY DO IT ???

Before you read the post, let us answer why did I waste time in changing the behavior of the UpdateProgress control. Here is the deal. Suppose, you have 5 or 6 controls on your page, which when clicked result in an Ajax call. You know that the UpdateProgress control is pretty much static, meaning, wherever you put the <asp:UpdateProgress> tags, that is the place where the progressbar will show up when your Ajax call is in progress. So, no matter which control the user clicks the progressbar is being shown in a fixed place. Why may some people consider this bad ???

  • You or anyone looking at the screen, may lose track of why you are waiting because the progressbar just shows that you are waiting not what has triggered the wait.
  • If the page is long, and the progress bar is at a fixed place, there is a possibility that you clicked a control which is a scroll length away from where the progressbar will show.
  • If the control which was clicked can give a feedback for the user to wait, it prevents the user from clicking the same button twice.

So, much for justifying why I wrote this control. But those were just a few thoughts behind it. Let us get back to the nitty gritties. Btw, here is a YouTube vide of how it looks (video is a little fuzzy but it shows what I am trying to communicate)

The Code :

I will not go into too much detail explaining the code, because I am sharing the source code with you. But here is the high level overview :

  • Create a new control, which inherits from UpdateProgress control
  • Embed two resources in your control assembly. (the default hourglass image and the javascript used to make this control work)
  • Override Render method, and inject javascript. The javascript will hook into the AJAX InitializeRequest and EndRequest events, by calling add_initializeRequest() and add_endRequest() methods of Sys.WebForms.PageRequestManager class.
  • The first Image control in the <ProgressTemplate> of the UpdateProgress control will be assumed to be the hourglass image, and if you haven’t set an ImageURL value for it, a default image will be supplied. You can create your own hourglass images from this awesome website : http://www.ajaxload.info/#preview

That is it. Do leave comments and suggestions if you find this control useful. I am also attaching the source code and the assembly. If you don’t want to see the code, just use the assembly and things should work right away.

Use declarative security to show a Security trimmed navigation Menu along with enforcing role based security for your website

Hi All,

Here is what we are trying to achieve in this article.

  • Provide side-wide security without writing a single line of code
  • Show navigation menus to the user, which automatically hide the options which the user doesn’t have access to (also called Security Trimming)
  • Use declarative syntax in web.config to tighten your security so that even if the user knows a particular URL, he cannot get to it, unless he is explicitly granted access to the URL.

Background :-
I have this hierarchy of folders
/
|—UserManagement

|—Default.aspx

|—ChangePass.aspx

|—AdminPage.aspx (Only admins should have access to this page, and the menu control shouldn’t show this option)

I am using a ASP:Menu control and I want all logged in users to be able to see all menu options except the AdminPage link, but I want the administrator to be able to see every single menu option.

Here is a snapshot of the relevant tags from my web.config file —————————————————————-

<!–In this case my Roles are stored in an XML file, your roles can reside in SQL Server or AD or ADAM, it doesn’t matter–>

<add name=AzManPolicyStore connectionString=msxml://C:/Azman.xml />

<!–Here is how we enable the role manager. In this case the built in ASP.Net website config tool will automatically read and write Roles and their membership info in the file mentioned above. i.e Azman.xml–>

<roleManager enabled=true defaultProvider=RoleManagerProvider>

<providers>

<add name=RoleManagerProvider

type=System.Web.Security.AuthorizationStoreRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, publicKeyToken=b03f5f7f11d50a3a

connectionStringName=AzManPolicyStore

applicationName=TestApp/>

</providers>

</roleManager>

 


<!– To make security trimming and declarative security work, we need to follow the pessimistic approach. i.e first we will deny everyone access to our website–>

<authorization>

<deny users=*/>

</authorization>

<!– Then we selectively start permitting users and or Roles access to folders/files–>

<location path=UserManagement>

<system.web>

<authorization>

<allow roles=“Administrators, Managers, Users”/>

</authorization>

</system.web>

</location>

<!– Deny access to everyone except Admin on the Admin only page–>

<location path=UserManagement/AdminPage.aspx”>

<system.web>

<authorization>

 

<deny users=*/>

<allow roles=“Administrators”/>

</authorization>

</system.web>

</location>

Here is the excerpt from my Default.aspx page, which has the menu control ———————————–
<asp:Menu ID=Menu1 runat=server DataSourceID=SiteMapDataSource1

Orientation=Horizontal>

</asp:Menu>


<!–Note that I am explicitly mentioning Sitemapprovider=”” attribute, although if I dont mention it, it should pick up the default provider. But this is what made the security trimming work for me. If you don’t do this, the Security trimming in menus will not work !! –>

 

<asp:SiteMapDataSource ID=SiteMapDataSource1 runat=server SiteMapProvider=XmlSiteMapProvider />


 

 

Here is my Web.Sitemap file—————————–

 

<?xml version=1.0 encoding=utf-8 ?>

<siteMap xmlns=http://schemas.microsoft.com/AspNet/SiteMap-File-1.0>

<siteMapNode url=~/Default.aspx title=Home description=“”>

<siteMapNode url=~/UserManagement/Default.aspx title=Manage security settings description=“”/>

<siteMapNode url=~/UserManagement/ChangePass.aspx title=Change your password description=“”/>

<siteMapNode url=~/UserManagement/AdminPage.aspx title=Admin Only Functions description=“”/>

</siteMapNode>

</siteMap>

 

 

 

Thats it. Just by following these simple steps, you will have rock solid security for your website. And you don’t have to write a single line of code too.

 

AZMan Rolemanager keeps roles cached and does not reflect changes for a long time

If you have found this article, you already know who Mr. Azman is and what I am talking about here. So, I will not go into the details to explain you what asp.net 2.0 Rolemanager is and what all options it gives us to store role and membership information.

Here is the excerpt from my web.config file :

<!–In this case my Roles are stored in an XML file, your roles can reside in SQL Server or AD or ADAM, it doesn’t matter–>

<add name=AzManPolicyStore connectionString=msxml://C:/Azman.xml />

<!–Here
is how we enable the role manager. In this case the built in ASP.Net
website config tool will automatically read and write Roles and their
membership info in the file mentioned above. i.e Azman.xml–>

<roleManager enabled=true defaultProvider=RoleManagerProvider>

<providers>

<add name=RoleManagerProvider

type=System.Web.Security.AuthorizationStoreRoleProvider,
System.Web, Version=2.0.0.0, Culture=neutral,
publicKeyToken=b03f5f7f11d50a3a

connectionStringName=AzManPolicyStore

applicationName=TestApp/>

</providers>

</roleManager>

 

You must have noticed that in this example my Role data store is c:azman.xml file. The problem that I was facing was that, whenever I would make changed to my role membership by using ASP.Net web configuration tool, it would update the .xml file perfectly fine, but ASP.net would still continue using the cached roles that it had loaded when I started my application.

After some research I found out that, the the default behavior of Rolemanager is to cache roles in memory for 60 minutes. This might be unacceptable for some people. So, the solution for this is to add cacheRefreshInterval=”x” to your RoleManagerProvider node. The X can be as low as 1 which would mean 1 minute, but you can change to whatever suits you best.

<add name=RoleManagerProvider

type=System.Web.Security.AuthorizationStoreRoleProvider,
System.Web, Version=2.0.0.0, Culture=neutral,
publicKeyToken=b03f5f7f11d50a3a

connectionStringName=AzManPolicyStore

cacheRefreshInterval=”1

applicationName=TestApp/>

How to provide ASP.NetWebadminfiles (WSAT) like user management for your hosted or online site

I recently was working on a ASP.net 2.0 website. I used the ActiveDirectoryMembershipProvider and used the membership API along with Login controls to provide a nice experience to the user with features like Sign up as new user, change password, password reset, login and all related functionality which any website offers you.
When my code was in development, I had the built in WSAT (ASP.Net website administration tool), which I could launch from Visual Studio.Net and I could easily administer my website. You can launch this tool using the Website–>ASP.Net configuration menu. This tool is really cool and without writing a single line of code you can easily manage all the security and settings for your website.
But the problem arises when you move your code to production. The WSAT tool only works locally (i.e via localhost). By default, it prohibits remote access.
In this post, I will explore two ways of managing your website security remotely.

Option 1 :

Make changes to the WSAT tool to make it work remotely
The WSAT tool with source code is located in your C:WINDOWSMicrosoft.NETFrameworkv2.0.50727ASP.NETWebAdminFiles folder. To make it accessible on the network, all you have to do is go to IIS–>Create new virtual directory–>Point to the above folder and remove anonymous access from directory settings page.

Then you need to access it the same way your local ASP.Net configuration tool is accessed i.e via a URL which resembles something like :
http://SERVER/AdminTool/default.aspx?applicationPhysicalPath=C:Inetpubwwwrooottestsite&applicationUrl=/testsite

But you will notice, as soon as you try to access it, it will spit out an ugly error “This tool cannot be remotely accessed.“. This is because by default the tool is locked down for local access only. To fix this, all you need to do is open C:WINDOWSMicrosoft.NETFramework64v2.0.50727ASP.NETWebAdminFilesApp_CodeWebAdminPage.cs file in a text editor and change line#488 FROM >> if (!application.Context.Request.IsLocal) { << TO if(false){

Once you save your file, the tool will allow remote access.

Option 2

:
Some people may not allow you to mess with the production webserver like above, becasue it involves changing a .net framework file and it can be a security risk. 4guysfromrolla.com has done a nice thing, they have written a generic user management piece which works just like WSAT and you can easily include it as part of your website. Just package it with your website, since it comes with source code (although the source code is in C#). You just have to follow a few steps to make it work for you. You can find the article which talks about the custom tool here : http://aspnet.4guysfromrolla.com/articles/052307-1.aspx and download the source code here http://aspnet.4guysfromrolla.com/code/ezdeploy.zip

Here are few things I had to do to make it work for my website:

  • Copy the source code to a subfolder in my site
  • Delete the web.config from the root level which comes with the source code
  • Move the 4guys.master file to the root of my website (this is mentioned in the article)
  • Move images from the i folder to the images folder of my website and change links which point to these images (this is mentioned in the article)
  • Change the stylesheet link in 4guys.master file to point to the correct location.
  • Move _controls folder to the root of my website
  • Delete all subfolders except admin from the source code. We dont need these.
  • Changed the 4guys.master to remove menu links to pages which are irrelevant for the security piece.
  • NOTE: If you are using ActiveDirectoryMembershipProvider, you will get bunch of errors like
    The property 'LastLoginDate' is not supported by the Active Directory membership provider.]
    
    System.Web.Security.ActiveDirectoryMembershipUser.get_LastLoginDate()

To solve this all you have to do is remove following lines in all the .aspx pages.
<asp:BoundField DataField=”lastlogindate” HeaderText=”Last Login Date” />
<asp:BoundField DataField=”lastactivitydate” HeaderText=”Last Activity Date” />
<asp:BoundField DataField=”isonline” HeaderText=”Is Online” />

Show multiple columns in a dropdown with proper alignment

The problem : We have an ID column and a Name column in our database. We want to show both in a dropdown but if you concatenate both, you will notice that the alignment goes out of whack.

eg. 1
———————–
1 – Item one
2 – Item two
20 – Item twenty
100 -Item hundred
3 – Item 3

See what I mean. The alignment is completely out of whack. What we want to achieve here should kinda look like below.


eg. 2
———————–
1   – Item one
2   – Item two
20  – Item twenty
100 – Item hundred
3   – Item 3

So we have multiple problems here. One is that in most of the fonts every character takes up a different amount of space. eg. A will take more space on the screen than lower caps a. The other problem, is how to write our stored procedure in a way that it pads the extra spaces as in e.g 2 above

The Solution :
1. The solution to the problem with characters taking different screen space is very simple. All we need to do is use something called a Monospace font. Create a style like below :
/* Monospace font – same width for all characters */
.monospace{ FONT-SIZE: 8pt; font-family: monospace;}

If you are using ASP.Net just set your dropdown’s cssClass=”monospace”. If your are using plain html, just set the class=”monospace”

2. That takes care of problem #1. Now, let us kill the 2nd bird.
In your stored proc, do something like this :

SELECT
[Id],
[Name],
LEFT(LEFT(Name,25) + REPLICATE(‘.’,25),26) + Cast([Id] as varchar(3)) As IDName,

That is it. The IDName column will give you the perfectly aligned characters and Dots will be stuffed in between the name and id so that they align up nicely. The output is something like this (for someone reason wordperss is messing up the formatting..but I hope you get what I mean):

Item1…………1
Item number t….2
Item# 20 ……..20
Item # hundred…100

If you want the output, flipped around. i.e IDs showing first and Names showing later, change your select to this :
SELECT
[Id],
[Name],

LEFT(Cast([Id] as varchar(3)) + REPLICATE(‘.’,4),5) + Left([Name],25) As IDName,

This will give you output like this :
——————————–
1……Item1
2……Item number 2
30…..Item 30
299….Item 299

Hope you find this useful. Happy coding!!!