Login fails when recreating database with Code First - asp.net

I'm using ASP.NET Entity Framework's Code First to create my database from the model, and the login seems to fail when the database needs to be recreated after the model changes.
In Global.asax, I've got the following:
protected void Application_Start()
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<EntriesContext>());
// ...
}
In my controller, I've got the following:
public ActionResult Index()
{
// This is just to force the database to be created
var context = new EntriesContext();
var all = (from e in context.Entries select e).ToList();
}
When the database doesn't exist, it is created with no problems. However, when I make a change to the model, rebuild and refresh, I get the following error:
Login failed for user 'sa'.
My connection string looks like this:
<add name="EntriesContext"
connectionString="Server=(LOCAL);Database=MyDB;User Id=sa;Password=password"
providerName="System.Data.SqlClient" />
The login definitely works as I can connect to the server and the database from Management Studio using these credentials.
If I delete the database manually, everything works correctly and the database is recreated as expected with the schema reflecting the changes made to the model.
It seems like either the password or access to the database is being lost.
Is there something else I need to do to get this working?

It seems like Code First has a problem using the password specified in the connection string when connecting to a database that has been recreated. Changing this to use a trusted connection gets around the problem as the password no longer needs to be stored.
So, instead of this:
Server=(LOCAL);Database=MyDB;User Id=sa;Password=password
Use the following instead
Server=(LOCAL);Database=MyDB;Trusted_Connection=true
You may need to add your account or the one being used by ASP.NET to SQL Server and grant it the 'dbcreator' permission so that it can drop and recreate the database.

I believe using sa may be an issue. Either way I believe it is good practice to create a separate user profile to use in your connection string.

Related

Upgraded to MS Identity Core 2.0 and EF6.1 and login fails: Invalid column name 'Email'

I went to the Manage NuGet Package option and updated all the packages. I'm not using much: Linq-to-EF 6.1 and the packages needed to make MS Identity work. However, something broke because now when I go to log in, I get an error
Exception Details: System.Data.SqlClient.SqlException: Invalid column name 'Email'.
Invalid column name 'EmailConfirmed'.
Invalid column name 'PhoneNumber'.
Invalid column name 'PhoneNumberConfirmed'. //and so on
The exception looks like this:
What might have caused this and how do I fix and prevent this from happening again when all I did was just update the NuGet packages?
Edit
I resolved this issue by totally removing the database; the app recreated a new DB with the necessary columns. I was able to do that because the app is still in dev and no real user data was involved. However, I'm still interested in this issue because when new updates will be released, I want to make sure I don't have to throw away the current DB.
I solved this for my application by removing the DefaultConnection connection string. If you have DefaultConnection and MySpecialConnection in your config file, ASP.net identity seems to use DefaultConnection.
Sounds like you would need to create your own user object with the additional fields you want that are not provided by the base object. That would then need to match the table in the database.
Then you use that new user object every time you use the usermanager.
var mgr = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>());
public class ApplicationUser : IdentityUser
{
//my custom fields
public string Email { get; set; }
}
Hope that helps

ASP.NET MVC 4 single use impersonation

I am developing an ASP.NET MVC 4 application that utilizes impersonation (running as domain user), set in web.config and setup app pool to run as same user. Within the application we use a mix of windows authentication and a custom role provider to restrict access.
A new requirement has been brought about that requires querying data from a table locked down by AD groups. For security reasons, we cannot at our generic user to this table, or control it via custom roles. The only way our DBA will allow this data to leave it's respective home is when it is to one of the users within the AD groups assigned. So, I need to be able to make a call to an EF stored proc or Repo method as the current request's user.
I have tried something like the following (I hope this might clear up what I am attempting to do):
[HttpGet]
public ViewResult TestCall()
{
var lst = new List<Staged>();
//Get current request user
using (var person = ((WindowsIdentity) HttpContext.User.Identity))
{
//Impersonate said user
person.Impersonate();
//Make magics happen
using (var db = new TestEntities())
{
lst.AddRange(db.up_testseccall());
}
}
//Return magics, hopefully.
return View(lst.ToArray());
}
This code works fine locally, but fails miserably when tested remotely with a test account of mine. Any ideas or pointers would be greatly appreciated.
EDIT:
The issue I'm having now is that the DB is recieving the login as 'NT Authority\Anonymous Login'
EDIT 2:
Managed to receive a 'Safe handle has been closed' exception...so that's something new.
EDIT 3:
Current connection string
EDIT 4:
I gave up on this approach after a week of trying to get kerberos double-hop setup. I opted for a special domain service account for handling the required transactions.
<add name="TestEntities" connectionString="metadata=res://*/Models.TestModel.csdl|res://*/Models.TestModel.ssdl|res://*/Models.TestModel.msl;provider=System.Data.SqlClient;provider connection string="data source=DATABASENAME;initial catalog=CATALOG;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />

Connection string "DefaultConnection" was not found when using ASP.NET Default Membership with EF 5 Code First

I have created my application domain model using EF 5 Code First. I am then attempting to hook that with the ASP.NET Default Membership provider by running aspnet_reqsql.exe utility to generate the membership tables in my database.
This seems to be working fine, because when I check the database in SQL Management Studio, all the membership tables are there, along with my Code First tables.
Now then, we I run my application, all seems good, it loads up and I can browse the pages. However, as soon as I browse something that requires membership (e.g. Login page) the application breaks. I get the Yellow Screen of Death with the following message:
Server Error in '/' Application.
Connection string "DefaultConnection" was not found.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: Connection string "DefaultConnection" was not found.
Source File: c:\Users\XXX\Documents\Visual Studio 2012\Projects\YourApp\YourApp\Filters\InitializeSimpleMembershipAttribute.cs Line: 41
There is this thread by a User that faced a similar problem, however his solution is already implemented by me, the fact that I have to comment out the already existing connection string in web.config. I've done that, but still no success.
What am I doing wrong?
Where you call InitializeSimpleMembershipAttribute, if you still have "DefaultConnection" as the first parameter, then you need to change this to be the name of the connection you want it to use.
I managed to sort it out all thanks to Richard. Here is what I did:
I opened up my AccountController, in there on top of the class, you'll see this (if using ASP.NET MVC4):
[Authorize]
[InitializeSimpleMembership]
public class AccountController : Controller
{
// all code for controller here.
}
I then Right-Clicked on [InitializeSimpleMembership] and clicked Go To Definition.
Once there, if you scroll to the bottom, you'll see something like this:
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
Change DefaultConnection attribute to whatever you've named your EF Code First Context, and you're good to go.
Thanks Richard.

How to insert sample data without dropping the entire database in ASP.NET MVC3's ORM?

I am working on a website in ASP.NET MVC3 and following the MVC Music Store tutorial to do so, but editing things for what I need. I am now trying to get a preview of the site on my test deploy server but have trouble with the SQL connections.
I altered the SampleData file to fit my needs and replaced DropCreateDatabaseIfModelChanges<MyProjectEntities> with DropCreateDatabaseAlways<MyProjectEntities> because it did not update the sample data when I needed it.
When I try to look up something in the database I get the exception Cannot drop the database 'MyDatabase', because it does not exist or you do not have permission. I understand that this is because I simply cannot drop the database, and I will not be able to do this on my server. So how can I tell the ORM that I don't want it to drop the database to create the tables but rather empty or update an existing database?
Update:
I downloaded SQL Server Management Studio and managed to get the data from the local database to the online database but now I get a different error: Login failed for user '[username]'.
I double checked the connection string and it is correct, here is the full snippet of code from my Web.config:
<add name="AdzTowerEntities"
connectionString="Data Source=xxxxx.db.xxxxxxx.hostedresource.com; Initial Catalog=xxxxxroot; User ID=xxxxxroot; Password=xxxxxpassword;"
providerName="System.Data.SqlClient"/>
What about using MigrateDatabaseToLatestVersion?
If code first migrations are not available to you, then could create a custom IDatabaseInitializer, which will execute the SQL necessary to bring your changes :
public class AlwaysSeedInitializer : IDatabaseInitializer<MyProjectEntities>
{
public void InitializeDatabase( MyProjectEntities context )
{
if( context.Database.Exists() )
{
if( !context.Database.CompatibleWithModel( true ) )
{
context.Database.SqlCommand("Add SQL commands here");
}
}
if ( /* Insert test to see if database is already seeded */ )
{
//Seed here if the db is not seeded
}
}
}
And put that in your Application_Start() of your global.asax:
Database.SetInitializer(new AlwaysSeedInitializer());

SessionID changing across different instances in Azure (and probably in a web farm)

I have a problem with an Azure project with one WebRole but multiple instances that uses cookieless sessions. The application doesn't need Session storage, so it's not using any session storage provider, but I need to track the SessionID. Apparently, the SessionID should be the same accross the WebRole instances, but it changes suddently w/o explanation. We are using the SessionID to track some data, so it's very important.
In order to reproduce the issue:
Create a Cloud Project.
Add a ASP.NET Web Role. The code already in it will do.
Open Default.aspx
Add a control to see the current SessionID and a button to cause a postback
<p><%= Session.SessionID %></p>
<asp:Button ID="Button1" runat="server" Text="PostBack" onclick="Button1_Click" />
Add a event handler for button that will delay the response a bit:
protected void Button1_Click(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(150);
}
Open Web.Config
Enable cookieless sessions:
<system.web>
<sessionState cookieless="true" />
</system.web>
Run the project, and hit fast and repeteadly the "PostBack" button for a while giving attention to the session id in the address bar. Nothing happens, the session id is always the same :). Stop it.
Open ServiceConfiguration.csfg
Enable four instances:
<Instances count="4" />
Ensure that in the Web.config there is a line related with the machine key that has been added automatically by Visual Studio. (at the end of system.web).
Rerun the project, hit fast and repeteadly the "Postback" button for a while and give attention to the session id in the address bar. You'll see how the SessionID changes after a while.
Why is this happening? As far as I know, if all machines share the machineKey, the session should be the same across them. With cookies there are no problems, the issue apparently is just when cookieless sessions are used.
My best guess, is that something wrong is happening when there are several instances, when the SessionID generated in one WebRole goes to another, is rejected and regenerated. That doesn't make sense, as all the WebRoles have the same machineKey.
In order to find out the problem, and see it more clearly, I created my own SessionIDManager:
public class MySessionIDManager : SessionIDManager
{
public override string CreateSessionID(HttpContext context)
{
if (context.Items.Contains("AspCookielessSession"))
{
String formerSessionID = context.Items["AspCookielessSession"].ToString();
// if (!String.IsNullOrWhiteSpace(formerSessionID) && formerSessionID != base.CreateSessionID(context))
// Debugger.Break();
return formerSessionID;
}
else
{
return base.CreateSessionID(context);
}
}
}
And to use it change this line in the WebConfig:
<sessionState cookieless="true" sessionIDManagerType="WebRole1.MySessionIDManager" />
Now you can see that the SessionID doesn't change, no matter how fast and for how long you hit. If you uncomment those two lines, you will see how ASP.NET is creating a new sessionID even when there is already one.
In order to force ASP.NET to create a new session, just a redirect to an absolute URL in your site:
Response.Redirect(Request.Url.AbsoluteUri.Replace(Request.Url.AbsolutePath, String.Empty));
Why is this thing happening with cookieless sessions?
How reliable is my solution in MySessionIDManager ?
Kind regards.
UPDATE:
I've tried this workaround:
User-Specified Machine Keys
Overwritten by Site-Level Auto
Configuration, but the problem
still stands.
public override bool OnStart()
{
// For information on handling configuration changes
// see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
using (var server = new ServerManager())
{
try
{
// get the site's web configuration
var siteNameFromServiceModel = "Web"; // update this site name for your site.
var siteName =
string.Format("{0}_{1}", RoleEnvironment.CurrentRoleInstance.Id, siteNameFromServiceModel);
var siteConfig = server.Sites[siteName].GetWebConfiguration();
// get the appSettings section
var appSettings = siteConfig.GetSection("appSettings").GetCollection()
.ToDictionary(e => (string)e["key"], e => (string)e["value"]);
// reconfigure the machine key
var machineKeySection = siteConfig.GetSection("system.web/machineKey");
machineKeySection.SetAttributeValue("validationKey", appSettings["validationKey"]);
machineKeySection.SetAttributeValue("validation", appSettings["validation"]);
machineKeySection.SetAttributeValue("decryptionKey", appSettings["decryptionKey"]);
machineKeySection.SetAttributeValue("decryption", appSettings["decryption"]);
server.CommitChanges();
_init = true;
}
catch
{
}
}
return base.OnStart();
}
I've also tried this about put a
session start handler and add
some data, but no luck.
void Session_Start(object sender, EventArgs e)
{
Session.Add("dummyObject", "dummy");
}
Bounty up!
In short, unless you use cookies or a session provider there is no way for the session id to pass from one web role instance to the other. The post you mention says that the SessionID does NOT stay the same across web roles if you don't use cookies or session storage.
Check this previous question for ways to handle state storage in Azure, e.g. using Table Storage
The machineKey has nothing to do with sessions or the application domain, it is the key used to encrypt,decrypt,validate authentication and viewstate data. To verify this open SessionIDManager.CreateSessionID with Reflector. You will see that the ID value is just a random 16-byte value encoded as a string.
The AspCookielessSession value is already checked by SessionIDManager in the GetSessionID method, not CreateSessionID so the check is already finished before your code gets executed. Since the default sessionstate mode is InProc it makes sence that separate web roles will not be able to validate the session key so they create a new one.
In fact, a role may migrate to a different physical machine at any time, in which case its state will be lost. This post from the SQL Azure Team describes a way to use SQL Azure to store state for exactly this reason.
EDIT I finally got TableStorageSessionStateProvider to work in cookieless mode!
While TableStorageSessionStateProvider does support cookieless mode by overriding SessionStateStoreProviderBase.CreateUnititializedItem, it fails to handle empty sessions properly in private SessionStateStoreData GetSession(HttpContext context, string id, out bool locked, out TimeSpan lockAge,out object lockId, out SessionStateActions actions,bool exclusive). The solution is to return an empty SessionStateStoreData if no data is found in the underlying blob storage.
The method is 145 lines long so I won't paste it here. Search for the following code block
if (actions == SessionStateActions.InitializeItem)
{
// Return an empty SessionStateStoreData
result = new SessionStateStoreData(new SessionStateItemCollection(),
}
This block returns an empty session data object when a new session is created. Unfortunately the empty data object is not stored to the blob storage.
Replace the first line with the following line to make it return an empty object if the blob is empty:
if (actions == SessionStateActions.InitializeItem || stream.Length==0)
Long stroy short cookieles session state works as long as the provider supports it. You'll have to decide whether using cookieless state justifies using a sample provider though. Perhaps vtortola should check the AppFabric Caching CTP. It includes out-of-the-box ASP.NET providers, is a lot faster and it definitely has better support than the sample providers. There is even a step-by-step tutorial on how to set session state up with it.
Sounds tricky.
I have one suggestion/question for you. Don't know if it will help - but you sound like you're ready to try anything!
It sounds like maybe the session manager on the new machine is checking the central session storage provider and, when it finds that the session storage is empty, then it's issuing a new session key.
I think a solution may come from:
- using Session_Start as you have above in order to insert something into Session storage
- plus inserting a persistent Session storage provider of some description into the web.config - e.g. some of the oldest Azure samples provide a table based provider, or some of the newer samples provide an AppFabric caching solution.
I know your design is not using the session storage, but maybe you need to put something in (a bit like your Session_Start), plus you need to define something other than in-process session management.
Alternatively, you need to redesign your app around something other than ASP.NET sessions.
Hope that helps - good luck!
I experienced the same problem and after much research and debugging I found that the issue occurred because the "virtual servers" in the Azure SDK map the websites to different paths in the IIS metabase. (You can see this through through Request.ServerVariables["APPL_MD_PATH"].)
I just found this out now but wanted to post this so people could get working on testing it. My theory is that this problem may go away once it's published out to Azure proper. I'll update with any results I find.

Resources