예제 #1
0
 def _test_create_installs(self, datadir):
     """
     This is a bootstrap for one-time use creating lockers to be tested
     so we know nothing in our encryption has changed
     """
     for crypt_id in list_crypts():
         data_root = datadir['.']
         new_loc = Path(data_root / crypt_id)
         new_loc.mkdir()
         conf = ConfigModel(store_path=new_loc, editor='vim')
         write_config_file(new_loc, conf)
         new_lock = Locker.create(self.test_name, self.test_pw, crypt_id)
         for name, pt in plain_texts.items():
             ni = new_lock.create_item(name)
             ni.content = (
                 f"{pt}\nsite:www.zombo.com\npassword: YouCanD0NEthing!")
             new_lock.add_item(ni)
         # after lockers are stored, stub out the store_path
         conf._store_path = Path("/")
         conf.store = {
             'store_type': StoreType.FileSystem.name,
             'store_path': "/"
         }
         write_config_file(new_loc,
                           conf,
                           update=True,
                           bypass_validation=True)
예제 #2
0
 def test_multiple_cycles(self):
     """
     Ciphers can only be used once - CryptImpl is supposed to
     have a workaround that refreshes the cipher after each use
     :return:
     """
     crypt = create_crypt(self.pw)
     for tt in plain_texts:
         ct = crypt.encrypt(tt)
         pt = crypt.decrypt(ct)
         assert pt == tt
     for crypt_id in list_crypts():
         crypt = create_crypt(self.pw, crypt_id)
         crypt_id = crypt.crypt_id
         pw_hash = crypt.pw_hash
         salt = crypt.salt
         for tt in plain_texts:
             ct = crypt.encrypt(tt)
             pt = crypt.decrypt(ct)
             assert pt == tt
         crypt = get_crypt(crypt_id, self.pw, pw_hash, salt)
         for tt in plain_texts:
             ct = crypt.encrypt(tt)
             pt = crypt.decrypt(ct)
             assert pt == tt
예제 #3
0
 def custom_setup(self, tmp_path):
     super(EmptyLocker, self).custom_setup(tmp_path)
     for name in self.lockers:
         try:
             Locker.delete(name, self.password)
         except PhibesNotFoundError:
             pass
     self.lockers = {}
     try:
         Locker.delete(self.locker_name, self.password)
     except PhibesNotFoundError:
         pass
     finally:
         self.my_locker = Locker.create(
             password=self.password,
             crypt_id=crypto.default_id,
             locker_name=self.locker_name
         )
         # create a locker for each registered crypt instance
         for crypt_id in crypto.list_crypts():
             # dedupe the names with random numbers
             wart = str(random.randint(1000, 9999))
             # but make sure there isn't a freak collision
             while self.locker_name + str(wart) in self.lockers:
                 wart = str(random.randint(1000, 9999))
             locker_name = self.locker_name + wart
             self.lockers[locker_name] = Locker.create(
                 password=self.password,
                 crypt_id=crypt_id,
                 locker_name=locker_name
             )
     return
예제 #4
0
 def test_create_encrypt_decrypt(self, plaintext):
     for crypt_id in list_crypts():
         crypt = create_crypt(self.pw, crypt_id)
         step1 = crypt.encrypt(plaintext)
         step2 = crypt.decrypt(step1)
         assert step2 == plaintext, (f"{crypt=}\n"
                                     f"{plaintext=}\n"
                                     f"{step1=}\n"
                                     f"{step2=}\n")
예제 #5
0
 def test_validate_installs(self, datadir):
     # Get the list of install directories under `tests/data`
     data_root = datadir['.']
     installs = data_root.listdir()
     # Each directory is named for a crypt_id, representing an installation
     # Each installation has its own .phibes.cfg and one test locker
     installed_paths = set()
     crypt_dirs = set()
     for install in installs:
         installed_paths.add(Path(install))
         crypt_dirs.add(install.basename)
     # The directory listing should exactly match registered crypt_ids
     warnings = ""
     for item in set(list_crypts()) - crypt_dirs:
         warnings += f"Registered crypt {item} missing test install\n"
     for item in crypt_dirs - set(list_crypts()):
         warnings += f"No crypt registered matching test install {item}\n"
     assert not warnings
     for pt in installed_paths:
         validate_install(pt, self.test_name, self.test_pw)
예제 #6
0
 def custom_setup(self, tmp_path):
     super(TestDeleteLocker, self).custom_setup(tmp_path)
     try:
         Locker.delete(password=self.pw, locker_name=self.locker_name)
     except PhibesNotFoundError:
         pass
     crypt_id = crypto.list_crypts()[0]
     Locker.create(
         password=self.pw, crypt_id=crypt_id, locker_name=self.locker_name
     )
     self.setup_command()
예제 #7
0
class TestNoName(ConfigLoadingTestClass, MixinItemsList):

    password = "******"
    test_item_name = 'gonna_getchall'
    test_content = f"here is some stuff\npassword: HardHat\nsome name\n"

    def custom_setup(self, tmp_path):
        super(TestNoName, self).custom_setup(tmp_path)
        self.setup_command()

    def custom_teardown(self, tmp_path):
        super(TestNoName, self).custom_teardown(tmp_path)

    def invoke(self, arg_dict: dict):
        """
        Helper method for often repeated code in test methods
        :return: click test-runner result
        """
        args = [
            "--password", arg_dict.get('password', self.password),
            "--path", arg_dict.get('path', self.test_path),
            "--verbose", True
        ]
        return CliRunner().invoke(cli=self.target_cmd, args=args)

    def prep_and_run(self, arg_dict):
        self.my_locker = Locker.create(
            password=self.password,
            crypt_id=arg_dict['crypt_id']
        )
        new_item = self.my_locker.create_item(
            item_name=self.test_item_name
        )
        new_item.content = self.test_content
        self.my_locker.add_item(item=new_item)
        update_config_option_default(self.target_cmd, self.test_path)
        return self.invoke(arg_dict=arg_dict)

    @pytest.mark.parametrize("crypt_id", list_crypts())
    @pytest.mark.positive
    def test_found(self, crypt_id, setup_and_teardown):
        result = self.prep_and_run({'crypt_id': crypt_id})
        assert result
        assert result.exit_code == 0, (
            f"{crypt_id=}\n"
            f"{result.exception=}\n"
            f"{result.output=}\n"
        )
        for part in [self.test_content, self.test_item_name]:
            assert part in result.output, (
                f"{crypt_id=}\n"
                f"{result.exception=}\n"
                f"{result.output=}\n"
            )
예제 #8
0
 def setup_method(self):
     self.pw = "s00p3rsekrit"
     self.crypts = {}
     for cid in list_crypts():
         crypt_impl = create_crypt(self.pw, crypt_id=cid)
         self.crypts[cid] = {
             'password': self.pw,
             'pw_hash': crypt_impl.pw_hash,
             'salt': crypt_impl.salt,
             'crypt_impl': crypt_impl
         }
     return
예제 #9
0
class TestNoName(ConfigLoadingTestClass, MixinItemDelete):

    password = "******"
    test_item_name = 'gonna_deletecha'
    test_content = f"here is some stuff\npassword: HardHat\nsome name\n"

    def custom_setup(self, tmp_path):
        super(TestNoName, self).custom_setup(tmp_path)
        self.setup_command()

    def custom_teardown(self, tmp_path):
        super(TestNoName, self).custom_teardown(tmp_path)

    def invoke(self, arg_dict: dict):
        """
        Helper method for often repeated code in test methods
        :return: click test-runner result
        """
        args = [
            "--password",
            arg_dict.get('password', self.password), "--path",
            arg_dict.get('path', self.test_path), "--item",
            arg_dict.get('item', self.test_item_name)
        ]
        return CliRunner().invoke(self.target_cmd, args)

    def prep_and_run(self, arg_dict):
        self.my_locker = Locker.create(password=self.password,
                                       crypt_id=arg_dict['crypt_id'])
        new_item = self.my_locker.create_item(item_name=self.test_item_name)
        new_item.content = self.test_content
        self.my_locker.add_item(item=new_item)
        # change the configured working path to the test directory
        update_config_option_default(self.target_cmd, self.test_path)
        return self.invoke(arg_dict=arg_dict)

    @pytest.mark.parametrize("crypt_id", list_crypts())
    @pytest.mark.positive
    def test_success(self, crypt_id, setup_and_teardown):
        result = self.prep_and_run({'crypt_id': crypt_id})
        assert result
        assert result.exit_code == 0, (f"{crypt_id=}\n"
                                       f"{result.exception=}\n"
                                       f"{result.output=}\n")
        with pytest.raises(PhibesNotFoundError):
            self.my_locker.get_item(self.test_item_name)
예제 #10
0
class TestNoName(ConfigLoadingTestClass, MixinLockerGet):

    password = "******"

    def custom_setup(self, tmp_path):
        super(TestNoName, self).custom_setup(tmp_path)
        self.setup_command()

    def custom_teardown(self, tmp_path):
        super(TestNoName, self).custom_teardown(tmp_path)

    def prep_and_run(self, arg_dict):
        self.my_locker = Locker.create(
            password=self.password,
            crypt_id=arg_dict['crypt_id'],
            locker_name=None
        )
        # change the configured working path to the test directory
        update_config_option_default(self.target_cmd, self.test_path)
        arg_list = [
            "--path", arg_dict.get('path', self.test_path),
            "--password", arg_dict.get('password', self.password)
        ]
        return CliRunner().invoke(cli=self.target_cmd, args=arg_list)

    @pytest.mark.parametrize("crypt_id", list_crypts())
    @pytest.mark.positive
    def test_found(self, crypt_id, setup_and_teardown):
        result = self.prep_and_run({'crypt_id': crypt_id})
        assert result
        assert result.exit_code == 0, (
            f"{crypt_id=}\n"
            f"{result.exception=}\n"
            f"{result.output=}\n"
        )
        assert f'Crypt ID {crypt_id}' in result.output
        inst = Locker.get(password=self.password, locker_name=None)
        assert (
                inst.data_model.storage.locker_file ==
                self.test_path / LOCKER_FILE
        )
예제 #11
0
# Related third party imports
import pytest

# Local application/library specific imports
from phibes import crypto
from phibes.lib.errors import PhibesAuthError
from phibes.lib.errors import PhibesExistsError
from phibes.lib.errors import PhibesNotFoundError
from phibes.model import Locker

# Local test imports
from tests.lib.test_helpers import ConfigLoadingTestClass
from tests.lib.test_helpers import EmptyLocker, plain_texts, PopulatedLocker


crypt_list = crypto.list_crypts()


class TestLocker(EmptyLocker):

    my_locker = None
    locker_name = "my_locker"
    password = "******"

    def custom_setup(self, tmp_path):
        super(TestLocker, self).custom_setup(tmp_path)
        try:
            if Locker.get(
                    password=self.password, locker_name=self.locker_name
            ):
                Locker.delete(
예제 #12
0
from phibes.crypto import list_crypts
from phibes.lib.config import ConfigModel
from phibes.lib.errors import PhibesAuthError
from phibes.model import Locker, LOCKER_FILE

# Local test imports
from tests.cli.click_test_helpers import GroupProvider
from tests.cli.click_test_helpers import update_config_option_default
from tests.lib.test_helpers import ConfigLoadingTestClass
from tests.lib.test_helpers import PopulatedLocker


params = "crypt_id,config_arg"
include_config_arg = [False, True]
matrix_params = []
for element in itertools.product(list_crypts(), include_config_arg):
    matrix_params.append(element)


class MixinLockerGet(GroupProvider):

    target = Target.Locker
    action = Action.Get


class TestNoName(ConfigLoadingTestClass, MixinLockerGet):

    password = "******"

    def custom_setup(self, tmp_path):
        super(TestNoName, self).custom_setup(tmp_path)
예제 #13
0
class TestNoName(ConfigLoadingTestClass, MixinItemEdit):

    password = "******"
    test_item_name = 'gonna_editecha'
    start_content = f"replace this\n"
    edit_content = f"unique"

    def custom_setup(self, tmp_path):
        super(TestNoName, self).custom_setup(tmp_path)
        self.setup_command()

    def custom_teardown(self, tmp_path):
        super(TestNoName, self).custom_teardown(tmp_path)

    def invoke(self, arg_dict: dict):
        """
        Helper method for often repeated code in test methods
        :return: click test-runner result
        """
        args = [
            "--password",
            arg_dict.get('password', self.password), "--path",
            arg_dict.get('path', self.test_path), "--item",
            arg_dict.get('item', self.test_item_name)
        ]
        if 'editor' in arg_dict:
            args += ["--editor", arg_dict['editor']]
        return CliRunner().invoke(self.target_cmd, args)

    def prep_and_run(self, arg_dict):
        return self.invoke(arg_dict=arg_dict)

    @pytest.mark.parametrize("crypt_id", list_crypts())
    @pytest.mark.positive
    def test_success(self, crypt_id, tmp_path, setup_and_teardown):
        self.my_locker = Locker.create(password=self.password,
                                       crypt_id=crypt_id)
        new_item = self.my_locker.create_item(item_name=self.test_item_name)
        new_item.content = self.start_content
        self.my_locker.add_item(item=new_item)
        conf = CliConfig()
        conf.store = {
            'store_type': conf.store['store_type'],
            'store_path': tmp_path
        }
        conf.editor = f'echo {self.edit_content}> '
        write_config_file(tmp_path, update=True)
        load_config_file(tmp_path)
        # change the configured working path to the test directory
        update_config_option_default(self.target_cmd, self.test_path)
        result = self.prep_and_run({'crypt_id': crypt_id})
        assert result
        assert result.exit_code == 0, (f"{crypt_id=}\n"
                                       f"{result.exception=}\n"
                                       f"{result.output=}\n")
        assert self.start_content not in result.output
        inst = self.my_locker.get_item(self.test_item_name)
        assert self.edit_content == inst.content.strip()

    @pytest.mark.parametrize("crypt_id", list_crypts())
    @pytest.mark.positive
    def test_editor_option(self, crypt_id, tmp_path, setup_and_teardown):
        self.my_locker = Locker.create(password=self.password,
                                       crypt_id=crypt_id)
        new_item = self.my_locker.create_item(item_name=self.test_item_name)
        new_item.content = self.start_content
        self.my_locker.add_item(item=new_item)
        conf = CliConfig()
        conf.store = {
            'store_type': conf.store['store_type'],
            'store_path': tmp_path
        }
        conf.editor = f'echo {self.edit_content}> '
        write_config_file(tmp_path, update=True)
        load_config_file(tmp_path)
        # change the configured working path to the test directory
        update_config_option_default(self.target_cmd, self.test_path)
        replacement_content = "emacswhatwhat"
        cli_editor_option = f"echo {replacement_content}> "
        result = self.prep_and_run({
            'crypt_id': crypt_id,
            'editor': cli_editor_option
        })
        assert result
        assert result.exit_code == 0, (f"{crypt_id=}\n"
                                       f"{result.exception=}\n"
                                       f"{result.output=}\n")
        assert self.start_content not in result.output
        assert self.edit_content not in result.output
        inst = self.my_locker.get_item(self.test_item_name)
        assert replacement_content == inst.content.strip()
예제 #14
0
    if len(value) < 3:
        raise click.BadParameter("password must be a least 3 characters")
    return value


def validate_item_name(ctx: click.Context, param: click.Option, value: str):
    """
    Custom validation rule for item_name_option
    """
    # Possibly enforce max length, character set, or something
    return value


crypt_choices = MappedChoices(
    prompt='Crypt ID (accept default)\n',
    choices=list_crypts(),
    default_val=default_id,
    help="Encryption type, usually best to accept default")
crypt_option = crypt_choices.get_click_option("--crypt_id")
item_name_option = click.option(
    '--item',
    prompt='Item name',
    callback=validate_item_name,
    help='Name of item on which to perform this action',
)
template_name_option = click.option(
    '--template',
    prompt='Template name',
    help='Name of item to start with, can also be a text file',
    default='Empty')
locker_name_option = click.option(