Skip to content

Commit

Permalink
GUACAMOLE-1289: Add new translations and guacamole properties.
Browse files Browse the repository at this point in the history
  • Loading branch information
aleitner committed Apr 5, 2024
1 parent 7c49466 commit 8c185ea
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 128 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,60 +102,60 @@ public void verifyAuthenticatedUser(AuthenticatedUser authenticatedUser)

try {

String redirectUrl = confService.getRedirectUrl().toString();

String builtUrl = UriComponentsBuilder
.fromUriString(redirectUrl)
.queryParam(Credentials.RESUME_QUERY, DuoAuthenticationProvider.PROVIDER_IDENTIFER)
.build()
.toUriString();

// Set up the Duo Client
Client duoClient = new Client.Builder(
confService.getClientId(),
confService.getClientSecret(),
confService.getAPIHostname(),
builtUrl)
.build();
duoClient.healthCheck();
// Retrieve signed Duo Code and State from the request
String duoCode = request.getParameter(DUO_CODE_PARAMETER_NAME);
String duoState = request.getParameter(DUO_STATE_PARAMETER_NAME);

// If no code or state is received, assume Duo MFA redirect has not occured and do it.
if (duoCode == null || duoState == null) {

// Get a new session state from the Duo client
duoState = duoClient.generateState();
long expirationTimestamp = System.currentTimeMillis() + (confService.getAuthTimeout() * 1000L);

// Request additional credentials
throw new TranslatableGuacamoleInsufficientCredentialsException(
"Verification using Duo is required before authentication "
+ "can continue.", "LOGIN.INFO_DUO_AUTH_REQUIRED",
new CredentialsInfo(Collections.singletonList(
new RedirectField(
DUO_CODE_PARAMETER_NAME,
new URI(duoClient.createAuthUrl(username, duoState)),
new TranslatableMessage("LOGIN.INFO_DUO_REDIRECT_PENDING")
)
)),
duoState, DuoAuthenticationProvider.PROVIDER_IDENTIFER,
DUO_STATE_PARAMETER_NAME, expirationTimestamp
);

}
// Get the token from the DuoClient using the code and username, and check status
Token token = duoClient.exchangeAuthorizationCodeFor2FAResult(duoCode, username);
if (token == null
|| token.getAuth_result() == null
|| !DUO_TOKEN_SUCCESS_VALUE.equals(token.getAuth_result().getStatus()))
throw new TranslatableGuacamoleClientException("Provided Duo "
+ "validation code is incorrect.",
"LOGIN.INFO_DUO_VALIDATION_CODE_INCORRECT");
String redirectUrl = confService.getRedirectUri().toString();

String builtUrl = UriComponentsBuilder
.fromUriString(redirectUrl)
.queryParam(Credentials.RESUME_QUERY, DuoAuthenticationProvider.PROVIDER_IDENTIFER)
.build()
.toUriString();

// Set up the Duo Client
Client duoClient = new Client.Builder(
confService.getClientId(),
confService.getClientSecret(),
confService.getAPIHostname(),
builtUrl)
.build();

duoClient.healthCheck();

// Retrieve signed Duo Code and State from the request
String duoCode = request.getParameter(DUO_CODE_PARAMETER_NAME);
String duoState = request.getParameter(DUO_STATE_PARAMETER_NAME);

// If no code or state is received, assume Duo MFA redirect has not occured and do it
if (duoCode == null || duoState == null) {

// Get a new session state from the Duo client
duoState = duoClient.generateState();
long expirationTimestamp = System.currentTimeMillis() + (confService.getAuthTimeout() * 1000L);

// Request additional credentials
throw new TranslatableGuacamoleInsufficientCredentialsException(
"Verification using Duo is required before authentication "
+ "can continue.", "LOGIN.INFO_DUO_AUTH_REQUIRED",
new CredentialsInfo(Collections.singletonList(
new RedirectField(
DUO_CODE_PARAMETER_NAME,
new URI(duoClient.createAuthUrl(username, duoState)),
new TranslatableMessage("LOGIN.INFO_DUO_REDIRECT_PENDING")
)
)),
duoState, DuoAuthenticationProvider.PROVIDER_IDENTIFER,
DUO_STATE_PARAMETER_NAME, expirationTimestamp
);

}

// Get the token from the DuoClient using the code and username, and check status
Token token = duoClient.exchangeAuthorizationCodeFor2FAResult(duoCode, username);
if (token == null
|| token.getAuth_result() == null
|| !DUO_TOKEN_SUCCESS_VALUE.equals(token.getAuth_result().getStatus()))
throw new TranslatableGuacamoleClientException("Provided Duo "
+ "validation code is incorrect.",
"LOGIN.INFO_DUO_VALIDATION_CODE_INCORRECT");
}
catch (DuoException e) {
throw new GuacamoleServerException("Duo Client error.", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ public class ConfigurationService {
};

/**
* The property within guacamole.properties which defines the integration
* key received from Duo for verifying Guacamole users. This value MUST be
* The property within guacamole.properties which defines the client id
* received from Duo for verifying Guacamole users. This value MUST be
* exactly 20 characters.
*/
private static final StringGuacamoleProperty DUO_CLIENT_ID =
Expand All @@ -79,17 +79,17 @@ public class ConfigurationService {
public String getName() { return "duo-client-secret"; }

};

/**
* The property within guacamole.properties which defines the redirect URL
* The property within guacamole.properties which defines the redirect URI
* that Duo will call after the second factor has been completed. This
* should be the URL used to access Guacamole.
* should be the URI used to access Guacamole.
*/
private static final URIGuacamoleProperty DUO_REDIRECT_URL =
private static final URIGuacamoleProperty DUO_REDIRECT_URI =
new URIGuacamoleProperty() {

@Override
public String getName() { return "duo-redirect-url"; }
public String getName() { return "duo-redirect-uri"; }

};

Expand Down Expand Up @@ -140,8 +140,8 @@ public String getClientId() throws GuacamoleException {
}

/**
* Returns the client secert received from Duo for verifying Guacamole users,
* as defined in guacamole.properties by the "duo-client-secert" property.
* Returns the client secret received from Duo for verifying Guacamole users,
* as defined in guacamole.properties by the "duo-client-secret" property.
* This value MUST be exactly 20 characters.
*
* @return
Expand All @@ -153,11 +153,11 @@ public String getClientId() throws GuacamoleException {
public String getClientSecret() throws GuacamoleException {
return environment.getRequiredProperty(DUO_CLIENT_SECRET);
}

/**
* Return the callback URL that will be called by Duo after authentication
* with Duo has been completed. This should be the URL to return the user
* to the Guacamole interface, and will be a full URL.
* Return the callback URI that will be called by Duo after authentication
* with Duo has been completed. This should be the URI to return the user
* to the Guacamole interface, and will be a full URI.
*
* @return
* The URL for Duo to use to callback to the Guacamole interface after
Expand All @@ -167,8 +167,8 @@ public String getClientSecret() throws GuacamoleException {
* If guacamole.properties cannot be read, or if the property is not
* defined.
*/
public URI getRedirectUrl() throws GuacamoleException {
return environment.getRequiredProperty(DUO_REDIRECT_URL);
public URI getRedirectUri() throws GuacamoleException {
return environment.getRequiredProperty(DUO_REDIRECT_URI);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"LOGIN" : {
"FIELD_HEADER_GUAC_DUO_SIGNED_RESPONSE" : "",
"INFO_DUO_VALIDATION_CODE_INCORRECT" : "Duo validation code incorrect.",
"INFO_DUO_AUTH_REQUIRED" : "Please authenticate with Duo to continue."
"INFO_DUO_AUTH_REQUIRED" : "Please authenticate with Duo to continue.",
"INFO_DUO_REDIRECT_PENDING" : "Please wait, redirecting to Duo..."
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

"LOGIN" : {
"INFO_DUO_VALIDATION_CODE_INCORRECT" : "Duoの認証コードが間違っています。",
"INFO_DUO_AUTH_REQUIRED" : "Duoで認証してください。"
"INFO_DUO_AUTH_REQUIRED" : "Duoで認証してください。",
"INFO_DUO_REDIRECT_PENDING" : "Duoへリダイレクトしています。"
}

}
18 changes: 11 additions & 7 deletions guacamole-docker/bin/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -850,9 +850,10 @@ associate_totp() {
##
associate_duo() {
# Verify required parameters are present
if [ -z "$DUO_INTEGRATION_KEY" ] || \
[ -z "$DUO_SECRET_KEY" ] || \
[ ${#DUO_APPLICATION_KEY} -lt 40 ]
if [ -z "$DUO_CLIENT_ID" ] || \
[ -z "$DUO_CLIENT_SECRET" ] || \
[ ${#DUO_APPLICATION_KEY} -lt 40 ] || \
[-z "$DUO_REDIRECT_URI"]
then
cat <<END
FATAL: Missing required environment variables
Expand All @@ -862,21 +863,24 @@ following environment variables:
DUO_API_HOSTNAME The hostname of the Duo API endpoint.
DUO_INTEGRATION_KEY The integration key provided for Guacamole by Duo.
DUO_CLIENT_ID The client id (or integration key) provided for Guacamole by Duo.
DUO_SECRET_KEY The secret key provided for Guacamole by Duo.
DUO_CLIENT_SECRET The secret key provided for Guacamole by Duo.
DUO_APPLICATION_KEY An arbitrary, random key.
This value must be at least 40 characters.
DUO_REDIRECT_URI The URI to redirect back to upon successful authentication.
END
exit 1;
fi

# Update config file
set_property "duo-api-hostname" "$DUO_API_HOSTNAME"
set_property "duo-integration-key" "$DUO_INTEGRATION_KEY"
set_property "duo-secret-key" "$DUO_SECRET_KEY"
set_property "duo-client-id" "$DUO_CLIENT_ID"
set_property "duo-client-secret" "$DUO_CLIENT_SECRET"
set_property "duo-application-key" "$DUO_APPLICATION_KEY"
set_property "duo-redirect-uri" "$DUO_REDIRECT_URI"

# Add required .jar files to GUACAMOLE_EXT
ln -s /opt/guacamole/duo/guacamole-auth-*.jar "$GUACAMOLE_EXT"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,36 +28,36 @@
*/
public class GuacamoleInsufficientCredentialsException extends GuacamoleCredentialsException {

/**
* The default state token to use when no specific state information is provided.
*/
private static final String DEFAULT_STATE = "";
/**
* The default state token to use when no specific state information is provided.
*/
private static final String DEFAULT_STATE = "";

/**
* The default provider identifier to use when no specific provider is identified.
* This serves as a placeholder indicating that either no specific provider is
* responsible for the exception or the responsible provider has not been identified.
*/
private static final String DEFAULT_PROVIDER_IDENTIFIER = "";
/**
* The default provider identifier to use when no specific provider is identified.
* This serves as a placeholder indicating that either no specific provider is
* responsible for the exception or the responsible provider has not been identified.
*/
private static final String DEFAULT_PROVIDER_IDENTIFIER = "";

/**
* The default query identifier to use when no specific query is identified.
* This serves as a placeholder and indicates that the specific query related to
* the provider's state resume operation has not been provided.
*/
private static final String DEFAULT_QUERY_IDENTIFIER = "";
/**
* The default query identifier to use when no specific query is identified.
* This serves as a placeholder and indicates that the specific query related to
* the provider's state resume operation has not been provided.
*/
private static final String DEFAULT_QUERY_IDENTIFIER = "";

/**
* The default expiration timestamp to use when no specific expiration is provided,
* effectively indicating that the state token does not expire.
*/
private static final long DEFAULT_EXPIRES = -1L;
/**
* The default expiration timestamp to use when no specific expiration is provided,
* effectively indicating that the state token does not expire.
*/
private static final long DEFAULT_EXPIRES = -1L;

/**
* An opaque value that may be used by a client to maintain state across requests
* which are part of the same authentication transaction.
*/
protected final String state;
/**
* An opaque value that may be used by a client to maintain state across requests
* which are part of the same authentication transaction.
*/
protected final String state;

/**
* The identifier for the authentication provider that threw this exception.
Expand All @@ -73,12 +73,12 @@ public class GuacamoleInsufficientCredentialsException extends GuacamoleCredenti
*/
protected final String queryIdentifier;

/**
* The timestamp after which the state token associated with the authentication process
* should no longer be considered valid, expressed as the number of milliseconds since
* UNIX epoch.
*/
protected final long expires;
/**
* The timestamp after which the state token associated with the authentication process
* should no longer be considered valid, expressed as the number of milliseconds since
* UNIX epoch.
*/
protected final long expires;

/**
* Creates a new GuacamoleInsufficientCredentialsException with the specified
Expand Down Expand Up @@ -107,9 +107,9 @@ public class GuacamoleInsufficientCredentialsException extends GuacamoleCredenti
* authentication process should no longer be considered valid, expressed
* as the number of milliseconds since UNIX epoch.
*/
public GuacamoleInsufficientCredentialsException(String message,
CredentialsInfo credentialsInfo, String state, String providerIdentifier, String queryIdentifier,
long expires) {
public GuacamoleInsufficientCredentialsException(String message,
CredentialsInfo credentialsInfo, String state,
String providerIdentifier, String queryIdentifier, long expires) {
super(message, credentialsInfo);
this.state = state;
this.providerIdentifier = providerIdentifier;
Expand Down
Loading

0 comments on commit 8c185ea

Please sign in to comment.