mirror of
https://github.com/datahub-project/datahub.git
synced 2025-07-04 07:34:44 +00:00
167 lines
7.7 KiB
Java
167 lines
7.7 KiB
Java
package react.auth;
|
|
|
|
import com.google.inject.AbstractModule;
|
|
import com.google.inject.Provides;
|
|
import com.google.inject.Singleton;
|
|
import com.linkedin.common.urn.CorpuserUrn;
|
|
import org.pac4j.core.client.Client;
|
|
import org.pac4j.core.client.Clients;
|
|
import org.pac4j.core.config.Config;
|
|
import org.pac4j.core.context.session.SessionStore;
|
|
import org.pac4j.core.engine.DefaultCallbackLogic;
|
|
import org.pac4j.core.http.adapter.HttpActionAdapter;
|
|
import org.pac4j.core.http.callback.PathParameterCallbackUrlResolver;
|
|
import org.pac4j.core.profile.CommonProfile;
|
|
import org.pac4j.core.profile.ProfileManager;
|
|
import org.pac4j.oidc.client.OidcClient;
|
|
import org.pac4j.oidc.config.OidcConfiguration;
|
|
import org.pac4j.play.CallbackController;
|
|
import org.pac4j.play.PlayWebContext;
|
|
import org.pac4j.play.http.PlayHttpActionAdapter;
|
|
import org.pac4j.play.store.PlayCookieSessionStore;
|
|
import org.pac4j.play.store.PlaySessionStore;
|
|
import play.Environment;
|
|
import play.mvc.Result;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
import static react.auth.AuthUtils.*;
|
|
|
|
/**
|
|
* Responsible for configuring, validating, and providing authentication related components.
|
|
*/
|
|
public class AuthModule extends AbstractModule {
|
|
|
|
private static final String AUTH_BASE_URL_CONFIG_PATH = "auth.baseUrl";
|
|
private static final String AUTH_BASE_CALLBACK_PATH_CONFIG_PATH = "auth.baseCallbackPath";
|
|
private static final String AUTH_SUCCESS_REDIRECT_PATH_CONFIG_PATH = "auth.successRedirectPath";
|
|
|
|
private static final String DEFAULT_BASE_CALLBACK_PATH = "/callback";
|
|
private static final String DEFAULT_SUCCESS_REDIRECT_PATH = "/";
|
|
|
|
private String _authBaseUrl;
|
|
private String _authBaseCallbackPath;
|
|
private String _authSuccessRedirectPath;
|
|
|
|
private final com.typesafe.config.Config _configs;
|
|
|
|
/**
|
|
* OIDC-specific configurations.
|
|
*/
|
|
private final OidcConfigs _oidcConfigs;
|
|
|
|
public AuthModule(final Environment environment, final com.typesafe.config.Config configs) {
|
|
_configs = configs;
|
|
_oidcConfigs = new OidcConfigs(configs);
|
|
|
|
if (isIndirectAuthEnabled()) {
|
|
_authBaseUrl = configs.getString(AUTH_BASE_URL_CONFIG_PATH);
|
|
_authBaseCallbackPath = configs.hasPath(AUTH_BASE_CALLBACK_PATH_CONFIG_PATH)
|
|
? configs.getString(AUTH_BASE_CALLBACK_PATH_CONFIG_PATH)
|
|
: DEFAULT_BASE_CALLBACK_PATH;
|
|
_authSuccessRedirectPath = configs.hasPath(AUTH_SUCCESS_REDIRECT_PATH_CONFIG_PATH)
|
|
? configs.getString(AUTH_SUCCESS_REDIRECT_PATH_CONFIG_PATH)
|
|
: DEFAULT_SUCCESS_REDIRECT_PATH;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void configure() {
|
|
final PlayCookieSessionStore playCacheCookieStore = new PlayCookieSessionStore();
|
|
bind(SessionStore.class).toInstance(playCacheCookieStore);
|
|
bind(PlaySessionStore.class).toInstance(playCacheCookieStore);
|
|
|
|
final CallbackController callbackController = new CallbackController() {};
|
|
callbackController.setDefaultUrl(_authSuccessRedirectPath);
|
|
callbackController.setCallbackLogic(new DefaultCallbackLogic<Result, PlayWebContext>() {
|
|
@Override
|
|
public Result perform(final PlayWebContext context, final Config config, final HttpActionAdapter<Result, PlayWebContext> httpActionAdapter,
|
|
final String inputDefaultUrl, final Boolean inputSaveInSession, final Boolean inputMultiProfile,
|
|
final Boolean inputRenewSession, final String client) {
|
|
final Result result = super.perform(context, config, httpActionAdapter, inputDefaultUrl, inputSaveInSession, inputMultiProfile, inputRenewSession, client);
|
|
if (_oidcConfigs.getClientName().equals(client)) {
|
|
return handleOidcCallback(result, context, getProfileManager(context, config));
|
|
}
|
|
throw new RuntimeException(String.format("Unrecognized client with name %s provided to callback URL.", client));
|
|
}
|
|
});
|
|
// Make OIDC the default SSO client.
|
|
if (_oidcConfigs.isOidcEnabled()) {
|
|
callbackController.setDefaultClient(_oidcConfigs.getClientName());
|
|
}
|
|
bind(CallbackController.class).toInstance(callbackController);
|
|
}
|
|
|
|
@Provides @Singleton
|
|
protected Config provideConfig() {
|
|
if (isIndirectAuthEnabled()) {
|
|
|
|
final Clients clients = new Clients(_authBaseUrl + _authBaseCallbackPath);
|
|
final List<Client> clientList = new ArrayList<>();
|
|
|
|
if (_oidcConfigs.isOidcEnabled()) {
|
|
final OidcConfiguration oidcConfiguration = new OidcConfiguration();
|
|
oidcConfiguration.setClientId(_oidcConfigs.getClientId());
|
|
oidcConfiguration.setSecret(_oidcConfigs.getClientSecret());
|
|
oidcConfiguration.setDiscoveryURI(_oidcConfigs.getDiscoveryUri());
|
|
oidcConfiguration.setClientAuthenticationMethodAsString(_oidcConfigs.getClientAuthenticationMethod());
|
|
oidcConfiguration.setScope(_oidcConfigs.getScope());
|
|
|
|
final OidcClient oidcClient = new OidcClient(oidcConfiguration);
|
|
oidcClient.setName(_oidcConfigs.getClientName());
|
|
oidcClient.setCallbackUrlResolver(new PathParameterCallbackUrlResolver());
|
|
clientList.add(oidcClient);
|
|
}
|
|
clients.setClients(clientList);
|
|
|
|
final Config config = new Config(clients);
|
|
config.setHttpActionAdapter(new PlayHttpActionAdapter());
|
|
return config;
|
|
}
|
|
return new Config();
|
|
}
|
|
|
|
private Result handleOidcCallback(final Result result, final PlayWebContext context, ProfileManager<CommonProfile> profileManager) {
|
|
if (profileManager.isAuthenticated() && profileManager.get(true).isPresent()) {
|
|
final CommonProfile profile = profileManager.get(true).get();
|
|
if (!profile.containsAttribute(_oidcConfigs.getUserNameClaim())) {
|
|
throw new RuntimeException(
|
|
String.format(
|
|
"Failed to resolve user name claim from profile provided by Identity Provider. Missing attribute '%s'",
|
|
_oidcConfigs.getUserNameClaim()
|
|
));
|
|
}
|
|
|
|
final String userNameClaim = (String) profile.getAttribute(_oidcConfigs.getUserNameClaim());
|
|
final Pattern pattern = Pattern.compile(_oidcConfigs.getUserNameClaimRegex());
|
|
final Matcher matcher = pattern.matcher(userNameClaim);
|
|
if (matcher.find()) {
|
|
final String userName = matcher.group();
|
|
final String actorUrn = new CorpuserUrn(userName).toString();
|
|
context.getJavaSession().put(ACTOR, actorUrn);
|
|
return result.withCookies(createActorCookie(actorUrn, _configs.hasPath(SESSION_TTL_CONFIG_PATH)
|
|
? _configs.getInt(SESSION_TTL_CONFIG_PATH)
|
|
: DEFAULT_SESSION_TTL_DAYS));
|
|
} else {
|
|
throw new RuntimeException(
|
|
String.format("Failed to extract DataHub username from username claim %s using regex %s",
|
|
userNameClaim,
|
|
_oidcConfigs.getUserNameClaimRegex()));
|
|
}
|
|
}
|
|
throw new RuntimeException(String.format("Failed to authenticate current user. Cannot find valid identity provider profile in session"));
|
|
}
|
|
|
|
/**
|
|
* Returns true if indirect authentication is enabled (callback-based SSO), false otherwise.
|
|
* Currently, only OIDC is supported.
|
|
*/
|
|
private boolean isIndirectAuthEnabled() {
|
|
return _oidcConfigs.isOidcEnabled();
|
|
}
|
|
}
|
|
|