How do you do security? Spring/JPA/ACLs/ILAY/Whatever :)

Hello Together,

it is a long time but it is time for a new application.
I learned alot in the past projects and the new business application i want to start from scratch.

I am planning on integrating Sping Boot and JPA and thinking alot about security, because other third parties, like customer should have access to the application.
My main question/concern is the data security.

For example:
I have set up a new project with Spring and JPA for Login and automatic “database management”. Now i have set up roles like Admin, User, Customer etc.
The main security control on all tutorials and pages i read is to control the views, which are accessible by the roles.
For Example have a Role for UserManagement, which is only accessible by the Admin-Role. So far thats ok and i am fine with it.
More complex would be information based on the user, which is for example linked to a customer, which could be many users.
Then this user should only see entities for this customer, like an order of other users of the customer.
Then i cannot simply restrict to access to the view. Instead i have to restrict the data.
For example on an EditorForm with an Binder i would not show edit buttons, if you do dont have the rights to edit the item. But this feels to me only like disabling the button, but not the logic behind, which could somehow triggered by the user, even by accident because the form could be just show the button, because the UI designer has forgotten about a new role.

On something like an list to show for the user i got some more problems, which i done like this in the past:

My traditinal way of something like that would be:


class SomeSecuredController(){
    ....
    public List<Order> getOrders(){

        PreparedStatment stmt = getConnection().prepareStatement("Select * FROM orders JOIN customerPermissions ON orders.fk_customerid = customerPermissions WHERE customerPermissions.fk_userid = ?");
        stmt.setInt(1, User.getID());   
        return executeQueryAndBuildOrders(stmt);
    }   
    .... 
}

Which JPA i could set up the repository with a “global” where clause on the entity with hibernate:

@Entity
@Where(1 = "SELECT COUNT(ID) as c FROM customerPermissions WHERE fk_customerid = id and fk_userid = " + User.getID())
class Order{
    ...
}

But i don’t think thats a good idea, it looks awfull and sounds stupid.

I am just wondering if this is the best approach or if there is something better, i just can find.

I am thinking of some “simple” way to get “security” on all my data and simple use.
My goal would be to have an Simple to use backend, which only gives me the items the user has access to and only let me write, and save the items the user has access to.

Could you give me little example of how you would do this in your application or if there is some good API/Framework to use. I found for example ILAY in the Vaadin tutorials but there is restriction to Views/Routes only, as far as i found from the docs.

Thanks for your time.

Hi Michael

You mention different aspects of data security. Let me try to categorize them, and give my 2 cents for each:

1) user can only see his own data
Data Access Security is very specific to your own model architecture and business requirements. It cannot (at least not to my knowledge) be simplified easily.
As you have already done yourself, you need to include the user (or their organization, or role, etc, depending on what kind of security requirements you face) into the db query, in order to be sure to only receive data which the user is allowed to access. This means that your model must have the needed relations between user, customer, and whatever data you want to read.

2) Editing only items that user has access to
This is actually already achieved by 1) because we can only edit/save the items that have been loaded. If we only load the ones that the user is allowed to, the user can edit/save ALL the items he sees. If not each user has the right to edit/save EVERY item he can see, then read on with 3)

3) Different UI actions for different user roles
In this step we can differentiate between read-and-write users, and read-only users. This is not done with relations of the data models as in step 1, but with the users role(s). An Admin or a Manager role could be able to edit all accessible data, while a simple user could be only allowed to fetch the data but not modify it.
You can achieve this by i.e. disabling the grid editor, or if not a grid then hiding (or disabling) all Edit-, Insert-, and Delete-Buttons that you have when the user does not have a certain role. (Note: you should also check user roles upon clicks on those buttons even if disabled or hidden, because these things could be overridden manually in the front-end.)

4) Having complete Views only accessible to certain roles
If the logged in user does not even have permission to navigate to a certain view because of his userrole, you can achieve this by not giving the user the opportunity to navigate there easily in the first place (for example don’t add a RouterLink to this view in the menu). But since the user could simply type in the correct URL, you should also add a global UIInitListener that compares the users roles with the value of the @Secured (or similar) annotation on your view. A similar implementation of a global UIInitListener can be seen in the [Setting up Spring Security for Vaadin applications Tutorial]
(https://vaadin.com/learn/tutorials/securing-your-app-with-spring-security/setting-up-spring-security#_secure_router_navigation) - but there they do not compare user roles, they only check if the user is logged in. If you have questions about this part, I can follow up with some code if you wish.

Hello Kaspar,

first of all - Thank you for your time and afford and detailed answer.

I catch up on your points in reverse order :slight_smile:

4. Having the Views restricted to certain Roles i think i thats the easy part and some annotaion like @Secured will work perfect for this one.
3. Disabling the Buttons/Editors in the frontend is the part which which i was wondering if it is enough. Therefore you talked about that i have to also check on some things like a controller and there i have not the correct idea right now how to to that one.

2. and 1. The main question is on what part we decide what a user can to and what not. Obviosly we do not fetch all items to an grid and then filter the datasource of the grid for items which are availible to a the user :slight_smile: I am thinking about a more simple, but complex structure and the main security risk is the user object itself is secure to can’t be changed in some way.

Lets assume a dream world, a controller would look like this:


@Repository
class OrderController(){

    //Checks if the user has the ROLE_ORDERVIEWER on the specific customer
    @Filter(User.getCustomers().map(c -> e.getRoles()),"ROLE_ORDERVIEWER")
    public List<Order>findOrders();


    //Checks if the user has the ROLE_ORDERVIEWER on the specific customer
    @Filter(User.getCustomers().map(c -> e.getRoles()),"ROLE_ORDERSAVER")
    @Except(order.getOwner() == User.getOwner())
    public void save(Order order) throws YouAreNotAllowedToSaveExceotion;

    @Filter(User.getCustomers().map(c -> e.getRoles()),"ROLE_ORDERSAVER")
    @Except(order.getOwner() == User.getOwner())
    public boolean canSave(Order e);
}

I think now code is perfect and there will be always bugs. I just would like the security on the “Repository/Controller” Level instead on the UI. If the UI does not disable an Edit button then i will always fail on the backend.

I just have no Idea how to code it to have a simple usage on the ControllerLevel and a role based permission on customer level, not on all.

 - Users
 - - Customers
 - - - Roles
  1. Disabling the Buttons/Editors in the frontend is the part which which i was wondering if it is enough.

just disabling a button will not be enough, because an experienced user could edit the HTML and remove the disabled tag, therefore making the button clickable again against your wishes. Therefore, you need to check the users privilege as first thing in the button click listener

Button saveButton = new Button("Save", click -> {
	// backend restriction: check on server side if user indeed has the needed role
	if(!MySecurityUtils.userHasRole("ROLE_ORDERSAVER")){ // check if the User Authentication's authorities contain "ROLE_ORDERSAVER".
		return;
	} else {
		// user is definitely allowed to save
		// do the save here
	}
});
add(saveButton);

// frontend restriction: add 'disabled' to html tag of button
if(!MySecurityUtils.userHasRole("ROLE_ORDERSAVER")){ // check if the User Authentication's authorities contain "ROLE_ORDERSAVER".
	saveButton.setEnabled(false);
}
  1. and 1. Obviosly we do not fetch all items to an grid and then filter the datasource of the grid for items which are availible to a the user :slight_smile:

Yes that would be really bad.
I think we are talking about the same thing here. I’m just unaware of how @Filter and @Except work, but if I had to guess from the context I think this will do the same thing in the end as I was trying to say. I understand now that you want to make sure on the repository level that it can only save the order if the user is allowed to do so. I have to admit I don’t know how to achieve this. But IMO if you do backend checks in the button click listeners as described above, you should be safe, because it doesn’t just rely on HTML but it will read userroles from the Authentication of the logged-in user. logically, a user without the ROLE_ORDERSAVER role will never reach the point of the repositories’ save method in the first place.

Edit: here is my method MySecurityUtils.userHasRole for code completeness

public static boolean userHasRole(String role){
	Authentication userAuthentication = SecurityContextHolder.getContext().getAuthentication();
	List<String> allowedRoles = Arrays.asList(role);
	return userAuthentication != null && userAuthentication.getAuthorities().stream().map(GrantedAuthority::getAuthority)
			.anyMatch(allowedRoles::contains);
}

There a two points:

The button Disable by “html”

There would be then a check in the front if i check in the button click.
One thing i will try, because i just don’t know is what vaadin does, when a button is disabled. A secure thing would be to remove the clicklistener from the button and not just disable the html. But i right now, don’t know what vaadin does when we disable a button or set the visiblilty to false.

Filter and Except

The @Filter and @Except are made up annotations what i would love to have or something like that. Something like @Secure for ViewClasses but on Method level and not for the views. I just thought there would be a really nice existing libary for that :slight_smile:

A secure thing would be to remove the clicklistener from the button

You can do that! When you add a ClikListener to a button, it returns a Registration instance, which you can remove again.

Button button = new Button();
Registration registration = button.addClickListener(click -> {...});

// when disabling the button, also remove its clicklistener
registration.remove();

In the end, this will have the same effect as the solution I gave in my last comment under point 3 (check the user-role again inside the click listener) - both times, clicking on the button will do nothing, even if the user manually enables the button.

@Filter and @Except

You could in theory write some extended SQL query that will check the users roles. I’m not sure how to do this for an insert statement, but for select and update statements you can specify user roles in a where clause. Sorry if my SQL is not valid, but I hope it still brings my point across.

@Repository
class OrderController(){

    @Query("SELECT Order FROM Order o JOIN User u ON o.user = u WHERE o.user = :user AND u.roles LIKE '%ROLE_ORDERSAVER%'")
    public List<Order>findOrdersForUser(@Param("user") User user);
}