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

org.wildfly.security.auth.server.ServerAuthenticationContext Maven / Gradle / Ivy

Go to download

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

There is a newer version: 34.0.0.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.server;

import static org.wildfly.common.Assert.checkNotNullParam;
import static org.wildfly.security.auth.server._private.ElytronMessages.log;
import static org.wildfly.security.authz.RoleDecoder.KEY_SOURCE_ADDRESS;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Predicate;

import javax.net.ssl.SSLPeerUnverifiedException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.sasl.AuthorizeCallback;
import javax.security.sasl.RealmCallback;

import org.wildfly.common.Assert;
import org.wildfly.security.auth.SupportLevel;
import org.wildfly.security.auth.callback.AnonymousAuthorizationCallback;
import org.wildfly.security.auth.callback.AuthenticationCompleteCallback;
import org.wildfly.security.auth.callback.AuthenticationConfigurationCallback;
import org.wildfly.security.auth.callback.AvailableRealmsCallback;
import org.wildfly.security.auth.callback.CachedIdentityAuthorizeCallback;
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.CredentialUpdateCallback;
import org.wildfly.security.auth.callback.EvidenceDecodePrincipalCallback;
import org.wildfly.security.auth.callback.EvidenceVerifyCallback;
import org.wildfly.security.auth.callback.ExclusiveNameCallback;
import org.wildfly.security.auth.callback.FastUnsupportedCallbackException;
import org.wildfly.security.auth.callback.PrincipalAuthorizeCallback;
import org.wildfly.security.auth.callback.MechanismInformationCallback;
import org.wildfly.security.auth.callback.IdentityCredentialCallback;
import org.wildfly.security.auth.callback.PeerPrincipalCallback;
import org.wildfly.security.auth.callback.RequestInformationCallback;
import org.wildfly.security.auth.callback.SSLCallback;
import org.wildfly.security.auth.callback.SecurityIdentityCallback;
import org.wildfly.security.auth.callback.ServerCredentialCallback;
import org.wildfly.security.auth.callback.SocketAddressCallback;
import org.wildfly.security.auth.permission.LoginPermission;
import org.wildfly.security.auth.permission.RunAsPrincipalPermission;
import org.wildfly.security.auth.principal.AnonymousPrincipal;
import org.wildfly.security.auth.principal.NamePrincipal;
import org.wildfly.security.auth.server.event.RealmFailedAuthenticationEvent;
import org.wildfly.security.auth.server.event.RealmIdentityFailedAuthorizationEvent;
import org.wildfly.security.auth.server.event.RealmIdentitySuccessfulAuthorizationEvent;
import org.wildfly.security.auth.server.event.RealmSuccessfulAuthenticationEvent;
import org.wildfly.security.auth.server.event.SecurityAuthenticationFailedEvent;
import org.wildfly.security.auth.server.event.SecurityAuthenticationSuccessfulEvent;
import org.wildfly.security.auth.server.event.SecurityRealmUnavailableEvent;
import org.wildfly.security.authz.AggregateAttributes;
import org.wildfly.security.authz.Attributes;
import org.wildfly.security.authz.AuthorizationIdentity;
import org.wildfly.security.authz.MapAttributes;
import org.wildfly.security.credential.Credential;
import org.wildfly.security.credential.PasswordCredential;
import org.wildfly.security.credential.source.CredentialSource;
import org.wildfly.security.evidence.AlgorithmEvidence;
import org.wildfly.security.evidence.Evidence;
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.DigestPassword;
import org.wildfly.security.password.spec.ClearPasswordSpec;
import org.wildfly.security.ssl.SSLConnection;
import org.wildfly.security.x500.X500;
import org.wildfly.security.auth.server._private.ElytronMessages;

/**
 * Server-side authentication context.  Instances of this class are used to perform all authentication and re-authorization
 * operations that involve the usage of an identity in a {@linkplain SecurityDomain security domain}.
 * 

* There are various effective states, described as follows: *

    *
  • The inactive state.
  • *
  • * The unassigned states: *
      *
    • Initial
    • *
    • Realm-assigned
    • *
    *
  • *
  • The assigned state
  • *
  • * The authorized states: *
      *
    • Anonymous-authorized
    • *
    • Authorized
    • *
    • Authorized-authenticated
    • *
    *
  • *
  • * The terminal states: *
      *
    • Complete
    • *
    • Failed
    • *
    *
  • *
* *

* When an instance of this class is first constructed, it is in the inactive state. In this state, the context retains * a captured {@linkplain SecurityIdentity identity} and contains a reference to a * {@linkplain MechanismConfigurationSelector}. The captured identity may be used for various * context-sensitive authorization decisions. Additional mechanism information can be supplied to this state so that when * authentication begins an appropriate {@linkplain MechanismConfiguration} can be selected. *

* Once authentication commences the state will automatically transition to the initial state. In this state, the * context retains an captured {@linkplain SecurityIdentity identity} and a {@linkplain MechanismConfiguration mechanism configuration} * which was resolved from the information supplied to the inactive state. The captured identity may be * used for various context-sensitive authorization decisions. The mechanism configuration is used to associate * an authentication mechanism-specific configuration, including rewriters, {@linkplain MechanismRealmConfiguration mechanism realms}, * server credential factories, and more. *

* When an authentication mechanism is "realm-aware" (that is, it has a notion of realms that is specific to that particular * authentication mechanism, e.g. the DIGEST-MD5 SASL mechanism), it * is necessary for the mechanism to relay the realm selection. This is done by way of the {@link #setMechanismRealmName(String) setMechanismRealmName()} * method. Calling this method in the initial state causes a transition to the realm-assigned state, * in which the method may be reinvoked idempotently as long as it is called with the same name (calling the method with * a different name will result in an exception). *

* The realm-assigned state is nearly identical to the initial state, except that from this state, the * mechanism realm-specific configuration is applied to all subsequent operation. *

* From these unassigned states, several possible actions may be taken, depending on the necessary progression * of the authentication: *

    *
  • * A name may be assigned by way of the {@link #setAuthenticationName(String)} method. The name is * {@linkplain NameRewriter rewritten} and {@linkplain RealmMapper mapped to a realm} according to the * domain settings, the mechanism configuration, and/or the mechanism realm configuration. The * {@linkplain SecurityRealm realm} that is the resultant target of the mapping is queried for a * {@linkplain RealmIdentity realm identity}. The realm identity may or may not be * existent; this status will affect the outcome of certain operations in subsequent states (as described below). * After the realm identity is selected, any final rewrite operations which are configured are applied, * and the resultant name is transformed into a {@link NamePrincipal}, and associated as the * {@linkplain #getAuthenticationPrincipal() authentication principal} which may subsequently be queried. *
  • *
  • * A principal may be assigned using the {@link #setAuthenticationPrincipal(Principal)} method. The * principal is {@linkplain PrincipalDecoder decoded} according to the configuration of the security domain (see * the method documentation for input requirements and failure conditions). Once a name is decoded from the * principal, it is assigned as described above. *
  • *
  • * A unit of {@linkplain Evidence evidence} may be verified. This is mostly described below in the * context of the assigned state, but with the important distinction the evidence is first examined * to locate the corresponding evidence, in the following steps: *
      *
    • * Firstly, the evidence is examined to determine whether it {@linkplain Evidence#getPrincipal() contains a principal}. * If so, the principal name is first established using the procedure described above, and then the normal * evidence verification procedure described below commences. *
    • *
    • * Secondly, the evidence is socialized to each realm in turn, to see if a realm can recognize * and {@linkplain SecurityRealm#getRealmIdentity(Principal) locate} an identity based on * the evidence. If so, the realm identity is {@linkplain RealmIdentity#getRealmIdentityPrincipal() queried} * for an authentication principal, which is then decoded and established as described above. Once this * is done successfully, the evidence verification procedure described below commences. *
    • *
    • Finally, if none of these steps succeeds, the verification fails and no state transition occurs.
    • *
    *
  • *
  • * An identity may be {@linkplain #importIdentity(SecurityIdentity) imported}. In this process, * a {@link SecurityIdentity} instance is examined to determine whether it can be used to complete an implicit * authentication operation which would yield an authorized identity. The {@code SecurityIdentity} may * be from the same domain or from a different one. *

    * If the identity being imported is from the same security domain as this context, then the identity * is implicitly authorized for usage, entering the authorized state described below. *

    * If the identity being imported is not from the same security domain, then the principal is extracted * from the identity and used to assign a realm identity in the same manner as {@link #setAuthenticationPrincipal(Principal)}. * The domain is then {@linkplain SecurityDomain.Builder#setTrustedSecurityDomainPredicate(Predicate) queried} * to determine whether the target identity's source domain is trusted. If so, a normal * authorization is carried out as described below for the assigned state, resulting in an * authorized-authenticated state. If not, then the realm of the realm identity is * compared against the realm of the identity being imported. If they are the same, the * identity is imported and a normal authorization is carried out as described below. *

  • *
  • * An anonymous authorization may be carried out by way of the {@link #authorizeAnonymous()} method. * If the {@linkplain SecurityDomain#getAnonymousSecurityIdentity() anonymous identity} has the * {@link LoginPermission} granted to it, the context will transition into the anonymous-authorized * state; otherwise no state transition occurs. *
  • *
  • * An external authorization may be carried out using the {@link #authorize()} method. The * captured identity (which may be anonymous) is queried for the presence of the * {@link LoginPermission}; if present, the context will transition into the authorized or * anonymous-authorized state (depending on whether the captured identity is anonymous); * otherwise no state transition occurs. *
  • *
  • * An external run-as authorization may be carried out using the {@link #authorize(String)} method. * First, the given name is rewritten in the same manner as the {@link #setAuthenticationName(String)} * method. Then, the captured identity (which may be anonymous) is queried for the presence of a * {@link RunAsPrincipalPermission} for the target name. If present, the authentication name is assigned * as described above, and the resultant realm identity is queried for {@link LoginPermission}. If present, * the context will transition to the authorized-authenticated state. If any step fails, no state transition * occurs. *
  • *
  • * The authentication may be failed by way of the {@link #fail()} method. This method will dispose * of all authentication resources and transition to the failed state. *
  • *
*

* In the name-assigned (or, for brevity, assigned) state, the following actions may be performed: *

    *
  • * A name or principal may be assigned as above, however the resultant decoded and rewritten name * and realm identity must be identical to the previously selected name and identity. *
  • *
  • * Evidence may be verified. The realm identity is queried directly and no state transitions * will occur. Evidence verification will fail if the evidence has an evidence principal which does * not result in the same realm identity as the current one after decoding and rewriting. *
  • *
  • * An authorization may be performed via the {@link #authorize()} method. If the selected realm identity * possesses the {@link LoginPermission}, then the context transitions to the authorized-authenticated state, * otherwise no state transition occurs. *
  • *
  • * A run-as authorization may be performed via the {@link #authorize(String)} method. * First, the given name is rewritten in the same manner as the {@link #setAuthenticationName(String)} method. * The current identity is then authorized as described above, and then the authorized identity * is tested for a {@link RunAsPrincipalPermission} for the rewritten target name. If authorized, * the context transitions to the authorized state for the realm identity corresponding to the * rewritten name; otherwise no state transition occurs. *
  • *
  • * The authentication may be failed by way of the {@link #fail()} method. This method will dispose * of all authentication resources and transition to the failed state. *
  • *
*

* There are three states related to authorization: the anonymous-authorized state, the authorized state, * and the authorized-authenticated state. In all three states, the following actions may be taken: *

    *
  • * As above, a name or principal may be assigned so long as it matches the existing identity. In particular, * for the anonymous-authorized state, all names are rejected, and only the {@linkplain AnonymousPrincipal anonymous principal} * is accepted. *
  • *
  • * An authorization may be performed via the {@link #authorize()} method. Since the identity is * always authorized, this is generally a no-op. *
  • *
  • * A run-as authorization may be performed via the {@link #authorize(String)} method. The given * name is rewritten as previously described, and then the authorized identity * is tested for a {@link RunAsPrincipalPermission} for the rewritten target name. If authorized, * the context transitions to the authorized state for the realm identity corresponding to the * rewritten name; otherwise no state transition occurs. *
  • *
  • * The authentication may be completed by way of the {@link #succeed()} method. This method will * dispose of all authentication resources and transition to the complete state. *
  • *
  • * The authentication may be failed by way of the {@link #fail()} method. This method will dispose * of all authentication resources and transition to the failed state. *
  • *
* The authorized-authenticated state has the additional capability of verifying credentials as described above for * the assigned state. *

* The complete state has only one capability: the retrieval of the final authorized identity by way * of the {@link #getAuthorizedIdentity()} method. *

* The failed state has no capabilities and retains no reference to any identities or objects used during * authentication. * * @author David M. Lloyd * @author Darran Lofthouse */ public final class ServerAuthenticationContext implements AutoCloseable { private final AtomicReference stateRef; ServerAuthenticationContext(final SecurityDomain domain, final MechanismConfigurationSelector mechanismConfigurationSelector) { this(domain.getCurrentSecurityIdentity(), mechanismConfigurationSelector); } ServerAuthenticationContext(final SecurityIdentity capturedIdentity, final MechanismConfigurationSelector mechanismConfigurationSelector) { stateRef = new AtomicReference<>(new InactiveState(capturedIdentity, mechanismConfigurationSelector, IdentityCredentials.NONE, IdentityCredentials.NONE, Attributes.EMPTY)); } /** * Set information about the current mechanism and request for this authentication attempt. If the mechanism * information cannot be resolved to a mechanism configuration, an exception is thrown. * * @param mechanismInformation the mechanism information about the current authentication attempt. * @throws IllegalStateException if the mechanism information about the current authentication attempt cannot be * resolved to a mechanism configuration */ public void setMechanismInformation(final MechanismInformation mechanismInformation) throws IllegalStateException { stateRef.get().setMechanismInformation(mechanismInformation); } /** * Get the authorized identity result of this authentication. * * @return the authorized identity * @throws IllegalStateException if the authentication is incomplete */ public SecurityIdentity getAuthorizedIdentity() throws IllegalStateException { return stateRef.get().getAuthorizedIdentity(); } /** * Set the authentication to anonymous, completing the authentication process. * * @throws IllegalStateException if the authentication is already complete */ public boolean authorizeAnonymous() throws IllegalStateException { return authorizeAnonymous(true); } /** * Set the authentication to anonymous, completing the authentication process. * * @param requireLoginPermission {@code true} if {@link LoginPermission} is required and {@code false} otherwise * @throws IllegalStateException if the authentication is already complete */ public boolean authorizeAnonymous(boolean requireLoginPermission) throws IllegalStateException { return stateRef.get().authorizeAnonymous(requireLoginPermission); } /** * Set the authentication name for this authentication. If the name is already set, then the new name must be * equal to the old name, or else an exception is thrown. * * @param name the authentication name * @throws IllegalArgumentException if the name is syntactically invalid * @throws RealmUnavailableException if the realm is not available * @throws IllegalStateException if the authentication name was already set and there is a mismatch */ public void setAuthenticationName(String name) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { setAuthenticationName(name, false); } /** * Set the authentication name for this authentication. If the name is already set, then the new name must be * equal to the old name, or else an exception is thrown. * * @param name the authentication name * @param exclusive {@code true} if exclusive access to the backing identity is required * @throws IllegalArgumentException if the name is syntactically invalid * @throws RealmUnavailableException if the realm is not available or if exclusive access to the backing identity * is required but could not be granted * @throws IllegalStateException if the authentication name was already set and there is a mismatch */ public void setAuthenticationName(String name, boolean exclusive) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { Assert.checkNotNullParam("name", name); setAuthenticationPrincipal(new NamePrincipal(name), exclusive); } /** * Set the authentication principal for this authentication. Calling this method initiates authentication. * * @param principal the authentication principal * @throws IllegalArgumentException if the principal cannot be mapped to a name, or the mapped name is syntactically invalid * @throws RealmUnavailableException if the realm is not available * @throws IllegalStateException if the authentication name was already set */ public void setAuthenticationPrincipal(Principal principal) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { setAuthenticationPrincipal(principal, false); } /** * Set the authentication principal for this authentication. Calling this method initiates authentication. * * @param principal the authentication principal * @param exclusive {@code true} if exclusive access to the backing identity is required * @throws IllegalArgumentException if the principal cannot be mapped to a name, or the mapped name is syntactically invalid * @throws RealmUnavailableException if the realm is not available * @throws IllegalStateException if the authentication name was already set */ public void setAuthenticationPrincipal(Principal principal, boolean exclusive) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { Assert.checkNotNullParam("principal", principal); stateRef.get().setPrincipal(principal, exclusive); } /** * Determine if the given name refers to the same identity as the currently set authentication name. * * @param name the authentication name * @return {@code true} if the name matches the current identity, {@code false} otherwise * @throws IllegalArgumentException if the name is syntactically invalid * @throws RealmUnavailableException if the realm is not available * @throws IllegalStateException if the authentication name was already set */ public boolean isSameName(String name) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { Assert.checkNotNullParam("name", name); return isSamePrincipal(new NamePrincipal(name)); } /** * Determine if the given principal refers to the same identity as the currently set authentication name. * * @param principal the authentication name * @return {@code true} if the name matches the current identity, {@code false} otherwise * @throws IllegalArgumentException if the name is syntactically invalid * @throws RealmUnavailableException if the realm is not available * @throws IllegalStateException if the authentication name was already set */ public boolean isSamePrincipal(Principal principal) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { Assert.checkNotNullParam("principal", principal); return stateRef.get().isSamePrincipal(principal); } /** * Determine if the current authentication identity actually exists in the realm. * * @return {@code true} if the identity exists, {@code false} otherwise * @throws RealmUnavailableException if the realm failed to access the identity * @throws IllegalStateException if there is no authentication name set */ public boolean exists() throws RealmUnavailableException, IllegalStateException { return stateRef.get().getRealmIdentity().exists(); } /** * Mark this authentication as "failed". The context cannot be used after this method is called. * * @throws IllegalStateException if no authentication has been initiated or authentication is already completed */ public void fail() throws IllegalStateException { stateRef.get().fail(true); } /** * Attempt to authorize an authentication attempt. If the authorization is successful, {@code true} is returned and * the context is placed in the "authorized" state with the new authorization identity. If the authorization fails, * {@code false} is returned and the state of the context is unchanged. * * @return {@code true} if the authorization succeeded, {@code false} otherwise * @throws RealmUnavailableException if the realm is not available * @throws IllegalStateException if the authentication name was not set or authentication was already complete */ public boolean authorize() throws RealmUnavailableException, IllegalStateException { return authorize(true); } boolean authorize(boolean requireLoginPermission) throws RealmUnavailableException, IllegalStateException { return stateRef.get().authorize(requireLoginPermission); } /** * Attempt to authorize a change to a new user (possibly including an authentication attempt). If the authorization * is successful, {@code true} is returned and the context is placed in the "authorized" state with the new authorization * identity. If the authorization fails, {@code false} is returned and the state of the context is unchanged. * * @param name the authorization name (must not be {@code null}) * @return {@code true} if the authorization succeeded, {@code false} otherwise * @throws IllegalArgumentException if the name is syntactically invalid * @throws RealmUnavailableException if the realm is not available * @throws IllegalStateException if the authentication name was not set or authentication was already complete */ public boolean authorize(String name) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { checkNotNullParam("name", name); return authorize(new NamePrincipal(name), true); } /** * Attempt to authorize a change to a new user (possibly including an authentication attempt). If the authorization * is successful, {@code true} is returned and the context is placed in the "authorized" state with the new authorization * identity. If the authorization fails, {@code false} is returned and the state of the context is unchanged. * * @param principal the authorization principal (must not be {@code null}) * @return {@code true} if the authorization succeeded, {@code false} otherwise * @throws IllegalArgumentException if the principal is syntactically invalid * @throws RealmUnavailableException if the realm is not available * @throws IllegalStateException if the authentication principal was not set or authentication was already complete */ public boolean authorize(Principal principal) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { return authorize(principal, true); } boolean authorize(Principal principal, boolean authorizeRunAs) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { checkNotNullParam("principal", principal); return stateRef.get().authorize(principal, authorizeRunAs); } /** * Mark this authentication as "successful". The context cannot be used after this method is called, however * the authorized identity may thereafter be accessed via the {@link #getAuthorizedIdentity()} method. If no * authentication actually happened, then authentication will complete anonymously. * * @throws IllegalStateException if authentication is already completed * @throws RealmUnavailableException if the realm is not able to handle requests for any reason */ public void succeed() throws IllegalStateException, RealmUnavailableException { stateRef.get().succeed(); } /** * Determine if authentication was already completed on this context. * * @return {@code true} if authentication was completed; {@code false} otherwise */ public boolean isDone() { return stateRef.get().isDone(); } /** * Get the principal associated with the current authentication name. Only valid during authentication process. * * @return the principal * @throws IllegalStateException if no authentication has been initiated or authentication is already completed */ public Principal getAuthenticationPrincipal() { return stateRef.get().getAuthenticationPrincipal(); } /** * Determine whether a given credential is definitely obtainable, possibly obtainable, or definitely not obtainable. * * If an authentication identity is established this will be for that identity, otherwise this will be the general * level of support advertised by the security domain. * * @param credentialType the credential type class (must not be {@code null}) * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does * not support algorithm names * @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type * does not support algorithm parameters * @return the level of support for this credential type * * @throws RealmUnavailableException if the realm is not able to handle requests for any reason * @throws IllegalStateException if no authentication has been initiated or authentication is already completed */ public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { Assert.checkNotNullParam("credentialType", credentialType); return stateRef.get().getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); } /** * Determine whether a given credential is definitely obtainable, possibly obtainable, or definitely not obtainable. * * If an authentication identity is established this will be for that identity, otherwise this will be the general * level of support advertised by the security domain. * * @param credentialType the credential type class (must not be {@code null}) * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does * not support algorithm names * @return the level of support for this credential type * * @throws RealmUnavailableException if the realm is not able to handle requests for any reason * @throws IllegalStateException if no authentication has been initiated or authentication is already completed */ public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName) throws RealmUnavailableException { return getCredentialAcquireSupport(credentialType, algorithmName, null); } /** * Determine whether a given credential is definitely obtainable, possibly obtainable, or definitely not obtainable. * * If an authentication identity is established this will be for that identity, otherwise this will be the general * level of support advertised by the security domain. * * @param credentialType the credential type class (must not be {@code null}) * @return the level of support for this credential type * * @throws RealmUnavailableException if the realm is not able to handle requests for any reason * @throws IllegalStateException if no authentication has been initiated or authentication is already completed */ public SupportLevel getCredentialAcquireSupport(Class credentialType) throws RealmUnavailableException { Assert.checkNotNullParam("credentialType", credentialType); return getCredentialAcquireSupport(credentialType, null); } /** * Determine whether a given piece of evidence is definitely verifiable, possibly verifiable, or definitely not verifiable. * * If an authentication identity is established this will be for that identity, otherwise this will be the general * level of support advertised by the security domain. * * @param evidenceType the evidence type class (must not be {@code null}) * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the evidence type does * not support algorithm names * @return the level of support for this credential type * * @throws RealmUnavailableException if the realm is not able to handle requests for any reason * @throws IllegalStateException if no authentication has been initiated or authentication is already completed */ public SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) throws RealmUnavailableException { Assert.checkNotNullParam("evidenceType", evidenceType); return stateRef.get().getEvidenceVerifySupport(evidenceType, algorithmName); } /** * Determine whether a given piece of evidence is definitely verifiable, possibly verifiable, or definitely not verifiable. * * If an authentication identity is established this will be for that identity, otherwise this will be the general * level of support advertised by the security domain. * * @param evidenceType the evidence type class (must not be {@code null}) * @return the level of support for this credential type * * @throws RealmUnavailableException if the realm is not able to handle requests for any reason * @throws IllegalStateException if no authentication has been initiated or authentication is already completed */ public SupportLevel getEvidenceVerifySupport(Class evidenceType) throws RealmUnavailableException { Assert.checkNotNullParam("evidenceType", evidenceType); return getEvidenceVerifySupport(evidenceType, null); } /** * Acquire a credential of the given type. The credential type is defined by its {@code Class} and an optional {@code algorithmName}. If the * algorithm name is not given, then the query is performed for any algorithm of the given type. * * @param credentialType the credential type class (must not be {@code null}) * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does * not support algorithm names * @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type * does not support algorithm parameters * @param the credential type * * @return the credential, or {@code null} if the principal has no credential of that type * * @throws RealmUnavailableException if the realm is not able to handle requests for any reason * @throws IllegalStateException if no authentication has been initiated or authentication is already completed */ public C getCredential(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { Assert.checkNotNullParam("credentialType", credentialType); return stateRef.get().getCredential(credentialType, algorithmName, parameterSpec); } /** * Acquire a credential of the given type. The credential type is defined by its {@code Class} and an optional {@code algorithmName}. If the * algorithm name is not given, then the query is performed for any algorithm of the given type. * * @param credentialType the credential type class (must not be {@code null}) * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does * not support algorithm names * @param the credential type * * @return the credential, or {@code null} if the principal has no credential of that type * * @throws RealmUnavailableException if the realm is not able to handle requests for any reason * @throws IllegalStateException if no authentication has been initiated or authentication is already completed */ public C getCredential(Class credentialType, String algorithmName) throws RealmUnavailableException { Assert.checkNotNullParam("credentialType", credentialType); return stateRef.get().getCredential(credentialType, algorithmName, null); } /** * Acquire a credential of the given type. The credential type is defined by its {@code Class} and an optional {@code algorithmName}. If the * algorithm name is not given, then the query is performed for any algorithm of the given type. * * @param credentialType the credential type class (must not be {@code null}) * @param the credential type * * @return the credential, or {@code null} if the principal has no credential of that type * * @throws RealmUnavailableException if the realm is not able to handle requests for any reason * @throws IllegalStateException if no authentication has been initiated or authentication is already completed */ public C getCredential(Class credentialType) throws RealmUnavailableException { Assert.checkNotNullParam("credentialType", credentialType); return stateRef.get().getCredential(credentialType, null, null); } /** * Apply the given function to the acquired credential, if it is set and of the given type. * * @param credentialType the credential type class (must not be {@code null}) * @param function the function to apply (must not be {@code null}) * @param the credential type * @param the return type * @return the result of the function, or {@code null} if the criteria are not met * * @throws RealmUnavailableException if the realm is not able to handle requests for any reason * @throws IllegalStateException if no authentication has been initiated or authentication is already completed */ public R applyToCredential(Class credentialType, Function function) throws RealmUnavailableException { final Credential credential = getCredential(credentialType); return credential == null ? null : credential.castAndApply(credentialType, function); } /** * Apply the given function to the acquired credential, if it is set and of the given type and algorithm. * * @param credentialType the credential type class (must not be {@code null}) * @param algorithmName the algorithm name * @param function the function to apply (must not be {@code null}) * @param the credential type * @param the return type * @return the result of the function, or {@code null} if the criteria are not met * * @throws RealmUnavailableException if the realm is not able to handle requests for any reason * @throws IllegalStateException if no authentication has been initiated or authentication is already completed */ public R applyToCredential(Class credentialType, String algorithmName, Function function) throws RealmUnavailableException { final Credential credential = getCredential(credentialType, algorithmName); return credential == null ? null : credential.castAndApply(credentialType, algorithmName, function); } /** * Apply the given function to the acquired credential, if it is set and of the given type and algorithm. * * @param credentialType the credential type class (must not be {@code null}) * @param algorithmName the algorithm name * @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type * does not support algorithm parameters * @param function the function to apply (must not be {@code null}) * @param the credential type * @param the return type * @return the result of the function, or {@code null} if the criteria are not met * * @throws RealmUnavailableException if the realm is not able to handle requests for any reason * @throws IllegalStateException if no authentication has been initiated or authentication is already completed */ public R applyToCredential(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec, Function function) throws RealmUnavailableException { final Credential credential = getCredential(credentialType, algorithmName, parameterSpec); return credential == null ? null : credential.castAndApply(credentialType, algorithmName, parameterSpec, function); } /** * Verify the given evidence. * * @param evidence the evidence to verify * * @return {@code true} if verification was successful, {@code false} otherwise * * @throws RealmUnavailableException if the realm is not able to handle requests for any reason * @throws IllegalStateException if no authentication has been initiated or authentication is already completed */ public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException { Assert.checkNotNullParam("evidence", evidence); return stateRef.get().verifyEvidence(evidence); } /** * Set the decoded evidence principal. * * @param evidence the evidence to decode and associate with a principal * @since 1.10.0 */ public void setDecodedEvidencePrincipal(Evidence evidence) throws RealmUnavailableException { Assert.checkNotNullParam("evidence", evidence); evidence.setDecodedPrincipal(stateRef.get().getSecurityDomain().getEvidenceDecoder().apply(evidence)); } /** * Add a public credential to the identity being authenticated. * * @param credential the credential to add (must not be {@code null}) */ public void addPublicCredential(Credential credential) { Assert.checkNotNullParam("credential", credential); stateRef.get().addPublicCredential(credential); } /** * Add a private credential to the identity being authenticated. This credential may be forwarded to outbound * authentication mechanisms. * * @param credential the credential to add (must not be {@code null}) */ public void addPrivateCredential(Credential credential) { Assert.checkNotNullParam("credential", credential); stateRef.get().addPrivateCredential(credential); } /** * Add runtime attributes to the identity being authenticated. * * @param runtimeAttributes the runtime attributes to add (must not be {@code null}) */ public void addRuntimeAttributes(Attributes runtimeAttributes) { Assert.checkNotNullParam("runtimeAttributes", runtimeAttributes); stateRef.get().addRuntimeAttributes(runtimeAttributes); } /** * Attempt to import the given security identity as a trusted identity. If this method returns {@code true}, * the context will be in an authorized state, and the new identity can be retrieved. * * @param identity the identity to import (must not be {@code null}) * @return {@code true} if the identity is authorized, {@code false} otherwise * @throws RealmUnavailableException if the realm is not able to handle requests for any reason */ public boolean importIdentity(SecurityIdentity identity) throws RealmUnavailableException { Assert.checkNotNullParam("identity", identity); return stateRef.get().importIdentity(identity); } /** * Set the mechanism realm name to be equal to the given name. If no mechanism realms are configured, the realm * name is ignored. * * @param realmName the selected realm name * @throws IllegalStateException if a realm name was already selected or it is too late to choose a realm * @throws IllegalArgumentException if the selected realm name was not offered */ public void setMechanismRealmName(String realmName) throws IllegalStateException, IllegalArgumentException { Assert.checkNotNullParam("realmName", realmName); stateRef.get().setMechanismRealmName(realmName); } /** * Update the credential for the current authentication identity. * * @param credential the new credential (must not be {@code null}) * @throws RealmUnavailableException if the realm is not able to handle requests for any reason */ public void updateCredential(Credential credential) throws RealmUnavailableException { Assert.checkNotNullParam("credential", credential); stateRef.get().updateCredential(credential); } /** * Close the server authentication context, failing any in-progress authentication and releasing any * associated resources. */ public void close() { stateRef.get().fail(false); } AtomicReference getStateRef() { return stateRef; } CallbackHandler createCallbackHandler() { return new CallbackHandler() { private SSLConnection sslConnection; private X509Certificate[] peerCerts; private boolean saslSkipCertificateVerification; @Override public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException { handleOne(callbacks, 0); } private void handleOne(final Callback[] callbacks, final int idx) throws IOException, UnsupportedCallbackException { if (idx == callbacks.length) { return; } final AtomicReference stateRef = getStateRef(); final Callback callback = callbacks[idx]; if (callback instanceof AnonymousAuthorizationCallback) { boolean authorized = authorizeAnonymous(); log.tracef("Handling AnonymousAuthorizationCallback: authorized = %b", authorized); ((AnonymousAuthorizationCallback) callback).setAuthorized(authorized); handleOne(callbacks, idx + 1); } else if (callback instanceof AuthorizeCallback) { final AuthorizeCallback authorizeCallback = (AuthorizeCallback) callback; String authenticationID = authorizeCallback.getAuthenticationID(); if (authenticationID != null) { // always re-set the authentication name to ensure it hasn't changed. setAuthenticationName(authenticationID); } else { // This is a special case to support scenarios where the identity was already established by some // external method (e.g.: EXTERNAL SASL and TLS) where only authorization is necessary. We delay authentication // until we receive an authorization request. // In the future, we may want to support external methods other than TLS peer authentication if (stateRef.get().canVerifyEvidence() && peerCerts != null) { log.tracef("Authentication ID is null but SSL peer certificates are available. Trying to authenticate peer"); // if SASL mechanism is used with skip-certificate-verification property then do not verifyEvidence against the security realm if (saslSkipCertificateVerification) { // Since evidence verification is being skipped here, ensure evidence decoding still takes place X509PeerCertificateChainEvidence evidence = new X509PeerCertificateChainEvidence(peerCerts); setDecodedEvidencePrincipal(evidence); stateRef.get().setPrincipal(evidence.getDecodedPrincipal(), false); } else { verifyEvidence(new X509PeerCertificateChainEvidence(peerCerts)); } } } String authorizationID = authorizeCallback.getAuthorizationID(); boolean authorized = authorizationID != null ? authorize(authorizationID) : authorize(); log.tracef("Handling AuthorizeCallback: authenticationID = %s authorizationID = %s authorized = %b", authenticationID, authorizationID, authorized); authorizeCallback.setAuthorized(authorized); handleOne(callbacks, idx + 1); } else if (callback instanceof ExclusiveNameCallback) { final ExclusiveNameCallback exclusiveNameCallback = ((ExclusiveNameCallback) callback); // login name final String name = exclusiveNameCallback.getDefaultName(); try { boolean exclusive = exclusiveNameCallback.needsExclusiveAccess(); log.tracef("Handling ExclusiveNameCallback: authenticationName = %s needsExclusiveAccess = %b", name, exclusive); if (exclusive) { setAuthenticationName(name, true); exclusiveNameCallback.setExclusiveAccess(true); } else { setAuthenticationName(name); } } catch (Exception e) { throw new IOException(e); } handleOne(callbacks, idx + 1); } else if (callback instanceof NameCallback) { // login name final String name = ((NameCallback) callback).getDefaultName(); try { log.tracef("Handling NameCallback: authenticationName = %s", name); setAuthenticationName(name); } catch (Exception e) { throw new IOException(e); } handleOne(callbacks, idx + 1); } else if (callback instanceof PeerPrincipalCallback) { // login name final Principal principal = ((PeerPrincipalCallback) callback).getPrincipal(); try { log.tracef("Handling PeerPrincipalCallback: principal = %s", principal); setAuthenticationPrincipal(principal); } catch (Exception e) { throw new IOException(e); } handleOne(callbacks, idx + 1); } else if (callback instanceof PasswordCallback) { final PasswordCallback passwordCallback = (PasswordCallback) callback; if (getCredentialAcquireSupport(PasswordCredential.class).mayBeSupported()) { final TwoWayPassword password = applyToCredential(PasswordCredential.class, c -> c.getPassword(TwoWayPassword.class)); if (password != null) { final ClearPasswordSpec clearPasswordSpec; try { final PasswordFactory passwordFactory = PasswordFactory.getInstance(password.getAlgorithm()); clearPasswordSpec = passwordFactory.getKeySpec(password, ClearPasswordSpec.class); } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { log.trace("Unable to get key spec", e); throw new FastUnsupportedCallbackException(callback); } log.tracef("Handling PasswordCallback: obtained successfully"); passwordCallback.setPassword(clearPasswordSpec.getEncodedPassword()); handleOne(callbacks, idx + 1); return; } log.tracef("Handling PasswordCallback: failed to obtain PasswordCredential"); throw new FastUnsupportedCallbackException(callback); } // otherwise just fail out; some mechanisms will try again with different credentials log.tracef("Handling PasswordCallback: PasswordCredential may not be supported"); throw new FastUnsupportedCallbackException(callback); } else if (callback instanceof CredentialCallback) { final CredentialCallback credentialCallback = (CredentialCallback) callback; String requestedRealm = stateRef.get().getMechanismRealmConfiguration().getRealmName(); final Credential credential = getCredential(credentialCallback.getCredentialType(), credentialCallback.getAlgorithm(), credentialCallback.getParameterSpec()); if (credential != null) { if (credential instanceof PasswordCredential) { Password password = ((PasswordCredential) credential).getPassword(); if (password instanceof DigestPassword) { String providedRealm = ((DigestPassword) password).getRealm(); if ( ! providedRealm.equals(requestedRealm)) { log.tracef("Handling CredentialCallback: credential for realm \"%s\" is not available (\"%s\" provided)", requestedRealm, providedRealm); throw new FastUnsupportedCallbackException(callback); } else { log.tracef("Handling CredentialCallback: obtained credential for correct realm \"%s\"", providedRealm); } } } log.tracef("Handling CredentialCallback: obtained credential: %s", credential); credentialCallback.setCredential(credential); handleOne(callbacks, idx + 1); return; } // otherwise just fail out; some mechanisms will try again with different credentials log.tracef("Handling CredentialCallback: failed to obtain credential"); throw new FastUnsupportedCallbackException(callback); } else if (callback instanceof ServerCredentialCallback) { final ServerCredentialCallback serverCredentialCallback = (ServerCredentialCallback) callback; CredentialSource serverCredentialSource = stateRef.get().getMechanismConfiguration().getServerCredentialSource(); final Class credentialType = serverCredentialCallback.getCredentialType(); final String algorithm = serverCredentialCallback.getAlgorithm(); final AlgorithmParameterSpec parameterSpec = serverCredentialCallback.getParameterSpec(); // optimize for some cases if (serverCredentialSource.getCredentialAcquireSupport(credentialType, algorithm, parameterSpec).mayBeSupported()) { final Credential credential = serverCredentialSource.getCredential(credentialType, algorithm, parameterSpec); if (credential != null) { log.tracef("Handling ServerCredentialCallback: successfully obtained credential type type=%s, algorithm=%s, params=%s", credentialType, algorithm, parameterSpec); serverCredentialCallback.setCredential(credential); handleOne(callbacks, idx + 1); // return here so we don't double-log, or double-handle callbacks return; } } log.tracef("Handling ServerCredentialCallback: skipping credential type type=%s, algorithm=%s, params=%s", credentialType, algorithm, parameterSpec); handleOne(callbacks, idx + 1); } else if (callback instanceof EvidenceVerifyCallback) { EvidenceVerifyCallback evidenceVerifyCallback = (EvidenceVerifyCallback) callback; evidenceVerifyCallback.setVerified(verifyEvidence(evidenceVerifyCallback.getEvidence())); handleOne(callbacks, idx + 1); } else if (callback instanceof EvidenceDecodePrincipalCallback) { EvidenceDecodePrincipalCallback evidenceDecodePrincipalCallback = (EvidenceDecodePrincipalCallback) callback; setDecodedEvidencePrincipal(evidenceDecodePrincipalCallback.getEvidence()); } else if (callback instanceof SSLCallback) { SSLCallback sslCallback = (SSLCallback) callback; this.sslConnection = sslCallback.getSslConnection(); try { peerCerts = X500.asX509CertificateArray(sslCallback.getSslConnection().getSession().getPeerCertificates()); } catch (SSLPeerUnverifiedException e) { log.trace("Peer unverified", e); peerCerts = null; } handleOne(callbacks, idx + 1); } else if (callback instanceof ChannelBindingCallback) { final SSLConnection sslConnection = this.sslConnection; if (sslConnection != null) { sslConnection.handleChannelBindingCallback((ChannelBindingCallback) callback); } handleOne(callbacks, idx + 1); } else if (callback instanceof AuthenticationCompleteCallback) { if (! isDone()) { if (((AuthenticationCompleteCallback) callback).succeeded()) { log.tracef("Handling AuthenticationCompleteCallback: succeed"); succeed(); } else { log.tracef("Handling AuthenticationCompleteCallback: fail"); fail(); } } handleOne(callbacks, idx + 1); } else if (callback instanceof SocketAddressCallback) { final SocketAddressCallback socketAddressCallback = (SocketAddressCallback) callback; log.tracef("Handling SocketAddressCallback"); if (socketAddressCallback.getKind() == SocketAddressCallback.Kind.PEER) { Attributes runtimeAttributes = new MapAttributes(); if ((socketAddressCallback.getAddress() != null) && ((InetSocketAddress) socketAddressCallback.getAddress()).getAddress() != null) { runtimeAttributes.addFirst(KEY_SOURCE_ADDRESS, ((InetSocketAddress) socketAddressCallback.getAddress()).getAddress().getHostAddress()); addRuntimeAttributes(runtimeAttributes); } else { log.tracef("Client's IP address is unknown."); } } handleOne(callbacks, idx + 1); } else if (callback instanceof SecurityIdentityCallback) { SecurityIdentity identity = getAuthorizedIdentity(); log.tracef("Handling SecurityIdentityCallback: identity = %s", identity); ((SecurityIdentityCallback) callback).setSecurityIdentity(identity); handleOne(callbacks, idx + 1); } else if (callback instanceof AvailableRealmsCallback) { Collection names = stateRef.get().getMechanismConfiguration().getMechanismRealmNames(); if (log.isTraceEnabled()) { log.tracef("Handling AvailableRealmsCallback: realms = [%s]", String.join(", ", names)); } if (! names.isEmpty()) { ((AvailableRealmsCallback) callback).setRealmNames(names.toArray(new String[names.size()])); } handleOne(callbacks, idx + 1); } else if (callback instanceof RealmCallback) { RealmCallback rcb = (RealmCallback) callback; String mechanismRealm = rcb.getText(); if (mechanismRealm == null) { mechanismRealm = rcb.getDefaultText(); } log.tracef("Handling RealmCallback: selected = [%s]", mechanismRealm); setMechanismRealmName(mechanismRealm); handleOne(callbacks, idx + 1); } else if (callback instanceof MechanismInformationCallback) { MechanismInformationCallback mic = (MechanismInformationCallback) callback; try { MechanismInformation mi = mic.getMechanismInformation(); if (log.isTraceEnabled()) { log.tracef("Handling MechanismInformationCallback type='%s' name='%s' host-name='%s' protocol='%s'", mi.getMechanismType(), mi.getMechanismName(), mi.getHostName(), mi.getProtocol()); } setMechanismInformation(mi); handleOne(callbacks, idx + 1); } catch (Exception e) { throw new IOException(e); } } else if (callback instanceof CredentialUpdateCallback) { final CredentialUpdateCallback credentialUpdateCallback = (CredentialUpdateCallback) callback; log.tracef("Handling CredentialUpdateCallback"); updateCredential(credentialUpdateCallback.getCredential()); handleOne(callbacks, idx + 1); } else if (callback instanceof CachedIdentityAuthorizeCallback) { CachedIdentityAuthorizeCallback authorizeCallback = (CachedIdentityAuthorizeCallback) callback; authorizeCallback.setSecurityDomain(stateRef.get().getSecurityDomain()); SecurityIdentity authorizedIdentity = null; Principal principal = null; SecurityIdentity identity = authorizeCallback.getIdentity(); if (identity != null && importIdentity(identity)) { authorizedIdentity = getAuthorizedIdentity(); } else { principal = authorizeCallback.getPrincipal(); if (principal == null) { principal = authorizeCallback.getAuthorizationPrincipal(); } if (principal != null) { setAuthenticationPrincipal(principal); if (authorize()) { authorizedIdentity = getAuthorizedIdentity(); } } } log.tracef("Handling CachedIdentityAuthorizeCallback: principal = %s authorizedIdentity = %s", principal, authorizedIdentity); authorizeCallback.setAuthorized(authorizedIdentity); handleOne(callbacks, idx + 1); } else if (callback instanceof IdentityCredentialCallback) { IdentityCredentialCallback icc = (IdentityCredentialCallback) callback; Credential credential = icc.getCredential(); if (icc.isPrivate()) { addPrivateCredential(credential); } else { addPublicCredential(credential); } handleOne(callbacks, idx + 1); } else if (callback instanceof PrincipalAuthorizeCallback) { PrincipalAuthorizeCallback authorizeCallback = (PrincipalAuthorizeCallback) callback; Principal principal = authorizeCallback.getPrincipal(); // always re-set the principal to ensure it hasn't changed. setAuthenticationPrincipal(principal); boolean authorized = authorize(); log.tracef("Handling PrincipalAuthorizeCallback: principal = %s authorized = %b", principal, authorized); authorizeCallback.setAuthorized(authorized); handleOne(callbacks, idx + 1); } else if (callback instanceof AuthenticationConfigurationCallback) { AuthenticationConfigurationCallback authenticationConfigurationCallback = (AuthenticationConfigurationCallback) callback; saslSkipCertificateVerification = authenticationConfigurationCallback.getSaslSkipCertificateVerification(); handleOne(callbacks, idx + 1); } else if (callback instanceof RequestInformationCallback) { HashMap props = ((RequestInformationCallback) callback).getProperties(); Attributes runtimeAttributes = new MapAttributes(); for (Map.Entry entry : props.entrySet()) { if (entry.getValue() != null) { runtimeAttributes.addFirst(entry.getKey(), entry.getValue().toString()); } } addRuntimeAttributes(runtimeAttributes); handleOne(callbacks, idx + 1); } else { CallbackUtil.unsupported(callback); handleOne(callbacks, idx + 1); } } }; } private static Principal rewriteAll(Principal principal, Function r1, Function r2, Function r3) { principal = r1.apply(principal); if (principal == null) return null; principal = r2.apply(principal); if (principal == null) return null; principal = r3.apply(principal); return principal; } static String mapAll(Principal principal, RealmMapper r1, RealmMapper r2, RealmMapper r3, String defaultRealmName, Evidence evidence) { if (r1 != null) { return mapRealmName(principal, r1, defaultRealmName, evidence); } if (r2 != null) { return mapRealmName(principal, r2, defaultRealmName, evidence); } if (r3 != null) { return mapRealmName(principal, r3, defaultRealmName, evidence); } return defaultRealmName; } private static String mapRealmName(Principal principal, RealmMapper realmMapper, String defaultRealmName, Evidence evidence) { String realmName = realmMapper.getRealmMapping(principal, evidence); return realmName != null ? realmName : defaultRealmName; } State assignName(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration, Principal originalPrincipal, final Evidence evidence, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) throws RealmUnavailableException { return assignName(capturedIdentity, mechanismConfiguration, mechanismRealmConfiguration, originalPrincipal, evidence, privateCredentials, publicCredentials, false, runtimeAttributes); } State assignName(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration, Principal originalPrincipal, final Evidence evidence, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final boolean exclusive, final Attributes runtimeAttributes) throws RealmUnavailableException { final SecurityDomain domain = capturedIdentity.getSecurityDomain(); final Principal preRealmPrincipal = rewriteAll(originalPrincipal, mechanismRealmConfiguration.getPreRealmRewriter(), mechanismConfiguration.getPreRealmRewriter(), domain.getPreRealmRewriter()); if (preRealmPrincipal == null) { log.tracef("Unable to rewrite principal [%s] by pre-realm rewritters", originalPrincipal); return new InvalidNameState(capturedIdentity, mechanismConfiguration, mechanismRealmConfiguration, privateCredentials, publicCredentials, runtimeAttributes); } String realmName = mapAll(preRealmPrincipal, mechanismRealmConfiguration.getRealmMapper(), mechanismConfiguration.getRealmMapper(), domain.getRealmMapper(), domain.getDefaultRealmName(), evidence); final RealmInfo realmInfo = domain.getRealmInfo(realmName); final Principal postRealmPrincipal = rewriteAll(preRealmPrincipal, mechanismRealmConfiguration.getPostRealmRewriter(), mechanismConfiguration.getPostRealmRewriter(), domain.getPostRealmRewriter()); if (postRealmPrincipal == null) { log.tracef("Unable to rewrite principal [%s] by post-realm rewritters", preRealmPrincipal); return new InvalidNameState(capturedIdentity, mechanismConfiguration, mechanismRealmConfiguration, privateCredentials, publicCredentials, runtimeAttributes); } final Principal finalPrincipal = rewriteAll(postRealmPrincipal, mechanismRealmConfiguration.getFinalRewriter(), mechanismConfiguration.getFinalRewriter(), realmInfo.getPrincipalRewriter()); if (finalPrincipal == null) { log.tracef("Unable to rewrite principal [%s] by final rewritters", postRealmPrincipal); return new InvalidNameState(capturedIdentity, mechanismConfiguration, mechanismRealmConfiguration, privateCredentials, publicCredentials, runtimeAttributes); } log.tracef("Principal assigning: [%s], pre-realm rewritten: [%s], realm name: [%s], post-realm rewritten: [%s], realm rewritten: [%s]", originalPrincipal, preRealmPrincipal, realmName, postRealmPrincipal, finalPrincipal); final SecurityRealm securityRealm = realmInfo.getSecurityRealm(); final RealmIdentity realmIdentity; try { if (exclusive) { if (securityRealm instanceof ModifiableSecurityRealm) { realmIdentity = ((ModifiableSecurityRealm) securityRealm).getRealmIdentityForUpdate(finalPrincipal); } else { throw log.unableToObtainExclusiveAccess(); } } else { realmIdentity = securityRealm.getRealmIdentity(finalPrincipal); } } catch (RealmUnavailableException e) { SecurityDomain.safeHandleSecurityEvent(domain, new SecurityRealmUnavailableEvent(capturedIdentity, realmName)); throw e; } return new NameAssignedState(capturedIdentity, realmInfo, realmIdentity, preRealmPrincipal, mechanismConfiguration, mechanismRealmConfiguration, privateCredentials, publicCredentials, runtimeAttributes); } abstract static class State { MechanismConfiguration getMechanismConfiguration() { throw log.noAuthenticationInProgress(); } MechanismRealmConfiguration getMechanismRealmConfiguration() { throw log.noAuthenticationInProgress(); } SecurityIdentity getAuthorizedIdentity() { throw log.noAuthenticationInProgress(); } Principal getAuthenticationPrincipal() { throw log.noAuthenticationInProgress(); } boolean isSamePrincipal(Principal principal) { return false; } SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { throw log.noAuthenticationInProgress(); } SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) throws RealmUnavailableException { throw log.noAuthenticationInProgress(); } C getCredential(Class credentialType, String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { throw log.noAuthenticationInProgress(); } boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { throw log.noAuthenticationInProgress(); } boolean importIdentity(final SecurityIdentity identity) throws RealmUnavailableException { throw log.noAuthenticationInProgress(); } RealmIdentity getRealmIdentity() { throw log.noAuthenticationInProgress(); } SecurityDomain getSecurityDomain() { throw log.noAuthenticationInProgress(); } boolean authorizeAnonymous(final boolean requireLoginPermission) { throw log.noAuthenticationInProgress(); } void setMechanismInformation(final MechanismInformation mechanismInformation) { throw log.noAuthenticationInProgress(); } void setPrincipal(Principal principal, boolean exclusive) throws RealmUnavailableException { throw log.noAuthenticationInProgress(); } boolean authorize(final boolean requireLoginPermission) throws RealmUnavailableException { throw log.noAuthenticationInProgress(); } boolean authorize(Principal authorizationId, final boolean authorizeRunAs) throws RealmUnavailableException { throw log.noAuthenticationInProgress(); } void setMechanismRealmName(String name) { throw log.noAuthenticationInProgress(); } void updateCredential(Credential credential) throws RealmUnavailableException { throw log.noAuthenticationInProgress(); } void succeed() { throw log.noAuthenticationInProgress(); } void fail(final boolean requireInProgress) { if (requireInProgress) throw log.noAuthenticationInProgress(); } boolean isDone() { return false; } void addPublicCredential(final Credential credential) { throw log.noAuthenticationInProgress(); } void addPrivateCredential(final Credential credential) { throw log.noAuthenticationInProgress(); } void addRuntimeAttributes(final Attributes runtimeAttributes) { throw log.noAuthenticationInProgress(); } /** * Indicate whether or not current state is {@link NameAssignedState}. * * @return {@code true} if state is {@link NameAssignedState}. Otherwise, {@code false}. */ public boolean isNameAssigned() { return this instanceof NameAssignedState; } /** * Indicate whether or not current state is {@link AuthorizedState}. * * @return {@code true} if state is {@link AuthorizedState}. Otherwise, {@code false}. */ public boolean isAuthorized() { return this instanceof AuthorizedState; } /** * Indicate whether or not evidence verification is allowed. * * @return {@code true} if evidence verification can be performed. Otherwise, {@code false}. */ public boolean canVerifyEvidence() { return !(this instanceof NameAssignedState || this instanceof AuthorizedState); } } final class InactiveState extends State { private final SecurityIdentity capturedIdentity; private final MechanismConfigurationSelector mechanismConfigurationSelector; private final MechanismInformation mechanismInformation; private final IdentityCredentials privateCredentials; private final IdentityCredentials publicCredentials; private final Attributes runtimeAttributes; public InactiveState(SecurityIdentity capturedIdentity, MechanismConfigurationSelector mechanismConfigurationSelector, IdentityCredentials privateCredentials, IdentityCredentials publicCredentials, Attributes runtimeAttributes) { this(capturedIdentity, mechanismConfigurationSelector, MechanismInformation.DEFAULT, privateCredentials, publicCredentials, runtimeAttributes); } public InactiveState(SecurityIdentity capturedIdentity, MechanismConfigurationSelector mechanismConfigurationSelector, MechanismInformation mechanismInformation, IdentityCredentials privateCredentials, IdentityCredentials publicCredentials, Attributes runtimeAttributes) { this.capturedIdentity = capturedIdentity; this.mechanismConfigurationSelector = mechanismConfigurationSelector; this.mechanismInformation = checkNotNullParam("mechanismInformation", mechanismInformation); this.privateCredentials = privateCredentials; this.publicCredentials = publicCredentials; this.runtimeAttributes = runtimeAttributes; } @Override void setMechanismInformation(MechanismInformation mechanismInformation) { InactiveState inactiveState = new InactiveState(capturedIdentity, mechanismConfigurationSelector, mechanismInformation, privateCredentials, publicCredentials, runtimeAttributes); InitialState nextState = inactiveState.selectMechanismConfiguration(); if (! stateRef.compareAndSet(this, nextState)) { stateRef.get().setMechanismInformation(mechanismInformation); } } @Override SecurityDomain getSecurityDomain() { return capturedIdentity.getSecurityDomain(); } boolean authorize(Principal authorizationId, boolean authorizeRunAs) throws RealmUnavailableException { transition(); return stateRef.get().authorize(authorizationId, authorizeRunAs); } @Override void setMechanismRealmName(String name) { transition(); stateRef.get().setMechanismRealmName(name); } @Override MechanismRealmConfiguration getMechanismRealmConfiguration() { transition(); return stateRef.get().getMechanismRealmConfiguration(); } @Override void fail(final boolean requireInProgress) { transition(); stateRef.get().fail(requireInProgress); } @Override boolean authorizeAnonymous(boolean requireLoginPermission) { transition(); return stateRef.get().authorizeAnonymous(requireLoginPermission); } @Override boolean authorize(boolean requireLoginPermission) throws RealmUnavailableException { transition(); return stateRef.get().authorize(requireLoginPermission); } @Override boolean importIdentity(SecurityIdentity identity) throws RealmUnavailableException { transition(); return stateRef.get().importIdentity(identity); } @Override SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { return getSecurityDomain().getEvidenceVerifySupport(evidenceType, algorithmName); } @Override boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException { transition(); return stateRef.get().verifyEvidence(evidence); } @Override void setPrincipal(Principal principal, boolean exclusive) throws RealmUnavailableException { transition(); stateRef.get().setPrincipal(principal, exclusive); } @Override MechanismConfiguration getMechanismConfiguration() { transition(); return stateRef.get().getMechanismConfiguration(); } @Override void addPublicCredential(final Credential credential) { final InactiveState newState = new InactiveState(capturedIdentity, mechanismConfigurationSelector, mechanismInformation, privateCredentials, publicCredentials.withCredential(credential), runtimeAttributes); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addPublicCredential(credential); } } @Override void addPrivateCredential(final Credential credential) { final InactiveState newState = new InactiveState(capturedIdentity, mechanismConfigurationSelector, mechanismInformation, privateCredentials.withCredential(credential), publicCredentials, runtimeAttributes); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addPrivateCredential(credential); } } @Override void addRuntimeAttributes(final Attributes runtimeAttributes) { final InactiveState newState = new InactiveState(capturedIdentity, mechanismConfigurationSelector, mechanismInformation, privateCredentials, publicCredentials, AggregateAttributes.aggregateOf(this.runtimeAttributes, runtimeAttributes)); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addRuntimeAttributes(runtimeAttributes); } } private void transition() { InitialState initialState = selectMechanismConfiguration(); stateRef.compareAndSet(this, initialState); } private InitialState selectMechanismConfiguration() { MechanismConfiguration mechanismConfiguration = mechanismConfigurationSelector.selectConfiguration(mechanismInformation); if (mechanismConfiguration == null) { throw log.unableToSelectMechanismConfiguration(mechanismInformation.getMechanismType(), mechanismInformation.getMechanismName(), mechanismInformation.getHostName(), mechanismInformation.getProtocol()); } return new InitialState(capturedIdentity, mechanismConfiguration, mechanismConfigurationSelector, privateCredentials, publicCredentials, runtimeAttributes); } } abstract class ActiveState extends State { ActiveState() { } boolean authorize(Principal authorizationId, boolean authorizeRunAs) throws RealmUnavailableException { final AtomicReference stateRef = getStateRef(); // get the identity we are authorizing from final SecurityIdentity sourceIdentity = getSourceIdentity(); final State state = assignName(sourceIdentity, getMechanismConfiguration(), getMechanismRealmConfiguration(), authorizationId, null, IdentityCredentials.NONE, IdentityCredentials.NONE, Attributes.EMPTY); if (!state.isNameAssigned()) { ElytronMessages.log.tracef("Authorization failed - unable to assign identity name"); return false; } final NameAssignedState nameAssignedState = (NameAssignedState) state; final RealmIdentity realmIdentity = nameAssignedState.getRealmIdentity(); boolean ok = false; try { if (! realmIdentity.exists()) { ElytronMessages.log.tracef("Authorization failed - identity does not exist"); return false; } // check the run-as permission on the old identity if (authorizeRunAs && ! sourceIdentity.implies(new RunAsPrincipalPermission(nameAssignedState.getAuthenticationPrincipal().getName()))) { ElytronMessages.log.tracef("Authorization failed - source identity does not have RunAsPrincipalPermission"); return false; } final AuthorizedAuthenticationState newState = nameAssignedState.doAuthorization(false); if (newState == null) { return false; } if (! stateRef.compareAndSet(this, newState)) { // try again return stateRef.get().authorize(authorizationId, authorizeRunAs); } ok = true; return true; } finally { if (! ok) realmIdentity.dispose(); } } @Override void setMechanismRealmName(final String realmName) { final MechanismRealmConfiguration currentConfiguration = getMechanismRealmConfiguration(); final MechanismConfiguration mechanismConfiguration = getMechanismConfiguration(); if (mechanismConfiguration.getMechanismRealmNames().isEmpty()) { // no realms are configured throw log.invalidMechRealmSelection(realmName); } final MechanismRealmConfiguration configuration = mechanismConfiguration.getMechanismRealmConfiguration(realmName); if (configuration == null) { throw log.invalidMechRealmSelection(realmName); } if (currentConfiguration != configuration) { throw log.mechRealmAlreadySelected(); } } @Override void setMechanismInformation(MechanismInformation mechanismInformation) { throw log.tooLateToSetMechanismInformation(); } abstract SecurityIdentity getSourceIdentity(); } /** * State shared among both the initial state and the realm-assigned state, where no authentication name is yet set. */ abstract class UnassignedState extends ActiveState { final SecurityIdentity capturedIdentity; final MechanismConfiguration mechanismConfiguration; final IdentityCredentials privateCredentials; final IdentityCredentials publicCredentials; final Attributes runtimeAttributes; UnassignedState(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) { this.capturedIdentity = capturedIdentity; this.mechanismConfiguration = mechanismConfiguration; this.privateCredentials = privateCredentials; this.publicCredentials = publicCredentials; this.runtimeAttributes = runtimeAttributes; } SecurityIdentity getSourceIdentity() { return capturedIdentity; } @Override SecurityDomain getSecurityDomain() { return capturedIdentity.getSecurityDomain(); } @Override void fail(final boolean requireInProgress) { final AtomicReference stateRef = getStateRef(); if (! stateRef.compareAndSet(this, FAILED)) { // recurse & retry stateRef.get().fail(requireInProgress); } } @Override boolean authorizeAnonymous(final boolean requireLoginPermission) { final AtomicReference stateRef = getStateRef(); final SecurityIdentity anonymousIdentity = getSecurityDomain().getAnonymousSecurityIdentity(); return (! requireLoginPermission || anonymousIdentity.implies(LoginPermission.getInstance())) && (stateRef.compareAndSet(this, new AnonymousAuthorizedState(anonymousIdentity)) || stateRef.get().authorizeAnonymous(requireLoginPermission)); } @Override boolean authorize(final boolean requireLoginPermission) throws RealmUnavailableException { final SecurityIdentity capturedIdentity = this.capturedIdentity; if (capturedIdentity.isAnonymous()) { return authorizeAnonymous(requireLoginPermission); } final AtomicReference stateRef = getStateRef(); return (! requireLoginPermission || capturedIdentity.implies(LoginPermission.getInstance())) && (stateRef.compareAndSet(this, new AuthorizedState(capturedIdentity, capturedIdentity.getPrincipal(), capturedIdentity.getRealmInfo(), mechanismConfiguration, getMechanismRealmConfiguration())) || stateRef.get().authorize(requireLoginPermission)); } @Override boolean importIdentity(final SecurityIdentity importedIdentity) throws RealmUnavailableException { // As long as a name is not yet assigned, we can authorize an imported identity final RealmInfo evidenceRealmInfo = importedIdentity.getRealmInfo(); final SecurityRealm evidenceSecurityRealm = evidenceRealmInfo.getSecurityRealm(); final SecurityDomain evidenceSecurityDomain = importedIdentity.getSecurityDomain(); final AtomicReference stateRef = getStateRef(); final SecurityIdentity sourceIdentity = getSourceIdentity(); final SecurityDomain domain = sourceIdentity.getSecurityDomain(); // Check that the given security identity evidence either corresponds to the same realm that created the // current authentication identity or it corresponds to a domain that is trusted by the current domain if (importedIdentity.isAnonymous()) { AnonymousAuthorizedState newState = new AnonymousAuthorizedState(domain.getAnonymousSecurityIdentity()); return stateRef.compareAndSet(this, newState) || stateRef.get().importIdentity(importedIdentity); } final Principal importedPrincipal = importedIdentity.getPrincipal(); if (domain == importedIdentity.getSecurityDomain()) { // it's authorized already because it's the same domain AuthorizedState newState = new AuthorizedState(importedIdentity, importedPrincipal, importedIdentity.getRealmInfo(), mechanismConfiguration, getMechanismRealmConfiguration()); return stateRef.compareAndSet(this, newState) || stateRef.get().importIdentity(importedIdentity); } boolean trusted = false; // it didn't come from our domain. Check to see if it came from a trusted domain. if (domain.trustsDomain(evidenceSecurityDomain)) { trusted = true; } // Finally, run the identity through the normal name selection process. final State state = assignName(sourceIdentity, mechanismConfiguration, getMechanismRealmConfiguration(), importedPrincipal, null, privateCredentials, publicCredentials, runtimeAttributes); if (!state.isNameAssigned()) { return false; } final NameAssignedState nameState = (NameAssignedState) state; final RealmIdentity realmIdentity = nameState.getRealmIdentity(); boolean ok = false; try { if (! trusted) { if (nameState.getRealmInfo().getSecurityRealm() != evidenceSecurityRealm) { // mapped realm does not correspond with the imported realm name return false; } } // with the name we have now, try and authorize final AuthorizedAuthenticationState authzState = nameState.doAuthorization(false); if (authzState == null) { return false; } if (! stateRef.compareAndSet(this, authzState)) { return stateRef.get().importIdentity(importedIdentity); } ok = true; return true; } finally { if (! ok) realmIdentity.dispose(); } } @Override SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { return getSecurityDomain().getEvidenceVerifySupport(evidenceType, algorithmName); } @Override boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { // TODO: this method probably should never cause a state change... consider setEvidence or something instead? final AtomicReference stateRef = getStateRef(); setDecodedEvidencePrincipal(evidence); Principal evidencePrincipal = evidence.getDecodedPrincipal(); log.tracef("Evidence verification: evidence = %s evidencePrincipal = %s", evidence, evidencePrincipal); final MechanismRealmConfiguration mechanismRealmConfiguration = getMechanismRealmConfiguration(); if (evidencePrincipal != null) { final State newState = assignName(getSourceIdentity(), mechanismConfiguration, mechanismRealmConfiguration, evidencePrincipal, evidence, privateCredentials, publicCredentials, runtimeAttributes); if (! newState.verifyEvidence(evidence)) { if (newState.isNameAssigned()) { ((NameAssignedState)newState).realmIdentity.dispose(); } return false; } if (! stateRef.compareAndSet(this, newState)) { if (newState.isNameAssigned()) { ((NameAssignedState)newState).realmIdentity.dispose(); } return stateRef.get().verifyEvidence(evidence); } return true; } Class evidenceType = evidence.getClass(); String algorithm = evidence instanceof AlgorithmEvidence ? ((AlgorithmEvidence) evidence).getAlgorithm() : null; // verify evidence with no name set: use the realms to find a match (SSO scenario, etc.) final SecurityDomain domain = getSecurityDomain(); final Collection realmInfos = domain.getRealmInfos(); RealmIdentity realmIdentity = null; RealmInfo realmInfo = null; for (RealmInfo info : realmInfos) { try { realmIdentity = info.getSecurityRealm().getRealmIdentity(evidence); if (realmIdentity.getEvidenceVerifySupport(evidenceType, algorithm).mayBeSupported()) { realmInfo = info; break; } else { realmIdentity.dispose(); } } catch (RealmUnavailableException e) { SecurityDomain.safeHandleSecurityEvent(domain, new SecurityRealmUnavailableEvent(domain.getCurrentSecurityIdentity(), info.getName())); throw e; } } if (realmInfo == null) { // no verification possible, no identity found return false; } if (! realmIdentity.verifyEvidence(evidence)) { realmIdentity.dispose(); return false; } final Principal resolvedPrincipal = realmIdentity.getRealmIdentityPrincipal(); if (resolvedPrincipal == null) { // we have to have a principal realmIdentity.dispose(); return false; } final NameAssignedState newState = new NameAssignedState(getSourceIdentity(), realmInfo, realmIdentity, resolvedPrincipal, mechanismConfiguration, mechanismRealmConfiguration, privateCredentials, publicCredentials, runtimeAttributes); if (! stateRef.compareAndSet(this, newState)) { realmIdentity.dispose(); return stateRef.get().verifyEvidence(evidence); } return true; } @Override void setPrincipal(final Principal principal, final boolean exclusive) throws RealmUnavailableException { Assert.checkNotNullParam("principal", principal); final AtomicReference stateRef = getStateRef(); final State newState = assignName(capturedIdentity, mechanismConfiguration, getMechanismRealmConfiguration(), principal, null, privateCredentials, publicCredentials, exclusive, runtimeAttributes); if (! stateRef.compareAndSet(this, newState)) { if (newState.isNameAssigned()) { ((NameAssignedState)newState).realmIdentity.dispose(); } stateRef.get().setPrincipal(principal, exclusive); } } @Override MechanismConfiguration getMechanismConfiguration() { return mechanismConfiguration; } IdentityCredentials getPrivateCredentials() { return privateCredentials; } IdentityCredentials getPublicCredentials() { return publicCredentials; } Attributes getRuntimeAttributes() { return runtimeAttributes; } } final class InitialState extends UnassignedState { private final MechanismConfigurationSelector mechanismConfigurationSelector; InitialState(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final MechanismConfigurationSelector mechanismConfigurationSelector, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) { super(capturedIdentity, mechanismConfiguration, privateCredentials, publicCredentials, runtimeAttributes); this.mechanismConfigurationSelector = mechanismConfigurationSelector; } @Override void setMechanismRealmName(final String realmName) { final MechanismConfiguration mechanismConfiguration = getMechanismConfiguration(); if (mechanismConfiguration.getMechanismRealmNames().isEmpty()) { // no realms are configured throw log.invalidMechRealmSelection(realmName); } final MechanismRealmConfiguration configuration = mechanismConfiguration.getMechanismRealmConfiguration(realmName); if (configuration == null) { throw log.invalidMechRealmSelection(realmName); } final AtomicReference stateRef = getStateRef(); if (! stateRef.compareAndSet(this, new RealmAssignedState(capturedIdentity, mechanismConfiguration, configuration, privateCredentials, publicCredentials, runtimeAttributes))) { stateRef.get().setMechanismRealmName(realmName); } } @Override MechanismRealmConfiguration getMechanismRealmConfiguration() { final Collection mechanismRealmNames = mechanismConfiguration.getMechanismRealmNames(); final Iterator iterator = mechanismRealmNames.iterator(); if (iterator.hasNext()) { // use the default realm return mechanismConfiguration.getMechanismRealmConfiguration(iterator.next()); } else { return MechanismRealmConfiguration.NO_REALM; } } @Override void setMechanismInformation(MechanismInformation mechanismInformation) { InactiveState inactiveState = new InactiveState(capturedIdentity, mechanismConfigurationSelector, mechanismInformation, privateCredentials, publicCredentials, runtimeAttributes); InitialState newState = inactiveState.selectMechanismConfiguration(); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().setMechanismInformation(mechanismInformation); } } void addPublicCredential(final Credential credential) { final InitialState newState = new InitialState(getSourceIdentity(), getMechanismConfiguration(), mechanismConfigurationSelector, getPrivateCredentials(), getPublicCredentials().withCredential(credential), runtimeAttributes); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addPublicCredential(credential); } } @Override void addPrivateCredential(final Credential credential) { final InitialState newState = new InitialState(getSourceIdentity(), getMechanismConfiguration(), mechanismConfigurationSelector, getPrivateCredentials().withCredential(credential), getPublicCredentials(), runtimeAttributes); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addPublicCredential(credential); } } void addRuntimeAttributes(final Attributes runtimeAttributes) { final InitialState newState = new InitialState(getSourceIdentity(), getMechanismConfiguration(), mechanismConfigurationSelector, getPrivateCredentials(), getPublicCredentials(), AggregateAttributes.aggregateOf(getRuntimeAttributes(), runtimeAttributes)); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addRuntimeAttributes(runtimeAttributes); } } } final class RealmAssignedState extends UnassignedState { final MechanismRealmConfiguration mechanismRealmConfiguration; RealmAssignedState(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) { super(capturedIdentity, mechanismConfiguration, privateCredentials, publicCredentials, runtimeAttributes); this.mechanismRealmConfiguration = mechanismRealmConfiguration; } @Override MechanismRealmConfiguration getMechanismRealmConfiguration() { return mechanismRealmConfiguration; } @Override void addPublicCredential(final Credential credential) { final RealmAssignedState newState = new RealmAssignedState(getSourceIdentity(), getMechanismConfiguration(), getMechanismRealmConfiguration(), getPrivateCredentials(), getPublicCredentials().withCredential(credential), runtimeAttributes); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addPublicCredential(credential); } } @Override void addPrivateCredential(final Credential credential) { final RealmAssignedState newState = new RealmAssignedState(getSourceIdentity(), getMechanismConfiguration(), getMechanismRealmConfiguration(), getPrivateCredentials().withCredential(credential), getPublicCredentials(), runtimeAttributes); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addPublicCredential(credential); } } @Override void addRuntimeAttributes(final Attributes runtimeAttributes) { final RealmAssignedState newState = new RealmAssignedState(getSourceIdentity(), getMechanismConfiguration(), getMechanismRealmConfiguration(), getPrivateCredentials(), getPublicCredentials(), AggregateAttributes.aggregateOf(getRuntimeAttributes(), runtimeAttributes)); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addRuntimeAttributes(runtimeAttributes); } } } final class InvalidNameState extends UnassignedState { final MechanismRealmConfiguration mechanismRealmConfiguration; InvalidNameState(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) { super(capturedIdentity, mechanismConfiguration, privateCredentials, publicCredentials, runtimeAttributes); this.mechanismRealmConfiguration = mechanismRealmConfiguration; } @Override MechanismRealmConfiguration getMechanismRealmConfiguration() { return mechanismRealmConfiguration; } @Override RealmIdentity getRealmIdentity() { return RealmIdentity.NON_EXISTENT; } @Override void fail(final boolean requireInProgress) { final AtomicReference stateRef = getStateRef(); if (! stateRef.compareAndSet(this, FAILED)) { // recurse & retry stateRef.get().fail(requireInProgress); } } @Override boolean isDone() { return true; } } final class NameAssignedState extends ActiveState { private final SecurityIdentity capturedIdentity; private final RealmInfo realmInfo; private final RealmIdentity realmIdentity; private final Principal authenticationPrincipal; private final MechanismConfiguration mechanismConfiguration; private final MechanismRealmConfiguration mechanismRealmConfiguration; private final IdentityCredentials privateCredentials; private final IdentityCredentials publicCredentials; private final Attributes runtimeAttributes; NameAssignedState(final SecurityIdentity capturedIdentity, final RealmInfo realmInfo, final RealmIdentity realmIdentity, final Principal authenticationPrincipal, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) { this.capturedIdentity = capturedIdentity; this.realmInfo = realmInfo; this.realmIdentity = realmIdentity; this.authenticationPrincipal = authenticationPrincipal; this.mechanismConfiguration = mechanismConfiguration; this.mechanismRealmConfiguration = mechanismRealmConfiguration; this.privateCredentials = privateCredentials; this.publicCredentials = publicCredentials; this.runtimeAttributes = runtimeAttributes; } @Override MechanismConfiguration getMechanismConfiguration() { return mechanismConfiguration; } @Override MechanismRealmConfiguration getMechanismRealmConfiguration() { return mechanismRealmConfiguration; } @Override Principal getAuthenticationPrincipal() { return authenticationPrincipal; } @Override RealmIdentity getRealmIdentity() { return realmIdentity; } @Override SecurityDomain getSecurityDomain() { return capturedIdentity.getSecurityDomain(); } @Override SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { return realmIdentity.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); } @Override SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { return realmIdentity.getEvidenceVerifySupport(evidenceType, algorithmName); } @Override C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { return realmIdentity.getCredential(credentialType, algorithmName, parameterSpec); } @Override boolean authorize(final boolean requireLoginPermission) throws RealmUnavailableException { AuthorizedAuthenticationState newState = doAuthorization(requireLoginPermission); if (newState == null) { return false; } final AtomicReference stateRef = getStateRef(); // retry if necessary return stateRef.compareAndSet(this, newState) || stateRef.get().authorize(requireLoginPermission); } AuthorizedAuthenticationState doAuthorization(final boolean requireLoginPermission) throws RealmUnavailableException { final RealmIdentity realmIdentity = this.realmIdentity; if (! realmIdentity.exists()) { ElytronMessages.log.trace("Authorization failed - realm identity does not exists"); return null; } final RealmInfo realmInfo = this.realmInfo; final Principal authenticationPrincipal = this.authenticationPrincipal; final AuthorizationIdentity authorizationIdentity = runtimeAttributes == Attributes.EMPTY ? realmIdentity.getAuthorizationIdentity() : AuthorizationIdentity.basicIdentity(realmIdentity.getAuthorizationIdentity(), runtimeAttributes); final SecurityDomain domain = capturedIdentity.getSecurityDomain(); SecurityIdentity authorizedIdentity = Assert.assertNotNull(domain.transform(new SecurityIdentity(domain, authenticationPrincipal, realmInfo, authorizationIdentity, domain.getCategoryRoleMappers(), IdentityCredentials.NONE, IdentityCredentials.NONE))); authorizedIdentity = authorizedIdentity.withPublicCredentials(publicCredentials).withPrivateCredentials(privateCredentials); if (log.isTraceEnabled()) { log.tracef("Authorizing principal %s.", authenticationPrincipal.getName()); if (authorizationIdentity != null) { log.tracef("Authorizing against the following attributes: %s => %s", authorizationIdentity.getAttributes().keySet(), authorizationIdentity.getAttributes().values()); log.tracef("Authorizing against the following runtime attributes: %s => %s", authorizationIdentity.getRuntimeAttributes().keySet(), authorizationIdentity.getRuntimeAttributes().values()); } else { log.tracef("Authorizing against the following attributes: Cannot obtain the attributes. Authorization Identity is null."); } } if (requireLoginPermission) { if (! authorizedIdentity.implies(LoginPermission.getInstance())) { SecurityRealm.safeHandleRealmEvent(realmInfo.getSecurityRealm(), new RealmIdentityFailedAuthorizationEvent(authorizedIdentity.getAuthorizationIdentity(), authorizedIdentity.getPrincipal(), authenticationPrincipal)); ElytronMessages.log.trace("Authorization failed - identity does not have required LoginPermission"); return null; } else { SecurityRealm.safeHandleRealmEvent(realmInfo.getSecurityRealm(), new RealmIdentitySuccessfulAuthorizationEvent(authorizedIdentity.getAuthorizationIdentity(), authorizedIdentity.getPrincipal(), authenticationPrincipal)); } } ElytronMessages.log.trace("Authorization succeed"); return new AuthorizedAuthenticationState(authorizedIdentity, authenticationPrincipal, realmInfo, realmIdentity, mechanismRealmConfiguration, mechanismConfiguration); } @Override boolean authorize(final Principal authorizationId, final boolean authorizeRunAs) throws RealmUnavailableException { final AuthorizedAuthenticationState authzState = doAuthorization(true); if (authzState == null) { return false; } final AuthorizedState newState = authzState.authorizeRunAs(authorizationId, authorizeRunAs); if (newState == null) { return false; } final AtomicReference stateRef = getStateRef(); if (! stateRef.compareAndSet(this, newState)) { return stateRef.get().authorize(authorizationId, authorizeRunAs); } if (newState != authzState) getRealmIdentity().dispose(); return true; } @Override SecurityIdentity getSourceIdentity() { return capturedIdentity; } @Override boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { // At this stage, we just verify that the evidence principal matches, and verify it with the realm. final Principal evidencePrincipal = evidence.getDecodedPrincipal(); return (evidencePrincipal == null || isSamePrincipal(evidencePrincipal)) && getRealmIdentity().verifyEvidence(evidence); } @Override void updateCredential(Credential credential) throws RealmUnavailableException { realmIdentity.updateCredential(credential); } @Override void succeed() { throw log.cannotSucceedNotAuthorized(); } @Override void fail(final boolean requireInProgress) { final SecurityIdentity capturedIdentity = getSourceIdentity(); final AtomicReference stateRef = getStateRef(); if (!stateRef.compareAndSet(this, FAILED)) { stateRef.get().fail(requireInProgress); return; } SecurityRealm.safeHandleRealmEvent(getRealmInfo().getSecurityRealm(), new RealmFailedAuthenticationEvent(getRealmIdentityWithRuntimeAttributes(), null, null)); SecurityDomain.safeHandleSecurityEvent(capturedIdentity.getSecurityDomain(), new SecurityAuthenticationFailedEvent(capturedIdentity, realmIdentity.getRealmIdentityPrincipal())); realmIdentity.dispose(); } private RealmIdentity getRealmIdentityWithRuntimeAttributes() { return new RealmIdentity() { @Override public Principal getRealmIdentityPrincipal() { return realmIdentity.getRealmIdentityPrincipal(); } @Override public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { return realmIdentity.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); } @Override public C getCredential(Class credentialType) throws RealmUnavailableException { return realmIdentity.getCredential(credentialType); } @Override public SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) throws RealmUnavailableException { return realmIdentity.getEvidenceVerifySupport(evidenceType, algorithmName); } @Override public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException { return realmIdentity.verifyEvidence(evidence); } @Override public boolean exists() throws RealmUnavailableException { return realmIdentity.exists(); } @Override public AuthorizationIdentity getAuthorizationIdentity() throws RealmUnavailableException { if (realmIdentity.exists()) { return AuthorizationIdentity.basicIdentity(realmIdentity.getAuthorizationIdentity(), runtimeAttributes); } else { return AuthorizationIdentity.basicIdentity(AuthorizationIdentity.EMPTY, runtimeAttributes); } } }; } @Override void setPrincipal(final Principal principal, final boolean exclusive) { if (isSamePrincipal(principal)) { return; } throw log.nameAlreadySet(); } @Override boolean isSamePrincipal(Principal principal) { final SecurityDomain domain = capturedIdentity.getSecurityDomain(); principal = rewriteAll(principal, mechanismRealmConfiguration.getPreRealmRewriter(), mechanismConfiguration.getPreRealmRewriter(), domain.getPreRealmRewriter()); return authenticationPrincipal.equals(principal); } @Override void addPublicCredential(final Credential credential) { final NameAssignedState newState = new NameAssignedState(getSourceIdentity(), getRealmInfo(), getRealmIdentity(), getAuthenticationPrincipal(), getMechanismConfiguration(), getMechanismRealmConfiguration(), privateCredentials, publicCredentials.withCredential(credential), runtimeAttributes); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addPublicCredential(credential); } } @Override void addPrivateCredential(final Credential credential) { final NameAssignedState newState = new NameAssignedState(getSourceIdentity(), getRealmInfo(), getRealmIdentity(), getAuthenticationPrincipal(), getMechanismConfiguration(), getMechanismRealmConfiguration(), privateCredentials.withCredential(credential), publicCredentials, runtimeAttributes); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addPublicCredential(credential); } } @Override void addRuntimeAttributes(final Attributes runtimeAttributes) { final NameAssignedState newState = new NameAssignedState(getSourceIdentity(), getRealmInfo(), getRealmIdentity(), getAuthenticationPrincipal(), getMechanismConfiguration(), getMechanismRealmConfiguration(), privateCredentials, publicCredentials, AggregateAttributes.aggregateOf(this.runtimeAttributes, runtimeAttributes)); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addRuntimeAttributes(runtimeAttributes); } } RealmInfo getRealmInfo() { return realmInfo; } } final class AnonymousAuthorizedState extends ActiveState { private final SecurityIdentity anonymousIdentity; AnonymousAuthorizedState(final SecurityIdentity anonymousIdentity) { this.anonymousIdentity = anonymousIdentity; } @Override MechanismConfiguration getMechanismConfiguration() { return MechanismConfiguration.EMPTY; } @Override MechanismRealmConfiguration getMechanismRealmConfiguration() { return MechanismRealmConfiguration.NO_REALM; } @Override SecurityIdentity getAuthorizedIdentity() { return anonymousIdentity; } @Override Principal getAuthenticationPrincipal() { return AnonymousPrincipal.getInstance(); } @Override boolean isSamePrincipal(final Principal principal) { return principal instanceof AnonymousPrincipal; } @Override SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { return SupportLevel.UNSUPPORTED; } @Override SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { return SupportLevel.UNSUPPORTED; } @Override C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { return null; } @Override boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { return false; } @Override RealmIdentity getRealmIdentity() { return RealmIdentity.ANONYMOUS; } @Override SecurityDomain getSecurityDomain() { return anonymousIdentity.getSecurityDomain(); } @Override boolean authorizeAnonymous(final boolean requireLoginPermission) { return true; } @Override void setPrincipal(final Principal principal, final boolean exclusive) throws RealmUnavailableException { if (! (principal instanceof AnonymousPrincipal)) { super.setPrincipal(principal, exclusive); } } @Override boolean authorize(final boolean requireLoginPermission) throws RealmUnavailableException { return ! requireLoginPermission || anonymousIdentity.implies(LoginPermission.getInstance()); } @Override void updateCredential(Credential credential) throws RealmUnavailableException { // no-op } @Override void succeed() { final AtomicReference stateRef = getStateRef(); if (! stateRef.compareAndSet(this, new CompleteState(anonymousIdentity))) { stateRef.get().succeed(); } } @Override void fail(final boolean requireInProgress) { final AtomicReference stateRef = getStateRef(); if (! stateRef.compareAndSet(this, FAILED)) { stateRef.get().fail(requireInProgress); } } @Override SecurityIdentity getSourceIdentity() { return anonymousIdentity; } } class AuthorizedState extends ActiveState { private final SecurityIdentity authorizedIdentity; private final Principal authenticationPrincipal; private final RealmInfo realmInfo; private final MechanismConfiguration mechanismConfiguration; private final MechanismRealmConfiguration mechanismRealmConfiguration; AuthorizedState(final SecurityIdentity authorizedIdentity, final Principal authenticationPrincipal, final RealmInfo realmInfo, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration) { this.authorizedIdentity = authorizedIdentity; this.authenticationPrincipal = authenticationPrincipal; this.realmInfo = realmInfo; this.mechanismConfiguration = mechanismConfiguration; this.mechanismRealmConfiguration = mechanismRealmConfiguration; } @Override MechanismRealmConfiguration getMechanismRealmConfiguration() { return mechanismRealmConfiguration; } @Override MechanismConfiguration getMechanismConfiguration() { return mechanismConfiguration; } @Override SecurityIdentity getAuthorizedIdentity() { return authorizedIdentity; } @Override Principal getAuthenticationPrincipal() { return authenticationPrincipal; } @Override SecurityDomain getSecurityDomain() { return authorizedIdentity.getSecurityDomain(); } @Override SecurityIdentity getSourceIdentity() { return authorizedIdentity; } @Override boolean isSamePrincipal(Principal principal) { final SecurityDomain domain = authorizedIdentity.getSecurityDomain(); principal = rewriteAll(principal, mechanismRealmConfiguration.getPreRealmRewriter(), mechanismConfiguration.getPreRealmRewriter(), domain.getPreRealmRewriter()); return authenticationPrincipal.equals(principal); } RealmInfo getRealmInfo() { return realmInfo; } @Override boolean authorize(final boolean requireLoginPermission) throws RealmUnavailableException { return ! requireLoginPermission || authorizedIdentity.implies(LoginPermission.getInstance()); } AuthorizedState authorizeRunAs(final Principal authorizationId, final boolean authorizeRunAs) throws RealmUnavailableException { if (isSamePrincipal(authorizationId)) { ElytronMessages.log.trace("RunAs authorization succeed - the same identity"); return this; } final State state = assignName(authorizedIdentity, getMechanismConfiguration(), getMechanismRealmConfiguration(), authorizationId, null, IdentityCredentials.NONE, IdentityCredentials.NONE, Attributes.EMPTY); if (!state.isNameAssigned()) { ElytronMessages.log.tracef("RunAs authorization failed - unable to assign identity name"); return null; } final NameAssignedState nameAssignedState = (NameAssignedState) state; final RealmIdentity realmIdentity = nameAssignedState.getRealmIdentity(); boolean ok = false; try { String targetName = nameAssignedState.getAuthenticationPrincipal().getName(); if (authorizeRunAs && ! authorizedIdentity.implies(new RunAsPrincipalPermission(targetName))) { ElytronMessages.log.tracef("RunAs authorization failed - identity does not have required RunAsPrincipalPermission(%s)", targetName); return null; } final AuthorizedAuthenticationState newState = nameAssignedState.doAuthorization(false); if (newState == null) { ElytronMessages.log.trace("RunAs authorization failed"); return null; } ok = true; ElytronMessages.log.trace("RunAs authorization succeed"); return newState; } finally { if (! ok) realmIdentity.dispose(); } } @Override void succeed() { if (authorizedIdentity != null) { return; } super.succeed(); } void addPublicCredential(final Credential credential) { final SecurityIdentity sourceIdentity = getSourceIdentity(); final AuthorizedState newState = new AuthorizedState(sourceIdentity.withPublicCredential(credential), getAuthenticationPrincipal(), getRealmInfo(), getMechanismConfiguration(), getMechanismRealmConfiguration()); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addPublicCredential(credential); } } @Override void addPrivateCredential(final Credential credential) { final SecurityIdentity sourceIdentity = getSourceIdentity(); final AuthorizedState newState = new AuthorizedState(sourceIdentity.withPrivateCredential(credential), getAuthenticationPrincipal(), getRealmInfo(), getMechanismConfiguration(), getMechanismRealmConfiguration()); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addPrivateCredential(credential); } } @Override void addRuntimeAttributes(final Attributes runtimeAttributes) { final SecurityIdentity sourceIdentity = getSourceIdentity(); final AuthorizedState newState = new AuthorizedState(sourceIdentity.withRuntimeAttributes(runtimeAttributes), getAuthenticationPrincipal(), getRealmInfo(), getMechanismConfiguration(), getMechanismRealmConfiguration()); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addRuntimeAttributes(runtimeAttributes); } } } final class AuthorizedAuthenticationState extends AuthorizedState { private final RealmIdentity realmIdentity; AuthorizedAuthenticationState(final SecurityIdentity authorizedIdentity, final Principal authenticationPrincipal, final RealmInfo realmInfo, final RealmIdentity realmIdentity, final MechanismRealmConfiguration mechanismRealmConfiguration, final MechanismConfiguration mechanismConfiguration) { super(authorizedIdentity, authenticationPrincipal, realmInfo, mechanismConfiguration, mechanismRealmConfiguration); this.realmIdentity = realmIdentity; } @Override SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { return realmIdentity.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); } @Override SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { return realmIdentity.getEvidenceVerifySupport(evidenceType, algorithmName); } @Override C getCredential(final Class credentialType, String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { return realmIdentity.getCredential(credentialType, algorithmName, parameterSpec); } @Override boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { return realmIdentity.verifyEvidence(evidence); } @Override RealmIdentity getRealmIdentity() { return realmIdentity; } @Override void updateCredential(Credential credential) throws RealmUnavailableException { realmIdentity.updateCredential(credential); } @Override void succeed() { final SecurityIdentity authorizedIdentity = getSourceIdentity(); final AtomicReference stateRef = getStateRef(); if (! stateRef.compareAndSet(this, new CompleteState(authorizedIdentity))) { stateRef.get().succeed(); return; } SecurityRealm.safeHandleRealmEvent(getRealmInfo().getSecurityRealm(), new RealmSuccessfulAuthenticationEvent(realmIdentity, authorizedIdentity.getAuthorizationIdentity(), null, null)); SecurityDomain.safeHandleSecurityEvent(authorizedIdentity.getSecurityDomain(), new SecurityAuthenticationSuccessfulEvent(authorizedIdentity)); realmIdentity.dispose(); } @Override void fail(final boolean requireInProgress) { final SecurityIdentity authorizedIdentity = getSourceIdentity(); final AtomicReference stateRef = getStateRef(); if (! stateRef.compareAndSet(this, FAILED)) { stateRef.get().fail(requireInProgress); return; } SecurityRealm.safeHandleRealmEvent(getRealmInfo().getSecurityRealm(), new RealmFailedAuthenticationEvent(realmIdentity, null, null)); SecurityDomain.safeHandleSecurityEvent(authorizedIdentity.getSecurityDomain(), new SecurityAuthenticationFailedEvent(authorizedIdentity, realmIdentity.getRealmIdentityPrincipal())); realmIdentity.dispose(); } @Override void addPublicCredential(final Credential credential) { final SecurityIdentity sourceIdentity = getSourceIdentity(); final AuthorizedAuthenticationState newState = new AuthorizedAuthenticationState(sourceIdentity.withPublicCredential(credential), getAuthenticationPrincipal(), getRealmInfo(), getRealmIdentity(), getMechanismRealmConfiguration(), getMechanismConfiguration()); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addPublicCredential(credential); } } @Override void addPrivateCredential(final Credential credential) { final SecurityIdentity sourceIdentity = getSourceIdentity(); final AuthorizedAuthenticationState newState = new AuthorizedAuthenticationState(sourceIdentity.withPrivateCredential(credential), getAuthenticationPrincipal(), getRealmInfo(), getRealmIdentity(), getMechanismRealmConfiguration(), getMechanismConfiguration()); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addPrivateCredential(credential); } } @Override void addRuntimeAttributes(final Attributes runtimeAttributes) { final SecurityIdentity sourceIdentity = getSourceIdentity(); final AuthorizedAuthenticationState newState = new AuthorizedAuthenticationState(sourceIdentity.withRuntimeAttributes(runtimeAttributes), getAuthenticationPrincipal(), getRealmInfo(), getRealmIdentity(), getMechanismRealmConfiguration(), getMechanismConfiguration()); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addRuntimeAttributes(runtimeAttributes); } } } static final class CompleteState extends State { private final SecurityIdentity identity; public CompleteState(final SecurityIdentity identity) { this.identity = identity; } @Override SecurityIdentity getAuthorizedIdentity() { return identity; } @Override boolean isDone() { return true; } void succeed() { // always works } } private static final State FAILED = new State() { @Override void fail(final boolean requireInProgress) { } @Override boolean isDone() { return true; } }; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy