Skip to content

Commit

Permalink
JNG-6017 Extending auth interceptor api (#218)
Browse files Browse the repository at this point in the history
  • Loading branch information
robertcsakany authored Nov 21, 2024
1 parent b57e55d commit c34d2d8
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,56 @@

import java.util.Map;

/**
* This interface defines an interceptor for authentication / authorization call.
*/
public interface AuthenticationInterceptor {
String getName();

boolean isSuitableForOperation(EOperation operation, final String claim,
final String realm, final String client, Map<String, Object> attributes);
default boolean isSuitableForOperation(EOperation operation, final String claim,
final String realm, final String client, Map<String, Object> attributes) {
return true;
}

void success(final EOperation operation,
/**
* It is called after the extraction of principal but before the load of the mapped principal load.
* CAUTION! From the implementation of this method expressions which uses actor cannot be used.
* (for example getVariables for actor).
*
* @param operationFullyQualifiedName The called operation's fully qualified name
* @param exchange The request exchange
* @param claim The user's name
* @param realm Realm authenticated
* @param client Authenticated client
* @param attributes Token attributes
*/
default void authenticate(final String operationFullyQualifiedName,
final Map<String, Object> exchange,
final String claim,
final String realm,
final String client,
final Map<String, Object> attributes
) {};


/**
* It is called after successfully authorization, but before operation call,when the given operation have to be checked for
* authorization.
*
* @param operation The called operation's fully qualified name
* @param signedIdentifier The mapped operation's owner signed identifier
* @param exchange The request exchange
* @param claim The user's name
* @param realm Realm authenticated
* @param client Authenticated client
* @param attributes Token attributes
*/
default void success(final EOperation operation,
final SignedIdentifier signedIdentifier,
final Map<String, Object> exchange,
final String claim,
final String realm,
final String client,
final Map<String, Object> attributes
);
) {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EOperation;

import java.security.Principal;
import java.util.*;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -92,8 +93,8 @@ public void authorizeOperation(final EOperation operation, final SignedIdentifie
throw new IllegalStateException("Unsupported principal");
}

final JudoPrincipal principal = exchange.get(Dispatcher.PRINCIPAL_KEY) != null ? (JudoPrincipal) exchange.get(Dispatcher.PRINCIPAL_KEY) : null;
final String actorFqName = principal != null ? principal.getClient() : null;
final Principal principal = exchange.get(Dispatcher.PRINCIPAL_KEY) != null ? (Principal) exchange.get(Dispatcher.PRINCIPAL_KEY) : null;
final String actorFqName = principal != null && principal instanceof JudoPrincipal ? ((JudoPrincipal) principal).getClient() : null;

final boolean exposedForPublicOrTokenActor = AsmUtils.getExtensionAnnotationListByName(operation, "exposedBy").stream()
.anyMatch(a -> publicActors.contains(a.getDetails().get("value")) || Objects.equals(actorFqName, a.getDetails().get("value")));
Expand Down Expand Up @@ -131,21 +132,23 @@ public void authorizeOperation(final EOperation operation, final SignedIdentifie
}

if (authenticationInterceptorProvider != null && principal != null) {

JudoPrincipal judoPrincipal = principal instanceof JudoPrincipal ? (JudoPrincipal) principal : null;
authenticationInterceptorProvider.getAuthenticationInterceptors().stream()
.filter(authenticationInterceptor -> authenticationInterceptor.isSuitableForOperation(
operation,
principal.getName(),
principal.getRealm(),
principal.getClient(),
principal.getAttributes()))
judoPrincipal != null ? judoPrincipal.getRealm() : null,
judoPrincipal != null ? judoPrincipal.getClient(): null,
judoPrincipal != null ? judoPrincipal.getAttributes() : null))
.forEach(authenticationInterceptor -> {
authenticationInterceptor.success(operation,
signedIdentifier,
exchange,
principal.getName(),
principal.getRealm(),
principal.getClient(),
principal.getAttributes());
judoPrincipal != null ? judoPrincipal.getRealm() : null,
judoPrincipal != null ? judoPrincipal.getClient() : null,
judoPrincipal != null ? judoPrincipal.getAttributes() : null);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import hu.blackbelt.judo.meta.asm.runtime.AsmModel;
import hu.blackbelt.judo.meta.asm.runtime.AsmUtils;
import hu.blackbelt.judo.runtime.core.DataTypeManager;
import hu.blackbelt.judo.runtime.core.accessmanager.api.AuthenticationInterceptorProvider;
import hu.blackbelt.judo.runtime.core.dispatcher.behaviours.QueryCustomizerParameterProcessor;
import hu.blackbelt.judo.runtime.core.dispatcher.security.ActorResolver;
import hu.blackbelt.judo.runtime.core.exception.AccessDeniedException;
Expand All @@ -40,6 +41,7 @@

import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.Principal;
import java.time.*;
import java.util.*;
import java.util.stream.Collectors;
Expand All @@ -59,23 +61,42 @@ public class DefaultActorResolver<ID> implements ActorResolver {

private AsmUtils asmUtils;

AuthenticationInterceptorProvider authenticationInterceptorProvider;


@Builder
public DefaultActorResolver(
@NonNull DataTypeManager dataTypeManager,
@NonNull DAO<ID> dao,
@NonNull AsmModel asmModel,
AuthenticationInterceptorProvider authenticationInterceptorProvider,
Boolean checkMappedActors) {
this.dataTypeManager = dataTypeManager;
this.dao = dao;
this.asmModel = asmModel;
this.checkMappedActors = checkMappedActors == null ? false : checkMappedActors;
this.asmUtils = new AsmUtils(asmModel.getResourceSet());
this.authenticationInterceptorProvider = authenticationInterceptorProvider;
}

@Override
public void authenticateActor(final Map<String, Object> exchange) {
final Object principal = exchange.get(Dispatcher.PRINCIPAL_KEY);
final Principal principal = (Principal) exchange.get(Dispatcher.PRINCIPAL_KEY);

final String operationFullyQualifiedName = (String) exchange.get("__operationFullyQualifiedName");
if (authenticationInterceptorProvider != null &&
operationFullyQualifiedName != null && principal != null) {
JudoPrincipal judoPrincipal = principal instanceof JudoPrincipal ? (JudoPrincipal) principal : null;
authenticationInterceptorProvider.getAuthenticationInterceptors().stream()
.forEach(authenticationInterceptor -> {
authenticationInterceptor.authenticate(operationFullyQualifiedName,
exchange,
principal.getName(),
judoPrincipal != null ? judoPrincipal.getRealm() : null,
judoPrincipal != null ? judoPrincipal.getClient() : null,
judoPrincipal != null ? judoPrincipal.getAttributes() : null);
});
}

if ((principal instanceof JudoPrincipal) && !exchange.containsKey(Dispatcher.ACTOR_KEY) && checkMappedActors) {
final Optional<Payload> actor = authenticateByPrincipal((JudoPrincipal) principal);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,10 @@ public Map<String, Object> callOperation(final String operationFullyQualifiedNam
}

try (MetricsCancelToken ignored = metricsCollector.start(METRICS_DISPATCHER)) {
exchange.put("__operationFullyQualifiedName", operationFullyQualifiedName);
actorResolver.authenticateActor(exchange);
exchange.remove("__operationFullyQualifiedName");

if (exchange.containsKey(ACTOR_KEY)) {
context.putIfAbsent(ACTOR_KEY, exchange.get(ACTOR_KEY));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import hu.blackbelt.judo.dao.api.DAO;
import hu.blackbelt.judo.meta.asm.runtime.AsmModel;
import hu.blackbelt.judo.runtime.core.DataTypeManager;
import hu.blackbelt.judo.runtime.core.accessmanager.api.AuthenticationInterceptorProvider;
import hu.blackbelt.judo.runtime.core.dispatcher.DefaultActorResolver;
import hu.blackbelt.judo.runtime.core.dispatcher.security.ActorResolver;

Expand All @@ -45,6 +46,9 @@ public class DefaultActorResolverProvider implements Provider<ActorResolver> {
@Inject
DataTypeManager dataTypeManager;

@Inject
AuthenticationInterceptorProvider authenticationInterceptorProvider;

@Inject(optional = true)
@Named(ACTOR_RESOLVER_CHECK_MAPPED_ACTORS)
@Nullable
Expand All @@ -58,6 +62,7 @@ public ActorResolver get() {
.dao(dao)
.asmModel(asmModel)
.checkMappedActors(checkMappedActors)
.authenticationInterceptorProvider(authenticationInterceptorProvider)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotat

@Override
public void writeTo(PayloadImpl payload, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(entityStream);
objectMapper.writeValue(outputStreamWriter, payload);
try {
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(entityStream);
objectMapper.writeValue(outputStreamWriter, payload);
} catch (Throwable throwable) {
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ public ActorResolver getActorResolver(
.dao(dao)
.asmModel(asmModel)
.checkMappedActors(checkMappedActors)
.authenticationInterceptorProvider(authenticationInterceptorProvider)
.build();
}

Expand Down

0 comments on commit c34d2d8

Please sign in to comment.