PrincipalCache And Wcf

PrincipalCache And Wcf

Old forum URL: forums.lhotka.net/forums/t/10361.aspx


NemisisJedi posted on Wednesday, May 11, 2011

I am new to Wcf and have now created a new service using a similar security setup that is in the 2008 Business Objects book, where by a custom UserNamePasswordValidator is used along with a custom AuthorizationPolicy.

The problem being that our security is two fold, first the user must be authenticated using the username and password, once that is done, the user can then chose a database that they can access, so as well as IsAuthenticated i have a property called IsAuthorized against my custom Identity object.

To accomplish this i have setup 2 UserNamePasswordValidators, the first one to check the username and password, the second checks the username and password, as well as checking if the user has selected a database.  If they havent selected a database, then it fails.  All good so far.

My problem arises in the AuthorizationPolicy file because if i make a call to the Wcf service the first time (with only username and password, no database selected) my code calls the LoginPolicy file which contains this code (like in the book).

        Dim lPrincipal As IPrincipal = Csla.Security.PrincipalCache.GetPrincipal(lUsername)

        If lPrincipal Is Nothing Then

            UserPrincipal.Logout()

            ' load prinicpal based on username authenticated in CredentialValidator

            UserPrincipal.LoadPrincipal(lUsername)

            ' add current principal to rolling cache
            Csla.Security.PrincipalCache.AddPrincipal(Csla.ApplicationContext.User)

            lPrincipal = Csla.ApplicationContext.User

        End If

lPrincipal Is Nothing so the Identity is reloaded using as suggested in the book the LoadPrincipal code.

Now my user calls the service again after selecting a database and my code calls the AuthorizationPolicy, which again contains exactly the same code as above (separated at the moment as had to figure out what was wrong).  

Now lPrincipal IS NOT NOTHING because it was added in the previous LoginPolicy file, but the Identity added there was not Athorized, so i cannot use that Identity.
If i comment out the If statement and load my principal, that works fine, but when the line

            ' add current principal to rolling cache
            Csla.Security.PrincipalCache.AddPrincipal(Csla.ApplicationContext.User)

Is called, my principal is re-added.  Upon looking into the AddPrincipal code it runs this line before attempting to add the principal

if (!_cache.Contains(principal))

But my principal is different, as i have now set the IsAuthorized value.  So this code adds another principal object and the GetPrincipal returns the incorrect principal because.....

In the GetPrincipal code for PrincipalCache, it runs a much simplier

if (item.Identity.Name == name)

Therefore, would it not be correct to change the AddPrincipal to only check by name since a principal always contains a Name as implemented IPrincipal as this would cope with all custom principal objects??

If not, i think i need some way to remove the principal by name or i can re-add it?

If someone knows of a way in which i can get round my problem, instead of changing the Csla code, please let me know.  As i said, i am VERY new to Wcf

Thanks all in advance. 

RockfordLhotka replied on Wednesday, May 11, 2011

How are you storing the user's database choice in between service calls? You can't rely on the principal for this purpose - the user load could overrun the cache and some principal objects might be lost (and then reloaded).

The normal way to implement WCF authn is to hit the security database twice on each service call,, and to never maintain anything in memory. I created the PrincipalCache to minimize the extra burden on the security database, but the overall model is designed to continue working even if your user load is higher than the cache can support. And in a real app that is very likely to happen at times when you get a load spike.

You should also consider the potential need to scale out with multiple servers. In that case the principal cache is useless, because it is in memory on one server, and so isn't available to all your servers. In a scale out scenario you have to assume nothing is ever maintained in memory on the server. Or if you really must keep something in memory, you'd use somethink like the AppFabric cache so the cache is distributed across all your servers.

I would suggest that the user's database choice be passed with every service call, or be stored in a table in your database, keyed on the username, and possibly with some expiration period.

 

NemisisJedi replied on Wednesday, May 11, 2011

Rocky,

Thanks for quick reply.

My users database choice is stored in a table in the database, but i was just following the PrincipalCache example as it was written in the book and i had problems with a previous try before seeing your example.

I have now removed the PrincipalCache from the Validators and Policy files and it appears to work perfectly. 

I am not bothered about the extra calls to the security database, rather have extra calls and be more secure.

Thanks for your help

Copyright (c) Marimer LLC