Skip to content

Commit de699e2

Browse files
authored
BAEL-6620: RBAC Quarkus (#16416)
* BAEL-6620: RBAC quarkus * BAEL-6620: fix permissions * Code fix * Fix test name
1 parent 0c1e41f commit de699e2

25 files changed

+869
-0
lines changed
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
## Relevant Articles
2+

quarkus-modules/quarkus-rbac/pom.xml

+177
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
4+
xmlns="http://maven.apache.org/POM/4.0.0"
5+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
6+
<modelVersion>4.0.0</modelVersion>
7+
<groupId>com.baeldung.quarkus</groupId>
8+
<artifactId>quarkus-rbac</artifactId>
9+
<version>1.0.0-SNAPSHOT</version>
10+
11+
<parent>
12+
<groupId>com.baeldung</groupId>
13+
<artifactId>quarkus-modules</artifactId>
14+
<version>1.0.0-SNAPSHOT</version>
15+
</parent>
16+
17+
18+
<dependencyManagement>
19+
<dependencies>
20+
<dependency>
21+
<groupId>${quarkus.platform.group-id}</groupId>
22+
<artifactId>${quarkus.platform.artifact-id}</artifactId>
23+
<version>${quarkus.platform.version}</version>
24+
<type>pom</type>
25+
<scope>import</scope>
26+
</dependency>
27+
</dependencies>
28+
</dependencyManagement>
29+
30+
<dependencies>
31+
<dependency>
32+
<groupId>io.quarkus</groupId>
33+
<artifactId>quarkus-jdbc-h2</artifactId>
34+
</dependency>
35+
<dependency>
36+
<groupId>io.quarkus</groupId>
37+
<artifactId>quarkus-hibernate-orm</artifactId>
38+
</dependency>
39+
<dependency>
40+
<groupId>io.quarkus</groupId>
41+
<artifactId>quarkus-hibernate-orm-panache</artifactId>
42+
</dependency>
43+
<dependency>
44+
<groupId>io.quarkus</groupId>
45+
<artifactId>quarkus-resteasy</artifactId>
46+
</dependency>
47+
<dependency>
48+
<groupId>io.quarkus</groupId>
49+
<artifactId>quarkus-smallrye-jwt-build</artifactId>
50+
</dependency>
51+
<dependency>
52+
<groupId>io.quarkus</groupId>
53+
<artifactId>quarkus-smallrye-jwt</artifactId>
54+
</dependency>
55+
<dependency>
56+
<groupId>io.quarkus</groupId>
57+
<artifactId>quarkus-resteasy-jackson</artifactId>
58+
</dependency>
59+
<dependency>
60+
<groupId>io.quarkus</groupId>
61+
<artifactId>quarkus-security-jpa</artifactId>
62+
</dependency>
63+
<dependency>
64+
<groupId>io.quarkus</groupId>
65+
<artifactId>quarkus-arc</artifactId>
66+
</dependency>
67+
<dependency>
68+
<groupId>io.quarkus</groupId>
69+
<artifactId>quarkus-junit5</artifactId>
70+
<scope>test</scope>
71+
</dependency>
72+
<dependency>
73+
<groupId>io.rest-assured</groupId>
74+
<artifactId>rest-assured</artifactId>
75+
<scope>test</scope>
76+
</dependency>
77+
<dependency>
78+
<groupId>io.quarkus</groupId>
79+
<artifactId>quarkus-test-security</artifactId>
80+
<scope>test</scope>
81+
</dependency>
82+
<dependency>
83+
<groupId>io.quarkus</groupId>
84+
<artifactId>quarkus-test-security-jwt</artifactId>
85+
<scope>test</scope>
86+
</dependency>
87+
</dependencies>
88+
89+
<build>
90+
<plugins>
91+
<plugin>
92+
<groupId>${quarkus.platform.group-id}</groupId>
93+
<artifactId>quarkus-maven-plugin</artifactId>
94+
<version>${quarkus.platform.version}</version>
95+
<extensions>true</extensions>
96+
<executions>
97+
<execution>
98+
<goals>
99+
<goal>build</goal>
100+
<goal>generate-code</goal>
101+
<goal>generate-code-tests</goal>
102+
</goals>
103+
</execution>
104+
</executions>
105+
</plugin>
106+
<plugin>
107+
<artifactId>maven-compiler-plugin</artifactId>
108+
<version>${compiler-plugin.version}</version>
109+
<configuration>
110+
<compilerArgs>
111+
<arg>-parameters</arg>
112+
</compilerArgs>
113+
</configuration>
114+
</plugin>
115+
<plugin>
116+
<artifactId>maven-surefire-plugin</artifactId>
117+
<version>${surefire-plugin.version}</version>
118+
<configuration>
119+
<systemPropertyVariables>
120+
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
121+
<maven.home>${maven.home}</maven.home>
122+
</systemPropertyVariables>
123+
</configuration>
124+
</plugin>
125+
</plugins>
126+
</build>
127+
128+
<profiles>
129+
<profile>
130+
<id>native</id>
131+
<activation>
132+
<property>
133+
<name>native</name>
134+
</property>
135+
</activation>
136+
<build>
137+
<plugins>
138+
<plugin>
139+
<artifactId>maven-failsafe-plugin</artifactId>
140+
<version>${surefire-plugin.version}</version>
141+
<executions>
142+
<execution>
143+
<goals>
144+
<goal>integration-test</goal>
145+
<goal>verify</goal>
146+
</goals>
147+
<configuration>
148+
<systemPropertyVariables>
149+
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
150+
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
151+
<maven.home>${maven.home}</maven.home>
152+
</systemPropertyVariables>
153+
</configuration>
154+
</execution>
155+
</executions>
156+
</plugin>
157+
</plugins>
158+
</build>
159+
<properties>
160+
<quarkus.package.type>native</quarkus.package.type>
161+
</properties>
162+
</profile>
163+
</profiles>
164+
165+
<properties>
166+
<compiler-plugin.version>3.12.1</compiler-plugin.version>
167+
<failsafe.useModulePath>false</failsafe.useModulePath>
168+
<maven.compiler.release>17</maven.compiler.release>
169+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
170+
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
171+
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
172+
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
173+
<quarkus.platform.version>3.9.3</quarkus.platform.version>
174+
<surefire-plugin.version>3.0.0-M7</surefire-plugin.version>
175+
</properties>
176+
177+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.baeldung.quarkus.rbac.api;
2+
3+
import com.baeldung.quarkus.rbac.users.errors.DomainDataException;
4+
import jakarta.ws.rs.core.Response;
5+
import jakarta.ws.rs.ext.ExceptionMapper;
6+
import jakarta.ws.rs.ext.Provider;
7+
8+
@Provider
9+
public class ApiErrorHandler implements ExceptionMapper<DomainDataException> {
10+
11+
@Override
12+
public Response toResponse(final DomainDataException exception) {
13+
return Response.status(Response.Status.BAD_REQUEST)
14+
.entity(exception.getEntity() == null? exception.getEntity() : new Message(exception.getMessage()))
15+
.build();
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.baeldung.quarkus.rbac.api;
2+
3+
import jakarta.validation.constraints.NotNull;
4+
5+
public record LoginDto(@NotNull String username, @NotNull String password) {
6+
7+
@Override
8+
public String toString() {
9+
return "LoginDto{" +
10+
"username='" + username + '\'' +
11+
", password='*********'" +
12+
'}';
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package com.baeldung.quarkus.rbac.api;
2+
3+
public record Message(String message) { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.baeldung.quarkus.rbac.api;
2+
3+
import io.quarkus.security.PermissionsAllowed;
4+
import io.quarkus.security.identity.SecurityIdentity;
5+
import jakarta.ws.rs.Consumes;
6+
import jakarta.ws.rs.GET;
7+
import jakarta.ws.rs.Path;
8+
import jakarta.ws.rs.Produces;
9+
import jakarta.ws.rs.core.MediaType;
10+
11+
@Path("/permission-based")
12+
public class PermissionBasedController {
13+
14+
private final SecurityIdentity securityIdentity;
15+
16+
public PermissionBasedController(SecurityIdentity securityIdentity) {
17+
this.securityIdentity = securityIdentity;
18+
}
19+
20+
@GET
21+
@Path("/resource/version")
22+
@Consumes(MediaType.APPLICATION_JSON)
23+
@Produces(MediaType.APPLICATION_JSON)
24+
@PermissionsAllowed("VIEW_ADMIN_DETAILS")
25+
public String get() {
26+
return "2.0.0";
27+
}
28+
29+
@GET
30+
@Consumes(MediaType.APPLICATION_JSON)
31+
@Produces(MediaType.APPLICATION_JSON)
32+
@Path("/resource/message")
33+
@PermissionsAllowed(value = {"SEND_MESSAGE", "OPERATOR"}, inclusive = true)
34+
public Message message() {
35+
return new Message("Hello "+securityIdentity.getPrincipal().getName()+"!");
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.baeldung.quarkus.rbac.api;
2+
3+
import com.baeldung.quarkus.rbac.users.Role;
4+
import com.baeldung.quarkus.rbac.users.UserService;
5+
import io.quarkus.security.identity.SecurityIdentity;
6+
import jakarta.annotation.security.PermitAll;
7+
import jakarta.annotation.security.RolesAllowed;
8+
import jakarta.validation.Valid;
9+
import jakarta.ws.rs.Consumes;
10+
import jakarta.ws.rs.GET;
11+
import jakarta.ws.rs.POST;
12+
import jakarta.ws.rs.Path;
13+
import jakarta.ws.rs.Produces;
14+
import jakarta.ws.rs.core.MediaType;
15+
import jakarta.ws.rs.core.Response;
16+
17+
import java.util.stream.Collectors;
18+
19+
@Path("/secured")
20+
public class SecureResourceController {
21+
22+
private final UserService userService;
23+
private final SecurityIdentity securityIdentity;
24+
25+
public SecureResourceController(UserService userService, SecurityIdentity securityIdentity) {
26+
this.userService = userService;
27+
this.securityIdentity = securityIdentity;
28+
}
29+
30+
@GET
31+
@Path("/resource")
32+
@Consumes(MediaType.APPLICATION_JSON)
33+
@Produces(MediaType.APPLICATION_JSON)
34+
@RolesAllowed({"VIEW_ADMIN_DETAILS"})
35+
public String get() {
36+
return "Hello world, here are some details about the admin!";
37+
}
38+
39+
@GET
40+
@Path("/resource/user")
41+
@Consumes(MediaType.APPLICATION_JSON)
42+
@Produces(MediaType.APPLICATION_JSON)
43+
@RolesAllowed({"VIEW_USER_DETAILS"})
44+
public Message getUser() {
45+
return new Message("Hello "+securityIdentity.getPrincipal().getName()+"!");
46+
}
47+
48+
@POST
49+
@Path("/resource")
50+
@Consumes(MediaType.APPLICATION_JSON)
51+
@Produces(MediaType.APPLICATION_JSON)
52+
@RolesAllowed("${customer.send.message.permission:SEND_MESSAGE}")
53+
public Response getUser(Message message) {
54+
return Response.ok(message).build();
55+
}
56+
57+
@POST
58+
@Path("/user")
59+
@RolesAllowed({"CREATE_USER"})
60+
@Consumes(MediaType.APPLICATION_JSON)
61+
@Produces(MediaType.APPLICATION_JSON)
62+
public Response postUser(@Valid final UserDto userDto) {
63+
64+
final var user = userService.createUser(userDto);
65+
66+
final var roles = user.getRoles().stream().map(Role::getName).collect(Collectors.toSet());
67+
68+
return Response.ok(new UserResponse(user.getUsername(), roles)).build();
69+
}
70+
71+
@POST
72+
@Path("/login")
73+
@Consumes(MediaType.APPLICATION_JSON)
74+
@Produces(MediaType.APPLICATION_JSON)
75+
@PermitAll
76+
public Response login(@Valid final LoginDto loginDto) {
77+
if (userService.checkUserCredentials(loginDto.username(), loginDto.password())) {
78+
final var user = userService.findByUsername(loginDto.username());
79+
final var token = userService.generateJwtToken(user);
80+
return Response.ok().entity(new TokenResponse("Bearer " + token,"3600")).build();
81+
} else {
82+
return Response.status(Response.Status.UNAUTHORIZED).entity(new Message("Invalid credentials")).build();
83+
}
84+
}
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.baeldung.quarkus.rbac.api;
2+
3+
public record TokenResponse(String token, String expiresIn){
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.baeldung.quarkus.rbac.api;
2+
3+
import io.smallrye.common.constraint.NotNull;
4+
import jakarta.validation.constraints.Email;
5+
import jakarta.validation.constraints.Size;
6+
7+
import java.util.Set;
8+
9+
public record UserDto(@NotNull String username, @NotNull String password, @Size(min = 1) Set<String> roles, @Email String email) { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.baeldung.quarkus.rbac.api;
2+
3+
import java.util.Set;
4+
5+
public record UserResponse(String username, Set<String> roles) { }
6+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.baeldung.quarkus.rbac.users;
2+
3+
public enum Permission {
4+
5+
VIEW_ADMIN_DETAILS,
6+
VIEW_USER_DETAILS,
7+
SEND_MESSAGE,
8+
CREATE_USER
9+
10+
}

0 commit comments

Comments
 (0)