Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
pl.edu.icm.unity.engine.authn.AuthenticationProcessorImpl Maven / Gradle / Ivy
/*
* Copyright (c) 2018 Bixbit - Krzysztof Benedyczak All rights reserved.
* See LICENCE.txt file for licensing information.
*/
package pl.edu.icm.unity.engine.authn;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import org.apache.logging.log4j.Logger;
import org.mvel2.MVEL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import pl.edu.icm.unity.base.authn.AuthenticationFlowDefinition.Policy;
import pl.edu.icm.unity.base.authn.AuthenticationOptionKey;
import pl.edu.icm.unity.base.authn.CredentialDefinition;
import pl.edu.icm.unity.base.authn.DynamicExpressionPolicyConfiguration;
import pl.edu.icm.unity.base.entity.EntityParam;
import pl.edu.icm.unity.base.exceptions.EngineException;
import pl.edu.icm.unity.base.utils.Log;
import pl.edu.icm.unity.engine.api.authn.AuthenticatedEntity;
import pl.edu.icm.unity.engine.api.authn.AuthenticationException;
import pl.edu.icm.unity.engine.api.authn.AuthenticationFlow;
import pl.edu.icm.unity.engine.api.authn.AuthenticationProcessor;
import pl.edu.icm.unity.engine.api.authn.AuthenticationResult;
import pl.edu.icm.unity.engine.api.authn.AuthenticationResult.Status;
import pl.edu.icm.unity.engine.api.authn.AuthenticatorInstance;
import pl.edu.icm.unity.engine.api.authn.AuthenticatorInstanceMetadata;
import pl.edu.icm.unity.engine.api.authn.IllegalCredentialException;
import pl.edu.icm.unity.engine.api.authn.PartialAuthnState;
import pl.edu.icm.unity.engine.api.authn.local.LocalCredentialsRegistry;
import pl.edu.icm.unity.engine.api.authn.remote.UnknownRemoteUserException;
import pl.edu.icm.unity.engine.credential.CredentialRepository;
import pl.edu.icm.unity.engine.identity.SecondFactorOptInService;
/**
* Utility methods processing results of authenticators.
*
* @author K. Benedyczak
*/
@Component
class AuthenticationProcessorImpl implements AuthenticationProcessor
{
private static final Logger log = Log.getLogger(Log.U_SERVER_AUTHN, AuthenticationProcessorImpl.class);
private final SecondFactorOptInService secondFactorOptInService;
private final LocalCredentialsRegistry localCred;
private final CredentialRepository credRepo;
private final AuthenticationFlowPolicyConfigMVELContextBuilder policyConfigMVELContextBuilder;
@Autowired
AuthenticationProcessorImpl(SecondFactorOptInService secondFactorOptInService, LocalCredentialsRegistry localCred,
CredentialRepository credRepo,
AuthenticationFlowPolicyConfigMVELContextBuilder policyConfigMVELContextBuilder)
{
this.secondFactorOptInService = secondFactorOptInService;
this.localCred = localCred;
this.credRepo = credRepo;
this.policyConfigMVELContextBuilder = policyConfigMVELContextBuilder;
}
/**
* Starting point: the result of the primary authenticator is verified. If the
* authentication failed then an exception is thrown. Otherwise it is checked
* whether, according to the {@link AuthenticationFlow} selected, second
* authentication should be performed, what is returned.
*/
@Override
public PartialAuthnState processPrimaryAuthnResult(AuthenticationResult result,
AuthenticationFlow authenticationFlow, AuthenticationOptionKey authnOptionId) throws AuthenticationException
{
if (result.getStatus() != Status.success)
{
if (result.getStatus() == Status.unknownRemotePrincipal)
throw new UnknownRemoteUserException("AuthenticationProcessorImpl.authnFailed", result.asRemote());
throw new AuthenticationException("AuthenticationProcessorImpl.authnFailed");
}
Policy flowPolicy = authenticationFlow.getPolicy();
switch (flowPolicy)
{
case REQUIRE:
return proccessRequiredPolicy(result, authenticationFlow, authnOptionId);
case DYNAMIC_EXPRESSION:
return proccessDynamicPolicy(result, authenticationFlow, authnOptionId);
case USER_OPTIN:
return proccessUserOptInPolicy(result, authenticationFlow, authnOptionId);
case NEVER:
return new PartialAuthnState(authnOptionId, null, result, authenticationFlow);
default:
return new PartialAuthnState(authnOptionId, null, result, authenticationFlow);
}
}
private PartialAuthnState proccessRequiredPolicy(AuthenticationResult result, AuthenticationFlow authenticationFlow,
AuthenticationOptionKey authnOptionId) throws AuthenticationException
{
PartialAuthnState partialAuthnState = getSecondFactorAuthn(authenticationFlow, result, authnOptionId);
if (partialAuthnState != null)
return partialAuthnState;
throw new AuthenticationException("AuthenticationProcessorImpl.secondFactorRequire");
}
private PartialAuthnState proccessUserOptInPolicy(AuthenticationResult result,
AuthenticationFlow authenticationFlow, AuthenticationOptionKey authnOptionId) throws AuthenticationException
{
PartialAuthnState partialAuthnState = null;
if (getUserOptInAttribute(result.getSuccessResult().authenticatedEntity.getEntityId()))
{
partialAuthnState = getSecondFactorAuthn(authenticationFlow, result, authnOptionId);
if (partialAuthnState != null)
return partialAuthnState;
throw new AuthenticationException("AuthenticationProcessorImpl.secondFactorRequire");
}
return new PartialAuthnState(authnOptionId, null, result, authenticationFlow);
}
private PartialAuthnState proccessDynamicPolicy(AuthenticationResult result,
AuthenticationFlow authenticationFlow, AuthenticationOptionKey authnOptionId) throws AuthenticationException
{
PartialAuthnState partialAuthnState = null;
try
{
String mvelExpression = ((DynamicExpressionPolicyConfiguration) authenticationFlow
.getPolicyConfiguration()).mvelExpression;
if (evaluateCondition(mvelExpression,
policyConfigMVELContextBuilder.createMvelContext(authnOptionId, result,
getUserOptInAttribute(result.getSuccessResult().authenticatedEntity.getEntityId()),
authenticationFlow)))
{
partialAuthnState = getSecondFactorAuthn(authenticationFlow, result, authnOptionId);
if (partialAuthnState != null)
return partialAuthnState;
throw new AuthenticationException("AuthenticationProcessorImpl.secondFactorRequire");
}
} catch (EngineException e)
{
throw new AuthenticationException("AuthenticationProcessorImpl.authnFailed");
}
return new PartialAuthnState(authnOptionId, null, result, authenticationFlow);
}
private boolean evaluateCondition(String condition, Object input) throws EngineException
{
Serializable compiled = MVEL.compileExpression(condition);
Boolean result = null;
try
{
result = (Boolean) MVEL.executeExpression(compiled, input, new HashMap<>());
} catch (Exception e)
{
log.warn("Error during expression execution.", e);
throw new EngineException(e);
}
if (result == null)
{
log.debug("Condition evaluated to null value, assuming false");
return false;
}
log.debug("Condition \"{}\" evaluated to {}", condition, result.booleanValue());
return result.booleanValue();
}
private boolean getUserOptInAttribute(long entityId)
{
try
{
return secondFactorOptInService.getUserOptin(entityId);
} catch (EngineException e)
{
log.debug("Can not get user optin attribute for entity " + entityId);
// force second factor
return true;
}
}
private PartialAuthnState getSecondFactorAuthn(AuthenticationFlow authenticationFlow, AuthenticationResult result,
AuthenticationOptionKey firstFactorauthnOptionId)
{
if (result.getSuccessResult().authenticatedEntity == null)
return null;
AuthenticatorInstance secondFactorAuthenticator = getValidAuthenticatorForEntity(
authenticationFlow.getSecondFactorAuthenticators(),
result.getSuccessResult().authenticatedEntity.getEntityId());
if (secondFactorAuthenticator == null)
return null;
return new PartialAuthnState(firstFactorauthnOptionId, secondFactorAuthenticator.getRetrieval(), result,
authenticationFlow);
}
@Override
public AuthenticatorInstance getValidAuthenticatorForEntity(Collection pool, long entityId)
{
for (AuthenticatorInstance authn : pool)
{
AuthenticatorInstanceMetadata authenticator = authn.getMetadata();
if (authenticator != null)
{
if (!authenticator.getTypeDescription()
.isLocal())
return authn;
else if (checkIfUserHasCredential(authenticator, entityId))
return authn;
}
}
return null;
}
private boolean checkIfUserHasLocalCredential(long entityId, String credentialId)
throws IllegalCredentialException, EngineException
{
CredentialDefinition credentialDefinition = credRepo.get(credentialId);
return localCred.createLocalCredentialVerificator(credentialDefinition)
.isCredentialSet(new EntityParam(entityId));
}
@Override
public boolean checkIfUserHasCredential(AuthenticatorInstanceMetadata authn, long entityId)
{
try
{
boolean ret = checkIfUserHasLocalCredential(entityId, authn.getLocalCredentialName());
log.debug("Check if user {} has defined credential {}: {}", entityId, authn.getLocalCredentialName(), ret);
return ret;
} catch (Exception e)
{
log.warn("Can not check entity local credential state", e);
return false;
}
}
@Override
public AuthenticatedEntity finalizeAfterPrimaryAuthentication(PartialAuthnState state, boolean skipSecondFactor)
{
if (state.isSecondaryAuthenticationRequired() && !skipSecondFactor)
throw new IllegalStateException(
"BUG: code tried to finalize authentication " + "requiring MFA after first authentication");
return state.getPrimaryResult()
.getSuccessResult().authenticatedEntity;
}
@Override
public AuthenticatedEntity finalizeAfterSecondaryAuthentication(PartialAuthnState state,
AuthenticationResult result2) throws AuthenticationException
{
if (!state.isSecondaryAuthenticationRequired())
throw new IllegalStateException("BUG: code tried to finalize authentication "
+ "with additional authentication while only one was selected");
if (result2.getStatus() != Status.success)
{
if (result2.getStatus() == Status.unknownRemotePrincipal)
throw new AuthenticationException("AuthenticationProcessorImpl.authnWrongUsers");
throw new AuthenticationException("AuthenticationProcessorImpl.authnFailed");
}
Long secondId = result2.getSuccessResult().authenticatedEntity.getEntityId();
AuthenticatedEntity firstAuthenticated = state.getPrimaryResult()
.getSuccessResult().authenticatedEntity;
Long primaryId = firstAuthenticated.getEntityId();
if (!secondId.equals(primaryId))
{
throw new AuthenticationException("AuthenticationProcessorImpl.authnWrongUsers");
}
AuthenticatedEntity logInfo = result2.getSuccessResult().authenticatedEntity;
logInfo.getAuthenticatedWith()
.addAll(firstAuthenticated.getAuthenticatedWith());
if (firstAuthenticated.getOutdatedCredentialId() != null)
logInfo.setOutdatedCredentialId(firstAuthenticated.getOutdatedCredentialId());
return logInfo;
}
}