Now, we will create the authorization for our application. We will learn how to create permission and groups as well as how to apply permisions to business logic.
The basic configuration has already been created for you from the application template.
You can find it in the class BaseWebSecurityConfig
(and BaseUserDetailsService
).
As you can see in a first file there are some users configured already.
First we need to configure the available permissions of our application and group them for better administration. Next we extend our use-cases with authorization so they can only be executed by users with the required permission.
We open the class ApplicationAccessControlConfig
and have a look.
Here we define permissions as constants following devon4j naming conventions.
For our entities Item
and Order
we create according permissions as constants here:
-
FindItem
-
SaveItem
-
DeleteItem
-
FindOrder
-
SaveOrder
-
DeleteOrder
Also we add this group (yes, ommit prefix for simplicity so the waiter account defined in ApplicationAccessControlConfig
matches):
public static final String GROUP_WAITER = "waiter";
Additionally, we add a new user in BaseWebSecurityConfig.configureGlobal
:
auth.inMemoryAuthentication() ...
.and().withUser("waiter").password(this.passwordEncoder.encode("waiter")).roles("waiter");
Note: This entire login-mechanism with hardcoded accounts including passwords is obviously entirely inacceptable for a real application. In a real project you would here integrate with an Identity- und Access-Management (IAM). However, to start fast, easy and agile, the devon4j application template intentionally ships with this as a starting point to be then replaced. The real IAM integration will differ between projects and add an additional application with quite some complexity. Typically, this will happen via JWT authentication.
ATTENTION: Further, there is also the issue devon4j#385: currently the roles (in the above code example roles("waiter")
) are ignored and in reality the login is automatically used as the only role of the user.
Next, we go to the ApplicationAccessControlConfig
constructor and add the new Find*
permissions to the group readMasterData
.
Then, we create the waiter group that inherits from readMasterData
and extends it with the other new permissions:
group(GROUP_WAITER, readMasterData, PERMISSION_SAVE_ITEM, PERMISSION_DELETE_ITEM, PERMISSION_SAVE_ORDER, PERMISSION_DELETE_ORDER);
Test your authorization by creating a new JUnit test:
-
Use
TestUtil
to mock the login in your test methods (e.g.TestUtil.login("testuser", ApplicationAccessControlConfig.PERMISSION_SAVE_ITEM)
). Then call the authorized use-case method to test. -
Override
doTearDown()
and addTestUtil.logout()
to revert the login after each test method and prevent side-effects on other tests. -
Ensure you write both positive tests that work if the mocked user has appropriate permissions as well as negative tests that fail because the required permission is not granted to the user. For negative tests you may use
assertThrows
method.
If you have some time left, you can create an integration test based on RestServiceTest
and Service-Client (see also exercise 4 services) including permission checks (positive and negative test).