Call custom MembershipProvider without casting Membership.Provider - asp.net

Is there a way to call the custom MembershipProvider I have implemented in code without having to cast the Membership class to my custom provider every time? For example, I setup my web.config like so:
<connectionStrings>
<add name="TestDB"
connectionString="conn_str_here"
providerName="System.Data.SqlClient" />
</connectionStrings>
<membership defaultProvider="CustomSqlMembershipProvider">
<providers>
<clear/>
<add name="CustomSqlMembershipProvider" type="Common.CustomSqlMembershipProvider" connectionStringName="TestDB"
enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false"
maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10"
applicationName="/" />
</providers>
</membership>
And my custom provider class:
namespace Common
{
public class CustomSqlMembershipProvider : SqlMembershipProvider
{
public void ChangeUsername()
{
// ...
}
}
}
Now to call my new custom function, is there some way to allow Membership.ChangeUsername() versus having to do this:
CustomSqlMembershipProvider customMembership = Membership.Provider as CustomSqlMembershipProvider;
customMembership.ChangeUsername();
Doing the cast everywhere you want to use it starts to get annoying after a while. Thanks in advance.

You could add an extension method:
namespace System.Web.Membership
{
public static class MembershipProviderExtensions
{
public static void ChangeUsername(this MembershipProvider provider, string oldUsername, string newUsername)
{
var customProvider = provider as CustomSqlMembershipProvider;
if (customProvider == null) throw new Exception("Invalid provider type.");
customProvider.ChangeUsername(oldUsername, newUsername);
}
}
}
Usage:
Membership.Provider.ChangeUsername("bob", "jane");

Related

Windsor Castle : Property Inject

I implemented a CustomMembershipProvider which is deriving from ExtendedMembershipProvider. IUserService is a dependency of CustomMembershipProvider which will be used to validate the given credentials.
This will be configured in Web.Config;
<membership defaultProvider="DefaultMembershipProvider">
<providers>
<add name="DefaultMembershipProvider" type="BorderExpress.AutoImport.Web.Security.CustomMembershipProvider" connectionStringName="DefaultConnection" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
</providers>
</membership>
Injecting IUserService implementation via constructor not possible as CustomMembershipProvider require a parameterless constructor.
So thought of introducing Property injection. I made IUserService as a public property of CustomMembershipProvider.
public class CustomMembershipProvider : ExtendedMembershipProvider
{
public IUserService UserService { get; set; }
public CustomMembershipProvider()
{
}
...
public override bool ValidateUser(string username, string password)
{
var user = UserService.GetUser(username);
if (user != null && SaltedHash.Verify(user.Salt, user.Hash, password))
{
return true;
}
return false;
}
}
I wrote a separate installer only for this registration
public class WindsorMembershipInstaller:IWindsorInstaller
{
public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
container.Register(
Component.For<CustomMembershipProvider>()
);
}
}
That didn't work and UserService always NULL at the time of calling the ValidateUser.
And I tried contributors;
public class RequireUserServiceProperties : IContributeComponentModelConstruction
{
public void ProcessModel(Castle.MicroKernel.IKernel kernel, Castle.Core.ComponentModel model)
{
model.Properties
.Where(p => p.Dependency.TargetItemType == typeof(IUserService))
.All(p => p.Dependency.IsOptional = false);
}
}
And register the contributor where I bootstrap the container.
_container = new WindsorContainer()
.Install(FromAssembly.This());
_container.Kernel.ComponentModelBuilder.AddContributor(new RequireUserServiceProperties());
Please do let me know how to inject the IUserService property of CustomMembershipProvider
I found an answer within the SO.
#Mauricio Scheffer has done custom implementation to utilize windsor to inject dependencies to membership provider.
Original question
How do I control MembershipProvider instance creation/lifetime?
#Mauricio Scheffer 's blog post
http://bugsquash.blogspot.com/2010/11/windsor-managed-membershipproviders.html

SimpleMembershipProvider not adding tables to my database

I have a standard ASP.NET MVC4 application, and I am building out the DAL with EF Code First. Currently there are around 20-30 models and I am at a point where I want to integrate users and roles. I have research this a ton and still can't seem to get it to work. Here is what I have right now:
In my database initilization class (Gets called every time I change the model) I seed it with a bunch of data, then call this:
public class DbInit : DropCreateDatabaseIfModelChanges<TrackerContext>
{
protected override void Seed(TrackerContext context)
{
...seed stuff and save it...
WebSecurity.InitializeDatabaseConnection("TrackerContext", "User", "Id", "UserName", autoCreateTables: true);
}
}
Through debugging it does not throw any error and I can confirm it is hitting this line of code. My User model looks like this:
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public PasswordQuestion PasswordQuestion{ get; set; }
public string PasswordAnswer { get; set; }
public string Type { get; set; }
}
From everything I have read, I thought this should be all I need to do to get this working, but I have two problems:
No membership tables are being loaded
My Config seems to be throwing an error even though I am referencing the WebMatrix.WebData dll
Here is the config section:
<roleManager enabled="true" defaultProvider="SimpleRoleProvider">
<providers>
<clear/>
<add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData"/>
</providers>
</roleManager>
<membership defaultProvider="SimpleMembershipProvider">
<providers>
<clear/>
<add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" />
</providers>
</membership>
What am I missing here?
As far as I can see, you're missing the
public DbSet<User> User{ get; set; }
in your context class. You'll have to do this for every table you want Migrations to create for you in your database.
1) Did you check your ConnectionString? Should be something like this:
<connectionStrings>
<add name="DefaultConnection" connectionString="data source=SERVER;initial catalog=DATABASE;user id=USER;password=PASSWORD;" providerName="System.Data.SqlClient" />
</connectionStrings>
Furthermore you should have this in your config too:
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
</entityFramework>
2) Check your references. System.Web.Webpages should be there and CopyLocal should be set to true. Then check your .NET Framework Version as well (4.0, no Client Edition)
Hope this helps.

CustomSqlMembership throws server error intermittently

I've a asp.net MVC app deployed to server it uses forms authentication and uses CustomSqlMembership provider basically I've not changed anything in the SqlMembershipPRovider just copied the source from MS and included the source in my project and renamed it from time to time this error comes up.
This happens on localhost and remotely deployed system and I can't figure out what could be the cause of it.
Server Error in '/' Application.
Configuration Error
Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.
Parser Error Message: Object reference not set to an instance of an object.
Source Error:
Line 50: <clear/>
Line 51: <!--<add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="dq_systemConnectionString" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression="" applicationName="/"/>-->
Line 52: `<add name="CustomSqlMembershipProvider" type="AcmeCorp.CustomSqlMembershipProvider, AcmeCorp, Version=1.0.0.0, Culture=neutral" connectionStringName="AcmeCorpConnectionString" enablePasswordRetrieval="False" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="True" passwordFormat="Hashed" maxInvalidPasswordAttempts="6" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression="" applicationName="/" />`
Line 53: </providers>
Line 54: </membership>
this is the complete listing of membership object in web.config
<membership defaultProvider="CustomSqlMembershipProvider">
<providers>
<clear />
<!--<add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="dq_systemConnectionString" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression="" applicationName="/" />-->
<add name="CustomSqlMembershipProvider" type="AcmeCorp.CustomSqlMembershipProvider, AcmeCorp, Version=1.0.0.0, Culture=neutral" connectionStringName="dq_systemConnectionString" enablePasswordRetrieval="False" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="True" passwordFormat="Hashed" maxInvalidPasswordAttempts="6" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression="" applicationName="/" />
public class CustomSqlMembershipProvider : AcmeCorp.SqlMembershipProvider
{
static public ConnectionStringSettings css {get; set;}
public override void Initialize(string name, NameValueCollection config)
{
config.Add("connectionString", css.ConnectionString);
base.Initialize(name, config);
}
}
public class CustomSqlRoleProvider : AcmeCorp.SqlRoleProvider
{
static public ConnectionStringSettings css { get; set; }
public override string GetConnectionString()
{
return css.ConnectionString;
}
public override void Initialize(string name, NameValueCollection config)
{
//config.Add("connectionString", css.ConnectionString);
base.Initialize(name, config);
}
}
public interface ISiteProvider
{
bool Initialise(string host);
Site GetCurrentSite();
}
public class SiteProvider : ISiteProvider
{
SystemMetaDataContext mDB;
Site mSite;
public SiteProvider(SystemMetaDataContext db)
{
mDB = db;
}
public bool Initialise(string host)
{
mSite = mDB.Sites.SingleOrDefault(s => s.Host == host);
if (null != mSite)
{
CustomSqlMembershipProvider.css = new ConnectionStringSettings();
CustomSqlMembershipProvider.css.ConnectionString = mSite.Connection;
CustomSqlMembershipProvider.css.ProviderName = "System.Data.SqlClient";
CustomSqlMembershipProvider.css.Name = "dq_systemConnectionString";
CustomSqlMembershipProvider.css.ConnectionString = mSite.Connection;
CustomSqlRoleProvider.css = new ConnectionStringSettings();
CustomSqlRoleProvider.css.ConnectionString = mSite.Connection;
CustomSqlRoleProvider.css.ProviderName = "System.Data.SqlClient";
CustomSqlRoleProvider.css.Name = "dq_systemConnectionString";
CustomSqlRoleProvider.css.ConnectionString = mSite.Connection;
return true;
}
else
{
return false;
}
}
public Site GetCurrentSite()
{
return mSite;
}
}
public class BaseController : Controller
{
ISiteProvider mSiteProvider;
protected IRepository mRepository { get; private set; }
protected int DefaultPageSize { get; set; }
public BaseController()
{
DefaultPageSize = 10;
mSiteProvider = new SiteProvider(new SystemMetaDataContext());
}
public BaseController(IDQRepository repository)
{
mRepository = repository;
DefaultPageSize = 10;
if (Session["ActiveView"] == null)
{
IList<RoleViewModel> roles = mRepository.GetAllRoles();
foreach (RoleViewModel rvm in roles)
{
if (Roles.IsUserInRole(rvm.Name))
{
Session["ActiveView"] = rvm.Name;
break;
}
}
}
}
protected override void Initialize(RequestContext requestContext)
{
string[] host = requestContext.HttpContext.Request.Headers["Host"].Split(':');
MetaInfo.PopulateMeta(host[0]);
if (!mSiteProvider.Initialise(host[0]))
RedirectToRoute("Default");
if (null == mRepository)
mRepository = new DQRepository();
base.Initialize(requestContext);
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
ViewData["Site"] = Site;
base.OnActionExecuting(filterContext);
}
public Site Site {
get {
return mSiteProvider.GetCurrentSite();
}
}
Where is initialized this static variable ?
static public ConnectionStringSettings css {get; set;}
Static variables are not thread safe.
You must initialize them in a thread safe way, especially if running in web farm mode.
If the server uses an application pool with more than 1 processor set in its config, it is running in web farm mode. In web farm mode, you will have 2 web applications running, but your static variable will be created only once as this memory space is shared (in fact there is more than that but you can write books on this subject).
You can disable web farm mode by setting the processor count to 1 in the application pool.
Where is the code initializing this css variable ?
Could you write it here ?
From where is it called ?
Check your custom code. Most likely, you are attempting to access a property without checking nullability of the object. This is probably due to asking for user name, or something, when membership has not found the person in question. Adding a null ref check to the code will at least rid the error so you can raise an appropriate exception (and then give the user a friendly error message).
Your configuration for this provider does have many redundant fields. What happens if you change <add name="CustomSqlMembershipProvider" type="AcmeCorp.CustomSqlMembershipProvider, AcmeCorp, Version=1.0.0.0, Culture=neutral" ... />
to <add name="CustomSqlMembershipProvider" type="AcmeCorp.CustomSqlMembershipProvider"/> ?

Windows Authentication with custom (my own) RoleProvider don't work on IIS

I try to implement my own RoleProvider:
public class MyRoleManger : RoleProvider
{
public override bool IsUserInRole(string username, string roleName)
{
throw new NotImplementedException();
}
public override string[] GetRolesForUser(string username)
{
throw new NotImplementedException();
}
private string appName = "";
public override string ApplicationName
{
get { return appName; }
set { appName = value; }
}
....
}
It is my Web.config:
<system.web>
.....
<roleManager defaultProvider="RoleManger" enabled="true" >
<providers>
<clear/>
<add name="RoleManger" type="UserManager.Client.Web.MyRoleManger" applicationName="MyApp"/>
</providers>
</roleManager>
<authentication mode="Windows"/>
<identity impersonate="true"/>
</system.web>
<system.webServer >
<security>
<authorization >
<clear/>
<add accessType="Allow" roles="anyRole"/>
<add accessType="Deny" users="?"/>
</authorization>
</security>
...
</system.webServer >
But ASP.NET under IIS don't call MyRoleManager methos. Instead its try get "anyRole" from Windows Security Groups (AD). How to enforce IIS to use MyRoleManager with Windows Authentication?

asp.net mvc custom profile provider

I want to have custom profile provider in my asp.net mvc 3 app. The problem is, that I don't want to use default DB that is generated by ASP.NET Membership/Role/Profile provider, mainly because authentication is already done with WebService and DBs already exist.
I want to user profile properties to populate them and use within different areas of the site.
I took a look at this example (How to assign Profile values?) but I am getting this error:
An attempt to attach an auto-named database for file
C:\Projects\FWManager\App_Data\aspnetdb.mdf failed. A database with
the same name exists, or specified file cannot be opened, or it is
located on UNC share.
Here is the web.config
<profile inherits="FWMembership.Membership.FWProfileProvider" defaultProvider="AspNetSqlProfileProvider" automaticSaveEnabled="false" enabled="true">
<providers>
<clear/>
<add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/" />
</providers>
</profile>
This is my custom class
public class FWProfileProvider : ProfileBase
{
[SettingsAllowAnonymous(false)]
public string FirstName
{
get { return base["FirstName"] as string; }
set { base["FirstName"] = value; }
}
[SettingsAllowAnonymous(false)]
public string LastName
{
get { return base["LastName"] as string; }
set { base["LastName"] = value; }
}
[SettingsAllowAnonymous(false)]
public int? UserID
{
get { return base["UserID"] as int?; }
set { base["UserID"] = value; }
}
[SettingsAllowAnonymous(false)]
public string UserCompany
{
get { return base["UserCompany"] as string; }
set { base["UserCompany"] = value; }
}
[SettingsAllowAnonymous(false)]
public string Email
{
get { return base["Email"] as string; }
set { base["Email"] = value; }
}
public StringCollection Entitlements
{
get { return base["Entitlements"] as StringCollection; }
set { base["Entitlements"] = value; }
}
public string username;
public FWProfileProvider()
{
}
public FWProfileProvider(string username)
{
this.username = username;
}
static public FWProfileProvider CurrentUser
{
get
{
return (FWProfileProvider)
(ProfileBase.Create("Joe"));
}
}
}
The key is to avoid using asp.net default membership tables.
Any ideas?
EDIT:
Forgot to add - this web application, but profile provider is placed in the class library project within same soulution:
Solution
|->FWProfile (class library project)
|->UI (asp.net mvc 3 web application)
I think you have to write your own MemberShip Provider as well. Your web.config refers to the default asp.net membership provider. How to write a Membership provider you can find here custom membership provider
The default membership provider uses a connection string into a locally installed SQL Express database and that causes your error.
Your web.config would look like this:
<membership defaultProvider="MyCustomMembershipProvider">
<providers>
<clear />
<add name="MyCustomMembershipProvider"
type="FWMembership.Membership.MyCustomMembershipProvider"
enablePasswordRetrieval="true"
enablePasswordReset="true"
requiresQuestionAndAnswer="false"
requiresUniqueEmail="true"
passwordFormat="Clear"/>
</providers>
</membership>
<profile defaultProvider="MyProfileProvider" enabled="true">
<providers>
<clear/>
<add name="MyProfileProvider" type="FWMembership.Membership.FWProfileProvider" connectionStringName="ApplicationServices" applicationName="/" />
</providers>
</profile>
Hope this helps.

Resources