Book Image

Jakarta EE Cookbook - Second Edition

By : Elder Moraes
Book Image

Jakarta EE Cookbook - Second Edition

By: Elder Moraes

Overview of this book

Jakarta EE is widely used around the world for developing enterprise applications for a variety of domains. With this book, Java professionals will be able to enhance their skills to deliver powerful enterprise solutions using practical recipes. This second edition of the Jakarta EE Cookbook takes you through the improvements introduced in its latest version and helps you get hands-on with its significant APIs and features used for server-side development. You'll use Jakarta EE for creating RESTful web services and web applications with the JAX-RS, JSON-P, and JSON-B APIs and learn how you can improve the security of your enterprise solutions. Not only will you learn how to use the most important servers on the market, but you'll also learn to make the best of what they have to offer for your project. From an architectural point of view, this Jakarta book covers microservices, cloud computing, and containers. It allows you to explore all the tools for building reactive applications using Jakarta EE and core Java features such as lambdas. Finally, you'll discover how professionals can improve their projects by engaging with and contributing to the community. By the end of this book, you'll have become proficient in developing and deploying enterprise applications using Jakarta EE.
Table of Contents (14 chapters)

Running your first Jakarta Security code

Security is one of the top concerns when you build an enterprise application. Luckily, the Jakarta EE platform now has this API that handles many of the enterprise requirements in a standardized way.

In this recipe, you will learn how to define roles and give them the right authorization based on rules defined in the methods that manage sensitive data.

Getting ready

We start by adding our dependencies to the project:

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>openejb-core</artifactId>
<version>7.0.4</version>
</dependency>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>8.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>

How to do it...

You need to perform the following steps to try this recipe:

  1. We first create a User entity:
@Entity 
public class User implements Serializable{

@Id
private Long id;
private String name;
private String email;

public User(){
}

public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}

//DON'T FORGET THE GETTERS AND SETTERS
//THIS RECIPE WON'T WORK WITHOUT THEM
}
  1. Here, we create a class to store our security roles:
public class Roles {
public static final String ADMIN = "ADMIN";
public static final String OPERATOR = "OPERATOR";
}
  1. Then, we create a stateful bean to manage our user operations:
@Stateful
public class UserBean {

@PersistenceContext(unitName = "ch01-security-pu",
type = PersistenceContextType.EXTENDED)
private EntityManager em;

@RolesAllowed({Roles.ADMIN, Roles.OPERATOR})
public void add(User user){
em.persist(user);
}

@RolesAllowed({Roles.ADMIN})
public void remove(User user){
em.remove(user);
}

@RolesAllowed({Roles.ADMIN})
public void update(User user){
em.merge(user);
}

@PermitAll
public List<User> get(){
Query q = em.createQuery("SELECT u FROM User as u ");
return q.getResultList();
}
  1. Now, we need to create an executor for each role:
public class RoleExecutor {

public interface Executable {
void execute() throws Exception;
}

@Stateless
@RunAs(Roles.ADMIN)
public static class AdminExecutor {
public void run(Executable executable) throws Exception {
executable.execute();
}
}

@Stateless
@RunAs(Roles.OPERATOR)
public static class OperatorExecutor {
public void run(Executable executable) throws Exception {
executable.execute();
}
}
}
  1. And finally, we create a test class to try our security rules.
  2. Our code uses three test methods: asAdmin(), asOperator(), and asAnonymous().
  3. First, it tests asAdmin():
    //Lot of setup code before this point

@Test
public void asAdmin() throws Exception {
adminExecutor.run(() -> {
userBean.add(new User(1L, "user1", "[email protected]"));
userBean.add(new User(2L, "user2", "[email protected]"));
userBean.add(new User(3L, "user3", "[email protected]"));
userBean.add(new User(4L, "user4", "[email protected]"));

List<User> list = userBean.get();

list.forEach((user) -> {
userBean.remove(user);
});

Assert.assertEquals("userBean.get()", 0,
userBean.get().size());
});
}
  1. Then, it tests asOperator():
    @Test
public void asOperator() throws Exception {

operatorExecutor.run(() -> {
userBean.add(new User(1L, "user1", "[email protected]"));
userBean.add(new User(2L, "user2", "[email protected]"));
userBean.add(new User(3L, "user3", "[email protected]"));
userBean.add(new User(4L, "user4", "[email protected]"));

List<User> list = userBean.get();

list.forEach((user) -> {
try {
userBean.remove(user);
Assert.fail("Operator was able to remove user " +
user.getName());
} catch (EJBAccessException e) {
}
});
Assert.assertEquals("userBean.get()", 4,
userBean.get().size());
});
}
  1. And, finally, it tests asAnonymous():
    @Test
public void asAnonymous() {

try {
userBean.add(new User(1L, "elder",
"[email protected]"));
Assert.fail("Anonymous user should not add users");
} catch (EJBAccessException e) {
}

try {
userBean.remove(new User(1L, "elder",
"[email protected]"));
Assert.fail("Anonymous user should not remove users");
} catch (EJBAccessException e) {
}

try {
userBean.get();
} catch (EJBAccessException e) {
Assert.fail("Everyone can list users");
}
}

This class is huge! For the full source code, check the link at the end of this recipe.

How it works...

The whole point in this recipe is to do with the @RolesAllowed, @RunsAs, and @PermitAll annotations. They define what operations each role can do and what happens when a user tries an operation using the wrong role.

There's more...

What we did here is called programmatic security, that is, we defined the security rules and roles through our code (the program). There's another approach called declarative security, where you declare the rules and roles through application and server configurations.

One good step up for this recipe is to evolve the roles management to a source outside the application, such as a database or a service.

See also

  • You can stay tuned with everything related to Jakarta Security at https://github.com/javaee-security-spec.
  • The source code of this recipe is at https://github.com/eldermoraes/javaee8-cookbook/tree/master/chapter01/ch01-security.