Switch connectionstring by visiting subfolder EF - c#

Rewritten my previous question without confusing code samples.
This is what i'm trying to achieve (or have done so far)
I created a webapplication to be viewed at www.myschool.com
I want to use subdomains so different schools can login to their personal school database (no central db)
I have 2 subfolders in my webapplication "school1" and "school2"
When a school visits www.myschool.com/school1 it should load the webapplication in the root folder by using the connectionstring provided in web.config from it's subfolder (school1 has web.config, school2 has web.config)
When www.myschool.com is entered (no subdomain) it should show the database "school1" as default
The databases are sqlserver databases and I use EF with .edmx file "SchoolModel" wich contains the entity model for the individual school databases, those have equal tabbles, fields, ... .
What currently happens:
When visiting www.myschool.com it shows the application with database from default school1
When entering www.myschool.com/school1 it just shows HTTP Error 403.14 - Forbidden because that folder only contains a web.config with another connectionstring for that specific school.
In the end a school (school1 or school2) should be able to enter school1.myschool.com or school2.myschool.com to view the webapplication loaded with content of it's personal database.
Using central database to serve both is not an option in this particular application.
Thanks for helping me setting this up

Related

Unconfirmed Email Login: PasswordSignInAsync Returning 'NotAllowed' Result Despite 'options.SignIn.RequireConfirmedEmail = false;'

I've created a Razor Pages Web App targeting .NET 5.0. No authentication was selected on creation, and ASP.NET Identity was added to the project as a scaffolded item immediately after creation, with all Identity files being overridden. 
While experimenting with the ASP.NET Identity options, I set 'options.SignIn.RequireConfirmedEmail = false;' in the ConfigureServices method of the Startup.cs class file as shown here.
Despite options.SignIn.RequireConfirmedEmail being set to false, I was still unable to login using an unconfirmed email, with the catch-all error message of "Invalid login attempt." appearing above the form.
Setting a breakpoint, I found that, during login, the result variable receives a "NotAllowed" value from the _signInManager.PasswordSignInAsync() method.
Image of Breakpoint in OnPostAsync Method in Login.cshtml.cs
Image of Local Variables Immediately After Calling _signInManager.PasswordSignInAsync()
Upon setting the email to be confirmed, the login succeeds. Therefore, the only difference between login success and failure is whether or not the email is confirmed, even though options.SignIn.RequireConfirmedEmail has been set to false in the ConfigureServices method of the Startup.cs class file.
The question then is, have I missed something that is required to allow for an unconfirmed email to successfully login? If not, then is this a bug? Also, how could one allow for an unconfirmed email to successfully login, assuming that options.SignIn.RequireConfirmedEmail blocks unconfirmed email login attempts regardless of whether it has been set to true or false?
Thank you very much.
While posting my question to stack overflow, one of the similar questions suggested by stack overflow helped me figure out the answer to my question. The following is my response to my own question for the sake of those who will struggle with the same misunderstanding as I did.
Stack Overflow Question:
Why is the login attempt returning {NotAllowed} in the PasswordSignInAsync method (ASP.NET)
Stack Overflow Answer:
“File that is most likely your issue:
‘TaskManager/TaskManager/Areas/Identity/IdentityHostingStartup.cs’
Location is in requiring the account to be confirmed in this line:
‘services.AddDefaultIdentity(options =>
options.SignIn.RequireConfirmedAccount = true)’
My guess is that your account is not confirmed so you need to confirm
it or remove this constraint.”
It appears that when one creates a Razor Pages Web App without authentication, and then add the ASP.NET Identity files as a scaffolded item (‘override all files’ checked), the Identity folder in the Areas folder contains an IdentityHostingStartup.cs file in addition to the Data folder and Pages folder. Additionally, the application also contains the typical Startup.cs class file in the root folder.
On the other hand, when one creates a Razor Pages Web App with individual accounts authentication using ASP.NET Identity, the Identity folder in the Areas folder only contains a Pages folder, and all configuration settings are located in the typical Startup.cs class file in the root folder.
The line of code that is actually preventing the successful login of an account with an unconfirmed email is the following:
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
For an application with scaffolded Identity, the above line of code is located in the IdentityHostingStartup.cs file, while for an application created with Identity authentication, the above line of code is located in the Startup.cs file.
Therefore, the solution for an application with scaffolded Identity is to edit the IdentityHostingStartup.cs file and either 1) set ‘SignIn.RequireConfirmedAccount’ to ‘false’, or 2) completely remove the configuration setting from the ‘services.AddDefaultIdentity<IdentityUser>()’ method and instead place all the configuration settings in the ‘services.Configure<IdentityOptions>()’ method.
The latter approach would probably be the most clear and readable approach, and will allow for the developer to use the 'options.SignIn.RequireConfirmedEmail = false;' setting as originally intended.
Image of the First Approach: Setting SignIn.RequireConfirmedAccount to False
Image of the Second Approach: Removing SignIn.RequireConfirmedAccount from services.AddDefaultIdentity<IdentityUser>() and Setting SignIn.RequireConfirmedEmail to False in services.Configure<IdentityOptions>()
The main point of misunderstanding here and the key takeaway is that the developer of an application with scaffolded Identity should configure the settings of ASP.NET Identity in the IdentityHostingStartup.cs file, and not the Startup.cs file.
The use of IdentityHostingStartup.cs for configuring Identity is in fact explicitly mentioned in the documentation regarding scaffolding Identity into a Razor project without existing authorization.
Scaffold Identity in ASP.NET Core projects: Scaffold Identity into a Razor project without existing authorization
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/scaffold-identity?view=aspnetcore-5.0&tabs=visual-studio#scaffold-identity-into-a-razor-project-without-existing-authorization
Please feel free to add any other comments that will help others better understand this topic.

DOTNET Core - Persisting a database name for a session

It was suggested to use this concept to run a multitenant separate database which appears to be the simplest option regarding configuring the DBContext.
This option although out of date uses a separate catalog database to store each tenants details including the database name.
I would like to use the second option so I can update the database easily with new clients. However, I note that every time I would send a request in I would have to access the catalog database to retrieve the database name again in order to configure the DBContext. This has performance issues in that I have now two database calls - one to configure the context and one for the actual data request.
I note that there is a httpContext.Session.SetString(xxx, yyy) and HttpContext.Session.GetString("xxx") option that you could use to store the database name for the session.
My question is, is this a valid option for temporarily storing the database name so I dont have to do a database call everytime I get a request? Is it safe? Is there a better way to do this with a catalog database(holds user name and database connection details)?

ASP.NET (MVC) Users, Roles and Users in Roles

I have had a mini-battle over the past two days and although I have solved it, its not in the way I expected.
ASP.NET 4.5 on Visual Studio 2013.
If you create a new project and fire it up for the first time, and register, everything is fine.
It creates a table called AspNetUsers and puts your in that table.
It also creates other tables (including - but not limited to)
- AspNetRoles - stores roles
- AspNetUserRoles - stores users in roles
So I used Database first to create Models and Controllers for all 3 tables so I can manage them from an interface (I know there used to be something built in to VS2010 but it seems to have disappeared - no worries as I learnt how to do DB first)
Problem is when I added a user to a role, the data showed in my database but when I tried to lockdown a controller method using the decorator:
[Authorize(Roles="admin")]
public string test()
{
return "hello";
}
everything started to go pear shaped.
So I tested User.IsInRole("admin") and to my dismay it returned false!
So I went down the journey of web.config (this tells the role manager to point at my DefaultConnection (which I am using for everything)
<roleManager enabled="true" defaultProvider="SqlRoleManager">
<providers>
<add name="SqlRoleManager"
type="System.Web.Security.SqlRoleProvider"
connectionStringName="DefaultConnection"
applicationName="ConferenceOrg" />
</providers>
</roleManager>
Then in code I used:
string role = "admin";
if (!Roles.RoleExists(role))
{
Roles.CreateRole(role);
}
else
{
string[] usrs = { User.Identity.Name };
if (!User.IsInRole(role))
{
Roles.AddUsersToRole(usrs, role);
}
}
Initially this didn't work because a stored procedure didn't exist.
So, I went to C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\Shortcuts and fired up the Native Tools Cmd prompt (x86) and used that to add the stored procedures etc
Now the code above works but it has created a whole bunch of tables in addition to the original set.
The code creates the role in the new aspnet_roles table and it now creates my user in BOTH AspNetUsers and aspnet_users
So my next move is to just delete the tables I created with database first and redo it for the aspnet_ tables instead...
Not happy but it will work. Any thoughts on my situation?
Am I doing it right?
Your project is mixed with Legacy SQL Membership Provider and New ASP.Net Identity.
You only need one Membership which is New ASP.Net Identity.
In ASP.Net Identity, you do not need to configure any setting in web.config (except AppStartup for owin).
How to Solve it
Now your database is a mess. Since your application is new and no existing users, it is easily to recreate a new Application and new Database, and start all over again.
Then make sure both User and Role are assigned to AspNetUserRoles table.

CreateUserWizard control problem

i put data in, but no mdf file is created. No database is created.. Why is that?
An extract from the book that i have read now:
At this point, the CreateUserWizard control uses the ASP.NET Membership class behind the scenes to create a new user. The default membership provider creates the aspnetdb.mdf file (if it doesn’t exist already) and then adds the new user record. Once this process is complete, the CreateUserWizard control shows a message informing you that the user was created. Miraculously, all of this takes place automatically even though you haven’t configured anything in the web.config file and you didn’t create the database file in advance.
Things to check:
1: Refresh your solution. The file might be hidden under App_Data folder. Check in the file system i.e. your physical folder.
2: Check your web.config to make sure if there are any specific configuration related to membership provider or connectionstring.
3: Handle CreateUserError event to see if it gives any more info.
4: Check your Event Log to see if there are any errors.
5: Launch ASP.NET Website Configuration tool and see if the user you created exists there.
Or we need more info like web.config settings and your CUW code(if customized).

ASP.NET MVC authentication using custom database instead of ASPNETDB?

I already have a User table in my primary application database with an email address (which will act as the user name) and a password. I would like to authenticate using my database instead of the default authentication database (ASPNETDB).
Questions:
Is this a bad idea? Is it a huge can of worms to use my own DB for authentication?
How much work am I adding by doing this? I already have code for hashing the password and a query that will check if the email and password match the DB. So, I wouldn't be starting from scratch.
What would I need to do to use my database instead of ASPNETDB? I'm hoping this can be described in a few simple steps, but if not, could you point me to good source?
Update
I'm still looking for a little more detail here on my third question. Do I need to write my own MembershipProvider? What changes do I need to make to my web.config file? Will the [Authorize] attribute still work if I write my own solution? Can I use the automatically-generated AccountController with some minor modifications or do I basically need to rewrite the account controller from scratch?
It's quite simple, you need to derrive MembershipProvider and implement the ValidateUser method. Take a look at this post. I'm using custom membership provider with Postgres and MVC just fine.
I'll answer your updated questions:
Do I need to write my own MembershipProvider?
If you (a) want to continue using Forms Authentication, and (b) have an authorization table structure that doesn't follow the same conventions as the ASPNETDB, then yes. If you don't need FormsAuth (see below), then you can do away with the MembershipProvider entirely, but I wouldn't recommend it. Or, if you're using the exact same security tables as ASPNETDB but just want to point it to a different database, you can continue using the default provider and simply change its configuration.
What changes do I need to make to my web.config file?
If you are using your own custom MembershipProvider, then you need to register it in the <providers> section of the <membership> element and change the defaultProvider property. If you are using the standard AspNetSqlProvider then you probably just need to change the connection string.
Will the [Authorize] attribute still work if I write my own solution?
Yes, if you stick to Forms Authentication (either use the AspNetSqlProvider or write and register your own membership provider). No, if you abandon Forms Authentication (again, not recommended).
Can I use the automatically-generated AccountController with some minor modifications or do I basically need to rewrite the account controller from scratch?
You should rewrite the AccountController anyway - don't leave demo code in a production app. But if you must - yes, the AccountController will work under the same conditions as above.
No. And I would suspect most people do not trust that cruddy mechanism
Not much at all, especially since you have the table already.
Take a look at this for example: http://forums.asp.net/t/1250726.aspx
Hi ,
Just follow these simple steps :
First, you can delete the .mdf file in App_Data folder. Since we don’t need any of these tables.Then, we need to update the default connection string in the web.config to point to our database.
<connectionStrings>
<add name=”DefaultConnection” connectionString=”Data Source=SERVER\INSTANCENAME;Initial Catalog=DBNAME;Integrated Security=True” providerName=”System.Data.SqlClient” />
</connectionStrings>
Third, Open Nuget Package Manager and write the following commands:
Enable-Migrations
Add-Migration Init
Update-Database
Check out your database, all ASP.NET membership tables with Prefix Asp have been create and then you can test it out by running your application and execute membership actions such as Signing up or Signing in to your application.
Created tables after running above commands:
AspNetRoles
AspNetUserClaims
AspNetUserLogins
AspNetUserRoles
AspNetUsers
__MigrationHistory
Source : https://blogs.msmvps.com/marafa/2014/06/13/how-to-create-asp-net-mvc-authentication-tables-in-an-existing-database/
We're doing exactly this in one of our applications, and find it quite simple. We have an authentication service (called from the controller) that handles the mechanics of hashing the entered password to see if it is a match, then simply returns a bool for a method we call "IsValidLogon".
In our case, the purpose was to keep the management of what should be a pretty trivial task as lightweight as possible.
We bascially ignored ASPNETDB entirely. If we get a valid response from our user/password check, we simply call the standard FormsAuthentication.RedirectFromLoginPage(username, createCookieBool);
Hope that helps.
just building the same, so answer to 1 must be NO :)
I'm using the standard asp.net forms authentication, where i use the FormsAuthentication.RedirectFromLoginPage(username, createCookieBool) method to log a user in.
I gave a user a unique guid (you can use any other user id) and i'm storing it in the UserName parameter along with the username (to display on the masterpage: Html.Encode(Page.User.Identity.Name.Split("|".ToCharArray())[1]))
In each controller/method in which i must know which user is logged on (via User.Identity.Name, split the string and get the userguid).
Also i decorate those routines with the [Authorize] attribute.

Categories