forked from fugue/credstash
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow for alternative storage services via the entry point `credsmash…
….storage_service`.
- Loading branch information
1 parent
cfc7a2f
commit 081ae66
Showing
14 changed files
with
266 additions
and
210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,20 @@ | ||
from __future__ import absolute_import, division, print_function, unicode_literals | ||
|
||
import logging | ||
from boto3.dynamodb.conditions import Attr | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def delete_secret(secrets_table, secret_name): | ||
response = secrets_table.scan( | ||
FilterExpression=Attr("name").eq(secret_name), | ||
ProjectionExpression="#N, version", | ||
ExpressionAttributeNames={"#N": "name"} | ||
) | ||
if response['Count'] == 0: | ||
def delete_secret(storage_service, secret_name): | ||
secrets = storage_service.list_one(secret_name) | ||
if not secrets: | ||
logger.info('Not found: %s', secret_name) | ||
return | ||
|
||
for secret in response["Items"]: | ||
for secret in secrets: | ||
logger.info("Deleting %s -- version %s", | ||
secret["name"], secret["version"]) | ||
secrets_table.delete_item(Key=secret) | ||
storage_service.delete_one( | ||
secret["name"], secret["version"] | ||
) | ||
logger.info('Deleted %s', secret_name) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,11 @@ | ||
from __future__ import absolute_import, division, print_function, unicode_literals | ||
|
||
from boto3.dynamodb.types import Binary | ||
from boto3.dynamodb.conditions import Key as ConditionKey | ||
from credsmash.util import ItemNotFound, padded_int | ||
from credsmash.crypto import open_secret | ||
from credsmash.util import ItemNotFound | ||
|
||
|
||
def get_secret(secrets_table, key_service, secret_name, version=None): | ||
if version is None: | ||
ciphertext = get_latest_secret(secrets_table, secret_name) | ||
else: | ||
ciphertext = get_versioned_secret(secrets_table, secret_name, version) | ||
|
||
def get_secret(storage_service, key_service, secret_name, version=None): | ||
ciphertext = storage_service.get_one(secret_name, version=version) | ||
if not ciphertext: | ||
raise ItemNotFound("Item {'name': '%s', 'version': %s} couldn't be found." % (secret_name, version)) | ||
return open_secret(key_service, ciphertext) | ||
|
||
|
||
def _unwrap_dynamodb_types(obj): | ||
return { | ||
k: (v.value if isinstance(v, Binary) else v) | ||
for k, v in obj.items() | ||
} | ||
|
||
|
||
def get_latest_secret(secrets_table, secret_name): | ||
# do a consistent fetch of the credential with the highest version | ||
response = secrets_table.query( | ||
Limit=1, | ||
ScanIndexForward=False, | ||
ConsistentRead=True, | ||
KeyConditionExpression=ConditionKey("name").eq(secret_name) | ||
) | ||
if response["Count"] == 0: | ||
raise ItemNotFound("Item {'name': '%s'} couldn't be found." % secret_name) | ||
return _unwrap_dynamodb_types(response["Items"][0]) | ||
|
||
|
||
def get_versioned_secret(secrets_table, secret_name, version): | ||
version = padded_int(version) | ||
response = secrets_table.get_item(Key={"name": secret_name, "version": version}) | ||
if "Item" not in response: | ||
raise ItemNotFound( | ||
"Item {'name': '%s', 'version': '%s'} couldn't be found." % (secret_name, version)) | ||
return _unwrap_dynamodb_types(response["Item"]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,5 @@ | ||
from __future__ import absolute_import, division, print_function, unicode_literals | ||
|
||
|
||
def list_secrets(secrets_table): | ||
response = secrets_table.scan( | ||
ProjectionExpression="#N, version", | ||
ExpressionAttributeNames={"#N": "name"} | ||
) | ||
return response["Items"] | ||
def list_secrets(storage_service): | ||
return storage_service.list_all() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,27 @@ | ||
from __future__ import absolute_import, division, print_function, unicode_literals | ||
|
||
import logging | ||
from boto3.dynamodb.conditions import Attr | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def prune_secret(secrets_table, secret_name): | ||
response = secrets_table.scan( | ||
FilterExpression=Attr("name").eq(secret_name), | ||
ProjectionExpression="#N, version", | ||
ExpressionAttributeNames={"#N": "name"} | ||
) | ||
if response['Count'] == 0: | ||
def prune_secret(storage_service, secret_name): | ||
secrets = storage_service.list_one(secret_name) | ||
if not secrets: | ||
logger.info('Not found: %s', secret_name) | ||
return | ||
|
||
max_version = max( | ||
int(secret['version']) | ||
for secret in response['Items'] | ||
secret['version'] | ||
for secret in secrets | ||
) | ||
|
||
for secret in response["Items"]: | ||
if int(secret['version']) == max_version: | ||
for secret in secrets: | ||
if secret['version'] == max_version: | ||
continue | ||
logger.info("Deleting %s -- version %s", | ||
secret["name"], secret["version"]) | ||
secrets_table.delete_item(Key=secret) | ||
storage_service.delete_one( | ||
secret['name'], secret['version'] | ||
) | ||
logger.info('Pruned %s (current version=%d)', secret_name, max_version) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,26 @@ | ||
from __future__ import absolute_import, division, print_function, unicode_literals | ||
|
||
from boto3.dynamodb.conditions import Attr, Key as ConditionKey | ||
from boto3.dynamodb.types import Binary | ||
|
||
from credsmash.crypto import seal_secret, ALGO_AES_CTR | ||
from credsmash.util import padded_int | ||
|
||
|
||
def put_secret( | ||
secrets_table, key_service, secret_name, | ||
storage_service, key_service, secret_name, | ||
plaintext, version=None, algorithm=ALGO_AES_CTR, **seal_kwargs | ||
): | ||
sealed = seal_secret( | ||
key_service, | ||
plaintext, | ||
algorithm=algorithm, | ||
binary_type=Binary, | ||
binary_type=storage_service.binary_type, | ||
**seal_kwargs | ||
) | ||
|
||
if version is None: | ||
version = 1 + get_highest_version( | ||
secrets_table, secret_name | ||
) | ||
latest_secret = storage_service.get_one(secret_name) | ||
version = 1 | ||
if latest_secret: | ||
version += latest_secret['version'] | ||
|
||
data = { | ||
'name': secret_name, | ||
'version': padded_int(version), | ||
} | ||
data.update(sealed) | ||
secrets_table.put_item(Item=data, ConditionExpression=Attr('name').not_exists()) | ||
storage_service.put_one(secret_name, version, sealed) | ||
return version | ||
|
||
|
||
def get_highest_version(secrets_table, secret_name): | ||
response = secrets_table.query( | ||
Limit=1, | ||
ScanIndexForward=False, | ||
ConsistentRead=True, | ||
KeyConditionExpression=ConditionKey("name").eq(secret_name), | ||
ProjectionExpression="version" | ||
) | ||
|
||
if response["Count"] == 0: | ||
return 0 | ||
try: | ||
return int(response["Items"][0]["version"], 10) | ||
except ValueError: | ||
raise RuntimeError('Could not parse current version: %s' % response["Items"][0]["version"]) |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.