Skip to content

Commit

Permalink
Vert.x.Rest update for v5
Browse files Browse the repository at this point in the history
  • Loading branch information
GedMarc committed Dec 17, 2024
1 parent 36062ad commit 9b46051
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 132 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ public RestBuilder enableCors() {
allowedHeaders.add("Access-Control-Request-Method");
allowedHeaders.add("Access-Control-Request-Headers");
//allowedHeaders.add("Access-Control-Max-Age");
return enableCors("*", false, -1, allowedHeaders);
return enableCors(List.of("*"), false, -1, allowedHeaders);
}

/**
Expand All @@ -180,13 +180,14 @@ public RestBuilder enableCors() {
* @param methods list of methods ... if empty all methods are allowed @return self
* @return self
*/
public RestBuilder enableCors(String allowedOriginPattern,
public RestBuilder enableCors(List<String> allowedOriginPattern,
boolean allowCredentials,
int maxAge,
Set<String> allowedHeaders,
HttpMethod... methods) {

corsHandler = CorsHandler.create(allowedOriginPattern)
corsHandler = CorsHandler.create()
.addOrigins(allowedOriginPattern)
.allowCredentials(allowCredentials)
.maxAgeSeconds(maxAge);

Expand Down
135 changes: 45 additions & 90 deletions Vert.x/rest.vertx/src/main/java/com/zandero/rest/RestRouter.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import jakarta.validation.*;
import jakarta.validation.executable.*;
import jakarta.ws.rs.core.*;

import java.lang.reflect.*;
import java.util.*;

Expand Down Expand Up @@ -167,19 +168,19 @@ public static Router register(Router router, Object... restApi) {
// Authentication
if (definition.getAuthenticationProvider() != null || defaultAuthenticationProvider != null) {
route.handler(getAuthenticationProvider(definition.getAuthenticationProvider(),
definition));
definition));
}

// Authorization
if (definition.getAuthorizationProvider() != null || defaultAuthorizationProvider != null) {
route.handler(getAuthorizationHandler(definition.getAuthorizationProvider(),
definition));
definition));
} else if (definition.checkSecurity()) {
// for back compatibility purposes
// add security check handler in front of regular route handler
// (in case @PermitAll, @DenyAll or @RolesAllowed is used)
route.handler(getAuthorizationHandler(RoleBasedUserAuthorizationProvider.class,
definition));
definition));
}

// bind handler // blocking or async
Expand Down Expand Up @@ -213,9 +214,9 @@ public static void provide(Router output, Class<? extends ContextProvider<?>> pr
try {
Class<?> clazz = (Class<?>) getGenericType(provider);
ContextProvider<?> instance = getContextProviders().getContextProvider(getInjectionProvider(),
clazz,
provider,
null);
clazz,
provider,
null);
// set before other routes ...
output.route().order(ORDER_PROVIDER_HANDLER).handler(getContextHandler(instance));
} catch (Throwable e) {
Expand All @@ -236,14 +237,6 @@ private static Handler<RoutingContext> getContextHandler(ContextProvider<?> inst
try {
Object provided = instance.provide(context.request());

if (provided instanceof User) {
context.setUser((User) provided);
}

if (provided instanceof Session) {
context.setSession((Session) provided);
}

if (provided != null) { // push provided context into request data
context.data().put(ContextProviderCache.getContextDataKey(provided), provided);
}
Expand Down Expand Up @@ -338,15 +331,16 @@ private static void addLastHandler(Router router, String path, Handler<RoutingCo
* @param methods list of methods or empty for all
*/
public static void enableCors(Router router,
String allowedOriginPattern,
List<String> allowedOriginPattern,
boolean allowCredentials,
int maxAge,
Set<String> allowedHeaders,
HttpMethod... methods) {

CorsHandler handler = CorsHandler.create(allowedOriginPattern)
.allowCredentials(allowCredentials)
.maxAgeSeconds(maxAge);
CorsHandler handler = CorsHandler.create()
.addOrigins(allowedOriginPattern)
.allowCredentials(allowCredentials)
.maxAgeSeconds(maxAge);

if (methods == null || methods.length == 0) { // if not given than all
methods = HttpMethod.values().toArray(new HttpMethod[]{});
Expand Down Expand Up @@ -400,20 +394,20 @@ private static void checkBodyReader(RouteDefinition definition) {
}

ValueReader<?> bodyReader = getReaders().get(definition.getBodyParameter(),
definition.getReader(),
getInjectionProvider(),
null,
definition.getConsumes());
definition.getReader(),
getInjectionProvider(),
null,
definition.getConsumes());

if (bodyReader != null && definition.checkCompatibility()) {

Type readerType = getGenericType(bodyReader.getClass());
MethodParameter bodyParameter = definition.getBodyParameter();

checkIfCompatibleType(bodyParameter.getDataType(), readerType,
definition.toString().trim() + " - Parameter type: '" +
bodyParameter.getDataType() + "' not matching reader type: '" +
readerType + "' in: '" + bodyReader.getClass() + "'!");
definition.toString().trim() + " - Parameter type: '" +
bodyParameter.getDataType() + "' not matching reader type: '" +
readerType + "' in: '" + bodyReader.getClass() + "'!");
}
}

Expand All @@ -422,20 +416,10 @@ private static Handler<RoutingContext> getAuthenticationProvider(Class<? extends
return context -> {
try {
RestAuthenticationProvider authenticator = authenticatorProviderClass != null ?
getAuthenticationProviders().provide(authenticatorProviderClass, getInjectionProvider(), context) :
defaultAuthenticationProvider;

authenticator.authenticate(context, userAsyncResult -> {
if (userAsyncResult.failed()) {
Throwable ex = (userAsyncResult.cause() != null ?
userAsyncResult.cause() :
new UnauthorizedException(context.user()));
handleException(ex, context, definition);
} else {
context.setUser(userAsyncResult.result());
context.next();
}
});
getAuthenticationProviders().provide(authenticatorProviderClass, getInjectionProvider(), context) :
defaultAuthenticationProvider;

authenticator.authenticate(context);
} catch (Throwable e) {
log.error("Authentication failed: " + e.getMessage(), e);
handleException(e, context, definition);
Expand All @@ -448,19 +432,10 @@ private static Handler<RoutingContext> getAuthorizationHandler(Class<? extends A

try {
AuthorizationProvider provider = providerClass != null ?
getAuthorizationProviders().provide(providerClass, getInjectionProvider(), context) :
defaultAuthorizationProvider;

provider.getAuthorizations(context.user(), userAuthorizationResult -> {
if (userAuthorizationResult.failed()) {
Throwable ex = (userAuthorizationResult.cause() != null ?
userAuthorizationResult.cause() :
new ForbiddenException(context.user()));
handleException(ex, context, definition);
} else {
context.next();
}
});
getAuthorizationProviders().provide(providerClass, getInjectionProvider(), context) :
defaultAuthorizationProvider;

provider.getAuthorizations(context.user().get());
} catch (Throwable e) {
log.error("Authorization failed: " + e.getMessage(), e);
handleException(e, context, definition);
Expand All @@ -471,45 +446,25 @@ private static Handler<RoutingContext> getAuthorizationHandler(Class<? extends A
private static Handler<RoutingContext> getHandler(final Object toInvoke, final RouteDefinition definition, final Method method) {

return context -> context.vertx().executeBlocking(
fut -> {
try {
() -> {
log.info(definition.getMethod().name() + " " + definition.getPath());
Object[] args = ArgumentProvider.getArguments(method,
definition,
context,
getReaders(),
getContextProviders(),
getInjectionProvider(),
beanProvider);

validate(method, definition, validator, toInvoke, args);

fut.complete(method.invoke(toInvoke, args));
} catch (Throwable e) {
fut.fail(e);
}
},
definition.executeBlockingOrdered(), // false by default
res -> {
if (res.succeeded()) {
Object[] args = null;
try {
Object result = res.result();

Class returnType = result != null ? result.getClass() : definition.getReturnType();

HttpResponseWriter writer = forge.getResponseWriter(returnType,
definition,
context);

validateResult(result, method, definition, validator, toInvoke);
produceResponse(result, context, definition, writer);
args = ArgumentProvider.getArguments(method,
definition,
context,
getReaders(),
getContextProviders(),
getInjectionProvider(),
beanProvider);
} catch (Throwable e) {
handleException(e, context, definition);
throw new RuntimeException(e);
}
} else {
handleException(res.cause(), context, definition);
}
}

validate(method, definition, validator, toInvoke, args);
return method.invoke(toInvoke, args);
},
definition.executeBlockingOrdered()
);
}

Expand Down Expand Up @@ -545,8 +500,8 @@ private static Handler<RoutingContext> getAsyncHandler(final Object toInvoke, fi
HttpResponseWriter writer;
if (futureResult != null) { // get writer from result type otherwise we don't know
writer = forge.getResponseWriter(futureResult.getClass(),
definition,
context);
definition,
context);
} else { // due to limitations of Java generics we can't tell the type if response is null
Class<?> writerClass = definition.getWriter() == null ? GenericResponseWriter.class : definition.getWriter();
writer = (HttpResponseWriter) ClassFactory.newInstanceOf(writerClass);
Expand Down Expand Up @@ -665,7 +620,7 @@ private static void handleException(Throwable e, RoutingContext context, final R
handler.write(ex.getCause(), request, response);

eventExecutor.triggerEvents(ex.getCause(), response.getStatusCode(), definition, context,
getInjectionProvider());
getInjectionProvider());
} catch (Throwable handlerException) {
// this should not happen
log.error("Failed to write out handled exception: " + e.getMessage(), e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

public interface RestAuthenticationProvider extends AuthenticationProvider {

default void authenticate(RoutingContext context, Handler<AsyncResult<User>> resultHandler) {
authenticate(provideCredentials(context), resultHandler);
default void authenticate(RoutingContext context) {
authenticate(provideCredentials(context));
}

Credentials provideCredentials(RoutingContext context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,43 +30,44 @@ public String getId() {
}

@Override
public void getAuthorizations(User user, Handler<AsyncResult<Void>> handler) {

if (definition.getPermitAll() != null) {
if (definition.getPermitAll()) {
handler.handle(Future.succeededFuture());
public Future<Void> getAuthorizations(User user) {
return Future.future(handler ->{
if (definition.getPermitAll() != null) {
if (definition.getPermitAll()) {
handler.handle(Future.succeededFuture());
} else {
handler.handle(Future.failedFuture(new ForbiddenException(user)));
}
} else {
handler.handle(Future.failedFuture(new ForbiddenException(user)));
}
} else {
try {
if (user != null && user.authorizations() != null && definition.getRoles() != null) {
Optional<String> found = Arrays.stream(definition.getRoles())
.filter(role -> RoleBasedAuthorization.create(role).match(user))
.findFirst();
try {
if (user != null && user.authorizations() != null && definition.getRoles() != null) {
Optional<String> found = Arrays.stream(definition.getRoles())
.filter(role -> RoleBasedAuthorization.create(role).match(user))
.findFirst();

if (found.isPresent()) {
handler.handle(Future.succeededFuture());
if (found.isPresent()) {
handler.handle(Future.succeededFuture());
} else {
log.trace("User authorization failed: '" + user.principal() + "', not authorized to access: " + definition.toString());
handler.handle(Future.failedFuture(new ForbiddenException(user)));
}
} else {
log.trace("User authorization failed: '" + user.principal() + "', not authorized to access: " + definition.toString());
if (definition.getRoles() == null) {
log.trace("User authorization failed: " + definition.toString() + ", is missing @RolesAllowed annotation. " +
"Either provide @RolesAllowed annotation or use different AuthorizationProvider");
} else if (user != null) {
log.trace("User authorization failed: '" + user.principal() + "', not authorized to access: " + definition.toString());
} else {
log.trace("User authorization failed: no user was provided, for: " + definition.toString());
}

handler.handle(Future.failedFuture(new ForbiddenException(user)));
}
} else {
if (definition.getRoles() == null) {
log.trace("User authorization failed: " + definition.toString() + ", is missing @RolesAllowed annotation. " +
"Either provide @RolesAllowed annotation or use different AuthorizationProvider");
} else if (user != null) {
log.trace("User authorization failed: '" + user.principal() + "', not authorized to access: " + definition.toString());
} else {
log.trace("User authorization failed: no user was provided, for: " + definition.toString());
}

handler.handle(Future.failedFuture(new ForbiddenException(user)));
} catch (Throwable e) {
log.error("Failed to provide user authorization: " + e.getMessage(), e);
handler.handle(Future.failedFuture(e));
}
} catch (Throwable e) {
log.error("Failed to provide user authorization: " + e.getMessage(), e);
handler.handle(Future.failedFuture(e));
}
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
import java.lang.reflect.*;
import java.util.*;

import static io.vertx.core.cli.impl.ReflectionUtils.isSetter;

public class BeanDefinition {

public static boolean isSetter(Method method) {
return method.getName().startsWith("set") && method.getParameterCount() == 1;
}

private final static Logger log = LoggerFactory.getLogger(BeanDefinition.class);

private static final String METHOD_PREFIX = "m:";
Expand Down Expand Up @@ -43,7 +45,7 @@ private void init(Class<?> clazz) {
}

for (Method method : methods) {
if (isSetter(method)) {
if (BeanDefinition.isSetter(method)) {
MethodParameter paramValues = getValueFromAnnotations(method.getAnnotations(), method.getParameterTypes()[0], 0);
if (paramValues != null) {
parameters.put(METHOD_PREFIX + method.getName(), paramValues);
Expand Down
Loading

0 comments on commit 9b46051

Please sign in to comment.