2021-08-20 10:58:07 -07:00
|
|
|
package controllers;
|
2021-08-20 07:42:18 -07:00
|
|
|
|
2023-02-14 13:36:47 -05:00
|
|
|
import auth.CookieConfigs;
|
2023-12-06 11:02:42 +05:30
|
|
|
import auth.sso.SsoManager;
|
|
|
|
import auth.sso.SsoProvider;
|
|
|
|
import auth.sso.oidc.OidcCallbackLogic;
|
2021-11-22 16:33:14 -08:00
|
|
|
import client.AuthServiceClient;
|
2023-09-21 22:00:14 -05:00
|
|
|
import com.linkedin.entity.client.SystemEntityClient;
|
2024-10-28 09:05:16 -05:00
|
|
|
import io.datahubproject.metadata.context.OperationContext;
|
2022-08-23 09:54:34 -07:00
|
|
|
import java.net.URLEncoder;
|
2022-12-08 20:27:51 -06:00
|
|
|
import java.nio.charset.StandardCharsets;
|
2023-12-26 14:34:10 -06:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
2021-08-20 07:42:18 -07:00
|
|
|
import java.util.concurrent.CompletableFuture;
|
|
|
|
import java.util.concurrent.CompletionStage;
|
2021-11-22 16:33:14 -08:00
|
|
|
import javax.annotation.Nonnull;
|
2021-08-20 07:42:18 -07:00
|
|
|
import javax.inject.Inject;
|
2024-04-16 10:12:48 -05:00
|
|
|
import javax.inject.Named;
|
2021-08-20 07:42:18 -07:00
|
|
|
import lombok.extern.slf4j.Slf4j;
|
2024-10-28 09:05:16 -05:00
|
|
|
import org.pac4j.core.adapter.FrameworkAdapter;
|
2023-12-26 14:34:10 -06:00
|
|
|
import org.pac4j.core.client.Client;
|
|
|
|
import org.pac4j.core.client.Clients;
|
2021-08-20 07:42:18 -07:00
|
|
|
import org.pac4j.core.config.Config;
|
2024-10-28 09:05:16 -05:00
|
|
|
import org.pac4j.core.context.FrameworkParameters;
|
2021-08-20 07:42:18 -07:00
|
|
|
import org.pac4j.core.engine.CallbackLogic;
|
|
|
|
import org.pac4j.play.CallbackController;
|
2024-10-28 09:05:16 -05:00
|
|
|
import org.pac4j.play.context.PlayFrameworkParameters;
|
2022-12-08 20:27:51 -06:00
|
|
|
import play.mvc.Http;
|
2021-08-20 07:42:18 -07:00
|
|
|
import play.mvc.Result;
|
2022-12-08 20:27:51 -06:00
|
|
|
import play.mvc.Results;
|
2021-08-20 07:42:18 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A dedicated Controller for handling redirects to DataHub by 3rd-party Identity Providers after
|
|
|
|
* off-platform authentication.
|
|
|
|
*
|
2023-12-06 11:02:42 +05:30
|
|
|
* <p>Handles a single "callback/{protocol}" route, where the protocol (ie. OIDC / SAML) determines
|
2021-08-20 07:42:18 -07:00
|
|
|
* the handling logic to invoke.
|
|
|
|
*/
|
|
|
|
@Slf4j
|
|
|
|
public class SsoCallbackController extends CallbackController {
|
|
|
|
|
2024-10-28 09:05:16 -05:00
|
|
|
private final SsoManager ssoManager;
|
|
|
|
private final Config config;
|
|
|
|
private final CallbackLogic callbackLogic;
|
2021-08-20 07:42:18 -07:00
|
|
|
|
|
|
|
@Inject
|
2021-11-22 16:33:14 -08:00
|
|
|
public SsoCallbackController(
|
|
|
|
@Nonnull SsoManager ssoManager,
|
2024-04-16 10:12:48 -05:00
|
|
|
@Named("systemOperationContext") @Nonnull OperationContext systemOperationContext,
|
2023-09-21 22:00:14 -05:00
|
|
|
@Nonnull SystemEntityClient entityClient,
|
2023-02-14 13:36:47 -05:00
|
|
|
@Nonnull AuthServiceClient authClient,
|
2023-12-26 14:34:10 -06:00
|
|
|
@Nonnull Config config,
|
2023-02-14 13:36:47 -05:00
|
|
|
@Nonnull com.typesafe.config.Config configs) {
|
2024-10-28 09:05:16 -05:00
|
|
|
this.ssoManager = ssoManager;
|
|
|
|
this.config = config;
|
2021-08-20 07:42:18 -07:00
|
|
|
setDefaultUrl("/"); // By default, redirects to Home Page on log in.
|
2024-10-28 09:05:16 -05:00
|
|
|
|
|
|
|
callbackLogic =
|
2023-12-06 11:02:42 +05:30
|
|
|
new SsoCallbackLogic(
|
|
|
|
ssoManager,
|
2024-10-28 09:05:16 -05:00
|
|
|
systemOperationContext,
|
2023-12-06 11:02:42 +05:30
|
|
|
entityClient,
|
|
|
|
authClient,
|
2024-10-28 09:05:16 -05:00
|
|
|
new CookieConfigs(configs));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public CompletionStage<Result> callback(Http.Request request) {
|
|
|
|
FrameworkAdapter.INSTANCE.applyDefaultSettingsIfUndefined(this.config);
|
|
|
|
|
|
|
|
return CompletableFuture.supplyAsync(
|
|
|
|
() -> {
|
|
|
|
return (Result)
|
|
|
|
callbackLogic.perform(
|
|
|
|
this.config,
|
|
|
|
getDefaultUrl(),
|
|
|
|
getRenewSession(),
|
|
|
|
getDefaultClient(),
|
|
|
|
new PlayFrameworkParameters(request));
|
|
|
|
},
|
|
|
|
this.ec.current());
|
2021-08-20 07:42:18 -07:00
|
|
|
}
|
|
|
|
|
2022-12-08 20:27:51 -06:00
|
|
|
public CompletionStage<Result> handleCallback(String protocol, Http.Request request) {
|
2021-08-20 07:42:18 -07:00
|
|
|
if (shouldHandleCallback(protocol)) {
|
2024-10-28 09:05:16 -05:00
|
|
|
log.debug(
|
|
|
|
"Handling SSO callback. Protocol: {}",
|
|
|
|
ssoManager.getSsoProvider().protocol().getCommonName());
|
2023-12-06 11:02:42 +05:30
|
|
|
return callback(request)
|
|
|
|
.handle(
|
|
|
|
(res, e) -> {
|
|
|
|
if (e != null) {
|
|
|
|
log.error(
|
|
|
|
"Caught exception while attempting to handle SSO callback! It's likely that SSO integration is mis-configured.",
|
|
|
|
e);
|
|
|
|
return Results.redirect(
|
|
|
|
String.format(
|
|
|
|
"/login?error_msg=%s",
|
|
|
|
URLEncoder.encode(
|
|
|
|
"Failed to sign in using Single Sign-On provider. Please try again, or contact your DataHub Administrator.",
|
|
|
|
StandardCharsets.UTF_8)))
|
|
|
|
.discardingCookie("actor")
|
|
|
|
.withNewSession();
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
});
|
2021-08-20 07:42:18 -07:00
|
|
|
}
|
2023-12-06 11:02:42 +05:30
|
|
|
return CompletableFuture.completedFuture(
|
|
|
|
Results.internalServerError(
|
|
|
|
String.format(
|
|
|
|
"Failed to perform SSO callback. SSO is not enabled for protocol: %s", protocol)));
|
2021-08-20 07:42:18 -07:00
|
|
|
}
|
|
|
|
|
2023-12-06 11:02:42 +05:30
|
|
|
/** Logic responsible for delegating to protocol-specific callback logic. */
|
2024-10-28 09:05:16 -05:00
|
|
|
public class SsoCallbackLogic implements CallbackLogic {
|
2021-08-20 07:42:18 -07:00
|
|
|
|
2024-10-28 09:05:16 -05:00
|
|
|
private final OidcCallbackLogic oidcCallbackLogic;
|
2021-08-20 07:42:18 -07:00
|
|
|
|
2023-12-06 11:02:42 +05:30
|
|
|
SsoCallbackLogic(
|
|
|
|
final SsoManager ssoManager,
|
2024-04-16 10:12:48 -05:00
|
|
|
final OperationContext systemOperationContext,
|
2023-12-06 11:02:42 +05:30
|
|
|
final SystemEntityClient entityClient,
|
|
|
|
final AuthServiceClient authClient,
|
|
|
|
final CookieConfigs cookieConfigs) {
|
2024-10-28 09:05:16 -05:00
|
|
|
oidcCallbackLogic =
|
2023-12-06 11:02:42 +05:30
|
|
|
new OidcCallbackLogic(
|
2024-04-16 10:12:48 -05:00
|
|
|
ssoManager, systemOperationContext, entityClient, authClient, cookieConfigs);
|
2021-08-20 07:42:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2024-10-28 09:05:16 -05:00
|
|
|
public Object perform(
|
2023-12-06 11:02:42 +05:30
|
|
|
Config config,
|
2024-10-28 09:05:16 -05:00
|
|
|
String inputDefaultUrl,
|
|
|
|
Boolean inputRenewSession,
|
|
|
|
String defaultClient,
|
|
|
|
FrameworkParameters parameters) {
|
|
|
|
if (SsoProvider.SsoProtocol.OIDC.equals(ssoManager.getSsoProvider().protocol())) {
|
|
|
|
return oidcCallbackLogic.perform(
|
|
|
|
config, inputDefaultUrl, inputRenewSession, defaultClient, parameters);
|
2021-08-20 07:42:18 -07:00
|
|
|
}
|
|
|
|
// Should never occur.
|
2023-12-06 11:02:42 +05:30
|
|
|
throw new UnsupportedOperationException(
|
|
|
|
"Failed to find matching SSO Provider. Only one supported is OIDC.");
|
2021-08-20 07:42:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean shouldHandleCallback(final String protocol) {
|
2024-10-28 09:05:16 -05:00
|
|
|
if (!ssoManager.isSsoEnabled()) {
|
2023-12-26 14:34:10 -06:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
updateConfig();
|
2024-10-28 09:05:16 -05:00
|
|
|
return ssoManager.getSsoProvider().protocol().getCommonName().equals(protocol);
|
2023-12-26 14:34:10 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
private void updateConfig() {
|
|
|
|
final Clients clients = new Clients();
|
|
|
|
final List<Client> clientList = new ArrayList<>();
|
2024-10-28 09:05:16 -05:00
|
|
|
clientList.add(ssoManager.getSsoProvider().client());
|
2023-12-26 14:34:10 -06:00
|
|
|
clients.setClients(clientList);
|
2024-10-28 09:05:16 -05:00
|
|
|
config.setClients(clients);
|
2021-08-20 07:42:18 -07:00
|
|
|
}
|
|
|
|
}
|