in extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/UserVerificationService.java [314:433]
public void verifyIdentity(UserContext context,
AuthenticatedUser authenticatedUser) throws GuacamoleException {
// Pull the original HTTP request used to authenticate
Credentials credentials = authenticatedUser.getCredentials();
// Get the current client address
IPAddress clientAddr = new IPAddressString(credentials.getRemoteAddress()).getAddress();
// Ignore anonymous users
if (authenticatedUser.getIdentifier().equals(AuthenticatedUser.ANONYMOUS_IDENTIFIER))
return;
// Pull address lists to check from configuration. Note that the enforce
// list will override the bypass list, which means that, if the client
// address happens to be in both lists, Duo MFA will be enforced.
List<IPAddress> bypassAddresses = confService.getBypassHosts();
List<IPAddress> enforceAddresses = confService.getEnforceHosts();
// Check the bypass list for the client address, and set the enforce
// flag to the opposite.
boolean enforceHost = !(IPAddressListProperty.addressListContains(bypassAddresses, clientAddr));
// Only continue processing if the list is not empty
if (!enforceAddresses.isEmpty()) {
// If client address is not available or invalid, MFA will
// be enforced.
if (clientAddr == null || !clientAddr.isIPAddress())
enforceHost = true;
// Check the enforce list and set the flag if the client address
// is found in the list.
else
enforceHost = IPAddressListProperty.addressListContains(enforceAddresses, clientAddr);
}
// If the enforce flag is not true, bypass TOTP MFA.
if (!enforceHost)
return;
// Ignore anonymous users
String username = authenticatedUser.getIdentifier();
if (username.equals(AuthenticatedUser.ANONYMOUS_IDENTIFIER))
return;
// Check if TOTP has been disabled for this user
if (totpDisabled(context, authenticatedUser))
return;
// Ignore users which do not have an associated key
UserTOTPKey key = getKey(context, username);
if (key == null)
return;
// Retrieve TOTP from request
String code = credentials.getParameter(AuthenticationCodeField.PARAMETER_NAME);
// If no TOTP provided, request one
if (code == null) {
AuthenticationCodeField field = codeFieldProvider.get();
// If the user hasn't completed enrollment, request that they do
if (!key.isConfirmed()) {
// If the key has not yet been confirmed, generate a new one.
key = generateKey(context, username);
field.exposeKey(key);
throw new TranslatableGuacamoleInsufficientCredentialsException(
"TOTP enrollment must be completed before "
+ "authentication can continue",
"TOTP.INFO_ENROLL_REQUIRED", new CredentialsInfo(
Collections.<Field>singletonList(field)
));
}
// Otherwise simply request the user's authentication code
throw new TranslatableGuacamoleInsufficientCredentialsException(
"A TOTP authentication code is required before login can "
+ "continue", "TOTP.INFO_CODE_REQUIRED", new CredentialsInfo(
Collections.<Field>singletonList(field)
));
}
try {
// Get generator based on user's key and provided configuration
TOTPGenerator totp = new TOTPGenerator(key.getSecret(),
confService.getMode(), confService.getDigits(),
TOTPGenerator.DEFAULT_START_TIME, confService.getPeriod());
// Verify provided TOTP against value produced by generator
if ((code.equals(totp.generate()) || code.equals(totp.previous()))
&& codeService.useCode(username, code)) {
// Record key as confirmed, if it hasn't already been so recorded
if (!key.isConfirmed()) {
key.setConfirmed(true);
setKey(context, key);
}
// User has been verified
return;
}
}
catch (InvalidKeyException e) {
logger.warn("User \"{}\" is associated with an invalid TOTP key.", username);
logger.debug("TOTP key is not valid.", e);
}
// Provided code is not valid
throw new TranslatableGuacamoleClientException("Provided TOTP code "
+ "is not valid.", "TOTP.INFO_VERIFICATION_FAILED");
}