-[Danny Chen]- Blog of an ASP.NET QA tester

Tips and info about Site Navigation, ImageMap, Menu and other cool ASP.NET v2.0 features.

April 2005 - Posts

Working with Roles and Windows Authentication

  This post will be pretty short and simple but when I starting thinking about putting up this blog, this was the first topic I came up with. The reason is simply that it took me a lot longer to figure out all the details than I had expected it to (and I have direct access to the devs!).
  In forms authentication, users and roles are very straight-forward. The Web Admin Tool allows web-admins to create users, create roles, put users in roles, and it's done. But with Windows Authentication, it's a little vague. Sure, we have "users" but what exactly are "Roles"? Well, the anti-climatic answer is pretty much: Roles are "groups" either local or domain.

Lets say we had two users: "User1" and "User2" who are in two groups (and therefore in two roles) "UsersGroup" and "Group1" like so:

And to show the correlation, I'll also include a simple web.sitemap that looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="
http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
    <siteMapNode title="Root Node, Everyone can see it" roles="*">
        <siteMapNode title="UsersGroup can see this node" roles="dannych-02\UsersGroup" />
        <siteMapNode title="Only Group 1 can see this node" roles="dannych-02\Group1" />
    </siteMapNode>
</siteMap>
Here is User1's page:

 

And here is User2's page:

 

  There is one more little side note. In this example, I didn't put any urls on the siteMap for demonstration purposes. However, putting this into practice takes a little more effort than I've shown. In Forms Authentication, different parts of the site are secured with location tags in web.config. These tags will implicitly filter a site map (so usually roles attributes usually necessary except to expand visibility). With Windows authentication, the security is also dictated by the file authorization. Denied file access can also implicitly filter a site map in addition to the way location tags do it.

Link to source code

Build a custom hotspot for ImageMap

  Out of the box, ImageMap will support 3 types of HotSpots: Circle, Rectangle, and Polygon. The reason for just these three shapes is that the image map standard only allow for these 3 shapes. Since polygon can create just about any shape we need, there certainly isn't any loss of flexibility.
 
However, entering coordinates for a polygon type of hotspot is quite tedious.  Lets say we wanted a particular shape that we would reuse, it would be convenient to package that into a usable HotSpot that we could add an ImageMap. The good news is that you can.  Just extend the HotSpot base class to create a custom HotSpot. In this example, I'll create a Diamond shaped hotspot.

 To start with, we'll create a new class that inherits from System.Web.UI.WebControl.HotSpot.  This can be in App_Code or a compiled assembly in bin.  In fact, it is pretty much no different then creating a custom control. 

Namespace imhs
    Public Class DiamondHotSpot
        Inherits System.Web.UI.WebControls.HotSpot

    End Class
End Namespace

Next add some properties:

        Public Property CenterX() As Integer
            Get
                Dim val As Object = ViewState("centerx")
                If val Is Nothing Then
                    Return 0
                Else
                    Return CInt(val)
                End If
            End Get
            Set(ByVal value As Integer)
                ViewState("centerx") = value
            End Set
        End Property

        ... repeated for CenterY, Width, and Height ...

Lastly, we need to override 2 functions: MarkupName and GetCoordinates
  We specify the MarkupName of poly because that is the type of AREA tag we'll be using in the underlying image map (remember the standard only recognizes 3 types).

        Protected Overrides ReadOnly Property MarkupName() As String
            Get
                Return "poly"
            End Get
        End Property

        Public Overrides Function GetCoordinates() As String
            Return CenterX.ToString() & "," & _
                   (CenterY - Height/2).ToString() & "," & _
                   (CenterX + Width/2).ToString() & "," & _
                   CenterY.ToString() & "," & _
                   CenterX.ToString() & "," & _
                   (CenterY + Height/2).ToString() & "," & _
                   (CenterX - Width / 2).ToString() & "," & _ 
                   CenterY.ToString()
        End Function

In order to use this in our page, we treat it like any other custom control:

Register it at the top of the page:

 <%@ Register TagPrefix="imhs" Namespace="imhs" %>

And we can add it to our control programmatically or declaritively:

    Protected Sub Page_Load(ByVal sender As Object, _ 
                            ByVal e As System.EventArgs)
        If Not IsPostBack Then
            Dim dhs As New DiamondHotSpot
            dhs.CenterX = 100
            dhs.CenterY = 50
            dhs.Height = 100
            dhs.Width = 50
            ImageMap1.HotSpots.Add(dhs)           
        End If
    End Sub

    <asp:ImageMap ID="ImageMap1" runat="server" ImageUrl="~/superwhiskey.jpg">
        <imhs:DiamondHotSpot CenterX="100" CenterY="50"
                             Height="100" Width="50" />
    </asp:ImageMap>

 

Link to full code listing

Default HotSpot Types:
Circle

Rectangle

Polygon


Custom Diamond HotSpot

Why does StartingNodeOffset make my SiteMap dissappear?

This used to confused me too before I finally understood what was happening.  First, let me explain what I'm talking about.  Let’s say our SiteMap looks like this:



And in home.aspx we have the following:

<asp:SiteMapDataSource runat="server" id="SiteMapDataSource1"
    StartingNodeOffset="1" />
<asp:TreeView runat="server" id="TreeView1"
    DataSourceID="SiteMapDataSource1" />

Here's the output:



Now, the same code in page4.aspx and in page10.aspx


So what happened?  Well, the SiteMapDataSource needs a new root node for the tree because we requested a StartingNodeOffset.  In order to determine this new root node, ASP.NET takes the path to the current node and chooses the next node down from the root node as the new root node.  From there, ASP.NET can derive a sub tree that the SiteMapDataSource should represent.



But, in the case of home.aspx, we were at the last node in the chain.  The exact same thing would happen if we did this: 

<asp:SiteMapDataSource runat="server" id="SiteMapDataSource1"
    StartingNodeOffset="1" StartFromCurrentNode="true" />. 

The result is that ASP.NET did not have enough nodes in the path to get the new root node, so 'Nothing' was returned and no tree gets rendered:

[StartingNodeOffset="5", page10.aspx]

So when would StartingNodeOffset and StartFromCurrentNode="true" ever be useful together?  StartingNodeOffset doesn't have to be positive, it can also be a negative number.  Using: 

<asp:SiteMapDataSource runat="server" id="SiteMapDataSource1"
    StartFromCurrentNode="true" StartingNodeOffset="-3" />

will only show the tree from 3 levels above and downward from wherever you are and thus filtering out potentially large amounts of non-relevant data. 

More Posts