All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wildfly.security.auth.client.AuthenticationConfiguration Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and Jakarta Messaging BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

The newest version!
/*
 * 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 java.security.AccessController.doPrivileged;
import static java.security.AccessController.getContext;
import static org.wildfly.security.auth.client._private.ElytronMessages.log;
import static org.wildfly.security.provider.util.ProviderUtil.INSTALLED_PROVIDERS;

import java.io.IOException;
import java.net.URI;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.PrivilegedAction;
import java.security.Provider;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;

import javax.net.ssl.SSLSession;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.ChoiceCallback;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextInputCallback;
import javax.security.auth.callback.TextOutputCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.x500.X500Principal;
import javax.security.sasl.RealmCallback;
import javax.security.sasl.RealmChoiceCallback;
import javax.security.sasl.SaslClient;
import javax.security.sasl.SaslClientFactory;
import javax.security.sasl.SaslException;

import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.Oid;
import org.wildfly.common.Assert;
import org.wildfly.common.annotation.NotNull;
import org.wildfly.common.array.Arrays2;
import org.wildfly.common.iteration.CodePointIterator;
import org.wildfly.security.FixedSecurityFactory;
import org.wildfly.security.SecurityFactory;
import org.wildfly.security.auth.callback.CallbackUtil;
import org.wildfly.security.auth.callback.ChannelBindingCallback;
import org.wildfly.security.auth.callback.CredentialCallback;
import org.wildfly.security.auth.callback.EvidenceVerifyCallback;
import org.wildfly.security.auth.callback.ParameterCallback;
import org.wildfly.security.auth.callback.PasswordResetCallback;
import org.wildfly.security.auth.callback.SSLCallback;
import org.wildfly.security.auth.callback.TrustedAuthoritiesCallback;
import org.wildfly.security.auth.principal.AnonymousPrincipal;
import org.wildfly.security.auth.principal.NamePrincipal;
import org.wildfly.security.auth.server.IdentityCredentials;
import org.wildfly.security.auth.server.NameRewriter;
import org.wildfly.security.auth.server.SecurityDomain;
import org.wildfly.security.credential.AlgorithmCredential;
import org.wildfly.security.credential.BearerTokenCredential;
import org.wildfly.security.credential.Credential;
import org.wildfly.security.credential.GSSKerberosCredential;
import org.wildfly.security.credential.PasswordCredential;
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.FactoryCredentialSource;
import org.wildfly.security.credential.source.impl.KeyStoreCredentialSource;
import org.wildfly.security.credential.source.impl.LocalKerberosCredentialSource;
import org.wildfly.security.credential.store.CredentialStore;
import org.wildfly.security.evidence.X509PeerCertificateChainEvidence;
import org.wildfly.security.password.Password;
import org.wildfly.security.password.PasswordFactory;
import org.wildfly.security.password.TwoWayPassword;
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.provider.util.ProviderFactory;
import org.wildfly.security.provider.util.ProviderServiceLoaderSupplier;
import org.wildfly.security.sasl.SaslMechanismSelector;
import org.wildfly.security.sasl.util.FilterMechanismSaslClientFactory;
import org.wildfly.security.sasl.util.LocalPrincipalSaslClientFactory;
import org.wildfly.security.sasl.util.PrivilegedSaslClientFactory;
import org.wildfly.security.sasl.util.PropertiesSaslClientFactory;
import org.wildfly.security.sasl.util.ProtocolSaslClientFactory;
import org.wildfly.security.sasl.util.SSLSaslClientFactory;
import org.wildfly.security.sasl.util.SaslMechanismInformation;
import org.wildfly.security.sasl.util.SecurityProviderSaslClientFactory;
import org.wildfly.security.sasl.util.ServerNameSaslClientFactory;
import org.wildfly.security.ssl.SSLConnection;
import org.wildfly.security.ssl.SSLUtils;
import org.wildfly.security.x500.TrustedAuthority;

/**
 * A configuration which controls how authentication is performed.
 *
 * @author David M. Lloyd
 * @author Darran Lofthouse
 */
public final class AuthenticationConfiguration {
    // constants

    private static final Principal[] NO_PRINCIPALS = new Principal[0];
    private static final Callback[] NO_CALLBACKS = new Callback[0];
    private static final String[] NO_STRINGS = new String[0];

    private static final EnumSet NO_CALLBACK_KINDS = EnumSet.noneOf(CallbackKind.class);

    private static final int SET_PRINCIPAL = 0;
    private static final int SET_HOST = 1;
    private static final int SET_PROTOCOL = 2;
    private static final int SET_REALM = 3;
    private static final int SET_AUTHZ_PRINCIPAL = 4;
    private static final int SET_FWD_AUTH_NAME_DOMAIN = 5;
    private static final int SET_USER_CBH = 6;
    private static final int SET_USER_CB_KINDS = 7;
    private static final int SET_CRED_SOURCE = 8;
    private static final int SET_PROVIDER_SUPPLIER = 9;
    private static final int SET_KEY_MGR_FAC = 10;
    private static final int SET_SASL_SELECTOR = 11;
    private static final int SET_FWD_AUTH_CRED_DOMAIN = 12;
    private static final int SET_PRINCIPAL_RW = 13;
    private static final int SET_SASL_FAC_SUP = 14;
    private static final int SET_PARAM_SPECS = 15;
    private static final int SET_TRUST_MGR_FAC = 16;
    private static final int SET_SASL_MECH_PROPS = 17;
    private static final int SET_ACCESS_CTXT = 18;
    private static final int SET_CALLBACK_INTERCEPT = 19;
    private static final int SET_SASL_PROTOCOL = 20;
    private static final int SET_FWD_AUTHZ_NAME_DOMAIN = 21;
    private static final int SET_WEBSERVICES_PROPS = 22;

    private static final String JBOSS_LOCAL_USER_QUIET_AUTH = "wildfly.sasl.local-user.quiet-auth";
    private static final String JBOSS_LOCAL_USER_LEGACY_QUIET_AUTH = "jboss.sasl.local-user.quiet-auth";


    private static final Supplier DEFAULT_PROVIDER_SUPPLIER = ProviderFactory.getDefaultProviderSupplier(AuthenticationConfiguration.class.getClassLoader());

    static final String WILDFLY_ELYTRON_CAPTURE_ACCESS_CONTROL_CONTEXT_PROPERTY_NAME = "wildfly.elytron.capture.access.control.context";
    static final boolean WILDFLY_ELYTRON_CAPTURE_ACCESS_CONTROL_CONTEXT_PROPERTY;

    static {
        WILDFLY_ELYTRON_CAPTURE_ACCESS_CONTROL_CONTEXT_PROPERTY = AccessController.doPrivileged(new PrivilegedAction() {
            @Override
            public Boolean run() {
                return Boolean.parseBoolean(System.getProperty(WILDFLY_ELYTRON_CAPTURE_ACCESS_CONTROL_CONTEXT_PROPERTY_NAME, "true"));
            }
        });
    }

    /**
     * An empty configuration which can be used as the basis for any configuration.  This configuration supports no
     * remapping of any kind, and always uses an anonymous principal.
     * @deprecated to obtain empty configuration use {@link #empty()} method instead
     */
    @Deprecated
    public static final AuthenticationConfiguration EMPTY = new AuthenticationConfiguration();

    /**
     * An empty configuration which can be used as the basis for any configuration.  This configuration supports no
     * remapping of any kind, and always uses an anonymous principal.
     */
    public static AuthenticationConfiguration empty() {
        return EMPTY;
    }

    private volatile SaslClientFactory saslClientFactory = null;
    private int hashCode;
    private String toString;

    final AccessControlContext capturedAccessContext;
    @NotNull final Principal principal;
    final String setHost;
    final String setProtocol;
    final String setRealm;
    final Principal setAuthzPrincipal;
    final SecurityDomain authenticationNameForwardSecurityDomain;
    final SecurityDomain authenticationCredentialsForwardSecurityDomain;
    final SecurityDomain authorizationNameForwardSecurityDomain;
    final CallbackHandler userCallbackHandler;
    final EnumSet userCallbackKinds;
    final CredentialSource credentialSource;
    final int setPort;
    final Supplier providerSupplier;
    final SecurityFactory keyManagerFactory;
    final SaslMechanismSelector saslMechanismSelector;
    final Function principalRewriter;
    final Supplier saslClientFactorySupplier;
    final List parameterSpecs;
    final SecurityFactory trustManagerFactory;
    final Map saslMechanismProperties;
    final Predicate callbackIntercept;
    final String saslProtocol;
    final Map webServicesProperties;

    // constructors

    /**
     * Construct the empty configuration instance.
     */
    private AuthenticationConfiguration() {
        this.capturedAccessContext = null;
        this.principal = AnonymousPrincipal.getInstance();
        this.setHost = null;
        this.setProtocol = null;
        this.setRealm = null;
        this.setAuthzPrincipal = null;
        this.authenticationNameForwardSecurityDomain = null;
        this.authenticationCredentialsForwardSecurityDomain = null;
        this.authorizationNameForwardSecurityDomain = null;
        this.userCallbackHandler = null;
        this.userCallbackKinds = NO_CALLBACK_KINDS;
        this.credentialSource = IdentityCredentials.NONE;
        this.setPort = -1;
        this.providerSupplier = DEFAULT_PROVIDER_SUPPLIER;
        this.keyManagerFactory = null;
        this.saslMechanismSelector = null;
        this.principalRewriter = null;
        this.saslClientFactorySupplier = null;
        this.parameterSpecs = Collections.emptyList();
        this.trustManagerFactory = null;
        this.saslMechanismProperties = Collections.singletonMap(JBOSS_LOCAL_USER_QUIET_AUTH, "true");
        this.callbackIntercept = null;
        this.saslProtocol = null;
        this.webServicesProperties = null;
    }

    /**
     * Copy constructor for mutating one object field.  It's not pretty but the alternative (many constructors) is much
     * worse.
     *
     * @param original the original configuration (must not be {@code null})
     * @param what the field to mutate
     * @param value the field value to set
     */
    @SuppressWarnings("unchecked")
    private AuthenticationConfiguration(final AuthenticationConfiguration original, final int what, final Object value) {
        this.capturedAccessContext = what == SET_ACCESS_CTXT ? (AccessControlContext) value : original.capturedAccessContext;
        this.principal = what == SET_PRINCIPAL ? (Principal) value : original.principal;
        this.setHost = what == SET_HOST ? (String) value : original.setHost;
        this.setProtocol = what == SET_PROTOCOL ? (String) value : original.setProtocol;
        this.setRealm = what == SET_REALM ? (String) value : original.setRealm;
        this.setAuthzPrincipal = what == SET_AUTHZ_PRINCIPAL ? (Principal) value : original.setAuthzPrincipal;
        this.authenticationNameForwardSecurityDomain = what == SET_FWD_AUTH_NAME_DOMAIN ? (SecurityDomain) value : original.authenticationNameForwardSecurityDomain;
        this.authenticationCredentialsForwardSecurityDomain = what == SET_FWD_AUTH_CRED_DOMAIN ? (SecurityDomain) value : original.authenticationCredentialsForwardSecurityDomain;
        this.authorizationNameForwardSecurityDomain = what == SET_FWD_AUTHZ_NAME_DOMAIN ? (SecurityDomain) value : original.authorizationNameForwardSecurityDomain;
        this.userCallbackHandler = what == SET_USER_CBH ? (CallbackHandler) value : original.userCallbackHandler;
        this.userCallbackKinds = (what == SET_USER_CB_KINDS ? (EnumSet) value : original.userCallbackKinds).clone();
        this.credentialSource = what == SET_CRED_SOURCE ? (CredentialSource) value : original.credentialSource;
        this.setPort = original.setPort;
        this.providerSupplier = what == SET_PROVIDER_SUPPLIER ? (Supplier) value : original.providerSupplier;
        this.keyManagerFactory = what == SET_KEY_MGR_FAC ? (SecurityFactory) value : original.keyManagerFactory;
        this.saslMechanismSelector = what == SET_SASL_SELECTOR ? (SaslMechanismSelector) value : original.saslMechanismSelector;
        this.principalRewriter = what == SET_PRINCIPAL_RW ? (Function) value : original.principalRewriter;
        this.saslClientFactorySupplier = what == SET_SASL_FAC_SUP ? (Supplier) value : original.saslClientFactorySupplier;
        this.parameterSpecs = what == SET_PARAM_SPECS ? (List) value : original.parameterSpecs;
        this.trustManagerFactory = what == SET_TRUST_MGR_FAC ? (SecurityFactory) value : original.trustManagerFactory;
        this.saslMechanismProperties = what == SET_SASL_MECH_PROPS ? (Map) value : original.saslMechanismProperties;
        this.callbackIntercept = what == SET_CALLBACK_INTERCEPT ? (Predicate) value : original.callbackIntercept;
        this.saslProtocol = what == SET_SASL_PROTOCOL ? (String) value : original.saslProtocol;
        this.webServicesProperties = what == SET_WEBSERVICES_PROPS ? (Map) value : original.webServicesProperties;
        sanitazeOnMutation(what);
    }

    /**
     * Copy constructor for mutating two object fields.  It's not pretty but the alternative (many constructors) is much
     * worse.
     *
     * @param original the original configuration (must not be {@code null})
     * @param what1 the field to mutate
     * @param value1 the field value to set
     * @param what2 the field to mutate
     * @param value2 the field value to set
     */
    @SuppressWarnings("unchecked")
    private AuthenticationConfiguration(final AuthenticationConfiguration original, final int what1, final Object value1, final int what2, final Object value2) {
        this.capturedAccessContext = what1 == SET_ACCESS_CTXT ? (AccessControlContext) value1 : what2 == SET_ACCESS_CTXT ? (AccessControlContext) value2 : original.capturedAccessContext;
        this.principal = what1 == SET_PRINCIPAL ? (Principal) value1 : what2 == SET_PRINCIPAL ? (Principal) value2 : original.principal;
        this.setHost = what1 == SET_HOST ? (String) value1 : what2 == SET_HOST ? (String) value2 : original.setHost;
        this.setProtocol = what1 == SET_PROTOCOL ? (String) value1 : what2 == SET_PROTOCOL ? (String) value2 : original.setProtocol;
        this.setRealm = what1 == SET_REALM ? (String) value1 : what2 == SET_REALM ? (String) value2 : original.setRealm;
        this.setAuthzPrincipal = what1 == SET_AUTHZ_PRINCIPAL ? (Principal) value1 : what2 == SET_AUTHZ_PRINCIPAL ? (Principal) value2 : original.setAuthzPrincipal;
        this.authenticationNameForwardSecurityDomain = what1 == SET_FWD_AUTH_NAME_DOMAIN ? (SecurityDomain) value1 : what2 == SET_FWD_AUTH_NAME_DOMAIN ? (SecurityDomain) value2 : original.authenticationNameForwardSecurityDomain;
        this.authenticationCredentialsForwardSecurityDomain = what1 == SET_FWD_AUTH_CRED_DOMAIN ? (SecurityDomain) value1 : what2 == SET_FWD_AUTH_CRED_DOMAIN ? (SecurityDomain) value2 : original.authenticationCredentialsForwardSecurityDomain;
        this.authorizationNameForwardSecurityDomain = what1 == SET_FWD_AUTHZ_NAME_DOMAIN ? (SecurityDomain) value1 : what2 == SET_FWD_AUTHZ_NAME_DOMAIN ? (SecurityDomain) value2 : original.authorizationNameForwardSecurityDomain;
        this.userCallbackHandler = what1 == SET_USER_CBH ? (CallbackHandler) value1 : what2 == SET_USER_CBH ? (CallbackHandler) value2 : original.userCallbackHandler;
        this.userCallbackKinds = (what1 == SET_USER_CB_KINDS ? (EnumSet) value1 : what2 == SET_USER_CB_KINDS ? (EnumSet) value2 : original.userCallbackKinds).clone();
        this.credentialSource = what1 == SET_CRED_SOURCE ? (CredentialSource) value1 : what2 == SET_CRED_SOURCE ? (CredentialSource) value2 : original.credentialSource;
        this.setPort = original.setPort;
        this.providerSupplier = what1 == SET_PROVIDER_SUPPLIER ? (Supplier) value1 : what2 == SET_PROVIDER_SUPPLIER ? (Supplier) value2 : original.providerSupplier;
        this.keyManagerFactory = what1 == SET_KEY_MGR_FAC ? (SecurityFactory) value1 : what2 == SET_KEY_MGR_FAC ? (SecurityFactory) value2 : original.keyManagerFactory;
        this.saslMechanismSelector = what1 == SET_SASL_SELECTOR ? (SaslMechanismSelector) value1 : what2 == SET_SASL_SELECTOR ? (SaslMechanismSelector) value2 : original.saslMechanismSelector;
        this.principalRewriter = what1 == SET_PRINCIPAL_RW ? (Function) value1 : what2 == SET_PRINCIPAL_RW ? (Function) value2 : original.principalRewriter;
        this.saslClientFactorySupplier = what1 == SET_SASL_FAC_SUP ? (Supplier) value1 : what2 == SET_SASL_FAC_SUP ? (Supplier) value2 : original.saslClientFactorySupplier;
        this.parameterSpecs = what1 == SET_PARAM_SPECS ? (List) value1 : what2 == SET_PARAM_SPECS ? (List) value2 : original.parameterSpecs;
        this.trustManagerFactory = what1 == SET_TRUST_MGR_FAC ? (SecurityFactory) value1 : what2 == SET_TRUST_MGR_FAC ? (SecurityFactory) value2 : original.trustManagerFactory;
        this.saslMechanismProperties = what1 == SET_SASL_MECH_PROPS ? (Map) value1 : what2 == SET_SASL_MECH_PROPS ? (Map) value2 : original.saslMechanismProperties;
        this.callbackIntercept = what1 == SET_CALLBACK_INTERCEPT ? (Predicate) value1 : what2 == SET_CALLBACK_INTERCEPT ? (Predicate) value2 : original.callbackIntercept;
        this.saslProtocol = what1 == SET_SASL_PROTOCOL ? (String) value1 : what2 == SET_SASL_PROTOCOL ? (String) value2 : original.saslProtocol;
        this.webServicesProperties = what1 == SET_WEBSERVICES_PROPS ? (Map) value1 : what2 == SET_WEBSERVICES_PROPS ? (Map) value2 : original.webServicesProperties;
        sanitazeOnMutation(what1);
        sanitazeOnMutation(what2);
    }

    /**
     * Copy constructor for mutating three object fields.
     *
     * @param original the original configuration (must not be {@code null})
     * @param what1 the field to mutate
     * @param value1 the field value to set
     * @param what2 the field to mutate
     * @param value2 the field value to set
     * @param what3 the field to mutate
     * @param value3 the field value to set
     */
    @SuppressWarnings("unchecked")
    private AuthenticationConfiguration(final AuthenticationConfiguration original, final int what1, final Object value1, final int what2, final Object value2, final int what3, final Object value3) {
        this.capturedAccessContext = what1 == SET_ACCESS_CTXT ? (AccessControlContext) value1 : what2 == SET_ACCESS_CTXT ? (AccessControlContext) value2 : what3 == SET_ACCESS_CTXT ? (AccessControlContext) value3 : original.capturedAccessContext;
        this.principal = what1 == SET_PRINCIPAL ? (Principal) value1 : what2 == SET_PRINCIPAL ? (Principal) value2 : what3 == SET_PRINCIPAL ? (Principal) value3 : original.principal;
        this.setHost = what1 == SET_HOST ? (String) value1 : what2 == SET_HOST ? (String) value2 : what3 == SET_HOST ? (String) value3 :original.setHost;
        this.setProtocol = what1 == SET_PROTOCOL ? (String) value1 : what2 == SET_PROTOCOL ? (String) value2 : what3 == SET_PROTOCOL ? (String) value3 : original.setProtocol;
        this.setRealm = what1 == SET_REALM ? (String) value1 : what2 == SET_REALM ? (String) value2 : what3 == SET_REALM ? (String) value3 : original.setRealm;
        this.setAuthzPrincipal = what1 == SET_AUTHZ_PRINCIPAL ? (Principal) value1 : what2 == SET_AUTHZ_PRINCIPAL ? (Principal) value2 : what3 == SET_AUTHZ_PRINCIPAL ? (Principal) value3 : original.setAuthzPrincipal;
        this.authenticationNameForwardSecurityDomain = what1 == SET_FWD_AUTH_NAME_DOMAIN ? (SecurityDomain) value1 : what2 == SET_FWD_AUTH_NAME_DOMAIN ? (SecurityDomain) value2 : what3 == SET_FWD_AUTH_NAME_DOMAIN ? (SecurityDomain) value3 : original.authenticationNameForwardSecurityDomain;
        this.authenticationCredentialsForwardSecurityDomain = what1 == SET_FWD_AUTH_CRED_DOMAIN ? (SecurityDomain) value1 : what2 == SET_FWD_AUTH_CRED_DOMAIN ? (SecurityDomain) value2 : what3 == SET_FWD_AUTH_CRED_DOMAIN ? (SecurityDomain) value3 : original.authenticationCredentialsForwardSecurityDomain;
        this.authorizationNameForwardSecurityDomain = what1 == SET_FWD_AUTHZ_NAME_DOMAIN ? (SecurityDomain) value1 : what2 == SET_FWD_AUTHZ_NAME_DOMAIN ? (SecurityDomain) value2 : what3 == SET_FWD_AUTHZ_NAME_DOMAIN ? (SecurityDomain) value3 : original.authorizationNameForwardSecurityDomain;
        this.userCallbackHandler = what1 == SET_USER_CBH ? (CallbackHandler) value1 : what2 == SET_USER_CBH ? (CallbackHandler) value2 : what3 == SET_USER_CBH ? (CallbackHandler) value3 : original.userCallbackHandler;
        this.userCallbackKinds = (what1 == SET_USER_CB_KINDS ? (EnumSet) value1 : what2 == SET_USER_CB_KINDS ? (EnumSet) value2 : what3 == SET_USER_CB_KINDS ? (EnumSet) value3 : original.userCallbackKinds).clone();
        this.credentialSource = what1 == SET_CRED_SOURCE ? (CredentialSource) value1 : what2 == SET_CRED_SOURCE ? (CredentialSource) value2 : what3 == SET_CRED_SOURCE ? (CredentialSource) value3 : original.credentialSource;
        this.setPort = original.setPort;
        this.providerSupplier = what1 == SET_PROVIDER_SUPPLIER ? (Supplier) value1 : what2 == SET_PROVIDER_SUPPLIER ? (Supplier) value2 : what3 == SET_PROVIDER_SUPPLIER ? (Supplier) value3 : original.providerSupplier;
        this.keyManagerFactory = what1 == SET_KEY_MGR_FAC ? (SecurityFactory) value1 : what2 == SET_KEY_MGR_FAC ? (SecurityFactory) value2 : what3 == SET_KEY_MGR_FAC ? (SecurityFactory) value3 : original.keyManagerFactory;
        this.saslMechanismSelector = what1 == SET_SASL_SELECTOR ? (SaslMechanismSelector) value1 : what2 == SET_SASL_SELECTOR ? (SaslMechanismSelector) value2 : what3 == SET_SASL_SELECTOR ? (SaslMechanismSelector) value3 : original.saslMechanismSelector;
        this.principalRewriter = what1 == SET_PRINCIPAL_RW ? (Function) value1 : what2 == SET_PRINCIPAL_RW ? (Function) value2 : what3 == SET_PRINCIPAL_RW ? (Function) value3 : original.principalRewriter;
        this.saslClientFactorySupplier = what1 == SET_SASL_FAC_SUP ? (Supplier) value1 : what2 == SET_SASL_FAC_SUP ? (Supplier) value2 : what3 == SET_SASL_FAC_SUP ? (Supplier) value3 : original.saslClientFactorySupplier;
        this.parameterSpecs = what1 == SET_PARAM_SPECS ? (List) value1 : what2 == SET_PARAM_SPECS ? (List) value2 : what3 == SET_PARAM_SPECS ? (List) value3 : original.parameterSpecs;
        this.trustManagerFactory = what1 == SET_TRUST_MGR_FAC ? (SecurityFactory) value1 : what2 == SET_TRUST_MGR_FAC ? (SecurityFactory) value2 : what3 == SET_TRUST_MGR_FAC ? (SecurityFactory) value3 : original.trustManagerFactory;
        this.saslMechanismProperties = what1 == SET_SASL_MECH_PROPS ? (Map) value1 : what2 == SET_SASL_MECH_PROPS ? (Map) value2 : what3 == SET_SASL_MECH_PROPS ? (Map) value3 : original.saslMechanismProperties;
        this.callbackIntercept = what1 == SET_CALLBACK_INTERCEPT ? (Predicate) value1 : what2 == SET_CALLBACK_INTERCEPT ? (Predicate) value2 : what3 == SET_CALLBACK_INTERCEPT ? (Predicate) value3 : original.callbackIntercept;
        this.saslProtocol = what1 == SET_SASL_PROTOCOL ? (String) value1 : what2 == SET_SASL_PROTOCOL ? (String) value2 : what3 == SET_SASL_PROTOCOL ? (String) value3 : original.saslProtocol;
        this.webServicesProperties = what1 == SET_WEBSERVICES_PROPS ? (Map) value1 : what2 == SET_WEBSERVICES_PROPS ? (Map) value2 : what3 == SET_WEBSERVICES_PROPS ? (Map) value3 : original.webServicesProperties;
        sanitazeOnMutation(what1);
        sanitazeOnMutation(what2);
        sanitazeOnMutation(what3);
    }

    /**
     * Copy constructor for mutating the port number.
     *
     * @param original the original configuration (must not be {@code null})
     * @param port the port number
     */
    private AuthenticationConfiguration(final AuthenticationConfiguration original, final int port) {
        this.capturedAccessContext = original.capturedAccessContext;
        this.principal = original.principal;
        this.setHost = original.setHost;
        this.setProtocol = original.setProtocol;
        this.setRealm = original.setRealm;
        this.setAuthzPrincipal = original.setAuthzPrincipal;
        this.authenticationNameForwardSecurityDomain = original.authenticationNameForwardSecurityDomain;
        this.authenticationCredentialsForwardSecurityDomain = original.authenticationCredentialsForwardSecurityDomain;
        this.authorizationNameForwardSecurityDomain = original.authorizationNameForwardSecurityDomain;
        this.userCallbackHandler = original.userCallbackHandler;
        this.userCallbackKinds = original.userCallbackKinds;
        this.credentialSource = original.credentialSource;
        this.setPort = port;
        this.providerSupplier = original.providerSupplier;
        this.keyManagerFactory = original.keyManagerFactory;
        this.saslMechanismSelector = original.saslMechanismSelector;
        this.principalRewriter = original.principalRewriter;
        this.saslClientFactorySupplier = original.saslClientFactorySupplier;
        this.parameterSpecs = original.parameterSpecs;
        this.trustManagerFactory = original.trustManagerFactory;
        this.saslMechanismProperties = original.saslMechanismProperties;
        this.callbackIntercept = original.callbackIntercept;
        this.saslProtocol = original.saslProtocol;
        this.webServicesProperties = original.webServicesProperties;
    }

    private AuthenticationConfiguration(final AuthenticationConfiguration original, final AuthenticationConfiguration other) {
        this.capturedAccessContext = getOrDefault(other.capturedAccessContext, original.capturedAccessContext);
        this.principal = other.principal instanceof AnonymousPrincipal ? original.principal : other.principal;
        this.setHost = getOrDefault(other.setHost, original.setHost);
        this.setProtocol = getOrDefault(other.setProtocol, original.setProtocol);
        this.setRealm = getOrDefault(other.setRealm, original.setRealm);
        this.setAuthzPrincipal = getOrDefault(other.setAuthzPrincipal, original.setAuthzPrincipal);
        this.authenticationNameForwardSecurityDomain = getOrDefault(other.authenticationNameForwardSecurityDomain, original.authenticationNameForwardSecurityDomain);
        this.authenticationCredentialsForwardSecurityDomain = getOrDefault(other.authenticationCredentialsForwardSecurityDomain, original.authenticationCredentialsForwardSecurityDomain);
        this.authorizationNameForwardSecurityDomain = getOrDefault(other.authorizationNameForwardSecurityDomain, original.authorizationNameForwardSecurityDomain);
        this.userCallbackHandler = getOrDefault(other.userCallbackHandler, original.userCallbackHandler);
        this.userCallbackKinds = getOrDefault(other.userCallbackKinds, original.userCallbackKinds).clone();
        this.credentialSource = other.credentialSource == IdentityCredentials.NONE ? original.credentialSource : other.credentialSource;
        this.setPort = getOrDefault(other.setPort, original.setPort);
        this.providerSupplier = getOrDefault(other.providerSupplier, original.providerSupplier);
        this.keyManagerFactory = getOrDefault(other.keyManagerFactory, original.keyManagerFactory);
        this.saslMechanismSelector = getOrDefault(other.saslMechanismSelector, original.saslMechanismSelector);
        this.principalRewriter = getOrDefault(other.principalRewriter, original.principalRewriter);
        this.saslClientFactorySupplier = getOrDefault(other.saslClientFactorySupplier, original.saslClientFactorySupplier);
        this.parameterSpecs = getOrDefault(other.parameterSpecs, original.parameterSpecs);
        this.trustManagerFactory = getOrDefault(other.trustManagerFactory, original.trustManagerFactory);
        this.saslMechanismProperties = getOrDefault(other.saslMechanismProperties, original.saslMechanismProperties);
        this.callbackIntercept = other.callbackIntercept == null ? original.callbackIntercept : original.callbackIntercept == null ? other.callbackIntercept : other.callbackIntercept.or(original.callbackIntercept);
        this.saslProtocol =  getOrDefault(other.saslProtocol, original.saslProtocol);
        this.webServicesProperties =  getOrDefault(other.webServicesProperties, original.webServicesProperties);
        sanitazeOnMutation(SET_USER_CBH);
    }

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

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

    // test method

    Principal getPrincipal() {
        return authenticationNameForwardSecurityDomain != null ? authenticationNameForwardSecurityDomain.getCurrentSecurityIdentity().getPrincipal() : principal;
    }

    @Deprecated
    String getHost() {
        return setHost;
    }

    @Deprecated
    String getProtocol() {
        return setProtocol;
    }

    String getSaslProtocol() {
        return saslProtocol;
    }

    @Deprecated
    int getPort() {
        return setPort;
    }

    String getWsHttpMechanism() {
        if (webServicesProperties != null) {
            Object wsHttpMechanism = webServicesProperties.get("http-mechanism");
            if (wsHttpMechanism != null) {
                return wsHttpMechanism.toString();
            }
        }
        return null;
    }

    String getWsSecurityType() {
        if (webServicesProperties != null) {
            Object wsSecurityType = webServicesProperties.get("ws-security-type");
            if (wsSecurityType != null) {
                return wsSecurityType.toString();
            }
        }
        return null;
    }

    // internal actions

    /**
     * Determine if this SASL mechanism is supported by this configuration (not policy).  Implementations must
     * combine using boolean-OR operations.
     *
     * @param mechanismName the mech name (must not be {@code null})
     * @return {@code true} if supported, {@code false} otherwise
     */
    boolean saslSupportedByConfiguration(String mechanismName) {
        // special case for local, quiet auth
        // anonymous is only supported if the principal is anonymous.  If the principal is anonymous, only anonymous or principal-less mechanisms are supported.
        if (! userCallbackKinds.contains(CallbackKind.PRINCIPAL) && principal != AnonymousPrincipal.getInstance()) {
            // no callback which can handle a principal.
            if (! (mechanismName.equals(SaslMechanismInformation.Names.JBOSS_LOCAL_USER) || SaslMechanismInformation.doesNotUsePrincipal(mechanismName))) {
                // the mechanism requires a principal.
                if (getPrincipal() instanceof AnonymousPrincipal != mechanismName.equals(SaslMechanismInformation.Names.ANONYMOUS)) {
                    // either we have no principal & the mech requires one, or we have a principal but the mech is anonymous.
                    return false;
                }
            }
        }
        // if we have a credential-providing callback handler, we support any mechanism from here on out
        if (userCallbackKinds.contains(CallbackKind.CREDENTIAL) || (credentialSource != null && ! credentialSource.equals(IdentityCredentials.NONE))) {
            return true;
        }
        // mechanisms that do not need credentials are probably supported
        if (SaslMechanismInformation.doesNotRequireClientCredentials(mechanismName)) {
            return true;
        }
        // if we have a key manager factory, we definitely support IEC/ISO 9798
        if (keyManagerFactory != null && SaslMechanismInformation.IEC_ISO_9798.test(mechanismName)) {
            return true;
        }
        // otherwise, use mechanism information and our credential set
        if (credentialSource != null) {
            Set> types = SaslMechanismInformation.getSupportedClientCredentialTypes(mechanismName);
            final CredentialSource credentials = credentialSource;
            for (Class type : types) {
                if (AlgorithmCredential.class.isAssignableFrom(type)) {
                    Set algorithms = SaslMechanismInformation.getSupportedClientCredentialAlgorithms(mechanismName, type);
                    if (algorithms.contains("*")) {
                        try {
                            if (credentials.getCredentialAcquireSupport(type, null).mayBeSupported()) {
                                return true;
                            }
                        } catch (IOException e) {
                            // no match
                        }
                    } else {
                        for (String algorithm : algorithms) {
                            try {
                                if (credentials.getCredentialAcquireSupport(type, algorithm).mayBeSupported()) {
                                    return true;
                                }
                            } catch (IOException e) {
                                // no match
                            }
                        }
                    }
                } else {
                    try {
                        if (credentials.getCredentialAcquireSupport(type).mayBeSupported()) {
                            return true;
                        }
                    } catch (IOException e) {
                        // no match
                    }
                }
            }
        }
        // no apparent way to support the mechanism
        return false;
    }

    Principal doRewriteUser(Principal original) {
        final Function principalRewriter = this.principalRewriter;
        final Principal rewritten = principalRewriter == null ? original : principalRewriter.apply(original);
        if (rewritten == null) {
            throw log.invalidName();
        }
        return rewritten;
    }

    Principal getAuthorizationPrincipal() {
        return authorizationNameForwardSecurityDomain != null ? authorizationNameForwardSecurityDomain.getCurrentSecurityIdentity().getPrincipal() : setAuthzPrincipal;
    }

    Supplier getProviderSupplier() {
        final Supplier providerSupplier = this.providerSupplier;
        return providerSupplier == null ? INSTALLED_PROVIDERS : providerSupplier;
    }

    SaslClientFactory getSaslClientFactory(Supplier providers) {
        final Supplier supplier = saslClientFactorySupplier;
        return supplier != null ? supplier.get() : new SecurityProviderSaslClientFactory(providers);
    }

    SecurityFactory getX509TrustManagerFactory() {
        return trustManagerFactory == null ? SSLUtils.getDefaultX509TrustManagerSecurityFactory() : trustManagerFactory;
    }

    SecurityFactory getX509KeyManagerFactory() {
        return keyManagerFactory;
    }

    CredentialSource getCredentialSource() {
        if (authenticationCredentialsForwardSecurityDomain != null) {
            return doPrivileged((PrivilegedAction) () -> authenticationCredentialsForwardSecurityDomain.getCurrentSecurityIdentity().getPrivateCredentials(), capturedAccessContext);
        } else {
            return credentialSource;
        }
    }

    // assembly methods - rewrite

    /**
     * Create a new configuration which is the same as this configuration, but rewrites the user name using the given
     * name rewriter.  The name rewriter is appended to the the existing name rewrite function.
     *
     * @param rewriter the name rewriter
     * @return the new configuration
     */
    public AuthenticationConfiguration rewriteUser(NameRewriter rewriter) {
        if (rewriter == null) {
            return this;
        }

        if (this.principalRewriter == null) {
            return new AuthenticationConfiguration(this, SET_PRINCIPAL_RW, rewriter.asPrincipalRewriter());
        }

        return new AuthenticationConfiguration(this, SET_PRINCIPAL_RW, principalRewriter.andThen(rewriter.asPrincipalRewriter()));
    }

    /**
     * Create a new configuration which is the same as this configuration, but rewrites the user name using only
     * the given name rewriter.  Any name rewriters on this configuration are ignored for the new configuration.
     *
     * @param rewriter the name rewriter
     * @return the new configuration
     */
    public AuthenticationConfiguration rewriteUserOnlyWith(NameRewriter rewriter) {
        if (rewriter == null) {
            return this;
        }
        return new AuthenticationConfiguration(this, SET_PRINCIPAL_RW, rewriter.asPrincipalRewriter());
    }

    // assembly methods - filter

    // assembly methods - configuration

    /**
     * Create a new configuration which is the same as this configuration, but which uses an anonymous login.
     *
     * @return the new configuration
     */
    public AuthenticationConfiguration useAnonymous() {
        return usePrincipal(AnonymousPrincipal.getInstance());
    }

    /**
     * Create a new configuration which is the same as this configuration, but which uses the given principal to authenticate.
     *
     * @param principal the principal to use (must not be {@code null})
     * @return the new configuration
     */
    public AuthenticationConfiguration usePrincipal(NamePrincipal principal) {
        return usePrincipal((Principal) principal);
    }

    /**
     * Create a new configuration which is the same as this configuration, but which uses the given principal to authenticate.
     *
     * @param principal the principal to use (must not be {@code null})
     * @return the new configuration
     */
    public AuthenticationConfiguration usePrincipal(Principal principal) {
        Assert.checkNotNullParam("principal", principal);
        if (Objects.equals(this.principal, principal)) {
            return this;
        }
        return new AuthenticationConfiguration(this, SET_PRINCIPAL, principal);
    }

    /**
     * Create a new configuration which is the same as this configuration, but which uses the given login name to authenticate.
     *
     * @param name the principal to use (must not be {@code null})
     * @return the new configuration
     */
    public AuthenticationConfiguration useName(String name) {
        Assert.checkNotNullParam("name", name);
        return usePrincipal(new NamePrincipal(name));
    }

    /**
     * Create a new configuration which is the same as this configuration, but which attempts to authorize to the given
     * name after authentication.  Only mechanisms which support an authorization name principal will be selected.
     *
     * @param name the name to use, or {@code null} to not request authorization in the new configuration
     * @return the new configuration
     */
    public AuthenticationConfiguration useAuthorizationName(String name) {
        return useAuthorizationPrincipal(name == null ? null : new NamePrincipal(name));
    }

    /**
     * Create a new configuration which is the same as this configuration, but which attempts to authorize to the given
     * principal after authentication.  Only mechanisms which support an authorization principal of the given type will
     * be selected.
     *
     * @param principal the principal to use, or {@code null} to not request authorization in the new configuration
     * @return the new configuration
     */
    public AuthenticationConfiguration useAuthorizationPrincipal(Principal principal) {
        if (Objects.equals(principal, setAuthzPrincipal)) {
            return this;
        } else {
            return new AuthenticationConfiguration(this, SET_AUTHZ_PRINCIPAL, principal);
        }
    }

    /**
     * Create a new configuration which is the same as this configuration, but which uses the given credential to authenticate.
     *
     * @param credential the credential to authenticate
     * @return the new configuration
     */
    public AuthenticationConfiguration useCredential(Credential credential) {
        if (credential == null) return this;
        final CredentialSource credentialSource = this.credentialSource;
        if (credentialSource == CredentialSource.NONE) {
            return new AuthenticationConfiguration(this, SET_CRED_SOURCE, IdentityCredentials.NONE.withCredential(credential));
        } else if (credentialSource instanceof IdentityCredentials) {
            return new AuthenticationConfiguration(this, SET_CRED_SOURCE, ((IdentityCredentials) credentialSource).withCredential(credential));
        } else {
            return new AuthenticationConfiguration(this, SET_CRED_SOURCE, credentialSource.with(IdentityCredentials.NONE.withCredential(credential)));
        }
    }

    /**
     * Create a new configuration which is the same as this configuration, but which uses the given password to authenticate.
     *
     * @param password the password to use
     * @return the new configuration
     */
    public AuthenticationConfiguration usePassword(Password password) {
        final CredentialSource filtered = getCredentialSource().without(PasswordCredential.class);
        return password == null ? useCredentials(filtered) : useCredentials(filtered).useCredential(new PasswordCredential(password));
    }

    /**
     * Create a new configuration which is the same as this configuration, but which uses the given password to authenticate.
     *
     * @param password the password to use
     * @return the new configuration
     */
    public AuthenticationConfiguration usePassword(char[] password) {
        return usePassword(password == null ? null : ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, password));
    }

    /**
     * Create a new configuration which is the same as this configuration, but which uses the given password to authenticate.
     *
     * @param password the password to use
     * @return the new configuration
     */
    public AuthenticationConfiguration usePassword(String password) {
        return usePassword(password == null ? null : ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, password.toCharArray()));
    }

    /**
     * Create a new configuration which is the same as this configuration, but converts the given masked password to a
     * clear password and uses the clear password to authenticate.
     *
     * @param password the password to use
     * @return the new configuration
     * @throws NoSuchAlgorithmException if algorithm used to get PasswordFactory instance is invalid
     * @throws InvalidKeySpecException if invalid spec is used to generate password
     */
    public AuthenticationConfiguration useMaskedPassword(MaskedPassword password) throws NoSuchAlgorithmException, InvalidKeySpecException {
        Assert.assertNotNull(password);
        final PasswordFactory passwordFactory = PasswordFactory.getInstance(password.getAlgorithm());
        final ClearPasswordSpec spec = passwordFactory.getKeySpec(password, ClearPasswordSpec.class);
        final char[] clearPassword = spec.getEncodedPassword();

        PasswordFactory factory = PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR);
        Password finalPassword = factory.generatePassword(new ClearPasswordSpec(clearPassword)).castAs(ClearPassword.class);
        final CredentialSource filtered = getCredentialSource().without(PasswordCredential.class);
        return finalPassword == null ? useCredentials(filtered) : useCredentials(filtered).useCredential(new PasswordCredential(finalPassword));
    }

    /**
     * Create a new configuration which is the same as this configuration, but which uses the given masked password to authenticate.
     *
     * @param maskedPasswordBytes the masked password bytes (must not be {@code null})
     * @param algorithm the algorithm (can be {@code null}, default:"masked-MD5-DES")
     * @param initialKeyMaterial the initial key material (can be {@code null}, default:"somearbitrarycrazystringthatdoesnotmatter")
     * @param iterationCount the iteration count (must not be less than 1)
     * @param salt the salt bytes (must not be {@code null})
     * @param initializationVector the initialization vector (can be {@code null})
     * @return the new configuration
     * @throws NoSuchAlgorithmException if algorithm used to get PasswordFactory instance is invalid
     * @throws InvalidKeySpecException if invalid spec is used to generate password
     */
    public AuthenticationConfiguration useMaskedPassword(byte[] maskedPasswordBytes, String algorithm, char[] initialKeyMaterial, int iterationCount, byte[] salt, byte[] initializationVector) throws NoSuchAlgorithmException, InvalidKeySpecException {
        Assert.checkMinimumParameter("iterationCount", 1, iterationCount);
        Assert.assertNotNull(salt);
        Assert.assertNotNull(maskedPasswordBytes);
        if (algorithm == null) algorithm = MaskedPassword.ALGORITHM_MASKED_MD5_DES;
        if (initialKeyMaterial == null) initialKeyMaterial = "somearbitrarycrazystringthatdoesnotmatter".toCharArray();
        final MaskedPasswordSpec spec = new MaskedPasswordSpec(initialKeyMaterial, iterationCount, salt, maskedPasswordBytes, initializationVector);

        PasswordFactory factory = PasswordFactory.getInstance(algorithm);
        MaskedPassword password = factory.generatePassword(spec).castAs(MaskedPassword.class);
        return useMaskedPassword(password);
    }


    /**
     * Create a new configuration which is the same as this configuration, but which uses the given masked password to authenticate.
     *
     * @param maskedPassword the masked password, as a string (must not be {@code null})
     * @param algorithm the algorithm (can be {@code null}, default:"masked-MD5-DES")
     * @param initialKeyMaterial the initial key material, as a string(can be {@code null}, default:"somearbitrarycrazystringthatdoesnotmatter")
     * @param iterationCount the iteration count, as an integer (must not be less than 1)
     * @param salt the salt, as a string (must not be {@code null})
     * @param initializationVector the initialization vector, as a string (can be {@code null})
     * @return the new configuration
     * @throws NoSuchAlgorithmException if algorithm used to get PasswordFactory instance is invalid
     * @throws InvalidKeySpecException if invalid spec is used to generate password
     */
    public AuthenticationConfiguration useMaskedPassword(String maskedPassword, String algorithm, String initialKeyMaterial, int iterationCount, String salt, String initializationVector) throws InvalidKeySpecException, NoSuchAlgorithmException {
        byte[] finalMaskedPasswordBytes = maskedPassword == null ? null : CodePointIterator.ofString(maskedPassword).base64Decode().drain();
        char[] finalInitialKeyMaterial = initialKeyMaterial == null ? null : initialKeyMaterial.toCharArray();
        byte[] finalSalt = salt == null ? null : CodePointIterator.ofString(salt).asUtf8().drain();
        byte[] finalInitializationVector = initializationVector == null ? null : CodePointIterator.ofString(initializationVector).base64Decode().drain();

        return useMaskedPassword(finalMaskedPasswordBytes, algorithm, finalInitialKeyMaterial, iterationCount, finalSalt, finalInitializationVector);
    }

    /**
     * Create a new configuration which is the same as this configuration, but which uses the given callback handler to
     * acquire a password with which to authenticate, when a password-based authentication algorithm is in use.
     *
     * @param callbackHandler the password callback handler
     * @return the new configuration
     */
    public AuthenticationConfiguration useCredentialCallbackHandler(CallbackHandler callbackHandler) {
        return useCallbackHandler(callbackHandler, EnumSet.of(CallbackKind.CREDENTIAL));
    }

    /**
     * Create a new configuration which is the same as this configuration, but which uses the given callback handler
     * to authenticate.
     * 

* Important notes: It is important to ensure that each distinct client identity uses a distinct {@code CallbackHandler} * instance in order to avoid mis-pooling of connections, identity crossovers, and other potentially serious problems. * It is not recommended that a {@code CallbackHandler} implement {@code equals()} and {@code hashCode()}, however if it does, * it is important to ensure that these methods consider equality based on an authenticating identity that does not * change between instances. In particular, a callback handler which requests user input on each usage is likely to cause * a problem if the user name can change on each authentication request. *

* Because {@code CallbackHandler} instances are unique per identity, it is often useful for instances to cache * identity information, credentials, and/or other authentication-related information in order to facilitate fast * re-authentication. * * @param callbackHandler the callback handler to use * @return the new configuration */ public AuthenticationConfiguration useCallbackHandler(CallbackHandler callbackHandler) { return callbackHandler == null ? this : new AuthenticationConfiguration(this, SET_USER_CBH, callbackHandler, SET_USER_CB_KINDS, EnumSet.allOf(CallbackKind.class)); } /** * Create a new configuration which is the same as this configuration, but which uses the given callback handler * to authenticate. *

* Important notes: It is important to ensure that each distinct client identity uses a distinct {@code CallbackHandler} * instance in order to avoid mis-pooling of connections, identity crossovers, and other potentially serious problems. * It is not recommended that a {@code CallbackHandler} implement {@code equals()} and {@code hashCode()}, however if it does, * it is important to ensure that these methods consider equality based on an authenticating identity that does not * change between instances. In particular, a callback handler which requests user input on each usage is likely to cause * a problem if the user name can change on each authentication request. *

* Because {@code CallbackHandler} instances are unique per identity, it is often useful for instances to cache * identity information, credentials, and/or other authentication-related information in order to facilitate fast * re-authentication. * * @param callbackHandler the callback handler to use * @param callbackKinds the kinds of callbacks that the handler should use * @return the new configuration */ public AuthenticationConfiguration useCallbackHandler(CallbackHandler callbackHandler, Set callbackKinds) { return callbackHandler == null ? this : new AuthenticationConfiguration(this, SET_USER_CBH, callbackHandler, SET_USER_CB_KINDS, EnumSet.copyOf(callbackKinds)); } /** * Create a new configuration which is the same as this configuration, but which uses the given GSS-API credential to authenticate. * * @param credential the GSS-API credential to use * @return the new configuration */ public AuthenticationConfiguration useGSSCredential(GSSCredential credential) { return credential == null ? this : useCredential(new GSSKerberosCredential(credential)); } /** * Create a new configuration which is the same as this configuration, but which uses the given key store and alias * to acquire the credential required for authentication. * * @param keyStoreEntry the key store entry to use * @return the new configuration */ public AuthenticationConfiguration useKeyStoreCredential(KeyStore.Entry keyStoreEntry) { return keyStoreEntry == null ? this : useCredentials(getCredentialSource().with(new KeyStoreCredentialSource(new FixedSecurityFactory<>(keyStoreEntry)))); } /** * Create a new configuration which is the same as this configuration, but which uses the given key store and alias * to acquire the credential required for authentication. * * @param keyStore the key store to use * @param alias the key store alias * @return the new configuration */ public AuthenticationConfiguration useKeyStoreCredential(KeyStore keyStore, String alias) { return keyStore == null || alias == null ? this : useCredentials(getCredentialSource().with(new KeyStoreCredentialSource(keyStore, alias, null))); } /** * Create a new configuration which is the same as this configuration, but which uses the given key store and alias * to acquire the credential required for authentication. * * @param keyStore the key store to use * @param alias the key store alias * @param protectionParameter the protection parameter to use to access the key store entry * @return the new configuration */ public AuthenticationConfiguration useKeyStoreCredential(KeyStore keyStore, String alias, KeyStore.ProtectionParameter protectionParameter) { return keyStore == null || alias == null ? this : useCredentials(getCredentialSource().with(new KeyStoreCredentialSource(keyStore, alias, protectionParameter))); } /** * Create a new configuration which is the same as this configuration, but which uses the given private key and X.509 * certificate chain to authenticate. * * @param privateKey the client private key * @param certificateChain the client certificate chain * @return the new configuration */ public AuthenticationConfiguration useCertificateCredential(PrivateKey privateKey, X509Certificate... certificateChain) { return certificateChain == null || certificateChain.length == 0 || privateKey == null ? this : useCertificateCredential(new X509CertificateChainPrivateCredential(privateKey, certificateChain)); } /** * Create a new configuration which is the same as this configuration, but which uses the given private key and X.509 * certificate chain to authenticate. * * @param credential the credential containing the private key and certificate chain * @return the new configuration */ public AuthenticationConfiguration useCertificateCredential(X509CertificateChainPrivateCredential credential) { return credential == null ? this : useCredential(credential); } /** * Create a new configuration which is the same as this configuration, but uses credentials found at the given * alias and credential store. * * @param credentialStore the credential store (must not be {@code null}) * @param alias the alias within the store (must not be {@code null}) * @return the new configuration */ public AuthenticationConfiguration useCredentialStoreEntry(CredentialStore credentialStore, String alias) { Assert.checkNotNullParam("credentialStore", credentialStore); Assert.checkNotNullParam("alias", alias); CredentialStoreCredentialSource csCredentialSource = new CredentialStoreCredentialSource(credentialStore, alias); return useCredentials(getCredentialSource().with(csCredentialSource)); } /** * Create a new configuration which is the same as this configuration, but which uses the given key manager * to acquire the credential required for authentication. * * @param keyManager the key manager to use * @return the new configuration */ public AuthenticationConfiguration useKeyManagerCredential(X509KeyManager keyManager) { return new AuthenticationConfiguration(this, SET_KEY_MGR_FAC, new FixedSecurityFactory<>(keyManager)); } /** * Create a new configuration which is the same as this configuration, but which uses local kerberos ticket cache * to acquire the credential required for authentication. * * @param mechanismOids array of oid's indicating the mechanisms over which the credential is to be acquired * @return the new configuration * * @since 1.2.0 * @deprecated can be ommited - kerberos based authentication mechanism obtains credential himself */ @Deprecated public AuthenticationConfiguration useLocalKerberosCredential(Oid[] mechanismOids) { return useCredentials(getCredentialSource().with(LocalKerberosCredentialSource.builder().setMechanismOids(mechanismOids).build())); } /** * Create a new configuration which is the same as this configuration, but which uses the given identity * credentials to acquire the credential required for authentication. * * @param credentials the credentials to use * @return the new configuration */ public AuthenticationConfiguration useCredentials(CredentialSource credentials) { return new AuthenticationConfiguration(this, SET_CRED_SOURCE, credentials == null ? CredentialSource.NONE : credentials); } /** * Create a new configuration which is the same as this configuration, but which uses the given choice if the given * predicate evaluates to {@code true}. * * @param matchPredicate the predicate that should be used to determine if a choice callback type and prompt are * relevant for the given choice * @param choice the choice to use if the given predicate evaluates to {@code true} * @return the new configuration */ public AuthenticationConfiguration useChoice(BiPredicate, String> matchPredicate, String choice) { Assert.checkNotNullParam("matchPredicate", matchPredicate); Assert.checkNotNullParam("choice", choice); final Predicate callbackIntercept = this.callbackIntercept; Predicate newIntercept = cb -> { if (! (cb instanceof ChoiceCallback)) { return false; } final ChoiceCallback choiceCallback = (ChoiceCallback) cb; if (matchPredicate.test(choiceCallback.getClass(), choiceCallback.getPrompt())) { final String[] choices = choiceCallback.getChoices(); final int choicesLength = choices.length; for (int i = 0; i < choicesLength; i++) { if (choices[i].equals(choice)) { choiceCallback.setSelectedIndex(i); return true; } } } return false; }; if (callbackIntercept == null) { return new AuthenticationConfiguration(this, SET_CALLBACK_INTERCEPT, newIntercept); } else { return new AuthenticationConfiguration(this, SET_CALLBACK_INTERCEPT, newIntercept.or(callbackIntercept)); } } /** * Create a new configuration which is the same as this configuration, but which uses the given parameter specification. * * @param parameterSpec the algorithm parameter specification to use * @return the new configuration */ public AuthenticationConfiguration useParameterSpec(AlgorithmParameterSpec parameterSpec) { if (parameterSpec == null) { return this; } final List specs = parameterSpecs; if (specs.isEmpty()) { return new AuthenticationConfiguration(this, SET_PARAM_SPECS, Collections.singletonList(parameterSpec)); } else { ArrayList newList = new ArrayList<>(); for (AlgorithmParameterSpec spec : specs) { if (spec.getClass() == parameterSpec.getClass()) continue; newList.add(spec); } if (newList.isEmpty()) { return new AuthenticationConfiguration(this, SET_PARAM_SPECS, Collections.singletonList(parameterSpec)); } else { newList.add(parameterSpec); return new AuthenticationConfiguration(this, SET_PARAM_SPECS, newList); } } } /** * Create a new configuration which is the same as this configuration, but which uses the given trust manager * for trust verification. * * @param trustManager the trust manager to use or {@code null} if the default trust manager should be used * @return the new configuration */ public AuthenticationConfiguration useTrustManager(X509TrustManager trustManager) { return trustManager == null ? new AuthenticationConfiguration(this, SET_TRUST_MGR_FAC, null) : new AuthenticationConfiguration(this, SET_TRUST_MGR_FAC, new FixedSecurityFactory<>(trustManager)); } /** * Create a new configuration which is the same as this configuration, but which connects to a different host name. * * @param hostName the host name to connect to * @return the new configuration * @deprecated This configuration is not supported by most providers and will be removed in a future release. */ @Deprecated public AuthenticationConfiguration useHost(String hostName) { if (hostName == null || hostName.isEmpty()) { hostName = null; } if (Objects.equals(this.setHost, hostName)) { return this; } else { return new AuthenticationConfiguration(this, SET_HOST, hostName); } } /** * Create a new configuration which is the same as this configuration, but which specifies a different protocol to be used for outgoing connection. * * @param protocol the protocol to be used for outgoing connection. * @return the new configuration * @deprecated This configuration is not supported by most providers and will be removed in a future release. */ @Deprecated public AuthenticationConfiguration useProtocol(String protocol) { if (protocol == null || protocol.isEmpty()) { protocol = null; } if (Objects.equals(this.setProtocol, protocol)) { return this; } else { return new AuthenticationConfiguration(this, SET_PROTOCOL, protocol); } } /** * Create a new configuration which is the same as this configuration, but which specifies a different protocol to be passed to the authentication mechanisms. * * @param saslProtocol the protocol to pass to the authentication mechanisms. * @return the new configuration */ public AuthenticationConfiguration useSaslProtocol(String saslProtocol) { if (saslProtocol == null || saslProtocol.isEmpty()) { saslProtocol = null; } if (Objects.equals(this.saslProtocol, saslProtocol)) { return this; } else { return new AuthenticationConfiguration(this, SET_SASL_PROTOCOL, saslProtocol); } } public AuthenticationConfiguration useWebServices(Map webservicesProperties) { final HashMap newMap = new HashMap<>(); newMap.putAll(webservicesProperties); newMap.values().removeIf(Objects::isNull); return new AuthenticationConfiguration(this, SET_WEBSERVICES_PROPS, optimizeMap(newMap)); } /** * Create a new configuration which is the same as this configuration, but which connects to a different port. * * @param port the port to connect to, or -1 to not override the port * @return the new configuration * @deprecated This configuration is not supported by most providers and will be removed in a future release. */ @Deprecated public AuthenticationConfiguration usePort(int port) { if (port < -1 || port > 65535) throw log.invalidPortNumber(port); if (port == setPort) return this; return new AuthenticationConfiguration(this, port); } /** * Create a new configuration which is the same as this configuration, but which forwards the authentication name * and credentials from the current identity of the given security domain. * * @param securityDomain the security domain * @return the new configuration */ public AuthenticationConfiguration useForwardedIdentity(SecurityDomain securityDomain) { return useForwardedAuthenticationIdentity(securityDomain).useForwardedAuthenticationCredentials(securityDomain); } /** * Create a new configuration which is the same as this configuration, but which forwards the authentication name * from the current identity of the given security domain. * * @param securityDomain the security domain * @return the new configuration */ public AuthenticationConfiguration useForwardedAuthenticationIdentity(SecurityDomain securityDomain) { if (Objects.equals(authenticationNameForwardSecurityDomain, securityDomain)) { return this; } else { return new AuthenticationConfiguration(this, SET_ACCESS_CTXT, securityDomain != null ? getContext() : null, SET_FWD_AUTH_NAME_DOMAIN, securityDomain); } } /** * Create a new configuration which is the same as this configuration, but which forwards the authentication * credentials from the current identity of the given security domain. * * @param securityDomain the security domain * @return the new configuration */ public AuthenticationConfiguration useForwardedAuthenticationCredentials(SecurityDomain securityDomain) { if (Objects.equals(authenticationCredentialsForwardSecurityDomain, securityDomain)) { return this; } else { return new AuthenticationConfiguration(this, SET_ACCESS_CTXT, securityDomain != null ? getContext() : null, SET_FWD_AUTH_CRED_DOMAIN, securityDomain); } } /** * Create a new configuration which is the same as this configuration, but which forwards the authorization name * from the current identity of the given security domain. * * @param securityDomain the security domain * @return the new configuration */ public AuthenticationConfiguration useForwardedAuthorizationIdentity(SecurityDomain securityDomain) { if (Objects.equals(authorizationNameForwardSecurityDomain, securityDomain)) { return this; } else { return new AuthenticationConfiguration(this, SET_ACCESS_CTXT, securityDomain != null ? getContext() : null, SET_FWD_AUTHZ_NAME_DOMAIN, securityDomain); } } // Providers /** * Use the given security provider supplier to locate security implementations. * * @param providerSupplier the provider supplier * @return the new configuration */ public AuthenticationConfiguration useProviders(Supplier providerSupplier) { if (Objects.equals(this.providerSupplier, providerSupplier)) { return this; } return new AuthenticationConfiguration(this, SET_PROVIDER_SUPPLIER, providerSupplier); } /** * Use the default provider discovery behaviour of combining service loader discovered providers with the system default * security providers when locating security implementations. * * @return the new configuration */ public AuthenticationConfiguration useDefaultProviders() { return useProviders(DEFAULT_PROVIDER_SUPPLIER); } /** * Use security providers from the given class loader. * * @param classLoader the class loader to search for security providers * @return the new configuration */ public AuthenticationConfiguration useProvidersFromClassLoader(ClassLoader classLoader) { return useProviders(new ProviderServiceLoaderSupplier(classLoader)); } // SASL Mechanisms /** * Use a pre-existing {@link SaslClientFactory} instead of discovery. * * @param saslClientFactory the pre-existing {@link SaslClientFactory} to use. * @return the new configuration. */ public AuthenticationConfiguration useSaslClientFactory(final SaslClientFactory saslClientFactory) { return useSaslClientFactory(() -> saslClientFactory); } /** * Use the given sasl client factory supplier to obtain the {@link SaslClientFactory} to use. * * @param saslClientFactory the sasl client factory supplier to use. * @return the new configuration. */ public AuthenticationConfiguration useSaslClientFactory(final Supplier saslClientFactory) { return new AuthenticationConfiguration(this, SET_SASL_FAC_SUP, saslClientFactory); } /** * Use provider based discovery to load available {@link SaslClientFactory} implementations. * * @return the new configuration. */ public AuthenticationConfiguration useSaslClientFactoryFromProviders() { return new AuthenticationConfiguration(this, SET_SASL_FAC_SUP, null); } // SASL Configuration /** * Create a new configuration which is the same as this configuration, but which sets the properties that will be passed to * the {@code SaslClientFactory} when the mechanism is created. * * Existing properties defined on this authentication context will be retained unless overridden by new properties, any * properties resulting with a value of {@code null} will be removed. * * @param mechanismProperties the properties to be passed to the {@code SaslClientFactory} to create the mechanism. * @return the new configuration. * @deprecated use {@link #useSaslMechanismProperties(Map)} */ @Deprecated public AuthenticationConfiguration useMechanismProperties(Map mechanismProperties) { return useSaslMechanismProperties(mechanismProperties); } /** * Create a new configuration which is the same as this configuration, but which sets the properties that will be passed to * the {@code SaslClientFactory} when the mechanism is created. * * Existing properties defined on this authentication context will be retained unless overridden by new properties, any * properties resulting with a value of {@code null} will be removed. * * @param mechanismProperties the properties to be passed to the {@code SaslClientFactory} to create the mechanism. * @return the new configuration. */ public AuthenticationConfiguration useSaslMechanismProperties(Map mechanismProperties) { return useSaslMechanismProperties(mechanismProperties, false); } /** * Create a new configuration which is the same as this configuration, but which sets the properties that will be passed to * the {@code SaslClientFactory} when the mechanism is created. * * If exclusive the existing properties will be discarded and replaced with the new properties otherwise existing properties * defined on this authentication context will be retained unless overridden by new properties, any properties resulting * with a value of {@code null} will be removed. * * @param mechanismProperties the properties to be passed to the {@code SaslClientFactory} to create the mechanism. * @param exclusive should the provided properties be used exclusively or merged with the existing properties? * @return the new configuration. * @deprecated use {@link #useSaslMechanismProperties(Map, boolean)} */ @Deprecated public AuthenticationConfiguration useMechanismProperties(Map mechanismProperties, boolean exclusive) { return useSaslMechanismProperties(mechanismProperties, exclusive); } /** * Create a new configuration which is the same as this configuration, but which sets the properties that will be passed to * the {@code SaslClientFactory} when the mechanism is created. * * If exclusive the existing properties will be discarded and replaced with the new properties otherwise existing properties * defined on this authentication context will be retained unless overridden by new properties, any properties resulting * with a value of {@code null} will be removed. * * @param mechanismProperties the properties to be passed to the {@code SaslClientFactory} to create the mechanism. * @param exclusive should the provided properties be used exclusively or merged with the existing properties? * @return the new configuration. */ public AuthenticationConfiguration useSaslMechanismProperties(Map mechanismProperties, boolean exclusive) { if (!exclusive && (mechanismProperties == null || mechanismProperties.isEmpty())) return this; final HashMap newMap = exclusive ? new HashMap<>() : new HashMap<>(this.saslMechanismProperties); newMap.putAll(mechanismProperties); newMap.values().removeIf(Objects::isNull); return new AuthenticationConfiguration(this, SET_SASL_MECH_PROPS, optimizeMap(newMap)); } /** * Create a new configuration which is the same as this configuration, but which sets the properties that can be used by * WebServices client. * * @param webServicesProperties the properties that can be used by WS client. * @return the new configuration. */ public AuthenticationConfiguration useWebServicesProperties(Map webServicesProperties) { final HashMap newMap = new HashMap<>(); newMap.putAll(webServicesProperties); newMap.values().removeIf(Objects::isNull); return new AuthenticationConfiguration(this, SET_WEBSERVICES_PROPS, optimizeMap(newMap)); } private static Map optimizeMap(Map orig) { if (orig.isEmpty()) return Collections.emptyMap(); if (orig.size() == 1) { final Map.Entry entry = orig.entrySet().iterator().next(); return Collections.singletonMap(entry.getKey(), entry.getValue()); } return orig; } /** * Create a new configuration which is the same as this configuration, but which uses the given kerberos security * factory to acquire the GSS credential required for authentication. * * @param kerberosSecurityFactory a reference to the kerberos security factory to be use * @return the new configuration */ @Deprecated public AuthenticationConfiguration useKerberosSecurityFactory(SecurityFactory kerberosSecurityFactory) { CredentialSource cs = getCredentialSource(); if (kerberosSecurityFactory != null) { return cs != null ? new AuthenticationConfiguration(this, SET_CRED_SOURCE, cs.with(new FactoryCredentialSource(kerberosSecurityFactory))) : new AuthenticationConfiguration(this, SET_CRED_SOURCE, new FactoryCredentialSource(kerberosSecurityFactory)); } return this; } /** * Set the SASL mechanism selector for this authentication configuration. * * @param saslMechanismSelector the SASL mechanism selector, or {@code null} to clear the current selector * @return the new configuration */ public AuthenticationConfiguration setSaslMechanismSelector(SaslMechanismSelector saslMechanismSelector) { if (Objects.equals(this.saslMechanismSelector, saslMechanismSelector)) { return this; } return new AuthenticationConfiguration(this, SET_SASL_SELECTOR, saslMechanismSelector); } // other /** * Create a new configuration which is the same as this configuration, but uses the given realm for authentication. * * @param realm the realm to use, or {@code null} to accept the default realm always * @return the new configuration */ public AuthenticationConfiguration useRealm(String realm) { if (Objects.equals(realm, this.setRealm)) { return this; } else { return new AuthenticationConfiguration(this, SET_REALM, realm); } } /** * Create a new configuration which is the same as this configuration, but which uses the given {@link BearerTokenCredential} to authenticate. * * @param credential the bearer token credential to use * @return the new configuration */ public AuthenticationConfiguration useBearerTokenCredential(BearerTokenCredential credential) { return credential == null ? this : useCredentials(getCredentialSource().with(IdentityCredentials.NONE.withCredential(credential))); } /** * Create a new configuration which is the same as this configuration, but which captures the caller's access * control context to be used in authentication decisions. * * @return the new configuration */ public AuthenticationConfiguration withCapturedAccessControlContext() { return new AuthenticationConfiguration(this, SET_ACCESS_CTXT, getContext()); } // merging /** * Create a new configuration which is the same as this configuration, but which adds or replaces every item in the * {@code other} configuration with that item, overwriting any corresponding such item in this configuration. * * @param other the other authentication configuration * @return the merged authentication configuration */ public AuthenticationConfiguration with(AuthenticationConfiguration other) { return new AuthenticationConfiguration(this, other); } /** * Create a new configuration which is the same as this configuration, but which attempts to authorize to the * principal from the current identity from the configured security domain. * * @return the new configuration */ public AuthenticationConfiguration captureAuthorizationIdentity() { if (authorizationNameForwardSecurityDomain == null) { return this; } return new AuthenticationConfiguration(this, SET_ACCESS_CTXT, null, SET_FWD_AUTHZ_NAME_DOMAIN, null, SET_AUTHZ_PRINCIPAL, authorizationNameForwardSecurityDomain.getCurrentSecurityIdentity().getPrincipal()); } // client methods CallbackHandler getUserCallbackHandler() { return userCallbackHandler; } EnumSet getUserCallbackKinds() { return userCallbackKinds; } private SaslClientFactory getSaslClientFactory() { if (saslClientFactory == null) { synchronized (this) { if (saslClientFactory == null) { saslClientFactory = getSaslClientFactory(getProviderSupplier()); } } } return saslClientFactory; } SaslClient createSaslClient(URI uri, Collection serverMechanisms, UnaryOperator factoryOperator, SSLSession sslSession) throws SaslException { SaslClientFactory saslClientFactory = factoryOperator.apply(getSaslClientFactory()); final SaslMechanismSelector selector = this.saslMechanismSelector; serverMechanisms = (selector == null ? SaslMechanismSelector.DEFAULT : selector).apply(serverMechanisms, sslSession); if (serverMechanisms.isEmpty()) { return null; } final Principal authorizationPrincipal = getAuthorizationPrincipal(); final Predicate filter; final String authzName; if (authorizationPrincipal == null) { filter = this::saslSupportedByConfiguration; authzName = null; } else if (authorizationPrincipal instanceof NamePrincipal) { filter = this::saslSupportedByConfiguration; authzName = authorizationPrincipal.getName(); } else if (authorizationPrincipal instanceof AnonymousPrincipal) { filter = ((Predicate) this::saslSupportedByConfiguration).and("ANONYMOUS"::equals); authzName = null; } else { return null; } Map mechanismProperties = this.saslMechanismProperties; if (! mechanismProperties.isEmpty()) { mechanismProperties = new HashMap<>(mechanismProperties); // special handling for JBOSS-LOCAL-USER quiet auth... only pass it through if we have a user callback if (! userCallbackKinds.contains(CallbackKind.PRINCIPAL) && principal != AnonymousPrincipal.getInstance()) { mechanismProperties.remove(JBOSS_LOCAL_USER_QUIET_AUTH); mechanismProperties.remove(JBOSS_LOCAL_USER_LEGACY_QUIET_AUTH); } if (! mechanismProperties.isEmpty()) { saslClientFactory = new PropertiesSaslClientFactory(saslClientFactory, mechanismProperties); } } String host = uri.getHost(); if (host != null) { saslClientFactory = new ServerNameSaslClientFactory(saslClientFactory, host); } String protocol = getSaslProtocol(); if (protocol != null) { saslClientFactory = new ProtocolSaslClientFactory(saslClientFactory, protocol); } if (sslSession != null) { saslClientFactory = new SSLSaslClientFactory(() -> SSLConnection.forSession(sslSession, true), saslClientFactory); } saslClientFactory = new LocalPrincipalSaslClientFactory(new FilterMechanismSaslClientFactory(saslClientFactory, filter)); final SaslClientFactory finalSaslClientFactory = saslClientFactory; saslClientFactory = WILDFLY_ELYTRON_CAPTURE_ACCESS_CONTROL_CONTEXT_PROPERTY ? doPrivileged((PrivilegedAction) () -> new PrivilegedSaslClientFactory(finalSaslClientFactory), capturedAccessContext) : new PrivilegedSaslClientFactory(finalSaslClientFactory); SaslClient saslClient = saslClientFactory.createSaslClient(serverMechanisms.toArray(NO_STRINGS), authzName, uri.getScheme(), uri.getHost(), Collections.emptyMap(), createCallbackHandler()); if (log.isTraceEnabled()) { log.tracef("Created SaslClient [%s] for mechanisms %s", saslClient, Arrays2.objectToString(serverMechanisms)); } return saslClient; } CallbackHandler createCallbackHandler() { return new ClientCallbackHandler(this); } // equality /** * Determine whether this configuration is equal to another object. Two configurations are equal if they * apply the same items. * * @param obj the other object * @return {@code true} if they are equal, {@code false} otherwise */ public boolean equals(final Object obj) { return obj instanceof AuthenticationConfiguration && equals((AuthenticationConfiguration) obj); } /** * Determine whether this configuration is equal to another object. Two configurations are equal if they * apply the same items. * * @param other the other object * @return {@code true} if they are equal, {@code false} otherwise */ public boolean equals(final AuthenticationConfiguration other) { return hashCode() == other.hashCode() && Objects.equals(principal, other.principal) && Objects.equals(setHost, other.setHost) && Objects.equals(setProtocol, other.setProtocol) && Objects.equals(setRealm, other.setRealm) && Objects.equals(setAuthzPrincipal, other.setAuthzPrincipal) && Objects.equals(authenticationNameForwardSecurityDomain, other.authenticationNameForwardSecurityDomain) && Objects.equals(authenticationCredentialsForwardSecurityDomain, other.authenticationCredentialsForwardSecurityDomain) && Objects.equals(authorizationNameForwardSecurityDomain, other.authorizationNameForwardSecurityDomain) && Objects.equals(userCallbackHandler, other.userCallbackHandler) && Objects.equals(userCallbackKinds, other.userCallbackKinds) && Objects.equals(credentialSource, other.credentialSource) && this.setPort == other.setPort && Objects.equals(providerSupplier, other.providerSupplier) && Objects.equals(keyManagerFactory, other.keyManagerFactory) && Objects.equals(saslMechanismSelector, other.saslMechanismSelector) && Objects.equals(principalRewriter, other.principalRewriter) && Objects.equals(saslClientFactorySupplier, other.saslClientFactorySupplier) && Objects.equals(parameterSpecs, other.parameterSpecs) && Objects.equals(trustManagerFactory, other.trustManagerFactory) && Objects.equals(saslMechanismProperties, other.saslMechanismProperties) && Objects.equals(saslProtocol, other.saslProtocol) && Objects.equals(webServicesProperties, other.webServicesProperties); } /** * Get the hash code of this authentication configuration. * * @return the hash code of this authentication configuration */ public int hashCode() { int hashCode = this.hashCode; if (hashCode == 0) { hashCode = Objects.hash( principal, setHost, setProtocol, setRealm, setAuthzPrincipal, authenticationNameForwardSecurityDomain, authenticationCredentialsForwardSecurityDomain, authorizationNameForwardSecurityDomain, userCallbackHandler, credentialSource, providerSupplier, keyManagerFactory, saslMechanismSelector, principalRewriter, saslClientFactorySupplier, parameterSpecs, trustManagerFactory, saslMechanismProperties, saslProtocol, webServicesProperties) * 19 + setPort; if (hashCode == 0) { hashCode = 1; } this.hashCode = hashCode; } return hashCode; } // String Representation @Override public String toString() { String toString = this.toString; if (toString == null) { StringBuilder b = new StringBuilder(64); b.append("AuthenticationConfiguration:"); b.append("principal=").append(principal).append(','); if (setAuthzPrincipal != null) b.append("authorization-id=").append(setAuthzPrincipal).append(','); if (setHost != null) b.append("set-host=").append(setHost).append(','); if (setProtocol != null) b.append("set-protocol=").append(setProtocol).append(','); if (saslProtocol != null) b.append("sasl-protocol-name=").append(saslProtocol).append(','); if (setPort != -1) b.append("set-port=").append(setPort).append(','); if (setRealm != null) b.append("set-realm=").append(setRealm).append(','); if (authenticationNameForwardSecurityDomain != null) b.append("forwarding-authentication-name,"); if (authenticationCredentialsForwardSecurityDomain != null) b.append("forwarding-authentication-credentials,"); if (authorizationNameForwardSecurityDomain != null) b.append("forwarding-authorization-name,"); if (userCallbackHandler != null) b.append("user-callback-handler=").append(userCallbackHandler).append(','); if (! userCallbackKinds.isEmpty()) b.append("user-callback-kinds=").append(userCallbackKinds).append(','); if (credentialSource != null && credentialSource != CredentialSource.NONE && credentialSource != IdentityCredentials.NONE) b.append("credentials-present,"); if (providerSupplier != null) b.append("providers-supplier=").append(providerSupplier).append(','); if (keyManagerFactory != null) b.append("key-manager-factory=").append(keyManagerFactory).append(','); if (saslMechanismSelector != null) b.append("sasl-mechanism-selector=").append(saslMechanismSelector).append(','); if (principalRewriter != null) b.append("principal-rewriter=").append(principalRewriter).append(','); if (saslClientFactorySupplier != null) b.append("sasl-client-factory-supplier=").append(saslClientFactorySupplier).append(','); if (! parameterSpecs.isEmpty()) b.append("parameter-specifications=").append(parameterSpecs).append(','); if (trustManagerFactory != null) b.append("trust-manager-factory=").append(trustManagerFactory).append(','); if (! saslMechanismProperties.isEmpty()) b.append("mechanism-properties=").append(saslMechanismProperties).append(','); if (webServicesProperties != null && ! webServicesProperties.isEmpty()) b.append("webservices-properties=").append(webServicesProperties).append(','); b.setLength(b.length() - 1); this.toString = b.toString(); return this.toString; } return toString; } //user callback sanitation private void sanitazeOnMutation(final int what) { switch (what) { case SET_PRINCIPAL: // CallbackKind.PRINCIPAL if (this.principal != null && ! this.principal.equals(AnonymousPrincipal.getInstance())) { this.userCallbackKinds.remove(CallbackKind.PRINCIPAL); } break; case SET_CRED_SOURCE: // CallbackKind.CREDENTIAL if (this.credentialSource != null && ! this.credentialSource.equals(IdentityCredentials.NONE)) { this.userCallbackKinds.remove(CallbackKind.CREDENTIAL); } break; case SET_REALM: // CallbackKind.REALM this.userCallbackKinds.remove(CallbackKind.REALM); break; case SET_PARAM_SPECS: // CallbackKind.PARAMETERS this.userCallbackKinds.remove(CallbackKind.PARAMETERS); break; case SET_KEY_MGR_FAC: // CallbackKind.PEER_CREDENTIAL this.userCallbackKinds.remove(CallbackKind.PEER_CREDENTIAL); break; case SET_USER_CB_KINDS: // SANITAZE on above content if (this.principal != null) { sanitazeOnMutation(SET_PRINCIPAL); } if (this.credentialSource != null) { sanitazeOnMutation(SET_CRED_SOURCE); } if (this.setRealm != null) { sanitazeOnMutation(SET_REALM); } if (this.parameterSpecs != null) { sanitazeOnMutation(SET_PARAM_SPECS); } if (this.keyManagerFactory != null) { sanitazeOnMutation(SET_KEY_MGR_FAC); } break; } } AccessControlContext getCapturedContext() { return capturedAccessContext; } // delegates for equality tests // interfaces static class ClientCallbackHandler implements CallbackHandler { private final AuthenticationConfiguration config; private final CallbackHandler userCallbackHandler; private List trustedAuthorities; private SSLConnection sslConnection; ClientCallbackHandler(final AuthenticationConfiguration config) { this.config = config; userCallbackHandler = config.getUserCallbackHandler(); } @SuppressWarnings("UnnecessaryContinue") public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException { final AuthenticationConfiguration config = this.config; final Predicate callbackIntercept = config.callbackIntercept; final ArrayList userCallbacks = new ArrayList<>(callbacks.length); for (final Callback callback : callbacks) { if (callbackIntercept != null && callbackIntercept.test(callback)) { continue; } else if (callback instanceof NameCallback) { if (config.getUserCallbackKinds().contains(CallbackKind.PRINCIPAL)) { userCallbacks.add(callback); continue; } final NameCallback nameCallback = (NameCallback) callback; // populate with our authentication name final Principal principal = config.getPrincipal(); if (principal instanceof AnonymousPrincipal) { final String defaultName = nameCallback.getDefaultName(); if (defaultName != null) { nameCallback.setName(defaultName); } // otherwise set nothing; the mech can decide if that's OK or not continue; } else { nameCallback.setName(config.doRewriteUser(principal).getName()); continue; } } else if (callback instanceof PasswordCallback) { if (config.getUserCallbackKinds().contains(CallbackKind.CREDENTIAL)) { userCallbacks.add(callback); continue; } final PasswordCallback passwordCallback = (PasswordCallback) callback; final CredentialSource credentials = config.getCredentialSource(); if (credentials != null) { final TwoWayPassword password = credentials.applyToCredential(PasswordCredential.class, ClearPassword.ALGORITHM_CLEAR, c -> c.getPassword(TwoWayPassword.class)); if (password instanceof ClearPassword) { // shortcut passwordCallback.setPassword(((ClearPassword) password).getPassword()); continue; } else if (password != null) try { PasswordFactory passwordFactory = PasswordFactory.getInstance(password.getAlgorithm()); ClearPasswordSpec clearPasswordSpec = passwordFactory.getKeySpec(passwordFactory.translate(password), ClearPasswordSpec.class); passwordCallback.setPassword(clearPasswordSpec.getEncodedPassword()); continue; } catch (GeneralSecurityException e) { // not supported CallbackUtil.unsupported(passwordCallback); continue; } else { // supported but no credentials continue; } } else { // supported but no credentials continue; } } else if (callback instanceof PasswordResetCallback) { if (config.getUserCallbackKinds().contains(CallbackKind.CREDENTIAL_RESET)) { userCallbacks.add(callback); continue; } // not supported CallbackUtil.unsupported(callback); continue; } else if (callback instanceof CredentialCallback) { if (config.getUserCallbackKinds().contains(CallbackKind.CREDENTIAL)) { userCallbacks.add(callback); continue; } final CredentialCallback credentialCallback = (CredentialCallback) callback; // special handling for X.509 when a key manager factory is set final SecurityFactory keyManagerFactory = config.getX509KeyManagerFactory(); if (keyManagerFactory != null) { final String allowedAlgorithm = credentialCallback.getAlgorithm(); if (allowedAlgorithm != null && credentialCallback.isCredentialTypeSupported(X509CertificateChainPrivateCredential.class, allowedAlgorithm)) { final X509KeyManager keyManager; try { keyManager = keyManagerFactory.create(); } catch (GeneralSecurityException e) { throw log.unableToCreateKeyManager(e); } Principal[] acceptableIssuers; if (trustedAuthorities != null) { List issuers = new ArrayList(); for (TrustedAuthority trustedAuthority : trustedAuthorities) { if (trustedAuthority instanceof TrustedAuthority.CertificateTrustedAuthority) { final X509Certificate authorityCertificate = ((TrustedAuthority.CertificateTrustedAuthority) trustedAuthority).getIdentifier(); issuers.add(authorityCertificate.getSubjectX500Principal()); } else if (trustedAuthority instanceof TrustedAuthority.NameTrustedAuthority) { final String authorityName = ((TrustedAuthority.NameTrustedAuthority) trustedAuthority).getIdentifier(); issuers.add(new X500Principal(authorityName)); } } acceptableIssuers = issuers.toArray(NO_PRINCIPALS); } else { acceptableIssuers = null; } final String alias = keyManager.chooseClientAlias(new String[] { allowedAlgorithm }, acceptableIssuers, null); if (alias != null) { final X509Certificate[] certificateChain = keyManager.getCertificateChain(alias); final PrivateKey privateKey = keyManager.getPrivateKey(alias); credentialCallback.setCredential(new X509CertificateChainPrivateCredential(privateKey, certificateChain)); continue; } // otherwise fall out to normal handling } } // normal handling final Credential credential = config.getCredentialSource().getCredential(credentialCallback.getCredentialType(), credentialCallback.getAlgorithm(), credentialCallback.getParameterSpec()); if (credential != null && credentialCallback.isCredentialSupported(credential)) { credentialCallback.setCredential(credential); continue; } else { // supported but no credentials continue; } } else if (callback instanceof RealmChoiceCallback) { if (config.getUserCallbackKinds().contains(CallbackKind.REALM)) { userCallbacks.add(callback); continue; } final RealmChoiceCallback realmChoiceCallback = (RealmChoiceCallback) callback; // find our realm final String realm = config.setRealm; if (realm == null) { realmChoiceCallback.setSelectedIndex(realmChoiceCallback.getDefaultChoice()); continue; } else { String[] choices = realmChoiceCallback.getChoices(); for (int i = 0; i < choices.length; i++) { if (realm.equals(choices[i])) { realmChoiceCallback.setSelectedIndex(i); break; } } // no choice matches, so just fall out and choose nothing continue; } } else if (callback instanceof RealmCallback) { if (config.getUserCallbackKinds().contains(CallbackKind.REALM)) { userCallbacks.add(callback); continue; } RealmCallback realmCallback = (RealmCallback) callback; final String realm = config.setRealm; realmCallback.setText(realm != null ? realm : realmCallback.getDefaultText()); continue; } else if (callback instanceof ParameterCallback) { if (config.getUserCallbackKinds().contains(CallbackKind.PARAMETERS)) { userCallbacks.add(callback); continue; } ParameterCallback parameterCallback = (ParameterCallback) callback; if (parameterCallback.getParameterSpec() == null) { for (AlgorithmParameterSpec parameterSpec : config.parameterSpecs) { if (parameterCallback.isParameterSupported(parameterSpec)) { parameterCallback.setParameterSpec(parameterSpec); break; // inner loop break } } } continue; } else if (callback instanceof SSLCallback) { if (config.getUserCallbackKinds().contains(CallbackKind.SSL)) { userCallbacks.add(callback); continue; } SSLCallback sslCallback = (SSLCallback) callback; this.sslConnection = sslCallback.getSslConnection(); continue; } else if (callback instanceof ChannelBindingCallback) { if (config.getUserCallbackKinds().contains(CallbackKind.CHANNEL_BINDING)) { userCallbacks.add(callback); continue; } final SSLConnection sslConnection = this.sslConnection; if (sslConnection != null) { sslConnection.handleChannelBindingCallback((ChannelBindingCallback) callback); } continue; } else if (callback instanceof ChoiceCallback) { // Must come AFTER RealmChoiceCallback if (config.getUserCallbackKinds().contains(CallbackKind.CHOICE)) { userCallbacks.add(callback); continue; } final ChoiceCallback choiceCallback = (ChoiceCallback) callback; choiceCallback.setSelectedIndex(choiceCallback.getDefaultChoice()); continue; } else if (callback instanceof TrustedAuthoritiesCallback) { if (config.getUserCallbackKinds().contains(CallbackKind.SERVER_TRUSTED_AUTHORITIES)) { userCallbacks.add(callback); continue; } final TrustedAuthoritiesCallback trustedAuthoritiesCallback = (TrustedAuthoritiesCallback) callback; final List trustedAuthoritiesHolder = trustedAuthoritiesCallback.getTrustedAuthorities(); if (trustedAuthoritiesHolder != null) { if (trustedAuthorities == null) { trustedAuthorities = new ArrayList<>(trustedAuthoritiesHolder); } else { final List authorities = new ArrayList<>(trustedAuthoritiesHolder); authorities.removeIf(trustedAuthorities::contains); trustedAuthorities.addAll(authorities); } } continue; } else if (callback instanceof EvidenceVerifyCallback) { if (config.getUserCallbackKinds().contains(CallbackKind.PEER_CREDENTIAL)) { userCallbacks.add(callback); continue; } final EvidenceVerifyCallback evidenceVerifyCallback = (EvidenceVerifyCallback) callback; // special handling for X.509 final SecurityFactory trustManagerFactory = config.getX509TrustManagerFactory(); if (trustManagerFactory != null) { final X509PeerCertificateChainEvidence peerCertificateChainEvidence = evidenceVerifyCallback.getEvidence(X509PeerCertificateChainEvidence.class); if (peerCertificateChainEvidence != null) { X509TrustManager trustManager; try { trustManager = trustManagerFactory.create(); } catch (GeneralSecurityException e) { throw log.unableToCreateTrustManager(e); } try { trustManager.checkServerTrusted(peerCertificateChainEvidence.getPeerCertificateChain(), peerCertificateChainEvidence.getAlgorithm()); evidenceVerifyCallback.setVerified(true); } catch (CertificateException e) { } continue; } } continue; } else if (callback instanceof TextOutputCallback) { if (config.getUserCallbackKinds().contains(CallbackKind.GENERAL_OUTPUT)) { userCallbacks.add(callback); continue; } // ignore continue; } else if (callback instanceof TextInputCallback) { // must come after RealmCallback if (config.getUserCallbackKinds().contains(CallbackKind.GENERAL_INPUT)) { userCallbacks.add(callback); continue; } // always choose the default final TextInputCallback inputCallback = (TextInputCallback) callback; final String text = inputCallback.getText(); if (text == null) { final String defaultText = inputCallback.getDefaultText(); if (defaultText != null) { inputCallback.setText(defaultText); } else { CallbackUtil.unsupported(callback); continue; } } continue; } else if (userCallbackHandler != null) { userCallbacks.add(callback); } else { CallbackUtil.unsupported(callback); continue; } } if (! userCallbacks.isEmpty()) { // pass on to the user callback handler assert userCallbackHandler != null; // otherwise userCallbacks would be empty final Callback[] userCallbackArray = userCallbacks.toArray(NO_CALLBACKS); userCallbackHandler.handle(userCallbackArray); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy