package org.gcube.common.keycloak; import static org.gcube.resources.discovery.icclient.ICFactory.clientFor; import static org.gcube.resources.discovery.icclient.ICFactory.queryFor; import java.net.MalformedURLException; import java.net.URL; import java.util.List; import org.gcube.common.keycloak.model.ModelUtils; import org.gcube.common.keycloak.model.TokenIntrospectionResponse; import org.gcube.common.keycloak.model.TokenResponse; import org.gcube.common.resources.gcore.ServiceEndpoint; import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint; import org.gcube.common.scope.api.ScopeProvider; import org.gcube.resources.discovery.client.api.DiscoveryClient; import org.gcube.resources.discovery.client.queries.api.SimpleQuery; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @SuppressWarnings("deprecation") public class DefaultKeycloakClientLegacyIS extends DefaultKeycloakClient implements KeycloakClientLegacyIS { protected static Logger logger = LoggerFactory.getLogger(DefaultKeycloakClientLegacyIS.class); @Override public URL findTokenEndpointURL() throws KeycloakClientException { logger.debug("Checking ScopeProvider's scope presence and format"); String originalScope = ScopeProvider.instance.get(); if (originalScope == null || !originalScope.startsWith("/") || originalScope.length() < 2) { throw new KeycloakClientException(originalScope == null ? "Scope not found in ScopeProvider" : "Bad scope name found: " + originalScope); } logger.debug("Assuring use the rootVO to query the endpoint simple query. Actual scope is: {}", originalScope); String rootVOScope = "/" + originalScope.split("/")[1]; logger.debug("Setting rootVO scope into provider as: {}", rootVOScope); List accessPoints = null; // trying to be thread safe at least for these calls synchronized (ScopeProvider.instance) { boolean scopeModified = false; if (!ScopeProvider.instance.get().equals(rootVOScope)) { logger.debug("Overriding scope in the provider with rootVO scope : {}", rootVOScope); ScopeProvider.instance.set(rootVOScope); scopeModified = true; } logger.debug("Creating simple query"); SimpleQuery query = queryFor(ServiceEndpoint.class); query.addCondition( String.format("$resource/Profile/Category/text() eq '%s'", CATEGORY)) .addCondition(String.format("$resource/Profile/Name/text() eq '%s'", NAME)) .setResult(String.format("$resource/Profile/AccessPoint[Description/text() eq '%s']", DESCRIPTION)); logger.debug("Creating client for AccessPoint"); DiscoveryClient client = clientFor(AccessPoint.class); logger.trace("Submitting query: {}", query); accessPoints = client.submit(query); if (scopeModified) { logger.debug("Resetting scope into provider to the original value: {}", originalScope); ScopeProvider.instance.set(originalScope); } } if (accessPoints.size() == 0) { throw new KeycloakClientException("Service endpoint not found"); } else if (accessPoints.size() > 1) { throw new KeycloakClientException("Found more than one endpoint with query"); } String address = accessPoints.iterator().next().address(); logger.debug("Found address: {}", address); try { return new URL(address); } catch (MalformedURLException e) { throw new KeycloakClientException("Cannot create URL from address: " + address, e); } } @Override public URL computeIntrospectionEndpointURL() throws KeycloakClientException { return computeIntrospectionEndpointURL(findTokenEndpointURL()); } @Override public TokenResponse queryOIDCToken(String clientId, String clientSecret) throws KeycloakClientException { return queryOIDCToken(findTokenEndpointURL(), clientId, clientSecret); } @Override public TokenResponse queryUMAToken(String clientId, String clientSecret, List permissions) throws KeycloakClientException { return queryUMAToken(clientId, clientSecret, ScopeProvider.instance.get(), permissions); } @Override public TokenResponse queryUMAToken(TokenResponse oidcTokenResponse, String audience, List permissions) throws KeycloakClientException { return queryUMAToken(findTokenEndpointURL(), constructBeareAuthenticationHeader(oidcTokenResponse), audience, permissions); } @Override public TokenResponse queryUMAToken(String clientId, String clientSecret, String audience, List permissions) throws KeycloakClientException { return queryUMAToken(findTokenEndpointURL(), clientId, clientSecret, audience, permissions); } @Override public TokenResponse refreshToken(TokenResponse tokenResponse) throws KeycloakClientException { return refreshToken((String) null, tokenResponse); } @Override public TokenResponse refreshToken(String clientId, TokenResponse tokenResponse) throws KeycloakClientException { return refreshToken(clientId, null, tokenResponse); } @Override public TokenResponse refreshToken(String clientId, String clientSecret, TokenResponse tokenResponse) throws KeycloakClientException { return refreshToken(findTokenEndpointURL(), clientId, clientSecret, tokenResponse); } @Override public TokenResponse refreshToken(String refreshTokenJWTString) throws KeycloakClientException { try { String clientId = ModelUtils.getClientIdFromToken(ModelUtils.getRefreshTokenFrom(refreshTokenJWTString)); return refreshToken(clientId, refreshTokenJWTString); } catch (Exception e) { throw new KeycloakClientException("Cannot construct access token object from token response", e); } } @Override public TokenResponse refreshToken(String clientId, String refreshTokenJWTString) throws KeycloakClientException { return refreshToken(clientId, null, refreshTokenJWTString); } @Override public TokenResponse refreshToken(String clientId, String clientSecret, String refreshTokenJWTString) throws KeycloakClientException { return refreshToken(findTokenEndpointURL(), clientId, clientSecret, refreshTokenJWTString); } @Override public TokenIntrospectionResponse introspectAccessToken(String clientId, String clientSecret, String accessTokenJWTString) throws KeycloakClientException { return introspectAccessToken(computeIntrospectionEndpointURL(), clientId, clientSecret, accessTokenJWTString); } @Override public boolean isAccessTokenVerified(String clientId, String clientSecret, String accessTokenJWTString) throws KeycloakClientException { return isAccessTokenVerified(computeIntrospectionEndpointURL(), clientId, clientSecret, accessTokenJWTString); } }