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

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

There is a newer version: 2.4.1.Final
Show 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); return this.toString = b.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 - 2024 Weber Informatics LLC | Privacy Policy