Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating or Editing the Web.config dynamically for Page Level Access (Asp.net web forms)

0.00/5 (No votes)
27 Oct 2014 1  
This tip/trick shows you how to edit the web.config file dynamically .

Introduction

The point of this tip is to show how to create/edit the web.config file dynamically for role based access to pages.

Background

Even if it’s not recommended to create/edit the web.config file dynamically, for different reasons you might want to do so . In my case our clients needed to give access to individual pages on their own using a user interface .

Here we won't be editing the main web.config file that contains the settings for the whole project .We will be creating/editing a web.config which is found on the folders where your .aspx pages reside.

For user management purposes I used SqlMembership provider and there are a lot of materials on the internet that shows how to use it and there is no need to repeat it here.

After you created users and roles using SqlMembership provider then the need comes for you to give access to pages either by role or to individual users. This tip shows you how to achieve that . 

To get started with I used a tutorial by Dan Clem which is found at http://www.4guysfromrolla.com/articles/053007-1.aspx  . The tutorial shows you how to give folder level access to roles or users .

But incase if you needed to provide access to your individual pages that are inside your folders and also create your web.config on the fly you can use this tip.

The Steps are organized as follows

1.Create the user interface

2.Populate the tree

3.Create the web.config file dyanamically (i.e if it doesn't exist)

4.Create a rule

5.Update a rule

6.Delete a rule

7.Display the Access rules on Grid

1.Creating the user Interface

 Use the below code to create the user interface and don't forget to change the images with your own  .

 I am also using telerik RadGrid you might also want to change that to Microsoft Gridview control   .                      
                                                                                          

 <div>
        <table>
            <tr>
                <th>
                    <asp:Localize ID="Localize1" runat="server"
                        Text="Access Rule Management" />
                </th>
            </tr>
            <tr>
                <td class="details" valign="top">
                    <p>
                        <i>
                            <asp:Localize ID="Localize2" runat="server"
                                Text="Use this page to manage access rules for the system users. Rules are applied to pages." />
                        </i>
                    </p>
                    <asp:Label ID="lblError" runat="server" CssClass="Important" Visible="False" 
                     style="font-weight: 700"></asp:Label>
                    <table style="width: 100%">
                        <tr>
                            <td valign="top" style="padding-right: 30px;">
                                <div class="treeview">
                                    <asp:TreeView runat="server" ID="FolderTree"
                                        OnSelectedNodeChanged="FolderTree_SelectedNodeChanged" ExpandDepth="1" OnTreeNodeExpanded="FolderTree_TreeNodeExpanded">
                                        <RootNodeStyle ImageUrl="~/Content/Images/folder.gif" />
                                        <ParentNodeStyle ImageUrl="~/Content/Images/folder.gif" />
                                        <LeafNodeStyle ImageUrl="~/Content/Images/folder.gif" />
                                        <SelectedNodeStyle Font-Underline="True" ForeColor="#A21818" />
                                    </asp:TreeView>
                                </div>
                            </td>
                            <td valign="top" style="padding-left: 30px; border-left: 1px solid #999; width: 80%">
                                <asp:Panel runat="server" ID="SecurityInfoSection" Visible="False">
                                    <h4 runat="server" id="TitleOne" class="alert"></h4>
                                    <p align="center">
                                        <asp:Label ID="ActionStatus" runat="server" CssClass="Important" Visible="False"></asp:Label>
                                    </p>                                  
                                    <telerik:RadGrid ID="RadGridRuleDisplay" runat="server"
                                         GridLines="None" CellSpacing="0" Skin="Vista" AutoGenerateColumns="False">
                                        <ClientSettings>
                                            <Selecting AllowRowSelect="True" />
                                        </ClientSettings>
                                        <AlternatingItemStyle BackColor="#F0F8FF" />
                                        <MasterTableView>
                                            <Columns>                                               
                                                <telerik:GridBoundColumn DataField="Roles" HeaderText="Allowed Roles">
                                                    <ColumnValidationSettings>
                                                        <ModelErrorMessage Text=""></ModelErrorMessage>
                                                    </ColumnValidationSettings>
                                                </telerik:GridBoundColumn>                                                                              
                                               
                                                <telerik:GridTemplateColumn HeaderText="Delete Rule" UniqueName="DeleteRule">
                                                    <ItemTemplate>
                                                        <asp:ImageButton ID="Button1" ToolTip="Delete Rule" AlternateText="Delete Rule"
    runat="server"
                                                            ImageUrl='<%# RadAjaxLoadingPanel.GetWebResourceUrl(Page, "Telerik.Web.UI.Skins.Default.Grid.Delete.gif") %>'
                                                            
                                                            OnClick="DeleteRule"
                                                            OnClientClick="return confirm('Click OK to delete this rule.')" />
                                                    </ItemTemplate>
                                                </telerik:GridTemplateColumn>                                              
                                            </Columns>
                                        </MasterTableView>
                                    </telerik:RadGrid>
                                   <br />
                                    <hr />
                                    <h4 runat="server" id="TitleTwo" class="alert"></h4>
                                    <b>
                                        <asp:Literal ID="Literal1" runat="server" Text="Action:" /></b>
                                    <asp:RadioButton runat="server" ID="ActionDeny" GroupName="action"
                                        Text="Deny" Checked="True" />
                                    <asp:RadioButton runat="server" ID="ActionAllow" GroupName="action"
                                        Text="Allow" />
                                    <br />
                                    <br />
                                    <b>
                                        <asp:Literal ID="Literal2" runat="server" Text="Rule applies to:"
                                            meta:resourcekey="Literal2Resource1" /></b>
                                    <br />

                                    <asp:RadioButton runat="server" ID="ApplyRole" GroupName="applyto"
                                        Text="This Role:" Checked="True" meta:resourcekey="ApplyRoleResource1" />
                                    <telerik:RadComboBox ID="UserRoles" runat="server" AppendDataBoundItems="True"
                                        Filter="Contains">
                                        <Items>
                                            <telerik:RadComboBoxItem Text="Select Role" Value="Select Role" runat="server"
                                                Owner="" />
                                        </Items>
                                    </telerik:RadComboBox>
                                    <br />
                                    <asp:Button ID="Button4" runat="server" Text="Create Rule" OnClick="CreateRule"
                                        OnClientClick="return confirm('Click OK to create this rule.');" />
                                    <asp:Literal runat="server" ID="RuleCreationError"></asp:Literal>
                                </asp:Panel>
                            </td>
                        </tr>
                    </table>
                </td>
            </tr>
        </table>
    </div>
Colourised in 161ms

On my machine the final UI looks like the image shown below

2.Populating the tree

Call the method populate tree on Page_Load to populate the tree

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                PopulateTree();
            }
        }
        private const string VirtualImageRoot = "~/";
        string selectedFolderName;
        protected void Page_Init()
        {
            UserRoles.DataSource = Roles.GetAllRoles();
            UserRoles.DataBind();
            if (IsPostBack)
            {
                selectedFolderName = "";
            }
            else
            {
                selectedFolderName = Request.QueryString["selectedFolderName"];
            }
        }        
        protected void PopulateTree()
        {
            // Populate the tree based on the subfolders of the specified VirtualImageRoot
            DirectoryInfo rootFolder = new DirectoryInfo(Server.MapPath(VirtualImageRoot));
            TreeNode root = AddNodeAndDescendents(rootFolder, null);
            FolderTree.Nodes.Add(root);
            try
            {
                FolderTree.SelectedNode.ImageUrl = "~/Content/Images/folder.gif";
            }
            catch
            { 

            }
        }
Colourised in 48ms

Here is the Method that addes the Nodes and it Descendants

        /// <summary>
        /// This method is used to populate the tree.It populates the folders
        /// by excluding those folders which are not necessary for user mangement purposes
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="parentNode"></param>
        /// <returns></returns>
        protected TreeNode AddNodeAndDescendents(DirectoryInfo folder, TreeNode parentNode)
        {
            string virtualFolderPath;
            if (parentNode == null)
            {
                virtualFolderPath = VirtualImageRoot;
            }
            else
            {
                virtualFolderPath = parentNode.Value + folder.Name + "/";
            }
            TreeNode node = new TreeNode(folder.Name, virtualFolderPath);
            node.Selected = (folder.Name == selectedFolderName);
            // Recurse through this folder's subfolders
            DirectoryInfo[] subFolders = folder.GetDirectories();
            foreach (DirectoryInfo subFolder in subFolders)
            {
                if (subFolder.Name != "_controls" && subFolder.Name != "App_Data" && subFolder.Name != "App_Code" 
                    &&  subFolder.Name != "bin" && subFolder.Name != "fonts" && subFolder.Name != "Scripts" 
                    && subFolder.Name != "Styles" && subFolder.Name != "Temp" && subFolder.Name != "UserControls"
                    && subFolder.Name != "obj" && subFolder.Name != "Service References" && subFolder.Name != "App_Start"
                    && subFolder.Name != "Account" && subFolder.Name != "Properties" && subFolder.Name != "Models" 
                    && subFolder.Name != "Content" && subFolder.Name != "Images")
                {
                    TreeNode child = AddNodeAndDescendents(subFolder, node);
                    node.ChildNodes.Add(child);
                    foreach (FileInfo File in subFolder.GetFiles())
                    {
                        //create node and add the pages to the tree
                        if (File.Extension == ".aspx")
                        {
                            TreeNode childnode = new TreeNode(File.Name, File.FullName);
                            childnode.ImageUrl = "~/Content/Images/icon-new.png";
                            child.ChildNodes.Add(childnode);
                        }
                    }
                }
            }
            return node; // Return the new TreeNode
        }
Colourised in 78ms

3.Create the Web.config Dynamically

On the TreeNodeExpanded event check whether there is a web.config file on the selected folder and create it dynamically if it doesn't exist

        protected void FolderTree_TreeNodeExpanded(object sender, TreeNodeEventArgs e)
        {            
                if (!System.IO.File.Exists(e.Node.Value))
                {
                    CreateWebConfigFile(e.Node.Value);
                }         
        }
        /// <summary>
        /// this method is used to create a web.config file dyamically if it doesn't exist
        /// on a folder
        /// </summary>
        /// <param name="folderPath"></param>
        void CreateWebConfigFile(string folderPath)
        {
            string pathString = System.IO.Path.Combine(Server.MapPath(folderPath), "Web.config");
            if (!System.IO.File.Exists(pathString))
            {
                FileInfo f = new FileInfo(pathString);
                using (StreamWriter fs = f.CreateText())
                {
                    // Use the FileStream object...
                    string xmlFile = "<?xml version='1.0'?><configuration></configuration>";
                    fs.Write(xmlFile);
                    fs.Close();
                }
            }
        }   
Colourised in 31ms

4.Create rule

On the button click event of createRule call the AddRoleRule method

        protected void CreateRule(object sender, EventArgs e)
        {
            AuthorizationRule newRule;
            if (ActionAllow.Checked)
                newRule = new AuthorizationRule(AuthorizationRuleAction.Allow);
            else
                newRule = new AuthorizationRule(AuthorizationRuleAction.Deny);

            if (ApplyRole.Checked && UserRoles.SelectedIndex > 0)
            {               
                AddRoleRule(newRule, UserRoles.Text);
            }  
        
        }
Colourised in 12ms

The method that is responsible for creating the rule is shown below     

How it works ,

It starts by loading the web.config file as an Xml document .Before creating the new authorization rule it checks whether the authorization rule is already updated . If the rule is not updated it means it's a new rule it and it creates it as an xml element including it’s attributes and child elements.Finally after it has created the elements it appends them to the end of the Xml document (i.e the loaded web.config file) .      

        /// <summary>
        /// this method is used to add an access rule to a web.config if the rule
        /// doesn't already exists
        /// </summary>
        /// <param name="newRule"></param>
        /// <param name="selectedrole"></param>
        protected void AddRoleRule(AuthorizationRule newRule, string selectedrole)
        {
            bool updated = false;
            string virtualFolderPath = FolderTree.SelectedNode.Parent.Value;
            Configuration config = WebConfigurationManager.OpenWebConfiguration(virtualFolderPath);
            XmlDocument xDoc = new XmlDocument();
            xDoc.Load(config.FilePath);
            FileInfo myFile = new FileInfo(config.FilePath);
            myFile.IsReadOnly = false;
            // if the rule exists update the rule
            updated = getOldAssignedRolesAndUpdate(config, newRule, Path.GetFileName(FolderTree.SelectedValue), selectedrole, xDoc);
           
            if (!updated)
            {
                //create location element and the path attribute with it's value set to the 
                //selected page 
                XmlElement newLocationelement = xDoc.CreateElement("location");
                XmlAttribute newLocationAttrib = xDoc.CreateAttribute("path");
                newLocationAttrib.Value = Path.GetFileName(FolderTree.SelectedValue);
                newLocationelement.Attributes.Append(newLocationAttrib);    
                XmlElement newSystemWebelement = xDoc.CreateElement("system.web");
                XmlElement newAuthorizationelement = xDoc.CreateElement("authorization");
                //create the allow element
                XmlElement newAllowelement = xDoc.CreateElement("allow");
                XmlAttribute newAllowAttrib = xDoc.CreateAttribute("roles");
                newRule.Roles.Add(selectedrole).ToString();
                string listofRoles = "";
                foreach (var item in newRule.Roles)
                {
                    listofRoles = item.ToString() ;
                }
                newAllowAttrib.Value = listofRoles;
                newAllowelement.Attributes.Append(newAllowAttrib);   
                //create the deny element
                XmlElement newDenyelement = xDoc.CreateElement("deny");
                XmlAttribute newUsersAttrib = xDoc.CreateAttribute("users");
                newUsersAttrib.Value = "*";
                newDenyelement.Attributes.Append(newUsersAttrib);            
                newAuthorizationelement.AppendChild(newAllowelement);
                newAuthorizationelement.AppendChild(newDenyelement);
                newLocationelement.AppendChild(newSystemWebelement);
                newSystemWebelement.AppendChild(newAuthorizationelement);
                xDoc.DocumentElement.AppendChild(newLocationelement);
                xDoc.PreserveWhitespace = true;
                //write to web.config file using xml writer
                XmlTextWriter xwriter = new XmlTextWriter(config.FilePath, null);               
                try
                {
                    xDoc.WriteTo(xwriter);
                    xwriter.Close();
                    PopulateRolesGrid(Path.GetFileName(FolderTree.SelectedValue), config);
                    ActionStatus.Visible = true;
                    ActionStatus.ForeColor = Color.Green;
                    ActionStatus.Text = "Role Added Successfuly!";
                    RuleCreationError.Visible = false;
                }

                catch (Exception ex)
                {
                    RuleCreationError.Visible = true;
                    RuleCreationError.Text = "An error occurred and the rule was not added.";
                }
            }
        }



       
Colourised in 101ms

5.Update rule

Use the below method to update a specific rule if exist for the selected page

How it works ,

It starts by loading the web.config file as an XDocument . First it gets the "location" element which has it's "path" attibute value of the selected page, then it gets the "authorization" element and checks whether it has "allow" element inside it if it has no "allow" element it creates it . If there already an "allow" element it updates it by concatenating the new role to the older one.

    
        /// <summary>
        /// This method is used to update a rule if it already exist in the web.config file
        /// </summary>
        /// <param name="config"></param>
        /// <param name="newRule"></param>
        /// <param name="selectedPage"></param>
        /// <param name="selectedRole"></param>
        /// <param name="xDoc"></param>
        /// <returns></returns>
        bool getOldAssignedRolesAndUpdate(Configuration config, AuthorizationRule newRule, 
                                        string selectedPage, string selectedRole, XmlDocument xDoc)
        {
            bool updated = false;

            XDocument xmlFile = XDocument.Load(config.FilePath);
            IEnumerable<XElement> location = from el in xmlFile.Elements("configuration").
                                          Elements("location")
                                          where (string)el.Attribute("path") == selectedPage
                                          select el;
            var authorizationElement = from authorization in location.Elements("system.web").
                                        Elements("authorization")
                                        select authorization;  
            var allowRoleRuleElements = from category in location.Elements("system.web").
                                        Elements("authorization").Elements("allow") 
                                        where (string)category.Attribute("roles") != null   
                                        select category;
            if (authorizationElement.Count() > 0)    
            {
                if (allowRoleRuleElements.Count() == 0)
                {
                    XmlDocument xDocNew = new XmlDocument();
                    xDocNew.Load(config.FilePath);
                    XmlNode xNode = xDocNew.CreateNode(XmlNodeType.Element, "allow", "");
                    XmlAttribute xKey = xDocNew.CreateAttribute("roles");
                    xKey.Value = selectedRole;
                    xNode.Attributes.Append(xKey);
                    authorizationElement.Single().AddFirst(xNode);
                    xDocNew.Save(config.FilePath);
                    ActionStatus.Visible = true;
                    ActionStatus.ForeColor = Color.Green;
                    ActionStatus.Text = "Role Added Successfuly!";
                    return true;
                }

               else
               {
                    string oldrole = allowRoleRuleElements.Single().Attribute("roles").Value.ToString();
                    if (newRule.Action == AuthorizationRuleAction.Deny)
                    {
                       int index = oldrole.IndexOf(selectedRole);
                       string newRole = (index < 0) ? oldrole : oldrole.Remove(index, selectedRole.TrimStart(',').Length);
                       allowRoleRuleElements.Single().Attribute("roles").Value = newRole.TrimEnd(',').TrimStart(',');
                    }

                    else
                    {
                      string newRole = (oldrole == String.Empty) ? selectedRole.Trim() : (oldrole + "," + selectedRole);
                      allowRoleRuleElements.Single().Attribute("roles").Value = newRole;
                    }
                    try
                    {
                      xmlFile.Save(config.FilePath);
                      PopulateRolesGrid(Path.GetFileName(FolderTree.SelectedValue), config);
                      RuleCreationError.Visible = false;
                    }
                    catch (Exception ex)
                    {
                        RuleCreationError.Visible = true;
                        RuleCreationError.Text = "An error occurred and the rule was not added.";
                    }
             }
                updated = true;
            }
            return updated;


       }
Colourised in 146ms

6.Delete rule

How it works,

It loads the the web.config file as XDocument and gets the node which has it's path attibute of the selected page and remove/delete all that are associated with it. 

        protected void DeleteRule(object sender, EventArgs e)
        {
            ImageButton button = (ImageButton)sender;

            GridDataItem item = (GridDataItem)button.Parent.Parent;
            string virtualFolderPath = FolderTree.SelectedNode.Parent.Value;
            Configuration config = WebConfigurationManager.OpenWebConfiguration(virtualFolderPath);
            //change the attribute of the web.config so that we can manipulate it
            FileInfo myFile = new FileInfo(config.FilePath);
            myFile.IsReadOnly = false;
            ConfigurationLocationCollection loc = (ConfigurationLocationCollection)config.Locations;
            SystemWebSectionGroup systemWeb = (SystemWebSectionGroup)config.GetSectionGroup("system.web");
            AuthorizationSection section = (AuthorizationSection)systemWeb.Sections["authorization"];
            XDocument doc = XDocument.Load(config.FilePath);
            doc.Declaration = new XDeclaration("1.0", null, null);

            var ruleTobeDeleted = from node in doc.Descendants("configuration").Elements("location")
                    where (string)node.Attribute("path") == Path.GetFileName(FolderTree.SelectedValue)
                    select node;

            ruleTobeDeleted.ToList().ForEach(x => x.Remove());
            doc.Save(config.FilePath, SaveOptions.OmitDuplicateNamespaces);
            ActionStatus.ForeColor = Color.Green;
            ActionStatus.Visible = true;
            ActionStatus.Text = "Role Deleted Successfuly!";
            PopulateRolesGrid(Path.GetFileName(FolderTree.SelectedValue), config);

        }
Colourised in 58ms

7.Display Access rules on grid

For data binding purpose to the grid create a class which contains the necessary fields 

       //class that is used for the data binding purpose      
       public class AuthorizationRoleCollection
       {
           public string Roles { get; set; }
           public string Users { get; set; }
           public string Action { get; set; }
       }
              
Colourised in 8ms

Display the access rules on Grid

        protected void FolderTree_SelectedNodeChanged(object sender, EventArgs e)
        {
           if (FolderTree.SelectedNode.Value.Contains(".aspx"))
            {
                ActionDeny.Checked = true;
                ActionAllow.Checked = false;
                ApplyRole.Checked = true;
                ActionStatus.Visible = false;
                UserRoles.SelectedIndex = 0;
                RuleCreationError.Visible = false;
                lblError.Visible = false;           
                FolderTree.SelectedNode.ImageUrl = "~/Content/Images/icon-new.png"; 
                string folderPath = FolderTree.SelectedNode.Parent.Value;
                DisplayAccessRules(folderPath, Path.GetFileName(FolderTree.SelectedValue));
            }
            else
            {
                lblError.Visible = true;
                lblError.Text = "Please select a Page.";
                SecurityInfoSection.Visible = false;
            }

       }
Colourised in 36ms

 

Display access rules method

        /// <summary>
        /// this method is used to display the access rules given to a selected page
        /// </summary>
        /// <param name="virtualFolderPath"></param>
        /// <param name="page"></param>
        protected void DisplayAccessRules(string virtualFolderPath, string page)
        {
            try
            {
                SecurityInfoSection.Visible = true;
                lblError.Visible = false;
                Configuration config = WebConfigurationManager.OpenWebConfiguration(virtualFolderPath);
                PopulateRolesGrid(page, config);
                TitleOne.InnerText = "Rules applied to " + page;
                TitleTwo.InnerText = "Create new rule for " + page;
            }
            catch (Exception)
            {
                throw;
            } 
     
        }
Colourised in 35ms

Use the below method to populate the grid with roles that have access to pages

        /// <summary>
        /// this method is used to populate the grid thats shows the access that are
        /// given to a selected page on a folder
        /// </summary>
        /// <param name="page"></param>
        /// <param name="config"></param>
        private void PopulateRolesGrid(string page, Configuration config)
        {
            XDocument xmlFile = XDocument.Load(config.FilePath);
            IEnumerable<XElement> roles = from el in xmlFile.Elements("configuration").
                                          Elements("location")
                                          where (string)el.Attribute("path") == page
                                          select el;            

            var authorizationElement = from authorization in roles.Elements("system.web").
                                       Elements("authorization")
                                       select authorization;
           
            var allowRoleRuleElements = from category in roles.Elements("system.web").
                                        Elements("authorization").Elements("allow")
                                        where (string)category.Attribute("roles") != null
                                        select category;

              
            var denyUserRuleElements = from category in roles.Elements("system.web").
                                       Elements("authorization").Elements("deny")
                                       where (string)category.Attribute("users") != null
                                       select category;

            List<AuthorizationRoleCollection> arList = new List<AuthorizationRoleCollection>();
            foreach (var item in authorizationElement)
            {
                AuthorizationRoleCollection ar = new AuthorizationRoleCollection();
                ar.Roles = allowRoleRuleElements.First().FirstAttribute.Value.ToString();
                ar.Users = denyUserRuleElements.First().FirstAttribute.Value.ToString();
                ar.Action = "";
                arList.Add(ar);
            }
            RadGridRuleDisplay.DataSource = arList;
            RadGridRuleDisplay.DataBind();
        }
Colourised in 75ms

 

History

First version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here