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.
Related
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...
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.
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){
I need to track the users who are logged in to my application, and I'm trying to do this by using JSF2's applicationMap. So I create a list of users, and in my login bean I add (remove) users by doing this:
FacesContext.getCurrentInstance().getExternalContext().getApplicationMap().put("usersList", usersList);
In the backing bean of the usersTracking.xhtml page I try to retrieve the list like this:
AppllicationMap map = (ApplicationMap)FacesContext.getCurrentInstance().getExternalContext().getApplicationMap();
List users = (LinkedList)map.get("usersList");
But "users" always comes as null. What am I doing wrong?
Just make it an application scoped managed bean.
#ManagedBean(eager=true)
#ApplicationScoped
public class Users {
private List<User> list;
public Users() {
list = new LinkedList<User>();
}
public List<User> getList() {
return list;
}
}
This way you can inject and access it in every arbitrary managed bean by #ManagedProperty:
#ManagedBean
#ViewScoped
public class ArbitraryBean {
#ManagedProperty("#{users}")
private Users users;
// ...
}
I have a JSF2 application. I have a login bean which is session scoped and a logout bean which is view scoped. When I login I use redirect and it works fine. However the logout fails with redirect. If I logout without redirect it works.
#ManagedBean
#ViewScoped
public class MbLogout extends BaseJsf {
private static final long serialVersionUID = 2992671241358926373L;
public String logout() throws DfException {
getFacesContext().getExternalContext().invalidateSession();
//return "login?faces-redirect=true"; // fails with this
return "login";
}
}
The login page has bindings to the login bean so I suspect this may have something to do with it, although I don't see why it doesn't work. The error is:
java.lang.IllegalStateException: Cannot create a session after the response has been committed
My guess is it's trying to create a session on the login page since I access the session bean although I don't see anything wrong with this and it works without redirect.
I'm using MyFaces 2.1.
I would recommend using a Servlet rather than a bean for logout, a managed bean (especially view scoped) is not fitting for the purpose of logging out. For example:
#WebServlet(name = "LogoutServlet", urlPatterns = {"/logout"}) // Can be configured in web.xml aswell
public class LogoutServlet extends HttpServlet {
private static final String redirectURL = "http://www.somepage.com";
#Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Destroys the session for this user.
if (request.getSession(false) != null) {
request.getSession(false).invalidate();
}
response.sendRedirect(redirectURL );
}
}
It seems to be related to the bean being in the view scope which should by itself be serialized in the session. Make it request scoped instead. The view scope doesn't make much sense for logout anyway.
#ManagedBean
#RequestScoped
public class MbLogout extends BaseJsf {
// ...
}