-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added denied access role filter. * Updated ProxiedUserDetails to use dynamic type for newInstance method. * Removed usage of zookeeper stringutils * Updated with latest changes from integration * bumped release version * removed unnecessary accumulo exclusion * bumped versions for some modules * Updated with latest changes from main/integration * Added a server user details supplier bean * Updated conditionals for server user details supplier * Implemented authorization and query federation for the query microservices * Updating federated user logic to match latest changes on integration * Updated usage of remote user operations for query microservices * Added ordering to authorization bean registrar * pr feedback * PR feedback * PR Feedback * pr feedback
- Loading branch information
Showing
12 changed files
with
575 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
src/main/java/datawave/microservice/authorization/config/AuthorizationsListSupplier.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package datawave.microservice.authorization.config; | ||
|
||
import java.util.function.Supplier; | ||
|
||
import org.springframework.stereotype.Component; | ||
|
||
import datawave.user.AuthorizationsListBase; | ||
import datawave.user.DefaultAuthorizationsList; | ||
|
||
@Component | ||
public class AuthorizationsListSupplier implements Supplier<AuthorizationsListBase<?>> { | ||
@Override | ||
public AuthorizationsListBase<?> get() { | ||
return new DefaultAuthorizationsList(); | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
...authorization/federation/DynamicFederatedAuthorizationServiceBeanDefinitionRegistrar.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package datawave.microservice.authorization.federation; | ||
|
||
import static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
import org.springframework.beans.BeansException; | ||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; | ||
import org.springframework.beans.factory.config.ConstructorArgumentValues; | ||
import org.springframework.beans.factory.support.BeanDefinitionRegistry; | ||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; | ||
import org.springframework.beans.factory.support.GenericBeanDefinition; | ||
import org.springframework.boot.context.properties.bind.Bindable; | ||
import org.springframework.boot.context.properties.bind.Binder; | ||
import org.springframework.core.Ordered; | ||
import org.springframework.core.env.Environment; | ||
|
||
import datawave.microservice.authorization.federation.config.FederatedAuthorizationServiceProperties; | ||
|
||
/** | ||
* This class is used to dynamically create and register FederatedAuthorizationService beans via properties. | ||
*/ | ||
public class DynamicFederatedAuthorizationServiceBeanDefinitionRegistrar implements BeanDefinitionRegistryPostProcessor, Ordered { | ||
|
||
public static final String FEDERATED_AUTHORIZATION_SERVICE_PREFIX = "datawave.authorization.federation.services"; | ||
private final Map<String,FederatedAuthorizationServiceProperties> federatedAuthorizationProperties; | ||
|
||
public DynamicFederatedAuthorizationServiceBeanDefinitionRegistrar(Environment environment) { | ||
// @formatter:off | ||
federatedAuthorizationProperties = Binder.get(environment) | ||
.bind(FEDERATED_AUTHORIZATION_SERVICE_PREFIX, Bindable.mapOf(String.class, FederatedAuthorizationServiceProperties.class)) | ||
.orElse(new HashMap<>()); | ||
// @formatter:off | ||
} | ||
|
||
@Override | ||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { | ||
federatedAuthorizationProperties.forEach((name, props) -> { | ||
GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); | ||
beanDefinition.setBeanClass(FederatedAuthorizationService.class); | ||
|
||
ConstructorArgumentValues constructorArgValues = new ConstructorArgumentValues(); | ||
constructorArgValues.addGenericArgumentValue(props); | ||
beanDefinition.setConstructorArgumentValues(constructorArgValues); | ||
|
||
beanDefinition.setScope(SCOPE_PROTOTYPE); | ||
beanDefinitionRegistry.registerBeanDefinition(name, beanDefinition); | ||
}); | ||
} | ||
|
||
@Override | ||
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { | ||
// intentionally blank | ||
} | ||
@Override | ||
public int getOrder() { | ||
return getPrecedence(); | ||
} | ||
|
||
public static int getPrecedence() { | ||
return HIGHEST_PRECEDENCE; | ||
} | ||
} |
194 changes: 194 additions & 0 deletions
194
...in/java/datawave/microservice/authorization/federation/FederatedAuthorizationService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
package datawave.microservice.authorization.federation; | ||
|
||
import static datawave.microservice.authorization.preauth.ProxiedEntityX509Filter.ENTITIES_HEADER; | ||
import static datawave.microservice.authorization.preauth.ProxiedEntityX509Filter.ISSUERS_HEADER; | ||
|
||
import java.time.Duration; | ||
import java.util.function.Function; | ||
|
||
import org.apache.http.HttpHeaders; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.cache.annotation.Cacheable; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.reactive.function.client.ExchangeStrategies; | ||
import org.springframework.web.reactive.function.client.WebClient; | ||
|
||
import datawave.microservice.authorization.config.AuthorizationsListSupplier; | ||
import datawave.microservice.authorization.federation.config.FederatedAuthorizationServiceProperties; | ||
import datawave.microservice.authorization.federation.config.FederatedAuthorizationServiceProperties.RetryTimeoutProperties; | ||
import datawave.security.authorization.AuthorizationException; | ||
import datawave.security.authorization.DatawaveUser; | ||
import datawave.security.authorization.ProxiedUserDetails; | ||
import datawave.security.authorization.UserOperations; | ||
import datawave.user.AuthorizationsListBase; | ||
import datawave.webservice.result.GenericResponse; | ||
import reactor.core.publisher.Mono; | ||
import reactor.util.retry.Retry; | ||
|
||
public class FederatedAuthorizationService implements UserOperations { | ||
private static final Logger log = LoggerFactory.getLogger(FederatedAuthorizationService.class); | ||
|
||
public static final String INCLUDE_REMOTE_SERVICES = "includeRemoteServices"; | ||
|
||
private FederatedAuthorizationServiceProperties federatedAuthorizationProperties; | ||
private final WebClient webClient; | ||
private AuthorizationsListSupplier authorizationsListSupplier; | ||
|
||
public FederatedAuthorizationService(FederatedAuthorizationServiceProperties federatedAuthorizationProperties, WebClient.Builder webClientBuilder, | ||
AuthorizationsListSupplier authorizationsListSupplier) { | ||
this.federatedAuthorizationProperties = federatedAuthorizationProperties; | ||
// @formatter:off | ||
this.webClient = webClientBuilder | ||
.baseUrl(federatedAuthorizationProperties.getFederatedAuthorizationUri()) | ||
.exchangeStrategies(ExchangeStrategies.builder() | ||
.codecs(clientCodecConfigurer -> clientCodecConfigurer | ||
.defaultCodecs() | ||
.maxInMemorySize(federatedAuthorizationProperties.getMaxBytesToBuffer())) | ||
.build()) | ||
.build(); | ||
// @formatter:on | ||
this.authorizationsListSupplier = authorizationsListSupplier; | ||
} | ||
|
||
private String getProxiedEntities(ProxiedUserDetails currentUser) { | ||
return getProxiedDN(currentUser, (user) -> user.getDn().subjectDN()); | ||
} | ||
|
||
private String getProxiedIssuers(ProxiedUserDetails currentUser) { | ||
return getProxiedDN(currentUser, (user) -> user.getDn().issuerDN()); | ||
} | ||
|
||
private String getProxiedDN(ProxiedUserDetails currentUser, Function<DatawaveUser,String> getDN) { | ||
StringBuilder builder = new StringBuilder(); | ||
for (DatawaveUser user : currentUser.getProxiedUsers()) { | ||
builder.append('<').append(getDN.apply(user)).append('>'); | ||
} | ||
return builder.toString(); | ||
} | ||
|
||
@Override | ||
@Cacheable(value = "getRemoteUser", key = "{#currentUser}", cacheManager = "remoteOperationsCacheManager") | ||
public <T extends ProxiedUserDetails> T getRemoteUser(T currentUser) throws AuthorizationException { | ||
return UserOperations.super.getRemoteUser(currentUser); | ||
} | ||
|
||
@Override | ||
@Cacheable(value = "listEffectiveAuthorizations", key = "{#currentUser}", cacheManager = "remoteOperationsCacheManager") | ||
public AuthorizationsListBase listEffectiveAuthorizations(ProxiedUserDetails currentUser) throws AuthorizationException { | ||
return listEffectiveAuthorizations(currentUser, true); | ||
} | ||
|
||
public AuthorizationsListBase listEffectiveAuthorizations(ProxiedUserDetails currentUser, boolean federate) throws AuthorizationException { | ||
log.info("FederatedAuthorizationService listEffectiveAuthorizations (federate: {}) for {}", federate, currentUser.getPrimaryUser()); | ||
|
||
try { | ||
// @formatter:off | ||
//noinspection rawtypes,unchecked | ||
ResponseEntity<AuthorizationsListBase> authorizationsListBaseResponseEntity = (ResponseEntity<AuthorizationsListBase>) getResponseEntity( | ||
currentUser, | ||
federate, | ||
federatedAuthorizationProperties.getListEffectiveAuthorizationsRetry(), | ||
"listEffectiveAuthorizations", | ||
authorizationsListSupplier.get().getClass()); | ||
// @formatter:on | ||
|
||
AuthorizationException authorizationException; | ||
if (authorizationsListBaseResponseEntity != null) { | ||
AuthorizationsListBase authorizationsListBase = authorizationsListBaseResponseEntity.getBody(); | ||
|
||
if (authorizationsListBaseResponseEntity.getStatusCode() == HttpStatus.OK) { | ||
return authorizationsListBase; | ||
} else { | ||
authorizationException = new AuthorizationException("Unknown error occurred while calling listEffectiveAuthorizations for " | ||
+ currentUser.getPrimaryUser() + ", Status Code: " + authorizationsListBaseResponseEntity.getStatusCodeValue()); | ||
} | ||
} else { | ||
authorizationException = new AuthorizationException( | ||
"Unknown error occurred while calling listEffectiveAuthorizations for " + currentUser.getPrimaryUser()); | ||
} | ||
throw authorizationException; | ||
} catch (ServiceException e) { | ||
log.error("Timed out waiting for federated listEffectiveAuthorizations response"); | ||
throw new AuthorizationException("Timed out waiting for federated listEffectiveAuthorizations response", e); | ||
} | ||
} | ||
|
||
@Override | ||
public GenericResponse<String> flushCachedCredentials(ProxiedUserDetails currentUser) throws AuthorizationException { | ||
return flushCachedCredentials(currentUser, true); | ||
} | ||
|
||
public GenericResponse<String> flushCachedCredentials(ProxiedUserDetails currentUser, boolean federate) throws AuthorizationException { | ||
log.info("FederatedAuthorizationService flushCachedCredentials (federate: {}) for {}", federate, currentUser.getPrimaryUser()); | ||
|
||
try { | ||
// @formatter:off | ||
//noinspection rawtypes,unchecked | ||
ResponseEntity<GenericResponse> genericResponseEntity = (ResponseEntity<GenericResponse>)getResponseEntity( | ||
currentUser, | ||
federate, | ||
federatedAuthorizationProperties.getFlushCachedCredentialsRetry(), | ||
"flushCachedCredentials", | ||
GenericResponse.class); | ||
// @formatter:on | ||
|
||
AuthorizationException authorizationException; | ||
if (genericResponseEntity != null) { | ||
GenericResponse genericResponse = genericResponseEntity.getBody(); | ||
|
||
if (genericResponseEntity.getStatusCode() == HttpStatus.OK) { | ||
return genericResponse; | ||
} else { | ||
authorizationException = new AuthorizationException("Unknown error occurred while calling flushCachedCredentials for " | ||
+ currentUser.getPrimaryUser() + ", Status Code: " + genericResponseEntity.getStatusCodeValue()); | ||
} | ||
} else { | ||
authorizationException = new AuthorizationException( | ||
"Unknown error occurred while calling flushCachedCredentials for " + currentUser.getPrimaryUser()); | ||
} | ||
throw authorizationException; | ||
} catch (ServiceException e) { | ||
log.error("Timed out waiting for federated flushCachedCredentials response"); | ||
throw new AuthorizationException("Timed out waiting for federated flushCachedCredentials response", e); | ||
} | ||
} | ||
|
||
protected ResponseEntity<?> getResponseEntity(ProxiedUserDetails currentUser, boolean federate, RetryTimeoutProperties retry, String endpoint, | ||
Class entityClass) { | ||
// @formatter:off | ||
return (ResponseEntity<?>) webClient.get() | ||
.uri(uriBuilder -> uriBuilder | ||
.path(endpoint) | ||
.queryParam("includeRemoteServices", federate) | ||
.build()) | ||
.header(ENTITIES_HEADER, getProxiedEntities(currentUser)) | ||
.header(ISSUERS_HEADER, getProxiedIssuers(currentUser)) | ||
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) | ||
.retrieve() | ||
// don't retry on 4xx errors | ||
.onStatus(HttpStatus::is5xxServerError, | ||
clientResponse -> Mono.error(new ServiceException("Service Error", clientResponse.rawStatusCode()))) | ||
.toEntity(entityClass) | ||
.retryWhen(Retry | ||
.fixedDelay(retry.getRetries(), Duration.ofMillis(retry.getRetryDelayMillis())) | ||
.filter(throwable -> throwable instanceof ServiceException) | ||
.onRetryExhaustedThrow(((retryBackoffSpec, retrySignal) -> { | ||
throw new ServiceException("External Service failed to process after max retries", | ||
HttpStatus.SERVICE_UNAVAILABLE.value()); | ||
}))) | ||
.block(Duration.ofMillis(retry.getTimeoutMillis())); | ||
// @formatter:on | ||
} | ||
|
||
public class ServiceException extends RuntimeException { | ||
int statusCode; | ||
|
||
public ServiceException(String message, int statusCode) { | ||
super(message); | ||
this.statusCode = statusCode; | ||
} | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
...ave/microservice/authorization/federation/config/FederatedAuthorizationConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package datawave.microservice.authorization.federation.config; | ||
|
||
import java.util.LinkedHashSet; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
import datawave.security.authorization.UserOperations; | ||
|
||
@EnableConfigurationProperties(FederatedAuthorizationProperties.class) | ||
@Configuration | ||
public class FederatedAuthorizationConfiguration { | ||
|
||
@Bean | ||
public Set<UserOperations> registeredFederatedUserOperations(FederatedAuthorizationProperties federatedAuthorizationProperties, | ||
Map<String,UserOperations> userOperationsMap) { | ||
Set<UserOperations> registeredFederatedUserOperations = new LinkedHashSet<>(); | ||
for (Map.Entry<String,UserOperations> entry : userOperationsMap.entrySet()) { | ||
if (federatedAuthorizationProperties.getRegisteredServices().contains(entry.getKey())) { | ||
registeredFederatedUserOperations.add(entry.getValue()); | ||
} | ||
} | ||
return registeredFederatedUserOperations; | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
...tawave/microservice/authorization/federation/config/FederatedAuthorizationProperties.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package datawave.microservice.authorization.federation.config; | ||
|
||
import java.util.LinkedHashSet; | ||
import java.util.Set; | ||
|
||
import org.springframework.boot.context.properties.ConfigurationProperties; | ||
|
||
@ConfigurationProperties(prefix = "datawave.authorization.federation") | ||
public class FederatedAuthorizationProperties { | ||
private Set<String> registeredServices = new LinkedHashSet<>(); | ||
|
||
public Set<String> getRegisteredServices() { | ||
return registeredServices; | ||
} | ||
|
||
public void setRegisteredServices(Set<String> registeredServices) { | ||
this.registeredServices = registeredServices; | ||
} | ||
} |
Oops, something went wrong.