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.
Related
Within a signalR hub method, I'm trying to check if the user authenticated is within a specific role. In plane MVC, I can call user.IsInRole and the class overriding RoleProvider's GetRolesForUser method is called.
The CustomRoleProvider is set up in the web.config with:
<roleManager cacheRolesInCookie="false" defaultProvider="CustomRoleProvider" enabled="true">
<providers>
<clear />
<add name="CustomRoleProvider" type="CustomRoleProvider" />
</providers>
</roleManager>
When user.IsInRole is called CustomRoleProvider.GetRolesForUser(string username) is called.
Hub code
public IEnumerable<Items> GetList()
{
IList<Items> result;
var user = Context.User;
if (user.IsInRole("Test_Role"))
result = itemProvider.GetItems();
else
result = new[] { };
return result;
}
CustomRoleProvider
public class CustomRoleProvider:RoleProvider
{
public override string[] GetRolesForUser(string username)
{
return new[]{"Test_Role"};
}
}
Why is this not working in signal R? user.IsInRole does not call GetRolesForUser and always returns False.
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
Im implementing the ASP.NET Profile Provider alongside the Membership Provider using Jon Galloway's (slightly dated) example here:
and Im getting the error Provider must implement the class 'System.Web.Profile.ProfileProvider'
As per the example, I've created a custom UserProfile inheriting from ProfileBase
public class UserProfile : ProfileBase
{
public static UserProfile GetUserProfile(string username)
{
if (username != null)
{
var profile = Create(username) as UserProfile;
return profile;
}
}
public static UserProfile GetUserProfile()
{
var membershipUser = MembershipProvider.GetUser();
if (membershipUser != null)
return Create(membershipUser.UserName) as UserProfile;
}
[SettingsAllowAnonymousAttribute(true)]
public virtual string Name
{
get { return base["Name"] as string; }
set { base["Name"] = value; }
}
}
I'm calling above method from a Class library like this:
var profile = UserProfile.GetUserProfile(name);
In turn, Im calling that method from a Unit test project, which has an app.config:
<profile enabled="true" defaultProvider="MyProfileProvider" inherits="NAMESPACE.UserProfile, NAMESPACE">
<providers>
<clear/>
<add
name="MyProfileProvider"
connectionStringName="MembershipConnection"
applicationName="/"
type="NAMESPACE.UserProfile, NAMESPACE"
/>
</providers>
<properties>
<add name="Name" type="String"/>
</properties>
</profile>
Inheriting from ProfileBase (like Jon's example) should be enough right? I think i've used all variations on the 'type' and 'inherit' attributes, am I missing something here?
I've managed to at least save Profile values using a variation on this example
dynamic profile = ProfileBase.Create(UserName);
profile.Name = name;
profile.Save();
Not sure how to retrieve that same property yet tho, since ProfileBase has no such method...
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.
So I have an asp.net Web Application (Not Web Site) that I am trying to support profiles for anonymous users. I have a form and I want anonymous users to be able to enter their name and email only once, and have that information automatically accessible on the next load for them.
In my Web.config I have anonymous ID setup like so:
<anonymousIdentification enabled="true" cookieless="AutoDetect" />
I have my profile section setup like this:
<profile defaultProvider="SqlProvider" enabled="true" inherits="QA_Web_Tools.UserProfile">
<providers>
<clear />
<add connectionStringName="QAToolsConnectionString" name="SqlProvider"
type="System.Web.Profile.SqlProfileProvider" />
</providers>
</profile>
Finally, due to my app being a Web App and not a Web Site, I am using the profiles via this custom object:
public class UserProfile : ProfileBase
{
public static UserProfile GetUserProfile(string username)
{
return Create(username) as UserProfile;
}
public static UserProfile GetUserProfile()
{
return Create(Membership.GetUser().UserName) as UserProfile;
}
[SettingsAllowAnonymous(true)]
public string FullName
{
get { return base["FullName"] as string; }
set { base["FullName"] = value; }
}
[SettingsAllowAnonymous(true)]
public string BuildEmail
{
get { return base["BuildEmail"] as string; }
set { base["BuildEvmail"] = value; }
}
}
This code is based off of this reference.
The issue is that that code does not support anonymous users, or if it does I don't know how. I can't use the GetUserProfile() method with no parameters because if the user is anonymous, Membership.GetUser() is null. I could pass in the anonymous ID token into the first GetUserProfile(string username) method but I cant' find any way to get the anonymous ID token for the current user.
Does anyone know how to get this information? Google doesn't seem to be returning useful results.
Thanks,
Success!
I changed:
public static UserProfile GetUserProfile()
{
return Create(Membership.GetUser().UserName) as UserProfile;
}
to
public static UserProfile GetUserProfile()
{
return HttpContext.Current.Profile as UserProfile;
}
and now it works!