# Path: mozsvc/secrets.py
# class Secrets(object):
#     """Load node-specific secrets from a file.
# 
#     This class provides a method to get a list of secrets for a node
#     ordered by timestamps. The secrets are stored in a CSV file which
#     is loaded when the object is created.
# 
#     Options:
# 
#     - **filename**: a list of file paths, or a single path.
# 
#     """
#     def __init__(self, filename=None):
#         self._secrets = defaultdict(list)
#         if filename is not None:
#             self.load(filename)
# 
#     def keys(self):
#         return list(self._secrets.keys())
# 
#     def load(self, filename):
#         if not isinstance(filename, (list, tuple)):
#             filename = [filename]
# 
#         for name in filename:
#             with open(name, 'rb') as f:
# 
#                 reader = csv.reader(f, delimiter=',')
#                 for line, row in enumerate(reader):
#                     if len(row) < 2:
#                         continue
#                     node = row[0]
#                     if node in self._secrets:
#                         raise ValueError("Duplicate node line %d" % line)
#                     secrets = []
#                     for secret in row[1:]:
#                         secret = secret.split(':')
#                         if len(secret) != 2:
#                             raise ValueError("Invalid secret line %d" % line)
#                         secrets.append(tuple(secret))
#                     secrets.sort()
#                     self._secrets[node] = secrets
# 
#     def save(self, filename):
#         with open(filename, 'wb') as f:
#             writer = csv.writer(f, delimiter=',')
#             for node, secrets in list(self._secrets.items()):
#                 secrets = ['%s:%s' % (timestamp, secret)
#                            for timestamp, secret in secrets]
#                 secrets.insert(0, node)
#                 writer.writerow(secrets)
# 
#     def get(self, node):
#         return [secret for timestamp, secret in self._secrets[node]]
# 
#     def add(self, node, size=256):
#         timestamp = str(int(time.time()))
#         secret = binascii.b2a_hex(os.urandom(size))[:size]
#         # The new secret *must* sort at the end of the list.
#         # This forbids you from adding multiple secrets per second.
#         try:
#             if timestamp <= self._secrets[node][-1][0]:
#                 assert False, "You can only add one secret per second"
#         except IndexError:
#             pass
#         self._secrets[node].append((timestamp, secret))
# 
# class FixedSecrets(object):
#     """Use a fixed set of secrets for all nodes.
# 
#     This class provides the same API as the Secrets class, but uses a
#     single list of secrets for all nodes rather than using different
#     secrets for each node.
# 
#     Options:
# 
#     - **secrets**: a list of hex-encoded secrets to use for all nodes.
# 
#     """
#     def __init__(self, secrets):
#         if isinstance(secrets, str):
#             secrets = secrets.split()
#         self._secrets = secrets
# 
#     def get(self, node):
#         return list(self._secrets)
# 
#     def keys(self):
#         return []
# 
# class DerivedSecrets(object):
#     """Use a HKDF-derived secret for each nodes.
# 
#     This class provides the same API as the Secrets class, but rather than
#     keeping a big mapping of node-names to secrets, it uses a single list of
#     master secrets and HKDF-derives a unique secret for each node.
# 
#     Options:
# 
#     - **secrets**: a list of hex-encoded master secrets to use.
# 
#     """
# 
#     # Namespace prefix for HKDF "info" parameter.
#     HKDF_INFO_NODE_SECRET = b"services.mozilla.com/mozsvc/v1/node_secret/"
# 
#     def __init__(self, master_secrets):
#         if isinstance(master_secrets, str):
#             master_secrets = master_secrets.split()
#         self._master_secrets = master_secrets
# 
#     def get(self, node):
#         hkdf_params = {
#             "salt": None,
#             "info": self.HKDF_INFO_NODE_SECRET + node,
#             "hashmod": hashlib.sha256,
#         }
#         node_secrets = []
#         for master_secret in self._master_secrets:
#             # We want each hex-encoded derived secret to be the same
#             # size as its (presumably hex-encoded) master secret.
#             size = len(master_secret) / 2
#             node_secret = HKDF(master_secret, size=size, **hkdf_params)
#             node_secrets.append(binascii.b2a_hex(node_secret))
#         return node_secrets
# 
#     def keys(self):
#         return []


import unittest2
import tempfile
import os
import time
import itertools
from mozsvc.secrets import Secrets, FixedSecrets, DerivedSecrets
                os.remove(file)

    def tempfile(self):
        fd, path = tempfile.mkstemp()
        os.close(fd)
        self._files.append(path)
        return path

    def test_read_write(self):
        secrets = Secrets()

        # We can only add one secret per second to the file, since
        # they are timestamped to 1s resolution.  Fake it.
        real_time = time.time
        time.time = itertools.count(int(real_time())).__next__
        try:
            secrets.add('phx23456')
            secrets.add('phx23456')
            secrets.add('phx23')
        finally:
            time.time = real_time

        phx23456_secrets = secrets.get('phx23456')
        self.assertEqual(len(secrets.get('phx23456')), 2)
        self.assertEqual(len(secrets.get('phx23')), 1)

        path = self.tempfile()

        secrets.save(path)


# Path: mozsvc/secrets.py
# class Secrets(object):
#     """Load node-specific secrets from a file.
# 
#     This class provides a method to get a list of secrets for a node
#     ordered by timestamps. The secrets are stored in a CSV file which
#     is loaded when the object is created.
# 
#     Options:
# 
#     - **filename**: a list of file paths, or a single path.
# 
#     """
#     def __init__(self, filename=None):
#         self._secrets = defaultdict(list)
#         if filename is not None:
#             self.load(filename)
# 
#     def keys(self):
#         return list(self._secrets.keys())
# 
#     def load(self, filename):
#         if not isinstance(filename, (list, tuple)):
#             filename = [filename]
# 
#         for name in filename:
#             with open(name, 'rb') as f:
# 
#                 reader = csv.reader(f, delimiter=',')
#                 for line, row in enumerate(reader):
#                     if len(row) < 2:
#                         continue
#                     node = row[0]
#                     if node in self._secrets:
#                         raise ValueError("Duplicate node line %d" % line)
#                     secrets = []
#                     for secret in row[1:]:
#                         secret = secret.split(':')
#                         if len(secret) != 2:
#                             raise ValueError("Invalid secret line %d" % line)
#                         secrets.append(tuple(secret))
#                     secrets.sort()
#                     self._secrets[node] = secrets
# 
#     def save(self, filename):
#         with open(filename, 'wb') as f:
#             writer = csv.writer(f, delimiter=',')
#             for node, secrets in list(self._secrets.items()):
#                 secrets = ['%s:%s' % (timestamp, secret)
#                            for timestamp, secret in secrets]
#                 secrets.insert(0, node)
#                 writer.writerow(secrets)
# 
#     def get(self, node):
#         return [secret for timestamp, secret in self._secrets[node]]
# 
#     def add(self, node, size=256):
#         timestamp = str(int(time.time()))
#         secret = binascii.b2a_hex(os.urandom(size))[:size]
#         # The new secret *must* sort at the end of the list.
#         # This forbids you from adding multiple secrets per second.
#         try:
#             if timestamp <= self._secrets[node][-1][0]:
#                 assert False, "You can only add one secret per second"
#         except IndexError:
#             pass
#         self._secrets[node].append((timestamp, secret))
# 
# class FixedSecrets(object):
#     """Use a fixed set of secrets for all nodes.
# 
#     This class provides the same API as the Secrets class, but uses a
#     single list of secrets for all nodes rather than using different
#     secrets for each node.
# 
#     Options:
# 
#     - **secrets**: a list of hex-encoded secrets to use for all nodes.
# 
#     """
#     def __init__(self, secrets):
#         if isinstance(secrets, str):
#             secrets = secrets.split()
#         self._secrets = secrets
# 
#     def get(self, node):
#         return list(self._secrets)
# 
#     def keys(self):
#         return []
# 
# class DerivedSecrets(object):
#     """Use a HKDF-derived secret for each nodes.
# 
#     This class provides the same API as the Secrets class, but rather than
#     keeping a big mapping of node-names to secrets, it uses a single list of
#     master secrets and HKDF-derives a unique secret for each node.
# 
#     Options:
# 
#     - **secrets**: a list of hex-encoded master secrets to use.
# 
#     """
# 
#     # Namespace prefix for HKDF "info" parameter.
#     HKDF_INFO_NODE_SECRET = b"services.mozilla.com/mozsvc/v1/node_secret/"
# 
#     def __init__(self, master_secrets):
#         if isinstance(master_secrets, str):
#             master_secrets = master_secrets.split()
#         self._master_secrets = master_secrets
# 
#     def get(self, node):
#         hkdf_params = {
#             "salt": None,
#             "info": self.HKDF_INFO_NODE_SECRET + node,
#             "hashmod": hashlib.sha256,
#         }
#         node_secrets = []
#         for master_secret in self._master_secrets:
#             # We want each hex-encoded derived secret to be the same
#             # size as its (presumably hex-encoded) master secret.
#             size = len(master_secret) / 2
#             node_secret = HKDF(master_secret, size=size, **hkdf_params)
#             node_secrets.append(binascii.b2a_hex(node_secret))
#         return node_secrets
# 
#     def keys(self):
#         return []


# Path: mozsvc/tests/test_secrets.py
import unittest2
import tempfile
import os
import time
import itertools
from mozsvc.secrets import Secrets, FixedSecrets, DerivedSecrets

                os.remove(file)

    def tempfile(self):
        fd, path = tempfile.mkstemp()
        os.close(fd)
        self._files.append(path)
        return path

    def test_read_write(self):
        secrets = Secrets()

        # We can only add one secret per second to the file, since
        # they are timestamped to 1s resolution.  Fake it.
        real_time = time.time
        time.time = itertools.count(int(real_time())).__next__
        try:
            secrets.add('phx23456')
            secrets.add('phx23456')
            secrets.add('phx23')
        finally:
            time.time = real_time

        phx23456_secrets = secrets.get('phx23456')
        self.assertEqual(len(secrets.get('phx23456')), 2)
        self.assertEqual(len(secrets.get('phx23')), 1)

        path = self.tempfile()

        secrets.save(path)


        secrets2 = Secrets(path)