Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add stash init examples #136

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,35 @@ Commands:
ssh Use a key to SSH-connect to a machine
unlock Unlock a key

----- (Optional) -----

# Initializing a stash with a tutorial
$ ghost init --tutorial
Stash: tinydb at ~/.ghost/ghost.json
Initializing stash...
Initialized stash at: ~/.ghost/ghost.json
Your passphrase can be found under the `passphrase.ghost` file in the current directory.
Make sure you save your passphrase somewhere safe. If lost, you will lose access to your stash.
TUTORIAL:
For your convenience we've stored an example key named 'example'. Usually this is done with 'ghost put example first_key=first_value [second_key=second_value [...]]'. You may retrieve it with 'ghost get example'. You may also delete it with 'ghost delete example'.

$ export GHOST_PASSPHRASE=$(cat passphrase.ghost)

$ ghost get example
Stash: tinydb at ~/.ghost/ghost.json
Retrieving key...

Description: This is the key description
Lock: False
Created_At: 2017-05-29 13:49:56
Modified_At: 2017-05-29 13:49:56
Value: key=value;
Uid: f7e236f7-450c-400b-9328-95e18d5fac52
Metadata: some_key=some_value;
Type: secret
Name: example

----- (Optional) -----

# Initializing a stash
$ ghost init
Expand Down
30 changes: 26 additions & 4 deletions ghost.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@

PASSPHRASE_FILENAME = 'passphrase.ghost'

EXAMPLE_KEY = {
'name': 'example',
'value': {'key': 'value'},
'metadata': {'some_key': 'some_value'},
'description': 'This is the key description'
}

POTENTIAL_PASSPHRASE_LOCATIONS = [
os.path.abspath(PASSPHRASE_FILENAME),
os.path.join(GHOST_HOME, PASSPHRASE_FILENAME),
Expand Down Expand Up @@ -185,7 +192,8 @@ def init(self):
@property
def is_initialized(self):
if self._storage.is_initialized:
self.passphrase = get_passphrase(self.passphrase)
if not self.passphrase:
self.passphrase = get_passphrase(self.passphrase)
if self.get('stored_passphrase'):
return True
return False
Expand Down Expand Up @@ -1263,7 +1271,6 @@ def main():


@main.command(name='init', short_help='Initialize a stash')
@click.argument('STASH_PATH', required=False, type=click.STRING)
@click.option('-p',
'--passphrase',
default=None,
Expand All @@ -1275,7 +1282,14 @@ def main():
default='tinydb',
type=click.Choice(STORAGE_MAPPING.keys()),
help='Storage backend for the stash')
def init_stash(stash_path, passphrase, passphrase_size, backend):
@click.option('-t',
'--tutorial',
is_flag=True,
help='Whether to create example key')
@stash_option
@passphrase_option
@backend_option
def init_stash(stash, passphrase, passphrase_size, backend, tutorial):
r"""Init a stash

`STASH_PATH` is the path to the storage endpoint. If this isn't supplied,
Expand All @@ -1293,7 +1307,7 @@ def init_stash(stash_path, passphrase, passphrase_size, backend):

export GHOST_BACKEND='tinydb'
"""
stash_path = stash_path or STORAGE_DEFAULT_PATH_MAPPING[backend]
stash_path = stash or STORAGE_DEFAULT_PATH_MAPPING[backend]
click.echo('Stash: {0} at {1}'.format(backend, stash_path))
storage = STORAGE_MAPPING[backend](**_parse_path_string(stash_path))

Expand Down Expand Up @@ -1338,6 +1352,14 @@ def init_stash(stash_path, passphrase, passphrase_size, backend):
'Make sure you save your passphrase somewhere safe. '
'If lost, you will lose access to your stash.')

if tutorial:
stash.put(**EXAMPLE_KEY)
click.echo("TUTORIAL: \nFor your convenience we've stored an example "
"key named 'example'. Usually this is done with 'ghost put "
"example first_key=first_value [second_key=second_value "
"[...]]'. You may retrieve it with 'ghost get example'. You"
" may also delete it with 'ghost delete example'.")


@main.command(name='put', short_help='Insert a new key')
@click.argument('KEY_NAME')
Expand Down
66 changes: 54 additions & 12 deletions tests/test_ghost.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ def _invoke(command):
return cfy.invoke(getattr(ghost, func), params)


def _make_temp_passphrase_file():
fd, temp_file_path = tempfile.mkstemp()
os.close(fd)
os.remove(temp_file_path)
return temp_file_path


class TestGeneral:
def test_get_current_time(self):
assert len(ghost._get_current_time()) == 19
Expand Down Expand Up @@ -110,12 +117,6 @@ def test_prettify_list_input_not_list(self):
ghost._prettify_list('')

def test_get_passphrase(self):
def _make_temp_passphrase_file():
fd, temp_file_path = tempfile.mkstemp()
os.close(fd)
os.remove(temp_file_path)
return temp_file_path

tempfile1 = _make_temp_passphrase_file()
tempfile2 = _make_temp_passphrase_file()

Expand Down Expand Up @@ -995,6 +996,13 @@ def assert_stash_initialized(stash_path):
assert len(db) == 1


def assert_stash_initialized_tutorial(stash_path):
db = get_tinydb(stash_path)
assert '2' in db
assert db['2']['name'] == 'example'
assert len(db) == 2


def assert_key_put(db, dont_verify_value=False):
key = db['2']
assert key['name'] == 'aws'
Expand Down Expand Up @@ -1326,6 +1334,36 @@ def test_list_locked_filtered_matches(self, test_stash):
assert len(result) == 1
assert 'aws-2' in result

def test_is_initialized_passphrase_overridden(self):
prev_dir = os.getcwd()
stash_dir = tempfile.mkdtemp()
os.chdir(stash_dir)
stash_path = os.path.join(stash_dir, 'stash.json')
try:
tempfile1 = _make_temp_passphrase_file()

passphrase = '123'
ghost.POTENTIAL_PASSPHRASE_LOCATIONS = [tempfile1]
with open(ghost.POTENTIAL_PASSPHRASE_LOCATIONS[0], 'w') \
as passphrase_file:
passphrase_file.write(passphrase)

storage = ghost.TinyDBStorage(stash_path)
stash = ghost.Stash(storage, 'some_passphrase')
stash.init()
stash.is_initialized
assert stash.passphrase == 'some_passphrase'
stash.passphrase = None
try:
stash.is_initialized
except ghost.GhostError:
pass
assert stash.passphrase == passphrase
finally:
os.remove(ghost.POTENTIAL_PASSPHRASE_LOCATIONS[0])
os.chdir(prev_dir)
shutil.rmtree(stash_dir, ignore_errors=True)


def _create_migration_env(test_stash, temp_file_path):
test_stash.put('aws', {'a': 'b'})
Expand Down Expand Up @@ -1360,7 +1398,7 @@ def test_cli_stash(stash_path):
os.close(fd)
os.remove(passphrase_file_path)
ghost.PASSPHRASE_FILENAME = passphrase_file_path
_invoke('init_stash "{0}"'.format(stash_path))
_invoke('init_stash -s "{0}"'.format(stash_path))
os.environ['GHOST_STASH_PATH'] = stash_path
with open(passphrase_file_path) as passphrase_file:
passphrase = passphrase_file.read()
Expand Down Expand Up @@ -1404,7 +1442,7 @@ def test_init(self, test_cli_stash):
assert_stash_initialized(test_cli_stash._storage.db_path)

def test_init_already_initialized(self, test_cli_stash):
result = _invoke('init_stash "{0}" -p {1}'.format(
result = _invoke('init_stash -s "{0}" -p {1}'.format(
os.environ['GHOST_STASH_PATH'], test_cli_stash.passphrase))
assert 'Stash already initialized' in result.output
assert result.exit_code == 0
Expand All @@ -1415,7 +1453,7 @@ def test_init_permission_denied_on_passphrase(self):
fd, temp_file = tempfile.mkstemp()
os.close(fd)
os.remove(temp_file)
result = _invoke('init_stash "{0}" -p whatever'.format(temp_file))
result = _invoke('init_stash -s "{0}" -p whatever'.format(temp_file))
assert 'Expected OSError' in str(result.exception)
assert 'Removing stale stash and passphrase' in str(result.output)
assert type(result.exception) == SystemExit
Expand All @@ -1428,7 +1466,7 @@ def test_init_permission_denied_on_passphrase(self):

@pytest.mark.skipif(os.name == 'nt', reason='Irrelevant on Windows')
def test_init_permission_denied_on_stash(self, test_cli_stash):
result = _invoke('init_stash "/x" -p {0}'.format(
result = _invoke('init_stash -s "/x" -p {0}'.format(
test_cli_stash.passphrase))
assert 'Permission denied' in str(result.exception)
assert type(result.exception) == SystemExit
Expand Down Expand Up @@ -1642,8 +1680,8 @@ def test_load(self, test_cli_stash, temp_file_path):
def test_fail_init_two_stashes_passphrase_file_exists(self,
stash_path,
temp_file_path):
_invoke('init_stash "{0}"'.format(stash_path))
result = _invoke('init_stash "{0}" -b sqlalchemy'.format(
_invoke('init_stash -s "{0}"'.format(stash_path))
result = _invoke('init_stash -s "{0}" -b sqlalchemy'.format(
temp_file_path))

assert 'Overwriting might prevent you' in result.output
Expand Down Expand Up @@ -1787,6 +1825,10 @@ def test_ssh_with_key_path_and_extension(self, test_cli_stash):
'ssh -i /path/to/key [email protected] -o Key="Value"'
assert expected_command in str(result.exception)

def test_tutorial(self, stash_path):
_invoke('init_stash -s "{0}" -t'.format(stash_path))
assert_stash_initialized_tutorial(stash_path)


class TestMultiStash:
# TODO: Test that migrate works when using multi-stash mode
Expand Down