Exemple #1
0
import guard
from flask import Flask
from flask_sslify import SSLify

from confidant import settings
from confidant.routes import (
    blind_credentials,
    certificates,
    credentials,
    identity,
    saml,
    services,
    static_files,
)

if not settings.get('DEBUG'):
    boto3.set_stream_logger(level=logging.CRITICAL)
    logging.getLogger('botocore').setLevel(logging.CRITICAL)
    logging.getLogger('pynamodb').setLevel(logging.WARNING)

CSP_POLICY = {
    'default-src': ["'self'"],
    'style-src': [
        "'self'",
        "'unsafe-inline'"  # for spin.js
    ]
}


def create_app():
    static_folder = settings.STATIC_FOLDER
Exemple #2
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 #3
0
def create_blind_credential():
    data = request.get_json()
    missing = []
    required_args = [
        'cipher_version', 'cipher_type', 'credential_pairs', 'data_key'
    ]
    if settings.get('ENFORCE_DOCUMENTATION'):
        required_args.append('documentation')
    for arg in required_args:
        if not data.get(arg):
            missing.append(arg)
    if missing:
        return jsonify({
            'error':
            'The following fields are required: {0}'.format(missing)
        }), 400
    if not isinstance(data['data_key'], dict):
        return jsonify(
            {'error':
             'data_key must be a dict with a region/key mapping.'}), 400
    if not isinstance(data.get('credential_keys', []), list):
        return jsonify({'error': 'credential_keys must be a list.'}), 400
    if not isinstance(data.get('metadata', {}), dict):
        return jsonify({'error': 'metadata must be a dict'}), 400
    for cred in BlindCredential.data_type_date_index.query(
            'blind-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
    cred = BlindCredential(
        id='{0}-{1}'.format(id, revision),
        data_type='archive-blind-credential',
        name=data['name'],
        credential_pairs=data['credential_pairs'],
        credential_keys=data.get('credential_keys'),
        metadata=data.get('metadata'),
        revision=revision,
        enabled=data.get('enabled'),
        data_key=data['data_key'],
        cipher_type=data['cipher_type'],
        cipher_version=data['cipher_version'],
        modified_by=authnz.get_logged_in_user(),
        documentation=data.get('documentation')).save(id__null=True)
    # Make this the current revision
    cred = BlindCredential(id=id,
                           data_type='blind-credential',
                           name=data['name'],
                           credential_pairs=data['credential_pairs'],
                           credential_keys=data.get('credential_keys'),
                           metadata=data.get('metadata'),
                           revision=revision,
                           enabled=data.get('enabled'),
                           data_key=data['data_key'],
                           cipher_type=data['cipher_type'],
                           cipher_version=data['cipher_version'],
                           modified_by=authnz.get_logged_in_user(),
                           documentation=data.get('documentation'))
    cred.save()
    return jsonify({
        'id': cred.id,
        'name': cred.name,
        'credential_pairs': cred.credential_pairs,
        'credential_keys': list(cred.credential_keys),
        'cipher_type': cred.cipher_type,
        'cipher_version': cred.cipher_version,
        'metadata': cred.metadata,
        'revision': cred.revision,
        'enabled': cred.enabled,
        'data_key': cred.data_key,
        'modified_date': cred.modified_date,
        'modified_by': cred.modified_by,
        'documentation': cred.documentation
    })
Exemple #4
0
def update_blind_credential(id):
    try:
        _cred = BlindCredential.get(id)
    except DoesNotExist:
        return jsonify({'error': 'Blind credential not found.'}), 404
    if _cred.data_type != 'blind-credential':
        msg = 'id provided is not a blind-credential.'
        return jsonify({'error': msg}), 400
    data = request.get_json()
    update = {}
    revision = _get_latest_blind_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_blind_credential(id)
    if 'credential_pairs' in data:
        for key in ['data_key', 'cipher_type', 'cipher_version']:
            if key not in data:
                msg = '{0} required when updating credential_pairs.'
                msg = msg.format(key)
                return jsonify({'error': msg}), 400
        update['credential_pairs'] = data['credential_pairs']
        update['credential_keys'] = data.get('credential_keys', [])
        if not isinstance(update['credential_keys'], list):
            return jsonify({'error': 'credential_keys must be a list.'}), 400
        # Ensure credential keys don't conflicts with pairs from other
        # services
        conflicts = _pair_key_conflicts_for_services(id,
                                                     data['credential_keys'],
                                                     services)
        if conflicts:
            ret = {
                'error': 'Conflicting key pairs in mapped service.',
                'conflicts': conflicts
            }
            return jsonify(ret), 400
        if not isinstance(data['data_key'], dict):
            return jsonify({
                'error':
                'data_key must be a dict with a region/key mapping.'
            }), 400
        update['data_key'] = data['data_key']
        update['cipher_type'] = data['cipher_type']
        update['cipher_version'] = data['cipher_version']
    else:
        update['credential_pairs'] = _cred.credential_pairs
        update['credential_keys'] = _cred.credential_keys
        update['data_key'] = _cred.data_key
        update['cipher_type'] = _cred.cipher_type
        update['cipher_version'] = _cred.cipher_version
    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:
        BlindCredential(
            id='{0}-{1}'.format(id, revision),
            data_type='archive-blind-credential',
            name=update['name'],
            credential_pairs=update['credential_pairs'],
            credential_keys=update['credential_keys'],
            metadata=update['metadata'],
            revision=revision,
            enabled=update['enabled'],
            data_key=update['data_key'],
            cipher_type=update['cipher_type'],
            cipher_version=update['cipher_version'],
            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 blind-credential to archive.'}), 500
    try:
        cred = BlindCredential(id=id,
                               data_type='blind-credential',
                               name=update['name'],
                               credential_pairs=update['credential_pairs'],
                               credential_keys=update['credential_keys'],
                               metadata=update['metadata'],
                               revision=revision,
                               enabled=update['enabled'],
                               data_key=update['data_key'],
                               cipher_type=update['cipher_type'],
                               cipher_version=update['cipher_version'],
                               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 blind-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('blind_credential_update', service_names, [cred.id])
    return jsonify({
        'id': cred.id,
        'name': cred.name,
        'credential_pairs': cred.credential_pairs,
        'credential_keys': list(cred.credential_keys),
        'cipher_type': cred.cipher_type,
        'cipher_version': cred.cipher_version,
        'metadata': cred.metadata,
        'revision': cred.revision,
        'enabled': cred.enabled,
        'data_key': cred.data_key,
        'modified_date': cred.modified_date,
        'modified_by': cred.modified_by,
        'documentation': cred.documentation
    })
Exemple #5
0
def create_credential():
    data = request.get_json()
    if not data.get('documentation') and settings.get('ENFORCE_DOCUMENTATION'):
        return jsonify({'error': 'documentation is a required field'}), 400
    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(),
        documentation=data.get('documentation')).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(),
                      documentation=data.get('documentation'))
    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,
        'documentation':
        cred.documentation
    })
Exemple #6
0
def create_credential():
    '''
    Create a credential using the data provided in the POST body.

    .. :quickref: Credential; Create a credential using the data provided in
                  the post body.

    **Example request**:

    .. sourcecode:: http

       POST /v1/credentials

    :<json string name: The friendly name for the credential. (required)
    :<json Dictionary{string: string} credential_pairs: A dictionary of
      arbitrary key/value pairs to be encrypted at rest. (required)
    :<json Dictionary{string: string} metadata: A dictionary of arbitrary key/
      value pairs for custom per-credential end-user extensions. This is not
      encrypted at rest.
    :<json boolean enabled: Whether or not this credential is enabled.
      (default: true)
    :<json string documentation: End-user provided documentation for this
      credential. (required)

    **Example response**:

    .. sourcecode:: http

       HTTP/1.1 200 OK
       Content-Type: application/json

        {
          "id": "abcd12345bf4f1cafe8e722d3860404",
          "name": "Example Credential",
          "credential_keys": ["example_credential_key"],
          "credential_pairs": {
            "example_credential_key": "example_credential_value"
          },
          "metadata": {
            "example_metadata_key": "example_value"
          },
          "revision": 1,
          "enabled": true,
          "documentation": "Example documentation",
          "modified_date": "2019-12-16T23:16:11.413299+00:00",
          "modified_by": "*****@*****.**",
          "permissions": {
            "metadata": true,
            "get": true,
            "update": true
          }
        }

    :resheader Content-Type: application/json
    :statuscode 200: Success
    :statuscode 400: Invalid input; either the data provided was not in the
                     correct format, or a required field was not provided.
    :statuscode 403: Client does not have access to create credentials.
    '''
    if not acl_module_check(resource_type='credential', action='create'):
        msg = "{} does not have access to create credentials".format(
            authnz.get_logged_in_user())
        error_msg = {'error': msg}
        return jsonify(error_msg), 403

    data = request.get_json()
    if not data.get('documentation') and settings.get('ENFORCE_DOCUMENTATION'):
        return jsonify({'error': 'documentation is a required field'}), 400
    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 = credentialmanager.lowercase_credential_pairs(
        data['credential_pairs'])
    _check, ret = credentialmanager.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)
    last_rotation_date = misc.utcnow()
    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(),
        documentation=data.get('documentation'),
        tags=data.get('tags', []),
        last_rotation_date=last_rotation_date,
    ).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(),
        documentation=data.get('documentation'),
        tags=data.get('tags', []),
        last_rotation_date=last_rotation_date,
    )
    cred.save()
    permissions = {
        'metadata': True,
        'get': True,
        'update': True,
    }
    credential_response = CredentialResponse.from_credential(
        cred,
        include_credential_keys=True,
        include_credential_pairs=True,
    )
    credential_response.permissions = permissions
    return credential_response_schema.dumps(credential_response)
Exemple #7
0
def update_credential(id):
    '''
    Update the provided credential using the data provided in the POST body.

    .. :quickref: Credential; Update the provided credential using the data
                  provided in the post body.

    **Example request**:

    .. sourcecode:: http

       PUT /v1/credentials/abcd12345bf4f1cafe8e722d3860404

    :param id: The credential ID to update.
    :type id: str
    :<json string name: The friendly name for the credential.
    :<json Dictionary{string: string} credential_pairs: A dictionary of
      arbitrary key/value pairs to be encrypted at rest.
    :<json Dictionary{string: string} metadata: A dictionary of arbitrary key/
      value pairs for custom per-credential end-user extensions. This is not
      encrypted at rest.
    :<json boolean enabled: Whether or not this credential is enabled.
    :<json string documentation: End-user provided documentation for this
      credential.

    **Example response**:

    .. sourcecode:: http

       HTTP/1.1 200 OK
       Content-Type: application/json

        {
          "id": "abcd12345bf4f1cafe8e722d3860404",
          "name": "Example Credential",
          "credential_keys": ["example_credential_key"],
          "credential_pairs": {
            "example_credential_key": "example_credential_value"
          },
          "metadata": {
            "example_metadata_key": "example_value"
          },
          "revision": 1,
          "enabled": true,
          "documentation": "Example documentation",
          "modified_date": "2019-12-16T23:16:11.413299+00:00",
          "modified_by": "*****@*****.**",
          "permissions": {
            "metadata": true,
            "get": true,
            "update": true
          }
        }

    :resheader Content-Type: application/json
    :statuscode 200: Success
    :statuscode 400: Invalid input; either the data provided was not in the
                     correct format, or the update would create conflicting
                     credential keys in a mapped service.
    :statuscode 403: Client does not have access to update the provided
                     credential ID.
    '''
    if not acl_module_check(
            resource_type='credential', action='update', resource_id=id):
        msg = "{} does not have access to update credential {}".format(
            authnz.get_logged_in_user(), id)
        error_msg = {'error': msg, 'reference': id}
        return jsonify(error_msg), 403

    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()
    if not isinstance(data.get('metadata', {}), dict):
        return jsonify({'error': 'metadata must be a dict'}), 400

    update = {
        'name': data.get('name', _cred.name),
        'last_rotation_date': _cred.last_rotation_date,
        'credential_pairs': _cred.credential_pairs,
        'enabled': _cred.enabled,
        'metadata': data.get('metadata', _cred.metadata),
        'documentation': data.get('documentation', _cred.documentation),
        'tags': data.get('tags', _cred.tags),
    }
    # 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
    if 'enabled' in data:
        if not isinstance(data['enabled'], bool):
            return jsonify({'error': 'Enabled must be a boolean.'}), 400
        update['enabled'] = data['enabled']

    services = servicemanager.get_services_for_credential(id)
    revision = credentialmanager.get_latest_credential_revision(
        id, _cred.revision)
    if 'credential_pairs' in data:
        # Ensure credential pair keys are lowercase
        credential_pairs = credentialmanager.lowercase_credential_pairs(
            data['credential_pairs'])
        _check, ret = credentialmanager.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 = servicemanager.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

        # If the credential pair passed in the update is different from the
        # decrypted credential pair of the most recent revision, assume that
        # this is a new credential pair and update last_rotation_date
        if credential_pairs != _cred.decrypted_credential_pairs:
            update['last_rotation_date'] = misc.utcnow()
        data_key = keymanager.create_datakey(encryption_context={'id': id})
        cipher = CipherManager(data_key['plaintext'], version=2)
        update['credential_pairs'] = cipher.encrypt(
            json.dumps(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=update['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'],
            tags=update['tags'],
            last_rotation_date=update['last_rotation_date'],
        ).save(id__null=True)
    except PutError as e:
        logger.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=update['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'],
            tags=update['tags'],
            last_rotation_date=update['last_rotation_date'],
        )
        cred.save()
    except PutError as e:
        logger.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])
    permissions = {
        'metadata': True,
        'get': True,
        'update': True,
    }
    credential_response = CredentialResponse.from_credential(
        cred,
        include_credential_keys=True,
        include_credential_pairs=True,
    )
    credential_response.permissions = permissions
    return credential_response_schema.dumps(credential_response)
Exemple #8
0
import boto3
import logging
import redis

from flask import Flask
from flask.ext.session import Session
from flask_sslify import SSLify
from confidant import lru
from confidant import settings

if not settings.get('DEBUG'):
    boto3.set_stream_logger(level=logging.CRITICAL)
    logging.getLogger('botocore').setLevel(logging.CRITICAL)
    logging.getLogger('pynamodb').setLevel(logging.WARNING)
log = logging.getLogger(__name__)

static_folder = settings.get('STATIC_FOLDER')

app = Flask(__name__, static_folder=static_folder)
app.config.from_object(settings)
app.debug = app.config['DEBUG']

if app.config['SSLIFY']:
    sslify = SSLify(app, skips=['healthcheck'])

cache = lru.LRUCache(2048)

if app.config.get('REDIS_URL'):
    app.config['SESSION_REDIS'] = redis.Redis.from_url(
        app.config['REDIS_URL']
    )
Exemple #9
0
import boto3
import logging
import redis

from flask import Flask
from flask.ext.session import Session
from flask_sslify import SSLify
from confidant import lru
from confidant import settings

if not settings.get('DEBUG'):
    boto3.set_stream_logger(level=logging.CRITICAL)
    logging.getLogger('botocore').setLevel(logging.CRITICAL)
    logging.getLogger('pynamodb').setLevel(logging.WARNING)
log = logging.getLogger(__name__)

static_folder = settings.get('STATIC_FOLDER')

app = Flask(__name__, static_folder=static_folder)
app.config.from_object(settings)
app.debug = app.config['DEBUG']

if app.config['SSLIFY']:
    sslify = SSLify(app, skips=['healthcheck'])

cache = lru.LRUCache(2048)

if app.config.get('REDIS_URL'):
    app.config['SESSION_REDIS'] = redis.Redis.from_url(app.config['REDIS_URL'])
    Session(app)
Exemple #10
0
 def __init__(self):
     self.username_header = settings.HEADER_AUTH_USERNAME_HEADER
     self.email_header = settings.HEADER_AUTH_EMAIL_HEADER
     self.first_name_header = settings.get('HEADER_AUTH_FIRST_NAME_HEADER')
     self.last_name_header = settings.get('HEADER_AUTH_LAST_NAME_HEADER')
Exemple #11
0
    def _render_saml_settings_dict(self):
        """
        Given the configuration present in current_app.config, render a
        settings dict suitable for passing to OneLogin_Saml2_Auth() in
        initialization.
        """

        debug = settings.SAML_DEBUG
        if debug is None:
            debug = current_app.debug

        root_url = settings.SAML_CONFIDANT_URL_ROOT
        if not root_url:
            raise ValueError("Must provide SAML_CONFIDANT_URL_ROOT")
        root_url = root_url.rstrip('/')

        # TODO: also support unspecified?
        name_id_fmt = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'

        # Service Provider section
        sp_data = {
            'entityId': root_url + '/v1/saml/metadata',
            'assertionConsumerService': {
                'url': root_url + '/v1/saml/consume',
                'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
            },
            'singleLogoutService': {
                'url': root_url + '/v1/saml/logout',
                'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-REDIRECT'
            },
            'NameIDFormat': name_id_fmt,
        }

        sp_has_key = False
        if settings.SAML_SP_KEY_FILE:
            sp_has_key = True
            sp_data['privateKey'] = self._load_rsa_for_saml(
                settings.SAML_SP_KEY_FILE,
                password=settings.get('SAML_SP_KEY_FILE_PASSWORD'))
        if settings.SAML_SP_KEY:
            sp_has_key = True
            sp_data['privateKey'] = settings.SAML_SP_KEY

        if settings.SAML_SP_CERT_FILE:
            sp_data['x509cert'] = self._load_x509_for_saml(
                settings.SAML_SP_CERT_FILE)
        if settings.SAML_SP_CERT:
            sp_data['x509cert'] = settings.SAML_SP_CERT

        # security defaults: sign everything if SP key was provided
        security_data = {
            'nameIdEncrypted': False,
            'authnRequestsSigned': sp_has_key,
            'logoutRequestsSigned': sp_has_key,
            'logoutResponsesSigned': settings.SAML_SECURITY_SLO_RESP_SIGNED,
            'signMetadata': sp_has_key,
            'wantMessagesSigned': settings.SAML_SECURITY_MESSAGES_SIGNED,
            'wantAssertionsSigned': settings.SAML_SECURITY_ASSERTIONS_SIGNED,
            'wantNameIdEncrypted': False,
            'wantAttributeStatement': settings.SAML_WANT_ATTRIBUTE_STATEMENT,
            "signatureAlgorithm": settings.SAML_SECURITY_SIG_ALGO,
        }

        # Identity provider section
        idp_data = {
            'entityId': settings.SAML_IDP_ENTITY_ID,
            'singleSignOnService': {
                'url': settings.SAML_IDP_SIGNON_URL,
                'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
            },
        }

        if settings.SAML_IDP_LOGOUT_URL:
            idp_data['singleLogoutService'] = {
                'url': settings.SAML_IDP_LOGOUT_URL,
                'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
            }

        if settings.SAML_IDP_CERT_FILE:
            idp_data['x509cert'] = self._load_x509_for_saml(
                settings.SAML_IDP_CERT_FILE)
        if settings.SAML_IDP_CERT:
            idp_data['x509cert'] = settings.SAML_IDP_CERT

        # put it all together into the settings
        data = {
            'strict': True,  # must not be changed for security
            'debug': debug,
            'sp': sp_data,
            'idp': idp_data,
            'security': security_data,
        }

        # if SAML_RAW_JSON_SETTINGS is set, merge the settings in, doing one
        # level of deep merging.
        if settings.SAML_RAW_JSON_SETTINGS:
            logging.debug('overriding SAML settings from JSON')
            dict_deep_update(data, settings.SAML_RAW_JSON_SETTINGS)

        logging.debug('Rendered SAML settings: {!r}'.format(data))

        return data
Exemple #12
0
import guard

from confidant import app, settings

CSP_POLICY = {
    'default-src': ["'self'"],
    'style-src': [
        "'self'",
        "'unsafe-inline'"  # for spin.js
        ]
    }

app.wsgi_app = guard.ContentSecurityPolicy(app.wsgi_app, CSP_POLICY)

if __name__ == '__main__':
    app.run(
        host='0.0.0.0',
        port=settings.get('PORT', 5000),
        debug=settings.get('DEBUG', True))
Exemple #13
0
def create_credential():
    if not acl_module_check(resource_type='credential', action='create'):
        msg = "{} does not have access to create credentials".format(
            authnz.get_logged_in_user())
        error_msg = {'error': msg}
        return jsonify(error_msg), 403

    data = request.get_json()
    if not data.get('documentation') and settings.get('ENFORCE_DOCUMENTATION'):
        return jsonify({'error': 'documentation is a required field'}), 400
    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 = credentialmanager.lowercase_credential_pairs(
        data['credential_pairs'])
    _check, ret = credentialmanager.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(),
        documentation=data.get('documentation')).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(),
                      documentation=data.get('documentation'))
    cred.save()
    permissions = {
        'metadata': True,
        'get': True,
        'update': True,
    }
    credential_response = CredentialResponse.from_credential(
        cred,
        include_credential_keys=True,
        include_credential_pairs=True,
    )
    credential_response.permissions = permissions
    return credential_response_schema.dumps(credential_response)
Exemple #14
0
def update_credential(id):
    #credentialid가져오는 중 없을 경우 error발생 예외처리
    try:
        _cred = Credential.get(id)
    except DoesNotExist:
        return jsonify({'error': 'Credential not found.'}), 404
    #자료의 종류가 credential인지 아닌지 check 아닐 경우 error msg와 함께 리턴
    if _cred.data_type != 'credential':
        msg = 'id provided is not a credential.'
        return jsonify({'error': msg}), 400
    #데이터 갖고온다
    data = request.get_json()
    #update라는 dictionary생성
    update = {}
    #가장 최근의 revision을 가져온다
    revision = _get_latest_credential_revision(id, _cred.revision)
    #이름 업데이트
    update['name'] = data.get('name', _cred.name)
    #data에 'enabled'이 있을 경우 제어문 안으로
    if 'enabled' in data:
        #허용 안되는 부분이 bool 인스턴스가 아니라면 error발생
        if not isinstance(data['enabled'], bool):
            return jsonify({'error': 'Enabled must be a boolean.'}), 400
        #'enabled'data 대입
        update['enabled'] = data['enabled']
    else:
        #data에 없다면 _cred에서 대입
        update['enabled'] = _cred.enabled
    #metadata가 dictionary타입이 아닌 경우 error발생
    if not isinstance(data.get('metadata', {}), dict):
        return jsonify({'error': 'metadata must be a dict'}), 400
    #credential의 서비스 가져오기
    services = _get_services_for_credential(id)
    #data에 credential_pairs가 있다면 제어문 안으로
    if 'credential_pairs' in data:
        # Ensure credential pair keys are lowercase
        # credential pair key가 소문자인지 확실하게 하기 위해 소문자로 만드는 함수를 사용해 credential_pairs가져오기
        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,
            credential_pairs.keys(),
            services
        )
        # 충돌한다면 error발생
        if conflicts:
            ret = {
                'error': 'Conflicting key pairs in mapped service.',
                'conflicts': conflicts
            }
            return jsonify(ret), 400
        update['credential_pairs'] = json.dumps(credential_pairs)
    #data에 credential_pairs가 없다면 제어문 안으로
    else:
    #key생성
        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문
    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])
    #update된 내용 리턴
    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 #15
0
import guard

from confidant import app, settings

CSP_POLICY = {
    'default-src': ["'self'"],
    'style-src': [
        "'self'",
        "'unsafe-inline'"  # for spin.js
    ]
}

app.wsgi_app = guard.ContentSecurityPolicy(app.wsgi_app, CSP_POLICY)

if __name__ == '__main__':
    app.run(host='0.0.0.0',
            port=settings.get('PORT', 5000),
            debug=settings.get('DEBUG', True))
Exemple #16
0
from confidant import settings
from confidant.app import create_app

if __name__ == '__main__':
    app = create_app()
    app.run(
        host=settings.get('HOST', '127.0.0.1'),
        port=settings.get('PORT', 5000),
        debug=settings.get('DEBUG', True)
    )