Skip to content

Commit 42e5468

Browse files
authored
Add roles support for Helidon (#506)
Helidon has security providers e.g. OIDC. These integrate with WebServer Security as `SecurityHandler`s with the shorthand `SecurityFeature.rolesAllowed(<roles>)` and the authorization provider will then validate the role before delegating to the actual handler. This PR takes Avaje's `@Roles` and populates them and the rest is up to Helidon.
1 parent 1c27559 commit 42e5468

File tree

8 files changed

+56
-3
lines changed

8 files changed

+56
-3
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ public class WidgetController$Route implements HttpFeature {
184184
private void _getById(ServerRequest req, ServerResponse res) throws Exception {
185185
res.status(OK_200);
186186
var pathParams = req.path().pathParameters();
187-
var id = asInt(pathParams.first("id").get());
187+
var id = asInt(pathParams.contains("id") ? pathParams.get("id") : null);
188188
var result = controller.getById(id);
189189
res.send(result);
190190
}
@@ -263,7 +263,7 @@ public class WidgetController$Route implements HttpFeature {
263263
private void _getById(ServerRequest req, ServerResponse res) throws Exception {
264264
res.status(OK_200);
265265
var pathParams = req.path().pathParameters();
266-
var id = asInt(pathParams.first("id").get());
266+
var id = asInt(pathParams.contains("id") ? pathParams.get("id") : null);
267267
var result = controller.getById(id);
268268
res.headers().contentType(MediaTypes.APPLICATION_JSON);
269269
//jsonb has a special accommodation for helidon to improve performance

http-generator-helidon/src/main/java/io/avaje/http/generator/helidon/nima/ControllerMethodWriter.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ void writeRule() {
8888
writer.append(" routing.addFilter(this::_%s);", method.simpleName()).eol();
8989
} else {
9090
writer.append(" routing.%s(\"%s\", ", webMethod.name().toLowerCase(), method.fullPath().replace("\\", "\\\\"));
91+
var roles = method.roles();
92+
if (!roles.isEmpty()) {
93+
writer.append("SecurityFeature.rolesAllowed(");
94+
writer.append("\"%s\"", Util.shortName(roles.getFirst(), true));
95+
for (var i = 1; i < roles.size(); i++) {
96+
writer.append(", \"%s\"", Util.shortName(roles.get(i), true));
97+
}
98+
writer.append("), ");
99+
}
91100
var hxRequest = method.hxRequest();
92101
if (hxRequest != null) {
93102
writer.append("HxHandler.builder(this::_%s)", method.simpleName());

http-generator-helidon/src/main/java/io/avaje/http/generator/helidon/nima/ControllerWriter.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ class ControllerWriter extends BaseControllerWriter {
5050
reader.addImportType("io.helidon.webserver.http.ServerResponse");
5151
reader.addImportType("io.helidon.webserver.http.HttpFeature");
5252
reader.addImportType("io.helidon.http.HeaderNames");
53+
if (!reader.roles().isEmpty() || reader.methods().stream().anyMatch(m -> !m.roles().isEmpty())) {
54+
reader.addImportType("io.helidon.webserver.security.SecurityFeature");
55+
}
5356
if (reader.isIncludeValidator()) {
5457
reader.addImportType("io.helidon.http.HeaderName");
5558
}

http-generator-helidon/src/main/java/io/avaje/http/generator/helidon/nima/NimaPlatformAdapter.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ public void methodRoles(List<String> roles, ControllerReader controller) {
6161
}
6262

6363
private void addRoleImports(List<String> roles, ControllerReader controller) {
64-
// nothing here yet
64+
for (final String role : roles) {
65+
controller.addStaticImportType(role);
66+
}
6567
}
6668

6769
@Override

tests/test-nima/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333
<artifactId>helidon-webserver</artifactId>
3434
<version>${nima.version}</version>
3535
</dependency>
36+
<dependency>
37+
<groupId>io.helidon.webserver</groupId>
38+
<artifactId>helidon-webserver-security</artifactId>
39+
<version>${nima.version}</version>
40+
</dependency>
3641
<dependency>
3742
<groupId>io.helidon.http.media</groupId>
3843
<artifactId>helidon-http-media-jsonb</artifactId>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package org.example;
2+
3+
public enum AppRoles {
4+
ANYONE, ADMIN, BASIC_USER
5+
}

tests/test-nima/src/main/java/org/example/HelloController.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io.avaje.http.api.Controller;
44
import io.avaje.http.api.Get;
5+
import io.avaje.http.api.Produces;
56

67
@Controller
78
public class HelloController {
@@ -18,4 +19,11 @@ Person person(String name, String sortBy) {
1819
p.setName(name + " hello" + " sortBy:" + sortBy);
1920
return p;
2021
}
22+
23+
@Roles({AppRoles.ADMIN, AppRoles.BASIC_USER})
24+
@Produces("text/plain")
25+
@Get("other/{name}")
26+
String name(String name) {
27+
return "hi " + name;
28+
}
2129
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.example;
2+
3+
import java.lang.annotation.Retention;
4+
import java.lang.annotation.Target;
5+
6+
import static java.lang.annotation.ElementType.METHOD;
7+
import static java.lang.annotation.ElementType.TYPE;
8+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
9+
10+
/**
11+
* Specify permitted roles.
12+
*/
13+
@Target(value={METHOD, TYPE})
14+
@Retention(value=RUNTIME)
15+
public @interface Roles {
16+
17+
/**
18+
* Specify the permitted roles.
19+
*/
20+
AppRoles[] value() default {};
21+
}

0 commit comments

Comments
 (0)