Wednesday, September 8, 2010

Role based security in asp.net

Step1: While designing users table in the database add one more column called 'roles'  or we can have a table called 'Users' another table called 'Roles' and we can have one more table called 'UserRoles' with many-to-many relationship (one user can have many roles and one role can be assigned to many users).

Here I included the column 'Roles' in tblUsers table itself.
image 

 Step2: Now we need a method using which we can get the roles from database, probably ',' separated or a string array of roles. We can write this method in a class, which is placed in App_Code folder (for say UserInfo.cs).


public static string[] GetRoles(string un)
    {
        SqlConnection con = new SqlConnection();
        con.ConnectionString = ConfigurationManager.ConnectionStrings["csMSNET"].ToString();
        try
        {
            con.Open();
            string sql = "select roles from tblUsers where UserName = '" + un + "'";
            SqlCommand cmd = new SqlCommand(sql, con);
            string strRoles = cmd.ExecuteScalar().ToString();
            return strRoles.Split(',');
        }
        finally
        {
            if (con.State == ConnectionState.Open)
                con.Close();
        }
    }



Note: Here I am  getting the connection string from web.config, which is named 'csMSNET'.

Step3: This is an important step, as we are going to attach the roles to the given user.

User.Identity.Name gives us username and once we have roles, we have to change the user which is there in the current context, so that it includes both username as well as roles.

The right place to attach roles to the user is Application_AuthenticateRequest event in Global.asax.

protected void Application_AuthenticateRequest(object sender, EventArgs e)
    {
        if (Request.IsAuthenticated)
        {
            string[] arRoles = UserInfo.GetRoles(User.Identity.Name);
            Context.User = new System.Security.Principal.GenericPrincipal(User.Identity, arRoles);
        }
    }
Note: Before getting the roles and attaching them to the user, first you need to ensure that the user is authenticated or not.

Step4: Now user has identity and roles, both.  Now based on roles deny or allow access.

ex: Deny access to all anonymous users and also users with r1 or r3 roles.
<authorization>
      <deny users="?"/>
      <deny roles ="r1, r3"/>
</authorization>

We can programmatically find out if a given user has some role or not using IsInRole method of Page.User

Ex:
          User.IsInRole("r1")

Conclusion:  Generally what we do is based on the role, we will hide certain items on the page.  If the user is administrator, I would provide a facility to add and delete, but if the user is some report generator, I would just give the facility of view, no add or delete. 

So, we need to set the visible property to false, in that way we use User.IsInRole to know the given user is in the specified role or not. 


Uploading a large file using FileUpload Control in ASP.NET

By default FileUpload control in asp.net uploads upto 4MB size of files and it doesn't allow 'exe' files.

To increase the file size limit that a fileupload control can upload, just add the following line to web.config

< system.web >
           < httpruntime executiontimeout="500" maxrequestlength="4096" >
< /system.web >

Here 'executionTimeout' in milliseconds and 'maxRequestLength' in kb. So, increase the maxRequestLength to upload large files.

URL Routing on IIS7 and ASP.NET 3.5 with SP1

Purpose : To allow users browse your site using extensionless and user-friendly urls.
Step1: Add a Reference to System.Web.Routing to Your Project
Step2: 
Add UrlRoutingModule to 'httpModules' under <system.web> and to 'modules' under  <system.webServer>;
Add UrlRoutingHandler to 'handlers' section under <system.webServer>

<configuration>
   ...

   <system.web>
      ...
      <httpModules>
         ...
         <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      </httpModules>

   </system.web>

   <system.webServer>
      <validation validateIntegratedModeConfiguration="false"/>
      <modules>
         ...
         <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      </modules>


      <handlers>
         ...
         <add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd" type="System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
      </handlers>
      ...
   </system.webServer>

</configuration>

Step 3: Define the Routes in Global.asax

Define one to many routes in Application_Start event of global.asax.

Here in this example defined a route to 
SchoolType/SchoolName - used to view school detail page. (where actually you have to go to this page by virtual path 'ViewSchool.aspx?schoolid = 14')


<%@ Import Namespace="System.Web.Routing" %>
<script runat="server">
void Application_Start(object sender, EventArgs e) 
{
     RegisterRoutes(RouteTable.Routes);
}

void RegisterRoutes(RouteCollection routes)
{
    routes.Add("ViewSchool", new Route("{SchoolType}/{*SchoolName}", new   SchoolRouteHandler()));
}
</script>


Note: If you are using ASP.NET AJAX along with URL Re-Writing, avoid rewriting paths to '.axd' files. Otherwise, it will rewrite WebResource.axd and ScriptResource.axd file paths also and that is the reason AJAX will not work on your site (ex: update panel, update progress etc.).


For this reason I changed my RegisterRoutes method to be as below.


void RegisterRoutes(RouteCollection routes)
{
    string url = HttpContext.Current.Request.Url.AbsolutePath;
if (! url.Contains(".axd"))
{ routes.Add("ViewSchool", new Route("{SchoolType}/{*SchoolName}", new     SchoolRouteHandler()));
    }
}

Step 4: Create the Route Handler Classes
 A route handler class is a class that is passed information about the incoming request and must return an HTTP Handler to process the request. It must implement the IRouteHandler  interface and, as a consequence, must provide at least one method - GetHttpHandler - which is responsible for returning the HTTP Handler (or ASP.NET page) that will process the request.
Typically, a route handler performs the following steps:
  1. Parse the URL as needed
  2. Load any information from the URL that needs to be passed to the ASP.NET page or HTTP Handler that will handle this request. One way to convey such information is to place it in the HttpContext.Items collection, which serves as a repository for storing data that persists the length of the request.
  3. Return an instance of the ASP.NET page or HTTP Handler that does the processing

public class SchoolRouteHandler : IRouteHandler
{
     public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        string schoolType = requestContext.RouteData.Values["SchoolType"] as string;
        string schoolName = requestContext.RouteData.Values["SchoolName"] as string;
      
        if (string.IsNullOrEmpty(schoolName) || string.IsNullOrEmpty(schoolType))
            return BuildManager.CreateInstanceFromVirtualPath("~/schoolnotfound.aspx", typeof(Page)) as   
            Page;
        else
        {
            //Code here to get school id from database 
           //store values, whichever you want to pass them to .aspx page

            HttpContext.Current.Items["SchoolType"] = schoolType;
            HttpContext.Current.Items["SchoolID"] = schoolid;

            return BuildManager.CreateInstanceFromVirtualPath("~/viewschool.aspx", typeof(Page)) as Page;
        }
            return BuildManager.CreateInstanceFromVirtualPath("~/schoolnotfound.aspx", typeof(Page)) as         Page;
    }
}

Step 5: Create the ASP.NET Pages That Process the Requests

protected void Page_Load(object sender, EventArgs e)
   //Retrieve values back from httpcontext
   Int64 schoolid = Convert.ToInt64(
HttpContext.Current.Items["schoolid"]);

If you do till here it will work fine on your local system using development server.  But, when you host the website on a shared server you get HTTP 404 page. This means IIS receives the request and it serves the page configured for HTTP 404. Generally this is a 404.html file that resides in IIS directory. IIS does not send the request to ASP.NET handler and thus you cannot intercept 404 requests. So, if you can forward all HTTP 404 to ASP.NET, you can serve such extensionless URL. In order to forward HTTP 404 to ASP.NET ISAPI, all you need to do is configure IIS to redirect to some .aspx extension when a missing extensionless url is hit.

Step6:
Add below section to your web.config , under <system.webserver>
<httpErrors errorMode="Custom">
     <remove statusCode="404" subStatusCode="-1" />
     <error statusCode="404" prefixLanguageFilePath="" path="/404.aspx" responseMode="ExecuteURL" />
</httpErrors>

When you will request an URL like "www.findaschool.in/preschools/drskids" it will redirect to "www.findaschool.in/404.aspx?404;http://www.findaschool.in/preschools/drskids". From Global.asax BeginRequest event, capture this URL and see whether it's an extensionless URL request. If it is, do your URL rewriting stuff for such extensionless URL.


protected void Application_BeginRequest(object sender, EventArgs e)
{
      string url = HttpContext.Current.Request.Url.AbsolutePath;
   
      // HTTP 404 redirection for extensionless URL or some missing file
      if (url.Contains("404.aspx"))
      {
          // On 404 redirection, query string contains the original URL in this format:
          // 404;http://www.findaschool.in/preschools/drskids    
          string[] urlInfo404 = Request.Url.Query.ToString().Split(';');
          if (urlInfo404.Length > 1)
          {
              string originalUrl = urlInfo404[1];
System.Uri rwUri = new Uri(originalUrl);
              HttpContext.Current.RewritePath(rwUri.PathAndQuery); 
}
}
}

More detailed information at below links:.

Set the browser window size using JavaScript


You may want to control the size of the browser window which opens when a viewer accesses your web page. The default behavior is for the browser window to open at its last setting (e.g. maximized, or resized according to the viewer's preference). You can force the browser window to open at a specific pixel size (width and height). The viewer can still resize, maximize, or minimize the window once it has opened, but re-opening or refreshing the window will pull it back to the settings you have defined. You will use some simple JavaScript to perform this function. You need to add code to the <head> section and in the <body> tag.



In the <head> section

<script type="text/javascript">


   function changeScreenSize(w,h)
     {
       window.resizeTo( w,h )
     }
</script>


In the <body> tag

<body onload="changeScreenSize(500,300)">




In this example, the window size has been set to 500 pixels wide and 300 pixels high. Experiment with different width/height pixel values to get the effect you want.

Opening a new window without toolbar, menu bar, etc. like a popup with width and height set to prefered size

Use java script open method for the above effect.

Syntax : oNewWindow = object.open( [sURL] [, sName] [, sFeatures] [, bReplace])  

sURL (Optional): String that specifies the URL of the document to display. If no URL is specified, a new window with about:blank is displayed.

sName (Optional):  String that specifies the name of the window. This name is used as the value for the TARGET attribute on a form or an anchor element.

sFeatures (Optional) :Optional. String that contains a list of items separated by commas. Each item consists of an option and a value, separated by an equals sign (for example, "fullscreen=yes, toolbar=yes"). The following features are supported.


bReplace (Optional) : Boolean that specifies whether the sURL creates a new entry or replaces the current entry in the window's history list. This parameter only takes effect if the sURL is loaded into the same window. 
true
sURL replaces the current document in the history list
false
sURL creates a new entry in the history list.
 
ex:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Page1</title>
    <script type ="text/javascript" >
    function OpenPopupWindow()
    {
          window.open("Page2.aspx","","toolbar=no,location=no, status = yes,   menubar=no,scrollbars=no, width =500, height = 300");
               
    }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
   
        <asp:LinkButton ID="lnkPage2" runat="server" OnClientClick = "OpenPopupWindow();" >Page2</asp:LinkButton>
   
    </div>
    </form>
</body>
</html>

For a complete reference Click Here