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
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 })
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 })
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 })
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 })
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)
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)
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'] )
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)
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')
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
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))
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)
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 })
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))
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) )