GUACAMOLE-96: Add configuration parameters for details of TOTP generation.

Project: http://git-wip-us.apache.org/repos/asf/guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-client/commit/a422fdf9
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-client/tree/a422fdf9
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-client/diff/a422fdf9

Branch: refs/heads/master
Commit: a422fdf9c235e898d5c05499cef638501beb6508
Parents: 170a11b
Author: Michael Jumper <mjum...@apache.org>
Authored: Mon Nov 20 14:29:03 2017 -0800
Committer: Michael Jumper <mjum...@apache.org>
Committed: Sun Feb 4 19:45:18 2018 -0800

----------------------------------------------------------------------
 .../totp/TOTPAuthenticationProviderModule.java  |   2 +
 .../auth/totp/UserVerificationService.java      |  33 +++-
 .../auth/totp/conf/ConfigurationService.java    | 161 +++++++++++++++++++
 .../auth/totp/conf/TOTPModeProperty.java        |  62 +++++++
 .../auth/totp/form/AuthenticationCodeField.java |  42 ++---
 5 files changed, 274 insertions(+), 26 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/a422fdf9/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/TOTPAuthenticationProviderModule.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/TOTPAuthenticationProviderModule.java
 
b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/TOTPAuthenticationProviderModule.java
index 757a412..e72beec 100644
--- 
a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/TOTPAuthenticationProviderModule.java
+++ 
b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/TOTPAuthenticationProviderModule.java
@@ -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);
 
     }

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/a422fdf9/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/UserVerificationService.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/UserVerificationService.java
 
b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/UserVerificationService.java
index a44b6ee..987d4ca 100644
--- 
a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/UserVerificationService.java
+++ 
b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/UserVerificationService.java
@@ -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;
@@ -67,6 +70,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,
      * a random key is generated and associated with the user. If the extension
@@ -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

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/a422fdf9/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/conf/ConfigurationService.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/conf/ConfigurationService.java
 
b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/conf/ConfigurationService.java
new file mode 100644
index 0000000..8658849
--- /dev/null
+++ 
b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/conf/ConfigurationService.java
@@ -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);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/a422fdf9/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/conf/TOTPModeProperty.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/conf/TOTPModeProperty.java
 
b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/conf/TOTPModeProperty.java
new file mode 100644
index 0000000..bfe3ef3
--- /dev/null
+++ 
b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/conf/TOTPModeProperty.java
@@ -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\".");
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/a422fdf9/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/form/AuthenticationCodeField.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/form/AuthenticationCodeField.java
 
b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/form/AuthenticationCodeField.java
index 1be5f27..e0333dd 100644
--- 
a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/form/AuthenticationCodeField.java
+++ 
b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/form/AuthenticationCodeField.java
@@ -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;
 
@@ -67,30 +69,33 @@ 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;
+    private 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.
+     * generated via TOTP. The user's TOTP key is not exposed for enrollment.
      */
-    public AuthenticationCodeField(UserTOTPKey key) {
+    public AuthenticationCodeField() {
         super(PARAMETER_NAME, FIELD_TYPE_NAME);
-        this.key = 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.
+     * Exposes the given key to facilitate enrollment.
+     *
+     * @param key
+     *     The TOTP key to expose to the user for the sake of enrollment.
      */
-    public AuthenticationCodeField() {
-        this(null);
+    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();
 
     }

Reply via email to