git.fiddlerwoaroof.com
Raw Blame History
/* Copyright (C) CJ Affiliate
 *
 * You may use, distribute and modify this code under  the
 * terms of the  GNU General Public License version 2  or
 * later.
 *
 * You should have received a copy of the license with this
 * file. If not, you will find a copy in the "LICENSE" file
 * at https://github.com/cjdev/dual-control.
 */

#include <string>
#include <vector>
#include <memory>
#include <fstream>
#include <iostream>
#include <functional>

#include "token.h"
#include "user.h"
#include "base32.h"
#include "sys_fstream.h"
#include "random_source.h"

namespace
{
class tokens_impl : public tokens_ifc
{
private:
    fstreams fstreams_;
    totp_generator generator_;
    random_source rand_;
public:
    tokens_impl (const fstreams &fstreams, const totp_generator generator,
                 const random_source rand) :
        fstreams_ (fstreams), generator_ (generator), rand_ (rand) {}
    std::string token (const user &user) const override
    {
        // Get key
        std::string line = read_key (user);

        if (line == "") {
            return "";
        }

        base32 codec;
        std::vector<uint8_t> key = codec.decode (line);

        return generator_.generate_token (key);
    }

private:
    std::string get_key_path (const user &user) const
    {
        return user.home_directory() + "/.dual_control";
    }

    bool key_exists (const user &user) const
    {
        // check if file exists
        std::string file_path = get_key_path (user);
        fstreams::pstream stream (fstreams_.open_fstream (file_path));
        return stream->good();
    }

    std::string read_key (const user &user) const
    {
        std::string file_path = get_key_path (user);
        fstreams::pstream stream (fstreams_.open_fstream (file_path));

        std::vector<char> line_v (16);
        stream->read (line_v.data(), line_v.size());

        if (stream->fail()) {
            return "";
        }

        std::string line (line_v.begin(), line_v.end());
        return line;
    }
public:
    std::string generate_key() const override
    {
        base32 codec;
        // get randomness
        int length = 10;
        std::vector<uint8_t> random_bytes (rand_.get_random_bytes (length));
        // base32encode it
        std::string key = codec.encode (random_bytes);
        return key;
    }

    std::string ensure_key (const user &user) const override
    {
        if (!key_exists (user)) {
            std::string key = generate_key();
            save (user, key);
            return key;
        } else {
            return read_key (user);
        }
    }

    void save (const user &user, const std::string &key) const override
    {
        std::string file_path = get_key_path (user);
        fstreams::postream stream (fstreams_.open_ofstream (file_path,
                                   std::ios_base::trunc));
        *stream << key<< std::endl;
    }
};
}

tokens tokens::create (const fstreams &fstreams,
                       const totp_generator &generator,
                       const random_source &rand)
{
    return tokens (tokens::delegate
                   (new tokens_impl (fstreams, generator, rand)));
}