I have an asp.net web api.
I want to own selfhost my Web API later on an azure website.
A logged in user could do this in the browser /api/bankaccounts/3
to get all details about bank account number 3
.
But the logged in user is not the owner of bank account number 3
.
How do I have to design my Controllers and the services behind that the logged
in user can only retrieve/modify his own resources in the database?
UPDATE
After I created a:
public class UserActionsAuthorizationFilter : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext != null)
{
bool canUserExecuteAction = IsResourceOwner(actionContext);
// stop propagation
}
}
private bool IsResourceOwner(HttpActionContext actionContext)
{
var principal = (ClaimsPrincipal)Thread.CurrentPrincipal;
var userIdAuthenticated = Convert.ToInt32(principal.Claims.Single(c => c.Type == ClaimTypes.Sid).Value);
int targetId = Convert.ToInt32(actionContext.Request.GetRouteData().Values["Id"]);
var requstScope = actionContext.ControllerContext.Request.GetDependencyScope();
var service = (ISchoolyearService)requstScope.GetService(typeof(ISchoolyearService));
bool canUserExecuteAction = service.HasUserPermission(userIdAuthenticated, targetId);
return canUserExecuteAction;
}
}
The question is now that the IsResouceOwner is hardcoded to a certain service => SchoolyearService thus bound to the Schoolyear SQL table
I need to keep the IsResourceOwner method generically working for all sql tables having a field UserId/UserEmail.
The problem is -and I really think nobody is doing that this way- that I have to map each Resource owner check to the correct Sql table in the HasUserPermission method.
How should that mapping look like?
Check Controller name "SchoolyearController" thus the table to check is the "schoolyear" table? thats ridiculous.
This custom attribute "UserActionsAuthorizationFilter" will be on every "Data" controller.
Whatever controller url the user triggers to fetch data, before I have to check wether he is resource owner.
I guess I can not decide this inside a filter.
I have to let the data retrieval/modification go through the controller and do the ResourceOwner check inside maybe in a Repository just before the data retrieval is done.
What do you think of this:
API
public async Task<IHttpActionResult> Delete(int id)
{
var result = await service.Delete(id, User.Identity.UserId);
if (result == 0)
return NotFound();
return Ok();
}
REPO
public async Task<int> Delete(int id, int userId)
{
var schoolyerToDelete = await context.Schoolyears.SingleOrDefaultAsync(s => s.Id == id && s.UserId == userId);
// If schoolyearToDelete is null nothing is removed, thus the affected rows are ZERO.
context.Schoolyears.Remove(schoolyerToDelete);
return await context.SaveChangesAsync();
}
- For the Get Method nothing is returned for the wrong UserId
- For the Create Method: no problem, everyone should be able to create a resource if logged in.
- For the Update Method: same as Delete method the schoolyear is retrieved by id and UserId.
Generally spoken every method in my Repository should consider the UserId in the CRUD action.
What do you think?