Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WFLY-14984] Add support for encrypted expressions in the wildfly-config.xml #2081

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@
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.XMLParserUtils.isSet;
import static org.wildfly.security.auth.client.XMLParserUtils.setBit;
import static org.wildfly.security.auth.client.XMLParserUtils.checkAttributeNamespace;
import static org.wildfly.security.auth.client.XMLParserUtils.requireNoAttributes;
import static org.wildfly.security.auth.client.XMLParserUtils.requireSingleAttribute;
import static org.wildfly.security.auth.client.XMLParserUtils.requireSingleURIAttribute;
import static org.wildfly.security.auth.client.XMLParserUtils.missingAttribute;
import static org.wildfly.security.auth.client.XMLParserUtils.invalidPortNumber;
import static org.wildfly.security.auth.client.XMLParserUtils.andThenOp;
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;
Expand Down Expand Up @@ -222,7 +231,7 @@ public static SecurityFactory<AuthenticationContext> parseAuthenticationClientCo
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());
xmlLog.tracef("Parsing configuration from %s for namespace %s", streamReader.getUri(), streamReader.getNamespaceURI());
return parseAuthenticationClientConfiguration(streamReader);
} else {
if (xmlLog.isTraceEnabled()) {
Expand Down Expand Up @@ -1434,18 +1443,6 @@ private static MatchRule parseMatchAbstractType(final MatchRule rule, final Conf
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 <T, E extends Exception> ExceptionUnaryOperator<T, E> andThenOp(ExceptionUnaryOperator<T, E> first, ExceptionUnaryOperator<T, E> second) {
return t -> second.apply(first.apply(t));
}

private static ExceptionSupplier<CredentialSource, ConfigXMLParseException> parseCredentialsType(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map<String, ExceptionSupplier<KeyStore, ConfigXMLParseException>> keyStoresMap, final Map<String, ExceptionSupplier<CredentialStore, ConfigXMLParseException>> credentialStoresMap, Supplier<Provider[]> providers) throws ConfigXMLParseException {
ExceptionUnaryOperator<CredentialSource, ConfigXMLParseException> function = parent -> CredentialSource.NONE;
requireNoAttributes(reader);
Expand Down Expand Up @@ -3501,51 +3498,6 @@ private static void checkElementNamespace(final ConfigurationXMLStreamReader rea
}
}

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<String, ConfigXMLParseException>) () -> 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> A requireSingleAttribute(final ConfigurationXMLStreamReader reader, final String attributeName, ExceptionSupplier<A, ConfigXMLParseException> 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<KeyStore, ConfigXMLParseException> {
private final String providerName;
private final Supplier<Provider[]> providers;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.wildfly.security.auth.client;


import org.wildfly.common.Assert;
import org.wildfly.security.auth.server.IdentityCredentials;
import org.wildfly.security.credential.Credential;
import org.wildfly.security.credential.source.CredentialSource;
import org.wildfly.security.credential.source.impl.CredentialStoreCredentialSource;
import org.wildfly.security.credential.store.CredentialStore;
import org.wildfly.security.provider.util.ProviderFactory;

import java.security.Provider;
import java.util.function.Supplier;


public final class EncryptedExpressionConfig {

private static final int SET_CREDENTIAL_STORE = 0;
private static final int SET_RESOLVER = 1;
private static final int SET_DEFAULT_RESOLVER = 2;
private static final Supplier<Provider[]> DEFAULT_PROVIDER_SUPPLIER = ProviderFactory.getDefaultProviderSupplier(EncryptedExpressionConfig.class.getClassLoader());

public static EncryptedExpressionConfig empty() {
return new EncryptedExpressionConfig();
}

CredentialSource credentialSource;

EncryptedExpressionConfig() {
this.credentialSource = null;
}

private EncryptedExpressionConfig(final EncryptedExpressionConfig original, final int what, final Object value) {
this.credentialSource = what == SET_CREDENTIAL_STORE ? (CredentialSource) value : original.credentialSource;
}

private EncryptedExpressionConfig(final EncryptedExpressionConfig original, final EncryptedExpressionConfig other) {
this.credentialSource = other.credentialSource;
}

private static <T> T getOrDefault(T value, T defVal) {
return value != null ? value : defVal;
}

private static int getOrDefault(int value, int defVal) {
return value != -1 ? value : defVal;
}

CredentialSource getCredentialSource() {
return credentialSource;
}
public EncryptedExpressionConfig useCredentialStoreEntry(CredentialStore credentialStore, String alias) {
Assert.checkNotNullParam("credentialStore", credentialStore);
Assert.checkNotNullParam("alias", alias);
CredentialStoreCredentialSource csCredentialSource = new CredentialStoreCredentialSource(credentialStore, alias);
return useCredentials(getCredentialSource().with(csCredentialSource));
}

public EncryptedExpressionConfig useCredentials(CredentialSource credentials) {
return new EncryptedExpressionConfig(this, SET_CREDENTIAL_STORE, credentials == null ? CredentialSource.NONE : credentials);
}

public EncryptedExpressionConfig useCredential(Credential credential) {
if (credential == null) return this;
final CredentialSource credentialSource = this.credentialSource;
if (credentialSource == CredentialSource.NONE) {
return new EncryptedExpressionConfig(this, SET_CREDENTIAL_STORE, IdentityCredentials.NONE.withCredential(credential));
} else {
return new EncryptedExpressionConfig(this, SET_CREDENTIAL_STORE, credentialSource.with(IdentityCredentials.NONE.withCredential(credential)));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2023 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 org.wildfly.common.context.ContextManager;
import org.wildfly.common.context.Contextual;
import org.wildfly.security.Version;

import java.security.PrivilegedAction;
import java.util.function.Supplier;

import static java.security.AccessController.doPrivileged;

/**
* A set of resolvers and credential stores used to handle encrypted expressions.
*
* @author <a href="mailto:[email protected]">Prarthona Paul</a>
*/
public final class EncryptedExpressionContext implements Contextual<EncryptedExpressionContext>{
private static final ContextManager<EncryptedExpressionContext> CONTEXT_MANAGER = new ContextManager<EncryptedExpressionContext>(EncryptedExpressionContext.class);

private static final Supplier<EncryptedExpressionContext> SUPPLIER = doPrivileged((PrivilegedAction<Supplier<EncryptedExpressionContext>>) CONTEXT_MANAGER::getPrivilegedSupplier);

static {
Version.getVersion();
CONTEXT_MANAGER.setGlobalDefaultSupplier(() -> EncryptedExpressionContext.EMPTY);
}

final EncryptedExpressionConfig encryptedExpressionConfig = new EncryptedExpressionConfig();

static final EncryptedExpressionContext EMPTY = new EncryptedExpressionContext();
final RuleNode<EncryptedExpressionConfig> encryptionRuleNode;

EncryptedExpressionContext() {
this(null);
}

EncryptedExpressionContext(final RuleNode<EncryptedExpressionConfig> encryptionRuleNode) {
this.encryptionRuleNode = encryptionRuleNode;
}

/**
* Get a new, empty encrypted expression context.
*
* @return the new encrypted expression context.
*/
public static EncryptedExpressionContext empty() {
return EMPTY;
}

/**
* Get the current thread's captured authentication context.
*
* @return the current thread's captured authentication context
*/
public static EncryptedExpressionContext captureCurrent() {
return SUPPLIER.get();
}


private static <T> RuleNode<T> with(RuleNode<T> node, MatchRule rule, T item) {
return node == null ? new RuleNode<T>(null, rule, item) : node.with(rule, item);
}

private static <T> RuleNode<T> with(RuleNode<T> node, MatchRule rule, T item, int idx) {
return node == null ? new RuleNode<T>(null, rule, item) : node.with(rule, item, idx);
}

private static <T> RuleNode<T> replacing(RuleNode<T> node, MatchRule rule, T item, int idx) {
return node == null ? new RuleNode<T>(null, rule, item) : node.replacing(rule, item, idx);
}

private static <T> RuleNode<T> withAll(RuleNode<T> node, RuleNode<T> otherNode) {
return node == null ? otherNode : otherNode == null ? node : node.withAll(otherNode);
}

private static <T> RuleNode<T> withAll(RuleNode<T> node, RuleNode<T> otherNode, int idx) {
return node == null ? otherNode : otherNode == null ? node : node.withAll(otherNode, idx);
}

private static <T> RuleNode<T> without(RuleNode<T> node, int idx) {
return node == null ? null : node.without(idx);
}

/**
* Get a new encrypted expression context which is the same as this one, but which includes the given rule and configuration at
* the end of its list.
*
* @param rule the rule to match
* @param configuration the configuration to select when the rule matches
* @return the combined encrypted expression context
*/
public EncryptedExpressionContext with(MatchRule rule, EncryptedExpressionConfig configuration) {
if (configuration == null || rule == null) return this;
return new EncryptedExpressionContext(with(encryptionRuleNode, rule, configuration));
}

/**
* Get a new encryptedExpression context which is the same as this one, but which includes the configurations
* of the given context at the end of its list.
*
* @param other the other encryptedExpression context
* @return the combined encryptedExpression context
*/
public EncryptedExpressionContext with(EncryptedExpressionContext other) {
if (other == null) return this;
return new EncryptedExpressionContext(withAll(encryptionRuleNode, other.encryptionRuleNode));
}

/**
* Get a new encryptedExpression context which is the same as this one, but which includes the given configuration
* inserted at the position of its list indicated by the {@code idx} parameter.
*
* @param idx the index at which insertion should be done
* @param rule the rule to match
* @param configuration the configuration to select when the rule matches
* @return the combined encryptedExpression context
* @throws IndexOutOfBoundsException if the index is out of bounds
*/
public EncryptedExpressionContext with(int idx, MatchRule rule, EncryptedExpressionConfig configuration) throws IndexOutOfBoundsException {
if (configuration == null || rule == null) return this;
return new EncryptedExpressionContext(with(encryptionRuleNode, rule, configuration, idx));
}

/**
* Get a new encryptedExpression context which is the same as this one, but which replaces the rule and configuration at the given
* index with the given rule and configuration.
*
* @param idx the index at which insertion should be done
* @param rule the rule to match
* @param configuration the configuration to select when the rule matches
* @return the combined encryptedExpression context
* @throws IndexOutOfBoundsException if the index is out of bounds
*/
public EncryptedExpressionContext replacing(int idx, MatchRule rule, EncryptedExpressionConfig configuration) throws IndexOutOfBoundsException {
if (configuration == null || rule == null) return this;
return new EncryptedExpressionContext(replacing(encryptionRuleNode, rule, configuration, idx));
}

/**
* Get a new encryptedExpression context which is the same as this one, but without the rule and configuration at the index
* indicated by the {@code idx} parameter.
*
* @param idx the index at which removal should be done
* @return the modified encryptedExpression context
* @throws IndexOutOfBoundsException if the index is out of bounds
*/
public EncryptedExpressionContext without(int idx) throws IndexOutOfBoundsException {
return new EncryptedExpressionContext(without(encryptionRuleNode, idx));
}

/**
* Run a privileged action with this encrypted expression context associated for the duration of the task.
*
* @param action the action to run under association
* @param <T> the action return type
* @return the action return value
*/
public <T> T run(PrivilegedAction<T> action) {
return runAction(action);
}

public ContextManager<EncryptedExpressionContext> getInstanceContextManager() {
return getContextManager();
}

public static ContextManager<EncryptedExpressionContext> getContextManager() {
return CONTEXT_MANAGER;
}

}
Loading
Loading