Limiting access to data of other users

Now, this is a little more tricky, as this requires us to change code at the service layer on the backend, but it is not hard. Let's get right to it.

Let's start with the product order entity. Let's modify the findAll method in src/main/java/com/mycompany/store/service/ProductOrderService.java as follows:

    @Transactional(readOnly = true)
public Page<ProductOrder> findAll(Pageable pageable) {
log.debug("Request to get all ProductOrders");
if (SecurityUtils.isCurrentUserInRole(AuthoritiesConstants.ADMIN)) {
return productOrderRepository.findAll(pageable);
} else
return productOrderRepository.findAllByCustomerUserLogin(
SecurityUtils.getCurrentUserLogin().get(),
pageable
);
}

As you can see, we modified the original call to productOrderRepository.findAll(pageable) so that we call it only when the current user has the Admin role, else we call findAllByCustomerUserLogin, but our generated ProductOrderRepository interface does not have this method yet so let's add that. In src/main/java/com/mycompany/store/repository/ProductOrderRepository.java let's add a new method as follows. Currently, the interface doesn't have any methods and only uses methods inherited from JpaRepository:

Page<ProductOrder> findAllByCustomerUserLogin(String login, Pageable pageable);

There is a lot of magic going on here. This is a Spring Data interface and hence, we can simply write a new method and expect Spring Data to create an implementation for this automatically; we just need to follow the naming conventions. In our use case, we need to find all product orders where the user relationship for the customer has the same login as our current logged in user. In SQL, this would be as follows:

select * from product_order po cross join customer c cross join jhi_user u where po.customer_id=c.id and c.user_id=u.id and u.login=:login

In simple terms, we could say find all product orders where customer.user.login equals login and that is exactly what we have written as the findAllByCustomerUserLogin method. The entity under operation is implicit, hence the product order is omitted. By providing the Pageable parameter we tell Spring Data to provide us a page from the paginated list of entities. You can refer to the Spring Data docs (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/) for more information.

While calling the productOrderRepository.findAllByCustomerUserLogin method we can pass the current user login using the SecurityUtils.getCurrentUserLogin() method. The SecurityUtils class is generated by JHipster as well, as it has useful methods such as getCurrentUserLogingetCurrentUserJWTisAuthenticated, and isCurrentUserInRole.

That is it. Now log in as admin and create two new users, create two customers, and create product orders for each of them. Then log out and log in again as the default user and see if you can see the product order for the newly created user.

Now let's make similar updates for other services. The repository methods for those would be as follows:
For src/main/java/com/mycompany/store/repository/InvoiceRepository:

Page<Invoice> findAllByOrderCustomerUserLogin(String login, Pageable pageable);

For src/main/java/com/mycompany/store/repository/OrderItemRepository:

Page<OrderItem> findAllByOrderCustomerUserLogin(String login, Pageable pageable);

For src/main/java/com/mycompany/store/repository/ShipmentRepository:

Page<Shipment> findAllByInvoiceOrderCustomerUserLogin(String login, Pageable pageable);

Now we need to make similar changes for findOne methods on the services.

For the ProductOrderService it would be as follows:

    @Transactional(readOnly = true)
public ProductOrder findOne(Long id) {
log.debug("Request to get ProductOrder : {}", id);
if (SecurityUtils.isCurrentUserInRole(AuthoritiesConstants.ADMIN)) {
return productOrderRepository.findOne(id);
} else
return productOrderRepository.findOneByIdAndCustomerUserLogin(
id,
SecurityUtils.getCurrentUserLogin().get()
);
}

As you can see, we changed the methods to find one by ID and customer user login. The repository method for the same would be as follows:

ProductOrder findOneByIdAndCustomerUserLogin(Long id, String login);

For src/main/java/com/mycompany/store/repository/InvoiceRepository:

Invoice findOneByIdAndOrderCustomerUserLogin(Long id, String login);

For src/main/java/com/mycompany/store/repository/OrderItemRepository:

OrderItem findOneByIdAndOrderCustomerUserLogin(Long id, String login);

For src/main/java/com/mycompany/store/repository/ShipmentRepository:

Shipment findOneByIdAndInvoiceOrderCustomerUserLogin(Long id, String login);

The same queries can be written using the @Query annotation provided by Spring Data as well.

That's it. We have implemented a good role-based authorization logic for the application.

Let's commit this checkpoint:

> git add --all
> git commit -am "update role based authorization logic"

In a real-world scenario, the changes we have made so far are not enough for an e-commerce website. But since our aim is to learn JHipster and its supported tools rather than to create a feature perfect application, consider this a minimum viable product. To make this e-commerce application usable, we would need to build more features, such as a shopping cart, invoice generation, customer registration, and so on. Why don't you take it up as an assignment and see if you can build more features for this application? This would be part of the next steps to take once you finish the book. The use case and instructions will be detailed in Chapter 14, Best Practices with JHipster.