I am using owin authentification on my MVC project.
I have Orders And Users(ApplicationUser) tables with navigation properties.
Every time I am querieng orders for authorized user i need to check user:
public class TestController : Controller
{
public ActionResult Index()
{
using (var ctx = new TestContext())
{
ctx.Orders.Where(o => o.User == User.Identity.Name).Where(...).ToList();
}
}
}
If i forget to check it somewhere, user will get access to all orders.
I want something like or some better way to do it:
[InterceptQuery]
public Expression<Func<Orders, Boolean>> FilterUser(){
return o => o.Where(o => o.User == HttpContext.User.Identity.Name);
}
And it will always fire when i am quering Orders table.
Create a repository around your context and let your code uses the repository so you will add the filtering in the repository method, and by doing this every call to the repository method will get the filtered data, so yku don't repeat yourself and filter everywhere, just in the repository.
EDIT :
You can implement the repository like this:
// an interface around the current user, so you can mock this later
// and test your repository with your own username.
public interface ICurrentUser
{
string UserName { get; }
}
// the implementation of the current user which get the username from the current identity.
public class CurrentUser : ICurrentUser
{
public string UserName
{
get
{
return HttpContext.Current.User.Identity.Name;
}
}
}
// a simple repository to get the orders filtered by the current user.
public interface IOrderRespositroy
{
List<Order> GetAllOrders();
}
// the implementation of the orderrepository interface,
// this get the dbcontext, which get the orders from the data base,
// but you will filter all orders by the username
public class OrderRepository : IOrderRespositroy
{
private readonly TestContext _context;
public OrderRepository(TestContext context, ICurrentUser currentUser)
{
_context = context;
}
public List<Order> GetAllOrders()
{
return _context.Orders.Where(o=> o.User == currentUser.UserName).Where()
}
}
// your new controller which depends on IOrderRepository, and your code will not be polluted
// by the code to filter the orders by the current user.
public class TestController : Controller
{
private readonly IOrderRespositroy _repository;
public TestController(IOrderRespositroy repository)
{
_repository = repository;
}
public ActionResult Index()
{
var orders = _repository.GetAllOrders();
// .
// .
// .
}
}
Now you can setup your dependencies using an IoC container like Autofac, but by using the above pattern you can easily change the logic to filter the orders if you for example decided that all orders should be filtered by userName and userAccess (for example).
Hope that helps.
I would suggest you to overwrite the AuthorizeAttribute and have it applied on Actions and/or Controllers as needed.
public class OrdersAuthorizeAttribute : AuthorizeAttribute
{
protected virtual bool AuthorizeCore(HttpContextBase httpContext) {
{
base.AuthorizeCore(httpContext);
IPrincipal user = httpContext.User;
if (!user.Identity.IsAuthenticated) {
return false;
}
return ctx.Orders.FirstOrDefault(o => o.User == user.Identity.Name) != null;
}
}
Related
[Authorize]
public class ContactController{
private readonly ContactService service;
public ContactController(ContactService service){
this.service = service;
}
public IActionResult Index() {
var model = service.GetContacts();
return View(model);
}
}
public class ContactService {
private readonly ContactRepository repository;
public ContactService(ContactRepository repository){
this.repository = repository;
}
public IEnumerable<ContactViewModel> GetContacts() {
var contacts = repository.GetAll();
return contacts.Select(c=> new ContactViewModel(c));
}
}
public class ContactRepository {
private readonly ApplicationDbContext db;
public ContactRepository(ApplicationDbContext context){
db = context;
}
public IQueryable<Contact> GetAll() {
return db.Contacts;
}
}
Now I want to only get contacts that are of the authenticated user. Let's say contact have a UserId property so,
I want to do .Where(c=> c.UserId == userId) 'somewhere';
Where is supposed that code to be ? On service? On repository? on Controller ?
An implementation on the service will be like this :
public class ContactService {
private readonly ContactRepository repository;
private readonly IIdentity identity;
public ContactService(ContactRepository repository, HttpContext httpContext){
if (context?.User?.Identity?.IsAuthenticated == false)
throw new ArgumentException("Non authenticated request", nameof(httpContext));
identity = HttpContext.Current.User.Identity;
this.repository = repository;
}
public IEnumerable<ContactViewModel> GetContacts() {
var contacts = repository.GetAll()
.Where(c=> c.UserId == identity.GetUserId()); // <-- authorization logic.
return contacts.Select(c=> new ContactViewModel(c));
}
}
Is this the correct approach?
How are you doing it? Another layer?
I would warn against publicly exposing IQueryable from your repository. Exposing the IQueryable means that the classes which leverage the repository determine when when the IQueryable actually gets resolved as a database round trip. This means your repository is hard to work with and fails to provide a clean separation of concerns.
For this reason, I use more specific interfaces on any database repository like
public Contact[] GetAllContacts() {}
public Contact GetContact(int userId) {}
Returning concretes like Lists and Arrays instead of IQueryable will force the database round trip within the repository. The trade off is that it is more verbose, but worth it for the separation of concerns this provides.
[EDIT] For example - where does the database round trip actually occur in your code above? It actually doesn't happen until the view gets executed and actually reads the data from the model.
I have 3 roles in my webapp: Admin,Moderator,User.
I have a user #model WebApplication2.Models.ApplicationUser I want to check inside Razor view if user.Roles contains role Moderator. How to do so? I tried #if(#Model.Roles.Contains(DON'T_KNOW_WHAT_TO_WRITE_HERE){}.
NOTE: I am not asking how to check if currently authorized user is in certain role.
What you could do is create an extension method on IPrincipal that operates the same way as User.IsInRole(...)
public static bool IsInAppRole(this IPrincipal user, string role)
{
using(var db = new MyEntities())
{
var dbUser = db.Users.Find(user.Identity.GetUserId());
return dbUser.Roles.Any(r => r.RoleName == role)
}
}
Import extensions into a view
#using MyApplication.Web.Extensions
Use like you would IsInRole()
#if(User.IsInAppRole("Admin"))
{
}
Though, not sure why you'd do this as the user's roles can be put into their Identity object.
The simplest way that I could find is to use:
#Model.Roles.SingleOrDefault().RoleId
This, of course, requires you to work with the ID rather than the name in your comparison. Without creating a new ViewModel, I have not found a good way to get direct access to the name.
EDIT: If multiple roles are assigned, you should be able to do something like this:
#{
var isMod = false;
}
foreach (var r in Model.Roles)
{
if(r.RoleId == "1")
{
isMod = true;
}
}
I think you should use User.IsInRole(...) in your view code
Why don't you just print them out to preview possible values?
Your code seems to have minor bug to me. I believe it should be
#if(Model.Roles.Contains(DON'T_KNOW_WHAT_TO_WRITE_HERE){}
(just one '#')
EDIT:
Since Model.Roles are just plain Many-To-Many references, you need to call UserManager to obtain user roles. For example:
public class UserDetailsModel {
public string Id { get; set; }
public ApplicationUser User { get; set; }
public IList<string> Roles { get; set; }
}
and Details action in controller:
public ActionResult Details(string id) {
var model = new UserDetailsModel
{
Id = id,
User = UserManager.FindById(id),
Roles = UserManager.GetRoles(id)
};
return View(model);
}
You can get UserManager from OwinContext or inject it in controller:
private readonly ApplicationUserManager _userManager;
public UsersController(){}
public UsersController(ApplicationUserManager userManager) {
_userManager = userManager;
}
public ApplicationUserManager UserManager {
get {
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
}
When using Breeze, I was wondering how one would go about integrating it with a service layer which handles things such as email notifications, audit logs, business validations (ie Customer must exist) etc..
For example, given the following scenario:
public class SalesAppRepository
{
private readonly EFContextProvider<SalesAppContext> _contextProvider;
public SalesAppRepository(EFContextProvider<SalesAppContext> contextProvider)
{
_contextProvider = contextProvider;
}
private SalesAppContext Context { get { return _contextProvider.Context; } }
public string MetaData
{
get { return _contextProvider.Metadata(); }
}
public SaveResult SaveChanges(JObject saveBundle)
{
return _contextProvider.SaveChanges(saveBundle);
}
public IQueryable<Customer> Customers
{
get { return Context.Customers; }
}
public IQueryable<Order> Orders
{
get { return Context.Orders; }
}
/* Other entities */
}
[BreezeController]
public class BreezeController : ApiController
{
private readonly SalesAppRepository _repository;
public BreezeController(SalesAppRepository repository)
{
_repository = repository;
}
[HttpGet]
public string Metadata()
{
return _repository.MetaData;
}
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
return _repository.SaveChanges(saveBundle);
}
[HttpGet]
public IQueryable<Customer> Customers()
{
return _repository.Customers;
}
[HttpGet]
public IQueryable<Order> Orders()
{
return _repository.Orders;
}
/* Other entities */
}
I have some other business logic when entities are being created, modified etc.. such as when an Order is created; the Customer must be able to place an order (bool value canOrder) and must exist, also an email must be sent and an audit log must be updated to show the newly created order. This is just one scenario, there are other situations which would usually sit in my service layer, but I'm not sure how I would incorporate this with breeze?
I understand I can override the SaveChanges method on the SalesAppContext to generate the audit log, but what about the other situations? I'm not sure where I would put this in regards to above?
Edit
I seem to have found an, albeit, hacky way to get around this problem, I have uploaded a gist which I hope could help someone or be improved upon: https://gist.github.com/CallumVass/8300400
i've incorporated my business logic in the beforeSaveEntities(..) Method:
http://www.breezejs.com/documentation/efcontextprovider
If I want only administrator to access the action called "ManagerUser", I know I can do this:
[Authorize( Roles = Constants.ROLES_ADMINISTRATOR )]
public ActionResult ManageUser( string id )
{
}
What if I want to give everyone access except to administrator? I do not want to write all roles up there on function :|.
Any recommendations/way outs?
You can create your own custom Authorize attribute, something like "AuthorizeAllExceptAdmin." Within that class you would simply need to check whether or not the current user was an admin, and if they were reject it, otherwise accept it.
Here's a good tutorial, but you'll probably end up with something like:
public class AuthorizeAllExceptAdmin : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return !httpContext.User.IsInRole(Constants.ROLES_ADMINISTRATOR);
}
}
Then your controller method becomes:
[AuthorizeAllExceptAdmin]
public ActionResult SomethingOnlyNonAdminsCanDo()
{
}
Here's an example of the custom attribute that takes in roles to deny.
public class DoNotAuthorize : AuthorizeAttribute
{
private IEnumerable<string> _rolesToReject;
public DoNotAuthorize(IEnumerable<string> rolesToReject)
{
_rolesToReject = rolesToReject;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
foreach (var role in _rolesToReject)
{
if (httpContext.User.IsInRole(role))
return false;
}
return true;
}
}
Then your controller method becomes:
[DoNotAuthorize(new [] {Constants.ROLES_ADMINISTRATOR})]
public ActionResult SomethingOnlyNonAdminsCanDo()
{
}
I would put some thought into it before choosing one of the above options. If you think you'll have several methods (or entire controllers) with similar authorization requirements (i.e, several actions an admin can not perform) then I would stick with the non-parameterized custom attribute. This way, you can evolve them all together (by only changing the custom attribute) later on. For example, maybe later on you want admins to be able to go into a special mode where they can perform these actions.
Alternatively, if the autorization is more varied amongst the actions, then using the parameterized list makes sense, since they'll evolve relatively independently.
Besides creating a custom AuthorizeAttribute, suggested by manu, you could use PrincipalPermission, with a Deny-SecurityAction:
[PrincipalPermission(SecurityAction.Deny, Role="Administrator")]
In my app I don't use roles so I have to query the database to determine whether the user has access or not. The benefits of the code below is that you can redirect the user to a certain action very easily. I explained the code in my blog post at http://blog.athe.la/2009/12/implementing-permission-via-windows-authentication-in-asp-mvc-using-action-filters/
public class DatabaseRepository()
{
private readonly DatabaseDataContext db = new DatabaseDataContext();
public bool UserHasPermission(string userLogon) {
return (from permission this.db.Permissions
where permission.HasPermissionSw == true
select permission).Contains(userLogon);
}
}
public class UserHasPermission: ActionFilterAttribute
{
private readonly DatabaseRepository databaseRepository = new DatabaseRepository();
private readonly string redirectAction;
public UserHasPermission(string redirectTo)
{
this.redirectAction = redirectTo;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string userLogon = filterContext.HttpContext.User.Identity.Name;
if (!this.databaseRepository.UserHasPermission(userLogon))
{
string routeController = filterContext.Controller.ControllerContext.RouteData.Values["controller"];
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = routeController, action = this.redirectAction }));
}
}
}
Your controller would then look something like this:
[UserHasPermission("NoAccess")]
public ActionResult SecretArea()
{
// run all the logic
return View();
}
public ActionResult NoAccess()
{
return View();
}
How can I mock a DataServiceQuery for unit testing purpose?
Long Details follow:
Imagine an ASP.NET MVC application, where the controller talks to an ADO.NET DataService that encapsulates the storage of our models (for example sake we'll be reading a list of Customers). With a reference to the service, we get a generated class inheriting from DataServiceContext:
namespace Sample.Services
{
public partial class MyDataContext : global::System.Data.Services.Client.DataServiceContext
{
public MyDataContext(global::System.Uri serviceRoot) : base(serviceRoot) { /* ... */ }
public global::System.Data.Services.Client.DataServiceQuery<Customer> Customers
{
get
{
if((this._Customers==null))
{
this._Customers = base.CreateQuery<Customer>("Customers");
}
return this._Customers;
}
}
/* and many more members */
}
}
The Controller could be:
namespace Sample.Controllers
{
public class CustomerController : Controller
{
private IMyDataContext context;
public CustomerController(IMyDataContext context)
{
this.context=context;
}
public ActionResult Index() { return View(context.Customers); }
}
}
As you can see, I used a constructor that accepts an IMyDataContext instance so that we can use a mock in our unit test:
[TestFixture]
public class TestCustomerController
{
[Test]
public void Test_Index()
{
MockContext mockContext = new MockContext();
CustomerController controller = new CustomerController(mockContext);
var customersToReturn = new List<Customer>
{
new Customer{ Id=1, Name="Fred" },
new Customer{ Id=2, Name="Wilma" }
};
mockContext.CustomersToReturn = customersToReturn;
var result = controller.Index() as ViewResult;
var models = result.ViewData.Model;
//Now we have to compare the Customers in models with those in customersToReturn,
//Maybe by loopping over them?
foreach(Customer c in models) //*** LINE A ***
{
//TODO: compare with the Customer in the same position from customersToreturn
}
}
}
MockContext and MyDataContext need to implement the same interface IMyDataContext:
namespace Sample.Services
{
public interface IMyDataContext
{
DataServiceQuery<Customer> Customers { get; }
/* and more */
}
}
However, when we try and implement the MockContext class, we run into problems due to the nature of DataServiceQuery (which, to be clear, we're using in the IMyDataContext interface simply because that's the data type we found in the auto-generated MyDataContext class that we started with). If we try to write:
public class MockContext : IMyDataContext
{
public IList<Customer> CustomersToReturn { set; private get; }
public DataServiceQuery<Customer> Customers { get { /* ??? */ } }
}
In the Customers getter we'd like to instantiate a DataServiceQuery instance, populate it with the Customers in CustomersToReturn, and return it. The problems I run into:
1~ DataServiceQuery has no public constructor; to instantiate one you should call CreateQuery on a DataServiceContext; see MSDN
2~ If I make the MockContext inherit from DataServiceContext as well, and call CreateQuery to get a DataServiceQuery to use, the service and query have to be tied to a valid URI and, when I try to iterate or access the objects in the query, it will try and execute against that URI. In other words, if I change the MockContext as such:
namespace Sample.Tests.Controllers.Mocks
{
public class MockContext : DataServiceContext, IMyDataContext
{
public MockContext() :base(new Uri("http://www.contoso.com")) { }
public IList<Customer> CustomersToReturn { set; private get; }
public DataServiceQuery<Customer> Customers
{
get
{
var query = CreateQuery<Customer>("Customers");
query.Concat(CustomersToReturn.AsEnumerable<Customer>());
return query;
}
}
}
}
Then, in the unit test, we get an error on the line marked as LINE A, because http://www.contoso.com doesn't host our service. The same error is triggered even if LINE A tries to get the number of elements in models.
Thanks in advance.
I solved this by creating an interface IDataServiceQuery with two implementations:
DataServiceQueryWrapper
MockDataServiceQuery
I then use IDataServiceQuery wherever I would have previously used a DataServiceQuery.
public interface IDataServiceQuery<TElement> : IQueryable<TElement>, IEnumerable<TElement>, IQueryable, IEnumerable
{
IDataServiceQuery<TElement> Expand(string path);
IDataServiceQuery<TElement> IncludeTotalCount();
IDataServiceQuery<TElement> AddQueryOption(string name, object value);
}
The DataServiceQueryWrapper takes a DataServiceQuery in it's constructor and then delegates all functionality to the query passed in. Similarly, the MockDataServiceQuery takes an IQueryable and delegates everything it can to the query.
For the mock IDataServiceQuery methods, I currently just return this, though you could do something to mock the functionality if you want to.
For example:
// (in DataServiceQueryWrapper.cs)
public IDataServiceQuery<TElement> Expand(string path)
{
return new DataServiceQueryWrapper<TElement>(_query.Expand(path));
}
// (in MockDataServiceQuery.cs)
public IDataServiceQuery<TElement> Expand(string path)
{
return this;
}
[Disclaimer - I work at Typemock]
Have you considered using a mocking framework?
You can use Typemock Isolator to create a fake instance of DataServiceQuery:
var fake = Isolate.Fake.Instance<DataServiceQuery>();
And you can create a similar fake DataServiceContext and set it's behavior instead of trying to inherit it.