Browse Source

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
Alexander Hughes 3 months ago
parent
commit
4b00a4340c

+ 37
- 0
doc/source/cli/cli.rst View File

@@ -809,3 +809,40 @@ P003 - All repos contain expected directories.
809 809
 .. _Shipyard: https://github.com/openstack/airship-shipyard
810 810
 .. _CLI documentation: https://airship-shipyard.readthedocs.io/en/latest/CLI.html#openstack-keystone-authorization-environment-variables
811 811
 .. _Pegleg Passphrase Catalog: https://airship-specs.readthedocs.io/en/latest/specs/approved/pegleg-secrets.html#document-generation
812
+
813
+
814
+Generate
815
+========
816
+
817
+Allows you to perform generate operations.
818
+
819
+Passphrase
820
+----------
821
+
822
+Generate a passphrase and print to ``stdout``.
823
+
824
+**-l / --length** (Optional).
825
+
826
+Length of passphrase to generate.  By default length is 24.
827
+Minimum length is 24.  No maximum length.
828
+
829
+Usage:
830
+
831
+::
832
+
833
+    ./pegleg.sh generate passphrase -l <length>
834
+
835
+Examples
836
+^^^^^^^^
837
+
838
+Example without length specified:
839
+
840
+::
841
+
842
+    ./pegleg.sh generate passphrase
843
+
844
+Example with length specified:
845
+
846
+::
847
+
848
+    ./pegleg.sh generate passphrase -l <length>

+ 22
- 6
pegleg/cli.py View File

@@ -528,9 +528,25 @@ def encrypt(*, save_location, author, site_name):
528 528
 @click.argument('site_name')
529 529
 def decrypt(*, file_name, site_name):
530 530
     engine.repository.process_repositories(site_name)
531
-    try:
532
-        click.echo(engine.secrets.decrypt(file_name, site_name))
533
-    except FileNotFoundError:
534
-        raise click.exceptions.FileError("Couldn't find file %s, "
535
-                                         "check your arguments and try "
536
-                                         "again." % file_name)
531
+
532
+    engine.secrets.decrypt(file_name, site_name)
533
+
534
+
535
+@main.group(help="Miscellaneous generate commands")
536
+def generate():
537
+    pass
538
+
539
+
540
+@generate.command(
541
+    'passphrase',
542
+    help='Command to generate a passphrase and print out to stdout')
543
+@click.option(
544
+    '-l',
545
+    '--length',
546
+    'length',
547
+    default=24,
548
+    help='Generate a passphrase of the given length. '
549
+         'Length is >= 24, default length is 24, no maximum length')
550
+def generate_passphrase(length):
551
+    click.echo("Generated Passhprase: {}".format(
552
+        engine.secrets.generate_passphrase(length)))

pegleg/engine/generators/passpharase_generator.py → pegleg/engine/generators/passphrase_generator.py View File


+ 13
- 1
pegleg/engine/secrets.py View File

@@ -15,9 +15,10 @@
15 15
 import logging
16 16
 import os
17 17
 
18
-from pegleg.engine.generators.passpharase_generator import PassphraseGenerator
18
+from pegleg.engine.generators.passphrase_generator import PassphraseGenerator
19 19
 from pegleg.engine.util import definition
20 20
 from pegleg.engine.util import files
21
+from pegleg.engine.util.passphrase import Passphrase
21 22
 from pegleg.engine.util.pegleg_secret_management import PeglegSecretManagement
22 23
 
23 24
 __all__ = ('encrypt', 'decrypt', 'generate_passphrases')
@@ -129,3 +130,14 @@ def generate_passphrases(site_name, save_location, author, interactive=False):
129 130
 
130 131
     PassphraseGenerator(site_name, save_location, author).generate(
131 132
         interactive=interactive)
133
+
134
+
135
+def generate_passphrase(length):
136
+    """
137
+    Create a passphrase.
138
+
139
+    :param int length: Length of passphrase.
140
+    :rtype: string
141
+    """
142
+
143
+    return Passphrase().get_pass(length)

+ 1
- 1
tests/unit/engine/test_generate_passphrases.py View File

@@ -20,7 +20,7 @@ import string
20 20
 import yaml
21 21
 
22 22
 from pegleg.engine.util.passphrase import Passphrase
23
-from pegleg.engine.generators.passpharase_generator import PassphraseGenerator
23
+from pegleg.engine.generators.passphrase_generator import PassphraseGenerator
24 24
 from pegleg.engine.util import encryption
25 25
 from pegleg.engine import util
26 26
 import pegleg

+ 6
- 0
tests/unit/engine/test_secrets.py View File

@@ -64,6 +64,12 @@ def test_encrypt_and_decrypt():
64 64
     enc2 = crypt.encrypt(dec1, passphrase, salt)
65 65
     dec2 = crypt.decrypt(enc2, passphrase, salt)
66 66
     assert data == dec2
67
+    passphrase2 = test_utils.rand_name("passphrase2", "pegleg").encode()
68
+    salt2 = test_utils.rand_name("salt2", "pegleg").encode()
69
+    enc3 = crypt.encrypt(dec2, passphrase2, salt2)
70
+    dec3 = crypt.decrypt(enc3, passphrase2, salt2)
71
+    assert data == dec3
72
+    assert data != enc3
67 73
 
68 74
 
69 75
 @mock.patch.dict(os.environ, {

+ 7
- 0
tests/unit/test_cli.py View File

@@ -383,6 +383,13 @@ class TestSiteCliActions(BaseCLIActionTest):
383 383
         mock_obj.assert_called_once()
384 384
 
385 385
 
386
+class TestGenerateActions(BaseCLIActionTest):
387
+    def test_generate_passphrase(self):
388
+        result = self.runner.invoke(cli.generate, ['passphrase'])
389
+
390
+        assert result.exit_code == 0, result.output
391
+
392
+
386 393
 class TestRepoCliActions(BaseCLIActionTest):
387 394
     """Tests repo-level CLI actions."""
388 395
 

Loading…
Cancel
Save