/* * 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._private.ElytronMessages.log; import java.io.IOException; 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.Iterator; 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._private.ElytronMessages; 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.AvailableRealmsCallback; import org.wildfly.security.auth.callback.CachedIdentityAuthorizeCallback; import org.wildfly.security.auth.callback.CallbackUtil; import org.wildfly.security.auth.callback.CredentialCallback; import org.wildfly.security.auth.callback.CredentialUpdateCallback; 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.MechanismInformationCallback; import org.wildfly.security.auth.callback.IdentityCredentialCallback; import org.wildfly.security.auth.callback.PeerPrincipalCallback; 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.authz.AuthorizationIdentity; import org.wildfly.security.credential.Credential; import org.wildfly.security.credential.PasswordCredential; import org.wildfly.security.credential.source.CredentialSource; 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.x500.X500; /** * 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}. * <p> * There are various effective states, described as follows: * <ul> * <li>The <em>inactive</em> state.</li> * <li> * The <em>unassigned</em> states: * <ul> * <li><em>Initial</em></li> * <li><em>Realm-assigned</em></li> * </ul> * </li> * <li>The <em>assigned</em> state</li> * <li> * The <em>authorized</em> states: * <ul> * <li><em>Anonymous-authorized</em></li> * <li><em>Authorized</em></li> * <li><em>Authorized-authenticated</em></li> * </ul> * </li> * <li> * The <em>terminal</em> states: * <ul> * <li><em>Complete</em></li> * <li><em>Failed</em></li> * </ul> * </li> * </ul> * * <p> * When an instance of this class is first constructed, it is in the <em>inactive</em> state. In this state, the context retains * a <em>captured {@linkplain SecurityIdentity identity}</em> and contains a reference to a * <em>{@linkplain MechanismConfigurationSelector}</em>. The <em>captured identity</em> 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 <em>{@linkplain MechanismConfiguration}</em> can be selected. * <p> * Once authentication commences the state will automatically transition to the <em>initial</em> state. In this state, the * context retains an <em>captured {@linkplain SecurityIdentity identity}</em> and a <em>{@linkplain MechanismConfiguration mechanism configuration}</em> * which was resolved from the information supplied to the <em>inactive</em> state. The <em>captured identity</em> may be * used for various context-sensitive authorization decisions. The <em>mechanism configuration</em> is used to associate * an authentication mechanism-specific configuration, including rewriters, {@linkplain MechanismRealmConfiguration mechanism realms}, * server credential factories, and more. * <p> * 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. <a href="https://tools.ietf.org/html/rfc2831">the DIGEST-MD5 SASL mechanism</a>), 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 <em>initial</em> state causes a transition to the <em>realm-assigned</em> 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). * <p> * The <em>realm-assigned</em> state is nearly identical to the <em>initial</em> state, except that from this state, the * mechanism realm-specific configuration is applied to all subsequent operation. * <p> * From these <em>unassigned</em> states, several possible actions may be taken, depending on the necessary progression * of the authentication: * <ul> * <li> * A <em>name</em> 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 <em>mechanism configuration</em>, and/or the <em>mechanism realm configuration</em>. The * <em>{@linkplain SecurityRealm realm}</em> that is the resultant target of the mapping is queried for a * <em>{@linkplain RealmIdentity realm identity}</em>. The <em>realm identity</em> may or may not be * existent; this status will affect the outcome of certain operations in subsequent states (as described below). * After the <em>realm identity</em> 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 * <em>{@linkplain #getAuthenticationPrincipal() authentication principal}</em> which may subsequently be queried. * </li> * <li> * A <em>principal</em> 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. * </li> * <li> * A unit of <em>{@linkplain Evidence evidence}</em> may be verified. This is mostly described below in the * context of the <em>assigned</em> state, but with the important distinction the evidence is first examined * to locate the corresponding evidence, in the following steps: * <ul> * <li> * 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. * </li> * <li> * Secondly, the evidence is socialized to each <em>realm</em> in turn, to see if a realm can recognize * and {@linkplain SecurityRealm#getRealmIdentity(Principal) locate} an identity based on * the evidence. If so, the <em>realm identity</em> 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. * </li> * <li>Finally, if none of these steps succeeds, the verification fails and no state transition occurs.</li> * </ul> * </li> * <li> * An <em>identity</em> 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 <em>authorized identity</em>. The {@code SecurityIdentity} may * be from the same <em>domain</em> or from a different one. * <p> * If the <em>identity</em> being imported is from the same security domain as this context, then the <em>identity</em> * is implicitly <em>authorized</em> for usage, entering the <em>authorized</em> state described below. * <p> * If the <em>identity</em> being imported is not from the same security domain, then the principal is extracted * from the identity and used to assign a <em>realm identity</em> in the same manner as {@link #setAuthenticationPrincipal(Principal)}. * The <em>domain</em> is then {@linkplain SecurityDomain.Builder#setTrustedSecurityDomainPredicate(Predicate) queried} * to determine whether the target identity's source <em>domain</em> is <em>trusted</em>. If so, a normal * <em>authorization</em> is carried out as described below for the <em>assigned</em> state, resulting in an * <em>authorized-authenticated</em> state. If not, then the <em>realm</em> of the <em>realm identity</em> is * compared against the <em>realm</em> of the <em>identity</em> being imported. If they are the same, the * identity is imported and a normal <em>authorization</em> is carried out as described below. * </li> * <li> * An <em>anonymous authorization</em> may be carried out by way of the {@link #authorizeAnonymous()} method. * If the <em>{@linkplain SecurityDomain#getAnonymousSecurityIdentity() anonymous identity}</em> has the * {@link LoginPermission} granted to it, the context will transition into the <em>anonymous-authorized</em> * state; otherwise no state transition occurs. * </li> * <li> * An <em>external authorization</em> may be carried out using the {@link #authorize()} method. The * <em>captured identity</em> (which may be <em>anonymous</em>) is queried for the presence of the * {@link LoginPermission}; if present, the context will transition into the <em>authorized</em> or * <em>anonymous-authorized</em> state (depending on whether the <em>captured identity</em> is <em>anonymous</em>); * otherwise no state transition occurs. * </li> * <li> * An <em>external run-as authorization</em> may be carried out using the {@link #authorize(String)} method. * First, the given name is <em>rewritten</em> in the same manner as the {@link #setAuthenticationName(String)} * method. Then, the <em>captured identity</em> (which may be <em>anonymous</em>) is queried for the presence of a * {@link RunAsPrincipalPermission} for the target name. If present, the <em>authentication name</em> is assigned * as described above, and the resultant <em>realm identity</em> is queried for {@link LoginPermission}. If present, * the context will transition to the <em>authorized-authenticated</em> state. If any step fails, no state transition * occurs. * </li> * <li> * The authentication may be <em>failed</em> by way of the {@link #fail()} method. This method will dispose * of all authentication resources and transition to the <em>failed</em> state. * </li> * </ul> * <p> * In the <em>name-assigned</em> (or, for brevity, <em>assigned</em>) state, the following actions may be performed: * <ul> * <li> * A name or principal may be assigned as above, however the resultant <em>decoded</em> and <em>rewritten</em> name * and <em>realm identity</em> must be identical to the previously selected name and identity. * </li> * <li> * <em>Evidence</em> may be verified. The <em>realm identity</em> is queried directly and no state transitions * will occur. Evidence verification will fail if the evidence has an <em>evidence principal</em> which does * not result in the same <em>realm identity</em> as the current one after <em>decoding</em> and <em>rewriting</em>. * </li> * <li> * An <em>authorization</em> may be performed via the {@link #authorize()} method. If the selected <em>realm identity</em> * possesses the {@link LoginPermission}, then the context transitions to the <em>authorized-authenticated</em> state, * otherwise no state transition occurs. * </li> * <li> * A <em>run-as authorization</em> may be performed via the {@link #authorize(String)} method. * First, the given name is <em>rewritten</em> in the same manner as the {@link #setAuthenticationName(String)} method. * The current identity is then <em>authorized</em> as described above, and then the <em>authorized identity</em> * is tested for a {@link RunAsPrincipalPermission} for the <em>rewritten</em> target name. If authorized, * the context transitions to the <em>authorized</em> state for the <em>realm identity</em> corresponding to the * <em>rewritten</em> name; otherwise no state transition occurs. * </li> * <li> * The authentication may be <em>failed</em> by way of the {@link #fail()} method. This method will dispose * of all authentication resources and transition to the <em>failed</em> state. * </li> * </ul> * <p> * There are three states related to authorization: the <em>anonymous-authorized</em> state, the <em>authorized</em> state, * and the <em>authorized-authenticated</em> state. In all three states, the following actions may be taken: * <ul> * <li> * As above, a name or principal may be assigned so long as it matches the existing identity. In particular, * for the <em>anonymous-authorized</em> state, all names are rejected, and only the {@linkplain AnonymousPrincipal anonymous principal} * is accepted. * </li> * <li> * An <em>authorization</em> may be performed via the {@link #authorize()} method. Since the identity is * always authorized, this is generally a no-op. * </li> * <li> * A <em>run-as authorization</em> may be performed via the {@link #authorize(String)} method. The given * name is <em>rewritten</em> as previously described, and then the <em>authorized identity</em> * is tested for a {@link RunAsPrincipalPermission} for the <em>rewritten</em> target name. If authorized, * the context transitions to the <em>authorized</em> state for the <em>realm identity</em> corresponding to the * <em>rewritten</em> name; otherwise no state transition occurs. * </li> * <li> * The authentication may be <em>completed</em> by way of the {@link #succeed()} method. This method will * dispose of all authentication resources and transition to the <em>complete</em> state. * </li> * <li> * The authentication may be <em>failed</em> by way of the {@link #fail()} method. This method will dispose * of all authentication resources and transition to the <em>failed</em> state. * </li> * </ul> * The <em>authorized-authenticated</em> state has the additional capability of verifying credentials as described above for * the <em>assigned</em> state. * <p> * The <em>complete</em> state has only one capability: the retrieval of the final <em>authorized identity</em> by way * of the {@link #getAuthorizedIdentity()} method. * <p> * The <em>failed</em> state has no capabilities and retains no reference to any identities or objects used during * authentication. * * @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a> * @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a> */ public final class ServerAuthenticationContext { private final AtomicReference<State> 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)); } /** * 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); stateRef.get().setName(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 { Assert.checkNotNullParam("principal", principal); stateRef.get().setPrincipal(principal); } /** * 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 stateRef.get().isSameName(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(); } /** * 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 * @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 { return authorize(name, true); } boolean authorize(String name, boolean authorizeRunAs) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { Assert.checkNotNullParam("name", name); return stateRef.get().authorize(name, 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 * @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<? extends Credential> credentialType, String algorithmName) throws RealmUnavailableException { Assert.checkNotNullParam("credentialType", credentialType); return stateRef.get().getCredentialAcquireSupport(credentialType, algorithmName); } /** * 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<? extends Credential> 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<? extends Evidence> 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<? extends Evidence> 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 <C> 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 extends Credential> C getCredential(Class<C> credentialType, String algorithmName) throws RealmUnavailableException { Assert.checkNotNullParam("credentialType", credentialType); return stateRef.get().getCredential(credentialType, algorithmName); } /** * 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 <C> 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 extends Credential> C getCredential(Class<C> credentialType) throws RealmUnavailableException { Assert.checkNotNullParam("credentialType", credentialType); return stateRef.get().getCredential(credentialType, 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 <C> the credential type * @param <R> 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 <C extends Credential, R> R applyToCredential(Class<C> credentialType, Function<C, R> 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 <C> the credential type * @param <R> 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 <C extends Credential, R> R applyToCredential(Class<C> credentialType, String algorithmName, Function<C, R> function) throws RealmUnavailableException { final Credential credential = getCredential(credentialType, algorithmName); return credential == null ? null : credential.castAndApply(credentialType, algorithmName, 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); } /** * 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); } /** * 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); } AtomicReference<State> getStateRef() { return stateRef; } CallbackHandler createCallbackHandler() { return new CallbackHandler() { @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<State> 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); } 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(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()); if (credential != null) { if (credential instanceof PasswordCredential) { Password password = ((PasswordCredential) credential).getPassword(); if (password != null && 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<? extends Credential> 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())); } else if (callback instanceof SSLCallback) { SSLCallback sslCallback = (SSLCallback) callback; try { X509Certificate[] x509Certificates = X500.asX509CertificateArray(sslCallback.getSslSession().getPeerCertificates()); verifyEvidence(new X509PeerCertificateChainEvidence(x509Certificates)); } catch (SSLPeerUnverifiedException e) { log.trace(e); } 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) { // todo: filter by IP address } 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<String> 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); } 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; try { SecurityIdentity identity = authorizeCallback.getIdentity(); if (identity != null && importIdentity(identity)) { authorizedIdentity = getAuthorizedIdentity(); return; } principal = authorizeCallback.getPrincipal(); if (principal == null) { principal = authorizeCallback.getAuthorizationPrincipal(); } if (principal != null) { setAuthenticationPrincipal(principal); authorize(); authorizedIdentity = getAuthorizedIdentity(); return; } } finally { log.tracef("Handling CachedIdentityAuthorizeCallback: principal = %s authorizedIdentity = %s", principal, authorizedIdentity); authorizeCallback.setAuthorized(authorizedIdentity); } } 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 { CallbackUtil.unsupported(callback); } } }; } private static Principal rewriteAll(Principal principal, Function<Principal, Principal> r1, Function<Principal, Principal> r2, Function<Principal, Principal> 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) { if (r1 != null) { return mapRealmName(principal, r1, defaultRealmName); } if (r2 != null) { return mapRealmName(principal, r2, defaultRealmName); } if (r3 != null) { return mapRealmName(principal, r3, defaultRealmName); } return defaultRealmName; } private static String mapRealmName(Principal principal, RealmMapper realmMapper, String defaultRealmName) { String realmName = realmMapper.getRealmMapping(principal, null); 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) throws RealmUnavailableException { return assignName(capturedIdentity, mechanismConfiguration, mechanismRealmConfiguration, originalPrincipal, evidence, privateCredentials, publicCredentials, false); } 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) 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, privateCredentials, publicCredentials); } String realmName = mapAll(preRealmPrincipal, mechanismRealmConfiguration.getRealmMapper(), mechanismConfiguration.getRealmMapper(), domain.getRealmMapper(), domain.getDefaultRealmName()); 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, privateCredentials, publicCredentials); } 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, privateCredentials, publicCredentials); } 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; if (exclusive) { if (securityRealm instanceof ModifiableSecurityRealm) { realmIdentity = ((ModifiableSecurityRealm) securityRealm).getRealmIdentityForUpdate(finalPrincipal); } else { throw log.unableToObtainExclusiveAccess(); } } else { realmIdentity = securityRealm.getRealmIdentity(finalPrincipal); } return new NameAssignedState(capturedIdentity, realmInfo, realmIdentity, preRealmPrincipal, mechanismConfiguration, mechanismRealmConfiguration, privateCredentials, publicCredentials); } 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 isSameName(String name) { return false; } boolean isSamePrincipal(Principal principal) { return false; } SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName) throws RealmUnavailableException { throw log.noAuthenticationInProgress(); } SupportLevel getEvidenceVerifySupport(Class<? extends Evidence> evidenceType, String algorithmName) throws RealmUnavailableException { throw log.noAuthenticationInProgress(); } <C extends Credential> C getCredential(Class<C> credentialType, String algorithmName) 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 setName(String name) throws RealmUnavailableException { setName(name, false); } void setName(String name, boolean exclusive) throws RealmUnavailableException { throw log.noAuthenticationInProgress(); } void setPrincipal(Principal principal) throws RealmUnavailableException { throw log.noAuthenticationInProgress(); } boolean authorize(final boolean requireLoginPermission) throws RealmUnavailableException { throw log.noAuthenticationInProgress(); } boolean authorize(String 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() { throw log.noAuthenticationInProgress(); } boolean isDone() { return false; } void addPublicCredential(final Credential credential) { throw log.noAuthenticationInProgress(); } void addPrivateCredential(final Credential credential) { throw log.noAuthenticationInProgress(); } } 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; public InactiveState(SecurityIdentity capturedIdentity, MechanismConfigurationSelector mechanismConfigurationSelector, IdentityCredentials privateCredentials, IdentityCredentials publicCredentials) { this(capturedIdentity, mechanismConfigurationSelector, MechanismInformation.DEFAULT, privateCredentials, publicCredentials); } public InactiveState(SecurityIdentity capturedIdentity, MechanismConfigurationSelector mechanismConfigurationSelector, MechanismInformation mechanismInformation, IdentityCredentials privateCredentials, IdentityCredentials publicCredentials) { this.capturedIdentity = capturedIdentity; this.mechanismConfigurationSelector = mechanismConfigurationSelector; this.mechanismInformation = checkNotNullParam("mechanismInformation", mechanismInformation); this.privateCredentials = privateCredentials; this.publicCredentials = publicCredentials; } @Override void setMechanismInformation(MechanismInformation mechanismInformation) { InactiveState inactiveState = new InactiveState(capturedIdentity, mechanismConfigurationSelector, mechanismInformation, privateCredentials, publicCredentials); InitialState nextState = inactiveState.selectMechanismConfiguration(); if (! stateRef.compareAndSet(this, nextState)) { stateRef.get().setMechanismInformation(mechanismInformation); } } @Override SecurityDomain getSecurityDomain() { return capturedIdentity.getSecurityDomain(); } boolean authorize(String 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() { transition(); stateRef.get().fail(); } @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 void setName(String name) throws RealmUnavailableException { setName(name, false); } @Override void setName(String name, boolean exclusive) throws RealmUnavailableException { transition(); stateRef.get().setName(name, exclusive); } @Override SupportLevel getEvidenceVerifySupport(final Class<? extends Evidence> 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) throws RealmUnavailableException { transition(); stateRef.get().setPrincipal(principal); } @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)); 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); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addPrivateCredential(credential); } } 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); } } abstract class ActiveState extends State { ActiveState() { } boolean authorize(String authorizationId, boolean authorizeRunAs) throws RealmUnavailableException { final AtomicReference<State> stateRef = getStateRef(); // get the identity we are authorizing from final SecurityIdentity sourceIdentity = getSourceIdentity(); final State state = assignName(sourceIdentity, getMechanismConfiguration(), getMechanismRealmConfiguration(), new NamePrincipal(authorizationId), null, IdentityCredentials.NONE, IdentityCredentials.NONE); if ( ! (state instanceof NameAssignedState)) { 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 exists"); 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; UnassignedState(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials) { this.capturedIdentity = capturedIdentity; this.mechanismConfiguration = mechanismConfiguration; this.privateCredentials = privateCredentials; this.publicCredentials = publicCredentials; } SecurityIdentity getSourceIdentity() { return capturedIdentity; } @Override SecurityDomain getSecurityDomain() { return capturedIdentity.getSecurityDomain(); } @Override void fail() { final AtomicReference<State> stateRef = getStateRef(); if (! stateRef.compareAndSet(this, FAILED)) { // recurse & retry stateRef.get().fail(); } } @Override boolean authorizeAnonymous(final boolean requireLoginPermission) { final AtomicReference<State> 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<State> 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<State> 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); if ( ! (state instanceof NameAssignedState)) { 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 void setName(final String name) throws RealmUnavailableException { setName(name, false); } @Override void setName(final String name, final boolean exclusive) throws RealmUnavailableException { final AtomicReference<State> stateRef = getStateRef(); final State newState = assignName(capturedIdentity, mechanismConfiguration, getMechanismRealmConfiguration(), new NamePrincipal(name), null, privateCredentials, publicCredentials, exclusive); if (! stateRef.compareAndSet(this, newState)) { if (newState instanceof NameAssignedState) { ((NameAssignedState)newState).realmIdentity.dispose(); } stateRef.get().setName(name, exclusive); } } @Override SupportLevel getEvidenceVerifySupport(final Class<? extends Evidence> 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<State> stateRef = getStateRef(); final Principal evidencePrincipal = evidence.getPrincipal(); 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); if (! newState.verifyEvidence(evidence)) { if (newState instanceof NameAssignedState) { ((NameAssignedState)newState).realmIdentity.dispose(); } return false; } if (! stateRef.compareAndSet(this, newState)) { if (newState instanceof NameAssignedState) { ((NameAssignedState)newState).realmIdentity.dispose(); } return stateRef.get().verifyEvidence(evidence); } return true; } // verify evidence with no name set: use the realms to find a match (SSO scenario, etc.) final SecurityDomain domain = getSecurityDomain(); final Collection<RealmInfo> realmInfos = domain.getRealmInfos(); RealmIdentity realmIdentity = null; RealmInfo realmInfo = null; for (RealmInfo info : realmInfos) { realmIdentity = info.getSecurityRealm().getRealmIdentity(evidence); if (realmIdentity.exists()) { realmInfo = info; break; } else { realmIdentity.dispose(); } } if (realmInfo == null) { // no verification possible, no identity found return false; } assert realmIdentity != null && realmIdentity.exists(); final Principal resolvedPrincipal = realmIdentity.getRealmIdentityPrincipal(); if (resolvedPrincipal == null) { // we have to have a principal realmIdentity.dispose(); return false; } if (! realmIdentity.verifyEvidence(evidence)) { realmIdentity.dispose(); return false; } final NameAssignedState newState = new NameAssignedState(getSourceIdentity(), realmInfo, realmIdentity, resolvedPrincipal, mechanismConfiguration, mechanismRealmConfiguration, privateCredentials, publicCredentials); if (! stateRef.compareAndSet(this, newState)) { realmIdentity.dispose(); return stateRef.get().verifyEvidence(evidence); } return true; } @Override void setPrincipal(final Principal principal) throws RealmUnavailableException { Assert.checkNotNullParam("principal", principal); final AtomicReference<State> stateRef = getStateRef(); final State newState = assignName(capturedIdentity, mechanismConfiguration, getMechanismRealmConfiguration(), principal, null, privateCredentials, publicCredentials); if (! stateRef.compareAndSet(this, newState)) { if (newState instanceof NameAssignedState) { ((NameAssignedState)newState).realmIdentity.dispose(); } stateRef.get().setPrincipal(principal); } } @Override MechanismConfiguration getMechanismConfiguration() { return mechanismConfiguration; } IdentityCredentials getPrivateCredentials() { return privateCredentials; } IdentityCredentials getPublicCredentials() { return publicCredentials; } } 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) { super(capturedIdentity, mechanismConfiguration, privateCredentials, publicCredentials); 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<State> stateRef = getStateRef(); if (! stateRef.compareAndSet(this, new RealmAssignedState(capturedIdentity, mechanismConfiguration, configuration, privateCredentials, publicCredentials))) { stateRef.get().setMechanismRealmName(realmName); } } @Override MechanismRealmConfiguration getMechanismRealmConfiguration() { final Collection<String> mechanismRealmNames = mechanismConfiguration.getMechanismRealmNames(); final Iterator<String> 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); 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)); 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()); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addPublicCredential(credential); } } } final class RealmAssignedState extends UnassignedState { final MechanismRealmConfiguration mechanismRealmConfiguration; RealmAssignedState(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials) { super(capturedIdentity, mechanismConfiguration, privateCredentials, publicCredentials); 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)); 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()); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addPublicCredential(credential); } } } final class InvalidNameState extends UnassignedState { InvalidNameState(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials) { super(capturedIdentity, mechanismConfiguration, privateCredentials, publicCredentials); } @Override void fail() { final AtomicReference<State> stateRef = getStateRef(); if (! stateRef.compareAndSet(this, FAILED)) { // recurse & retry stateRef.get().fail(); } } @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; 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) { this.capturedIdentity = capturedIdentity; this.realmInfo = realmInfo; this.realmIdentity = realmIdentity; this.authenticationPrincipal = authenticationPrincipal; this.mechanismConfiguration = mechanismConfiguration; this.mechanismRealmConfiguration = mechanismRealmConfiguration; this.privateCredentials = privateCredentials; this.publicCredentials = publicCredentials; } @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<? extends Credential> credentialType, final String algorithmName) throws RealmUnavailableException { return realmIdentity.getCredentialAcquireSupport(credentialType, algorithmName); } @Override SupportLevel getEvidenceVerifySupport(final Class<? extends Evidence> evidenceType, final String algorithmName) throws RealmUnavailableException { return realmIdentity.getEvidenceVerifySupport(evidenceType, algorithmName); } @Override <C extends Credential> C getCredential(final Class<C> credentialType, final String algorithmName) throws RealmUnavailableException { return realmIdentity.getCredential(credentialType, algorithmName); } @Override boolean authorize(final boolean requireLoginPermission) throws RealmUnavailableException { AuthorizedAuthenticationState newState = doAuthorization(requireLoginPermission); if (newState == null) { return false; } final AtomicReference<State> 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 = realmIdentity.getAuthorizationIdentity(); final SecurityDomain domain = capturedIdentity.getSecurityDomain(); SecurityIdentity authorizedIdentity = Assert.assertNotNull(domain.transform(new SecurityIdentity(domain, authenticationPrincipal, realmInfo, authorizationIdentity, domain.getCategoryRoleMappers(), capturedIdentity.getPublicCredentials(), capturedIdentity.getPrivateCredentialsPrivate()))); 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()); } 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 String 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<State> 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.getPrincipal(); 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 SecurityIdentity capturedIdentity = getSourceIdentity(); final AtomicReference<State> stateRef = getStateRef(); if (! stateRef.compareAndSet(this, FAILED)) { stateRef.get().fail(); return; } SecurityRealm.safeHandleRealmEvent(getRealmInfo().getSecurityRealm(), new RealmFailedAuthenticationEvent(realmIdentity, null, null)); SecurityDomain.safeHandleSecurityEvent(capturedIdentity.getSecurityDomain(), new SecurityAuthenticationFailedEvent(capturedIdentity, realmIdentity.getRealmIdentityPrincipal())); realmIdentity.dispose(); } @Override void setName(final String name) { setName(name, false); } @Override void setName(final String name, final boolean exclusive) { if (isSameName(name)) { return; } throw log.nameAlreadySet(); } @Override void setPrincipal(final Principal principal) { if (isSamePrincipal(principal)) { return; } throw log.nameAlreadySet(); } @Override boolean isSameName(String name) { return isSamePrincipal(new NamePrincipal(name)); } @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)); 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); if (! stateRef.compareAndSet(this, newState)) { stateRef.get().addPublicCredential(credential); } } 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 isSameName(final String name) { return false; } @Override boolean isSamePrincipal(final Principal principal) { return principal instanceof AnonymousPrincipal; } @Override SupportLevel getCredentialAcquireSupport(final Class<? extends Credential> credentialType, final String algorithmName) throws RealmUnavailableException { return SupportLevel.UNSUPPORTED; } @Override SupportLevel getEvidenceVerifySupport(final Class<? extends Evidence> evidenceType, final String algorithmName) throws RealmUnavailableException { return SupportLevel.UNSUPPORTED; } @Override <C extends Credential> C getCredential(final Class<C> credentialType, final String algorithmName) 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 setName(final String name) throws RealmUnavailableException { setName(name, false); } @Override void setName(final String name, final boolean exclusive) throws RealmUnavailableException { // reject all names super.setName(name); } @Override void setPrincipal(final Principal principal) throws RealmUnavailableException { if (! (principal instanceof AnonymousPrincipal)) { super.setPrincipal(principal); } } @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<State> stateRef = getStateRef(); if (! stateRef.compareAndSet(this, new CompleteState(anonymousIdentity))) { stateRef.get().succeed(); } } @Override void fail() { final AtomicReference<State> stateRef = getStateRef(); if (! stateRef.compareAndSet(this, FAILED)) { stateRef.get().fail(); } } @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 isSameName(String name) { return isSamePrincipal(new NamePrincipal(name)); } @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 String authorizationId, final boolean authorizeRunAs) throws RealmUnavailableException { if (isSameName(authorizationId)) { ElytronMessages.log.trace("RunAs authorization succeed - the same identity"); return this; } final State state = assignName(authorizedIdentity, getMechanismConfiguration(), getMechanismRealmConfiguration(), new NamePrincipal(authorizationId), null, IdentityCredentials.NONE, IdentityCredentials.NONE); if ( ! (state instanceof NameAssignedState)) { 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); } } } 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<? extends Credential> credentialType, final String algorithmName) throws RealmUnavailableException { return realmIdentity.getCredentialAcquireSupport(credentialType, algorithmName); } @Override SupportLevel getEvidenceVerifySupport(final Class<? extends Evidence> evidenceType, final String algorithmName) throws RealmUnavailableException { return realmIdentity.getEvidenceVerifySupport(evidenceType, algorithmName); } @Override <C extends Credential> C getCredential(final Class<C> credentialType, String algorithmName) throws RealmUnavailableException { return realmIdentity.getCredential(credentialType, algorithmName); } @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<State> 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 SecurityIdentity authorizedIdentity = getSourceIdentity(); final AtomicReference<State> stateRef = getStateRef(); if (! stateRef.compareAndSet(this, FAILED)) { stateRef.get().fail(); 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); } } } 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() { } @Override boolean isDone() { return true; } }; }