mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 21:27:40 +00:00
GUACAMOLE-96: Add configuration parameters for details of TOTP generation.
This commit is contained in:
@@ -21,6 +21,7 @@ package org.apache.guacamole.auth.totp;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.auth.totp.conf.ConfigurationService;
|
||||
import org.apache.guacamole.environment.Environment;
|
||||
import org.apache.guacamole.environment.LocalEnvironment;
|
||||
import org.apache.guacamole.net.auth.AuthenticationProvider;
|
||||
@@ -71,6 +72,7 @@ public class TOTPAuthenticationProviderModule extends AbstractModule {
|
||||
bind(Environment.class).toInstance(environment);
|
||||
|
||||
// Bind TOTP-specific services
|
||||
bind(ConfigurationService.class);
|
||||
bind(UserVerificationService.class);
|
||||
|
||||
}
|
||||
|
@@ -20,6 +20,8 @@
|
||||
package org.apache.guacamole.auth.totp;
|
||||
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -28,6 +30,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.guacamole.GuacamoleClientException;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleUnsupportedException;
|
||||
import org.apache.guacamole.auth.totp.conf.ConfigurationService;
|
||||
import org.apache.guacamole.auth.totp.form.AuthenticationCodeField;
|
||||
import org.apache.guacamole.form.Field;
|
||||
import org.apache.guacamole.net.auth.AuthenticatedUser;
|
||||
@@ -66,6 +69,18 @@ public class UserVerificationService {
|
||||
*/
|
||||
private static final BaseEncoding BASE32 = BaseEncoding.base32();
|
||||
|
||||
/**
|
||||
* Service for retrieving configuration information.
|
||||
*/
|
||||
@Inject
|
||||
private ConfigurationService confService;
|
||||
|
||||
/**
|
||||
* Provider for AuthenticationCodeField instances.
|
||||
*/
|
||||
@Inject
|
||||
private Provider<AuthenticationCodeField> codeFieldProvider;
|
||||
|
||||
/**
|
||||
* Retrieves and decodes the base32-encoded TOTP key associated with user
|
||||
* having the given UserContext. If no TOTP key is associated with the user,
|
||||
@@ -100,7 +115,8 @@ public class UserVerificationService {
|
||||
if (secret == null) {
|
||||
|
||||
// Generate random key for user
|
||||
UserTOTPKey generated = new UserTOTPKey(username, TOTPGenerator.Mode.SHA1.getRecommendedKeyLength());
|
||||
TOTPGenerator.Mode mode = confService.getMode();
|
||||
UserTOTPKey generated = new UserTOTPKey(username,mode.getRecommendedKeyLength());
|
||||
if (setKey(context, generated))
|
||||
return generated;
|
||||
|
||||
@@ -223,25 +239,32 @@ public class UserVerificationService {
|
||||
// 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 (!key.isConfirmed()) {
|
||||
field.exposeKey(key);
|
||||
throw new GuacamoleInsufficientCredentialsException(
|
||||
"LOGIN.INFO_TOTP_REQUIRED", new CredentialsInfo(
|
||||
Collections.<Field>singletonList(new AuthenticationCodeField(key))
|
||||
Collections.<Field>singletonList(field)
|
||||
));
|
||||
}
|
||||
|
||||
// Otherwise simply request the user's authentication code
|
||||
throw new GuacamoleInsufficientCredentialsException(
|
||||
"LOGIN.INFO_TOTP_REQUIRED", new CredentialsInfo(
|
||||
Collections.<Field>singletonList(new AuthenticationCodeField())
|
||||
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());
|
||||
|
||||
// Verify provided TOTP against value produced by generator
|
||||
TOTPGenerator totp = new TOTPGenerator(key.getSecret(), TOTPGenerator.Mode.SHA1, 6);
|
||||
if (code.equals(totp.generate()) || code.equals(totp.previous())) {
|
||||
|
||||
// Record key as confirmed, if it hasn't already been so recorded
|
||||
|
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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.totp.conf;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
import org.apache.guacamole.environment.Environment;
|
||||
import org.apache.guacamole.properties.IntegerGuacamoleProperty;
|
||||
import org.apache.guacamole.properties.StringGuacamoleProperty;
|
||||
import org.apache.guacamole.totp.TOTPGenerator;
|
||||
|
||||
/**
|
||||
* Service for retrieving configuration information regarding the TOTP
|
||||
* authentication extension.
|
||||
*/
|
||||
public class ConfigurationService {
|
||||
|
||||
/**
|
||||
* The Guacamole server environment.
|
||||
*/
|
||||
@Inject
|
||||
private Environment environment;
|
||||
|
||||
/**
|
||||
* The human-readable name of the entity issuing user accounts. By default,
|
||||
* this will be "Apache Guacamole".
|
||||
*/
|
||||
private static final StringGuacamoleProperty TOTP_ISSUER =
|
||||
new StringGuacamoleProperty() {
|
||||
|
||||
@Override
|
||||
public String getName() { return "totp-issuer"; }
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The number of digits which should be included in each generated TOTP
|
||||
* code. By default, this will be 6.
|
||||
*/
|
||||
private static final IntegerGuacamoleProperty TOTP_DIGITS=
|
||||
new IntegerGuacamoleProperty() {
|
||||
|
||||
@Override
|
||||
public String getName() { return "totp-digits"; }
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The duration that each generated code should remain valid, in seconds.
|
||||
* By default, this will be 30.
|
||||
*/
|
||||
private static final IntegerGuacamoleProperty TOTP_PERIOD =
|
||||
new IntegerGuacamoleProperty() {
|
||||
|
||||
@Override
|
||||
public String getName() { return "totp-period"; }
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The hash algorithm that should be used to generate TOTP codes. By
|
||||
* default, this will be "sha1". Legal values are "sha1", "sha256", and
|
||||
* "sha512".
|
||||
*/
|
||||
private static final TOTPModeProperty TOTP_MODE =
|
||||
new TOTPModeProperty() {
|
||||
|
||||
@Override
|
||||
public String getName() { return "totp-mode"; }
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the human-readable name of the entity issuing user accounts. If
|
||||
* not specified, "Apache Guacamole" will be used by default.
|
||||
*
|
||||
* @return
|
||||
* The human-readable name of the entity issuing user accounts.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the "totp-issuer" property cannot be read from
|
||||
* guacamole.properties.
|
||||
*/
|
||||
public String getIssuer() throws GuacamoleException {
|
||||
return environment.getProperty(TOTP_ISSUER, "Apache Guacamole");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of digits which should be included in each generated
|
||||
* TOTP code. If not specified, 6 will be used by default.
|
||||
*
|
||||
* @return
|
||||
* The number of digits which should be included in each generated
|
||||
* TOTP code.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the "totp-digits" property cannot be read from
|
||||
* guacamole.properties.
|
||||
*/
|
||||
public int getDigits() throws GuacamoleException {
|
||||
|
||||
// Validate legal number of digits
|
||||
int digits = environment.getProperty(TOTP_DIGITS, 6);
|
||||
if (digits < 6 || digits > 8)
|
||||
throw new GuacamoleServerException("TOTP codes may have no fewer "
|
||||
+ "than 6 digits and no more than 8 digits.");
|
||||
|
||||
return digits;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the duration that each generated code should remain valid, in
|
||||
* seconds. If not specified, 30 will be used by default.
|
||||
*
|
||||
* @return
|
||||
* The duration that each generated code should remain valid, in
|
||||
* seconds.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the "totp-period" property cannot be read from
|
||||
* guacamole.properties.
|
||||
*/
|
||||
public int getPeriod() throws GuacamoleException {
|
||||
return environment.getProperty(TOTP_PERIOD, 30);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash algorithm that should be used to generate TOTP codes. If
|
||||
* not specified, SHA1 will be used by default.
|
||||
*
|
||||
* @return
|
||||
* The hash algorithm that should be used to generate TOTP codes.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the "totp-mode" property cannot be read from
|
||||
* guacamole.properties.
|
||||
*/
|
||||
public TOTPGenerator.Mode getMode() throws GuacamoleException {
|
||||
return environment.getProperty(TOTP_MODE, TOTPGenerator.Mode.SHA1);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.totp.conf;
|
||||
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
import org.apache.guacamole.properties.GuacamoleProperty;
|
||||
import org.apache.guacamole.totp.TOTPGenerator;
|
||||
|
||||
/**
|
||||
* A GuacamoleProperty whose value is a TOTP generation method. The string
|
||||
* values "sha1", "sha256", and "sha512" are each parsed to their corresponding
|
||||
* values within the TOTPGenerator.Mode enum. All other string values result in
|
||||
* parse errors.
|
||||
*/
|
||||
public abstract class TOTPModeProperty
|
||||
implements GuacamoleProperty<TOTPGenerator.Mode> {
|
||||
|
||||
@Override
|
||||
public TOTPGenerator.Mode parseValue(String value)
|
||||
throws GuacamoleException {
|
||||
|
||||
// If no value provided, return null.
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
// SHA1
|
||||
if (value.equals("sha1"))
|
||||
return TOTPGenerator.Mode.SHA1;
|
||||
|
||||
// SHA256
|
||||
if (value.equals("sha256"))
|
||||
return TOTPGenerator.Mode.SHA256;
|
||||
|
||||
// SHA512
|
||||
if (value.equals("sha512"))
|
||||
return TOTPGenerator.Mode.SHA512;
|
||||
|
||||
// The provided value is not legal
|
||||
throw new GuacamoleServerException("TOTP mode must be one of "
|
||||
+ "\"sha1\", \"sha256\", or \"sha512\".");
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -20,6 +20,7 @@
|
||||
package org.apache.guacamole.auth.totp.form;
|
||||
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.WriterException;
|
||||
import com.google.zxing.client.j2se.MatrixToImageWriter;
|
||||
@@ -32,6 +33,7 @@ import javax.ws.rs.core.UriBuilder;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.auth.totp.UserTOTPKey;
|
||||
import org.apache.guacamole.auth.totp.conf.ConfigurationService;
|
||||
import org.apache.guacamole.form.Field;
|
||||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
|
||||
@@ -66,31 +68,34 @@ public class AuthenticationCodeField extends Field {
|
||||
*/
|
||||
private static final BaseEncoding BASE32 = BaseEncoding.base32();
|
||||
|
||||
/**
|
||||
* Service for retrieving configuration information.
|
||||
*/
|
||||
@Inject
|
||||
private ConfigurationService confService;
|
||||
|
||||
/**
|
||||
* The TOTP key to expose to the user for the sake of enrollment, if any.
|
||||
* If no such key should be exposed to the user, this will be null.
|
||||
*/
|
||||
private final UserTOTPKey key;
|
||||
|
||||
/**
|
||||
* Creates a new field which prompts the user for an authentication code
|
||||
* generated via TOTP, and provide the user with their TOTP key to
|
||||
* facilitate enrollment.
|
||||
*
|
||||
* @param key
|
||||
* The TOTP key to expose to the user for the sake of enrollment.
|
||||
*/
|
||||
public AuthenticationCodeField(UserTOTPKey key) {
|
||||
super(PARAMETER_NAME, FIELD_TYPE_NAME);
|
||||
this.key = key;
|
||||
}
|
||||
private UserTOTPKey key;
|
||||
|
||||
/**
|
||||
* Creates a new field which prompts the user for an authentication code
|
||||
* generated via TOTP. The user's TOTP key is not exposed for enrollment.
|
||||
*/
|
||||
public AuthenticationCodeField() {
|
||||
this(null);
|
||||
super(PARAMETER_NAME, FIELD_TYPE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes the given key to facilitate enrollment.
|
||||
*
|
||||
* @param key
|
||||
* The TOTP key to expose to the user for the sake of enrollment.
|
||||
*/
|
||||
public void exposeKey(UserTOTPKey key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,20 +119,15 @@ public class AuthenticationCodeField extends Field {
|
||||
if (key == null)
|
||||
return null;
|
||||
|
||||
// FIXME: Pull from configuration
|
||||
String issuer = "Some Issuer";
|
||||
String algorithm = "SHA1";
|
||||
String digits = "6";
|
||||
String period = "30";
|
||||
|
||||
// Format "otpauth" URL (see https://github.com/google/google-authenticator/wiki/Key-Uri-Format)
|
||||
String issuer = confService.getIssuer();
|
||||
return UriBuilder.fromUri("otpauth://totp/")
|
||||
.path(issuer + ":" + key.getUsername())
|
||||
.queryParam("secret", BASE32.encode(key.getSecret()))
|
||||
.queryParam("issuer", issuer)
|
||||
.queryParam("algorithm", algorithm)
|
||||
.queryParam("digits", digits)
|
||||
.queryParam("period", period)
|
||||
.queryParam("algorithm", confService.getMode())
|
||||
.queryParam("digits", confService.getDigits())
|
||||
.queryParam("period", confService.getPeriod())
|
||||
.build();
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user