Question

I’m developing an intranet web application (asp.net and C#) and now I’ve reached the security and access control landmark.

The app has:

  • Login.aspx page (can be accessed by anyone within the company)
  • Home.aspx page (accessed only after logging in) that has a menu on the left with all the departmental pages and a logoff button that doesn't fire
  • Several departmental pages

Right now, I got the login system working via a “Users” table on the DB that the app uses (FORMS AUTHENTICATION), however and after searching for quite some time, I dunno how to implement roles in order to restrict access to departmental pages and hide menu options.

Here's the solution structure in VS Express for Web:

structure

The "Users" table records are like this, as you can see, James is special and has access to multiple pages:

Users

I plan on having the following scenarios:

  • Users can only access their departmental pages

  • Certain users can access other department’s pages

I know i have to have something like this, on the web config, but how do I accomplish it if i have forms authentication?:

<location path="Marketing.aspx">
<system.web>
    <authorization>
        <deny roles="AD\Grupo Planeamento"/>
        <allow roles="AD\Grupo Marketing"/>
    </authorization>
</system.web>

Now onto the code, Page_Load event is empty on all pages

Menu on the master page

<div class="span4">
                <asp:ContentPlaceholder ID="menu_lateral" runat="server">
                    <ul class="nav nav-list">
                          <li class="nav-header">Departamentos</li>
                          <li><a href="Comercial.aspx" title="Comercial">Comercial</a></li>
                          <li><a href="Compras.aspx">Compras</a></li>
                          <li><a href="Contabilidade.aspx">Contabilidade</a></li>
                          <li><a href="Controloegestao.aspx">Controlo e Gestão</a></li>
                          <li><a href="Exportação.aspx">Exportação</a></li>
                          <li><a href="Manutenção.aspx">Manutenção</a></li>
                          <li><a href="Matérias Primas.aspx">Matérias Primas</a></li>
                          <li><a href="Mercado Nacional.aspx">Mercado Nacional</a></li>
                          <li><a href="Planeamento.aspx">Planeamento</a></li>
                          <li><a href="Qualidade.aspx">Qualidade</a></li>
                          <li><a href="Tesouraria.aspx">Tesouraria</a></li>
                    </ul>
                </asp:ContentPlaceholder>
            </div>

Webconfig

<system.web>
<authentication mode="Forms">
    <forms name="frmlogin" />
    <!—if i add “loginUrl="Login.aspx"” I can only login at the second page load/attempt, dunno why />-->
</authentication>
    <authorization>
        <deny users="?"/>
    </authorization>

Login.aspx

<asp:Content ID="rightcontentdown" ContentPlaceHolderID="rightcontentdown" Runat="Server">
<form id="frmlogin" runat="server">
            <table class="logintable">
            <tr><th colspan="3" rowspan="1">Login</th>
            <tr><td>User:</td>
                <td colspan="2" rowspan="1"><input id="UserNameTextBox" type="text" runat="server"></td>
                <td colspan="2" rowspan="1"><asp:RequiredFieldValidator ID="vUserName" ControlToValidate="UserNameTextBox" Display="Static" ErrorMessage="Username Inválido" runat="server" /></td>

            <tr>
                <td>Password:</td>
                <td colspan="2" rowspan="1"><input id="PasswordTextBox" type="password" runat="server"></td>
                <td colspan="2" rowspan="1"><asp:RequiredFieldValidator ID="vUserPass" ControlToValidate="PasswordTextBox" Display="Static" ErrorMessage="Password Inválida" runat="server" /></td>

            <tr>
                <td><asp:Button ID="LoginButton" runat="server" Text="Login" onclick="LoginButton_Click" /><asp:CheckBox id="RemPassword" runat="server" autopostback="false" text="Lembrar-me"/></td>
            <tr>
                <td><asp:Label id="MsgLabel" runat="server" /></td>

        </table>
</form>

Login.aspx.cs

private bool ValidateUser(string userName, string password)
{
    SqlConnection conn;
    SqlCommand cmd;
    string pwd = null;

    //Checks for invalid userName
    //username must not be null
    if ((userName == null) || (userName.Length == 0))
    {
        return false;
    }

    //checks for invalid password, password should not be null
    if ((password == null) || (password.Length == 0))
    {
        return false;
    }

    try
    {
        //Creating connection to the sql server
        //Connection string is fetched from web.config file, see TestDb1ConnectionString there
        conn = new SqlConnection(GetConnectionString());
        conn.Open(); //Opening connection

        string sqlcmd = "Select PASSWORD from [Users] where NOME=@username";
        cmd = new SqlCommand(sqlcmd, conn);
        cmd.Parameters.Add("@username", System.Data.SqlDbType.VarChar).Value = userName;

        // Execute command and fetch password field into pwd string
        pwd = (string)cmd.ExecuteScalar();

        //Cleanup command and close connection
        cmd.Dispose();
        conn.Close();
        if (pwd == password)
            return true; // Validates the user and return true
        else
            return false;
    }
    catch (Exception ex)
    {
        // Add errer handling code here
        throw ex;
    }
}

protected void LoginButton_Click(object sender, EventArgs e)
{
    if (ValidateUser(UserNameTextBox.Value, PasswordTextBox.Value))
         {
                 FormsAuthenticationTicket tkt;
                 string cookiestr;
                 HttpCookie ck;
                 tkt = new FormsAuthenticationTicket(1, UserNameTextBox.Value, DateTime.Now, DateTime.Now.AddMinutes(30), RemPassword.Checked, "Custom Data");
                 cookiestr = FormsAuthentication.Encrypt(tkt);
                 ck = new HttpCookie(FormsAuthentication.FormsCookieName, cookiestr);
                 if (RemPassword.Checked)
                 ck.Expires = tkt.Expiration;
                 ck.Path = FormsAuthentication.FormsCookiePath;
                 Response.Cookies.Add(ck);

                 string strRedirect;
                 strRedirect = Request["ReturnUrl"];
                 if (strRedirect == null)
                 strRedirect = "Home.aspx";
                 Response.Redirect(strRedirect, true);
         }
                 else
                 Response.Redirect("Login.aspx", true);
}

Home.aspx

<asp:Content ID="rightcontentdown" ContentPlaceHolderID="rightcontentdown" Runat="Server">
    <asp:Button runat="server" Text="Terminar Sessão" ID="SignOutButton" OnClick="Signoutbutton_click" />
</asp:Content>

Home.aspx.cs

protected void Signoutbutton_click(object sender, EventArgs e)
{
    FormsAuthentication.SignOut();
    Session.Abandon();
    Session.Contents.RemoveAll();
    Response.Redirect("Login.aspx", true);
}
Was it helpful?

Solution

First create a pagesByDepatment table and a department table like the following:

  • Department
    • Id (int)
    • Name (string)
  • PagesByDepartment
    • Id (int)
    • DepartmentId (int)
    • PageName (string)
  • User
    • Id (int)
    • UserName (string)
    • Password (string) - it would be better if you can hash it into a bit array :)
    • DepartmentId (int)

You need to authorize the user in every request for all the pages, to me it could be the case to customize the principal object of your user

for example:

public class MyAppPrincipal :IPrincipal, IMyAppPrincipal
      {
            private IIdentity _identity;
            private string _department;
            public MyAppPrincipal( IIdentity identity, department)
            {
                  _identity = identity;
                  _department = department;
            }

            public bool IsPageEnabled(string pageName)
            {
                //DB is your access to your database, I know that you´re using plain ADO.NET here so put query here or cache the elements in your app_start and read them from it....
                //let´s say you have a method that you pass the pagename and the department
                 return DB.IsPageEnabled( pageName, this._department);

            }
        }

add the department to the authentication ticket in the custom user data something like

protected void LoginButton_Click(object sender, EventArgs e)
{
    if (ValidateUser(UserNameTextBox.Value, PasswordTextBox.Value))
         {
                 // get the department from your DB
                 string department = DB.GetDepartmentByUsername(UserNameTextBox.Value);

                 FormsAuthenticationTicket tkt;
                 string cookiestr;
                 HttpCookie ck;

                          tkt = new FormsAuthenticationTicket(2,  // version 
                          UserNameTextBox.Value, 
                          DateTime.Now, 
                          DateTime.Now.AddMinutes(30), 
                          RemPassword.Checked, 
                          department, // instead of custom data
                          FormsAuthentication.FormsCookiePath);

                 cookiestr = FormsAuthentication.Encrypt(tkt);
                 ck = new HttpCookie(FormsAuthentication.FormsCookieName, cookiestr);
                 if (RemPassword.Checked)
                 ck.Expires = tkt.Expiration;
                 ck.Path = FormsAuthentication.FormsCookiePath;
                 Response.Cookies.Add(ck);

                 string strRedirect;
                 strRedirect = Request["ReturnUrl"];
                 if (strRedirect == null)
                 strRedirect = "Home.aspx";
                 Response.Redirect(strRedirect, true);
         }
                 else
                 Response.Redirect("Login.aspx", true);
}

then you need a httpmodule to authenticate / authorize

public class CustomAuthenticationModule : IHttpModule 
      { 

            public void Init(HttpApplication httpApp) 
            { 
                  httpApp.AuthorizeRequest += new EventHandler(this.AuthorizaRequest); 
                  httpApp.AuthenticateRequest += new EventHandler(this.AuthenticateRequest); 
            } 
            public void Dispose() 
            {} 

            private void AuthorizaRequest( object sender, EventArgs e) 
            {     
                  if (HttpContext.Current.User != null) 
                  { 
                         if (HttpContext.Current.User.Identity.IsAuthenticated) 
                        { 
                              if (HttpContext.Current.User is MyAppPrincipal) 
                              { 

                              MyAppPrincipal principal = (MyAppPrincipal) HttpContext.Current.User; 
                              if (!principal.IsPageEnabled(HttpContext.Current.Request.Path) ) 
                                          HttpContext.Current.Server.Transfer( "unauthorized.aspx"); 
                              } 
                        } 
                  } 
            } 

            private void AuthenticateRequest(object sender, EventArgs e) 
            { 
                  if (HttpContext.Current.User != null) 
                  { 

                        if (HttpContext.Current.User.Identity.IsAuthenticated) 
                        { 
                              if (HttpContext.Current.User.Identity is FormsIdentity) 
                              { 
                                    var id = HttpContext.Current.User.Identity; 
                                    FormsAuthenticationTicket ticket = id.Ticket; 
                                    string cookieName = System.Web.Security.FormsAuthentication.FormsCookieName; 
                                    string userData = 
                                          System.Web.HttpContext.Current.Request.Cookies[cookieName].Value; 

                                    ticket  = FormsAuthentication.Decrypt(userData); 

                                    string department=""; 
                                    if( userData.Length > 0 ) 
                                          department= ticket.UserData; 
                                    HttpContext.Current.User = new 
                                    MyAppPrincipal(_identity, department);                          
                              } 
                        } 
                  } 
            }//AuthenticateRequest 
      } //class 
}

don´t forget to add your custom module

<httpModules > 
      < add type ="myapp, SecurityModules" name ="CustomAuthenticationModule" /> 
</ httpModules > 

PD:you can read it as pseudo code since I'm coding on the textbox and not VS, I hope that helps

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top