I’m starting to develop a ASP NET MVC 3 application and I have sought to follow some good DDD pratices. I have the following situation which would like an opinion.
One of the System features is the creation of activities which one or more users of the system will participate, a meeting for example. Any user with a certain access profile can create new activities, perhaps only the user who create an activity can change her. The question is:
What’s the correct place to insert this rule?
-> In every setter of “Activity” entity? Seems break the DRY.
-> In the repository at the moment of saving the changes? In this case , what would be the correct moment to pass the user permissions?
One more parameter to this method? In the class constructor(In my model, the repositories are interfaces, if I adopt this option,
the dependency would be explicit only in the infrastructure layer where the repositories are implemented?)
-> Controller? It seems to collaborate with an anemic model.
By the way, questions abound... What do you think about?
If you are using ASP.NET Membership you can take advantage of the Roles and Profile providers and use Authorize attributes to restrict access to the actual views where creation or editing occur. For example, the create action could be:
[Authorize(Roles="Activity Admin")]
public ActionResult CreateActivity()
{
return View();
}
Where "Activity Admin" is your creator role. Then your edit could look something like this:
[Authorize(Roles="Activity Admin")]
public ActionResult EditActivity(int id)
{
Activity activity = ActivityRepository.GetActivityByID(id);
if (activity.CreatorID != CurrentUser.ID)
{
return RedirectToAction("Activities");
}
return View(activity);
}
That if statement does the check to make sure the current logged-in user was the user who actually created the activity. The CurrentUser.UserID can be replaced with whatever method you use to retrieve the current logged-in user's unique ID. I usually use the ProfileBase class to implement a class that allows me to track the current user's info. The link below to another SO question shows how you can do that.
How can i use Profilebase class?
Related
I'm developing some website which is a kind of online workplace, there will be some users and some ongoing computer programming projects, and each user can have multiple roles, for example one particular user can be a project manager for an project and a developer for another project. naturally the project manager has more authority than the developer in the project. my question is how to manage this in my code neatly? I was going to use my custom Role Provider and use the Authorize attribute with this, but it's not sufficient , since I'd need the project Id plus the user Id to find the role of user in an specific project.
First all you will have to create additional tables for your extended role management like projects and there relationship with the users in context of operations, which might be your controller's actions.
One way of doing is to create your own table for roles. In that case you will only use only Asp net membership users, but it all depends your requirements.
Secondly you have to handle it in MVC, In my opinion the best way is to implement it through your own custom Authorization attribute, and decorate your controller's actions with your custom authorization attribute instead of [Authorization] attribute.
Its very simple.
[CustomAuthorize]
//[Authorize]
public ActionResult GetProjectTasks(string projectname)
{
}
For that you have to inherent your class from FilterAttribute and also have to implement IAuthorizationFilter interface.
public void OnAuthorization(AuthorizationContext filterContext)
{
HttpCookie authCookie = filterContext.HttpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
var identity = new GenericIdentity(authTicket.Name, "Forms");
var principal = new GenericPrincipal(identity, new string[] { authTicket.UserData });
filterContext.HttpContext.User = principal;
}
var controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
var action = filterContext.ActionDescriptor.ActionName;
var user = filterContext.HttpContext.User;
var ip = filterContext.HttpContext.Request.UserHostAddress;
var isAccessAllowed = CustomAuthenticationLogic.IsAccessAllowed(controller, action, user, ip);
if (!isAccessAllowed)
{
// Code if user is authenticated
FormsAuthentication.RedirectToLoginPage();
}
}
In the method OnAuthorization, you can get all the information which you might be require in your custom authorization logic like HttpContext, Controller name, Action name. You have to just call your custom authentication logic from this method.
Your custom authentication logic might look like the following.
public class CustomAuthenticationLogic
{
public static bool IsAccessAllowed(string controller, string action, IPrincipal user, string ip)
{
//
// Your custom logic here
//
}
}
I did some research a while ago and can assure you:
ASP.NET builtin features most probably wont help (there s no way to take into account things like project Id)
Role based access model is most suitable, there are different ways to implement it. AzMan suggested by Rusted is actually good, but managing context related rules may be difficult. For example: User A performing operation B in Project C, while its lets say Sunday. Take a look at azman.
Mixing you access rules with code is extreamly bad. Your security model does'nt need to be related to they way application works (ASP.NET MVC)
so this one is wrong:
var isAllowed = AccessControl.IsAccessAllowed(controller, action, user, ip);
it must look like:
var isAllowed = AccessControl.IsAccessAllowed(user, operation, context);
then you can use it whenever you like, In each action or wrap it as attribute.
where operation is "Login", "Post reply", "Read topics", etc. context is everithing else, like you "project id", "day of week", "user ip", etc
there are a lot of things could be written, like, role overlapping, context etc.
In short: Google for ".NET role based access model" probably it may be easier to write small custom security framework. Make it work with Users, Roles, Operations and Project Id
Operations are assigned to Roles,
Roles are assigned to users with defined project Id,
you can hardcode operations and roles, so in your DB will be only one small change: User to Roles mapping
If you have rules that are more complex and attributes are not enough, then you can calculate in your Controller if the user can access some features and add properties in your ViewModel that reflect access or no access to those features.
This way your view would be very slim, it would display stuff depending on those ViewModel boolean properties.
So, imagining your user can only read, you could have a property bool IsReadOnly that would be filles in the controller, depending on the authorization rules, and that would be used in the view to, for instance, generate labels instead of textboxes.
I like the main idea with AzMan is the concept of programming against operations.
Operations are very granular things that should have no overlap in usage and defined only by developer.
By grouping operations into tasks and tasks into roles and then mapping principals (users and groups) to roles, you have a very power model for defining authorization in your app. Since you program directly to the operations, your code doesn't need to know what roles a user has and that can be changed by the administrator at runtime. In fact, someone could define a completely different set of roles to consume the operations you use in your code and you wouldn't need to change any code at all. That's where the real power lies.
I don't mean "use AzMan in your app" (but maybe you should try). It is a powerful model, but it is also complex and is probably overkill for simple things. If you just have a role or two and the operations they protect don't overlap or aren't likely to change, then it
probably isn't warranted.
I would suggest you to create a custom Authorize filter by extending the built-in AuthorizeAttribute filter instead of implementing the IAuthorizationFilter interface. The built-in AuthorizeAttribute does many plumbing work that takes care of the cache problem and other stuff and if you are going to implement the interface you have to do all those work.
You have to override the AuthorizeCore method and there you have do all your role checking logic. The user id you have to store in session and project id you have to figure it out.
public override bool AuthorizeCore(HttpContextBase httpContext)
{
}
AuthorizeAttribute source code - http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/913d37658a44#src%2fSystem.Web.Mvc%2fAuthorizeAttribute.cs
Custom Authorize Attribute Example: http://msdn.microsoft.com/en-us/library/ee707357(v=vs.91).aspx
You can have groups based on roles.
Then Add different users to specific groups. Groups can be >
1) Admin Group
2) Developer Group
3) Project1-QA Group
4) Project2-Manager Group
Save the mapping of [user - group] and [group - projects], depending on your database design.
You can have as many roles(groups) for one user as you want.
A very simple approach - for site-wide access control, you can add an INT column to the user table and map each bit of that INT to an [flags] Enum - e.g. [Flags] enum Access { UpdateProjects, AddProjects }.
For per-project access control, create a table named e.g. ProjectAccessControl with three columns: ProjectID (foreign key to Project table), UserID (foreign key to User table) and Role (INT). The Role column is an INT, and each its bit should mean different boolean flag (as in previous example, you can map this to a enum in C#) and say that if the first bit is on, then user has rights to update description, if the second bit is on, user can change schedules and so on.
[Flags]
enum ProjectAccessRole
{
UpdateDescription,
ChangeSchedule,
etc...
}
In the code, you can test if the user's role has right to update schedule this way:
if( (intUserRole & ProjectAccessRole.ChangeSchedule)
== ProjectAccessRole.ChangeSchedule)
{
/*user has right*/
}
Then you can wrap this check into a simple function that takes two parameters, 1) role that is to be checked if it has 2) role. Then you just call HasRights(intUserRole, ProjectAccessRole.ChangeSchedule);.
I've been given the task of updating an asp system to MVC3. My main issue is that the current system has 2 different kinds of user, each user has their own user table, and in the current asp site, each user is defined by a number of different session held variables.
Because other systems use the two different user tables, I can't merge them. So what would be the best way forward?
I initally thought about keeping them seperate in the system; having a class type for each user which holds the seperate variables mirrored from their asp counterpart. On login, I could push user type to userdata in the auth cookie, then create an extension method on the IPrincipal to return the user type when required.
This feels a bit of a hack, and as the users will be viewing the same pages, there would be a lot of duplication of code.
Would it be better to create some form of facade before the user repository which would attach a role to a common user object which would identify the user type in the system? I could then check this role and pull out the data, that used to be stored in session variables, when needed.
If I would given a chance to do this I would do it in following way:
Implement asp.net membership provider and import all users into it.
Create two different roles for user types
Use profile provider to store additional properties from user type tables.
This way, you can use combination of role and profile to handle any situation related to authorization and restriction.
I would define an interface with methods common to both users, and have both user types implement the interface. So, if you have something like:
interface IUser { bool CanViewPage1(); }
class UserType1 : IUser { }
class UserType2 : IUser { }
Session["user"] = new UserType1();
Then you can do:
var user = (IUser)Session["user"];
For common operations:
if (user.CanViewPage1())
{
...
}
and for operations where you need the user object:
bool CanViewPage2(IUser user)
{
if(user is UserType1)
{
var ut1 = (UserType1)user;
...
} else if (user is UserType2)
{
var ut2 = (UserType2)user;
...
}
}
The last part can be done via extension methods as well, as you said.
Id suggest to use WIF with custom simple STS server and use claims for user types.
http://msdn.microsoft.com/en-us/library/ee748475.aspx
And for checking this stuff - use custom attribute, or simple - Identity.HasClaim("someclaim name or type").
Also this will standartize your approach for authentication/authorization, which can save some time later=)
If I was building this application from the ground up, i'd differentiate user type by role. With this in mind, I've created an anticorruption class between the User builder and reps. This injects/removes a role (id of 0) to distinguish user type. In future, the client hopes to merge tables, so this seemed the most sensible way forward for now.
We're currently rewriting our organizations ASP.NET MVC application which has been written twice already. (Once MVC1, once MVC2). (Thank god it wasn't production ready and too mature back then).
This time, anyhow, it's going to be the real deal because we'll be implementing more and more features as time passes and the testruns with MVC1 and MVC2 showed that we're ready to upscale.
Until now we were using Controller and Action authorization with AuthorizeAttribute's.
But that won't do it any longer because our views are supposed to show different results based on the logged in user.
Use Case: Let's say you're a major of a city and you login to a federal managed software and you can only access and edit the citizens in your city.
Where you are allowed to access those citizens via an entry in a specialized MajorHasRightsForCity table containing a MajorId and a CityId.
What I thought of is something like this:
Public ViewResult Edit(int cityId) {
if(Access.UserCanEditCity(currentUser, cityId) {
var currentCity = Db.Cities.Single(c => c.id == cityId);
Return View(currentCity);
} else {
TempData["ErrorMessage"] = "Yo are not awesome enough to edit that shizzle!"
Return View();
}
The static class Access would do all kinds of checks and return either true or false from it's methods.
This implies that I would need to change and edit all of my controllers every time I change something. (Which would be a pain, because all unit tests would need to be adjusted every time something changes..)
Is doing something like that even allowed?
That's pretty much what I'd do for a decent sized application.
I would return a generic error view return View("Error"); if the user doesn't have any access so you don't need to handle the an error message on every View.
Conceptually, this (the Controller) is where the logic determining what View to return should lie. It stops business logic bleeding into the View.
You could abstract out certain role-dependant parts of the page to partial views to keep the clutter down:
<% if (User.IsInRole("Admin")) { %>
Html.RenderPartial("AdminPanel");
<% } %>
Have you thought about writing your own Action Filter Attribute?
That way you could just decorate your controller with the attribute and save you a lot of copying and pasting, plus if it ever needs to be changed then its only done in one place.
If you get the source code for MVC from Codeplex you can see how the Authorize attribute is done and modify that.
What is the ultimate workaround?)
Desired scenarios are:
Multistep forms.
If a page has tabs on it, the tabs should persist their `viewstate'
Whatever navigation user has chosen, it shouldn't affect (more appropriately, bother) the conversational state management.
Those are just several aspects, but I find them much practically relevant.
I have worked once on Wizard kind of form in Asp.Net MVC and best thing to do in this case is to use Model/ModelBinding to keep track of form input.
We can create a chain of Controller Actions (for each steps) with output model of each serving as the input Model for the next Step (Action).
For example if we have three steps for creating a user. Then UserController can have actions for each steps.
public ActionResult Step1()
{
User user = new User();
return View(user);
}
[Post]
public ActionResult Step1(User user)
{
//perform business validation
RedirectToAction<UserController>(u => Step2(user));
}
After this Step2 action will take over with modified user from Step1 and can render its own view and so on.
You may also want to check out http://blog.maartenballiauw.be/post/2009/10/08/Leveraging-ASPNET-MVC-2-futures-ViewState.aspx. There's an Html.Serialize() helper in MVC Futures. The article refers to it as lightweight Viewstate, but it's effectively just a glorified wrapper around "serialize this object and store the base64 string in a hidden form field." If you need state to be associated with individual pages rather than an entire Session, this helper might be appropriate for your needs.
Not sure about a "workaround", but have you considered using AJAX and jQuery? Both would be appropriate based on the requirements you listed.
I'm workng on a new, green-field ASP.Net application. We're implementing a base page which all pages will be, er, based on. The application will be running under Integrate Windows Auth, so I'll have the user's account details. With these, I'll be going to several databases (in which the user will exist) to find out what roles they are assigned to in each db. I'll be holding the role yay/nay in a bool array, and key into it via an enum.
There will be a session object that will hold a few things, and the roles assigned for that user. I'm thinking of making the session object available as a property of the base page, as the code would be something like this:
public SessionObject MasterSessionObject
{
get
{
if (Session["SessionObject"] == null)
{
// Create session object, assign user name, etc.
// Do something with roles...
Session["SessionObject"] = sessionObject;
}
return (SessionObject)Session["SessionObject"]
}
}
In order to control what happens on the (sub-classed) page, I want to provide a CheckSecurity method - e.g. if the user is not authorised to a certain part of a page, it can be hidden / disabled, or they could be booted back to a "not yours" page. The logical place for it is the base page, but seeing as the base page is already exposing the SessionObject that holds the roles permissions, would it not make more sense to Create a DatabaseSecurity type object and have the check on that?
Dealing with the latter approach, I've used abstract base classes to get me so far: I have a DatabaseRoles abstract class which contains the bool array, and a method to retrieve the roles for the user. The concrete implementation holds an Enum (as previously mentioned) to key into the array (in the base class). The abstract class also has the CheckRole method which takes in an int, to which I'm intending use a cast of the enum...
The SessionObject contains several of these DatabaseRoles implementations, and essentially does away with the need for a CheckSecurity in the base page class, leading to code like this in the actual page:
if (MasterSessionObject.SampleDatabaseRoles.Check((int)SampleDatabaseRolesEnum.RoleView))
{
// Do something
}
But, I'm sure you'll agree, it looks sucky...
If there was a CheckSecurity method on the base page, it would have to take a concrete DatabaseRoles object, but also an enum of which role to check, which would also look sucky. And finally, there would be a requirement at a later date to add more databases and their security settings...
I'll add code tomorrow if required... :-s
I dunno, I'm not that thick, but I do have a hard time sometimes binding all this together...
Thank you,
Mike K.
IF you happen to use ASP.Net / ASP.Net MVC, I would say the best place to do this would be via a custom HTTP Module by handling the AuthenticateRequest method & continuing with the request only if the request has been authenticated. There are tons of excellent articles online for this code.
Also - have a look at the Roles & Memberships of ASP.Net - it is pretty good & generally satisfies most requirements or you are always free to extend it. Again - tons of articles on custom membership providers...
unless I am missing something - HTH.