Esempio n. 1
0
    def __init__(self,
                 session,
                 vault,
                 vaultkey,
                 region,
                 prefix,
                 project,
                 env='default'):
        self.client = session.client('s3')
        self.bucket = vault
        self.vault = Kms(session=session, key=vaultkey)
        self.vaultkey = vaultkey
        self.region = region
        self.prefix = prefix
        self.project = project
        self.env = env + '/'

        if self.prefix:
            self.prefix += '/'
Esempio n. 2
0
 def __init__(self, session, vault, vaultkey, project, env='default'):
     self.client = session.client('s3')
     self.bucket = vault
     self.vaultkey = vaultkey
     self.vault = Kms(session=session, key=vaultkey)
     self.project = project
     self.prefix = self.project.load().get('project', '')
     self.env = env + '/'
     if self.prefix:
         self.prefix += '/'
Esempio n. 3
0
    def __init__(self, session, vault, vaultkey, region, prefix, project, env='default'):
        self.client = session.client('s3')
        self.bucket = vault
        self.vault = Kms(session=session, key=vaultkey)
        self.vaultkey = vaultkey
        self.region = region
        self.prefix = prefix
        self.project = project
        self.env = env + '/'

        if self.prefix:
            self.prefix += '/'
Esempio n. 4
0
class S3(Storage):
    """
    A single bucket holds multiple projects.
    """
    def __init__(self,
                 session,
                 vault,
                 vaultkey,
                 region,
                 prefix,
                 project,
                 env='default'):
        self.client = session.client('s3')
        self.bucket = vault
        self.vault = Kms(session=session, key=vaultkey)
        self.vaultkey = vaultkey
        self.region = region
        self.prefix = prefix
        self.project = project
        self.env = env + '/'

        if self.prefix:
            self.prefix += '/'

    @asyncio.coroutine
    def create_bucket(self, name=None, **kw):
        # administrator creates bucket for projects
        name = name or self.bucket
        result = self.client.create_bucket(Bucket=name)
        raise Return(result)

    @asyncio.coroutine
    def setup(self, vault, project, **kw):
        # project-user creates a folder inside bucket
        if not all([vault, project]):
            sys.exit("Error! Vault and/or project undefined")
        project_s3 = project.rstrip('/') + '/'
        try:
            result = yield From(self.get(project_s3, in_group_check=False))
            res = "Error! A project with this name already exists"
        except ClientError as e:
            result = yield From(self.put(project_s3, '', prefixify=False))
            self.project.save(
                dict(vault=vault,
                     project=project,
                     key=self.vaultkey,
                     region=self.region))
            res = "Success! Project created. Configuration stored in %s" % self.project.name
        raise Return(res)

    @asyncio.coroutine
    def list_backend(self, prefix=None, **kw):
        prefix = prefix or self.prefixify('')
        raise Return(
            self.client.list_objects(Bucket=self.bucket,
                                     Prefix=prefix,
                                     MaxKeys=1000,
                                     Delimiter=kw.get('delimiter',
                                                      BOTO_DEFAULT)))

    @asyncio.coroutine
    def list(self, key=None, **kw):
        key = self.prefixify(key)
        response = yield From(self.list_backend(prefix=key, **kw))
        raise Return([
            self.prefixify(k['Key'], reverse=True)
            for k in response.get('Contents', [])
        ])

    @asyncio.coroutine
    def config(self, key=None, **kw):
        contents = yield From(self.list_backend(prefix=self.prefixify(key)))
        contents = contents.get('Contents', [])
        kw.setdefault('in_group_check', True)
        tasks = [self.get(key=obj['Key'], **kw) for obj in contents]
        try:
            results = yield From(asyncio.gather(*tasks))
        except ValueError as e:
            raise Return({})
        keys = [k['Key'].split('/')[-1] for k in contents]
        raise Return(OrderedDict(zip(keys, results)))

    @asyncio.coroutine
    def put_backend(self, **kw):
        raise Return(self.client.put_object(**kw))

    @asyncio.coroutine
    def put(self, key, value, **kw):
        if value is None:
            raise Return("Error! No value provided.")

        if kw.get('prefixify', True):
            key = self.prefixify(key)
        is_file = False
        is_binary = False  # TODO: --binary
        mode = 'rb'

        # TODO --encode flag to encode value
        if os.path.isfile(value):
            kwargs = dict(mode=mode)
            if PY3:
                kwargs = dict(mode=mode, encoding=ENCODING)
            value = codecs.open(os.path.expandvars(os.path.expanduser(value)),
                                **kwargs).read().rstrip('\n')
            is_file = True

        if PY3:
            value = value.encode(ENCODING)

        # boto expects bytestrings
        data = self.vault.encrypt(value, is_file=is_file, is_binary=is_binary)
        data['name'] = key
        result = yield From(
            self.put_backend(Bucket=self.bucket,
                             Key=key,
                             Body=json.dumps(data)))
        if result.get('ResponseMetadata').get('HTTPStatusCode') == 200:
            raise Return("Success! Wrote: %s" % key)
        else:
            raise Return(result)

    @asyncio.coroutine
    def get_backend(self, **kw):
        raise Return(self.client.get_object(**kw))

    @asyncio.coroutine
    def get(self, key, **kw):
        if key is None:
            raise Return("Error! No key provided.")

        key = self.prefixify(key)
        extra = {}
        if kw.get('version'):
            extra['VersionId'] = kw.get('version')
        try:
            result = yield From(
                self.get_backend(Bucket=self.bucket, Key=key, **extra))
        except Exception as e:
            if kw.get('in_group_check', True):
                # check for grouped keys
                key = key.rstrip('/') + '/'
                kw['in_group_check'] = True
                result = yield From(self.config(key=key, **kw))
                if not result:
                    result = "Error! The specified key does not exist."
                raise Return(result)
            raise
        body = json.loads(result['Body'].read().decode(ENCODING))
        data = self.vault.decrypt(body)
        is_binary = body.get('is_binary', False)
        if not is_binary:
            data = data.decode(ENCODING)
        raise Return(data)

    @asyncio.coroutine
    def delete_backend(self, **kw):
        raise Return(self.client.delete_object(**kw))

    @asyncio.coroutine
    def delete(self, key, **kw):
        key = self.prefixify(key)
        result = yield From(self.delete_backend(Bucket=self.bucket, Key=key))
        if result.get('ResponseMetadata').get('HTTPStatusCode') == 204:
            raise Return("Success! Deleted: %s" % key)
        else:
            raise Return(result)

    @asyncio.coroutine
    def versions(self, **kw):
        prefix = kw.get('prefix', self.prefix)
        response = self.client.list_object_versions(Bucket=self.bucket,
                                                    Prefix=self.prefix)
        versions = []
        for k in response.get('Versions', []):
            key = self.prefixify(k['Key'], reverse=True)
            if kw.get('key') and kw.get('key') != key: continue
            versions.append(
                dict(key=key,
                     version=k['VersionId'],
                     is_latest=k['IsLatest'],
                     modified=k['LastModified']))
        raise Return(versions)

    @asyncio.coroutine
    def envs(self, **kw):
        contents = yield From(
            self.list_backend(prefix=self.prefix, delimiter='/'))
        raise Return(
            list(k['Prefix'].split('/')[1]
                 for k in contents.get('CommonPrefixes', [])))

    def prefixify(self, key, reverse=False):
        if reverse:
            if self.prefix and self.env:
                key = key.replace('{}{}'.format(self.prefix, self.env), '')
        elif key is not None:
            if self.env:
                if self.prefix not in key and self.env not in key:
                    key = '{}{}{}'.format(self.prefix, self.env, key)
            else:
                if self.prefix not in key:
                    key = '{}{}'.format(self.prefix, key)
        key = key or ''
        key = key.replace('//', '/')
        return key
Esempio n. 5
0
class S3(Storage):
    """
    A single bucket holds multiple projects.
    """
    def __init__(self, session, vault, vaultkey, region, prefix, project, env='default'):
        self.client = session.client('s3')
        self.bucket = vault
        self.vault = Kms(session=session, key=vaultkey)
        self.vaultkey = vaultkey
        self.region = region
        self.prefix = prefix
        self.project = project
        self.env = env + '/'

        if self.prefix:
            self.prefix += '/'

    @asyncio.coroutine
    def create_bucket(self, name=None, **kw):
        # administrator creates bucket for projects
        name = name or self.bucket
        result = self.client.create_bucket(Bucket=name)
        raise Return(result)

    @asyncio.coroutine
    def setup(self, vault, project, **kw):
        # project-user creates a folder inside bucket
        if not all([vault, project]):
            sys.exit("Error! Vault and/or project undefined")
        project_s3 = project.rstrip('/')+'/'
        try:
            result = yield From(self.get(project_s3, in_group_check=False))
            res = "Error! A project with this name already exists"
        except ClientError as e:
            result = yield From(self.put(project_s3, '', prefixify=False))
            self.project.save(dict(vault=vault, project=project, key=self.vaultkey, region=self.region))
            res = "Success! Project created. Configuration stored in %s"%self.project.name
        raise Return(res)

    @asyncio.coroutine
    def list_backend(self, prefix=None, **kw):
        prefix = prefix or self.prefixify('')
        raise Return(self.client.list_objects(Bucket=self.bucket, Prefix=prefix, MaxKeys=1000, Delimiter=kw.get('delimiter', BOTO_DEFAULT)))

    @asyncio.coroutine
    def list(self, key=None, **kw):
        key = self.prefixify(key)
        response = yield From(self.list_backend(prefix=key, **kw))
        raise Return([self.prefixify(k['Key'], reverse=True) for k in response.get('Contents', [])])

    @asyncio.coroutine
    def config(self, key=None, **kw):
        args = prepare()
        contents = yield From(self.list_backend(prefix=self.prefixify(key)))
        contents = contents.get('Contents', [])
        kw.setdefault('in_group_check', False)
        kw.setdefault('no_files', True)
        tasks = [self._get(key=obj['Key'], **kw) for obj in contents]
        try:
            results = yield From(asyncio.gather(*tasks))
        except ValueError as e:
            raise Return({})
        result = OrderedDict()
        project_env = "{}/{}".format(args.project, args.env)
        for k, v in enumerate(results):
            if kw.get('in_group_check'): # for grouped keys strip prefixes
                key_ = contents[k]['Key'].split('/')[-1]
            else: # otherwise full key
                key_ = contents[k]['Key'].replace(project_env, '').lstrip('/')
            if args.skip_files and v['is_file']:
                continue
            result[key_] = v['data']
        raise Return(result)

    @asyncio.coroutine
    def put_backend(self, **kw):
        raise Return(self.client.put_object(**kw))

    @asyncio.coroutine
    def put(self, key, value, **kw):
        if value is None:
            raise Return("Error! No value provided.")

        if kw.get('prefixify', True):
            key = self.prefixify(key)
        is_file = False
        is_binary = False # TODO: --binary
        mode = 'rb'

        # TODO --encode flag to encode value
        if os.path.isfile(value):
            kwargs = dict(mode=mode)
            if PY3:
                kwargs = dict(mode=mode, encoding=ENCODING)
            value = codecs.open(os.path.expandvars(os.path.expanduser(value)), **kwargs).read().rstrip('\n')
            is_file = True

        if PY3:
            value = value.encode(ENCODING)

        # boto expects bytestrings
        data = self.vault.encrypt(value, is_file=is_file, is_binary=is_binary)
        data['name'] = key
        result = yield From(self.put_backend(Bucket=self.bucket, Key=key, Body=json.dumps(data)))
        if result.get('ResponseMetadata').get('HTTPStatusCode') == 200:
            raise Return("Success! Wrote: %s"%key)
        else:
            raise Return(result)

    @asyncio.coroutine
    def get_backend(self, **kw):
        raise Return(self.client.get_object(**kw))

    @asyncio.coroutine
    def _get(self, key, **kw):
        if key is None:
            raise Return("Error! No key provided.")

        key = self.prefixify(key)
        extra = {}
        if kw.get('version'):
            extra['VersionId'] = kw.get('version')
        result = yield From(self.get_backend(Bucket=self.bucket, Key=key, **extra))
        body = json.loads(result['Body'].read().decode(ENCODING))
        data = self.vault.decrypt(body)
        is_binary = body.get('is_binary', False)
        if not is_binary:
            data = data.decode(ENCODING)

        if kw.get('no_files') and body.get('is_file'):
            data = "<FILE>"
        body["data"] = data
        raise Return(body)

    @asyncio.coroutine
    def get(self, key, **kw):
        try:
            data = yield From(self._get(key, **kw))
        except Exception as e:
            if kw.get('in_group_check', True):
                # check for grouped keys
                key = key.rstrip('/')+'/'
                kw['in_group_check'] = True
                result = yield From(self.config(key=key, **kw))
                if not result:
                    result = "Error! The specified key does not exist."
                raise Return(result)
            raise
        raise Return(data["data"])

    @asyncio.coroutine
    def delete_backend(self, **kw):
        raise Return(self.client.delete_object(**kw))

    @asyncio.coroutine
    def delete(self, key, **kw):
        key = self.prefixify(key)
        result = yield From(self.delete_backend(Bucket=self.bucket, Key=key))
        if result.get('ResponseMetadata').get('HTTPStatusCode') == 204:
            raise Return("Success! Deleted: %s"%key)
        else:
            raise Return(result)

    @asyncio.coroutine
    def versions(self, **kw):
        prefix = kw.get('prefix', self.prefix)
        response = self.client.list_object_versions(Bucket=self.bucket, Prefix=self.prefix)
        versions = []
        for k in response.get('Versions', []):
            key = self.prefixify(k['Key'], reverse=True)
            if kw.get('key') and kw.get('key')!=key: continue
            versions.append(dict(key=key, version=k['VersionId'], is_latest=k['IsLatest'], modified=k['LastModified']))
        raise Return(versions)

    @asyncio.coroutine
    def envs(self, **kw):
        contents = yield From(self.list_backend(prefix=self.prefix, delimiter='/'))
        raise Return(list(k['Prefix'].split('/')[1] for k in contents.get('CommonPrefixes', [])))

    def prefixify(self, key, reverse=False):
        if reverse:
            if self.prefix and self.env:
                key = key.replace('{}{}'.format(self.prefix, self.env), '')
        elif key is not None:
            if self.env:
                if self.prefix not in key and self.env not in key:
                    key = '{}{}{}'.format(self.prefix, self.env, key)
            else:
                if self.prefix not in key:
                    key = '{}{}'.format(self.prefix, key)
        key = key or ''
        key = key.replace('//','/')
        return key
Esempio n. 6
0
class S3(Storage):
    def __init__(self, session, vault, vaultkey, project, env='default'):
        self.client = session.client('s3')
        self.bucket = vault
        self.vaultkey = vaultkey
        self.vault = Kms(session=session, key=vaultkey)
        self.project = project
        self.prefix = self.project.load().get('project', '')
        self.env = env + '/'
        if self.prefix:
            self.prefix += '/'

    @asyncio.coroutine
    def create_bucket(self, name=None, **kw):
        # administrator creates bucket for projects
        name = name or self.bucket
        result = self.client.create_bucket(Bucket=name)
        raise Return(result)

    @asyncio.coroutine
    def setup(self, vault, project, **kw):
        # project-user creates a folder inside bucket
        if not all([vault, project]):
            sys.exit("Error! Vault and/or project undefined")
        try:
            result = yield From(self.get(project))
            res = "Error! Project with this name already exists"
        except:
            result = yield From(self.put(project, ''))
            self.project.save(dict(vault=vault, project=project, key=self.vaultkey))
            res = "Success! Project created. Configuration stored in %s"%self.project.name
        raise Return(res)

    @asyncio.coroutine
    def list_backend(self, prefix=None, **kw):
        if not prefix:
            prefix = self.prefix + self.env
        raise Return(self.client.list_objects(Bucket=self.bucket, Prefix=prefix, MaxKeys=1000, Delimiter=kw.get('delimiter', BOTO_DEFAULT)))

    @asyncio.coroutine
    def list(self, key=None, **kw):
        key = self.prefixify(key)
        response = yield From(self.list_backend(prefix=key, **kw))
        raise Return([self.prefixify(k['Key'], reverse=True) for k in response.get('Contents', [])])

    @asyncio.coroutine
    def config(self, key=None, **kw):
        contents = yield From(self.list_backend(prefix=self.prefixify(key)))
        contents = contents.get('Contents', [])
        tasks = [self.get(key=obj['Key'], **kw) for obj in contents]
        results = yield From(asyncio.gather(*tasks))
        keys = [k['Key'].split('/')[-1] for k in contents]
        raise Return(OrderedDict(zip(keys, results)))

    @asyncio.coroutine
    def put_backend(self, **kw):
        raise Return(self.client.put_object(**kw))

    @asyncio.coroutine
    def put(self, key, value, **kw):
        if not value: raise Return("Error! No value provided.")

        key = self.prefixify(key)
        is_file = False
        is_binary = False # TODO: --binary
        mode = 'rb'
        if os.path.isfile(value):
            value = codecs.open(os.path.expandvars(os.path.expanduser(value)), mode=mode, encoding=ENCODING).read().rstrip('\n')
            is_file = True
        value = value.encode(ENCODING)
        data = self.vault.encrypt(value, is_file=is_file, is_binary=is_binary)
        data['name'] = key
        result = yield From(self.put_backend(Bucket=self.bucket, Key=key, Body=json.dumps(data)))
        if result.get('ResponseMetadata').get('HTTPStatusCode') == 200:
            raise Return("Success! Wrote: %s"%key)
        else:
            raise Return(result)

    @asyncio.coroutine
    def get_backend(self, **kw):
        raise Return(self.client.get_object(**kw))

    @asyncio.coroutine
    def get(self, key, **kw):
        key = self.prefixify(key or '')
        extra = {}
        if kw.get('version'):
            extra['VersionId'] = kw.get('version')
        try:
            result = yield From(self.get_backend(Bucket=self.bucket, Key=key, **extra))
        except Exception as e:
            if not kw.get('in_group_check'):
                # check for grouped keys
                key = key.rstrip('/')+'/'
                kw['in_group_check'] = True
                result = yield From(self.config(key=key, **kw))
                if not result:
                    result = "Error! The specified key does not exist."
                raise Return(result)
            raise
        body = json.loads(result['Body'].read().decode(ENCODING))
        data = self.vault.decrypt(body)
        is_binary = body.get('is_binary', False)
        if not is_binary:
            data = data.decode(ENCODING)
        raise Return(data)

    @asyncio.coroutine
    def delete_backend(self, **kw):
        raise Return(self.client.delete_object(**kw))

    @asyncio.coroutine
    def delete(self, key, **kw):
        key = self.prefixify(key)
        result = yield From(self.delete_backend(Bucket=self.bucket, Key=key))
        if result.get('ResponseMetadata').get('HTTPStatusCode') == 204:
            raise Return("Success! Deleted: %s"%key)
        else:
            raise Return(result)

    @asyncio.coroutine
    def versions(self, **kw):
        prefix = kw.get('prefix', self.prefix)
        response = self.client.list_object_versions(Bucket=self.bucket, Prefix=self.prefix)
        versions = []
        for k in response.get('Versions', []):
            key = self.prefixify(k['Key'], reverse=True)
            if kw.get('key') and kw.get('key')!=key: continue
            versions.append(dict(key=key, version=k['VersionId'], is_latest=k['IsLatest'], modified=k['LastModified']))
        raise Return(versions)

    @asyncio.coroutine
    def envs(self, **kw):
        contents = yield From(self.list_backend(prefix=self.prefix, delimiter='/'))
        raise Return(list(k['Prefix'].split('/')[1] for k in contents.get('CommonPrefixes', [])))

    def prefixify(self, key, reverse=False):
        if reverse:
            key = key.replace('{}{}'.format(self.prefix, self.env), '')
        elif key is not None:
            if self.prefix not in key and self.env not in key:
                key = '{}{}{}'.format(self.prefix, self.env, key)
            elif self.prefix not in key:
                key = '{}{}'.format(self.prefix, key)
        return key