How should I design endpoint to return different models depending on User role in Spring - spring

So lets assume I have 2 model classes - User and Presentation, and they look something like this:
#Entity
public class Presentation {
#Id
private Long id;
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
}
#Entity
public class User implements UserDetails {
#Id
private Long id;
private String username;
private String name;
private String surname;
private String password;
#Enumerated(EnumType.STRING)
private Role role;
}
As you can see I have a unidirectional mapping for user in Presentation class. My endpoint looks something like this:
#RestController
public class PresentationController {
#GetMapping("/{presentationId}")
public PresentationDTO getPresentation(#PathVariable Long presentationId) {
return presentationService.getPresentationById(presentationId);
}
#GetMapping
public List<PresentationDTO> getAllPresentations() {
return presentationService.getAllPresentations();
}
}
Now for my question - how do I change getAllPresentations() to return the presentations that the users with role "user" own, and return all presentations for users with role "admin"? I know I can create a separate endpoint with a different mapping (like /admin/presentations) and add #PreAuthorize(hasRole("admin")), but here is the tricky part.
For the getAllPresentations() endpoint which everyone who is authenticated is supposed to fetch his own presentations, how do I know for which user I have to return his presentations? Maybe I can get the username as a parameter but that might be dangerous cause he can submit any username he wants and get the presentations for that user. I don't know too much about Spring Security and I don't even know the right question to ask google to get an answer so I'm stuck...
Any help will be appreciated, thanks!

You don't have to pass username to your controller method. The currently authenticated user is available through a number of different mechanisms in Spring.The simplest way to retrieve the currently authenticated principal is via a static call to the SecurityContextHolder like this :
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = authentication.getName();
So you can refactor you method getAllPresentations() in service class to accept username as argument, and then you can load user by passed username and return presentations for that user.

One way to do what you want is to use #PostFilter annotation to filter List<Presentation> that the authenticated user owns or if the authenticated user has a role ADMIN like this:
#GetMapping
#PostFilter("filterObject.user.username == authentication.principal.username or hasRole('ADMIN')")
public List<PresentationDTO> getAllPresentations() {
return presentationService.getAllPresentations();
}

Related

How to implement Password validation in Spring Boot

I have a Website written in Spring Boot where you can change your password.
Now I want the user to follow some specific rules ( password length, ... ) for changing his/her password.
My problem is that I get a whole list of users and I cannot use #ValidPassword on this list.
As far as I understand is that you have to use it on fields.
So does my Controller look like:
#PostMapping
public String updateOldPassword(#ModelAttribute
#Valid UserCreationDto userTableSettings,
#RequestParam("radiobutton") String radiobuttonName, BindingResult result, Model model, Errors errors)
This is my UserCreationDto:
public class UserCreationDto {
private List<User> users;
...
And here comes my List where I use the #ValidPassword annotation, however, it is not triggered and I think that I have to move it into my UserCreationDto class but then I cannot use the List<User> anymore.
#Data
public class User {
//#SafeHtml prevents XSS ( Cross-Site Scripting )
#SafeHtml
private String username;
#ValidPassword
private String password;
private String anzeigename;
private String dienstnummer;
private long id;
private Boolean isActive;
}
I hope I described my problem as clearly as possible.
Maybe someone has a good hint for me how to solve this problem.
Thank you very much.

How to handle user privilege in spring security and inside thyemeleaf

I'm not new to java (i did some projects with javafx and jee ) but I'm new to the whole spring thing
Here is what my scenario is
All users have same level of access but some have more accee defiened by their role in the database ( there can be upto 25!(yes that is factorial) roles all created dynamically by the main admin) .
My template is made of 3 fragments (header with admin links that are available to users associated to their roles , side bar available to everyone and footer)
So I'm stuck in how to make a global variable for the header part to check the logged in user on each page load and choose the correct links
I know how to do this with jee and jsp but not with spring
Thanks in advance.
Use a repository and hold your session data after login like below
#Repository
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class LoginRepository {
private String accessToken;
private String name;
private String password;
private String userId;
private String rolename;
private String department;
}
then use #ControllerAdvice to pass this in every Model
#Autowired
LoginRepository settings;
#ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("rolenamecode", settings.getRolename());
.......
}
Then based on your role or other parameters (th:if) you can load your links in you page

Spring Data REST - prevent property edit based on role

I use Spring Boot with Spring Data REST for data access and Spring Security for access restriction.
Assume I've got simple entity:
#Entity
public class Person {
#Id #GeneratedValue
private Long id;
private String firstName;
private Boolean isAuthorizedForClasifiedData;
}
I've got two roles in the application: USER and ADMIN.
Is there any simple way to prevent USER from changing value of isAuthorizedForClasifiedData while allowing ADMIN to update it?
The only solution I came up with is writing your own setter method.
public void setIsAuthorizedForClasifiedData(Boolean isAuthorizedForClasifiedData) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Optional<? extends GrantedAuthority> role_admin = authentication.getAuthorities().stream().filter(role -> role.getAuthority().equals("ROLE_ADMIN")).findAny();
role_admin.orElseThrow(() -> new YourOwnForbiddenException());
this.test = test;
}

Security filtering entity collection with jax-rs and jersey

I've been looking around on how to filter results based on certain security roles. I've been looking at filters but this just seems to filter out certain fields rather than the complete entity itself. Let me try to explain what I need. Lets say I have 2 entity classes:
public class Project {
private Long id;
private String name;
private String description;
// getters and setters
}
public class User {
private Long id;
private String name;
private String email;
// getters and setters
}
resource:
#Path("projects")
#Produces("application/json")
public class ProjectsResource {
#GET
public List<Project> getProjects() {
return getDetailedProjects();
}
}
Also I have 2 companies, each with their own projects. For convenience lets call them company A and company B.
What I would like to achieve is that 3 users all using this same resource get different results based on their security role.
User 1: the Super user, should get everything
User 2: manager for company A, should only get projects for company A
User 3: manager for company B, should only get projects for company B
How should I go about doing this? It just doesn't seem right to do this filtering in each resource.
I'm using jax-rs, with role based security. Users are linked to a role which grants them access to projects for a specific company.
I think you would have to parameterize your getDetailedProjectsmethod with the accessing user.
To achieve that you can inject the jersey SecurityContextinto your Resource like so (from jersey documentation):
#Path("basket")
public ShoppingBasketResource get(#Context SecurityContext sc) {
if (sc.isUserInRole("PreferredCustomer") {
return new PreferredCustomerShoppingBasketResource();
} else {
return new ShoppingBasketResource();
}
}
The securityContext.getUserPrincipal().getName()method will allow you to get hold of the requesting user and map that to a method parameter (or use it plain).

spring security datamodel

I'm currently using the spring-security libraries and I asked myself the following question: How should I combine my database model with the spring-security tables?
As you know spring-security needs two tables (users and authorities) to define an authentication manager in the database. From my pov there are now two possibilities where I store my additional user-information (like email, lastname, last-logged-on, ....)
I could have a plain user-table for authentication purposes and another one for the rest (linked by the username)
I extend the user-table of spring-security with my necessary attributes.
What is the best design from your perspective? What are your experiences?
Lomu
I created a POJO User which represents the User entity as conceived by the Spring Security library, and secondly I created a POJO ProfiledUser to represent a specialized type of user of my application. It is called ProfiledUser because I needed a user associated to a profile. Of course, a similar approach can be applyied for every type of user you need to represent. Basically, if you need more than one type of user you can make your classes to extend the User POJO.
In the following you find the class, with the JPA annotations.
#Entity
#Table(name="USERS")
#Inheritance(strategy=InheritanceType.JOINED)
public class User implements UserDetails {
private static final long serialVersionUID = 1L;
private long id;
private String username;
private String password;
private boolean enabled = true;
Set<Authority> authorities = new HashSet<Authority>();
//...getters & setters
}
#Entity
#Table(name="PROFILED_USERS")
public class ProfiledUser extends User{
private static final long serialVersionUID = 1L;
//some custom attributes
private PersonalData personalData;
private ContactData contactData;
private AddressData addressData;
//...getters & setters
}
If you need to represent only one type of user, I think it should work to add attributes to the User class. However, I prefer to separate the abstract concept of user defined by the Spring Security framework from my business logic. So I'd recommend to implement your own SomethingUser and extend the User class.
A person is a person and you should have a class/table representing a person†.
A user is a user, and is different from a person (hence the two different words), and you should have a class/table representing a user.
Can a person exist without a user? Yes
Can a user exist without a person? No, a username belongs to someone.
#Entity
abstract class Party {
#Id
Long id;
String name;
#OneToMany
List<User> usernames = new ArrayList<>();
}
#Entity
class Individual extends Party {
DateTime dateOfBirth;
}
#Entity
class User {
#ManyToOne
Party party;
String username;
String password; //you better use BCrypt/Blowfish hashing!
Boolean enabled = true;
}
You could instead use a #OneToOne relationship if you only want one username per party.
† Actually you should have a more abstract class/table representing a legal party.

Resources