Welcome to AspAdvice Sign in | Join | Help

.Net Discoveries

An attempt to pass along some answers I have discovered in my .Net coding.
A Provider by Any Other Name…Doesn’t Smell So Sweet

Prologue

A funny thing happened on the way to the… management page. I found a kind of an anomaly taking place when you configure your Membership Provider use one application and your Role Provider is configured to use a different one. What I found is that I had users I deleted still assigned to some of my roles (phantom users).

Problem

In the process of creating ‘A Better ASP.Net User/Role Management Page’ found that I (without realizing it) had set the application name for my Roles Provider to be different than the one for the Membership Provider. This leads to some interesting side effects that I didn’t expect. Since I haven’t found any posts regarding this on the internet, I thought I’d touch on the subject mostly as an interesting side note so people can see what’s going on, not that we solve any problems per se but we’ll be informed at least by the time we’re done.

Solution

Before we can get started, we’ll need to make sure we have a website setup with a Membership and a Role provider configured. We should be able to piggyback off the SQLExpress Membership Provider website that was created in the last post. We’ll want to follow the post and create a Membership provider that we can configure. We’ll scrap all the front end and back-end code from the project except the web.config. We’ll also need to define a Role provider while we’re at it so after you add your Membership provider information, add the following as a Role provider right below it:

        <roleManager enabled="true" defaultProvider="myCustomRoleProvider">
            <providers>
                <add name="myApp"
             connectionStringName="mySqlServer" 
             type="System.Web.Security.SqlRoleProvider, System.Web,
                       Version=2.0.0.0, Culture=neutral, 
                       PublicKeyToken=b03f5f7f11d50a3a"/>
            </providers>
        </roleManager>

As a side note, make both your membership and roles provider’s applicationName=”myApp”, so that we start with our providers using the same application. Along the same lines of the project we’re piggybacking on, this will allow us to customize our role provider using the ASPNETDB database that was provided to us. Next we’ll want to create a front end that will allow us to show the results we’re creating as we go, so we know what’s happening.

Open a blank default.aspx (or create it if it doesn’t already exist) and add the following to the front-end code:

<div>
    Roles by User:<br />
    <asp:Label ID="lblRolesByUser" runat="server" Text="" />
    <br />
    <br />
    Users by Role:<br />
    <asp:Label ID="lblUsersByRole" runat="server" Text="Label" />
</div>

This give us two labels to populate, one we’ll populate with our users and show what roles they are assigned to, and the other we’ll populate with the roles and the users that are assigned to them. Now for the back-end code. Add the following helper functions to the code behind:

Private Function GetRolesByUser() As String
    Dim sb As New StringBuilder()
    For Each member As MembershipUser In Membership.GetAllUsers()
        sb.Append("<strong>" & member.UserName & "</strong><br>")
        For Each role As String In Roles.GetRolesForUser(member.UserName)
            sb.Append("-" & role & "<br>")
        Next
    Next
    Return sb.ToString()
End Function

Private Function GetUsersByRole() As String
    Dim sb As New StringBuilder()
    For Each role As String In Roles.GetAllRoles()
        sb.Append("<strong>" & role & "</strong><br>")
        For Each member As String In Roles.GetUsersInRole(role)
            sb.Append("-" & member & "<br>")
        Next
    Next
    Return sb.ToString()
End Function

These functions do exactly what we were talking about before. Once cycles through the members in the database, retrieves the roles they are assigned to and then displays this list. The other cycles through the roles and makes a list of each role and all the members assigned to it then displays it. To run the functions, add the following to your Page_Load event handler:

lblRolesByUser.Text = GetRolesByUser()
lblUsersByRole.Text = GetUsersByRole()

This will populate the labels we have on the page using our two helper functions as soon as the page is run. Now were’ ready to do some playing around. Let’s make sure that to start, you have your membership and roles providers both using the same application name. This way we can see how things SHOULD behave. Add the following line to both your membership and your role provider:

applicationName="myApp"

For First My First Trick – or – Meet the Phantom User

Now let’s go add some users and roles and make some assignments. If you open the solution explorer panel and click ‘ASP.Net Configuration’ button, we’ll get the ‘ASP.net Web Site Administration Tool’ (WSAT – well call it) webpage. Let’s add 2 users and 2 roles and then assign 1 user to each role. For my purposes, I’ll use:

Users Roles
MajorPain BigManOnCampus
JunkMeister LittleLoser

and just assign roles directly across a row. Once we have our users and roles setup, we can run our application and we should get a listing that looks like this:

Roles by User:
JunkMeister
-LittleLoser
MajorPain
-BigManOnCampus

Users by Role:
BigManOnCampus
-MajorPain
LittleLoser
-JunkMeister

You can see that we have our 2 users, 2 roles and 2 assignments (each user is assigned to one role). All this is displayed to us two different ways. Now delete one of your users (I got rid of MajorPain) and refresh the page. Your results should look something like this:

Roles by User:
JunkMeister
-LittleLoser

Users by Role:
BigManOnCampus
LittleLoser
-JunkMeister

Notice that we have our 1 user, and that the user we deleted was removed from the roles list as well, we have 1 user, 2 roles, and only 1 user assigned. This is what we would expect to see. If we remove a user, the provider code should remove its entry from all the roles assignments.

Now let’s get a little fancy with things and see what happens. First, let’s change the name of the membership provider. In your Web.config, change the membership provider’s applicationName to be something different:

applicationName="myOtherApp"

Now refresh your page and see what gets listed. It should look something like this:

Roles by User:

Users by Role:
BigManOnCampus
LittleLoser
-JunkMeister

We have 0 users and 2 roles, but we do have 1 user assigned to our role… Wait a minute, how can we have a user assigned to our LitteLoser role if we don’t have any users? We’ll answer that in a minute. For now, let’s look at it the other way around, change our membership’s applicationName back to “myApp” and change the Roles provider’s applicationName to “MyOtherApp”. Refresh your page and see that it should come up like this":

Roles by User:
JunkMeister

Users by Role:

Notice that we have our user back, but we don’t have any roles in our application AND that since there aren’t any roles in our application, our user isn’t assigned to any… It seems there a bit of a disconnect? We get roles with a phantom user assigned, but not any phantom roles assigned for our user. Interesting, No?

A Good Magician Never Reveals – or – I am Not a Good Magician

Ok, let’s dig into what’s happening and see how it all works, change your membership and role’s applicationName back to myApp and setup with your 2 users, 2 roles and 2 assignments again. If you’ve got it setup right, your page should display as follows:

Roles by User:
JunkMeister
-LittleLoser
MajorPain
-BigManOnCampus

Users by Role:
BigManOnCampus
-MajorPain
LittleLoser
-JunkMeister

Now, let’s dig into the behind the scenes. Open your ‘Server Explorer’ panel. Open your database, then the tables node and let’s look into our tables. First, right-click your Users table and select ‘Show table data’. You should see 2 users, like the picture to the right.

Let’s also dig into the Roles table, show the table data for the roles, and you should see a table with 2 roles, again nothing surprising (see image at right).

Ok, let’s look at one more table, show the table data for your UsersInRoles table. You should see two assignments (two rows). While the IDs may look intimidating, we can see from our pictures above that the UserID comes from the Users table and the RoleID comes form the Roles table. Thus we can translate our IDs and see that we have our 2 users, 2 roles and 2 assignments.

Now, let’s repeat our previous experiment, but let’s start with the Role provider. Change your Role Provider’s applicationName and refresh our browser, we should get:

Roles by User:
JunkMeister
MajorPain

Users by Role:

2 users, 0 Roles and 0 role assignments, as it should be. Where did our roles go? For that we need to open one more table, show table data on our Applications table. This should show our ApplicationID’s notice how we have two applications, now notice that our roles and our users both have an applicationID as well. Both These all have an applicationID that matches our ‘myApp’ application. Meaning that when we change our Roles provider to to be ‘myOtherApp’, it no longer detects that we have any roles or role assignments since it looks for roles and role assignments only with our applicationID (Note: not strictly correct as we’ll see shortly). This allows you to use the same database for more than one website, each website just needs a different applicationID and the data resides in the same database.

Now let’s try it with our Membership Provider, change the Roles Provider’s applicationName back to ‘myApp’ and our Membership Provider’s applicationName to ‘myOtherApp’. Now refresh our page and see what happens. If it works like when we changed our Roles provider, we we would expect to end up with 2 Roles, 0 users, and 0 role assignments, as all the users assigned to roles exist in ‘myApp’ and we’ve ask for users in ‘myOtherApp’. Refresh your page and you should get:

Roles by User:

Users by Role:
BigManOnCampus
-MajorPain
LittleLoser
-JunkMeister

We should be expecting 0 users, 2 roles and 0 assignments, but Notice that we’re getting usernames that we SHOUDN’T be getting: 0 users, 2 roles and 2 assignments. What gives? Check out our code behind. In our ‘GetUsersByRole’ function, we’re getting all our roles, iterating through them and using the role to pull all members in the role by using the Roles.GetUsersInRole(role) method. For some reason, this built-in function has some kind of glitch where it doesn’t filter out users that aren’t in our specified application (myOtherApp). That’s the secret of the trick: we should be getting only users returned to us who are IN our specified application (myOtherApp), but in fact we’re getting back users that WE SHOULD NOT BE ABLE TO SEE by virtue of our configuration.

We can see part of the root of the problem by looking at our UsersInRoles table. Notice something missing? Yeah, it’s got no applicationID. When we use the Roles.GetUsersInRole, it should process the UserID against the applicationID (from a different table) and do a filter. It doesn’t.

To confirm that we’re getting roles assignments (and thus usernames) from multiple applications, right now add another user (MickyMouse) and assign him to the ‘BigManOnCampus’ role and refresh your page. You should see something like this:

Roles by User:
MickyMouse
-BigManOnCampus
Users by Role:

BigManOnCampus
-MajorPain
-MickyMouse
LittleLoser
-JunkMeister

We only have 1 user in our users list, but we have a total of 3 users to roles assignments AND we’ve pulled usernames from TWO DIFFERENT APPLICATIONS AT THE SAME TIME and used them!

I Think You Underestimate My Trickiness – or – Different appNames From the Get Go

Ok, now let’s get a little more hairy with it. While we’re here, delete your MickeyMouse user. and then refresh your page. Since we’re in the application where we created our MickyMouse user, the user should be deleted and removed from all roles, right? Refresh and see, you should see something like this:

Roles by User:

Users by Role:
BigManOnCampus
-MajorPain
-MickyMouse
LittleLoser
-JunkMeister

We’ve got 0 users, but 3 users assigned to roles. Technically, we’re pulling two of the users from a different application, so they DO exist in the database, but our MickeyMouse user no longer exists in the Database or he would show up in the user section (he’s not being filtered out, we’re in his application). What gives?

We’re finally getting down to the crux of the problem I originally found. I had accidentally created an applicationID for each the Role and the Membership, and didn’t even notice till I pulled the data we are here that little things were being left floating around (like our MickeyMouse assignment).

Let’s see if we can create the issue from the ground up and watch what’s happening in our database so we can see how it happens. Rather than going through the interface of the WSAT, I’ll have you delete directly from the tables since we won’t be able to 100% flush our users out otherwise (since MickyMouse doesn’t exist yet still shows up).

If you select all your rows in VS.Net, while ‘showing table data’, you can press delete and delete all the rows. Let’s do this for the following tables and in this order (no cheating, no looking at the data in the tables, now):

UsersInRole
Roles
Membership
Users

The order of tables here will give a little bit of hint of things to come, (hint: we haven’t mentioned the membership table at all, have we). Also, in preparation for our next excercise we want to make sure that our Membership and Roles providers have DIFFERENT applicationNames. Ok, now if you run your page again, you should end up with a blank list:

Roles by User:

Users by Role:

This is to be expected since we don’t have any users or roles in our database. So, now that we have everything setup, let’s add 2 users and 2 roles, but NOT make any assignments just yet. Open your WSAT and add back in our MajorPain and JunkMeister users and add back in our BigManOnCampus and LittleLoser roles. Refresh your page and you should see:

Roles by User:
JunkMeister
MajorPain

Users by Role:
BigManOnCampus
LittleLoser

2 users, 2 roles and 0 assignments – as expected. First, let’s look into our tables. Let’s start with UsersInRoles. Notice we get nothing (no assignments yet). Now, let’s check our Roles and Users tables. You shouldn’t be surprised to see that each has 2 entries, and also shouldn’t be surprised that the roles have a different ApplicationID than the users do.

Ok, now let’s add a users to a role. Start with MajorPain, add him to the BMOC role and then refresh our page. We should see our assignment:

Roles by User:
JunkMeister
MajorPain
-BigManOnCampus

Users by Role:
BigManOnCampus
-MajorPain
LittleLoser

Now let’s look back at our UsersInRole table and see what it looks like. Much like we’d expect, one row showing our one assignment. Now, let’s go back and look at our users table.

WHOA. Where did the extra MajorPain come from? Well, since we didn’t have a MajorPain in our Role’s Application, the database creates one in our users table and assigns it the ApplicationID for our Roles provider. Notice that we have two MajorPains, but they do have 2 different ApplicationIDs assigned to them (one for our membership provider’s application and one for our role provider’s application).

Now, observe what happens when we delete our user MajorPain. Go ahead and delete him. What happened? Right, only one disappeared. Notice it was the one associated with our Membership provider. Our Roles provider’s MajorPain user is still intact. Refresh our page and see what happens.

Roles by User:
JunkMeister

Users by Role:
BigManOnCampus
-MajorPain
LittleLoser

We have no MajorPain user, but we have a phantom MajorPain in our role assignments list. Thus we created a 2nd MajorPain user and now we’ve stranded him in our database. And we can’t just change our membership provider to match his applicationID and delete him. Change your Membership provider’s ApplicationName to match your Roles Provider and refresh your page. Theoretically, he should show up as a user right? WRONG! When you refresh, you get:

Roles by User:

Users by Role:
BigManOnCampus
-MajorPain
LittleLoser

No users, but we do get our phantom user to role assignment. What gives? Go back to our Membership table and take a look. We only have one row in this table, it belongs to JunkMeister (match is UserID up with the User’s table). But if you go to the User’s table and look you’ll see our phantom MajorPain user. Try to lookup our phantom MajorPain’s UserID, it isn’t in the Membership table. Unless the user has an entry in this table (Membership), it won’t show up when we use our membership methods. Even removing our ‘phantom’ user from the role doesn’t remove our phantom user from the Users table.

Epilogue

So there it is, phantom users in our membership/roles database.

Funny, I started out this post with the intent to create an innocently intentioned post about something strange that I found. However as I worked my way through presenting it, I found I was kind of disillusioned with the innocence. While I’m generally pretty happy with Microsoft and their related .Net technologies, it seems that this would be considered a fairly LARGE security loophole and/or could cause problems for any one of a number of reasons:

  • By changing the role provider’s application name, I can (if they exist) retrieve usernames that belong to a different application. While it might be assumed that I wouldn’t be able to access more than one application without proper permission, this seems somewhat lacking in security. (Especially when you consider some of the functionality that wasn’t baked into the providers for security’s sake – such as changing a username – it seems that this wasn’t a deliberate oversight).
  • Along with the above problem, I can, by changing the role provider’s application name, pull username/role assignments from MULTIPLE applications AT ONE TIME. Again, this seems like somewhat of a security problem – and I’ll get INCORRECT data that may give me hiccups in my code.
  • I can retrieve usernames and role associations that no longer exist. This poses a problem if I want to process things using information from the roles list. I may not be able to manipulate things based on the Roles.GetUsersInRoles() method, because it returns INCORRECT data and may thus return some unexpected exceptions.
  • We can create phantom users in our users table, however, we CANNOT, through the providers mechanisms provided by Microsoft, purge the phantom user(s) entirely from the database.
  • And on top of it all, I wasn’t able to find ANY documentation anywhere regarding the issue. Maybe nobody else is dumb enough to split their providers (like I was), but it seems like if it was designed to behave this way, that it would be explained somewhere in documentation or at least on a blog somewhere. At least tell us it’s possible, but warn us that it’s stupid a bad idea.

Anyhow, as I see it “Lucy, you got some splainin’ to do!” – or – At the very least, we should be very careful to make sure that we don’t mix up our applicatioNames when we are configuring our providers, or we may get some unexpected and undesirable results.

Posted: Tuesday, June 16, 2009 11:42 AM by Yougotiger
Filed under: , ,

Comments

No Comments

Leave a Comment

(required) 

(required) 

(optional)

(required) 

Enter the code you see below

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS