Best approach to user roles with an intranet application - asp.net-mvc-3

I'm developing an ASP.NET MVC intranet website which needs to have a few different user roles (admin, editor, writer, etc.) and the backend uses SQL Server. I read this post by scottgu about role-based security and used that as a starting point. The steps I followed were:
Configured a DB using the asp_regsql.exe application
Set the authentication mode to windows
<authentication mode = "Window" />
Added a connection string entry to the Web.config,
<connectionStrings>
<add name="SqlRoleManagerConnection"
connectionString="Data Source=localhost; Initial Catalog=aspservicedb; Integrated Security=SSPI;" />
</connectionStrings>
Added a roleManager entry to the Web.config section,
<roleManager enabled="true" defaultProvider="sqlRoleManager">
<providers>
<clear />
<add name="sqlRoleManager" type="System.Web.Security.SqlRoleProvider"
connectionStringName="SqlRoleManagerConnection"
applicationName="MyApplication" />
</providers>
</roleManager>
Added some role code into the Application_Start() method of the Global.asax.cs file,
if (!Roles.RoleExists("Editor"))
{
Roles.CreateRole("Editor");
}
if (!Roles.RoleExists("Writer"))
{
Roles.CreateRole("Writer");
}
if (!Roles.RoleExists("SiteAdmin"))
{
Roles.CreateRole("SiteAdmin");
Roles.AddUserToRole("MYCOMPUTER\\Matt", "SiteAdmin");
}
Modified my controllers to use the roles:
[Authorize(Roles = "SiteAdmin")]
public class SiteAdminController : Controller
{
.
.
.
}
And this all seems to work at this point but I'm wondering if there is a better approach to handing roles or if there are problems with this approach. It's easy to convince oneself that the approach is a good one because it worked but I'd like to take a different approach now rather than later if this isn't the best approach to solving the problem. Elsewhere I'd read someone say this was "hack" but never really qualified why he wouldn't solve the problem this way. Your thoughts? Do you have a better what to solve this?

In some of my production MVC apps, I simply use the built in sql role provider. It works out of the box, your MVC3 templates will be configured to use it already. Simply open up the admin site from within Visual Studio and manage the security and add your roles, users, users to roles, etc and thats it. Do not use your web.config to manage what roles have access to what URIs, this has been recommended over and over to stay away from in MVC as more than one uri could potentially get to a single route, so you use (as you did) the Authorize attribute in conjunction with the automatic role management, and thats all you need. It's pretty simple.

Well there are two alternatives worth mentioning:
Configure your role based security right in the web config. This centralizes your security configuration, though it must be updated to mirror your paths / routing, so there's a bit of manual maintenance involved
Configure your role based security in the database and create custom Action Filter to read, cache, and apply these roles based on the logged in user. This is dynamic but a little more involved because you'll probably have to create an admin screen to edit the configuration.
Let me know if you need examples of these and I can link you.

Related

Umbraco, Azure deployment slots and Connection Strings

We're trying to leverage Azures deployment slots for an Umbraco site we've built.
By default Umbraco uses a DSN defined in the connectionStrings sections of the web.config and we want it to use the connection string for the deployment slot it's sitting in.
What we've tried
Azure deployment slots put all defined app settings (and connection strings) into environment variables and to access them we can use Environment.GetEnvironmentVariable() which works but there doesn't seem to be a way to tell Umbraco to do this.
So in OnApplicationInitialized() (in /App_Code/Core/UmbracoAppStart.cs) we loaded the connectionstring section from the web.config, grabbed the connstr from env vars, added the DSN to the connectionstring section and saved.
The correct connection string is grabbed and stored but this seems to recycle the app (due to a web.config change) and thus we just get timeouts. (Or Umbraco XML cache errors, or it takes 20 mins to load the page).
I know you can store the appsettings and connectionstrings sections in separate files. But the file attribute (that doesn't cause a recycle if the referenced file is changed) doesn't work on the connectionStrings section - only the configSource attribute and that DOES recycle if changed.
(from: ASP.NET web.config: configSource vs. file attributes)
Help
Has anyone found a way around this?
We simply need to get Umbraco to use the deployment slot connection string - not the one in webconfig.
I'm even willing to copy and paste blindly at the moment without understanding how it works - and I hate doing that :). But that's what happens when people agree when the client wants to go live just before Christmas...
You don't need to do any code to use Azure connection string or the app settings. Just give them the same keys/names as you have on your web.config and they will be used instead.
So if you have this on your web config:
<add name="umbracoDbDSN" connectionString="Server={server};Initial Catalog={db};Persist Security Info=False;User ID={user};Password={password};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=300;" providerName="System.Data.SqlClient" />
Your slot configuration should be this:
To replace an app setting just use the same key. So for this:
<add key="umbracoUseSSL" value="false" />
You'd use this:
If you want the setting to be slot specific you have to activate the Slot setting checkbox.

Using the same DbCompiledModel to create contexts against different types of database servers is not supported

I am learning ASP.NET MVC from tutorials of Microsoft :
http://www.asp.net/mvc/tutorials/getting-started-with-aspnet-mvc3/cs/accessing-your-model's-https://stackoverflow.com/editing-helpdata-from-a-controller
At the link above mentioned, while adding a controller named "MoviesController" , i am getting this error
"Unable to retreive metadata for 'MvcMovie.Models.Movie'. Using the same DbCompiledModel to create contexts against different types of database servers is not supported. Instead, create a separate DbCompiledModel for each type of server being used"
How can i fix that?
I had the same issue.
I switched providerName="System.Data.SqlServerCe.4.0" with providerName="System.Data.SqlClient", and it created the Controller and Views.
I found this: http://msdn.microsoft.com/en-US/library/ms171861.aspx
I followed the directions and added a reference to SQL Server Compact, but it still doesn't work.
I also tried commenting out the default SQL Server Express connection, but it still gave the same error when trying to add the controller.
I'm just going to use SQL Server Express. I will let you know if I have any issues.
I've found that using the following works:
(Assuming that you've got SQL Server Express or higher installed)
<add name="MovieDBContext" providerName="System.Data.SqlClient" connectionString="Data Source=(LocalDb)\v11.0;AttachDBFilename=|DataDirectory|MovieDB.sdf"/>
I think the reason of this error is because the VS is having some difficult to reuse your existing DBContext, while scaffolding. The VS try to use a property in DBContext with similar name of the Domain (like trying the Domain name concatenating "s"). If it cant find the property and your context already has a DBSet with you Domain class, it generates that error.
My solution was to create a new DBContext named "DeleteContext". After creating with success the controller and views, I have replaced the "DeleteContext" in my Controller to my existing one. Finally I deleted the "DeleteContext" class.
It works really fine.
That solution was not working for me, it blows on return View(db.Movies.ToList());
Instead use this :
<add name="MovieDBContext" connectionString="Data Source=|DataDirectory|Movie.sdf" providerName="System.Data.SqlServerCe.4.0"/>
and this:
public class MovieDBContext : DbContext
{
public MovieDBContext() : base("Movie") { }
public DbSet<Movie> Movies { get; set; }
}
I ran into the same error on another Microsoft ASP.NET MVC tutorial while using Visual Studio 2012 (http://www.asp.net/mvc/tutorials/mvc-music-store/mvc-music-store-part-5).
I decided that for the sake of completing the tutorial, it was easier to just use SQL Express in place of SQL CE.
What I did to resolve the issue was delete MvcMusicStore.sdf from Server Explorer, then deleted the same database file from App_Data in Solution Explorer.
I updated the connection strings section in the Web.config to use a Sql Express database (.mdf) in place of Sql CE (.sdf). For this particular tutorial, MusicStoreEntities is the name of the class that extends DbContext:
<connectionStrings>
<add name="MusicStoreEntities" connectionString="Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\MvcMusicStore.mdf;Integrated Security=True" providerName="System.Data.SqlClient"/>
</connectionStrings>
I built the solution, ran the site, and the database was regenerated for me. To add the database file back to the solution, click the "Show All Files" button in Solution Explorer, right-click the .mdf file in App_Data, and select "Include in Project."
Hi there's one solution that worked fine to me. In your Web.config, the tutorial told you to add the following line in the connectionStrings section:
<add name="SchoolContext" connectionString="Data Source=|DataDirectory|School.sdf" providerName="System.Data.SqlServerCe.4.0"/>
Do not delete it or change it!
When you're adding a new controller you can make this to avoid the error that is presenting:
Comment the line above.
Add the following line:
<'add name="SchoolContext" connectionString="Data Source=|DataDirectory|School.sdf" providerName="System.Data.SqlClient"/>
Save your solution
Add the controller
Before you run your app, uncomment the line you have commented (1), and comment the line you added (the one with the providerName="System.Data.SqlClient") (2).
This worked very fine to me, when adding the controllers.
Hope this could help you.
I just tryed a bit around and found the problem.
In the tutorial you are adding the following line in your web.config
<add name="MovieDBContext" connectionString="Data Source=|DataDirectory|Movies.sdf" providerName="System.Data.SqlServerCe.4.0"/>
It seems that there is a problem with it.. i just commented it out and used the SqlServer and it works for me. If you still want to use the SqlServerCe you need to take a look how to fix that problem.
If you're using VS 2012, you will need to also tell EF to use SQL Compact instead of localDb.
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlCeConnectionFactory, EntityFramework">
<parameters>
<parameter value="System.Data.SqlServerCe.4.0" />
</parameters>
</defaultConnectionFactory>
An easier way is to install the EF SQL Compact Nuget package.
For more details, check out this blog entry.
I ran into this same problem while working through the Contoso University asp.net MVC tutorial. It appears that the problem comes from mixing the SQL Server Compact connection strings with the Membership provider Sql Server connection.
I initially used hyperGeoMetric's fix, and that did work. Then I looked at the downloadable code's web.config and noticed some additional configuration.
If you add/replace the default parameter of entityFramework with this:
<parameters>
<parameter value="Data Source=(localdb)\v11.0; Integrated Security=True; MultipleActiveResultSets=True" />
</parameters>
add a system.data section like this:
<system.data>
<DbProviderFactories>
<remove invariant="System.Data.SqlServerCe.4.0" />
<add name="Microsoft SQL Server Compact Data Provider 4.0" invariant="System.Data.SqlServerCe.4.0" description=".NET Framework Data Provider for Microsoft SQL Server Compact" type="System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" />
</DbProviderFactories>
</system.data>
and modify the existing (default) DefaultConnection to look like this:
<add name="DefaultConnection" providerName="System.Data.SqlServerCe.4.0" connectionString="Data Source=|DataDirectory|aspnet-membership.sdf" />
After these modification, I was able to continue the tutorial using the Sql Server Compact edition.
Sam
I followed that tutorial too, and got the same issue. I deleted the connection string, then I was able to add the controller, and it created the other files. Then I added the same connection string again to the Web.config file, inserted it ecxactly to the same place where it was before. It solved my problem.
I'm learning this tutorial:http://mvcmusicstore.codeplex.com/. and got the same error. I found a item name "EntityFramework.SqlServerCompact" in the NuGet packages list, and install it, all things go fine! note, the packages dependent entityframework, you can view all the version here:https://www.nuget.org/packages/EntityFramework.SqlServerCompact. GOOD LUCK!
the path of data source is wrong , you can add a "\" before "Movie.sdf".
like this:
<add name="MovieDBContext" connectionString="Data Source=|DataDirectory|\Movie.sdf" providerName="System.Data.SqlServerCe.4.0"/>
I ran into the same problem in another solution. It appeared just after I introduced a parametrized call to the base constructor, like the one included in the default UsersContext class.
This fails
public class MovieDBContext : DbContext
{
public MovieDBContext() : base("DefaultConnection")
{
}
...
}
This works
public class MovieDBContext : DbContext
{
// No constructor here
...
}
It seems that naming the connection string in the constructor creates the error. I only have one connection string in my web.config, so DefaultConnection is still used, although I don't name it explicitly.

Move my Code First Entity Framework created database to the Default Membership Provider database

I have an ASP.NET MVC3 application that I have built and it has two databases.
One is in reference to the Default Membership Provider and was automatically created when I built the app.
The other is a database I have named SchoolContext and I would like all the information within SchoolContext to be stored in the same database as all the data in Default Membership Provider.
Here are my two connection strings:
<add name="ApplicationServices" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient" />
<add name="SchoolContext" connectionString="Data Source=|DataDirectory|School.sdf" providerName="System.Data.SqlServerCe.4.0" />
I tried renaming ApplicationServices to SchoolContext and then commenting out my second connection string and I can load up and "log in" using the default membership provider just fine, but whenever I need to queue the database for information that was stored within the original SchoolContext, I get errors stating that the model is nonexistant, etc. . . and the data is not built within the database that contains the information from the default membership provider.
I am using an initializer to rebuild the database if the model changes:
public class SchoolInitializer : DropCreateDatabaseIfModelChanges<SchoolContext>
And this works fine any time I edit something in the model and I am using the two database scheme. However, it is not working when I want to join the two databases into one.
I apparently do not know how to connect the two databases as what I am trying to do is not working, how should I appropriately set up mvc to put my SchoolContext into the same database as the information needed for the Default Membership Provider?
Two potential answers:
If I understand your intent you are trying to use two separate DB
Contexts and perform queries that join across both of them. This is
not possible in Entity Framework (as of this writing) though it may
be considered in the future. See this post for more specifics:
https://stackoverflow.com/a/8536400/941058
If you need to join up information you will need to do it in memory
using LINQ. Essentially you'll need to store foreign key
information in either database and reference it when querying one or
the other.
If you are not trying to cross-context join but instead just want to use the Default Membership Provider with EF consider this:
http://efmembership.codeplex.com/

What creates a Web.config appSettings entry?

I am fairly green at ASP.NET coding, even though I have done very basic tasks for a while.
Recently, I have been assigned our company's website, so I am learning more of the details.
I downloaded the project from Source Safe, and I am making changes in the code.
A co-worker and I were looking at the Web.config file, and noticed this under the <configuration> section:
<appSettings>
<add key="HR_EMAIL" value="myEmailAddress#work.com"/>
<add key="APP_MODE" value="TEST"/>
<!-- PROD is the production value for ssl pages -->
<add key="HR_EMAIL_SITE_A" value="myEmailAddress#work.com"/>
<add key="HR_EMAIL_SITE_B" value="myEmailAddress#work.com"/>
</appSettings>
where myEmailAddress#work.com used to list my actual email address.
My co-worker said, "Oh, you've changed it and removed my email address."
Uh, no I have not! I could care less if these people email me!
I'm guessing something configured on my local machine (maybe in machine.config) went in and updated these values whenever I rebuilt the project.
I have used a walkthrough recently published by Microsoft (Walkthrough: Creating a Web Site with Membership and User Login), but it was in a different project.
What changed these values? Surely I did not do this in my sleep!
Chances are that someone committed these values to source control.
You got the latest value - possibly your workmate has not updated this file in a while.
Take a look at the file history in Source Safe to see what happened with this value.
section, as the name suggests, is specific to application to store custom settings. Before ASP .NET 2, this section was used to store things like connection string used by the web application.
In you case, I am guessing that, you have an admin site/system that is writing out your email adress to app.config.
I have seen another scenario, where setting will be updated by the build/release script.
You'll likely find that due to differences in enviroments, in most cases you don't deploy a web.config from enviroment to enviroment. You wouldn't want test settings, like connection strings, emails, etc getting propigated to production.
When you're likely finiding is the config in VSS is a local testing copy and the production copy has different values.

How to set up new Membership and Session providers to run in Windows Azure? Using MVC3 and Web Role

I've read about the New Membership and Session providers, and the information in this article is that just changing the connectionStrings the database would build itself magically in my SQL Azure Database.
So, I first changed my connection to:
<add name="Project" connectionString="Server=tcp:xxxxxxxx.database.windows.net,xxxx;Database=xxxxx;User ID=xxxxxxxxxxx;Password=xxxxxxxxxxx;Trusted_Connection=False;Encrypt=True;" providerName="System.Data.SqlClient" />
When I tried to debug my app with the emulator, I got the error:
EFProviders require MultipleActiveResultSets=True for System.Data.SqlClient connection strings.
I researched about this error and realized that it was something related to Entity Framework. I tried to make an Entity Framework connection string with no success. In addtion, I read somewhere that SQL Azure doesn't support MultipleActiveResultSets.
So, I have 2 questions:
Is it true that if I run the application and register any user via website interface the Membership and Session tables, views and procedures will magically build in my SQL Azure database?
What am I missing to make my app run?
Detail: I didn't add any new item in Models folder. It's only AccountModels class, as it generates from MVC3 template.
Fortunatelly now I have the 2 answers:
Yes, it's true and very easy!
The mistake I did first time it was try to put MultipleActiveResultsSet as a new attribute in the connectionString line. The code have to be like this:
<add name="Project" connectionString="Server=tcp:xxxxxxxx.database.windows.net,xxxx;Database=xxxxx;User ID=xxxxxxxxxxx;Password=xxxxxxxxxxx;Trusted_Connection=False;Encrypt=True;**MultipleActiveResultSets=True**" providerName="System.Data.SqlClient" />
Edited
Now I found out other way to do that with AppFabric Caching for Session State. Just follow this tutorial.

Resources