How to Surgically Disable Role-Checking inone place?

How to Surgically Disable Role-Checking inone place?

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


david.wendelken posted on Thursday, October 05, 2006

 

Where in CSLA do I go to circumvent the Active Directory role checking?

If I'm programming disconnected from an Active Directory server, I would like to be able to go into just one function and tell it to return "sure, you've got the role" instead of having to disable the security checking in all the classes I'm working with.

I tried to find it but just plain got lost in the code...

 

Brian Criswell replied on Thursday, October 05, 2006

What about altering MyBusinessIdentity.IsInRole to return true if you are disconnected?

david.wendelken replied on Thursday, October 05, 2006

MyBusinessIdentity.IsInRole?

Couldn't find that with a search on the CSLA Library code.

If you mean in each business object that I code, no way!  This is just so I can work at home without having to alter a bunch of code just to get basic functionality up and running, then having to undo the changes before I check the code back in...

Brian Criswell replied on Thursday, October 05, 2006

No, I mean the Identity class you have created could recognize that it is being run off of the domain and respond by authorizing the user to do everything regardless of what role is asked for.

david.wendelken replied on Thursday, October 05, 2006

But I didn't create an identity class!

I'm just using the standard apply authorization rules code in the business objects using (I assume) the default CSLA features that implement that.  

Brian Criswell replied on Thursday, October 05, 2006

Right.  Sorry.  Brain fade.  You did mention domain security.  Well, creating a principal and an identity class would take very little time.  You could have them get a reference to the Windows principal in the Login method and IsInRole could delegate to the Windows principle when on the domain and just return true when not on the domain.  You could then call the Login method when your application starts up, and wrap the call to Login in an #if DEBUG so that the custom principal would not come into play at all for the released version.

This would allow your application to behave normally when in debug mode on the domain, give full permissions when in debug mode and not on the domain, and require the domain when in release mode.

david.wendelken replied on Friday, October 06, 2006

If I already knew how to do all that, I would agree with you. :)

But I don't, and my learning curve time is already over-allocated for this phase of the project.

Regardless, I would need to know what class/method in CSLA to delegate to, and that was my original question:

Exactly what piece of code in CSLA makes that active directory verification call?

Mark replied on Friday, October 06, 2006

CSLA doesn't do any Active Directory authentication.  It defines a BusinessPrincipalBase class that you sub-class and implement your own authentication mechanism (either via a DB, Active Directory, etc).

If your application is doing Active Directory authentication, it's because someone (you or someone else) wrote the code to do so.  Look for a class that is subclassed from BusinessPrincipalBase or that implements the IIdentity interface.

What I've done in several projects is create an SSPI wrapper that allows me to authenticate either against a domain or against my local machine for when I'm on the road.  Once I authenticate, I load the user permissions from the DB. 

Brian Criswell replied on Friday, October 06, 2006

The authorization rules engine checks the current identity for the roles through Csla.ApplicationContext.User which checks System.Web.HttpContext.User or System.Threading.Thread.CurrentPrincipal as appropriate.  This is automatically set by .NET with the current Windows principal, which is why it has "just worked".  I really do think that your best bet is with a custom principal.

I have hacked together a custom principal using the project tracker principal as an example.  If you make some small modifications to it (mainly changing name and namespace and determining if you are on the domain) it should work (I think).  Somewher near the beginning of your program do the following:

#if DEBUG

PTPrincipal.Login();

#end if

and you should be set.

EDIT:  Oops, shows what I know.  It looks like Mark is right and the principal is not set up automatically, so there should be a principal in your application somewhere.  If you modify its IsInRole method with an #if DEBUG that automatically returns true if you are not on the domain then you should be set.

Mark replied on Friday, October 06, 2006

Exactly.  There has to be a principal defined somewhere in the project that is handling the active directory authentication and user roles.  That's not supplied "out-of-the-box".

Overriding the IsInRole via conditional compilations statements will not necessarily solve the problem, though.  It does eliminate any run-time role checking concerns but doesn't address the authentication problem.

If the code is truly authenticating against active directory, modify the Login() method in the principal object.

public static bool Login(string username, string password)
{
#IF DEBUG
   Csla.ApplicationContext.User = <CREATE YOUR OWN CUSTOM PRINCIPAL>;
   return true;
#ELSE
   MyIdentity identity = MyIdentity.GetIdentity(username, password);  //assumes this hitting an AD server
   if (identity.IsAuthenticated)
   {
      MyPrincipal principal = new MyPrincipal(identity);
      Csla.ApplicationContext.User = principal;
   }
   return identity.IsAuthenticated;
#ENDIF
}

david.wendelken replied on Wednesday, October 18, 2006

We're working on putting in a custom principal and identity object.

But I have to tell you, I'm confused about all this!

When I work with my laptop connected to the network (and thus active directory), the code ran.

When I work with my laptop disconnected, all the IsInRole checks built into the business objects raise an error because ActiveDirectory isn't available.

So, silly me, I assumed it was working with ActiveDirectory by default.

It's not, because it's not disabling the editing buttons on the screen when I take away the necessary active directory role (and log in fresh afterwards).

So, by default, it doesn't appear to USE active directory, but it won't run without active directory!

That doesn't make a lick of sense to me...   What's going on that I'm missing?

 

 

Mark replied on Wednesday, October 18, 2006

You need to look at your code.  Do you have a Login() method that's called somewhere?  If so, jump into that method to find how/where you're being authenticated.  If it's against a SQL database on a remote server (not on your laptop), you will obviously have problems when you're disconnected from the network.  If it's truly against an Active Directory or LDAP server (possible, but unlikely), we can look at that as well.

As I mentioned in a previous message, you need to search *your* project for a class that inherits from BusinessPrincipalBase.  That's where the relevant authorization code will likely be found.  If you can post the code here, we can help you diagnose your authentication issues.

david.wendelken replied on Wednesday, October 18, 2006

There is no custom class in my project that inherited from BusinessPrincipalBase. 

I didn't create one, and no one else did either.  Really. :)

And, no, I didn't do a "login" to the application either.  I just login to windows in the normal way and compile and run the application from visual studio.

The code is just like this:

protected override void AddAuthorizationRules()

{
   AuthorizationRules.AllowWrite("Id", "ApplicationWriteRole");
}

and like this:

public static bool CanAddObject()

{
   return Csla.ApplicationContext.User.IsInRole("ApplicationWriteRole");
}

When disconnected from the ActiveDirectory domain, the application runs fine until these code units are run, at which time it blows up.

When connected to the ActiveDirectory domain, the application runs fine and the objects act appropriately based upon whether I have the role or not.

We started writing a custom principle today, but I still don't understand why I get the behaviour I do unless it actually does support ActiveDirectory out of the box.

 

RockfordLhotka replied on Wednesday, October 18, 2006

If you are using Windows integrated security, then .NET itself (through the WindowsPrincipal and WindowsIdentity classes) may be talking to AD on your behalf. I've never really researched how the interaction works there - and I suppose it may be the case that when your workstation is a member of a domain, that .NET directly tries to access AD behind the scenes.

Mark replied on Wednesday, October 18, 2006

Ok - I looked a bit closer at the code in CSLA - ApplicationContext.  If you're not calling your own login method and assigning a CustomPrincipal, it looks when you make a caller that access ApplicationContext.User, it will return whatever is sitting in Thread.CurrentPrincipal.

If you happen to be on a domain, Thread.CurrentPrincipal would probably be your domain-level account.  Calling IsInRole() on that principal would return a list of security roles that you've been granted on the domain (I think).

Now - I wouldn't call this supporting Active Directory out of the box.  You simply get that behavior because your machine happens to be part of a domain.  My laptop isn't part of a domain, so Thread.CurrentPrincipal would return a local machine account.

Anyway - to get the behavior you want, you need to implement a custom principal object, and check if you're on the domain or not when logging in.

ajj3085 replied on Wednesday, October 18, 2006

Mark:
If you happen to be on a domain, Thread.CurrentPrincipal would probably be your domain-level account.


I think by default you are assigned GenericPrincipal.  In my application I need to set it explicity by creating a WindowsPrincipal.  The roles will be returned as you specified though.

Mark:
Now - I wouldn't call this supporting Active Directory out of the box.  You simply get that behavior because your machine happens to be part of a domain.  My laptop isn't part of a domain, so Thread.CurrentPrincipal would return a local machine account.


This is true too, but if your laptop was part of the domain I believe even if you are disconnected from the network you'll get the credentails you want if you login as your domain user, unless of course Windows has been set not to cache logon information (which can be done in Admin Tools -> Local Security policy, which of course can be set by a domain controller).

HTH
Andy

shane.sanders replied on Wednesday, October 18, 2006

Csla returns Thread.CurrentPrincipal from within ApplicationContext.cs

System.Thread.Threading.CurrentPrincipal.IsInRole("DOMAIN\\MY_SECURITY_GROUP")  returns false.  Possibly because of some changes in the 2.0 framework?  2003 Server domain... 

Oddly enough, the 2.0 framework no longer seems to allow casting a WindowsPrincipal out of the CurrentPrincipal which is really an IPrincipal type.  

I was able to get functionality by doing the following:

//Security/ApplicationContext.cs

public static IPrincipal User

{

get

{

if (HttpContext.Current == null)

{

//Substitution ***

WindowsPrincipal wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());

return wp;

}

//return Thread.CurrentPrincipal; //IsInRole() Returns false, always

else

return HttpContext.Current.User;

}

ajj3085 replied on Thursday, October 19, 2006

Shane,

I don't include the DOMAIN\ part when asking for a role, and I don't think its necessary.

You can cast Current.User to WindowsPrincipal, I do it in my application.  Keep in mind new threads usually start with GenericPrincipal, unless you set the default for the app domain.  What exactly is the scenario when you're getting a casting error?

shane.sanders replied on Thursday, October 19, 2006

C#
WindowsPrincipal MyPrincipal = 
    (WindowsPrincipal) Thread.CurrentPrincipal;
from http://msdn2.microsoft.com/en-us/library/t6547wf1.aspx
This used to work, I believe they may have increased the level of type checking in .NET 2.0.  
The type of CurrentPrincipal should be IPrincipal.  * EDITED * It turns out it's
a GenericPrincipal.
 
Side Note:  I belive the reason I need to use Domain\Security_Group is because the 
group scope is Domain.Local.   

ajj3085 replied on Thursday, October 19, 2006

That doesn't make sense...

The princpal in CurrentPrincipal can be of both type IPrincipal and WindowsPrincipal at the same time.. but if CurrentPrincipal is currently set to an object of type GenericPrincipal, you'll hit errors.  Set a breakpoint on the line where you're reading out of CurrentPrincipal and check out the actual type using the debugger.

I think all the domains we are using are global.. so it may be the case that you need to preprend the domain\ to your role checks.  Again though, try it in the quick watch window.. type in both with and without the qualifier and see what gets returned (or use the debugger to peak at the windowsprincipal interal collection of roles).

HTH
Andy

shane.sanders replied on Thursday, October 19, 2006

It is a GenericPrincipal.  nice

ajj3085 replied on Thursday, October 19, 2006

Ok... so you'll need to set it usually on program startup if you're not already.  If its in another thread, the default is that new threads get GenericPrincipal.  You can use SetThreadPrincipal to set the default, but do so right at the beginning of the application.

HTH
Andy

shane.sanders replied on Thursday, October 19, 2006

AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);

WindowsPrincipal wp = (WindowsPrincipal)Thread.CurrentPrincipal;

 

It was stated on the msdn page I previously posted Embarrassed [:$]

I should be able to add that to the csla framework and get the original code to work.

 

Thanks.

L8r

ajj3085 replied on Thursday, October 19, 2006

No problem... you'd think it'd be WindowsPrincipal by default anyway.. ah well, glad to hear its working for you now.

Copyright (c) Marimer LLC