-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ENDOC-578 Update the RBAC tutorial for Entando 7.1 #587
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,7 @@ The basic security setup for a blueprint-generated application allows any authen | |
|
||
The list of Conferences must be visible to only the `conference-user` and `conference-admin` user roles. | ||
|
||
1. Go to the `src/main/java/com/YOUR-ORG/YOUR-NAME/web/rest` directory | ||
1. Go to `microservices/conference-ms/src/main/java/com/YOUR-ORG/YOUR-APP-NAME/web/rest` | ||
2. Open `ConferenceResource.java` | ||
3. Add the following to the list of imports: | ||
```java | ||
|
@@ -30,80 +30,43 @@ The list of Conferences must be visible to only the `conference-user` and `confe | |
``` | ||
This confines use of the `getAllConferences` method to users who are assigned either the `conference-user` or the `conference-admin` role on the Keycloak client configured for the microservice. | ||
|
||
> Note: In local testing, the default client is `internal`. Refer to the [Spring Security documentation](https://spring.io/projects/spring-security) for more information. | ||
|
||
We also need to modify the blueprint JWT handling to deal with a recent change to the Spring libraries. | ||
|
||
5. Edit `src/main/java/com/mycompany/myapp/config/SecurityConfiguration.java` and make two changes. | ||
* Add this code after the other @Value fields | ||
``` java | ||
@Value("${spring.security.oauth2.client.registration.oidc.client-id}") | ||
private String clientId; | ||
``` | ||
* Modify the following call in the `authenticationConverter` method to provide the clientId field | ||
``` java | ||
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(new JwtGrantedAuthorityConverter(clientId)); | ||
``` | ||
6. Now modify | ||
`src/main/java/com/mycompany/myapp/security/oauth2/JwtGrantedAuthorityConverter.java` to accept the clientId. Three changes are required. | ||
* Remove the @Component annotation on the class definition | ||
```java{1} | ||
@Component | ||
public class JwtGrantedAuthorityConverter implements Converter<Jwt, Collection<GrantedAuthority>> { | ||
``` | ||
* Remove the @Value annotation on the clientId field | ||
```java{1} | ||
@Value("${spring.security.oauth2.client.registration.oidc.client-id}") | ||
private String clientId; | ||
``` | ||
* Modify the constructor to accept the clientId | ||
```java | ||
public JwtGrantedAuthorityConverter(String clientId) { | ||
this.clientId = clientId; | ||
} | ||
``` | ||
|
||
### Step 2: Run your project in a local developer environment | ||
The following commands must be run from your project directory. They leverage the [ent CLI](../../../docs/getting-started/entando-cli.md). | ||
The following commands must be run from your bundle project directory. They leverage the [ent CLI](../../../docs/getting-started/entando-cli.md). | ||
|
||
> Note: Refer to the [Run Blueprint-generated Microservices and Micro Frontends in Dev Mode tutorial](./run-local.md) for details. | ||
> Note: Refer to the [Run Blueprint-generated Microservices and Micro Frontends in Dev Mode tutorial](./run-local.md) for more details. | ||
|
||
1. Start up your Keycloak instance | ||
``` sh | ||
ent prj ext-keycloak start | ||
ent bundle svc start keycloak | ||
``` | ||
2. Start the microservice in another shell | ||
``` sh | ||
ent prj be-test-run | ||
ent bundle run conference-ms | ||
``` | ||
3. Start the tableWidget MFE in a third shell | ||
3. Start the conference-table MFE in a third shell | ||
``` sh | ||
ent prj fe-test-run | ||
ent bundle run conference-table | ||
``` | ||
4. When prompted to select a widget to run, choose the option corresponding to the tableWidget, e.g. `ui/widgets/conference/tableWidget` | ||
|
||
### Step 3: Access the tableWidget MFE | ||
### Step 3: Access the conference-table MFE | ||
|
||
1. In your browser, go to <http://localhost:3000>. This is typically the location of the tableWidget MFE. | ||
2. Access the tableWidget MFE with the default credentials of `username: admin`, `password: admin` | ||
1. In your browser, go to <http://localhost:3000>. | ||
2. Access the conference-table MFE with the default credentials of `username: admin`, `password: admin` | ||
|
||
> Note: Once authenticated, the message "No conferences are available" is generated. If you check your browser | ||
console, you should see a `403 (Forbidden)` error for the request made to `localhost:8080/services/conference/api/conferences`. This is expected because the admin user has not yet been granted the new role. | ||
> Note: Once authenticated, the message "No conferences are available" is generated. If you check your browser console, you should see a `403 (Forbidden)` error for the request made to `localhost:8080/services/conference/api/conferences`. This is expected because the admin user has not yet been granted the new role. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. backtick admin in last line |
||
|
||
### Step 4: Login to Keycloak | ||
|
||
1. Go to <http://localhost:9080> | ||
2. Login using the the default credentials of `username: admin`, `password: admin` | ||
2. Login using the default credentials of `username: admin`, `password: admin` | ||
|
||
### Step 5: Create the `conference-user` and `conference-admin` roles | ||
|
||
Add the `conference-user` and `conference-admin` roles to the `internal` client. | ||
|
||
1. Go to `Clients` → `internal` → `Roles` | ||
2. Click `Add Role` | ||
3. Fill in the `Role Name` with `conference-admin` | ||
3. Fill in the `Role Name` with `conference-user` | ||
4. Click `Save` | ||
5. Repeat these steps to create the `conference-user` role | ||
5. Repeat these steps to create the `conference-admin` role | ||
|
||
> Note: The `internal` client is configured by default in the Spring Boot `application.yml`. | ||
|
||
|
@@ -114,13 +77,13 @@ To grant access to the `getAllConferences` API: | |
1. Go to `Users` → `View all users` → `admin` → `Role Mappings` | ||
2. Select `internal` for the `Client Roles` | ||
3. Move `conference-user` from `Available Roles` to `Assigned Roles` | ||
4. Return to the MFE to confirm you see the full list of Conferences | ||
4. Return to the MFE to confirm you can now see the full list of Conferences | ||
|
||
### Step 7: Restrict the ability to delete Conferences | ||
|
||
The `conference-admin` role should grant a user permission to delete Conferences. To restrict the delete method to the `conference-admin` role: | ||
|
||
1. Go to the `src/main/java/com/YOUR-ORG/YOUR-NAME/web/rest` directory | ||
1. Go to the `src/main/java/com/YOUR-ORG/YOUR-APP-NAME/web/rest` directory | ||
2. Open `ConferenceResource.java` | ||
3. Modify the `deleteConference` method by preceding it with the following annotation: | ||
```java{1} | ||
|
@@ -130,9 +93,9 @@ The `conference-admin` role should grant a user permission to delete Conferences | |
|
||
To verify that a user without the `conference-admin` role is unable to call the delete API: | ||
|
||
1. Restart the microservice. By default this includes rebuilding any changed source files. | ||
2. Once the microservice is available, return to the MFE and try deleting one of the Conferences in the list | ||
3. Verify that attempting to delete via the UI generates a `403 error` in the browser console and an error in the service logs similar to the following: | ||
1. Restart the microservice. By default, this includes rebuilding any changed source files. | ||
2. Return to the MFE and try deleting one of the Conferences in the list | ||
3. Verify that attempting to delete a conference via the UI generates a `403 error` in the browser console. There should be an error in the service logs similar to the following: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "conference" is capitalized in the step above, which is contextually similar |
||
``` | ||
WARN 3208 --- [ XNIO-1 task-3] o.z.problem.spring.common.AdviceTraits : Forbidden: Access is denied | ||
``` | ||
|
@@ -141,7 +104,7 @@ WARN 3208 --- [ XNIO-1 task-3] o.z.problem.spring.common.AdviceTraits : Forbi | |
|
||
The MFE UI can be updated to hide the delete button from a user without the `conference-admin` authority. The key logic checks whether the `internal` client role `conference-admin` is mapped to the current user via the hasResourceRole call. | ||
|
||
1. Go to the `ui/widgets/conference/tableWidget/src/components` directory | ||
1. Go to the `microfrontends/conference-table/src/components` directory | ||
2. Open `ConferenceTableContainer.js` | ||
3. Replace the `onDelete` logic with an additional user permission: | ||
```javascript | ||
|
@@ -164,32 +127,39 @@ Promote the admin user to a full `conference-admin` to reinstate the ability to | |
5. Confirm the delete icon is visible | ||
6. Confirm a Conference can be successfully deleted from the list | ||
|
||
## Notes | ||
### Realm Roles versus Client Authorities | ||
This tutorial utilizes authorities. In Keycloak, authorities are roles mapped to a user for a specific client. It is possible to assign higher-level Realm Roles directly to users, e.g. `ROLE_ADMIN`, but this can result in collisions between applications using the same roles. | ||
### Step 10. Configure the roles in `entando.json` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. prefer this reference to |
||
Entando can automatically add roles to your client (see [the notes below](#notes) for different client options) when your microservice is deployed. | ||
|
||
To implement Realm-assigned roles, the code above must be modified: | ||
- In the backend, use the annotation `@Secured('ROLE_ADMIN)` or `@PreAuthorize(hasRole('ROLE_ADMIN'))` | ||
- In the frontend, use `keycloak.hasRealmRole` instead of `keycloak.hasResourceRole` | ||
1. Modify the `entando.json` by adding the following line to the `microservices/conference-ms`: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove "the" :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. think it's correct either way, with or without the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
```json | ||
"roles": ["conference-admin","conference-user"] | ||
``` | ||
|
||
See the [Spring Security page](https://www.baeldung.com/spring-security-check-user-role) for more examples. | ||
## Next Steps | ||
Follow one of the links below to run the bundle components locally, or build and publish the bundle into an Entando Application: | ||
|
||
- [Run Blueprint-generated components locally in dev mode](./run-local.md) | ||
- [Build and publish a project bundle](../pb/publish-project-bundle.md) to deploy your microservice and micro frontends to Entando | ||
- [Iterate on your data model](./update-data-model.md) using the JHipster Domain Language (JDL) | ||
|
||
## Notes | ||
|
||
### Local vs. Kubernetes Testing | ||
### Local vs. Entando Application Testing | ||
This tutorial leverages the `internal` client, which is configured in the microservice via the `application.yml`. Client roles are manually created and assigned in Keycloak. | ||
|
||
In Kubernetes, Entando will automatically create client roles per the bundle plugin definition (see the [plugin definition](../../../docs/curate/bundle-details.md) for more information). These roles are created for the client specific to the microservice, e.g. `<docker username>-conference-server`. The client name is injected as an environment variable into the plugin container, so the annotations noted above will work in both local and Kubernetes environments. | ||
In Kubernetes, Entando will automatically create client roles per the bundle plugin definition (see the [plugin definition](../../../docs/curate/bundle-details.md) for more information). These roles are created for the client specific to the microservice, e.g. `pn-YOUR-SERVICE-ID-conference-ms`. The client name is injected as an environment variable into the plugin container, so the annotations noted above will work in both local and Kubernetes environments. | ||
|
||
#### Modify Security Checks for Kubernetes | ||
#### Keycloak Client options in an Entando Application | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Option (capitalized to match style of other headers) |
||
|
||
In this tutorial, the MFE authorization checks explicitly note the client ID, e.g. `internal`. The following options modify the checks to work in Kubernetes: | ||
In this tutorial, the MFE authorization checks explicitly note the client ID, e.g. `internal`. The following options modify the checks to work in an Entando Application: | ||
|
||
1) Change the `application.yml` client ID under `security.oauth2.client.registration.oidc` to match the Kubernetes client ID. | ||
|
||
This is the most secure option and allows the MFE checks to work identically in both local and Kubernetes environments. However, you may not be be able to use the same clientId, depending on how the microservice is deployed. | ||
This is the most secure option and allows the MFE checks to work identically in both local and Kubernetes environments. However, you may not be able to use the same clientId, depending on how the microservice is deployed. | ||
|
||
2) Broaden the MFE authorization check to look for a named role on any client. | ||
|
||
This could result in overlap with other clients, but this is the most flexible option when using appropriately named roles (e.g. with a bundle or feature prefix like `conference-` in `conference-admin`). It can be achieved via a helper function, e.g. `api/helpers.js`, and results in a simpler role check: | ||
This could result in overlap with roles created for other clients, but this is the most flexible option when using appropriately named roles (e.g. with a bundle or feature prefix like `conference-` in `conference-admin`). It can be achieved via a helper function, e.g. `api/helpers.js`, and results in a simpler role check: | ||
```javascript | ||
// Add helper function | ||
// Check if the authenticated user has the clientRole for any Keycloak clients | ||
|
@@ -209,9 +179,18 @@ export const hasKeycloakClientRole = clientRole => { | |
return false; | ||
}; | ||
|
||
// Perform role check | ||
// Update the role check in ConferenceTableContainer.js using the new helper function | ||
const isAdmin = hasKeycloakClientRole('conference-admin'); | ||
``` | ||
|
||
### Realm Roles versus Client Authorities | ||
This tutorial utilizes authorities. In Keycloak, authorities are roles mapped to a user for a specific client. It is possible to assign higher-level Realm Roles directly to users, e.g. `ROLE_ADMIN`, but this can result in collisions between applications using the same roles. | ||
|
||
To implement Realm-assigned roles, the code above must be modified: | ||
- In the backend, use the annotation `@Secured('ROLE_ADMIN)` or `@PreAuthorize(hasRole('ROLE_ADMIN'))` | ||
- In the frontend, use `keycloak.hasRealmRole` instead of `keycloak.hasResourceRole` | ||
|
||
See the [Spring Security page](https://www.baeldung.com/spring-security-check-user-role) for more examples. | ||
|
||
### Troubleshooting | ||
In both local and Kubernetes environments, the default Blueprint Javascript provides a global variable in the browser, e.g. `window.entando.keycloak`. Examining this variable can help diagnose issues with assigned roles and authorities. In some cases, you may need to logout of Entando and reauthenticate for the latest role assignments to be applied. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor: remove period for consistency