Github user necouchman commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/247#discussion_r166056157
  
    --- Diff: 
extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/CodeUsageTrackingService.java
 ---
    @@ -0,0 +1,264 @@
    +/*
    + * 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.user;
    +
    +import com.google.inject.Inject;
    +import com.google.inject.Singleton;
    +import java.util.Iterator;
    +import java.util.Map;
    +import java.util.concurrent.ConcurrentHashMap;
    +import java.util.concurrent.ConcurrentMap;
    +import java.util.concurrent.Executors;
    +import java.util.concurrent.ScheduledExecutorService;
    +import java.util.concurrent.TimeUnit;
    +import org.apache.guacamole.GuacamoleException;
    +import org.apache.guacamole.auth.totp.conf.ConfigurationService;
    +import org.slf4j.Logger;
    +import org.slf4j.LoggerFactory;
    +
    +/**
    + * Service for tracking past valid uses of TOTP codes. An internal thread
    + * periodically walks through records of past codes, removing records which
    + * should be invalid by their own nature (no longer matching codes 
generated by
    + * the secret key).
    + */
    +@Singleton
    +public class CodeUsageTrackingService {
    +
    +    /**
    +     * The number of periods during which a previously-used code should 
remain
    +     * unusable. Once this period has elapsed, the code can be reused 
again if
    +     * it is otherwise valid.
    +     */
    +    private static final int INVALID_INTERVAL = 2;
    +
    +    /**
    +     * Logger for this class.
    +     */
    +    private final Logger logger = 
LoggerFactory.getLogger(CodeUsageTrackingService.class);
    +
    +    /**
    +     * Executor service which runs the cleanup task.
    +     */
    +    private final ScheduledExecutorService executor = 
Executors.newScheduledThreadPool(1);
    +
    +    /**
    +     * Service for retrieving configuration information.
    +     */
    +    @Inject
    +    private ConfigurationService confService;
    +
    +    /**
    +     * Map of previously-used codes to the timestamp after which the code 
can
    +     * be used again, providing the TOTP key legitimately generates that 
code.
    +     */
    +    private final ConcurrentMap<UsedCode, Long> invalidCodes =
    +            new ConcurrentHashMap<UsedCode, Long>();
    +
    +    /**
    +     * Creates a new CodeUsageTrackingService which tracks past valid uses 
of
    +     * TOTP codes on a per-user basis.
    +     */
    +    public CodeUsageTrackingService() {
    +        executor.scheduleAtFixedRate(new CodeEvictionTask(), 1, 1, 
TimeUnit.MINUTES);
    +    }
    +
    +    /**
    +     * Task which iterates through all explicitly-invalidated codes, 
evicting
    +     * those codes which are old enough that they would fail validation 
against
    +     * the secret key anyway.
    +     */
    +    private class CodeEvictionTask implements Runnable {
    +
    +        @Override
    +        public void run() {
    +
    +            // Get start time of cleanup check
    +            long checkStart = System.currentTimeMillis();
    +
    +            // For each code still being tracked, remove those which are 
old
    +            // enough that they would fail validation against the secret 
key
    +            Iterator<Map.Entry<UsedCode, Long>> entries = 
invalidCodes.entrySet().iterator();
    +            while (entries.hasNext()) {
    +
    +                Map.Entry<UsedCode, Long> entry = entries.next();
    +                long invalidUntil = entry.getValue();
    +
    +                // If code is sufficiently old, evict it and check the 
next one
    +                if (checkStart >= invalidUntil)
    +                    entries.remove();
    +
    +            }
    +
    +            // Log completion and duration
    +            logger.debug("TOTP tracking cleanup check completed in {} ms.",
    +                    System.currentTimeMillis() - checkStart);
    +
    +        }
    +
    +    }
    +
    +    /**
    +     * A valid TOTP code which was previously used by a particular user.
    +     */
    +    private class UsedCode {
    +
    +        /**
    +         * The username of the user which previously used this code.
    +         */
    +        private final String username;
    +
    +        /**
    +         * The valid code given by the user.
    +         */
    +        private final String code;
    +
    +        /**
    +         * Creates a new UsedCode which records the given code as having 
been
    +         * used by the given user.
    +         *
    +         * @param username
    +         *     The username of the user which previously used the given 
code.
    +         *
    +         * @param code
    +         *     The valid code given by the user.
    +         */
    +        public UsedCode(String username, String code) {
    +            this.username = username;
    +            this.code = code;
    +        }
    +
    +        /**
    +         * Returns the username of the user which previously used the code
    +         * associated with this UsedCode.
    +         *
    +         * @return
    +         *     The username of the user which previously used this code.
    +         */
    +        public String getUsername() {
    +            return username;
    +        }
    +
    +        /**
    +         * Returns the valid code given by the user when this UsedCode was
    +         * created.
    +         *
    +         * @return
    +         *     The valid code given by the user.
    +         */
    +        public String getCode() {
    +            return code;
    +        }
    +
    +        @Override
    +        public int hashCode() {
    +            int hash = 7;
    +            hash = 79 * hash + this.username.hashCode();
    +            hash = 79 * hash + this.code.hashCode();
    --- End diff --
    
    ok


---

Reply via email to