Skip to content

Commit

Permalink
Feature/query microservices (#8)
Browse files Browse the repository at this point in the history
* Removed unnecessary jackson annotations to address enunciate issues

* bumped release version

* bumped versions for some modules

* Updated with latest changes from main/integration

* Implemented authorization and query federation for the query microservices
  • Loading branch information
jwomeara authored May 20, 2024
1 parent d5029a3 commit 9f64203
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 4 deletions.
8 changes: 4 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
<parent>
<groupId>gov.nsa.datawave.microservice</groupId>
<artifactId>datawave-microservice-parent</artifactId>
<version>3.0.4</version>
<version>4.0.0-SNAPSHOT</version>
<relativePath>../../microservices/microservice-parent/pom.xml</relativePath>
</parent>
<artifactId>base-rest-responses</artifactId>
<version>3.0.1-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<url>https://code.nsa.gov/datawave-base-rest-responses</url>
<licenses>
<license>
Expand All @@ -26,8 +26,8 @@
<datawave.webservice.namespace>http://webservice.datawave.nsa/v1</datawave.webservice.namespace>
<version.commons-lang3>3.12.0</version.commons-lang3>
<version.jaxb>2.3.3</version.jaxb>
<version.microservice.accumulo-utils>3.0.1</version.microservice.accumulo-utils>
<version.microservice.common-utils>2.0.0</version.microservice.common-utils>
<version.microservice.accumulo-utils>4.0.0-SNAPSHOT</version.microservice.accumulo-utils>
<version.microservice.common-utils>3.0.0-SNAPSHOT</version.microservice.common-utils>
<version.powermock>2.0.4</version.powermock>
<version.protobuf>2.5.0</version.protobuf>
<version.protostuff>1.6.2</version.protostuff>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package datawave.security.authorization;

import java.util.Collections;
import java.util.function.Function;
import java.util.function.Supplier;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import datawave.user.AuthorizationsListBase;
import datawave.webservice.result.GenericResponse;

/**
* A conditional remote user operations will only invoke the delegate remote service base on a specified function of the specified principal. For example we may
* only need to invoke the remote user operations if we know the remote system will have additional auths that this user will need for the query logic being
* invoked.
*
* An example may be a composite query that call a local and a remote query logic. Perhaps we can already tell that the user will not be able to get any
* additional authorities from the remote system and hence the remote call will not be required.
*/
public class ConditionalRemoteUserOperations implements UserOperations {
private static final Logger log = LoggerFactory.getLogger(ConditionalRemoteUserOperations.class);

private UserOperations delegate;
private Function<ProxiedUserDetails,Boolean> condition;
private Supplier<AuthorizationsListBase> authorizationsListBaseSupplier;

private static final GenericResponse<String> EMPTY_RESPONSE = new GenericResponse<>();

public boolean isFiltered(ProxiedUserDetails principal) {
if (!condition.apply(principal)) {
if (log.isDebugEnabled()) {
log.debug("Filter " + condition + " blocking " + principal.getName() + " from " + delegate + " user operations");
}
return true;
} else {
if (log.isDebugEnabled()) {
log.debug("Passing through filter " + condition + " for " + principal.getName() + " for " + delegate + " user operations");
}
return false;
}
}

@Override
public AuthorizationsListBase listEffectiveAuthorizations(ProxiedUserDetails callerObject) throws AuthorizationException {
assert (delegate != null);
assert (condition != null);
assert (authorizationsListBaseSupplier != null);

if (!isFiltered(callerObject)) {
return delegate.listEffectiveAuthorizations(callerObject);
} else {
AuthorizationsListBase response = authorizationsListBaseSupplier.get();
response.setUserAuths(callerObject.getPrimaryUser().getDn().subjectDN(), callerObject.getPrimaryUser().getDn().issuerDN(), Collections.EMPTY_LIST);
return response;
}
}

@Override
public GenericResponse<String> flushCachedCredentials(ProxiedUserDetails callerObject) throws AuthorizationException {
assert (delegate != null);
assert (condition != null);
assert (authorizationsListBaseSupplier != null);

if (!isFiltered(callerObject)) {
return delegate.flushCachedCredentials(callerObject);
} else {
return EMPTY_RESPONSE;
}
}

public UserOperations getDelegate() {
return delegate;
}

public void setDelegate(UserOperations delegate) {
this.delegate = delegate;
}

public Function<ProxiedUserDetails,Boolean> getCondition() {
return condition;
}

public void setCondition(Function<ProxiedUserDetails,Boolean> condition) {
this.condition = condition;
}

public Supplier<AuthorizationsListBase> getAuthorizationsListBaseSupplier() {
return authorizationsListBaseSupplier;
}

public void setAuthorizationsListBaseSupplier(Supplier<AuthorizationsListBase> authorizationsListBaseSupplier) {
this.authorizationsListBaseSupplier = authorizationsListBaseSupplier;
}
}
58 changes: 58 additions & 0 deletions src/main/java/datawave/security/authorization/UserOperations.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package datawave.security.authorization;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;

import datawave.user.AuthorizationsListBase;
import datawave.webservice.result.GenericResponse;

/**
* A user operations service is one that can pass calls off to another external user operations endpoint
*/
public interface UserOperations {

AuthorizationsListBase listEffectiveAuthorizations(ProxiedUserDetails callerObject) throws AuthorizationException;

GenericResponse<String> flushCachedCredentials(ProxiedUserDetails callerObject) throws AuthorizationException;

default <T extends ProxiedUserDetails> T getRemoteUser(T currentUser) throws AuthorizationException {
// get the effective authorizations for this user
AuthorizationsListBase auths = listEffectiveAuthorizations(currentUser);
// create a new set of proxied users
List<DatawaveUser> mappedUsers = new ArrayList<>();
Map<SubjectIssuerDNPair,DatawaveUser> localUsers = currentUser.getProxiedUsers().stream()
.collect(Collectors.toMap(DatawaveUser::getDn, Function.identity(), (v1, v2) -> v2));

// create a mapped user for the primary user with the auths returned by listEffectiveAuthorizations
SubjectIssuerDNPair primaryDn = SubjectIssuerDNPair.of(auths.getUserDn(), auths.getIssuerDn());
DatawaveUser localUser = localUsers.get(primaryDn);
mappedUsers.add(new DatawaveUser(primaryDn, localUser.getUserType(), auths.getAllAuths(), auths.getAuthMapping().keySet(),
toMultimap(auths.getAuthMapping()), System.currentTimeMillis()));
// for each proxied user, create a new user with the auths returned by listEffectiveAuthorizations
Map<AuthorizationsListBase.SubjectIssuerDNPair,Set<String>> authMap = auths.getAuths();
for (Map.Entry<AuthorizationsListBase.SubjectIssuerDNPair,Set<String>> entry : authMap.entrySet()) {
SubjectIssuerDNPair pair = SubjectIssuerDNPair.of(entry.getKey().subjectDN, entry.getKey().issuerDN);
if (!pair.equals(primaryDn)) {
mappedUsers.add(new DatawaveUser(pair, DatawaveUser.UserType.SERVER, entry.getValue(), null, null, System.currentTimeMillis()));
}
}

// return a proxied user details with the mapped users
return currentUser.newInstance(mappedUsers);
}

static Multimap<String,String> toMultimap(Map<String,Collection<String>> map) {
Multimap<String,String> multimap = HashMultimap.create();
map.forEach(multimap::putAll);
return multimap;
}

}

0 comments on commit 9f64203

Please sign in to comment.