Reputation: 573
Users are assigned to one or more departments.
Users have one or more roles for instance, Read Own role can only view his/her Tasks. While Team Member role can view and edit others Tasks within department he/she is assigned to. User with role Admin can view and edit all Tasks in the system.
Due to unauthorized access prevention and performance reasons we want to pass the current logged in user id all the way down to the database to be able only fetch the records he/she has access to.
Our system design is:
Web API -> Business/Service layer -> Repositories -> DB
Currently we are passing User id from web API to service layer in each method where it checks for instance if user has role Team Member (who can view/edit other users Tasks within departments he has access to) and gets all the Departments he has access to and then that is passed further to the Repositories.
Is there any better approach to avoid passing user id in each method? What is the best place in the above design to check for users Access?
We ideally want the method without user id parameter to be able to use the same classes for reporting in another application.
Any ideas?
Upvotes: 0
Views: 2001
Reputation: 41075
Have a Security Layer (comprised of classes that decorate your service layer classes) that checks if the user has rights to raise the request.
For instance if your Web API call is to ../viewTask/456
check if the user is Admin, Team member of the department to which the task belongs to or if its his/her own task.
The decorator classes pass down to the wrapped service layer class if the access control check passes or raise an Unauthorized exception if it fails.
Something like...
public class SecuredTaskController : ApiController
{
private IContext _context;
private ITaskService _taskService;
// other services needed for access check (eg. userService?)
public SecuredTaskController(ITaskService taskService, IContext context
// other services needed for access check (eg. userService?)
)
{
_taskService = taskService;
_context = context;
}
public IHttpActionResult Get(Task task)
{
if (hasGetAccess(task, _context.UserId))
return Ok(_taskService.Get(task));
else
return Unauthorized();
}
private bool hasGetAccess(Task task, long userId)
{
// check if userId has acces to get task
}
}
Upvotes: 0
Reputation: 6976
Use dependency injection to inject some ICurrentUser
instance to the services that require the user id to perform queries and other tasks.
public interface ICurrentUser
{
int UserId { get; }
}
public class AspNetCurrentUser : ICurrentUser
{
public int UserId { get { return HttpContext.Current.User.GetUserId<int>(); } }
}
public class Service : IService
{
private readonly ICurrentUser _currentUser;
public Service(ICurrentUser currentUser)
{
_currentUser = currentUser;
}
public object WorkWithUserId()
{
return _currentUser.UserId;
}
}
Upvotes: 1