OK, Custom Policy Based Authorization in ASP.NET Core. I kinda of understood the idea of this new identity framework, but still not 100% clear what you can achieve with this. Assuming we have an Action in HomeController called List. This action will query and display a list of products from the database. The users that must access this list must be part of the Marketing division. Therefore in our policy we check if user has a claim called Division and the value of that is Marketing. If yes then he will be allowed to see the list otherwise not. We can decorate our action like this:
[Authorize(Policy = "ProductsAccess")]
public IActionResult List()
{
//do query and return the products view model
return View();
}
All good with this. It will work perfectly.
Scenario 1: What if I want to add the policy at the product level, and based on the policy the user will see only the products from his division. So marketing guy will see his products, R&D will see his and so forth. How can I achieve that? Can it be done with a policy? If yes how?
Scenario 2: What about access at the field level? Let's say maybe I want to hide certain fields? Example: all products will have certain columns that must be visible to Managers and hidden to the rest of users? Can this be done using custom policies? If yes how?
For Scenario 1 you can use resource based authorization.
In essence, you'd inject IAuthorizationService into your service or controller, then and have one or more authorization handlers which derive form AuthorizationHandler<TRequirement, TDocument> and then call
if(await _authorizationService.AuthorizeAsync(User, document, "MyPolicy"))
{
// Success, user has access to it
}
Downside: You have to fetch all products from database, then filter in memory, so it will work well for single documents or smaller data, where you don't need pagination. Pagination will break it, even on smaller data (i.e. if you request 50 products, but user don't have access to 40 of them, only 10 will be returned, despite the page size being 50).
an alternative will be possible with EF Core 2.0 (that's if you use EF Core as your ORM). You can add global filters, which will applied to all queries to a certain entity.
For more information, see Entity Framework Core 2.0 Announcement blog post:
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public int TenantId {get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasQueryFilter(p => !p.IsDeleted &&
p.TenantId == this.TenantId );
}
}
It may or may not be suitable for your case, it depends if you have a row-level data you can use (i.e. some kind of "resource owner" field).
Scenario 2 isn't possible out of the box with Identity as far as I know, you'd have to implement something on your own, but it's a very complex topic (if you ever worked with Dynamics CRM, you know what I mean).
Update
Just for an quick implementation, you can try to wrap your response around an ExpandoObject (that's what's used underlying when you use dynamic keyword) and the iterate over it, removing properties the user doesn't have access to before returning it from the controller action or write an Authorization filter, which will automatically do that for specific or all controllers.
For an rough idea (on how to construct/use the expando object), see my answer here.
I don't think policies were designed to solve the cases you have. I'm uncertain if it's possible, and even if it would, I feel like the code itself would suffer from the complexity and confusion it would bring. I wouldn't give my authorization filters that much responsibility.
As for your second scenario, you could just skip outputting certain data in your view based on the role, claim or whatever of the current user. Seems unnecessarily complex to solve that with policies.
Use policies for what it was made for, authorizing if the user is allowed to even run a method. Any differences to what is being returned? Handle it in the normal code flow.
Related
We are at the design stage of developing an application.
We decided to implement DDD oriented design.
Our application has a service layer.
Each method in the service has its own task.
Sample
let's consider a user service.
This method gets all users
public User GetAll()
{
//codes
}
Now if I want to sort all users by name.
Option-1
Use another method
public User GetAllAndOrderByAsc()
Or
public User GetAllAndOrderByDesc()
Different method for each different situation.
it doesn't look good at all
Option-2
Continue query at Api or Application level
public IQueryable<User> GetAll()
{
//codes
}
Api or Application Level
var users = from u in _userService.GetAll()
select u;
switch (sortOrder)
{
case "name_desc":
users = users.OrderByDescending(u => u.Name);
break;
case "name_asc":
users = students.OrderBy(u => u.Name);
break;
default:
users = students.OrderBy(u => u.Name);
break;
}
The options that come to my mind are limited to these.
am I making a mistake somewhere or couldn't grasp the logic. would you help me?
You're not crazy: this is a common problem, and the options you've listed are legitimate, each with pros and cons, and commonly used in practice.
Depending on the context of your app, it's theoretically possible to expose the IQueryable<> all the way to the presentation layer (using OData if you're running a web service, e.g.). But I find that it's wiser to exercise a little more control over the API to avoid the possibility of having someone hit the endpoint with a request that basically downloads your entire database.
In your service layer, you want to think about what use cases you really expect people to use.
It's rare for someone to literally need to see all the users: that might only happen when it's part of an export process, and rather than returning a collection you might want to return an IObservable that pushes values out as they emerge in a stream from the database.
More often, you're going to want to get a page of users at a time to show in the UI. This will probably need to have sorting and searching applied to it as well.
You'll almost certainly want a way to get one user whose ID you already know (maybe one that was picked from the paged list).
You might also expect to need to query based on specific criteria. Maybe people should typically only see "Active" users. Maybe they should be thinking in terms of specific groupings of users.
In my experience, it has worked well to make my Service layer represent these Domain-level assumptions about how the application should think about things, and write interfaces that support the use cases you actually want to support, even if that means you often find yourself adding new Service methods to support new features. For example:
Task<IReadOnlyCollection<User>> GetPageOfActiveUsersAsync(
PagingOptions pagingOptions,
SortOptions sortOptions,
string searchTerm,
IReadOnlyCollection<int> organizationIds);
Task<User> GetActiveUserByUsernameAsync(string username);
Task<User> GetActiveUserByIdAsync(int userId);
IObservable<User> GetAllUsersForAuditExport();
The project I currently work on uses a class like this:
public class SortSettings
{
public string SortField { get; set; }
public SortDirection SortDirection { get; set; } = SortDirection.Asc;
}
Then, depending on what we're querying, we may use reflection, a switch statement, or some other mechanism to pick a SortExpression (Expression<Func<TSource, TResult>>), and then apply the sort like this:
query = sortOptions.SortDirection == SortDirection.Asc
? query.OrderBy(sortExpression)
: query.OrderByDescending(sortExpression);
That logic is fairly easy to extract out into a separate method if you find you're using it in a lot of places.
You'll likely find other common features like Searching and Paging require a similar approach.
I'm creating a Web API with users having different roles, in addition as any other application I do not want User A to access User B's resources. Like below:
Orders/1 (User A)
Orders/2 (User B)
Of course I can grab the JWT from the request and query the database to check if this user owns that order but that will make my controller Actions' too heavy.
This example uses AuthorizeAttribute but it seems too broad and I'll have to add tons of conditionals for all routes in the API to check which route is being accessed and then query the database making several joins that lead back to the users table to return if the request Is Valid or not.
Update
For Routes the first line of defense is a security policy which
require certain claims.
My question is about the second line of defense that is responsible to
make sure users only access their data/resources.
Are there any standard approaches to be taken in this scenario ?
The approach that I take is to automatically restrict queries to records owned by the currently authenticated user account.
I use an interface to indicate which data records are account specific.
public interface IAccountOwnedEntity
{
Guid AccountKey { get; set; }
}
And provide an interface to inject the logic for identifying which account the repository should be targeting.
public interface IAccountResolver
{
Guid ResolveAccount();
}
The implementation of IAccountResolver I use today is based on the authenticated users claims.
public class ClaimsPrincipalAccountResolver : IAccountResolver
{
private readonly HttpContext _httpContext;
public ClaimsPrincipalAccountResolver(IHttpContextAccessor httpContextAccessor)
{
_httpContext = httpContextAccessor.HttpContext;
}
public Guid ResolveAccount()
{
var AccountKeyClaim = _httpContext
?.User
?.Claims
?.FirstOrDefault(c => String.Equals(c.Type, ClaimNames.AccountKey, StringComparison.InvariantCulture));
var validAccoutnKey = Guid.TryParse(AccountKeyClaim?.Value, out var accountKey));
return (validAccoutnKey) ? accountKey : throw new AccountResolutionException();
}
}
Then within the repository I limit all returned records to being owned by that account.
public class SqlRepository<TRecord, TKey>
where TRecord : class, IAccountOwnedEntity
{
private readonly DbContext _dbContext;
private readonly IAccountResolver _accountResolver;
public SqlRepository(DbContext dbContext, IAccountResolver accountResolver)
{
_dbContext = dbContext;
_accountResolver = accountResolver;
}
public async Task<IEnumerable<TRecord>> GetAsync()
{
var accountKey = _accountResolver.ResolveAccount();
return await _dbContext
.Set<TRecord>()
.Where(record => record.AccountKey == accountKey)
.ToListAsync();
}
// Other CRUD operations
}
With this approach, I don't have to remember to apply my account restrictions on each query. It just happens automatically.
Using [Authorize] attribute is called declarative authorization. But it is executed before the controller or action is executed. When you need a resource-based authorization and document has an author property, you must load the document from storage before authorization evaluation. It's called imperative authorization.
There is a post on Microsoft Docs how to deal with imperative authorization in ASP.NET Core. I think it is quite comprehensive and it answers your question about standard approach.
Also here you can find the code sample.
To make sure User A cannot view Order with Id=2 (belongs to User B). I would do one of this two things:
One:
Have a GetOrderByIdAndUser(long orderId, string username), and of course you take username from the jwt.
If the user does't own the order he wont see it, and no extra db-call.
Two:
First fetch the Order GetOrderById(long orderId) from database and then validate that username-property of the order is the same as the logged on user in the jwt.
If the user does't own the order Throw exception, return 404 or whatever, and no extra db-call.
void ValidateUserOwnsOrder(Order order, string username)
{
if (order.username != username)
{
throw new Exception("Wrong user.");
}
}
You can make multiple policies in the ConfigureServices method of your startup, containing Roles or, fitting to your example here, names, like this:
AddPolicy("UserA", builder => builder.RequireClaim("Name", "UserA"))
or replace "UserA" with "Accounting" and "Name" with "Role".
Then restrict controller methods by role:
[Authorize(Policy = "UserA")
Of course this in on the controller level again, but you don't have to hack around tokens or the database. This will give your a direct indicator as to what role or user can use what method.
Your statements are wrong, and you are also designing it wrong.
Over optimization is the root of all evil
This link can be summarized in "test the performance before claiming it won't work."
Using the identity (jwt token or whatever you configured) to check if the actual user is accessing the right resource (or maybe better to serve just the resources it owns) is not too heavy.
If it becomes heavy, you are doing something wrong.
It might be that you have tons of simultaneous access and you just need to cache some data, like a dictionary order->ownerid that gets cleared over time... but that doesn't seem the case.
about the design: make a reusable service that can get injected and have a method to access every resource you need which accept an user (IdentityUser, or jwt subject, just the user id, or whatever you have)
something like
ICustomerStore
{
Task<Order[]> GetUserOrders(String userid);
Task<Bool> CanUserSeeOrder(String userid, String orderid);
}
implement accordingly and use this class to sistematically check if the user can access the resources.
The first question you need to answer is "When I can make this authorisation decision?". When do you actually have the information needed to make the check?
If you can almost always determine the resource being accessed from route data (or other request context), then an policy with matching requirement and handler may be appropriate. This works best when you are interacting with data clearly silo'd out by resource - as it doesn't help at all with things like filtering of lists, and you'll have to fall back to imperative checks in these cases.
If you can't really figure out whether a user can fiddle with a resource until you've actually examined it then you are pretty much stuck with imperative checks. There is standard framework for this but it isn't imo as useful as the policy framework. It's probably valuable at some point to write an IUserContext which can be injected at the point you query you domain (so into repos/wherever you use linq) which encapsulates some of these filters (IEnumerable<Order> Restrict(this IEnumerable<Order> orders, IUserContext ctx)).
For a complex domain there won't be an easy silver bullet. If you use an ORM it may be able to help you - but don't forget that navigable relationships in your domain will allow code to break context, particularly if you haven't been strict on trying to keep aggregates isolated (myOrder.Items[n].Product.Orderees[notme]...).
Last time I did this I managed to use the policy-based-on-route approach for 90% of cases, but still had to do some manual imperative checks for the odd listing or complex query. The danger in using imperative checks, as I'm sure you are aware, is that you forget them. A potential solution for this is to apply your [Authorize(Policy = "MatchingUserPolicy")] at controller level, add an additional policy "ISolemlySwearIHaveDoneImperativeChecks" on the action, and then in your MatchUserRequirementsHandler, check the context and bypass the naive user/order matching checks if imperative checks have been 'declared'.
I'm building some sort of a complicated inventory system, accepting offers from suppliers, etc.
Normally I would have basic permissions like Read/Write/Edit/Delete and I can easily update read and write bool variables on each page to check if true do this, else do that.
But that's not the case. I have some permissions like (Owner, SamePseudoCity) which respectively means that the user is allowed access to the records he only created, and the other means returning the records that belongs to the PsuedoCity as the user.
Currently the UI has local variables with the applicable permissions and when the UI request some data from the db it calls the BL that first get the permissions the user is entitled to and bind them to UI/Page local variables.
Also it checks if the permission list contains 'Owner' then it will get the records created by the UserID, if it contains 'SamePseudoCity' it will get all the records in the same city.
I'm not sure if this is a good design and if I can modify it. I know there is no right or wrong here but there's smelly-design, okay-design, and better-design. So I'm just looking for some ideas if someone implemented this before.
It took a lot to explain my problem if it's still not clear please let me know and I can post some snippets from my code.
Getting to grips with your requirements
What you need is an authorization framework that is capable enough of handling your requirements. In your post, you mention that you have
Permissions e.g. Read/Write/Edit/Delete
other parameters e.g. Owner, SamePseudoCity that mean different things:
user is allowed access to the records he only created
returning the records that belongs to the PsuedoCity as the user.
Attribute-based access control
You need to turn to [tag: ABAC] (attribute based access control) which will give you the ability to define your requirements using attributes (key-value pairs) and policies. The policies are maintained and evaluated inside a 3rd party engine called the Policy Decision Point (PDP).
ABAC is a standard defined by NIST. It is an evolution of RBAC (role-based access control). XACML, the eXtensible Access Control Markup Language is an implementation of ABAC,
You can apply abac to different layers in your architecture from the UI (presentation tier) to the business tier and all the way down to your data tier.
Policies are expressed in alfa or xacml.
Example:
namespace stackoverflow{
You first define your attributes
namespace user{
attribute identifier{
category = subjectCat
id = "user.identifier"
type = string
}
attribute city{
category = subjectCat
id = "user.city"
type = string
}
}
namespace item{
attribute owner{
category = resourceCat
id = "item.owner"
type = string
}
attribute city{
category = resourceCat
id = "item.city"
type = string
}
}
attribute actionId{
category = actionCat
id = "actionId"
type = string
}
Then you define the policies that use these attributes
/**
* Control access to the inventory
*/
policy inventory{
apply firstApplicable
/**
* Anyone can view a record they own
*/
rule viewRecord{
target clause actionId == "view"
condition user.identifier == item.owner
permit
}
/**
* Anyone can view a record that is in the same city
*/
rule viewRecordsSameCity{
target clause actionId == "view"
condition user.city == item.city
permit
}
}
}
Next step
You then need to deploy a Policy Decision Point / Policy Server. You can choose from several:
Axiomatics Policy Server (disclaimer I work for Axiomatics)
SunXACML
Oracle Entitlements Server
WSO2 Identity Server
If you want to apply your policies to both the UI and the database, then you can use a feature called dynamic data masking via an Axiomatics product called Data Access Filter MD.
Update
The OP later commented the following
ABAC" never heard of it and it sounds brilliant but I assume I need access to a dedicated server or a VPS to install the PDP, right ? .. I know this can be too much to ask but I have 3 questions, Can I programmatically change the rules ? Is it possible to implement this scenario where Every product has a pseudo city and every manager also has a pseudo city and managers are only allowed access to their own city products ? is it possible do simple read/write/edit rules and hide and show UI based on that ? –
So first off, let's start with the ABAC architectural diagram:
The PEP is the policy enforcement point, the piece responsible for protecting your apps, APIs, and databases. It enforces authorization decisions.
The PDP is the policy decision point, the piece responsible for evaluating policies and reaching decisions. It processes requests it receives from the PEP and returns authorization decisions (Permit, Deny).
The PAP is the policy administration point where you define and manage your policies
the PIP is the policy information point. It's the interface the PDP uses to connect to third party attribute sources e.g. A user LDAP, a database, or a web service. The PDP uses the PIP when it needs to know more about the user or the resource.
I assume I need access to a dedicated server or a VPS to install the PDP, right ?
Yes, you would install the PDP on a server (or the cloud). It becomes part of your infrastructure.
Can I programmatically change the rules?
Yes you can. The Axiomatics PAP has an API that you can use to upload, export, and create policies programmatically.
Is it possible to implement this scenario where Every product has a pseudo city and every manager also has a pseudo city and managers are only allowed access to their own city products ?
Yes, that is actually what I wrote in my original example and that is the beauty of ABAC. You write a single policy that works no matter the number of cities: A user can view a record if user.city==record.city
is it possible do simple read/write/edit rules and hide and show UI based on that ?
Yes, you can use any number of attributes in your policies. So for instance you could have:
Deny users access to records outside their city
users can view records
a user can edit a record they own
a user can approve a record they do not own
You can use the logic to drive authorization in your UI or your business layer or even at the data layer. So you could ask the PDP:
Can I show the Edit button?
Can I show the Details button?
Can I show the Delete button?
In our MVC project we are attempting to make everything as generic as possible.
Because of this we want to have one authentication class/method which covers all our methods.
As a example: The following code is a MVC class which can be called to from a client
public class Test
{
public void Test()
{
}
public int Test2(int i)
{
return i
}
public void Test3(string i)
{
}
}
A customer of our webservice can use a service reference to get access to Test(), Test2() and Test3().
Now i'm searching for a class, model, interface or anything else which I can use to alter the access to the method (Currently using [PrincipalPermission] attribute) as well as alter the parameter value.
Example:
Customer A calls Test2(150)
The class/method checks whether Customer A has access to Test2. The class/method validates the user but notices that the user does not have access to 150. He only has access to 100.So the class/method sets the parameter to 100 and lets it follow through on it's journey.
Customber B class Test()
The class/method checks whether Customer B has access to Test. After validation it shows that the user does not have access so it throws a SecurityException.
My question:
In what class, interface, attribute or whatever can I best do this?
(ps. As example i've only used authentication and parameter handling, but we plan to do a lot more in this stage.)
Edit
I notice most, if not all, assume I'm using actionResults. So i'd like to state that this is used in a webservice where we provide our customers with information from our database. In no way will we come in contact with a ActionResult during the requests to our webservice. (Atleast, not our customers)
Authentication can also be done through an aspect. The aspect oriented paradigm is designed to honor those so-called cross-cutting concerns. Cross-cutting concerns implemented in the "old-fashioned" oo-way make your business logic harder to read (like in Nick's example above) or even worse to understand, because they don't bring any "direct" benefit to your code:
public ActionResult YourAction(int id) {
if (!CustomerCanAccess(id)) {
return new HttpUnauthorizedResult();
}
/* the rest of your code */
}
The only thing you want here is /* the rest of your code */ and nothing more.
Stuff like logging, exception handling, caching and authorization for example could be implemented as an aspect and thus be maintained at one single point.
PostSharp is an example for an aspect-oriented C# framework. With PostSharp you could create a custom aspect and then annotate your method (like you did with the PrincipalPermissionAttribute). PostSharp will then weave your aspect code into your code during compilation. With the use of PostSharp aspects it would be possible to hook into the method invocation authenticating the calling user, changing method parameters or throw custom exceptions (See this blog post for a brief explanation how this is implemented).
There isn't a built-in attribute that handles this scenario.
I find it's usually best to just do something like this:
public ActionResult YourAction(int id) {
if (!CustomerCanAccess(id)) {
return new HttpUnauthorizedResult();
}
/* the rest of your code */
}
This is as simple as it gets and easy to extend. I think you'll find that in many cases this is all you need. It also keeps your security assertions testable. You can write a unit test that simply calls the method (without any MVC plumbing), and checks whether the caller was authorized or not.
Note that if you are using ASP.Net Forms Authentication, you may also need to add:
Response.SuppressFormsAuthenticationRedirect = true;
if you don't want your users to be redirected to the login page when they attempt to access a resource for which they are not authorized.
Here's how I've made my life simpler.
Never use simple values for action arguments. Always create a class that represents the action arguments. Even if there's only one value. I've found that I usually end up being able to re-use this class.
Make sure that all of teh properties of this class are nullable (this keeps you from running into default values (0 for integers) being automatically filles out) and thatallowable ranges are defined (this makes sure you don't worry about negative numbers)
Once you have a class that represents your arguments, throwing a validator onto a property ends up being trivial.
The thing is that you're not passing a meaningless int. It has a purpose, it could be a product number, an account number, etc. Create a class that has that as a property (e.g An AccountIdentifier class with a single field called 'id). Then all you have to do is create a [CurrentUsedCanAccessAccountId] attribute and place it on that property.
All your controller has to do is check whether or not ModelState.IsValid and you're done.
There are more elegant solutions out there, such as adding an action filter to the methods that would automatically re-direct based on whether or not the user has access to a specific value for the parameter, but this will work rather well
First, just to say it, that your own methods are probably the most appropriate place to handle input values (adjust/discard) - and with the addition of Authorize and custom filter actions you can get most done, and the 'MVC way'. You could also go the 'OO way' and have your ITest interface, dispatcher etc. (you get more compiler support - but it's more coupled). However, let's just presume that you need something more complex...
I'm also assuming that your Test is a controller - and even if it isn't it can be made part of the 'pipeline' (or by mimicking what MVC does), And with MVC in mind...
One obvious solution would be to apply filters, or action filters via
ActionFilterAttribute
Class
(like Authorize etc.) - by creating your own custom attribute and
overriding OnActionExecuting etc.
And while that is fine, it's not going to help much with parameters manipulation as you'd have to specify the code 'out of place' - or somehow inject delegates, lambda expressions for each attribute.
It is basically an interceptor of some sort that you need - which allows you to attach your own processing. I've done something similar - but this guy did a great job explaining and implementing a solution - so instead of me repeating most of that I'd suggest just to read through that.
ASP.NET MVC controller action with Interceptor pattern (by Amar, I think)
What that does is to use existing MVC mechanisms for filters - but it exposes it via a different 'interface' - and I think it's much easier dealing with inputs. Basically, what you'd do is something like...
[ActionInterceptor(InterceptionOrder.Before, typeof(TestController), "Test1")]
public void OnTest1(InterceptorParasDictionary<string, object> paras, object result)
The parameters and changes are propagated, you have a context of a sort so you can terminate further execution - or let both methods do their work etc.
What's also interesting - is the whole pattern - which is IOC of a
sort - you define the intercepting code in another class/controller
all together - so instead of 'decorating' your own Test methods -
attributes and most of the work are placed outside.
And to change your parameters you'd do something like...
// I'd create/wrap my own User and make this w/ more support interfaces etc.
if (paras.Count > 0 && Context.User...)
{
(paras["id"] as int) = 100;
}
And I'm guessing you could further change the implementation for your own case at hand.
That's just a rough design - I don't know if the code there is ready for production (it's for MVC3 but things are similar if not the same), but it's simplistic enough (when explained) and should work fine with some minor adjustments on your side.
I'm not sure if I understood your question, but it looks like a model binder can help.
Your model binder can have an interface injected that is responsible for determining if a user has permissions or not to a method, and in case it is needed it can change the value provided as a parameter.
ValueProviders, that implement the interface IValueProvider, may also be helpful in your case.
I believe the reason you haven't gotten ay good enough answer is because there are a few ambiguities in your question.
First, you say you have an MVC class that is called from a client and yet you say there are no ActionResults. So you would do well to clarify if you are using asp.net mvc framework, web api, wcf service or soap (asmx) web service.
If my assumption is right and you are using asp.net mvc framework, how are you defining web services without using action results and how does your client 'call' this service.
I am not saying it is impossible or that what you may have done is wrong, but a bit more clarity (and code) would help.
My advice if you are using asp.net mvc3 would be to design it so that you use controllers and actions to create your web service. all you would need to do would be to return Json, xml or whatever else your client expects in an action result.
If you did this, then I would suggest you implement your business logic in a class much like the one you have posted in your question. This class should have no knowledge of you authentication or access level requirements and should concentrate solely on implementing the required business logic and producing correct results.
You could then write a custom action filter for your action methods which could inspect the action parameter and determine if the caller is authenticated and authorized to actually access the method. Please see here for how to write a custom action filter.
If you think this sounds like what you want and my assumptions are correct, let me know and I will be happy to post some code to capture what I have described above.
If I have gone off on a tangent, please clarify the questions and we might be one step closer to suggesting a solution.
p.s. An AOP 'way of thinking' is what you need. PostSharp as an AOP tool is great, but I doubt there is anything postsharp will do for you here that you cannot achieve with a slightly different architecture and proper use of the features of asp.net mvc.
first create an attribute by inheriting from ActionFilterAttribute (system.web.mvc)
then override OnActionExecuting method and check if user has permission or not
this the example
public class CheckLoginAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!Membership.IslogedIn)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary
{
{ "area",""},
{ "action", "login" },
{ "controller", "user" },
{ "redirecturl",filterContext.RequestContext.HttpContext.Request.RawUrl}
});
}
}
}
and then, use this attribute for every method you need to check user permission
public class Test
{
[ChecklLogin]
public void Test()
{
}
[ChecklLogin]
public int Test2(int i)
{
return i
}
[ChecklLogin]
public void Test3(string i)
{
}
}
So, I've successfully implemented my own MembershipProvider and that's working exactly as I intended it to.
I've decorated my controller actions with the [Authorize] attribute, and this is also work exactly as I want.
My question is, where should I put code that determines "how much" access a use has?
[Authorize] simply means that the current user is a valid user of the system. The current user may have UserAccess = 1, or 2, or 4, or 8 or whatever. Would I perform this check in the Controller method? or should I pass the user into my Repository class, and have the respository class return only those records which the current user has access to?
In other words, what's the best way to seperate this concern? since its related to authentication. I'm thinking that I should pass the user into the Repository.GetData() method, and have it perform the necessary lookups.
The second part of this queston, is how do I limit access on a specific View, based on the user? For example if the current user has UserAccess = 2, I want to omit some fields, but if its UserAccess = 4 I want to show all fields.
Update
After a little bit more research, it looks like I could potentially kill two birds with one stone, if I implement my own RoleProvider -- I see how I can do this to restrict data access on the controller [Authorize(Roles = "Admin)], and it looks like the best option for me. How can I use this to render my View differently based on Role? Would I make seperate Views and return the correct view from the Controller? Or make one View with in-line C#?
First part of your question: keep the controller thin and put the access level code in your repository class/model. Second part of your question: you can create different views for each access level. Or you can put logic in the view itself to check the access level of the user, but that's fairly hackish and not secure. I would just have the view not render any fields that come back null/empty from the model.
You can make your own AuthorizeAttribute that takes the UserRole needed to execute this action in parameter.
[CustomAuthorize(UserRole.Administrator)]
public ActionResult YourAction()
{
}