mirror of
https://github.com/datahub-project/datahub.git
synced 2025-07-04 23:57:03 +00:00
161 lines
7.1 KiB
Java
161 lines
7.1 KiB
Java
package auth;
|
|
|
|
import com.linkedin.common.urn.CorpuserUrn;
|
|
import java.time.Duration;
|
|
import java.time.temporal.ChronoUnit;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import javax.annotation.Nonnull;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import play.mvc.Http;
|
|
|
|
@Slf4j
|
|
public class AuthUtils {
|
|
|
|
/**
|
|
* The config path that determines whether Metadata Service Authentication is enabled.
|
|
*
|
|
* <p>When enabled, the frontend server will proxy requests to the Metadata Service without
|
|
* requiring them to have a valid frontend-issued Session Cookie. This effectively means
|
|
* delegating the act of authentication to the Metadata Service. It is critical that if Metadata
|
|
* Service authentication is enabled at the frontend service layer, it is also enabled in the
|
|
* Metadata Service itself. Otherwise, unauthenticated traffic may reach the Metadata itself.
|
|
*
|
|
* <p>When disabled, the frontend server will require that all requests have a valid Session
|
|
* Cookie associated with them. Otherwise, requests will be denied with an Unauthorized error.
|
|
*/
|
|
public static final String METADATA_SERVICE_AUTH_ENABLED_CONFIG_PATH =
|
|
"metadataService.auth.enabled";
|
|
|
|
/** The attribute inside session cookie representing a GMS-issued access token */
|
|
public static final String SESSION_COOKIE_GMS_TOKEN_NAME = "token";
|
|
|
|
/**
|
|
* An ID used to identify system callers that are internal to DataHub. Provided via configuration.
|
|
*/
|
|
public static final String SYSTEM_CLIENT_ID_CONFIG_PATH = "systemClientId";
|
|
|
|
/**
|
|
* An Secret used to authenticate system callers that are internal to DataHub. Provided via
|
|
* configuration.
|
|
*/
|
|
public static final String SYSTEM_CLIENT_SECRET_CONFIG_PATH = "systemClientSecret";
|
|
|
|
/** Cookie name for redirect url that is manually separated from the session to reduce size */
|
|
public static final String REDIRECT_URL_COOKIE_NAME = "REDIRECT_URL";
|
|
|
|
public static final CorpuserUrn DEFAULT_ACTOR_URN = new CorpuserUrn("datahub");
|
|
|
|
public static final String LOGIN_ROUTE = "/login";
|
|
public static final String USER_NAME = "username";
|
|
public static final String PASSWORD = "password";
|
|
public static final String ACTOR = "actor";
|
|
public static final String ACCESS_TOKEN = "token";
|
|
public static final String FULL_NAME = "fullName";
|
|
public static final String EMAIL = "email";
|
|
public static final String TITLE = "title";
|
|
public static final String INVITE_TOKEN = "inviteToken";
|
|
public static final String RESET_TOKEN = "resetToken";
|
|
public static final String BASE_URL = "baseUrl";
|
|
public static final String OIDC_ENABLED = "oidcEnabled";
|
|
public static final String CLIENT_ID = "clientId";
|
|
public static final String CLIENT_SECRET = "clientSecret";
|
|
public static final String DISCOVERY_URI = "discoveryUri";
|
|
|
|
public static final String USER_NAME_CLAIM = "userNameClaim";
|
|
public static final String USER_NAME_CLAIM_REGEX = "userNameClaimRegex";
|
|
public static final String SCOPE = "scope";
|
|
public static final String CLIENT_NAME = "clientName";
|
|
public static final String CLIENT_AUTHENTICATION_METHOD = "clientAuthenticationMethod";
|
|
public static final String JIT_PROVISIONING_ENABLED = "jitProvisioningEnabled";
|
|
public static final String PRE_PROVISIONING_REQUIRED = "preProvisioningRequired";
|
|
public static final String EXTRACT_GROUPS_ENABLED = "extractGroupsEnabled";
|
|
public static final String GROUPS_CLAIM = "groupsClaim";
|
|
public static final String RESPONSE_TYPE = "responseType";
|
|
public static final String RESPONSE_MODE = "responseMode";
|
|
public static final String USE_NONCE = "useNonce";
|
|
public static final String READ_TIMEOUT = "readTimeout";
|
|
public static final String CONNECT_TIMEOUT = "connectTimeout";
|
|
public static final String EXTRACT_JWT_ACCESS_TOKEN_CLAIMS = "extractJwtAccessTokenClaims";
|
|
// Retained for backwards compatibility
|
|
public static final String PREFERRED_JWS_ALGORITHM = "preferredJwsAlgorithm";
|
|
public static final String PREFERRED_JWS_ALGORITHM_2 = "preferredJwsAlgorithm2";
|
|
|
|
/**
|
|
* Determines whether the inbound request should be forward to downstream Metadata Service. Today,
|
|
* this simply checks for the presence of an "Authorization" header or the presence of a valid
|
|
* session cookie issued by the frontend.
|
|
*
|
|
* <p>Note that this method DOES NOT actually verify the authentication token of an inbound
|
|
* request. That will be handled by the downstream Metadata Service. Until then, the request
|
|
* should be treated as UNAUTHENTICATED.
|
|
*
|
|
* <p>Returns true if the request is eligible to be forwarded to GMS, false otherwise.
|
|
*/
|
|
public static boolean isEligibleForForwarding(Http.Request req) {
|
|
return hasValidSessionCookie(req) || hasAuthHeader(req);
|
|
}
|
|
|
|
/**
|
|
* Returns true if a request has a valid session cookie issued by the frontend server. Note that
|
|
* this DOES NOT verify whether the token within the session cookie will be accepted by the
|
|
* downstream GMS service.
|
|
*
|
|
* <p>Note that we depend on the presence of 2 cookies, one accessible to the browser and one not,
|
|
* as well as their agreement to determine authentication status.
|
|
*/
|
|
public static boolean hasValidSessionCookie(final Http.Request req) {
|
|
Map<String, String> sessionCookie = req.session().data();
|
|
return sessionCookie.containsKey(ACCESS_TOKEN)
|
|
&& sessionCookie.containsKey(ACTOR)
|
|
&& req.getCookie(ACTOR).isPresent()
|
|
&& req.session().data().get(ACTOR).equals(req.getCookie(ACTOR).get().value());
|
|
}
|
|
|
|
/** Returns true if a request includes the Authorization header, false otherwise */
|
|
public static boolean hasAuthHeader(final Http.Request req) {
|
|
return req.getHeaders().contains(Http.HeaderNames.AUTHORIZATION);
|
|
}
|
|
|
|
/**
|
|
* Creates a client authentication cookie (actor cookie) with a specified TTL in hours.
|
|
*
|
|
* @param actorUrn the urn of the authenticated actor, e.g. "urn:li:corpuser:datahub"
|
|
* @param ttlInHours the number of hours until the actor cookie expires after being set
|
|
*/
|
|
public static Http.Cookie createActorCookie(
|
|
@Nonnull final String actorUrn,
|
|
@Nonnull final Integer ttlInHours,
|
|
@Nonnull final String sameSite,
|
|
final boolean isSecure) {
|
|
return Http.Cookie.builder(ACTOR, actorUrn)
|
|
.withHttpOnly(false)
|
|
.withMaxAge(Duration.of(ttlInHours, ChronoUnit.HOURS))
|
|
.withSameSite(convertSameSiteValue(sameSite))
|
|
.withSecure(isSecure)
|
|
.build();
|
|
}
|
|
|
|
public static Map<String, String> createSessionMap(
|
|
final String userUrnStr, final String accessToken) {
|
|
final Map<String, String> sessionAttributes = new HashMap<>();
|
|
sessionAttributes.put(ACTOR, userUrnStr);
|
|
sessionAttributes.put(ACCESS_TOKEN, accessToken);
|
|
return sessionAttributes;
|
|
}
|
|
|
|
private AuthUtils() {}
|
|
|
|
private static Http.Cookie.SameSite convertSameSiteValue(@Nonnull final String sameSiteValue) {
|
|
try {
|
|
return Http.Cookie.SameSite.valueOf(sameSiteValue);
|
|
} catch (IllegalArgumentException e) {
|
|
log.warn(
|
|
String.format(
|
|
"Invalid AUTH_COOKIE_SAME_SITE value: %s. Using LAX instead.", sameSiteValue),
|
|
e);
|
|
return Http.Cookie.SameSite.LAX;
|
|
}
|
|
}
|
|
}
|