Add .gitignore and .ratignore files for various directories
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
gyurix
2025-04-29 21:43:12 +02:00
parent 983ecbfc53
commit be9f66dee9
2167 changed files with 254128 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
src/main/resources/generated/
target/
*~

View File

@@ -0,0 +1,2 @@
src/main/resources/lib/DuoWeb/**/*
src/main/java/com/duosecurity/duoweb/**/*

View File

@@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-duo</artifactId>
<packaging>jar</packaging>
<version>1.6.0</version>
<name>guacamole-auth-duo</name>
<url>http://guacamole.apache.org/</url>
<parent>
<groupId>org.apache.guacamole</groupId>
<artifactId>extensions</artifactId>
<version>1.6.0</version>
<relativePath>../</relativePath>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<okhttp.version>4.12.0</okhttp.version>
<kotlin.version>1.9.25</kotlin.version>
</properties>
<dependencies>
<!-- Guacamole Extension API -->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
<version>1.6.0</version>
<scope>provided</scope>
</dependency>
<!-- Guava - Utility Library -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<!-- Guice -->
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
<!-- Java servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!-- Duo SDK -->
<dependency>
<groupId>com.duosecurity</groupId>
<artifactId>duo-universal-sdk</artifactId>
<version>1.2.0</version>
<exclusions>
<exclusion>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</exclusion>
<exclusion>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Force the use of a consistent version of "okhttp" -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
<exclusions>
<exclusion>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-common</artifactId>
</exclusion>
<exclusion>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
<version>${okhttp.version}</version>
<exclusions>
<exclusion>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Force the use of a consistent version of Kotlin standard library common -->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-common</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
<!-- Library for unified IPv4/6 parsing and validation -->
<dependency>
<groupId>com.github.seancfoley</groupId>
<artifactId>ipaddress</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>dist</id>
<baseDirectory>${project.artifactId}-${project.version}</baseDirectory>
<!-- Output tar.gz -->
<formats>
<format>tar.gz</format>
</formats>
<!-- Include licenses and extension .jar -->
<fileSets>
<!-- Include licenses -->
<fileSet>
<outputDirectory></outputDirectory>
<directory>target/licenses</directory>
</fileSet>
<!-- Include extension .jar -->
<fileSet>
<directory>target</directory>
<outputDirectory></outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
</fileSets>
</assembly>

View File

@@ -0,0 +1,123 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.guacamole.auth.duo;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.AbstractAuthenticationProvider;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.Credentials;
import org.apache.guacamole.net.auth.UserContext;
/**
* AuthenticationProvider implementation which uses Duo as an additional
* authentication factor for users which have already been authenticated by
* some other AuthenticationProvider.
*/
public class DuoAuthenticationProvider extends AbstractAuthenticationProvider {
/**
* The unique identifier for this authentication provider. This is used in
* various parts of the Guacamole client to distinguish this provider from
* others, particularly when multiple authentication providers are used.
*/
public static String PROVIDER_IDENTIFER = "duo";
/**
* Service for verifying the identity of users that Guacamole has otherwise
* already authenticated.
*/
private final UserVerificationService verificationService;
/**
* Session manager for storing/retrieving the state of a user's
* authentication attempt while they are redirected to the Duo service.
*/
private final DuoAuthenticationSessionManager sessionManager;
/**
* Creates a new DuoAuthenticationProvider that verifies users
* using the Duo authentication service
*
* @throws GuacamoleException
* If a required property is missing, or an error occurs while parsing
* a property.
*/
public DuoAuthenticationProvider() throws GuacamoleException {
// Set up Guice injector.
Injector injector = Guice.createInjector(
new DuoAuthenticationProviderModule(this)
);
sessionManager = injector.getInstance(DuoAuthenticationSessionManager.class);
verificationService = injector.getInstance(UserVerificationService.class);
}
@Override
public String getIdentifier() {
return PROVIDER_IDENTIFER;
}
@Override
public Credentials updateCredentials(Credentials credentials)
throws GuacamoleException {
// Ignore requests with no corresponding authentication session ID, as
// there are no credentials to reconstitute if the user has not yet
// attempted to authenticate
String duoState = credentials.getParameter(UserVerificationService.DUO_STATE_PARAMETER_NAME);
if (duoState == null)
return credentials;
// Ignore requests with invalid/expired authentication session IDs
DuoAuthenticationSession session = sessionManager.resume(duoState);
if (session == null)
return credentials;
// Reconstitute the originally-provided credentials from the users
// authentication attempt prior to being redirected to Duo
Credentials previousCredentials = session.getCredentials();
previousCredentials.setRequestDetails(credentials.getRequestDetails());
return previousCredentials;
}
@Override
public UserContext getUserContext(AuthenticatedUser authenticatedUser)
throws GuacamoleException {
// Verify user against Duo service
verificationService.verifyAuthenticatedUser(authenticatedUser);
// User has been verified, and authentication should be allowed to
// continue
return null;
}
@Override
public void shutdown() {
sessionManager.shutdown();
}
}

View File

@@ -0,0 +1,78 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.guacamole.auth.duo;
import com.google.inject.AbstractModule;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.duo.conf.ConfigurationService;
import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.environment.LocalEnvironment;
import org.apache.guacamole.net.auth.AuthenticationProvider;
/**
* Guice module which configures Duo-specific injections.
*/
public class DuoAuthenticationProviderModule extends AbstractModule {
/**
* Guacamole server environment.
*/
private final Environment environment;
/**
* A reference to the DuoAuthenticationProvider on behalf of which this
* module has configured injection.
*/
private final AuthenticationProvider authProvider;
/**
* Creates a new Duo authentication provider module which configures
* injection for the DuoAuthenticationProvider.
*
* @param authProvider
* The AuthenticationProvider for which injection is being configured.
*
* @throws GuacamoleException
* If an error occurs while retrieving the Guacamole server
* environment.
*/
public DuoAuthenticationProviderModule(AuthenticationProvider authProvider)
throws GuacamoleException {
// Get local environment
this.environment = LocalEnvironment.getInstance();
// Store associated auth provider
this.authProvider = authProvider;
}
@Override
protected void configure() {
// Bind core implementations of guacamole-ext classes
bind(AuthenticationProvider.class).toInstance(authProvider);
bind(Environment.class).toInstance(environment);
// Bind Duo-specific services
bind(ConfigurationService.class);
bind(UserVerificationService.class);
}
}

View File

@@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.guacamole.auth.duo;
import org.apache.guacamole.net.auth.AuthenticationSession;
import org.apache.guacamole.net.auth.Credentials;
/**
* Representation of an in-progress Duo authentication attempt.
*/
public class DuoAuthenticationSession extends AuthenticationSession {
/**
* The credentials that the user originally provided to Guacamole prior to
* being redirected to the Duo service.
*/
private final Credentials credentials;
/**
* Creates a new AuthenticationSession representing an in-progress Duo
* authentication attempt.
*
* @param credentials
* The credentials that the user originally provided to Guacamole prior
* to being redirected to the Duo service.
*
* @param expires
* The number of milliseconds that may elapse before this session must
* be considered invalid.
*/
public DuoAuthenticationSession(Credentials credentials, long expires) {
super(expires);
this.credentials = credentials;
}
/**
* Returns the credentials that the user originally provided to Guacamole
* prior to being redirected to the Duo service.
*
* @return
* The credentials that the user originally provided to Guacamole prior
* to being redirected to the Duo service.
*/
public Credentials getCredentials() {
return credentials;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.guacamole.auth.duo;
import com.google.inject.Singleton;
import org.apache.guacamole.net.auth.AuthenticationSessionManager;
/**
* Manager service that temporarily stores authentication attempts while
* the Duo authentication flow is underway.
*/
@Singleton
public class DuoAuthenticationSessionManager
extends AuthenticationSessionManager<DuoAuthenticationSession> {
// Intentionally empty (the default functions inherited from the
// AuthenticationSessionManager base class are sufficient for our needs)
}

View File

@@ -0,0 +1,276 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.guacamole.auth.duo;
import com.duosecurity.Client;
import com.duosecurity.exception.DuoException;
import com.duosecurity.model.Token;
import com.google.inject.Inject;
import inet.ipaddr.IPAddress;
import inet.ipaddr.IPAddressString;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.List;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.auth.duo.conf.ConfigurationService;
import org.apache.guacamole.form.RedirectField;
import org.apache.guacamole.language.TranslatableGuacamoleInsufficientCredentialsException;
import org.apache.guacamole.language.TranslatableMessage;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.Credentials;
import org.apache.guacamole.net.auth.credentials.CredentialsInfo;
import org.apache.guacamole.properties.IPAddressListProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Service for verifying the identity of a user against Duo.
*/
public class UserVerificationService {
/**
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(UserVerificationService.class);
/**
* The name of the HTTP parameter that Duo will use to communicate the
* result of the user's attempt to authenticate with their service. This
* parameter is provided in the GET request received when Duo redirects the
* user back to Guacamole.
*/
public static final String DUO_CODE_PARAMETER_NAME = "duo_code";
/**
* The name of the HTTP parameter that we will be using to hold the opaque
* authentication session ID. This session ID is transmitted to Duo during
* the initial redirect and received back from Duo via this parameter in
* the GET request received when Duo redirects the user back to Guacamole.
* The session ID is ultimately used to reconstitute the original
* credentials received from the user by Guacamole such that parameter
* tokens like GUAC_USERNAME and GUAC_PASSWORD can continue to work as
* expected.
*/
public static final String DUO_STATE_PARAMETER_NAME = "state";
/**
* The value that will be returned in the token if Duo authentication
* was successful.
*/
private static final String DUO_TOKEN_SUCCESS_VALUE = "allow";
/**
* Session manager for storing/retrieving the state of a user's
* authentication attempt while they are redirected to the Duo service.
*/
@Inject
private DuoAuthenticationSessionManager sessionManager;
/**
* Service for retrieving Duo configuration information.
*/
@Inject
private ConfigurationService confService;
/**
* Verifies the identity of the given user via the Duo multi-factor
* authentication service. If a signed response from Duo has not already
* been provided, a signed response from Duo is requested in the
* form of additional expected credentials. Any provided signed response
* is cryptographically verified. If no signed response is present, or the
* signed response is invalid, an exception is thrown.
*
* @param authenticatedUser
* The user whose identity should be verified against Duo.
*
* @throws GuacamoleException
* If required Duo-specific configuration options are missing or
* malformed, or if the user's identity cannot be verified.
*/
public void verifyAuthenticatedUser(AuthenticatedUser authenticatedUser)
throws GuacamoleException {
// Pull the original HTTP request used to authenticate
Credentials credentials = authenticatedUser.getCredentials();
IPAddress clientAddr = new IPAddressString(credentials.getRemoteAddress()).getAddress();
// Ignore anonymous users
String username = authenticatedUser.getIdentifier();
if (username == null || username.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 if the bypass list contains 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 for the client address and set enforcement flag.
else
enforceHost = IPAddressListProperty.addressListContains(enforceAddresses, clientAddr);
}
// If the enforce flag is not true, bypass Duo MFA.
if (!enforceHost)
return;
// Obtain a Duo client for redirecting the user to the Duo service and
// verifying any received authentication code
Client duoClient;
try {
duoClient = new Client.Builder(
confService.getClientId(),
confService.getClientSecret(),
confService.getAPIHostname(),
confService.getRedirectUri().toString())
.build();
}
catch (DuoException e) {
throw new GuacamoleServerException("Client for communicating with "
+ "the Duo authentication service could not be created.", e);
}
// Verify that the Duo service is healthy and available
try {
duoClient.healthCheck();
}
catch (DuoException e) {
throw new GuacamoleServerException("Duo authentication service is "
+ "not currently available (failed health check).", e);
}
// Retrieve signed Duo authentication code and session state from the
// request (these will be absent if this is an initial authentication
// attempt and not a redirect back from Duo)
String duoCode = credentials.getParameter(DUO_CODE_PARAMETER_NAME);
String duoState = credentials.getParameter(DUO_STATE_PARAMETER_NAME);
// Redirect to Duo to obtain an authentication code if that redirect
// has not yet occurred
if (duoCode != null && duoState != null) {
// Validate that the user has successfully verified their identify with
// the Duo service
try {
// Note unexpected behavior (Duo is expected to always return
// a token)
Token token = duoClient.exchangeAuthorizationCodeFor2FAResult(duoCode, username);
if (token == null) {
logger.warn("Duo did not return an authentication result "
+ "at all for the authentication attempt by user "
+ "\"{}\". This is unexpected behavior and may be "
+ "a bug in the Duo service or the Duo SDK. "
+ "Guacamole will attempt to automatically work "
+ "around the issue by making a fresh Duo "
+ "authentication request.", username);
}
// Warn if Duo explicitly denies authentication
else if (token.getAuth_result() == null || !DUO_TOKEN_SUCCESS_VALUE.equals(token.getAuth_result().getStatus())) {
logger.warn("Duo did not return an explicitly successful "
+ "authentication result for the authentication "
+ "attempt by user \"{}\". The user will now be "
+ "redirected back to the Duo service to reattempt"
+ "authentication.", username);
}
// Allow user to continue authenticating with Guacamole only if
// Duo has validated their identity
else
return;
}
catch (DuoException e) {
logger.debug("The Duo client failed internally while "
+ "attempting to validate the identity of user "
+ "\"{}\". This is commonly caused by stale query "
+ "parameters from an older Duo request remaining "
+ "present in the Guacamole URL. The user will now be "
+ "redirected back to the Duo service to reattempt "
+ "authentication.", e);
}
}
// Store received credentials for later retrieval leveraging Duo's
// opaque session state identifier (we need to maintain these
// credentials so that things like the GUAC_USERNAME and
// GUAC_PASSWORD tokens continue to work as expected despite the
// redirect to/from the external Duo service)
duoState = duoClient.generateState();
long expiresAfter = TimeUnit.MINUTES.toMillis(confService.getAuthenticationTimeout());
sessionManager.defer(new DuoAuthenticationSession(credentials, expiresAfter), duoState);
// Obtain authentication URL from Duo client
String duoAuthUrlString;
try {
duoAuthUrlString = duoClient.createAuthUrl(username, duoState);
}
catch (DuoException e) {
throw new GuacamoleServerException("Duo client failed to "
+ "generate the authentication URL necessary to "
+ "redirect the authenticating user to the Duo "
+ "service.", e);
}
// Parse and validate URL obtained from Duo client
URI duoAuthUrl;
try {
duoAuthUrl = new URI(duoAuthUrlString);
}
catch (URISyntaxException e) {
throw new GuacamoleServerException("Authentication URL "
+ "generated by the Duo client is not actually a "
+ "valid URL and cannot be used to redirect the "
+ "authenticating user to the Duo service.", e);
}
// Request that user be redirected to the Duo service to obtain
// a Duo authentication code
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, duoAuthUrl,
new TranslatableMessage("LOGIN.INFO_DUO_REDIRECT_PENDING")
)
))
);
}
}

View File

@@ -0,0 +1,268 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.guacamole.auth.duo.conf;
import com.google.inject.Inject;
import inet.ipaddr.IPAddress;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.properties.IntegerGuacamoleProperty;
import org.apache.guacamole.properties.IPAddressListProperty;
import org.apache.guacamole.properties.StringGuacamoleProperty;
import org.apache.guacamole.properties.URIGuacamoleProperty;
/**
* Service for retrieving configuration information regarding the Duo
* authentication extension.
*/
public class ConfigurationService {
/**
* The Guacamole server environment.
*/
@Inject
private Environment environment;
/**
* The property within guacamole.properties which defines the hostname
* of the Duo API endpoint to be used to verify user identities. This will
* usually be in the form "api-XXXXXXXX.duosecurity.com", where "XXXXXXXX"
* is some arbitrary alphanumeric value assigned by Duo and specific to
* your organization.
*/
private static final StringGuacamoleProperty DUO_API_HOSTNAME =
new StringGuacamoleProperty() {
@Override
public String getName() { return "duo-api-hostname"; }
};
/**
* 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 =
new StringGuacamoleProperty() {
@Override
public String getName() { return "duo-client-id"; }
};
/**
* The property within guacamole.properties which defines the secret key
* received from Duo for verifying Guacamole users. This value MUST be
* exactly 40 characters.
*/
private static final StringGuacamoleProperty DUO_CLIENT_SECRET =
new StringGuacamoleProperty() {
@Override
public String getName() { return "duo-client-secret"; }
};
/**
* 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 URI used to access Guacamole.
*/
private static final URIGuacamoleProperty DUO_REDIRECT_URI =
new URIGuacamoleProperty() {
@Override
public String getName() { return "duo-redirect-uri"; }
};
/**
* The property that configures the timeout, in minutes, of in-progress
* Duo authentication attempts. Authentication attempts that take longer
* than this period of time will be invalidated.
*/
private static final IntegerGuacamoleProperty DUO_AUTH_TIMEOUT =
new IntegerGuacamoleProperty() {
@Override
public String getName() { return "duo-auth-timeout"; }
};
/**
* The optional property that contains a comma-separated list of IP addresses
* or CIDRs for which the MFA requirement should be bypassed. If the Duo
* extension is installed, any/all users authenticating from clients that
* match this list will be able to successfully log in without fulfilling
* the MFA requirement. If this option is omitted or is empty, and the
* Duo module is installed, all users from all hosts will have Duo MFA
* enforced.
*/
private static final IPAddressListProperty DUO_BYPASS_HOSTS =
new IPAddressListProperty() {
@Override
public String getName() { return "duo-bypass-hosts"; }
};
/**
* The optional property that contains a comma-separated list of IP addresses
* or CIDRs for which the MFA requirement should be explicitly enforced. If
* the Duo module is enabled and this property is specified, users that log
* in from hosts that match the items in this list will have Duo MFA required,
* and all users from hosts that do not match this list will be able to log
* in without the MFA requirement. If this option is missing or empty and
* the Duo module is installed, MFA will be enforced for all users.
*/
private static final IPAddressListProperty DUO_ENFORCE_HOSTS =
new IPAddressListProperty() {
@Override
public String getName() { return "duo-enforce-hosts"; }
};
/**
* Returns the hostname of the Duo API endpoint to be used to verify user
* identities, as defined in guacamole.properties by the "duo-api-hostname"
* property. This will usually be in the form
* "api-XXXXXXXX.duosecurity.com", where "XXXXXXXX" is some arbitrary
* alphanumeric value assigned by Duo and specific to your organization.
*
* @return
* The hostname of the Duo API endpoint to be used to verify user
* identities.
*
* @throws GuacamoleException
* If the associated property within guacamole.properties is missing.
*/
public String getAPIHostname() throws GuacamoleException {
return environment.getRequiredProperty(DUO_API_HOSTNAME);
}
/**
* Returns the Duo client id received from Duo for verifying Guacamole
* users, as defined in guacamole.properties by the "duo-client-id"
* property. This value MUST be exactly 20 characters.
*
* @return
* The client id received from Duo for verifying Guacamole users.
*
* @throws GuacamoleException
* If the associated property within guacamole.properties is missing.
*/
public String getClientId() throws GuacamoleException {
return environment.getRequiredProperty(DUO_CLIENT_ID);
}
/**
* 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
* The client secret received from Duo for verifying Guacamole users.
*
* @throws GuacamoleException
* If the associated property within guacamole.properties is missing.
*/
public String getClientSecret() throws GuacamoleException {
return environment.getRequiredProperty(DUO_CLIENT_SECRET);
}
/**
* 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
* authentication has been completed.
*
* @throws GuacamoleException
* If guacamole.properties cannot be read, or if the property is not
* defined.
*/
public URI getRedirectUri() throws GuacamoleException {
return environment.getRequiredProperty(DUO_REDIRECT_URI);
}
/**
* Returns the maximum amount of time to allow for an in-progress Duo
* authentication attempt to be completed, in minutes. A user that takes
* longer than this amount of time to complete authentication with Duo
* will need to try again.
*
* @return
* The maximum amount of time to allow for an in-progress Duo
* authentication attempt to be completed, in minutes.
*
* @throws GuacamoleException
* If the authentication timeout cannot be parsed.
*/
public int getAuthenticationTimeout() throws GuacamoleException {
return environment.getProperty(DUO_AUTH_TIMEOUT, 5);
}
/**
* Returns the list of IP addresses and subnets defined in guacamole.properties
* for which Duo MFA should _not_ be enforced. Users logging in from hosts
* contained in this list will be logged in without the MFA requirement.
*
* @return
* A list of IP addresses and subnets for which Duo MFA should not be
* enforced.
*
* @throws GuacamoleException
* If guacamole.properties cannot be parsed, or if an invalid IP address
* or subnet is specified.
*/
public List<IPAddress> getBypassHosts() throws GuacamoleException {
return environment.getProperty(DUO_BYPASS_HOSTS, Collections.emptyList());
}
/**
* Returns the list of IP addresses and subnets defined in guacamole.properties
* for which Duo MFA should explicitly be enforced, while logins from all
* other hosts should not enforce MFA. Users logging in from hosts
* contained in this list will be required to complete the Duo MFA authentication,
* while users from all other hosts will be logged in without the MFA requirement.
*
* @return
* A list of IP addresses and subnets for which Duo MFA should be
* explicitly enforced.
*
* @throws GuacamoleException
* If guacamole.properties cannot be parsed, or if an invalid IP address
* or subnet is specified.
*/
public List<IPAddress> getEnforceHosts() throws GuacamoleException {
return environment.getProperty(DUO_ENFORCE_HOSTS, Collections.emptyList());
}
}

View File

@@ -0,0 +1,25 @@
{
"guacamoleVersion" : "1.6.0",
"name" : "Duo TFA Authentication Backend",
"namespace" : "duo",
"authProviders" : [
"org.apache.guacamole.auth.duo.DuoAuthenticationProvider"
],
"translations" : [
"translations/ca.json",
"translations/de.json",
"translations/en.json",
"translations/fr.json",
"translations/ja.json",
"translations/ko.json",
"translations/pl.json",
"translations/pt.json",
"translations/ru.json",
"translations/zh.json"
]
}

View File

@@ -0,0 +1,13 @@
{
"DATA_SOURCE_DUO" : {
"NAME" : "Duo TFA Backend"
},
"LOGIN" : {
"FIELD_HEADER_GUAC_DUO_SIGNED_RESPONSE" : "",
"INFO_DUO_VALIDATION_CODE_INCORRECT" : "El codi de validació duo és incorrecte.",
"INFO_DUO_AUTH_REQUIRED" : "Si us plau, autentiqueu amb Duo per continuar."
}
}

View File

@@ -0,0 +1,13 @@
{
"DATA_SOURCE_DUO" : {
"NAME" : "Duo TFA Backend"
},
"LOGIN" : {
"FIELD_HEADER_GUAC_DUO_SIGNED_RESPONSE" : "",
"INFO_DUO_VALIDATION_CODE_INCORRECT" : "Duo Validierungscode nicht korrekt.",
"INFO_DUO_AUTH_REQUIRED" : "Bitte melden Sie sich mit Duo an, um fortzufahren."
}
}

View File

@@ -0,0 +1,14 @@
{
"DATA_SOURCE_DUO" : {
"NAME" : "Duo TFA Backend"
},
"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_REDIRECT_PENDING" : "Please wait, redirecting to Duo..."
}
}

View File

@@ -0,0 +1,14 @@
{
"DATA_SOURCE_DUO" : {
"NAME" : "Duo TFA Backend"
},
"LOGIN" : {
"FIELD_HEADER_GUAC_DUO_SIGNED_RESPONSE" : "",
"INFO_DUO_VALIDATION_CODE_INCORRECT" : "Code de validation Duo incorrect.",
"INFO_DUO_AUTH_REQUIRED" : "Veuillez vous authentifier avec Duo pour continuer.",
"INFO_DUO_REDIRECT_PENDING" : "Veuillez patienter, redirection vers Duo..."
}
}

View File

@@ -0,0 +1,8 @@
{
"LOGIN" : {
"INFO_DUO_VALIDATION_CODE_INCORRECT" : "Codice di convalida Duo errato.",
"INFO_DUO_AUTH_REQUIRED" : "Si prega di autenticarsi con Duo per continuare."
}
}

View File

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

View File

@@ -0,0 +1,8 @@
{
"LOGIN" : {
"INFO_DUO_VALIDATION_CODE_INCORRECT" : "Duo 유효성 검사 코드가 잘못되었습니다.",
"INFO_DUO_AUTH_REQUIRED" : "계속하려면 Duo로 인증하세요."
}
}

View File

@@ -0,0 +1,13 @@
{
"DATA_SOURCE_DUO" : {
"NAME" : "Duo TFA Backend"
},
"LOGIN" : {
"FIELD_HEADER_GUAC_DUO_SIGNED_RESPONSE" : "",
"INFO_DUO_VALIDATION_CODE_INCORRECT" : "Nieprawidłowy kod weryfikacyjny Duo.",
"INFO_DUO_AUTH_REQUIRED" : "Aby kontynuować uwierzytelnij się w Duo."
}
}

View File

@@ -0,0 +1,13 @@
{
"DATA_SOURCE_DUO" : {
"NAME" : "Duo TFA Backend"
},
"LOGIN" : {
"FIELD_HEADER_GUAC_DUO_SIGNED_RESPONSE" : "",
"INFO_DUO_VALIDATION_CODE_INCORRECT" : "Código de validação Duo incorreto.",
"INFO_DUO_AUTH_REQUIRED" : "Por favor autentique com Duo para continuar."
}
}

View File

@@ -0,0 +1,12 @@
{
"DATA_SOURCE_DUO" : {
"NAME" : "Бэкенд Duo TFA"
},
"LOGIN" : {
"INFO_DUO_VALIDATION_CODE_INCORRECT" : "Неверный код валидации Duo.",
"INFO_DUO_AUTH_REQUIRED" : "Пожалуйста, аутентифицируйтесь в Duo для продолжения."
}
}

View File

@@ -0,0 +1,13 @@
{
"DATA_SOURCE_DUO" : {
"NAME" : "Duo TFA后端"
},
"LOGIN" : {
"FIELD_HEADER_GUAC_DUO_SIGNED_RESPONSE" : "",
"INFO_DUO_VALIDATION_CODE_INCORRECT" : "Duo验证码不正确。",
"INFO_DUO_AUTH_REQUIRED" : "请先使用Duo进行身份验证。"
}
}