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?
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;
}
}
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
}
}
Related
I'm currently creating a Blazor Server application that uses Azure AD for Authentication. The authentication works perfectly but I want to set up some AuthorisedView's within the application.
I've created a custom Authorization Handler whereby I take the user's email and find what user group they belong to within my own sql server. For the database calls I'm using Dapper with some table models. When I make a database call within the HandleRequirementAsync function it returns a NullReference exception. I cannot see where there could be an error within the code, am I missing something obvious?
The users list should return the user in the database and then groups list should return the group that user is assigned to based on an ID. Both of these calls work perfectly throughout the rest of the application, it only causes errors within this section below.
GroupHandler.cs
public class GroupHandler : AuthorizationHandler<GroupRequirement>
{
public IUserData _dbUser;
public IGroupData _dbGroup;
protected async override Task<Task> HandleRequirement(AuthorizationHandlerContect context, GroupRequirement requirement)
{
var emailAddress = context.User.Identity.Name;
List<UserModel> users = await _dbUser.GetUserByEmail(emailAddress);
List<GroupModel> groups = await _dbGroup.GetGroupByID(users[0].Group_ID.ToString());
if(groups[0].Group.Contains(requirement.Group))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
GroupRequirement.cs
public class GroupRequirement : IAuthorizationRequirement
{
public string Group { get; }
public GroupRequirement(string group)
{
Group = group;
}
}
Startup.cs
services.AddAuthorization(config =>
{
config.AddPolicy("IsAdmin", policy =>
policy.Requirements.Add(new GroupRequirement("Admin")));
});
Error
NullReferenceException: Object reference not set an instance of an object
GroupHandler.HandleRequirementAsync(AuthorizationHandlerContext context, GroupRequirement requirement) in GroupHandler.cs, line 30
List<UserModel> users = await _dbUser.GetUserByEmail(emailAddress);
The problem lies inside these lines, as you don't initialize your objects at all. So, they are null by default. Please, initialize this properties.
public IUserData _dbUser;
public IGroupData _dbGroup;
It looks like you never assign the _dbGroup Field in your GroupHandler class.
Depending on your Setup you may be able to inject a IUserData via a constructor.
I have created a struct on validating models on Business Layer which is based on Steven's answer.
It is working well but something confuses my mind. I inject UserService in CreateUserValidator to able to use GetUser method. This means I call validator in UserService and create a new UserService instance to check whether user exist.
UserService -> [ValidateUser -> new UserService().GetUser()]
It works but seems to be a very bad design. But I have to use that method.
Could you please let me know how I can solve this problem, or Shouldn't I worry about it?
public class CreateUser
{
public string Name { get; set; }
public string Email { get; set; }
}
public sealed class CreateUserValidator : Validator<CreateUser>
{
private IUserService _userService;
public CreateUserValidator(IUserService userService)
{
_userService = userService;
}
protected override IEnumerable<ValidationResult> Validate(
CreateUser entity)
{
var user = _userService.GetUserByEmail(entity.Email);
if (user != null)
{
yield return new ValidationResult("Email", "Email address is already exist!");
}
}
}
UserService.cs
public partial class UserService : IUserService
{
IGenericUnitofWork _uow = null;
private readonly IValidationProvider _validationProvider;
public UserService(IGenericUnitofWork uow, IValidationProvider validationProvider)
{
_uow = uow;
_validationProvider = validationProvider;
}
public User CreateUser(CreateUser createUser)
{
this._validationProvider.Validate(createUser);
var user = new User()
{
Email = createUser.Email,
Name = createUser.Name,
};
_uow.Repository<User>().Insert(User);
_uow.SaveChanges();
return user;
}
public User GetUser(string email)
{
var user = _uow.Repository<User>().Where(m => m.Email == email).FirstOrDefault();
return user;
}
}
You dependency graph is cyclic. As described in section 6.3 of Dependency Injection in .NET second edition, dependency cycles are often caused by Single Responsibility Principle violations, as is the case in your design.
The problem is that UserService has too many responsibilities: Creating a user is a different responsibility than getting a user. Creating a user can become a very complex use case, as the validation logic hints at, while getting a user is something typically quite simple. It would therefore be beneficial to split UserService into multiple smaller classes. This would allow the validator to depend on the service that allows retrieving the user by its mail address, while the 'create user' service can depend on the validator.
To take it even one step further, you might want to remove validation from the 'create user' service completely. Validation is a cross-cutting concern, and mixing it with the class that contains the business logic, makes such class harder to maintain.
A design that might benefit you is one where you place all state changing business actions behind a common abstraction, as described here.
I am testing the following controller:
public class MyController : ApiController
{
private readonly IUserManager _users;
private int num = 0;
public MyController(IUserManager users)
{
_users = users;
}
public User GetByCredentials(string username, string pass)
{
var account = new Account(username, pass);
User u = _users.GetByCredentials(num, account);
return u;
}
I was thinking to mock the IUserManager.GetByCredentials method, as I only want to see that MyController.GetByCredentials method works as expected. The problem is that the User class cannot be instantiated directly, so I cannot mock a User object, because the constructor is private:
public class User
{
// private attributes here
protected User()
{
// do a lot of stuff here, call other objects to initialize attributes
}
private User(int num, string id, string userid, Account account)
: this()
{
// do other stuff here with the params
}
public static User CreateUser(int num, string id, string userid, Account account)
{
return new User(num, id, userid, account);
}
// and so on
}
I am using the Moq framework, but I am open to different solutions. I would prefer to avoid the creation of test data in this case, because it depends on database the initialization, servers and so on - and then it would not be a unit test anymore. Have you ever had an issue like this? How do you deal with it? Thanks.
You don't need to mock User - you can use the real User class. You only need to mock (or fake) IUserManager. Your mock/fake IUserManager can use User.CreateUser to create the User objects to be returned to your controller.
Unless the User class itself "knows" about the database, that should be fine. Resist the temptation to mock everything - you only need to get rid of dependencies that make it hard to write tests.
Now you've written that your User private constructor "does a lot of stuff" - if that reaches out too far, you should redesign User so it's simpler... your user manager should possibly be responsible for some of the work that's going on there.
While it's an oversimplification to completely split the world into services (like the user manager) and dumb data objects (like the user), it makes things much easier if a design reasonably naturally splits itself in that respect.
In order to test your MyController, you will be mocking IUserManager not User so you will be able to do something like this:
var mockUserManager = new Mock<IUserManager>();
// Configure the User to be returned by calling GetByCredentials
mockUserManager
.Setup(x => x.GetByCredentials(It.IsAny<int>(), It.IsAny<Account>())
.Returns(User.CreateUser(1, "foo", "username", new Account());
var controller = new MyController(mockUserManager.Object);
var user = controller.GetByCredentials("username", "password");
Assert.NotNull(user);
mockUserManager.Verify(x => x.GetByCredentials(It.IsAny<int>(), It.IsAny<Account>(), Times.Once());
The point of mock/fake objects is to avoid database/web service calls. Your Account and User classes should be poco classes - e.g. only contain methods and properties which act upon itself, no database or web service calls etc so they don't actually need mocking.
Throughout my ASP.net site i need to check if the logged in users belongs to a role or has a field in my "UserInstance" table in my database set to true. To do this i can do the following.
if(Roles.IsUserInRole("Global Admin")
|| uow.UserInstanceService.GetUserInstance(userId,InstanceId).Admin)
{
//Do something
}
However as i am going to be using this code a lot as much of the permissions depend on the logged in user either being a "Global Admin" or a field of my table being true I don't want to write this out constantly.
One solution i have found is to create a method in the "UserInstance" Service which checks for both as seen in the "IsUserAdminOrGlobalAdmin" method.
public class UserInstanceService
{
IRepository<UserInstance> userInstanceRepository;
public UserInstanceService(IRepository<UserInstance> userInstanceRepository)
{
this.userInstanceRepository = userInstanceRepository;
}
public UserInstance GetByUserIdAndInstanceId(Guid userId, int instanceId)
{
return userInstanceRepository.GetSingle(u => u.UserId == userId && u.InstanceId == instanceId);
}
public bool IsUserAdminOrGlobalAdmin(Guid userId,int instanceId)
{
bool valid = false;
if (System.Web.Security.Roles.IsUserInRole("Global Admin"))
valid = true;
if (GetByUserIdAndInstanceId(userId, instanceId).Admin)
valid = true;
return valid;
}
//Removed rest of methods for readability
}
As this is buisness logic I put this method is in my "UserInstanceService" class which interacts with the repository class which contains the entity context. This service class resides in a seperate Model project so i had to add a reference to System.Web.Security and i am not sure if doing this is good practice. One thing i have noticed is that i can not write unit tests for this method as it relies on a user being logged in.
So my question is, is it acceptable to combine HttpContext specific functionality like the Logged in users roles, in a service?
Edit - After reading the answers I have changed my code so a Auth service (in the Web app project) is called which in turn calls the UserInstanceService to something like this.
public class Auth: IAuthService {
public bool IsUserAdminOrGlobalAdmin(Guid userId,int instanceId) {
myEntities entityContext = new myEntities
//RepsitoryBase inherits my IRepository<TEntity> class
UserInstanceService uis = new UserInstanceService(new RepositoryBase<UserInstance>(entityContext));
bool valid = false
if(Roles.IsUserInRole("Global Admin"))
valid = true;
if(uis.GetByUserIdAndInstanceId(userId,instanceId).Admin)
valid = true;
return valid;
}
}
So i could call this in my pages like this
if(Auth.IsUserAdminOrGlobalAdmin(userId,InstanceId)
{
//Do stuff
}
The original answer was written assuming the UserAccess requires the Authentication, but it appears that the Authentication consumes the UserAccess; simply invert the dependencies, but everything else should be usable in about the same manner.
Original answer:
Pull the ASP.NET-specific code into it's own service separate from the repository. Then that service - say, the Auth Service - can be used by any component (such as the UserInstanceService) that needs access to centralized authentication/authorization logic.
Consume the Auth as a dependency per IoC principles, hopefully using some DI to make life easier.
If the Auth service is kept separate it can also be trivially mocked for testing, such as testing what happens when the use is authenticated or not, which entirely avoids the need to setup a full ASP.NET stack for the User service.
In addition, because services (interfaces) and components (classes) are separate, the actualy HTTP-utilizing component can live in a separate project from the service and wired in later - this will avoid pulling in Web dependencies to the Model project.
For example,
// This is the Service Contract and can live in the Model
public class IAuthService {
void AssertCurrentUserIsAdminOrGlobalAdmin();
void AssertIsUserAdminOrGlobalAdmin(Guid userId,int instanceId);
}
// This is the Component, which provides the Service, and is part
// of the Web/HTTP-specific project. It is wired up via IoC/DI from
// the large context of the application.
public class Auth: IAuthService {
public void AssertCurrentUserIsAdminOrGlobalAdmin() {
// This one already knows the applicable HTTP/User Context
}
public void AssertIsUserAdminOrGlobalAdmin(Guid userId,int instanceId) {
// Do whatever
}
}
// This Component is part of the Model
public class UserInstanceService
{
// IoC dependencies
IRepository<UserInstance> userInstanceRepository;
IAuthService authService;
}
You could set the current principal on the thread and use that instead. I think thats most of what ASP.Net does for you as well.
I'm working on an application which has two projects:
Core - houses the data access layer using repository pattern and domain-driven design
UI - using ASP.Net MVC. Currently, I am able to get the current logged in user's info(id, name, etc..) inside the UI controller via the User property like this:
using Microsoft.AspNet.Identity;
public class ExamController : Controller
{
IExaminationRepository _repository;
public ExamController()
{
_repository = RepositoryFactory.Get<IExaminationRepository>();
}
[HttpPost]
[Authorize(Roles = "Examiner")]
public ActionResult Create(ExamViewModel viewModel)
{
try
{
ExaminationDomain domain = Mapper.Map<ExamViewModel, ExaminationDomain>(viewModel);
//TODO: Move this to the repository
domain.AuthorId = User.Identity.GetUserId();
_repository.Add(domain);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
}
I would like to move the line: domain.AuthorId = User.Identity.GetUserId(); to my repository concrete implementation like this:
using Microsoft.AspNet.Identity;
using System.Security.Principal;
internal class ExaminationRepository
{
public DBEntities context;
public IPrincipal User;
public ExaminationRepository(DBEntities context)
{
this.context = context;
//I'd like to instantiate User property here:
this.User = "not sure what to instantiate with";
}
public void Add(ExaminationDomain domain)
{
Examination newExam = Mapper.Map<ExaminationDomain, Examination>(domain);
newExam.AuthorId = User.Identity.GetUserId();
newExam.CreatedBy = User.Identity.Name;
newExam.CreatedDate = DateTime.Now;
context.Examinations.Add(newExam);
context.SaveChanges();
}
But I am not sure what to instantiate the User property to in the constructor. I've read some suggestions to use WindowsIdentity.GetCurrent().User instead of creating a user property but this doesn't contain the user id, only user name.
Any other suggestions on getting user info?
I'd really appreciate some help on this..
Thanks,
I would decouple your repository from the httpcontext with a custom manager. For example I have a interface called IAUthenticationManager
public interface IAUthenticationManager
{
string CurrentUserId();
bool HasCurrentUserRole(string roleName),
}
Easy to test and fully decoupled.
This won't work easily since the repository can be used in many different contexts, even such contexts where user is not set. If you create a concrete dependency in your constructor, your repository will no longer be an independent data provider.
For example, referencing
HttpContext.Current.User.Identity
directly would create a dependency to a web context and the repository would be unusable in non-web contexts.
The best you could do is just to let the repository client provide this:
public void Add(ExaminationDomain domain, IPrincipal principal)
{
Examination newExam = Mapper.Map<ExaminationDomain, Examination>(domain);
newExam.AuthorId = principal.Identity.GetUserId();
newExam.CreatedBy = principal.Identity.Name;
newExam.CreatedDate = DateTime.Now;
context.Examinations.Add(newExam);
context.SaveChanges();
}
or (which could be possible)
public ExaminationRepository(DBEntities context, IPrincipal user)
{
this.context = context;
this.user = user;
}
The latter case could still be correctly resolved by an IoC container if you tell the container how to resolve the dependency.
In a web context, you could set the container to resolve it to HttpContext.Current.User.
In a non-web context, you could set the container to resolve it to WindowsIdentity.GetCurrent().User.
use HttpContext class witch is a singleton class:
first add a using to Microsoft.AspNet.Identity;
and then you can do some thing like this:
HttpContext.Current.User.Identity.GetUserId();
since GetUserId is an extension method you have a reference to Microsoft.AspNet.Identity
but if you need to access user information in several places of you app I suggest to have a wrapper class with properties that you need and instantiate when user logs in then store it in session variable this way you have two benefits:
1- you don't need to query db to get username, email etc.. on each user info usage across the app.
2- you don't need assembly that your repository lives to aspnet identity.
You need to instantiate this.User with identity information of current thread:
this.User = System.Threading.Thread.CurrentPrincipal;
var currentIdentity = (System.Security.Claims.ClaimsIdentity)User.Identity;
var userId = currentIdentity.Claims
.Where(p => p.Type.EndsWith("nameidentifier")).Single().Value;
Note that the type of CurrentPrincipal.Identity is an IIdentity. You can cast it to System.Security.Claims.ClaimsIdentity, which contains a property named Claims. This property contains all your claims, including userid and 3rd party token (e.g. Facebook token).
To retrieve UserId, find a claims with Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"