Exemple #1
0
def credential_list(mocker):
    credentials = [
        Credential(
            id='1234',
            revision=1,
            data_type='credential',
            enabled=True,
            name='Test credential',
            credential_pairs='akjlaklkaj==',
            data_key='slkjlksfjklsdjf==',
            cipher_version=2,
            metadata={},
            modified_date=datetime.now(),
            modified_by='*****@*****.**',
            documentation='',
        ),
        Credential(
            id='5678',
            revision=2,
            data_type='credential',
            enabled=True,
            name='Test credential 2',
            credential_pairs='akjlaklkaj==',
            data_key='slkjlksfjklsdjf==',
            cipher_version=2,
            metadata={},
            modified_date=datetime.now(),
            modified_by='*****@*****.**',
            documentation='',
        ),
    ]
    return credentials
Exemple #2
0
def get_archive_credential_revisions(id):
    try:
        cred = Credential.get(id)
    except DoesNotExist:
        logging.warning('Item with id {0} does not exist.'.format(id))
        return jsonify({}), 404
    if (cred.data_type != 'credential'
            and cred.data_type != 'archive-credential'):
        return jsonify({}), 404
    revisions = []
    _range = range(1, cred.revision + 1)
    ids = []
    for i in _range:
        ids.append("{0}-{1}".format(id, i))
    for revision in Credential.batch_get(ids):
        revisions.append({
            'id': revision.id,
            'name': revision.name,
            'revision': revision.revision,
            'enabled': revision.enabled,
            'modified_date': revision.modified_date,
            'modified_by': revision.modified_by,
            'documentation': revision.documentation
        })
    return jsonify({
        'revisions':
        sorted(revisions, key=lambda k: k['revision'], reverse=True)
    })
Exemple #3
0
def create_credential():
    data = request.get_json()
    if not data.get('credential_pairs'):
        return jsonify({'error': 'credential_pairs is a required field'}), 400
    if not isinstance(data.get('metadata', {}), dict):
        return jsonify({'error': 'metadata must be a dict'}), 400
    # Ensure credential pair keys are lowercase
    credential_pairs = _lowercase_credential_pairs(data['credential_pairs'])
    _check, ret = _check_credential_pair_values(credential_pairs)
    if not _check:
        return jsonify(ret), 400
    for cred in Credential.data_type_date_index.query(
            'credential', name__eq=data['name']):
        # Conflict, the name already exists
        msg = 'Name already exists. See id: {0}'.format(cred.id)
        return jsonify({'error': msg, 'reference': cred.id}), 409
    # Generate an initial stable ID to allow name changes
    id = str(uuid.uuid4()).replace('-', '')
    # Try to save to the archive
    revision = 1
    credential_pairs = json.dumps(credential_pairs)
    data_key = keymanager.create_datakey(encryption_context={'id': id})
    cipher = CipherManager(data_key['plaintext'], version=2)
    credential_pairs = cipher.encrypt(credential_pairs)
    cred = Credential(
        id='{0}-{1}'.format(id, revision),
        data_type='archive-credential',
        name=data['name'],
        credential_pairs=credential_pairs,
        metadata=data.get('metadata'),
        revision=revision,
        enabled=data.get('enabled'),
        data_key=data_key['ciphertext'],
        cipher_version=2,
        modified_by=authnz.get_logged_in_user()
    ).save(id__null=True)
    # Make this the current revision
    cred = Credential(
        id=id,
        data_type='credential',
        name=data['name'],
        credential_pairs=credential_pairs,
        metadata=data.get('metadata'),
        revision=revision,
        enabled=data.get('enabled'),
        data_key=data_key['ciphertext'],
        cipher_version=2,
        modified_by=authnz.get_logged_in_user()
    )
    cred.save()
    return jsonify({
        'id': cred.id,
        'name': cred.name,
        'credential_pairs': json.loads(cipher.decrypt(cred.credential_pairs)),
        'metadata': cred.metadata,
        'revision': cred.revision,
        'enabled': cred.enabled,
        'modified_date': cred.modified_date,
        'modified_by': cred.modified_by
    })
Exemple #4
0
def get_archive_credential_revisions(id):
    try:
        cred = Credential.get(id)
    except Credential.DoesNotExist:
        return jsonify({}), 404
    if (cred.data_type != 'credential' and
            cred.data_type != 'archive-credential'):
        return jsonify({}), 404
    revisions = []
    _range = range(1, cred.revision + 1)
    ids = []
    for i in _range:
        ids.append("{0}-{1}".format(id, i))
    for revision in Credential.batch_get(ids):
        revisions.append({
            'id': revision.id,
            'name': revision.name,
            'revision': revision.revision,
            'enabled': revision.enabled,
            'modified_date': revision.modified_date,
            'modified_by': revision.modified_by
        })
    return jsonify({
        'revisions': sorted(
            revisions,
            key=lambda k: k['revision'],
            reverse=True
        )
    })
Exemple #5
0
def create_dynamodb_tables():
    i = 0
    # This loop is absurd, but there's race conditions with dynamodb local
    while i < 5:
        try:
            if not Credential.exists():
                Credential.create_table(
                    read_capacity_units=10,
                    write_capacity_units=10,
                    wait=True
                )
            if not BlindCredential.exists():
                BlindCredential.create_table(
                    read_capacity_units=10,
                    write_capacity_units=10,
                    wait=True
                )
            if not Service.exists():
                Service.create_table(
                    read_capacity_units=10,
                    write_capacity_units=10,
                    wait=True
                )
            break
        except TableError:
            i = i + 1
            time.sleep(2)
Exemple #6
0
def test_diff(mocker):
    mocker.patch(
        'confidant.models.credential.Credential'
        '._get_decrypted_credential_pairs',
        return_value={},
    )
    modified_by = '*****@*****.**'
    modified_date_old = datetime.now
    modified_date_new = datetime.now
    old = Credential(
        name='test',
        revision=1,
        enabled=True,
        documentation='old',
        metadata={'hello': 'world'},
        modified_by=modified_by,
        modified_date=modified_date_old,
        tags=['FINANCIALLY_SENSITIVE', 'IMPORTANT'],
    )
    new = Credential(
        name='test2',
        revision=2,
        enabled=True,
        documentation='',
        metadata={'foo': 'bar'},
        modified_by=modified_by,
        modified_date=modified_date_new,
        tags=['ADMIN_PRIV', 'IMPORTANT'],
    )
    # TODO: figure out how to test decrypted_credential_pairs. Mocking
    # it is turning out to be difficult.
    expectedDiff = {
        'name': {
            'removed': 'test',
            'added': 'test2',
        },
        'metadata': {
            'removed': ['hello'],
            'added': ['foo'],
        },
        'documentation': {
            'removed': 'old',
            'added': '',
        },
        'modified_by': {
            'removed': modified_by,
            'added': modified_by,
        },
        'modified_date': {
            'removed': modified_date_old,
            'added': modified_date_new,
        },
        'tags': {
            'removed': ['FINANCIALLY_SENSITIVE'],
            'added': ['ADMIN_PRIV'],
        },
    }
    assert old.diff(new) == expectedDiff
Exemple #7
0
def credentials(mocker, now):
    gmd_mock = mocker.Mock(return_value='test')
    gmd_mock.range_keyname = 'test'
    mocker.patch(
        'confidant.scripts.restore.Credential._get_meta_data',
        return_value=gmd_mock,
    )
    mocker.patch(
        'confidant.scripts.restore.CredentialArchive._get_meta_data',
        return_value=gmd_mock,
    )
    archive_credential = CredentialArchive(
        id='1234',
        name='test',
        data_type='credential',
        revision=2,
        enabled=True,
        modified_date=now,
        modified_by='*****@*****.**',
    )
    credential = Credential.from_archive_credential(archive_credential)
    archive_revision1 = CredentialArchive(
        id='1234-1',
        name='test revision1',
        data_type='archive-credential',
        revision=1,
        enabled=True,
        modified_date=now,
        modified_by='*****@*****.**',
    )
    revision1 = Credential.from_archive_credential(archive_revision1)
    archive_revision2 = Credential(
        id='1234-2',
        name='test revision2',
        data_type='archive-credential',
        revision=2,
        enabled=True,
        modified_date=now,
        modified_by='*****@*****.**',
    )
    revision2 = Credential.from_archive_credential(archive_revision2)

    def from_archive_credential(archive_credential):
        if archive_credential.id == '1234':
            return credential
        elif archive_credential.id == '1234-1':
            return revision1
        elif archive_credential.id == '1234-2':
            return revision2

    mocker.patch.object(Credential, 'from_archive_credential',
                        from_archive_credential)
    return {
        'credentials': [credential],
        'archive_credentials': [archive_credential],
        'revisions': [revision1, revision2],
        'archive_revisions': [archive_revision1, archive_revision2],
    }
Exemple #8
0
def _get_latest_credential_revision(id, revision):
    i = revision + 1
    while True:
        _id = '{0}-{1}'.format(id, i)
        try:
            Credential.get(_id)
        except Credential.DoesNotExist:
            return i
        i = i + 1
Exemple #9
0
def _get_latest_credential_revision(id, revision):
    i = revision + 1
    while True:
        _id = '{0}-{1}'.format(id, i)
        try:
            Credential.get(_id)
        except DoesNotExist:
            return i
        i = i + 1
Exemple #10
0
def test_exempt_from_rotation(mocker):
    mocker.patch(
        'confidant.models.credential.settings.TAGS_EXCLUDING_ROTATION',
        ['ADMIN_PRIV'],
    )
    cred = Credential(tags=['ADMIN_PRIV'])
    assert cred.exempt_from_rotation is True

    cred = Credential(tags=['FINANCIALLY_SENSITIVE'])
    assert cred.exempt_from_rotation is False
Exemple #11
0
 def run(self, days, force, ids):
     if not settings.DYNAMODB_TABLE_ARCHIVE:
         logger.error('DYNAMODB_TABLE_ARCHIVE is not configured, exiting.')
         return 1
     if days and ids:
         logger.error('--days and --ids options are mutually exclusive')
         return 1
     if not days and not ids:
         logger.error('Either --days or --ids options are required')
         return 1
     credentials = []
     if ids:
         # filter strips an empty string
         _ids = [_id.strip() for _id in list(filter(None, ids.split(',')))]
         if not _ids:
             logger.error('Passed in --ids argument is empty')
             return 1
         for credential in Credential.batch_get(_ids):
             if credential.enabled:
                 logger.warning('Skipping enabled credential {}'.format(
                     credential.id))
                 continue
             credentials.append(credential)
     else:
         for credential in Credential.data_type_date_index.query(
                 'credential'):
             tz = credential.modified_date.tzinfo
             now = datetime.now(tz)
             delta = now - credential.modified_date
             if not credential.enabled and delta.days > days:
                 credentials.append(credential)
     self.archive(credentials, force=force)
Exemple #12
0
def get_credential(id):
    try:
        cred = Credential.get(id)
    except Credential.DoesNotExist:
        return jsonify({}), 404
    if (cred.data_type != 'credential' and
            cred.data_type != 'archive-credential'):
        return jsonify({}), 404
    services = []
    for service in Service.data_type_date_index.query('service'):
        services.append(service.id)
    if cred.data_type == 'credential':
        context = id
    else:
        context = id.split('-')[0]
    data_key = keymanager.decrypt_key(
        cred.data_key,
        encryption_context={'id': context}
    )
    cipher_version = cred.cipher_version
    cipher = CipherManager(data_key, cipher_version)
    _credential_pairs = cipher.decrypt(cred.credential_pairs)
    _credential_pairs = json.loads(_credential_pairs)
    return jsonify({
        'id': id,
        'name': cred.name,
        'credential_pairs': _credential_pairs,
        'services': services,
        'revision': cred.revision,
        'enabled': cred.enabled,
        'modified_date': cred.modified_date,
        'modified_by': cred.modified_by
    })
Exemple #13
0
def test_next_rotation_date_never_rotated(mocker):
    mocker.patch(
        'confidant.models.credential.settings.TAGS_EXCLUDING_ROTATION',
        [],
    )
    cred = Credential(tags=['FINANCIALLY_SENSITIVE'])
    assert cred.next_rotation_date <= datetime.utcnow()
Exemple #14
0
def get_credential(id):
    try:
        cred = Credential.get(id)
    except DoesNotExist:
        logging.warning('Item with id {0} does not exist.'.format(id))
        return jsonify({}), 404
    if (cred.data_type != 'credential'
            and cred.data_type != 'archive-credential'):
        return jsonify({}), 404
    services = []
    for service in Service.data_type_date_index.query('service'):
        services.append(service.id)
    if cred.data_type == 'credential':
        context = id
    else:
        context = id.split('-')[0]
    data_key = keymanager.decrypt_datakey(cred.data_key,
                                          encryption_context={'id': context})
    cipher_version = cred.cipher_version
    cipher = CipherManager(data_key, cipher_version)
    _credential_pairs = cipher.decrypt(cred.credential_pairs)
    _credential_pairs = json.loads(_credential_pairs)
    return jsonify({
        'id': id,
        'name': cred.name,
        'credential_pairs': _credential_pairs,
        'metadata': cred.metadata,
        'services': services,
        'revision': cred.revision,
        'enabled': cred.enabled,
        'modified_date': cred.modified_date,
        'modified_by': cred.modified_by,
        'documentation': cred.documentation
    })
Exemple #15
0
def test_equals(mocker):
    decrypted_pairs_mock = mocker.patch(
        'confidant.models.credential.Credential.decrypted_credential_pairs'
    )
    decrypted_pairs_mock.return_value = {'test': 'me'}
    cred1 = Credential(
        name='test',
        enabled=True,
        documentation='',
        metadata={},
    )
    cred2 = Credential(
        name='test',
        enabled=True,
        documentation='',
        metadata={},
    )
    assert cred1.equals(cred2) is True
Exemple #16
0
def test_not_equals(mocker):
    mocker.patch(
        'confidant.models.credential.Credential'
        '._get_decrypted_credential_pairs',
        return_value={'test': 'me'},
    )
    cred1 = Credential(
        name='test',
        enabled=True,
        documentation='',
        metadata={},
    )
    cred2 = Credential(
        name='test2',
        enabled=True,
        documentation='',
        metadata={},
    )
    assert cred1.equals(cred2) is False
Exemple #17
0
def test_not_equals_different_tags(mocker):
    decrypted_pairs_mock = mocker.patch(
        'confidant.models.credential.Credential.decrypted_credential_pairs')
    decrypted_pairs_mock.return_value = {'test': 'me'}
    cred1 = Credential(
        name='test',
        enabled=True,
        documentation='',
        metadata={},
        tags=['ADMIN_PRIV'],
    )
    cred2 = Credential(
        name='test',
        enabled=True,
        documentation='',
        metadata={},
        tags=['FINANCIALLY_SENSITIVE'],
    )
    assert cred1.equals(cred2) is False
Exemple #18
0
def test_get_revision_ids_for_credential():
    credential = Credential(
        id='1234',
        revision=3,
        name='test',
        enabled=True,
    )
    assert credentialmanager.get_revision_ids_for_credential(credential) == [
        '1234-1',
        '1234-2',
        '1234-3',
    ]
Exemple #19
0
 def delete(self, deletes, force=False):
     _deletes = ', '.join([delete.id for delete in deletes])
     if not force:
         logger.info(
             'Would have deleted credential and revisions: {}'.format(
                 _deletes, ))
         return
     logger.info('Deleting credential and revisions: {}'.format(_deletes, ))
     with Credential.batch_write() as batch:
         for delete in deletes:
             batch.delete(delete)
     stats.incr('archive.delete.success')
Exemple #20
0
def create_credential():
    data = request.get_json()
    if not data.get('credential_pairs'):
        return jsonify({'error': 'credential_pairs is a required field'}), 400
    if not isinstance(data.get('metadata', {}), dict):
        return jsonify({'error': 'metadata must be a dict'}), 400
    # Ensure credential pair keys are lowercase
    credential_pairs = _lowercase_credential_pairs(data['credential_pairs'])
    _check, ret = _check_credential_pair_values(credential_pairs)
    if not _check:
        return jsonify(ret), 400
    for cred in Credential.data_type_date_index.query(
            'credential', name__eq=data['name']):
        # Conflict, the name already exists
        msg = 'Name already exists. See id: {0}'.format(cred.id)
        return jsonify({'error': msg, 'reference': cred.id}), 409
    # Generate an initial stable ID to allow name changes
    id = str(uuid.uuid4()).replace('-', '')
    # Try to save to the archive
    revision = 1
    credential_pairs = json.dumps(credential_pairs)
    data_key = keymanager.create_datakey(encryption_context={'id': id})
    cipher = CipherManager(data_key['plaintext'], version=2)
    credential_pairs = cipher.encrypt(credential_pairs)
    cred = Credential(
        id='{0}-{1}'.format(id, revision),
        data_type='archive-credential',
        name=data['name'],
        credential_pairs=credential_pairs,
        metadata=data.get('metadata'),
        revision=revision,
        enabled=data.get('enabled'),
        data_key=data_key['ciphertext'],
        cipher_version=2,
        modified_by=authnz.get_logged_in_user()
    ).save(id__null=True)
    # Make this the current revision
    cred = Credential(
        id=id,
        data_type='credential',
        name=data['name'],
        credential_pairs=credential_pairs,
        metadata=data.get('metadata'),
        revision=revision,
        enabled=data.get('enabled'),
        data_key=data_key['ciphertext'],
        cipher_version=2,
        modified_by=authnz.get_logged_in_user()
    )
    cred.save()
    return jsonify({
        'id': cred.id,
        'name': cred.name,
        'credential_pairs': json.loads(cipher.decrypt(cred.credential_pairs)),
        'metadata': cred.metadata,
        'revision': cred.revision,
        'enabled': cred.enabled,
        'modified_date': cred.modified_date,
        'modified_by': cred.modified_by
    })
Exemple #21
0
def get_archive_credential_revisions(id):
    if not acl_module_check(
            resource_type='credential', action='metadata', resource_id=id):
        msg = "{} does not have access to credential {} revisions".format(
            authnz.get_logged_in_user(), id)
        error_msg = {'error': msg}
        return jsonify(error_msg), 403

    try:
        cred = Credential.get(id)
    except DoesNotExist:
        logging.warning('Item with id {0} does not exist.'.format(id))
        return jsonify({}), 404
    if (cred.data_type != 'credential'
            and cred.data_type != 'archive-credential'):
        return jsonify({}), 404
    _range = range(1, cred.revision + 1)
    ids = []
    for i in _range:
        ids.append("{0}-{1}".format(id, i))
    revisions_response = RevisionsResponse.from_credentials(
        Credential.batch_get(ids))
    return revisions_response_schema.dumps(revisions_response)
Exemple #22
0
def test_credential_archive(mocker):
    mocker.patch(
        'confidant.models.credential.Credential'
        '._get_decrypted_credential_pairs',
        return_value={},
    )
    cred = Credential(
        name='test',
        enabled=True,
        documentation='',
        metadata={},
    )
    archive_cred = CredentialArchive.from_credential(cred)
    # TODO: do a more thorough equality test here.
    assert cred.id == archive_cred.id
Exemple #23
0
 def restore(self, archive_credentials, force):
     for archive_credential in archive_credentials:
         saves = []
         # restore the current record
         credential = Credential.from_archive_credential(
             archive_credential, )
         saves.append(credential)
         # fetch and restore every revision
         _range = range(1, credential.revision + 1)
         ids = []
         for i in _range:
             ids.append("{0}-{1}".format(credential.id, i))
         archive_revisions = CredentialArchive.batch_get(ids)
         for archive_revision in archive_revisions:
             revision = Credential.from_archive_credential(
                 archive_revision, )
             saves.append(revision)
         try:
             self.save(saves, force=force)
         except Exception:
             logger.exception('Failed to batch save {}.'.format(
                 credential.id))
             stats.incr('restore.save.failure')
             continue
Exemple #24
0
def diff_credential(id, old_revision, new_revision):
    if not acl_module_check(
            resource_type='credential', action='metadata', resource_id=id):
        msg = "{} does not have access to diff credential {}".format(
            authnz.get_logged_in_user(), id)
        error_msg = {'error': msg, 'reference': id}
        return jsonify(error_msg), 403

    try:
        old_credential = Credential.get('{}-{}'.format(id, old_revision))
    except DoesNotExist:
        return jsonify({'error': 'Credential not found.'}), 404
    if old_credential.data_type != 'archive-credential':
        msg = 'id provided is not a credential.'
        return jsonify({'error': msg}), 400
    try:
        new_credential = Credential.get('{}-{}'.format(id, new_revision))
    except DoesNotExist:
        logging.warning('Item with id {0} does not exist.'.format(id))
        return jsonify({}), 404
    if new_credential.data_type != 'archive-credential':
        msg = 'id provided is not a credential.'
        return jsonify({'error': msg}), 400
    return jsonify(old_credential.diff(new_credential))
Exemple #25
0
def archive_credential(mocker):
    return Credential(
        id='123-1',
        revision=1,
        data_type='archive-credential',
        enabled=True,
        name='Archive credential',
        credential_pairs='akjlaklkaj==',
        data_key='slkjlksfjklsdjf==',
        cipher_version=2,
        metadata={},
        modified_date=datetime.now(),
        modified_by='*****@*****.**',
        documentation='',
        tags=['OLD TAG'],
    )
Exemple #26
0
def credential(mocker):
    return Credential(
        id='1234',
        revision=1,
        data_type='credential',
        enabled=True,
        name='Test credential',
        credential_pairs='akjlaklkaj==',
        data_key='slkjlksfjklsdjf==',
        cipher_version=2,
        metadata={},
        modified_date=datetime.now(),
        modified_by='*****@*****.**',
        documentation='',
        last_rotation_date=datetime(2020, 1, 1, tzinfo=pytz.utc),
    )
Exemple #27
0
def test_next_rotation_date_last_rotation_present(mocker):
    mocker.patch(
        'confidant.models.credential.settings.TAGS_EXCLUDING_ROTATION',
        [],
    )
    mocker.patch(
        'confidant.models.credential.settings.MAXIMUM_ROTATION_DAYS',
        100,
    )
    mocker.patch(
        'confidant.models.credential.settings.ROTATION_DAYS_CONFIG',
        {'FINANCIALLY_SENSITIVE': 30},
    )
    cred = Credential(
        tags=['FINANCIALLY_SENSITIVE'],
        last_rotation_date=datetime(2020, 1, 1),
    )
    assert cred.next_rotation_date == datetime(2020, 1, 31)
Exemple #28
0
def _get_credentials(credential_ids):
    credentials = []
    with stats.timer('service_batch_get_credentials'):
        for cred in Credential.batch_get(credential_ids):
            data_key = keymanager.decrypt_key(
                cred.data_key, encryption_context={'id': cred.id})
            cipher_version = cred.cipher_version
            cipher = CipherManager(data_key, cipher_version)
            _credential_pairs = cipher.decrypt(cred.credential_pairs)
            _credential_pairs = json.loads(_credential_pairs)
            credentials.append({
                'id': cred.id,
                'name': cred.name,
                'enabled': cred.enabled,
                'revision': cred.revision,
                'credential_pairs': _credential_pairs
            })
    return credentials
Exemple #29
0
def get_credential(id):
    if not acl_module_check(
            resource_type='credential', action='metadata', resource_id=id):
        msg = "{} does not have access to credential {}".format(
            authnz.get_logged_in_user(), id)
        error_msg = {'error': msg, 'reference': id}
        return jsonify(error_msg), 403

    try:
        credential = Credential.get(id)
    except DoesNotExist:
        logging.warning('Item with id {0} does not exist.'.format(id))
        return jsonify({}), 404
    if (credential.data_type != 'credential'
            and credential.data_type != 'archive-credential'):
        return jsonify({}), 404

    permissions = {
        'metadata':
        True,
        'get':
        False,
        'update':
        acl_module_check(resource_type='credential',
                         action='update',
                         resource_id=id),
    }
    include_credential_pairs = False
    if acl_module_check(resource_type='credential',
                        action='get',
                        resource_id=id):
        permissions['get'] = True
        include_credential_pairs = True
        log_line = "{0} get credential {1}".format(authnz.get_logged_in_user(),
                                                   id)
        logging.info(log_line)
    credential_response = CredentialResponse.from_credential(
        credential,
        include_credential_keys=True,
        include_credential_pairs=include_credential_pairs,
    )
    credential_response.permissions = permissions
    return credential_response_schema.dumps(credential_response)
Exemple #30
0
def _get_credentials(credential_ids):
    credentials = []
    with stats.timer('service_batch_get_credentials'):
        for cred in Credential.batch_get(credential_ids):
            data_key = keymanager.decrypt_key(
                cred.data_key,
                encryption_context={'id': cred.id}
            )
            cipher_version = cred.cipher_version
            cipher = CipherManager(data_key, cipher_version)
            _credential_pairs = cipher.decrypt(cred.credential_pairs)
            _credential_pairs = json.loads(_credential_pairs)
            credentials.append({
                'id': cred.id,
                'name': cred.name,
                'enabled': cred.enabled,
                'revision': cred.revision,
                'credential_pairs': _credential_pairs
            })
    return credentials
Exemple #31
0
 def archive(self, credentials, force):
     services = list(Service.data_type_date_index.query('service'))
     for credential in credentials:
         if self.credential_in_service(credential.id, services):
             msg = ('Skipping archival of disabled credential {}, as it'
                    ' is still mapped to a service.')
             logger.warning(msg.format(credential.id))
             continue
         saves = []
         deletes = []
         # save the current record.
         archive_credential = CredentialArchive.from_credential(
             credential, )
         saves.append(archive_credential)
         # fetch and save every revision
         revisions = Credential.batch_get(
             credentialmanager.get_revision_ids_for_credential(credential))
         for revision in revisions:
             archive_revision = CredentialArchive.from_credential(
                 revision, )
             saves.append(archive_revision)
             deletes.append(revision)
         deletes.append(credential)
         try:
             self.save(saves, force=force)
         except Exception:
             logger.exception(
                 'Failed to batch save {}, skipping deletion.'.format(
                     credential.id))
             stats.incr('archive.save.failure')
             continue
         try:
             self.delete(deletes, force=force)
         except Exception:
             logger.exception('Failed to batch delete {}'.format(
                 credential.id))
             stats.incr('archive.delete.failure')
Exemple #32
0
 def save(self, saves, force=False):
     # Do not restore a credential if it exists in the primary table.
     # We do this check at the point of all saves so that we can
     # restore revisions, if one of them failed to restore for some
     # reason.
     _saves = []
     for save in saves:
         if self.credential_exists(save.id):
             continue
         _saves.append(save)
     if not _saves:
         return
     save_msg = ', '.join([save.id for save in _saves])
     if not force:
         logger.info(
             'Would have restored credential and revisions: {}'.format(
                 save_msg, ))
         return
     logger.info('Restoring credential and revisions: {}'.format(
         save_msg, ))
     with Credential.batch_write() as batch:
         for save in _saves:
             batch.save(save)
     stats.incr('restore.save.success')
Exemple #33
0
import time

from confidant import app
from confidant.models.credential import Credential
from confidant.models.service import Service
from pynamodb.exceptions import TableError

# Only used when using dynamodb local
if app.config.get('DYNAMODB_URL'):
    i = 0
    # This loop is absurd, but there's race conditions with dynamodb local
    while i < 5:
        try:
            if not Credential.exists():
                Credential.create_table(
                    read_capacity_units=10,
                    write_capacity_units=10,
                    wait=True
                )
            if not Service.exists():
                Service.create_table(
                    read_capacity_units=10,
                    write_capacity_units=10,
                    wait=True
                )
            break
        except TableError:
            i = i + 1
            time.sleep(2)
Exemple #34
0
def update_credential(id):
    try:
        _cred = Credential.get(id)
    except Credential.DoesNotExist:
        return jsonify({'error': 'Credential not found.'}), 404
    if _cred.data_type != 'credential':
        msg = 'id provided is not a credential.'
        return jsonify({'error': msg}), 400
    data = request.get_json()
    update = {}
    revision = _cred.revision + 1
    update['name'] = data.get('name', _cred.name)
    if 'enabled' in data:
        if not isinstance(data['enabled'], bool):
            return jsonify({'error': 'Enabled must be a boolean.'}), 400
        update['enabled'] = data['enabled']
    else:
        update['enabled'] = _cred.enabled
    services = _get_services_for_credential(id)
    if 'credential_pairs' in data:
        # Ensure credential pair keys are lowercase
        credential_pairs = _lowercase_credential_pairs(
            data['credential_pairs']
        )
        if not _check_credential_pair_uniqueness(credential_pairs):
            ret = {'error': 'credential pairs must be key: value'}
            return jsonify(ret), 400
        # Ensure credential pairs don't conflicts with pairs from other
        # services
        conflicts = _pair_key_conflicts_for_services(
            id,
            credential_pairs,
            services
        )
        if conflicts:
            ret = {
                'error': 'Conflicting key pairs in mapped service.',
                'conflicts': conflicts
            }
            return jsonify(ret), 400
        update['credential_pairs'] = json.dumps(credential_pairs)
    else:
        data_key = keymanager.decrypt_key(
            _cred.data_key,
            encryption_context={'id': id}
        )
        cipher_version = _cred.cipher_version
        cipher = CipherManager(data_key, cipher_version)
        update['credential_pairs'] = cipher.decrypt(_cred.credential_pairs)
    data_key = keymanager.create_datakey(encryption_context={'id': id})
    cipher = CipherManager(data_key['plaintext'], version=2)
    credential_pairs = cipher.encrypt(update['credential_pairs'])
    # Try to save to the archive
    try:
        Credential(
            id='{0}-{1}'.format(id, revision),
            name=update['name'],
            data_type='archive-credential',
            credential_pairs=credential_pairs,
            enabled=update['enabled'],
            revision=revision,
            data_key=data_key['ciphertext'],
            cipher_version=2,
            modified_by=authnz.get_logged_in_user_email()
        ).save(id__null=True)
    except PutError as e:
        logging.error(e)
        return jsonify({'error': 'Failed to add credential to archive.'}), 500
    try:
        cred = Credential(
            id=id,
            name=update['name'],
            data_type='credential',
            credential_pairs=credential_pairs,
            enabled=update['enabled'],
            revision=revision,
            data_key=data_key['ciphertext'],
            cipher_version=2,
            modified_by=authnz.get_logged_in_user_email()
        )
        cred.save()
    except PutError as e:
        logging.error(e)
        return jsonify({'error': 'Failed to update active credential.'}), 500
    if services:
        service_names = [x.id for x in services]
        msg = 'Updated credential "{0}" ({1}); Revision {2}'
        msg = msg.format(cred.name, cred.id, cred.revision)
        graphite.send_event(service_names, msg)
    return jsonify({
        'id': cred.id,
        'name': cred.name,
        'credential_pairs': json.loads(cipher.decrypt(cred.credential_pairs)),
        'revision': cred.revision,
        'enabled': cred.enabled,
        'modified_date': cred.modified_date,
        'modified_by': cred.modified_by
    })
Exemple #35
0
def test_next_rotation_date_no_rotation_required(mocker):
    mocker.patch(
        'confidant.models.credential.settings.TAGS_EXCLUDING_ROTATION',
        ['ADMIN_PRIV'],
    )
    assert Credential(tags=['ADMIN_PRIV']).next_rotation_date is None
Exemple #36
0
def update_credential(id):
    try:
        _cred = Credential.get(id)
    except DoesNotExist:
        return jsonify({'error': 'Credential not found.'}), 404
    if _cred.data_type != 'credential':
        msg = 'id provided is not a credential.'
        return jsonify({'error': msg}), 400
    data = request.get_json()
    update = {}
    revision = _get_latest_credential_revision(id, _cred.revision)
    update['name'] = data.get('name', _cred.name)
    if 'enabled' in data:
        if not isinstance(data['enabled'], bool):
            return jsonify({'error': 'Enabled must be a boolean.'}), 400
        update['enabled'] = data['enabled']
    else:
        update['enabled'] = _cred.enabled
    if not isinstance(data.get('metadata', {}), dict):
        return jsonify({'error': 'metadata must be a dict'}), 400
    services = _get_services_for_credential(id)
    if 'credential_pairs' in data:
        # Ensure credential pair keys are lowercase
        credential_pairs = _lowercase_credential_pairs(
            data['credential_pairs'])
        _check, ret = _check_credential_pair_values(credential_pairs)
        if not _check:
            return jsonify(ret), 400
        # Ensure credential pairs don't conflicts with pairs from other
        # services
        conflicts = _pair_key_conflicts_for_services(
            id, list(credential_pairs.keys()), services)
        if conflicts:
            ret = {
                'error': 'Conflicting key pairs in mapped service.',
                'conflicts': conflicts
            }
            return jsonify(ret), 400
        update['credential_pairs'] = json.dumps(credential_pairs)
    else:
        data_key = keymanager.decrypt_datakey(_cred.data_key,
                                              encryption_context={'id': id})
        cipher_version = _cred.cipher_version
        cipher = CipherManager(data_key, cipher_version)
        update['credential_pairs'] = cipher.decrypt(_cred.credential_pairs)
    data_key = keymanager.create_datakey(encryption_context={'id': id})
    cipher = CipherManager(data_key['plaintext'], version=2)
    credential_pairs = cipher.encrypt(update['credential_pairs'])
    update['metadata'] = data.get('metadata', _cred.metadata)
    update['documentation'] = data.get('documentation', _cred.documentation)
    # Enforce documentation, EXCEPT if we are restoring an old revision
    if (not update['documentation'] and settings.get('ENFORCE_DOCUMENTATION')
            and not data.get('revision')):
        return jsonify({'error': 'documentation is a required field'}), 400
    # Try to save to the archive
    try:
        Credential(id='{0}-{1}'.format(id, revision),
                   name=update['name'],
                   data_type='archive-credential',
                   credential_pairs=credential_pairs,
                   metadata=update['metadata'],
                   enabled=update['enabled'],
                   revision=revision,
                   data_key=data_key['ciphertext'],
                   cipher_version=2,
                   modified_by=authnz.get_logged_in_user(),
                   documentation=update['documentation']).save(id__null=True)
    except PutError as e:
        logging.error(e)
        return jsonify({'error': 'Failed to add credential to archive.'}), 500
    try:
        cred = Credential(id=id,
                          name=update['name'],
                          data_type='credential',
                          credential_pairs=credential_pairs,
                          metadata=update['metadata'],
                          enabled=update['enabled'],
                          revision=revision,
                          data_key=data_key['ciphertext'],
                          cipher_version=2,
                          modified_by=authnz.get_logged_in_user(),
                          documentation=update['documentation'])
        cred.save()
    except PutError as e:
        logging.error(e)
        return jsonify({'error': 'Failed to update active credential.'}), 500
    if services:
        service_names = [x.id for x in services]
        msg = 'Updated credential "{0}" ({1}); Revision {2}'
        msg = msg.format(cred.name, cred.id, cred.revision)
        graphite.send_event(service_names, msg)
        webhook.send_event('credential_update', service_names, [cred.id])
    return jsonify({
        'id':
        cred.id,
        'name':
        cred.name,
        'credential_pairs':
        json.loads(cipher.decrypt(cred.credential_pairs)),
        'metadata':
        cred.metadata,
        'revision':
        cred.revision,
        'enabled':
        cred.enabled,
        'modified_date':
        cred.modified_date,
        'modified_by':
        cred.modified_by,
        'documentation':
        cred.documentation
    })
Exemple #37
0
 def credential_exists(self, credential_id):
     try:
         Credential.get(credential_id)
         return True
     except DoesNotExist:
         return False