Skip to content

Commit

Permalink
Add sync with Serverauditior cloud module.
Browse files Browse the repository at this point in the history
Improve serializers and add cryptor usage.

Also, add tests for saving models.

Fix storing data and remote_instances fields.

Add context manager for storage.

Fix storage logic.
Now terminal entries stored in lists not i dictionaries.

Move password prompt to mixin.

Add taghosts model.
  • Loading branch information
EvgeneOskin committed Jul 25, 2015
1 parent db6bfd6 commit e7e5173
Show file tree
Hide file tree
Showing 34 changed files with 599 additions and 540 deletions.
8 changes: 8 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[run]
branch = True
source = serverauditor_sshconfig
include = *.py

[report]
precision = 2
show_missing = True
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
.DS_Store

.idea
.tox
.coverage

*.pyc
*.pyo
Expand Down
2 changes: 2 additions & 0 deletions .noserc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[nosetests]
with-coverage=1
16 changes: 16 additions & 0 deletions .prospector.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
inherits:
- strictness_veryhigh
- full_pep8
- doc_warnings

ignore-paths:
- .git

pylint:
options:
max-parents: 12
disable:
# - broad-except
# - pointless-except
# - bad-super-call
# - nonstandard-exception
20 changes: 11 additions & 9 deletions serverauditor_sshconfig/account/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,20 @@
from .managers import AccountManager


class LoginCommand(AbstractCommand):
class BaseAccountCommand(AbstractCommand):

def __init__(self, app, app_args, cmd_name=None):
super(BaseAccountCommand, self).__init__(app, app_args, cmd_name)
self.manager = AccountManager(self.config)


class LoginCommand(BaseAccountCommand):

"""Sign into serverauditor cloud."""

def prompt_username(self):
return six.moves.input("Serverauditor's username: ")

def prompt_password(self):
return getpass("Serverauditor's password: ")

def get_parser(self, prog_name):
parser = super(LoginCommand, self).get_parser(prog_name)
parser.add_argument('-u', '--username', metavar='USERNAME')
Expand All @@ -27,14 +31,13 @@ def get_parser(self, prog_name):
return parser

def take_action(self, parsed_args):
manager = AccountManager(self.app.NAME)
username = parsed_args.username or self.prompt_username()
password = parsed_args.password or self.prompt_password()
manager.login(username, password)
self.manager.login(username, password)
self.log.info('Sign into serverauditor cloud.')


class LogoutCommand(AbstractCommand):
class LogoutCommand(BaseAccountCommand):

"""Sign out serverauditor cloud."""

Expand All @@ -44,6 +47,5 @@ def get_parser(self, prog_name):
return parser

def take_action(self, parsed_args):
manager = AccountManager(self.app.NAME)
manager.logout()
self.manager.logout()
self.log.info('Sign out serverauditor cloud.')
5 changes: 2 additions & 3 deletions serverauditor_sshconfig/account/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
License BSD, see LICENSE for more details.
"""

from ..core.settings import Config
from ..core.api import API


class AccountManager(object):

def __init__(self, application_name):
self.config = Config(application_name)
def __init__(self, config):
self.config = config
self.api = API()

def login(self, username, password):
Expand Down
Empty file.
50 changes: 50 additions & 0 deletions serverauditor_sshconfig/cloud/commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from base64 import b64decode
from ..core.commands import AbstractCommand
from .controllers import ApiController
from .cryptor import RNCryptor


class PushCommand(AbstractCommand):

"""Push data to Serverauditor cloud."""

def get_parser(self, prog_name):
parser = super(PushCommand, self).get_parser(prog_name)
parser.add_argument(
'-s', '--silent', action='store_true',
help='Do not produce any interactions.'
)
parser.add_argument(
'-S', '--strategy', metavar='STRATEGY_NAME',
help='Force to use specific strategy to merge data.'
)
return parser

def take_action(self, parsed_args):
self.log.info('Push data to Serverauditor cloud.')


class PullCommand(AbstractCommand):

"""Pull data from Serverauditor cloud."""

def get_parser(self, prog_name):
parser = super(PullCommand, self).get_parser(prog_name)
parser.add_argument(
'-s', '--strategy', metavar='STRATEGY_NAME',
help='Force to use specific strategy to merge data.'
)
return parser

def take_action(self, parsed_args):
encryption_salt = b64decode(self.config.get('User', 'salt'))
hmac_salt = b64decode(self.config.get('User', 'hmac_salt'))
password = self.prompt_password()
cryptor = RNCryptor()
cryptor.password = password
cryptor.encryption_salt = encryption_salt
cryptor.hmac_salt = hmac_salt
controller = ApiController(self.storage, self.config, cryptor)
with self.storage:
controller.get_bulk()
self.log.info('Pull data from Serverauditor cloud.')
78 changes: 78 additions & 0 deletions serverauditor_sshconfig/cloud/controllers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from .serializers import BulkSerializer
from ..core.api import API


class CryptoController(object):

def __init__(self, cryptor):
self.cryptor = cryptor

def _mutate_fields(self, model, mutator):
for i in model.crypto_fields:
crypto_field = getattr(model, i)
if crypto_field:
setattr(model, i, mutator(crypto_field))
return model

def encrypt(self, model):
return self._mutate_fields(model, self.cryptor.encrypt)

def decrypt(self, model):
return self._mutate_fields(model, self.cryptor.decrypt)


class ApiController(object):

mapping = dict(
bulk=dict(url='v2/terminal/bulk/', serializer=BulkSerializer)
)

def __init__(self, storage, config, cryptor):
self.config = config
username = self.config.get('User', 'username')
apikey = self.config.get('User', 'apikey')
assert username
assert apikey
self.api = API(username, apikey)
self.storage = storage
self.crypto_controller = CryptoController(cryptor)

def _get(self, mapped):
serializer = mapped['serializer'](
storage=self.storage, crypto_controller=self.crypto_controller
)
response = self.api.get(mapped['url'])

model = serializer.to_model(response)
return model

def get_bulk(self):
mapped = self.mapping['bulk']
model = self._get(mapped)
self.config.set('CloudSynchronization', 'last_synced',
model['last_synced'])
self.config.write()

def _post(self, mapped, request_model):
request_model = request_model
serializer = mapped['serializer'](
storage=self.storage, crypto_controller=self.crypto_controller
)

payload = serializer.to_payload(request_model)
response = self.api.post(mapped['url'], payload)

response_model = serializer.to_models(response)
return response_model

def post_bulk(self):
mapped = self.mapping['bulk']
model = {}
model['last_synced'] = self.config.get(
'CloudSynchronization', 'last_synced'
)
assert model['last_synced']
out_model = self._post(mapped, model)
self.config.set('CloudSynchronization', 'last_synced',
out_model['last_synced'])
self.config.write()
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from Crypto.Protocol import KDF
from Crypto import Random

from .utils import bchr, bord, to_bytes, to_str
from ..core.utils import bchr, bord, to_bytes, to_str


class CryptorException(Exception):
Expand Down Expand Up @@ -161,34 +161,3 @@ def _pbkdf2(self, password, salt, iterations=10000, key_length=32):
## passlib version -- the fastest version
# from passlib.utils.pbkdf2 import pbkdf2
# return pbkdf2(password, salt, iterations, key_length)


def main():
from time import time

cryptor = RNCryptor()
cryptor.encryption_salt = b'1' * 8
cryptor.hmac_salt = b'1' * 8

passwords = 'p@s$VV0Rd', 'пароль'
texts = 'www.crystalnix.com', 'текст', '', '1' * 16, '2' * 15, '3' * 17

for password in passwords:
cryptor.password = password
for text in texts:
print('text: "{}"'.format(text))

s = time()
encrypted_data = cryptor.encrypt(text)
print('encrypted {}: "{}"'.format(time() - s, encrypted_data))

s = time()
decrypted_data = cryptor.decrypt(encrypted_data)
print('decrypted {}: "{}"\n'.format(time() - s, decrypted_data))

assert text == decrypted_data


if __name__ == '__main__':

main()
Empty file.
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ class Tag(Model):

fields = {'label'}
set_name = 'tag_set'
crypto_fields = fields


class SshKey(Model):

fields = {'label', 'passphrase', 'private_key', 'public_key'}
set_name = 'sshkeycrypt_set'
crypto_fields = fields


class SshIdentity(Model):
Expand All @@ -20,7 +22,7 @@ class SshIdentity(Model):
mapping = {
'ssh_key': Mapping(SshKey, many=False),
}

crypto_fields = {'label', 'username', 'password'}

class SshConfig(Model):

Expand All @@ -38,18 +40,42 @@ class Group(Model):
mapping = {
'ssh_config': Mapping(SshConfig, many=False),
}
crypto_fields = {'label',}


Group.mapping['parent_group'] = Mapping(Group, many=False)


class Host(Model):

fields = {'label', 'address', 'group', 'tags', 'address', 'ssh_config'}
fields = {'label', 'group', # 'tags',
'address', 'ssh_config'}
set_name = 'host_set'
mapping = {
'ssh_config': Mapping(SshConfig, many=False),
'tags': Mapping(Tag, many=True),
# 'tags': Mapping(Tag, many=True),
}
crypto_fields = {'label', 'address'}


class Host(Model):

fields = {'label', 'group', 'address', 'ssh_config'}
set_name = 'host_set'
mapping = {
'ssh_config': Mapping(SshConfig, many=False),
# 'tags': Mapping(Tag, many=True),
}
crypto_fields = {'label', 'address'}


class TagHost(Model):

fields = {'host', 'tag'}
set_name = 'taghost_set'
mapping = {
'host': Mapping(Host, many=False),
'tag': Mapping(Tag, many=False),
}


Expand All @@ -61,3 +87,4 @@ class PFRule(Model):
mapping = {
'host': Mapping(Host, many=False),
}
crypto_fields = {'label', 'bound_address', 'hostname'}
Empty file.
Loading

0 comments on commit e7e5173

Please sign in to comment.