Skip to content

Commit

Permalink
Add roles support for Helidon (#506)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
re-thc authored Oct 10, 2024
1 parent 1c27559 commit 42e5468
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 3 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ public class WidgetController$Route implements HttpFeature {
private void _getById(ServerRequest req, ServerResponse res) throws Exception {
res.status(OK_200);
var pathParams = req.path().pathParameters();
var id = asInt(pathParams.first("id").get());
var id = asInt(pathParams.contains("id") ? pathParams.get("id") : null);
var result = controller.getById(id);
res.send(result);
}
Expand Down Expand Up @@ -263,7 +263,7 @@ public class WidgetController$Route implements HttpFeature {
private void _getById(ServerRequest req, ServerResponse res) throws Exception {
res.status(OK_200);
var pathParams = req.path().pathParameters();
var id = asInt(pathParams.first("id").get());
var id = asInt(pathParams.contains("id") ? pathParams.get("id") : null);
var result = controller.getById(id);
res.headers().contentType(MediaTypes.APPLICATION_JSON);
//jsonb has a special accommodation for helidon to improve performance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ void writeRule() {
writer.append(" routing.addFilter(this::_%s);", method.simpleName()).eol();
} else {
writer.append(" routing.%s(\"%s\", ", webMethod.name().toLowerCase(), method.fullPath().replace("\\", "\\\\"));
var roles = method.roles();
if (!roles.isEmpty()) {
writer.append("SecurityFeature.rolesAllowed(");
writer.append("\"%s\"", Util.shortName(roles.getFirst(), true));
for (var i = 1; i < roles.size(); i++) {
writer.append(", \"%s\"", Util.shortName(roles.get(i), true));
}
writer.append("), ");
}
var hxRequest = method.hxRequest();
if (hxRequest != null) {
writer.append("HxHandler.builder(this::_%s)", method.simpleName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class ControllerWriter extends BaseControllerWriter {
reader.addImportType("io.helidon.webserver.http.ServerResponse");
reader.addImportType("io.helidon.webserver.http.HttpFeature");
reader.addImportType("io.helidon.http.HeaderNames");
if (!reader.roles().isEmpty() || reader.methods().stream().anyMatch(m -> !m.roles().isEmpty())) {
reader.addImportType("io.helidon.webserver.security.SecurityFeature");
}
if (reader.isIncludeValidator()) {
reader.addImportType("io.helidon.http.HeaderName");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ public void methodRoles(List<String> roles, ControllerReader controller) {
}

private void addRoleImports(List<String> roles, ControllerReader controller) {
// nothing here yet
for (final String role : roles) {
controller.addStaticImportType(role);
}
}

@Override
Expand Down
5 changes: 5 additions & 0 deletions tests/test-nima/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
<artifactId>helidon-webserver</artifactId>
<version>${nima.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.webserver</groupId>
<artifactId>helidon-webserver-security</artifactId>
<version>${nima.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.http.media</groupId>
<artifactId>helidon-http-media-jsonb</artifactId>
Expand Down
5 changes: 5 additions & 0 deletions tests/test-nima/src/main/java/org/example/AppRoles.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.example;

public enum AppRoles {
ANYONE, ADMIN, BASIC_USER
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.avaje.http.api.Controller;
import io.avaje.http.api.Get;
import io.avaje.http.api.Produces;

@Controller
public class HelloController {
Expand All @@ -18,4 +19,11 @@ Person person(String name, String sortBy) {
p.setName(name + " hello" + " sortBy:" + sortBy);
return p;
}

@Roles({AppRoles.ADMIN, AppRoles.BASIC_USER})
@Produces("text/plain")
@Get("other/{name}")
String name(String name) {
return "hi " + name;
}
}
21 changes: 21 additions & 0 deletions tests/test-nima/src/main/java/org/example/Roles.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.example;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
* Specify permitted roles.
*/
@Target(value={METHOD, TYPE})
@Retention(value=RUNTIME)
public @interface Roles {

/**
* Specify the permitted roles.
*/
AppRoles[] value() default {};
}

0 comments on commit 42e5468

Please sign in to comment.