org.wildfly.security.auth.server.ServerAuthenticationContext Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wildfly.security.auth.server;
import static org.wildfly.common.Assert.checkNotNullParam;
import static org.wildfly.security.auth.server._private.ElytronMessages.log;
import static org.wildfly.security.authz.RoleDecoder.KEY_SOURCE_ADDRESS;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Predicate;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.sasl.AuthorizeCallback;
import javax.security.sasl.RealmCallback;
import org.wildfly.common.Assert;
import org.wildfly.security.auth.SupportLevel;
import org.wildfly.security.auth.callback.AnonymousAuthorizationCallback;
import org.wildfly.security.auth.callback.AuthenticationCompleteCallback;
import org.wildfly.security.auth.callback.AuthenticationConfigurationCallback;
import org.wildfly.security.auth.callback.AvailableRealmsCallback;
import org.wildfly.security.auth.callback.CachedIdentityAuthorizeCallback;
import org.wildfly.security.auth.callback.CallbackUtil;
import org.wildfly.security.auth.callback.ChannelBindingCallback;
import org.wildfly.security.auth.callback.CredentialCallback;
import org.wildfly.security.auth.callback.CredentialUpdateCallback;
import org.wildfly.security.auth.callback.EvidenceDecodePrincipalCallback;
import org.wildfly.security.auth.callback.EvidenceVerifyCallback;
import org.wildfly.security.auth.callback.ExclusiveNameCallback;
import org.wildfly.security.auth.callback.FastUnsupportedCallbackException;
import org.wildfly.security.auth.callback.PrincipalAuthorizeCallback;
import org.wildfly.security.auth.callback.MechanismInformationCallback;
import org.wildfly.security.auth.callback.IdentityCredentialCallback;
import org.wildfly.security.auth.callback.PeerPrincipalCallback;
import org.wildfly.security.auth.callback.RequestInformationCallback;
import org.wildfly.security.auth.callback.SSLCallback;
import org.wildfly.security.auth.callback.SecurityIdentityCallback;
import org.wildfly.security.auth.callback.ServerCredentialCallback;
import org.wildfly.security.auth.callback.SocketAddressCallback;
import org.wildfly.security.auth.permission.LoginPermission;
import org.wildfly.security.auth.permission.RunAsPrincipalPermission;
import org.wildfly.security.auth.principal.AnonymousPrincipal;
import org.wildfly.security.auth.principal.NamePrincipal;
import org.wildfly.security.auth.server.event.RealmFailedAuthenticationEvent;
import org.wildfly.security.auth.server.event.RealmIdentityFailedAuthorizationEvent;
import org.wildfly.security.auth.server.event.RealmIdentitySuccessfulAuthorizationEvent;
import org.wildfly.security.auth.server.event.RealmSuccessfulAuthenticationEvent;
import org.wildfly.security.auth.server.event.SecurityAuthenticationFailedEvent;
import org.wildfly.security.auth.server.event.SecurityAuthenticationSuccessfulEvent;
import org.wildfly.security.auth.server.event.SecurityRealmUnavailableEvent;
import org.wildfly.security.authz.AggregateAttributes;
import org.wildfly.security.authz.Attributes;
import org.wildfly.security.authz.AuthorizationIdentity;
import org.wildfly.security.authz.MapAttributes;
import org.wildfly.security.credential.Credential;
import org.wildfly.security.credential.PasswordCredential;
import org.wildfly.security.credential.source.CredentialSource;
import org.wildfly.security.evidence.AlgorithmEvidence;
import org.wildfly.security.evidence.Evidence;
import org.wildfly.security.evidence.X509PeerCertificateChainEvidence;
import org.wildfly.security.password.Password;
import org.wildfly.security.password.PasswordFactory;
import org.wildfly.security.password.TwoWayPassword;
import org.wildfly.security.password.interfaces.DigestPassword;
import org.wildfly.security.password.spec.ClearPasswordSpec;
import org.wildfly.security.ssl.SSLConnection;
import org.wildfly.security.x500.X500;
import org.wildfly.security.auth.server._private.ElytronMessages;
/**
* Server-side authentication context. Instances of this class are used to perform all authentication and re-authorization
* operations that involve the usage of an identity in a {@linkplain SecurityDomain security domain}.
*
* There are various effective states, described as follows:
*
* - The inactive state.
* -
* The unassigned states:
*
* - Initial
* - Realm-assigned
*
*
* - The assigned state
* -
* The authorized states:
*
* - Anonymous-authorized
* - Authorized
* - Authorized-authenticated
*
*
* -
* The terminal states:
*
* - Complete
* - Failed
*
*
*
*
*
* When an instance of this class is first constructed, it is in the inactive state. In this state, the context retains
* a captured {@linkplain SecurityIdentity identity} and contains a reference to a
* {@linkplain MechanismConfigurationSelector}. The captured identity may be used for various
* context-sensitive authorization decisions. Additional mechanism information can be supplied to this state so that when
* authentication begins an appropriate {@linkplain MechanismConfiguration} can be selected.
*
* Once authentication commences the state will automatically transition to the initial state. In this state, the
* context retains an captured {@linkplain SecurityIdentity identity} and a {@linkplain MechanismConfiguration mechanism configuration}
* which was resolved from the information supplied to the inactive state. The captured identity may be
* used for various context-sensitive authorization decisions. The mechanism configuration is used to associate
* an authentication mechanism-specific configuration, including rewriters, {@linkplain MechanismRealmConfiguration mechanism realms},
* server credential factories, and more.
*
* When an authentication mechanism is "realm-aware" (that is, it has a notion of realms that is specific to that particular
* authentication mechanism, e.g. the DIGEST-MD5 SASL mechanism), it
* is necessary for the mechanism to relay the realm selection. This is done by way of the {@link #setMechanismRealmName(String) setMechanismRealmName()}
* method. Calling this method in the initial state causes a transition to the realm-assigned state,
* in which the method may be reinvoked idempotently as long as it is called with the same name (calling the method with
* a different name will result in an exception).
*
* The realm-assigned state is nearly identical to the initial state, except that from this state, the
* mechanism realm-specific configuration is applied to all subsequent operation.
*
* From these unassigned states, several possible actions may be taken, depending on the necessary progression
* of the authentication:
*
* -
* A name may be assigned by way of the {@link #setAuthenticationName(String)} method. The name is
* {@linkplain NameRewriter rewritten} and {@linkplain RealmMapper mapped to a realm} according to the
* domain settings, the mechanism configuration, and/or the mechanism realm configuration. The
* {@linkplain SecurityRealm realm} that is the resultant target of the mapping is queried for a
* {@linkplain RealmIdentity realm identity}. The realm identity may or may not be
* existent; this status will affect the outcome of certain operations in subsequent states (as described below).
* After the realm identity is selected, any final rewrite operations which are configured are applied,
* and the resultant name is transformed into a {@link NamePrincipal}, and associated as the
* {@linkplain #getAuthenticationPrincipal() authentication principal} which may subsequently be queried.
*
* -
* A principal may be assigned using the {@link #setAuthenticationPrincipal(Principal)} method. The
* principal is {@linkplain PrincipalDecoder decoded} according to the configuration of the security domain (see
* the method documentation for input requirements and failure conditions). Once a name is decoded from the
* principal, it is assigned as described above.
*
* -
* A unit of {@linkplain Evidence evidence} may be verified. This is mostly described below in the
* context of the assigned state, but with the important distinction the evidence is first examined
* to locate the corresponding evidence, in the following steps:
*
* -
* Firstly, the evidence is examined to determine whether it {@linkplain Evidence#getPrincipal() contains a principal}.
* If so, the principal name is first established using the procedure described above, and then the normal
* evidence verification procedure described below commences.
*
* -
* Secondly, the evidence is socialized to each realm in turn, to see if a realm can recognize
* and {@linkplain SecurityRealm#getRealmIdentity(Principal) locate} an identity based on
* the evidence. If so, the realm identity is {@linkplain RealmIdentity#getRealmIdentityPrincipal() queried}
* for an authentication principal, which is then decoded and established as described above. Once this
* is done successfully, the evidence verification procedure described below commences.
*
* - Finally, if none of these steps succeeds, the verification fails and no state transition occurs.
*
*
* -
* An identity may be {@linkplain #importIdentity(SecurityIdentity) imported}. In this process,
* a {@link SecurityIdentity} instance is examined to determine whether it can be used to complete an implicit
* authentication operation which would yield an authorized identity. The {@code SecurityIdentity} may
* be from the same domain or from a different one.
*
* If the identity being imported is from the same security domain as this context, then the identity
* is implicitly authorized for usage, entering the authorized state described below.
*
* If the identity being imported is not from the same security domain, then the principal is extracted
* from the identity and used to assign a realm identity in the same manner as {@link #setAuthenticationPrincipal(Principal)}.
* The domain is then {@linkplain SecurityDomain.Builder#setTrustedSecurityDomainPredicate(Predicate) queried}
* to determine whether the target identity's source domain is trusted. If so, a normal
* authorization is carried out as described below for the assigned state, resulting in an
* authorized-authenticated state. If not, then the realm of the realm identity is
* compared against the realm of the identity being imported. If they are the same, the
* identity is imported and a normal authorization is carried out as described below.
*
* -
* An anonymous authorization may be carried out by way of the {@link #authorizeAnonymous()} method.
* If the {@linkplain SecurityDomain#getAnonymousSecurityIdentity() anonymous identity} has the
* {@link LoginPermission} granted to it, the context will transition into the anonymous-authorized
* state; otherwise no state transition occurs.
*
* -
* An external authorization may be carried out using the {@link #authorize()} method. The
* captured identity (which may be anonymous) is queried for the presence of the
* {@link LoginPermission}; if present, the context will transition into the authorized or
* anonymous-authorized state (depending on whether the captured identity is anonymous);
* otherwise no state transition occurs.
*
* -
* An external run-as authorization may be carried out using the {@link #authorize(String)} method.
* First, the given name is rewritten in the same manner as the {@link #setAuthenticationName(String)}
* method. Then, the captured identity (which may be anonymous) is queried for the presence of a
* {@link RunAsPrincipalPermission} for the target name. If present, the authentication name is assigned
* as described above, and the resultant realm identity is queried for {@link LoginPermission}. If present,
* the context will transition to the authorized-authenticated state. If any step fails, no state transition
* occurs.
*
* -
* The authentication may be failed by way of the {@link #fail()} method. This method will dispose
* of all authentication resources and transition to the failed state.
*
*
*
* In the name-assigned (or, for brevity, assigned) state, the following actions may be performed:
*
* -
* A name or principal may be assigned as above, however the resultant decoded and rewritten name
* and realm identity must be identical to the previously selected name and identity.
*
* -
* Evidence may be verified. The realm identity is queried directly and no state transitions
* will occur. Evidence verification will fail if the evidence has an evidence principal which does
* not result in the same realm identity as the current one after decoding and rewriting.
*
* -
* An authorization may be performed via the {@link #authorize()} method. If the selected realm identity
* possesses the {@link LoginPermission}, then the context transitions to the authorized-authenticated state,
* otherwise no state transition occurs.
*
* -
* A run-as authorization may be performed via the {@link #authorize(String)} method.
* First, the given name is rewritten in the same manner as the {@link #setAuthenticationName(String)} method.
* The current identity is then authorized as described above, and then the authorized identity
* is tested for a {@link RunAsPrincipalPermission} for the rewritten target name. If authorized,
* the context transitions to the authorized state for the realm identity corresponding to the
* rewritten name; otherwise no state transition occurs.
*
* -
* The authentication may be failed by way of the {@link #fail()} method. This method will dispose
* of all authentication resources and transition to the failed state.
*
*
*
* There are three states related to authorization: the anonymous-authorized state, the authorized state,
* and the authorized-authenticated state. In all three states, the following actions may be taken:
*
* -
* As above, a name or principal may be assigned so long as it matches the existing identity. In particular,
* for the anonymous-authorized state, all names are rejected, and only the {@linkplain AnonymousPrincipal anonymous principal}
* is accepted.
*
* -
* An authorization may be performed via the {@link #authorize()} method. Since the identity is
* always authorized, this is generally a no-op.
*
* -
* A run-as authorization may be performed via the {@link #authorize(String)} method. The given
* name is rewritten as previously described, and then the authorized identity
* is tested for a {@link RunAsPrincipalPermission} for the rewritten target name. If authorized,
* the context transitions to the authorized state for the realm identity corresponding to the
* rewritten name; otherwise no state transition occurs.
*
* -
* The authentication may be completed by way of the {@link #succeed()} method. This method will
* dispose of all authentication resources and transition to the complete state.
*
* -
* The authentication may be failed by way of the {@link #fail()} method. This method will dispose
* of all authentication resources and transition to the failed state.
*
*
* The authorized-authenticated state has the additional capability of verifying credentials as described above for
* the assigned state.
*
* The complete state has only one capability: the retrieval of the final authorized identity by way
* of the {@link #getAuthorizedIdentity()} method.
*
* The failed state has no capabilities and retains no reference to any identities or objects used during
* authentication.
*
* @author David M. Lloyd
* @author Darran Lofthouse
*/
public final class ServerAuthenticationContext implements AutoCloseable {
private final AtomicReference stateRef;
ServerAuthenticationContext(final SecurityDomain domain, final MechanismConfigurationSelector mechanismConfigurationSelector) {
this(domain.getCurrentSecurityIdentity(), mechanismConfigurationSelector);
}
ServerAuthenticationContext(final SecurityIdentity capturedIdentity, final MechanismConfigurationSelector mechanismConfigurationSelector) {
stateRef = new AtomicReference<>(new InactiveState(capturedIdentity, mechanismConfigurationSelector, IdentityCredentials.NONE, IdentityCredentials.NONE, Attributes.EMPTY));
}
/**
* Set information about the current mechanism and request for this authentication attempt. If the mechanism
* information cannot be resolved to a mechanism configuration, an exception is thrown.
*
* @param mechanismInformation the mechanism information about the current authentication attempt.
* @throws IllegalStateException if the mechanism information about the current authentication attempt cannot be
* resolved to a mechanism configuration
*/
public void setMechanismInformation(final MechanismInformation mechanismInformation) throws IllegalStateException {
stateRef.get().setMechanismInformation(mechanismInformation);
}
/**
* Get the authorized identity result of this authentication.
*
* @return the authorized identity
* @throws IllegalStateException if the authentication is incomplete
*/
public SecurityIdentity getAuthorizedIdentity() throws IllegalStateException {
return stateRef.get().getAuthorizedIdentity();
}
/**
* Set the authentication to anonymous, completing the authentication process.
*
* @throws IllegalStateException if the authentication is already complete
*/
public boolean authorizeAnonymous() throws IllegalStateException {
return authorizeAnonymous(true);
}
/**
* Set the authentication to anonymous, completing the authentication process.
*
* @param requireLoginPermission {@code true} if {@link LoginPermission} is required and {@code false} otherwise
* @throws IllegalStateException if the authentication is already complete
*/
public boolean authorizeAnonymous(boolean requireLoginPermission) throws IllegalStateException {
return stateRef.get().authorizeAnonymous(requireLoginPermission);
}
/**
* Set the authentication name for this authentication. If the name is already set, then the new name must be
* equal to the old name, or else an exception is thrown.
*
* @param name the authentication name
* @throws IllegalArgumentException if the name is syntactically invalid
* @throws RealmUnavailableException if the realm is not available
* @throws IllegalStateException if the authentication name was already set and there is a mismatch
*/
public void setAuthenticationName(String name) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
setAuthenticationName(name, false);
}
/**
* Set the authentication name for this authentication. If the name is already set, then the new name must be
* equal to the old name, or else an exception is thrown.
*
* @param name the authentication name
* @param exclusive {@code true} if exclusive access to the backing identity is required
* @throws IllegalArgumentException if the name is syntactically invalid
* @throws RealmUnavailableException if the realm is not available or if exclusive access to the backing identity
* is required but could not be granted
* @throws IllegalStateException if the authentication name was already set and there is a mismatch
*/
public void setAuthenticationName(String name, boolean exclusive) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
Assert.checkNotNullParam("name", name);
setAuthenticationPrincipal(new NamePrincipal(name), exclusive);
}
/**
* Set the authentication principal for this authentication. Calling this method initiates authentication.
*
* @param principal the authentication principal
* @throws IllegalArgumentException if the principal cannot be mapped to a name, or the mapped name is syntactically invalid
* @throws RealmUnavailableException if the realm is not available
* @throws IllegalStateException if the authentication name was already set
*/
public void setAuthenticationPrincipal(Principal principal) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
setAuthenticationPrincipal(principal, false);
}
/**
* Set the authentication principal for this authentication. Calling this method initiates authentication.
*
* @param principal the authentication principal
* @param exclusive {@code true} if exclusive access to the backing identity is required
* @throws IllegalArgumentException if the principal cannot be mapped to a name, or the mapped name is syntactically invalid
* @throws RealmUnavailableException if the realm is not available
* @throws IllegalStateException if the authentication name was already set
*/
public void setAuthenticationPrincipal(Principal principal, boolean exclusive) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
Assert.checkNotNullParam("principal", principal);
stateRef.get().setPrincipal(principal, exclusive);
}
/**
* Determine if the given name refers to the same identity as the currently set authentication name.
*
* @param name the authentication name
* @return {@code true} if the name matches the current identity, {@code false} otherwise
* @throws IllegalArgumentException if the name is syntactically invalid
* @throws RealmUnavailableException if the realm is not available
* @throws IllegalStateException if the authentication name was already set
*/
public boolean isSameName(String name) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
Assert.checkNotNullParam("name", name);
return isSamePrincipal(new NamePrincipal(name));
}
/**
* Determine if the given principal refers to the same identity as the currently set authentication name.
*
* @param principal the authentication name
* @return {@code true} if the name matches the current identity, {@code false} otherwise
* @throws IllegalArgumentException if the name is syntactically invalid
* @throws RealmUnavailableException if the realm is not available
* @throws IllegalStateException if the authentication name was already set
*/
public boolean isSamePrincipal(Principal principal) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
Assert.checkNotNullParam("principal", principal);
return stateRef.get().isSamePrincipal(principal);
}
/**
* Determine if the current authentication identity actually exists in the realm.
*
* @return {@code true} if the identity exists, {@code false} otherwise
* @throws RealmUnavailableException if the realm failed to access the identity
* @throws IllegalStateException if there is no authentication name set
*/
public boolean exists() throws RealmUnavailableException, IllegalStateException {
return stateRef.get().getRealmIdentity().exists();
}
/**
* Mark this authentication as "failed". The context cannot be used after this method is called.
*
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public void fail() throws IllegalStateException {
stateRef.get().fail(true);
}
/**
* Attempt to authorize an authentication attempt. If the authorization is successful, {@code true} is returned and
* the context is placed in the "authorized" state with the new authorization identity. If the authorization fails,
* {@code false} is returned and the state of the context is unchanged.
*
* @return {@code true} if the authorization succeeded, {@code false} otherwise
* @throws RealmUnavailableException if the realm is not available
* @throws IllegalStateException if the authentication name was not set or authentication was already complete
*/
public boolean authorize() throws RealmUnavailableException, IllegalStateException {
return authorize(true);
}
boolean authorize(boolean requireLoginPermission) throws RealmUnavailableException, IllegalStateException {
return stateRef.get().authorize(requireLoginPermission);
}
/**
* Attempt to authorize a change to a new user (possibly including an authentication attempt). If the authorization
* is successful, {@code true} is returned and the context is placed in the "authorized" state with the new authorization
* identity. If the authorization fails, {@code false} is returned and the state of the context is unchanged.
*
* @param name the authorization name (must not be {@code null})
* @return {@code true} if the authorization succeeded, {@code false} otherwise
* @throws IllegalArgumentException if the name is syntactically invalid
* @throws RealmUnavailableException if the realm is not available
* @throws IllegalStateException if the authentication name was not set or authentication was already complete
*/
public boolean authorize(String name) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
checkNotNullParam("name", name);
return authorize(new NamePrincipal(name), true);
}
/**
* Attempt to authorize a change to a new user (possibly including an authentication attempt). If the authorization
* is successful, {@code true} is returned and the context is placed in the "authorized" state with the new authorization
* identity. If the authorization fails, {@code false} is returned and the state of the context is unchanged.
*
* @param principal the authorization principal (must not be {@code null})
* @return {@code true} if the authorization succeeded, {@code false} otherwise
* @throws IllegalArgumentException if the principal is syntactically invalid
* @throws RealmUnavailableException if the realm is not available
* @throws IllegalStateException if the authentication principal was not set or authentication was already complete
*/
public boolean authorize(Principal principal) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
return authorize(principal, true);
}
boolean authorize(Principal principal, boolean authorizeRunAs) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
checkNotNullParam("principal", principal);
return stateRef.get().authorize(principal, authorizeRunAs);
}
/**
* Mark this authentication as "successful". The context cannot be used after this method is called, however
* the authorized identity may thereafter be accessed via the {@link #getAuthorizedIdentity()} method. If no
* authentication actually happened, then authentication will complete anonymously.
*
* @throws IllegalStateException if authentication is already completed
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
*/
public void succeed() throws IllegalStateException, RealmUnavailableException {
stateRef.get().succeed();
}
/**
* Determine if authentication was already completed on this context.
*
* @return {@code true} if authentication was completed; {@code false} otherwise
*/
public boolean isDone() {
return stateRef.get().isDone();
}
/**
* Get the principal associated with the current authentication name. Only valid during authentication process.
*
* @return the principal
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public Principal getAuthenticationPrincipal() {
return stateRef.get().getAuthenticationPrincipal();
}
/**
* Determine whether a given credential is definitely obtainable, possibly obtainable, or definitely not obtainable.
*
* If an authentication identity is established this will be for that identity, otherwise this will be the general
* level of support advertised by the security domain.
*
* @param credentialType the credential type class (must not be {@code null})
* @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does
* not support algorithm names
* @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type
* does not support algorithm parameters
* @return the level of support for this credential type
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public SupportLevel getCredentialAcquireSupport(Class extends Credential> credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
Assert.checkNotNullParam("credentialType", credentialType);
return stateRef.get().getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec);
}
/**
* Determine whether a given credential is definitely obtainable, possibly obtainable, or definitely not obtainable.
*
* If an authentication identity is established this will be for that identity, otherwise this will be the general
* level of support advertised by the security domain.
*
* @param credentialType the credential type class (must not be {@code null})
* @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does
* not support algorithm names
* @return the level of support for this credential type
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public SupportLevel getCredentialAcquireSupport(Class extends Credential> credentialType, String algorithmName) throws RealmUnavailableException {
return getCredentialAcquireSupport(credentialType, algorithmName, null);
}
/**
* Determine whether a given credential is definitely obtainable, possibly obtainable, or definitely not obtainable.
*
* If an authentication identity is established this will be for that identity, otherwise this will be the general
* level of support advertised by the security domain.
*
* @param credentialType the credential type class (must not be {@code null})
* @return the level of support for this credential type
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public SupportLevel getCredentialAcquireSupport(Class 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 parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type
* does not support algorithm parameters
* @param the credential type
*
* @return the credential, or {@code null} if the principal has no credential of that type
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public C getCredential(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
Assert.checkNotNullParam("credentialType", credentialType);
return stateRef.get().getCredential(credentialType, algorithmName, parameterSpec);
}
/**
* Acquire a credential of the given type. The credential type is defined by its {@code Class} and an optional {@code algorithmName}. If the
* algorithm name is not given, then the query is performed for any algorithm of the given type.
*
* @param credentialType the credential type class (must not be {@code null})
* @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does
* not support algorithm names
* @param the credential type
*
* @return the credential, or {@code null} if the principal has no credential of that type
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public C getCredential(Class credentialType, String algorithmName) throws RealmUnavailableException {
Assert.checkNotNullParam("credentialType", credentialType);
return stateRef.get().getCredential(credentialType, algorithmName, null);
}
/**
* Acquire a credential of the given type. The credential type is defined by its {@code Class} and an optional {@code algorithmName}. If the
* algorithm name is not given, then the query is performed for any algorithm of the given type.
*
* @param credentialType the credential type class (must not be {@code null})
* @param the credential type
*
* @return the credential, or {@code null} if the principal has no credential of that type
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public C getCredential(Class credentialType) throws RealmUnavailableException {
Assert.checkNotNullParam("credentialType", credentialType);
return stateRef.get().getCredential(credentialType, null, null);
}
/**
* Apply the given function to the acquired credential, if it is set and of the given type.
*
* @param credentialType the credential type class (must not be {@code null})
* @param function the function to apply (must not be {@code null})
* @param the credential type
* @param the return type
* @return the result of the function, or {@code null} if the criteria are not met
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public R applyToCredential(Class credentialType, Function function) throws RealmUnavailableException {
final Credential credential = getCredential(credentialType);
return credential == null ? null : credential.castAndApply(credentialType, function);
}
/**
* Apply the given function to the acquired credential, if it is set and of the given type and algorithm.
*
* @param credentialType the credential type class (must not be {@code null})
* @param algorithmName the algorithm name
* @param function the function to apply (must not be {@code null})
* @param the credential type
* @param the return type
* @return the result of the function, or {@code null} if the criteria are not met
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public R applyToCredential(Class credentialType, String algorithmName, Function function) throws RealmUnavailableException {
final Credential credential = getCredential(credentialType, algorithmName);
return credential == null ? null : credential.castAndApply(credentialType, algorithmName, function);
}
/**
* Apply the given function to the acquired credential, if it is set and of the given type and algorithm.
*
* @param credentialType the credential type class (must not be {@code null})
* @param algorithmName the algorithm name
* @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type
* does not support algorithm parameters
* @param function the function to apply (must not be {@code null})
* @param the credential type
* @param the return type
* @return the result of the function, or {@code null} if the criteria are not met
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public R applyToCredential(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec, Function function) throws RealmUnavailableException {
final Credential credential = getCredential(credentialType, algorithmName, parameterSpec);
return credential == null ? null : credential.castAndApply(credentialType, algorithmName, parameterSpec, function);
}
/**
* Verify the given evidence.
*
* @param evidence the evidence to verify
*
* @return {@code true} if verification was successful, {@code false} otherwise
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException {
Assert.checkNotNullParam("evidence", evidence);
return stateRef.get().verifyEvidence(evidence);
}
/**
* Set the decoded evidence principal.
*
* @param evidence the evidence to decode and associate with a principal
* @since 1.10.0
*/
public void setDecodedEvidencePrincipal(Evidence evidence) throws RealmUnavailableException {
Assert.checkNotNullParam("evidence", evidence);
evidence.setDecodedPrincipal(stateRef.get().getSecurityDomain().getEvidenceDecoder().apply(evidence));
}
/**
* Add a public credential to the identity being authenticated.
*
* @param credential the credential to add (must not be {@code null})
*/
public void addPublicCredential(Credential credential) {
Assert.checkNotNullParam("credential", credential);
stateRef.get().addPublicCredential(credential);
}
/**
* Add a private credential to the identity being authenticated. This credential may be forwarded to outbound
* authentication mechanisms.
*
* @param credential the credential to add (must not be {@code null})
*/
public void addPrivateCredential(Credential credential) {
Assert.checkNotNullParam("credential", credential);
stateRef.get().addPrivateCredential(credential);
}
/**
* Add runtime attributes to the identity being authenticated.
*
* @param runtimeAttributes the runtime attributes to add (must not be {@code null})
*/
public void addRuntimeAttributes(Attributes runtimeAttributes) {
Assert.checkNotNullParam("runtimeAttributes", runtimeAttributes);
stateRef.get().addRuntimeAttributes(runtimeAttributes);
}
/**
* Attempt to import the given security identity as a trusted identity. If this method returns {@code true},
* the context will be in an authorized state, and the new identity can be retrieved.
*
* @param identity the identity to import (must not be {@code null})
* @return {@code true} if the identity is authorized, {@code false} otherwise
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
*/
public boolean importIdentity(SecurityIdentity identity) throws RealmUnavailableException {
Assert.checkNotNullParam("identity", identity);
return stateRef.get().importIdentity(identity);
}
/**
* Set the mechanism realm name to be equal to the given name. If no mechanism realms are configured, the realm
* name is ignored.
*
* @param realmName the selected realm name
* @throws IllegalStateException if a realm name was already selected or it is too late to choose a realm
* @throws IllegalArgumentException if the selected realm name was not offered
*/
public void setMechanismRealmName(String realmName) throws IllegalStateException, IllegalArgumentException {
Assert.checkNotNullParam("realmName", realmName);
stateRef.get().setMechanismRealmName(realmName);
}
/**
* Update the credential for the current authentication identity.
*
* @param credential the new credential (must not be {@code null})
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
*/
public void updateCredential(Credential credential) throws RealmUnavailableException {
Assert.checkNotNullParam("credential", credential);
stateRef.get().updateCredential(credential);
}
/**
* Close the server authentication context, failing any in-progress authentication and releasing any
* associated resources.
*/
public void close() {
stateRef.get().fail(false);
}
AtomicReference getStateRef() {
return stateRef;
}
CallbackHandler createCallbackHandler() {
return new CallbackHandler() {
private SSLConnection sslConnection;
private X509Certificate[] peerCerts;
private boolean saslSkipCertificateVerification;
@Override
public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException {
handleOne(callbacks, 0);
}
private void handleOne(final Callback[] callbacks, final int idx) throws IOException, UnsupportedCallbackException {
if (idx == callbacks.length) {
return;
}
final AtomicReference stateRef = getStateRef();
final Callback callback = callbacks[idx];
if (callback instanceof AnonymousAuthorizationCallback) {
boolean authorized = authorizeAnonymous();
log.tracef("Handling AnonymousAuthorizationCallback: authorized = %b", authorized);
((AnonymousAuthorizationCallback) callback).setAuthorized(authorized);
handleOne(callbacks, idx + 1);
} else if (callback instanceof AuthorizeCallback) {
final AuthorizeCallback authorizeCallback = (AuthorizeCallback) callback;
String authenticationID = authorizeCallback.getAuthenticationID();
if (authenticationID != null) {
// always re-set the authentication name to ensure it hasn't changed.
setAuthenticationName(authenticationID);
} else {
// This is a special case to support scenarios where the identity was already established by some
// external method (e.g.: EXTERNAL SASL and TLS) where only authorization is necessary. We delay authentication
// until we receive an authorization request.
// In the future, we may want to support external methods other than TLS peer authentication
if (stateRef.get().canVerifyEvidence() && peerCerts != null) {
log.tracef("Authentication ID is null but SSL peer certificates are available. Trying to authenticate peer");
// if SASL mechanism is used with skip-certificate-verification property then do not verifyEvidence against the security realm
if (saslSkipCertificateVerification) {
// Since evidence verification is being skipped here, ensure evidence decoding still takes place
X509PeerCertificateChainEvidence evidence = new X509PeerCertificateChainEvidence(peerCerts);
setDecodedEvidencePrincipal(evidence);
stateRef.get().setPrincipal(evidence.getDecodedPrincipal(), false);
}
else {
verifyEvidence(new X509PeerCertificateChainEvidence(peerCerts));
}
}
}
String authorizationID = authorizeCallback.getAuthorizationID();
boolean authorized = authorizationID != null ? authorize(authorizationID) : authorize();
log.tracef("Handling AuthorizeCallback: authenticationID = %s authorizationID = %s authorized = %b", authenticationID, authorizationID, authorized);
authorizeCallback.setAuthorized(authorized);
handleOne(callbacks, idx + 1);
} else if (callback instanceof ExclusiveNameCallback) {
final ExclusiveNameCallback exclusiveNameCallback = ((ExclusiveNameCallback) callback);
// login name
final String name = exclusiveNameCallback.getDefaultName();
try {
boolean exclusive = exclusiveNameCallback.needsExclusiveAccess();
log.tracef("Handling ExclusiveNameCallback: authenticationName = %s needsExclusiveAccess = %b", name, exclusive);
if (exclusive) {
setAuthenticationName(name, true);
exclusiveNameCallback.setExclusiveAccess(true);
} else {
setAuthenticationName(name);
}
} catch (Exception e) {
throw new IOException(e);
}
handleOne(callbacks, idx + 1);
} else if (callback instanceof NameCallback) {
// login name
final String name = ((NameCallback) callback).getDefaultName();
try {
log.tracef("Handling NameCallback: authenticationName = %s", name);
setAuthenticationName(name);
} catch (Exception e) {
throw new IOException(e);
}
handleOne(callbacks, idx + 1);
} else if (callback instanceof PeerPrincipalCallback) {
// login name
final Principal principal = ((PeerPrincipalCallback) callback).getPrincipal();
try {
log.tracef("Handling PeerPrincipalCallback: principal = %s", principal);
setAuthenticationPrincipal(principal);
} catch (Exception e) {
throw new IOException(e);
}
handleOne(callbacks, idx + 1);
} else if (callback instanceof PasswordCallback) {
final PasswordCallback passwordCallback = (PasswordCallback) callback;
if (getCredentialAcquireSupport(PasswordCredential.class).mayBeSupported()) {
final TwoWayPassword password = applyToCredential(PasswordCredential.class, c -> c.getPassword(TwoWayPassword.class));
if (password != null) {
final ClearPasswordSpec clearPasswordSpec;
try {
final PasswordFactory passwordFactory = PasswordFactory.getInstance(password.getAlgorithm());
clearPasswordSpec = passwordFactory.getKeySpec(password, ClearPasswordSpec.class);
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
log.trace("Unable to get key spec", e);
throw new FastUnsupportedCallbackException(callback);
}
log.tracef("Handling PasswordCallback: obtained successfully");
passwordCallback.setPassword(clearPasswordSpec.getEncodedPassword());
handleOne(callbacks, idx + 1);
return;
}
log.tracef("Handling PasswordCallback: failed to obtain PasswordCredential");
throw new FastUnsupportedCallbackException(callback);
}
// otherwise just fail out; some mechanisms will try again with different credentials
log.tracef("Handling PasswordCallback: PasswordCredential may not be supported");
throw new FastUnsupportedCallbackException(callback);
} else if (callback instanceof CredentialCallback) {
final CredentialCallback credentialCallback = (CredentialCallback) callback;
String requestedRealm = stateRef.get().getMechanismRealmConfiguration().getRealmName();
final Credential credential = getCredential(credentialCallback.getCredentialType(), credentialCallback.getAlgorithm(), credentialCallback.getParameterSpec());
if (credential != null) {
if (credential instanceof PasswordCredential) {
Password password = ((PasswordCredential) credential).getPassword();
if (password instanceof DigestPassword) {
String providedRealm = ((DigestPassword) password).getRealm();
if ( ! providedRealm.equals(requestedRealm)) {
log.tracef("Handling CredentialCallback: credential for realm \"%s\" is not available (\"%s\" provided)", requestedRealm, providedRealm);
throw new FastUnsupportedCallbackException(callback);
} else {
log.tracef("Handling CredentialCallback: obtained credential for correct realm \"%s\"", providedRealm);
}
}
}
log.tracef("Handling CredentialCallback: obtained credential: %s", credential);
credentialCallback.setCredential(credential);
handleOne(callbacks, idx + 1);
return;
}
// otherwise just fail out; some mechanisms will try again with different credentials
log.tracef("Handling CredentialCallback: failed to obtain credential");
throw new FastUnsupportedCallbackException(callback);
} else if (callback instanceof ServerCredentialCallback) {
final ServerCredentialCallback serverCredentialCallback = (ServerCredentialCallback) callback;
CredentialSource serverCredentialSource = stateRef.get().getMechanismConfiguration().getServerCredentialSource();
final Class 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()));
handleOne(callbacks, idx + 1);
} else if (callback instanceof EvidenceDecodePrincipalCallback) {
EvidenceDecodePrincipalCallback evidenceDecodePrincipalCallback = (EvidenceDecodePrincipalCallback) callback;
setDecodedEvidencePrincipal(evidenceDecodePrincipalCallback.getEvidence());
} else if (callback instanceof SSLCallback) {
SSLCallback sslCallback = (SSLCallback) callback;
this.sslConnection = sslCallback.getSslConnection();
try {
peerCerts = X500.asX509CertificateArray(sslCallback.getSslConnection().getSession().getPeerCertificates());
} catch (SSLPeerUnverifiedException e) {
log.trace("Peer unverified", e);
peerCerts = null;
}
handleOne(callbacks, idx + 1);
} else if (callback instanceof ChannelBindingCallback) {
final SSLConnection sslConnection = this.sslConnection;
if (sslConnection != null) {
sslConnection.handleChannelBindingCallback((ChannelBindingCallback) callback);
}
handleOne(callbacks, idx + 1);
} else if (callback instanceof AuthenticationCompleteCallback) {
if (! isDone()) {
if (((AuthenticationCompleteCallback) callback).succeeded()) {
log.tracef("Handling AuthenticationCompleteCallback: succeed");
succeed();
} else {
log.tracef("Handling AuthenticationCompleteCallback: fail");
fail();
}
}
handleOne(callbacks, idx + 1);
} else if (callback instanceof SocketAddressCallback) {
final SocketAddressCallback socketAddressCallback = (SocketAddressCallback) callback;
log.tracef("Handling SocketAddressCallback");
if (socketAddressCallback.getKind() == SocketAddressCallback.Kind.PEER) {
Attributes runtimeAttributes = new MapAttributes();
if ((socketAddressCallback.getAddress() != null) && ((InetSocketAddress) socketAddressCallback.getAddress()).getAddress() != null) {
runtimeAttributes.addFirst(KEY_SOURCE_ADDRESS, ((InetSocketAddress) socketAddressCallback.getAddress()).getAddress().getHostAddress());
addRuntimeAttributes(runtimeAttributes);
} else {
log.tracef("Client's IP address is unknown.");
}
}
handleOne(callbacks, idx + 1);
} else if (callback instanceof SecurityIdentityCallback) {
SecurityIdentity identity = getAuthorizedIdentity();
log.tracef("Handling SecurityIdentityCallback: identity = %s", identity);
((SecurityIdentityCallback) callback).setSecurityIdentity(identity);
handleOne(callbacks, idx + 1);
} else if (callback instanceof AvailableRealmsCallback) {
Collection names = stateRef.get().getMechanismConfiguration().getMechanismRealmNames();
if (log.isTraceEnabled()) {
log.tracef("Handling AvailableRealmsCallback: realms = [%s]", String.join(", ", names));
}
if (! names.isEmpty()) {
((AvailableRealmsCallback) callback).setRealmNames(names.toArray(new String[names.size()]));
}
handleOne(callbacks, idx + 1);
} else if (callback instanceof RealmCallback) {
RealmCallback rcb = (RealmCallback) callback;
String mechanismRealm = rcb.getText();
if (mechanismRealm == null) {
mechanismRealm = rcb.getDefaultText();
}
log.tracef("Handling RealmCallback: selected = [%s]", mechanismRealm);
setMechanismRealmName(mechanismRealm);
handleOne(callbacks, idx + 1);
} else if (callback instanceof MechanismInformationCallback) {
MechanismInformationCallback mic = (MechanismInformationCallback) callback;
try {
MechanismInformation mi = mic.getMechanismInformation();
if (log.isTraceEnabled()) {
log.tracef("Handling MechanismInformationCallback type='%s' name='%s' host-name='%s' protocol='%s'",
mi.getMechanismType(), mi.getMechanismName(), mi.getHostName(), mi.getProtocol());
}
setMechanismInformation(mi);
handleOne(callbacks, idx + 1);
} catch (Exception e) {
throw new IOException(e);
}
} else if (callback instanceof CredentialUpdateCallback) {
final CredentialUpdateCallback credentialUpdateCallback = (CredentialUpdateCallback) callback;
log.tracef("Handling CredentialUpdateCallback");
updateCredential(credentialUpdateCallback.getCredential());
handleOne(callbacks, idx + 1);
} else if (callback instanceof CachedIdentityAuthorizeCallback) {
CachedIdentityAuthorizeCallback authorizeCallback = (CachedIdentityAuthorizeCallback) callback;
authorizeCallback.setSecurityDomain(stateRef.get().getSecurityDomain());
SecurityIdentity authorizedIdentity = null;
Principal principal = null;
SecurityIdentity identity = authorizeCallback.getIdentity();
if (identity != null && importIdentity(identity)) {
authorizedIdentity = getAuthorizedIdentity();
} else {
principal = authorizeCallback.getPrincipal();
if (principal == null) {
principal = authorizeCallback.getAuthorizationPrincipal();
}
if (principal != null) {
setAuthenticationPrincipal(principal);
if (authorize()) {
authorizedIdentity = getAuthorizedIdentity();
}
}
}
log.tracef("Handling CachedIdentityAuthorizeCallback: principal = %s authorizedIdentity = %s", principal, authorizedIdentity);
authorizeCallback.setAuthorized(authorizedIdentity);
handleOne(callbacks, idx + 1);
} else if (callback instanceof IdentityCredentialCallback) {
IdentityCredentialCallback icc = (IdentityCredentialCallback) callback;
Credential credential = icc.getCredential();
if (icc.isPrivate()) {
addPrivateCredential(credential);
} else {
addPublicCredential(credential);
}
handleOne(callbacks, idx + 1);
} else if (callback instanceof PrincipalAuthorizeCallback) {
PrincipalAuthorizeCallback authorizeCallback = (PrincipalAuthorizeCallback) callback;
Principal principal = authorizeCallback.getPrincipal();
// always re-set the principal to ensure it hasn't changed.
setAuthenticationPrincipal(principal);
boolean authorized = authorize();
log.tracef("Handling PrincipalAuthorizeCallback: principal = %s authorized = %b", principal, authorized);
authorizeCallback.setAuthorized(authorized);
handleOne(callbacks, idx + 1);
} else if (callback instanceof AuthenticationConfigurationCallback) {
AuthenticationConfigurationCallback authenticationConfigurationCallback = (AuthenticationConfigurationCallback) callback;
saslSkipCertificateVerification = authenticationConfigurationCallback.getSaslSkipCertificateVerification();
handleOne(callbacks, idx + 1);
} else if (callback instanceof RequestInformationCallback) {
HashMap props = ((RequestInformationCallback) callback).getProperties();
Attributes runtimeAttributes = new MapAttributes();
for (Map.Entry entry : props.entrySet()) {
if (entry.getValue() != null) {
runtimeAttributes.addFirst(entry.getKey(), entry.getValue().toString());
}
}
addRuntimeAttributes(runtimeAttributes);
handleOne(callbacks, idx + 1);
}
else {
CallbackUtil.unsupported(callback);
handleOne(callbacks, idx + 1);
}
}
};
}
private static Principal rewriteAll(Principal principal, Function r1, Function r2, Function r3) {
principal = r1.apply(principal);
if (principal == null) return null;
principal = r2.apply(principal);
if (principal == null) return null;
principal = r3.apply(principal);
return principal;
}
static String mapAll(Principal principal, RealmMapper r1, RealmMapper r2, RealmMapper r3, String defaultRealmName, Evidence evidence) {
if (r1 != null) {
return mapRealmName(principal, r1, defaultRealmName, evidence);
}
if (r2 != null) {
return mapRealmName(principal, r2, defaultRealmName, evidence);
}
if (r3 != null) {
return mapRealmName(principal, r3, defaultRealmName, evidence);
}
return defaultRealmName;
}
private static String mapRealmName(Principal principal, RealmMapper realmMapper, String defaultRealmName, Evidence evidence) {
String realmName = realmMapper.getRealmMapping(principal, evidence);
return realmName != null ? realmName : defaultRealmName;
}
State assignName(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration, Principal originalPrincipal, final Evidence evidence, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) throws RealmUnavailableException {
return assignName(capturedIdentity, mechanismConfiguration, mechanismRealmConfiguration, originalPrincipal, evidence, privateCredentials, publicCredentials, false, runtimeAttributes);
}
State assignName(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration, Principal originalPrincipal, final Evidence evidence, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final boolean exclusive, final Attributes runtimeAttributes) throws RealmUnavailableException {
final SecurityDomain domain = capturedIdentity.getSecurityDomain();
final Principal preRealmPrincipal = rewriteAll(originalPrincipal, mechanismRealmConfiguration.getPreRealmRewriter(), mechanismConfiguration.getPreRealmRewriter(), domain.getPreRealmRewriter());
if (preRealmPrincipal == null) {
log.tracef("Unable to rewrite principal [%s] by pre-realm rewritters", originalPrincipal);
return new InvalidNameState(capturedIdentity, mechanismConfiguration, mechanismRealmConfiguration, privateCredentials, publicCredentials, runtimeAttributes);
}
String realmName = mapAll(preRealmPrincipal, mechanismRealmConfiguration.getRealmMapper(), mechanismConfiguration.getRealmMapper(), domain.getRealmMapper(), domain.getDefaultRealmName(), evidence);
final RealmInfo realmInfo = domain.getRealmInfo(realmName);
final Principal postRealmPrincipal = rewriteAll(preRealmPrincipal, mechanismRealmConfiguration.getPostRealmRewriter(), mechanismConfiguration.getPostRealmRewriter(), domain.getPostRealmRewriter());
if (postRealmPrincipal == null) {
log.tracef("Unable to rewrite principal [%s] by post-realm rewritters", preRealmPrincipal);
return new InvalidNameState(capturedIdentity, mechanismConfiguration, mechanismRealmConfiguration, privateCredentials, publicCredentials, runtimeAttributes);
}
final Principal finalPrincipal = rewriteAll(postRealmPrincipal, mechanismRealmConfiguration.getFinalRewriter(), mechanismConfiguration.getFinalRewriter(), realmInfo.getPrincipalRewriter());
if (finalPrincipal == null) {
log.tracef("Unable to rewrite principal [%s] by final rewritters", postRealmPrincipal);
return new InvalidNameState(capturedIdentity, mechanismConfiguration, mechanismRealmConfiguration, privateCredentials, publicCredentials, runtimeAttributes);
}
log.tracef("Principal assigning: [%s], pre-realm rewritten: [%s], realm name: [%s], post-realm rewritten: [%s], realm rewritten: [%s]",
originalPrincipal, preRealmPrincipal, realmName, postRealmPrincipal, finalPrincipal);
final SecurityRealm securityRealm = realmInfo.getSecurityRealm();
final RealmIdentity realmIdentity;
try {
if (exclusive) {
if (securityRealm instanceof ModifiableSecurityRealm) {
realmIdentity = ((ModifiableSecurityRealm) securityRealm).getRealmIdentityForUpdate(finalPrincipal);
} else {
throw log.unableToObtainExclusiveAccess();
}
} else {
realmIdentity = securityRealm.getRealmIdentity(finalPrincipal);
}
} catch (RealmUnavailableException e) {
SecurityDomain.safeHandleSecurityEvent(domain, new SecurityRealmUnavailableEvent(capturedIdentity, realmName));
throw e;
}
return new NameAssignedState(capturedIdentity, realmInfo, realmIdentity, preRealmPrincipal, mechanismConfiguration, mechanismRealmConfiguration, privateCredentials, publicCredentials, runtimeAttributes);
}
abstract static class State {
MechanismConfiguration getMechanismConfiguration() {
throw log.noAuthenticationInProgress();
}
MechanismRealmConfiguration getMechanismRealmConfiguration() {
throw log.noAuthenticationInProgress();
}
SecurityIdentity getAuthorizedIdentity() {
throw log.noAuthenticationInProgress();
}
Principal getAuthenticationPrincipal() {
throw log.noAuthenticationInProgress();
}
boolean isSamePrincipal(Principal principal) {
return false;
}
SupportLevel getCredentialAcquireSupport(Class extends Credential> credentialType, String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
throw log.noAuthenticationInProgress();
}
SupportLevel getEvidenceVerifySupport(Class extends Evidence> evidenceType, String algorithmName) throws RealmUnavailableException {
throw log.noAuthenticationInProgress();
}
C getCredential(Class credentialType, String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
throw log.noAuthenticationInProgress();
}
boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException {
throw log.noAuthenticationInProgress();
}
boolean importIdentity(final SecurityIdentity identity) throws RealmUnavailableException {
throw log.noAuthenticationInProgress();
}
RealmIdentity getRealmIdentity() {
throw log.noAuthenticationInProgress();
}
SecurityDomain getSecurityDomain() {
throw log.noAuthenticationInProgress();
}
boolean authorizeAnonymous(final boolean requireLoginPermission) {
throw log.noAuthenticationInProgress();
}
void setMechanismInformation(final MechanismInformation mechanismInformation) {
throw log.noAuthenticationInProgress();
}
void setPrincipal(Principal principal, boolean exclusive) throws RealmUnavailableException {
throw log.noAuthenticationInProgress();
}
boolean authorize(final boolean requireLoginPermission) throws RealmUnavailableException {
throw log.noAuthenticationInProgress();
}
boolean authorize(Principal authorizationId, final boolean authorizeRunAs) throws RealmUnavailableException {
throw log.noAuthenticationInProgress();
}
void setMechanismRealmName(String name) {
throw log.noAuthenticationInProgress();
}
void updateCredential(Credential credential) throws RealmUnavailableException {
throw log.noAuthenticationInProgress();
}
void succeed() {
throw log.noAuthenticationInProgress();
}
void fail(final boolean requireInProgress) {
if (requireInProgress) throw log.noAuthenticationInProgress();
}
boolean isDone() {
return false;
}
void addPublicCredential(final Credential credential) {
throw log.noAuthenticationInProgress();
}
void addPrivateCredential(final Credential credential) {
throw log.noAuthenticationInProgress();
}
void addRuntimeAttributes(final Attributes runtimeAttributes) {
throw log.noAuthenticationInProgress();
}
/**
* Indicate whether or not current state is {@link NameAssignedState}.
*
* @return {@code true} if state is {@link NameAssignedState}. Otherwise, {@code false}.
*/
public boolean isNameAssigned() {
return this instanceof NameAssignedState;
}
/**
* Indicate whether or not current state is {@link AuthorizedState}.
*
* @return {@code true} if state is {@link AuthorizedState}. Otherwise, {@code false}.
*/
public boolean isAuthorized() {
return this instanceof AuthorizedState;
}
/**
* Indicate whether or not evidence verification is allowed.
*
* @return {@code true} if evidence verification can be performed. Otherwise, {@code false}.
*/
public boolean canVerifyEvidence() {
return !(this instanceof NameAssignedState || this instanceof AuthorizedState);
}
}
final class InactiveState extends State {
private final SecurityIdentity capturedIdentity;
private final MechanismConfigurationSelector mechanismConfigurationSelector;
private final MechanismInformation mechanismInformation;
private final IdentityCredentials privateCredentials;
private final IdentityCredentials publicCredentials;
private final Attributes runtimeAttributes;
public InactiveState(SecurityIdentity capturedIdentity, MechanismConfigurationSelector mechanismConfigurationSelector, IdentityCredentials privateCredentials, IdentityCredentials publicCredentials, Attributes runtimeAttributes) {
this(capturedIdentity, mechanismConfigurationSelector, MechanismInformation.DEFAULT, privateCredentials, publicCredentials, runtimeAttributes);
}
public InactiveState(SecurityIdentity capturedIdentity, MechanismConfigurationSelector mechanismConfigurationSelector,
MechanismInformation mechanismInformation, IdentityCredentials privateCredentials, IdentityCredentials publicCredentials, Attributes runtimeAttributes) {
this.capturedIdentity = capturedIdentity;
this.mechanismConfigurationSelector = mechanismConfigurationSelector;
this.mechanismInformation = checkNotNullParam("mechanismInformation", mechanismInformation);
this.privateCredentials = privateCredentials;
this.publicCredentials = publicCredentials;
this.runtimeAttributes = runtimeAttributes;
}
@Override
void setMechanismInformation(MechanismInformation mechanismInformation) {
InactiveState inactiveState = new InactiveState(capturedIdentity, mechanismConfigurationSelector, mechanismInformation, privateCredentials, publicCredentials, runtimeAttributes);
InitialState nextState = inactiveState.selectMechanismConfiguration();
if (! stateRef.compareAndSet(this, nextState)) {
stateRef.get().setMechanismInformation(mechanismInformation);
}
}
@Override
SecurityDomain getSecurityDomain() {
return capturedIdentity.getSecurityDomain();
}
boolean authorize(Principal authorizationId, boolean authorizeRunAs) throws RealmUnavailableException {
transition();
return stateRef.get().authorize(authorizationId, authorizeRunAs);
}
@Override
void setMechanismRealmName(String name) {
transition();
stateRef.get().setMechanismRealmName(name);
}
@Override
MechanismRealmConfiguration getMechanismRealmConfiguration() {
transition();
return stateRef.get().getMechanismRealmConfiguration();
}
@Override
void fail(final boolean requireInProgress) {
transition();
stateRef.get().fail(requireInProgress);
}
@Override
boolean authorizeAnonymous(boolean requireLoginPermission) {
transition();
return stateRef.get().authorizeAnonymous(requireLoginPermission);
}
@Override
boolean authorize(boolean requireLoginPermission) throws RealmUnavailableException {
transition();
return stateRef.get().authorize(requireLoginPermission);
}
@Override
boolean importIdentity(SecurityIdentity identity) throws RealmUnavailableException {
transition();
return stateRef.get().importIdentity(identity);
}
@Override
SupportLevel getEvidenceVerifySupport(final Class 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, boolean exclusive) throws RealmUnavailableException {
transition();
stateRef.get().setPrincipal(principal, exclusive);
}
@Override
MechanismConfiguration getMechanismConfiguration() {
transition();
return stateRef.get().getMechanismConfiguration();
}
@Override
void addPublicCredential(final Credential credential) {
final InactiveState newState = new InactiveState(capturedIdentity, mechanismConfigurationSelector, mechanismInformation, privateCredentials, publicCredentials.withCredential(credential), runtimeAttributes);
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addPublicCredential(credential);
}
}
@Override
void addPrivateCredential(final Credential credential) {
final InactiveState newState = new InactiveState(capturedIdentity, mechanismConfigurationSelector, mechanismInformation, privateCredentials.withCredential(credential), publicCredentials, runtimeAttributes);
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addPrivateCredential(credential);
}
}
@Override
void addRuntimeAttributes(final Attributes runtimeAttributes) {
final InactiveState newState = new InactiveState(capturedIdentity, mechanismConfigurationSelector, mechanismInformation, privateCredentials, publicCredentials, AggregateAttributes.aggregateOf(this.runtimeAttributes, runtimeAttributes));
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addRuntimeAttributes(runtimeAttributes);
}
}
private void transition() {
InitialState initialState = selectMechanismConfiguration();
stateRef.compareAndSet(this, initialState);
}
private InitialState selectMechanismConfiguration() {
MechanismConfiguration mechanismConfiguration = mechanismConfigurationSelector.selectConfiguration(mechanismInformation);
if (mechanismConfiguration == null) {
throw log.unableToSelectMechanismConfiguration(mechanismInformation.getMechanismType(),
mechanismInformation.getMechanismName(), mechanismInformation.getHostName(),
mechanismInformation.getProtocol());
}
return new InitialState(capturedIdentity, mechanismConfiguration, mechanismConfigurationSelector, privateCredentials, publicCredentials, runtimeAttributes);
}
}
abstract class ActiveState extends State {
ActiveState() {
}
boolean authorize(Principal authorizationId, boolean authorizeRunAs) throws RealmUnavailableException {
final AtomicReference stateRef = getStateRef();
// get the identity we are authorizing from
final SecurityIdentity sourceIdentity = getSourceIdentity();
final State state = assignName(sourceIdentity, getMechanismConfiguration(), getMechanismRealmConfiguration(), authorizationId, null, IdentityCredentials.NONE, IdentityCredentials.NONE, Attributes.EMPTY);
if (!state.isNameAssigned()) {
ElytronMessages.log.tracef("Authorization failed - unable to assign identity name");
return false;
}
final NameAssignedState nameAssignedState = (NameAssignedState) state;
final RealmIdentity realmIdentity = nameAssignedState.getRealmIdentity();
boolean ok = false;
try {
if (! realmIdentity.exists()) {
ElytronMessages.log.tracef("Authorization failed - identity does not exist");
return false;
}
// check the run-as permission on the old identity
if (authorizeRunAs && ! sourceIdentity.implies(new RunAsPrincipalPermission(nameAssignedState.getAuthenticationPrincipal().getName()))) {
ElytronMessages.log.tracef("Authorization failed - source identity does not have RunAsPrincipalPermission");
return false;
}
final AuthorizedAuthenticationState newState = nameAssignedState.doAuthorization(false);
if (newState == null) {
return false;
}
if (! stateRef.compareAndSet(this, newState)) {
// try again
return stateRef.get().authorize(authorizationId, authorizeRunAs);
}
ok = true;
return true;
} finally {
if (! ok) realmIdentity.dispose();
}
}
@Override
void setMechanismRealmName(final String realmName) {
final MechanismRealmConfiguration currentConfiguration = getMechanismRealmConfiguration();
final MechanismConfiguration mechanismConfiguration = getMechanismConfiguration();
if (mechanismConfiguration.getMechanismRealmNames().isEmpty()) {
// no realms are configured
throw log.invalidMechRealmSelection(realmName);
}
final MechanismRealmConfiguration configuration = mechanismConfiguration.getMechanismRealmConfiguration(realmName);
if (configuration == null) {
throw log.invalidMechRealmSelection(realmName);
}
if (currentConfiguration != configuration) {
throw log.mechRealmAlreadySelected();
}
}
@Override
void setMechanismInformation(MechanismInformation mechanismInformation) {
throw log.tooLateToSetMechanismInformation();
}
abstract SecurityIdentity getSourceIdentity();
}
/**
* State shared among both the initial state and the realm-assigned state, where no authentication name is yet set.
*/
abstract class UnassignedState extends ActiveState {
final SecurityIdentity capturedIdentity;
final MechanismConfiguration mechanismConfiguration;
final IdentityCredentials privateCredentials;
final IdentityCredentials publicCredentials;
final Attributes runtimeAttributes;
UnassignedState(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) {
this.capturedIdentity = capturedIdentity;
this.mechanismConfiguration = mechanismConfiguration;
this.privateCredentials = privateCredentials;
this.publicCredentials = publicCredentials;
this.runtimeAttributes = runtimeAttributes;
}
SecurityIdentity getSourceIdentity() {
return capturedIdentity;
}
@Override
SecurityDomain getSecurityDomain() {
return capturedIdentity.getSecurityDomain();
}
@Override
void fail(final boolean requireInProgress) {
final AtomicReference stateRef = getStateRef();
if (! stateRef.compareAndSet(this, FAILED)) {
// recurse & retry
stateRef.get().fail(requireInProgress);
}
}
@Override
boolean authorizeAnonymous(final boolean requireLoginPermission) {
final AtomicReference stateRef = getStateRef();
final SecurityIdentity anonymousIdentity = getSecurityDomain().getAnonymousSecurityIdentity();
return (! requireLoginPermission || anonymousIdentity.implies(LoginPermission.getInstance())) && (stateRef.compareAndSet(this, new AnonymousAuthorizedState(anonymousIdentity)) || stateRef.get().authorizeAnonymous(requireLoginPermission));
}
@Override
boolean authorize(final boolean requireLoginPermission) throws RealmUnavailableException {
final SecurityIdentity capturedIdentity = this.capturedIdentity;
if (capturedIdentity.isAnonymous()) {
return authorizeAnonymous(requireLoginPermission);
}
final AtomicReference stateRef = getStateRef();
return (! requireLoginPermission || capturedIdentity.implies(LoginPermission.getInstance())) && (stateRef.compareAndSet(this, new AuthorizedState(capturedIdentity, capturedIdentity.getPrincipal(), capturedIdentity.getRealmInfo(), mechanismConfiguration, getMechanismRealmConfiguration())) || stateRef.get().authorize(requireLoginPermission));
}
@Override
boolean importIdentity(final SecurityIdentity importedIdentity) throws RealmUnavailableException {
// As long as a name is not yet assigned, we can authorize an imported identity
final RealmInfo evidenceRealmInfo = importedIdentity.getRealmInfo();
final SecurityRealm evidenceSecurityRealm = evidenceRealmInfo.getSecurityRealm();
final SecurityDomain evidenceSecurityDomain = importedIdentity.getSecurityDomain();
final AtomicReference stateRef = getStateRef();
final SecurityIdentity sourceIdentity = getSourceIdentity();
final SecurityDomain domain = sourceIdentity.getSecurityDomain();
// Check that the given security identity evidence either corresponds to the same realm that created the
// current authentication identity or it corresponds to a domain that is trusted by the current domain
if (importedIdentity.isAnonymous()) {
AnonymousAuthorizedState newState = new AnonymousAuthorizedState(domain.getAnonymousSecurityIdentity());
return stateRef.compareAndSet(this, newState) || stateRef.get().importIdentity(importedIdentity);
}
final Principal importedPrincipal = importedIdentity.getPrincipal();
if (domain == importedIdentity.getSecurityDomain()) {
// it's authorized already because it's the same domain
AuthorizedState newState = new AuthorizedState(importedIdentity, importedPrincipal, importedIdentity.getRealmInfo(), mechanismConfiguration, getMechanismRealmConfiguration());
return stateRef.compareAndSet(this, newState) || stateRef.get().importIdentity(importedIdentity);
}
boolean trusted = false;
// it didn't come from our domain. Check to see if it came from a trusted domain.
if (domain.trustsDomain(evidenceSecurityDomain)) {
trusted = true;
}
// Finally, run the identity through the normal name selection process.
final State state = assignName(sourceIdentity, mechanismConfiguration, getMechanismRealmConfiguration(), importedPrincipal, null, privateCredentials, publicCredentials, runtimeAttributes);
if (!state.isNameAssigned()) {
return false;
}
final NameAssignedState nameState = (NameAssignedState) state;
final RealmIdentity realmIdentity = nameState.getRealmIdentity();
boolean ok = false;
try {
if (! trusted) {
if (nameState.getRealmInfo().getSecurityRealm() != evidenceSecurityRealm) {
// mapped realm does not correspond with the imported realm name
return false;
}
}
// with the name we have now, try and authorize
final AuthorizedAuthenticationState authzState = nameState.doAuthorization(false);
if (authzState == null) {
return false;
}
if (! stateRef.compareAndSet(this, authzState)) {
return stateRef.get().importIdentity(importedIdentity);
}
ok = true;
return true;
} finally {
if (! ok) realmIdentity.dispose();
}
}
@Override
SupportLevel getEvidenceVerifySupport(final Class 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 stateRef = getStateRef();
setDecodedEvidencePrincipal(evidence);
Principal evidencePrincipal = evidence.getDecodedPrincipal();
log.tracef("Evidence verification: evidence = %s evidencePrincipal = %s", evidence, evidencePrincipal);
final MechanismRealmConfiguration mechanismRealmConfiguration = getMechanismRealmConfiguration();
if (evidencePrincipal != null) {
final State newState = assignName(getSourceIdentity(), mechanismConfiguration, mechanismRealmConfiguration, evidencePrincipal, evidence, privateCredentials, publicCredentials, runtimeAttributes);
if (! newState.verifyEvidence(evidence)) {
if (newState.isNameAssigned()) {
((NameAssignedState)newState).realmIdentity.dispose();
}
return false;
}
if (! stateRef.compareAndSet(this, newState)) {
if (newState.isNameAssigned()) {
((NameAssignedState)newState).realmIdentity.dispose();
}
return stateRef.get().verifyEvidence(evidence);
}
return true;
}
Class extends Evidence> evidenceType = evidence.getClass();
String algorithm = evidence instanceof AlgorithmEvidence ? ((AlgorithmEvidence) evidence).getAlgorithm() : null;
// verify evidence with no name set: use the realms to find a match (SSO scenario, etc.)
final SecurityDomain domain = getSecurityDomain();
final Collection realmInfos = domain.getRealmInfos();
RealmIdentity realmIdentity = null;
RealmInfo realmInfo = null;
for (RealmInfo info : realmInfos) {
try {
realmIdentity = info.getSecurityRealm().getRealmIdentity(evidence);
if (realmIdentity.getEvidenceVerifySupport(evidenceType, algorithm).mayBeSupported()) {
realmInfo = info;
break;
} else {
realmIdentity.dispose();
}
} catch (RealmUnavailableException e) {
SecurityDomain.safeHandleSecurityEvent(domain, new SecurityRealmUnavailableEvent(domain.getCurrentSecurityIdentity(), info.getName()));
throw e;
}
}
if (realmInfo == null) {
// no verification possible, no identity found
return false;
}
if (! realmIdentity.verifyEvidence(evidence)) {
realmIdentity.dispose();
return false;
}
final Principal resolvedPrincipal = realmIdentity.getRealmIdentityPrincipal();
if (resolvedPrincipal == null) {
// we have to have a principal
realmIdentity.dispose();
return false;
}
final NameAssignedState newState = new NameAssignedState(getSourceIdentity(), realmInfo, realmIdentity, resolvedPrincipal, mechanismConfiguration, mechanismRealmConfiguration, privateCredentials, publicCredentials, runtimeAttributes);
if (! stateRef.compareAndSet(this, newState)) {
realmIdentity.dispose();
return stateRef.get().verifyEvidence(evidence);
}
return true;
}
@Override
void setPrincipal(final Principal principal, final boolean exclusive) throws RealmUnavailableException {
Assert.checkNotNullParam("principal", principal);
final AtomicReference stateRef = getStateRef();
final State newState = assignName(capturedIdentity, mechanismConfiguration, getMechanismRealmConfiguration(), principal, null, privateCredentials, publicCredentials, exclusive, runtimeAttributes);
if (! stateRef.compareAndSet(this, newState)) {
if (newState.isNameAssigned()) {
((NameAssignedState)newState).realmIdentity.dispose();
}
stateRef.get().setPrincipal(principal, exclusive);
}
}
@Override
MechanismConfiguration getMechanismConfiguration() {
return mechanismConfiguration;
}
IdentityCredentials getPrivateCredentials() {
return privateCredentials;
}
IdentityCredentials getPublicCredentials() {
return publicCredentials;
}
Attributes getRuntimeAttributes() {
return runtimeAttributes;
}
}
final class InitialState extends UnassignedState {
private final MechanismConfigurationSelector mechanismConfigurationSelector;
InitialState(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final MechanismConfigurationSelector mechanismConfigurationSelector, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) {
super(capturedIdentity, mechanismConfiguration, privateCredentials, publicCredentials, runtimeAttributes);
this.mechanismConfigurationSelector = mechanismConfigurationSelector;
}
@Override
void setMechanismRealmName(final String realmName) {
final MechanismConfiguration mechanismConfiguration = getMechanismConfiguration();
if (mechanismConfiguration.getMechanismRealmNames().isEmpty()) {
// no realms are configured
throw log.invalidMechRealmSelection(realmName);
}
final MechanismRealmConfiguration configuration = mechanismConfiguration.getMechanismRealmConfiguration(realmName);
if (configuration == null) {
throw log.invalidMechRealmSelection(realmName);
}
final AtomicReference stateRef = getStateRef();
if (! stateRef.compareAndSet(this, new RealmAssignedState(capturedIdentity, mechanismConfiguration, configuration, privateCredentials, publicCredentials, runtimeAttributes))) {
stateRef.get().setMechanismRealmName(realmName);
}
}
@Override
MechanismRealmConfiguration getMechanismRealmConfiguration() {
final Collection mechanismRealmNames = mechanismConfiguration.getMechanismRealmNames();
final Iterator iterator = mechanismRealmNames.iterator();
if (iterator.hasNext()) {
// use the default realm
return mechanismConfiguration.getMechanismRealmConfiguration(iterator.next());
} else {
return MechanismRealmConfiguration.NO_REALM;
}
}
@Override
void setMechanismInformation(MechanismInformation mechanismInformation) {
InactiveState inactiveState = new InactiveState(capturedIdentity, mechanismConfigurationSelector, mechanismInformation, privateCredentials, publicCredentials, runtimeAttributes);
InitialState newState = inactiveState.selectMechanismConfiguration();
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().setMechanismInformation(mechanismInformation);
}
}
void addPublicCredential(final Credential credential) {
final InitialState newState = new InitialState(getSourceIdentity(), getMechanismConfiguration(), mechanismConfigurationSelector, getPrivateCredentials(), getPublicCredentials().withCredential(credential), runtimeAttributes);
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addPublicCredential(credential);
}
}
@Override
void addPrivateCredential(final Credential credential) {
final InitialState newState = new InitialState(getSourceIdentity(), getMechanismConfiguration(), mechanismConfigurationSelector, getPrivateCredentials().withCredential(credential), getPublicCredentials(), runtimeAttributes);
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addPublicCredential(credential);
}
}
void addRuntimeAttributes(final Attributes runtimeAttributes) {
final InitialState newState = new InitialState(getSourceIdentity(), getMechanismConfiguration(), mechanismConfigurationSelector, getPrivateCredentials(), getPublicCredentials(), AggregateAttributes.aggregateOf(getRuntimeAttributes(), runtimeAttributes));
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addRuntimeAttributes(runtimeAttributes);
}
}
}
final class RealmAssignedState extends UnassignedState {
final MechanismRealmConfiguration mechanismRealmConfiguration;
RealmAssignedState(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) {
super(capturedIdentity, mechanismConfiguration, privateCredentials, publicCredentials, runtimeAttributes);
this.mechanismRealmConfiguration = mechanismRealmConfiguration;
}
@Override
MechanismRealmConfiguration getMechanismRealmConfiguration() {
return mechanismRealmConfiguration;
}
@Override
void addPublicCredential(final Credential credential) {
final RealmAssignedState newState = new RealmAssignedState(getSourceIdentity(), getMechanismConfiguration(), getMechanismRealmConfiguration(), getPrivateCredentials(), getPublicCredentials().withCredential(credential), runtimeAttributes);
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addPublicCredential(credential);
}
}
@Override
void addPrivateCredential(final Credential credential) {
final RealmAssignedState newState = new RealmAssignedState(getSourceIdentity(), getMechanismConfiguration(), getMechanismRealmConfiguration(), getPrivateCredentials().withCredential(credential), getPublicCredentials(), runtimeAttributes);
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addPublicCredential(credential);
}
}
@Override
void addRuntimeAttributes(final Attributes runtimeAttributes) {
final RealmAssignedState newState = new RealmAssignedState(getSourceIdentity(), getMechanismConfiguration(), getMechanismRealmConfiguration(), getPrivateCredentials(), getPublicCredentials(), AggregateAttributes.aggregateOf(getRuntimeAttributes(), runtimeAttributes));
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addRuntimeAttributes(runtimeAttributes);
}
}
}
final class InvalidNameState extends UnassignedState {
final MechanismRealmConfiguration mechanismRealmConfiguration;
InvalidNameState(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) {
super(capturedIdentity, mechanismConfiguration, privateCredentials, publicCredentials, runtimeAttributes);
this.mechanismRealmConfiguration = mechanismRealmConfiguration;
}
@Override
MechanismRealmConfiguration getMechanismRealmConfiguration() {
return mechanismRealmConfiguration;
}
@Override
RealmIdentity getRealmIdentity() {
return RealmIdentity.NON_EXISTENT;
}
@Override
void fail(final boolean requireInProgress) {
final AtomicReference stateRef = getStateRef();
if (! stateRef.compareAndSet(this, FAILED)) {
// recurse & retry
stateRef.get().fail(requireInProgress);
}
}
@Override
boolean isDone() {
return true;
}
}
final class NameAssignedState extends ActiveState {
private final SecurityIdentity capturedIdentity;
private final RealmInfo realmInfo;
private final RealmIdentity realmIdentity;
private final Principal authenticationPrincipal;
private final MechanismConfiguration mechanismConfiguration;
private final MechanismRealmConfiguration mechanismRealmConfiguration;
private final IdentityCredentials privateCredentials;
private final IdentityCredentials publicCredentials;
private final Attributes runtimeAttributes;
NameAssignedState(final SecurityIdentity capturedIdentity, final RealmInfo realmInfo, final RealmIdentity realmIdentity, final Principal authenticationPrincipal, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) {
this.capturedIdentity = capturedIdentity;
this.realmInfo = realmInfo;
this.realmIdentity = realmIdentity;
this.authenticationPrincipal = authenticationPrincipal;
this.mechanismConfiguration = mechanismConfiguration;
this.mechanismRealmConfiguration = mechanismRealmConfiguration;
this.privateCredentials = privateCredentials;
this.publicCredentials = publicCredentials;
this.runtimeAttributes = runtimeAttributes;
}
@Override
MechanismConfiguration getMechanismConfiguration() {
return mechanismConfiguration;
}
@Override
MechanismRealmConfiguration getMechanismRealmConfiguration() {
return mechanismRealmConfiguration;
}
@Override
Principal getAuthenticationPrincipal() {
return authenticationPrincipal;
}
@Override
RealmIdentity getRealmIdentity() {
return realmIdentity;
}
@Override
SecurityDomain getSecurityDomain() {
return capturedIdentity.getSecurityDomain();
}
@Override
SupportLevel getCredentialAcquireSupport(final Class extends Credential> credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
return realmIdentity.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec);
}
@Override
SupportLevel getEvidenceVerifySupport(final Class extends Evidence> evidenceType, final String algorithmName) throws RealmUnavailableException {
return realmIdentity.getEvidenceVerifySupport(evidenceType, algorithmName);
}
@Override
C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
return realmIdentity.getCredential(credentialType, algorithmName, parameterSpec);
}
@Override
boolean authorize(final boolean requireLoginPermission) throws RealmUnavailableException {
AuthorizedAuthenticationState newState = doAuthorization(requireLoginPermission);
if (newState == null) {
return false;
}
final AtomicReference stateRef = getStateRef();
// retry if necessary
return stateRef.compareAndSet(this, newState) || stateRef.get().authorize(requireLoginPermission);
}
AuthorizedAuthenticationState doAuthorization(final boolean requireLoginPermission) throws RealmUnavailableException {
final RealmIdentity realmIdentity = this.realmIdentity;
if (! realmIdentity.exists()) {
ElytronMessages.log.trace("Authorization failed - realm identity does not exists");
return null;
}
final RealmInfo realmInfo = this.realmInfo;
final Principal authenticationPrincipal = this.authenticationPrincipal;
final AuthorizationIdentity authorizationIdentity = runtimeAttributes == Attributes.EMPTY ? realmIdentity.getAuthorizationIdentity()
: AuthorizationIdentity.basicIdentity(realmIdentity.getAuthorizationIdentity(), runtimeAttributes);
final SecurityDomain domain = capturedIdentity.getSecurityDomain();
SecurityIdentity authorizedIdentity = Assert.assertNotNull(domain.transform(new SecurityIdentity(domain, authenticationPrincipal, realmInfo, authorizationIdentity, domain.getCategoryRoleMappers(), IdentityCredentials.NONE, IdentityCredentials.NONE)));
authorizedIdentity = authorizedIdentity.withPublicCredentials(publicCredentials).withPrivateCredentials(privateCredentials);
if (log.isTraceEnabled()) {
log.tracef("Authorizing principal %s.", authenticationPrincipal.getName());
if (authorizationIdentity != null) {
log.tracef("Authorizing against the following attributes: %s => %s",
authorizationIdentity.getAttributes().keySet(), authorizationIdentity.getAttributes().values());
log.tracef("Authorizing against the following runtime attributes: %s => %s",
authorizationIdentity.getRuntimeAttributes().keySet(), authorizationIdentity.getRuntimeAttributes().values());
} else {
log.tracef("Authorizing against the following attributes: Cannot obtain the attributes. Authorization Identity is null.");
}
}
if (requireLoginPermission) {
if (! authorizedIdentity.implies(LoginPermission.getInstance())) {
SecurityRealm.safeHandleRealmEvent(realmInfo.getSecurityRealm(), new RealmIdentityFailedAuthorizationEvent(authorizedIdentity.getAuthorizationIdentity(), authorizedIdentity.getPrincipal(), authenticationPrincipal));
ElytronMessages.log.trace("Authorization failed - identity does not have required LoginPermission");
return null;
} else {
SecurityRealm.safeHandleRealmEvent(realmInfo.getSecurityRealm(), new RealmIdentitySuccessfulAuthorizationEvent(authorizedIdentity.getAuthorizationIdentity(), authorizedIdentity.getPrincipal(), authenticationPrincipal));
}
}
ElytronMessages.log.trace("Authorization succeed");
return new AuthorizedAuthenticationState(authorizedIdentity, authenticationPrincipal, realmInfo, realmIdentity, mechanismRealmConfiguration, mechanismConfiguration);
}
@Override
boolean authorize(final Principal authorizationId, final boolean authorizeRunAs) throws RealmUnavailableException {
final AuthorizedAuthenticationState authzState = doAuthorization(true);
if (authzState == null) {
return false;
}
final AuthorizedState newState = authzState.authorizeRunAs(authorizationId, authorizeRunAs);
if (newState == null) {
return false;
}
final AtomicReference stateRef = getStateRef();
if (! stateRef.compareAndSet(this, newState)) {
return stateRef.get().authorize(authorizationId, authorizeRunAs);
}
if (newState != authzState) getRealmIdentity().dispose();
return true;
}
@Override
SecurityIdentity getSourceIdentity() {
return capturedIdentity;
}
@Override
boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException {
// At this stage, we just verify that the evidence principal matches, and verify it with the realm.
final Principal evidencePrincipal = evidence.getDecodedPrincipal();
return (evidencePrincipal == null || isSamePrincipal(evidencePrincipal)) && getRealmIdentity().verifyEvidence(evidence);
}
@Override
void updateCredential(Credential credential) throws RealmUnavailableException {
realmIdentity.updateCredential(credential);
}
@Override
void succeed() {
throw log.cannotSucceedNotAuthorized();
}
@Override
void fail(final boolean requireInProgress) {
final SecurityIdentity capturedIdentity = getSourceIdentity();
final AtomicReference stateRef = getStateRef();
if (!stateRef.compareAndSet(this, FAILED)) {
stateRef.get().fail(requireInProgress);
return;
}
SecurityRealm.safeHandleRealmEvent(getRealmInfo().getSecurityRealm(), new RealmFailedAuthenticationEvent(getRealmIdentityWithRuntimeAttributes(), null, null));
SecurityDomain.safeHandleSecurityEvent(capturedIdentity.getSecurityDomain(), new SecurityAuthenticationFailedEvent(capturedIdentity, realmIdentity.getRealmIdentityPrincipal()));
realmIdentity.dispose();
}
private RealmIdentity getRealmIdentityWithRuntimeAttributes() {
return new RealmIdentity() {
@Override
public Principal getRealmIdentityPrincipal() {
return realmIdentity.getRealmIdentityPrincipal();
}
@Override
public SupportLevel getCredentialAcquireSupport(Class extends Credential> credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
return realmIdentity.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec);
}
@Override
public C getCredential(Class credentialType) throws RealmUnavailableException {
return realmIdentity.getCredential(credentialType);
}
@Override
public SupportLevel getEvidenceVerifySupport(Class extends Evidence> evidenceType, String algorithmName) throws RealmUnavailableException {
return realmIdentity.getEvidenceVerifySupport(evidenceType, algorithmName);
}
@Override
public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException {
return realmIdentity.verifyEvidence(evidence);
}
@Override
public boolean exists() throws RealmUnavailableException {
return realmIdentity.exists();
}
@Override
public AuthorizationIdentity getAuthorizationIdentity() throws RealmUnavailableException {
if (realmIdentity.exists()) {
return AuthorizationIdentity.basicIdentity(realmIdentity.getAuthorizationIdentity(), runtimeAttributes);
} else {
return AuthorizationIdentity.basicIdentity(AuthorizationIdentity.EMPTY, runtimeAttributes);
}
}
};
}
@Override
void setPrincipal(final Principal principal, final boolean exclusive) {
if (isSamePrincipal(principal)) {
return;
}
throw log.nameAlreadySet();
}
@Override
boolean isSamePrincipal(Principal principal) {
final SecurityDomain domain = capturedIdentity.getSecurityDomain();
principal = rewriteAll(principal, mechanismRealmConfiguration.getPreRealmRewriter(), mechanismConfiguration.getPreRealmRewriter(), domain.getPreRealmRewriter());
return authenticationPrincipal.equals(principal);
}
@Override
void addPublicCredential(final Credential credential) {
final NameAssignedState newState = new NameAssignedState(getSourceIdentity(), getRealmInfo(), getRealmIdentity(), getAuthenticationPrincipal(), getMechanismConfiguration(), getMechanismRealmConfiguration(), privateCredentials, publicCredentials.withCredential(credential), runtimeAttributes);
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addPublicCredential(credential);
}
}
@Override
void addPrivateCredential(final Credential credential) {
final NameAssignedState newState = new NameAssignedState(getSourceIdentity(), getRealmInfo(), getRealmIdentity(), getAuthenticationPrincipal(), getMechanismConfiguration(), getMechanismRealmConfiguration(), privateCredentials.withCredential(credential), publicCredentials, runtimeAttributes);
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addPublicCredential(credential);
}
}
@Override
void addRuntimeAttributes(final Attributes runtimeAttributes) {
final NameAssignedState newState = new NameAssignedState(getSourceIdentity(), getRealmInfo(), getRealmIdentity(), getAuthenticationPrincipal(), getMechanismConfiguration(), getMechanismRealmConfiguration(), privateCredentials, publicCredentials, AggregateAttributes.aggregateOf(this.runtimeAttributes, runtimeAttributes));
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addRuntimeAttributes(runtimeAttributes);
}
}
RealmInfo getRealmInfo() {
return realmInfo;
}
}
final class AnonymousAuthorizedState extends ActiveState {
private final SecurityIdentity anonymousIdentity;
AnonymousAuthorizedState(final SecurityIdentity anonymousIdentity) {
this.anonymousIdentity = anonymousIdentity;
}
@Override
MechanismConfiguration getMechanismConfiguration() {
return MechanismConfiguration.EMPTY;
}
@Override
MechanismRealmConfiguration getMechanismRealmConfiguration() {
return MechanismRealmConfiguration.NO_REALM;
}
@Override
SecurityIdentity getAuthorizedIdentity() {
return anonymousIdentity;
}
@Override
Principal getAuthenticationPrincipal() {
return AnonymousPrincipal.getInstance();
}
@Override
boolean isSamePrincipal(final Principal principal) {
return principal instanceof AnonymousPrincipal;
}
@Override
SupportLevel getCredentialAcquireSupport(final Class extends Credential> credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
return SupportLevel.UNSUPPORTED;
}
@Override
SupportLevel getEvidenceVerifySupport(final Class extends Evidence> evidenceType, final String algorithmName) throws RealmUnavailableException {
return SupportLevel.UNSUPPORTED;
}
@Override
C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
return null;
}
@Override
boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException {
return false;
}
@Override
RealmIdentity getRealmIdentity() {
return RealmIdentity.ANONYMOUS;
}
@Override
SecurityDomain getSecurityDomain() {
return anonymousIdentity.getSecurityDomain();
}
@Override
boolean authorizeAnonymous(final boolean requireLoginPermission) {
return true;
}
@Override
void setPrincipal(final Principal principal, final boolean exclusive) throws RealmUnavailableException {
if (! (principal instanceof AnonymousPrincipal)) {
super.setPrincipal(principal, exclusive);
}
}
@Override
boolean authorize(final boolean requireLoginPermission) throws RealmUnavailableException {
return ! requireLoginPermission || anonymousIdentity.implies(LoginPermission.getInstance());
}
@Override
void updateCredential(Credential credential) throws RealmUnavailableException {
// no-op
}
@Override
void succeed() {
final AtomicReference stateRef = getStateRef();
if (! stateRef.compareAndSet(this, new CompleteState(anonymousIdentity))) {
stateRef.get().succeed();
}
}
@Override
void fail(final boolean requireInProgress) {
final AtomicReference stateRef = getStateRef();
if (! stateRef.compareAndSet(this, FAILED)) {
stateRef.get().fail(requireInProgress);
}
}
@Override
SecurityIdentity getSourceIdentity() {
return anonymousIdentity;
}
}
class AuthorizedState extends ActiveState {
private final SecurityIdentity authorizedIdentity;
private final Principal authenticationPrincipal;
private final RealmInfo realmInfo;
private final MechanismConfiguration mechanismConfiguration;
private final MechanismRealmConfiguration mechanismRealmConfiguration;
AuthorizedState(final SecurityIdentity authorizedIdentity, final Principal authenticationPrincipal, final RealmInfo realmInfo, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration) {
this.authorizedIdentity = authorizedIdentity;
this.authenticationPrincipal = authenticationPrincipal;
this.realmInfo = realmInfo;
this.mechanismConfiguration = mechanismConfiguration;
this.mechanismRealmConfiguration = mechanismRealmConfiguration;
}
@Override
MechanismRealmConfiguration getMechanismRealmConfiguration() {
return mechanismRealmConfiguration;
}
@Override
MechanismConfiguration getMechanismConfiguration() {
return mechanismConfiguration;
}
@Override
SecurityIdentity getAuthorizedIdentity() {
return authorizedIdentity;
}
@Override
Principal getAuthenticationPrincipal() {
return authenticationPrincipal;
}
@Override
SecurityDomain getSecurityDomain() {
return authorizedIdentity.getSecurityDomain();
}
@Override
SecurityIdentity getSourceIdentity() {
return authorizedIdentity;
}
@Override
boolean isSamePrincipal(Principal principal) {
final SecurityDomain domain = authorizedIdentity.getSecurityDomain();
principal = rewriteAll(principal, mechanismRealmConfiguration.getPreRealmRewriter(), mechanismConfiguration.getPreRealmRewriter(), domain.getPreRealmRewriter());
return authenticationPrincipal.equals(principal);
}
RealmInfo getRealmInfo() {
return realmInfo;
}
@Override
boolean authorize(final boolean requireLoginPermission) throws RealmUnavailableException {
return ! requireLoginPermission || authorizedIdentity.implies(LoginPermission.getInstance());
}
AuthorizedState authorizeRunAs(final Principal authorizationId, final boolean authorizeRunAs) throws RealmUnavailableException {
if (isSamePrincipal(authorizationId)) {
ElytronMessages.log.trace("RunAs authorization succeed - the same identity");
return this;
}
final State state = assignName(authorizedIdentity, getMechanismConfiguration(), getMechanismRealmConfiguration(), authorizationId, null, IdentityCredentials.NONE, IdentityCredentials.NONE, Attributes.EMPTY);
if (!state.isNameAssigned()) {
ElytronMessages.log.tracef("RunAs authorization failed - unable to assign identity name");
return null;
}
final NameAssignedState nameAssignedState = (NameAssignedState) state;
final RealmIdentity realmIdentity = nameAssignedState.getRealmIdentity();
boolean ok = false;
try {
String targetName = nameAssignedState.getAuthenticationPrincipal().getName();
if (authorizeRunAs && ! authorizedIdentity.implies(new RunAsPrincipalPermission(targetName))) {
ElytronMessages.log.tracef("RunAs authorization failed - identity does not have required RunAsPrincipalPermission(%s)", targetName);
return null;
}
final AuthorizedAuthenticationState newState = nameAssignedState.doAuthorization(false);
if (newState == null) {
ElytronMessages.log.trace("RunAs authorization failed");
return null;
}
ok = true;
ElytronMessages.log.trace("RunAs authorization succeed");
return newState;
} finally {
if (! ok) realmIdentity.dispose();
}
}
@Override
void succeed() {
if (authorizedIdentity != null) {
return;
}
super.succeed();
}
void addPublicCredential(final Credential credential) {
final SecurityIdentity sourceIdentity = getSourceIdentity();
final AuthorizedState newState = new AuthorizedState(sourceIdentity.withPublicCredential(credential), getAuthenticationPrincipal(), getRealmInfo(), getMechanismConfiguration(), getMechanismRealmConfiguration());
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addPublicCredential(credential);
}
}
@Override
void addPrivateCredential(final Credential credential) {
final SecurityIdentity sourceIdentity = getSourceIdentity();
final AuthorizedState newState = new AuthorizedState(sourceIdentity.withPrivateCredential(credential), getAuthenticationPrincipal(), getRealmInfo(), getMechanismConfiguration(), getMechanismRealmConfiguration());
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addPrivateCredential(credential);
}
}
@Override
void addRuntimeAttributes(final Attributes runtimeAttributes) {
final SecurityIdentity sourceIdentity = getSourceIdentity();
final AuthorizedState newState = new AuthorizedState(sourceIdentity.withRuntimeAttributes(runtimeAttributes), getAuthenticationPrincipal(), getRealmInfo(), getMechanismConfiguration(), getMechanismRealmConfiguration());
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addRuntimeAttributes(runtimeAttributes);
}
}
}
final class AuthorizedAuthenticationState extends AuthorizedState {
private final RealmIdentity realmIdentity;
AuthorizedAuthenticationState(final SecurityIdentity authorizedIdentity, final Principal authenticationPrincipal, final RealmInfo realmInfo, final RealmIdentity realmIdentity, final MechanismRealmConfiguration mechanismRealmConfiguration, final MechanismConfiguration mechanismConfiguration) {
super(authorizedIdentity, authenticationPrincipal, realmInfo, mechanismConfiguration, mechanismRealmConfiguration);
this.realmIdentity = realmIdentity;
}
@Override
SupportLevel getCredentialAcquireSupport(final Class extends Credential> credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
return realmIdentity.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec);
}
@Override
SupportLevel getEvidenceVerifySupport(final Class extends Evidence> evidenceType, final String algorithmName) throws RealmUnavailableException {
return realmIdentity.getEvidenceVerifySupport(evidenceType, algorithmName);
}
@Override
C getCredential(final Class credentialType, String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
return realmIdentity.getCredential(credentialType, algorithmName, parameterSpec);
}
@Override
boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException {
return realmIdentity.verifyEvidence(evidence);
}
@Override
RealmIdentity getRealmIdentity() {
return realmIdentity;
}
@Override
void updateCredential(Credential credential) throws RealmUnavailableException {
realmIdentity.updateCredential(credential);
}
@Override
void succeed() {
final SecurityIdentity authorizedIdentity = getSourceIdentity();
final AtomicReference stateRef = getStateRef();
if (! stateRef.compareAndSet(this, new CompleteState(authorizedIdentity))) {
stateRef.get().succeed();
return;
}
SecurityRealm.safeHandleRealmEvent(getRealmInfo().getSecurityRealm(), new RealmSuccessfulAuthenticationEvent(realmIdentity, authorizedIdentity.getAuthorizationIdentity(), null, null));
SecurityDomain.safeHandleSecurityEvent(authorizedIdentity.getSecurityDomain(), new SecurityAuthenticationSuccessfulEvent(authorizedIdentity));
realmIdentity.dispose();
}
@Override
void fail(final boolean requireInProgress) {
final SecurityIdentity authorizedIdentity = getSourceIdentity();
final AtomicReference stateRef = getStateRef();
if (! stateRef.compareAndSet(this, FAILED)) {
stateRef.get().fail(requireInProgress);
return;
}
SecurityRealm.safeHandleRealmEvent(getRealmInfo().getSecurityRealm(), new RealmFailedAuthenticationEvent(realmIdentity, null, null));
SecurityDomain.safeHandleSecurityEvent(authorizedIdentity.getSecurityDomain(), new SecurityAuthenticationFailedEvent(authorizedIdentity, realmIdentity.getRealmIdentityPrincipal()));
realmIdentity.dispose();
}
@Override
void addPublicCredential(final Credential credential) {
final SecurityIdentity sourceIdentity = getSourceIdentity();
final AuthorizedAuthenticationState newState = new AuthorizedAuthenticationState(sourceIdentity.withPublicCredential(credential), getAuthenticationPrincipal(), getRealmInfo(), getRealmIdentity(), getMechanismRealmConfiguration(), getMechanismConfiguration());
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addPublicCredential(credential);
}
}
@Override
void addPrivateCredential(final Credential credential) {
final SecurityIdentity sourceIdentity = getSourceIdentity();
final AuthorizedAuthenticationState newState = new AuthorizedAuthenticationState(sourceIdentity.withPrivateCredential(credential), getAuthenticationPrincipal(), getRealmInfo(), getRealmIdentity(), getMechanismRealmConfiguration(), getMechanismConfiguration());
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addPrivateCredential(credential);
}
}
@Override
void addRuntimeAttributes(final Attributes runtimeAttributes) {
final SecurityIdentity sourceIdentity = getSourceIdentity();
final AuthorizedAuthenticationState newState = new AuthorizedAuthenticationState(sourceIdentity.withRuntimeAttributes(runtimeAttributes), getAuthenticationPrincipal(), getRealmInfo(), getRealmIdentity(), getMechanismRealmConfiguration(), getMechanismConfiguration());
if (! stateRef.compareAndSet(this, newState)) {
stateRef.get().addRuntimeAttributes(runtimeAttributes);
}
}
}
static final class CompleteState extends State {
private final SecurityIdentity identity;
public CompleteState(final SecurityIdentity identity) {
this.identity = identity;
}
@Override
SecurityIdentity getAuthorizedIdentity() {
return identity;
}
@Override
boolean isDone() {
return true;
}
void succeed() {
// always works
}
}
private static final State FAILED = new State() {
@Override
void fail(final boolean requireInProgress) {
}
@Override
boolean isDone() {
return true;
}
};
}