ASP.Net 2.0 Provider Model - Code

ASP.Net 2.0 Provider Model - Code

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


skagen00 posted on Monday, May 21, 2007

I've seen a fair number of posts on this (one more this morning) so I thought I'd share some code.

My solution is to completely leverage the implementation of the provided SqlMembershipProvider and SqlRoleProvider. Of course, these are really only 2-tier and so the CslaProviders are here to facilitate the 3-tier behavior - using a command object to remote over and execute the appropriate logic on the app server.

The intention for us is to use the membership & role providers if we should move to Windows forms as well. Thus, this has been integrated in the principal and identity classes.

#1: Setup DB

So one of the first things you'll need to do to roll with this implementation is to run the appropriate utility that configures all the necessary tables and stored procedures for the implementation of SqlMembershipProvider and SqlRoleProvider that exists in the .Net framework. This utility is: aspnet_regsql.exe. There's plenty of info online on how to do this.

The next thing I might suggest is to download the sample code of the implementation for the SqlMembershipProvider and SqlRoleProvider - you can find it here: http://weblogs.asp.net/scottgu/archive/2006/04/13/442772.aspx. The reason why I might suggest it is because you can include it within your project and debug step through it. It's entirely optional, however.

#2: Web.config file

In the Web.config, when you're setting up your role provider and membership provider for the respective API's, it'll look like this:

<membership defaultProvider="CslaMembershipProvider">
 <providers>
  <!-- Note that the connection string is irrelevant on the client side Web app -->
  <!-- It is supplied on the remoting site's Web config. SqlMembershipProvider balks if it's not  there. -->
  <clear/>
  <add name="CslaMembershipProvider"
   type="<YourNameSpaceForTheCslaProviders>.CslaMembershipProvider"
   connectionStringName="<YourDBConnString>"
   functionalProvider="Microsoft.Samples.SqlMembershipProvider"
   passwordFormat="Encrypted"
   minRequiredNonalphanumericCharacters="0"
   minRequiredPasswordLength="6"
   enablePasswordRetrieval="false"
   requiresUniqueEmail="true"
   requiresQuestionAndAnswer="true"
   enablePasswordReset="true"
   description="Csla enabled membership provider"/>   
 </providers>
</membership>
<roleManager enabled="true" defaultProvider="CslaRoleProvider" cacheRolesInCookie="true" cookieName=".RolesCookie" cookieTimeout="30" cookieSlidingExpiration="true" cookieProtection="All">
 <providers>
  <add name="CslaRoleProvider"
   type="<YourNameSpaceForTheCslaProviders>.CslaRoleProvider"
   functionalProvider="Microsoft.Samples.SqlRoleProvider"
   connectionStringName="<YourDBConnString>"
   description="Csla enabled role provider"/>
 </providers>
</roleManager>

The functionalProvider is the only additional key - otherwise the setup is exactly how the default providers would be implemented. The functionalProvider tells the CslaProvider which implemented provider it should utilize. Note that I happen to be using the samples ones - if you weren't using the sample ones, you'd use: System.Web.Security.SqlMembershipProvider, etc.

The web.config portions above should match between the remoting app and the web app. The one exception here is that you don't need to place a real connection string in the web app's web.config. Data access won't be happening from the web application. However, if you don't include the key, the built-in providers will throw an exception. So just "connectionStringName="none"" is sufficient.

Since we're leveraging the implementation of the built-in providers, feel free to utilize all of the bells and whistles that it provides. Encrypting passwords, etc.

#3: Principal & Identity objects

The identity object gets it's "authenticated" value by leveraging the membership API.

private void DataPortal_Fetch(Criteria criteria)

{

bool authenticated = System.Web.Security.Membership.ValidateUser(criteria.Username, criteria.Password);

if (authenticated)

{

_isAuthenticated = true;

_name = criteria.Username;

}

else

{

_isAuthenticated = false;

_name = "";

}

}

And the principal gets its roles via the roles API:

public override bool IsInRole(string role)

{

return Roles.IsUserInRole(Identity.Name, role);

}

To be honest I don't know if it caches the roles yet (haven't looked), but obviously that wouldn't be a problem to implement.

#4: Authenticating via login page

protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)

{

e.Authenticated = <PrincipalClass>.LogOn(Login1.UserName, Login1.Password);

HttpContext.Current.Session["CslaPrincipal"] = Csla.ApplicationContext.User;

}

When authenticating, simply going through the principal's login will invoke the appropriate membership and role API.

#5: CslaRoleProvider & CslaMembershipProvider

These classes effectively "wrap" a functional provider - instantiating and initializing the provider so that whenever the Role API or Membership API calls it, that it calls the appropriate "functional provider".

The initialization of them looks like this:

/// <summary>

/// Initialize is overridden to take the name/value collection and strip off the one thing

/// we care about - the functionalProvider. It's removed to not cause exceptions on the

/// implemented providers.

/// </summary>

public override void Initialize(string name, NameValueCollection config)

{

if (config == null)

{

throw new ArgumentNullException("config");

}

// Determine the functional provider, instantiate and initialize it.

Type functionalProviderType = Type.GetType(config["functionalProvider"]);

config.Remove("functionalProvider");

_wrappedProvider = (MembershipProvider)Activator.CreateInstance(functionalProviderType);

_wrappedProvider.Initialize(name, config);

}

That is, create an instance of the right functional provider and initialize it with the config information.

And with each invocation of the different methods, we simply delegate to the underlying behavior of the functional provider. But what happens is that before a delegation occurs, it looks to see if it needs to execute the functionality on a remoting application - if it does, it uses a command object to facilitate the execution of the method on the app server, returning the appropriate return values and out parameters. There's some duplication of logic in the "ExecuteRemoteMethod" (exists both in the role and membership providers) but I'm not worried about it at this point.

It should be stated that this hasn't been given the rounds in a production environment but it seems to have worked well for us up to this point and I feel like it should be a sound approach. Also, I've only pursued this approach with the Sql providers within the .Net framework - how well it might work with any others, I haven't explored.

Happy coding,

Chris

p.s. the two providers were put into one txt file as I couldn't seem to figure out multiple attachments.

 

skaue replied on Monday, May 21, 2007

Great stuff, Chris!!

Thanks for sharing :)

skagen00 replied on Monday, May 21, 2007

There was one additional thing I forgot to mention.

On the remoting directory'sWeb.config file, you'll need to specify "applicationName" as a member for both within the Web config file.

That is, if your main web application is "twiggy", then you'll need in your remoting Web.config to have: applicationName="/twiggy" so that when it runs the appropriate membership and/or role api implementation that it is querying for the authorization/authentication of the appropriate application.

 

skaue replied on Tuesday, May 22, 2007

What I dont understand is why this approach doesnt have a downloadable example on this site. Is it so uncommon to use the asp.net provider model and make a webapplication using the membership and roles AND CSLA?

This forumthread should be a sticky imo :-/

devbanana replied on Wednesday, July 11, 2007

I'm sorry to bump this thread, but I'm having a slight problem.

 

The SqlMembershipProvider seems to work, but the SqlRoleProvider is not. I get this error when trying to put it into Web.config:

 

Parser Error Message: Value cannot be null.
Parameter name: type

Source Error:

 
Line 252:      <providers>
Line 253:        <add name="SqlRoleProvider"
Line 254:             type="Azavia.Web.Security.SqlRoleProvider"
Line 255:             functionalProvider="System.Web.Security.SqlRoleProvider"
Line 256:             connectionStringName="main"

Source File: C:\projects\azavia\www\Web\web.config    Line: 254

 

The weird thing is, I am specifying the type parameter, as you can see below.

<roleManager cacheRolesInCookie="true"

cookieName=".AZAVIAROLES"

createPersistentCookie="true"

defaultProvider="SqlRoleProvider"

enabled="true">

<providers>

<add name="SqlRoleProvider"

type="Azavia.Web.Security.SqlRoleProvider"

functionalProvider="System.Web.Security.SqlRoleProvider"

connectionStringName="main"

description="SqlRoleProvider for Azavia"

applicationName="Azavia" />

</providers>

</roleManager>

 

If anyone could help with this, it'd be much appreciated.

 

Also, does anyone have something similar for the profile provider?

skagen00 replied on Wednesday, July 11, 2007

Hey there -

You won't be able to get the profile provider to work in the same way. Unfortunately there are some classes used in the API that are not serializable. I started to try to get it to work but it was going to be too much of a PITA when I ran into the non-serializable classes. I decided I didn't care that much about the profile provider so I hadn't pursued it further.

At first glance I don't see a problem with what you have but I do know that I had included more specific information for the functional provider. Also, you could certainly do as I suggest further above, download the sample code and actually step through it in the debugger - that's probably the best way to diagnose what you're running into.

Good luck,

Chris

My portion in me web.config

<roleManager enabled="true" defaultProvider="CslaRoleProvider" cacheRolesInCookie="true" cookieName=".RolesCookie" cookieTimeout="30" cookieSlidingExpiration="true" cookieProtection="All">

<providers>

<add name="CslaRoleProvider" type="<mynamespace>.CslaRoleProvider" functionalProvider="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="<conn>" description="Csla enabled role provider"/>

</providers>

</roleManager>

 

devbanana replied on Wednesday, July 11, 2007

skagen00:

Hey there -

You won't be able to get the profile provider to work in the same way. Unfortunately there are some classes used in the API that are not serializable. I started to try to get it to work but it was going to be too much of a PITA when I ran into the non-serializable classes. I decided I didn't care that much about the profile provider so I hadn't pursued it further.

Ah, I see. May I ask then what you do for profile information?

skagen00:
At first glance I don't see a problem with what you have but I do know that I had included more specific information for the functional provider. Also, you could certainly do as I suggest further above, download the sample code and actually step through it in the debugger - that's probably the best way to diagnose what you're running into.

Good luck,

Chris

Oh, for some reason I had to set type to:

type="Azavia.Web.Security.SqlRoleProvider, Azavia.Web"

It didn't require me to do that for the membership provider, so that is strange.

Seems to be working now though, thanks.

One thing; is it required to put that code you specified for the login control? Following the example from project tracker, well I don't think it has anything like that.

Again thank you.

skagen00 replied on Wednesday, July 11, 2007

First, just don't really have that big of a need for profile information (and wasn't really fond of how it's stored anyways in the implemented provider) - so I just didn't pursue it further. I initially wanted to carry it through simply "because I could" and that it wouldn't be that much work.

I'm glad you got it to work. As far as the code there for the login control - it's up to you. The authenticated principal is attached to the current thread and it is done so with each request - storing it in the session allows one to grab it and reattach it to the current request so that if roles and so forth need to be checked for authorization purposes - it's there. (I think I showed the global.asax code... but that at least is similar to PTracker I think)

I don't exactly recall how PTracker did it but I found the approach I used above to be a more to my liking. With the implementation I used, the principal and identity objects are intertwined with the role and membership providers. (These can be used in a WinForms client too)

Good luck,

Chris

 

colema18 replied on Wednesday, October 10, 2007

Thanks for the code, I am having a bit of trouble however if someone could help I would appreciate it. 

Error when I try to login:

Server Error in '/Sandbox.Web' Application.

Security Exception

Description: The application attempted to perform an operation not allowed by the security policy.  To grant this application the required permission please contact your system administrator or change the application's trust level in the configuration file.

Exception Details: System.Security.SecurityException: Principal must be of type BusinessPrincipal, not System.Web.Security.RolePrincipal

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[SecurityException: Principal must be of type BusinessPrincipal, not System.Web.Security.RolePrincipal]


Applicable Web.config Info:
<appSettings>
    <add key="CslaAuthentication" value="CSLA"/>
    <add key="CslaDataPortalProxy"
      value="Csla.DataPortalClient.WebServicesProxy, Csla"/>
    <add key="CslaDataPortalUrl"
      value="http://localhost/WSHost/WebServicePortal.asmx"/>
    </appSettings>
 
  <connectionStrings>
    <add name="PTracker" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=&quot;C:\Websites\Sandbox\PTracker.mdf&quot;;Integrated Security=True;User Instance=True" providerName="System.Data.SqlClient"/>
    <add name="Security" connectionString="Server=.\SQLExpress;AttachDbFilename=|DataDirectory|mydbfile.mdf; Database=dbname;Trusted_Connection=Yes;" providerName="System.Data.SqlClient"/>
  </connectionStrings>
 
    <system.web>
        <authentication mode="Forms">
          <forms loginUrl="Login.aspx" name="ptracker"/>
        </authentication>
       
        <authorization>
          <allow users="*"/>
        </authorization>

        <membership defaultProvider="CslaMembershipProvider">
            <providers>
                <clear/>
                <add name="CslaMembershipProvider"
                   type="Sandbox.Library.Security.CslaMembershipProvider"
                   functionalProvider="System.Web.Security.SqlMembershipProvider"
                   connectionStringName="Security"
                   passwordFormat="Encrypted"
                   minRequiredNonalphanumericCharacters="0"
                   minRequiredPasswordLength="6"
                   enablePasswordRetrieval="false"
                   requiresUniqueEmail="true"
                   requiresQuestionAndAnswer="true"
                   enablePasswordReset="true"
                   description="Csla enabled membership provider"/>
            </providers>
        </membership>

        <roleManager enabled="true" defaultProvider="CslaRoleProvider" cacheRolesInCookie="true" cookieName=".RolesCookie" cookieTimeout="30" cookieSlidingExpiration="true" cookieProtection="All">
            <providers>
                <add name="CslaRoleProvider"
                      type="Sandbox.Library.Security.CslaRoleProvider"
                      functionalProvider="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
                      connectionStringName="Security"
                      description="Csla enabled role provider"/>
            </providers>
        </roleManager>

skagen00 replied on Wednesday, October 10, 2007

I don't believe your error is related to the Csla providers.

I looked up the error in Csla and it occurs within SetContext of the DataPortal.

Does your principal which you are using for your current user inherit from BusinessPrincipal?

You might try putting a breakpoint within this method and see what the value is during execution.

good luck,

Chris

 

colema18 replied on Thursday, January 17, 2008

Hey Chris,

Do you think you can post or email me your principal and identity classes you are using for this sample?  I am still having trouble and the provider/identity I am using is the code from the PTracker sample.

Thanks
~james
colema18@gmail.com

skagen00 replied on Thursday, January 17, 2008

Hey James

I'm attaching the principal/identity that I had when I first put together this code and should be representative of an identity/principal that works with this approach.

If you are having specific difficulties, please elaborate - information on where the process is breaking down is useful in giving you information you might be able to leverage.

Good luck,

Chris

 

colema18 replied on Friday, January 18, 2008

Thanks for the quick response Chris.  I am indeed having the same issue so it doesn't look like it is related to the principal or identity.  I didn't give specifics because I am actually trying to get CSLA 3.5, ASP.MVC (.NET 3.5 extensions) and the Membership Provider all working together.

If you have the time and desire (and visual studio 2008) you can take a look at my source code here: http://www.agileblue.net/CSLAProviderSpike.zip

I have tried it with a regualr asp.net website instead of a asp.mvc website to make sure it didn't have to do with the MVC framework and I still get the same error.  It looks like when the Logon is getting called on the Principal object and it makes it's way to the WCF Data Portal it is mad because the principal is of type GenericPrincipal instead of BusinessPrincipal.

Is it a catch 22 scenario, where you need to call Logon to get a BusinessPrincipal set but you have to have a BusinessPrincipal to call Logon?

Exception: Principal must be of type BusinessPrincipal, not System.Security.Principal.GenericPrincipal

The Controller that Calls the Method:
        [ControllerAction]
        public void Authenticate(string username, string password)
        {
            if (ProPrincipal.LogOn(username, password))
            {
                System.Web.HttpContext.Current.Session["CslaPrincipal"] = Csla.ApplicationContext.User;
                System.Web.Security.FormsAuthentication.RedirectFromLoginPage(username, false);
            }
        }

Thanks for the help.
~james

skagen00 replied on Friday, January 18, 2008

I'm afraid I don't have a whole lot of time to look at your code right now. The message you have (the exception) is I'm pretty sure a common one that you might want to do a little searching on the forums for.

This one seemed to mention it in conjunction with WCF:

http://forums.lhotka.net/forums/post/14931.aspx

The way I was doing the authentication at the time of my posted code (it's gotten a little more robust for multiple client handling) but still works fine is in the login control's authenticate event (could be done wherever of course):

e.Authenticated = ProPrincipal.LogOn(Login1.UserName, Login1.Password);

HttpContext.Current.Session["CslaPrincipal"] = Csla.ApplicationContext.User;

That is, authenticate the user using the principal's logon, and then store the principal in a session variable for future requests - reestablishing it on future requests from the session variable.

I wish I had more time to assist you right now but I'm afraid I don't -- I can assure you that we haven't had any problems running with this code so as far as I'm concerned this is still entirely a viable approach and represents our approach. Do a little searching on the forums and see if that helps you out.

Also, I'm assuming running locally you don't have a problem but it's with remoting when it starts flaking out? There was a post on the forum where someone had the casing of CSLA wrong in the config file of the remoting server and that was causing the same exception you listed...

Good luck,

Chris

 

 

JoOfMetL replied on Friday, February 01, 2008


Hi I have the same problem as you. In my opinion the problem is that the AcquireRequestState in Global.asax is not called before the method in CslaRole(or Membership) is called (I am sur, i have checked with Debugger)
 
So By Default The type Csla.ApplicationContext.User is RolePrincipal and there is some error.

I Use the Csla 3.0.2 and I Have a WebSite which use a WebService.

I don't understand how it's working to the other people because Csla.ApplicationContext isn't correctly initialized.

I Solve (?????) the problem, i have put the code in the method ExecuteRemoteMethod (both CslaMembershipProvider, and CslaRoleProvider) :

PTPrincipal principal = Csla.ApplicationContext.User as PTPrincipal;
            if (principal == null)
                PTPrincipal.Logout();

So in the Csla.ApplicationContext.USer you have a object PTPrincipal and it is unantuthtificated, so the type is good and no error, but Not the Good PTPtincipal Object.
BUT, just after you pass by the global.asax and you put the good PTPtincipal with Session.

Why I don't put directlty the good PTPrincipal in session, because when CslaProvider is called, the Session is null

ljcorreia replied on Tuesday, October 30, 2007

Hi Chris,

Thanks a lot for posting the code. It's really a clever solution.

I have a problem getting all members GetAllUsers() and I'm wondering if you can help me.

_wrappedProvider is null and I don't know why it has not been inicialised at this time. So, it is giving me the null exception.

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

Any idea?

Thanks.

skagen00 replied on Tuesday, October 30, 2007

Have you successfully had other methods work? (i.e. is this your first attempt?)

Have you tried throwing a breakpoint within here to see what is happening?

When you invoke System.Web.Security.Membership.GetAllUsers(), it will call Provider.GetAllUsers. Provider, as a property of Membership, calls Initialize(), and within here it will attempt to initialize the CslaMembershipProvider (provided it has been properly configured in the Web.config).

In CslaMembershipProvider, here's where _wrappedProvider is instantiated:

        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            // Determine the functional provider, instantiate and initialize it.
            Type functionalProviderType = Type.GetType(config["functionalProvider"]);
            config.Remove("functionalProvider");
            _wrappedProvider = (MembershipProvider)Activator.CreateInstance(functionalProviderType);
            _wrappedProvider.Initialize(name, config);
        }

So, try throwing a breakpoint in there and see what occurs. I suspect you've got something a touch off in your configuration. Remember as well if you're using remoting that you need the Web.config components over on the app server as when remoting occurs the membership API needs to initialize itself with the right information.

 

ljcorreia replied on Thursday, November 01, 2007

Hi Chris,

Thanks a lot for your reply. Sorry if I haven't sent you enough information.

To be honest, the only method I have tested until now is the ValidateUser();

Seems to be working fine. I've throwed the breakpoint, even before bother you with this problem. What happened maybe is mine lack of knowledge about this framework. Anyway...

First, it stops on Initialize() just when I try to log in (I don't know if it's right). It calls the asp:Login and authenticate the user correctly. At the end I got my Csla.ApplicationContext.User, using my TMSPrincipal and TMSIdentity. Until here everything looks fine.

Have a look at my web.config:

  <membership defaultProvider="CslaMembershipProvider" userIsOnlineTimeWindow="15">
   <providers>
    <clear />
    <add name="CslaMembershipProvider"
      type="CslaMembershipProvider"
      connectionStringName="TMSSecurity"
      functionalProvider="Microsoft.Samples.SqlMembershipProvider"
      applicationName="\"
      enablePasswordRetrieval="false"
      enablePasswordReset="true"
      requiresQuestionAndAnswer="true"
      requiresUniqueEmail="false"
      passwordFormat="Hashed"
      maxInvalidPasswordAttempts="5"
      passwordAttemptWindow="10"
      description="Csla enabled membership provider" />
   </providers>
  </membership>
  <!-- functionalProvider="Microsoft.Samples.SqlRoleProvider" -->
  <roleManager enabled="true" cacheRolesInCookie="true" cookieName=".ASPROLES"
      cookieRequireSSL="true" defaultProvider="CslaRoleProvider">
   <providers>
    <clear />
    <add connectionStringName="TMSSecurity" applicationName="\"
      name="CslaRoleProvider" type="CslaRoleProvider"
      functionalProvider="Microsoft.Samples.SqlRoleProvider"
      description="Csla enabled role provider" />
   </providers>
  </roleManager>


So, it throws the exception when I try to create an ObjectDataSource using CslaMembershipProvider, and associate it with a ListBox. Even logged on the page (which means passing through Initialize()) my _wrappedProvider is null when it GetAllUsers() and it is what I am struggling to understand.

What is really weird is that if I throw a breakpoint on ValidateUser(), the _wrappedProvider is not null and working absolutelly fine: Watch result: {Microsoft.Samples.SqlMembershipProvider}

Any idea will be really appreciated. Thanks a lot Chris.

I can't tell you that the role is using the methods...
The role provider seems to be working fine, but I'm not sure I'm using correctly:

My TMSPrincipal:

        public override bool IsInRole(string role)
        {
            TMSIdentity identity = (TMSIdentity)this.Identity;
            return identity.IsInRole(role);
        }

        //public override bool IsInRole(string role)
        //{
        //    return Roles.IsUserInRole(Identity.Name, role);
        //}

Note that I use IsInRole straight from System.Web.Security what I think is not ideal, because this way I'm not using my CslaRoleProvider, right?

My TMSIdentity:

        private void DataPortal_Fetch(Criteria criteria)
        {
            bool authenticated = System.Web.Security.Membership.ValidateUser(criteria.Username, criteria.Password);
            if (authenticated)
            {
                _isAuthenticated = true;
                _name = criteria.Username;
                foreach (string role in System.Web.Security.Roles.GetRolesForUser(_name))
                {
                    _roles.Add(role);
                }
            }
            else
            {
                _isAuthenticated = false;
                _name = "";
            }
        }

skagen00 replied on Thursday, November 01, 2007

Ohhh... the provider is working fine except when you're trying to bind a listbox to GetAllUsers() as an ObjectDataSource, it's not working?

Are you binding it to Membership.GetAllUsers(), or are you calling the provider directly? (I don't think offhand it would matter but I'd have to look).

If you simply throw a button on a form and call Membership.GetAllUsers(), do you get the results you're looking for? (in the handler just throw an object obj = Membership.GetAllUsers();). If you're not getting users, it should be possible to step through the debugger to see what is occurring - if it's simply not working, try dumbing it down & shut off remoting if that's turned on in your app and see if that remedies the problem. There are undoubtedly some possible ways to narrow down the problem.

Do you have remoting turned on? If so, I want to remind you to make sure the information is in the remoting app's web.config. And if you have the membership provider in an assembly, you may need to be more specific when declaring the type.

My web.config is below. (xxx is just suppressed info)

<membership defaultProvider="xxx">

<providers>

<!-- Note that the connection string is irrelevant on the client side Web app -->

<!-- It is supplied on the remoting site's Web config. SqlMembershipProvider balks if it's not there. -->

<clear/>

<add name="xxx" applicationName="/xxx" type="xxx.Security.CslaMembershipProvider" connectionStringName="xxx" functionalProvider="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" passwordFormat="Encrypted" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="6" enablePasswordRetrieval="false" requiresUniqueEmail="true" requiresQuestionAndAnswer="true" enablePasswordReset="true" description="Csla enabled membership provider"/>

<add name="xxx" applicationName="/xxx" type="xxx.Security.CslaMembershipProvider" connectionStringName="Demo" functionalProvider="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="6" enablePasswordRetrieval="false" requiresUniqueEmail="true" requiresQuestionAndAnswer="true" enablePasswordReset="true" description="Csla enabled membership provider"/>

</providers>

</membership>

 

skagen00 replied on Thursday, November 01, 2007

<doublepost>

AKaplan replied on Wednesday, June 25, 2008

Ok enough insanity! I've gone through this piece of code too many times. I continously get the same error of ArgumentNullException in the CSLARoleProvider Initialization method. The name and config parameters have proper values. Now this exception isn't being thrown at the "If config Is Nothing Then Throw ArgumentNullException("config"). It actually skips over this conditional statement as config being of something value. I believe the problem really happens during the conversion from functionalprovider variable Type to the RoleProvider Type. I dont know why? The functionalProvider variable before the conversion has of type value. Can anyone help me ?

skagen00 replied on Wednesday, June 25, 2008

Hey there -

Is it when you initialize the wrapped provider that you get the error? Which line exactly are you bombing out on?

If you're talking about when Activator.CreateInstance is run and then cast, try breaking it into two lines such as:

      object instance = Activator.CreateInstance(functionalProviderType); // Verify creation
      _wrappedProvider = (RoleProvider)instance;

Please give a bit more detail if this isn't where it's bombing out.

Chris

AKaplan replied on Wednesday, June 25, 2008

ok that was the exact line that was bombing out. So i've taken your advice and seperated the Activator.CreateInstance from the conversion. That line is the exact line where it's bombing out. The instance has values of Name="Nothing", ApplicationName="Nothing" and Description="Nothing". I know I've supply these values in the web.config with their proper values. So why would it be bombing out at this point? How can this be corrected?

skagen00 replied on Wednesday, June 25, 2008

It's OK if the instance doesn't have values, because those will get supplied when this line is invoked:

            _wrappedProvider.Initialize(name, config);

It's still unclear to me which line was exactly bombing out. It created the instance of SqlRoleProvider OK (when breaking out the lines so that the only thing one line does is create an instance of SqlRoleProvider? Or did Activator.CreateInstance fail?

If Activator.CreateInstance failed, did you have a value for functionalProviderType that was established from Type.GetType()? 

It's a little tricky to diagnose w/ the information given so far, but I'm trying!

 

AKaplan replied on Thursday, June 26, 2008

Ok it bombs out right after the Initialize method and the next step that its going to is the Property Name, which has a return value of Nothing. So I'm guessing this whole problem is in the Initialize Method that isnt' fulfilling everything. This is my code, which isn't anything different from some of the samples or anything in this thread.

To answer your questions if the _functionProviderType has values, yes it does. It has the Name="SqlRoleProvider" and the FullName="System.Web.Security.SqlRoleProvider......". 

 

Public Class CustomRoleProvider

Inherits RoleProvider

Private _wrappedprovider As RoleProvider = Nothing

#Region "Properties"

Private Shared ReadOnly Property ExecutingOnServer() As Boolean

Get

Return Csla.ApplicationContext.DataPortalProxy = "Local" OrElse Csla.ApplicationContext.ExecutionLocation = ExecutionLocations.Server

End Get

End Property

Public Overrides ReadOnly Property Name() As String

Get

Return _wrappedprovider.Name      <----- Bombs out with a value of "Nothing"

End Get

End Property

Public Overrides ReadOnly Property Description() As String

Get

Return _wrappedprovider.Description

End Get

End Property

Public Overrides Property ApplicationName() As String

Get

Return _wrappedprovider.ApplicationName

End Get

Set(ByVal value As String)

_wrappedprovider.ApplicationName = value

End Set

End Property

#End Region

#Region "Methods"

Public Overloads Overrides Sub Initialize(ByVal name As String, ByVal config As System.Collections.Specialized.NameValueCollection)

If config Is Nothing Then

Throw New ArgumentNullException("config")

End If

Dim _functionProviderType As Type = Type.[GetType](config("rolefunctionalProvider"))

config.Remove("rolefunctionalProvider")

Dim instance As Object = Activator.CreateInstance(_functionProviderType)

_wrappedprovider = DirectCast(instance, RoleProvider)

_wrappedprovider.Initialize(name, config)

End Sub

End Class

 

AKaplan replied on Thursday, June 26, 2008

oh yea.. it errors out with a ArgumentNullException

skagen00 replied on Thursday, June 26, 2008

So the line that it's bombing out on is this one, right?

            _wrappedprovider.Initialize(name, config);

If _wrappedprovider does get created as a SqlRoleProvider, the implementation for Initialize is below (via Reflector).

Is your ArgumentNullException's argument "config"? If so, you may be passing in an empty config array (look at the code below for SqlRoleProvider). Mine has 3 values for applicationName, connectionStringName, and description respectively.

 

Chris

 

public override void Initialize(string name, NameValueCollection config)
{
    HttpRuntime.CheckAspNetHostingPermission(AspNetHostingPermissionLevel.Low, "Feature_not_supported_at_this_level");
    if (config == null)
    {
        throw new ArgumentNullException("config");
    }
    if (string.IsNullOrEmpty(name))
    {
        name = "SqlRoleProvider";
    }
    if (string.IsNullOrEmpty(config["description"]))
    {
        config.Remove("description");
        config.Add("description", SR.GetString("RoleSqlProvider_description"));
    }
    base.Initialize(name, config);
    this._SchemaVersionCheck = 0;
    this._CommandTimeout = SecUtility.GetIntValue(config, "commandTimeout", 30, true, 0);
    string specifiedConnectionString = config["connectionStringName"];
    if ((specifiedConnectionString == null) || (specifiedConnectionString.Length < 1))
    {
        throw new ProviderException(SR.GetString("Connection_name_not_specified"));
    }
    this._sqlConnectionString = SqlConnectionHelper.GetConnectionString(specifiedConnectionString, true, true);
    if ((this._sqlConnectionString == null) || (this._sqlConnectionString.Length < 1))
    {
        throw new ProviderException(SR.GetString("Connection_string_not_found", new object[] { specifiedConnectionString }));
    }
    this._AppName = config["applicationName"];
    if (string.IsNullOrEmpty(this._AppName))
    {
        this._AppName = SecUtility.GetDefaultAppName();
    }
    if (this._AppName.Length > 0x100)
    {
        throw new ProviderException(SR.GetString("Provider_application_name_too_long"));
    }
    config.Remove("connectionStringName");
    config.Remove("applicationName");
    config.Remove("commandTimeout");
    if (config.Count > 0)
    {
        string key = config.GetKey(0);
        if (!string.IsNullOrEmpty(key))
        {
            throw new ProviderException(SR.GetString("Provider_unrecognized_attribute", new object[] { key }));
        }
    }
}

AKaplan replied on Thursday, June 26, 2008

Are you using the Microsoft.Samples. Because I am not.  Is it better to be using them?

skagen00 replied on Thursday, June 26, 2008

Not using Microsoft.Samples code, nope. I just grabbed the code for System.Web.Security.SqlRoleProvider using reflector.

 

 

AKaplan replied on Thursday, June 26, 2008

Alright well anyways... The check for the config being nothing is passed and that thrown exception for the ArgumentNullException isn't being thrown at that point. It's being thrown after the Initialization Method when the Property for the Name is completed.

greengumby replied on Thursday, November 20, 2008

Hi,

CSLA Version : 3.5

New to CSLA and decided to use the method explained by OP.

Everything works correctly except when logging off.  I use an asp.net LoginStatus Control and I can confirm that the MMPrincipal.Logoff() method is being run on the LoggingOff() method of the control.

Only problem is that as the principal is being restored from the session data with the original logged in credentials it keeps the website looking like it is authenticated. ie SiteMap

As you can see I added the

if (Csla.ApplicationContext.User.Identity.Name == "") return ;

This fixes the problem due to an unauthenticated user having no name however I feel this is a bit of a hack and I am wondering if I am hiding a bigger problem.

 

Many Thanks

Jeremy

 

 

protected void Application_AcquireRequestState(object sender, EventArgs e)

{

if (HttpContext.Current.Handler is IRequiresSessionState)

{

if (Csla.ApplicationContext.User.Identity.Name == "")

return;

System.Security.Principal.IPrincipal principal;

try

{

principal = (System.Security.Principal.IPrincipal)

HttpContext.Current.Session["CslaPrincipal"];

}

catch

{

principal = null;

}

if (principal == null)

{

if (User.Identity.IsAuthenticated &&

User.Identity is FormsIdentity)

{

// no principal in session, but ASP.NET token

// still valid - so sign out ASP.NET

FormsAuthentication.SignOut();

Response.Redirect(Request.Url.PathAndQuery);

}

// didn't get a principal from Session, so

// set it to an unauthenticted PTPrincipal

MM_V2.Framework.Providers.MMPrincipal.LogOff();

}

else

{

// use the principal from Session

Csla.ApplicationContext.User = principal;

}

}

}

Copyright (c) Marimer LLC