Skip to content

Commit

Permalink
Native import export (#5)
Browse files Browse the repository at this point in the history
* Create new methods to facilitate adding new import/export formats

* Native import/export

* Texts
  • Loading branch information
gabfl authored Jun 27, 2017
1 parent 5ccd8cd commit e4687a3
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 99 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ python3 vault.py
```
usage: vault.py [-h] [-t [CLIPBOARD_TTL]] [-p [HIDE_SECRET_TTL]]
[-a [AUTO_LOCK_TTL]] [-v VAULT_LOCATION] [-c CONFIG_LOCATION]
[-k] [-i IMPORT_ITEMS] [-x EXPORT] [-e]
[-k] [-i IMPORT_ITEMS] [-x EXPORT] [-f {json,native}] [-e]
optional arguments:
-h, --help show this help message and exit
Expand All @@ -57,5 +57,7 @@ optional arguments:
File to import credentials from
-x EXPORT, --export EXPORT
File to export credentials to
-f {json,native}, --file_format {json,native}
Import/export file format (default: 'json')
-e, --erase_vault Erase the vault and config file
```
5 changes: 5 additions & 0 deletions docs/export.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

Exporting items is easy. You just have to use the flag `--export`.

## Security notice

For obvious reasons, **export files are not encrypted**, all the vault secrets will be stored in clear.
Make sure to safely store the export file and securely dispose of it after its use.

## Usage

```
Expand Down
60 changes: 21 additions & 39 deletions docs/rebuild.md
Original file line number Diff line number Diff line change
@@ -1,61 +1,43 @@
# Vault: rebuild the vault

If for some reason you wanted to completely rebuild your vault (for example to make bulk changes), here is how to proceed.
If for some reason you wanted to completely rebuild your vault, here is how to proceed.

## Rebuilding the vault
## Why rebuild the vault?

Process to rebuild the vault:
Here are some use cases tor rebuild the vault:

- Export all secrets
- Delete the vault
- Create a new vault and import the secrets
- Rebuilding the vault is the only solution to change the configuration file salt
- Make bulk changes

## How to proceed

### Export all secrets
```
./vault.py --export all.json
# ...type master key
```
## Rebuilding the vault

### Write down the list of categories
Process to rebuild the vault:

Open the vault and take note of all categories and their IDs.
At this time, categories cannot be exported and imported automatically.
- Export the vault to a native file format
- Import the native file to the new vault

```
./vault.py
# ...type master key
# ...type 'cat'
# ...write down all categories
```
## Security notice

### Delete the vault and configuration file
For obvious reasons, **export files are not encrypted**, all the vault secrets will be stored in clear.
Make sure to safely store the export file and securely dispose of it after its use.

```
./vault.py --erase_vault
# ...type 'y' to confirm
```
## How to proceed

### Setup a new vault
### Backup the vault and configuration file

```
./vault.py
# Choose a new master key and confirm
# and unlock the vault
```
Importing and exporting is experimental, please backup the vault and configuration file before proceeding.

### Re-create the categories
### Export the vault

```
# ...type 'cat'
# ...re-create the categories (make sure they have the same IDs)
./vault.py --export vault.native --file_format native
# ...type master key
```

### Re-import all the secrets
### Import the vault

```
./vault.py --import_items all.json
./vault.py --import_items vault.native --file_format native
# ...type master key
# ...review list and type `y` to confirm import
# ...confirm
```
212 changes: 170 additions & 42 deletions lib/ImportExport.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,71 @@

import os, sys, json

"""
Adding import or export formats:
To add an import or export format, you need to add a file format name to
`self.importItems()` or `self.export()` and create an associated method
called `importFrom[SomeFormat]()` or `exportTo[SomeFormat]()`.
The format name must also be added to `../vault.py` in argparse choices.
The easiest solution to create the import and/or export method would be to duplicate
`self.importFromJson()` or `self.exportToJson()` as they are fairly standard.
If you create a format that can be useful to others, please fork the project
first and submit a merge request!
"""

class ImportExport:

vault = None; # Vault instance
vault = None # Vault instance
fileFormat = None # File format (default: 'json')
path = None # Import or export path

def __init__(self, vault):
def __init__(self, vault, path, fileFormat = 'json'):
self.vault = vault
self.path = path
self.fileFormat = fileFormat

def importItems(self):
"""
Routing to format specific import methods
"""

if self.fileFormat == 'json':
self.importFromJson()
elif self.fileFormat == 'native':
self.importFromNative()
else:
raise ValueError('%s is not a supported file format' % (self.fileFormat))

def importItems(self, path):
def export(self):
"""
Routing to format specific export methods
"""

if self.fileFormat == 'json':
self.exportToJson()
elif self.fileFormat == 'native':
self.exportToNative()
else:
raise ValueError('%s is not a supported file format' % (self.fileFormat))

def importFromJson(self):
"""
Format: json
Import items to the vault
"""

from lib.Misc import confirm

# Unlock the vault with the existing key
self.vault.unlock(False) # `False` = don't load menu after unlocking
self.unlockVault()
print()

# Read import file
try:
file = open(path)
fileContent = file.read()
file.close()
except Exception as e:
print("The file `%s` could not be opened." % (path));
print(e)
sys.exit()
fileContent = self.readFile()

# Decode json
items = json.loads(fileContent)
Expand Down Expand Up @@ -74,40 +112,130 @@ def importItems(self, path):

sys.exit()

def export(self, path):
def importFromNative(self):
"""
Export the vault content to a specific file
Format: native
Replace the vault content with a native format import file
"""

import pickle

from lib.Misc import confirm

# Unlock the vault with the existing key
self.vault.unlock(False) # `False` = don't load menu after unlocking
self.unlockVault()

# If we have a valid vault
if self.vault.vault.get('secrets'):
# Iterate thru the items
output = []
for item in self.vault.vault['secrets']:
# Add to output
output.append({
'category': item['category'],
'categoryName': self.vault.categoryName(item['category']),
'name': item['name'],
'login': item['login'],
'password': item['password'],
'notes': item['notes']
});

# Save to file
try:
print("The vault has been exported to the file `%s`." % (path));

file = open(path, 'w')
file.write(json.dumps(output))
file.close()
except Exception as e:
print("The vault could not be exported to the file `%s`." % (path));
print(e)
else:
print("There are no secrets in the vault.");
# Get import file content
content = self.readFile('rb')

# Unpickle the content
content = pickle.loads(content)

print()
if confirm('Importing from a native format will erase and replace the vault content. Continue?', False):
# Replace and save vault content
self.vault.vault = content
self.vault.saveVault()

print("The vault has been imported.")

sys.exit()

def exportToJson(self):
"""
Format: json
Export the vault content to a file
"""

# Unlock the vault with the existing key
self.unlockVault()

# Check if the vault has some content
self.checkEmptyVault()

# Iterate thru the items
output = []
for item in self.vault.getVault()['secrets']:
# Add to output
output.append({
'category': item['category'],
'categoryName': self.vault.categoryName(item['category']),
'name': item['name'],
'login': item['login'],
'password': item['password'],
'notes': item['notes']
});

self.saveFile(json.dumps(output))

sys.exit()

def exportToNative(self):
"""
Format: native
Export the vault content to a file
"""

import pickle

# Unlock the vault with the existing key
self.unlockVault()

# Check if the vault has some content
self.checkEmptyVault()

# Pickle the vault using the highest protocol available.
output = pickle.dumps(self.vault.getVault(), pickle.HIGHEST_PROTOCOL)

# Save to file
self.saveFile(output, 'wb')

sys.exit()

def readFile(self, mode = 'r'):
"""
Read an import file and return its content
"""

# Read import file
try:
file = open(self.path, mode = mode)
fileContent = file.read()
file.close()

return fileContent
except Exception as e:
print("The file `%s` could not be opened." % (self.path));
print(e)
sys.exit()

def saveFile(self, content, mode = 'w'):
"""
Save exported items to a file
"""

# Save to file
try:
file = open(self.path, mode)
file.write(content)
file.close()

print("The vault has been exported to the file `%s`." % (self.path))
except Exception as e:
print("The vault could not be exported to the file `%s`." % (self.path))
print(e)

def unlockVault(self):
"""
Ask user to unlock the vault
"""

self.vault.unlock(False) # `False` = don't load menu after unlocking

def checkEmptyVault(self):
"""
Will raise an error if the user tries to export an empty vault
"""

if not self.vault.getVault().get('secrets'):
raise ValueError('There are no secrets in the vault stored at `%s`.' % (self.path))
15 changes: 11 additions & 4 deletions lib/Vault.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ def get(self, id = None):

from lib.Misc import confirm

if id is None: # If the user did not pre-selected an item
if id is None: # If the user did not pre-select an item
print()
try:
id = input('Enter item number: ')
Expand Down Expand Up @@ -479,10 +479,10 @@ def search(self):
search.upper() in item['login'].upper() or \
search.upper() in item['notes'].upper() or \
search.upper() in self.categoryName(item['category']).upper():
# Increment quickKey
# Increment search result item number
searchResultItemNumber += 1

# Set quickKey value
# Set search result value
searchResultItems[searchResultItemNumber] = i

# Add item to search results
Expand Down Expand Up @@ -522,7 +522,7 @@ def searchResultSelection(self, searchResultItems):
print()
self.menu()

# Try getting an item of send back to the main mane
# Try getting an item or send user back to the main menu
try:
# Get item ID
id = searchResultItems[int(resultItem)]
Expand Down Expand Up @@ -887,3 +887,10 @@ def changeKey(self):
print()
# Try again
self.changeKey();

def getVault(self):
"""
Returns the vault content
"""

return self.vault;
Loading

0 comments on commit e4687a3

Please sign in to comment.