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
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
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 })
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) })
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 ) })
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 })
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))
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)
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)
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 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 })
def get_archive_credential_revisions(id): """ Returns a list of the metadata of all the revisions of the provided credential. .. :quickref: Credential Revisions; Get a list of the metadata of all the revisions of the provided credential. **Example request**: .. sourcecode:: http GET /v1/archive/credentials/abcd12345bf4f1cafe8e722d3860404 :query string next_page: If paged results were returned in a call, this query string can be used to fetch the next page. **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "revisions": [ { "id": "abcd12345bf4f1cafe8e722d3860404-1", "name": "Example Credential", "credential_keys": [], "credential_pairs": {}, "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": {} }, ... ], next_page: null } :resheader Content-Type: application/json :statuscode 200: Success :statuscode 403: Client does not have permissions to get credential metadata for the provided credential ID. :statuscode 404: The provided credential ID does not exist. """ 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: logger.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_response = RevisionsResponse.from_credentials( Credential.batch_get( credentialmanager.get_revision_ids_for_credential(cred))) return revisions_response_schema.dumps(revisions_response)
def diff_credential(id, old_revision, new_revision): """ Returns a diff between old_revision and new_revision for the provided credential id. .. :quickref: Credential Diff; Get a diff of two revisions of a credential. **Example request**: .. sourcecode:: http GET /v1/credentials/abcd12345bf4f1cafe8e722d3860404/1/2 :param id: The credential ID to get. :type id: str :param old_revision: One of the two revisions to diff against. :type old_revision: int :param new_revision: One of the two revisions to diff against. :type new_revision: int **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "name": { "added": "New credential name", "removed": "Example credential name" }, "credential_pairs": { "added": [ "api_key", "api_user" ], "removed": [ "api_certificate" ] }, "metadata": { "added": "example_key" }, "enabled": { "added": false, "removed": true }, "documentation": { "added": "The way you rotate this credential is to...", "removed": "Example documentation" }, "modified_date": { "added": "2019-12-16T23:16:11.413299+00:00", "removed": "2019-11-16T23:16:11.413299+00:00" }, "modified_by": { "added": "*****@*****.**", "removed": "*****@*****.**" } } :resheader Content-Type: application/json :statuscode 200: Success :statuscode 403: Client does not have permissions to diff the provided credential ID. :statuscode 404: The provided credential ID or one of the provided revisions does not exist. """ 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: logger.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))
def get_credential(id): """ Returns a credential object for the provided credential id. .. :quickref: Credential; Get a credential from the provided id. **Example request**: .. sourcecode:: http GET /v1/credentials/abcd12345bf4f1cafe8e722d3860404 :param id: The credential ID to get. :type id: str **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "id": "...", "name": "Example Credential", "credential_keys": [ "api_key", "api_user" ], "credential_pairs": { "api_key": "1234", "api_user": "******" }, "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 403: Client does not have permissions to get the credential for the provided ID. :statuscode 404: The provided credential ID does not exist. """ metadata_only = misc.get_boolean(request.args.get('metadata_only')) 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: logger.warning('Item with id {0} does not exist.'.format(id)) return jsonify({}), 404 if credential.data_type != '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 not metadata_only and acl_module_check( resource_type='credential', action='get', resource_id=id): permissions['get'] = True include_credential_pairs = True if settings.ENABLE_SAVE_LAST_DECRYPTION_TIME: # Also try to save the archived credential to stay consistent try: archived_credential = Credential.get('{}-{}'.format( id, credential.revision)) except DoesNotExist: archived_credential = None logger.error('Archived credential {}-{} not found'.format( id, credential.revision)) now = misc.utcnow() credential.last_decrypted_date = now credential.save() if archived_credential: archived_credential.last_decrypted_date = now archived_credential.save() log_line = "{0} get credential {1}".format(authnz.get_logged_in_user(), id) logger.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)
def revert_credential_to_revision(id, to_revision): if not acl_module_check( resource_type='credential', action='revert', resource_id=id): msg = "{} does not have access to revert credential {}".format( authnz.get_logged_in_user(), id) error_msg = {'error': msg, 'reference': id} return jsonify(error_msg), 403 try: current_credential = Credential.get(id) except DoesNotExist: return jsonify({'error': 'Credential not found.'}), 404 if current_credential.data_type != 'credential': msg = 'id provided is not a credential.' return jsonify({'error': msg}), 400 new_revision = credentialmanager.get_latest_credential_revision( id, current_credential.revision) try: revert_credential = Credential.get('{}-{}'.format(id, to_revision)) except DoesNotExist: logging.warning('Item with id {0} does not exist.'.format(id)) return jsonify({}), 404 if revert_credential.data_type != 'archive-credential': msg = 'id provided is not a credential.' return jsonify({'error': msg}), 400 if revert_credential.equals(current_credential): ret = {'error': 'No difference between old and new credential.'} return jsonify(ret), 400 services = servicemanager.get_services_for_credential(id) if revert_credential.credential_pairs: _credential_pairs = revert_credential.decrypted_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 # Try to save to the archive try: Credential( id='{0}-{1}'.format(id, new_revision), name=revert_credential.name, data_type='archive-credential', credential_pairs=revert_credential.credential_pairs, metadata=revert_credential.metadata, enabled=revert_credential.enabled, revision=new_revision, data_key=revert_credential.data_key, cipher_version=revert_credential.cipher_version, modified_by=authnz.get_logged_in_user(), documentation=revert_credential.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=revert_credential.name, data_type='credential', credential_pairs=revert_credential.credential_pairs, metadata=revert_credential.metadata, enabled=revert_credential.enabled, revision=new_revision, data_key=revert_credential.data_key, cipher_version=revert_credential.cipher_version, modified_by=authnz.get_logged_in_user(), documentation=revert_credential.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 credential_response_schema.dumps( CredentialResponse.from_credential(cred))
def credential_exists(self, credential_id): try: Credential.get(credential_id) return True except DoesNotExist: return False
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)
def revert_credential_to_revision(id, to_revision): ''' Revert the provided credential to the provided revision. .. :quickref: Credential; Revert the provided credential to the provided revision **Example request**: .. sourcecode:: http PUT /v1/credentials/abcd12345bf4f1cafe8e722d3860404/1 :param id: The credential ID to revert. :type id: str :param to_revision: The revision to revert this credential to. :type to_revision: int **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": {}, "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; the update would create conflicting credential keys in a mapped service. :statuscode 403: Client does not have access to revert the provided credential ID. ''' if not acl_module_check( resource_type='credential', action='revert', resource_id=id): msg = "{} does not have access to revert credential {}".format( authnz.get_logged_in_user(), id) error_msg = {'error': msg, 'reference': id} return jsonify(error_msg), 403 try: current_credential = Credential.get(id) except DoesNotExist: return jsonify({'error': 'Credential not found.'}), 404 if current_credential.data_type != 'credential': msg = 'id provided is not a credential.' return jsonify({'error': msg}), 400 new_revision = credentialmanager.get_latest_credential_revision( id, current_credential.revision) try: revert_credential = Credential.get('{}-{}'.format(id, to_revision)) except DoesNotExist: logger.warning('Item with id {0} does not exist.'.format(id)) return jsonify({}), 404 if revert_credential.data_type != 'archive-credential': msg = 'id provided is not a credential.' return jsonify({'error': msg}), 400 if revert_credential.equals(current_credential): ret = {'error': 'No difference between old and new credential.'} return jsonify(ret), 400 services = servicemanager.get_services_for_credential(id) if revert_credential.credential_pairs: _credential_pairs = revert_credential.decrypted_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 # Try to save to the archive try: Credential( id='{0}-{1}'.format(id, new_revision), name=revert_credential.name, data_type='archive-credential', credential_pairs=revert_credential.credential_pairs, metadata=revert_credential.metadata, enabled=revert_credential.enabled, revision=new_revision, data_key=revert_credential.data_key, cipher_version=revert_credential.cipher_version, modified_by=authnz.get_logged_in_user(), documentation=revert_credential.documentation, tags=revert_credential.tags, last_rotation_date=revert_credential.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=revert_credential.name, data_type='credential', credential_pairs=revert_credential.credential_pairs, metadata=revert_credential.metadata, enabled=revert_credential.enabled, revision=new_revision, data_key=revert_credential.data_key, cipher_version=revert_credential.cipher_version, modified_by=authnz.get_logged_in_user(), documentation=revert_credential.documentation, tags=revert_credential.tags, last_rotation_date=revert_credential.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]) return credential_response_schema.dumps( CredentialResponse.from_credential(cred))
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 })
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: log.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: log.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 })