bekwam courses

WildFly Security with Contexts

December 6, 2020

This article shows how to security Java Persistence Architecture data using JavaEE Contexts in the WildFly app server. The WildFly security subystems authenticates the user and makes that information available throughout the application. This information can be used to parameterize JPA queries that are built on a data model that includes row-level ownership.

The JavaEE code described this article is on GitHub.

One of JavaEE's most important features is security. In the WildFly App Server, a security subsystem establishes a user identity and role set that the application can use for its business logic and data access. This provides an umbrella of protection over a set of components, freeing developers from having to issue security-related calls en route to implementing their functional requirements.

This example references Basic Authentication set up as described in this article.

While you can execute queries based on parameters received from a web service, this can lead to a security hole if those parameters are not verified. For instance, if a service accepts a "user=" parameter from a form, that parameter must be checked against the security subsystem before services are invoked. If that check is skipped -- say some code was moved around -- then a hacker could swap in their own user value and gain unauthorized access.

In JavaEE, we have access to the logged-in user through Contexts. In JAX-RS code, there is the SecurityContext class which when injected gives you access to roles and the username. There's a similar construct in EJB code, the EJBContext class.

This demo is a JAX-RS call invoking an EJB. An HTTP GET for a list of items will return all of the items if the user has the "admin" role. If the user doesn't have the admin role and they have the role "user", then the username will be used to return only those items belonging to the user. Unauthenticated users are turned away with a 401.

The following UML activity diagram shows the role decision running two separate queries.

UML Activity Diagram
Role Determines Query

JAX-RS

This UML class diagram shows the classes involved in the demo. The most important ones are ItemsResource and ItemBean. ItemsResource is a JAX-RS endpoint that delegates immediately to the EJB ItemBean. ItemsResource logs a debug message using the injected SecurityContext. Through the web.xml file, the HTTP GET in ItemsResource can only be invoked by authenticated users.

UML Class Diagram
Classes Servicing Items Request

This is a snippet from ItemsResource showing the injected SecurityContext and delegating call.


    @Inject
    private ItemBean itemBean;

    @Context
    private SecurityContext sc;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<Item> getItems() {
        if( log.isDebugEnabled() ) {
            log.debug("[GET ITEMS] owner (at web layer) is {}",
                    sc.getUserPrincipal().getName());
        }
        return itemBean.findItemsByOwner();
    }

EJB

ItemBean issues a Java Persistence Architecture (JPA) call to the H2 database that ships with WildFly. JPA will build a schema from the marked-up @Entity class "Item". This is the code from ItemBean which will examine the role and route the request to one of two calls. Note that the ItemBean class is annotated with @RolesAllowed("user") which means that the calling service needs to have established this identity.


    @PersistenceContext
    private EntityManager em;

    @Resource
    private EJBContext context;

    public List<Item> findItemsByOwner() {

        if( context.isCallerInRole("admin") ) {
            return this.findAllItems();
        }

        Query q = em.createQuery("SELECT i FROM Item i WHERE i.owner = :owner ORDER BY i.id DESC");
        q.setParameter("owner", context.getCallerPrincipal().getName());
        return q.getResultList();
    }

The user role will retrieve the user from the injected EJBContext and apply that as a parameter for the JPA query. If the user is "admin", then then the following parameter-less call will be made. Notice the @RolesAllowed("admin") call which overrides the "user" annotation on the class itself. This ensures that the call cannot be made by a non-admin user.


    @RolesAllowed("admin")
    public List<Item> findAllItems() {
        Query q = em.createQuery("SELECT i FROM Item i ORDER BY i.owner, i.id DESC");
        return q.getResultList();
    }

There is a second EJB which is invoked at startup which writes out the records used in the demo. Since the H2 database is in-memory, these records need to be added each time the app server starts.

Security is an important feature in JavaEE and it's enforced by the WildFly security subsystem. The information coming directly from the subsystem is available to JAX-RS and EJB code. This means that the application developer doesn't have to worry about missing a parameter or library call which could open up a security hole when coding.


Headshot of Carl Walker

By Carl Walker

President and Principal Consultant of Bekwam, Inc