Spring Data REST - prevent property edit based on role - spring

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;
}

Related

How should I design endpoint to return different models depending on User role in 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();
}

Spring Data Rest Does Not Update Default Value in DB

I have a Spring Boot application using Spring Data REST. I have a domain entity called User with a boolean field isTeacher. This field has been already setup by our DBA in the User table with type bit and a default value of 1:
#Data
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // This Id has been setup as auto generated in DB
#Column(name = "IS_TEACHER")
private boolean isTeacher;
}
And the User repository:
public interface UserRepository extends CrudRepository<User, Long>{
}
I was able to add a new user by giving the below request and POST to http://localhost:8080/users, a new user was created in the DB having isTeacher value 1:
{
"isTeacher" : true
}
However, when I tried to change IS_TEACHER by giving PATCH (or PUT) and this request:
{
"isTeacher" : false
}
The response showed that "isTeacher" is still true and the value didn't get changed in the table either. Can someone please let me know why this is happening?
The issue is due to #Data annotation of lombok is ignoring if you have a field that start with isXx it generates getters and setters to boolean with isTeacher for getters and setTeacher for setters then you are not able to update correctly your property, if you put "teacher" when updating should work but you should solve this by overriding that setter.
#Setter(AccessLevel.NONE) private boolean isTeacher;
public void setIsTeacher(boolean isTeacher) {
this.isTeacher = isTeacher;
}

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

Externalize mongo json query using spring boot

I have just started using spring data MongoDb with Spring-Boot.
I have some mongo based json queries added in the interface using #query annotation when using spring data repository.
I want to know if it is possible to externalize or separate out the JSON query outside the codebase so that it can be optimized separately and
also not having it mixed with code.
Thanks for your suggestions.
This is the code which i have added in my interface and annotated with #query annotation.
#Query("{ 'firstname' : ?0 ,'lastname': ?1}")
List findByCriteria(String firstname,String lastname);
The above is a simple example. I have complex conditions involving $and and $or operators too .
What i basically want to achieve is externalize the above native mongo json query to a config file and refer that in the above annotation.
Spring data supports something similar when using jpa with hibernate. But not sure if we can do the same using spring data mongodb with spring boot.
Do like this (I am explaining only for the API)
Suppose you have an Entity user
At the Top there will be User domain
public class User extends CoreDomain {
private static final long serialVersionUID = -4292195532570879677L;
#Length(min = 2)
private String name;
#Length(min = 2)
#UniqueUserName(message = "User name already registered,Please choose something Different")
private String userName;
#Length(min = 6)
private String password;
}
User Controller
User Service (Interface)
User ServiceImpl(Service Implementation)
Mongo Repository(Since, I have MongoDb)
Now in userController you will take all the queries , Param(Parameters) , Pagerequest like this
public class UserController extends CoreController {
#Autowired
private UserService userService;
/*
* This controller is for getting the UserDetails on passing the UserId in
* the #param Annotation
*/
#GET
#Path("{id}")
public User getUser(#PathParam("id") String UserId) {
User user = new User();
user = userService.findUserId(UserId);
if (user == null)
throw new NotFoundException();
log.info("The userId you searched is having the details as :" + user);
return user;
}}
For serviceInterface you will have :
public interface UserService {
// Boolean authenticateUser(User user);
User findUserId(String UserId);
}
For serviceImpl :
public class UserServiceImpl implements UserService {
#Setter
#Autowired
private UserRepository userRepository;
/*
* This method will find user on the basis of their userIds passed in the
* parameter.
*/
#Override
public User findUserId(String UserId) {
User userIdResult = userRepository.findOne(UserId);
log.info("The userDetail is" + userIdResult);
return userIdResult;
}
In mongoRepository for user we will have:
A default query findById(String userId);
Hopefully this will help you.

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