El manejo de la información hoy en día es importante, pero lo es aún más la forma en que se controlan los accesos a la misma.

Hoy veremos como crear reglas de accesso a nuestras aplicaciones web basadas en asp.net utilizando el membership provider que nos proporciona asp.net de una forma fácil y rápida.

Para empezar, agregamos una nueva página a nuestro proyecto asp.net y definimos lo siguiente

 <table class="webparts">
        <tr>
            <th>
                Website Access Rules</th>
        </tr>
        <tr>
            <td class="details" valign="top">
                <p>
                    Use this page to manage access rules for your Web site. Rules are applied to
                    folders, thus providing robust folder-level security enforced by the ASP.NET
                    infrastructure. Rules are persisted as XML in each folder&#39;s Web.config file. <i>
                    Page-level security and inner-page security are not handled using this tool —
                    they are handled using specialized code that is available to the Web Developers.</i>
                </p>
                <table>
                    <tr>
                        <td style="padding-right: 30px; width: 78px;" valign="top">
                        <asp:Label ID="lblsoftestructure" Text="Current System Estructure" runat="server" />
                            <div class="treeview">
                           
                                <asp:TreeView ID="FolderTree" runat="server"
                                    OnSelectedNodeChanged="FolderTree_SelectedNodeChanged" ImageSet="Arrows">
                                    <RootNodeStyle ImageUrl="~/Styles/img/folder.gif" />
                                    <ParentNodeStyle ImageUrl="~/Styles/img/folder.gif" Font-Bold="False" />
                                    <NodeStyle Font-Names="Tahoma" Font-Size="10pt" ForeColor="Black"
                                        HorizontalPadding="5px" NodeSpacing="0px" VerticalPadding="0px" />
                                    <LeafNodeStyle ImageUrl="~/Styles/img/folder.gif" />
                                    <HoverNodeStyle Font-Underline="True" ForeColor="#5555DD" />
                                    <SelectedNodeStyle Font-Underline="true" ForeColor="#5555DD"
                                        HorizontalPadding="0px" VerticalPadding="0px" />
                                </asp:TreeView>
                            </div>
                        </td>
                        <td style="padding-left: 30px; border-left: 1px solid #999;" valign="top">
                            <asp:Panel ID="SecurityInfoSection" runat="server" Visible="false">
                                <h2 ID="TitleOne" runat="server" class="alert">
                                </h2>
                                <p>
                                    Rules are applied in order. The first rule that matches applies, and the
                                    permission in each rule overrides the permissions in all following rules. Use
                                    the Move Up and Move Down buttons to change the order of the selected rule.
                                    Rules that appear dimmed are inherited from the parent and cannot be changed at
                                    this level.
                                </p>
                               
                                <table style="width: 100%">
                                    <tr>
                                        <td style="width: 259px" class="style2">
                                            &nbsp;</td>
                                        <td style="width: 541px">
                                            <asp:GridView ID="RulesGrid" runat="server" AutoGenerateColumns="false"
                                                CssClass="GridViewStyle"  GridLines="none" OnRowDataBound="RowDataBound">
                                                <Columns>
                                                    <asp:TemplateField HeaderText="Action">
                                                        <ItemTemplate>
                                                           <%#CType(Container.DataItem, AuthorizationRule).Action%>
                                                        </ItemTemplate>
                                                    </asp:TemplateField>
                                                    <asp:TemplateField HeaderText="Roles">
                                                        <ItemTemplate>
                                                            <%#CType(Container.DataItem, AuthorizationRule).Action%>
                                                        </ItemTemplate>
                                                    </asp:TemplateField>
                                                    <asp:TemplateField HeaderText="User">
                                                        <ItemTemplate>
                                                            <%#CType(Container.DataItem, AuthorizationRule).Action%>
                                                        </ItemTemplate>
                                                    </asp:TemplateField>
                                                    <asp:TemplateField HeaderText="Delete Rule">
                                                        <ItemTemplate>
                                                            <asp:Button ID="Button1" runat="server"
                                                                CommandArgument="<%# CType(Container.DataItem, AuthorizationRule) %>"
                                                                OnClick="DeleteRule"
                                                                OnClientClick="return confirm('Click OK to delete this rule.')"
                                                                Text="Delete Rule" />
                                                        </ItemTemplate>
                                                    </asp:TemplateField>
                                                    <asp:TemplateField HeaderText="Move Rule">
                                                        <ItemTemplate>
                                                            <asp:Button ID="Button2" runat="server"
                                                                CommandArgument="<%# CType(Container.DataItem, AuthorizationRule) %>"
                                                                OnClick="MoveUp" Text="  Up  " />
                                                            <asp:Button ID="Button3" runat="server"
                                                                CommandArgument="<%# CType(Container.DataItem, AuthorizationRule)%>"
                                                                OnClick="MoveDown" Text="Down" />
                                                        </ItemTemplate>
                                                    </asp:TemplateField>
                                                </Columns>
                                                 <RowStyle CssClass="RowStyle"     />
                     <EmptyDataRowStyle CssClass="EmptyRowStyle" />
                     <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
                     <PagerStyle CssClass="PagerStyle"   />
                     <SelectedRowStyle CssClass="SelectedRowStyle" />
                     <HeaderStyle CssClass="HeaderStyle"   />
                     <EditRowStyle CssClass="EditRowStyle" />
                     <AlternatingRowStyle CssClass="AltRowStyle" />
                                            </asp:GridView>
                                        </td>
                                        <td>
                                            &nbsp;</td>
                                    </tr>
                                </table>
                                <br />
                                <hr />
                                <h2 ID="TitleTwo" runat="server" class="alert">
                                </h2>
                                <b>Action:</b>
                                <asp:RadioButton ID="ActionDeny" runat="server" Checked="true"
                                    GroupName="action" Text="Deny" />
                                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                <asp:RadioButton ID="ActionAllow" runat="server" GroupName="action"
                                    Text="Allow" />
                                <br />
                                <br />
                                <b>Rule applies to:</b>
                                <br />
                                <asp:RadioButton ID="ApplyRole" runat="server" Checked="true"
                                    GroupName="applyto" Text="This Role:" />
                                <asp:DropDownList ID="UserRoles" runat="server" AppendDataBoundItems="true">
                                    <asp:ListItem>Select Role</asp:ListItem>
                                </asp:DropDownList>
                                <br />
                                <asp:RadioButton ID="ApplyUser" runat="server" GroupName="applyto"
                                    Text="This User:" />
                                <asp:DropDownList ID="UserList" runat="server" AppendDataBoundItems="true">
                                    <asp:ListItem>Select User</asp:ListItem>
                                </asp:DropDownList>
                                <br />
                                <asp:RadioButton ID="ApplyAllUsers" runat="server" GroupName="applyto"
                                    Text="All Users (*)" />
                                <br />
                                <asp:RadioButton ID="ApplyAnonUser" runat="server" GroupName="applyto"
                                    Text="Anonymous Users (?)" />
                                <br />
                                <br />
                                <asp:Button ID="Button4" runat="server" OnClick="CreateRule"
                                    OnClientClick="return confirm('Click OK to create this rule.');"
                                    Text="Create Rule" />
                                <asp:Literal ID="RuleCreationError" runat="server"></asp:Literal>
                            </asp:Panel>
                        </td>
                    </tr>
                </table>
            </td>
        </tr>
    </table>

En el código anterior hemos echo varias cosas:

 

La primera, hemos declarado y creado un TreeView, que nos servirá para visualizar toda la estructura de directorios de nuestra aplicación.

 

La segunda hemos creado un gridview que utilizaremos para mostrar los permisos que tiene un rol en un determinado directorio.

 

Y por ultimo, hemos creado la estructura que nos permitirá seleccionar los roles, darles o denegarles acceso y atributos.

 

Ahora, veamos el Code Behind.

Declaramos dos variables

Private Const VirtualImageRoot As String = "~/"
Private selectedFolderName As String

 

Declaramos el procedimiento, addrule. Que nos permitirá agregar reglas a cada uno de los folders.

Protected Sub AddRule(ByVal newRule As AuthorizationRule)
        Dim virtualFolderPath As String = FolderTree.SelectedValue
        Dim config As Configuration = WebConfigurationManager.OpenWebConfiguration(virtualFolderPath)
        Dim systemWeb As SystemWebSectionGroup = DirectCast(config.GetSectionGroup("system.web"), SystemWebSectionGroup)
        Dim section As AuthorizationSection = DirectCast(systemWeb.Sections("authorization"), AuthorizationSection)
        section.Rules.Add(newRule)
        Try
            config.Save()
            RuleCreationError.Visible = False
        Catch ex As Exception
            RuleCreationError.Visible = True
            RuleCreationError.Text = "<div class=""alert""><br />An error occurred and the rule was not added. I saw this happen during testing when I attempted to create a rule that the ASP.NET infrastructure realized was redundant. Specifically, I had the rule <i>DENY ALL USERS</i> in one folder, then attempted to add the same rule in a subfolder, which caused ASP.NET to throw an exception.<br /><br />Here's the error message that was thrown just now:<br /><br /><i>" + ex.Message + "</i></div>"
        End Try
    End Sub

 

Ahora creamos el procedimiento Create Rule

  Protected Sub CreateRule(ByVal sender As Object, ByVal e As EventArgs)
        Dim newRule As AuthorizationRule
        If ActionAllow.Checked Then
            newRule = New AuthorizationRule(AuthorizationRuleAction.Allow)
        Else
            newRule = New AuthorizationRule(AuthorizationRuleAction.Deny)
        End If

        If ApplyRole.Checked AndAlso UserRoles.SelectedIndex > 0 Then
            newRule.Roles.Add(UserRoles.Text)
            AddRule(newRule)
        ElseIf ApplyUser.Checked AndAlso UserList.SelectedIndex > 0 Then
            newRule.Users.Add(UserList.Text)
            AddRule(newRule)
        ElseIf ApplyAllUsers.Checked Then
            newRule.Users.Add("*")
            AddRule(newRule)
        ElseIf ApplyAnonUser.Checked Then
            newRule.Users.Add("?")
            AddRule(newRule)
        End If
    End Sub

Nuestro siguiente metodo PullLocalRulesOutOfAuthorizationSection

 Protected Function PullLocalRulesOutOfAuthorizationSection(ByVal section As AuthorizationSection) As ArrayList
        ' Dan Clem, 3/17/2007.
        ' First load the local rules into an ArrayList.

        Dim rulesArray As New ArrayList()
        For Each rule As AuthorizationRule In section.Rules
            If rule.ElementInformation.IsPresent Then
                rulesArray.Add(rule)
            End If
        Next

        ' Next delete the rules from the section.
        For Each rule As AuthorizationRule In rulesArray
            section.Rules.Remove(rule)
        Next
        Return rulesArray
    End Function

Un poco mas de código

  Protected Sub AddTheTwoSwappedRules(ByVal section As AuthorizationSection, ByVal rulesArray As ArrayList, ByVal selectedIndex As Integer, ByVal upOrDown As String)
        If upOrDown = "up" Then
            section.Rules.Add(DirectCast(rulesArray(selectedIndex), AuthorizationRule))
            section.Rules.Add(DirectCast(rulesArray(selectedIndex - 1), AuthorizationRule))
        ElseIf upOrDown = "down" Then
            section.Rules.Add(DirectCast(rulesArray(selectedIndex + 1), AuthorizationRule))
            section.Rules.Add(DirectCast(rulesArray(selectedIndex), AuthorizationRule))
        End If
    End Sub
    Protected Sub AddFinalGroupOfRules(ByVal section As AuthorizationSection, ByVal rulesArray As ArrayList, ByVal selectedIndex As Integer, ByVal upOrDown As String)
        Dim adj As Integer
        If upOrDown = "up" Then
            adj = 1
        Else
            adj = 2
        End If
        For x As Integer = selectedIndex + adj To rulesArray.Count - 1
            section.Rules.Add(DirectCast(rulesArray(x), AuthorizationRule))
        Next
    End Sub

    Protected Sub LoadRulesInNewOrder(ByVal section As AuthorizationSection, ByVal rulesArray As ArrayList, ByVal selectedIndex As Integer, ByVal upOrDown As String)
        '
 
        '         * Imagine you have five local rules and you click a button to move the middle one.
        '         * In that scenario, all three of these methods will add rules.
        '         * If, however, there are only two local rules to start with, then only the middle method will add rules.
        '         * The first and third methods won't do anything, because their FOR loops will never execute.
        '         


        AddFirstGroupOfRules(section, rulesArray, selectedIndex, upOrDown)
        AddTheTwoSwappedRules(section, rulesArray, selectedIndex, upOrDown)
        AddFinalGroupOfRules(section, rulesArray, selectedIndex, upOrDown)
    End Sub

 

 Protected Sub AddFirstGroupOfRules(ByVal section As AuthorizationSection, ByVal rulesArray As ArrayList, ByVal selectedIndex As Integer, ByVal upOrDown As String)
        Dim adj As Integer
        If upOrDown = "up" Then
            adj = 1
        Else
            adj = 0
        End If
        For x As Integer = 0 To selectedIndex - adj - 1
            section.Rules.Add(DirectCast(rulesArray(x), AuthorizationRule))
        Next
    End Sub

    Protected Sub MoveRule(ByVal sender As Object, ByVal e As EventArgs, ByVal upOrDown As String)
        '
      
        upOrDown = upOrDown.ToLower()

        If upOrDown = "up" OrElse upOrDown = "down" Then
            Dim button As Button = DirectCast(sender, Button)
            Dim item As GridViewRow = DirectCast(button.Parent.Parent, GridViewRow)
            Dim selectedIndex As Integer = item.RowIndex
            If (selectedIndex > 0 AndAlso upOrDown = "up") OrElse (upOrDown = "down") Then
                Dim virtualFolderPath As String = FolderTree.SelectedValue
                Dim config As Configuration = WebConfigurationManager.OpenWebConfiguration(virtualFolderPath)
                Dim systemWeb As SystemWebSectionGroup = DirectCast(config.GetSectionGroup("system.web"), SystemWebSectionGroup)
                Dim section As AuthorizationSection = DirectCast(systemWeb.Sections("authorization"), AuthorizationSection)

                ' Pull the local rules out of the authorization section, deleting them from same:
                Dim rulesArray As ArrayList = PullLocalRulesOutOfAuthorizationSection(section)
                If upOrDown = "up" Then
                    LoadRulesInNewOrder(section, rulesArray, selectedIndex, upOrDown)
                ElseIf upOrDown = "down" Then
                    If selectedIndex < rulesArray.Count - 1 Then
                        LoadRulesInNewOrder(section, rulesArray, selectedIndex, upOrDown)
                    Else
                        ' DOWN button in last row was pressed. Load the rules array back in without resorting.
                        For x As Integer = 0 To rulesArray.Count - 1
                            section.Rules.Add(DirectCast(rulesArray(x), AuthorizationRule))
                        Next
                    End If
                End If
                config.Save()
            End If
        End If
    End Sub

 

 Protected Sub DeleteRule(ByVal sender As Object, ByVal e As EventArgs)
        '
        '      
        '         * This is working quite well, however there is a defect that I am not planning to fix right now.
        '         * If you delete a rule, then attempt to delete another rule from the same folder without
        '         * refreshing the page, you'll get a page error. The workaround is to re-click the folder in the
        '         * tree to refresh it, then delete the rule.
        '         * Don't feel like worrying about this right now.
        '         *
        '         * Note: this problem may have been fixed already.
        '         * I stopped using the session array method for handling things.
        '         * This may have fixed it. I'll test later.
        '         

        Dim button As Button = DirectCast(sender, Button)
        Dim item As GridViewRow = DirectCast(button.Parent.Parent, GridViewRow)
        Dim virtualFolderPath As String = FolderTree.SelectedValue
        Dim config As Configuration = WebConfigurationManager.OpenWebConfiguration(virtualFolderPath)
        Dim systemWeb As SystemWebSectionGroup = DirectCast(config.GetSectionGroup("system.web"), SystemWebSectionGroup)
        Dim section As AuthorizationSection = DirectCast(systemWeb.Sections("authorization"), AuthorizationSection)
        section.Rules.RemoveAt(item.RowIndex)
        config.Save()
    End Sub
    Protected Sub MoveUp(ByVal sender As Object, ByVal e As EventArgs)
        MoveRule(sender, e, "up")
    End Sub
    Protected Sub MoveDown(ByVal sender As Object, ByVal e As EventArgs)
        MoveRule(sender, e, "down")
    End Sub


    Function GetAction(ByVal rule As AuthorizationRule) As String
        Return rule.Action.ToString()
    End Function

    Function GetRole(ByVal rule As AuthorizationRule) As String
        Return rule.Roles.ToString()
    End Function

    Function GetUser(ByVal rule As AuthorizationRule) As String
        Return rule.Users.ToString()
    End Function

    Protected Sub DisplayAccessRules(ByVal virtualFolderPath As String)
        If Not virtualFolderPath.StartsWith(VirtualImageRoot) OrElse virtualFolderPath.IndexOf("..") >= 0 Then
            '
            '             *  from my brief testing, it appears that this may not be necessary, since ASP.NET seems to prevent this inherently, throwing this error:
            '             * Cannot use a leading .. to exit above the top directory.
            '         
            '             

            Throw New ApplicationException("An attempt to access a folder outside of the website directory has been detected and blocked.")
        End If
        Dim config As Configuration = WebConfigurationManager.OpenWebConfiguration(virtualFolderPath)
        Dim systemWeb As SystemWebSectionGroup = DirectCast(config.GetSectionGroup("system.web"), SystemWebSectionGroup)
        Dim authorizationRules As AuthorizationRuleCollection = systemWeb.Authorization.Rules
        RulesGrid.DataSource = authorizationRules
        RulesGrid.DataBind()

        TitleOne.InnerText = "Rules applied to " + virtualFolderPath
        TitleTwo.InnerText = "Create new rule for " + virtualFolderPath
    End Sub

    Protected Sub RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs)
        If e.Row.RowType = DataControlRowType.DataRow Then
            Dim rule As AuthorizationRule = DirectCast(e.Row.DataItem, AuthorizationRule)
            If Not rule.ElementInformation.IsPresent Then
                e.Row.Cells(3).Text = "Inherited from higher level"
                e.Row.Cells(4).Text = "Inherited from higher level"
                e.Row.CssClass = "odd"
            End If
        End If
    End Sub

    Protected Sub ResetFolderImageUrls(ByVal parentNode As TreeNode)
        '
        '
        '         * I really wanted a strong visual queue indicating which folder was selected.
        '         * For some reason, the ImageUrl attribute of the <SelectedNodeStyle> tag does not work here,
        '         * so I resorted to this method after a number of other unsuccessful attempts.
        '         * Note that without this method, each successively selected folder will retain its target.gif
        '         * image on successive postbacks.
        '         *
        '         * A better way to do this would be to store the value path of the selected node in ViewState,
        '         * then use that value to find the previously selected node.
        '         * However, I could not figure out how to use the TreeView.FindNode(valuePath) method.
        '         * I even tried using hardcoded string values instead of the viewstate's stored valuePath string,
        '         * but nothing ever returned a valid node, whether I tried it in Page_Load or Page_PreRender.
        '         


        parentNode.ImageUrl = "~/Styles/img/folder.gif"

        ' Recurse through this node's child nodes.
        Dim nodes As TreeNodeCollection = parentNode.ChildNodes
        For Each childNode As TreeNode In nodes
            ResetFolderImageUrls(childNode)
        Next
    End Sub

    Protected Sub FolderTree_SelectedNodeChanged(ByVal sender As Object, ByVal e As EventArgs)
        '
        '    
        '         * I want to reset the Add Rule form field values whenever the user moves folders.
        '         * Note that the FALSE statements below are all necessary. It is not sufficient to set
        '         * the desired radio to TRUE, since the ASP.NET framework seems to treat radio buttons
        '         * as individual items even if they share the same group name.
        '         

        ActionDeny.Checked = True
        ActionAllow.Checked = False
        ApplyRole.Checked = True
        ApplyUser.Checked = False
        ApplyAllUsers.Checked = False
        ApplyAnonUser.Checked = False
        UserRoles.SelectedIndex = 0
        UserList.SelectedIndex = 0

        RuleCreationError.Visible = False

        ResetFolderImageUrls(FolderTree.Nodes(0))
        ' Restore previously selected folder's ImageUrl.
        FolderTree.SelectedNode.ImageUrl = "~/Styles/img/target.gif"
        ' Set the newly selected folder's ImageUrl.
    End Sub

    Protected Function AddNodeAndDescendents(ByVal folder As DirectoryInfo, ByVal parentNode As TreeNode) As TreeNode
        '
        '         * The PopulateTree and AddNodeAndDescendents are taken almost verbatim from Scott Mitchell's article
        '         * "Using the TreeView Control and a DataList to Create an Online Image Gallery", which is located at
        '         * http://aspnet.4guysfromrolla.com/articles/083006-1.aspx
        '         


        ' Add the TreeNode, displaying the folder's name and storing the full path to the folder as the value...

        Dim virtualFolderPath As String
        If parentNode Is Nothing Then
            virtualFolderPath = VirtualImageRoot
        Else
            virtualFolderPath = parentNode.Value + folder.Name + "/"
        End If

        Dim node As New TreeNode(folder.Name, virtualFolderPath)
        node.Selected = (folder.Name = selectedFolderName)

        ' Recurse through this folder's subfolders
        Dim subFolders As DirectoryInfo() = folder.GetDirectories()
        For Each subFolder As DirectoryInfo In subFolders
            If subFolder.Name <> "_controls" AndAlso subFolder.Name <> "App_Data" Then
                Dim child As TreeNode = AddNodeAndDescendents(subFolder, node)
                node.ChildNodes.Add(child)
            End If
        Next
        Return node
        ' Return the new TreeNode
    End Function

    Protected Sub PopulateTree()
        '
        '        
        '         * The PopulateTree and AddNodeAndDescendents are taken almost verbatim from Scott Mitchell's article
        '         * "Using the TreeView Control and a DataList to Create an Online Image Gallery", which is located at
        '         * http://aspnet.4guysfromrolla.com/articles/083006-1.aspx
        '         


        ' Populate the tree based on the subfolders of the specified VirtualImageRoot
        Dim rootFolder As New DirectoryInfo(Server.MapPath(VirtualImageRoot))
        Dim root As TreeNode = AddNodeAndDescendents(rootFolder, Nothing)
        FolderTree.Nodes.Add(root)
        Try
            FolderTree.SelectedNode.ImageUrl = "/Simple/i/target.gif"
        Catch
        End Try
    End Sub

    Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
        UserRoles.DataSource = Roles.GetAllRoles()
        UserRoles.DataBind()

        UserList.DataSource = Membership.GetAllUsers()
        UserList.DataBind()

        If IsPostBack Then
            selectedFolderName = ""
        Else
            selectedFolderName = Request.QueryString("selectedFolderName")
        End If

        crabit.RegCss.gridcss(Me)
    End Sub

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If Not IsPostBack Then
            PopulateTree()
        End If

    End Sub


    Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
 
        '         * The call to DisplayAccessRules, and the subsequent binding of the gridview, MUST occur
        '         * inside the Page_PreRender event. I don't fully understand why, but this fixes two more .NET gotchas:
        '         * 1) My technique to hide the delete rule button did not work the other way.
        '         *    Why? Upon page refresh, ALL ROWS HAD BUTTONS because I'd been calling DisplayAccessRules
        '         *    from the FolderTree_SelectedNodeChanged event.
        '         * 2) I first moved this into the Page_Load event, but that caused the weird Event validation error
        '         *    that occurs when ASP.NET thinks someone is hacking your postback values.
        '         *
        '         * This appears to be working now, so I'm leaving well enough alone.
        '         

        If FolderTree.SelectedNode IsNot Nothing Then
            DisplayAccessRules(FolderTree.SelectedValue)
            SecurityInfoSection.Visible = True
        End If

    End Sub