MembershipProvider change connection string in code - asp.net

I am trying to implement Asp.net Forms Authentication in my site. Usually, you provide the name of your DB connection string in your web.config. However, as our database setup is a little more complicated, I was looking for a way to manually provide the MembershipProvider the connection string in code.
Thanks!

You don't have to use a connectionStringName for SqlMembershipProvider, instead you can supply the connection string directly. I.e. instead of:
<membership defaultProvider="SqlProvider" ...>
<providers>
<add name="SqlProvider" type="System.Web.Security.SqlMembershipProvider"
connectionStringName="MyConnectionStringName"
.../>
</providers>
</membership>
You can specify the connection string directly as:
<membership defaultProvider="SqlProvider" ...>
<providers>
<add name="SqlProvider" type="System.Web.Security.SqlMembershipProvider"
connectionString="data source=... "
.../>
</providers>
</membership>
Therefore you could also derive a custom provider from SqlMembershipProvider, and build the connection string dynamically as follows:
public class MySqlMembershipProvider : SqlMembershipProvider
{
public override void Initialize(string name, NameValueCollection config)
{
config["connectionString"] = BuildMyCustomConnectionString();
base.Initialize(name, config);
}
}

I came across this needing to do the same thing, set the connection string via code and not in the web.config, although I needed to change more than the name, I needed the actual value to be dynamically generated. If you want to change the actual connection string to be generated from code you can do the following:
web.config
...
<connectionStrings>
<add name="ConnectionPlaceHolder" connectionString="This is a place holder"/>
</connectionStrings>
...
<roleManager defaultProvider="SqlRoleProvider" enabled="true">
<providers>
<clear/>
<add name="SqlRoleProvider" type="MyClassLibraries.Web.Security.MyCustomSqlRoleProvider" connectionStringName="ConnectionPlaceHolder" applicationName="MyApplicationName"/>
</providers>
</roleManager>
Provider Class
public class MySqlRoleProvider : SqlRoleProvider
{
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
try
{
config["connectionStringName"] = "ConnectionPlaceHolder";
base.Initialize(name, config);
FieldInfo connectionStringField = GetType().BaseType.GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);
connectionStringField.SetValue(this, ApplicationConfiguration.RetrieveApplicationConfigurationValue(myConnectionString));
}
catch (Exception ex)
{
CurrentOperation.RaiseException(ex);
throw ex;
}
}
private string myConnectionString()
{
return "Server=MyServer;database=MyDB;uid=aspnetDBUser;pwd=myPassword"
}
}
When you call base.Initialize() the .NET class requires there be a name specified in the web.config which is why you have to put something, so I just used a placeholder since I knew I would be overriding it in the code.
Our team did this because we needed to dynamically build connection strings based on different environments and didn't want to have to worry about having multiple web.configs floating around.

Related

Why do I need to name my connection string as "DefaultConnection" to create database MDF file?

I wonder what's going on: if my connection string name is something other than DefaultConnection, then the .mdf file will not be created in the App_data folder, and the database will be created with the name efaultConnection as it appear when I go to SQL Server.
If I rename the connection string to DefaultConnection, then the .mdf file will be created in the App_data folder and I will not see the database in SQL Server using SQL Server Management Studio.
By the way, the database will be created once I try to create new user using Identity Framework.
<connectionStrings>
<add name="DefaultConnection"
connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\JobsWebsiteDB.mdf;Integrated Security=True;User Instance=True"
providerName="System.Data.SqlClient"/>
</connectionStrings>
You don't, at least I didn't. When you create your DB context, you will need to pass the name of your connection string. For example, my connection string is named "LonelyCacheIdentity":
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, long, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
public ApplicationDbContext() : base("LonelyCacheIdentity")
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Properties<DateTime>().Configure(property => property.HasColumnType("datetime2"));
}
}
Then, in my web.config, I have the following connection strings defined:
<connectionStrings>
<add name="LonelyCacheConnection" connectionString="Data Source=localhost\SQLEXPRESS;Initial Catalog=lonelycacheproject;Integrated Security=True" providerName="System.Data.SqlClient" />
<add name="LonelyCacheIdentity" connectionString="Data Source=localhost\SQLEXPRESS;Initial Catalog=lonelycacheproject;Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionStrings>
For production, I transform the connection strings to work on the production server.

ASP.NET MVC5 with existing Membership database

I have an existing ASP.NET Membership database which I have to stick to. I'm developing a new MVC5 website in VS2013 (update 4) using a new MVC website project template. I have modified web.config to ensure the old Membership type of authentication is specified. I have also modified generated [HttpPost]Login action to ensure I login against my Membership database - it logs me in correctly and generates an authentication cookie as required.
However the website still redirects me to the Login page as I'm not authenticated. The Request.IsAuthenticated does show that I'm not authenticated. What am I missing? What are my options?
EDIT:
web.config changes (only changes):
<connectionStrings>
<add name="MyCS" connectionString="Data Source=sql1;Initial Catalog=MyDB;Persist Security Info=True;User ID=mysa;Password=mypw" providerName="System.Data.SqlClient" />
</connectionStrings>
<system.web>
<authentication mode="Forms">
<forms timeout="20" name="seAdmin" loginUrl="~/Account/Login" />
</authentication>
<roleManager enabled="true" defaultProvider="CustomizedRoleProvider">
<providers>
<clear />
<add name="CustomizedRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="MyCS" applicationName="/seAdmin" />
</providers>
</roleManager>
<membership defaultProvider="CustomizedMembershipProvider">
<providers>
<clear />
<add name="CustomizedMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="MyCS" applicationName="/seAdmin" />
</providers>
</membership>
<machineKey validationKey="63A7C07B191BA3EF02DD4866C420DCAB81C9FFCCC617DE40ED6E2B89A2FC2BA3FA32C39D183FE0708E9279C14E58318D0C5E171C0AF802F154430679D1778485" decryptionKey="F5B9049DECB8C9A23B1D131E63D2ED5C15FF0AEB3C3E96FC" validation="SHA1" />
</system.web>
Login action:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
if (Membership.ValidateUser(model.Email, model.Password))
{
FormsAuthentication.SetAuthCookie(model.Email, model.RememberMe);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1)
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
return View(model);
}
FilterConfig adds a global authorisation attribute
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AuthorizeAttribute() { Roles = "CWA" });
}
}
I have some theories as to why it doesn't work, firstly in web.config there is a <system.webServer> tag, there must be a line for removing FormsAuthentication, try commenting it out, or add one again, like so:
<system.webServer>
<modules>
<remove name="FormsAuthentication" />
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
</modules>
secondly try User.Identity.IsAuthenticated to see it is false too, also are you have any role in your app?

ASP.NET API Windows authentication w/o login

Intranet application that needs to use Windows AD for authorization. Anyone will have access to the site, but only members of a specific AD group will have access to certain pages and API calls. The group name is in the web.config as it will vary by environment. I want to be able to use attributes on API controllers and methods:
[MyAdminAttribute]
public class MyController : ApiController
Here is my custom attribute:
public class MyAdminAttribute: AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext)
{
var user = ((ApiController)actionContext.ControllerContext.Controller).User; // THIS USER IS ALWAYS NULL, WHY?
var adminGroup = WebConfigurationManager.AppSettings["MyAdminGroup"];
var isInAdminGroup = user.IsInRole(adminGroup);
var roles = ((ClaimsIdentity)user.Identity).Claims
.Where(c => c.Type == ClaimTypes.Role)
.Select(c => c.Value).ToList(); // Just checking here, 0 roles found.
return isInAdminGroup;
}
}
I also need to be able to check that the user is in the role within a Razor view:
#if (User.IsInRole(WebConfigurationManager.AppSettings["MyAdminGroup"])) {
Here are the relevent web.config settings:
<authentication mode="Windows"/>
<roleManager enabled="true" defaultProvider="AspNetWindowsTokenRoleProvider">
<providers>
<clear />
<add
name="AspNetWindowsTokenRoleProvider"
type="System.Web.Security.WindowsTokenRoleProvider"
applicationName="/" />
</providers>
</roleManager>
However, the User is always null and User.IsInRole() is always false. Why would this be occuring?

Asp .Net Custom membership parameters from web.config

I am currently writting a custom membership provider for asp .net and the problem that I am having is that I don't know how to give parameters to the custom membership provider in the same way that you give to the standard asp .net membership providers in the web.config file like password length.
When you derive your own class from MembershipProvider you have to override the Initialize() method, it has following signature:
public override void Initialize(string name, NameValueCollection config);
The System.Collections.NameValueCollection is a dictionary where you find the options written in the web.config file. These options are given in the same way you specify options for "standard" providers (as attributes). Each dictionary entry has the key of the attribute name and as value the attribute's value (as string).
public class MyMembershipProvider : MembershipProvider
{
public override void Initialize(string name, NameValueCollection config)
{
base.Initialize(name, config);
_enablePasswordReset = config.GetBoolean("enablePasswordReset", true);
}
}
Where, in my example, GetBoolean() is an extension method declared somewhere as follow:
public static bool GetBoolean(this NameValueCollection config,
string valueName, bool? defaultValue)
{
object obj = config[valueName];
if (obj == null)
{
if (!defaultValue.HasValue)
throw new WarningException("Required field has not been specified.");
return defaultValue.Value;
}
bool value = defaultValue;
if (obj is Boolean)
return (bool)obj;
IConvertible convertible = obj as IConvertible;
try
{
return convertible.ToBoolean(CultureInfo.InvariantCulture);
}
catch (Exception)
{
if (!defaultValue.HasValue)
throw new WarningException("Required field has invalid format.");
return defaultValue.Value;
}
}
If your provider derives from MembershipProvider : ProviderBase than all the configuration should be loaded and applied form web.config.
Consider implementing custom IPrincipal and/or IIdentity - it's sometimes a better extension point and since not everyone is aware of it it's often not used.
In the same way you define the standard .net membership:
<membership defaultProvider="MyCustomMembershipProvider" userIsOnlineTimeWindow="30">
<providers>
<clear />
<add name="MyCustomMembershipProvider" type="Namespace.MyCustomMembershipProvider" connectionStringName="db_ConnectionString" enablePasswordRetrieval="true" enablePasswordReset="true" requiresQuestionAndAnswer="true" requiresUniqueEmail="false" passwordFormat="Encrypted" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="8" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression="" applicationName="/" />
<add name="StandardMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="db_ConnectionString" enablePasswordRetrieval="true" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="true" passwordFormat="Encrypted" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="8" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression="" applicationName="/" />
</providers>
</membership>

Which Membership Provider implements users stored in web.config?

Having a code-blind moment.
ASP.NET 4.0.
Web.config:
<?xml version="1.0"?>
<configuration>
<system.web>
<authentication mode="Forms">
<forms name="DataViewer" loginUrl="login.aspx">
<credentials passwordFormat="Clear">
<user name="devuser" password="test" />
</credentials>
</forms>
</authentication>
<authorization>
<deny users="?" />
</authorization>
</system.web>
and a login control:
<asp:Login ID="login" runat="server" />
If I enter a username and password, and click Login, it hangs.
If I break, I can see in the call stack that login.AuthenticateUsingMembershipProvider() is in the middle of calling SqlMembershipProvider.ValidateUser(). There is no database defined or involved in this project at all, and I haven't specified that SqlMembershipProvider should be used.
So my question is, what membership provider should I use to get ASP.NET to use the usernames and passwords in the <credentials> element of web.config?
I'm amazed that considering how the framework designers went to the trouble of defining a <credentials /> element that they didn't implement any code to consume it.
I found a sort-of-working implementation of this here which I have fixed up and included below. All other members of MembershipProvider throw NotImplementedException.
using System.Configuration;
using System.Web.Configuration;
using System.Web.Security;
public class WebConfigMembershipProvider : MembershipProvider
{
private FormsAuthenticationUserCollection _users = null;
private FormsAuthPasswordFormat _passwordFormat;
public override void Initialize(string name,
System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(name, config);
_passwordFormat = getPasswordFormat();
}
public override bool ValidateUser(string username, string password)
{
var user = getUsers()[username];
if (user == null) return false;
if (_passwordFormat == FormsAuthPasswordFormat.Clear)
{
if (user.Password == password)
{
return true;
}
}
else
{
if (user.Password == FormsAuthentication.HashPasswordForStoringInConfigFile(password,
_passwordFormat.ToString()))
{
return true;
}
}
return false;
}
protected FormsAuthenticationUserCollection getUsers()
{
if (_users == null)
{
AuthenticationSection section = getAuthenticationSection();
FormsAuthenticationCredentials creds = section.Forms.Credentials;
_users = section.Forms.Credentials.Users;
}
return _users;
}
protected AuthenticationSection getAuthenticationSection()
{
Configuration config = WebConfigurationManager.OpenWebConfiguration("~");
return (AuthenticationSection)config.GetSection("system.web/authentication");
}
protected FormsAuthPasswordFormat getPasswordFormat()
{
return getAuthenticationSection().Forms.Credentials.PasswordFormat;
}
}
You are going to need to write your own provider for this. It should be relatively straightforward to take the sample ReadOnlyXmlMembershipProvider in the MSDN documentation and change it to read users and credentials from web.config, instead of an external XML file.
I'm not sure if you have tried but....
The FormsAuthentication.Authenticate is in charge to do that for you (although it is deprecated now because the recommended behavior is to use the Membership object)
From MSDN:
The Authenticate method verifies user credentials that are stored in the credentials section of the application configuration file. Alternatively, you can use ASP.NET membership to store user credentials and call the ValidateUser to verify the credentials.
You can also remove the membership providers (because even when you do not declare them on your web.config, they are inherited from the machine.config file)
<membership>
<providers>
<remove name="AspNetSqlMembershipProvider"/>
</providers>
</membership>
Try using this method. I hope it helps. http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.login.onauthenticate.aspx

Resources