Looking for ideas to guard against malicious data changes: userA manipulating (editing or deleting) data that belongs to userB. Since we are creating entities on the client, we need to assign them (or at least some of them) to the authenticated user.
For example:
var newItem = ds.createNewItem();
newItem.OwnerId(22); //this is the problem that I see.
newItem.Name("New Item");
newItem.Description("I just changed your item!");
... //and so on
ds.saveChanges();
Assuming we know the identity of the user calling SaveChanges on our API, how do we validate our entities (new or modified) against this user?
The first thought that comes to mind is to subclass EFContextProvider, override BeforeSaveEntity and examine the entities OwnerId property against the identity of our user. For example:
if (entityInfo.Entity.GetType() == typeof(Item)
&& (entityInfo.EntityState == EntityState.Added
|| entityInfo.EntityState == EntityState.Modified)
&& ((Item)entityInfo.Entity).OwnerId != _currentUserId) {
return false
... //and so on
If using this approach, does it make sense to establish _currentUserId in the constructor of our new EFContextProvider class?
An ideas or perhaps a better way to approach this problem?
I think you are on the right track. I’ve been noodling this myself and have gone down much the same path.
Let’s assume you’ve handled authentication and there’s an
IPrincipalavailable. You’ve got yourself a customIIdentitytoo (call itAppIdentity) where you can stash theUserIdfor the authenticated user.The Web Api’s base
ApiControllerclass makes the ambientIPrincipalavailable via itsUserproperty. We will leverage that in your custom Breeze Web Api controller which might begin like this:[Authorize] [JsonFormatter, ODataActionFilter] public class BreezeApiController : ApiController { private readonly AppContextProvider _context; public BreezeApiController() { // pass 'User' IPrincipal to the context ctor _context = new AppContextProvider(User); } ... // one of the Query action methods [HttpGet] public IQueryable<Foo> Foos() { return _context.Foos } ...Your custom
EFContextProvidermight begin like this:public class AppContextProvider : EFContextProvider<AppDbContext> { public AppContextProvider(IPrincipal user) { UserId = ((AppIdentity) user.Identity).UserId; } public int UserId { get; private set; } ...Now you probably want to prevent UserB’s entities from being seen by UserA. So instead of allowing every
Footo go out the door, your customEFContextProvidercould filter accordingly.public DbQuery Foos { get { // Here the 'Context' is your EF DbContext return (DbQuery) Context.Foos .Where(f => f.UserId == UserId); } }Looking back at the controller, we see that its
FoosGET action method is oblivious to the filter … as it should be. We want our controllers to be light and move the business logic to the customEFContextProviderand its helpers.Finally, a highly simplified, general purpose
BeforeSaveEntitycould look like this:private bool BeforeSaveEntity(EntityInfo info) { var entity = info.Entity; if (info.EntityState == EntityState.Added) { entity.UserId = UserId; return true; } return UserId == entity.UserId || throwCannotSaveEntityForThisUser(); } ... private bool throwCannotSaveEntityForThisUser() { throw new SecurityException("Unauthorized user"); }Notice that the custom context provider on the server is responsible for setting the
UserIdof added entities. We wouldn’t trust the client to do that anyway. And of course it is responsible for verifying theUserIdof modified and deleted entities.Hope this helps. Remember, this is only a sketch. The real deal would have greater sophistication and be refactored into helpers.