org.wildfly.security.auth.client.ElytronXmlParser Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wildfly.security.auth.client;
import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
import static javax.xml.stream.XMLStreamConstants.START_ELEMENT;
import static org.wildfly.common.Assert.checkMinimumParameter;
import static org.wildfly.common.Assert.checkNotNullParam;
import static org.wildfly.security.auth.client._private.ElytronMessages.xmlLog;
import static org.wildfly.security.provider.util.ProviderUtil.INSTALLED_PROVIDERS;
import static org.wildfly.security.provider.util.ProviderUtil.findProvider;
import java.io.Closeable;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.Authenticator;
import java.net.MalformedURLException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.function.IntFunction;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509TrustManager;
import javax.xml.stream.Location;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.Oid;
import org.wildfly.client.config.ClientConfiguration;
import org.wildfly.client.config.ConfigXMLParseException;
import org.wildfly.client.config.ConfigurationXMLStreamReader;
import org.wildfly.client.config.XMLLocation;
import org.wildfly.common.Assert;
import org.wildfly.common.function.ExceptionBiFunction;
import org.wildfly.common.function.ExceptionSupplier;
import org.wildfly.common.function.ExceptionUnaryOperator;
import org.wildfly.common.iteration.CodePointIterator;
import org.wildfly.security.FixedSecurityFactory;
import org.wildfly.security.SecurityFactory;
import org.wildfly.security.asn1.OidsUtil;
import org.wildfly.security.auth.client._private.ElytronMessages;
import org.wildfly.security.auth.server.IdentityCredentials;
import org.wildfly.security.auth.server.NameRewriter;
import org.wildfly.security.auth.util.ElytronAuthenticator;
import org.wildfly.security.auth.util.ElytronFilePasswordProvider;
import org.wildfly.security.auth.util.RegexNameRewriter;
import org.wildfly.security.credential.BearerTokenCredential;
import org.wildfly.security.credential.Credential;
import org.wildfly.security.credential.KeyPairCredential;
import org.wildfly.security.credential.PasswordCredential;
import org.wildfly.security.credential.PublicKeyCredential;
import org.wildfly.security.credential.SSHCredential;
import org.wildfly.security.credential.X509CertificateChainPrivateCredential;
import org.wildfly.security.credential.source.CredentialSource;
import org.wildfly.security.credential.source.impl.CredentialStoreCredentialSource;
import org.wildfly.security.credential.source.impl.KeyStoreCredentialSource;
import org.wildfly.security.credential.source.impl.LocalKerberosCredentialSource;
import org.wildfly.security.credential.source.OAuth2CredentialSource;
import org.wildfly.security.credential.store.CredentialStore;
import org.wildfly.security.keystore.AliasFilter;
import org.wildfly.security.keystore.FilteringKeyStore;
import org.wildfly.security.keystore.KeyStoreUtil;
import org.wildfly.security.keystore.PasswordEntry;
import org.wildfly.security.keystore.WrappingPasswordKeyStore;
import org.wildfly.security.mechanism.gssapi.GSSCredentialSecurityFactory;
import org.wildfly.security.password.Password;
import org.wildfly.security.password.PasswordFactory;
import org.wildfly.security.password.WildFlyElytronPasswordProvider;
import org.wildfly.security.password.interfaces.ClearPassword;
import org.wildfly.security.password.interfaces.MaskedPassword;
import org.wildfly.security.password.spec.ClearPasswordSpec;
import org.wildfly.security.password.spec.MaskedPasswordSpec;
import org.wildfly.security.pem.Pem;
import org.wildfly.security.pem.PemEntry;
import org.wildfly.security.provider.util.ProviderFactory;
import org.wildfly.security.provider.util.ProviderServiceLoaderSupplier;
import org.wildfly.security.provider.util.ProviderUtil;
import org.wildfly.security.sasl.SaslMechanismSelector;
import org.wildfly.security.sasl.util.ServiceLoaderSaslClientFactory;
import org.wildfly.security.ssl.CipherSuiteSelector;
import org.wildfly.security.ssl.ProtocolSelector;
import org.wildfly.security.ssl.SSLContextBuilder;
import org.wildfly.security.ssl.X509RevocationTrustManager;
import org.wildfly.security.ssh.util.SshUtil;
/**
* A parser for the Elytron XML schema.
*
* @author David M. Lloyd
*/
public final class ElytronXmlParser {
private static final Supplier ELYTRON_PROVIDER_SUPPLIER = ProviderFactory.getElytronProviderSupplier(ElytronXmlParser.class.getClassLoader());
private static final Supplier DEFAULT_PROVIDER_SUPPLIER = ProviderUtil.aggregate(ELYTRON_PROVIDER_SUPPLIER, INSTALLED_PROVIDERS);
static final Map KNOWN_NAMESPACES;
private enum Version {
VERSION_1_0("urn:elytron:1.0", null),
VERSION_1_0_1("urn:elytron:1.0.1", VERSION_1_0),
VERSION_1_1("urn:elytron:client:1.1", VERSION_1_0_1),
VERSION_1_2("urn:elytron:client:1.2", VERSION_1_1),
VERSION_1_3("urn:elytron:client:1.3", VERSION_1_2),
VERSION_1_4("urn:elytron:client:1.4", VERSION_1_3),
VERSION_1_5("urn:elytron:client:1.5", VERSION_1_4),
VERSION_1_6("urn:elytron:client:1.6", VERSION_1_5),
VERSION_1_7("urn:elytron:client:1.7", VERSION_1_6);
final String namespace;
/*
* In the future we could support multiple parents but wait until that becomes a reality before adding it.
*/
final Version parent;
Version(String namespace, Version parent) {
this.namespace = namespace;
this.parent = parent;
}
boolean isAtLeast(Version version) {
return this.equals(version) || (parent != null ? parent.isAtLeast(version) : false);
}
}
static {
Map knownNamespaces = new HashMap<>();
for (Version version : Version.values()) {
knownNamespaces.put(version.namespace, version);
}
KNOWN_NAMESPACES = Collections.unmodifiableMap(knownNamespaces);
}
private ElytronXmlParser() {
}
// authentication client document
/**
* Parse an Elytron authentication client configuration from a configuration discovered using the default wildfly-client-config discovery rules.
*
* @return the authentication context factory
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
public static SecurityFactory parseAuthenticationClientConfiguration() throws ConfigXMLParseException {
final ClientConfiguration clientConfiguration = ClientConfiguration.getInstance();
if (clientConfiguration != null) try (final ConfigurationXMLStreamReader streamReader = clientConfiguration.readConfiguration(KNOWN_NAMESPACES.keySet())) {
if (streamReader != null) {
xmlLog.tracef("Parsing configuration from %s for namespace %s", streamReader.getUri(), streamReader.getNamespaceURI());
return parseAuthenticationClientConfiguration(streamReader);
} else {
if (xmlLog.isTraceEnabled()) {
xmlLog.tracef("No configuration found for known namespaces '%s'", namespacesToString());
}
}
}
xmlLog.trace("Fallback to parse legacy configuration.");
// Try legacy configuration next
return parseLegacyConfiguration();
}
/**
* Parse an Elytron authentication client configuration from a resource located at a specified {@link URI}.
*
* @param uri the {@link URI} of the configuration.
* @return the authentication context factory
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
public static SecurityFactory parseAuthenticationClientConfiguration(URI uri) throws ConfigXMLParseException {
final ClientConfiguration clientConfiguration = ClientConfiguration.getInstance(uri);
if (clientConfiguration != null) try (final ConfigurationXMLStreamReader streamReader = clientConfiguration.readConfiguration(KNOWN_NAMESPACES.keySet())) {
if (streamReader != null) {
xmlLog.tracef("Parsig configuration from %s for namespace %s", streamReader.getUri(), streamReader.getNamespaceURI());
return parseAuthenticationClientConfiguration(streamReader);
} else {
if (xmlLog.isTraceEnabled()) {
xmlLog.tracef("No configuration found for known namespaces '%s'", namespacesToString());
}
}
}
xmlLog.trace("Fallback to parse legacy configuration.");
// Try legacy configuration next
return parseLegacyConfiguration();
}
private static String namespacesToString() {
Iterator namespaceIterator = KNOWN_NAMESPACES.keySet().iterator();
StringBuilder namespaces = new StringBuilder(namespaceIterator.next());
while (namespaceIterator.hasNext()) {
namespaces.append(",").append(namespaceIterator.next());
}
return namespaces.toString();
}
/**
* Parse a Elytron authentication client configuration from a configuration XML reader.
*
* @param reader the XML stream reader
* @return the authentication context factory
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static SecurityFactory parseAuthenticationClientConfiguration(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException {
if (reader.hasNext()) {
switch (reader.nextTag()) {
case START_ELEMENT: {
Version xmlVersion = KNOWN_NAMESPACES.get(checkGetElementNamespace(reader));
switch (reader.getLocalName()) {
case "authentication-client": {
return parseAuthenticationClientType(reader, xmlVersion);
}
default: {
throw reader.unexpectedElement();
}
}
}
default: {
throw reader.unexpectedContent();
}
}
}
xmlLog.trace("No authentication-client element found, falling back to empty AuthenticationContext");
return AuthenticationContext::empty;
}
private static SecurityFactory parseLegacyConfiguration() {
final ServiceLoader loader = ServiceLoader.load(LegacyConfiguration.class, ElytronXmlParser.class.getClassLoader());
final Iterator iterator = loader.iterator();
final List configs = new ArrayList<>();
for (;;) try {
if (! iterator.hasNext()) break;
configs.add(iterator.next());
} catch (ServiceConfigurationError ignored) {}
return () -> {
for (LegacyConfiguration config : configs) {
final AuthenticationContext context = config.getConfiguredAuthenticationContext();
if (context != null) {
xmlLog.trace("Found AuthenticationContext in legacy configuration");
return context;
}
}
xmlLog.trace("No legacy configuration available, using AuthenticationContext.empty()");
return AuthenticationContext.empty();
};
}
// authentication client types
/**
* Parse an XML element of type {@code authentication-client-type} from an XML reader.
*
* @param reader the XML stream reader
* @param xmlVersion the version of parsed XML
* @return the authentication context factory
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static SecurityFactory parseAuthenticationClientType(ConfigurationXMLStreamReader reader, final Version xmlVersion) throws ConfigXMLParseException {
requireNoAttributes(reader);
ExceptionSupplier, ConfigXMLParseException> authFactory = () -> null;
ExceptionSupplier>, ConfigXMLParseException> sslFactory = () -> null;
Map> keyStoresMap = new HashMap<>();
Map> credentialStoresMap = new HashMap<>();
Map, ConfigXMLParseException>> sslContextsMap = new HashMap<>();
Map> authenticationConfigurationsMap = new HashMap<>();
final DeferredSupplier providersSupplier = new DeferredSupplier<>(DEFAULT_PROVIDER_SUPPLIER);
boolean netAuthenticator = false;
int foundBits = 0;
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "authentication-rules": {
if (isSet(foundBits, 0)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 0);
authFactory = parseRulesType(reader, xmlVersion, authenticationConfigurationsMap, (r, m) -> parseAuthenticationRuleType(r, xmlVersion, m));
break;
}
case "ssl-context-rules": {
if (isSet(foundBits, 1)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 1);
sslFactory = parseRulesType(reader, xmlVersion, sslContextsMap, (r,m) -> parseSslContextRuleType(r, xmlVersion, m));
break;
}
case "authentication-configurations": {
if (isSet(foundBits, 2)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 2);
parseAuthenticationConfigurationsType(reader, xmlVersion, authenticationConfigurationsMap, keyStoresMap, credentialStoresMap, providersSupplier);
break;
}
case "ssl-contexts": {
if (isSet(foundBits, 3)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 3);
parseSslContextsType(reader, xmlVersion, sslContextsMap, keyStoresMap, credentialStoresMap, providersSupplier);
break;
}
case "key-stores": {
if (isSet(foundBits, 4)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 4);
parseKeyStoresType(reader, xmlVersion, keyStoresMap, credentialStoresMap, providersSupplier);
break;
}
case "net-authenticator": {
if (isSet(foundBits, 5)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 5);
netAuthenticator = true;
parseEmptyType(reader);
break;
}
case "credential-stores": {
if (isSet(foundBits, 6)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 6);
parseCredentialStoresType(reader, xmlVersion, keyStoresMap, credentialStoresMap, providersSupplier);
break;
}
case "providers": {
if (isSet(foundBits, 7)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 7);
Supplier supplier = parseProvidersType(reader, xmlVersion);
if (supplier != null) {
providersSupplier.setSupplier(supplier);
}
break;
}
default: throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
assert reader.getLocalName().equals("authentication-client");
if (netAuthenticator) {
Authenticator.setDefault(new ElytronAuthenticator());
}
// validate key and credential stores...
for (ExceptionSupplier supplier : keyStoresMap.values()) {
supplier.get();
}
for (ExceptionSupplier supplier : credentialStoresMap.values()) {
supplier.get();
}
final RuleNode authNode = authFactory.get();
final RuleNode> sslNode = sslFactory.get();
return () -> new AuthenticationContext(authNode, sslNode);
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
private static void parseAuthenticationConfigurationsType(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> authenticationConfigurationsMap, final Map> keyStoresMap, final Map> credentialStoresMap, final Supplier providers) throws ConfigXMLParseException {
requireNoAttributes(reader);
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "configuration": {
parseAuthenticationConfigurationType(reader, xmlVersion, authenticationConfigurationsMap, keyStoresMap, credentialStoresMap, providers);
break;
}
default: {
throw reader.unexpectedElement();
}
}
} else if (tag == END_ELEMENT) {
return;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
private static void parseSslContextsType(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map, ConfigXMLParseException>> sslContextsMap, final Map> keyStoresMap, final Map> credentialStoresMap, final Supplier providers) throws ConfigXMLParseException {
requireNoAttributes(reader);
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "ssl-context": {
parseSslContextType(reader, xmlVersion, sslContextsMap, keyStoresMap, credentialStoresMap, providers);
break;
}
case "default-ssl-context": {
final String name = parseNameType(reader);
sslContextsMap.put(name, () -> SSLContext::getDefault);
break;
}
default: {
throw reader.unexpectedElement();
}
}
} else if (tag == END_ELEMENT) {
return;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
private static void parseSslContextType(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map, ConfigXMLParseException>> sslContextsMap, final Map> keyStoresMap, final Map> credentialStoresMap, final Supplier providers) throws ConfigXMLParseException {
final String name = requireSingleAttribute(reader, "name");
if (sslContextsMap.containsKey(name)) {
throw xmlLog.xmlDuplicateSslContextName(name, reader);
}
final XMLLocation location = reader.getLocation();
int foundBits = 0;
String providerName = null;
CipherSuiteSelector cipherSuiteSelector = null;
ProtocolSelector protocolSelector = null;
ExceptionSupplier keyManagerSupplier = null;
ExceptionSupplier trustStoreSupplier = null;
DeferredSupplier providersSupplier = new DeferredSupplier<>(providers);
TrustManagerBuilder trustManagerBuilder = new TrustManagerBuilder(providersSupplier, location);
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "key-store-ssl-certificate": {
if (isSet(foundBits, 0)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 0);
keyManagerSupplier = parseKeyStoreSslCertificate(reader, xmlVersion, keyStoresMap, credentialStoresMap, providers);
break;
}
case "cipher-suite": {
if (isSet(foundBits, 1)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 1);
cipherSuiteSelector = parseCipherSuiteSelectorType(reader, xmlVersion);
break;
}
case "protocol": {
if (isSet(foundBits, 2)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 2);
protocolSelector = parseProtocolSelectorNamesType(reader);
break;
}
case "provider-name": {
if (isSet(foundBits, 3)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 3);
providerName = parseNameType(reader);
break;
}
case "providers": {
if (isSet(foundBits, 4)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 4);
Supplier supplier = parseProvidersType(reader, xmlVersion);
if (supplier != null) {
providersSupplier.setSupplier(supplier);
}
break;
}
case "trust-store": {
if (isSet(foundBits, 5)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 5);
trustStoreSupplier = parseTrustStoreRefType(reader, keyStoresMap);
break;
}
case "certificate-revocation-list": {
// rejects attribute if certificate-revocation-lists has been set
if (isSet(foundBits, 6) || isSet(foundBits, 9)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 6);
parseCertificateRevocationList(reader, trustManagerBuilder, xmlVersion, false);
break;
}
case "trust-manager": {
if (isSet(foundBits, 7) || !xmlVersion.isAtLeast(Version.VERSION_1_1)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 7);
parseTrustManager(reader, trustManagerBuilder, xmlVersion);
break;
}
case "ocsp": {
if (isSet(foundBits, 8) || !xmlVersion.isAtLeast(Version.VERSION_1_4)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 8);
parseOcsp(reader, trustManagerBuilder, keyStoresMap);
break;
}
case "certificate-revocation-lists" : {
// rejects attribute if certificate-revocation-list has been set
if (isSet(foundBits, 9) || isSet(foundBits, 6) || !xmlVersion.isAtLeast(Version.VERSION_1_7)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 9);
parseCertificateRevocationLists(reader, trustManagerBuilder, xmlVersion);
break;
}
default: throw reader.unexpectedElement();
}
} else if (tag != END_ELEMENT) {
throw reader.unexpectedContent();
} else {
// ready to register!
final Supplier finalProvidersSupplier = providersSupplier;
final ProtocolSelector finalProtocolSelector = protocolSelector;
final CipherSuiteSelector finalCipherSuiteSelector = cipherSuiteSelector;
final String finalProviderName = providerName;
final ExceptionSupplier finalKeyManagerSupplier = keyManagerSupplier;
final ExceptionSupplier finalTrustStoreSupplier = trustStoreSupplier;
final boolean initTrustManager = finalTrustStoreSupplier != null || isSet(foundBits, 7);
sslContextsMap.putIfAbsent(name, () -> {
final SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
sslContextBuilder.setClientMode(true);
if (finalCipherSuiteSelector != null) {
sslContextBuilder.setCipherSuiteSelector(finalCipherSuiteSelector);
}
if (finalProtocolSelector != null) {
sslContextBuilder.setProtocolSelector(finalProtocolSelector);
}
if (finalKeyManagerSupplier != null) {
sslContextBuilder.setKeyManager(finalKeyManagerSupplier.get());
}
if (initTrustManager) {
if (finalTrustStoreSupplier != null) {
trustManagerBuilder.setTrustStore(finalTrustStoreSupplier.get());
}
try {
sslContextBuilder.setTrustManager(trustManagerBuilder.build());
} catch (GeneralSecurityException e) {
throw new ConfigXMLParseException(e);
}
}
sslContextBuilder.setProviderName(finalProviderName);
sslContextBuilder.setProviderSupplier(finalProvidersSupplier);
sslContextBuilder.setUseCipherSuitesOrder(true);
return sslContextBuilder.build();
});
return;
}
}
throw reader.unexpectedDocumentEnd();
}
private static class TrustManagerBuilder {
final Supplier providers;
final Location xmlLocation;
String providerName = null;
String algorithm = null;
KeyStore trustStore;
boolean crl = false;
List crlStreams = new ArrayList<>();
int maxCertPath = 5;
boolean ocsp = false;
boolean preferCrls = false;
boolean onlyLeafCert = false;
boolean softFail = false;
URI ocspResponder = null;
boolean maxCertPathSet = false;
String responderCertAlias = null;
ExceptionSupplier responderStoreSupplier = null;
TrustManagerBuilder(Supplier providers, Location xmlLocation) {
this.providers = providers;
this.xmlLocation = xmlLocation;
}
void setProviderName(String providerName) {
this.providerName = providerName;
}
void setAlgorithm(String algorithm) {
this.algorithm = algorithm;
}
void setTrustStore(KeyStore trustStore) {
this.trustStore = trustStore;
}
void setCrl() {
this.crl = true;
}
void addCrlStream(InputStream crlStream) {
this.crlStreams.add(crlStream);
}
void setMaxCertPath(int maxCertPath) {
checkMinimumParameter("maxCertPath", 1, maxCertPath);
this.maxCertPath = maxCertPath;
this.maxCertPathSet = true;
}
boolean isMaxCertPathSet() {
return maxCertPathSet;
}
public void setOcsp() {
this.ocsp = true;
}
public void setPreferCrls(boolean preferCrls) {
this.preferCrls = preferCrls;
}
public void setOnlyLeafCert(boolean onlyLeafCert) {
this.onlyLeafCert = onlyLeafCert;
}
public void setSoftFail(boolean softFail) {
this.softFail = softFail;
}
public void setOcspResponder(URI ocspResponder) {
this.ocspResponder = ocspResponder;
}
public void setOcspRescponderCertAlias(String alias) {
this.responderCertAlias = alias;
}
public void setOcspResponderCertKeystoreSupplier(ExceptionSupplier supplier) {
this.responderStoreSupplier = supplier;
}
X509TrustManager build() throws NoSuchAlgorithmException, KeyStoreException, ConfigXMLParseException {
final String algorithm = this.algorithm != null ? this.algorithm : TrustManagerFactory.getDefaultAlgorithm();
Provider provider = findProvider(providers, providerName, TrustManagerFactory.class, algorithm);
if (provider == null) {
throw xmlLog.xmlUnableToIdentifyProvider(xmlLocation, providerName, "TrustManagerFactory", algorithm);
}
final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm, provider);
if (crl || ocsp) {
X509RevocationTrustManager.Builder revocationBuilder = X509RevocationTrustManager.builder();
revocationBuilder.setCrlStreams(crlStreams);
revocationBuilder.setResponderURI(ocspResponder);
revocationBuilder.setTrustManagerFactory(trustManagerFactory);
revocationBuilder.setTrustStore(trustStore);
revocationBuilder.setOnlyEndEntity(onlyLeafCert);
revocationBuilder.setSoftFail(softFail);
revocationBuilder.setMaxCertPath(maxCertPath);
if (crl && ocsp) {
revocationBuilder.setPreferCrls(preferCrls);
revocationBuilder.setNoFallback(false);
} else if (crl) {
revocationBuilder.setPreferCrls(true);
revocationBuilder.setNoFallback(true);
} else {
revocationBuilder.setPreferCrls(false);
revocationBuilder.setNoFallback(true);
}
if (responderCertAlias != null) {
KeyStore responderStore = responderStoreSupplier != null ? responderStoreSupplier.get() : trustStore;
revocationBuilder.setOcspResponderCert((X509Certificate) responderStore.getCertificate(responderCertAlias));
}
return revocationBuilder.build();
} else {
trustManagerFactory.init(trustStore);
}
for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) {
if (trustManager instanceof X509TrustManager) {
return (X509TrustManager) trustManager;
}
}
throw ElytronMessages.log.noDefaultTrustManager();
}
}
private static void parseCertificateRevocationLists(ConfigurationXMLStreamReader reader, TrustManagerBuilder builder, final Version xmlVersion) throws ConfigXMLParseException {
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
if (reader.getLocalName().equals("certificate-revocation-list")) {
parseCertificateRevocationList(reader, builder, xmlVersion, true);
} else {
throw reader.unexpectedElement();
}
} else if (tag != END_ELEMENT) {
throw reader.unexpectedContent();
} else if (tag == END_ELEMENT) {
return;
}
}
throw reader.unexpectedDocumentEnd();
}
private static void parseCertificateRevocationList(ConfigurationXMLStreamReader reader, TrustManagerBuilder builder, final Version xmlVersion, boolean multipleCrls) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
String path = null;
ExceptionSupplier resourceSource = null;
URI uriSource = null;
boolean gotSource = false;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "path": {
if (gotSource) throw reader.unexpectedAttribute(i);
gotSource = true;
path = reader.getAttributeValueResolved(i);
break;
}
case "maximum-cert-path": { //Deprecated
if (builder.isMaxCertPathSet() || multipleCrls) throw reader.unexpectedAttribute(i);
xmlLog.xmlDeprecatedElement("maximum-cert-path", reader.getLocation());
// only set the maximum-cert-path inside a CRL if it's a single CRL.
builder.setMaxCertPath(reader.getIntAttributeValueResolved(i, 1, Integer.MAX_VALUE));
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "resource": {
if (gotSource || !xmlVersion.isAtLeast(Version.VERSION_1_1)) {
throw reader.unexpectedElement();
}
gotSource = true;
resourceSource = parseResourceType(reader, xmlVersion);
break;
}
case "uri": {
if (gotSource || !xmlVersion.isAtLeast(Version.VERSION_1_1)) {
throw reader.unexpectedElement();
}
gotSource = true;
uriSource = parseUriType(reader);
break;
}
default: throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
builder.setCrl();
if (gotSource) {
try {
if (path != null) builder.addCrlStream(new FileInputStream(path));
else if (resourceSource != null) builder.addCrlStream(resourceSource.get());
else if (uriSource != null) builder.addCrlStream(uriSource.toURL().openStream());
} catch (IOException e) {
throw new ConfigXMLParseException(e);
}
}
return;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
private static void parseOcsp(ConfigurationXMLStreamReader reader, TrustManagerBuilder builder, Map> keyStoresMap) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
boolean gotPreferCrls = false;
boolean gotResponder = false;
boolean gotResponderCertAlias = false;
boolean gotResponderKeystore = false;
builder.setOcsp();
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "responder": {
if (gotResponder) throw reader.unexpectedAttribute(i);
builder.setOcspResponder(reader.getURIAttributeValueResolved(i));
gotResponder = true;
break;
}
case "prefer-crls": {
if (gotPreferCrls) throw reader.unexpectedAttribute(i);
builder.setPreferCrls(reader.getBooleanAttributeValueResolved(i));
gotPreferCrls = true;
break;
}
case "responder-certificate": {
if (gotResponderCertAlias) throw reader.unexpectedAttribute(i);
builder.setOcspRescponderCertAlias(reader.getAttributeValueResolved(i));
gotResponderCertAlias = true;
break;
}
case "responder-keystore": {
if (gotResponderKeystore) throw reader.unexpectedAttribute(i);
builder.setOcspResponderCertKeystoreSupplier(keyStoresMap.get(reader.getAttributeValueResolved(i)));
gotResponderKeystore = true;
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == END_ELEMENT) {
return;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
private static ExceptionSupplier parseKeyStoreSslCertificate(ConfigurationXMLStreamReader reader,
final Version xmlVersion, Map> keyStoresMap,
final Map> credentialStoresMap, final Supplier providers)
throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
final XMLLocation location = reader.getLocation();
String providerName = null;
String algorithm = null;
String keyStoreName = null;
String alias = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "provider-name": {
if (providerName != null || !xmlVersion.isAtLeast(Version.VERSION_1_2)) throw reader.unexpectedAttribute(i);
providerName = reader.getAttributeValueResolved(i);
break;
}
case "algorithm": {
if (providerName != null || !xmlVersion.isAtLeast(Version.VERSION_1_2)) throw reader.unexpectedAttribute(i);
if (algorithm != null) throw reader.unexpectedAttribute(i);
algorithm = reader.getAttributeValueResolved(i);
break;
}
case "key-store-name": {
if (keyStoreName != null) throw reader.unexpectedAttribute(i);
keyStoreName = reader.getAttributeValueResolved(i);
break;
}
case "alias": {
if (alias != null) throw reader.unexpectedAttribute(i);
alias = reader.getAttributeValueResolved(i);
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
if (keyStoreName == null) {
throw missingAttribute(reader, "key-store-name");
}
ExceptionSupplier keyStoreCredential = null;
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "key-store-credential": {
if (keyStoreCredential != null) throw reader.unexpectedElement();
keyStoreCredential = parseKeyStoreRefType(reader, xmlVersion, keyStoresMap, credentialStoresMap, providers);
break;
}
case "key-store-clear-password": {
if (keyStoreCredential != null) throw reader.unexpectedElement();
ExceptionSupplier credential = parseClearPassword(reader, providers);
keyStoreCredential = () -> new PasswordEntry(credential.get());
break;
}
case "key-store-masked-password": {
if (keyStoreCredential != null || !xmlVersion.isAtLeast(Version.VERSION_1_4)) throw reader.unexpectedElement();
ExceptionSupplier credential = parseMaskedPassword(reader, providers);
keyStoreCredential = () -> new PasswordEntry(credential.get());
break;
}
case "credential-store-reference": {
if (keyStoreCredential != null || !xmlVersion.isAtLeast(Version.VERSION_1_0_1)) {
throw reader.unexpectedElement();
}
final XMLLocation nestedLocation = reader.getLocation();
ExceptionSupplier credentialSourceSupplier = parseCredentialStoreRefType(reader, credentialStoresMap);
keyStoreCredential = () -> {
try {
PasswordCredential passwordCredential = credentialSourceSupplier.get().getCredential(PasswordCredential.class);
if (passwordCredential == null) {
throw new ConfigXMLParseException(xmlLog.couldNotObtainCredential(), reader);
}
return new PasswordEntry(passwordCredential.getPassword());
} catch (IOException e) {
throw xmlLog.xmlFailedToCreateCredential(nestedLocation, e);
}
};
break;
}
default: throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
final ExceptionSupplier finalKeyStoreCredential = keyStoreCredential;
final String finalAlgorithm = algorithm;
final String finalProviderName = providerName;
final String finalKeyStoreName = keyStoreName;
final String finalAlias = alias;
return () -> {
try {
final ExceptionSupplier keyStoreSupplier = keyStoresMap.get(finalKeyStoreName);
if (keyStoreSupplier == null) {
throw xmlLog.xmlUnknownKeyStoreSpecified(location);
}
KeyStore keyStore = keyStoreSupplier.get();
if (xmlLog.isTraceEnabled()) {
xmlLog.tracef("Using KeyStore [%s] containing aliases %s", finalKeyStoreName, aliasesToString(keyStore.aliases()));
}
if (finalAlias != null) {
keyStore = FilteringKeyStore.filteringKeyStore(keyStore, AliasFilter.fromString(finalAlias));
if (xmlLog.isTraceEnabled()) xmlLog.tracef("Filtered aliases %s", aliasesToString(keyStore.aliases()));
if (keyStore.size() < 1) throw xmlLog.keyStoreEntryMissing(location, finalAlias);
}
String algorithmResolved = finalAlgorithm != null ? finalAlgorithm : KeyManagerFactory.getDefaultAlgorithm();
Provider provider = findProvider(providers, finalProviderName, KeyManagerFactory.class, algorithmResolved);
if (provider == null) {
throw xmlLog.xmlUnableToIdentifyProvider(location, finalProviderName, "KeyManagerFactory", algorithmResolved);
}
char[] password = keyStoreCredentialToPassword(finalKeyStoreCredential, providers);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(algorithmResolved, provider);
keyManagerFactory.init(keyStoreSupplier.get(), password);
for (KeyManager keyManager : keyManagerFactory.getKeyManagers()) {
if (keyManager instanceof X509ExtendedKeyManager) {
return (X509ExtendedKeyManager) keyManager;
}
}
throw ElytronMessages.log.noDefaultKeyManager();
} catch (GeneralSecurityException | IOException e) {
throw xmlLog.xmlFailedToLoadKeyStoreData(location, e);
}
};
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
private static String aliasesToString(Enumeration aliases) {
StringBuilder builder = new StringBuilder("[");
while (aliases.hasMoreElements()) {
builder.append(aliases.nextElement());
if (aliases.hasMoreElements()) builder.append(", ");
}
return builder.append(']').toString();
}
private static void parseTrustManager(ConfigurationXMLStreamReader reader, TrustManagerBuilder builder, final Version xmlVersion) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
String providerName = null;
String algorithm = null;
boolean gotSoftFail = false;
boolean gotOnlyLeafCert = false;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "provider-name": {
if (providerName != null) throw reader.unexpectedAttribute(i);
providerName = reader.getAttributeValueResolved(i);
break;
}
case "algorithm": {
if (algorithm != null) throw reader.unexpectedAttribute(i);
algorithm = reader.getAttributeValueResolved(i);
break;
}
case "soft-fail": {
if (gotSoftFail || !xmlVersion.isAtLeast(Version.VERSION_1_4)) {
throw reader.unexpectedAttribute(i);
}
gotSoftFail = true;
builder.setSoftFail(reader.getBooleanAttributeValueResolved(i));
break;
}
case "maximum-cert-path": {
if (builder.isMaxCertPathSet() || !xmlVersion.isAtLeast(Version.VERSION_1_4)) {
throw reader.unexpectedAttribute(i);
}
builder.setMaxCertPath(reader.getIntAttributeValueResolved(i, 1, Integer.MAX_VALUE));
break;
}
case "only-leaf-cert": {
if (gotOnlyLeafCert || !xmlVersion.isAtLeast(Version.VERSION_1_4)) {
throw reader.unexpectedAttribute(i);
}
gotOnlyLeafCert = true;
builder.setOnlyLeafCert(reader.getBooleanAttributeValueResolved(i));
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
throw reader.unexpectedElement();
} else if (tag == END_ELEMENT) {
builder.setProviderName(providerName);
builder.setAlgorithm(algorithm);
return;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
static ExceptionUnaryOperator>, ConfigXMLParseException> parseSslContextRuleType(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map, ConfigXMLParseException>> sslContextsMap) throws ConfigXMLParseException {
final String attributeName = "use-ssl-context";
final String name = requireSingleAttribute(reader, attributeName);
final XMLLocation location = reader.getLocation();
final MatchRule rule = parseAbstractMatchRuleType(reader, xmlVersion);
return next -> {
final ExceptionSupplier, ConfigXMLParseException> factory = sslContextsMap.get(name);
if (factory == null) throw xmlLog.xmlUnknownSslContextSpecified(location, name);
return new RuleNode<>(next, rule, factory.get());
};
}
static ExceptionUnaryOperator, ConfigXMLParseException> parseAuthenticationRuleType(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> authenticationConfigurationsMap) throws ConfigXMLParseException {
final String attributeName = "use-configuration";
final String name = requireSingleAttribute(reader, attributeName);
final XMLLocation location = reader.getLocation();
final MatchRule rule = parseAbstractMatchRuleType(reader, xmlVersion);
return next -> {
final ExceptionSupplier factory = authenticationConfigurationsMap.get(name);
if (factory == null) throw xmlLog.xmlUnknownAuthenticationConfigurationSpecified(location, name);
return new RuleNode<>(next, rule, factory.get());
};
}
static ExceptionSupplier, ConfigXMLParseException> parseRulesType(ConfigurationXMLStreamReader reader, final Version xmlVersion,
final Map> configurations, ExceptionBiFunction>, ExceptionUnaryOperator, ConfigXMLParseException>, ConfigXMLParseException> ruleParseFunction)
throws ConfigXMLParseException {
requireNoAttributes(reader);
final List, ConfigXMLParseException>> rulesList = new ArrayList<>();
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
if (reader.getLocalName().equals("rule")) {
rulesList.add(ruleParseFunction.apply(reader, configurations));
} else {
throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
return () -> {
RuleNode node = null;
final ListIterator, ConfigXMLParseException>> iterator = rulesList.listIterator(rulesList.size());
// iterate backwards to build the singly-linked list in constant time
while (iterator.hasPrevious()) {
node = iterator.previous().apply(node);
}
return node;
};
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
static void parseAuthenticationConfigurationType(ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> authenticationConfigurationsMap,
final Map> keyStoresMap, final Map> credentialStoresMap,
final Supplier providers) throws ConfigXMLParseException {
final String name = requireSingleAttribute(reader, "name");
if (authenticationConfigurationsMap.containsKey(name)) {
throw xmlLog.xmlDuplicateAuthenticationConfigurationName(name, reader);
}
ExceptionUnaryOperator configuration = ignored -> AuthenticationConfiguration.empty();
DeferredSupplier providerSupplier = new DeferredSupplier<>(providers);
configuration = andThenOp(configuration, parent -> parent.useProviders(providerSupplier));
int foundBits = 0;
if (! reader.hasNext()) {
throw reader.unexpectedDocumentEnd();
}
while (reader.hasNext()) {
int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
// -- set --
case "set-host": {
if (isSet(foundBits, 0)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 0);
final String hostName = parseNameType(reader);
configuration = andThenOp(configuration, parentConfig -> parentConfig.useHost(hostName));
xmlLog.xmlDeprecatedElement(reader.getLocalName(), reader.getLocation());
break;
}
case "set-port": {
if (isSet(foundBits, 1)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 1);
final int port = parsePortType(reader);
configuration = andThenOp(configuration, parentConfig -> parentConfig.usePort(port));
xmlLog.xmlDeprecatedElement(reader.getLocalName(), reader.getLocation());
break;
}
// these two are a which is why they share a bit #; you can have only one of them
case "set-user-name": {
if (isSet(foundBits, 2)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 2);
final String userName = parseNameType(reader);
configuration = andThenOp(configuration, parentConfig -> parentConfig.useName(userName));
break;
}
case "set-anonymous": {
if (isSet(foundBits, 2)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 2);
parseEmptyType(reader);
configuration = andThenOp(configuration, AuthenticationConfiguration::useAnonymous);
break;
}
case "set-mechanism-realm": {
if (isSet(foundBits, 3)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 3);
final String realm = parseNameType(reader);
configuration = andThenOp(configuration, parentConfig -> parentConfig.useRealm(realm));
break;
}
case "rewrite-user-name-regex": {
if (isSet(foundBits, 4)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 4);
final NameRewriter nameRewriter = parseRegexSubstitutionType(reader);
configuration = andThenOp(configuration, parentConfig -> parentConfig.rewriteUser(nameRewriter));
break;
}
case "set-mechanism-properties": {
if (isSet(foundBits, 5)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 5);
final Map mechanismProperties = parsePropertiesType(reader, xmlVersion);
configuration = andThenOp(configuration, parentConfig -> parentConfig.useSaslMechanismProperties(mechanismProperties, true));
break;
}
case "sasl-mechanism-selector": {
if (isSet(foundBits, 6)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 6);
final SaslMechanismSelector selector = parseSaslMechanismSelectorType(reader);
configuration = andThenOp(configuration, parentConfig -> parentConfig.setSaslMechanismSelector(selector));
break;
}
case "credentials": {
if (isSet(foundBits, 9)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 9);
final ExceptionSupplier credentialSource = parseCredentialsType(reader, xmlVersion, keyStoresMap, credentialStoresMap, providerSupplier);
configuration = andThenOp(configuration, parentConfig -> parentConfig.useCredentials(credentialSource.get()));
break;
}
case "set-authorization-name": {
if (isSet(foundBits, 10)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 10);
final String authName = parseNameType(reader);
configuration = andThenOp(configuration, parentConfig -> parentConfig.useAuthorizationName(authName));
break;
}
case "providers": {
if (isSet(foundBits, 11)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 11);
Supplier supplier = parseProvidersType(reader, xmlVersion);
if (supplier != null) {
providerSupplier.setSupplier(supplier);
}
break;
}
// these two are a which is why they share a bit #; you can have only one of them
case "use-provider-sasl-factory": {
if (isSet(foundBits, 12)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 12);
parseEmptyType(reader);
configuration = andThenOp(configuration, AuthenticationConfiguration::useSaslClientFactoryFromProviders);
break;
}
case "use-service-loader-sasl-factory": {
if (isSet(foundBits, 12)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 12);
final String moduleName = parseModuleRefType(reader);
final ClassLoader classLoader = (moduleName == null) ? ElytronXmlParser.class.getClassLoader() : ModuleLoader.getClassLoaderFromModule(reader, moduleName);
configuration = andThenOp(configuration, parentConfig -> parentConfig.useSaslClientFactory(new ServiceLoaderSaslClientFactory(classLoader)));
break;
}
case "set-protocol": {
if (isSet(foundBits, 13)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 13);
final String protocol = parseNameType(reader);
configuration = andThenOp(configuration, parentConfig -> parentConfig.useProtocol(protocol));
xmlLog.xmlDeprecatedElement(reader.getLocalName(), reader.getLocation());
break;
}
case "webservices": {
if (isSet(foundBits, 14)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 14);
Map webServices = parseWebServicesType(reader, xmlVersion);
configuration = andThenOp(configuration, parentConfig -> parentConfig.useWebServices(webServices));
break;
}
default: {
throw reader.unexpectedElement();
}
}
} else if (tag == END_ELEMENT) {
final ExceptionUnaryOperator finalConfiguration = configuration;
authenticationConfigurationsMap.put(name, () -> finalConfiguration.apply(AuthenticationConfiguration.empty()));
return;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
static Supplier parseProvidersType(ConfigurationXMLStreamReader reader, final Version xmlVersion) throws ConfigXMLParseException {
requireNoAttributes(reader);
Supplier providerSupplier = null;
int foundBits = 0;
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "global": {
if (isSet(foundBits, 1)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 1);
parseEmptyType(reader);
providerSupplier = providerSupplier == null ? INSTALLED_PROVIDERS : ProviderUtil.aggregate(providerSupplier, INSTALLED_PROVIDERS);
break;
}
case "use-service-loader": {
if (isSet(foundBits, 2)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 2);
final String moduleName = parseModuleRefType(reader);
Supplier serviceLoaderSupplier = (moduleName == null) ?
ELYTRON_PROVIDER_SUPPLIER :
new ProviderServiceLoaderSupplier(ModuleLoader.getClassLoaderFromModule(reader, moduleName));
providerSupplier = providerSupplier == null ? serviceLoaderSupplier : ProviderUtil.aggregate(providerSupplier, serviceLoaderSupplier);
break;
}
default: throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
return providerSupplier;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
static Map parseWebServicesType(ConfigurationXMLStreamReader reader, final Version xmlVersion) throws ConfigXMLParseException {
requireNoAttributes(reader);
int foundBits = 0;
if (! reader.hasNext()) {
throw reader.unexpectedDocumentEnd();
}
Map propertiesMap = new HashMap<>();
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "set-http-mechanism": {
if (isSet(foundBits, 0)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 0);
final String httpMechName = parseNameType(reader);
propertiesMap.put("http-mechanism", httpMechName);
break;
}
case "set-ws-security-type": {
if (isSet(foundBits, 1)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 1);
final String wsSecurityType = parseNameType(reader);
propertiesMap.put("ws-security-type", wsSecurityType);
break;
}
default: throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
return propertiesMap;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse the XML match-rule group. On return, the reader will be positioned either at a start tag for an element
* that is not included in this group, or at an end tag.
*
* @param reader the XML reader
* @param xmlVersion the version of parsed XML
* @return the parsed match rule
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static MatchRule parseAbstractMatchRuleType(ConfigurationXMLStreamReader reader, final Version xmlVersion) throws ConfigXMLParseException {
MatchRule rule = MatchRule.ALL;
int foundBits = 0;
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
// -- match --
case "match-no-user": {
if (isSet(foundBits, 0)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 0);
parseEmptyType(reader);
rule = rule.matchNoUser();
break;
}
case "match-user": {
if (isSet(foundBits, 0)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 0);
rule = rule.matchUser(parseNameType(reader));
break;
}
case "match-protocol": {
if (isSet(foundBits, 1)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 1);
rule = rule.matchProtocol(parseNameType(reader));
break;
}
case "match-host": {
if (isSet(foundBits, 2)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 2);
rule = rule.matchHost(parseNameType(reader));
break;
}
case "match-path": {
if (isSet(foundBits, 3)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 3);
rule = rule.matchPath(parseNameType(reader));
break;
}
case "match-port": {
if (isSet(foundBits, 4)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 4);
rule = rule.matchPort(parsePortType(reader));
break;
}
case "match-urn": {
if (isSet(foundBits, 5)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 5);
rule = rule.matchUrnName(parseNameType(reader));
break;
}
case "match-domain": {
if (isSet(foundBits, 6)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 6);
rule = rule.matchLocalSecurityDomain(parseNameType(reader));
break;
}
case "match-abstract-type": {
if (isSet(foundBits, 7)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 7);
rule = parseMatchAbstractType(rule, reader);
break;
}
default: {
return rule;
}
}
} else {
return rule;
}
}
throw reader.unexpectedDocumentEnd();
}
private static MatchRule parseMatchAbstractType(final MatchRule rule, final ConfigurationXMLStreamReader reader) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
String name = null;
String authority = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "name": name = reader.getAttributeValueResolved(i); break;
case "authority": authority = reader.getAttributeValueResolved(i); break;
default: throw reader.unexpectedAttribute(i);
}
}
if (! reader.hasNext()) throw reader.unexpectedDocumentEnd();
if (reader.nextTag() != END_ELEMENT) throw reader.unexpectedElement();
return name == null && authority == null ? rule : rule.matchAbstractType(name, authority);
}
private static boolean isSet(int var, int bit) {
return (var & 1 << bit) != 0;
}
private static int setBit(int var, int bit) {
return var | 1 << bit;
}
private static ExceptionUnaryOperator andThenOp(ExceptionUnaryOperator first, ExceptionUnaryOperator second) {
return t -> second.apply(first.apply(t));
}
private static ExceptionSupplier parseCredentialsType(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> keyStoresMap, final Map> credentialStoresMap, Supplier providers) throws ConfigXMLParseException {
ExceptionUnaryOperator function = parent -> CredentialSource.NONE;
requireNoAttributes(reader);
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "key-store-reference": {
final ExceptionSupplier supplier = parseKeyStoreRefType(reader, xmlVersion, keyStoresMap, credentialStoresMap, providers);
function = andThenOp(function, credentialSource -> credentialSource.with(new KeyStoreCredentialSource(new FixedSecurityFactory(supplier.get()))));
break;
}
case "credential-store-reference": {
final ExceptionSupplier supplier = parseCredentialStoreRefType(reader, credentialStoresMap);
function = andThenOp(function, credentialSource -> credentialSource.with(supplier.get()));
break;
}
case "clear-password": {
ExceptionSupplier password = parseClearPassword(reader, providers);
function = andThenOp(function, credentialSource -> credentialSource.with(IdentityCredentials.NONE.withCredential(new PasswordCredential(password.get()))));
break;
}
case "masked-password": {
if ( ! xmlVersion.isAtLeast(Version.VERSION_1_4)) {
throw reader.unexpectedElement();
}
final XMLLocation location = reader.getLocation();
ExceptionSupplier password = parseMaskedPassword(reader, providers);
Password maskedPassword = password.get();
Password finalPassword;
try {
final PasswordFactory passwordFactory = PasswordFactory.getInstance(maskedPassword.getAlgorithm(), providers);
final ClearPasswordSpec spec = passwordFactory.getKeySpec(maskedPassword, ClearPasswordSpec.class);
final char[] clearPassword = spec.getEncodedPassword();
PasswordFactory clearPasswordFactory = PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR, providers);
finalPassword = clearPasswordFactory.generatePassword(new ClearPasswordSpec(clearPassword)).castAs(ClearPassword.class);
} catch (InvalidKeySpecException | NoSuchAlgorithmException cause) {
throw xmlLog.xmlFailedToCreateCredential(location, cause);
}
function = andThenOp(function, credentialSource -> credentialSource.with(IdentityCredentials.NONE.withCredential(new PasswordCredential(finalPassword))));
break;
}
case "key-pair": {
KeyPairCredential keyPairCredential = parseKeyPair(reader, xmlVersion, credentialStoresMap, providers);
function = andThenOp(function, credentialSource -> credentialSource.with(IdentityCredentials.NONE.withCredential(keyPairCredential)));
break;
}
case "certificate": {
X509CertificateChainPrivateCredential credential = parseCertificateType(reader, xmlVersion);
function = andThenOp(function, credentialSource -> credentialSource.with(IdentityCredentials.NONE.withCredential(credential)));
break;
}
case "public-key-pem": {
PublicKey publicKey = parsePem(reader, PublicKey.class);
function = andThenOp(function, credentialSource -> credentialSource.with(IdentityCredentials.NONE.withCredential(new PublicKeyCredential(publicKey))));
break;
}
case "bearer-token": {
BearerTokenCredential bearerToken = parseBearerTokenType(reader);
function = andThenOp(function, credentialSource -> credentialSource.with(IdentityCredentials.NONE.withCredential(bearerToken)));
break;
}
case "oauth2-bearer-token": {
final ExceptionSupplier oauthCredentialSourceSupplier = parseOAuth2BearerTokenType(reader, credentialStoresMap, xmlVersion);
function = andThenOp(function, credentialSource -> credentialSource.with(oauthCredentialSourceSupplier.get()));
break;
}
case "local-kerberos": {
if ( ! xmlVersion.isAtLeast(Version.VERSION_1_1)) {
throw reader.unexpectedElement();
}
CredentialSource kerberosCredentialSource = parseLocalKerberos(reader);
function = andThenOp(function, credentialSource -> credentialSource.with(kerberosCredentialSource));
xmlLog.xmlDeprecatedElement(reader.getLocalName(), reader.getLocation());
break;
}
case "ssh-credential": {
if ( ! xmlVersion.isAtLeast(Version.VERSION_1_6)) {
throw reader.unexpectedElement();
}
SSHCredential sshCredential = parseSSHKeyLocationCredential(reader, xmlVersion, credentialStoresMap, providers);
function = andThenOp(function, credentialSource -> credentialSource.with(credentialSource.with(IdentityCredentials.NONE.withCredential(sshCredential))));
break;
}
default: {
throw reader.unexpectedElement();
}
}
} else if (tag == END_ELEMENT) {
assert reader.getLocalName().equals("credentials") || reader.getLocalName().equals("protection-parameter-credentials");
final ExceptionUnaryOperator finalFunction = function;
return () -> finalFunction.apply(null);
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
private static SSHCredential parseSSHKeyLocationCredential(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> credentialStoresMap, Supplier providers) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
String sshDirectory = null;
String privateKeyIdentity = null;
Credential passphrase = null;
String knownHostsFile = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "ssh-directory": {
sshDirectory = reader.getAttributeValueResolved(i);
break;
}
case "private-key-file": {
privateKeyIdentity = reader.getAttributeValueResolved(i);
break;
}
case "known-hosts-file": {
knownHostsFile = reader.getAttributeValueResolved(i);
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "credential-store-reference": {
final ExceptionSupplier supplier = parseCredentialStoreRefType(reader, credentialStoresMap);
try {
passphrase = supplier.get().getCredential(PasswordCredential.class);
} catch (IOException cause) {
throw xmlLog.couldNotObtainCredential();
}
break;
}
case "clear-password": {
ExceptionSupplier password = parseClearPassword(reader, providers);
passphrase = new PasswordCredential(password.get());
break;
}
case "masked-password": {
final XMLLocation location = reader.getLocation();
ExceptionSupplier password = parseMaskedPassword(reader, providers);
Password maskedPassword = password.get();
Password finalPassword;
try {
final PasswordFactory passwordFactory = PasswordFactory.getInstance(maskedPassword.getAlgorithm(), providers);
final ClearPasswordSpec spec = passwordFactory.getKeySpec(maskedPassword, ClearPasswordSpec.class);
final char[] clearPassword = spec.getEncodedPassword();
PasswordFactory clearPasswordFactory = PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR, providers);
finalPassword = clearPasswordFactory.generatePassword(new ClearPasswordSpec(clearPassword)).castAs(ClearPassword.class);
} catch (InvalidKeySpecException | NoSuchAlgorithmException cause) {
throw xmlLog.xmlFailedToCreateCredential(location, cause);
}
passphrase = new PasswordCredential(finalPassword);
break;
}
default: throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
SSHCredential.Builder builder = SSHCredential.builder();
if (sshDirectory != null) builder.setSSHDirectory(sshDirectory);
if (privateKeyIdentity != null) builder.setPrivateKeyIdentity(privateKeyIdentity);
if (passphrase != null) builder.setPassphrase(passphrase);
if (knownHostsFile != null) builder.setKnownHostsFile(knownHostsFile);
return builder.build();
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
private static KeyPairCredential parseKeyPair(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> credentialStoresMap, Supplier providers) throws ConfigXMLParseException {
PrivateKey privateKey = null;
PublicKey publicKey = null;
KeyPair keyPair = null;
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "private-key-pem": {
if (privateKey != null || keyPair != null) throw reader.unexpectedElement();
privateKey = parsePem(reader, PrivateKey.class);
break;
}
case "public-key-pem": {
if (publicKey != null || keyPair != null) throw reader.unexpectedElement();
publicKey = parsePem(reader, PublicKey.class);
break;
}
case "openssh-private-key": {
if ( ! xmlVersion.isAtLeast(Version.VERSION_1_6)) {
throw reader.unexpectedElement();
}
if (keyPair != null || privateKey != null || publicKey != null) throw reader.unexpectedElement();
keyPair = parseOpenSSHKeyType(reader, xmlVersion, credentialStoresMap, providers);
break;
}
default: {
throw reader.unexpectedElement();
}
}
} else if (tag == END_ELEMENT) {
if (keyPair == null) {
if (privateKey == null) throw reader.missingRequiredElement(xmlVersion.namespace, "private-key-pem");
if (publicKey == null) throw reader.missingRequiredElement(xmlVersion.namespace, "public-key-pem");
keyPair = new KeyPair(publicKey, privateKey);
}
return new KeyPairCredential(keyPair);
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
private static KeyPair parseOpenSSHKeyType(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> credentialStoresMap, Supplier providers) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
ExceptionUnaryOperator function = parent -> CredentialSource.NONE;
String keyContent = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "pem": {
if (keyContent != null) throw reader.unexpectedAttribute(i);
keyContent = reader.getAttributeValueResolved(i);
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "credential-store-reference": {
final ExceptionSupplier supplier = parseCredentialStoreRefType(reader, credentialStoresMap);
function = andThenOp(function, credentialSource -> credentialSource.with(supplier.get()));
break;
}
case "clear-password": {
ExceptionSupplier password = parseClearPassword(reader, providers);
function = andThenOp(function, credentialSource -> credentialSource.with(IdentityCredentials.NONE.withCredential(new PasswordCredential(password.get()))));
break;
}
case "masked-password": {
if ( ! xmlVersion.isAtLeast(Version.VERSION_1_4)) {
throw reader.unexpectedElement();
}
final XMLLocation location = reader.getLocation();
ExceptionSupplier password = parseMaskedPassword(reader, providers);
Password maskedPassword = password.get();
Password finalPassword;
try {
final PasswordFactory passwordFactory = PasswordFactory.getInstance(maskedPassword.getAlgorithm(), providers);
final ClearPasswordSpec spec = passwordFactory.getKeySpec(maskedPassword, ClearPasswordSpec.class);
final char[] clearPassword = spec.getEncodedPassword();
PasswordFactory clearPasswordFactory = PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR, providers);
finalPassword = clearPasswordFactory.generatePassword(new ClearPasswordSpec(clearPassword)).castAs(ClearPassword.class);
} catch (InvalidKeySpecException | NoSuchAlgorithmException cause) {
throw xmlLog.xmlFailedToCreateCredential(location, cause);
}
function = andThenOp(function, credentialSource -> credentialSource.with(IdentityCredentials.NONE.withCredential(new PasswordCredential(finalPassword))));
break;
}
default: throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
if (keyContent == null) throw reader.missingRequiredAttribute(reader.getNamespaceURI(), "openssh-private-key");
final ExceptionUnaryOperator finalFunction = function;
ElytronFilePasswordProvider passwordProvider = new ElytronFilePasswordProvider(()->finalFunction.apply(null));
Iterator> pemContent = SshUtil.parsePemOpenSSHContent(CodePointIterator.ofString(keyContent), passwordProvider);
final PemEntry> pemEntry = pemContent.next();
final KeyPair keyPair = pemEntry.tryCast(KeyPair.class);
if (keyPair == null) throw xmlLog.xmlInvalidOpenSSHKey(reader);
return keyPair;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
private static X509CertificateChainPrivateCredential parseCertificateType(final ConfigurationXMLStreamReader reader, final Version xmlVersion) throws ConfigXMLParseException {
requireNoAttributes(reader);
PrivateKey privateKey = null;
X509Certificate[] certificates = null;
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "private-key-pem": {
if (privateKey != null) throw reader.unexpectedElement();
privateKey = parsePem(reader, PrivateKey.class);
break;
}
case "pem": {
if (certificates != null) throw reader.unexpectedElement();
certificates = parseMultiPem(reader, X509Certificate.class, X509Certificate[]::new);
break;
}
default: {
throw reader.unexpectedElement();
}
}
} else if (tag == END_ELEMENT) {
if (privateKey == null) throw reader.missingRequiredElement(xmlVersion.namespace, "private-key-pem");
if (certificates == null) throw reader.missingRequiredElement(xmlVersion.namespace, "pem");
return new X509CertificateChainPrivateCredential(privateKey, certificates);
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
private static P[] parseMultiPem(final ConfigurationXMLStreamReader reader, final Class
pemType, final IntFunction
ctor) throws ConfigXMLParseException {
requireNoAttributes(reader);
final Iterator> pemContent = Pem.parsePemContent(CodePointIterator.ofString(reader.getElementText()));
if (! reader.hasNext()) throw reader.unexpectedDocumentEnd();
final ArrayList arrayList = new ArrayList<>();
while (pemContent.hasNext()) {
final PemEntry> pemEntry = pemContent.next();
final P pem = pemEntry.tryCast(pemType);
if (pem == null) throw xmlLog.xmlWrongPemType(reader, pemType, pemEntry.getEntry().getClass());
arrayList.add(pem);
}
if (arrayList.isEmpty()) throw xmlLog.xmlNoPemContent(reader);
return arrayList.toArray(ctor.apply(arrayList.size()));
}
private static
P parsePem(final ConfigurationXMLStreamReader reader, final Class
pemType) throws ConfigXMLParseException {
requireNoAttributes(reader);
final Iterator> pemContent = Pem.parsePemContent(CodePointIterator.ofString(reader.getElementText()));
if (! reader.hasNext()) throw reader.unexpectedDocumentEnd();
if (! pemContent.hasNext()) throw xmlLog.xmlNoPemContent(reader);
final PemEntry> pemEntry = pemContent.next();
final P pem = pemEntry.tryCast(pemType);
if (pem == null) throw xmlLog.xmlWrongPemType(reader, pemType, pemEntry.getEntry().getClass());
return pem;
}
/**
* Parse an XML element of type {@code key-stores-type} from an XML reader.
*
* @param reader the XML stream reader
* @param xmlVersion the version of parsed XML
* @param keyStoresMap the map of key stores to use
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static void parseKeyStoresType(ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> keyStoresMap, final Map> credentialStoresMap, final Supplier providers) throws ConfigXMLParseException {
requireNoAttributes(reader);
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "key-store": {
parseKeyStoreType(reader, xmlVersion, keyStoresMap, credentialStoresMap, providers);
break;
}
default: throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
return;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code key-store-type} from an XML reader.
*
* @param reader the XML stream reader
* @param xmlVersion the version of parsed XML
* @param keyStoresMap the map of key stores to use
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static void parseKeyStoreType(ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> keyStoresMap,
final Map> credentialStoresMap, final Supplier providers) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
String name = null;
String type = null;
String provider = null;
Boolean wrap = null;
DeferredSupplier providersSupplier = new DeferredSupplier<>(providers);
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "type": {
if (type != null) throw reader.unexpectedAttribute(i);
type = reader.getAttributeValueResolved(i);
break;
}
case "provider": {
if (provider != null) throw reader.unexpectedAttribute(i);
provider = reader.getAttributeValueResolved(i);
break;
}
case "name": {
if (name != null) throw reader.unexpectedAttribute(i);
name = reader.getAttributeValueResolved(i);
break;
}
case "wrap-passwords": {
if (wrap != null) throw reader.unexpectedAttribute(i);
wrap = Boolean.valueOf(Boolean.parseBoolean(reader.getAttributeValueResolved(i)));
break;
}
default:
throw reader.unexpectedAttribute(i);
}
}
if (type == null && !xmlVersion.isAtLeast(Version.VERSION_1_3)) {
throw missingAttribute(reader, "type");
}
if (name == null) {
throw missingAttribute(reader, "name");
}
final XMLLocation location = reader.getLocation();
ExceptionSupplier passwordFactory = null;
boolean gotSource = false;
boolean gotCredential = false;
boolean gotProviders = false;
String fileSource = null;
ExceptionSupplier resourceSource = null;
URI uriSource = null;
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "key-store-credential": {
// group 2
if (gotCredential) {
throw reader.unexpectedElement();
}
gotCredential = true;
final XMLLocation nestedLocation = reader.getLocation();
final ExceptionSupplier entryFactory = parseKeyStoreRefType(reader, xmlVersion, keyStoresMap, credentialStoresMap, providersSupplier);
passwordFactory = () -> {
final KeyStore.Entry entry = entryFactory.get();
if (entry instanceof PasswordEntry) try {
final Password password = ((PasswordEntry) entry).getPassword();
final PasswordFactory passwordFactory1 = PasswordFactory.getInstance(password.getAlgorithm(), providersSupplier);
final ClearPasswordSpec passwordSpec = passwordFactory1.getKeySpec(password, ClearPasswordSpec.class);
return passwordSpec.getEncodedPassword();
} catch (GeneralSecurityException e) {
throw xmlLog.xmlFailedToCreateCredential(nestedLocation, e);
}
return null;
};
break;
}
case "credential-store-reference": {
if (gotCredential || !xmlVersion.isAtLeast(Version.VERSION_1_0_1)) {
throw reader.unexpectedElement();
}
gotCredential = true;
final XMLLocation nestedLocation = reader.getLocation();
ExceptionSupplier credentialSourceSupplier = parseCredentialStoreRefType(reader, credentialStoresMap);
passwordFactory = () -> {
try {
return credentialSourceSupplier.get().applyToCredential(PasswordCredential.class,
c -> c.getPassword().castAndApply(ClearPassword.class, ClearPassword::getPassword));
} catch (IOException e) {
throw xmlLog.xmlFailedToCreateCredential(nestedLocation, e);
}
};
break;
}
case "key-store-clear-password": {
// group 2
if (gotCredential) {
throw reader.unexpectedElement();
}
gotCredential = true;
final ExceptionSupplier clearPassword = parseClearPassword(reader, providersSupplier);
passwordFactory = () -> ((ClearPassword)clearPassword.get()).getPassword();
break;
}
case "key-store-masked-password": {
// group 2
if (gotCredential || !xmlVersion.isAtLeast(Version.VERSION_1_4)) {
throw reader.unexpectedElement();
}
gotCredential = true;
final XMLLocation nestedLocation = reader.getLocation();
final ExceptionSupplier maskedPassword = parseMaskedPassword(reader, providersSupplier);
passwordFactory = () -> {
try {
Password password = maskedPassword.get();
PasswordFactory factory = PasswordFactory.getInstance(password.getAlgorithm(), providersSupplier);
ClearPasswordSpec spec = factory.getKeySpec(password, ClearPasswordSpec.class);
return spec.getEncodedPassword();
} catch (GeneralSecurityException e) {
throw xmlLog.xmlFailedToCreateCredential(nestedLocation, e);
}
};
break;
}
case "file": {
// group 1
if (gotSource || gotCredential) {
throw reader.unexpectedElement();
}
gotSource = true;
fileSource = parseNameType(reader);
break;
}
case "resource": {
// group 1
if (gotSource || gotCredential) {
throw reader.unexpectedElement();
}
gotSource = true;
resourceSource = parseResourceType(reader, xmlVersion);
break;
}
case "uri": {
// group 1
if (gotSource || gotCredential) {
throw reader.unexpectedElement();
}
gotSource = true;
uriSource = parseUriType(reader);
break;
}
case "providers": {
if (gotProviders || !xmlVersion.isAtLeast(Version.VERSION_1_1)) {
throw reader.unexpectedElement();
}
gotProviders = true;
Supplier supplier = parseProvidersType(reader, xmlVersion);
if (supplier != null) {
providersSupplier.setSupplier(supplier);
}
break;
}
default: throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
ExceptionSupplier keyStoreFactory = null;
if (type == null || type.equalsIgnoreCase("automatic")) {
keyStoreFactory = new UnknownTypeFileKeyStoreFactory(providers, provider, passwordFactory, fileSource, resourceSource, uriSource, location);
if (wrap == Boolean.TRUE) {
keyStoreFactory = new PasswordKeyStoreFactory(keyStoreFactory);
}
} else {
keyStoreFactory = new KeyStoreCreateFactory(providersSupplier, provider, type, location);
if (wrap == Boolean.TRUE) {
keyStoreFactory = new PasswordKeyStoreFactory(keyStoreFactory);
}
if (fileSource != null) {
keyStoreFactory = new FileLoadingKeyStoreFactory(keyStoreFactory, passwordFactory, fileSource, location);
} else if (resourceSource != null) {
keyStoreFactory = new ResourceLoadingKeyStoreFactory(keyStoreFactory, passwordFactory, resourceSource, location);
} else if (uriSource != null) {
keyStoreFactory = new URILoadingKeyStoreFactory(keyStoreFactory, passwordFactory, uriSource, location);
} else {
keyStoreFactory = new NullLoadingKeyStoreFactory(keyStoreFactory, passwordFactory, location);
}
}
keyStoresMap.put(name, keyStoreFactory);
return;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code key-store-ref-type} from an XML reader.
*
* @param reader the XML stream reader
* @param xmlVersion the version of parsed XML
* @param keyStoresMap the map of key stores to use
* @param credentialStoresMap the map of credential stores to use
* @param providers supplier of providers for loading services
* @return the key store entry factory
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static ExceptionSupplier parseKeyStoreRefType(ConfigurationXMLStreamReader reader, final Version xmlVersion,
final Map> keyStoresMap, final Map> credentialStoresMap,
final Supplier providers) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
final XMLLocation location = reader.getLocation();
String keyStoreName = null;
String alias = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "key-store-name": {
if (keyStoreName != null) throw reader.unexpectedAttribute(i);
keyStoreName = reader.getAttributeValueResolved(i);
break;
}
case "alias": {
if (alias != null) throw reader.unexpectedAttribute(i);
alias = reader.getAttributeValueResolved(i);
break;
}
default: throw reader.unexpectedElement();
}
}
if (keyStoreName == null) {
throw missingAttribute(reader, "key-store-name");
}
ExceptionSupplier keyStoreCredential = null;
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "key-store-credential": {
if (keyStoreCredential != null) throw reader.unexpectedElement();
keyStoreCredential = parseKeyStoreRefType(reader, xmlVersion, keyStoresMap, credentialStoresMap, providers);
break;
}
case "key-store-clear-password": {
if (keyStoreCredential != null) throw reader.unexpectedElement();
ExceptionSupplier credential = parseClearPassword(reader, providers);
keyStoreCredential = () -> new PasswordEntry(credential.get());
break;
}
case "key-store-masked-password": {
if (keyStoreCredential != null || !xmlVersion.isAtLeast(Version.VERSION_1_4)) throw reader.unexpectedElement();
ExceptionSupplier credential = parseMaskedPassword(reader, providers);
keyStoreCredential = () -> new PasswordEntry(credential.get());
break;
}
case "credential-store-reference": {
if (keyStoreCredential != null || !xmlVersion.isAtLeast(Version.VERSION_1_0_1)) {
throw reader.unexpectedElement();
}
final XMLLocation nestedLocation = reader.getLocation();
ExceptionSupplier credentialSourceSupplier = parseCredentialStoreRefType(reader, credentialStoresMap);
keyStoreCredential = () -> {
try {
PasswordCredential passwordCredential = credentialSourceSupplier.get().getCredential(PasswordCredential.class);
if (passwordCredential == null) {
throw new ConfigXMLParseException(xmlLog.couldNotObtainCredential(), reader);
}
return new PasswordEntry(passwordCredential.getPassword());
} catch (IOException e) {
throw xmlLog.xmlFailedToCreateCredential(nestedLocation, e);
}
};
break;
}
default: throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
final ExceptionSupplier finalKeyStoreCredential = keyStoreCredential;
final String finalKeyStoreName = keyStoreName;
final String finalAlias = alias;
return () -> {
try {
final ExceptionSupplier keyStoreSupplier = keyStoresMap.get(finalKeyStoreName);
if (keyStoreSupplier == null) {
throw xmlLog.xmlUnknownKeyStoreSpecified(location);
}
char[] password = keyStoreCredentialToPassword(finalKeyStoreCredential, providers);
final KeyStore.ProtectionParameter protectionParameter = password != null ? new KeyStore.PasswordProtection(password) : null;
if (finalAlias != null) {
KeyStore.Entry finalEntry = keyStoreSupplier.get().getEntry(finalAlias, protectionParameter == null ? null : protectionParameter);
if (finalEntry == null) {
throw xmlLog.keyStoreEntryMissing(location, finalAlias);
}
return finalEntry;
} else {
// allow to retrieve entry without providing alias only if keystore includes one and only entry.
if (keyStoreSupplier.get().size() > 1) {
throw xmlLog.missingAlias(location);
} else if (keyStoreSupplier.get().aliases().hasMoreElements()) {
String firstAlias = keyStoreSupplier.get().aliases().nextElement();
KeyStore.Entry finalEntry = keyStoreSupplier.get().getEntry(firstAlias, protectionParameter == null ? null : protectionParameter);
if (finalEntry == null) {
throw xmlLog.keyStoreEntryMissing(location, firstAlias);
}
return finalEntry;
} else {
return null;
}
}
} catch (GeneralSecurityException e) {
throw xmlLog.xmlFailedToLoadKeyStoreData(location, e);
}
};
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
private static char[] keyStoreCredentialToPassword(ExceptionSupplier keyStoreCredential,
Supplier providers) throws GeneralSecurityException, ConfigXMLParseException {
final KeyStore.Entry entry = keyStoreCredential == null ? null : keyStoreCredential.get();
if (entry instanceof PasswordEntry) {
Password password = ((PasswordEntry) entry).getPassword();
final PasswordFactory passwordFactory = PasswordFactory.getInstance(password.getAlgorithm(), providers);
password = passwordFactory.translate(password);
final ClearPasswordSpec spec = passwordFactory.getKeySpec(password, ClearPasswordSpec.class);
return spec.getEncodedPassword();
} else if (entry instanceof KeyStore.SecretKeyEntry) {
final SecretKey secretKey = ((KeyStore.SecretKeyEntry) entry).getSecretKey();
final SecretKeyFactory instance = SecretKeyFactory.getInstance(secretKey.getAlgorithm());
final SecretKeySpec keySpec = (SecretKeySpec) instance.getKeySpec(secretKey, SecretKeySpec.class);
final byte[] encoded = keySpec.getEncoded();
return encoded == null ? null : new String(encoded, StandardCharsets.UTF_8).toCharArray();
} else {
return null;
}
}
/**
* Parse an XML element of type {@code trust-store-ref-type} from an XML reader.
*
* @param reader the XML stream reader
* @param keyStoresMap the map of key stores to use
* @return the key store entry factory
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static ExceptionSupplier parseTrustStoreRefType(ConfigurationXMLStreamReader reader, final Map> keyStoresMap) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
final XMLLocation location = reader.getLocation();
String keyStoreName = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "key-store-name": {
if (keyStoreName != null) throw reader.unexpectedAttribute(i);
keyStoreName = reader.getAttributeValueResolved(i);
break;
}
default: throw reader.unexpectedElement();
}
}
if (keyStoreName == null) {
throw missingAttribute(reader, "key-store-name");
}
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
throw reader.unexpectedElement();
} else if (tag == END_ELEMENT) {
final ExceptionSupplier keyStoreSupplier = keyStoresMap.get(keyStoreName);
if (keyStoreSupplier == null) {
throw xmlLog.xmlUnknownKeyStoreSpecified(location);
}
return keyStoreSupplier;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
static ExceptionSupplier parseCredentialStoreRefType(ConfigurationXMLStreamReader reader, final Map> credentialStoresMap) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
String storeName = null;
String alias = null;
String clearText = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "store": {
if (storeName != null) throw reader.unexpectedAttribute(i);
storeName = reader.getAttributeValueResolved(i);
break;
}
case "alias": {
if (alias != null) throw reader.unexpectedAttribute(i);
alias = reader.getAttributeValueResolved(i);
break;
}
case "clear-text": {
if (clearText != null) throw reader.unexpectedAttribute(i);
clearText = reader.getAttributeValueResolved(i);
break;
}
default:
throw reader.unexpectedAttribute(i);
}
}
if (! reader.hasNext()) throw reader.unexpectedDocumentEnd();
if (reader.nextTag() != END_ELEMENT) throw reader.unexpectedContent();
final XMLLocation finalLocation = reader.getLocation();
final String finalStoreName = storeName;
final String finalClearText = clearText;
final String finalAlias = alias;
if (finalStoreName == null && finalClearText == null) throw xmlLog.xmlInvalidCredentialStoreRef(reader.getLocation());
if (finalStoreName != null && finalAlias == null) throw missingAttribute(reader, "alias");
return () -> {
if (finalStoreName != null) {
final ExceptionSupplier supplier = credentialStoresMap.get(finalStoreName);
if (supplier == null) {
throw xmlLog.xmlCredentialStoreNameNotDefined(finalLocation, finalStoreName);
}
final CredentialStore credentialStore = supplier.get();
return new CredentialStoreCredentialSource(credentialStore, finalAlias);
} else {
final PasswordCredential passwordCredential = new PasswordCredential(ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, finalClearText.toCharArray()));
return IdentityCredentials.NONE.withCredential(passwordCredential);
}
};
}
/**
* Parse an XML element of type {@code credential-stores-type} from an XML reader.
*
* @param reader the XML stream reader
* @param xmlVersion the version of parsed XML
* @param keyStoresMap the key stores map
* @param credentialStoresMap the map of credential stores to use @throws ConfigXMLParseException if the resource failed to be parsed
*/
private static void parseCredentialStoresType(ConfigurationXMLStreamReader reader, final Version xmlVersion, Map> keyStoresMap, final Map> credentialStoresMap, final Supplier providers) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
if (attributeCount > 0) {
throw reader.unexpectedAttribute(0);
}
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "credential-store": {
parseCredentialStoreType(reader, xmlVersion, keyStoresMap, credentialStoresMap, providers);
break;
}
default: throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
return;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code credential-store-type} from an XML reader.
*
* @param reader the XML stream reader
* @param xmlVersion the version of parsed XML
* @param keyStoresMap the key stores map
* @param credentialStoresMap the map of credential stores to fill @throws ConfigXMLParseException if the resource failed to be parsed
*/
private static void parseCredentialStoreType(ConfigurationXMLStreamReader reader, final Version xmlVersion, Map> keyStoresMap, final Map> credentialStoresMap, final Supplier providers) throws ConfigXMLParseException {
final XMLLocation location = reader.getLocation();
final int attributeCount = reader.getAttributeCount();
String name = null;
String type = null;
String provider = null;
for (int i = 0; i < attributeCount; i ++) {
final String attributeNamespace = reader.getAttributeNamespace(i);
if (attributeNamespace != null && ! attributeNamespace.isEmpty()) {
throw reader.unexpectedAttribute(i);
}
switch (reader.getAttributeLocalName(i)) {
case "type": {
if (type != null) throw reader.unexpectedAttribute(i);
type = reader.getAttributeValueResolved(i);
break;
}
case "provider": {
if (provider != null) throw reader.unexpectedAttribute(i);
provider = reader.getAttributeValueResolved(i);
break;
}
case "name": {
if (name != null) throw reader.unexpectedAttribute(i);
name = reader.getAttributeValueResolved(i);
break;
}
default:
throw reader.unexpectedAttribute(i);
}
}
if (name == null) {
throw missingAttribute(reader, "name");
}
final Map attributesMap = new HashMap<>();
int foundBits = 0;
ExceptionSupplier credentialSourceSupplier = null;
DeferredSupplier providersSupplier = new DeferredSupplier<>(providers);
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "attributes": {
if (isSet(foundBits, 1)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 1);
parseAttributesType(reader, xmlVersion, attributesMap);
break;
}
case "protection-parameter-credentials": {
if (isSet(foundBits, 2)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 2);
credentialSourceSupplier = parseCredentialsType(reader, xmlVersion, keyStoresMap, credentialStoresMap, providersSupplier);
break;
}
case "providers": {
if (isSet(foundBits, 3)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 3);
Supplier supplier = parseProvidersType(reader, xmlVersion);
if (supplier != null) {
providersSupplier.setSupplier(supplier);
}
break;
}
default: throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
if (!credentialStoresMap.containsKey(name)) {
ExceptionSupplier credentialStoreSecurityFactory = new CredentialStoreFactory(name, type, attributesMap, provider, location, credentialSourceSupplier, providersSupplier);
credentialStoresMap.put(name, credentialStoreSecurityFactory);
} else {
throw xmlLog.duplicateCredentialStoreName(reader, name);
}
return;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
// common types
/**
* Parse attributes {@code attributes-type} from an XML reader.
*
* @param reader the XML stream reader
* @param attributesMap the map to put attributes to.
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
private static void parseAttributesType(ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map attributesMap) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
if (attributeCount > 0) {
throw reader.unexpectedAttribute(0);
}
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "attribute": {
parseAttributeType(reader, attributesMap);
break;
}
default: throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
return;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an attribute {@code attribute-type} from an XML reader.
*
* @param reader the XML stream reader
* @param attributesMap the map to put attributes to.
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
private static void parseAttributeType(ConfigurationXMLStreamReader reader, final Map attributesMap) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
String name = null;
String value = null;
for (int i = 0; i < attributeCount; i ++) {
final String attributeNamespace = reader.getAttributeNamespace(i);
if (attributeNamespace != null && ! attributeNamespace.isEmpty()) {
throw reader.unexpectedAttribute(i);
}
switch (reader.getAttributeLocalName(i)) {
case "name": {
if (name != null) throw reader.unexpectedAttribute(i);
name = reader.getAttributeValueResolved(i);
break;
}
case "value": {
if (value != null) throw reader.unexpectedAttribute(i);
value = reader.getAttributeValueResolved(i);
break;
}
default:
throw reader.unexpectedAttribute(i);
}
}
if (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
throw reader.unexpectedContent();
} else if (tag == END_ELEMENT) {
if (!attributesMap.containsKey(name)) {
attributesMap.put(name, value);
} else {
throw xmlLog.duplicateAttributeFound(reader, name);
}
return;
}
throw reader.unexpectedContent();
}
throw reader.unexpectedContent();
}
/**
* Parse an XML element of type {@code empty-type} from an XML reader.
*
* @param reader the XML stream reader
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static void parseEmptyType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException {
requireNoAttributes(reader);
if (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
throw reader.unexpectedElement();
} else if (tag == END_ELEMENT) {
return;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code name-type} from an XML reader.
*
* @param reader the XML stream reader
* @return the parsed name
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static String parseNameType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException {
return parseNameType(reader, false);
}
/**
* Parse an XML element of type {@code name-type} from an XML reader.
*
* @param reader the XML stream reader
* @param optional is the name attribute optional?
* @return the parsed name
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static String parseNameType(ConfigurationXMLStreamReader reader, boolean optional) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
String name = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "name": {
name = reader.getAttributeValueResolved(i);
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
if (name == null && !optional) {
throw missingAttribute(reader, "name");
}
if (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
throw reader.unexpectedElement();
} else if (tag == END_ELEMENT) {
return name;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code resource-type} from an XML reader.
*
* @param reader the XML stream reader
* @param xmlVersion the version of the XML being parsed
* @return An {@code ExceptionSupplier} for the referenced resource
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static ExceptionSupplier parseResourceType(ConfigurationXMLStreamReader reader, Version xmlVersion) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
String name = null;
String module = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "name": {
name = reader.getAttributeValueResolved(i);
break;
}
case "module-name": {
if (xmlVersion.isAtLeast(Version.VERSION_1_1)) {
module = reader.getAttributeValueResolved(i);
break;
}
}
default: throw reader.unexpectedAttribute(i);
}
}
if (name == null) {
throw missingAttribute(reader, "name");
}
if (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
throw reader.unexpectedElement();
} else if (tag == END_ELEMENT) {
final String resourceName = name;
final ClassLoader classLoader = module != null ? ModuleLoader.getClassLoaderFromModule(reader, module) : Thread.currentThread().getContextClassLoader();
return () -> {
ClassLoader actualClassLoader = classLoader != null ? classLoader : ElytronXmlParser.class.getClassLoader();
final InputStream stream = actualClassLoader.getResourceAsStream(resourceName);
if (stream == null) throw new FileNotFoundException(resourceName);
return stream;
};
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code port-type} from an XML reader.
*
* @param reader the XML stream reader
* @return the port number (1-65535 inclusive)
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static int parsePortType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
int number = -1;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "number": {
String s = reader.getAttributeValueResolved(i);
try {
number = Integer.parseInt(s);
} catch (NumberFormatException ignored) {
throw invalidPortNumber(reader, i);
}
if (number < 1 || number > 65535) {
throw invalidPortNumber(reader, i);
}
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
if (number == -1) {
throw missingAttribute(reader, "number");
}
if (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
throw reader.unexpectedElement();
} else if (tag == END_ELEMENT) {
return number;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code regex-substitution-type} from an XML reader.
*
* @param reader the XML stream reader
* @return the regular expression based name rewriter
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static NameRewriter parseRegexSubstitutionType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
Pattern pattern = null;
String replacement = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "pattern": {
pattern = Pattern.compile(reader.getAttributeValueResolved(i));
break;
}
case "replacement": {
replacement = reader.getAttributeValueResolved(i);
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
if (pattern == null) {
throw missingAttribute(reader, "pattern");
}
if (replacement == null) {
throw missingAttribute(reader, "replacement");
}
if (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
throw reader.unexpectedElement();
} else if (tag == END_ELEMENT) {
return new RegexNameRewriter(pattern, replacement, true);
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code names-type} from an XML reader.
*
* @param reader the XML stream reader
* @return the array of parsed names
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static String[] parseNamesType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
String[] names = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "names": {
String s = reader.getAttributeValueResolved(i);
names = s.trim().split("\\s+");
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
if (names == null) {
throw missingAttribute(reader, "names");
}
if (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
throw reader.unexpectedElement();
} else if (tag == END_ELEMENT) {
return names;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code uri-type} from an XML reader.
*
* @param reader the XML stream reader
* @return the parsed URI
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static URI parseUriType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
URI uri = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "uri": {
uri = reader.getURIAttributeValueResolved(i);
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
if (uri == null) {
throw missingAttribute(reader, "uri");
}
if (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
throw reader.unexpectedElement();
} else if (tag == END_ELEMENT) {
return uri;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
static SaslMechanismSelector parseSaslMechanismSelectorType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
SaslMechanismSelector selector = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "selector": {
selector = SaslMechanismSelector.fromString(reader.getAttributeValueResolved(i));
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
if (selector == null) {
throw missingAttribute(reader, "selector");
}
if (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
throw reader.unexpectedElement();
} else if (tag == END_ELEMENT) {
return selector;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code ssl-cipher-selector-type} from an XML reader.
*
* @param reader the XML stream reader
* @return the parsed cipher suite selector
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static CipherSuiteSelector parseCipherSuiteSelectorType(ConfigurationXMLStreamReader reader, Version xmlVersion) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
CipherSuiteSelector selector = null;
CipherSuiteSelector names = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "selector": {
selector = CipherSuiteSelector.fromString(reader.getAttributeValueResolved(i));
break;
}
case "names": {
if (xmlVersion.isAtLeast(Version.VERSION_1_5)) {
names = CipherSuiteSelector.fromNamesString(reader.getAttributeValueResolved(i));
break;
}
}
default: throw reader.unexpectedAttribute(i);
}
}
if (selector == null && ! xmlVersion.isAtLeast(Version.VERSION_1_5)) {
throw missingAttribute(reader, "selector");
} else if (selector == null && names == null && xmlVersion.isAtLeast(Version.VERSION_1_5)) {
throw xmlLog.atLeastOneCipherSuiteAttributeMustBeProvided("selector", "names");
}
if (selector == null) {
selector = CipherSuiteSelector.openSslDefault(); // default cipher suites pre TLSv1.3
}
// ELY-1917: The following lines should be added back when once we are ready to enable TLS 1.3 by default
/*if ((names == null) && xmlVersion.isAtLeast(Version.VERSION_1_5)) {
names = CipherSuiteSelector.openSslDefaultCipherSuites(); // default cipher suites for TLSv1.3
}*/
if (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
throw reader.unexpectedElement();
} else if (tag == END_ELEMENT) {
return CipherSuiteSelector.aggregate(names, selector);
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code names} which yields a protocol selector from an XML reader.
*
* @param reader the XML stream reader
* @return the parsed protocol selector
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static ProtocolSelector parseProtocolSelectorNamesType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException {
ProtocolSelector selector = ProtocolSelector.empty();
for (String name : parseNamesType(reader)) {
selector = selector.add(name);
}
return selector;
}
/**
* Parse an XML element of type {@code module-ref-type} from an XML reader.
*
* @param reader the XML stream reader
* @return the corresponding module name
* @throws ConfigXMLParseException if the resource failed to be parsed or the module is not found
*/
static String parseModuleRefType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
String moduleName = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "module-name": {
moduleName = reader.getAttributeValueResolved(i);
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
if (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
throw reader.unexpectedElement();
} else if (tag == END_ELEMENT) {
return moduleName;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code clear-password-type} from an XML reader.
*
* @param reader the XML stream reader
* @return the clear password characters
* @throws ConfigXMLParseException if the resource failed to be parsed or the module is not found
*/
static ExceptionSupplier parseClearPassword(ConfigurationXMLStreamReader reader, Supplier providers) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
char[] password = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "password": {
password = reader.getAttributeValueResolved(i).toCharArray();
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
if (password == null) {
throw missingAttribute(reader, "password");
}
if (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
throw reader.unexpectedElement();
} else if (tag == END_ELEMENT) {
final XMLLocation location = reader.getLocation();
final char[] finalPassword = password;
return () -> {
try {
PasswordFactory factory = PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR, providers);
return Assert.assertNotNull(factory.generatePassword(new ClearPasswordSpec(finalPassword)).castAs(ClearPassword.class));
} catch (InvalidKeySpecException | NoSuchAlgorithmException cause) {
throw xmlLog.xmlFailedToCreateCredential(location, cause);
}
};
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code masked-password-type} from an XML reader.
*
* @param reader the XML stream reader
* @return a {@link MaskedPassword} supplier
* @throws ConfigXMLParseException if the resource failed to be parsed or the module is not found
*/
static ExceptionSupplier parseMaskedPassword(ConfigurationXMLStreamReader reader, Supplier providers) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
String algorithm = MaskedPassword.ALGORITHM_MASKED_MD5_DES;
char[] initialKeyMaterial = "somearbitrarycrazystringthatdoesnotmatter".toCharArray();
int iterationCount = 0;
byte[] salt = null;
byte[] maskedPasswordBytes = null;
byte[] initializationVector = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "algorithm":
algorithm = reader.getAttributeValueResolved(i);
break;
case "key-material":
initialKeyMaterial = reader.getAttributeValueResolved(i).toCharArray();
break;
case "iteration-count":
iterationCount = reader.getIntAttributeValueResolved(i, 1, Integer.MAX_VALUE);
break;
case "salt":
salt = CodePointIterator.ofString(reader.getAttributeValueResolved(i)).asUtf8().drain();
break;
case "masked-password":
maskedPasswordBytes = CodePointIterator.ofString(reader.getAttributeValueResolved(i)).base64Decode().drain();
break;
case "initialization-vector":
initializationVector = CodePointIterator.ofString(reader.getAttributeValueResolved(i)).base64Decode().drain();
break;
default:
throw reader.unexpectedAttribute(i);
}
}
if (iterationCount == 0) throw missingAttribute(reader, "iteration-count");
if (salt == null) throw missingAttribute(reader, "salt");
if (maskedPasswordBytes == null) throw missingAttribute(reader, "masked-password");
if (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
throw reader.unexpectedElement();
} else if (tag == END_ELEMENT) {
final XMLLocation location = reader.getLocation();
if (!MaskedPassword.isMaskedAlgorithm(algorithm)) {
throw xmlLog.xmlUnsupportedAlgorithmForType(location, algorithm, MaskedPassword.class.getSimpleName());
}
final String finalAlgorithm = algorithm;
final MaskedPasswordSpec spec = new MaskedPasswordSpec(initialKeyMaterial, iterationCount, salt, maskedPasswordBytes, initializationVector);
return () -> {
try {
PasswordFactory factory = PasswordFactory.getInstance(finalAlgorithm, providers);
return Assert.assertNotNull(factory.generatePassword(spec).castAs(MaskedPassword.class));
} catch (InvalidKeySpecException | NoSuchAlgorithmException cause) {
throw xmlLog.xmlFailedToCreateCredential(location, cause);
}
};
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
static Map parsePropertiesType(ConfigurationXMLStreamReader reader, final Version xmlVersion) throws ConfigXMLParseException {
if (reader.getAttributeCount() > 0) {
throw reader.unexpectedAttribute(0);
}
Map propertiesMap = new HashMap<>();
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "property":
final int attributeCount = reader.getAttributeCount();
String key = null;
String value = null;
for (int i = 0; i < attributeCount; i++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "key":
if (key != null)
throw reader.unexpectedAttribute(i);
key = reader.getAttributeValueResolved(i);
break;
case "value":
if (value != null)
throw reader.unexpectedAttribute(i);
value = reader.getAttributeValueResolved(i);
break;
default:
throw reader.unexpectedAttribute(i);
}
}
if (key == null) {
throw missingAttribute(reader, "key");
}
if (value == null) {
throw missingAttribute(reader, "value");
}
propertiesMap.put(key, value);
if (reader.hasNext()) {
final int innerTag = reader.nextTag();
if (innerTag == START_ELEMENT) {
throw reader.unexpectedElement();
} else if (innerTag == END_ELEMENT) {
} else {
throw reader.unexpectedContent();
}
} else {
throw reader.unexpectedDocumentEnd();
}
break;
default:
throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
return propertiesMap;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code bearer-token-type} from an XML reader.
*
* @param reader the XML stream reader
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static BearerTokenCredential parseBearerTokenType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException {
String value = requireSingleAttribute(reader, "value");
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
throw reader.unexpectedElement();
} else if (tag == END_ELEMENT) {
return new BearerTokenCredential(value);
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code oauth2-bearer-token-type} from an XML reader.
*
* @param reader the XML stream reader
* @param xmlVersion the version of parsed XML
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static ExceptionSupplier parseOAuth2BearerTokenType(ConfigurationXMLStreamReader reader, final Map> credentialStoresMap, final Version xmlVersion) throws ConfigXMLParseException {
URI tokenEndpointUri = requireSingleURIAttribute(reader, "token-endpoint-uri");
ExceptionSupplier builderSupplier = null;
DeferredSupplier providersSupplier = new DeferredSupplier<>(ProviderFactory.getElytronProviderSupplier(WildFlyElytronPasswordProvider.class.getClassLoader()));
builderSupplier = () -> {
try {
return OAuth2CredentialSource.builder(tokenEndpointUri.toURL());
} catch (MalformedURLException e) {
throw xmlLog.xmlInvalidUrl(tokenEndpointUri.toString());
}
};
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
switch (reader.getLocalName()) {
case "resource-owner-credentials": {
builderSupplier = parseOAuth2ResourceOwnerCredentials(reader, builderSupplier, credentialStoresMap, xmlVersion);
break;
}
case "client-credentials": {
builderSupplier = parseOAuth2ClientCredentials(reader, builderSupplier, credentialStoresMap, xmlVersion);
break;
}
case "masked-resource-owner-credentials": {
if (!xmlVersion.isAtLeast(Version.VERSION_1_4)) {
throw reader.unexpectedElement();
}
builderSupplier = parseOAuth2MaskedResourceOwnerCredentials(reader, builderSupplier, xmlVersion, providersSupplier);
break;
}
case "masked-client-credentials": {
if (!xmlVersion.isAtLeast(Version.VERSION_1_4)) {
throw reader.unexpectedElement();
}
builderSupplier = parseOAuth2MaskedClientCredentials(reader, builderSupplier, xmlVersion, providersSupplier);
break;
}
default: throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
final ExceptionSupplier finalBuilderSupplier = builderSupplier;
return () -> finalBuilderSupplier.get().build();
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code oauth2-bearer-token-type} from an XML reader.
*
* @param reader the XML stream reader
* @param builderSupplier the builder supplier
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static ExceptionSupplier parseOAuth2ResourceOwnerCredentials(ConfigurationXMLStreamReader reader, final ExceptionSupplier builderSupplier, final Map> credentialStoresMap, Version xmlVersion) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
ExceptionSupplier credentialSourceSupplier = null;
XMLLocation nestedLocation = null;
String userName = null;
String password = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "name": {
if (userName != null) throw reader.unexpectedAttribute(i);
userName = reader.getAttributeValueResolved(i);
break;
}
case "password": {
if (password != null) throw reader.unexpectedAttribute(i);
password = reader.getAttributeValueResolved(i);
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
if (userName == null) throw reader.missingRequiredAttribute(xmlVersion.namespace, "name");
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
if (!xmlVersion.isAtLeast(Version.VERSION_1_1)) {
throw reader.unexpectedElement();
}
if ("credential-store-reference".equals(reader.getLocalName())) {
if (password != null) {
throw reader.unexpectedElement();
}
if (credentialSourceSupplier != null) { // must not throw because compatibility
xmlLog.trace("Multiple credential-store-references in resource-owner-credentials - only the last one used!");
}
nestedLocation = reader.getLocation();
credentialSourceSupplier = parseCredentialStoreRefType(reader, credentialStoresMap);
} else {
throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
final String finalUserName = userName;
if (password != null) {
final String finalPassword = password;
return () -> builderSupplier.get().useResourceOwnerPassword(finalUserName, finalPassword);
}
if (credentialSourceSupplier != null) {
final XMLLocation finalLocation = nestedLocation;
final ExceptionSupplier finalCredentialSourceSupplier = credentialSourceSupplier;
return () -> {
try {
PasswordCredential passwordCredential = finalCredentialSourceSupplier.get().getCredential(PasswordCredential.class);
if (passwordCredential == null) {
throw new ConfigXMLParseException(xmlLog.couldNotObtainCredential(), reader);
}
char[] pass = passwordCredential.getPassword().castAndApply(ClearPassword.class, ClearPassword::getPassword);
if (pass == null) {
throw new ConfigXMLParseException(xmlLog.couldNotObtainCredential(), reader);
}
String finalPassword = String.valueOf(pass);
return builderSupplier.get().useResourceOwnerPassword(finalUserName, finalPassword);
} catch (IOException e) {
throw xmlLog.xmlFailedToCreateCredential(finalLocation, e);
}
};
}
throw reader.missingRequiredAttribute(xmlVersion.namespace, "password");
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code oauth2-client-credentials-type} from an XML reader.
*
* @param reader the XML stream reader
* @param builderSupplier the builder supplier
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static ExceptionSupplier parseOAuth2ClientCredentials(ConfigurationXMLStreamReader reader, final ExceptionSupplier builderSupplier, final Map> credentialStoresMap, Version xmlVersion) throws ConfigXMLParseException {
ExceptionSupplier credentialSourceSupplier = null;
XMLLocation nestedLocation = null;
String id = null;
String secret = null;
for (int i = 0; i < reader.getAttributeCount(); i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "client-id": {
if (id != null) throw reader.unexpectedAttribute(i);
id = reader.getAttributeValueResolved(i);
break;
}
case "client-secret": {
if (secret != null) throw reader.unexpectedAttribute(i);
secret = reader.getAttributeValueResolved(i);
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
if (!xmlVersion.isAtLeast(Version.VERSION_1_1)) {
throw reader.unexpectedElement();
}
if ("credential-store-reference".equals(reader.getLocalName())) {
if (secret != null) {
throw reader.unexpectedElement();
}
if (credentialSourceSupplier != null) { // must not throw because compatibility
xmlLog.trace("Multiple credential-store-references in client-credentials - only the last one used!");
}
nestedLocation = reader.getLocation();
credentialSourceSupplier = parseCredentialStoreRefType(reader, credentialStoresMap);
} else {
throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
if (id == null) throw reader.unexpectedContent();
final String finalId = id;
if (secret != null) {
final String finalSecret = secret;
return () -> builderSupplier.get().clientCredentials(finalId, finalSecret);
}
if (credentialSourceSupplier != null) {
final XMLLocation finalLocation = nestedLocation;
final ExceptionSupplier finalCredentialSourceSupplier = credentialSourceSupplier;
return () -> {
try {
PasswordCredential passwordCredential = finalCredentialSourceSupplier.get().getCredential(PasswordCredential.class);
if (passwordCredential == null) {
throw new ConfigXMLParseException(xmlLog.couldNotObtainCredential(), reader);
}
char[] pass = passwordCredential.getPassword().castAndApply(ClearPassword.class, ClearPassword::getPassword);
if (pass == null) {
throw new ConfigXMLParseException(xmlLog.couldNotObtainCredential(), reader);
}
String finalPassword = String.valueOf(pass);
return builderSupplier.get().clientCredentials(finalId, finalPassword);
} catch (IOException e) {
throw xmlLog.xmlFailedToCreateCredential(finalLocation, e);
}
};
}
throw reader.unexpectedContent();
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code oauth2-bearer-token-type} from an XML reader.
*
* @param reader the XML stream reader
* @param builderSupplier the builder supplier
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static ExceptionSupplier parseOAuth2MaskedResourceOwnerCredentials(ConfigurationXMLStreamReader reader, final ExceptionSupplier builderSupplier, Version xmlVersion, Supplier providers) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
XMLLocation nestedLocation = null;
String userName = null;
String password = null;
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "name": {
if (userName != null) throw reader.unexpectedAttribute(i);
userName = reader.getAttributeValueResolved(i);
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
if (userName == null) throw reader.missingRequiredAttribute(xmlVersion.namespace, "name");
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
if ("masked-password".equals(reader.getLocalName())) {
if (password != null) {
throw reader.unexpectedElement();
}
nestedLocation = reader.getLocation();
Password maskedPassword = parseMaskedPassword(reader, providers).get();
try {
final PasswordFactory passwordFactory = PasswordFactory.getInstance(maskedPassword.getAlgorithm(), providers);
final ClearPasswordSpec spec = passwordFactory.getKeySpec(maskedPassword, ClearPasswordSpec.class);
password = String.valueOf(spec.getEncodedPassword());
} catch (InvalidKeySpecException | NoSuchAlgorithmException cause) {
throw xmlLog.xmlFailedToCreateCredential(nestedLocation, cause);
}
} else {
throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
final String finalUserName = userName;
if (password != null) {
final String finalPassword = password;
return () -> builderSupplier.get().useResourceOwnerPassword(finalUserName, finalPassword);
}
throw reader.missingRequiredAttribute(xmlVersion.namespace, "password");
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code oauth2-client-credentials-type} from an XML reader.
*
* @param reader the XML stream reader
* @param builderSupplier the builder supplier
* @throws ConfigXMLParseException if the resource failed to be parsed
*/
static ExceptionSupplier parseOAuth2MaskedClientCredentials(ConfigurationXMLStreamReader reader, final ExceptionSupplier builderSupplier, Version xmlVersion,Supplier providers) throws ConfigXMLParseException {
XMLLocation nestedLocation = null;
String id = null;
String secret = null;
for (int i = 0; i < reader.getAttributeCount(); i ++) {
checkAttributeNamespace(reader, i);
if ("client-id".equals(reader.getAttributeLocalName(i))) {
if (id != null) throw reader.unexpectedAttribute(i);
id = reader.getAttributeValueResolved(i);
break;
}
else {
throw reader.unexpectedAttribute(i);
}
}
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
checkElementNamespace(reader, xmlVersion);
if ("masked-client-secret".equals(reader.getLocalName())) {
if (secret != null) {
throw reader.unexpectedElement();
}
nestedLocation = reader.getLocation();
Password maskedPassword = parseMaskedPassword(reader, providers).get();
try {
final PasswordFactory passwordFactory = PasswordFactory.getInstance(maskedPassword.getAlgorithm(), providers);
final ClearPasswordSpec spec = passwordFactory.getKeySpec(maskedPassword, ClearPasswordSpec.class);
secret = String.valueOf(spec.getEncodedPassword());
} catch (InvalidKeySpecException | NoSuchAlgorithmException cause) {
throw xmlLog.xmlFailedToCreateCredential(nestedLocation, cause);
}
} else {
throw reader.unexpectedElement();
}
} else if (tag == END_ELEMENT) {
if (id == null) throw reader.unexpectedContent();
final String finalId = id;
if (secret != null) {
final String finalSecret = secret;
return () -> builderSupplier.get().clientCredentials(finalId, finalSecret);
}
throw reader.unexpectedContent();
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
/**
* Parse an XML element of type {@code local-kerberos-type} from an XML reader.
*
* @param reader the XML stream reader
* @return the clear password characters
* @throws ConfigXMLParseException if the resource failed to be parsed or the module is not found
*/
static CredentialSource parseLocalKerberos(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
List mechanismOids = new LinkedList<>();
for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "mechanism-names": {
for (String name : reader.getListAttributeValueAsArrayResolved(i)) {
String oid = OidsUtil.attributeNameToOid(OidsUtil.Category.GSS, name);
if (oid == null) {
throw xmlLog.xmlInvalidGssMechanismName(reader, name);
}
try {
mechanismOids.add(new Oid(oid));
} catch (GSSException e) {
throw xmlLog.xmlGssMechanismOidConversionFailed(reader, oid, e);
}
}
break;
}
case "mechanism-oids": {
for (String oid : reader.getListAttributeValueAsArrayResolved(i)) {
try {
mechanismOids.add(new Oid(oid));
} catch (GSSException e) {
throw xmlLog.xmlGssMechanismOidConversionFailed(reader, oid, e);
}
}
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
if (mechanismOids.size() == 0) {
mechanismOids.add(GSSCredentialSecurityFactory.KERBEROS_V5);
mechanismOids.add(GSSCredentialSecurityFactory.SPNEGO);
}
if (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == START_ELEMENT) {
throw reader.unexpectedElement();
} else if (tag == END_ELEMENT) {
return LocalKerberosCredentialSource.builder().setMechanismOids(mechanismOids.toArray(new Oid[mechanismOids.size()])).build();
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}
// util
private static String checkGetElementNamespace(final ConfigurationXMLStreamReader reader) throws ConfigXMLParseException {
String namespaceUri = reader.getNamespaceURI();
if (! KNOWN_NAMESPACES.containsKey(namespaceUri)) {
throw reader.unexpectedElement();
}
return namespaceUri;
}
private static void checkElementNamespace(final ConfigurationXMLStreamReader reader, final Version xmlVersion) throws ConfigXMLParseException {
if (! xmlVersion.namespace.equals(reader.getNamespaceURI())) {
throw reader.unexpectedElement();
}
}
private static void checkAttributeNamespace(final ConfigurationXMLStreamReader reader, final int idx) throws ConfigXMLParseException {
final String attributeNamespace = reader.getAttributeNamespace(idx);
if (attributeNamespace != null && ! attributeNamespace.isEmpty()) {
throw reader.unexpectedAttribute(idx);
}
}
private static void requireNoAttributes(final ConfigurationXMLStreamReader reader) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
if (attributeCount > 0) {
throw reader.unexpectedAttribute(0);
}
}
private static String requireSingleAttribute(final ConfigurationXMLStreamReader reader, final String attributeName) throws ConfigXMLParseException {
return requireSingleAttribute(reader, attributeName, (ExceptionSupplier) () -> reader.getAttributeValueResolved(0));
}
private static URI requireSingleURIAttribute(final ConfigurationXMLStreamReader reader, final String attributeName) throws ConfigXMLParseException {
return requireSingleAttribute(reader, attributeName, () -> reader.getURIAttributeValueResolved(0));
}
private static A requireSingleAttribute(final ConfigurationXMLStreamReader reader, final String attributeName, ExceptionSupplier attributeFunction) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
if (attributeCount < 1) {
throw reader.missingRequiredAttribute("", attributeName);
}
checkAttributeNamespace(reader, 0);
if (! reader.getAttributeLocalName(0).equals(attributeName)) {
throw reader.unexpectedAttribute(0);
}
if (attributeCount > 1) {
throw reader.unexpectedAttribute(1);
}
return attributeFunction.get();
}
private static ConfigXMLParseException missingAttribute(final ConfigurationXMLStreamReader reader, final String name) {
return reader.missingRequiredAttribute(null, name);
}
private static ConfigXMLParseException invalidPortNumber(final ConfigurationXMLStreamReader reader, final int index) throws ConfigXMLParseException {
return xmlLog.xmlInvalidPortNumber(reader, reader.getAttributeValueResolved(index), reader.getAttributeLocalName(index), reader.getName());
}
static final class KeyStoreCreateFactory implements ExceptionSupplier {
private final String providerName;
private final Supplier providers;
private final String type;
private final XMLLocation location;
KeyStoreCreateFactory(final Supplier providers, final String providerName, final String type, final XMLLocation location) {
this.providerName = providerName;
this.providers = providers;
this.type = type;
this.location = location;
}
public KeyStore get() throws ConfigXMLParseException {
Provider provider = findProvider(providers, providerName, KeyStore.class, type);
if (provider == null) {
throw xmlLog.xmlUnableToIdentifyProvider(location, providerName, "KeyStore", type);
}
try {
return KeyStore.getInstance(type, provider);
} catch (GeneralSecurityException e) {
throw xmlLog.xmlFailedToCreateKeyStore(location, e);
}
}
}
static final class UnknownTypeFileKeyStoreFactory implements ExceptionSupplier {
private final String providerName;
private final Supplier providers;
private final XMLLocation location;
protected final ExceptionSupplier passwordFactory;
private final String fileName;
private final ExceptionSupplier resourceSupplier;
private final URI uri;
UnknownTypeFileKeyStoreFactory(final Supplier providers, final String providerName, final ExceptionSupplier passwordFactory, final String fileName, final ExceptionSupplier resourceSupplier, final URI uri, final XMLLocation location) {
this.providerName = providerName;
this.providers = providers;
this.location = location;
this.passwordFactory = passwordFactory;
this.fileName = fileName;
this.resourceSupplier = resourceSupplier;
this.uri = uri;
}
@Override
public KeyStore get() throws ConfigXMLParseException {
KeyStore keyStore = null;
FileInputStream fin = null;
try {
if (fileName != null) {
fin = new FileInputStream(fileName);
} else if (resourceSupplier != null) {
InputStream is = resourceSupplier.get();
if (is instanceof FileInputStream) {
fin = (FileInputStream) is;
}
} else {
fin = new FileInputStream(uri.toURL().getFile());
}
keyStore = KeyStoreUtil.loadKeyStore(providers, providerName, fin, fileName, passwordFactory.get());
} catch (Exception e) {
throw xmlLog.xmlFailedToCreateKeyStore(location, e);
} finally {
safeClose(fin);
}
return keyStore;
}
private static void safeClose(Closeable c) {
if (c != null) {
try {
c.close();
} catch (Throwable ignored) {}
}
}
}
static final class PasswordKeyStoreFactory implements ExceptionSupplier {
private final ExceptionSupplier delegateFactory;
PasswordKeyStoreFactory(final ExceptionSupplier delegateFactory) {
this.delegateFactory = delegateFactory;
}
public KeyStore get() throws ConfigXMLParseException {
return new WrappingPasswordKeyStore(delegateFactory.get());
}
}
abstract static class AbstractLoadingKeyStoreFactory implements ExceptionSupplier {
protected final ExceptionSupplier delegateFactory;
protected final ExceptionSupplier passwordFactory;
protected final XMLLocation location;
protected AbstractLoadingKeyStoreFactory(final ExceptionSupplier delegateFactory, final ExceptionSupplier passwordFactory, final XMLLocation location) {
this.delegateFactory = delegateFactory;
this.passwordFactory = passwordFactory;
this.location = location;
}
public KeyStore get() throws ConfigXMLParseException {
try {
KeyStore keyStore = delegateFactory.get();
if (passwordFactory == null || passwordFactory.get() == null) {
xmlLog.noKeystorePasswordSpecified(location);
}
try (InputStream fis = createStream()) {
keyStore.load(fis, passwordFactory == null ? null : passwordFactory.get());
}
return keyStore;
} catch (GeneralSecurityException | IOException e) {
throw xmlLog.xmlFailedToLoadKeyStoreData(location, e);
}
}
abstract InputStream createStream() throws IOException;
}
static final class FileLoadingKeyStoreFactory extends AbstractLoadingKeyStoreFactory {
private final String fileName;
FileLoadingKeyStoreFactory(final ExceptionSupplier delegateFactory, final ExceptionSupplier passwordFactory, final String fileName, final XMLLocation location) {
super(delegateFactory, passwordFactory, location);
this.fileName = fileName;
}
InputStream createStream() throws FileNotFoundException {
return new FileInputStream(fileName);
}
}
static final class ResourceLoadingKeyStoreFactory extends AbstractLoadingKeyStoreFactory {
private final ExceptionSupplier resourceSupplier;
ResourceLoadingKeyStoreFactory(final ExceptionSupplier delegateFactory, final ExceptionSupplier passwordFactory, final ExceptionSupplier resourceSupplier, final XMLLocation location) {
super(delegateFactory, passwordFactory, location);
this.resourceSupplier = resourceSupplier;
}
InputStream createStream() throws IOException {
return resourceSupplier.get();
}
}
static final class URILoadingKeyStoreFactory extends AbstractLoadingKeyStoreFactory {
private final URI uri;
URILoadingKeyStoreFactory(final ExceptionSupplier delegateFactory, final ExceptionSupplier passwordFactory, final URI uri, final XMLLocation location) {
super(delegateFactory, passwordFactory, location);
this.uri = uri;
}
InputStream createStream() throws IOException {
return uri.toURL().openStream();
}
}
static final class NullLoadingKeyStoreFactory extends AbstractLoadingKeyStoreFactory {
NullLoadingKeyStoreFactory(final ExceptionSupplier delegateFactory, final ExceptionSupplier passwordFactory, final XMLLocation location) {
super(delegateFactory, passwordFactory, location);
}
@Override
InputStream createStream() throws IOException {
return null;
}
}
static final class DeferredSupplier implements Supplier {
private volatile Supplier supplier;
private T value;
DeferredSupplier(Supplier supplier) {
checkNotNullParam("supplier", supplier);
this.supplier = supplier;
}
void setSupplier(Supplier supplier) {
checkNotNullParam("supplier", supplier);
this.supplier = supplier;
}
@Override
public T get() {
return supplier.get();
}
}
}