Add CLI passphrase generation

1. Add support to pegleg to generate a passphrase from CLI
2. Update unit test to ensure encryption/decryption supports passphrase rotation
3. Update order of import statements to satisfy pep8
4. Add unit test for CLI passphrase generation
5. Resolve merge conflicts via rebase

Change-Id: I5cb9e41b2f0fac2451bd2b74f33c48cda417c22d
This commit is contained in:
Alexander Hughes 2019-01-02 12:10:35 -06:00
parent e6af6ae87e
commit 4b00a4340c
7 changed files with 86 additions and 8 deletions

View File

@ -809,3 +809,40 @@ P003 - All repos contain expected directories.
.. _Shipyard: https://github.com/openstack/airship-shipyard
.. _CLI documentation: https://airship-shipyard.readthedocs.io/en/latest/CLI.html#openstack-keystone-authorization-environment-variables
.. _Pegleg Passphrase Catalog: https://airship-specs.readthedocs.io/en/latest/specs/approved/pegleg-secrets.html#document-generation
Generate
========
Allows you to perform generate operations.
Passphrase
----------
Generate a passphrase and print to ``stdout``.
**-l / --length** (Optional).
Length of passphrase to generate. By default length is 24.
Minimum length is 24. No maximum length.
Usage:
::
./pegleg.sh generate passphrase -l <length>
Examples
^^^^^^^^
Example without length specified:
::
./pegleg.sh generate passphrase
Example with length specified:
::
./pegleg.sh generate passphrase -l <length>

View File

@ -528,9 +528,25 @@ def encrypt(*, save_location, author, site_name):
@click.argument('site_name')
def decrypt(*, file_name, site_name):
engine.repository.process_repositories(site_name)
try:
click.echo(engine.secrets.decrypt(file_name, site_name))
except FileNotFoundError:
raise click.exceptions.FileError("Couldn't find file %s, "
"check your arguments and try "
"again." % file_name)
engine.secrets.decrypt(file_name, site_name)
@main.group(help="Miscellaneous generate commands")
def generate():
pass
@generate.command(
'passphrase',
help='Command to generate a passphrase and print out to stdout')
@click.option(
'-l',
'--length',
'length',
default=24,
help='Generate a passphrase of the given length. '
'Length is >= 24, default length is 24, no maximum length')
def generate_passphrase(length):
click.echo("Generated Passhprase: {}".format(
engine.secrets.generate_passphrase(length)))

View File

@ -15,9 +15,10 @@
import logging
import os
from pegleg.engine.generators.passpharase_generator import PassphraseGenerator
from pegleg.engine.generators.passphrase_generator import PassphraseGenerator
from pegleg.engine.util import definition
from pegleg.engine.util import files
from pegleg.engine.util.passphrase import Passphrase
from pegleg.engine.util.pegleg_secret_management import PeglegSecretManagement
__all__ = ('encrypt', 'decrypt', 'generate_passphrases')
@ -129,3 +130,14 @@ def generate_passphrases(site_name, save_location, author, interactive=False):
PassphraseGenerator(site_name, save_location, author).generate(
interactive=interactive)
def generate_passphrase(length):
"""
Create a passphrase.
:param int length: Length of passphrase.
:rtype: string
"""
return Passphrase().get_pass(length)

View File

@ -20,7 +20,7 @@ import string
import yaml
from pegleg.engine.util.passphrase import Passphrase
from pegleg.engine.generators.passpharase_generator import PassphraseGenerator
from pegleg.engine.generators.passphrase_generator import PassphraseGenerator
from pegleg.engine.util import encryption
from pegleg.engine import util
import pegleg

View File

@ -64,6 +64,12 @@ def test_encrypt_and_decrypt():
enc2 = crypt.encrypt(dec1, passphrase, salt)
dec2 = crypt.decrypt(enc2, passphrase, salt)
assert data == dec2
passphrase2 = test_utils.rand_name("passphrase2", "pegleg").encode()
salt2 = test_utils.rand_name("salt2", "pegleg").encode()
enc3 = crypt.encrypt(dec2, passphrase2, salt2)
dec3 = crypt.decrypt(enc3, passphrase2, salt2)
assert data == dec3
assert data != enc3
@mock.patch.dict(os.environ, {

View File

@ -383,6 +383,13 @@ class TestSiteCliActions(BaseCLIActionTest):
mock_obj.assert_called_once()
class TestGenerateActions(BaseCLIActionTest):
def test_generate_passphrase(self):
result = self.runner.invoke(cli.generate, ['passphrase'])
assert result.exit_code == 0, result.output
class TestRepoCliActions(BaseCLIActionTest):
"""Tests repo-level CLI actions."""