diff --git a/commons/pom.xml b/commons/pom.xml
new file mode 100644
index 00000000000..6d083bc6efe
--- /dev/null
+++ b/commons/pom.xml
@@ -0,0 +1,18 @@
+
+
+ 4.0.0
+
+ gov.nsa.datawave
+ datawave-parent
+ 7.40.0-SNAPSHOT
+
+
+ gov.nsa.datawave.commons
+ datawave-commons-parent
+ pom
+ ${project.artifactId}
+
+
+ security
+
+
diff --git a/commons/security/README.md b/commons/security/README.md
new file mode 100644
index 00000000000..cb2dcf01d62
--- /dev/null
+++ b/commons/security/README.md
@@ -0,0 +1,6 @@
+# Overview
+
+This project contains security-related classes that are commonly used between the main Datawave project and the microservices. It is expected that this project will be configured and deployed as a JBOSS module via the [Wildfly assembly](../../web-services/deploy/application) project to make it available to the Datawave EAR deployment.
+
+## Note:
+Any compile dependencies here are expected to be imported into the Datawave webservices projects with scope `provided`, and provided via JBOSS modules. This is required to avoid classloader conflicts between the JBOSS modules and the Datawave EAR deployment. See the [Wildfly assembly README](../../web-services/deploy/application/README.md) for more details.
diff --git a/commons/security/pom.xml b/commons/security/pom.xml
new file mode 100644
index 00000000000..b4d8a134291
--- /dev/null
+++ b/commons/security/pom.xml
@@ -0,0 +1,61 @@
+
+
+ 4.0.0
+
+ gov.nsa.datawave.commons
+ datawave-commons-parent
+ 7.40.0-SNAPSHOT
+
+
+ datawave-commons-security
+ ${project.artifactId}
+
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ ${version.jackson}
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${version.jackson}
+
+
+ com.google.guava
+ guava
+
+
+ io.jsonwebtoken
+ jjwt-api
+ ${version.jjwt}
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ ${version.jjwt}
+
+
+ jakarta.xml.bind
+ jakarta.xml.bind-api
+ ${version.jakarta}
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.junit-pioneer
+ junit-pioneer
+ test
+
+
+
diff --git a/microservices/services/authorization/api/src/main/java/datawave/security/authorization/AuthorizationException.java b/commons/security/src/main/java/datawave/security/authorization/AuthorizationException.java
similarity index 100%
rename from microservices/services/authorization/api/src/main/java/datawave/security/authorization/AuthorizationException.java
rename to commons/security/src/main/java/datawave/security/authorization/AuthorizationException.java
diff --git a/microservices/services/authorization/api/src/main/java/datawave/security/authorization/CachedDatawaveUserService.java b/commons/security/src/main/java/datawave/security/authorization/CachedDatawaveUserService.java
similarity index 100%
rename from microservices/services/authorization/api/src/main/java/datawave/security/authorization/CachedDatawaveUserService.java
rename to commons/security/src/main/java/datawave/security/authorization/CachedDatawaveUserService.java
diff --git a/core/common-util/src/main/java/datawave/security/authorization/DatawavePrincipal.java b/commons/security/src/main/java/datawave/security/authorization/DatawavePrincipal.java
similarity index 97%
rename from core/common-util/src/main/java/datawave/security/authorization/DatawavePrincipal.java
rename to commons/security/src/main/java/datawave/security/authorization/DatawavePrincipal.java
index 0a151843b62..a94deee0bd1 100644
--- a/core/common-util/src/main/java/datawave/security/authorization/DatawavePrincipal.java
+++ b/commons/security/src/main/java/datawave/security/authorization/DatawavePrincipal.java
@@ -17,7 +17,7 @@
import javax.xml.bind.annotation.XmlType;
import datawave.security.authorization.DatawaveUser.UserType;
-import datawave.security.util.ProxiedEntityUtils;
+import datawave.security.util.DnUtils;
/**
* A {@link Principal} that represents a set of proxied {@link DatawaveUser}s. For example, this proxied user could represent a GUI server acting on behalf of a
@@ -103,8 +103,8 @@ static protected List orderProxiedUsers(List datawav
if (position >= 0) {
users.add(datawaveUsers.get(position));
if (datawaveUsers.size() > 1) {
- datawaveUsers.stream().limit(position).forEach(u -> users.add(u));
- datawaveUsers.stream().skip(position + 1).forEach(u -> users.add(u));
+ datawaveUsers.stream().limit(position).forEach(users::add);
+ datawaveUsers.stream().skip(position + 1).forEach(users::add);
}
}
return users;
@@ -152,7 +152,7 @@ public String getName() {
@Override
public String getShortName() {
- return ProxiedEntityUtils.getShortName(getPrimaryUser().getName());
+ return DnUtils.getShortName(getPrimaryUser().getName());
}
public SubjectIssuerDNPair getUserDN() {
diff --git a/microservices/services/authorization/api/src/main/java/datawave/security/authorization/DatawaveUser.java b/commons/security/src/main/java/datawave/security/authorization/DatawaveUser.java
similarity index 96%
rename from microservices/services/authorization/api/src/main/java/datawave/security/authorization/DatawaveUser.java
rename to commons/security/src/main/java/datawave/security/authorization/DatawaveUser.java
index 9c03597efba..75aec6ced6f 100644
--- a/microservices/services/authorization/api/src/main/java/datawave/security/authorization/DatawaveUser.java
+++ b/commons/security/src/main/java/datawave/security/authorization/DatawaveUser.java
@@ -12,13 +12,14 @@
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
-import datawave.security.util.ProxiedEntityUtils;
+import datawave.security.util.DnUtils;
/**
* A user of a DATAWAVE service. Typically, one or more of these users (a chain where a user called an intermediate service which in turn called us) is
* represented with a DatawavePrincipal.
*/
public class DatawaveUser implements Serializable {
+
private static final long serialVersionUID = -6676807246749142999L;
public enum UserType {
@@ -26,6 +27,7 @@ public enum UserType {
}
public static final DatawaveUser ANONYMOUS_USER = new DatawaveUser(SubjectIssuerDNPair.of("ANONYMOUS"), UserType.USER, null, null, null, null, -1L);
+
private final String name;
private final String commonName;
private final String email;
@@ -58,8 +60,8 @@ public DatawaveUser(@JsonProperty(value = "dn", required = true) SubjectIssuerDN
@JsonProperty(value = "creationTime", defaultValue = "-1L") long creationTime,
@JsonProperty(value = "expirationTime", defaultValue = "-1L") long expirationTime) {
this.name = dn.toString();
- this.commonName = ProxiedEntityUtils.getCommonName(dn.subjectDN());
- this.login = ProxiedEntityUtils.getShortName(dn.subjectDN());
+ this.commonName = DnUtils.getCommonName(dn.subjectDN());
+ this.login = DnUtils.getShortName(dn.subjectDN());
this.email = email;
this.dn = dn;
this.userType = userType;
diff --git a/microservices/services/authorization/api/src/main/java/datawave/security/authorization/DatawaveUserInfo.java b/commons/security/src/main/java/datawave/security/authorization/DatawaveUserInfo.java
similarity index 100%
rename from microservices/services/authorization/api/src/main/java/datawave/security/authorization/DatawaveUserInfo.java
rename to commons/security/src/main/java/datawave/security/authorization/DatawaveUserInfo.java
diff --git a/microservices/services/authorization/api/src/main/java/datawave/security/authorization/DatawaveUserService.java b/commons/security/src/main/java/datawave/security/authorization/DatawaveUserService.java
similarity index 100%
rename from microservices/services/authorization/api/src/main/java/datawave/security/authorization/DatawaveUserService.java
rename to commons/security/src/main/java/datawave/security/authorization/DatawaveUserService.java
diff --git a/microservices/services/authorization/api/src/main/java/datawave/security/authorization/DatawaveUserV1.java b/commons/security/src/main/java/datawave/security/authorization/DatawaveUserV1.java
similarity index 100%
rename from microservices/services/authorization/api/src/main/java/datawave/security/authorization/DatawaveUserV1.java
rename to commons/security/src/main/java/datawave/security/authorization/DatawaveUserV1.java
diff --git a/microservices/services/authorization/api/src/main/java/datawave/security/authorization/JWTTokenHandler.java b/commons/security/src/main/java/datawave/security/authorization/JWTTokenHandler.java
similarity index 99%
rename from microservices/services/authorization/api/src/main/java/datawave/security/authorization/JWTTokenHandler.java
rename to commons/security/src/main/java/datawave/security/authorization/JWTTokenHandler.java
index 15fcd6c997a..431893cddd4 100644
--- a/microservices/services/authorization/api/src/main/java/datawave/security/authorization/JWTTokenHandler.java
+++ b/commons/security/src/main/java/datawave/security/authorization/JWTTokenHandler.java
@@ -137,7 +137,7 @@ public Collection createUsersFromToken(String token, String claimN
return principalsClaim.stream().map(obj -> objectMapper.convertValue(obj, DatawaveUser.class)).collect(Collectors.toList());
}
- private class CustomJWTBuilder extends DefaultJwtBuilder {
+ private static class CustomJWTBuilder extends DefaultJwtBuilder {
private final ObjectMapper objectMapper;
private CustomJWTBuilder(ObjectMapper objectMapper) {
diff --git a/microservices/services/authorization/api/src/main/java/datawave/security/authorization/ProxiedUserDetails.java b/commons/security/src/main/java/datawave/security/authorization/ProxiedUserDetails.java
similarity index 100%
rename from microservices/services/authorization/api/src/main/java/datawave/security/authorization/ProxiedUserDetails.java
rename to commons/security/src/main/java/datawave/security/authorization/ProxiedUserDetails.java
diff --git a/microservices/services/authorization/api/src/main/java/datawave/security/authorization/SubjectIssuerDNPair.java b/commons/security/src/main/java/datawave/security/authorization/SubjectIssuerDNPair.java
similarity index 88%
rename from microservices/services/authorization/api/src/main/java/datawave/security/authorization/SubjectIssuerDNPair.java
rename to commons/security/src/main/java/datawave/security/authorization/SubjectIssuerDNPair.java
index ea90cf789f6..cccbb120e88 100644
--- a/microservices/services/authorization/api/src/main/java/datawave/security/authorization/SubjectIssuerDNPair.java
+++ b/commons/security/src/main/java/datawave/security/authorization/SubjectIssuerDNPair.java
@@ -8,12 +8,12 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Preconditions;
-import datawave.security.util.ProxiedEntityUtils;
+import datawave.security.util.DnUtils;
/**
* A simple pair containing a subject and (optional) issuer DN. The supplied DN values are normalized into a lower-case form with the CN portion first.
*
- * @see ProxiedEntityUtils#normalizeDN(String)
+ * @see DnUtils#normalizeDN(String)
*/
public class SubjectIssuerDNPair implements Serializable {
@@ -61,7 +61,7 @@ public static SubjectIssuerDNPair of(@JsonProperty("subjectDN") String subjectDN
* if DNs cannot be parsed from the argument, or if exactly two DNs are not parsed
*/
public static SubjectIssuerDNPair parse(String value) {
- String[] dns = ProxiedEntityUtils.splitProxiedSubjectIssuerDNs(value);
+ String[] dns = DnUtils.splitProxiedSubjectIssuerDNs(value);
if (dns.length != 2) {
throw new IllegalArgumentException("'" + value + "' must contain a single subject and issuer DN");
}
@@ -70,9 +70,9 @@ public static SubjectIssuerDNPair parse(String value) {
protected SubjectIssuerDNPair(String subjectDN, String issuerDN) {
Preconditions.checkNotNull(subjectDN, "Parameter subjectDN must not be null");
- this.subjectDN = ProxiedEntityUtils.normalizeDN(subjectDN);
+ this.subjectDN = DnUtils.normalizeDN(subjectDN);
if (issuerDN != null) {
- this.issuerDN = ProxiedEntityUtils.normalizeDN(issuerDN);
+ this.issuerDN = DnUtils.normalizeDN(issuerDN);
} else {
this.issuerDN = null;
}
@@ -90,7 +90,7 @@ public String issuerDN() {
@Override
public String toString() {
- return issuerDN == null ? subjectDN + "<>" : ProxiedEntityUtils.buildProxiedDN(subjectDN, issuerDN);
+ return issuerDN == null ? subjectDN + "<>" : DnUtils.buildProxiedDN(subjectDN, issuerDN);
}
@Override
diff --git a/commons/security/src/main/java/datawave/security/cert/DatawaveCertVerifier.java b/commons/security/src/main/java/datawave/security/cert/DatawaveCertVerifier.java
new file mode 100644
index 00000000000..b39eabafbca
--- /dev/null
+++ b/commons/security/src/main/java/datawave/security/cert/DatawaveCertVerifier.java
@@ -0,0 +1,122 @@
+package datawave.security.cert;
+
+import java.security.KeyStore;
+import java.security.cert.X509Certificate;
+import java.util.Objects;
+
+import org.slf4j.Logger;
+
+/**
+ * A Datawave-specific {@link X509CertificateVerifier} implementation.
+ */
+public class DatawaveCertVerifier implements X509CertificateVerifier {
+
+ public enum OcspLevel {
+ OFF, OPTIONAL, REQUIRED
+ }
+
+ protected Logger log;
+ protected boolean trace;
+ protected OcspLevel ocspLevel = OcspLevel.OFF;
+
+ /**
+ * Verify the given certificate
+ *
+ * @param cert
+ * the X509Certificate to verify
+ * @param alias
+ * the certificate alias
+ * @param keystore
+ * the keystore for the cert
+ * @param truststore
+ * the truststore for the cert
+ * @return whether the certificate is considered valid
+ */
+ @Override
+ public boolean verify(X509Certificate cert, String alias, KeyStore keystore, KeyStore truststore) {
+ boolean validity = false;
+ try {
+ cert.checkValidity();
+ validity = checkOCSP(cert, alias, truststore);
+ } catch (Exception e) {
+ if (trace)
+ log.trace("Validity exception", e);
+ }
+ return validity;
+
+ }
+
+ /**
+ * Handle OSCP initialization.
+ */
+ protected void initOcsp() {}
+
+ /**
+ * Return the OSCP level set for this verifier is supported for the given certificate.
+ *
+ * @param cert
+ * the certificate
+ * @param alias
+ * the certificate alias
+ * @param truststore
+ * the truststore
+ * @return true if the OSCP level is supported, or false otherwise
+ */
+ protected boolean checkOCSP(X509Certificate cert, String alias, KeyStore truststore) {
+ if (Objects.requireNonNull(ocspLevel) == OcspLevel.OFF) {
+ return true;
+ } else {
+ log.error("OCSP level {} is not supported!", ocspLevel);
+ throw new IllegalArgumentException("OCSP level " + ocspLevel + " is not supported!");
+ }
+ }
+
+ /**
+ * Return whether the given issuer is supported.
+ *
+ * @param issuerSubjectDn
+ * the issuer DN
+ * @param trustStore
+ * the truststore
+ * @return true if the issuer is supported, or false otherwise
+ */
+ public boolean isIssuerSupported(String issuerSubjectDn, KeyStore trustStore) {
+ return true;
+ }
+
+ /**
+ * Set the delegate logger for this {@link DatawaveCertVerifier}.
+ *
+ * @param log
+ * the logger
+ */
+ public void setLogger(Logger log) {
+ this.log = log;
+ this.trace = log.isTraceEnabled();
+ }
+
+ /**
+ * Return the OSCP level.
+ *
+ * @return the OSCP level
+ */
+ public OcspLevel getOcspLevel() {
+ return ocspLevel;
+ }
+
+ /**
+ * Set the OSCP level.
+ *
+ * @param level
+ * the OSCP level
+ */
+ public void setOcspLevel(String level) {
+ ocspLevel = OcspLevel.valueOf(level.toUpperCase());
+ switch (ocspLevel) {
+ case REQUIRED:
+ case OPTIONAL:
+ initOcsp();
+ break;
+ }
+ }
+}
diff --git a/commons/security/src/main/java/datawave/security/cert/SSLStores.java b/commons/security/src/main/java/datawave/security/cert/SSLStores.java
new file mode 100644
index 00000000000..e14221ac5df
--- /dev/null
+++ b/commons/security/src/main/java/datawave/security/cert/SSLStores.java
@@ -0,0 +1,49 @@
+package datawave.security.cert;
+
+import java.security.KeyStore;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.TrustManager;
+
+/**
+ * Represents a key store/trust store pair.
+ */
+public interface SSLStores {
+
+ /**
+ * Return the key store
+ *
+ * @return the keystore
+ */
+ default KeyStore getKeyStore() {
+ return null;
+ }
+
+ /**
+ * Return the key managers
+ *
+ * @return the key managers
+ */
+ default KeyManager[] getKeyManagers() {
+ return new KeyManager[0];
+ }
+
+ /**
+ * Return the trust store
+ *
+ * @return the truststore
+ */
+ default KeyStore getTrustStore() {
+ return null;
+ }
+
+ /**
+ * Return the trust managers
+ *
+ * @return the trust managers
+ */
+ default TrustManager[] getTrustManagers() {
+ return new TrustManager[0];
+ }
+
+}
diff --git a/commons/security/src/main/java/datawave/security/cert/SSLStoresImpl.java b/commons/security/src/main/java/datawave/security/cert/SSLStoresImpl.java
new file mode 100644
index 00000000000..0a6d0c54abf
--- /dev/null
+++ b/commons/security/src/main/java/datawave/security/cert/SSLStoresImpl.java
@@ -0,0 +1,235 @@
+package datawave.security.cert;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Represents a key store/trust store pair.
+ */
+public class SSLStoresImpl implements SSLStores {
+
+ private final KeyStore keyStore;
+ private final KeyManager[] keyManagers;
+
+ private final KeyStore trustStore;
+ private final TrustManager[] trustManagers;
+
+ /**
+ * Return a new builder.
+ *
+ * @return the builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public SSLStoresImpl(KeyStore keyStore, KeyManager[] keyManagers, KeyStore trustStore, TrustManager[] trustManagers) {
+ this.keyStore = keyStore;
+ this.keyManagers = keyManagers;
+ this.trustStore = trustStore;
+ this.trustManagers = trustManagers;
+ }
+
+ @Override
+ public KeyStore getKeyStore() {
+ return this.keyStore;
+ }
+
+ @Override
+ public KeyManager[] getKeyManagers() {
+ return this.keyManagers;
+ }
+
+ @Override
+ public KeyStore getTrustStore() {
+ return this.trustStore;
+ }
+
+ @Override
+ public TrustManager[] getTrustManagers() {
+ return this.trustManagers;
+ }
+
+ public static class Builder {
+ private String keyStoreUrl;
+ private String keyStoreType;
+ private String keyStorePassword;
+ private String trustStoreUrl;
+ private String trustStoreType;
+ private String trustStorePassword;
+
+ /**
+ * Set the information required to load the keystore.
+ *
+ * @param url
+ * the keystore URL, this may be a URL, file path, or classloader resource
+ * @param password
+ * the keystore password
+ * @param type
+ * the keystore type
+ * @return this builder
+ */
+ public Builder withKeystore(String url, String password, String type) {
+ Preconditions.checkNotNull(url, "keystore URL cannot be null");
+ Preconditions.checkNotNull(password, "keystore password cannot be null");
+ Preconditions.checkNotNull(type, "keystore type cannot be null");
+ this.keyStoreUrl = url;
+ this.keyStoreType = type;
+ this.keyStorePassword = password;
+ return this;
+ }
+
+ /**
+ * Set the information required to load the trust store.
+ *
+ * @param url
+ * the trust store URL, this may be a URL, file path, or classpath resource
+ * @param password
+ * the trust store password
+ * @param type
+ * the trust store type
+ * @return this builder
+ */
+ public Builder withTruststore(String url, String password, String type) {
+ Preconditions.checkNotNull(url, "truststore URL cannot be null");
+ Preconditions.checkNotNull(password, "truststore password cannot be null");
+ Preconditions.checkNotNull(type, "truststore type cannot be null");
+ this.trustStoreUrl = url;
+ this.trustStoreType = type;
+ this.trustStorePassword = password;
+ return this;
+ }
+
+ /**
+ * Build and return an {@link SSLStoresImpl} instance.
+ *
+ * @return the new instance
+ * @throws Exception
+ * if an error occurs while loading the keystore and truststore
+ */
+ public SSLStoresImpl build() throws Exception {
+ // Load the keystore.
+ char[] keyStorePassword = this.keyStorePassword.toCharArray();
+ KeyStore keyStore = getKeyStore(keyStoreUrl, keyStorePassword, keyStoreType);
+ KeyManager[] keyManagers = getKeyManagers(keyStore, keyStorePassword);
+
+ KeyStore trustStore;
+ // If no trust store URL was set, use the keystore as the trust store.
+ if (trustStoreUrl != null) {
+ trustStore = getKeyStore(trustStoreUrl, trustStorePassword.toCharArray(), trustStoreType);
+ } else {
+ trustStore = keyStore;
+ }
+ TrustManager[] trustManagers = getTrustManagers(trustStore);
+
+ return new SSLStoresImpl(keyStore, keyManagers, trustStore, trustManagers);
+ }
+
+ private static final String PKCS11 = "PKCS11";
+ private static final String PKCS11IMPLKS = "PKCS11IMPLKS";
+
+ /**
+ * Load and return a {@link KeyStore}.
+ *
+ * @param url
+ * the keystore URL
+ * @param password
+ * the keystore password
+ * @param type
+ * the keystore type
+ * @return the keystore
+ */
+ private KeyStore getKeyStore(final String url, final char[] password, String type)
+ throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
+ // If no type was specified, use JKS by default.
+ KeyStore keyStore = KeyStore.getInstance(type);
+ // If the type is not PKCS11, load the keystore from the URL stream.
+ if (!PKCS11.equalsIgnoreCase(type) && !PKCS11IMPLKS.equalsIgnoreCase(type)) {
+ URL keyStoreUrl = validateKeystoreUrl(url);
+ try (InputStream is = keyStoreUrl.openStream()) {
+ keyStore.load(is, password);
+ }
+ }
+
+ return keyStore;
+ }
+
+ /**
+ * Return the key managers from the given keystore.
+ *
+ * @param keyStore
+ * the keystore
+ * @param password
+ * the keystore password
+ * @return the key managers
+ */
+ private KeyManager[] getKeyManagers(KeyStore keyStore, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException {
+ String algorithm = KeyManagerFactory.getDefaultAlgorithm();
+ KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(algorithm);
+ keyManagerFactory.init(keyStore, password);
+ return keyManagerFactory.getKeyManagers();
+ }
+
+ /**
+ * Return the trust managers from the given truststore.
+ *
+ * @param trustStore
+ * the truststore
+ * @return the trust managers
+ */
+ private TrustManager[] getTrustManagers(KeyStore trustStore) throws NoSuchAlgorithmException, KeyStoreException {
+ String algorithm = TrustManagerFactory.getDefaultAlgorithm();
+ TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);
+ trustManagerFactory.init(trustStore);
+ return trustManagerFactory.getTrustManagers();
+ }
+
+ /**
+ * Validate the given URL string and verify that it is a valid URL, file path, or classpath resource.
+ *
+ * @param urlStr
+ * the URL string
+ * @return the URL
+ */
+ private URL validateKeystoreUrl(String urlStr) throws MalformedURLException {
+ // First, try to parse it as a URL.
+ try {
+ return new URL(urlStr);
+ } catch (MalformedURLException e) {
+ // Either not a URL or protocol without a handler.
+ }
+
+ // Next, try to locate this as a file path.
+ File file = new File(urlStr);
+ if (file.exists()) {
+ return file.toURI().toURL();
+ }
+
+ // Next, try to locate this as a classpath resource.
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ if (classLoader != null) {
+ URL url = classLoader.getResource(urlStr);
+ if (url != null) {
+ return url;
+ }
+ }
+
+ throw new MalformedURLException("Failed to validate " + urlStr + " as a URL, file, or classpath resource.");
+ }
+ }
+}
diff --git a/commons/security/src/main/java/datawave/security/cert/X509CertificateVerifier.java b/commons/security/src/main/java/datawave/security/cert/X509CertificateVerifier.java
new file mode 100644
index 00000000000..bf6f495778b
--- /dev/null
+++ b/commons/security/src/main/java/datawave/security/cert/X509CertificateVerifier.java
@@ -0,0 +1,25 @@
+package datawave.security.cert;
+
+import java.security.KeyStore;
+import java.security.cert.X509Certificate;
+
+/**
+ * A base X509 certificate verifier.
+ */
+public interface X509CertificateVerifier {
+
+ /**
+ * Validate a cert.
+ *
+ * @param cert
+ * the X509Certificate to verify
+ * @param alias
+ * the expected keystore alias
+ * @param keyStore
+ * the keystore for the cert
+ * @param trustStore
+ * the truststore for the cert
+ * @return true if the cert is valid, false otherwise
+ */
+ boolean verify(X509Certificate cert, String alias, KeyStore keyStore, KeyStore trustStore);
+}
diff --git a/commons/security/src/main/java/datawave/security/util/DnProperties.java b/commons/security/src/main/java/datawave/security/util/DnProperties.java
new file mode 100644
index 00000000000..5ed179d3b30
--- /dev/null
+++ b/commons/security/src/main/java/datawave/security/util/DnProperties.java
@@ -0,0 +1,164 @@
+package datawave.security.util;
+
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringJoiner;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class contains properties that are often required when parsing/manipulating DNs.
+ */
+public class DnProperties {
+
+ private static final Logger log = LoggerFactory.getLogger(DnProperties.class);
+
+ /** Config for specifying default subject DN patterns and NPE OUs */
+ public static final String DEFAULT_PROPERTIES_FILE = "dnutils.properties";
+
+ /** System property that contains a regex pattern that matches against subject DNs. */
+ public static final String SUBJECT_DN_PATTERN_PROPERTY = "subject.dn.pattern";
+
+ /** System property containing a comma-delimited list of NPE OUs. */
+ public static final String NPE_OU_PROPERTY = "npe.ou.entries";
+
+ /**
+ * The default instance loaded from system properties and/or a properties file.
+ */
+ private static DnProperties defaultInstance;
+
+ private final Pattern subjectDnPattern;
+ private final Set npeOUs;
+
+ /**
+ * Return the default instance of {@link DnProperties}. If it does not exist, it will be set to the result of {@link #createInstanceFromProperties(String)}
+ * with the properties file {@value DEFAULT_PROPERTIES_FILE}.
+ *
+ * @return the default {@link DnProperties}
+ * @see #createInstanceFromProperties(String)
+ */
+ public static DnProperties getDefaultInstance() {
+ if (defaultInstance == null) {
+ defaultInstance = createInstanceFromProperties(DEFAULT_PROPERTIES_FILE);
+ }
+ return defaultInstance;
+ }
+
+ /**
+ * Create an instance of {@link DnProperties} where the subject DN pattern and NPE OU list are loaded (in priority order) from the system properties
+ * {@value SUBJECT_DN_PATTERN_PROPERTY} and {@value NPE_OU_PROPERTY}, or those same properties in the given properties file from the classloader if they
+ * were not specified in the system properties.
+ *
+ * @return a new {@link DnProperties}
+ */
+ public static DnProperties createInstanceFromProperties(String propertiesFile) {
+ // Attempt to fetch a subject DN pattern and NPE OU list from system properties.
+ String subjectDnPatternValue = System.getProperty(SUBJECT_DN_PATTERN_PROPERTY, "");
+ String npeOUsValue = System.getProperty(NPE_OU_PROPERTY, "");
+
+ // If either a subject DN pattern or NPE OU list were not specified, attempt to load them from the properties file.
+ if ((subjectDnPatternValue.isBlank()) || (npeOUsValue.isBlank())) {
+ // Attempt to load the default subject DN pattern and NPE OU list from the properties file available via the classloader.
+ Properties props = new Properties();
+ try (InputStream in = DnProperties.class.getClassLoader().getResourceAsStream(propertiesFile)) {
+ props.load(in);
+
+ // Update the subject DN if needed.
+ if (subjectDnPatternValue.isBlank()) {
+ subjectDnPatternValue = props.getProperty(SUBJECT_DN_PATTERN_PROPERTY, "");
+ if (subjectDnPatternValue.isBlank()) {
+ log.warn("Subject DN pattern property {} not set in {}", SUBJECT_DN_PATTERN_PROPERTY, propertiesFile);
+ }
+ }
+
+ // Update the NPE OU list if needed.
+ if (npeOUsValue.isBlank()) {
+ npeOUsValue = props.getProperty(NPE_OU_PROPERTY, "");
+ if (npeOUsValue.isBlank()) {
+ log.warn("NPE OUs list property {} not set in {}", NPE_OU_PROPERTY, propertiesFile);
+ }
+ }
+
+ } catch (Exception e) {
+ // If we failed to load the properties file, throw an error.
+ log.error("Failed to load properties file {}", propertiesFile, e);
+ throw new RuntimeException("Failed to load properties file " + propertiesFile, e);
+ }
+
+ // Throw an error if the subject DN is still blank.
+ if (subjectDnPatternValue.isBlank()) {
+ throw new IllegalArgumentException("Failed to load valid subject DN pattern from property " + SUBJECT_DN_PATTERN_PROPERTY
+ + " from system or properties file " + propertiesFile);
+ }
+
+ // Throw an error if the NPE OU list is still blank.
+ if (npeOUsValue.isBlank()) {
+ throw new IllegalArgumentException(
+ "Failed to load valid NPE OU list from property " + NPE_OU_PROPERTY + " from system or properties file " + propertiesFile);
+ }
+ }
+
+ // Compile the subject DN pattern.
+ Pattern subjectDnPattern;
+ try {
+ subjectDnPattern = Pattern.compile(subjectDnPatternValue, Pattern.CASE_INSENSITIVE);
+ } catch (Throwable t) {
+ log.error("{} = '{}' could not be compiled", SUBJECT_DN_PATTERN_PROPERTY, subjectDnPatternValue, t);
+ throw new RuntimeException("Unable to compile subject DN pattern '" + subjectDnPatternValue + "'", t);
+ }
+
+ // Parse the NPE OU list.
+ // @formatter:off
+ List npeOUs = Arrays.stream(npeOUsValue.split(","))
+ .map(String::trim)
+ .map(String::toUpperCase)
+ .collect(Collectors.toList());
+ // @formatter:on
+
+ return new DnProperties(subjectDnPattern, npeOUs);
+ }
+
+ /**
+ * Create a new {@link DnProperties} from the given subject DN pattern and NPE OUs.
+ *
+ * @param subjectDnPattern
+ * the subject DN patterns
+ * @param npeOUs
+ * the NPE OUs
+ */
+ public DnProperties(Pattern subjectDnPattern, Collection npeOUs) {
+ this.subjectDnPattern = subjectDnPattern;
+ this.npeOUs = npeOUs.stream().map(String::trim).map(String::toUpperCase).collect(Collectors.toUnmodifiableSet());
+ }
+
+ /**
+ * Return the subject DN pattern
+ *
+ * @return the subject DN pattern
+ */
+ public Pattern getSubjectDnPattern() {
+ return subjectDnPattern;
+ }
+
+ /**
+ * Return the set of NPE OUs, in all uppercase.
+ *
+ * @return the NPE OUs
+ */
+ public Set getNpeOUs() {
+ return npeOUs;
+ }
+
+ @Override
+ public String toString() {
+ return new StringJoiner(", ", DnProperties.class.getSimpleName() + "[", "]").add("subjectDnPattern=" + subjectDnPattern).add("npeOUs=" + npeOUs)
+ .toString();
+ }
+}
diff --git a/microservices/services/authorization/api/src/main/java/datawave/security/util/ProxiedEntityUtils.java b/commons/security/src/main/java/datawave/security/util/DnUtils.java
similarity index 56%
rename from microservices/services/authorization/api/src/main/java/datawave/security/util/ProxiedEntityUtils.java
rename to commons/security/src/main/java/datawave/security/util/DnUtils.java
index 630bb865c38..65a26863676 100644
--- a/microservices/services/authorization/api/src/main/java/datawave/security/util/ProxiedEntityUtils.java
+++ b/commons/security/src/main/java/datawave/security/util/DnUtils.java
@@ -2,8 +2,10 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -16,16 +18,23 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import datawave.security.authorization.SubjectIssuerDNPair;
+
/**
- * Provides utility functions for splitting and extracting components from DNs.
+ * Utilities for parsing and manipulating user/server DNs.
*/
-public class ProxiedEntityUtils {
+public final class DnUtils {
- private static final Logger log = LoggerFactory.getLogger(ProxiedEntityUtils.class);
+ private static final Logger log = LoggerFactory.getLogger(DnUtils.class);
+
+ /**
+ * Regex patterns to match against a comma followed by a space.
+ */
+ private static final Pattern COMMA_SPACE_PATTERN = Pattern.compile(",[^ ]");
/**
* Split the given string into proxied DNs and return them. It is expected that the DN string matches the format returned by
- * {@link ProxiedEntityUtils#buildProxiedDN(String...)}, i,e, that the DNs should be in the format {@code "dn1..."}.
+ * {@link DnUtils#buildProxiedDN(String...)}, i,e, that the DNs should be in the format {@code "dn1..."}.
*
* @param proxiedDNs
* the proxied DNs
@@ -56,8 +65,8 @@ public static String[] splitProxiedDNs(String proxiedDNs, boolean allowDuplicate
/**
* Split the given string into proxied subject and issuer pairs and return them. It is expected that the DN string matches the format returned by
- * {@link ProxiedEntityUtils#buildProxiedDN(String...)}, i,e, that the DNs should be in the format {@code "dn1..."}. If an uneven number of
- * DNs greater than 1 is found, an {@link IllegalArgumentException} will be thrown. Only the first pairing for any distinct subject DNs will be retained.
+ * {@link DnUtils#buildProxiedDN(String...)}, i,e, that the DNs should be in the format {@code "dn1..."}. If an uneven number of DNs greater
+ * than 1 is found, an {@link IllegalArgumentException} will be thrown. Only the first pairing for any distinct subject DNs will be retained.
*
* @param proxiedDNs
* the proxied DNs
@@ -198,11 +207,6 @@ public static String[] getComponents(String dn, String rdnName) {
return components.toArray(new String[0]);
}
- /**
- * Regex patterns to match against a comma followed by a space.
- */
- private static final Pattern COMMA_SPACE_PATTERN = Pattern.compile(",[^ ]");
-
/**
* Attempts to normalize a DN by taking it and reversing the components if it doesn't start with CN. Some systems require the DN components be in a specific
* order, or that order reversed. We cannot arbitrarily reorder the components, however, e.g., such as by sorting them. The username returned will fall into
@@ -238,10 +242,171 @@ public static String normalizeDN(String userName) {
return normalizedUserName;
}
+ /**
+ * Build and return a collection of normalized DNs built from the given DNs. The subject DN pattern will be used to verify that no proxied subject DNs were
+ * provided as issuer DNs. The subject DN and issuer DN will always be the last DNs in the list.
+ *
+ * @param subjectDN
+ * the subject DN
+ * @param issuerDN
+ * the issuer DN
+ * @param proxiedSubjectDNs
+ * the proxied subject DNs
+ * @param proxiedIssuerDNs
+ * the proxied issuer DNs
+ * @param subjectDnPattern
+ * the subject DN pattern
+ * @return a collection of normalized DNs
+ */
+ public static List buildNormalizedDNList(String subjectDN, String issuerDN, String proxiedSubjectDNs, String proxiedIssuerDNs,
+ Pattern subjectDnPattern) {
+ List dnList = new ArrayList<>();
+ if (proxiedSubjectDNs != null) {
+ if (proxiedIssuerDNs == null) {
+ throw new IllegalArgumentException("If proxied subject DNs are supplied, then issuer DNs must be supplied as well.");
+ }
+
+ // Parse the subject and issuer DNs, verifying we have valid pairs.
+ String[] subjectDns = splitProxiedDNs(proxiedSubjectDNs, true);
+ String[] issuerDns = splitProxiedDNs(proxiedIssuerDNs, true);
+ if (subjectDns.length != issuerDns.length)
+ throw new IllegalArgumentException("Subject and issuer DN lists do not have the same number of entries: " + Arrays.toString(subjectDns) + " vs "
+ + Arrays.toString(issuerDns));
+
+ // Normalize each subject/issuer DN, and add them to the final DN list.
+ for (int i = 0; i < subjectDns.length; ++i) {
+ subjectDns[i] = normalizeDN(subjectDns[i]);
+ issuerDns[i] = normalizeDN(issuerDns[i]);
+ // Verify the subject and issuer DN are not the same.
+ if (issuerDns[i].equalsIgnoreCase(subjectDns[i])) {
+ throw new IllegalArgumentException("Subject DN " + issuerDns[i] + " was passed as an issuer DN.");
+ }
+ // Verify the issuer DN is not a subject DN.
+ if (subjectDnPattern.matcher(issuerDns[i]).find()) {
+ throw new IllegalArgumentException("It appears that a subject DN (" + issuerDns[i] + ") was passed as an issuer DN.");
+ }
+
+ dnList.add(subjectDns[i]);
+ dnList.add(issuerDns[i]);
+ }
+ }
+
+ // Normalize the non-proxied subject and issuer DN, and add them to the list.
+ subjectDN = normalizeDN(subjectDN);
+ issuerDN = normalizeDN(issuerDN);
+ dnList.add(subjectDN.replaceAll("(?])", "\\\\$1"));
+ dnList.add(issuerDN.replaceAll("(?])", "\\\\$1"));
+ return dnList;
+ }
+
+ /**
+ * Build and return the result of {@link #buildNormalizedDNList(String, String, String, String, Pattern)}, where the string consist of each DN, and every DN
+ * after the first one is wrapped with {@code <} and {@code >}.
+ *
+ * @param subjectDN
+ * the subject DN
+ * @param issuerDN
+ * the issuer DN
+ * @param proxiedSubjectDNs
+ * the proxied subject DNs
+ * @param proxiedIssuerDNs
+ * the proxied issuer DNs
+ * @param subjectDnPattern
+ * the subject DN pattern
+ * @return the constructed string
+ * @see #buildNormalizedDNList(String, String, String, String, Pattern)
+ */
+ public static String buildNormalizedProxyDN(String subjectDN, String issuerDN, String proxiedSubjectDNs, String proxiedIssuerDNs,
+ Pattern subjectDnPattern) {
+ StringBuilder sb = new StringBuilder();
+ for (String escapedDN : buildNormalizedDNList(subjectDN, issuerDN, proxiedSubjectDNs, proxiedIssuerDNs, subjectDnPattern)) {
+ if (sb.length() == 0)
+ sb.append(escapedDN);
+ else
+ sb.append('<').append(escapedDN).append('>');
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Build and return a string consisting of normalized subject and issuer DNs from the given list, where the string consist of each DN, and every DN after
+ * the first one is wrapped with {@code <} and {@code >}.
+ *
+ * @param dns
+ * the DNs to normalize
+ * @return the constructed string
+ * @see #normalizeDN(String)
+ */
+ public static String buildNormalizedProxyDN(List dns) {
+ StringBuilder sb = new StringBuilder();
+ dns.forEach(dn -> {
+ if (sb.length() == 0) {
+ sb.append(normalizeDN(dn.subjectDN()));
+ } else {
+ sb.append('<').append(normalizeDN(dn.subjectDN())).append('>');
+ }
+ sb.append('<').append(normalizeDN(dn.issuerDN())).append('>');
+ });
+ return sb.toString();
+ }
+
+ /**
+ * Return whether the given DN is considered a server DN. The DN will be considered a server DN if the OU of the DN is found in the given list of NPE OUs.
+ *
+ * @param dn
+ * the DN
+ * @param npeOUs
+ * the list of NPE OUs, expected to be all uppercase
+ * @return true if the DN is a server DN, or false otherwise
+ */
+ public static boolean isServerDN(String dn, Collection npeOUs) {
+ String[] ouList = DnUtils.getOrganizationalUnits(dn);
+ return Arrays.stream(ouList).anyMatch((ou) -> npeOUs.contains(ou.toUpperCase()));
+ }
+
+ /**
+ * Return the first DN that represents a user. A DN will be considered a user DN if it does not have an OU that is found in the given list of NPE OUs.
+ *
+ * @param dns
+ * the DNs, expected to be all subject DNs
+ * @param npeOUs
+ * the list of NPE OUs, expected to be all uppercase
+ * @return the first user DN, or null if no user DN was found
+ */
+ public static String getUserDN(String[] dns, Collection npeOUs) {
+ return getUserDN(dns, false, npeOUs);
+ }
+
+ /**
+ * Return the first subject DN that represents a user. A DN will be considered a user DN if it does not have an OU that is found in the given list of NPE
+ * OUs.
+ *
+ * @param dns
+ * the DNs
+ * @param containsIssuerDns
+ * whether the argument {@code dns} consists of subject/issuer pairs, with issuer DNs present in odd indexes
+ * @param npeOUs
+ * the list of NPE OUs, expected to be all uppercase
+ * @return the first user subject DN, or null if no user DN was found
+ */
+ public static String getUserDN(String[] dns, boolean containsIssuerDns, Collection npeOUs) {
+ // If the array list contains subject/issuer pairs, verify it has an even length.
+ if (containsIssuerDns && (dns.length % 2) != 0)
+ throw new IllegalArgumentException("DNs array is not a subject/issuer DN list: " + Arrays.toString(dns));
+
+ // Find the first subject DN that is not a server DN.
+ for (int i = 0; i < dns.length; i += (containsIssuerDns) ? 2 : 1) {
+ String dn = dns[i];
+ if (!isServerDN(dn, npeOUs))
+ return dn;
+ }
+ return null;
+ }
+
/**
* Do not allow this class to be instantiated.
*/
- private ProxiedEntityUtils() {
+ private DnUtils() {
throw new UnsupportedOperationException();
}
}
diff --git a/commons/security/src/main/java/datawave/security/util/SecurityConstants.java b/commons/security/src/main/java/datawave/security/util/SecurityConstants.java
new file mode 100644
index 00000000000..f10245ec7a4
--- /dev/null
+++ b/commons/security/src/main/java/datawave/security/util/SecurityConstants.java
@@ -0,0 +1,66 @@
+package datawave.security.util;
+
+/**
+ * Contains commonly used security-related constants.
+ */
+public final class SecurityConstants {
+
+ /**
+ * An internal header used to store the start time of the HTTP request, as retrieved from the web container (e.g., Undertow).
+ */
+ public static final String REQUEST_START_TIME_HEADER = "X-Internal-RequestStartTimeNanos";
+
+ /**
+ * An internal header used to store the time required to authenticate the user for the current request.
+ */
+ public static final String REQUEST_LOGIN_TIME_HEADER = "X-Internal-RequestLoginTimeMillis";
+
+ /**
+ * The header used to store proxied subjects.
+ */
+ public static final String PROXIED_ENTITIES_HEADER = "X-ProxiedEntitiesChain";
+
+ /**
+ * The header used to store proxied issuers.
+ */
+ public static final String PROXIED_ISSUERS_HEADER = "X-ProxiedIssuersChain";
+
+ /**
+ * The default header used to store the subject DN when using trusted header authentication.
+ */
+ public static final String DEFAULT_TRUSTED_SUBJECT_DN_HEADER = "X-SSL-ClientCert-Subject";
+
+ /**
+ * The default header used to store the issuer DN when using trusted header authentication.
+ */
+ public static final String DEFAULT_TRUSTED_ISSUER_DN_HEADER = "X-SSL-ClientCert-Issuer";
+
+ /**
+ * The system property used to store whether JWT authentication is enabled.
+ */
+ public static final String JWT_HEADER_AUTHENTICATION_SYSTEM_PROPERTY = "dw.jwt.header.authentication";
+
+ /**
+ * The system property used to store whether trusted header authentication is enabled.
+ */
+ public static final String TRUSTED_HEADER_AUTHENTICATION_SYSTEM_PROPERTY = "dw.trusted.header.authentication";
+
+ /**
+ * The system property used to store the header that will be used to store the subject DN when using trusted header authentication.
+ */
+ public static final String TRUSTED_SUBJECT_DN_HEADER_SYSTEM_PROPERTY = "dw.trusted.header.subjectDn";
+
+ /**
+ * The system property used to store the header that will be used to store the issuer DN when using trusted header authentication.
+ */
+ public static final String TRUSTED_ISSUER_DN_HEADER_SYSTEM_PROPERTY = "dw.trusted.header.issuerDn";
+
+ /**
+ * The system property used to store trusted proxied entities.
+ */
+ public static final String TRUSTED_PROXIED_ENTITIES_SYSTEM_PROPERTY = "dw.trusted.proxied.entities";
+
+ private SecurityConstants() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/microservices/services/authorization/api/src/test/java/datawave/security/authorization/SubjectIssuerDNPairTest.java b/commons/security/src/test/java/datawave/security/authorization/SubjectIssuerDnPairTest.java
similarity index 100%
rename from microservices/services/authorization/api/src/test/java/datawave/security/authorization/SubjectIssuerDNPairTest.java
rename to commons/security/src/test/java/datawave/security/authorization/SubjectIssuerDnPairTest.java
diff --git a/commons/security/src/test/java/datawave/security/cert/SSLStoresImplTest.java b/commons/security/src/test/java/datawave/security/cert/SSLStoresImplTest.java
new file mode 100644
index 00000000000..746df7d56ab
--- /dev/null
+++ b/commons/security/src/test/java/datawave/security/cert/SSLStoresImplTest.java
@@ -0,0 +1,74 @@
+package datawave.security.cert;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+import java.net.URL;
+
+import org.junit.jupiter.api.Test;
+
+class SSLStoresImplTest {
+
+ private static final String JKS = "JKS";
+ private static final String PKCS12 = "PKCS12";
+
+ private static final String jksKeyStorePath = "/datawave-server-keystore.jks";
+ private static final String jksTrustStorePath = "/datawave-server-truststore.jks";
+ private static final String pkcs12KeyStorePath = "/datawave-server-keystore.p12";
+ private static final String pkcs12TrustStorePath = "/datawave-server-truststore.p12";
+
+ private static final String password = "ChangeIt";
+
+ @Test
+ void testJKSKeyStore() throws Exception {
+ SSLStoresImpl sslContext = SSLStoresImpl.builder().withKeystore(getResource(jksKeyStorePath), password, JKS).build();
+
+ assertNotNull(sslContext.getKeyStore(), "Keystore is null");
+ assertNotNull(sslContext.getTrustStore(), "Truststore is null");
+ assertSame(sslContext.getKeyStore(), sslContext.getTrustStore(), "Keystore and truststore are not the same instance");
+ }
+
+ @Test
+ void testJKSKeyStoreWithPKCS12TrustStore() throws Exception {
+ SSLStoresImpl sslContext = SSLStoresImpl.builder().withKeystore(getResource(jksKeyStorePath), password, JKS)
+ .withTruststore(getResource(pkcs12TrustStorePath), password, PKCS12).build();
+
+ assertNotNull(sslContext.getKeyStore(), "Keystore is null");
+ assertEquals(JKS, sslContext.getKeyStore().getType(), "Keystore is incorrect type");
+ assertNotNull(sslContext.getTrustStore(), "Truststore is null");
+ assertEquals(PKCS12, sslContext.getTrustStore().getType(), "Truststore is incorrect type");
+ assertNotSame(sslContext.getKeyStore(), sslContext.getTrustStore(), "Keystore and truststore are the same instance");
+ }
+
+ @Test
+ void testPKCS12KeyStore() throws Exception {
+ SSLStoresImpl sslContext = SSLStoresImpl.builder().withKeystore(getResource(pkcs12KeyStorePath), password, PKCS12).build();
+
+ assertNotNull(sslContext.getKeyStore(), "Keystore is null");
+ assertNotNull(sslContext.getTrustStore(), "Truststore is null");
+ assertSame(sslContext.getKeyStore(), sslContext.getTrustStore(), "Keystore and truststore are not the same instance");
+ }
+
+ @Test
+ void testPKCS12KeyStoreWithJKSTrustStore() throws Exception {
+ SSLStoresImpl sslContext = SSLStoresImpl.builder().withKeystore(getResource(pkcs12KeyStorePath), password, PKCS12)
+ .withTruststore(getResource(jksTrustStorePath), password, JKS).build();
+
+ assertNotNull(sslContext.getKeyStore(), "Keystore is null");
+ assertEquals(PKCS12, sslContext.getKeyStore().getType(), "Keystore is incorrect type");
+ assertNotNull(sslContext.getTrustStore(), "Truststore is null");
+ assertEquals(JKS, sslContext.getTrustStore().getType(), "Truststore is incorrect type");
+ assertNotSame(sslContext.getKeyStore(), sslContext.getTrustStore(), "Keystore and truststore are the same instance");
+ }
+
+ private String getResource(String resource) {
+ URL url = getClass().getResource(resource);
+ if (url != null) {
+ return url.toExternalForm();
+ } else {
+ throw new NullPointerException("Could not load resource " + resource);
+ }
+ }
+}
diff --git a/commons/security/src/test/java/datawave/security/util/DnPropertiesTest.java b/commons/security/src/test/java/datawave/security/util/DnPropertiesTest.java
new file mode 100644
index 00000000000..960ccb84d8e
--- /dev/null
+++ b/commons/security/src/test/java/datawave/security/util/DnPropertiesTest.java
@@ -0,0 +1,162 @@
+package datawave.security.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.ClearSystemProperty;
+import org.junitpioneer.jupiter.SetSystemProperty;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Tests for {@link DnProperties}.
+ */
+class DnPropertiesTest {
+
+ /**
+ * Verify that {@link DnProperties#getDefaultInstance()} will load properties from the properties file {@value DnProperties#DEFAULT_PROPERTIES_FILE}.
+ */
+ @ClearSystemProperty(key = DnProperties.SUBJECT_DN_PATTERN_PROPERTY)
+ @ClearSystemProperty(key = DnProperties.NPE_OU_PROPERTY)
+ @Test
+ void testGetDefaultInstance() {
+ Pattern expectedSubjectDnPattern = Pattern.compile("(?:^|,)\\s*OU\\s*=\\s*My Department\\s*(?:,|$)", Pattern.CASE_INSENSITIVE);
+ Set expectedNpeOUs = Sets.newHashSet("IAMNOTAPERSON", "NPE", "STILLNOTAPERSON");
+
+ DnProperties actual = DnProperties.getDefaultInstance();
+ assertTrue(arePatternsEqual(expectedSubjectDnPattern, actual.getSubjectDnPattern()));
+ assertEquals(expectedNpeOUs, actual.getNpeOUs());
+ }
+
+ /**
+ * Verify that {@link DnProperties#createInstanceFromProperties(String)} is able to load properties from a properties file when no system properties are
+ * configured.
+ */
+ @ClearSystemProperty(key = DnProperties.SUBJECT_DN_PATTERN_PROPERTY)
+ @ClearSystemProperty(key = DnProperties.NPE_OU_PROPERTY)
+ @Test
+ void testCreatingInstanceFromPropertiesFile() {
+ Pattern expectedSubjectDnPattern = Pattern.compile("(?:^|,)\\s*OU\\s*=\\s*My Department\\s*(?:,|$)", Pattern.CASE_INSENSITIVE);
+ Set expectedNpeOUs = Sets.newHashSet("IAMNOTAPERSON", "NPE", "STILLNOTAPERSON");
+
+ DnProperties actual = DnProperties.createInstanceFromProperties("dnutils.properties");
+ assertTrue(arePatternsEqual(expectedSubjectDnPattern, actual.getSubjectDnPattern()));
+ assertEquals(expectedNpeOUs, actual.getNpeOUs());
+ }
+
+ /**
+ * Verify that {@link DnProperties#createInstanceFromProperties(String)} is able to load properties from system properties when no properties file exists.
+ */
+ @SetSystemProperty(key = DnProperties.SUBJECT_DN_PATTERN_PROPERTY, value = "(?:^|,)\\s*OU\\s*=\\s*My Department\\s*(?:,|$)")
+ @SetSystemProperty(key = DnProperties.NPE_OU_PROPERTY, value = "iamnotaperson,npe,stillnotaperson")
+ @Test
+ void testCreatingInstanceFromSystemProperties() {
+ Pattern expectedSubjectDnPattern = Pattern.compile("(?:^|,)\\s*OU\\s*=\\s*My Department\\s*(?:,|$)", Pattern.CASE_INSENSITIVE);
+ Set expectedNpeOUs = Sets.newHashSet("IAMNOTAPERSON", "NPE", "STILLNOTAPERSON");
+
+ DnProperties actual = DnProperties.createInstanceFromProperties("nonexistent.properties");
+ assertTrue(arePatternsEqual(expectedSubjectDnPattern, actual.getSubjectDnPattern()));
+ assertEquals(expectedNpeOUs, actual.getNpeOUs());
+ }
+
+ /**
+ * Verify that {@link DnProperties#createInstanceFromProperties(String)} will prioritize system properties over the properties file.
+ */
+ @SetSystemProperty(key = DnProperties.SUBJECT_DN_PATTERN_PROPERTY, value = "(?:^|,)\\s*OU\\s*=\\s*My Other Department\\s*(?:,|$)")
+ @SetSystemProperty(key = DnProperties.NPE_OU_PROPERTY, value = "iamnotaperson")
+ @Test
+ void testSystemPropertiesOverridePropertiesFile() {
+ Pattern expectedSubjectDnPattern = Pattern.compile("(?:^|,)\\s*OU\\s*=\\s*My Other Department\\s*(?:,|$)", Pattern.CASE_INSENSITIVE);
+ Set expectedNpeOUs = Sets.newHashSet("IAMNOTAPERSON");
+
+ DnProperties actual = DnProperties.createInstanceFromProperties("dnutils.properties");
+ assertTrue(arePatternsEqual(expectedSubjectDnPattern, actual.getSubjectDnPattern()));
+ assertEquals(expectedNpeOUs, actual.getNpeOUs());
+ }
+
+ /**
+ * Verify an exception is thrown if the properties file cannot be loaded and either a subject DN pattern or NPE OU list were not specified via system
+ * properties.
+ */
+ @SetSystemProperty(key = DnProperties.SUBJECT_DN_PATTERN_PROPERTY, value = " ")
+ @SetSystemProperty(key = DnProperties.NPE_OU_PROPERTY, value = " ")
+ @Test
+ void testNonexistentPropertiesFileThrowsException() {
+ Throwable throwable = assertThrows(RuntimeException.class, () -> DnProperties.createInstanceFromProperties("nonexistent.properties"));
+ assertEquals("Failed to load properties file nonexistent.properties", throwable.getMessage());
+ }
+
+ /**
+ * Verify an exception is not thrown for a non-existent properties file if a subject DN pattern and NPE OU list were provided via system properties.
+ */
+ @SetSystemProperty(key = DnProperties.SUBJECT_DN_PATTERN_PROPERTY, value = "(?:^|,)\\s*OU\\s*=\\s*My Other Department\\s*(?:,|$)")
+ @SetSystemProperty(key = DnProperties.NPE_OU_PROPERTY, value = "iamnotaperson")
+ @Test
+ void testExceptionNotThrownForNonExistentPropertiesFileWhenSystemPropertiesSet() {
+ Pattern expectedSubjectDnPattern = Pattern.compile("(?:^|,)\\s*OU\\s*=\\s*My Other Department\\s*(?:,|$)", Pattern.CASE_INSENSITIVE);
+ Set expectedNpeOUs = Sets.newHashSet("IAMNOTAPERSON");
+
+ DnProperties actual = DnProperties.createInstanceFromProperties("dnutils.properties");
+ assertTrue(arePatternsEqual(expectedSubjectDnPattern, actual.getSubjectDnPattern()));
+ assertEquals(expectedNpeOUs, actual.getNpeOUs());
+ }
+
+ /**
+ * Verify an exception is thrown by {@link DnProperties#createInstanceFromProperties(String)} when no valid subject DN pattern can be loaded.
+ */
+ @SetSystemProperty(key = DnProperties.SUBJECT_DN_PATTERN_PROPERTY, value = " ")
+ @SetSystemProperty(key = DnProperties.NPE_OU_PROPERTY, value = "iamnotaperson,npe,stillnotaperson")
+ @Test
+ void testNoValidSubjectDnPattern() {
+ Throwable thrown = assertThrows(IllegalArgumentException.class, () -> DnProperties.createInstanceFromProperties("dnutils_blank.properties"));
+ assertEquals("Failed to load valid subject DN pattern from property subject.dn.pattern from system or properties file dnutils_blank.properties",
+ thrown.getMessage());
+ }
+
+ /**
+ * Verify an exception is thrown by {@link DnProperties#createInstanceFromProperties(String)} when no valid NPE OUs can be loaded.
+ */
+ @SetSystemProperty(key = DnProperties.SUBJECT_DN_PATTERN_PROPERTY, value = "(?:^|,)\\s*OU\\s*=\\s*My Department\\s*(?:,|$)")
+ @SetSystemProperty(key = DnProperties.NPE_OU_PROPERTY, value = " ")
+ @Test
+ void testNoValidNpeOUs() {
+ Throwable thrown = assertThrows(IllegalArgumentException.class, () -> DnProperties.createInstanceFromProperties("dnutils_blank.properties"));
+ assertEquals("Failed to load valid NPE OU list from property npe.ou.entries from system or properties file dnutils_blank.properties",
+ thrown.getMessage());
+ }
+
+ /**
+ * Verify an exception is thrown by {@link DnProperties#createInstanceFromProperties(String)} when the subject DN pattern cannot be compiled.
+ */
+ @SetSystemProperty(key = DnProperties.SUBJECT_DN_PATTERN_PROPERTY, value = "([sda")
+ @SetSystemProperty(key = DnProperties.NPE_OU_PROPERTY, value = "iamnotaperson,npe,stillnotaperson")
+ @Test
+ void givenUncompilableSubjectDnPattern() {
+ Throwable thrown = assertThrows(RuntimeException.class, () -> DnProperties.createInstanceFromProperties("nonexistent.properties"));
+ assertEquals("Unable to compile subject DN pattern '([sda'", thrown.getMessage());
+ assertInstanceOf(PatternSyntaxException.class, thrown.getCause());
+ }
+
+ /**
+ * Return whether the given patterns have the same pattern and flags.
+ *
+ * @param pattern1
+ * the first pattern
+ * @param pattern2
+ * the second pattern
+ * @return true if the patterns are equal, or false otherwise
+ */
+ private boolean arePatternsEqual(Pattern pattern1, Pattern pattern2) {
+ if (pattern1 == pattern2) {
+ return true;
+ }
+ return pattern1.pattern().equals(pattern2.pattern()) && pattern1.flags() == pattern2.flags();
+ }
+}
diff --git a/commons/security/src/test/java/datawave/security/util/DnUtilsTest.java b/commons/security/src/test/java/datawave/security/util/DnUtilsTest.java
new file mode 100644
index 00000000000..71490c794e9
--- /dev/null
+++ b/commons/security/src/test/java/datawave/security/util/DnUtilsTest.java
@@ -0,0 +1,370 @@
+package datawave.security.util;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.junit.jupiter.api.Test;
+
+import com.google.common.collect.Lists;
+
+import datawave.security.authorization.SubjectIssuerDNPair;
+
+class DnUtilsTest {
+
+ private static final Pattern subjectDnPattern = Pattern.compile("(?:^|,)\\s*OU\\s*=\\s*My Department\\s*(?:,|$)", Pattern.CASE_INSENSITIVE);
+ private static final List npeOUs = List.of("IAMNOTAPERSON", "NPE", "STILLNOTAPERSON");
+
+ /**
+ * Tests for {@link DnUtils#splitProxiedDNs(String, boolean)}.
+ */
+ @Test
+ void testSplitProxiedDNs() {
+ // Verify that a single DN results in an array with the DN.
+ assertArrayEquals(new String[] {"cn=john q. doe, c=us, o=my org, ou=my dept"},
+ DnUtils.splitProxiedDNs("cn=john q. doe, c=us, o=my org, ou=my dept", true));
+
+ // Verify that a single DN with escaped < and > characters results in an array with the DN.
+ assertArrayEquals(new String[] {"cn=john q. doe, c=\\, o=my org, ou=\\"},
+ DnUtils.splitProxiedDNs("cn=john q. doe, c=\\, o=my org, ou=\\", true));
+
+ // Verify that multiple DNs result in an array with the DNs.
+ assertArrayEquals(new String[] {"cn=john q. doe, c=\\, o=my org, ou=\\", "cn=server1, c=us, o=my org, ou=my dept"},
+ DnUtils.splitProxiedDNs("cn=john q. doe, c=\\, o=my org, ou=\\", true));
+
+ // Verify that duplicate DNs are retained when specified to allow duplicates.
+ // @formatter:off
+ assertArrayEquals(new String[] {"cn=john q. doe, c=\\, o=my org, ou=\\",
+ "cn=server1, c=us, o=my org, ou=my dept",
+ "cn=server1, c=us, o=my org, ou=my dept"},
+ DnUtils.splitProxiedDNs("cn=john q. doe, c=\\, o=my org, ou=\\", true));
+ // @formatter:on
+
+ // Verify that only the first instance of a duplicate DN is retained when duplicates are not allowed.
+ // Verify that duplicate DNs are retained when specified to allow duplicates.
+ // @formatter:off
+ assertArrayEquals(new String[] {"cn=john q. doe, c=\\, o=my org, ou=\\",
+ "cn=server1, c=us, o=my org, ou=my dept",
+ "cn=server2, c=us, o=my org, ou=my dept"},
+ DnUtils.splitProxiedDNs("cn=john q. doe, c=\\, o=my org, ou=\\", false));
+ // @formatter:on
+
+ // Verify that any blank DNs are pruned.
+ assertArrayEquals(new String[] {"cn=john q. doe, c=\\, o=my org, ou=\\", "cn=server1, c=us, o=my org, ou=my dept"},
+ DnUtils.splitProxiedDNs("cn=john q. doe, c=\\, o=my org, ou=\\< >", true));
+
+ }
+
+ /**
+ * Tests for {@link DnUtils#splitProxiedSubjectIssuerDNs(String)}.
+ */
+ @Test
+ void testSplitProxiedSubjectIssuerDNs() {
+ // Verify that a single DN results in an array with the DN.
+ assertArrayEquals(new String[] {"cn=john q. doe, c=us, o=my org, ou=my dept"},
+ DnUtils.splitProxiedSubjectIssuerDNs("cn=john q. doe, c=us, o=my org, ou=my dept"));
+
+ // Verify that a single DN with escaped < and > characters results in an array with the DN.
+ assertArrayEquals(new String[] {"cn=john q. doe, c=us, o=my org, ou=\\"},
+ DnUtils.splitProxiedSubjectIssuerDNs("cn=john q. doe, c=us, o=my org, ou=\\"));
+
+ // Verify that an uneven number of DNs greater than one results in an exception.
+ IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
+ () -> DnUtils.splitProxiedSubjectIssuerDNs("cn=subject1"));
+ assertEquals("Invalid proxied DNs list does not have entries in pairs.", exception.getMessage());
+
+ // Verify that only the first subject-issuer pair for any unique subject DN is retained.
+ assertArrayEquals(new String[] {"cn=subject1", "cn=issuer1"}, DnUtils.splitProxiedSubjectIssuerDNs("cn=subject1"));
+
+ // Verify that multiple subject-issuer pairs are parsed correctly.
+ assertArrayEquals(new String[] {"cn=subject1", "cn=issuer1", "cn=subject2", "cn=issuer2"},
+ DnUtils.splitProxiedSubjectIssuerDNs("cn=subject1"));
+
+ // Verify that any blank DNs are pruned.
+ assertArrayEquals(new String[] {"cn=subject1", "cn=issuer1", "cn=subject2", "cn=issuer2"},
+ DnUtils.splitProxiedSubjectIssuerDNs("cn=subject1< >< >"));
+
+ }
+
+ /**
+ * Tests for {@link DnUtils#buildProxiedDN(String...)}.
+ */
+ @Test
+ void testBuildProxiedDN() {
+ // Verify that a blank string results in the original string.
+ assertEquals(" ", DnUtils.buildProxiedDN(" "));
+
+ // Verify that a single DN with no arrows results in the original DN.
+ assertEquals("cn=john q. doe, c=us, o=my org, ou=my dept", DnUtils.buildProxiedDN("cn=john q. doe, c=us, o=my org, ou=my dept"));
+
+ // Verify that a single DN with arrows results in the original DN with the arrows escaped.
+ assertEquals("cn=john q. doe, c=\\, o=my org, ou=\\", DnUtils.buildProxiedDN("cn=john q. doe, c=, o=my org, ou="));
+
+ // Verify that multiple DNs, some with arrows, result in the DNs concatenated, wrapped by arrows, with original arrows escaped.
+ // @formatter:off
+ assertEquals("cn=john q. doe, c=us, o=my org, ou=my dept, o=my org, ou=\\>",
+ DnUtils.buildProxiedDN("cn=john q. doe, c=us, o=my org, ou=my dept",
+ "cn=server2, c=\\, o=my org, ou=\\",
+ "cn=server1, c=us, o=my org, ou=my dept"));
+ // @formatter:on
+ }
+
+ /**
+ * Tests for {@link DnUtils#getCommonName(String)}.
+ */
+ @Test
+ void testGetCommonName() {
+ // Verify that a blank string results in a null CN.
+ assertNull(DnUtils.getCommonName(" "));
+
+ // Verify that a non-DN results in a null CN.
+ assertNull(DnUtils.getCommonName("S-1-1-0"));
+
+ // Verify that a DN with no CN results in a null CN.
+ assertNull(DnUtils.getCommonName("c=us, o=my org, ou=my dept"));
+
+ // Verify that a DN with a single CN returns the value of the CN.
+ assertEquals("john q. doe", DnUtils.getCommonName("cn=john q. doe, c=us, o=my org, ou=my dept"));
+
+ // Verify that a DN with multiple CNs returns the value of the last CN.
+ assertEquals("john q. doe", DnUtils.getCommonName("cn=johnny q. doe, cn=johnathan q. doe, cn=john q. doe, c=us, o=my org, ou=my dept"));
+ }
+
+ /**
+ * Tests for {@link DnUtils#getOrganizationalUnits(String)}.
+ */
+ @Test
+ void testGetOrganizationalUnits() {
+ // Verify that a blank DN results in an empty array.
+ assertEquals(0, DnUtils.getOrganizationalUnits(" ").length);
+
+ // Verify that a DN with no matching OU results in an empty array.
+ assertEquals(0, DnUtils.getOrganizationalUnits("cn=john q. doe, c=us, o=my org").length);
+
+ // Verify that a DN with a single OU results in an array with a single element.
+ assertArrayEquals(new String[] {"my dept"}, DnUtils.getOrganizationalUnits("cn=john q. doe, c=us, o=my org, ou=my dept"));
+
+ // Verify that a DN with a multiple OUs results in an array with multiple elements.
+ assertArrayEquals(new String[] {"my subsidiary", "my dept"},
+ DnUtils.getOrganizationalUnits("cn=john q. doe, c=us, o=my org, ou=my dept, ou=my subsidiary"));
+
+ // Verify that DN that cannot be parsed results in an empty array.
+ assertEquals(0, DnUtils.getOrganizationalUnits("S-1-1-0").length);
+ }
+
+ /**
+ * Tests for {@link DnUtils#getShortName(String)}.
+ */
+ @Test
+ public void testGetShortName() {
+ // Verify that a blank string results in empty string.
+ assertEquals("", DnUtils.getShortName(" "));
+
+ // Verify that a non-DN results in the last text portion returned.
+ assertEquals("apples", DnUtils.getShortName("pears to apples"));
+
+ // Verify that a DN with no CN results in the last text portion returned.
+ assertEquals("dept", DnUtils.getShortName("c=us, o=my org, ou=my dept"));
+
+ // Verify that a DN with a single CN results in the last text portion of the CN.
+ assertEquals("doe", DnUtils.getShortName("cn=john q. doe, c=us, o=my org, ou=my dept"));
+
+ // Verify that a DN with multiple CNs results in the last text portion of the last CN.
+ assertEquals("hart", DnUtils.getShortName("cn=johnny q. doe, cn=jonathan q. buck, cn=john q. hart, c=us, o=my org, ou=my dept"));
+ }
+
+ /**
+ * Tests for {@link DnUtils#getComponents(String, String)}.
+ */
+ @Test
+ public void testGetComponents() {
+ // Verify that a blank DN results in an empty array.
+ assertEquals(0, DnUtils.getComponents(" ", "cn").length);
+
+ // Verify that a blank component name results in an empty array.
+ assertEquals(0, DnUtils.getComponents("cn=john q. doe, c=us, o=my org, ou=my dept", " ").length);
+
+ // Verify that a DN with no matching component results in an empty array.
+ assertEquals(0, DnUtils.getComponents("cn=john q. doe, c=us, o=my org, ou=my dept", "dc").length);
+
+ // Verify that a DN with a single-value matching component results in an array with a single element.
+ assertArrayEquals(new String[] {"my org"}, DnUtils.getComponents("cn=john q. doe, c=us, o=my org, ou=my dept", "o"));
+
+ // Verify that a DN with a multi-value matching component results in an array with multiple elements.
+ assertArrayEquals(new String[] {"com", "example"}, DnUtils.getComponents("cn=john q. doe, c=us, o=my org, ou=my dept, dc=example, dc=com", "dc"));
+
+ // Verify that component name matching is case-insensitive.
+ assertArrayEquals(new String[] {"my org"}, DnUtils.getComponents("cn=john q. doe, c=us, o=my org, ou=my dept", "O"));
+
+ // Verify that DN that cannot be parsed results in an empty array.
+ assertEquals(0, DnUtils.getComponents("S-1-1-0", "cn").length);
+ }
+
+ /**
+ * Tests for {@link DnUtils#normalizeDN(String)}.
+ */
+ @Test
+ public void testNormalizedDN() {
+ // Verify the DN is trimmed of whitespace and cast to lowercase.
+ assertEquals("c=us, o=my org, cn=john q. doe, ou=my dept", DnUtils.normalizeDN(" C=US, O=My Org, CN=John Q. Doe, OU=My Dept "));
+
+ // Verify that if the last RDN is the CN, that the RDNs are reversed.
+ assertEquals("cn=john q. doe, ou=my dept, o=my org, c=us", DnUtils.normalizeDN("C=US, O=My Org, OU=My Dept, CN=John Q. Doe"));
+
+ // Verify that the components are not reordered if the CN is already in the first position.
+ assertEquals("cn=john q. doe, c=us, o=my org, ou=my dept", DnUtils.normalizeDN("CN=John Q. Doe, C=US, O=My Org, OU=My Dept"));
+
+ // Verify a string that cannot be parsed as a DN, e.g., a sid, is returned in its original form, trimmed and in lowercase.
+ assertEquals("s-1-1-0", DnUtils.normalizeDN(" S-1-1-0 "));
+ }
+
+ /**
+ * Tests for {@link DnUtils#buildNormalizedDNList(String, String, String, String, Pattern)}.
+ */
+ @Test
+ public void testBuildNormalizedDNList() {
+ // Verify that given no proxied subject or issuer DNs, the list consists of the normalized subject and issuer Dn.
+ List expected = Lists.newArrayList("sdn", "idn");
+ List actual = DnUtils.buildNormalizedDNList("SDN", "IDN", null, null, subjectDnPattern);
+ assertEquals(expected, actual);
+
+ // Verify that given a single proxied subject and issuer dn, the list contains them in the correct order.
+ expected = Lists.newArrayList("sdn2", "idn2", "sdn1", "idn1");
+ actual = DnUtils.buildNormalizedDNList("SDN1", "IDN1", "SDN2", "IDN2", subjectDnPattern);
+ assertEquals(expected, actual);
+
+ // Verify that given multiple proxied subject and issuer DNs, the list contains them in the correct order.
+ expected = Lists.newArrayList("sdn2", "idn2", "sdn3", "idn3", "sdn1", "idn1");
+ actual = DnUtils.buildNormalizedDNList("SDN1", "IDN1", "SDN2", "IDN2", subjectDnPattern);
+ assertEquals(expected, actual);
+
+ // Verify that an exception is thrown if proxied subject DNs are given, but proxied issuer DNs are not.
+ Throwable throwable = assertThrows(IllegalArgumentException.class, () -> DnUtils.buildNormalizedDNList("SDN1", "IDN1", "SDN2", null, subjectDnPattern));
+ assertEquals("If proxied subject DNs are supplied, then issuer DNs must be supplied as well.", throwable.getMessage());
+
+ // Verify that an exception is thrown if an unequal number of subject DNs and issuer DNs were supplied.
+ throwable = assertThrows(IllegalArgumentException.class, () -> DnUtils.buildNormalizedDNList("SDN1", "IDN2", "SDN2", "IDN2", subjectDnPattern));
+ assertEquals("Subject and issuer DN lists do not have the same number of entries: [SDN2, SDN3] vs [IDN2]", throwable.getMessage());
+
+ // Verify that an exception is thrown if a proxied subject DN is equal to its issuer DN.
+ throwable = assertThrows(IllegalArgumentException.class, () -> DnUtils.buildNormalizedDNList("SDN1", "IDN2", "SDN2", "SDN2", subjectDnPattern));
+ assertEquals("Subject DN sdn2 was passed as an issuer DN.", throwable.getMessage());
+
+ // Verify that an exception is thrown if a proxied issuer DN matches the subject DN pattern.
+ throwable = assertThrows(IllegalArgumentException.class,
+ () -> DnUtils.buildNormalizedDNList("SDN1", "IDN2", "SDN2", "CN=foo,OU=My Department", subjectDnPattern));
+ assertEquals("It appears that a subject DN (cn=foo, ou=my department) was passed as an issuer DN.", throwable.getMessage());
+ }
+
+ /**
+ * Tests for {@link DnUtils#buildNormalizedProxyDN(String, String, String, String, Pattern)}.
+ */
+ @Test
+ public void testBuildNormalizedProxyDNGivenStringArgs() {
+ // Verify that given no proxied subject or issuer DN, string consists of the normalized subject and issuer DN.
+ String expected = "sdn";
+ String actual = DnUtils.buildNormalizedProxyDN("SDN", "IDN", null, null, subjectDnPattern);
+ assertEquals(expected, actual);
+
+ // Verify that given a single proxied subject and issuer dn, the string contains them in the correct order.
+ expected = "sdn2";
+ actual = DnUtils.buildNormalizedProxyDN("SDN1", "IDN1", "SDN2", "IDN2", subjectDnPattern);
+ assertEquals(expected, actual);
+
+ // Verify that given multiple proxied subject and issuer DNs, the string contains them in the correct order.
+ expected = "sdn2";
+ actual = DnUtils.buildNormalizedProxyDN("SDN1", "IDN1", "SDN2", "IDN2", subjectDnPattern);
+ assertEquals(expected, actual);
+
+ // Verify that an exception is thrown if proxied subject DNs are given, but proxied issuer DNs are not.
+ Throwable throwable = assertThrows(IllegalArgumentException.class,
+ () -> DnUtils.buildNormalizedProxyDN("SDN1", "IDN1", "SDN2", null, subjectDnPattern));
+ assertEquals("If proxied subject DNs are supplied, then issuer DNs must be supplied as well.", throwable.getMessage());
+
+ // Verify that an exception is thrown if an unequal number of subject DNs and issuer DNs were supplied.
+ throwable = assertThrows(IllegalArgumentException.class, () -> DnUtils.buildNormalizedProxyDN("SDN1", "IDN2", "SDN2", "IDN2", subjectDnPattern));
+ assertEquals("Subject and issuer DN lists do not have the same number of entries: [SDN2, SDN3] vs [IDN2]", throwable.getMessage());
+
+ // Verify that an exception is thrown if a proxied subject DN is equal to its issuer DN.
+ throwable = assertThrows(IllegalArgumentException.class, () -> DnUtils.buildNormalizedProxyDN("SDN1", "IDN2", "SDN2", "SDN2", subjectDnPattern));
+ assertEquals("Subject DN sdn2 was passed as an issuer DN.", throwable.getMessage());
+
+ // Verify that an exception is thrown if a proxied issuer DN matches the subject DN pattern.
+ throwable = assertThrows(IllegalArgumentException.class,
+ () -> DnUtils.buildNormalizedProxyDN("SDN1", "IDN2", "SDN2", "CN=foo,OU=My Department", subjectDnPattern));
+ assertEquals("It appears that a subject DN (cn=foo, ou=my department) was passed as an issuer DN.", throwable.getMessage());
+ }
+
+ /**
+ * Tests for {@link DnUtils#buildNormalizedProxyDN(List)}.
+ */
+ @Test
+ public void testBuildNormalizedProxyDNGivenSubjectIssuerDNPairArgs() {
+ // Verify that given an empty list, a blank string is returned.
+ assertEquals("", DnUtils.buildNormalizedProxyDN(List.of()));
+
+ // Verify that given a single SubjectIssuerDnPair, the string consists of the normalized subject and issuer DN.
+ assertEquals("sdn", DnUtils.buildNormalizedProxyDN(List.of(SubjectIssuerDNPair.of("SDN", "IDN"))));
+
+ // Verify that given multiple SubjectIssuerDnPairs, the string consists of them in normalized form.
+ assertEquals("sdn2", DnUtils.buildNormalizedProxyDN(
+ List.of(SubjectIssuerDNPair.of("SDN2", "IDN2"), SubjectIssuerDNPair.of("SDN3", "IDN3"), SubjectIssuerDNPair.of("SDN1", "IDN1"))));
+ }
+
+ /**
+ * Tests for {@link DnUtils#isServerDN(String, Collection)}.
+ */
+ @Test
+ void testIsServerDN() {
+ // Verify that given a DN with an OU that is in the NPE OUs, true is returned.
+ assertTrue(DnUtils.isServerDN("cn=serverA, OU=npe, OU=Other OU", npeOUs));
+
+ // Verify that given a DN without a NPE OU, false is returned.
+
+ // Verify that given a DN with an OU that is in the NPE OUs, true is returned.
+ assertFalse(DnUtils.isServerDN("cn=serverA, OU=Example, OU=Other OU", npeOUs));
+ }
+
+ /**
+ * Tests for {@link DnUtils#getUserDN(String[], Collection)}
+ */
+ @Test
+ void testGetUserDNGivenSubjectDNsOnly() {
+ // Verify that given an array that does not contain a user DN, null is returned.
+ assertNull(DnUtils.getUserDN(new String[] {"CN=Server1,OU=Npe", "CN=Server2,OU=IAmNotAPerson"}, npeOUs));
+
+ // Verify that given an array with user DNs, the first user DN is returned.
+ assertEquals("CN=User1,OU=Example", DnUtils
+ .getUserDN(new String[] {"CN=Server1,OU=Npe", "CN=User1,OU=Example", "CN=Server2,OU=IAmNotAPerson", "CN=User2,OU=Example"}, npeOUs));
+ }
+
+ /**
+ * Tests for {@link DnUtils#getUserDN(String[], boolean, Collection)}.
+ */
+ @Test
+ void testGetUserDNGivenSubjectAndIssuerDNs() {
+ // Verify that given an array that does not contain a user DN, null is returned.
+ assertNull(DnUtils.getUserDN(new String[] {"CN=Server1,OU=Npe", "CN=Server2,OU=IAmNotAPerson"}, false, npeOUs));
+
+ // Verify that given an array with user DNs, the first user DN is returned.
+ assertEquals("CN=User1,OU=Example", DnUtils.getUserDN(new String[] {"CN=Server1,OU=Npe", "CN=User1,OU=Example", "CN=User2,OU=Example"}, false, npeOUs));
+
+ // Verify that given an array with subject and issuer DNs, that only the subject DNs are examined.
+ assertNull(DnUtils.getUserDN(new String[] {"CN=Server1,OU=Npe", "CN=Issuer1,OU=Example", "CN=Server2,OU=IAmNotAPerson", "CN=Issuer2,OU=Example"}, true,
+ npeOUs));
+ assertEquals("CN=User,OU=Example", DnUtils
+ .getUserDN(new String[] {"CN=Server1,OU=Npe", "CN=Issuer1,OU=Example", "CN=User,OU=Example", "CN=Issuer2,OU=Example"}, true, npeOUs));
+
+ // Verify that an exception is thrown if containsIssuerDns is true, but the DN array has an uneven length.
+ Throwable throwable = assertThrows(IllegalArgumentException.class,
+ () -> DnUtils.getUserDN(new String[] {"CN=Server1,OU=Npe", "CN=Issuer1,OU=Example", "CN=User,OU=Example"}, true, npeOUs));
+ assertEquals("DNs array is not a subject/issuer DN list: [CN=Server1,OU=Npe, CN=Issuer1,OU=Example, CN=User,OU=Example]", throwable.getMessage());
+ }
+}
diff --git a/commons/security/src/test/resources/datawave-server-keystore.jks b/commons/security/src/test/resources/datawave-server-keystore.jks
new file mode 100644
index 00000000000..ca02e0bbb77
Binary files /dev/null and b/commons/security/src/test/resources/datawave-server-keystore.jks differ
diff --git a/commons/security/src/test/resources/datawave-server-keystore.p12 b/commons/security/src/test/resources/datawave-server-keystore.p12
new file mode 100644
index 00000000000..b4ab0f3cb7f
Binary files /dev/null and b/commons/security/src/test/resources/datawave-server-keystore.p12 differ
diff --git a/commons/security/src/test/resources/datawave-server-truststore.jks b/commons/security/src/test/resources/datawave-server-truststore.jks
new file mode 100644
index 00000000000..95feb046172
Binary files /dev/null and b/commons/security/src/test/resources/datawave-server-truststore.jks differ
diff --git a/commons/security/src/test/resources/datawave-server-truststore.p12 b/commons/security/src/test/resources/datawave-server-truststore.p12
new file mode 100644
index 00000000000..ebd02da5fd5
Binary files /dev/null and b/commons/security/src/test/resources/datawave-server-truststore.p12 differ
diff --git a/commons/security/src/test/resources/dnutils.properties b/commons/security/src/test/resources/dnutils.properties
new file mode 100644
index 00000000000..ffb4017dff2
--- /dev/null
+++ b/commons/security/src/test/resources/dnutils.properties
@@ -0,0 +1,2 @@
+npe.ou.entries=iamnotaperson,npe,stillnotaperson
+subject.dn.pattern=(?:^|,)\\s*OU\\s*=\\s*My Department\\s*(?:,|$)
\ No newline at end of file
diff --git a/commons/security/src/test/resources/dnutils_blank.properties b/commons/security/src/test/resources/dnutils_blank.properties
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/contrib/datawave-quickstart/bin/common.sh b/contrib/datawave-quickstart/bin/common.sh
index f83fd6bc0ac..9992ada95d4 100644
--- a/contrib/datawave-quickstart/bin/common.sh
+++ b/contrib/datawave-quickstart/bin/common.sh
@@ -341,7 +341,7 @@ function getDataWaveVersion() {
function allUninstall() {
# ${DW_CLOUD_DATA} will be removed by default. To keep it, use '--keep-data' flag
- # Uninstalls all registered services.
+ # Uninstalls all registered services.
if servicesAreRunning ; then
echo "Stop running services before uninstalling!"
allStatus
diff --git a/contrib/datawave-quickstart/bin/services/datawave/bootstrap-web.sh b/contrib/datawave-quickstart/bin/services/datawave/bootstrap-web.sh
index 3cb37f90ad4..c70adf44a05 100644
--- a/contrib/datawave-quickstart/bin/services/datawave/bootstrap-web.sh
+++ b/contrib/datawave-quickstart/bin/services/datawave/bootstrap-web.sh
@@ -3,7 +3,7 @@
DW_WILDFLY_VERSION="${DW_WILDFLY_VERSION:-$(mvn -q -f ${DW_CLOUD_HOME}/docker/pom.xml help:evaluate -DforceStdout -Dexpression=version.quickstart.wildfly | tail -1)}"
DW_WILDFLY_DIST_SHA512_CHECKSUM="${DW_WILDFLY_DIST_SHA512_CHECKSUM:-$(mvn -q -f ${DW_CLOUD_HOME}/docker/pom.xml help:evaluate -DforceStdout -Dexpression=sha512.checksum.wildfly | tail -1)}"
-DW_WILDFLY_DIST_URI="${DW_WILDFLY_DIST_URI:-https://download.jboss.org/wildfly/${DW_WILDFLY_VERSION}.Final/wildfly-${DW_WILDFLY_VERSION}.Final.tar.gz}"
+DW_WILDFLY_DIST_URI="${DW_WILDFLY_DIST_URI:-https://github.com/wildfly/wildfly/releases/download/${DW_WILDFLY_VERSION}/wildfly-${DW_WILDFLY_VERSION}.tar.gz}"
DW_WILDFLY_DIST="$( basename "${DW_WILDFLY_DIST_URI}" )"
DW_WILDFLY_BASEDIR="wildfly-install"
DW_WILDFLY_SYMLINK="wildfly"
diff --git a/contrib/datawave-quickstart/docker/pom.xml b/contrib/datawave-quickstart/docker/pom.xml
index 79b21a37c15..628daf498a4 100644
--- a/contrib/datawave-quickstart/docker/pom.xml
+++ b/contrib/datawave-quickstart/docker/pom.xml
@@ -20,7 +20,7 @@
3a5e9ade2c84d4f8e0cb0551a9f6ea74a5cc2611afa141f4685f26431132c4cc60daeeedf22ab27c961ed7cd2df8b687e9fcaf00280093743e5c576fcdb53a5209cda6943625bc8e4307deca7a4df76d676a51aca1b9a0171938b793521dfe1ab5970fdb9a490bab34c12a2230ffdaed2992bad16458169ac51b281be1ab6741
- fcbdff4bc275f478c3bf5f665a83e62468a920e58fcddeaa2710272dd0f1ce3154cdc371d5011763a6be24ae1a5e0bca0218cceea63543edb4b5cf22de60b485
+ e1e75dfaa1d6483acce02f76c1fdcb0f1318acf3ace749e66ad2c47b2d817c036f44894a53e60e75c1d9f9c700613661789fad1517962060caf4a206c6506c184d85d6f7644d5f36d9c4d65e78bd662ab35ebe1380d762c24c12b98af029027eee453437c9245dbdf2b9beb77cd6b690b69e26f91cf9d11b0a183a979c73fa43false
diff --git a/core/annotation/pom.xml b/core/annotation/pom.xml
index 86438ec92ff..454e97df3f7 100644
--- a/core/annotation/pom.xml
+++ b/core/annotation/pom.xml
@@ -39,6 +39,17 @@
gov.nsa.datawavedatawave-core${project.version}
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+
+ org.apache.commons
+ commons-lang3
+ ${version.commons-lang3}org.apache.accumulo
diff --git a/core/base-rest-responses/pom.xml b/core/base-rest-responses/pom.xml
index 6eb8da755fc..b658a35824e 100644
--- a/core/base-rest-responses/pom.xml
+++ b/core/base-rest-responses/pom.xml
@@ -59,6 +59,10 @@
com.sun.xml.bindjaxb-impl
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.coreaccumulo-utils
diff --git a/core/base-rest-responses/src/main/java/datawave/security/DnList.java b/core/base-rest-responses/src/main/java/datawave/security/DnList.java
index 99ba0061f54..245635a2f60 100644
--- a/core/base-rest-responses/src/main/java/datawave/security/DnList.java
+++ b/core/base-rest-responses/src/main/java/datawave/security/DnList.java
@@ -16,8 +16,8 @@
import org.apache.commons.text.StringEscapeUtils;
-import datawave.microservice.security.util.DnUtils;
import datawave.security.authorization.DatawaveUserInfo;
+import datawave.security.util.DnUtils;
import datawave.webservice.HtmlProvider;
@XmlRootElement
diff --git a/core/common-util/pom.xml b/core/common-util/pom.xml
index 4024a08b2f7..28b9f9c1b43 100644
--- a/core/common-util/pom.xml
+++ b/core/common-util/pom.xml
@@ -8,7 +8,9 @@
datawave-core-common-util${project.artifactId}
-
+
+ 3.17.0
+
@@ -26,6 +28,11 @@
gov.nsa.datawave.microserviceauthorization-api
+
+ org.apache.commons
+ commons-lang3
+ ${version.commons-lang3}
+ org.apache.accumuloaccumulo-core
@@ -51,6 +58,10 @@
test-classestruesrc/test/resources
+
+ *.jks
+ *.p12
+
diff --git a/core/map-reduce/pom.xml b/core/map-reduce/pom.xml
index 912c8152dd7..7d952658bf9 100644
--- a/core/map-reduce/pom.xml
+++ b/core/map-reduce/pom.xml
@@ -30,7 +30,12 @@
org.jboss.resteasy
- resteasy-jaxrs
+ resteasy-core
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-core-spiprovided
diff --git a/core/query/pom.xml b/core/query/pom.xml
index 43aca8b82dd..98fcd67b1c4 100644
--- a/core/query/pom.xml
+++ b/core/query/pom.xml
@@ -10,6 +10,10 @@
${project.artifactId}
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.corebase-rest-responses
diff --git a/core/utils/accumulo-utils/pom.xml b/core/utils/accumulo-utils/pom.xml
index 40c0b4e3beb..412f493a6da 100644
--- a/core/utils/accumulo-utils/pom.xml
+++ b/core/utils/accumulo-utils/pom.xml
@@ -54,8 +54,9 @@
commons-collections
- gov.nsa.datawave.microservice
- authorization-api
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ ${project.version}io.dropwizard.metrics
diff --git a/core/utils/common-utils/src/main/java/datawave/microservice/security/util/DnUtils.java b/core/utils/common-utils/src/main/java/datawave/microservice/security/util/DnUtils.java
deleted file mode 100644
index 45d54c83e5d..00000000000
--- a/core/utils/common-utils/src/main/java/datawave/microservice/security/util/DnUtils.java
+++ /dev/null
@@ -1,137 +0,0 @@
-package datawave.microservice.security.util;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.regex.Pattern;
-
-import datawave.security.authorization.SubjectIssuerDNPair;
-import datawave.security.util.ProxiedEntityUtils;
-
-public class DnUtils {
-
- private final Pattern subjectDnPattern;
-
- /** Parsed NPE OU identifiers */
- private final List npeOuList;
-
- public DnUtils(Pattern subjectDnPattern, List npeOuList) {
- this.subjectDnPattern = subjectDnPattern;
- this.npeOuList = npeOuList;
- }
-
- public static String[] splitProxiedDNs(String proxiedDNs, boolean allowDups) {
- return ProxiedEntityUtils.splitProxiedDNs(proxiedDNs, allowDups);
- }
-
- public static String[] splitProxiedSubjectIssuerDNs(String proxiedDNs) {
- return ProxiedEntityUtils.splitProxiedSubjectIssuerDNs(proxiedDNs);
- }
-
- public static String buildProxiedDN(String... dns) {
- return ProxiedEntityUtils.buildProxiedDN(dns);
- }
-
- public Collection buildNormalizedDNList(String subjectDN, String issuerDN, String proxiedSubjectDNs, String proxiedIssuerDNs) {
- List dnList = new ArrayList<>();
- if (proxiedSubjectDNs != null) {
- if (proxiedIssuerDNs == null)
- throw new IllegalArgumentException("If proxied subject DNs are supplied, then issuer DNs must be supplied as well.");
- String[] subjectDNarray = splitProxiedDNs(proxiedSubjectDNs, true);
- String[] issuerDNarray = splitProxiedDNs(proxiedIssuerDNs, true);
- if (subjectDNarray.length != issuerDNarray.length)
- throw new IllegalArgumentException("Subject and issuer DN lists do not have the same number of entries: " + Arrays.toString(subjectDNarray)
- + " vs " + Arrays.toString(issuerDNarray));
- for (int i = 0; i < subjectDNarray.length; ++i) {
- subjectDNarray[i] = normalizeDN(subjectDNarray[i]);
- issuerDNarray[i] = normalizeDN(issuerDNarray[i]);
- dnList.add(subjectDNarray[i]);
- dnList.add(issuerDNarray[i]);
- if (issuerDNarray[i].equalsIgnoreCase(subjectDNarray[i]))
- throw new IllegalArgumentException("Subject DN " + issuerDNarray[i] + " was passed as an issuer DN.");
- if (subjectDnPattern.matcher(issuerDNarray[i]).find())
- throw new IllegalArgumentException("It appears that a subject DN (" + issuerDNarray[i] + ") was passed as an issuer DN.");
- }
- }
- subjectDN = normalizeDN(subjectDN);
- issuerDN = normalizeDN(issuerDN);
- dnList.add(subjectDN.replaceAll("(?])", "\\\\$1"));
- dnList.add(issuerDN.replaceAll("(?])", "\\\\$1"));
- return dnList;
- }
-
- public String buildNormalizedProxyDN(String subjectDN, String issuerDN, String proxiedSubjectDNs, String proxiedIssuerDNs) {
- StringBuilder sb = new StringBuilder();
- for (String escapedDN : buildNormalizedDNList(subjectDN, issuerDN, proxiedSubjectDNs, proxiedIssuerDNs)) {
- if (sb.length() == 0)
- sb.append(escapedDN);
- else
- sb.append('<').append(escapedDN).append('>');
- }
- return sb.toString();
- }
-
- public static String buildNormalizedProxyDN(List dns) {
- StringBuilder sb = new StringBuilder();
- dns.stream().forEach(dn -> {
- if (sb.length() == 0) {
- sb.append(normalizeDN(dn.subjectDN()));
- } else {
- sb.append('<').append(normalizeDN(dn.subjectDN())).append('>');
- }
- sb.append('<').append(normalizeDN(dn.issuerDN())).append('>');
- });
- return sb.toString();
- }
-
- public static String getCommonName(String dn) {
- return ProxiedEntityUtils.getCommonName(dn);
- }
-
- public static String[] getOrganizationalUnits(String dn) {
- return ProxiedEntityUtils.getOrganizationalUnits(dn);
- }
-
- public static String getShortName(String dn) {
- return ProxiedEntityUtils.getShortName(dn);
- }
-
- public boolean isServerDN(String dn) {
- return isNPE(dn);
- }
-
- public String getUserDN(String[] dns) {
- return getUserDN(dns, false);
- }
-
- public String getUserDN(String[] dns, boolean issuerDNs) {
- if (issuerDNs && (dns.length % 2) != 0)
- throw new IllegalArgumentException("DNs array is not a subject/issuer DN list: " + Arrays.toString(dns));
-
- for (int i = 0; i < dns.length; i += (issuerDNs) ? 2 : 1) {
- String dn = dns[i];
- if (!isServerDN(dn))
- return dn;
- }
- return null;
- }
-
- public String[] getComponents(String dn, String componentName) {
- return ProxiedEntityUtils.getComponents(dn, componentName);
- }
-
- public static String normalizeDN(String userName) {
- return ProxiedEntityUtils.normalizeDN(userName);
- }
-
- private boolean isNPE(String dn) {
- String[] ouList = ProxiedEntityUtils.getOrganizationalUnits(dn);
- for (String ou : ouList) {
- if (npeOuList.contains(ou.toUpperCase())) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/core/utils/common-utils/src/test/java/datawave/microservice/security/util/DnUtilsTest.java b/core/utils/common-utils/src/test/java/datawave/microservice/security/util/DnUtilsTest.java
deleted file mode 100644
index ef07365b1f6..00000000000
--- a/core/utils/common-utils/src/test/java/datawave/microservice/security/util/DnUtilsTest.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package datawave.microservice.security.util;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.regex.Pattern;
-
-import org.junit.jupiter.api.Test;
-
-import com.google.common.collect.Lists;
-
-public class DnUtilsTest {
-
- private DnUtils dnUtils = new DnUtils(Pattern.compile("(?:^|,)\\s*OU\\s*=\\s*My Department\\s*(?:,|$)", Pattern.CASE_INSENSITIVE),
- Arrays.asList("iamnotaperson", "npe", "stillnotaperson"));
-
- @Test
- public void testBuildNormalizedProxyDN() {
- String expected = "sdn";
- String actual = dnUtils.buildNormalizedProxyDN("SDN", "IDN", null, null);
- assertEquals(expected, actual);
-
- expected = "sdn2";
- actual = dnUtils.buildNormalizedProxyDN("SDN1", "IDN1", "SDN2", "IDN2");
- assertEquals(expected, actual);
-
- expected = "sdn2";
- actual = dnUtils.buildNormalizedProxyDN("SDN1", "IDN1", "SDN2", "IDN2");
- assertEquals(expected, actual);
-
- expected = "sdn2";
- actual = dnUtils.buildNormalizedProxyDN("SDN1", "IDN1", "", "");
- assertEquals(expected, actual);
- }
-
- @Test
- public void testBuildNormalizedDN() {
- Collection expected = Lists.newArrayList("sdn", "idn");
- Collection actual = dnUtils.buildNormalizedDNList("SDN", "IDN", null, null);
- assertEquals(expected, actual);
-
- expected = Lists.newArrayList("sdn2", "idn2", "sdn1", "idn1");
- actual = dnUtils.buildNormalizedDNList("SDN1", "IDN1", "SDN2", "IDN2");
- assertEquals(expected, actual);
-
- expected = Lists.newArrayList("sdn2", "idn2", "sdn3", "idn3", "sdn1", "idn1");
- actual = dnUtils.buildNormalizedDNList("SDN1", "IDN1", "SDN2", "IDN2");
- assertEquals(expected, actual);
-
- expected = Lists.newArrayList("sdn2", "idn2", "sdn3", "idn3", "sdn1", "idn1");
- actual = dnUtils.buildNormalizedDNList("SDN1", "IDN1", "", "");
- assertEquals(expected, actual);
- }
-
- @Test
- public void testGetUserDnFromArray() {
- String userDnForTest = "snd1";
- String[] array = new String[] {userDnForTest, "idn"};
- String userDN = dnUtils.getUserDN(array);
- assertEquals(userDnForTest, userDN);
- }
-
- @Test
- public void testTest() {
- assertThrows(IllegalArgumentException.class, () -> {
- String[] dns = new String[] {"sdn"};
- dnUtils.getUserDN(dns, true);
- });
- }
-
- @Test
- public void testBuildNormalizedProxyDNTooMissingIssuers() {
- assertThrows(IllegalArgumentException.class, () -> {
- dnUtils.buildNormalizedProxyDN("SDN", "IDN", "SDN2", null);
- });
- }
-
- @Test
- public void testBuildNormalizedProxyDNTooFewIssuers() {
- assertThrows(IllegalArgumentException.class, () -> {
- dnUtils.buildNormalizedProxyDN("SDN", "IDN", "SDN2", "IDN2");
- });
- }
-
- @Test
- public void testBuildNormalizedProxyDNTooFewSubjects() {
- assertThrows(IllegalArgumentException.class, () -> {
- dnUtils.buildNormalizedProxyDN("SDN", "IDN", "SDN2", "IDN2");
- });
- }
-
- @Test
- public void testBuildNormalizedProxyDNSubjectEqualsIssuer() {
- assertThrows(IllegalArgumentException.class, () -> {
- dnUtils.buildNormalizedProxyDN("SDN", "IDN", "SDN2", "SDN2");
- });
- }
-
- @Test
- public void testBuildNormalizedProxyDNSubjectDNInIssuer() {
- assertThrows(IllegalArgumentException.class, () -> {
- dnUtils.buildNormalizedProxyDN("SDN", "IDN", "SDN2", "CN=foo,OU=My Department");
- });
- }
-
- @Test
- public void testBuildNormalizedDNListTooMissingIssuers() {
- assertThrows(IllegalArgumentException.class, () -> {
- dnUtils.buildNormalizedDNList("SDN", "IDN", "SDN2", null);
- });
- }
-
- @Test
- public void testBuildNormalizedDNListTooFewIssuers() {
- assertThrows(IllegalArgumentException.class, () -> {
- dnUtils.buildNormalizedDNList("SDN", "IDN", "SDN2", "IDN2");
- });
- }
-
- @Test
- public void testBuildNormalizedDNListTooFewSubjects() {
- assertThrows(IllegalArgumentException.class, () -> {
- dnUtils.buildNormalizedDNList("SDN", "IDN", "SDN2", "IDN2");
- });
- }
-
- @Test
- public void testBuildNormalizedDNListSubjectEqualsIssuer() {
- assertThrows(IllegalArgumentException.class, () -> {
- dnUtils.buildNormalizedDNList("SDN", "IDN", "SDN2", "SDN2");
- });
- }
-
- @Test
- public void testBuildNormalizedDNListSubjectDNInIssuer() {
- assertThrows(IllegalArgumentException.class, () -> {
- dnUtils.buildNormalizedDNList("SDN", "IDN", "SDN2", "CN=foo,OU=My Department");
- });
- }
-
-}
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 7a014326b67..de419567db0 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -37,6 +37,8 @@ services:
- "9443:8443"
# web server debug port
- "5011:8787"
+ # wildfly admin ui
+ - "9990:9990"
extra_hosts:
- "${DW_HOSTNAME}:${DW_HOST_IP}"
- "${DW_HOST_FQDN}:${DW_HOST_IP}"
diff --git a/docs/pom.xml b/docs/pom.xml
index 1159b8746b6..8ee1f03b515 100644
--- a/docs/pom.xml
+++ b/docs/pom.xml
@@ -82,6 +82,11 @@
${project.version}true
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ true
+ gov.nsa.datawave.webservicesdatawave-ws-accumulo
@@ -149,6 +154,12 @@
${project.version}true
+
+ gov.nsa.datawave.webservices
+ datawave-ws-security-elytron
+ ${project.version}
+ true
+ org.codehaus.jacksonjackson-jaxrs
@@ -206,7 +217,12 @@
org.jboss.resteasy
- resteasy-jaxrs
+ resteasy-core
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-core-spiprovided
@@ -221,7 +237,7 @@
org.jboss.spec.javax.transaction
- jboss-transaction-api_1.2_spec
+ jboss-transaction-api_1.3_specprovided
@@ -235,16 +251,6 @@
2.28.2provided
-
- org.picketbox
- picketbox
- provided
-
-
- org.picketbox
- picketbox-infinispan
- provided
- org.projectlomboklombok
diff --git a/microservices/microservice-parent/pom.xml b/microservices/microservice-parent/pom.xml
index 55a8f5ee33c..247de00389b 100644
--- a/microservices/microservice-parent/pom.xml
+++ b/microservices/microservice-parent/pom.xml
@@ -53,6 +53,7 @@
UTF-81C
+ 7.40.0-3508-RC13.3.42.14.3
@@ -65,6 +66,11 @@
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ ${version.datawave-commons-security}
+
diff --git a/microservices/microservice-service-parent/pom.xml b/microservices/microservice-service-parent/pom.xml
index c3321d86f10..0b00a0acf23 100644
--- a/microservices/microservice-service-parent/pom.xml
+++ b/microservices/microservice-service-parent/pom.xml
@@ -4,7 +4,7 @@
gov.nsa.datawave.microservicedatawave-microservice-parent
- 4.0.10
+ 4.0.11-3508-RC1../microservice-parentdatawave-microservice-service-parent
diff --git a/microservices/services/authorization/api/pom.xml b/microservices/services/authorization/api/pom.xml
index a1769178b2a..4ed12d2c845 100644
--- a/microservices/services/authorization/api/pom.xml
+++ b/microservices/services/authorization/api/pom.xml
@@ -4,7 +4,7 @@
gov.nsa.datawave.microservicedatawave-microservice-parent
- 4.0.10
+ 4.0.11-3508-RC1../../../microservice-parent/pom.xmlauthorization-api
@@ -59,16 +59,6 @@
jjwt-api${version.jjwt}
-
- io.jsonwebtoken
- jjwt-impl
- ${version.jjwt}
-
-
- io.jsonwebtoken
- jjwt-jackson
- ${version.jjwt}
- jakarta.xml.bindjakarta.xml.bind-api
@@ -85,6 +75,18 @@
+
+ io.jsonwebtoken
+ jjwt-impl
+ ${version.jjwt}
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ ${version.jjwt}
+ runtime
+
@@ -92,14 +94,6 @@
com.fasterxml.jackson.corejackson-annotations
-
- com.fasterxml.jackson.core
- jackson-core
-
-
- com.fasterxml.jackson.core
- jackson-databind
- com.google.guavaguava
@@ -108,14 +102,21 @@
com.sun.xml.bindjaxb-impl
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ ${version.datawave-commons-security}
+ io.jsonwebtokenjjwt-api
+
io.jsonwebtokenjjwt-impl
+
io.jsonwebtokenjjwt-jackson
diff --git a/microservices/services/authorization/api/src/test/java/datawave/security/util/ProxiedEntityUtilsTest.java b/microservices/services/authorization/api/src/test/java/datawave/security/util/ProxiedEntityUtilsTest.java
deleted file mode 100644
index 303fb78cff5..00000000000
--- a/microservices/services/authorization/api/src/test/java/datawave/security/util/ProxiedEntityUtilsTest.java
+++ /dev/null
@@ -1,220 +0,0 @@
-package datawave.security.util;
-
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-import org.junit.jupiter.api.Test;
-
-/**
- * Tests for {@link ProxiedEntityUtils}.
- */
-public class ProxiedEntityUtilsTest {
-
- /**
- * Tests for {@link ProxiedEntityUtils#splitProxiedDNs(String, boolean)}.
- */
- @Test
- void testSplitProxiedDNs() {
- // Verify that a single DN results in an array with the DN.
- assertArrayEquals(new String[] {"cn=john q. doe, c=us, o=my org, ou=my dept"},
- ProxiedEntityUtils.splitProxiedDNs("cn=john q. doe, c=us, o=my org, ou=my dept", true));
-
- // Verify that a single DN with escaped < and > characters results in an array with the DN.
- assertArrayEquals(new String[] {"cn=john q. doe, c=\\, o=my org, ou=\\"},
- ProxiedEntityUtils.splitProxiedDNs("cn=john q. doe, c=\\, o=my org, ou=\\", true));
-
- // Verify that multiple DNs result in an array with the DNs.
- assertArrayEquals(new String[] {"cn=john q. doe, c=\\, o=my org, ou=\\", "cn=server1, c=us, o=my org, ou=my dept"}, ProxiedEntityUtils
- .splitProxiedDNs("cn=john q. doe, c=\\, o=my org, ou=\\", true));
-
- // Verify that duplicate DNs are retained when specified to allow duplicates.
- // @formatter:off
- assertArrayEquals(new String[] {"cn=john q. doe, c=\\, o=my org, ou=\\",
- "cn=server1, c=us, o=my org, ou=my dept",
- "cn=server1, c=us, o=my org, ou=my dept"},
- ProxiedEntityUtils.splitProxiedDNs("cn=john q. doe, c=\\, o=my org, ou=\\", true));
- // @formatter:on
-
- // Verify that only the first instance of a duplicate DN is retained when duplicates are not allowed.
- // Verify that duplicate DNs are retained when specified to allow duplicates.
- // @formatter:off
- assertArrayEquals(new String[] {"cn=john q. doe, c=\\, o=my org, ou=\\",
- "cn=server1, c=us, o=my org, ou=my dept",
- "cn=server2, c=us, o=my org, ou=my dept"},
- ProxiedEntityUtils.splitProxiedDNs("cn=john q. doe, c=\\, o=my org, ou=\\", false));
- // @formatter:on
-
- // Verify that any blank DNs are pruned.
- assertArrayEquals(new String[] {"cn=john q. doe, c=\\, o=my org, ou=\\", "cn=server1, c=us, o=my org, ou=my dept"}, ProxiedEntityUtils
- .splitProxiedDNs("cn=john q. doe, c=\\, o=my org, ou=\\< >", true));
-
- }
-
- /**
- * Tests for {@link ProxiedEntityUtils#splitProxiedSubjectIssuerDNs(String)}.
- */
- @Test
- void testSplitProxiedSubjectIssuerDNs() {
- // Verify that a single DN results in an array with the DN.
- assertArrayEquals(new String[] {"cn=john q. doe, c=us, o=my org, ou=my dept"},
- ProxiedEntityUtils.splitProxiedSubjectIssuerDNs("cn=john q. doe, c=us, o=my org, ou=my dept"));
-
- // Verify that a single DN with escaped < and > characters results in an array with the DN.
- assertArrayEquals(new String[] {"cn=john q. doe, c=us, o=my org, ou=\\"},
- ProxiedEntityUtils.splitProxiedSubjectIssuerDNs("cn=john q. doe, c=us, o=my org, ou=\\"));
-
- // Verify that an uneven number of DNs greater than one results in an exception.
- IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
- () -> ProxiedEntityUtils.splitProxiedSubjectIssuerDNs("cn=subject1"));
- assertEquals("Invalid proxied DNs list does not have entries in pairs.", exception.getMessage());
-
- // Verify that only the first subject-issuer pair for any unique subject DN is retained.
- assertArrayEquals(new String[] {"cn=subject1", "cn=issuer1"},
- ProxiedEntityUtils.splitProxiedSubjectIssuerDNs("cn=subject1"));
-
- // Verify that multiple subject-issuer pairs are parsed correctly.
- assertArrayEquals(new String[] {"cn=subject1", "cn=issuer1", "cn=subject2", "cn=issuer2"},
- ProxiedEntityUtils.splitProxiedSubjectIssuerDNs("cn=subject1"));
-
- // Verify that any blank DNs are pruned.
- assertArrayEquals(new String[] {"cn=subject1", "cn=issuer1", "cn=subject2", "cn=issuer2"},
- ProxiedEntityUtils.splitProxiedSubjectIssuerDNs("cn=subject1< >< >"));
-
- }
-
- /**
- * Tests for {@link ProxiedEntityUtils#buildProxiedDN(String...)}.
- */
- @Test
- void testBuildProxiedDN() {
- // Verify that a blank string results in the original string.
- assertEquals(" ", ProxiedEntityUtils.buildProxiedDN(" "));
-
- // Verify that a single DN with no arrows results in the original DN.
- assertEquals("cn=john q. doe, c=us, o=my org, ou=my dept", ProxiedEntityUtils.buildProxiedDN("cn=john q. doe, c=us, o=my org, ou=my dept"));
-
- // Verify that a single DN with arrows results in the original DN with the arrows escaped.
- assertEquals("cn=john q. doe, c=\\, o=my org, ou=\\",
- ProxiedEntityUtils.buildProxiedDN("cn=john q. doe, c=, o=my org, ou="));
-
- // Verify that multiple DNs, some with arrows, result in the DNs concatenated, wrapped by arrows, with original arrows escaped.
- // @formatter:off
- assertEquals("cn=john q. doe, c=us, o=my org, ou=my dept, o=my org, ou=\\>",
- ProxiedEntityUtils.buildProxiedDN("cn=john q. doe, c=us, o=my org, ou=my dept",
- "cn=server2, c=\\, o=my org, ou=\\",
- "cn=server1, c=us, o=my org, ou=my dept"));
- // @formatter:on
- }
-
- /**
- * Tests for {@link ProxiedEntityUtils#getCommonName(String)}.
- */
- @Test
- void testGetCommonName() {
- // Verify that a blank string results in a null CN.
- assertNull(ProxiedEntityUtils.getCommonName(" "));
-
- // Verify that a non-DN results in a null CN.
- assertNull(ProxiedEntityUtils.getCommonName("S-1-1-0"));
-
- // Verify that a DN with no CN results in a null CN.
- assertNull(ProxiedEntityUtils.getCommonName("c=us, o=my org, ou=my dept"));
-
- // Verify that a DN with a single CN returns the value of the CN.
- assertEquals("john q. doe", ProxiedEntityUtils.getCommonName("cn=john q. doe, c=us, o=my org, ou=my dept"));
-
- // Verify that a DN with multiple CNs returns the value of the last CN.
- assertEquals("john q. doe", ProxiedEntityUtils.getCommonName("cn=johnny q. doe, cn=johnathan q. doe, cn=john q. doe, c=us, o=my org, ou=my dept"));
- }
-
- /**
- * Tests for {@link ProxiedEntityUtils#getOrganizationalUnits(String)}.
- */
- @Test
- void testGetOrganizationalUnits() {
- // Verify that a blank DN results in an empty array.
- assertEquals(0, ProxiedEntityUtils.getOrganizationalUnits(" ").length);
-
- // Verify that a DN with no matching OU results in an empty array.
- assertEquals(0, ProxiedEntityUtils.getOrganizationalUnits("cn=john q. doe, c=us, o=my org").length);
-
- // Verify that a DN with a single OU results in an array with a single element.
- assertArrayEquals(new String[] {"my dept"}, ProxiedEntityUtils.getOrganizationalUnits("cn=john q. doe, c=us, o=my org, ou=my dept"));
-
- // Verify that a DN with a multiple OUs results in an array with multiple elements.
- assertArrayEquals(new String[] {"my subsidiary", "my dept"},
- ProxiedEntityUtils.getOrganizationalUnits("cn=john q. doe, c=us, o=my org, ou=my dept, ou=my subsidiary"));
-
- // Verify that DN that cannot be parsed results in an empty array.
- assertEquals(0, ProxiedEntityUtils.getOrganizationalUnits("S-1-1-0").length);
- }
-
- /**
- * Tests for {@link ProxiedEntityUtils#getShortName(String)}.
- */
- @Test
- public void testGetShortName() {
- // Verify that a blank string results in empty string.
- assertEquals("", ProxiedEntityUtils.getShortName(" "));
-
- // Verify that a non-DN results in the last text portion returned.
- assertEquals("apples", ProxiedEntityUtils.getShortName("pears to apples"));
-
- // Verify that a DN with no CN results in the last text portion returned.
- assertEquals("dept", ProxiedEntityUtils.getShortName("c=us, o=my org, ou=my dept"));
-
- // Verify that a DN with a single CN results in the last text portion of the CN.
- assertEquals("doe", ProxiedEntityUtils.getShortName("cn=john q. doe, c=us, o=my org, ou=my dept"));
-
- // Verify that a DN with multiple CNs results in the last text portion of the last CN.
- assertEquals("hart", ProxiedEntityUtils.getShortName("cn=johnny q. doe, cn=jonathan q. buck, cn=john q. hart, c=us, o=my org, ou=my dept"));
- }
-
- /**
- * Tests for {@link ProxiedEntityUtils#getComponents(String, String)}.
- */
- @Test
- public void testGetComponents() {
- // Verify that a blank DN results in an empty array.
- assertEquals(0, ProxiedEntityUtils.getComponents(" ", "cn").length);
-
- // Verify that a blank component name results in an empty array.
- assertEquals(0, ProxiedEntityUtils.getComponents("cn=john q. doe, c=us, o=my org, ou=my dept", " ").length);
-
- // Verify that a DN with no matching component results in an empty array.
- assertEquals(0, ProxiedEntityUtils.getComponents("cn=john q. doe, c=us, o=my org, ou=my dept", "dc").length);
-
- // Verify that a DN with a single-value matching component results in an array with a single element.
- assertArrayEquals(new String[] {"my org"}, ProxiedEntityUtils.getComponents("cn=john q. doe, c=us, o=my org, ou=my dept", "o"));
-
- // Verify that a DN with a multi-value matching component results in an array with multiple elements.
- assertArrayEquals(new String[] {"com", "example"},
- ProxiedEntityUtils.getComponents("cn=john q. doe, c=us, o=my org, ou=my dept, dc=example, dc=com", "dc"));
-
- // Verify that component name matching is case-insensitive.
- assertArrayEquals(new String[] {"my org"}, ProxiedEntityUtils.getComponents("cn=john q. doe, c=us, o=my org, ou=my dept", "O"));
-
- // Verify that DN that cannot be parsed results in an empty array.
- assertEquals(0, ProxiedEntityUtils.getComponents("S-1-1-0", "cn").length);
- }
-
- /**
- * Tests for {@link ProxiedEntityUtils#normalizeDN(String)}.
- */
- @Test
- public void testNormalizedDN() {
- // Verify the DN is trimmed of whitespace and cast to lowercase.
- assertEquals("c=us, o=my org, cn=john q. doe, ou=my dept", ProxiedEntityUtils.normalizeDN(" C=US, O=My Org, CN=John Q. Doe, OU=My Dept "));
-
- // Verify that if the last RDN is the CN, that the RDNs are reversed.
- assertEquals("cn=john q. doe, ou=my dept, o=my org, c=us", ProxiedEntityUtils.normalizeDN("C=US, O=My Org, OU=My Dept, CN=John Q. Doe"));
-
- // Verify that the components are not reordered if the CN is already in the first position.
- assertEquals("cn=john q. doe, c=us, o=my org, ou=my dept", ProxiedEntityUtils.normalizeDN("CN=John Q. Doe, C=US, O=My Org, OU=My Dept"));
-
- // Verify a string that cannot be parsed as a DN, e.g., a sid, is returned in its original form, trimmed and in lowercase.
- assertEquals("s-1-1-0", ProxiedEntityUtils.normalizeDN(" S-1-1-0 "));
- }
-}
diff --git a/microservices/services/authorization/pom.xml b/microservices/services/authorization/pom.xml
index 2f5f20b5897..294eb1ac4c2 100644
--- a/microservices/services/authorization/pom.xml
+++ b/microservices/services/authorization/pom.xml
@@ -4,7 +4,7 @@
gov.nsa.datawave.microservicedatawave-microservice-parent
- 4.0.10
+ 4.0.11-3508-RC1../../microservice-parent/pom.xmlauthorization-service-parent
diff --git a/microservices/services/authorization/service/pom.xml b/microservices/services/authorization/service/pom.xml
index 361a9c6624b..511ea3114e5 100644
--- a/microservices/services/authorization/service/pom.xml
+++ b/microservices/services/authorization/service/pom.xml
@@ -4,7 +4,7 @@
gov.nsa.datawave.microservicedatawave-microservice-service-parent
- 5.0.11
+ 5.0.12-3508-RC1../../../microservice-service-parent/pom.xmlauthorization-service
@@ -21,10 +21,10 @@
datawave.microservice.authorization.AuthorizationService3.20.07.33.1
- 4.0.2
+ 4.0.3-3508-RC14.0.44.0.4
- 4.0.7
+ 4.0.8-3508-RC131.1-jre
@@ -51,6 +51,11 @@
jaxb-impl${version.jaxb-impl}
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ ${version.datawave-commons-security}
+ gov.nsa.datawave.corebase-rest-responses
@@ -125,6 +130,10 @@
com.hazelcasthazelcast
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.corebase-rest-responses
@@ -153,6 +162,7 @@
io.jsonwebtokenjjwt-api
+
io.swagger.core.v3swagger-annotations
diff --git a/microservices/services/authorization/service/src/main/java/datawave/microservice/authorization/AuthorizationOperationsV1.java b/microservices/services/authorization/service/src/main/java/datawave/microservice/authorization/AuthorizationOperationsV1.java
index f6a9a424d5b..9a43aac5929 100644
--- a/microservices/services/authorization/service/src/main/java/datawave/microservice/authorization/AuthorizationOperationsV1.java
+++ b/microservices/services/authorization/service/src/main/java/datawave/microservice/authorization/AuthorizationOperationsV1.java
@@ -24,7 +24,6 @@
import datawave.microservice.authorization.user.DatawaveUserDetails;
import datawave.microservice.authorization.user.DatawaveUserDetailsFactory;
import datawave.microservice.authorization.util.AuthorizationsUtil;
-import datawave.microservice.security.util.DnUtils;
import datawave.security.DnList;
import datawave.security.authorization.CachedDatawaveUserService;
import datawave.security.authorization.DatawaveUser;
@@ -33,6 +32,8 @@
import datawave.security.authorization.JWTTokenHandler;
import datawave.security.authorization.ProxiedUserDetails;
import datawave.security.authorization.UserOperations;
+import datawave.security.util.DnProperties;
+import datawave.security.util.DnUtils;
import datawave.user.AuthorizationsListBase;
import datawave.webservice.result.GenericResponse;
@@ -51,21 +52,21 @@ public class AuthorizationOperationsV1 {
protected final AuthorizationsListSupplier authorizationsListSupplier;
- protected final DnUtils dnUtils;
+ protected final DnProperties dnProperties;
protected final Set registeredFederatedUserOperations;
protected final DatawaveUserDetailsFactory datawaveUserDetailsFactory;
public AuthorizationOperationsV1(JWTTokenHandler tokenHandler, CachedDatawaveUserService cachedDatawaveUserService, ApplicationContext appCtx,
- BusProperties busProperties, AuthorizationsListSupplier authorizationsListSupplier, DnUtils dnUtils,
+ BusProperties busProperties, AuthorizationsListSupplier authorizationsListSupplier, DnProperties dnProperties,
Set registeredFederatedUserOperations, DatawaveUserDetailsFactory datawaveUserDetailsFactory) {
this.tokenHandler = tokenHandler;
this.cachedDatawaveUserService = cachedDatawaveUserService;
this.appCtx = appCtx;
this.busProperties = busProperties;
this.authorizationsListSupplier = authorizationsListSupplier;
- this.dnUtils = dnUtils;
+ this.dnProperties = dnProperties;
this.registeredFederatedUserOperations = registeredFederatedUserOperations;
this.datawaveUserDetailsFactory = datawaveUserDetailsFactory;
}
@@ -89,7 +90,7 @@ public AuthorizationsListBase> listEffectiveAuthorizations(DatawaveUserDetails
final AuthorizationsListBase> list = authorizationsListSupplier.get();
// Find out who/what called this method
- String name = dnUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String name = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (federate) {
for (UserOperations federatedUserOperations : registeredFederatedUserOperations) {
diff --git a/microservices/services/authorization/service/src/main/java/datawave/microservice/authorization/AuthorizationOperationsV2.java b/microservices/services/authorization/service/src/main/java/datawave/microservice/authorization/AuthorizationOperationsV2.java
index eca982ad8c3..8e346f699b3 100644
--- a/microservices/services/authorization/service/src/main/java/datawave/microservice/authorization/AuthorizationOperationsV2.java
+++ b/microservices/services/authorization/service/src/main/java/datawave/microservice/authorization/AuthorizationOperationsV2.java
@@ -10,11 +10,11 @@
import datawave.microservice.authorization.config.AuthorizationsListSupplier;
import datawave.microservice.authorization.user.DatawaveUserDetails;
import datawave.microservice.authorization.user.DatawaveUserDetailsFactory;
-import datawave.microservice.security.util.DnUtils;
import datawave.security.authorization.CachedDatawaveUserService;
import datawave.security.authorization.DatawaveUser;
import datawave.security.authorization.JWTTokenHandler;
import datawave.security.authorization.UserOperations;
+import datawave.security.util.DnProperties;
/**
* Presents the REST operations for the authorization service. This version returns the updated (V2) DatawaveUser
@@ -23,9 +23,9 @@
public class AuthorizationOperationsV2 extends AuthorizationOperationsV1 {
public AuthorizationOperationsV2(JWTTokenHandler tokenHandler, CachedDatawaveUserService cachedDatawaveUserService, ApplicationContext appCtx,
- BusProperties busProperties, AuthorizationsListSupplier authorizationsListSupplier, DnUtils dnUtils,
+ BusProperties busProperties, AuthorizationsListSupplier authorizationsListSupplier, DnProperties dnProperties,
Set registeredFederatedUserOperations, DatawaveUserDetailsFactory datawaveUserDetailsFactory) {
- super(tokenHandler, cachedDatawaveUserService, appCtx, busProperties, authorizationsListSupplier, dnUtils, registeredFederatedUserOperations,
+ super(tokenHandler, cachedDatawaveUserService, appCtx, busProperties, authorizationsListSupplier, dnProperties, registeredFederatedUserOperations,
datawaveUserDetailsFactory);
}
diff --git a/microservices/services/authorization/service/src/test/java/datawave/microservice/authorization/OAuthOperationsV2TestCommon.java b/microservices/services/authorization/service/src/test/java/datawave/microservice/authorization/OAuthOperationsV2TestCommon.java
index 899ea386569..f06c9339752 100644
--- a/microservices/services/authorization/service/src/test/java/datawave/microservice/authorization/OAuthOperationsV2TestCommon.java
+++ b/microservices/services/authorization/service/src/test/java/datawave/microservice/authorization/OAuthOperationsV2TestCommon.java
@@ -58,7 +58,7 @@
import datawave.security.authorization.SubjectIssuerDNPair;
import datawave.security.authorization.oauth.OAuthTokenResponse;
import datawave.security.authorization.oauth.OAuthUserInfo;
-import datawave.security.util.ProxiedEntityUtils;
+import datawave.security.util.DnUtils;
public class OAuthOperationsV2TestCommon {
@@ -144,7 +144,7 @@ public void TestCodeFlowValid(DatawaveUser dwUser, AUTH_TYPE authType) throws Ex
// Call the user endpoint with the access_token to get the primary user
ResponseEntity userResponse = user(access_token, JWTTokenHandler.PRINCIPALS_CLAIM);
OAuthUserInfo oAuthUserInfo = userResponse.getBody();
- assertEquals(ProxiedEntityUtils.getCommonName(dwUser.getDn().subjectDN()), oAuthUserInfo.getName());
+ assertEquals(DnUtils.getCommonName(dwUser.getDn().subjectDN()), oAuthUserInfo.getName());
assertEquals(dwUser.getLogin(), oAuthUserInfo.getLogin());
assertEquals(dwUser.getEmail(), oAuthUserInfo.getEmail());
assertEquals(dwUser.getDn(), oAuthUserInfo.getDn());
diff --git a/microservices/services/mapreduce-query/service/pom.xml b/microservices/services/mapreduce-query/service/pom.xml
index 672dbcb0ca4..b15e44dc929 100644
--- a/microservices/services/mapreduce-query/service/pom.xml
+++ b/microservices/services/mapreduce-query/service/pom.xml
@@ -4,7 +4,7 @@
gov.nsa.datawave.microservicedatawave-microservice-service-parent
- 5.0.11
+ 5.0.12-3508-RC1../../../microservice-service-parent/pom.xmlmapreduce-query-service
@@ -37,6 +37,11 @@
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ ${version.datawave-commons-security}
+ gov.nsa.datawave.corebase-rest-responses
@@ -186,6 +191,10 @@
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.corebase-rest-responses
diff --git a/microservices/services/mapreduce-query/service/src/main/java/datawave/microservice/query/mapreduce/MapReduceQueryJobRunner.java b/microservices/services/mapreduce-query/service/src/main/java/datawave/microservice/query/mapreduce/MapReduceQueryJobRunner.java
index e84b27b980d..646250853b3 100644
--- a/microservices/services/mapreduce-query/service/src/main/java/datawave/microservice/query/mapreduce/MapReduceQueryJobRunner.java
+++ b/microservices/services/mapreduce-query/service/src/main/java/datawave/microservice/query/mapreduce/MapReduceQueryJobRunner.java
@@ -36,7 +36,7 @@
import datawave.microservice.query.mapreduce.remote.MapReduceQueryRequestHandler;
import datawave.microservice.query.mapreduce.status.MapReduceQueryCache;
import datawave.microservice.query.mapreduce.status.MapReduceQueryStatus;
-import datawave.security.util.ProxiedEntityUtils;
+import datawave.security.util.DnUtils;
import datawave.webservice.query.exception.DatawaveErrorCode;
import datawave.webservice.query.exception.QueryException;
@@ -113,7 +113,7 @@ private void submit(MapReduceQueryStatus mapReduceQueryStatus) {
StringBuilder name = new StringBuilder()
.append(mapReduceQueryStatus.getJobName())
.append("_sid_")
- .append(ProxiedEntityUtils.getShortName(mapReduceQueryStatus.getCurrentUser().getPrimaryUser().getName()))
+ .append(DnUtils.getShortName(mapReduceQueryStatus.getCurrentUser().getPrimaryUser().getName()))
.append("_id_")
.append(mapReduceQueryStatus.getId());
// @formatter:on
diff --git a/microservices/services/query-metric/service/pom.xml b/microservices/services/query-metric/service/pom.xml
index 4623e1a5b89..ad491b85361 100644
--- a/microservices/services/query-metric/service/pom.xml
+++ b/microservices/services/query-metric/service/pom.xml
@@ -4,7 +4,7 @@
gov.nsa.datawave.microservicedatawave-microservice-service-parent
- 5.0.11
+ 5.0.12-3508-RC1../../../microservice-service-parent/pom.xmlquery-metric-service
@@ -31,7 +31,7 @@
4.0.41.0.14.1.3
- 4.0.7
+ 4.0.8-3508-RC13.0.43.0.531.1-jre
@@ -426,6 +426,11 @@
gov.nsa.datawavedatawave-query-core
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ ${version.datawave-commons-security}
+ gov.nsa.datawave.coreaccumulo-utils
diff --git a/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/QueryMetricOperations.java b/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/QueryMetricOperations.java
index 158e3d2e7f7..4bfebdffadd 100644
--- a/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/QueryMetricOperations.java
+++ b/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/QueryMetricOperations.java
@@ -60,9 +60,10 @@
import datawave.microservice.querymetric.handler.QueryGeometryHandler;
import datawave.microservice.querymetric.handler.ShardTableQueryMetricHandler;
import datawave.microservice.querymetric.handler.SimpleQueryGeometryHandler;
-import datawave.microservice.security.util.DnUtils;
import datawave.query.jexl.visitors.JexlFormattedStringBuildingVisitor;
import datawave.security.authorization.DatawaveUser;
+import datawave.security.util.DnProperties;
+import datawave.security.util.DnUtils;
import datawave.webservice.query.exception.DatawaveErrorCode;
import datawave.webservice.query.exception.QueryException;
import datawave.webservice.result.VoidResponse;
@@ -97,7 +98,7 @@ public class QueryMetricOperations {
private static Set inProcess = Collections.synchronizedSet(new HashSet<>());
private final QueryMetricClient queryMetricClient;
- private final DnUtils dnUtils;
+ private final DnProperties dnProperties;
/**
* The enum Default datetime.
@@ -136,14 +137,12 @@ enum DEFAULT_DATETIME {
* the stats
* @param queryMetricClient
* the QueryMetricClient
- * @param dnUtils
- * the dnUtils
*/
@Autowired
public QueryMetricOperations(@Qualifier("queryMetricCacheManager") CacheManager cacheManager, ShardTableQueryMetricHandler handler,
QueryGeometryHandler geometryHandler, MarkingFunctions markingFunctions, QueryMetricResponseFactory queryMetricResponseFactory,
MergeLockLifecycleListener mergeLock, Correlator correlator, MetricUpdateEntryProcessorFactory entryProcessorFactory,
- QueryMetricOperationsStats stats, QueryMetricClient queryMetricClient, DnUtils dnUtils) {
+ QueryMetricOperationsStats stats, QueryMetricClient queryMetricClient, DnProperties dnProperties) {
this.handler = handler;
this.geometryHandler = geometryHandler;
this.cacheManager = cacheManager;
@@ -155,7 +154,7 @@ public QueryMetricOperations(@Qualifier("queryMetricCacheManager") CacheManager
this.entryProcessorFactory = entryProcessorFactory;
this.stats = stats;
this.queryMetricClient = queryMetricClient;
- this.dnUtils = dnUtils;
+ this.dnProperties = dnProperties;
}
@PreDestroy
@@ -462,7 +461,7 @@ private boolean isSameUser(DatawaveUserDetails currentUser, BaseQueryMetric metr
boolean sameUser = false;
if (currentUser != null) {
String metricUser = metric.getUser();
- String requestingUser = dnUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String requestingUser = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
sameUser = metricUser != null && metricUser.equals(requestingUser);
}
return sameUser;
diff --git a/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/config/QueryMetricHandlerConfiguration.java b/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/config/QueryMetricHandlerConfiguration.java
index a11c1ea1619..febb282df9a 100644
--- a/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/config/QueryMetricHandlerConfiguration.java
+++ b/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/config/QueryMetricHandlerConfiguration.java
@@ -48,7 +48,6 @@
import datawave.microservice.querymetric.handler.RemoteShardTableQueryMetricHandler;
import datawave.microservice.querymetric.handler.ShardTableQueryMetricHandler;
import datawave.microservice.querymetric.handler.SimpleQueryGeometryHandler;
-import datawave.microservice.security.util.DnUtils;
import datawave.query.language.builder.jexl.JexlTreeBuilder;
import datawave.query.language.functions.jexl.EvaluationOnly;
import datawave.query.language.functions.jexl.JexlQueryFunction;
@@ -56,6 +55,7 @@
import datawave.query.util.DateIndexHelper;
import datawave.query.util.DateIndexHelperFactory;
import datawave.security.authorization.JWTTokenHandler;
+import datawave.security.util.DnProperties;
import datawave.webservice.query.result.event.ResponseObjectFactory;
@Configuration
@@ -101,14 +101,15 @@ public ShardTableQueryMetricHandler shardTableQueryMetricHandler(QueryMetricHand
AccumuloConnectionFactory connectionFactory, QueryMetricQueryLogicFactory logicFactory, QueryMetricFactory metricFactory,
MarkingFunctions markingFunctions, QueryMetricCombiner queryMetricCombiner, LuceneToJexlQueryParser luceneToJexlQueryParser,
ResponseObjectFactory responseObjectFactory, WebClient.Builder webClientBuilder,
- @Autowired(required = false) JWTTokenHandler jwtTokenHandler, DnUtils dnUtils, QueryMetricResponseFactory queryMetricResponseFactory) {
+ @Autowired(required = false) JWTTokenHandler jwtTokenHandler, DnProperties dnProperties,
+ QueryMetricResponseFactory queryMetricResponseFactory) {
ShardTableQueryMetricHandler handler;
if (queryMetricHandlerProperties.isUseRemoteQuery()) {
handler = new RemoteShardTableQueryMetricHandler(queryMetricHandlerProperties, connectionFactory, logicFactory, metricFactory, markingFunctions,
- queryMetricCombiner, luceneToJexlQueryParser, responseObjectFactory, webClientBuilder, jwtTokenHandler, dnUtils);
+ queryMetricCombiner, luceneToJexlQueryParser, responseObjectFactory, webClientBuilder, jwtTokenHandler, dnProperties);
} else {
handler = new LocalShardTableQueryMetricHandler(queryMetricHandlerProperties, connectionFactory, logicFactory, metricFactory, markingFunctions,
- queryMetricCombiner, luceneToJexlQueryParser, dnUtils);
+ queryMetricCombiner, luceneToJexlQueryParser, dnProperties);
}
handler.setQueryMetricResponseFactory(queryMetricResponseFactory);
return handler;
diff --git a/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/handler/LocalShardTableQueryMetricHandler.java b/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/handler/LocalShardTableQueryMetricHandler.java
index 90b776d72bd..3d702025968 100644
--- a/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/handler/LocalShardTableQueryMetricHandler.java
+++ b/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/handler/LocalShardTableQueryMetricHandler.java
@@ -29,11 +29,11 @@
import datawave.microservice.querymetric.QueryMetricFactory;
import datawave.microservice.querymetric.config.QueryMetricHandlerProperties;
import datawave.microservice.querymetric.factory.QueryMetricQueryLogicFactory;
-import datawave.microservice.security.util.DnUtils;
import datawave.query.language.parser.jexl.LuceneToJexlQueryParser;
import datawave.security.authorization.DatawavePrincipal;
import datawave.security.authorization.DatawaveUser;
import datawave.security.authorization.SubjectIssuerDNPair;
+import datawave.security.util.DnProperties;
import datawave.webservice.query.runner.RunningQuery;
import datawave.webservice.result.BaseQueryResponse;
@@ -49,9 +49,9 @@ public class LocalShardTableQueryMetricHandler extend
public LocalShardTableQueryMetricHandler(QueryMetricHandlerProperties queryMetricHandlerProperties, AccumuloConnectionFactory connectionFactory,
QueryMetricQueryLogicFactory logicFactory, QueryMetricFactory metricFactory, MarkingFunctions markingFunctions,
- QueryMetricCombiner queryMetricCombiner, LuceneToJexlQueryParser luceneToJexlQueryParser, DnUtils dnUtils) {
+ QueryMetricCombiner queryMetricCombiner, LuceneToJexlQueryParser luceneToJexlQueryParser, DnProperties dnProperties) {
super(queryMetricHandlerProperties, connectionFactory, logicFactory, metricFactory, markingFunctions, queryMetricCombiner, luceneToJexlQueryParser,
- dnUtils);
+ dnProperties);
this.datawaveQueryMetricFactory = metricFactory;
diff --git a/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/handler/RemoteShardTableQueryMetricHandler.java b/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/handler/RemoteShardTableQueryMetricHandler.java
index 81eae0285ce..57266b9975e 100644
--- a/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/handler/RemoteShardTableQueryMetricHandler.java
+++ b/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/handler/RemoteShardTableQueryMetricHandler.java
@@ -22,10 +22,10 @@
import datawave.microservice.querymetric.QueryMetricFactory;
import datawave.microservice.querymetric.config.QueryMetricHandlerProperties;
import datawave.microservice.querymetric.factory.QueryMetricQueryLogicFactory;
-import datawave.microservice.security.util.DnUtils;
import datawave.query.language.parser.jexl.LuceneToJexlQueryParser;
import datawave.security.authorization.DatawaveUser;
import datawave.security.authorization.JWTTokenHandler;
+import datawave.security.util.DnProperties;
import datawave.webservice.query.result.event.ResponseObjectFactory;
import datawave.webservice.result.BaseQueryResponse;
@@ -41,9 +41,9 @@ public class RemoteShardTableQueryMetricHandler exten
public RemoteShardTableQueryMetricHandler(QueryMetricHandlerProperties queryMetricHandlerProperties, AccumuloConnectionFactory connectionFactory,
QueryMetricQueryLogicFactory logicFactory, QueryMetricFactory metricFactory, MarkingFunctions markingFunctions,
QueryMetricCombiner queryMetricCombiner, LuceneToJexlQueryParser luceneToJexlQueryParser, ResponseObjectFactory responseObjectFactory,
- WebClient.Builder webClientBuilder, JWTTokenHandler jwtTokenHandler, DnUtils dnUtils) {
+ WebClient.Builder webClientBuilder, JWTTokenHandler jwtTokenHandler, DnProperties dnProperties) {
super(queryMetricHandlerProperties, connectionFactory, logicFactory, metricFactory, markingFunctions, queryMetricCombiner, luceneToJexlQueryParser,
- dnUtils);
+ dnProperties);
this.responseObjectFactory = responseObjectFactory;
diff --git a/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/handler/ShardTableQueryMetricHandler.java b/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/handler/ShardTableQueryMetricHandler.java
index 351ba82ad4b..9c48d931955 100644
--- a/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/handler/ShardTableQueryMetricHandler.java
+++ b/microservices/services/query-metric/service/src/main/java/datawave/microservice/querymetric/handler/ShardTableQueryMetricHandler.java
@@ -75,10 +75,11 @@
import datawave.microservice.querymetric.QueryMetricsSummaryResponse;
import datawave.microservice.querymetric.config.QueryMetricHandlerProperties;
import datawave.microservice.querymetric.factory.QueryMetricQueryLogicFactory;
-import datawave.microservice.security.util.DnUtils;
import datawave.query.QueryParameters;
import datawave.query.language.parser.jexl.LuceneToJexlQueryParser;
import datawave.security.authorization.DatawaveUser;
+import datawave.security.util.DnProperties;
+import datawave.security.util.DnUtils;
import datawave.security.util.WSAuthorizationsUtil;
import datawave.webservice.query.exception.QueryExceptionType;
import datawave.webservice.query.result.event.EventBase;
@@ -107,19 +108,19 @@ public abstract class ShardTableQueryMetricHandler ex
protected UIDBuilder uidBuilder = UID.builder();
protected QueryMetricCombiner queryMetricCombiner;
protected MarkingFunctions markingFunctions;
- protected DnUtils dnUtils;
+ protected DnProperties dnProperties;
// this lock is necessary for when there is an error condition and the accumuloRecordWriter needs to be replaced
protected ReentrantReadWriteLock accumuloRecordWriterLock = new ReentrantReadWriteLock();
public ShardTableQueryMetricHandler(QueryMetricHandlerProperties queryMetricHandlerProperties, AccumuloConnectionFactory connectionFactory,
QueryMetricQueryLogicFactory logicFactory, QueryMetricFactory metricFactory, MarkingFunctions markingFunctions,
- QueryMetricCombiner queryMetricCombiner, LuceneToJexlQueryParser luceneToJexlQueryParser, DnUtils dnUtils) {
+ QueryMetricCombiner queryMetricCombiner, LuceneToJexlQueryParser luceneToJexlQueryParser, DnProperties dnProperties) {
super(luceneToJexlQueryParser);
this.queryMetricHandlerProperties = queryMetricHandlerProperties;
this.logicFactory = logicFactory;
this.metricFactory = metricFactory;
this.markingFunctions = markingFunctions;
- this.dnUtils = dnUtils;
+ this.dnProperties = dnProperties;
this.connectionFactory = connectionFactory;
this.queryMetricCombiner = queryMetricCombiner;
@@ -890,7 +891,7 @@ public QueryMetricsSummaryResponse getQueryMetricsSummary(Date begin, Date end,
try {
// this method is open to any user
DatawaveUser datawaveUser = currentUser.getPrimaryUser();
- String datawaveUserShortName = dnUtils.getShortName(datawaveUser.getName());
+ String datawaveUserShortName = DnUtils.getShortName(datawaveUser.getName());
Collection userAuths = new ArrayList<>(datawaveUser.getAuths());
if (clientAuthorizations != null) {
Collection connectorAuths = new ArrayList<>();
diff --git a/microservices/services/query-metric/service/src/test/java/datawave/microservice/querymetric/QueryMetricTestBase.java b/microservices/services/query-metric/service/src/test/java/datawave/microservice/querymetric/QueryMetricTestBase.java
index a61f8dd3cf8..d3fe995f24a 100644
--- a/microservices/services/query-metric/service/src/test/java/datawave/microservice/querymetric/QueryMetricTestBase.java
+++ b/microservices/services/query-metric/service/src/test/java/datawave/microservice/querymetric/QueryMetricTestBase.java
@@ -73,10 +73,11 @@
import datawave.microservice.querymetric.function.QueryMetricSupplier;
import datawave.microservice.querymetric.handler.QueryMetricCombiner;
import datawave.microservice.querymetric.handler.ShardTableQueryMetricHandler;
-import datawave.microservice.security.util.DnUtils;
import datawave.security.authorization.DatawaveUser;
import datawave.security.authorization.JWTTokenHandler;
import datawave.security.authorization.SubjectIssuerDNPair;
+import datawave.security.util.DnProperties;
+import datawave.security.util.DnUtils;
import datawave.webservice.query.result.event.DefaultEvent;
import datawave.webservice.query.result.event.DefaultField;
import datawave.webservice.query.result.event.EventBase;
@@ -128,7 +129,7 @@ public class QueryMetricTestBase {
private QueryMetricClientProperties queryMetricClientProperties;
@Autowired
- private DnUtils dnUtils;
+ private DnProperties dnProperties;
@Autowired
@Qualifier("lastWrittenQueryMetrics")
diff --git a/microservices/services/query-metric/service/src/test/java/datawave/microservice/querymetric/config/AlternateQueryMetricConfiguration.java b/microservices/services/query-metric/service/src/test/java/datawave/microservice/querymetric/config/AlternateQueryMetricConfiguration.java
index 3695bf7602e..06e67a10f41 100644
--- a/microservices/services/query-metric/service/src/test/java/datawave/microservice/querymetric/config/AlternateQueryMetricConfiguration.java
+++ b/microservices/services/query-metric/service/src/test/java/datawave/microservice/querymetric/config/AlternateQueryMetricConfiguration.java
@@ -16,8 +16,8 @@
import datawave.microservice.querymetric.factory.QueryMetricQueryLogicFactory;
import datawave.microservice.querymetric.handler.QueryMetricCombiner;
import datawave.microservice.querymetric.handler.ShardTableQueryMetricHandler;
-import datawave.microservice.security.util.DnUtils;
import datawave.query.language.parser.jexl.LuceneToJexlQueryParser;
+import datawave.security.util.DnProperties;
@ImportAutoConfiguration({RefreshAutoConfiguration.class})
@AutoConfigureCache(cacheProvider = CacheType.HAZELCAST)
@@ -49,8 +49,8 @@ public BaseQueryMetric createMetric(boolean populateVersionMap) {
public ShardTableQueryMetricHandler shardTableQueryMetricHandler(QueryMetricHandlerProperties queryMetricHandlerProperties,
AccumuloConnectionFactory connectionFactory, QueryMetricQueryLogicFactory logicFactory, QueryMetricFactory metricFactory,
MarkingFunctions markingFunctions, QueryMetricCombiner queryMetricCombiner, LuceneToJexlQueryParser luceneToJexlQueryParser,
- DnUtils dnUtils) {
+ DnProperties dnProperties) {
return new AlternateShardTableQueryMetricHandler(queryMetricHandlerProperties, connectionFactory, logicFactory, metricFactory, markingFunctions,
- queryMetricCombiner, luceneToJexlQueryParser, dnUtils);
+ queryMetricCombiner, luceneToJexlQueryParser, dnProperties);
}
}
diff --git a/microservices/services/query-metric/service/src/test/java/datawave/microservice/querymetric/config/AlternateShardTableQueryMetricHandler.java b/microservices/services/query-metric/service/src/test/java/datawave/microservice/querymetric/config/AlternateShardTableQueryMetricHandler.java
index ff985dc0bd8..28549bfaaad 100644
--- a/microservices/services/query-metric/service/src/test/java/datawave/microservice/querymetric/config/AlternateShardTableQueryMetricHandler.java
+++ b/microservices/services/query-metric/service/src/test/java/datawave/microservice/querymetric/config/AlternateShardTableQueryMetricHandler.java
@@ -10,8 +10,8 @@
import datawave.microservice.querymetric.handler.ContentQueryMetricsIngestHelper;
import datawave.microservice.querymetric.handler.LocalShardTableQueryMetricHandler;
import datawave.microservice.querymetric.handler.QueryMetricCombiner;
-import datawave.microservice.security.util.DnUtils;
import datawave.query.language.parser.jexl.LuceneToJexlQueryParser;
+import datawave.security.util.DnProperties;
import datawave.webservice.query.result.event.EventBase;
import datawave.webservice.query.result.event.FieldBase;
@@ -19,9 +19,9 @@ public class AlternateShardTableQueryMetricHandler extends LocalShardTableQueryM
public AlternateShardTableQueryMetricHandler(QueryMetricHandlerProperties queryMetricHandlerProperties, AccumuloConnectionFactory connectionFactory,
QueryMetricQueryLogicFactory logicFactory, QueryMetricFactory metricFactory, MarkingFunctions markingFunctions,
- QueryMetricCombiner queryMetricCombiner, LuceneToJexlQueryParser luceneToJexlQueryParser, DnUtils dnUtils) {
+ QueryMetricCombiner queryMetricCombiner, LuceneToJexlQueryParser luceneToJexlQueryParser, DnProperties dnProperties) {
super(queryMetricHandlerProperties, connectionFactory, logicFactory, metricFactory, markingFunctions, queryMetricCombiner, luceneToJexlQueryParser,
- dnUtils);
+ dnProperties);
}
@Override
diff --git a/microservices/services/query/pom.xml b/microservices/services/query/pom.xml
index 7445827eff0..65a0c090d75 100644
--- a/microservices/services/query/pom.xml
+++ b/microservices/services/query/pom.xml
@@ -4,7 +4,7 @@
gov.nsa.datawave.microservicedatawave-microservice-parent
- 4.0.10
+ 4.0.11-3508-RC1../../microservice-parent/pom.xmlquery-service-parent
diff --git a/microservices/services/query/service/pom.xml b/microservices/services/query/service/pom.xml
index 4ee665c5a92..8cc5b198ff1 100644
--- a/microservices/services/query/service/pom.xml
+++ b/microservices/services/query/service/pom.xml
@@ -4,7 +4,7 @@
gov.nsa.datawave.microservicedatawave-microservice-service-parent
- 5.0.11
+ 5.0.12-3508-RC1../../../microservice-service-parent/pom.xmlquery-service
@@ -367,6 +367,10 @@
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.corebase-rest-responses
diff --git a/microservices/services/query/service/src/main/java/datawave/microservice/query/QueryManagementService.java b/microservices/services/query/service/src/main/java/datawave/microservice/query/QueryManagementService.java
index a16e1816bdd..a7808195216 100644
--- a/microservices/services/query/service/src/main/java/datawave/microservice/query/QueryManagementService.java
+++ b/microservices/services/query/service/src/main/java/datawave/microservice/query/QueryManagementService.java
@@ -75,7 +75,7 @@
import datawave.microservice.querymetric.BaseQueryMetric;
import datawave.microservice.querymetric.QueryMetricClient;
import datawave.microservice.querymetric.QueryMetricType;
-import datawave.security.util.ProxiedEntityUtils;
+import datawave.security.util.DnUtils;
import datawave.webservice.common.audit.AuditParameters;
import datawave.webservice.common.audit.Auditor;
import datawave.webservice.query.exception.BadRequestQueryException;
@@ -166,7 +166,7 @@ public QueryManagementService(QueryProperties queryProperties, ApplicationEventP
* @return the query logic descriptions
*/
public QueryLogicResponse listQueryLogic(DatawaveUserDetails currentUser) {
- log.info("Request: listQueryLogic from {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()));
+ log.info("Request: listQueryLogic from {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()));
QueryLogicResponse response = new QueryLogicResponse();
List> queryLogicList = queryLogicFactory.getQueryLogicList();
@@ -273,7 +273,7 @@ public QueryLogicResponse listQueryLogic(DatawaveUserDetails currentUser) {
*/
public GenericResponse define(String queryLogicName, MultiValueMap parameters, String pool, DatawaveUserDetails currentUser)
throws QueryException {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: {}/define from {} with params: {}", queryLogicName, user, parameters);
} else {
@@ -331,7 +331,7 @@ public GenericResponse define(String queryLogicName, MultiValueMap create(String queryLogicName, MultiValueMap parameters, String pool, DatawaveUserDetails currentUser)
throws QueryException {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: {}/create from {} with params: {}", queryLogicName, user, parameters);
} else {
@@ -386,7 +386,7 @@ public GenericResponse create(String queryLogicName, MultiValueMap plan(String queryLogicName, MultiValueMap parameters, String pool, DatawaveUserDetails currentUser)
throws QueryException {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: {}/plan from {} with params: {}", queryLogicName, user, parameters);
} else {
@@ -443,7 +443,7 @@ public GenericResponse plan(String queryLogicName, MultiValueMap predict(String queryLogicName, MultiValueMap parameters, String pool, DatawaveUserDetails currentUser)
throws QueryException {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: {}/predict from {} with params: {}", queryLogicName, user, parameters);
} else {
@@ -520,7 +520,7 @@ private TaskKey storeQuery(String queryLogicName, MultiValueMap p
// validate query and get a query logic
QueryLogic> queryLogic = validateQuery(queryLogicName, parameters, currentUser);
- String userId = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String userId = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
log.trace("{} has authorizations {}", userId, currentUser.getPrimaryUser().getAuths());
Query query = createQuery(queryLogicName, parameters, currentUser, queryId);
@@ -744,7 +744,7 @@ private void sendRequestAwaitResponse(QueryRequest request, String computedPool,
*/
public BaseQueryResponse createAndNext(String queryLogicName, MultiValueMap parameters, String pool, DatawaveUserDetails currentUser)
throws QueryException {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: {}/createAndNext from {} with params: {}", queryLogicName, user, parameters);
} else {
@@ -808,7 +808,7 @@ public BaseQueryResponse createAndNext(String queryLogicName, MultiValueMap queryStatuses = queryStorageCache.getQueryStatus();
@@ -1205,7 +1205,7 @@ public void cancel(String queryId, boolean publishEvent) throws InterruptedExcep
* if there is an unknown error
*/
public VoidResponse close(String queryId, DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: close from {} for {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId);
+ log.info("Request: close from {} for {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId);
return close(queryId, currentUser, false);
}
@@ -1234,7 +1234,7 @@ public VoidResponse close(String queryId, DatawaveUserDetails currentUser) throw
* if there is an unknown error
*/
public VoidResponse adminClose(String queryId, DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: adminClose from {} for {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId);
+ log.info("Request: adminClose from {} for {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId);
return close(queryId, currentUser, true);
}
@@ -1260,7 +1260,7 @@ public VoidResponse adminClose(String queryId, DatawaveUserDetails currentUser)
* if there is an unknown error
*/
public VoidResponse adminCloseAll(DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: adminCloseAll from {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()));
+ log.info("Request: adminCloseAll from {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()));
try {
List queryStatuses = queryStorageCache.getQueryStatus();
@@ -1423,7 +1423,7 @@ public void close(String queryId) throws InterruptedException, QueryException {
* if there is an unknown error
*/
public GenericResponse reset(String queryId, DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: reset from {} for {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId);
+ log.info("Request: reset from {} for {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId);
try {
// make sure the query is valid, and the user can act on it
@@ -1471,7 +1471,7 @@ public GenericResponse reset(String queryId, DatawaveUserDetails current
* if there is an unknown error
*/
public VoidResponse remove(String queryId, DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: remove from {} for {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId);
+ log.info("Request: remove from {} for {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId);
return remove(queryId, currentUser, false);
}
@@ -1495,7 +1495,7 @@ public VoidResponse remove(String queryId, DatawaveUserDetails currentUser) thro
* if there is an unknown error
*/
public VoidResponse adminRemove(String queryId, DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: adminRemove from {} for {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId);
+ log.info("Request: adminRemove from {} for {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId);
return remove(queryId, currentUser, true);
}
@@ -1516,7 +1516,7 @@ public VoidResponse adminRemove(String queryId, DatawaveUserDetails currentUser)
* if there is an unknown error
*/
public VoidResponse adminRemoveAll(DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: adminRemoveAll from {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()));
+ log.info("Request: adminRemoveAll from {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()));
try {
List queryStatuses = queryStorageCache.getQueryStatus();
@@ -1633,7 +1633,7 @@ private boolean remove(QueryStatus queryStatus) throws IOException {
* if there is an unknown error
*/
public GenericResponse update(String queryId, MultiValueMap parameters, DatawaveUserDetails currentUser) throws QueryException {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: {}/update from {} with params: {}", queryId, user, parameters);
} else {
@@ -1751,7 +1751,7 @@ public GenericResponse update(String queryId, MultiValueMap duplicate(String queryId, MultiValueMap parameters, DatawaveUserDetails currentUser) throws QueryException {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: {}/duplicate from {} with params: {}", queryId, user, parameters);
} else {
@@ -1879,10 +1879,9 @@ private boolean updateParameters(Collection parameterNames, MultiValueMa
* if there is an unknown error
*/
public QueryImplListResponse list(String queryId, String queryName, DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: list from {} for queryId: {}, queryName: {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId,
- queryName);
+ log.info("Request: list from {} for queryId: {}, queryName: {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId, queryName);
- return list(queryId, queryName, ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getDn().subjectDN()));
+ return list(queryId, queryName, DnUtils.getShortName(currentUser.getPrimaryUser().getDn().subjectDN()));
}
/**
@@ -1904,8 +1903,8 @@ public QueryImplListResponse list(String queryId, String queryName, DatawaveUser
* if there is an unknown error
*/
public QueryImplListResponse adminList(String queryId, String queryName, String userId, DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: adminList from {} for queryId: {}, queryName: {}, userId: {}",
- ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId, queryName, userId);
+ log.info("Request: adminList from {} for queryId: {}, queryName: {}, userId: {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId,
+ queryName, userId);
return list(queryId, queryName, userId);
}
@@ -1971,7 +1970,7 @@ private QueryImplListResponse list(String queryId, String queryName, String user
* if there is an unknown error
*/
public GenericResponse plan(String queryId, DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: plan from {} for queryId: {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId);
+ log.info("Request: plan from {} for queryId: {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId);
try {
// make sure the query is valid, and the user can act on it
@@ -2011,7 +2010,7 @@ public GenericResponse plan(String queryId, DatawaveUserDetails currentU
* if there is an unknown error
*/
public GenericResponse predictions(String queryId, DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: predictions from {} for queryId: {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId);
+ log.info("Request: predictions from {} for queryId: {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), queryId);
try {
// make sure the query is valid, and the user can act on it
@@ -2061,7 +2060,7 @@ public QueryStatus validateRequest(String queryId, DatawaveUserDetails currentUs
// admin requests can operate on any query, regardless of ownership
if (!adminOverride) {
// does the current user own this query?
- String userId = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getDn().subjectDN());
+ String userId = DnUtils.getShortName(currentUser.getPrimaryUser().getDn().subjectDN());
Query query = queryStatus.getQuery();
if (!query.getOwner().equals(userId)) {
throw new UnauthorizedQueryException(DatawaveErrorCode.QUERY_OWNER_MISMATCH, MessageFormat.format("{0} != {1}", userId, query.getOwner()));
diff --git a/microservices/services/query/service/src/main/java/datawave/microservice/query/lookup/LookupService.java b/microservices/services/query/service/src/main/java/datawave/microservice/query/lookup/LookupService.java
index 8fdb8c3eb38..a15e0579b81 100644
--- a/microservices/services/query/service/src/main/java/datawave/microservice/query/lookup/LookupService.java
+++ b/microservices/services/query/service/src/main/java/datawave/microservice/query/lookup/LookupService.java
@@ -48,7 +48,7 @@
import datawave.query.data.UUIDType;
import datawave.security.authorization.AuthorizationException;
import datawave.security.authorization.ProxiedUserDetails;
-import datawave.security.util.ProxiedEntityUtils;
+import datawave.security.util.DnUtils;
import datawave.webservice.query.exception.BadRequestQueryException;
import datawave.webservice.query.exception.DatawaveErrorCode;
import datawave.webservice.query.exception.NoResultsQueryException;
@@ -134,7 +134,7 @@ public LookupService(LookupProperties lookupProperties, QueryLogicFactory queryL
*/
public void lookupUUID(MultiValueMap parameters, String pool, DatawaveUserDetails currentUser, StreamingResponseListener listener)
throws QueryException {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: lookupUUID from {} with params: {}", user, parameters);
} else {
@@ -188,7 +188,7 @@ public void lookupUUID(MultiValueMap parameters, String pool, Dat
* if there is an unknown error
*/
public BaseQueryResponse lookupUUID(MultiValueMap parameters, String pool, DatawaveUserDetails currentUser) throws QueryException {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: lookupUUID from {} with params: {}", user, parameters);
} else {
@@ -245,7 +245,7 @@ public BaseQueryResponse lookupUUID(MultiValueMap parameters, Str
*/
public T lookupContentUUID(MultiValueMap parameters, String pool, DatawaveUserDetails currentUser, StreamingResponseListener listener)
throws QueryException {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: lookupContentUUID from {} with params: {}", user, parameters);
} else {
@@ -300,7 +300,7 @@ public T lookupContentUUID(MultiValueMap parameters, String p
* if there is an unknown error
*/
public BaseQueryResponse lookupContentUUID(MultiValueMap parameters, String pool, DatawaveUserDetails currentUser) throws QueryException {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: lookupContentUUID from {} with params: {}", user, parameters);
} else {
@@ -533,7 +533,7 @@ public String getAuths(MultiValueMap queryParameters, QueryLogic<
protected void setupEventQueryParameters(MultiValueMap parameters, LookupQueryLogic> queryLogic, DatawaveUserDetails currentUser)
throws AuthorizationException {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
final String queryName = user + "-" + UUID.randomUUID().toString();
final String endDate;
@@ -664,7 +664,7 @@ private List createContentQueries(Set contentLookupTerms) {
}
protected void setContentQueryParameters(MultiValueMap parameters, DatawaveUserDetails currentUser) {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
setOptionalQueryParameters(parameters);
diff --git a/microservices/services/query/service/src/main/java/datawave/microservice/query/mapreduce/MapReduceQueryManagementService.java b/microservices/services/query/service/src/main/java/datawave/microservice/query/mapreduce/MapReduceQueryManagementService.java
index 92e709476b4..6484314a55f 100644
--- a/microservices/services/query/service/src/main/java/datawave/microservice/query/mapreduce/MapReduceQueryManagementService.java
+++ b/microservices/services/query/service/src/main/java/datawave/microservice/query/mapreduce/MapReduceQueryManagementService.java
@@ -65,7 +65,7 @@
import datawave.microservice.query.mapreduce.status.MapReduceQueryCache;
import datawave.microservice.query.mapreduce.status.MapReduceQueryStatus;
import datawave.microservice.query.storage.QueryStatus;
-import datawave.security.util.ProxiedEntityUtils;
+import datawave.security.util.DnUtils;
import datawave.webservice.common.audit.AuditParameters;
import datawave.webservice.query.exception.BadRequestQueryException;
import datawave.webservice.query.exception.DatawaveErrorCode;
@@ -125,7 +125,7 @@ public MapReduceQueryManagementService(QueryProperties queryProperties, MapReduc
}
public MapReduceJobDescriptionList listConfigurations(String jobType, DatawaveUserDetails currentUser) {
- log.info("Request: listConfigurations from {} for {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), jobType);
+ log.info("Request: listConfigurations from {} for {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), jobType);
MapReduceJobDescriptionList response = new MapReduceJobDescriptionList();
List jobs = new ArrayList<>();
@@ -156,7 +156,7 @@ protected MapReduceJobDescription createMapReduceJobDescription(String name, Map
public GenericResponse oozieSubmit(MultiValueMap parameters, DatawaveUserDetails currentUser) throws QueryException {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: submit from {} with params: {}", user, parameters);
} else {
@@ -181,7 +181,7 @@ public GenericResponse oozieSubmit(MultiValueMap paramete
}
public GenericResponse submit(MultiValueMap parameters, DatawaveUserDetails currentUser) throws QueryException {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: submit from {} with params: {}", user, parameters);
} else {
@@ -410,13 +410,13 @@ public void handleRemoteRequest(MapReduceQueryRequest queryRequest, String origi
}
public GenericResponse cancel(String id, DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: cancel from {} for {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), id);
+ log.info("Request: cancel from {} for {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), id);
return cancel(id, currentUser, false);
}
public GenericResponse adminCancel(String id, DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: adminCancel from {} for {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), id);
+ log.info("Request: adminCancel from {} for {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), id);
return cancel(id, currentUser, true);
}
@@ -477,7 +477,7 @@ private void removeDirectory(String directory) throws IOException, QueryExceptio
}
public GenericResponse restart(String id, DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: restart from {} for {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), id);
+ log.info("Request: restart from {} for {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), id);
try {
// make sure the map reduce query is valid, and the user can act on it
@@ -506,7 +506,7 @@ public GenericResponse restart(String id, DatawaveUserDetails currentUse
}
public MapReduceInfoResponseList list(String id, DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: list from {} for {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), id);
+ log.info("Request: list from {} for {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), id);
try {
// make sure the query is valid, and the user can act on it
@@ -540,7 +540,7 @@ protected MapReduceInfoResponse createMapReduceInfoResponse(MapReduceQueryStatus
}
public MapReduceInfoResponseList list(DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: list for {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()));
+ log.info("Request: list for {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()));
try {
Set ids = mapReduceQueryCache.lookupQueryIdsByUsername(currentUser.getUsername());
@@ -578,7 +578,7 @@ public MapReduceQueryStatus validateRequest(String id, DatawaveUserDetails curre
// admin requests can operate on any job, regardless of ownership
if (!adminOverride) {
// does the current user own this job?
- String userId = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getDn().subjectDN());
+ String userId = DnUtils.getShortName(currentUser.getPrimaryUser().getDn().subjectDN());
Query query = mapReduceQueryStatus.getQuery();
if (!query.getOwner().equals(userId)) {
throw new UnauthorizedQueryException(DatawaveErrorCode.QUERY_OWNER_MISMATCH, MessageFormat.format("{0} != {1}", userId, query.getOwner()));
@@ -589,7 +589,7 @@ public MapReduceQueryStatus validateRequest(String id, DatawaveUserDetails curre
}
public Map.Entry getFile(String id, String fileName, DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: getFile from {} for {}, {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), id, fileName);
+ log.info("Request: getFile from {} for {}, {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), id, fileName);
try {
// make sure the query is valid, and the user can act on it
@@ -632,7 +632,7 @@ private FSDataInputStream getFileInputStream(Path filePath) throws QueryExceptio
}
public Map getAllFiles(String id, DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: getAllFiles from {} for {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), id);
+ log.info("Request: getAllFiles from {} for {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), id);
Map resultFiles = new HashMap<>();
try {
@@ -686,13 +686,13 @@ private Path getRelativeFilePath(FileStatus basePath, FileStatus filePath) {
}
public VoidResponse remove(String id, DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: remove from {} for {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), id);
+ log.info("Request: remove from {} for {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), id);
return remove(id, currentUser, false);
}
public VoidResponse adminRemove(String id, DatawaveUserDetails currentUser) throws QueryException {
- log.info("Request: adminRemove from {} for {}", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), id);
+ log.info("Request: adminRemove from {} for {}", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), id);
return remove(id, currentUser, true);
}
diff --git a/microservices/services/query/service/src/main/java/datawave/microservice/query/stream/StreamingService.java b/microservices/services/query/service/src/main/java/datawave/microservice/query/stream/StreamingService.java
index fb8e6d80c2f..00c611817cc 100644
--- a/microservices/services/query/service/src/main/java/datawave/microservice/query/stream/StreamingService.java
+++ b/microservices/services/query/service/src/main/java/datawave/microservice/query/stream/StreamingService.java
@@ -11,7 +11,7 @@
import datawave.microservice.query.stream.listener.StreamingResponseListener;
import datawave.microservice.query.stream.runner.StreamingCall;
import datawave.microservice.querymetric.QueryMetricClient;
-import datawave.security.util.ProxiedEntityUtils;
+import datawave.security.util.DnUtils;
import datawave.webservice.query.exception.BadRequestQueryException;
import datawave.webservice.query.exception.NoResultsQueryException;
import datawave.webservice.query.exception.QueryException;
@@ -72,7 +72,7 @@ public StreamingService(QueryManagementService queryManagementService, QueryMetr
*/
public String createAndExecute(String queryLogicName, MultiValueMap parameters, String pool, DatawaveUserDetails currentUser,
DatawaveUserDetails serverUser, StreamingResponseListener listener) throws QueryException {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: {}/createAndExecute from {} with params: {}", queryLogicName, user, parameters);
} else {
@@ -101,7 +101,7 @@ public String createAndExecute(String queryLogicName, MultiValueMap setupQueryParameters(MultiValueMapgov.nsa.datawave.microservicedatawave-microservice-service-parent
- 5.0.11
+ 5.0.12-3508-RC1../../microservice-service-parent/pom.xmlspring-boot-starter-datawave-cached-results
@@ -43,6 +43,11 @@
mysql-connector-j${version.mysql-connector}
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ ${version.datawave-commons-security}
+ gov.nsa.datawave.coreaccumulo-utils
@@ -169,6 +174,10 @@
com.mysqlmysql-connector-j
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.coreaccumulo-utils
diff --git a/microservices/starters/cached-results/src/main/java/datawave/microservice/query/cachedresults/CachedResultsQueryService.java b/microservices/starters/cached-results/src/main/java/datawave/microservice/query/cachedresults/CachedResultsQueryService.java
index cb294c76221..a2338d59123 100644
--- a/microservices/starters/cached-results/src/main/java/datawave/microservice/query/cachedresults/CachedResultsQueryService.java
+++ b/microservices/starters/cached-results/src/main/java/datawave/microservice/query/cachedresults/CachedResultsQueryService.java
@@ -61,7 +61,7 @@
import datawave.microservice.query.storage.QueryStatus;
import datawave.microservice.query.storage.QueryStorageCache;
import datawave.security.authorization.ProxiedUserDetails;
-import datawave.security.util.ProxiedEntityUtils;
+import datawave.security.util.DnUtils;
import datawave.webservice.common.audit.AuditParameters;
import datawave.webservice.common.audit.Auditor;
import datawave.webservice.query.cachedresults.CacheableQueryRow;
@@ -171,7 +171,7 @@ private void initializeTableTemplate() {
* if the operation fails
*/
public GenericResponse load(String definedQueryId, String alias, ProxiedUserDetails currentUser) throws QueryException {
- log.info("Request: {}/load from {} with alias: {}", definedQueryId, ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), alias);
+ log.info("Request: {}/load from {} with alias: {}", definedQueryId, DnUtils.getShortName(currentUser.getPrimaryUser().getName()), alias);
CachedResultsQueryStatus cachedResultsQueryStatus = null;
try {
@@ -558,7 +558,7 @@ private String getViewName(String newQueryId) {
}
public CachedResultsResponse create(String key, MultiValueMap parameters, ProxiedUserDetails currentUser) throws QueryException {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: {}/create from {} with params: {}", key, user, parameters);
} else {
@@ -679,7 +679,7 @@ private CachedResultsResponse create(String definedQueryId, MultiValueMap parameters, ProxiedUserDetails currentUser)
throws QueryException {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: {}/loadAndCreate from {} with params: {}", definedQueryId, user, parameters);
} else {
@@ -1076,7 +1076,7 @@ private boolean isFunction(String field) {
public BaseQueryResponse getRows(String key, Integer rowBegin, Integer rowEnd, ProxiedUserDetails currentUser) throws QueryException {
try {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: {}/getRows from {} with rowBegin: {} rowEnd: {}", key, user, rowBegin, rowEnd);
} else {
@@ -1187,7 +1187,7 @@ private String getSqlQuery(String sqlQuery, int beginRow, int endRow) {
}
public GenericResponse status(String key, ProxiedUserDetails currentUser) throws QueryException {
- log.info("Request: {}/status from {}", key, ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()));
+ log.info("Request: {}/status from {}", key, DnUtils.getShortName(currentUser.getPrimaryUser().getName()));
CachedResultsQueryStatus cachedResultsQueryStatus = validateRequest(key, currentUser);
@@ -1197,7 +1197,7 @@ public GenericResponse status(String key, ProxiedUserDetails currentUser
}
public CachedResultsDescribeResponse describe(String key, ProxiedUserDetails currentUser) throws QueryException {
- log.info("Request: {}/describe from {}", key, ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()));
+ log.info("Request: {}/describe from {}", key, DnUtils.getShortName(currentUser.getPrimaryUser().getName()));
CachedResultsQueryStatus cachedResultsQueryStatus = validateRequest(key, currentUser);
@@ -1209,12 +1209,12 @@ public CachedResultsDescribeResponse describe(String key, ProxiedUserDetails cur
}
public VoidResponse cancel(String key, ProxiedUserDetails currentUser) throws QueryException {
- log.info("Request: {}/cancel from {}", key, ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()));
+ log.info("Request: {}/cancel from {}", key, DnUtils.getShortName(currentUser.getPrimaryUser().getName()));
return cancel(key, currentUser, false);
}
public VoidResponse adminCancel(String key, ProxiedUserDetails currentUser) throws QueryException {
- log.info("Request: {}/adminCancel from {}", key, ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()));
+ log.info("Request: {}/adminCancel from {}", key, DnUtils.getShortName(currentUser.getPrimaryUser().getName()));
return cancel(key, currentUser, true);
}
@@ -1241,12 +1241,12 @@ private VoidResponse cancel(String key, ProxiedUserDetails currentUser, boolean
}
public VoidResponse close(String key, ProxiedUserDetails currentUser) throws QueryException {
- log.info("Request: {}/close from {}", key, ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()));
+ log.info("Request: {}/close from {}", key, DnUtils.getShortName(currentUser.getPrimaryUser().getName()));
return close(key, currentUser, false);
}
public VoidResponse adminClose(String key, ProxiedUserDetails currentUser) throws QueryException {
- log.info("Request: {}/adminClose from {}", key, ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()));
+ log.info("Request: {}/adminClose from {}", key, DnUtils.getShortName(currentUser.getPrimaryUser().getName()));
return close(key, currentUser, true);
}
@@ -1286,7 +1286,7 @@ private VoidResponse close(String key, ProxiedUserDetails currentUser, boolean a
public CachedResultsResponse setAlias(String key, String alias, ProxiedUserDetails currentUser) throws QueryException {
try {
- log.info("Request: {}/setAlias from {} with alias {}", key, ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), alias);
+ log.info("Request: {}/setAlias from {} with alias {}", key, DnUtils.getShortName(currentUser.getPrimaryUser().getName()), alias);
CachedResultsQueryStatus cachedResultsQueryStatus = validateRequest(key, currentUser);
@@ -1317,7 +1317,7 @@ public CachedResultsResponse setAlias(String key, String alias, ProxiedUserDetai
public CachedResultsResponse update(String key, String fields, String conditions, String grouping, String order, Integer pagesize,
ProxiedUserDetails currentUser) throws QueryException {
try {
- String user = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName());
+ String user = DnUtils.getShortName(currentUser.getPrimaryUser().getName());
if (log.isDebugEnabled()) {
log.info("Request: {}/udpate from {} with fields: {}, contitions: {}, groupind: {}, order: {}, pagesize: {}", key, user, fields, conditions,
grouping, order, pagesize);
@@ -1421,8 +1421,8 @@ private CachedResultsQueryStatus validateRequest(String key, ProxiedUserDetails
// admin requests can operate on any query, regardless of ownership
if (!adminOverride) {
// does the current user own this query?
- String currentUserId = ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getDn().subjectDN());
- String ownerUserId = ProxiedEntityUtils.getShortName(cachedResultsQueryStatus.getCurrentUser().getPrimaryUser().getDn().subjectDN());
+ String currentUserId = DnUtils.getShortName(currentUser.getPrimaryUser().getDn().subjectDN());
+ String ownerUserId = DnUtils.getShortName(cachedResultsQueryStatus.getCurrentUser().getPrimaryUser().getDn().subjectDN());
if (!ownerUserId.equals(currentUserId)) {
throw new UnauthorizedQueryException(DatawaveErrorCode.QUERY_OWNER_MISMATCH, MessageFormat.format("{0} != {1}", currentUserId, ownerUserId));
}
diff --git a/microservices/starters/datawave/pom.xml b/microservices/starters/datawave/pom.xml
index 2dc04cc69e8..4f1c5546ff9 100644
--- a/microservices/starters/datawave/pom.xml
+++ b/microservices/starters/datawave/pom.xml
@@ -4,7 +4,7 @@
gov.nsa.datawave.microservicedatawave-microservice-service-parent
- 5.0.11
+ 5.0.12-3508-RC1../../microservice-service-parent/pom.xmlspring-boot-starter-datawave
@@ -27,7 +27,7 @@
1.9.25.11.9.47.33.1
- 4.0.2
+ 4.0.3-3508-RC13.0.04.0.231.1-jre
diff --git a/microservices/starters/datawave/src/main/java/datawave/microservice/authorization/preauth/ProxiedEntityX509Filter.java b/microservices/starters/datawave/src/main/java/datawave/microservice/authorization/preauth/ProxiedEntityX509Filter.java
index ed83d5f2ac4..c1d2bfe91c7 100644
--- a/microservices/starters/datawave/src/main/java/datawave/microservice/authorization/preauth/ProxiedEntityX509Filter.java
+++ b/microservices/starters/datawave/src/main/java/datawave/microservice/authorization/preauth/ProxiedEntityX509Filter.java
@@ -28,7 +28,7 @@
import org.springframework.util.StringUtils;
import datawave.security.authorization.SubjectIssuerDNPair;
-import datawave.security.util.ProxiedEntityUtils;
+import datawave.security.util.DnUtils;
/**
* Allows authorization based on a supplied X.509 client certificate (or information from trusted headers) and proxied entities/issuers named in headers.
@@ -131,11 +131,11 @@ protected List getSubjectIssuerDNPairs(String proxiedSubjec
return null;
} else {
List proxiedEntities;
- Collection entities = Arrays.asList(ProxiedEntityUtils.splitProxiedDNs(proxiedSubjects, true));
+ Collection entities = Arrays.asList(DnUtils.splitProxiedDNs(proxiedSubjects, true));
if (!requireIssuers) {
proxiedEntities = entities.stream().map(SubjectIssuerDNPair::of).collect(Collectors.toCollection(ArrayList::new));
} else {
- Collection issuers = Arrays.asList(ProxiedEntityUtils.splitProxiedDNs(proxiedIssuers, true));
+ Collection issuers = Arrays.asList(DnUtils.splitProxiedDNs(proxiedIssuers, true));
if (issuers.size() != entities.size()) {
logger.warn("Failing authorization since issuers list (" + proxiedIssuers + ") and entities list (" + proxiedSubjects
+ ") don't match up.");
diff --git a/microservices/starters/datawave/src/main/java/datawave/microservice/authorization/service/RemoteAuthorizationServiceUserDetailsService.java b/microservices/starters/datawave/src/main/java/datawave/microservice/authorization/service/RemoteAuthorizationServiceUserDetailsService.java
index e5e434207dc..f2d8f0bb924 100644
--- a/microservices/starters/datawave/src/main/java/datawave/microservice/authorization/service/RemoteAuthorizationServiceUserDetailsService.java
+++ b/microservices/starters/datawave/src/main/java/datawave/microservice/authorization/service/RemoteAuthorizationServiceUserDetailsService.java
@@ -26,7 +26,7 @@
import datawave.security.authorization.DatawaveUser;
import datawave.security.authorization.JWTTokenHandler;
import datawave.security.authorization.SubjectIssuerDNPair;
-import datawave.security.util.ProxiedEntityUtils;
+import datawave.security.util.DnUtils;
/**
* An {@link AuthenticationUserDetailsService} that retrieves user information from a remote authorization service for a set of proxied entity names, and
@@ -94,7 +94,7 @@ private String buildDNChain(ProxiedEntityPreauthPrincipal principal, Function<"))
+ ">";
// @formatter:on
diff --git a/microservices/starters/datawave/src/main/java/datawave/microservice/authorization/user/DatawaveUserDetails.java b/microservices/starters/datawave/src/main/java/datawave/microservice/authorization/user/DatawaveUserDetails.java
index ae85234fd13..f0a2b0214be 100644
--- a/microservices/starters/datawave/src/main/java/datawave/microservice/authorization/user/DatawaveUserDetails.java
+++ b/microservices/starters/datawave/src/main/java/datawave/microservice/authorization/user/DatawaveUserDetails.java
@@ -20,7 +20,7 @@
import datawave.security.authorization.DatawaveUser.UserType;
import datawave.security.authorization.ProxiedUserDetails;
import datawave.security.authorization.SubjectIssuerDNPair;
-import datawave.security.util.ProxiedEntityUtils;
+import datawave.security.util.DnUtils;
/**
* A {@link UserDetails} that represents a set of proxied users. For example, this proxied user could represent a GUI server acting on behalf of a user. The GUI
@@ -77,7 +77,7 @@ public String getName() {
@Override
@JsonIgnore
public String getShortName() {
- return ProxiedEntityUtils.getShortName(getPrimaryUser().getName());
+ return DnUtils.getShortName(getPrimaryUser().getName());
}
/**
diff --git a/microservices/starters/datawave/src/main/java/datawave/microservice/config/security/util/DnUtilsProperties.java b/microservices/starters/datawave/src/main/java/datawave/microservice/security/util/DnPropertiesConfig.java
similarity index 91%
rename from microservices/starters/datawave/src/main/java/datawave/microservice/config/security/util/DnUtilsProperties.java
rename to microservices/starters/datawave/src/main/java/datawave/microservice/security/util/DnPropertiesConfig.java
index dcd6a0c2bfc..5d4c22fd733 100644
--- a/microservices/starters/datawave/src/main/java/datawave/microservice/config/security/util/DnUtilsProperties.java
+++ b/microservices/starters/datawave/src/main/java/datawave/microservice/security/util/DnPropertiesConfig.java
@@ -1,4 +1,4 @@
-package datawave.microservice.config.security.util;
+package datawave.microservice.security.util;
import java.util.List;
import java.util.regex.Pattern;
@@ -10,7 +10,7 @@
@Validated
@ConfigurationProperties(prefix = "datawave.security.util")
-public class DnUtilsProperties {
+public class DnPropertiesConfig {
@NotEmpty
private String subjectDnPattern;
@NotEmpty
diff --git a/microservices/starters/datawave/src/main/java/datawave/microservice/config/security/util/DnUtilsConfig.java b/microservices/starters/datawave/src/main/java/datawave/microservice/security/util/DnPropertiesProvider.java
similarity index 53%
rename from microservices/starters/datawave/src/main/java/datawave/microservice/config/security/util/DnUtilsConfig.java
rename to microservices/starters/datawave/src/main/java/datawave/microservice/security/util/DnPropertiesProvider.java
index ddb94593a17..aa1d41bf340 100644
--- a/microservices/starters/datawave/src/main/java/datawave/microservice/config/security/util/DnUtilsConfig.java
+++ b/microservices/starters/datawave/src/main/java/datawave/microservice/security/util/DnPropertiesProvider.java
@@ -1,18 +1,19 @@
-package datawave.microservice.config.security.util;
+package datawave.microservice.security.util;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import datawave.microservice.security.util.DnUtils;
+import datawave.security.util.DnProperties;
@Configuration
@ConditionalOnProperty(name = {"datawave.security.util.subjectDnPattern", "datawave.security.util.npeOuList"})
-@EnableConfigurationProperties(DnUtilsProperties.class)
-public class DnUtilsConfig {
+@EnableConfigurationProperties(DnPropertiesConfig.class)
+public class DnPropertiesProvider {
+
@Bean
- public DnUtils dnUtils(DnUtilsProperties dnUtilsProperties) {
- return new DnUtils(dnUtilsProperties.getCompiledSubjectDnPattern(), dnUtilsProperties.getNpeOuList());
+ public DnProperties dnProperties(DnPropertiesConfig config) {
+ return new DnProperties(config.getCompiledSubjectDnPattern(), config.getNpeOuList());
}
}
diff --git a/microservices/starters/query/pom.xml b/microservices/starters/query/pom.xml
index 628f46ffeee..260009f7a6d 100644
--- a/microservices/starters/query/pom.xml
+++ b/microservices/starters/query/pom.xml
@@ -4,7 +4,7 @@
gov.nsa.datawave.microservicedatawave-microservice-service-parent
- 5.0.11
+ 5.0.12-3508-RC1../../microservice-service-parent/pom.xmlspring-boot-starter-datawave-query
@@ -102,6 +102,11 @@
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ ${version.datawave-commons-security}
+ gov.nsa.datawave.coreaccumulo-utils
@@ -296,6 +301,10 @@
gov.nsa.datawavedatawave-query-core
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.coreaccumulo-utils
diff --git a/microservices/starters/query/src/main/java/datawave/microservice/query/mapreduce/jobs/OozieJob.java b/microservices/starters/query/src/main/java/datawave/microservice/query/mapreduce/jobs/OozieJob.java
index 056856d208a..e6207bf88b4 100644
--- a/microservices/starters/query/src/main/java/datawave/microservice/query/mapreduce/jobs/OozieJob.java
+++ b/microservices/starters/query/src/main/java/datawave/microservice/query/mapreduce/jobs/OozieJob.java
@@ -17,7 +17,7 @@
import datawave.microservice.authorization.user.DatawaveUserDetails;
import datawave.microservice.query.mapreduce.config.MapReduceQueryProperties;
import datawave.microservice.query.mapreduce.status.MapReduceQueryStatus;
-import datawave.security.util.ProxiedEntityUtils;
+import datawave.security.util.DnUtils;
import datawave.webservice.common.audit.Auditor;
public class OozieJob extends MapReduceJob {
@@ -38,7 +38,7 @@ public OozieJob(MapReduceQueryProperties mapReduceQueryProperties) {
@Override
public String createId(DatawaveUserDetails currentUser) {
- return String.join("_", ProxiedEntityUtils.getShortName(currentUser.getPrimaryUser().getName()), UUID.randomUUID().toString());
+ return String.join("_", DnUtils.getShortName(currentUser.getPrimaryUser().getName()), UUID.randomUUID().toString());
}
@Override
diff --git a/pom.xml b/pom.xml
index aa849568694..ae425a5c449 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,6 +19,7 @@
coreweb-serviceswarehouse
+ commonsscm:git:https://github.com/NationalSecurityAgency/datawave.git
@@ -53,7 +54,7 @@
1C2.1.41.4.1.Final
- 1.0.0.Final
+ 2.1.0.Final3.20.21.11.41.14.11
@@ -68,6 +69,7 @@
2.18.03.32.6
+ 3.20.01.23.9.01.6
@@ -101,7 +103,7 @@
4.4.84.0.49.4.21.Final
- 2.13.5
+ 2.12.71.9.132.3.33.24.0-GA
@@ -117,6 +119,7 @@
0.11.2202310131.19.0
+ 2.3.04.13.25.12.01.12.0
@@ -131,7 +134,6 @@
9.3.04.1.42.Final3.3
- 5.0.3.Final2.0.92.61.03.16.3
@@ -144,11 +146,11 @@
${version.spring}2.9.60.17.0
- 3.1.1.Final
- 3.1.1.Final
+ 3.1.9.Final
+ 3.1.9.Final2.3.5.Final
- 17.0.1.Final
+ 26.1.3.Final5.4.03.1.42.12.2
@@ -175,7 +177,7 @@
org.wildfly.bom
- wildfly-javaee8-with-tools
+ wildfly-jakartaee8-with-tools${version.wildfly}pomimport
@@ -398,6 +400,11 @@
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ ${project.version}
+ gov.nsa.datawave.coreaccumulo-utils
@@ -674,6 +681,11 @@
metrics-cdi${version.metrics-cdi}
+
+ io.jsonwebtoken
+ jjwt-api
+ ${version.jjwt}
+ io.jsonwebtokenjjwt-impl
@@ -1357,24 +1369,6 @@
${version.weld}provided
-
- org.picketbox
- picketbox
- ${version.picketbox}
- provided
-
-
- org.picketbox
- picketbox-infinispan
- ${version.picketbox}
- provided
-
-
- dom4j
- dom4j
-
-
- org.wildflywildfly-security
@@ -1459,14 +1453,14 @@
org.jboss.arquillian.container
- arquillian-weld-ee-embedded-1.1
- ${version.arquillian-weld-ee-embedded}
+ arquillian-weld-embedded
+ ${version.arquillian-weld-embedded}test
- org.jboss.weld
- weld-core
- ${version.weld-test}
+ org.junit-pioneer
+ junit-pioneer
+ ${version.junit-pioneer}test
diff --git a/warehouse/assemble/webservice/pom.xml b/warehouse/assemble/webservice/pom.xml
index 580f7c48c4d..3417b3f0bf3 100644
--- a/warehouse/assemble/webservice/pom.xml
+++ b/warehouse/assemble/webservice/pom.xml
@@ -68,7 +68,12 @@
org.jboss.resteasy
- resteasy-jaxrs
+ resteasy-core
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-core-spiprovided
diff --git a/warehouse/core/pom.xml b/warehouse/core/pom.xml
index 0adc95f7400..e055a3fbba4 100644
--- a/warehouse/core/pom.xml
+++ b/warehouse/core/pom.xml
@@ -75,12 +75,12 @@
protostuff-api
- javax.annotation
- javax.annotation-api
+ jakarta.enterprise
+ jakarta.enterprise.cdi-api
- javax.enterprise
- cdi-api
+ javax.annotation
+ javax.annotation-apinet.sf.opencsv
diff --git a/warehouse/data-dictionary-core/pom.xml b/warehouse/data-dictionary-core/pom.xml
index e2b4c293bf7..d4a29aa74e3 100644
--- a/warehouse/data-dictionary-core/pom.xml
+++ b/warehouse/data-dictionary-core/pom.xml
@@ -10,6 +10,10 @@
jar${project.artifactId}
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.microservicedictionary-api
@@ -19,8 +23,8 @@
datawave-ws-common
- javax.enterprise
- cdi-api
+ jakarta.enterprise
+ jakarta.enterprise.cdi-api
diff --git a/warehouse/edge-dictionary-core/pom.xml b/warehouse/edge-dictionary-core/pom.xml
index fc35e5c146d..d59cd8dbd21 100644
--- a/warehouse/edge-dictionary-core/pom.xml
+++ b/warehouse/edge-dictionary-core/pom.xml
@@ -10,6 +10,10 @@
jar${project.artifactId}
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.microservicedictionary-api
@@ -19,8 +23,8 @@
datawave-ws-common
- javax.enterprise
- cdi-api
+ jakarta.enterprise
+ jakarta.enterprise.cdi-api
diff --git a/warehouse/metrics-core/pom.xml b/warehouse/metrics-core/pom.xml
index aa693b62e8d..0b880d56619 100644
--- a/warehouse/metrics-core/pom.xml
+++ b/warehouse/metrics-core/pom.xml
@@ -31,8 +31,8 @@
datawave-query-core
- javax.enterprise
- cdi-api
+ jakarta.enterprise
+ jakarta.enterprise.cdi-apiorg.easymock
diff --git a/warehouse/query-core/pom.xml b/warehouse/query-core/pom.xml
index ee7fbd524df..fce8b562b59 100644
--- a/warehouse/query-core/pom.xml
+++ b/warehouse/query-core/pom.xml
@@ -26,6 +26,10 @@
com.fasterxml.jackson.corejackson-databind
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-guava
+ com.google.code.findbugsannotations
@@ -75,6 +79,10 @@
datawave-ssdeep-common${project.version}
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.coredatawave-core-annotation
@@ -139,8 +147,8 @@
- javax.enterprise
- cdi-api
+ jakarta.enterprise
+ jakarta.enterprise.cdi-api
@@ -206,7 +214,12 @@
org.jboss.resteasy
- resteasy-jaxrs
+ resteasy-core
+ true
+
+
+ org.jboss.resteasy
+ resteasy-core-spitrue
@@ -352,7 +365,7 @@
org.jboss.arquillian.container
- arquillian-weld-ee-embedded-1.1
+ arquillian-weld-embeddedtest
@@ -388,12 +401,7 @@
org.jboss.spec.javax.transaction
- jboss-transaction-api_1.2_spec
- test
-
-
- org.jboss.weld
- weld-core
+ jboss-transaction-api_1.3_spectest
@@ -417,11 +425,6 @@
${version.mockito}test
-
- org.picketbox
- picketbox
- test
- org.springframeworkspring-test
diff --git a/warehouse/query-core/src/test/java/datawave/query/cardinality/TestCardinalityWithQuery.java b/warehouse/query-core/src/test/java/datawave/query/cardinality/TestCardinalityWithQuery.java
index 0b985b5bf65..0244b55be7c 100644
--- a/warehouse/query-core/src/test/java/datawave/query/cardinality/TestCardinalityWithQuery.java
+++ b/warehouse/query-core/src/test/java/datawave/query/cardinality/TestCardinalityWithQuery.java
@@ -50,7 +50,7 @@
import datawave.security.authorization.DatawaveUser;
import datawave.security.authorization.DatawaveUser.UserType;
import datawave.security.authorization.SubjectIssuerDNPair;
-import datawave.security.util.DnUtils;
+import datawave.security.util.DnProperties;
import datawave.webservice.query.runner.RunningQuery;
import datawave.webservice.result.EventQueryResponseBase;
@@ -93,7 +93,7 @@ public static void setUp() throws Exception {
@Before
public void setup() throws Exception {
- System.setProperty(DnUtils.NPE_OU_PROPERTY, "iamnotaperson");
+ System.setProperty(DnProperties.NPE_OU_PROPERTY, "iamnotaperson");
temporaryFolder = tempDir.newFolder().toPath();
logic = new ShardQueryLogic();
diff --git a/warehouse/query-core/src/test/java/datawave/query/tables/RemoteEdgeQueryLogicHttpTest.java b/warehouse/query-core/src/test/java/datawave/query/tables/RemoteEdgeQueryLogicHttpTest.java
index 8ccce500fdd..2d54c566dea 100644
--- a/warehouse/query-core/src/test/java/datawave/query/tables/RemoteEdgeQueryLogicHttpTest.java
+++ b/warehouse/query-core/src/test/java/datawave/query/tables/RemoteEdgeQueryLogicHttpTest.java
@@ -53,9 +53,9 @@
import datawave.microservice.query.QueryImpl;
import datawave.microservice.query.QueryParameters;
import datawave.security.authorization.DatawavePrincipal;
-import datawave.security.util.DnUtils;
+import datawave.security.util.DnProperties;
import datawave.webservice.common.json.DefaultMapperDecorator;
-import datawave.webservice.common.remote.TestJSSESecurityDomain;
+import datawave.webservice.common.remote.TestSSLStores;
import datawave.webservice.query.remote.RemoteQueryServiceImpl;
import datawave.webservice.query.result.edge.DefaultEdge;
import datawave.webservice.query.result.edge.EdgeBase;
@@ -104,7 +104,7 @@ private void setContent(InputStream content) throws IOException {
@Before
public void setup() throws Exception {
final ObjectMapper objectMapper = new DefaultMapperDecorator().decorate(new ObjectMapper());
- System.setProperty(DnUtils.SUBJECT_DN_PATTERN_PROPERTY, ".*ou=server.*");
+ System.setProperty(DnProperties.SUBJECT_DN_PATTERN_PROPERTY, ".*ou=server.*");
KeyPairGenerator generater = KeyPairGenerator.getInstance("RSA");
generater.initialize(keysize);
KeyPair keypair = generater.generateKeyPair();
@@ -218,7 +218,7 @@ public void handle(HttpExchange exchange) throws IOException {
remote.setExecutorService(null);
remote.setObjectMapperDecorator(new DefaultMapperDecorator());
remote.setResponseObjectFactory(new DefaultResponseObjectFactory());
- remote.setJsseSecurityDomain(new TestJSSESecurityDomain(alias, privKey, keyPass, chain));
+ remote.setSslStores(new TestSSLStores(alias, privKey, keyPass, chain));
remote.setNextQueryResponseClass(remote.getResponseObjectFactory().getEdgeQueryResponse().getClass());
logic.setRemoteQueryService(remote);
diff --git a/warehouse/query-core/src/test/java/datawave/query/tables/RemoteQueryServiceTestUtil.java b/warehouse/query-core/src/test/java/datawave/query/tables/RemoteQueryServiceTestUtil.java
index 5c7ad67954f..3cf2dcaaf46 100644
--- a/warehouse/query-core/src/test/java/datawave/query/tables/RemoteQueryServiceTestUtil.java
+++ b/warehouse/query-core/src/test/java/datawave/query/tables/RemoteQueryServiceTestUtil.java
@@ -60,10 +60,10 @@
import datawave.security.authorization.DatawavePrincipal;
import datawave.security.authorization.DatawaveUser;
import datawave.security.authorization.SubjectIssuerDNPair;
-import datawave.security.util.DnUtils;
+import datawave.security.util.DnProperties;
import datawave.webservice.common.json.DefaultMapperDecorator;
import datawave.webservice.common.remote.RemoteServiceUtil;
-import datawave.webservice.common.remote.TestJSSESecurityDomain;
+import datawave.webservice.common.remote.TestSSLStores;
import datawave.webservice.query.remote.RemoteQueryServiceImpl;
import datawave.webservice.query.result.event.DefaultEvent;
import datawave.webservice.query.result.event.DefaultField;
@@ -104,7 +104,7 @@ public void initialize() throws IOException {
super.initialize();
final ObjectMapper objectMapper = new DefaultMapperDecorator().decorate(new ObjectMapper());
- System.setProperty(DnUtils.SUBJECT_DN_PATTERN_PROPERTY, ".*ou=server.*");
+ System.setProperty(DnProperties.SUBJECT_DN_PATTERN_PROPERTY, ".*ou=server.*");
KeyPairGenerator generater = null;
try {
generater = KeyPairGenerator.getInstance("RSA");
@@ -200,7 +200,7 @@ public RemoteQueryService getRemoteService() {
remote.setExecutorService(null);
remote.setObjectMapperDecorator(new DefaultMapperDecorator());
remote.setResponseObjectFactory(new DefaultResponseObjectFactory());
- remote.setJsseSecurityDomain(new TestJSSESecurityDomain(alias, privateKey, keyPass, chain));
+ remote.setSslStores(new TestSSLStores(alias, privateKey, keyPass, chain));
return remote;
}
diff --git a/warehouse/query-core/src/test/java/datawave/query/testframework/AbstractFunctionalQuery.java b/warehouse/query-core/src/test/java/datawave/query/testframework/AbstractFunctionalQuery.java
index f0b5d52cd8b..3f7106e29b8 100644
--- a/warehouse/query-core/src/test/java/datawave/query/testframework/AbstractFunctionalQuery.java
+++ b/warehouse/query-core/src/test/java/datawave/query/testframework/AbstractFunctionalQuery.java
@@ -85,7 +85,7 @@
import datawave.security.authorization.DatawaveUser;
import datawave.security.authorization.ProxiedUserDetails;
import datawave.security.authorization.SubjectIssuerDNPair;
-import datawave.security.util.DnUtils;
+import datawave.security.util.DnProperties;
import datawave.webservice.query.exception.QueryException;
import datawave.webservice.query.result.event.EventBase;
import datawave.webservice.query.result.event.FieldBase;
@@ -114,7 +114,7 @@ public abstract class AbstractFunctionalQuery implements QueryLogicTestHarness.T
static {
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
System.setProperty("file.encoding", StandardCharsets.UTF_8.name());
- System.setProperty(DnUtils.NPE_OU_PROPERTY, "iamnotaperson");
+ System.setProperty(DnProperties.NPE_OU_PROPERTY, "iamnotaperson");
try {
File dir = new File(ClassLoader.getSystemClassLoader().getResource(".").toURI());
File targetDir = dir.getParentFile();
diff --git a/web-services/accumulo/pom.xml b/web-services/accumulo/pom.xml
index e335b262bdc..98d97983488 100644
--- a/web-services/accumulo/pom.xml
+++ b/web-services/accumulo/pom.xml
@@ -26,6 +26,10 @@
dnsjavadnsjava
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.corebase-rest-responses
@@ -81,14 +85,22 @@
org.apache.hadoophadoop-client-api
+
+ org.apache.httpcomponents
+ httpclient
+
+
+ org.apache.httpcomponents
+ httpcore
+ com.fasterxml.woodstoxwoodstox-coreprovided
- javax.enterprise
- cdi-api
+ jakarta.enterprise
+ jakarta.enterprise.cdi-apiprovided
@@ -98,7 +110,12 @@
org.jboss.resteasy
- resteasy-jaxrs
+ resteasy-core
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-core-spiprovided
diff --git a/web-services/annotations/pom.xml b/web-services/annotations/pom.xml
index 0331ff7e474..f8970e4d590 100644
--- a/web-services/annotations/pom.xml
+++ b/web-services/annotations/pom.xml
@@ -41,6 +41,10 @@
datawave-query-core${project.version}
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.coredatawave-core-annotation
@@ -69,7 +73,12 @@
org.jboss.resteasy
- resteasy-jaxrs
+ resteasy-core
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-core-spiprovided
@@ -131,8 +140,8 @@
test
- javax.enterprise
- cdi-api
+ jakarta.enterprise
+ jakarta.enterprise.cdi-apitest
@@ -152,7 +161,7 @@
org.jboss.arquillian.container
- arquillian-weld-ee-embedded-1.1
+ arquillian-weld-embeddedtest
@@ -170,11 +179,6 @@
shrinkwrap-apitest
-
- org.jboss.weld
- weld-core
- test
- org.jboss.weldweld-core-impl
@@ -185,11 +189,6 @@
mockito-coretest
-
- org.picketbox
- picketbox
- test
- org.powermockpowermock-api-easymock
diff --git a/web-services/atom/pom.xml b/web-services/atom/pom.xml
index 72aaff3868c..7fccfab8c63 100644
--- a/web-services/atom/pom.xml
+++ b/web-services/atom/pom.xml
@@ -10,6 +10,10 @@
ejb${project.artifactId}
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.webservicesdatawave-ws-common
@@ -63,8 +67,8 @@
provided
- javax.enterprise
- cdi-api
+ jakarta.enterprise
+ jakarta.enterprise.cdi-apiprovided
@@ -79,7 +83,12 @@
org.jboss.resteasy
- resteasy-jaxrs
+ resteasy-core
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-core-spiprovided
@@ -99,7 +108,7 @@
org.jboss.spec.javax.transaction
- jboss-transaction-api_1.2_spec
+ jboss-transaction-api_1.3_specprovided
diff --git a/web-services/atom/src/main/java/datawave/webservice/atom/AtomKeyValueParser.java b/web-services/atom/src/main/java/datawave/webservice/atom/AtomKeyValueParser.java
index 00516277344..9a43c3103b6 100644
--- a/web-services/atom/src/main/java/datawave/webservice/atom/AtomKeyValueParser.java
+++ b/web-services/atom/src/main/java/datawave/webservice/atom/AtomKeyValueParser.java
@@ -14,7 +14,7 @@
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.iterators.LongCombiner;
-import org.jboss.resteasy.util.Base64;
+import org.apache.commons.net.util.Base64;
public class AtomKeyValueParser {
@@ -123,13 +123,13 @@ public static AtomKeyValueParser parse(Key key, Value value) throws IOException
}
public static String encodeId(String id) throws UnsupportedEncodingException {
- String key64 = Base64.encodeBytes(id.getBytes());
+ String key64 = Base64.encodeBase64String(id.getBytes());
return URLEncoder.encode(key64, "UTF-8");
}
public static String decodeId(String encodedId) throws IOException {
String key64 = URLDecoder.decode(encodedId, "UTF-8");
- byte[] bKey = Base64.decode(key64);
+ byte[] bKey = Base64.decodeBase64(key64);
return new String(bKey);
}
}
diff --git a/web-services/atom/src/main/java/datawave/webservice/atom/AtomServiceBean.java b/web-services/atom/src/main/java/datawave/webservice/atom/AtomServiceBean.java
index 9cc2fc468f2..a692dd93219 100644
--- a/web-services/atom/src/main/java/datawave/webservice/atom/AtomServiceBean.java
+++ b/web-services/atom/src/main/java/datawave/webservice/atom/AtomServiceBean.java
@@ -47,11 +47,11 @@
import org.apache.accumulo.core.data.Range;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.security.Authorizations;
+import org.apache.commons.net.util.Base64;
import org.apache.deltaspike.core.api.config.ConfigProperty;
import org.apache.hadoop.io.Text;
import org.apache.log4j.Logger;
import org.jboss.resteasy.annotations.GZIP;
-import org.jboss.resteasy.util.Base64;
import datawave.annotation.Required;
import datawave.core.common.connection.AccumuloConnectionFactory;
@@ -346,7 +346,7 @@ public Entry getEntry(@Required("category") @PathParam("category") String catego
private Key deserializeKey(String k) throws Exception {
String key64 = URLDecoder.decode(k, "UTF-8");
- byte[] bKey = Base64.decode(key64);
+ byte[] bKey = Base64.decodeBase64(key64);
ByteArrayInputStream bais = new ByteArrayInputStream(bKey);
DataInputStream in = new DataInputStream(bais);
Key key = new Key();
@@ -359,7 +359,7 @@ private String serializeKey(Key key) throws Exception {
DataOutputStream out = new DataOutputStream(baos);
key.write(out);
out.close();
- String key64 = Base64.encodeBytes(baos.toByteArray());
+ String key64 = Base64.encodeBase64String(baos.toByteArray());
return URLEncoder.encode(key64, "UTF-8");
}
diff --git a/web-services/cached-results/pom.xml b/web-services/cached-results/pom.xml
index 27604329ea1..c431e59ded9 100644
--- a/web-services/cached-results/pom.xml
+++ b/web-services/cached-results/pom.xml
@@ -26,6 +26,10 @@
gov.nsa.datawavedatawave-core
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.coredatawave-core-cached-results
@@ -46,8 +50,8 @@
query-metric-api
- javax.enterprise
- cdi-api
+ jakarta.enterprise
+ jakarta.enterprise.cdi-apiorg.apache.commons
@@ -101,7 +105,12 @@
org.jboss.resteasy
- resteasy-jaxrs
+ resteasy-core
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-core-spiprovided
diff --git a/web-services/client/pom.xml b/web-services/client/pom.xml
index 8c10e90c0ae..47d0cc75ee9 100644
--- a/web-services/client/pom.xml
+++ b/web-services/client/pom.xml
@@ -152,7 +152,12 @@
org.jboss.resteasy
- resteasy-jaxrs
+ resteasy-core
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-core-spiprovided
diff --git a/web-services/common-util/pom.xml b/web-services/common-util/pom.xml
index 1ec00254b44..7888485b760 100644
--- a/web-services/common-util/pom.xml
+++ b/web-services/common-util/pom.xml
@@ -31,6 +31,10 @@
datawave-common${project.version}
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.coreaccumulo-utils
@@ -160,8 +164,8 @@
provided
- javax.enterprise
- cdi-api
+ jakarta.enterprise
+ jakarta.enterprise.cdi-apiprovided
@@ -187,7 +191,12 @@
org.jboss.resteasy
- resteasy-jaxrs
+ resteasy-core
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-core-spiprovided
diff --git a/web-services/common-util/src/main/java/datawave/resteasy/interceptor/BaseMethodStatsInterceptor.java b/web-services/common-util/src/main/java/datawave/resteasy/interceptor/BaseMethodStatsInterceptor.java
index 56681724e3f..30399242ceb 100644
--- a/web-services/common-util/src/main/java/datawave/resteasy/interceptor/BaseMethodStatsInterceptor.java
+++ b/web-services/common-util/src/main/java/datawave/resteasy/interceptor/BaseMethodStatsInterceptor.java
@@ -1,7 +1,7 @@
package datawave.resteasy.interceptor;
-import static datawave.webservice.metrics.Constants.REQUEST_LOGIN_TIME_HEADER;
-import static datawave.webservice.metrics.Constants.REQUEST_START_TIME_HEADER;
+import static datawave.security.util.SecurityConstants.REQUEST_LOGIN_TIME_HEADER;
+import static datawave.security.util.SecurityConstants.REQUEST_START_TIME_HEADER;
import java.io.IOException;
import java.io.OutputStream;
@@ -22,7 +22,7 @@
import javax.ws.rs.ext.WriterInterceptorContext;
import org.apache.log4j.Logger;
-import org.jboss.resteasy.core.interception.PreMatchContainerRequestContext;
+import org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext;
import org.jboss.resteasy.specimpl.MultivaluedTreeMap;
import org.jboss.resteasy.spi.Failure;
import org.jboss.resteasy.util.CaseInsensitiveMap;
diff --git a/web-services/common-util/src/main/java/datawave/resteasy/interceptor/CreateQuerySessionIDFilter.java b/web-services/common-util/src/main/java/datawave/resteasy/interceptor/CreateQuerySessionIDFilter.java
index 8e14cd7868b..468303cdd2a 100644
--- a/web-services/common-util/src/main/java/datawave/resteasy/interceptor/CreateQuerySessionIDFilter.java
+++ b/web-services/common-util/src/main/java/datawave/resteasy/interceptor/CreateQuerySessionIDFilter.java
@@ -12,7 +12,7 @@
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.jboss.resteasy.core.ResourceMethodInvoker;
-import org.jboss.resteasy.util.FindAnnotation;
+import org.jboss.resteasy.spi.util.FindAnnotation;
import org.jboss.resteasy.util.HttpHeaderNames;
import datawave.Constants;
diff --git a/web-services/common-util/src/main/java/datawave/resteasy/interceptor/LoggingInterceptor.java b/web-services/common-util/src/main/java/datawave/resteasy/interceptor/LoggingInterceptor.java
index 66d83ac69fe..793b8cb5f27 100644
--- a/web-services/common-util/src/main/java/datawave/resteasy/interceptor/LoggingInterceptor.java
+++ b/web-services/common-util/src/main/java/datawave/resteasy/interceptor/LoggingInterceptor.java
@@ -13,7 +13,7 @@
import javax.ws.rs.ext.WriterInterceptorContext;
import org.apache.log4j.Logger;
-import org.jboss.resteasy.core.interception.PreMatchContainerRequestContext;
+import org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext;
@Provider
@Priority(Priorities.USER)
diff --git a/web-services/common-util/src/main/java/datawave/resteasy/util/DateParamConverterProvider.java b/web-services/common-util/src/main/java/datawave/resteasy/util/DateParamConverterProvider.java
index 2c050206c96..87954522c3a 100644
--- a/web-services/common-util/src/main/java/datawave/resteasy/util/DateParamConverterProvider.java
+++ b/web-services/common-util/src/main/java/datawave/resteasy/util/DateParamConverterProvider.java
@@ -8,7 +8,7 @@
import javax.ws.rs.ext.ParamConverterProvider;
import javax.ws.rs.ext.Provider;
-import org.jboss.resteasy.util.FindAnnotation;
+import org.jboss.resteasy.spi.util.FindAnnotation;
import datawave.annotation.DateFormat;
diff --git a/web-services/common-util/src/main/java/datawave/resteasy/util/RequiredProcessor.java b/web-services/common-util/src/main/java/datawave/resteasy/util/RequiredProcessor.java
index 8c24bbf21e0..dd78215649d 100644
--- a/web-services/common-util/src/main/java/datawave/resteasy/util/RequiredProcessor.java
+++ b/web-services/common-util/src/main/java/datawave/resteasy/util/RequiredProcessor.java
@@ -5,7 +5,7 @@
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.jboss.resteasy.spi.StringParameterUnmarshaller;
-import org.jboss.resteasy.util.FindAnnotation;
+import org.jboss.resteasy.spi.util.FindAnnotation;
import datawave.annotation.Required;
diff --git a/web-services/common-util/src/main/java/datawave/security/util/DnUtils.java b/web-services/common-util/src/main/java/datawave/security/util/DnUtils.java
deleted file mode 100644
index 8faa4ffbc0d..00000000000
--- a/web-services/common-util/src/main/java/datawave/security/util/DnUtils.java
+++ /dev/null
@@ -1,137 +0,0 @@
-package datawave.security.util;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Properties;
-import java.util.regex.Pattern;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import datawave.security.authorization.SubjectIssuerDNPair;
-
-public class DnUtils {
-
- /** Config for injecting NPE OU identifiers */
- public static final String PROPS_RESOURCE = "dnutils.properties";
-
- private static final Properties PROPS = new Properties();
-
- public static final String SUBJECT_DN_PATTERN_PROPERTY = "subject.dn.pattern";
-
- private static final Pattern SUBJECT_DN_PATTERN;
-
- /** Property containing a comma-delimited list of OUs */
- public static final String NPE_OU_PROPERTY = "npe.ou.entries";
-
- /** Parsed NPE OU identifiers */
- static final List NPE_OU_LIST;
-
- private static final Logger log = LoggerFactory.getLogger(DnUtils.class);
-
- private static final datawave.microservice.security.util.DnUtils dnUtils;
-
- static {
- InputStream in = null;
- try {
- in = DnUtils.class.getClassLoader().getResourceAsStream(PROPS_RESOURCE);
- PROPS.load(in);
- } catch (Throwable t) {
- log.error(PROPS_RESOURCE + " could not be loaded!", t);
- throw new RuntimeException(t);
- } finally {
- if (null != in) {
- try {
- in.close();
- } catch (IOException e) {
- log.warn("Failed to close input stream", e);
- }
- }
- }
-
- String subjectDnPattern = System.getProperty(SUBJECT_DN_PATTERN_PROPERTY, PROPS.getProperty(SUBJECT_DN_PATTERN_PROPERTY));
- try {
- if (null == subjectDnPattern || subjectDnPattern.isEmpty()) {
- throw new IllegalStateException(SUBJECT_DN_PATTERN_PROPERTY + " property value cannot be null");
- }
- SUBJECT_DN_PATTERN = Pattern.compile(subjectDnPattern, Pattern.CASE_INSENSITIVE);
- } catch (Throwable t) {
- log.error(SUBJECT_DN_PATTERN_PROPERTY + " = '" + subjectDnPattern + "' could not be compiled", t);
- throw new RuntimeException(t);
- }
-
- List npeOUs = new ArrayList<>();
- String ouString = System.getProperty(NPE_OU_PROPERTY, PROPS.getProperty(NPE_OU_PROPERTY));
- if (null == ouString || ouString.isEmpty()) {
- throw new IllegalStateException("No '" + NPE_OU_PROPERTY + "' value has been configured");
- }
- // Normalize and load...
- String[] ouArray = ouString.split(",");
- for (String ou : ouArray) {
- npeOUs.add(ou.trim().toUpperCase());
- }
- NPE_OU_LIST = Collections.unmodifiableList(npeOUs);
-
- dnUtils = new datawave.microservice.security.util.DnUtils(SUBJECT_DN_PATTERN, NPE_OU_LIST);
- }
-
- public static String[] splitProxiedDNs(String proxiedDNs, boolean allowDups) {
- return datawave.microservice.security.util.DnUtils.splitProxiedDNs(proxiedDNs, allowDups);
- }
-
- public static String[] splitProxiedSubjectIssuerDNs(String proxiedDNs) {
- return datawave.microservice.security.util.DnUtils.splitProxiedSubjectIssuerDNs(proxiedDNs);
- }
-
- public static String buildProxiedDN(String... dns) {
- return datawave.microservice.security.util.DnUtils.buildProxiedDN(dns);
- }
-
- public static Collection buildNormalizedDNList(String subjectDN, String issuerDN, String proxiedSubjectDNs, String proxiedIssuerDNs) {
- return dnUtils.buildNormalizedDNList(subjectDN, issuerDN, proxiedSubjectDNs, proxiedIssuerDNs);
- }
-
- public static String buildNormalizedProxyDN(String subjectDN, String issuerDN, String proxiedSubjectDNs, String proxiedIssuerDNs) {
- return dnUtils.buildNormalizedProxyDN(subjectDN, issuerDN, proxiedSubjectDNs, proxiedIssuerDNs);
- }
-
- public static String buildNormalizedProxyDN(List dns) {
- return datawave.microservice.security.util.DnUtils.buildNormalizedProxyDN(dns);
- }
-
- public static String getCommonName(String dn) {
- return datawave.microservice.security.util.DnUtils.getCommonName(dn);
- }
-
- public static String[] getOrganizationalUnits(String dn) {
- return datawave.microservice.security.util.DnUtils.getOrganizationalUnits(dn);
- }
-
- public static String getShortName(String dn) {
- return datawave.microservice.security.util.DnUtils.getShortName(dn);
- }
-
- public static boolean isServerDN(String dn) {
- return dnUtils.isServerDN(dn);
- }
-
- public static String getUserDN(String[] dns) {
- return dnUtils.getUserDN(dns);
- }
-
- public static String getUserDN(String[] dns, boolean issuerDNs) {
- return dnUtils.getUserDN(dns, issuerDNs);
- }
-
- public static String[] getComponents(String dn, String componentName) {
- return dnUtils.getComponents(dn, componentName);
- }
-
- public static String normalizeDN(String userName) {
- return datawave.microservice.security.util.DnUtils.normalizeDN(userName);
- }
-}
diff --git a/web-services/common-util/src/main/java/datawave/webservice/metrics/Constants.java b/web-services/common-util/src/main/java/datawave/webservice/metrics/Constants.java
deleted file mode 100644
index b22bfa6e5ec..00000000000
--- a/web-services/common-util/src/main/java/datawave/webservice/metrics/Constants.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package datawave.webservice.metrics;
-
-public interface Constants {
- /**
- * An internal header used to store the start time of the HTTP request, as retrieved from the web container (e.g., Undertow).
- */
- String REQUEST_START_TIME_HEADER = "X-Internal-RequestStartTimeNanos";
- /**
- * An internal header used to store the time required to authenticate the user for the current request.
- */
- String REQUEST_LOGIN_TIME_HEADER = "X-Internal-RequestLoginTimeMillis";
-}
diff --git a/web-services/common-util/src/test/java/datawave/resteasy/interceptor/CreateQuerySessionIDFilterTest.java b/web-services/common-util/src/test/java/datawave/resteasy/interceptor/CreateQuerySessionIDFilterTest.java
index 6ae6e3dd738..1cf3cb15fa1 100644
--- a/web-services/common-util/src/test/java/datawave/resteasy/interceptor/CreateQuerySessionIDFilterTest.java
+++ b/web-services/common-util/src/test/java/datawave/resteasy/interceptor/CreateQuerySessionIDFilterTest.java
@@ -16,8 +16,8 @@
import org.easymock.Mock;
import org.easymock.MockType;
import org.jboss.resteasy.core.ResourceMethodInvoker;
-import org.jboss.resteasy.core.interception.ContainerResponseContextImpl;
-import org.jboss.resteasy.core.interception.ResponseContainerRequestContext;
+import org.jboss.resteasy.core.interception.jaxrs.ContainerResponseContextImpl;
+import org.jboss.resteasy.core.interception.jaxrs.ResponseContainerRequestContext;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.jboss.resteasy.specimpl.BuiltResponse;
diff --git a/web-services/common-util/src/test/java/datawave/security/authorization/DatawavePrincipalTest.java b/web-services/common-util/src/test/java/datawave/security/authorization/DatawavePrincipalTest.java
deleted file mode 100644
index 8e894134d94..00000000000
--- a/web-services/common-util/src/test/java/datawave/security/authorization/DatawavePrincipalTest.java
+++ /dev/null
@@ -1,200 +0,0 @@
-package datawave.security.authorization;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.stream.Collectors;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.google.common.collect.Lists;
-
-import datawave.security.authorization.DatawaveUser.UserType;
-
-public class DatawavePrincipalTest {
-
- private DatawaveUser finalServer;
- private DatawaveUser server1;
- private DatawaveUser server2;
- private DatawaveUser server3;
- private DatawaveUser user;
-
- final private String finalConnectionServerSubjectDn = "cn=finalserver";
- final private String server1SubjectDn = "cn=server1";
- final private String server2SubjectDn = "cn=server2";
- final private String server3SubjectDn = "cn=server3";
- final private String userSubjectDn = "cn=user";
- final private String issuerDn = "cn=issuer";
-
- @Before
- public void setUp() throws Exception {
- long now = System.currentTimeMillis();
- SubjectIssuerDNPair finalConnectionServerDn = SubjectIssuerDNPair.of(finalConnectionServerSubjectDn, issuerDn);
- SubjectIssuerDNPair server1Dn = SubjectIssuerDNPair.of(server1SubjectDn, issuerDn);
- SubjectIssuerDNPair server2Dn = SubjectIssuerDNPair.of(server2SubjectDn, issuerDn);
- SubjectIssuerDNPair server3Dn = SubjectIssuerDNPair.of(server3SubjectDn, issuerDn);
- SubjectIssuerDNPair userDn = SubjectIssuerDNPair.of(userSubjectDn, issuerDn);
- finalServer = new DatawaveUser(finalConnectionServerDn, UserType.SERVER, null, null, null, now);
- server1 = new DatawaveUser(server1Dn, UserType.SERVER, null, null, null, now);
- server2 = new DatawaveUser(server2Dn, UserType.SERVER, null, null, null, now);
- server3 = new DatawaveUser(server3Dn, UserType.SERVER, null, null, null, now);
- user = new DatawaveUser(userDn, UserType.USER, null, null, null, now);
- }
-
- @Test
- public void GetPrimaryUserTest() {
- // direct call from a server
- DatawavePrincipal dp = new DatawavePrincipal(Lists.newArrayList(finalServer));
- Assert.assertEquals(finalConnectionServerSubjectDn, dp.getPrimaryUser().getDn().subjectDN());
-
- // direct call from a user
- dp = new DatawavePrincipal(Lists.newArrayList(user));
- Assert.assertEquals(userSubjectDn, dp.getPrimaryUser().getDn().subjectDN());
-
- // call from finalConnectionServer proxying initial caller server1
- dp = new DatawavePrincipal(Lists.newArrayList(server1, finalServer));
- Assert.assertEquals(server1SubjectDn, dp.getPrimaryUser().getDn().subjectDN());
-
- // call from finalConnectionServer proxying initial caller server1 through server2
- dp = new DatawavePrincipal(Lists.newArrayList(server1, server2, finalServer));
- Assert.assertEquals(server1SubjectDn, dp.getPrimaryUser().getDn().subjectDN());
-
- // call from finalConnectionServer proxying initial caller server1 through server2 and server3
- dp = new DatawavePrincipal(Lists.newArrayList(server1, server2, server3, finalServer));
- Assert.assertEquals(server1SubjectDn, dp.getPrimaryUser().getDn().subjectDN());
-
- // these tests are for case where a UserType.USER appears anywhere in the proxiedUsers collection
- dp = new DatawavePrincipal(Lists.newArrayList(user, server1, server2, server3));
- Assert.assertEquals(userSubjectDn, dp.getPrimaryUser().getDn().subjectDN());
-
- dp = new DatawavePrincipal(Lists.newArrayList(server1, user, server2, server3));
- Assert.assertEquals(userSubjectDn, dp.getPrimaryUser().getDn().subjectDN());
-
- dp = new DatawavePrincipal(Lists.newArrayList(server1, server2, user, server3));
- Assert.assertEquals(userSubjectDn, dp.getPrimaryUser().getDn().subjectDN());
-
- dp = new DatawavePrincipal(Lists.newArrayList(server1, server2, server3, user));
- Assert.assertEquals(userSubjectDn, dp.getPrimaryUser().getDn().subjectDN());
- }
-
- @Test
- public void GetProxyServersTest() {
- // direct call from finalServer
- DatawavePrincipal dp = new DatawavePrincipal(Lists.newArrayList(finalServer));
- Assert.assertEquals(null, dp.getProxyServers());
-
- // direct call from user
- dp = new DatawavePrincipal(Lists.newArrayList(user));
- Assert.assertEquals(null, dp.getProxyServers());
-
- // call from finalServer proxying initial caller server1
- dp = new DatawavePrincipal(Lists.newArrayList(server1, finalServer));
- Assert.assertEquals(Arrays.asList(finalConnectionServerSubjectDn), dp.getProxyServers());
-
- // call from finalServer proxying initial caller server1 through server2
- dp = new DatawavePrincipal(Lists.newArrayList(server1, server2, finalServer));
- Assert.assertEquals(Arrays.asList(server2SubjectDn, finalConnectionServerSubjectDn), dp.getProxyServers());
-
- // call from finalServer proxying initial caller server1 through server2 and server3
- dp = new DatawavePrincipal(Lists.newArrayList(server1, server2, server3, finalServer));
- Assert.assertEquals(Arrays.asList(server2SubjectDn, server3SubjectDn, finalConnectionServerSubjectDn), dp.getProxyServers());
-
- // these tests are for cases where a UserType.USER appears anywhere in the proxiedUsers collection
-
- dp = new DatawavePrincipal(Lists.newArrayList(user, server1, server2, server3));
- Assert.assertEquals(Arrays.asList(server1SubjectDn, server2SubjectDn, server3SubjectDn), dp.getProxyServers());
-
- dp = new DatawavePrincipal(Lists.newArrayList(server1, user, server2, server3));
- Assert.assertEquals(Arrays.asList(server1SubjectDn, server2SubjectDn, server3SubjectDn), dp.getProxyServers());
-
- dp = new DatawavePrincipal(Lists.newArrayList(server1, server2, user, server3));
- Assert.assertEquals(Arrays.asList(server1SubjectDn, server2SubjectDn, server3SubjectDn), dp.getProxyServers());
-
- // this case would be very odd -- call from user proxying initial caller server1 through server2 through server3
- dp = new DatawavePrincipal(Lists.newArrayList(server1, server2, server3, user));
- Assert.assertEquals(Arrays.asList(server1SubjectDn, server2SubjectDn, server3SubjectDn), dp.getProxyServers());
- }
-
- private String joinNames(Collection datawaveUsers) {
- return datawaveUsers.stream().map(DatawaveUser::getName).collect(Collectors.joining(" -> "));
- }
-
- @Test
- public void GetNameTest() {
- // direct call from finalServer
- DatawavePrincipal dp = new DatawavePrincipal(Lists.newArrayList(finalServer));
- Assert.assertEquals(joinNames(Lists.newArrayList(finalServer)), dp.getName());
-
- // direct call from user
- dp = new DatawavePrincipal(Lists.newArrayList(user));
- Assert.assertEquals(joinNames(Lists.newArrayList(user)), dp.getName());
-
- // call from finalServer proxying initial caller server1
- dp = new DatawavePrincipal(Lists.newArrayList(server1, finalServer));
- Assert.assertEquals(joinNames(Lists.newArrayList(server1, finalServer)), dp.getName());
-
- // call from finalServer proxying initial caller server1 through server2
- dp = new DatawavePrincipal(Lists.newArrayList(server1, server2, finalServer));
- Assert.assertEquals(joinNames(Lists.newArrayList(server1, server2, finalServer)), dp.getName());
-
- // call from finalServer proxying initial caller server1 through server2 and server3
- dp = new DatawavePrincipal(Lists.newArrayList(server1, server2, server3, finalServer));
- Assert.assertEquals(joinNames(Lists.newArrayList(server1, server2, server3, finalServer)), dp.getName());
-
- // these tests are for cases where a UserType.USER appears anywhere in the proxiedUsers collection
-
- // this first case would be very odd -- call from user proxying initial caller server1 through server2 through server3
- dp = new DatawavePrincipal(Lists.newArrayList(user, server1, server2, server3));
- Assert.assertEquals(joinNames(Lists.newArrayList(user, server1, server2, server3)), dp.getName());
-
- dp = new DatawavePrincipal(Lists.newArrayList(server1, user, server2, server3));
- Assert.assertEquals(joinNames(Lists.newArrayList(user, server1, server2, server3)), dp.getName());
-
- dp = new DatawavePrincipal(Lists.newArrayList(server1, server2, user, server3));
- Assert.assertEquals(joinNames(Lists.newArrayList(user, server1, server2, server3)), dp.getName());
-
- dp = new DatawavePrincipal(Lists.newArrayList(server1, server2, server3, user));
- Assert.assertEquals(joinNames(Lists.newArrayList(user, server1, server2, server3)), dp.getName());
- }
-
- @Test
- public void OrderProxiedUsers() {
-
- // call from finalServer proxying initial caller server1
- Assert.assertEquals(Lists.newArrayList(server1, finalServer), DatawavePrincipal.orderProxiedUsers(Lists.newArrayList(server1, finalServer)));
-
- // call from finalServer proxying initial caller server1 through server2
- Assert.assertEquals(Lists.newArrayList(server1, server2, finalServer),
- DatawavePrincipal.orderProxiedUsers(Lists.newArrayList(server1, server2, finalServer)));
-
- // call from finalServer proxying initial caller server1 through server2 and server3
- Assert.assertEquals(Lists.newArrayList(server1, server2, server3, finalServer),
- DatawavePrincipal.orderProxiedUsers(Lists.newArrayList(server1, server2, server3, finalServer)));
-
- // these tests are for cases where a UserType.USER appears anywhere in the proxiedUsers collection
-
- // this first case would be very odd -- call from user proxying initial caller server1 through server2 through server3
- Assert.assertEquals(Lists.newArrayList(user, server1, server2, server3),
- DatawavePrincipal.orderProxiedUsers(Lists.newArrayList(user, server1, server2, server3)));
-
- Assert.assertEquals(Lists.newArrayList(user, server1, server2, server3),
- DatawavePrincipal.orderProxiedUsers(Lists.newArrayList(server1, user, server2, server3)));
-
- Assert.assertEquals(Lists.newArrayList(user, server1, server2, server3),
- DatawavePrincipal.orderProxiedUsers(Lists.newArrayList(server1, server2, user, server3)));
-
- Assert.assertEquals(Lists.newArrayList(user, server1, server2, server3),
- DatawavePrincipal.orderProxiedUsers(Lists.newArrayList(server1, server2, server3, user)));
- }
-
- @Test
- public void DuplicateUserPreserved() {
- // check that duplicate users are preserved
- DatawavePrincipal dp = new DatawavePrincipal(Lists.newArrayList(server1, server2, server1));
- Assert.assertEquals(3, dp.getProxiedUsers().size());
- Assert.assertEquals(server1, dp.getProxiedUsers().stream().findFirst().get());
- Assert.assertEquals(server2, dp.getProxiedUsers().stream().skip(1).findFirst().get());
- Assert.assertEquals(server1, dp.getProxiedUsers().stream().skip(2).findFirst().get());
- }
-}
diff --git a/web-services/common-util/src/test/java/datawave/security/util/DnUtilsTest.java b/web-services/common-util/src/test/java/datawave/security/util/DnUtilsTest.java
deleted file mode 100644
index f413e84bc67..00000000000
--- a/web-services/common-util/src/test/java/datawave/security/util/DnUtilsTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package datawave.security.util;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.Collection;
-
-import org.junit.Test;
-
-import com.google.common.collect.Lists;
-
-public class DnUtilsTest {
-
- @Test
- public void testBuildNormalizedProxyDN() {
- String expected = "sdn";
- String actual = DnUtils.buildNormalizedProxyDN("SDN", "IDN", null, null);
- assertEquals(expected, actual);
-
- expected = "sdn2";
- actual = DnUtils.buildNormalizedProxyDN("SDN1", "IDN1", "SDN2", "IDN2");
- assertEquals(expected, actual);
-
- expected = "sdn2";
- actual = DnUtils.buildNormalizedProxyDN("SDN1", "IDN1", "SDN2", "IDN2");
- assertEquals(expected, actual);
-
- expected = "sdn2";
- actual = DnUtils.buildNormalizedProxyDN("SDN1", "IDN1", "", "");
- assertEquals(expected, actual);
- }
-
- @Test
- public void testBuildNormalizedDN() {
- Collection expected = Lists.newArrayList("sdn", "idn");
- Collection actual = DnUtils.buildNormalizedDNList("SDN", "IDN", null, null);
- assertEquals(expected, actual);
-
- expected = Lists.newArrayList("sdn2", "idn2", "sdn1", "idn1");
- actual = DnUtils.buildNormalizedDNList("SDN1", "IDN1", "SDN2", "IDN2");
- assertEquals(expected, actual);
-
- expected = Lists.newArrayList("sdn2", "idn2", "sdn3", "idn3", "sdn1", "idn1");
- actual = DnUtils.buildNormalizedDNList("SDN1", "IDN1", "SDN2", "IDN2");
- assertEquals(expected, actual);
-
- expected = Lists.newArrayList("sdn2", "idn2", "sdn3", "idn3", "sdn1", "idn1");
- actual = DnUtils.buildNormalizedDNList("SDN1", "IDN1", "", "");
- assertEquals(expected, actual);
- }
-
- @Test
- public void testGetUserDnFromArray() {
- String userDnForTest = "snd1";
- String[] array = new String[] {userDnForTest, "idn"};
- String userDN = DnUtils.getUserDN(array);
- assertEquals(userDnForTest, userDN);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testTest() {
- String[] dns = new String[] {"sdn"};
- DnUtils.getUserDN(dns, true);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testBuildNormalizedProxyDNTooMissingIssuers() {
- DnUtils.buildNormalizedProxyDN("SDN", "IDN", "SDN2", null);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testBuildNormalizedProxyDNTooFewIssuers() {
- DnUtils.buildNormalizedProxyDN("SDN", "IDN", "SDN2", "IDN2");
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testBuildNormalizedProxyDNTooFewSubjects() {
- DnUtils.buildNormalizedProxyDN("SDN", "IDN", "SDN2", "IDN2");
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testBuildNormalizedProxyDNSubjectEqualsIssuer() {
- DnUtils.buildNormalizedProxyDN("SDN", "IDN", "SDN2", "SDN2");
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testBuildNormalizedProxyDNSubjectDNInIssuer() {
- DnUtils.buildNormalizedProxyDN("SDN", "IDN", "SDN2", "CN=foo,OU=My Department");
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testBuildNormalizedDNListTooMissingIssuers() {
- DnUtils.buildNormalizedDNList("SDN", "IDN", "SDN2", null);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testBuildNormalizedDNListTooFewIssuers() {
- DnUtils.buildNormalizedDNList("SDN", "IDN", "SDN2", "IDN2");
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testBuildNormalizedDNListTooFewSubjects() {
- DnUtils.buildNormalizedDNList("SDN", "IDN", "SDN2", "IDN2");
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testBuildNormalizedDNListSubjectEqualsIssuer() {
- DnUtils.buildNormalizedDNList("SDN", "IDN", "SDN2", "SDN2");
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testBuildNormalizedDNListSubjectDNInIssuer() {
- DnUtils.buildNormalizedDNList("SDN", "IDN", "SDN2", "CN=foo,OU=My Department");
- }
-
-}
diff --git a/web-services/common-util/src/test/java/datawave/security/util/WSAuthorizationsUtilTest.java b/web-services/common-util/src/test/java/datawave/security/util/WSAuthorizationsUtilTest.java
index f3c789a350c..53f77b84cc4 100644
--- a/web-services/common-util/src/test/java/datawave/security/util/WSAuthorizationsUtilTest.java
+++ b/web-services/common-util/src/test/java/datawave/security/util/WSAuthorizationsUtilTest.java
@@ -43,7 +43,7 @@ public class WSAuthorizationsUtilTest {
@Before
public void initialize() {
- System.setProperty(DnUtils.NPE_OU_PROPERTY, "iamnotaperson");
+ System.setProperty(DnProperties.NPE_OU_PROPERTY, "iamnotaperson");
methodAuths = "A,C";
userAuths = new HashSet<>();
userAuths.add(Sets.newHashSet("A", "C", "D"));
diff --git a/web-services/common/pom.xml b/web-services/common/pom.xml
index 26efac19a8d..6787435589c 100644
--- a/web-services/common/pom.xml
+++ b/web-services/common/pom.xml
@@ -43,6 +43,10 @@
gov.nsa.datawavedatawave-in-memory-accumulo
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.coredatawave-core-annotation
@@ -176,8 +180,8 @@
provided
- javax.enterprise
- cdi-api
+ jakarta.enterprise
+ jakarta.enterprise.cdi-apiprovided
@@ -207,7 +211,12 @@
org.jboss.resteasy
- resteasy-jaxrs
+ resteasy-core
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-core-spiprovided
@@ -232,7 +241,7 @@
org.jboss.spec.javax.transaction
- jboss-transaction-api_1.2_spec
+ jboss-transaction-api_1.3_specprovided
@@ -240,11 +249,6 @@
weld-core-implprovided
-
- org.picketbox
- picketbox
- provided
- org.apache.accumuloaccumulo-tserver
diff --git a/web-services/common/src/main/java/datawave/webservice/common/remote/RemoteHttpService.java b/web-services/common/src/main/java/datawave/webservice/common/remote/RemoteHttpService.java
index e35d19ad2bc..a6d1ef3d241 100644
--- a/web-services/common/src/main/java/datawave/webservice/common/remote/RemoteHttpService.java
+++ b/web-services/common/src/main/java/datawave/webservice/common/remote/RemoteHttpService.java
@@ -54,7 +54,6 @@
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
-import org.jboss.security.JSSESecurityDomain;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xbill.DNS.DClass;
@@ -75,6 +74,7 @@
import datawave.security.authorization.DatawavePrincipal;
import datawave.security.authorization.JWTTokenHandler;
import datawave.security.authorization.JWTTokenHandler.TtlMode;
+import datawave.security.cert.SSLStores;
import datawave.security.util.DnUtils;
import datawave.webservice.common.exception.DatawaveWebApplicationException;
import datawave.webservice.common.json.ObjectMapperDecorator;
@@ -96,7 +96,7 @@ public abstract class RemoteHttpService {
private AtomicInteger activeExecutions = new AtomicInteger(0);
@Inject
- private JSSESecurityDomain jsseSecurityDomain;
+ private SSLStores sslStores;
@Resource
private ManagedExecutorService executorService;
@@ -110,8 +110,8 @@ public abstract class RemoteHttpService {
private RemoteHttpServiceConfiguration config = new RemoteHttpServiceConfiguration();
- public void setJsseSecurityDomain(JSSESecurityDomain jsseSecurityDomain) {
- this.jsseSecurityDomain = jsseSecurityDomain;
+ public void setSslStores(SSLStores sslStores) {
+ this.sslStores = sslStores;
}
public void setExecutorService(ManagedExecutorService executorService) {
@@ -163,10 +163,10 @@ protected void init() {
try {
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
- ctx.init(jsseSecurityDomain.getKeyManagers(), jsseSecurityDomain.getTrustManagers(), null);
+ ctx.init(sslStores.getKeyManagers(), sslStores.getTrustManagers(), null);
- String alias = jsseSecurityDomain.getKeyStore().aliases().nextElement();
- X509KeyManager keyManager = (X509KeyManager) jsseSecurityDomain.getKeyManagers()[0];
+ String alias = sslStores.getKeyStore().aliases().nextElement();
+ X509KeyManager keyManager = (X509KeyManager) sslStores.getKeyManagers()[0];
X509Certificate[] certs = keyManager.getCertificateChain(alias);
Key signingKey = keyManager.getPrivateKey(alias);
diff --git a/web-services/common/src/test/java/datawave/webservice/common/remote/TestJSSESecurityDomain.java b/web-services/common/src/test/java/datawave/webservice/common/remote/TestSSLStores.java
similarity index 65%
rename from web-services/common/src/test/java/datawave/webservice/common/remote/TestJSSESecurityDomain.java
rename to web-services/common/src/test/java/datawave/webservice/common/remote/TestSSLStores.java
index be369fcb5e2..83a7982c429 100644
--- a/web-services/common/src/test/java/datawave/webservice/common/remote/TestJSSESecurityDomain.java
+++ b/web-services/common/src/test/java/datawave/webservice/common/remote/TestSSLStores.java
@@ -3,28 +3,26 @@
import java.io.File;
import java.io.FileOutputStream;
import java.net.Socket;
-import java.security.Key;
import java.security.KeyStore;
import java.security.Principal;
import java.security.PrivateKey;
-import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
-import java.util.Properties;
import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509KeyManager;
-import org.jboss.security.JSSESecurityDomain;
+import datawave.security.cert.SSLStores;
-public class TestJSSESecurityDomain implements JSSESecurityDomain {
- private final PrivateKey privKey;
+public class TestSSLStores implements SSLStores {
+
+ private final PrivateKey privateKey;
private final X509Certificate[] chain;
private final String alias;
private final char[] keyPass;
- public TestJSSESecurityDomain(String alias, PrivateKey privKey, char[] keyPass, X509Certificate[] chain) {
- this.privKey = privKey;
+ public TestSSLStores(String alias, PrivateKey privateKey, char[] keyPass, X509Certificate[] chain) {
+ this.privateKey = privateKey;
this.chain = chain;
this.alias = alias;
this.keyPass = keyPass;
@@ -35,7 +33,7 @@ public KeyStore getKeyStore() throws SecurityException {
try {
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null, null);
- keyStore.setKeyEntry(alias, privKey, keyPass, chain);
+ keyStore.setKeyEntry(alias, privateKey, keyPass, chain);
File file = File.createTempFile("keystore", ".jks");
file.deleteOnExit();
keyStore.store(new FileOutputStream(file), keyPass);
@@ -76,7 +74,7 @@ public X509Certificate[] getCertificateChain(String alias) {
@Override
public PrivateKey getPrivateKey(String alias) {
- return privKey;
+ return privateKey;
}
};
return managers;
@@ -87,7 +85,7 @@ public KeyStore getTrustStore() throws SecurityException {
try {
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null, null);
- keyStore.setKeyEntry(alias, privKey, keyPass, chain);
+ keyStore.setKeyEntry(alias, privateKey, keyPass, chain);
keyStore.store(new FileOutputStream(".keystore"), keyPass);
return keyStore;
} catch (Exception e) {
@@ -99,54 +97,4 @@ public KeyStore getTrustStore() throws SecurityException {
public TrustManager[] getTrustManagers() throws SecurityException {
return new TrustManager[0];
}
-
- @Override
- public void reloadKeyAndTrustStore() throws Exception {
-
- }
-
- @Override
- public String getServerAlias() {
- return null;
- }
-
- @Override
- public String getClientAlias() {
- return null;
- }
-
- @Override
- public boolean isClientAuth() {
- return false;
- }
-
- @Override
- public Key getKey(String s, String s1) throws Exception {
- return null;
- }
-
- @Override
- public Certificate getCertificate(String s) throws Exception {
- return null;
- }
-
- @Override
- public String[] getCipherSuites() {
- return new String[0];
- }
-
- @Override
- public String[] getProtocols() {
- return new String[0];
- }
-
- @Override
- public Properties getAdditionalProperties() {
- return null;
- }
-
- @Override
- public String getSecurityDomain() {
- return null;
- }
}
diff --git a/web-services/deploy/application/README.md b/web-services/deploy/application/README.md
new file mode 100644
index 00000000000..eb028485133
--- /dev/null
+++ b/web-services/deploy/application/README.md
@@ -0,0 +1,42 @@
+# Overview
+
+This project helps prepares and assembles the EAR for deployment to Wildfly. It also prepares artifacts that should be copied over onto the Wildfly distribution once unpacked.
+
+## JBOSS Modules
+
+Wildfly uses a modular class loading system through the use of JBOSS modules. Each module can define the libraries it provides, as well as dependencies on other modules in order to have the modules from that module added to its class path. In order to avoid classloader conflicts, it is essential to have commonly used libraries isolated in their own modules, and added as dependencies in other modules or deployments that require access to the classes.
+
+In addition to the Wildfly application modules, EAR deployments are treated as modules, and are defined by the following rules:
+
+1. The `lib/` directory of the EAR is a single module called the parent module.
+2. Each WAR deployment within the EAR is a single module.
+3. Each EJB JAR deployment within the EAR is a single module.
+
+Managing the module dependencies for the Datawave EAR deployment is done through the [jboss-deployment.xml](src/main/application/META-INF/jboss-deployment-structure.xml). **IMPORTANT**: any dependencies that are provided to the EAR deployment through a module should be configured with a `provided` scope in the `webservice-parent` [pom.xml](../../pom.xml) to prevent them from being included in the EAR's `/lib` folder.
+
+### Custom Modules
+
+We create several JBOSS modules that will be part of Wildfly upon startup. These modules can be found in the project [modules](src/main/wildfly/overlay/modules) directory. Most of these modules are self-explanatory and are, except for the module `datawave.webservices.datawave-security-elytron-module`, common dependencies between other modules and the Datawave deployment.
+
+* [datawave.webservice.datawave-security-elytron-module](src/main/wildfly/overlay/modules/datawave/webservice/datawave-security-elytron-module)
+ * This module contains the custom Wildfly security components that create the Datawave authentication and authorization workflow leveraging Elytron. These classes must be deployed as a separate JBOSS module. This module should not be imported as a dependency to the Datawave deployment.
+* [datawave.webservice.datawave-security-elytron](src/main/wildfly/overlay/modules/datawave/webservice/datawave-security-elytron)
+ * Contains security-related artifacts that are commonly used between the Datawave deployment and the module `datawave.webservice.datawave-security-elytron-module`.
+* [datawave.commons.datawave-commons-security](src/main/wildfly/overlay/modules/datawave/commons/datawave-commons-security)
+ * Contains security-related artifacts that are commonly used between the Datawave deployment, the module `datawave.webservice.datawave-security-elytron-module`, and the microservices.
+* [com.fasterxml.jackson.datatype.jackson-datatype-guava](src/main/wildfly/overlay/modules/com/fasterxml/jackson/datatype/jackson-datatype-guava)
+* [com.fasterxml.jackson.module.jackson-module-jaxb-annotations](src/main/wildfly/overlay/modules/com/fasterxml/jackson/module/jackson-module-jaxb-annotations)
+* [com.github.ben-manes.caffeine](src/main/wildfly/overlay/modules/com/github/ben-manes/caffeine)
+* [io.jsonwebtoken.jjwt-api](src/main/wildfly/overlay/modules/io/jsonwebtoken/jjwt-api)
+* [io.jsonwebtoken.jjwt-impl](src/main/wildfly/overlay/modules/io/jsonwebtoken/jjwt-impl)
+* [io.jsonwebtoken.jjwt-jackson](src/main/wildfly/overlay/modules/io/jsonwebtoken/jjwt-jackson)
+* [org.apache.commons.commons-text](src/main/wildfly/overlay/modules/org/apache/commons/commons-text)
+* [org.apache.hadoop.common](src/main/wildfly/overlay/modules/org/apache/hadoop/common)
+
+### Overridden Modules
+
+We also override the library versions of several modules that are part of the core Wildfly application modules in order to synchronize the versions used by Datawave and Wildfly. These modules may get automatically added to deployments by Wildfly and may be dependencies of other core modules. The versions are overridden by copying updated versions of the libraries and `module.xml` files to the [modules/system/layers/base](src/main/wildfly/overlay/modules/system/layers/base) directory.
+
+- [org.slf4j](src/main/wildfly/overlay/modules/system/layers/base/org/slf4j)
+- [org.slf4j.impl](src/main/wildfly/overlay/modules/system/layers/base/org/slf4j/impl)
+- [org.apache.commons.lang3](src/main/wildfly/overlay/modules/system/layers/base/org/apache/commons/lang3)
diff --git a/web-services/deploy/application/pom.xml b/web-services/deploy/application/pom.xml
index 1ca5fd78d00..e559e336265 100644
--- a/web-services/deploy/application/pom.xml
+++ b/web-services/deploy/application/pom.xml
@@ -10,6 +10,10 @@
pom${project.artifactId}
+
+ ${project.build.directory}/wildfly/overlay/modules
+
+ ${base.wildfly.modules.dir}/system/layers/base${project.build.finalName}2.0.2.Final
@@ -571,6 +575,178 @@
maven-dependency-plugin
+
+
+
+ copy-org-apache-commons-lang3
+
+ copy
+
+ process-resources
+
+ ${base.wildfly.system.modules.dir}/org/apache/commons/lang3/main
+
+
+ org.apache.commons
+ commons-lang3
+ ${version.commons-lang3}
+
+
+
+
+
+
+
+ copy-org-apache-commons-text
+
+ copy
+
+ process-resources
+
+ ${base.wildfly.modules.dir}/org/apache/commons/commons-text/main
+
+
+ org.apache.commons
+ commons-text
+ ${version.commons-text}
+
+
+
+
+
+
+
+ copy-io-jsonwebtoken-jjwt-api
+
+ copy
+
+ process-resources
+
+ ${base.wildfly.modules.dir}/io/jsonwebtoken/jjwt-api/main
+
+
+ io.jsonwebtoken
+ jjwt-api
+ ${version.jjwt}
+
+
+
+
+
+
+
+ copy-io-jsonwebtoken-jjwt-impl
+
+ copy
+
+ process-resources
+
+ ${base.wildfly.modules.dir}/io/jsonwebtoken/jjwt-impl/main
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ ${version.jjwt}
+
+
+
+
+
+
+
+ copy-com-fasterxml-jackson-datatype-jackson-datatype-guava
+
+ copy
+
+ process-resources
+
+ ${base.wildfly.modules.dir}/com/fasterxml/jackson/datatype/jackson-datatype-guava/main
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-guava
+ ${version.jackson}
+
+
+
+
+
+
+
+ copy-com-fasterxml-jackson-module-jackson-module-jaxb-annotations
+
+ copy
+
+ process-resources
+
+ ${base.wildfly.modules.dir}/com/fasterxml/jackson/module/jackson-module-jaxb-annotations/main
+
+
+ com.fasterxml.jackson.module
+ jackson-module-jaxb-annotations
+ ${version.jackson}
+
+
+
+
+
+
+
+ copy-datawave-commons-security
+
+ copy
+
+ process-resources
+
+ ${base.wildfly.modules.dir}/datawave/commons/datawave-commons-security/main
+
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ ${project.version}
+
+
+
+
+
+
+
+ copy-datawave-ws-security-elytron
+
+ copy
+
+ process-resources
+
+ ${base.wildfly.modules.dir}/datawave/webservice/datawave-security-elytron/main
+
+
+ gov.nsa.datawave.webservices
+ datawave-ws-security-elytron
+ ${project.version}
+
+
+
+
+
+
+
+ copy-datawave-ws-security-elytron-module
+
+ copy
+
+ process-resources
+
+ ${base.wildfly.modules.dir}/datawave/webservice/datawave-security-elytron-module/main
+
+
+ gov.nsa.datawave.webservices
+ datawave-ws-security-elytron-module
+ ${project.version}
+
+
+
+
+
@@ -601,7 +777,7 @@
process-resources
- ${project.build.directory}/wildfly/overlay/modules/org/apache/hadoop/common/main
+ ${base.wildfly.modules.dir}/org/apache/hadoop/common/mainorg.apache.hadoop
@@ -649,10 +825,6 @@
org.apache.commonscommons-configuration2
-
- org.apache.commons
- commons-text
- javax.servletservlet-api
@@ -682,14 +854,18 @@
javax.inject1
-
- javax.enterprise
- cdi-api
-
-
+
+ jakarta.enterprise
+ jakarta.enterprise.cdi-api
+
+ org.jboss.resteasy
- resteasy-jaxrs
-
+ resteasy-core
+
+
+ org.jboss.resteasy
+ resteasy-core-spi
+ org.jboss.spec.javax.annotationjboss-annotations-api_1.3_spec
@@ -720,7 +896,7 @@
org.jboss.spec.javax.transaction
- jboss-transaction-api_1.2_spec
+ jboss-transaction-api_1.3_specxerces
@@ -729,6 +905,7 @@
+
copy-slf4j-api-dependencies
@@ -736,7 +913,7 @@
process-resources
- ${project.build.directory}/wildfly/overlay/modules/system/layers/base/org/slf4j/main
+ ${base.wildfly.system.modules.dir}/org/slf4j/mainorg.slf4j
@@ -745,6 +922,7 @@
+
copy-slf4j-impl-dependencies
@@ -752,7 +930,7 @@
process-resources
- ${project.build.directory}/wildfly/overlay/modules/system/layers/base/org/slf4j/impl/main
+ ${base.wildfly.system.modules.dir}/org/slf4j/impl/mainorg.jboss.slf4j
diff --git a/web-services/deploy/application/src/main/application/META-INF/jboss-deployment-structure.xml b/web-services/deploy/application/src/main/application/META-INF/jboss-deployment-structure.xml
index 0cf5fdbd952..325265e8984 100644
--- a/web-services/deploy/application/src/main/application/META-INF/jboss-deployment-structure.xml
+++ b/web-services/deploy/application/src/main/application/META-INF/jboss-deployment-structure.xml
@@ -1,14 +1,30 @@
-
+
false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/web-services/deploy/application/src/main/wildfly/add-datawave-configuration.cli b/web-services/deploy/application/src/main/wildfly/add-datawave-configuration.cli
index 21ef1387e7e..7fce0ca230e 100644
--- a/web-services/deploy/application/src/main/wildfly/add-datawave-configuration.cli
+++ b/web-services/deploy/application/src/main/wildfly/add-datawave-configuration.cli
@@ -9,16 +9,7 @@ batch
embed-server --server-config=standalone-full.xml
# Add the MySQL Connector as a module
-module add --name=com.mysql.driver --dependencies=javax.api,javax.transaction.api --resources=mysql/mysql-connector-j-${version.mysql-connector}.jar
-
-# Remove the Elytron security subsystem entirely. Our JAAS security setup doesn't work
-# with it due to some bugs (the RunAsLoginModule doesn't work -- WFLY-11892) so we'll
-# just continue to use the deprecated system for now
-/subsystem=elytron:remove
-
-# Remove the metrics subsystem since it is currently buggy (thread-safety issue) and can cause deployment failures
-# See WFLY-11801 for details.
-/subsystem=microprofile-metrics-smallrye:remove
+module add --name=com.mysql.driver --dependencies=javax.api,javax.transaction.api,com.fasterxml.jackson.core.jackson-core --resources=mysql/mysql-connector-j-${version.mysql-connector}.jar
# Change default ports to listen on all interfaces (can still override on command line)
/interface=public/:write-attribute(name=inet-address,value="${jboss.bind.address:0.0.0.0}")
@@ -94,7 +85,18 @@ module add --name=com.mysql.driver --dependencies=javax.api,javax.transaction.ap
/system-property=dw.model.defaultTableName:add(value=${table.name.metadata})
/system-property=dw.basemaps:add(value="${basemaps}")
-# proxied entities to be pruned from credentials
+# Security SSL Properties
+/system-property=dw.ssl.context.info.keyStoreURL:add(value=file://${KEYSTORE})
+/system-property=dw.ssl.context.info.keyStoreType:add(value="${KEYSTORE_TYPE}")
+/system-property=dw.ssl.context.info.keyStorePassword:add(value="${KEYSTORE_PASSWORD}")
+/system-property=dw.ssl.context.info.trustStoreURL:add(value=file://${TRUSTSTORE})
+/system-property=dw.ssl.context.info.trustStoreType:add(value="${TRUSTSTORE_TYPE}")
+/system-property=dw.ssl.context.info.trustStorePassword:add(value="${TRUSTSTORE_PASSWORD}")
+
+# Required to allow the elytron module to lookup a SecurityEJBProvider instance from JNDI.
+/system-property=dw.security.ejb.provider.jndi:add(value=java:global/datawave-ws-deploy-application-${project.version}-compose/gov.nsa.datawave.webservices-datawave-ws-security-${project.version}/SecurityEJBProviderImpl)
+
+# Proxied entities to be pruned from credentials.
/system-property=dw.trusted.proxied.entities:add(value="${trusted.proxied.entities}")
# Enable/disable the test authorization service (enable for development use only)
@@ -147,41 +149,128 @@ module add --name=com.mysql.driver --dependencies=javax.api,javax.transaction.ap
/system-property=jexl.function.namespace.registry:add(value="datawave.query.jexl.functions.JexlFunctionNamespaceRegistry.class")
#
-# Add cache container for the security realm
+# Configure SSL.
#
-/subsystem=infinispan/cache-container=security:add
-/subsystem=infinispan/cache-container=security/local-cache=security:add
-/subsystem=infinispan/cache-container=security:write-attribute(name=default-cache,value=security)
-/subsystem=infinispan/cache-container=security/local-cache=security/component=expiration:add(lifespan=300000)
+
+# Create the server key store.
+/subsystem=elytron/key-store=serverKeyStore:add( \
+ path=${KEYSTORE}, \
+ credential-reference={clear-text="${KEYSTORE_PASSWORD}"}, \
+ type=${KEYSTORE_TYPE})
+
+# Create the server key manager.
+/subsystem=elytron/key-manager=serverKeyManager:add( \
+ key-store=serverKeyStore, \
+ credential-reference={clear-text="${KEYSTORE_PASSWORD}"})
+
+# Create the server trust store.
+/subsystem=elytron/key-store=serverTrustStore:add( \
+ path=${TRUSTSTORE}, \
+ credential-reference={clear-text="${TRUSTSTORE_PASSWORD}"}, \
+ type=${TRUSTSTORE_TYPE})
+
+# Create the server trust manager.
+/subsystem=elytron/trust-manager=serverTrustManager:add( \
+ key-store=serverTrustStore)
+
+# Create the server SSL context. The protocols "TLSv1.1" and "TLSv1.2" will be supported, a client certificate will be required on an SSL handshake, and the
+# security-domain 'datawaveElytron' will be used for authentication during the SSL session establishment.
+/subsystem=elytron/server-ssl-context=serverSSLContext:add( \
+ key-manager=serverKeyManager, \
+ trust-manager=serverTrustManager, \
+ protocols=["TLSv1.1","TLSv1.2"], \
+ want-client-auth=true)
#
-# Add security realm and domain
+# Configure the datawave security domain.
#
-/core-service=management/security-realm=SSLRealm/:add
-/core-service=management/security-realm=SSLRealm/server-identity=ssl/:add(enabled-protocols=["TLSv1.1","TLSv1.2"],keystore-path=${KEYSTORE},keystore-provider=${KEYSTORE_TYPE},keystore-password="${KEYSTORE_PASSWORD}")
-/core-service=management/security-realm=SSLRealm/authentication=truststore/:add(keystore-path=${TRUSTSTORE},keystore-provider=${TRUSTSTORE_TYPE},keystore-password="${TRUSTSTORE_PASSWORD}")
-/core-service=management/security-realm=SSLRealm/authentication=jaas/:add(name=datawave)
-
-/subsystem=security/security-domain=datawave:add(cache-type=infinispan)
-/subsystem=security/security-domain=datawave/authentication=classic:add
-/subsystem=security/security-domain=datawave/authentication=classic/login-module=Remoting:add(code=Remoting,flag=optional,module-options={password-stacking=useFirstPass})
-/subsystem=security/security-domain=datawave/authentication=classic/login-module=RunAs:add(code=RunAs,flag=required,module-options={roleName=InternalUser})
-/subsystem=security/security-domain=datawave/authentication=classic/login-module=Datawave:add(code=datawave.security.login.DatawavePrincipalLoginModule,flag=required,module-options={principalClass=datawave.security.authorization.DatawavePrincipal,verifier=datawave.security.login.DatawaveCertVerifier,ocspLevel=off,allowUserProxying=false,trustedHeaderLogin="${dw.trusted.header.authentication:false}"})
-/subsystem=security/security-domain=datawave/jsse=classic:add(keystore={type="${KEYSTORE_TYPE}",password="${KEYSTORE_PASSWORD}",url="file://${KEYSTORE}"},truststore={type="${TRUSTSTORE_TYPE}",password="${TRUSTSTORE_PASSWORD}",url="file://${TRUSTSTORE}"})
-/subsystem=security/security-domain=JmsXARealm:add(cache-type=default)
-/subsystem=security/security-domain=JmsXARealm/authentication=classic:add
-/subsystem=security/security-domain=JmsXARealm/authentication=classic/login-module=ConfiguredIdentity:add(code=ConfiguredIdentity,flag=required,module-options={principal=${dw.hornetq.system.userName},userName=${dw.hornetq.system.userName},password=${dw.hornetq.system.password},managedConnectionFactoryName="jboss.jca:service=TxCM,name=JmsXA"})
-
-/subsystem=security/security-domain=datawave-client:add(cache-type=infinispan)
-/subsystem=security/security-domain=datawave-client/authentication=classic:add
-/subsystem=security/security-domain=datawave-client/authentication=classic/login-module=ClientCert:add(code=datawave.security.login.ClientCertLoginModule,flag=required,module-options={password-stacking=useFirstPass})
-/subsystem=security/security-domain=datawave-client/authentication=classic/login-module=Client:add(code=Client,flag=required,module-options={password-stacking=useFirstPass,restore-login-identity=true})
+# Create a custom role decoder that will decode the roles a user has from a DatawavePrincipal. The decoder can be
+# configured via the configuration attribute. See the class datawave.security.realm.DatawaveRoleDecoder for supported
+# configuration options.
+/subsystem=elytron/custom-role-decoder=datawaveRoleDecoder:add( \
+ class-name=datawave.security.realm.DatawaveRoleDecoder, \
+ module="datawave.webservice.datawave-security-elytron-module", \
+ configuration={ \
+ })
+
+# Create a custom evidence decoder that will decode a DatawavePrincipal from evidence. The evidence decoder can be configured via the configuration attribute.
+# See the class datawave.security.realm.DatawaveEvidenceDecoder for supported configuration options.
+/subsystem=elytron/custom-evidence-decoder=datawaveEvidenceDecoder:add( \
+ class-name=datawave.security.realm.DatawaveEvidenceDecoder, \
+ module="datawave.webservice.datawave-security-elytron-module", \
+ configuration={ \
+ jwtEnabled="${dw.jwt.header.authentication:false}", \
+ trustedHeadersEnabled="${dw.trusted.header.authentication:false}", \
+ maxCacheEntries="-1", \
+ maxCacheAge="-1" \
+ })
+
+# Create a custom realm that handles preparing the DatawavePrincipal for authentication. The realm can be configured via
+# the configuration attribute. See the class datawave.security.realm.DatawaveSecurityRealm for supported configuration
+# options.
+/subsystem=elytron/custom-realm=datawaveRealm:add( \
+ class-name=datawave.security.realm.DatawaveSecurityRealm, \
+ module="datawave.webservice.datawave-security-elytron-module", \
+ configuration={ \
+ certVerifier="datawave.security.cert.DatawaveCertVerifier", \
+ oscpLevel="off" \
+ })
+
+# Create and configure the security domain 'datawaveElytron' that will handle the authentication and authorization.
+/subsystem=elytron/security-domain=datawaveElytron:add( \
+ default-realm=datawaveRealm, \
+ realms=[{realm=datawaveRealm}], \
+ role-decoder=datawaveRoleDecoder, \
+ evidence-decoder=datawaveEvidenceDecoder, \
+ permission-mapper=default-permission-mapper)
+
+#
# Configure the HTTP/HTTPS listener for undertow
+#
+# Switch https-listener from the legacy security-realm to the Elytron server ssl context.
+/subsystem=undertow/server=default-server/https-listener=https/:undefine-attribute(name=security-realm)
+/subsystem=undertow/server=default-server/https-listener=https/:write-attribute(name=ssl-context,value=serverSSLContext)
+
+# Ensure that we log the start time.
/subsystem=undertow/server=default-server/http-listener=default/:write-attribute(name=record-request-start-time,value=true)
/subsystem=undertow/server=default-server/https-listener=https/:write-attribute(name=record-request-start-time,value=true)
-/subsystem=undertow/server=default-server/https-listener=https/:write-attribute(name=security-realm,value=SSLRealm)
-/subsystem=undertow/server=default-server/https-listener=https/:write-attribute(name=verify-client,value=REQUESTED)
+
+#
+# Configure the authentication mechanism for Elytron.
+#
+/subsystem=elytron/service-loader-http-server-mechanism-factory=datawaveMechanismFactory:add( \
+ module=datawave.webservice.datawave-security-elytron-module)
+# Wrap the mechanism factory in a configurable factory so that we can pass in custom configuration options in the
+# properties attribute. See the class datawave.security.auth.DatawaveHttpAuthenticationMechanism for supported
+# configuration options.
+/subsystem=elytron/configurable-http-server-mechanism-factory=configurableDatawaveMechanismFactory:add( \
+ http-server-mechanism-factory=datawaveMechanismFactory, \
+ properties={ \
+ })
+
+# Create an authentication factory that references the configurable mechanism factory.
+/subsystem=elytron/http-authentication-factory=datawaveAuthenticationFactory:add( \
+ http-server-mechanism-factory=configurableDatawaveMechanismFactory, \
+ security-domain=datawaveElytron, \
+ mechanism-configurations=[{ \
+ mechanism-name=DATAWAVE-AUTH, \
+ mechanism-realm-configurations=[{realm-name=datawave}] \
+ }])
+
+#
+# Ensure undertow is configured to use the custom authentication mechanism.
+#
+
+# Configure a security domain for the application that uses the datawave mechanism factory. Override the deployment
+# config in order to support the use of DATAWAVE-AUTH instead of BASIC only by default.
+/subsystem=undertow/application-security-domain=datawave:add( \
+ http-authentication-factory=datawaveAuthenticationFactory, \
+ override-deployment-config=true)
+
+# Ensure the default application security domain is 'datawave'.
+/subsystem=undertow:write-attribute( \
+ name=default-security-domain, value="datawave")
#
# Set up ActiveMQ, JMS Topics/Queues/DLQs
@@ -201,6 +290,8 @@ jms-queue add --queue-address=AccumuloTableCacheDLQ --entries=queue/AccumuloTabl
#
# EJB subsystem configuration
#
+# Map the 'datawaveElytron' security domain to the name 'datawave' and make it discoverable by EJBs.
+/subsystem=ejb3/application-security-domain=datawave:add(security-domain=datawaveElytron)
/subsystem=ejb3/:write-attribute(name=in-vm-remote-interface-invocation-pass-by-value,value=false)
/subsystem=ejb3/thread-pool=default/:write-attribute(name=max-threads,value=${jboss.ejb3.async.threads})
/subsystem=ejb3/strict-max-bean-instance-pool=mdb-strict-max-pool/:undefine-attribute(name=derive-size)
@@ -216,11 +307,32 @@ jms-queue add --queue-address=AccumuloTableCacheDLQ --entries=queue/AccumuloTabl
/subsystem=ee/managed-executor-service=default:write-attribute(name=max-threads,value=${jboss.managed.executor.service.default.max.threads})
# Configure the JDBC DataSource used by MySQL
-/subsystem=datasources/jdbc-driver=mysql:add(driver-name=mysql,driver-module-name=com.mysql.driver)
-/subsystem=datasources/data-source=CachedResultsDS:add(jndi-name=java:jboss/datasources/CachedResultsDS,connection-url="jdbc:mysql://${mysql.host}:3306/${mysql.dbname}?zeroDateTimeBehavior=convertToNull",min-pool-size=${mysql.pool.min.size},max-pool-size=${mysql.pool.max.size},blocking-timeout-wait-millis=5000,idle-timeout-minutes=15,exception-sorter-class-name=org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter,valid-connection-checker-class-name=org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker,user-name=${mysql.user.name},password=${mysql.user.password},driver-name=mysql)
+# Add a driver for MySQL.
+/subsystem=datasources/jdbc-driver=mysql:add( \
+ driver-name=mysql, \
+ driver-module-name=com.mysql.driver)
+
+# Add a MySQL datasource for caching results.
+/subsystem=datasources/data-source=CachedResultsDS:add( \
+ jndi-name=java:jboss/datasources/CachedResultsDS, \
+ connection-url="jdbc:mysql://${mysql.host}:3306/${mysql.dbname}?zeroDateTimeBehavior=convertToNull", \
+ min-pool-size=${mysql.pool.min.size}, \
+ max-pool-size=${mysql.pool.max.size}, \
+ blocking-timeout-wait-millis=5000, \
+ idle-timeout-minutes=15, \
+ exception-sorter-class-name=org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter, \
+ valid-connection-checker-class-name=org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker, \
+ user-name=${mysql.user.name}, \
+ password=${mysql.user.password}, \
+ driver-name=mysql)
# Configure the H2 DataSource used by the DatabaseUserService
-/subsystem=datasources/data-source=DatabaseUserServiceDS:add(jndi-name=java:jboss/datasources/DatabaseUserServiceDS,connection-url="jdbc:h2:${jboss.server.config.dir}/h2/databaseDatawaveUsers",user-name=sa,password=sa,driver-name=h2)
+/subsystem=datasources/data-source=DatabaseUserServiceDS:add( \
+ jndi-name=java:jboss/datasources/DatabaseUserServiceDS, \
+ connection-url="jdbc:h2:${jboss.server.config.dir}/h2/databaseDatawaveUsers", \
+ user-name=sa, \
+ password=sa, \
+ driver-name=h2)
#
# Configure custom loggers and categories
@@ -235,6 +347,7 @@ jms-queue add --queue-address=AccumuloTableCacheDLQ --entries=queue/AccumuloTabl
/subsystem=logging/periodic-rotating-file-handler=MODIFICATION_LOG/:add(level=TRACE,suffix=".yyyy-MM-dd",append=true,autoflush=true,named-formatter=PATTERN,file={relative-to=>"jboss.server.log.dir", path=>"ModificationService.log"})
/subsystem=logging/periodic-rotating-file-handler=DISTRIBUTED_QUERY_LOG/:add(level=TRACE,suffix=".yyyy-MM-dd",append=true,autoflush=true,named-formatter=PATTERN,file={relative-to=>"jboss.server.log.dir", path=>"DistributedQueryService.log"})
/subsystem=logging/periodic-rotating-file-handler=RESTEASY_LOG/:add(level=TRACE,suffix=".yyyy-MM-dd",append=true,autoflush=true,named-formatter=PATTERN,file={relative-to=>"jboss.server.log.dir", path=>"RestEasy.log"})
+/subsystem=logging/periodic-rotating-file-handler=JBOSS_LOG/:add(level=TRACE,suffix=".yyyy-MM-dd",append=true,autoflush=true,named-formatter=PATTERN,file={relative-to=>"jboss.server.log.dir", path=>"JBoss.log"})
/subsystem=logging/logger=datawave.query:add(use-parent-handlers=false,handlers=["QUERY_LOG"],level=DEBUG)
/subsystem=logging/logger=datawave.webservice.common.cache.SharedCacheCoordinator:add(use-parent-handlers=false,handlers=["QUERY_LOG"],level=INFO)
@@ -257,6 +370,10 @@ jms-queue add --queue-address=AccumuloTableCacheDLQ --entries=queue/AccumuloTabl
/subsystem=logging/logger=datawave.security:add(use-parent-handlers=false,handlers=["SECURITY_LOG"])
/subsystem=logging/logger=org.jboss.security.auth:add(use-parent-handlers=false,handlers=["SECURITY_LOG"])
/subsystem=logging/logger=org.jboss.security.authorization.modules.ejb.EJBPolicyModuleDelegate:add(use-parent-handlers=false,handlers=["SECURITY_LOG"])
+# Uncomment the following three lines to also capture logging from the Wildfly Elytron framework if desired.
+#/subsystem=logging/logger=org.wildfly.security:add(use-parent-handlers=false,handlers=["SECURITY_LOG"])
+#/subsystem=logging/logger=org.wildfly.security.tls:add(use-parent-handlers=false,handlers=["SECURITY_LOG"])
+#/subsystem=logging/logger=org.wildfly.extension.elytron:add(use-parent-handlers=false,handlers=["SECURITY_LOG"])
/subsystem=logging/logger=datawave.webservice.modification:add(use-parent-handlers=false,handlers=["MODIFICATION_LOG"])
/subsystem=logging/logger=datawave.webservice.query.distributed:add(use-parent-handlers=false,handlers=["DISTRIBUTED_QUERY_LOG"])
/subsystem=logging/logger=org.jboss.resteasy.core:add(use-parent-handlers=false,handlers=["RESTEASY_LOG"])
@@ -265,6 +382,10 @@ jms-queue add --queue-address=AccumuloTableCacheDLQ --entries=queue/AccumuloTabl
/subsystem=logging/logger=org.jboss.resteasy.specimpl:add(use-parent-handlers=false,handlers=["RESTEASY_LOG"])
/subsystem=logging/logger=datawave.resteasy.interceptor:add(use-parent-handlers=false,handlers=["RESTEASY_LOG"])
+# Add trace logging for JBoss module class loading. The JBoss.log file will get big fast if activated, so uncomment this
+# only when you need to see exactly which libraries/modules Wildfly is loading classes from in the classloaders.
+# /subsystem=logging/logger=org.jboss.modules:add(use-parent-handlers=false, handlers=["JBOSS_LOG"])
+
# Custom log levels
/subsystem=logging/logger=httpclient.wire:add(level=TRACE)
/subsystem=logging/logger=datawave.security.util.DnUtils:add(level=DEBUG)
diff --git a/web-services/deploy/application/src/main/wildfly/overlay/modules/com/fasterxml/jackson/datatype/jackson-datatype-guava/main/module.xml b/web-services/deploy/application/src/main/wildfly/overlay/modules/com/fasterxml/jackson/datatype/jackson-datatype-guava/main/module.xml
new file mode 100644
index 00000000000..1053818b02d
--- /dev/null
+++ b/web-services/deploy/application/src/main/wildfly/overlay/modules/com/fasterxml/jackson/datatype/jackson-datatype-guava/main/module.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-services/deploy/application/src/main/wildfly/overlay/modules/com/fasterxml/jackson/module/jackson-module-jaxb-annotations/main/module.xml b/web-services/deploy/application/src/main/wildfly/overlay/modules/com/fasterxml/jackson/module/jackson-module-jaxb-annotations/main/module.xml
new file mode 100644
index 00000000000..f3f6078bf64
--- /dev/null
+++ b/web-services/deploy/application/src/main/wildfly/overlay/modules/com/fasterxml/jackson/module/jackson-module-jaxb-annotations/main/module.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-services/deploy/application/src/main/wildfly/overlay/modules/datawave/commons/datawave-commons-security/main/module.xml b/web-services/deploy/application/src/main/wildfly/overlay/modules/datawave/commons/datawave-commons-security/main/module.xml
new file mode 100644
index 00000000000..9d5489d336f
--- /dev/null
+++ b/web-services/deploy/application/src/main/wildfly/overlay/modules/datawave/commons/datawave-commons-security/main/module.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-services/deploy/application/src/main/wildfly/overlay/modules/datawave/webservice/datawave-security-elytron-module/main/module.xml b/web-services/deploy/application/src/main/wildfly/overlay/modules/datawave/webservice/datawave-security-elytron-module/main/module.xml
new file mode 100644
index 00000000000..b5102e9f683
--- /dev/null
+++ b/web-services/deploy/application/src/main/wildfly/overlay/modules/datawave/webservice/datawave-security-elytron-module/main/module.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-services/deploy/application/src/main/wildfly/overlay/modules/datawave/webservice/datawave-security-elytron/main/module.xml b/web-services/deploy/application/src/main/wildfly/overlay/modules/datawave/webservice/datawave-security-elytron/main/module.xml
new file mode 100644
index 00000000000..fad0935a1bf
--- /dev/null
+++ b/web-services/deploy/application/src/main/wildfly/overlay/modules/datawave/webservice/datawave-security-elytron/main/module.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-services/deploy/application/src/main/wildfly/overlay/modules/io/jsonwebtoken/jjwt-api/main/module.xml b/web-services/deploy/application/src/main/wildfly/overlay/modules/io/jsonwebtoken/jjwt-api/main/module.xml
new file mode 100644
index 00000000000..a70a6144979
--- /dev/null
+++ b/web-services/deploy/application/src/main/wildfly/overlay/modules/io/jsonwebtoken/jjwt-api/main/module.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/web-services/deploy/application/src/main/wildfly/overlay/modules/io/jsonwebtoken/jjwt-impl/main/module.xml b/web-services/deploy/application/src/main/wildfly/overlay/modules/io/jsonwebtoken/jjwt-impl/main/module.xml
new file mode 100644
index 00000000000..66d159afa85
--- /dev/null
+++ b/web-services/deploy/application/src/main/wildfly/overlay/modules/io/jsonwebtoken/jjwt-impl/main/module.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-services/deploy/application/src/main/wildfly/overlay/modules/org/apache/commons/commons-text/main/module.xml b/web-services/deploy/application/src/main/wildfly/overlay/modules/org/apache/commons/commons-text/main/module.xml
new file mode 100644
index 00000000000..7f9edf84ceb
--- /dev/null
+++ b/web-services/deploy/application/src/main/wildfly/overlay/modules/org/apache/commons/commons-text/main/module.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/web-services/deploy/application/src/main/wildfly/overlay/modules/org/apache/hadoop/common/main/module.xml b/web-services/deploy/application/src/main/wildfly/overlay/modules/org/apache/hadoop/common/main/module.xml
index 3a25d1c41dd..83a7a5b4e8f 100644
--- a/web-services/deploy/application/src/main/wildfly/overlay/modules/org/apache/hadoop/common/main/module.xml
+++ b/web-services/deploy/application/src/main/wildfly/overlay/modules/org/apache/hadoop/common/main/module.xml
@@ -9,17 +9,16 @@
-
+
+
-
-
diff --git a/web-services/deploy/application/src/main/wildfly/overlay/modules/system/layers/base/org/apache/commons/lang3/main/module.xml b/web-services/deploy/application/src/main/wildfly/overlay/modules/system/layers/base/org/apache/commons/lang3/main/module.xml
new file mode 100644
index 00000000000..7245ce1b8bb
--- /dev/null
+++ b/web-services/deploy/application/src/main/wildfly/overlay/modules/system/layers/base/org/apache/commons/lang3/main/module.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-services/deploy/application/src/main/wildfly/overlay/modules/system/layers/base/org/slf4j/impl/main/module.xml b/web-services/deploy/application/src/main/wildfly/overlay/modules/system/layers/base/org/slf4j/impl/main/module.xml
index c4340eedb35..17aabaecfe4 100644
--- a/web-services/deploy/application/src/main/wildfly/overlay/modules/system/layers/base/org/slf4j/impl/main/module.xml
+++ b/web-services/deploy/application/src/main/wildfly/overlay/modules/system/layers/base/org/slf4j/impl/main/module.xml
@@ -1,4 +1,4 @@
-
+
@@ -9,6 +9,7 @@
+
diff --git a/web-services/deploy/application/src/main/wildfly/overlay/modules/system/layers/base/org/slf4j/main/module.xml b/web-services/deploy/application/src/main/wildfly/overlay/modules/system/layers/base/org/slf4j/main/module.xml
index 20fe7485434..3092e07fe1a 100644
--- a/web-services/deploy/application/src/main/wildfly/overlay/modules/system/layers/base/org/slf4j/main/module.xml
+++ b/web-services/deploy/application/src/main/wildfly/overlay/modules/system/layers/base/org/slf4j/main/module.xml
@@ -1,4 +1,4 @@
-
+
diff --git a/web-services/deploy/application/src/main/wildfly/runtime-config.cli b/web-services/deploy/application/src/main/wildfly/runtime-config.cli
index f338b068e5e..d2c47ad35d6 100644
--- a/web-services/deploy/application/src/main/wildfly/runtime-config.cli
+++ b/web-services/deploy/application/src/main/wildfly/runtime-config.cli
@@ -8,9 +8,30 @@ embed-server --server-config=standalone-full.xml
/system-property=dw.warehouse.accumulo.password:add(value=${env.ACCUMULO_PASSWORD})
/system-property=dw.metrics.accumulo.password:add(value=${env.ACCUMULO_PASSWORD})
/system-property=dw.uuid.accumulo.password:add(value=${env.ACCUMULO_PASSWORD})
-/core-service=management/security-realm=SSLRealm/server-identity=ssl/:add(enabled-protocols=["TLSv1.1","TLSv1.2"],keystore-path=${env.KEYSTORE},keystore-provider=${env.KEYSTORE_TYPE},keystore-password="${env.KEYSTORE_PASSWORD}")
-/core-service=management/security-realm=SSLRealm/authentication=truststore/:add(keystore-path=${env.TRUSTSTORE},keystore-provider=${env.TRUSTSTORE_TYPE},keystore-password="${env.TRUSTSTORE_PASSWORD}")
-/subsystem=security/security-domain=datawave/jsse=classic:add(keystore={type="${env.KEYSTORE_TYPE}",password="${env.KEYSTORE_PASSWORD}",url="file://${env.KEYSTORE}"},truststore={type="${env.TRUSTSTORE_TYPE}",password="${env.TRUSTSTORE_PASSWORD}",url="file://${env.TRUSTSTORE}"})
+
+/system-property=dw.ssl.context.info.keyStoreURL:add(value=file://${env.KEYSTORE})
+/system-property=dw.ssl.context.info.keyStoreType:add(value="${env.KEYSTORE_TYPE}")
+/system-property=dw.ssl.context.info.keyStorePassword:add(value="${env.KEYSTORE_PASSWORD}")
+/system-property=dw.ssl.context.info.trustStoreURL:add(value=file://${env.TRUSTSTORE})
+/system-property=dw.ssl.context.info.trustStoreType:add(value="${env.TRUSTSTORE_TYPE}")
+/system-property=dw.ssl.context.info.trustStorePassword:add(value="${env.TRUSTSTORE_PASSWORD}")
+
+# Security configuration via the legacy security framework
+#/core-service=management/security-realm=SSLRealm/server-identity=ssl/:add(enabled-protocols=["TLSv1.1","TLSv1.2"],keystore-path=${env.KEYSTORE},keystore-provider=${env.KEYSTORE_TYPE},keystore-password="${env.KEYSTORE_PASSWORD}")
+#/core-service=management/security-realm=SSLRealm/authentication=truststore/:add(keystore-path=${env.TRUSTSTORE},keystore-provider=${env.TRUSTSTORE_TYPE},keystore-password="${env.TRUSTSTORE_PASSWORD}")
+#/subsystem=security/security-domain=datawave/jsse=classic:add(keystore={type="${env.KEYSTORE_TYPE}",password="${env.KEYSTORE_PASSWORD}",url="file://${env.KEYSTORE}"},truststore={type="${env.TRUSTSTORE_TYPE}",password="${env.TRUSTSTORE_PASSWORD}",url="file://${env.TRUSTSTORE}"})
+
+
+#
+# Security configuration via the Elytron framework
+#
+/subsystem=elytron/key-store=httpsKeyStore:add(path=${KEYSTORE},credential-reference={clear-text="${env.KEYSTORE_PASSWORD}"},type=${env.KEYSTORE_TYPE})
+/subsystem=elytron/key-manager=httpsKeyManager:add(key-store=httpsKeyStore,credential-reference={clear-text=${env.KEYSTORE_PASSWORD})
+/subsystem=elytron/key-store=httpsTrustStore:add(path=${TRUSTSTORE},credential-reference={clear-text="${env.TRUSTSTORE_PASSWORD}"},type=${env.TRUSTSTORE_TYPE})
+/subsystem=elytron/trust-manager=httpsTrustManager:add(key-store=httpsTrustStore)
+/subsystem=elytron/server-ssl-context=httpsSSLContext:add(key-manager=httpsKeyManager,trust-manager=httpsTrustManager,protocols=["TLSv1.1","TLSv1.2"])
+/subsystem=elytron/key-store-realm=ksRealm:add(key-store=httpsKeyStore)
+/subsystem=elytron/security-domain=datawave:add(default-realm=ksRealm,realms=[ksRealm])
# Run the batch commands (MUST BE LAST)
-run-batch
\ No newline at end of file
+run-batch
diff --git a/web-services/deploy/spring-framework-integration/pom.xml b/web-services/deploy/spring-framework-integration/pom.xml
index f8fe7ef61ce..dbe11c53ab4 100644
--- a/web-services/deploy/spring-framework-integration/pom.xml
+++ b/web-services/deploy/spring-framework-integration/pom.xml
@@ -9,7 +9,12 @@
spring-framework-integrationpom${project.artifactId}
+
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ org.easymockeasymock
@@ -92,8 +97,8 @@
test
- javax.enterprise
- cdi-api
+ jakarta.enterprise
+ jakarta.enterprise.cdi-apitest
@@ -103,7 +108,7 @@
org.jboss.arquillian.container
- arquillian-weld-ee-embedded-1.1
+ arquillian-weld-embeddedtest
@@ -113,7 +118,18 @@
org.jboss.resteasy
- resteasy-jaxrs
+ resteasy-core
+ test
+
+
+ javax.validation
+ validation-api
+
+
+
+
+ org.jboss.resteasy
+ resteasy-core-spitest
@@ -144,7 +160,7 @@
org.jboss.spec.javax.transaction
- jboss-transaction-api_1.2_spec
+ jboss-transaction-api_1.3_spectest
@@ -153,8 +169,8 @@
test
- org.picketbox
- picketbox
+ org.jboss.weld.se
+ weld-se-coretest
diff --git a/web-services/deploy/spring-framework-integration/src/test/java/datawave/springframework/integration/WiredQueryExecutorBeanTest.java b/web-services/deploy/spring-framework-integration/src/test/java/datawave/springframework/integration/WiredQueryExecutorBeanTest.java
index 682c2069ab4..99a54e34db3 100644
--- a/web-services/deploy/spring-framework-integration/src/test/java/datawave/springframework/integration/WiredQueryExecutorBeanTest.java
+++ b/web-services/deploy/spring-framework-integration/src/test/java/datawave/springframework/integration/WiredQueryExecutorBeanTest.java
@@ -10,7 +10,6 @@
import org.easymock.EasyMock;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
-import org.jboss.security.JSSESecurityDomain;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
@@ -38,6 +37,7 @@
import datawave.query.transformer.EventQueryDataDecoratorTransformer;
import datawave.query.util.DateIndexHelperFactory;
import datawave.security.authorization.DatawavePrincipal;
+import datawave.security.cert.SSLStores;
import datawave.security.system.CallerPrincipal;
import datawave.security.system.ServerPrincipal;
import datawave.webservice.common.json.DefaultMapperDecorator;
@@ -106,15 +106,15 @@ public void testCreatingPrototypeBeans() {
}
}
- private static JSSESecurityDomain mockJsseSecurityDomain = EasyMock.createMock(JSSESecurityDomain.class);
+ private static SSLStores mockSSLStores = EasyMock.createMock(SSLStores.class);
private static DatawavePrincipal mockDatawavePrincipal = EasyMock.createMock(DatawavePrincipal.class);
private static RemoteEdgeDictionary mockRemoteEdgeDictionary = EasyMock.createMock(RemoteEdgeDictionary.class);
public static class Producer {
@Produces
- public static JSSESecurityDomain produceSecurityDomain() {
- return mockJsseSecurityDomain;
+ public static SSLStores produceSSLStores() {
+ return mockSSLStores;
}
@Produces
diff --git a/web-services/dictionary/pom.xml b/web-services/dictionary/pom.xml
index 3e79a26ffb5..b208fe60c5e 100644
--- a/web-services/dictionary/pom.xml
+++ b/web-services/dictionary/pom.xml
@@ -22,7 +22,11 @@
org.jboss.resteasy
- resteasy-jaxrs
+ resteasy-core
+
+
+ org.jboss.resteasy
+ resteasy-core-spi
diff --git a/web-services/examples/client-login/pom.xml b/web-services/examples/client-login/pom.xml
index 70878e5f321..a1e62a4fa09 100644
--- a/web-services/examples/client-login/pom.xml
+++ b/web-services/examples/client-login/pom.xml
@@ -25,18 +25,27 @@
datawave-ws-security${project.version}
+
+ gov.nsa.datawave.webservices
+ datawave-ws-security-elytron
+ io.protostuffprotostuff-apicompile
- javax.enterprise
- cdi-api
+ jakarta.enterprise
+ jakarta.enterprise.cdi-api
- org.picketbox
- picketbox
+ org.wildfly
+ wildfly-undertow
+
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ providedorg.jboss.spec.javax.ejb
diff --git a/web-services/examples/client-login/src/main/java/datawave/webservice/examples/ClientLoginExampleBean.java b/web-services/examples/client-login/src/main/java/datawave/webservice/examples/ClientLoginExampleBean.java
index 01a663be85d..327dac272b1 100644
--- a/web-services/examples/client-login/src/main/java/datawave/webservice/examples/ClientLoginExampleBean.java
+++ b/web-services/examples/client-login/src/main/java/datawave/webservice/examples/ClientLoginExampleBean.java
@@ -2,6 +2,7 @@
import java.security.KeyStore;
import java.security.cert.X509Certificate;
+import java.util.concurrent.Callable;
import javax.annotation.security.RunAs;
import javax.ejb.LocalBean;
@@ -9,20 +10,21 @@
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.inject.Inject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.login.LoginContext;
-import org.jboss.security.JSSESecurityDomain;
-import org.jboss.security.auth.callback.ObjectCallback;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.wildfly.security.auth.server.SecurityDomain;
+import org.wildfly.security.auth.server.SecurityIdentity;
+import datawave.security.cert.SSLStores;
+import datawave.security.evidence.EvidenceFactory;
+import datawave.security.evidence.X509CertificateEvidence;
import datawave.security.user.UserOperationsBean;
-import datawave.security.util.DnUtils;
import datawave.user.AuthorizationsListBase;
/**
- * An example timer bean that shows how one would call a secured EJB from an unsecured context, such as a message-driven bean callback.
+ * This class demonstrates an example of how to programmatically obtain a {@link SecurityIdentity} from the current {@link SecurityDomain} in order to execute
+ * operations on secured EJBs with a target user's permissions.
*/
@Startup
@Singleton
@@ -30,61 +32,52 @@
@RunAs("InternalUser")
public class ClientLoginExampleBean {
- // Inject a secured EJB that we need to call.
+ private static final Logger log = LoggerFactory.getLogger(ClientLoginExampleBean.class);
+
+ // Inject a secured EJB we need to call.
@Inject
- private UserOperationsBean userOps;
+ private UserOperationsBean userOperationsBean;
- // This only works if our bean is inside the EJB container.
- // That is, you can't do it from an arbitrary client, which would instead need
- // to get its certificate some other way.
+ // This only works if our bean is inside the EJB container. That is, you can't do it from an arbitrary client, which would instead need to get its
+ // certificate some other way.
@Inject
- private JSSESecurityDomain domain;
+ private SSLStores sslStores;
- @Schedule(hour = "*", minute = "*", second = "0", persistent = false)
- public void doScheduledEvent() {
+ // Execute this call every minute.
+ @Schedule(hour = "*", minute = "*", persistent = false)
+ public void executeCall() {
+ log.info("Executing scheduled call");
try {
- // Grab the server certificate from the keystore (we are assuming it is the first one).
- // This is the credential we'll set on the object callback.
- KeyStore keystore = domain.getKeyStore();
- final X509Certificate cert = (X509Certificate) keystore.getCertificate(keystore.aliases().nextElement());
+ // Show that the security identity before authentication is anonymous.
+ SecurityDomain securityDomain = SecurityDomain.getCurrent();
+ SecurityIdentity unauthenticatedIdentity = securityDomain.getCurrentSecurityIdentity();
+ log.info("Current security identity within unsecured context (should be anonymous): {}", unauthenticatedIdentity.getPrincipal());
- // Compute the username. This would either be just a user DN if you are using a user's client
- // certificate, or a server DN combined with a proxied user DN as we demonstrate here.
- String userDN = System.getenv("USER_DN"); // Normally a username would go here. Hack for local testing--query the sid running jboss.
- String userIssuerDN = System.getenv("ISSUER_DN"); // We need the issuer of the user's cert. This needs to be set in the environment for this test.
- String serverDN = cert.getSubjectX500Principal().getName();
- String serverIssuerDN = cert.getIssuerX500Principal().getName();
- final String dn = DnUtils.buildNormalizedProxyDN(serverDN, serverIssuerDN, userDN, userIssuerDN);
+ // Grab the server certificate from the keystore (assuming it's the first one).
+ KeyStore keyStore = sslStores.getKeyStore();
+ final X509Certificate certificate = (X509Certificate) keyStore.getCertificate(keyStore.aliases().nextElement());
- // Handle the callback for authentication. We expect two callbacks, a NameCallback and an ObjectCallback.
- CallbackHandler cbh = new CallbackHandler() {
- @Override
- public void handle(Callback[] callbacks) {
- NameCallback nc = (NameCallback) callbacks[0];
- ObjectCallback oc = (ObjectCallback) callbacks[1];
- nc.setName(dn);
- oc.setCredential(cert);
- }
- };
+ // Create a piece of evidence that will identify the server we want to authenticate as.
+ X509CertificateEvidence evidence = EvidenceFactory.getDefault().createX509CertificateEvidence(certificate, null, null);
+ log.info("Authenticating with evidence: {}", evidence);
- // Authenticate to the DATAWAVE client domain. This saves the credentials
- // we passed in the callback handler above, and passes them along to the server
- // when we attempt any calls that require a login on the server.
- LoginContext lc = new LoginContext("datawave-client", cbh);
- lc.login();
+ // Authenticate and fetch a security identity using the evidence we created.
+ SecurityDomain domain = SecurityDomain.getCurrent();
+ SecurityIdentity identity = domain.authenticate(evidence);
+ log.info("Obtained identity with principal {} and roles {}", identity.getPrincipal(), identity.getRoles());
- // Call secured EJBs
+ // Using the identity, execute the operations with the permissions of the authenticated server.
try {
- AuthorizationsListBase auths = userOps.listEffectiveAuthorizations(false);
- System.err.println("***** Auths for user " + dn + " are: " + auths);
- } finally {
- // Logout, which will restore previous credentials, if any.
- // Be sure to do this in a finally block.
- lc.logout();
+ identity.runAs((Callable) () -> {
+ AuthorizationsListBase auths = userOperationsBean.listEffectiveAuthorizations(false);
+ log.info("Authorizations for current user: {}", auths);
+ return null;
+ });
+ } catch (Exception e) {
+ log.error("Failed to fetch user from cached credentials", e);
}
} catch (Exception e) {
- System.err.println("Error doing login!");
- e.printStackTrace(System.err);
+ log.error("Failed to execute scheduled call", e);
}
}
}
diff --git a/web-services/metrics/pom.xml b/web-services/metrics/pom.xml
index 3e2f365493c..93ef46dc8c2 100644
--- a/web-services/metrics/pom.xml
+++ b/web-services/metrics/pom.xml
@@ -15,6 +15,10 @@
datawave-query-core${project.version}
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.webservicesdatawave-ws-common
diff --git a/web-services/model/pom.xml b/web-services/model/pom.xml
index b17af8a5c18..8dfd3d67c5c 100644
--- a/web-services/model/pom.xml
+++ b/web-services/model/pom.xml
@@ -27,8 +27,12 @@
commons-configuration
- javax.enterprise
- cdi-api
+ gov.nsa.datawave.commons
+ datawave-commons-security
+
+
+ jakarta.enterprise
+ jakarta.enterprise.cdi-apiorg.easymock
@@ -94,7 +98,12 @@
org.jboss.resteasy
- resteasy-jaxrs
+ resteasy-core
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-core-spiprovided
@@ -114,7 +123,7 @@
org.jboss.spec.javax.transaction
- jboss-transaction-api_1.2_spec
+ jboss-transaction-api_1.3_specprovided
diff --git a/web-services/model/src/test/java/datawave/webservice/query/model/ModelBeanTest.java b/web-services/model/src/test/java/datawave/webservice/query/model/ModelBeanTest.java
index ad5559cc753..9ede22f55fd 100644
--- a/web-services/model/src/test/java/datawave/webservice/query/model/ModelBeanTest.java
+++ b/web-services/model/src/test/java/datawave/webservice/query/model/ModelBeanTest.java
@@ -42,7 +42,7 @@
import datawave.security.authorization.DatawaveUser;
import datawave.security.authorization.DatawaveUser.UserType;
import datawave.security.authorization.SubjectIssuerDNPair;
-import datawave.security.util.DnUtils;
+import datawave.security.util.DnProperties;
import datawave.security.util.ScannerHelper;
import datawave.webservice.common.exception.DatawaveWebApplicationException;
import datawave.webservice.model.ModelList;
@@ -67,7 +67,7 @@ public class ModelBeanTest {
@BeforeEach
public void beforeEach() throws Exception {
- System.setProperty(DnUtils.NPE_OU_PROPERTY, "iamnotaperson");
+ System.setProperty(DnProperties.NPE_OU_PROPERTY, "iamnotaperson");
System.setProperty("dw.metadatahelper.all.auths", "A,B,C,D");
bean = new ModelBean();
connectionFactory = mock(AccumuloConnectionFactory.class);
diff --git a/web-services/modification/pom.xml b/web-services/modification/pom.xml
index b6a60831ee8..3ab1e5424e9 100644
--- a/web-services/modification/pom.xml
+++ b/web-services/modification/pom.xml
@@ -19,14 +19,18 @@
datawave-query-core${project.version}
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.coredatawave-core-modification${project.version}
- javax.enterprise
- cdi-api
+ jakarta.enterprise
+ jakarta.enterprise.cdi-apigov.nsa.datawave.webservices
@@ -59,7 +63,12 @@
org.jboss.resteasy
- resteasy-jaxrs
+ resteasy-core
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-core-spiprovided
diff --git a/web-services/pom.xml b/web-services/pom.xml
index 2afb2ff76d2..b029c5c8407 100644
--- a/web-services/pom.xml
+++ b/web-services/pom.xml
@@ -13,7 +13,6 @@
accumulocommon-util
- securitycommonqueryclient
@@ -27,6 +26,7 @@
modeldictionaryannotations
+ security-parentdefault
@@ -41,6 +41,7 @@
1.26.01.73.2
+ 3.20.03.9.03.6.31.1
@@ -116,6 +117,11 @@
dnsjava${version.dnsjava}
+
+ gov.nsa.datawave
+ datawave-common-test
+ ${project.version}
+ gov.nsa.datawavedatawave-core
@@ -489,6 +495,67 @@
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ ${version.jackson}
+ provided
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ ${version.jackson}
+ provided
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${version.jackson}
+ provided
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-guava
+ ${version.jackson}
+ provided
+
+
+ com.fasterxml.jackson.module
+ jackson-module-jaxb-annotations
+ ${version.jackson}
+ provided
+
+
+ com.google.guava
+ guava
+ ${version.google-guava}
+ provided
+
+
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ ${project.version}
+ provided
+
+
+ gov.nsa.datawave.webservices
+ datawave-ws-security-elytron
+ ${project.version}
+ provided
+
+
+ io.jsonwebtoken
+ jjwt-api
+ ${version.jjwt}
+ provided
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ ${version.jjwt}
+ provided
+ javax.jmsjms
@@ -501,6 +568,26 @@
+
+ org.apache.commons
+ commons-lang3
+ ${version.commons-lang3}
+ provided
+
+
+ org.apache.commons
+ commons-text
+ ${version.commons-text}
+
+ provided
+
+
+ org.slf4j
+ slf4j-api
+ ${version.slf4j}
+ provided
+ org.apache.mrunitmrunit
@@ -532,12 +619,6 @@
-
-
- gov.nsa.datawave
- datawave-common-test
-
-
diff --git a/web-services/query-websocket/pom.xml b/web-services/query-websocket/pom.xml
index d2bcd10ec76..e07543f9ea8 100644
--- a/web-services/query-websocket/pom.xml
+++ b/web-services/query-websocket/pom.xml
@@ -18,6 +18,10 @@
com.fasterxml.jackson.modulejackson-module-jaxb-annotations
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.webservicesdatawave-ws-common
@@ -34,17 +38,22 @@
${project.version}
- javax.enterprise
- cdi-api
+ jakarta.enterprise
+ jakarta.enterprise.cdi-api
- javax.json
- javax.json-api
+ jakarta.json
+ jakarta.json-apiprovidedorg.jboss.resteasy
- resteasy-jaxrs
+ resteasy-core
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-core-spiprovided
@@ -62,6 +71,11 @@
jboss-websocket-api_1.1_specprovided
+
+ org.wildfly
+ wildfly-undertow
+ provided
+ ${project.artifactId}
diff --git a/web-services/query-websocket/src/main/java/datawave/webservice/websocket/QueryWebsocket.java b/web-services/query-websocket/src/main/java/datawave/webservice/websocket/QueryWebsocket.java
index e25c417745c..8203f4a7482 100644
--- a/web-services/query-websocket/src/main/java/datawave/webservice/websocket/QueryWebsocket.java
+++ b/web-services/query-websocket/src/main/java/datawave/webservice/websocket/QueryWebsocket.java
@@ -1,12 +1,13 @@
package datawave.webservice.websocket;
-import static datawave.webservice.metrics.Constants.REQUEST_LOGIN_TIME_HEADER;
+import static datawave.security.util.SecurityConstants.REQUEST_LOGIN_TIME_HEADER;
+import static datawave.security.websocket.WebsocketSecurityConfigurator.SESSION_SECURITY_IDENTITY;
import java.io.IOException;
+import java.util.Map;
import java.util.concurrent.Future;
import javax.inject.Inject;
-import javax.interceptor.Interceptors;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
@@ -16,9 +17,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.wildfly.security.auth.server.SecurityIdentity;
import datawave.security.websocket.WebsocketSecurityConfigurator;
-import datawave.security.websocket.WebsocketSecurityInterceptor;
import datawave.webservice.query.exception.QueryException;
import datawave.webservice.query.runner.AsyncQueryStatusObserver;
import datawave.webservice.query.runner.QueryExecutorBean;
@@ -43,67 +44,135 @@
* Per the JSR-356 specification (section 2.1.1), since we have not configured the endpoint otherwise, there shall be one instance of this class per endpoint,
* per peer.
*
- * NOTE: This uses vendor-specific security extensions to work around a websocket specification hole. See
- * WEBSOCKET_SPEC-238 for more details.
+ * NOTE: This uses the vendor-specific security extension {@link WebsocketSecurityConfigurator} to work around a websocket specification hole.
+ * See Jakarta EE #238 for more details.
*/
@ServerEndpoint(value = "/{logic-name}", encoders = {QueryResponseMessageJsonEncoder.class}, decoders = {JsonQueryMessageDecoder.class},
- configurator = WebsocketSecurityConfigurator.class // required to propagate security along to individual websocket notification calls
-)
-@Interceptors({WebsocketSecurityInterceptor.class})
-// required to propagate security along to individual websocket notification calls
+ configurator = WebsocketSecurityConfigurator.class)
public class QueryWebsocket {
+
private static final String LOGIC_NAME = "logicName";
private static final String ACTIVE_QUERY_FUTURE = "activeQueryFuture";
private static final String ACTIVE_QUERY_ID = "activeQueryId";
- private Logger log = LoggerFactory.getLogger(getClass());
+ private final Logger log = LoggerFactory.getLogger(getClass());
@Inject
private QueryExecutorBean queryExecutorBean;
+ /**
+ * Runs when a new websocket connection is opened. The logic name will be extracted from the URL path and stored into the session.
+ *
+ * @param logicName
+ * the logic name
+ * @param session
+ * the session
+ */
@OnOpen
- public void openConnection(@PathParam("logic-name") String logicName, Session session) throws IOException {
+ public void openConnection(@PathParam("logic-name") String logicName, Session session) {
session.getUserProperties().put(LOGIC_NAME, logicName);
}
+ /**
+ * Runs when the websocket connection is closed. If a query is currently active for the session, it will be canceled.
+ */
@OnClose
public void closeConnection(Session session) throws IOException {
- cancelActiveQuery(session);
+ // Ensure the operation is executed using the permissions of the calling user.
+ runAsSessionUser(session, () -> cancelActiveQuery(session));
}
+ /**
+ * Runs when an incoming websocket message is received. The message is expected to be either a {@link CreateQueryMessage} or a {@link CancelMessage}.
+ *
+ * @param session
+ * the session
+ * @param message
+ * the message
+ */
@OnMessage
- public void handleMessage(final Session session, QueryMessage message) {
+ public void handleMessage(final Session session, QueryMessage message) throws IOException {
switch (message.getType()) {
- case CREATE: {
- if (session.getUserProperties().get(ACTIVE_QUERY_FUTURE) != null) {
- session.getAsyncRemote().sendObject(
- new QueryResponseMessage(ResponseType.CREATION_FAILURE, "Query already active. Only one query per websocket is allowed."));
- } else {
- CreateQueryMessage cqm = (CreateQueryMessage) message;
- String logicName = (String) session.getUserProperties().get(LOGIC_NAME);
- QueryObserver observer = new QueryObserver(log, session);
-
- Long startTime = System.nanoTime();
- Long loginTime = null;
- try {
- loginTime = Long.valueOf((String) session.getUserProperties().get(REQUEST_LOGIN_TIME_HEADER));
- } catch (Exception e) {
- // Ignore -- login time won't be available
- }
-
- Future> activeQuery = queryExecutorBean.executeAsync(logicName, cqm.getParameters(), startTime, loginTime, observer);
- session.getUserProperties().put(ACTIVE_QUERY_FUTURE, activeQuery);
- }
- }
+ case CREATE:
+ // Ensure the operation is executed using the permissions of the calling user.
+ runAsSessionUser(session, () -> createQuery(session, message));
break;
- case CANCEL: {
- cancelActiveQuery(session);
- }
+ case CANCEL:
+ // Ensure the operation is executed using the permissions of the calling user.
+ runAsSessionUser(session, () -> cancelActiveQuery(session));
break;
}
}
- protected void cancelActiveQuery(Session session) {
+ /**
+ * Executes the given runnable with the permissions of the calling user for the websocket session. We expected to find a {@link SecurityIdentity} stored in
+ * the {@value WebsocketSecurityConfigurator#SESSION_SECURITY_IDENTITY} user property.
+ *
+ * @param session
+ * the session
+ * @param runnable
+ * the operation to execute
+ */
+ private void runAsSessionUser(Session session, Runnable runnable) throws IOException {
+ // Fetch the calling user's security identity from the session.
+ Map userProperties = session.getUserProperties();
+ final SecurityIdentity identity = (SecurityIdentity) userProperties.get(SESSION_SECURITY_IDENTITY);
+
+ // If no identity was found, return an error message and close the session.
+ if (identity == null) {
+ if (log.isErrorEnabled()) {
+ log.error("No SecurityIdentity found in session user property {}", SESSION_SECURITY_IDENTITY);
+ }
+ session.getAsyncRemote().sendObject(new QueryResponseMessage(ResponseType.ERROR, "Failed to load authenticated user"));
+ session.close();
+ return;
+ }
+
+ // Execute the runnable with the permissions of the calling user.
+ identity.runAs(runnable);
+ }
+
+ /**
+ * Creates a new active query for the session.
+ *
+ * @param session
+ * the session
+ * @param message
+ * the message
+ */
+ private void createQuery(Session session, QueryMessage message) {
+ // If the session already has an active query, do not allow another one to be created.
+ if (session.getUserProperties().get(ACTIVE_QUERY_FUTURE) != null) {
+ session.getAsyncRemote().sendObject(
+ new QueryResponseMessage(ResponseType.CREATION_FAILURE, "Query already active. Only one query per websocket is allowed."));
+ } else {
+ CreateQueryMessage cqm = (CreateQueryMessage) message;
+ String logicName = (String) session.getUserProperties().get(LOGIC_NAME);
+ QueryObserver observer = new QueryObserver(log, session);
+
+ Long startTime = System.nanoTime();
+ // Extract the login time from the session.
+ Long loginTime = null;
+ try {
+ loginTime = Long.valueOf((String) session.getUserProperties().get(REQUEST_LOGIN_TIME_HEADER));
+ } catch (Exception e) {
+ // Ignore -- login time won't be available
+ }
+
+ // Create the query.
+ Future> activeQuery = queryExecutorBean.executeAsync(logicName, cqm.getParameters(), startTime, loginTime, observer);
+ // Add a property to track that there is now an active query associated with the session.
+ session.getUserProperties().put(ACTIVE_QUERY_FUTURE, activeQuery);
+ }
+ }
+
+ /**
+ * Cancels any active query running in the session.
+ *
+ * @param session
+ * the session
+ */
+ private void cancelActiveQuery(Session session) {
Future> activeQuery = (Future>) session.getUserProperties().get(ACTIVE_QUERY_FUTURE);
if (activeQuery != null && !activeQuery.isDone()) {
// Attempt to cancel the async query call. This will cause the async call to return when it is between next calls.
@@ -114,15 +183,15 @@ protected void cancelActiveQuery(Session session) {
try {
queryExecutorBean.cancel(activeQueryId);
} catch (Exception e) {
- log.warn("Failed to cancel query " + activeQueryId, e);
+ log.warn("Failed to cancel query {}", activeQueryId, e);
}
}
}
}
private static class QueryObserver implements AsyncQueryStatusObserver {
- private Logger log;
- private Session session;
+ private final Logger log;
+ private final Session session;
public QueryObserver(Logger log, Session session) {
this.log = log;
@@ -172,7 +241,7 @@ public void queryFinished(String queryId) {
try {
session.close();
} catch (IOException e) {
- log.error("Unable to close peer connection after query " + queryId + " completed.", e);
+ log.error("Unable to close peer connection after query {} completed.", queryId, e);
throw new RuntimeException(e);
}
}
diff --git a/web-services/query-websocket/src/main/webapp/WEB-INF/web.xml b/web-services/query-websocket/src/main/webapp/WEB-INF/web.xml
index d827d4d4d63..7d8330cb653 100644
--- a/web-services/query-websocket/src/main/webapp/WEB-INF/web.xml
+++ b/web-services/query-websocket/src/main/webapp/WEB-INF/web.xml
@@ -12,7 +12,7 @@
-->
DATAWAVE-AUTH
- DATAWAVE Web Services
+ datawave
diff --git a/web-services/query/pom.xml b/web-services/query/pom.xml
index 9abcbc0d2b2..068d7171639 100644
--- a/web-services/query/pom.xml
+++ b/web-services/query/pom.xml
@@ -52,6 +52,10 @@
datawave-ingest-core${project.version}
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ gov.nsa.datawave.coredatawave-core-query
@@ -162,8 +166,8 @@
provided
- javax.enterprise
- cdi-api
+ jakarta.enterprise
+ jakarta.enterprise.cdi-apiprovided
@@ -183,7 +187,12 @@
org.jboss.resteasy
- resteasy-jaxrs
+ resteasy-core
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-core-spiprovided
@@ -218,7 +227,7 @@
org.jboss.spec.javax.transaction
- jboss-transaction-api_1.2_spec
+ jboss-transaction-api_1.3_specprovided
diff --git a/web-services/query/src/main/java/datawave/webservice/query/interceptor/QueryMetricsEnrichmentInterceptor.java b/web-services/query/src/main/java/datawave/webservice/query/interceptor/QueryMetricsEnrichmentInterceptor.java
index ab10ff17850..18a888c5deb 100644
--- a/web-services/query/src/main/java/datawave/webservice/query/interceptor/QueryMetricsEnrichmentInterceptor.java
+++ b/web-services/query/src/main/java/datawave/webservice/query/interceptor/QueryMetricsEnrichmentInterceptor.java
@@ -13,9 +13,9 @@
import javax.ws.rs.ext.WriterInterceptorContext;
import org.apache.log4j.Logger;
-import org.jboss.resteasy.core.interception.ContainerResponseContextImpl;
-import org.jboss.resteasy.core.interception.PreMatchContainerRequestContext;
-import org.jboss.resteasy.util.FindAnnotation;
+import org.jboss.resteasy.core.interception.jaxrs.ContainerResponseContextImpl;
+import org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext;
+import org.jboss.resteasy.spi.util.FindAnnotation;
import datawave.core.query.logic.BaseQueryLogic;
import datawave.core.query.logic.QueryLogic;
diff --git a/web-services/query/src/main/java/datawave/webservice/query/remote/RemoteQueryServiceImpl.java b/web-services/query/src/main/java/datawave/webservice/query/remote/RemoteQueryServiceImpl.java
index 289217761fc..e6a37382a78 100644
--- a/web-services/query/src/main/java/datawave/webservice/query/remote/RemoteQueryServiceImpl.java
+++ b/web-services/query/src/main/java/datawave/webservice/query/remote/RemoteQueryServiceImpl.java
@@ -27,9 +27,9 @@
import datawave.core.query.remote.RemoteQueryService;
import datawave.core.query.remote.RemoteTimeoutQueryException;
-import datawave.security.auth.DatawaveAuthenticationMechanism;
import datawave.security.authorization.DatawavePrincipal;
import datawave.security.authorization.ProxiedUserDetails;
+import datawave.security.util.SecurityConstants;
import datawave.webservice.common.remote.RemoteHttpService;
import datawave.webservice.query.exception.QueryException;
import datawave.webservice.result.BaseQueryResponse;
@@ -39,8 +39,8 @@
public class RemoteQueryServiceImpl extends RemoteHttpService implements RemoteQueryService {
private static final Logger log = LoggerFactory.getLogger(RemoteQueryServiceImpl.class);
- public static final String PROXIED_ENTITIES_HEADER = DatawaveAuthenticationMechanism.PROXIED_ENTITIES_HEADER;
- public static final String PROXIED_ISSUERS_HEADER = DatawaveAuthenticationMechanism.PROXIED_ISSUERS_HEADER;
+ public static final String PROXIED_ENTITIES_HEADER = SecurityConstants.PROXIED_ENTITIES_HEADER;
+ public static final String PROXIED_ISSUERS_HEADER = SecurityConstants.PROXIED_ISSUERS_HEADER;
private static final String CREATE = "%s/create";
diff --git a/web-services/query/src/test/java/datawave/webservice/query/interceptor/QueryMetricsEnrichmentInterceptorTest.java b/web-services/query/src/test/java/datawave/webservice/query/interceptor/QueryMetricsEnrichmentInterceptorTest.java
index 1f8646b757c..f682b30f179 100644
--- a/web-services/query/src/test/java/datawave/webservice/query/interceptor/QueryMetricsEnrichmentInterceptorTest.java
+++ b/web-services/query/src/test/java/datawave/webservice/query/interceptor/QueryMetricsEnrichmentInterceptorTest.java
@@ -27,13 +27,13 @@
import org.easymock.Capture;
import org.easymock.IAnswer;
-import org.jboss.resteasy.core.interception.ContainerResponseContextImpl;
-import org.jboss.resteasy.core.interception.PreMatchContainerRequestContext;
+import org.jboss.resteasy.core.interception.jaxrs.ContainerResponseContextImpl;
+import org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext;
import org.jboss.resteasy.specimpl.BuiltResponse;
+import org.jboss.resteasy.specimpl.ResteasyUriInfo;
import org.jboss.resteasy.spi.HttpRequest;
-import org.jboss.resteasy.spi.ResteasyUriInfo;
-import org.jboss.resteasy.util.FindAnnotation;
-import org.jboss.resteasy.util.HttpResponseCodes;
+import org.jboss.resteasy.spi.HttpResponseCodes;
+import org.jboss.resteasy.spi.util.FindAnnotation;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -48,7 +48,7 @@
import datawave.core.query.logic.BaseQueryLogic;
import datawave.microservice.querymetric.BaseQueryMetric.PageMetric;
import datawave.microservice.querymetric.QueryMetric;
-import datawave.security.util.DnUtils;
+import datawave.security.util.DnProperties;
import datawave.webservice.query.annotation.EnrichQueryMetrics;
import datawave.webservice.query.cache.QueryCache;
import datawave.webservice.query.interceptor.QueryMetricsEnrichmentInterceptor.QueryCall;
@@ -117,7 +117,7 @@ public class QueryMetricsEnrichmentInterceptorTest {
@Before
public void setup() {
- System.setProperty(DnUtils.NPE_OU_PROPERTY, "iamnotaperson");
+ System.setProperty(DnProperties.NPE_OU_PROPERTY, "iamnotaperson");
System.setProperty("dw.metadatahelper.all.auths", "A,B,C,D");
// noinspection unchecked
diff --git a/web-services/query/src/test/java/datawave/webservice/query/logic/composite/CompositeQueryLogicTest.java b/web-services/query/src/test/java/datawave/webservice/query/logic/composite/CompositeQueryLogicTest.java
index 15e8a2597df..9a78325c5a0 100644
--- a/web-services/query/src/test/java/datawave/webservice/query/logic/composite/CompositeQueryLogicTest.java
+++ b/web-services/query/src/test/java/datawave/webservice/query/logic/composite/CompositeQueryLogicTest.java
@@ -50,7 +50,7 @@
import datawave.security.authorization.ProxiedUserDetails;
import datawave.security.authorization.SubjectIssuerDNPair;
import datawave.security.authorization.UserOperations;
-import datawave.security.util.DnUtils;
+import datawave.security.util.DnProperties;
import datawave.user.AuthorizationsListBase;
import datawave.user.DefaultAuthorizationsList;
import datawave.webservice.query.exception.QueryException;
@@ -505,7 +505,7 @@ public boolean isFiltered() {
@Before
public void setup() {
- System.setProperty(DnUtils.NPE_OU_PROPERTY, "iamnotaperson");
+ System.setProperty(DnProperties.NPE_OU_PROPERTY, "iamnotaperson");
System.setProperty("dw.metadatahelper.all.auths", "A,B,C,D");
}
diff --git a/web-services/query/src/test/java/datawave/webservice/query/runner/ExtendedRunningQueryTest.java b/web-services/query/src/test/java/datawave/webservice/query/runner/ExtendedRunningQueryTest.java
index 6675c693e14..ec47ea94bf8 100644
--- a/web-services/query/src/test/java/datawave/webservice/query/runner/ExtendedRunningQueryTest.java
+++ b/web-services/query/src/test/java/datawave/webservice/query/runner/ExtendedRunningQueryTest.java
@@ -45,7 +45,7 @@
import datawave.security.authorization.DatawaveUser;
import datawave.security.authorization.DatawaveUser.UserType;
import datawave.security.authorization.SubjectIssuerDNPair;
-import datawave.security.util.DnUtils;
+import datawave.security.util.DnProperties;
import datawave.webservice.query.cache.RunningQueryTimingImpl;
import datawave.webservice.query.metric.QueryMetricsBean;
import datawave.webservice.query.util.QueryUncaughtExceptionHandler;
@@ -68,7 +68,7 @@ public class ExtendedRunningQueryTest {
@BeforeEach
public void beforeEach() {
- System.setProperty(DnUtils.NPE_OU_PROPERTY, "iamnotaperson");
+ System.setProperty(DnProperties.NPE_OU_PROPERTY, "iamnotaperson");
System.setProperty("dw.metadatahelper.all.auths", "A,B,C,D");
executor = Executors.newSingleThreadExecutor();
}
diff --git a/web-services/query/src/test/java/datawave/webservice/query/runner/QueryExecutorBeanTest.java b/web-services/query/src/test/java/datawave/webservice/query/runner/QueryExecutorBeanTest.java
index 45e44a824bb..fa490ad4f73 100644
--- a/web-services/query/src/test/java/datawave/webservice/query/runner/QueryExecutorBeanTest.java
+++ b/web-services/query/src/test/java/datawave/webservice/query/runner/QueryExecutorBeanTest.java
@@ -42,11 +42,11 @@
import org.apache.log4j.Logger;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
-import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
+import org.jboss.resteasy.spi.Dispatcher;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -92,7 +92,7 @@
import datawave.security.authorization.DatawaveUser;
import datawave.security.authorization.DatawaveUser.UserType;
import datawave.security.authorization.SubjectIssuerDNPair;
-import datawave.security.util.DnUtils;
+import datawave.security.util.DnProperties;
import datawave.security.util.WSAuthorizationsUtil;
import datawave.webservice.common.audit.AuditBean;
import datawave.webservice.common.audit.AuditParameterBuilder;
@@ -163,7 +163,7 @@ public class QueryExecutorBeanTest {
@Before
public void setup() throws Exception {
- System.setProperty(DnUtils.NPE_OU_PROPERTY, "iamnotaperson");
+ System.setProperty(DnProperties.NPE_OU_PROPERTY, "iamnotaperson");
System.setProperty("dw.metadatahelper.all.auths", "A,B,C,D");
QueryTraceCache traceCache = new QueryTraceCache();
Whitebox.invokeMethod(traceCache, "init");
diff --git a/web-services/query/src/test/java/datawave/webservice/query/runner/RunningQueryTest.java b/web-services/query/src/test/java/datawave/webservice/query/runner/RunningQueryTest.java
index 30bf16ada90..caf36dbb26d 100644
--- a/web-services/query/src/test/java/datawave/webservice/query/runner/RunningQueryTest.java
+++ b/web-services/query/src/test/java/datawave/webservice/query/runner/RunningQueryTest.java
@@ -53,7 +53,7 @@
import datawave.security.authorization.DatawaveUser;
import datawave.security.authorization.DatawaveUser.UserType;
import datawave.security.authorization.SubjectIssuerDNPair;
-import datawave.security.util.DnUtils;
+import datawave.security.util.DnProperties;
import datawave.webservice.query.logic.TestQueryLogic;
import datawave.webservice.query.logic.composite.CompositeQueryLogicTest;
import datawave.webservice.result.BaseQueryResponse;
@@ -78,7 +78,7 @@ class SampleGenericQueryConfiguration extends GenericQueryConfiguration {
@Before
public void setup() throws MalformedURLException, IllegalArgumentException, IllegalAccessException {
- System.setProperty(DnUtils.NPE_OU_PROPERTY, "iamnotaperson");
+ System.setProperty(DnProperties.NPE_OU_PROPERTY, "iamnotaperson");
System.setProperty("dw.metadatahelper.all.auths", "A,B,C,D");
settings.setQueryLogicName("testQueryLogic");
diff --git a/web-services/rest-api/src/main/webapp/WEB-INF/web.xml b/web-services/rest-api/src/main/webapp/WEB-INF/web.xml
index 98132ab8325..c5b8b30b092 100644
--- a/web-services/rest-api/src/main/webapp/WEB-INF/web.xml
+++ b/web-services/rest-api/src/main/webapp/WEB-INF/web.xml
@@ -113,8 +113,8 @@
datawave.webservice.result.VoidResponseHtmlMessageBodyWriter,
datawave.resteasy.util.DateParamConverterProvider,
datawave.security.authorization.DatawaveUserHtmlMessageBodyWriter,
- org.jboss.resteasy.plugins.interceptors.encoding.GZIPEncodingInterceptor,
- org.jboss.resteasy.plugins.interceptors.encoding.GZIPDecodingInterceptor,
+ org.jboss.resteasy.plugins.interceptors.GZIPEncodingInterceptor,
+ org.jboss.resteasy.plugins.interceptors.GZIPDecodingInterceptor,
datawave.security.user.TextMessageBodyWriter
@@ -144,7 +144,7 @@
-->
DATAWAVE-AUTH
- DATAWAVE Web Services
+ datawave
diff --git a/web-services/security-parent/README.md b/web-services/security-parent/README.md
new file mode 100644
index 00000000000..9cd8349f193
--- /dev/null
+++ b/web-services/security-parent/README.md
@@ -0,0 +1,17 @@
+# Overview
+
+This project contains security-related classes that are commonly used between the main Datawave project and the microservices.
+
+## [datawave-ws-security](security)
+
+Contains security-related EJBs. Expected to be deployed as part of the Datawave EAR.
+
+## [datawave-ws-security-elytron](security-elytron)
+
+Contains security-related artifacts that are commonly used between projects that are deployed as part of the Datawave EAR, and the datawave-ws-security-elytron-module project. It is expected to be configured and deployed as a JBOSS module via the [Wildfly assembly](../../web-services/deploy/application) project.
+
+## [datawave-ws-security-elytron-module](security-elytron-module)
+
+Contains security-related artifacts that are required to create the Wildfly security domains that are used for authentication and authorization. It is expected to be configured and deployed as a JBOSS module via the [Wildfly assembly](../../web-services/deploy/application) project. Wildfly requires custom Elytron security components to be deployed in separate JBOSS modules. See the project [README](security-elytron-module/README.md) for more details.
+## Note:
+All non-test dependencies for datawave-ws-security-elytron and datawave-ws-security-elytron-module are expected have the scope `provided`. These dependencies must be provided via JBOSS modules in order to avoid classloader conflicts between the JBOSS modules and the Datawave EAR deployment. See the [Wildfly assembly README](../deploy/application/README.md) for more details.
diff --git a/web-services/security-parent/pom.xml b/web-services/security-parent/pom.xml
new file mode 100644
index 00000000000..ac1955753ca
--- /dev/null
+++ b/web-services/security-parent/pom.xml
@@ -0,0 +1,20 @@
+
+
+ 4.0.0
+
+ gov.nsa.datawave.webservices
+ datawave-ws-parent
+ 7.40.0-SNAPSHOT
+
+
+ datawave-ws-security-parent
+ pom
+ ${project.artifactId}
+
+
+ security
+ security-elytron-module
+ security-elytron
+
+
+
diff --git a/web-services/security-parent/security-elytron-module/README.md b/web-services/security-parent/security-elytron-module/README.md
new file mode 100644
index 00000000000..2f5933d0eb8
--- /dev/null
+++ b/web-services/security-parent/security-elytron-module/README.md
@@ -0,0 +1,90 @@
+# Overview
+
+Datawave leverages Wildfly's Elytron framework for authenticating and authorizing users within the Datawave EAR deployment. The full documentation for Wildfly is available via the [Wildfly docs](https://docs.wildfly.org/26/). There are several key concepts to understand when discussing Elytron, and for the sake of brevity, this README will only touch on concepts that are used by the Datawave security configuration.
+
+## Key Concepts
+* `HttpServerAuthenticationMechanismFactory`
+ * An HttpServerAuthenticationMechanismFactory is responsible for providing instances of an HttpServerAuthenticationMechanism, and can be associated with specific mechanisms that it supports, such as BASIC, DIGEST, FORM, etc.
+* `HttpServerAuthenticationMechanism`
+ * An HttpServerAuthenticationMechanism is an authentication policy for authentication using HTTP/Websocket mechanisms. A HttpAuthenticationMechanism will be backed by a SecurityDomain.
+* `SecurityDomain`
+ * SecurityDomain can be considered a security policy that is backed by one or more SecurityRealm instances. The SecurityDomain is responsible for providing a SecurityIdentity, which is a representation of the current identity with roles and permissions. The SecurityDomain is the general wrapper around the policy providing a resulting SecurityIdentity, and makes use of the following components to define this policy:
+
+* `SecurityRealm`
+ * One more named SecurityRealms are associated with a SecurityDomain, the SecurityRealms are the access to the underlying repository of identities and are used for obtaining credentials to allow authentication mechanisms to perform verification, for validation of Evidence and for obtaining the raw AuthorizationIdentity performing the authentication.
+* `RoleDecoder`
+ * Along with the SecurityRealm association is also a reference to a RoleDecoder, the RoleDecoder takes the raw AuthorizationIdentity returned from the SecurityRealm and converts its attributes into roles.
+* `EvidenceDecoder`
+ * A EvidenceDecoder converts from an Evidence to a Principal.
+* `PermissionMapper`
+ * In addition to having roles a SecurityIdentity can also have a set of permissions, the PermissionMapper assigns those permissions to the identity. Crucially, one such Permissions is the LoginPermission, which allows a request to be successfully authorized.
+
+## Datawave Elytron Implementations
+
+* [`DatawaveHttpAuthenticationMechanismFactory`](src/main/java/datawave/security/auth/DatawaveHttpAuthenticationMechanismFactory.java): An implementation of HttpServerAuthenticationMechanismFactory. This class is responsible for providing instances of `DatawaveHttpAuthenticationMechanism`, which will handle incoming HTTP Requests for the mechanism `DATAWAVE-AUTH`. In order to make this factory detected as a service by Wildfly, the fully qualified name of the class must be in a file named org.wildfly.security.http.HttpServerAuthenticationMechanismFactory in the META-INF/services folder.
+* [`DatawaveHttpAuthenticationMechanism`](src/main/java/datawave/security/auth/DatawaveHttpAuthenticationMechanism.java): An implementation of HttpServerAuthenticationMechanism. This class is responsible for extracting evidence from incoming HTTP requests (JWT, trusted headers, PKI certs) and submitting them to the backing security domain for authentication and authorization.
+* [`DatawaveEvidenceDecoder`](src/main/java/datawave/security/realm/DatawaveEvidenceDecoder.java): An implementation of EvidenceDecoder that will convert an incoming Evidence to a DatawavePrincipal.
+* [`DatawaveSecurityRealm`](src/main/java/datawave/security/realm/DatawaveSecurityRealm.java):: An implementation of SecurityRealm that is responsible for providing a RealmIdentity for authentication that is associated with a DatawavePrincipal. This realm is also responsible for extracting any information required for determining the roles of a user, and storing them within the RealmIdentity's Attributes.
+* [`DatawaveRoleDecoder`](src/main/java/datawave/security/realm/DatawaveRoleDecoder.java): An implementation of RoleDecoder that will accept an AuthorizationIdentity provided by a RealmIdentity from the DatawaveSecurityRealm, and return a set of roles to be associated with the final SecurityIdentity established within the security domain.
+
+## Basic Workflow
+
+This workflow description assumes that a security domain has been configured to:
+ - Pass HTTP requests to `DatawaveHttpAuthenticationMechanism`.
+ - Decode evidence using `DatawaveEvidenceDecoder`.
+ - Obtain a RealmIdentity using `DatawaveSecurityRealm`.
+ - Decode roles using `DatawaveRoleDecoder`.
+ - Use the default permissions mapper provided by Wildfly.
+
+1. An incoming request is received and sent to `DatawaveHttpAuthenticationMechanism.evaluateRequest(HttpServerRequest)`.
+2. If configured, the session scope for the request is examined for a previously cached identity. If one is found, we authorize using that identity. (Step 5, but using the cached identity's principal instead of a principal decoded from evidence).
+3. If an identity is not cached, we extract a piece of DatawaveEvidence (JWT, trusted headers, PKI cert) identifying the user from the HttpServerRequest.
+4. The Evidence is verified. This triggers the following:
+ 1. The Evidence is passed to `DatawaveEvidenceDecoder.getPrincipal(Evidence)` to obtain a DatawavePrincipal. This DatawavePrincipal is set as the Evidence's decoded principal.
+ 2. The DatawavePrincipal is passed to `DatawaveSecurityRealm.getRealmIdentity(Principal)` to obtain a RealmIdentity.
+ 3. Roles are extracted from the DatawavePrincipal and mapped to an Attributes that will be part of the AuthorizationIdentity returned from the RealmIdentity.
+ 4. The Evidence is passed to the RealmIdentity's `verifyEvidence(Evidence)`. If this returns false, i.e., fails verification, the user cannot be authorized, and login fails.
+5. If verification succeeds, we then attempt to authorize the request using the DatawaveEvidence's decoded principal, which will be a DatawavePrincipal. This triggers the following:
+ 1. The RealmIdentity associated with the DatawavePrincipal is obtained from the security realm.
+ 2. The AuthorizationIdentity from the RealmIdentity is passed to `DatawaveRoleDecoder.decodeRoles(AuthorizationIdentity)` to obtain a set of Roles for the user.
+ 3. If a non-empty role set was returned, the user is granted LoginPermission and BatchPermissions by the default permissions mapper, and login succeeds. If an empty role set is returned, login fails.
+
+This workflow is primarily executed through a series of Callbacks passed to the Wildfly framework. These callbacks are passed to the Wildfly class `org.wildfly.security.auth.server.ServerAuthenticationContext` which manages a state machine and handles the workflow steps described above. The Wildfly Elytron source code can be viewed at [Github](https://github.com/wildfly-security/wildfly-elytron).
+
+## Configuration Highlights
+
+### EJB JNDI Lookup
+One of the limitations of implementing custom Wildfly Elytron components is that they must be deployed as a separate Wildfly module, and cannot be bundled with the EAR deployment. Wildfly does not support injecting EJBs from a deployment into a separate Wildfly module, so we are forced to use JNDI to look up a [SecurityEJBProvider](../security-elytron/src/main/java/datawave/security/system/SecurityEJBProvider.java) instance that should be created and bound from the Datawave deployment. By default, this will be the singleton instance of [SecurityEJBProviderImpl](../security/src/main/java/datawave/security/system/SecurityEJBProviderImpl.java). The JNDI name must be configured via the system property `dw.security.ejb.provider.jndi`. For example:
+```text
+/system-property=dw.security.ejb.provider.jndi:add(value=java:global/datawave-ws-deploy-application-${project.version}-compose/gov.nsa.datawave.webservices-datawave-ws-security-${project.version}/SecurityEJBProviderImpl)
+```
+The elytron module can then access EJBs created by the Datawave deployment through the EJB provider.
+
+### Request Session Identity Caching
+The DatawaveHttpAuthenticationMechanism can be configured to cache an identity in the request's session, and to change the session ID via the configuration properties `enableRestoreIdentity` and `enableSessionIdChange`. These options are enabled by default.
+
+### JWT and Trusted Header Authentication
+JSON Web Token (JWT) and trusted header authentication is disabled by default, unless enabled in the DatawaveEvidenceDecoder configuration via the properties `jwtEnabled` and `trustedHeaderEnabled`. The header that the mechanism will extract a trusted subject DN from will be loaded in priority from:
+1. The DatawaveHttpAuthenticationMechanism configuration properties `trustedSubjectDnHeader`. If not specified, then:
+2. The system properties `dw.trusted.header.subjectDn`. If not specified, then:
+3. The header `X-SSL-ClientCert-Subject` will be used.
+
+The header that the mechanism will extract a trusted issuer DN from will be loaded in priority from:
+1. The DatawaveHttpAuthenticationMechanism configuration properties `trustedIssuerDnHeader`. If not specified, then:
+2. The system properties `dw.trusted.header.issuerDn`. If not specified, then:
+3. The header `X-SSL-ClientCert-Issuer` will be used.
+
+
+### PKI Certificate Validation
+A PKI certificate validator class can be set via the DatawaveSecurityRealm configuration property `certVerifier`. The class must implement `datawave.security.cert.X509CertificateVerifier`. If the class is an instance of `datawave.security.cert.DatawaveCertVerifier`, the OSCP level for the verifier must be set via the DatawaveSecurityRealm configuration property `oscpLevel`.
+
+### Local Roles
+In addition to the roles returned by the Datawave user service, local roles can be supplied for authenticated users via a properties file where the keys match either a DatawavePrincipal name, or a DatawaveEvidence username (case-insensitive), and the values are comma-delimited roles. To load these roles, the exact path of the role properties file must be specified via the DatawaveSecurityRealm configuration property `roleProperties`. Any local roles for matching users will be part of their final SecurityIdentity.
+
+### Caching
+Both `DatawaveEvidenceDecoder` and `DatawaveSecurityRealm` maintain caches for improving performances when returning DatawavePrincipal and RealmIdentity instances. The maximum size of the caches and the time to live in milliseconds for entries in the cache can be specified via the configuration properties `maxCacheEntries` and `maxCacheAge` for both DatawaveEvidenceDecoder and DatawaveSecurityRealm. The default values for both is `-1`, and negative values imply no limit.
+
+Additionally, both the DatawaveUser cache in DatawaveEvidenceDecoder and the RealmIdentity cache in DatawaveSecurityRealm will be added to the ElytronCacheManager if available via the configured SecurityEJBProvider from JNDI. These caches can then subsequently be used to fetch DatawaveUsers in the CredentialsCacheBean.
+
+## Client to Client Authentication
+See the class [ClientAuthenticationExample](../security-examples/src/main/java/datawave/security/examples/ClientAuthenticationExample.java) for an example of how to programmatically obtain a SecurityIdentity for executing secured operations from an unsecured context.
diff --git a/web-services/security-parent/security-elytron-module/pom.xml b/web-services/security-parent/security-elytron-module/pom.xml
new file mode 100644
index 00000000000..6db87a32dc5
--- /dev/null
+++ b/web-services/security-parent/security-elytron-module/pom.xml
@@ -0,0 +1,142 @@
+
+
+ 4.0.0
+
+ gov.nsa.datawave.webservices
+ datawave-ws-security-parent
+ 7.40.0-SNAPSHOT
+
+
+ datawave-ws-security-elytron-module
+ ${project.artifactId}
+
+
+
+ 2.9.3
+ 2.3.0
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ provided
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-guava
+ provided
+
+
+ com.fasterxml.jackson.module
+ jackson-module-jaxb-annotations
+ provided
+
+
+ com.github.ben-manes.caffeine
+ caffeine
+ ${version.caffeine}
+ provided
+
+
+ com.google.guava
+ guava
+ provided
+
+
+ gov.nsa.datawave.commons
+ datawave-commons-security
+ provided
+
+
+ gov.nsa.datawave.webservices
+ datawave-ws-security-elytron
+ ${project.version}
+ provided
+
+
+ org.apache.commons
+ commons-lang3
+ provided
+
+
+ org.slf4j
+ slf4j-api
+ provided
+
+
+ org.wildfly
+ wildfly-security
+ provided
+
+
+ org.wildfly
+ wildfly-undertow
+ provided
+
+
+ org.apache.logging.log4j
+ log4j-1.2-api
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+ org.bouncycastle
+ bcpkix-jdk15on
+ 1.67
+ test
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+ 1.67
+ test
+
+
+ org.junit-pioneer
+ junit-pioneer
+ ${version.junit-pioneer}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.junit.platform
+ junit-platform-suite
+ test
+
+
+ org.mockito
+ mockito-core
+ ${version.mockito}
+ test
+
+
+
+
+ ${project.artifactId}
+
+
+ true
+ src/main/resources
+
+
+ test-classes
+ true
+ src/test/resources
+
+ *.pkcs12
+ *.p12
+ *.jks
+
+
+
+
+
diff --git a/web-services/security-parent/security-elytron-module/src/main/java/datawave/security/auth/DatawaveHttpAuthenticationMechanism.java b/web-services/security-parent/security-elytron-module/src/main/java/datawave/security/auth/DatawaveHttpAuthenticationMechanism.java
new file mode 100644
index 00000000000..dba3632acd3
--- /dev/null
+++ b/web-services/security-parent/security-elytron-module/src/main/java/datawave/security/auth/DatawaveHttpAuthenticationMechanism.java
@@ -0,0 +1,622 @@
+package datawave.security.auth;
+
+import static datawave.security.auth.DatawaveHttpAuthenticationMechanismFactory.DATAWAVE_AUTH_NAME;
+import static datawave.security.util.SecurityConstants.DEFAULT_TRUSTED_ISSUER_DN_HEADER;
+import static datawave.security.util.SecurityConstants.DEFAULT_TRUSTED_SUBJECT_DN_HEADER;
+import static datawave.security.util.SecurityConstants.PROXIED_ENTITIES_HEADER;
+import static datawave.security.util.SecurityConstants.PROXIED_ISSUERS_HEADER;
+import static datawave.security.util.SecurityConstants.TRUSTED_ISSUER_DN_HEADER_SYSTEM_PROPERTY;
+import static datawave.security.util.SecurityConstants.TRUSTED_SUBJECT_DN_HEADER_SYSTEM_PROPERTY;
+
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.List;
+import java.util.Map;
+import java.util.StringJoiner;
+import java.util.function.Supplier;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.wildfly.security.auth.callback.AuthenticationCompleteCallback;
+import org.wildfly.security.auth.callback.CachedIdentityAuthorizeCallback;
+import org.wildfly.security.auth.callback.EvidenceVerifyCallback;
+import org.wildfly.security.auth.callback.PrincipalAuthorizeCallback;
+import org.wildfly.security.auth.server.SecurityIdentity;
+import org.wildfly.security.cache.CachedIdentity;
+import org.wildfly.security.cache.IdentityCache;
+import org.wildfly.security.evidence.Evidence;
+import org.wildfly.security.http.HttpAuthenticationException;
+import org.wildfly.security.http.HttpScope;
+import org.wildfly.security.http.HttpServerAuthenticationMechanism;
+import org.wildfly.security.http.HttpServerRequest;
+import org.wildfly.security.http.Scope;
+import org.wildfly.security.mechanism.AuthenticationMechanismException;
+import org.wildfly.security.x500.X500;
+
+import datawave.security.evidence.EvidenceFactory;
+import datawave.security.evidence.JWTEvidence;
+import datawave.security.evidence.TrustedHeaderEvidence;
+import datawave.security.evidence.X509CertificateEvidence;
+import datawave.security.util.SecurityConstants;
+import datawave.security.utils.ConfigUtils;
+
+/**
+ * A custom {@link HttpServerAuthenticationMechanism} that handles authentication for the mechanism
+ * {@value DatawaveHttpAuthenticationMechanismFactory#DATAWAVE_AUTH_NAME}.
+ */
+public class DatawaveHttpAuthenticationMechanism implements HttpServerAuthenticationMechanism {
+
+ private static final Logger log = LoggerFactory.getLogger(DatawaveHttpAuthenticationMechanism.class);
+
+ /**
+ * The HTTP header to fetch JWT tokens from.
+ */
+ private static final String AUTHORIZATION_HEADER = "Authorization";
+
+ /**
+ * The expected prefix for any tokens found in the header '{@value AUTHORIZATION_HEADER}'.
+ */
+ private static final String JWT_TOKEN_PREFIX = "Bearer ";
+
+ /**
+ * The length of the string {@link #JWT_TOKEN_PREFIX}.
+ */
+ private static final int JWT_TOKEN_PREFIX_LEN = JWT_TOKEN_PREFIX.length();
+
+ /**
+ * The key used when attaching a cached authorization result to an HTTP scope.
+ */
+ protected static final String CACHED_IDENTITY_KEY = DatawaveHttpAuthenticationMechanism.class.getName() + ".elytron-identity";
+
+ /**
+ * The configuration for this mechanism.
+ */
+ private final Config config;
+
+ /**
+ * The callback handler that will handle executing any callbacks we need to.
+ */
+ private final CallbackHandler callbackHandler;
+
+ public DatawaveHttpAuthenticationMechanism(Map properties, CallbackHandler callbackHandler) {
+ this.callbackHandler = callbackHandler;
+ // Parse the configuration.
+ this.config = Config.fromMap(properties);
+ if (log.isTraceEnabled()) {
+ log.trace("Created mechanism with config: {} from properties: {}", config, properties);
+ }
+ }
+
+ /**
+ * Return the mechanism name {@value datawave.security.auth.DatawaveHttpAuthenticationMechanismFactory#DATAWAVE_AUTH_NAME}.
+ *
+ * @return the mechanism name
+ */
+ @Override
+ public String getMechanismName() {
+ return DATAWAVE_AUTH_NAME;
+ }
+
+ /**
+ * Evaluate and attempt to authenticate the given request.
+ *
+ * @param request
+ * the request to authenticate
+ * @throws HttpAuthenticationException
+ * if an error occurs or authentication fails
+ */
+ @Override
+ public void evaluateRequest(HttpServerRequest request) throws HttpAuthenticationException {
+ // If identity restoration is enabled, and we succeed reauthentication, this is a success.
+ if (config.isIdentityRestorationEnabled() && attemptReauthentication(request)) {
+ log.trace("Reauthentication succeeded");
+ return;
+ }
+
+ // Otherwise, attempt to perform a new authentication.
+ if (attemptAuthentication(request)) {
+ log.trace("New authentication succeeded");
+ return;
+ }
+
+ // If we've reached this point, authentication has failed.
+ log.trace("Both re-authentication and authentication failed");
+
+ try {
+ // Free any resources required for the authentication process.
+ handleCallback(AuthenticationCompleteCallback.FAILED);
+ } catch (AuthenticationMechanismException e) {
+ throw e.toHttpAuthenticationException();
+ }
+
+ // Mark in the request that authentication failed.
+ request.authenticationFailed("Authentication failed");
+ }
+
+ /**
+ * Attempt to reauthenticate the calling user using an identity cached for the session, if any.
+ *
+ * @param request
+ * the request
+ * @return true if reauthentication succeeded, or false otherwise
+ * @throws HttpAuthenticationException
+ * if an error occurs
+ */
+ private boolean attemptReauthentication(HttpServerRequest request) throws HttpAuthenticationException {
+ IdentityCache identityCache = createIdentityCache(request);
+
+ // Authorize this attempt if a cached identity is present in the session, and it has permissions.
+ CachedIdentityAuthorizeCallback authorizeCallback = new CachedIdentityAuthorizeCallback(identityCache);
+ return attemptAuthorization(request, authorizeCallback, authorizeCallback::isAuthorized, identityCache::remove);
+ }
+
+ /**
+ * Attempt to authenticate the given request.
+ *
+ * @param request
+ * the request to authenticate
+ * @return true if authentication succeeded, or false otherwise
+ * @throws HttpAuthenticationException
+ * if an error occurs or authentication fails
+ */
+ private boolean attemptAuthentication(HttpServerRequest request) throws HttpAuthenticationException {
+ // Obtain a piece of evidence that identifies the calling user.
+ Evidence evidence;
+ try {
+ evidence = getEvidence(request);
+ } catch (Exception e) {
+ throw new HttpAuthenticationException("Error occurred when obtaining evidence for authentication", e);
+ }
+
+ // If we failed to obtain any evidence, fail the request.
+ if (evidence == null) {
+ log.trace("Failed to obtain any evidence for authentication");
+ return false;
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("Attempting authentication with evidence: {}", evidence);
+ }
+
+ // Verify the evidence. This will load the decoded principal into the evidence. NOTE: this mechanism expects an evidence decoder such as
+ // DatawaveEvidenceDecoder to be configured with the backing security domain.
+ EvidenceVerifyCallback evidenceVerifyCallback = new EvidenceVerifyCallback(evidence);
+ try {
+ handleCallback(evidenceVerifyCallback);
+ } catch (AuthenticationMechanismException e) {
+ throw e.toHttpAuthenticationException();
+ }
+
+ // If the evidence passed verification, attempt to authorize the decoded principal. Authorization will only succeed if the user has valid roles.
+ if (evidenceVerifyCallback.isVerified()) {
+ // If we should restore identities, cache the identity for the request session if authorization passes.
+ if (config.isIdentityRestorationEnabled()) {
+ IdentityCache identityCache = createIdentityCache(request);
+ CachedIdentityAuthorizeCallback authorizeCallback = new CachedIdentityAuthorizeCallback(evidence.getDecodedPrincipal(), identityCache);
+ return attemptAuthorization(request, authorizeCallback, authorizeCallback::isAuthorized, identityCache::remove);
+ } else {
+ // Otherwise, attempt to authorize the identity and do not cache it.
+ PrincipalAuthorizeCallback authorizedCallback = new PrincipalAuthorizeCallback(evidence.getDecodedPrincipal());
+ return attemptAuthorization(request, authorizedCallback, authorizedCallback::isAuthorized, null);
+ }
+ } else {
+ if (log.isTraceEnabled()) {
+ log.trace("Evidence verification failed with decoded principal {}", evidence.getDecodedPrincipal());
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Attempt to authorize the request using the given authorization callback to execute the authentication workflow. This method exists to handle different
+ * {@link Callback} types that may perform authorization.
+ *
+ * @param request
+ * the request
+ * @param authorizeCallback
+ * the callback
+ * @param authorizeResult
+ * the supplier that will return whether authentication succeeded via the callback
+ * @param logoutHandler
+ * an operation that should execute when the session logs out. This may be null, or an operation to remove an identity from the identity cache
+ * after the session is finished.
+ * @return true if authorization succeeded, or false otherwise
+ * @throws HttpAuthenticationException
+ * if an error occurs
+ */
+ private boolean attemptAuthorization(HttpServerRequest request, Callback authorizeCallback, Supplier authorizeResult, Runnable logoutHandler)
+ throws HttpAuthenticationException {
+ // Attempt to authorize the request.
+ try {
+ handleCallback(authorizeCallback);
+ } catch (AuthenticationMechanismException e) {
+ throw e.toHttpAuthenticationException();
+ }
+ // If authorization succeeded, mark the request as successfully authorized.
+ if (authorizeResult.get()) {
+ succeed(request, logoutHandler);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Mark the request with a successful authentication.
+ *
+ * @param request
+ * the request to mark as succeeded.
+ * @param logoutHandler
+ * an operation that should execute when the session logs out. This may be null, or an operation to remove an identity from the identity cache
+ * after the session is finished.
+ */
+ private void succeed(HttpServerRequest request, Runnable logoutHandler) throws HttpAuthenticationException {
+ try {
+ handleCallback(AuthenticationCompleteCallback.SUCCEEDED);
+ } catch (AuthenticationMechanismException e) {
+ throw e.toHttpAuthenticationException();
+ }
+
+ request.authenticationComplete(null, logoutHandler);
+ }
+
+ /**
+ * Execute the given callbacks using the callback handler of this {@link DatawaveHttpAuthenticationMechanism}.
+ *
+ * @param callbacks
+ * the callbacks to handle
+ * @throws AuthenticationMechanismException
+ * if an authentication error occurs
+ */
+ private void handleCallback(Callback... callbacks) throws AuthenticationMechanismException {
+ try {
+ this.callbackHandler.handle(callbacks);
+ } catch (AuthenticationMechanismException e) {
+ throw e;
+ } catch (Throwable e) {
+ throw new AuthenticationMechanismException("Callback handler failed for unknown reason", e);
+ }
+ }
+
+ /**
+ * Attempt to extract {@link Evidence} from the request representing the user that can be used for authentication and authorization.
+ *
+ * @param request
+ * the request
+ * @return the first valid {@link Evidence} found, or null if none was found
+ * @throws MultipleHeaderValuesException
+ * if a header had multiple values
+ * @throws MissingHeaderException
+ * if a header was missing
+ */
+ private Evidence getEvidence(HttpServerRequest request) throws MultipleHeaderValuesException, MissingHeaderException {
+ // First check if we have a JSON web token.
+ Evidence evidence = getJwtEvidence(request);
+ if (evidence != null) {
+ return evidence;
+ }
+
+ // Proxied entities and issuers may be specified in configured headers. Extract them for use when either creating evidence from a certificate or trusted
+ // headers.
+ Pair proxiedHeaderValues = getProxiedEntitiesAndIssuers(request);
+ String proxiedEntities = proxiedHeaderValues.getLeft();
+ String proxiedIssuers = proxiedHeaderValues.getRight();
+ if (log.isTraceEnabled()) {
+ log.trace("Authenticating with proxied entities={} amd proxied issuers={}", proxiedEntities, proxiedIssuers);
+ }
+
+ // Next check if we have a certificate from an SSL session.
+ evidence = getX509Evidence(request, proxiedEntities, proxiedIssuers);
+ if (evidence != null) {
+ return evidence;
+ }
+
+ // Lastly check if we have entities stored in trusted headers.
+ return getTrustedHeadersEvidence(request, proxiedEntities, proxiedIssuers);
+ }
+
+ /**
+ * Extract proxied entities and proxied issuers (if any) from the request headers {@value SecurityConstants#PROXIED_ENTITIES_HEADER} and
+ * {@value SecurityConstants#PROXIED_ISSUERS_HEADER}.
+ *
+ * @param request
+ * the request to extract the entities from
+ * @return the pair of values representing the proxied entities and issuers
+ * @throws MultipleHeaderValuesException
+ * if multiple values were found in the header
+ * @throws MissingHeaderException
+ * if proxied entities were provided, but proxied issuers were not.
+ */
+ private Pair getProxiedEntitiesAndIssuers(HttpServerRequest request) throws MultipleHeaderValuesException, MissingHeaderException {
+ String proxiedEntities = getSingularHeaderValue(request, PROXIED_ENTITIES_HEADER);
+ String proxiedIssuers = getSingularHeaderValue(request, PROXIED_ISSUERS_HEADER);
+
+ // If proxied entities are specified, but proxied issuers are not, then fail authentication immediately.
+ if (proxiedEntities != null && proxiedIssuers == null) {
+ throw new MissingHeaderException(PROXIED_ENTITIES_HEADER + " provided, but missing " + PROXIED_ISSUERS_HEADER);
+ }
+
+ return Pair.of(proxiedEntities, proxiedIssuers);
+ }
+
+ /**
+ * Attempt to find and return evidence based on a JWT token from the header {@value AUTHORIZATION_HEADER} in the request.
+ *
+ * @param request
+ * the request to examine
+ * @return a {@link JWTEvidence} if a JWT token was found, or null otherwise
+ * @throws MultipleHeaderValuesException
+ * if multiple token values were found in the header
+ */
+ private JWTEvidence getJwtEvidence(HttpServerRequest request) throws MultipleHeaderValuesException {
+ String authorizationHeader = getSingularHeaderValue(request, AUTHORIZATION_HEADER);
+ if (authorizationHeader != null && authorizationHeader.startsWith(JWT_TOKEN_PREFIX)) {
+ String jwtToken = authorizationHeader.substring(JWT_TOKEN_PREFIX_LEN);
+ return EvidenceFactory.getDefault().createJwtEvidence(jwtToken);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Attempt to find and return evidence based off certificates from the SSL session.
+ *
+ * @param request
+ * the request to examine
+ * @param proxiedSubjects
+ * the proxied subjects (if any)
+ * @param proxiedIssuers
+ * the proxied issuers (if any)
+ * @return a {@link X509CertificateEvidence} if a certificate was found in the SSL session, or null otherwise
+ */
+ private X509CertificateEvidence getX509Evidence(HttpServerRequest request, String proxiedSubjects, String proxiedIssuers) {
+ if (request.getSSLSession() != null) {
+ Certificate[] peerCertificates = request.getPeerCertificates();
+ if (peerCertificates != null) {
+ // If the request has any peer certificates, grab the first one.
+ X509Certificate[] x509Certificates = X500.asX509CertificateArray(peerCertificates);
+ X509Certificate certificate = x509Certificates[0];
+ return EvidenceFactory.getDefault().createX509CertificateEvidence(certificate, proxiedSubjects, proxiedIssuers);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Attempt to find and return evidence based of trusted headers.
+ *
+ * @param request
+ * the request to examine
+ * @param proxiedSubjects
+ * the proxied subjects (if any)
+ * @param proxiedIssuers
+ * the proxied issuers (if any)
+ * @return a {@link TrustedHeaderEvidence} if trusted subject and issuer DNs were found, or null otherwise
+ * @throws MultipleHeaderValuesException
+ * if multiple DNs were found in the headers
+ * @throws MissingHeaderException
+ * if either a subject DN or issuer DN was provided, but its counterpart was not
+ */
+ private TrustedHeaderEvidence getTrustedHeadersEvidence(HttpServerRequest request, String proxiedSubjects, String proxiedIssuers)
+ throws MultipleHeaderValuesException, MissingHeaderException {
+ String subjectDn = getSingularHeaderValue(request, config.getTrustedSubjectDnHeader());
+ String issuerDn = getSingularHeaderValue(request, config.getTrustedIssuerDnHeader());
+
+ // If no DN headers were supplied, we cannot create trusted header evidence.
+ if (subjectDn == null && issuerDn == null) {
+ return null;
+ }
+
+ // Require both a subject DN and issuer DN to be specified.
+ if (subjectDn == null || issuerDn == null) {
+ throw new MissingHeaderException(
+ "Missing trusted subject DN (" + subjectDn + ") or issuer DN (" + issuerDn + ") for trusted header authentication");
+ }
+
+ return EvidenceFactory.getDefault().createTrustedHeadersEvidence(subjectDn, issuerDn, proxiedSubjects, proxiedIssuers);
+ }
+
+ /**
+ * Returns the value if one was provided for the given header name in the given http request. If no value was provided, null will be returned. If multiple
+ * values were provided, an exception will be thrown.
+ *
+ * @param httpServerRequest
+ * the http request
+ * @param headerName
+ * the header name
+ * @return the value, possibly null
+ * @throws MultipleHeaderValuesException
+ * if multiple values were provided for the header
+ */
+ private String getSingularHeaderValue(HttpServerRequest httpServerRequest, String headerName) throws MultipleHeaderValuesException {
+ List values = httpServerRequest.getRequestHeaderValues(headerName);
+ if (values != null && !values.isEmpty()) {
+ if (values.size() > 1) {
+ throw new MultipleHeaderValuesException(headerName + " may not be specified multiple times");
+ }
+ return values.get(0);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Create an identity cache that can be associated with the SESSION scope of the request.
+ *
+ * @param request
+ * the request
+ * @return the identity cache
+ */
+ private IdentityCache createIdentityCache(HttpServerRequest request) {
+ return new IdentityCache() {
+
+ /**
+ * Attempt to attach the given identity to the request's SESSION scope.
+ *
+ * @param identity
+ * the identity to cache (not {@code null})
+ */
+ @Override
+ public void put(SecurityIdentity identity) {
+ // Attempt to get an attachable SESSION scope for the request, creating it if need be. If we cannot obtain the scope, return early.
+ HttpScope scope = getAttachableSessionScope(request, true);
+ if (scope == null || !scope.exists()) {
+ return;
+ }
+
+ // If we are associating an identity with the session for the first time, change the ID of the session unless otherwise disabled.
+ if (config.isSessionIdChangeEnabled() && scope.getAttachment(CACHED_IDENTITY_KEY) == null) {
+ scope.changeID();
+ }
+
+ // Wrap the identity in a CachedIdentity and attach it to the scope.
+ CachedIdentity cachedIdentity = new CachedIdentity(getMechanismName(), false, identity);
+ scope.setAttachment(CACHED_IDENTITY_KEY, cachedIdentity);
+ }
+
+ /**
+ * Return the cached identity attached to the SESSION scope of the request.
+ *
+ * @return the cached identity, or null if no identity is cached
+ */
+ @Override
+ public CachedIdentity get() {
+ // If we cannot obtain a scope that could have an attached identity, return early.
+ HttpScope scope = getAttachableSessionScope(request, false);
+ if (scope == null || !scope.exists()) {
+ return null;
+ }
+
+ return (CachedIdentity) scope.getAttachment(CACHED_IDENTITY_KEY);
+ }
+
+ /**
+ * Delete the cached identity (if any) attached to the SESSION scope of the request.
+ *
+ * @return the identity that was removed, possibly null
+ */
+ @Override
+ public CachedIdentity remove() {
+ // If we cannot obtain a scope that could have an attached identity, return early.
+ HttpScope scope = getAttachableSessionScope(request, false);
+ if (scope == null || !scope.exists()) {
+ return null;
+ }
+
+ CachedIdentity identity = (CachedIdentity) scope.getAttachment(CACHED_IDENTITY_KEY);
+ scope.setAttachment(CACHED_IDENTITY_KEY, null);
+ return identity;
+ }
+ };
+ }
+
+ /**
+ * Return the SESSION scope for the request if it exists and supports attachments.
+ *
+ * @param request
+ * the request
+ * @param createSession
+ * whether to create the session if it does not exist
+ * @return the scope, or null if no attachable SESSION scope could be obtained
+ */
+ private HttpScope getAttachableSessionScope(HttpServerRequest request, boolean createSession) {
+ HttpScope scope = request.getScope(Scope.SESSION);
+
+ // If no scope could be obtained, or it doesn't support attachments, return null.
+ if (scope == null || !scope.supportsAttachments()) {
+ return null;
+ }
+
+ // Create the scope if indicated.
+ if (!scope.exists() && createSession) {
+ scope.create();
+ }
+
+ return scope;
+ }
+
+ /**
+ * Configuration class that will handle parsing configuration options for a {@link DatawaveHttpAuthenticationMechanism} instance.
+ */
+ public static class Config {
+
+ /**
+ * The header that will be used to pass in the trusted subject DN when using trusted header authentication.
+ */
+ public static final String OPTION_TRUSTED_SUBJECT_DN_HEADER = "trustedSubjectDnHeader";
+
+ /**
+ * The header that will be used to pass in the trusted issuer DN when using trusted header authentication.
+ */
+ public static final String OPTION_TRUSTED_ISSUER_DN_HEADER = "trustedIssuerDnHeader";
+
+ /**
+ * Whether to enable the ability to restore identities for the HTTP session.
+ */
+ public static final String OPTION_ENABLE_RESTORE_IDENTITY = "enableRestoreIdentity";
+
+ /**
+ * Whether to enable the ability to change the session ID after an identity is first established.
+ */
+ public static final String OPTION_ENABLE_SESSION_ID_CHANGE = "enableSessionIdChange";
+
+ private final String trustedSubjectDnHeader;
+ private final String trustedIssuerDnHeader;
+ private final boolean identityRestorationEnabled;
+ private final boolean sessionIdChangeEnabled;
+
+ public static Config fromMap(Map properties) {
+ String trustedSubjectDnHeader = getValue((String) properties.get(OPTION_TRUSTED_SUBJECT_DN_HEADER), TRUSTED_SUBJECT_DN_HEADER_SYSTEM_PROPERTY,
+ DEFAULT_TRUSTED_SUBJECT_DN_HEADER);
+ String trustedIssuerDnHeader = getValue((String) properties.get(OPTION_TRUSTED_ISSUER_DN_HEADER), TRUSTED_ISSUER_DN_HEADER_SYSTEM_PROPERTY,
+ DEFAULT_TRUSTED_ISSUER_DN_HEADER);
+
+ boolean identityRestorationEnabled = ConfigUtils.getBoolean((String) properties.get(OPTION_ENABLE_RESTORE_IDENTITY), true);
+ boolean sessionIdChangeEnabled = ConfigUtils.getBoolean((String) properties.get(OPTION_ENABLE_SESSION_ID_CHANGE), true);
+
+ return new Config(trustedSubjectDnHeader, trustedIssuerDnHeader, identityRestorationEnabled, sessionIdChangeEnabled);
+ }
+
+ private static String getValue(String mapValue, String systemProperty, String defaultValue) {
+ String value = ConfigUtils.getString(mapValue, null);
+ if (value == null) {
+ value = ConfigUtils.getString(System.getProperty(systemProperty), defaultValue);
+ }
+ return value;
+ }
+
+ public Config(String trustedSubjectDnHeader, String trustedIssuerDnHeader, boolean identityRestorationEnabled, boolean sessionIdChangeEnabled) {
+ this.trustedSubjectDnHeader = trustedSubjectDnHeader;
+ this.trustedIssuerDnHeader = trustedIssuerDnHeader;
+ this.identityRestorationEnabled = identityRestorationEnabled;
+ this.sessionIdChangeEnabled = sessionIdChangeEnabled;
+ }
+
+ public String getTrustedSubjectDnHeader() {
+ return trustedSubjectDnHeader;
+ }
+
+ public String getTrustedIssuerDnHeader() {
+ return trustedIssuerDnHeader;
+ }
+
+ public boolean isIdentityRestorationEnabled() {
+ return identityRestorationEnabled;
+ }
+
+ public boolean isSessionIdChangeEnabled() {
+ return sessionIdChangeEnabled;
+ }
+
+ @Override
+ public String toString() {
+ return new StringJoiner(", ", Config.class.getSimpleName() + "[", "]").add("trustedSubjectDnHeader='" + trustedSubjectDnHeader + "'")
+ .add("trustedIssuerDnHeader='" + trustedIssuerDnHeader + "'").add("identityRestorationEnabled=" + identityRestorationEnabled)
+ .add("sessionIdChangeEnabled=" + sessionIdChangeEnabled).toString();
+ }
+ }
+}
diff --git a/web-services/security-parent/security-elytron-module/src/main/java/datawave/security/auth/DatawaveHttpAuthenticationMechanismFactory.java b/web-services/security-parent/security-elytron-module/src/main/java/datawave/security/auth/DatawaveHttpAuthenticationMechanismFactory.java
new file mode 100644
index 00000000000..3f35875ebcf
--- /dev/null
+++ b/web-services/security-parent/security-elytron-module/src/main/java/datawave/security/auth/DatawaveHttpAuthenticationMechanismFactory.java
@@ -0,0 +1,54 @@
+package datawave.security.auth;
+
+import java.util.Map;
+import java.util.Objects;
+
+import javax.security.auth.callback.CallbackHandler;
+
+import org.wildfly.security.http.HttpServerAuthenticationMechanism;
+import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory;
+
+/**
+ * A {@link HttpServerAuthenticationMechanismFactory} implementation to create instances of {@link DatawaveHttpAuthenticationMechanism} for the
+ * {@value #DATAWAVE_AUTH_NAME} mechanism.
+ */
+public class DatawaveHttpAuthenticationMechanismFactory implements HttpServerAuthenticationMechanismFactory {
+
+ public static final String DATAWAVE_AUTH_NAME = "DATAWAVE-AUTH";
+
+ /**
+ * Returns the name of the HTTP authentication mechanism that can be supplied by this factory, specifically {@value #DATAWAVE_AUTH_NAME}.
+ *
+ * @param properties
+ * the properties to pass configuration to the mechanisms that may be evaluated for mechanism availability.
+ * @return a single-element array containing the string {@value #DATAWAVE_AUTH_NAME}.
+ */
+ @Override
+ public String[] getMechanismNames(Map properties) {
+ return new String[] {DATAWAVE_AUTH_NAME};
+ }
+
+ /**
+ * Returns an instance of {@link DatawaveHttpAuthenticationMechanism} if the mechanism name is {@value #DATAWAVE_AUTH_NAME}, otherwise returns null.
+ *
+ * @param mechanismName
+ * the mechanism name
+ * @param properties
+ * the set of properties to select and configure the mechanism that may be evaluated for mechanism availability
+ * @param callbackHandler
+ * the {@link CallbackHandler} for use by the mechanism during authentication
+ * @return the configured {@link DatawaveHttpAuthenticationMechanism} or null if no mechanism could be resolved for the given mechanism name
+ */
+ @Override
+ public HttpServerAuthenticationMechanism createAuthenticationMechanism(String mechanismName, Map properties, CallbackHandler callbackHandler) {
+ Objects.requireNonNull(mechanismName, "mechanismName must not be null");
+ Objects.requireNonNull(properties, "properties must not be null");
+ Objects.requireNonNull(callbackHandler, "callbackHandler must not be null");
+
+ if (DATAWAVE_AUTH_NAME.equals(mechanismName)) {
+ return new DatawaveHttpAuthenticationMechanism(properties, callbackHandler);
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/web-services/security-parent/security-elytron-module/src/main/java/datawave/security/auth/MissingHeaderException.java b/web-services/security-parent/security-elytron-module/src/main/java/datawave/security/auth/MissingHeaderException.java
new file mode 100644
index 00000000000..0a5fb67b3dc
--- /dev/null
+++ b/web-services/security-parent/security-elytron-module/src/main/java/datawave/security/auth/MissingHeaderException.java
@@ -0,0 +1,13 @@
+package datawave.security.auth;
+
+/**
+ * An exception that indicates a required header is missing from a request.
+ */
+public final class MissingHeaderException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public MissingHeaderException(String message) {
+ super(message);
+ }
+}
diff --git a/web-services/security-parent/security-elytron-module/src/main/java/datawave/security/auth/MultipleHeaderValuesException.java b/web-services/security-parent/security-elytron-module/src/main/java/datawave/security/auth/MultipleHeaderValuesException.java
new file mode 100644
index 00000000000..cacb5ecebcc
--- /dev/null
+++ b/web-services/security-parent/security-elytron-module/src/main/java/datawave/security/auth/MultipleHeaderValuesException.java
@@ -0,0 +1,13 @@
+package datawave.security.auth;
+
+/**
+ * An exception that indicates a header is present in a request with multiple values when the header is expected to have a single value.
+ */
+public final class MultipleHeaderValuesException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public MultipleHeaderValuesException(String message) {
+ super(message);
+ }
+}
diff --git a/web-services/security-parent/security-elytron-module/src/main/java/datawave/security/cache/DatawaveRealmIdentityCache.java b/web-services/security-parent/security-elytron-module/src/main/java/datawave/security/cache/DatawaveRealmIdentityCache.java
new file mode 100644
index 00000000000..b7ea8ae6bbf
--- /dev/null
+++ b/web-services/security-parent/security-elytron-module/src/main/java/datawave/security/cache/DatawaveRealmIdentityCache.java
@@ -0,0 +1,291 @@
+package datawave.security.cache;
+
+import java.security.Principal;
+import java.time.Duration;
+import java.util.Collection;
+import java.util.Set;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.stream.Collectors;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.wildfly.security.auth.server.RealmIdentity;
+import org.wildfly.security.cache.RealmIdentityCache;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.RemovalCause;
+import com.github.benmanes.caffeine.cache.RemovalListener;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+
+import datawave.security.authorization.DatawavePrincipal;
+import datawave.security.authorization.DatawaveUser;
+
+/**
+ * A {@link RealmIdentityCache} implementation that can be treated as an {@link ElytronCache}. This cache supports caching {@link RealmIdentity} instances
+ * associated with security realm and security domain principals, and is intended for use with a {@link org.wildfly.security.auth.server.SecurityRealm} to
+ * support caching of identities without wrapping the realm in a caching realm. This cache requires the domain principals to be instances of
+ * {@link DatawavePrincipal}.
+ */
+public class DatawaveRealmIdentityCache implements RealmIdentityCache, ElytronCache {
+
+ private static final Logger log = LoggerFactory.getLogger(DatawaveRealmIdentityCache.class);
+
+ // Create the lock with fairness to ensure write operations do not get perpetually blocked by read operations.
+ private final ReadWriteLock lock = new ReentrantReadWriteLock(true);
+
+ /**
+ * Holds mappings for domain principals to realm identities.
+ */
+ private final Cache domainPrincipalsToRealmIdentities;
+
+ /**
+ * Holds mappings of realm identity principals to domain principals.
+ */
+ private final Multimap realmPrincipalsToDomainPrincipals;
+
+ /**
+ * Return a new {@link DatawaveRealmIdentityCache}.
+ *
+ * @param maxSize
+ * the maximum number of entries to keep in the cache, unlimited if given a negative value
+ * @param ttl
+ * the time in milliseconds that an entry can stay in the cache, unlimited if given a negative value
+ */
+ public DatawaveRealmIdentityCache(long maxSize, long ttl) {
+ Caffeine