Статьи

Тестирование защищенных EJB с Arquillian

Тестирование защищенных EJB исторически было трудно получить правильно. До сих пор я использовал проприетарные методы, такие как JBossLoginContextFactory, описанные в статье Тестирование защищенных EJB-компонентов на WildFly 8.1.x с Arquillian, для тестирования защищенных EJB- компонентов .

В течение этого года Devoxx , Дэвид Блевинс , основатель проекта Apache TomEE — облегченного сервера приложений Java EE, предоставил мне небольшую хитрость, которую мы можем использовать для стандартного подхода к обеспечению безопасности Java EE, который работает на всех серверах, совместимых с Java EE. ,

Пример, используемый в этом посте, доступен в javaee-testing / security на GitHub.

Код

Код для тестирования включает в себя сущность и службу EJB следующим образом.

Книжная сущность

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
@Entity
public class Book {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String isbn;
    private String title;
 
    public Book() {
    }
 
    public Book(String isbn, String title) {
        this.isbn = isbn;
        this.title = title;
    }
 
    // getters and setters omitted for brevity
}

Книжная полка EJB Service

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Stateless
public class BookshelfService {
 
    @PersistenceContext(unitName = "bookshelfManager")
    private EntityManager entityManager;
 
    @RolesAllowed({ "User", "Manager" })
    public void addBook(Book book) {
        entityManager.persist(book);
    }
 
    @RolesAllowed({ "Manager" })
    public void deleteBook(Book book) {
        entityManager.remove(book);
    }
 
    @PermitAll
    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
    public List<Book> getBooks() {
        TypedQuery<Book> query = entityManager.createQuery("SELECT b from Book as b", Book.class);
        return query.getResultList();
    }
}

Тестовый класс использует Arquillian для интеграционных тестов и утверждает, что роли безопасности, определенные в нашем EJB, соблюдаются.

Сервисные тесты на книжной полке

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
@RunWith(Arquillian.class)
public class BookshelfServiceIT {
 
    @Inject
    private BookshelfService bookshelfService;
    @Inject
    private BookshelfManager manager;
    @Inject
    private BookshelfUser user;
 
    @Deployment
    public static JavaArchive createDeployment() throws IOException {
        return ShrinkWrap.create(JavaArchive.class, "javaee-testing-security.jar")
                .addClasses(Book.class, BookshelfService.class, BookshelfManager.class, BookshelfUser.class)
                .addAsManifestResource("META-INF/persistence.xml", "persistence.xml")
                .addAsManifestResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml"));
    }
 
    @Test
    public void testAsManager() throws Exception {
        manager.call(new Callable<Book>() {
            @Override
            public Book call() throws Exception {
                bookshelfService.addBook(new Book("978-1-4302-4626-8", "Beginning Java EE 7"));
                bookshelfService.addBook(new Book("978-1-4493-2829-0", "Continuous Enterprise Development in Java"));
 
                List<Book> books = bookshelfService.getBooks();
                Assert.assertEquals("List.size()", 2, books.size());
 
                for (Book book : books) {
                    bookshelfService.deleteBook(book);
                }
 
                Assert.assertEquals("BookshelfService.getBooks()", 0, bookshelfService.getBooks().size());
                return null;
            }
        });
    }
 
    @Test
    public void testAsUser() throws Exception {
        user.call(new Callable<Book>() {
            @Override
            public Book call() throws Exception {
                bookshelfService.addBook(new Book("978-1-4302-4626-8", "Beginning Java EE 7"));
                bookshelfService.addBook(new Book("978-1-4493-2829-0", "Continuous Enterprise Development in Java"));
 
                List<Book> books = bookshelfService.getBooks();
                Assert.assertEquals("List.size()", 2, books.size());
 
                for (Book book : books) {
                    try {
                        bookshelfService.deleteBook(book);
                        Assert.fail("Users should not be allowed to delete");
                    } catch (EJBAccessException e) {
                        // Good, users cannot delete things
                    }
                }
 
                // The list should not be empty
                Assert.assertEquals("BookshelfService.getBooks()", 2, bookshelfService.getBooks().size());
                return null;
            }
        });
    }
 
    @Test
    public void testUnauthenticated() throws Exception {
        try {
            bookshelfService.addBook(new Book("978-1-4302-4626-8", "Beginning Java EE 7"));
            Assert.fail("Unauthenticated users should not be able to add books");
        } catch (EJBAccessException e) {
            // Good, unauthenticated users cannot add things
        }
 
        try {
            bookshelfService.deleteBook(null);
            Assert.fail("Unauthenticated users should not be allowed to delete");
        } catch (EJBAccessException e) {
            // Good, unauthenticated users cannot delete things
        }
 
        try {
            // Read access should be allowed
            List<Book> books = bookshelfService.getBooks();
            Assert.assertEquals("BookshelfService.getBooks()", 0, books.size());
        } catch (EJBAccessException e) {
            Assert.fail("Read access should be allowed");
        }
    }
}

Хитрость заключается в двух вспомогательных EJB-компонентах, которые позволяют нашему тестовому коду выполняться в нужной области безопасности с помощью стандартной аннотации @RunAs .

Роль менеджера книжной полки

1
2
3
4
5
6
7
8
@Stateless
@RunAs("Manager")
@PermitAll
public class BookshelfManager {
    public <V> V call(Callable<V> callable) throws Exception {
        return callable.call();
    }
}

Книжная полка Роль пользователя

1
2
3
4
5
6
7
8
@Stateless
@RunAs("User")
@PermitAll
public class BookshelfUser {
    public <V> V call(Callable<V> callable) throws Exception {
        return callable.call();
    }
}

Бег

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.samaxes.javaeetesting.security.BookshelfServiceIT
nov 23, 2014 2:44:48 AM org.xnio.Xnio <clinit>
INFO: XNIO version 3.2.0.Beta4
nov 23, 2014 2:44:48 AM org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.2.0.Beta4
nov 23, 2014 2:44:49 AM org.jboss.remoting3.EndpointImpl <clinit>
INFO: JBoss Remoting version (unknown)
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 36.69 sec - in com.samaxes.javaeetesting.security.BookshelfServiceIT
 
Results :
 
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0

Удачных тестов!

Ссылка: Тестирование защищенных EJB-компонентов с Arquillian от нашего партнера JCG Сэмюэля Сантоса в блоге Samaxes .