Skip to content

Commit

Permalink
Resolve HttpCredentialTransport dynamically
Browse files Browse the repository at this point in the history
  • Loading branch information
sberyozkin committed Jan 13, 2022
1 parent 9c9bff9 commit e827f7c
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@

@ApplicationScoped
public class OidcAuthenticationMechanism implements HttpAuthenticationMechanism {
private static HttpCredentialTransport OIDC_SERVICE_TRANSPORT = new HttpCredentialTransport(
HttpCredentialTransport.Type.AUTHORIZATION, OidcConstants.BEARER_SCHEME);
private static HttpCredentialTransport OIDC_WEB_APP_TRANSPORT = new HttpCredentialTransport(
HttpCredentialTransport.Type.AUTHORIZATION_CODE, OidcConstants.CODE_FLOW_CODE);

private final BearerAuthenticationMechanism bearerAuth = new BearerAuthenticationMechanism();
private final CodeAuthenticationMechanism codeAuth = new CodeAuthenticationMechanism();

private final DefaultTenantConfigResolver resolver;

public OidcAuthenticationMechanism(DefaultTenantConfigResolver resolver) {
Expand Down Expand Up @@ -89,10 +92,18 @@ public Set<Class<? extends AuthenticationRequest>> getCredentialTypes() {
}

@Override
public HttpCredentialTransport getCredentialTransport() {
//not 100% correct, but enough for now
//if OIDC is present we don't really want another bearer mechanism
return new HttpCredentialTransport(HttpCredentialTransport.Type.AUTHORIZATION, OidcConstants.BEARER_SCHEME);
public Uni<HttpCredentialTransport> getCredentialTransport(RoutingContext context) {
setTenantIdAttribute(context);
return resolve(context).onItem().transform(new Function<OidcTenantConfig, HttpCredentialTransport>() {
@Override
public HttpCredentialTransport apply(OidcTenantConfig oidcTenantConfig) {
if (!oidcTenantConfig.tenantEnabled) {
return null;
}
return isWebApp(context, oidcTenantConfig) ? OIDC_SERVICE_TRANSPORT
: OIDC_WEB_APP_TRANSPORT;
}
});
}

private static void setTenantIdAttribute(RoutingContext context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,20 @@ public Set<Class<? extends AuthenticationRequest>> getCredentialTypes() {
}

@Override
public HttpCredentialTransport getCredentialTransport() {
public Uni<HttpCredentialTransport> getCredentialTransport(RoutingContext context) {
final String tokenHeaderName = authContextInfo.getTokenHeader();
if (COOKIE_HEADER.equals(tokenHeaderName)) {
String tokenCookieName = authContextInfo.getTokenCookie();

if (tokenCookieName == null) {
tokenCookieName = BEARER;
}
return new HttpCredentialTransport(HttpCredentialTransport.Type.COOKIE, tokenCookieName);
return Uni.createFrom().item(new HttpCredentialTransport(HttpCredentialTransport.Type.COOKIE, tokenCookieName));
} else if (AUTHORIZATION_HEADER.equals(tokenHeaderName)) {
return new HttpCredentialTransport(HttpCredentialTransport.Type.AUTHORIZATION, BEARER);
return Uni.createFrom().item(new HttpCredentialTransport(HttpCredentialTransport.Type.AUTHORIZATION, BEARER));
} else {
return new HttpCredentialTransport(HttpCredentialTransport.Type.OTHER_HEADER, tokenHeaderName);
return Uni.createFrom()
.item(new HttpCredentialTransport(HttpCredentialTransport.Type.OTHER_HEADER, tokenHeaderName));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ public Set<Class<? extends AuthenticationRequest>> getCredentialTypes() {
}

@Override
public HttpCredentialTransport getCredentialTransport() {
return new HttpCredentialTransport(HttpCredentialTransport.Type.AUTHORIZATION, BASIC);
public Uni<HttpCredentialTransport> getCredentialTransport(RoutingContext context) {
return Uni.createFrom().item(new HttpCredentialTransport(HttpCredentialTransport.Type.AUTHORIZATION, BASIC));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ public Set<Class<? extends AuthenticationRequest>> getCredentialTypes() {
}

@Override
public HttpCredentialTransport getCredentialTransport() {
return new HttpCredentialTransport(HttpCredentialTransport.Type.POST, postLocation, FORM);
public Uni<HttpCredentialTransport> getCredentialTransport(RoutingContext context) {
return Uni.createFrom().item(new HttpCredentialTransport(HttpCredentialTransport.Type.POST, postLocation, FORM));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,26 @@ default Uni<Boolean> sendChallenge(RoutingContext context) {
}

/**
* The credential transport, used to make sure multiple incompatible mechanisms are not installed
* The credential transport, used for finding the best candidate for authenticating and challenging when more than one
* mechanism is installed.
* and finding the best candidate for issuing a challenge when more than one mechanism is installed.
*
* May be null if this mechanism cannot interfere with other mechanisms
*/
HttpCredentialTransport getCredentialTransport();
@Deprecated
default HttpCredentialTransport getCredentialTransport() {
throw new UnsupportedOperationException();
}

/**
* The credential transport, used for finding the best candidate for authenticating and challenging when more than one
* mechanism is installed.
*
* May be null if this mechanism cannot interfere with other mechanisms
*/
default Uni<HttpCredentialTransport> getCredentialTransport(RoutingContext context) {
throw new UnsupportedOperationException();
}

class ChallengeSender implements Function<ChallengeData, Boolean> {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
Expand Down Expand Up @@ -74,21 +72,6 @@ public int compare(HttpAuthenticationMechanism mech1, HttpAuthenticationMechanis
}
});
this.mechanisms = mechanisms.toArray(new HttpAuthenticationMechanism[mechanisms.size()]);
//validate that we don't have multiple incompatible mechanisms
Map<HttpCredentialTransport, HttpAuthenticationMechanism> map = new HashMap<>();
for (HttpAuthenticationMechanism i : mechanisms) {
HttpCredentialTransport credentialTransport = i.getCredentialTransport();
if (credentialTransport == null) {
continue;
}
HttpAuthenticationMechanism existing = map.get(credentialTransport);
if (existing != null) {
throw new RuntimeException("Multiple mechanisms present that use the same credential transport "
+ credentialTransport + ". Mechanisms are " + i + " and " + existing);
}
map.put(credentialTransport, i);
}

}
}

Expand All @@ -111,14 +94,25 @@ public Uni<SecurityIdentity> attemptAuthentication(RoutingContext routingContext
String pathSpecificMechanism = pathMatchingPolicy.isResolvable()
? pathMatchingPolicy.get().getAuthMechanismName(routingContext)
: null;
HttpAuthenticationMechanism matchingMech = findBestCandidateMechanism(routingContext, pathSpecificMechanism);
if (matchingMech != null) {
routingContext.put(HttpAuthenticationMechanism.class.getName(), matchingMech);
return matchingMech.authenticate(routingContext, identityProviderManager);
} else if (pathSpecificMechanism != null) {
return Uni.createFrom().optional(Optional.empty());
}
Uni<HttpAuthenticationMechanism> mechUni = findBestCandidateMechanism(routingContext, pathSpecificMechanism);
return mechUni.onItem().transformToUni(new Function<HttpAuthenticationMechanism, Uni<? extends SecurityIdentity>>() {

@Override
public Uni<SecurityIdentity> apply(HttpAuthenticationMechanism mech) {
if (mech != null) {
routingContext.put(HttpAuthenticationMechanism.class.getName(), mech);
return mech.authenticate(routingContext, identityProviderManager);
} else if (pathSpecificMechanism != null) {
return Uni.createFrom().optional(Optional.empty());
}
return createSecurityIdentity(routingContext);
}

});

}

private Uni<SecurityIdentity> createSecurityIdentity(RoutingContext routingContext) {
Uni<SecurityIdentity> result = mechanisms[0].authenticate(routingContext, identityProviderManager);
for (int i = 1; i < mechanisms.length; ++i) {
HttpAuthenticationMechanism mech = mechanisms[i];
Expand All @@ -132,7 +126,6 @@ public Uni<SecurityIdentity> apply(SecurityIdentity data) {
}
});
}

return result;
}

Expand Down Expand Up @@ -199,30 +192,69 @@ public Uni<? extends ChallengeData> apply(ChallengeData data) {
return result;
}

private HttpAuthenticationMechanism findBestCandidateMechanism(RoutingContext routingContext,
private Uni<HttpAuthenticationMechanism> findBestCandidateMechanism(RoutingContext routingContext,
String pathSpecificMechanism) {
if (pathSpecificMechanism != null) {
for (int i = 0; i < mechanisms.length; ++i) {
HttpCredentialTransport credType = mechanisms[i].getCredentialTransport();
if (credType != null && credType.getAuthenticationScheme().equalsIgnoreCase(pathSpecificMechanism)) {
return mechanisms[i];
}
}
return getPathSpecificMechanism(0, routingContext, pathSpecificMechanism);
} else {
String authScheme = getAuthorizationScheme(routingContext);
if (authScheme != null) {
for (int i = 0; i < mechanisms.length; ++i) {
HttpCredentialTransport credType = mechanisms[i].getCredentialTransport();
if (credType != null && credType.getTransportType() == Type.AUTHORIZATION
&& credType.getTypeTarget().toLowerCase().startsWith(authScheme.toLowerCase())) {
return mechanisms[i];
}
}
return getAuthorizationSchemeMechanism(0, routingContext, authScheme);
}
}
return null;
}

private Uni<HttpAuthenticationMechanism> getPathSpecificMechanism(int index, RoutingContext routingContext,
String pathSpecificMechanism) {
if (index == mechanisms.length) {
return Uni.createFrom().nullItem();
}
Uni<HttpCredentialTransport> credTypeUni = getCredentialTransport(mechanisms[index], routingContext);
return credTypeUni.onItem()
.transformToUni(new Function<HttpCredentialTransport, Uni<? extends HttpAuthenticationMechanism>>() {

@Override
public Uni<HttpAuthenticationMechanism> apply(HttpCredentialTransport t) {
if (t != null && t.getAuthenticationScheme().equalsIgnoreCase(pathSpecificMechanism)) {
return Uni.createFrom().item(mechanisms[index]);
}
return getPathSpecificMechanism(index + 1, routingContext, pathSpecificMechanism);
}

});
}

private Uni<HttpAuthenticationMechanism> getAuthorizationSchemeMechanism(int index, RoutingContext routingContext,
String authScheme) {
if (index == mechanisms.length) {
return Uni.createFrom().nullItem();
}
Uni<HttpCredentialTransport> credTypeUni = getCredentialTransport(mechanisms[index], routingContext);
return credTypeUni.onItem()
.transformToUni(new Function<HttpCredentialTransport, Uni<? extends HttpAuthenticationMechanism>>() {

@Override
public Uni<HttpAuthenticationMechanism> apply(HttpCredentialTransport t) {
if (t != null && t.getTransportType() == Type.AUTHORIZATION
&& t.getTypeTarget().toLowerCase().startsWith(authScheme.toLowerCase())) {
return Uni.createFrom().item(mechanisms[index]);
}
return getAuthorizationSchemeMechanism(index + 1, routingContext, authScheme);
}

});
}

private static Uni<HttpCredentialTransport> getCredentialTransport(HttpAuthenticationMechanism mechanism,
RoutingContext routingContext) {
try {
return mechanism.getCredentialTransport(routingContext);
} catch (UnsupportedOperationException ex) {
return Uni.createFrom().item(mechanism.getCredentialTransport());
}
}

private static String getAuthorizationScheme(RoutingContext routingContext) {
String authorization = routingContext.request().getHeader(HttpHeaders.AUTHORIZATION);
if (authorization != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
* Authorization header
* POST
*
* It is not permitted for multiple HTTP authentication mechanisms to use the same credential
* transport type, as they will not be able to figure out which mechanisms should process which
* Not that using multiple HTTP authentication mechanisms to use the same credential
* transport type can lead to unexpected authentication failures as they will not be able to figure out which mechanisms should
* process which
* request.
*/
public class HttpCredentialTransport {
Expand Down Expand Up @@ -49,7 +50,11 @@ public enum Type {
/**
* X509
*/
X509
X509,
/**
* Authorizatiob code, type target is the query 'code' parameter
*/
AUTHORIZATION_CODE
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public Set<Class<? extends AuthenticationRequest>> getCredentialTypes() {
}

@Override
public HttpCredentialTransport getCredentialTransport() {
return new HttpCredentialTransport(HttpCredentialTransport.Type.X509, "X509");
public Uni<HttpCredentialTransport> getCredentialTransport(RoutingContext context) {
return Uni.createFrom().item(new HttpCredentialTransport(HttpCredentialTransport.Type.X509, "X509"));
}
}

0 comments on commit e827f7c

Please sign in to comment.