How does Spring Security differentiate between multiple logged in user - spring-security

I am working on a Spring-MVC application where I am using spring security for authentication. for accessing secured functions, it is compulsory that the user is logged in. I am using a function where it can be determined whether the user is logged in or not.
I just wanted to know if the code I am posting below will hold if there are multiple users logged in at the same time, to distinguish like user A has logged in. If not, any solutions or ideas. Thank you.
Person Controller :
#Controller
public class PersonController {
private PersonService personService;
// Now whenever there are secure functions to be accessed, like below, I use it the following way :
}
#RequestMapping(value = "/note/list/{id}",method = RequestMethod.GET)
public String listNotes(#ModelAttribute("notices") Notes p,#PathVariable int id,Model model) {
Person person = personService.getCurrentlyAuthenticatedUser();
model.addAttribute("section1",this.notesService.listNotesBySectionId(1,person));
}
Get currently authenticated user function :
#Override
public Person getCurrentlyAuthenticatedUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String authenticatedUserId = authentication.getName();
Person person = personDAO.findPersonByUsername(authenticatedUserId);
return person;
}
I am implementing authentication this way :
#Transactional
#Service("userDetailsService")
public class LoginServiceImpl implements UserDetailsService{
#Autowired private PersonDAO personDAO;
#Autowired private Assembler assembler;
private static final GrantedAuthority USER_AUTH = new SimpleGrantedAuthority("ROLE_USER");
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException,DataAccessException {
Person person = personDAO.findPersonByUsername(username);
if(person == null) { throw new UsernameNotFoundException("Wrong username or password");} //Never specify which one was it exactly
return assembler.buildUserFromUserEntity(person);
}
}
Assembling the user
#Transactional
#Service("userDetailsService")
public class LoginServiceImpl implements UserDetailsService{
#Autowired private PersonDAO personDAO;
#Autowired private Assembler assembler;
private static final GrantedAuthority USER_AUTH = new SimpleGrantedAuthority("ROLE_USER");
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException,DataAccessException {
Person person = personDAO.findPersonByUsername(username);
if(person == null) { throw new UsernameNotFoundException("Wrong username or password");}
return assembler.buildUserFromUserEntity(person);
}
}

The SecurityContextHolder is internally implemented with a ThreadLocal. This is the default strategy spring security uses and it is proper for web applications.
Since a ThreadLocal is isolated to the scope of the current thread your code has access to the current user's information only no matter how many others are currently logged in.
In your code however you should check for null authentication objects unless you have enabled anonymous authentication.
String authenticatedUserId = authentication.getName();
authentication may be null in the line above.

Related

How to implement role authorization with custom database?

I have an application which requires role authorization using custom database. The database is set up with a tblUsers table that has a reference to a tblRoles table. The users are also already assigned to their roles.
I also want to use the [Authorize(Role = "RoleName")] attribute on each action to check if an authenticated user is assigned to "RoleName" in the database. I'm having a lot of trouble figuring out where I need to make a modification to the [Authorize] attribute so it behaves that way. I just want to see if a username has a role, I won't have a page to manage roles in the database.
I have tried implementing custom storage providers for ASP.NET Core Identity, but it's starting to look like this is not what I need because I'm not gonna be managing roles within the application, and I can't tell how it affects the behavior of [Authorize] attribute.
Also, it's likely that I have a false assumption in my understanding on how the [Authorize] attribute even works. If you notice it, I would appreciate if you could point it out.
I had a similar problem when my client asked for granular permissions for each role. I couldn't find a way to modify the Authorize attribute but was able to implement the solution with a custom attribute. But it depends on one thing i.e can you get the userId of the calling user? I used cookie authentication so I just include the userId in my claims when someone logs in so when a request comes I can always get it from there. I think the built-in Session logic in asp.net might get the job done too, I can't say for sure though. Anyways the logic for custom authorization goes like this:
Load users and roles from database to cache on startup. If you haven't set up a cache in your program (and don't want to) you can simply make your own for this purpose by making a UserRoleCache class with 2 static lists in it. Also there are several ways of loading data from db on startup but I found it easy to do that directly in Program.cs as you'll see below.
Define your custom attribute to check if the calling user has the required role by iterating over lists in cache and return 403 if not.
Modify your Program class like:
public class Program
{
public static async Task Main(string[] args)
{
IWebHost webHost = CreateWebHostBuilder(args).Build();
using (var scope = webHost.Services.CreateScope())
{
//Get the DbContext instance. Replace MyDbContext with the
//actual name of the context in your program
var context = scope.ServiceProvider.GetRequiredService<MyDbContext>();
List<User> users = await context.User.ToListAsync();
List<Role> roles = await context.Role.ToListAsync();
//You may make getters and setters, this is just to give you an idea
UserRoleCache.users = users;
UserRoleCache.roles = roles;
}
webHost.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
Then comes the logic for checking if user has a role. Notice I've used an array of roles because sometimes you'll want to allow access to multiple roles.
public class RoleRequirementFilter : IAuthorizationFilter
{
private readonly string[] _roles;
public PermissionRequirementFilter(string[] roles)
{
_roles = roles;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
bool hasRole = false;
//Assuming there's a way you can get the userId
var userId = GetUserId();
User user = UserRoleCache.users.FirstOrDefault(x => x.Id == userId);
//Where roleType is the name of the role like Admin, Manager etc
List<Role> roles = UserRoleCache.roles.FindAll(x => _roles.Contains(x.RoleType))
foreach(var role in roles)
{
if(user.RoleId == role.Id)
{
hasRole = true;
break;
}
}
if (!hasRole)
context.Result = new StatusCodeResult(403);
}
}
Finally make the Role attribute
public class RoleAttribute : TypeFilterAttribute
{
public RoleAttribute(params string[] roles) : base(typeof(RoleRequirementFilter))
{
Arguments = new object[] { roles };
}
}
Now you can use the Role attribute in your controllers:
public class SampleController : ControllerBase
{
[HttpGet]
[Role("Admin", "Manager")]
public async Task<ActionResult> Get()
{
}
[HttpPost]
[Role("Admin")]
public async Task<ActionResult> Post()
{
}
}

How to access the current user in a specific aspect

If I adopted the last scenario in this thesis :
Then my main layers will be like that:
UI Service (MVC application)
Business Layer
Security Service (used as a wrapper class library for MS identity
framework)
Aspects which use the previous security service to Authorize the
business layer methods.
public class EditEmployeeData : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
Employee emp = (Employee)args.Instance;
((System.Security.Claims.ClaimsIdentity)System.Web.HttpContext.Current.User.Identity).HasClaim("Employee", "EditName");
}
}
I want to set the current user in runtime.
How to access the current user to authorize him on a specific
functionality in business layer?
Should the authorization be more near to the UI to disable/hide functionality and to prevent calling not allowed action methods ?(In the preferred scenario there's not any interaction between the security layer and the UI !!)
Update
Please see this answer about using claims...
In a controller, you can get the current user like this:
using Microsoft.AspNet.Identity.Owin;
public class MyController : Controller
{
// this code will return 0 if user is not authenticated
protected long GetUserId()
{
// note: I have changed the default UserId type from Guid to long
return User.Identity.GetUserId<long>();
/*
* use this if you are using Guid UserIds (which is the default)
* return User.Identity.GetUserId();
*/
}
See this, if you want to know how to change type of UserId.
If you have access to HttpContext, you can get the user like this:
// note that I have changed UserId from Guid to long
HttpContext.Current.User.Identity.GetUserId<long>()
If you want to get ApplicationUser use this (more info here):
// this is how you get user manager from OwinContext
var userManager = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
// Get ApplicationUser from UserManager
ApplicationUser user = UserManager.FindById(User.Identity.GetUserId());
How to access the current user to authorize him on a specific
functionality in business layer?
If you need to access current user in a service, you can pass it through or you can inject it. Using ninject, this is how you can inject UserId into a service:
kernel.Bind<MyService>().ToConstructor(ctorArg => new MyService(
HttpContext.Current.User.Identity.GetUserId<long>()).InRequestScope();
And this is how MyService class looks like:
public class MyService
{
private readonly long _userId;
public MyService(long userId)
{
// this service always has access to current user (if logged in)
_userId = userId;
}
// more code...
I am not sure what is the process of your authorization... ASP.NET Identity, already implements authorization task for you. This is implemented in ApplicationUserManager and ApplicationSignInManager which comes with ASP.NET MVC default template. You can use [Authorize] attribute on your action/class to prevent unauthorized access:
[Authorize] // <-- restricts all action methods of the class, unless marked [AllowAnonymous]
public class MyController : Controller
{
[HttpPost]
[Authorize] // <-- restricts this particular action method
public ActionResult MyAction(long id)
{
// do some action which requires authorization
}
Regarding DDD layers, have a look at this this link which explains services which belong to each layer.
How to access the current user to authorize him on a specific functionality in business layer?
To access user information on the business layer, you can type an interface named ICurrentUser
namespace AOPSample
{
public interface ICurrentUser
{
User GetCurrentUser();
}
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Role { get; set; }
}
}
The CurrentUser class must be able to read the information of the user from a common location. HttpContext is available for this.
Let's write a helper class for this.
using System.Web;
namespace AOPSample
{
public class ContextHelper
{
public T Get<T>()
{
T local = default(T);
string key = typeof(T).GUID.ToString();
if (HttpContext.Current.Items.Contains(key))
{
local = (T)HttpContext.Current.Items[key];
}
return local;
}
public T Get<T>(string key)
{
T local = default(T);
if (HttpContext.Current.Items.Contains(key))
{
local = (T)HttpContext.Current.Items[key];
}
return local;
}
public void Set<T>(T value)
{
string str = typeof(T).GUID.ToString();
HttpContext.Current.Items[str] = value;
}
public void Set<T>(T value, string key)
{
HttpContext.Current.Items[key] = value;
}
}
}
Our CurrentUser class will return user information using your helper class
namespace AOPSample
{
public class CurrentUser : ICurrentUser
{
public User GetCurrentUser()
{
return new ContextHelper().Get<User>();
}
}
}
now user information write to HttpContext with ContextHelper class and for this use correct location interceptor class
public class EditEmployeeData : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
Employee emp = (Employee)args.Instance;
((System.Security.Claims.ClaimsIdentity)System.Web.HttpContext.Current.User.Identity).HasClaim("Employee", "EditName");
new ContextHelper().Set<User>(new User
{
});
}
}
You can access user information from the domain layer with ICurrentUser. HttpContext is unique for every request and response
Should the authorization be more near to the UI to disable/hide functionality and to prevent calling not allowed action methods ?(In the preferred scenario there's not any interaction between the security layer and the UI !!)
It's your choice
In my opinion, you can take user privileges and log them with cache and use them for client side actions, but according to the technology you use for server side, you can store user information for each request in a similar way. For example; The correct location to store the OperationContext for wcf.
If you use ASP.NET Identity, you can try the following approach in order to get current User:
ApplicationUser user = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(System.Web.HttpContext.Current.User.Identity.GetUserId());
//If you use int instead of string for primary key, use this:
ApplicationUser user = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(Convert.ToInt32(System.Web.HttpContext.Current.User.Identity.GetUserId()));
Hope this helps...

Can't use user with MANAGER role to create user from angular UI

I'm trying to add MANAGER role. User with MANAGER must be able to create other users.
I've update AuthoritiesConstants.java like below :
public final class AuthoritiesConstants {
public static final String ADMIN = "ROLE_ADMIN";
public static final String USER = "ROLE_USER";
public static final String ANONYMOUS = "ROLE_ANONYMOUS";
public static final String MANAGER = "ROLE_MANAGER";
private AuthoritiesConstants() {
}
}
I've also update authorities.csv:
name
ROLE_ADMIN
ROLE_USER
ROLE_MANAGER
I've try to update the UserResource.java like this :
#PostMapping("/users")
#Timed
#Secured({AuthoritiesConstants.ADMIN,AuthoritiesConstants.MANAGER})
public ResponseEntity createUser(#Valid #RequestBody ManagedUserVM managedUserVM) throws URISyntaxException {
log.debug("REST request to save User : {}", managedUserVM);
}
and this :
#RestController
#RequestMapping("/api")
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class UserResource {
#PostMapping("/users")
#Timed
#PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_MANAGER')")
public ResponseEntity createUser(#Valid #RequestBody ManagedUserVM managedUserVM) throws URISyntaxException {
log.debug("REST request to save User : {}", managedUserVM);
//....
}
//....
}
I've logged in the angular UI and create a user toto with ROLE_MANAGER and ROLE_USER roles. I've update html file so that user with ROLE_MANAGER have access to user management pages.
When the user toto submit user creation form, the backend responded with a status of 403 (Forbidden).
I reproduced your problem and got an access denied error but in the browser console it was for /api/users/authorities.
So the fix consists in authorizing ROLE_MANAGER role to access UserResource.getAuthorities():
#GetMapping("/users/authorities")
#Timed
#Secured({AuthoritiesConstants.ADMIN, AuthoritiesConstants.MANAGER})
public List<String> getAuthorities() {
return userService.getAuthorities();
}

#Produces for the current user object in JSF2 and JAX-RS?

I've a simple #Stateless EJB that looks like this (stripped of all logging and error handling):
#Stateless
public class CurrentUserBean {
#PersistenceContext
private EntityManager em;
#Produces #Named #LoggedIn
#SessionScoped
public User produceCurrentUser() {
Principal principal = Faces.getExternalContext().getUserPrincipal();
String username = (principal == null ? null : principal.getName());
return em.createNamedQuery("findByLogin", User.class)
.setParameter("login", username)
.getSingleResult();
}
}
Works fine when the user logs in with JSF. But the same user can also authenticate via webservice, where I can (need to?) get the user principal from SecurityContext in a bean controlled by JAX-RS (resteasy in my case):
public User doAuth(#Context SecurityContext ctx) {
return em.createNamedQuery("findByLogin", User.class)
.setParameter("login", ctx.getUserPrincial().getName())
.getSingleResult();
}
How can I unify these approaches so that the production of the current user object is the responsibility of only one class?
CDI allows you to inject the Principal directly. Just do this:
#Inject Principal userPrincipal;
And that will have the user name.

Authenticate method parameters using Spring-Security

I'm using Spring Security 3.1.4.
I have a UsersManager class as follow:
public class UsersManager {
#Secured("ROLE_ADMIN")
public void update(User user){
....
....
}
}
public class User{
Integer id;
String name;
Integer departmentId;
}
The requirement as follow:
A user is allowed to update only users from his department.
Taking in account that the User relies in the secured session, is there a way to do it with Spring Security?
You need to extend org.springframework.security.core.userdetails.User and add to it departmentId property. Then ensure that this object is used by Spring Security as principal (provide your UserDetailsService, set departmentId at the moment of login). Then you can do:
#PreAuthorize("hasRole('ROLE_ADMIN') and #principal.departmentId==#user.departmentId")
public void update(User user){

Resources