)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"5f60718a095c4c823673a4692549376f0a0d4653","unresolved":false,"context_lines":[{"line_number":15,"context_line":""},{"line_number":16,"context_line":"All credentials will be encrypted by default. There will not be a way to store"},{"line_number":17,"context_line":"unencrypted credentials in keystone from this point forward."},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"bp credential-encryption"},{"line_number":20,"context_line":"Change-Id: I31b7539db436ad270462cfaa3b14213e0ed1fc04"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":8,"id":"1ac06dbe_aae555a9","line":18,"updated":"2016-08-19 15:25:45.000000000","message":"Reminder for myself to add Werner as a co-author!","commit_id":"b739ad127d5e1059f27700e860ecabdd18aa7935"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"045dcdf2dfd80a1074c3f23008a2ded194f83845","unresolved":false,"context_lines":[{"line_number":15,"context_line":""},{"line_number":16,"context_line":"All credentials will be encrypted by default. There will not be a way to store"},{"line_number":17,"context_line":"unencrypted credentials in keystone from this point forward."},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"bp credential-encryption"},{"line_number":20,"context_line":"Change-Id: I31b7539db436ad270462cfaa3b14213e0ed1fc04"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":8,"id":"1ac06dbe_dcca0d4f","line":18,"in_reply_to":"1ac06dbe_aae555a9","updated":"2016-08-19 20:28:52.000000000","message":"Done","commit_id":"b739ad127d5e1059f27700e860ecabdd18aa7935"}],"keystone/cmd/cli.py":[{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"5c8d8fdaae762004d50ace8e272f1ce1d0b9b998","unresolved":false,"context_lines":[{"line_number":677,"context_line":"        from keystone.common import fernet_utils as utils"},{"line_number":678,"context_line":"        fernet_utils \u003d utils.FernetUtils("},{"line_number":679,"context_line":"            CONF.fernet_tokens.key_repository,"},{"line_number":680,"context_line":"            CONF.fernet_tokens.max_active_keys"},{"line_number":681,"context_line":"        )"},{"line_number":682,"context_line":""},{"line_number":683,"context_line":"        keystone_user_id, keystone_group_id \u003d cls.get_user_group()"}],"source_content_type":"text/x-python","patch_set":8,"id":"1ac06dbe_f6460abe","line":680,"updated":"2016-08-19 19:58:34.000000000","message":"This is pointing to the wrong repository.","commit_id":"b739ad127d5e1059f27700e860ecabdd18aa7935"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"045dcdf2dfd80a1074c3f23008a2ded194f83845","unresolved":false,"context_lines":[{"line_number":677,"context_line":"        from keystone.common import fernet_utils as utils"},{"line_number":678,"context_line":"        fernet_utils \u003d utils.FernetUtils("},{"line_number":679,"context_line":"            CONF.fernet_tokens.key_repository,"},{"line_number":680,"context_line":"            CONF.fernet_tokens.max_active_keys"},{"line_number":681,"context_line":"        )"},{"line_number":682,"context_line":""},{"line_number":683,"context_line":"        keystone_user_id, keystone_group_id \u003d cls.get_user_group()"}],"source_content_type":"text/x-python","patch_set":8,"id":"1ac06dbe_7cb959e9","line":680,"in_reply_to":"1ac06dbe_f6460abe","updated":"2016-08-19 20:28:52.000000000","message":"Done","commit_id":"b739ad127d5e1059f27700e860ecabdd18aa7935"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"b0bff8b868e1aea0fc4d1218c354e1be5844515f","unresolved":false,"context_lines":[{"line_number":642,"context_line":"    active key size of the credential repository is 3, once all credentials are"},{"line_number":643,"context_line":"    encrypted with the latest primary key we can safely introduce a new primary"},{"line_number":644,"context_line":"    key. All credentials will still be decryptable since they are all encrypted"},{"line_number":645,"context_line":"    with the only secondary key in the repository."},{"line_number":646,"context_line":""},{"line_number":647,"context_line":"    The `keystone-manage credential_rotate` and `keystone-manage"},{"line_number":648,"context_line":"    credential_migrate` are implemented to be done in lock-step. After"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_52918546","line":645,"updated":"2016-08-22 21:56:11.000000000","message":"++","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"b0bff8b868e1aea0fc4d1218c354e1be5844515f","unresolved":false,"context_lines":[{"line_number":645,"context_line":"    with the only secondary key in the repository."},{"line_number":646,"context_line":""},{"line_number":647,"context_line":"    The `keystone-manage credential_rotate` and `keystone-manage"},{"line_number":648,"context_line":"    credential_migrate` are implemented to be done in lock-step. After"},{"line_number":649,"context_line":"    performing a rotation, a migration must be done before performing another"},{"line_number":650,"context_line":"    rotation. This ensures we don\u0027t over-rotate."},{"line_number":651,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_5265051c","line":648,"updated":"2016-08-22 21:56:11.000000000","message":"nit: \"are intended to be done in sequence.\" would be a little more clear to me, if I\u0027m understanding this correctly.","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"836ef615c47339a96a983a58df515efbecfc5314","unresolved":false,"context_lines":[{"line_number":645,"context_line":"    with the only secondary key in the repository."},{"line_number":646,"context_line":""},{"line_number":647,"context_line":"    The `keystone-manage credential_rotate` and `keystone-manage"},{"line_number":648,"context_line":"    credential_migrate` are implemented to be done in lock-step. After"},{"line_number":649,"context_line":"    performing a rotation, a migration must be done before performing another"},{"line_number":650,"context_line":"    rotation. This ensures we don\u0027t over-rotate."},{"line_number":651,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_b546b54c","line":648,"in_reply_to":"1ac06dbe_5265051c","updated":"2016-08-23 14:35:00.000000000","message":"Done","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"b0bff8b868e1aea0fc4d1218c354e1be5844515f","unresolved":false,"context_lines":[{"line_number":690,"context_line":"class CredentialMigrate(BasePermissionsSetup):"},{"line_number":691,"context_line":"    \"\"\"Provides ability to encrypt credentials using a new primary key."},{"line_number":692,"context_line":""},{"line_number":693,"context_line":"    This assumes that there is already a credential key repository in place."},{"line_number":694,"context_line":"    You can use ``keystone-manage credential_setup`` to create one."},{"line_number":695,"context_line":"    \"\"\""},{"line_number":696,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_159ed762","line":693,"updated":"2016-08-22 21:56:11.000000000","message":"And that you\u0027ve have fully upgraded to at least the newton schema -- this should not be done during the upgrade process (before contract has run).","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"836ef615c47339a96a983a58df515efbecfc5314","unresolved":false,"context_lines":[{"line_number":690,"context_line":"class CredentialMigrate(BasePermissionsSetup):"},{"line_number":691,"context_line":"    \"\"\"Provides ability to encrypt credentials using a new primary key."},{"line_number":692,"context_line":""},{"line_number":693,"context_line":"    This assumes that there is already a credential key repository in place."},{"line_number":694,"context_line":"    You can use ``keystone-manage credential_setup`` to create one."},{"line_number":695,"context_line":"    \"\"\""},{"line_number":696,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_b5b99517","line":693,"in_reply_to":"1ac06dbe_159ed762","updated":"2016-08-23 14:35:00.000000000","message":"Done","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"b0bff8b868e1aea0fc4d1218c354e1be5844515f","unresolved":false,"context_lines":[{"line_number":708,"context_line":""},{"line_number":709,"context_line":"        # FIXME(lbragstad): We *should* be able to use Hints() to ask only for"},{"line_number":710,"context_line":"        # credentials that have a key_hash equal to a secondary key hash or"},{"line_number":711,"context_line":"        # None, but Hints() doens\u0027t seem to honor None values. The following"},{"line_number":712,"context_line":"        # commented out code should work. See"},{"line_number":713,"context_line":"        # https://bugs.launchpad.net/keystone/+bug/1614154"},{"line_number":714,"context_line":"        #"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_b5c9c35b","line":711,"updated":"2016-08-22 21:56:11.000000000","message":"How do you pronounce doens\u0027t?","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"3d79280eef1c89f11846fd61cc17e10512e8bd54","unresolved":false,"context_lines":[{"line_number":708,"context_line":""},{"line_number":709,"context_line":"        # FIXME(lbragstad): We *should* be able to use Hints() to ask only for"},{"line_number":710,"context_line":"        # credentials that have a key_hash equal to a secondary key hash or"},{"line_number":711,"context_line":"        # None, but Hints() doens\u0027t seem to honor None values. The following"},{"line_number":712,"context_line":"        # commented out code should work. See"},{"line_number":713,"context_line":"        # https://bugs.launchpad.net/keystone/+bug/1614154"},{"line_number":714,"context_line":"        #"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_302e81a5","line":711,"in_reply_to":"1ac06dbe_b5c9c35b","updated":"2016-08-22 22:05:38.000000000","message":"A lot of practice... beer helps.. ;)","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"836ef615c47339a96a983a58df515efbecfc5314","unresolved":false,"context_lines":[{"line_number":708,"context_line":""},{"line_number":709,"context_line":"        # FIXME(lbragstad): We *should* be able to use Hints() to ask only for"},{"line_number":710,"context_line":"        # credentials that have a key_hash equal to a secondary key hash or"},{"line_number":711,"context_line":"        # None, but Hints() doens\u0027t seem to honor None values. The following"},{"line_number":712,"context_line":"        # commented out code should work. See"},{"line_number":713,"context_line":"        # https://bugs.launchpad.net/keystone/+bug/1614154"},{"line_number":714,"context_line":"        #"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_754a7d27","line":711,"in_reply_to":"1ac06dbe_b5c9c35b","updated":"2016-08-23 14:35:00.000000000","message":"Done","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"a6b54e54fae3b5f43c6ea172cdeacadefb4cea7b","unresolved":false,"context_lines":[{"line_number":641,"context_line":"    active key size of the credential repository is 3, once all credentials are"},{"line_number":642,"context_line":"    encrypted with the latest primary key we can safely introduce a new primary"},{"line_number":643,"context_line":"    key. All credentials will still be decryptable since they are all encrypted"},{"line_number":644,"context_line":"    with the only secondary key in the repository."},{"line_number":645,"context_line":""},{"line_number":646,"context_line":"    The `keystone-manage credential_rotate` and `keystone-manage"},{"line_number":647,"context_line":"    credential_migrate` commands are intended to be done in sequence. After"}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_b515244a","line":644,"updated":"2016-08-29 16:04:42.000000000","message":"It may be worth stating that saving the created keys into a secure key storage system would allow for data recovery if something ever happens to a key on the like systems.","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0ac6d620655d55d1c3dde90839b0d1f91fc45859","unresolved":false,"context_lines":[{"line_number":641,"context_line":"    active key size of the credential repository is 3, once all credentials are"},{"line_number":642,"context_line":"    encrypted with the latest primary key we can safely introduce a new primary"},{"line_number":643,"context_line":"    key. All credentials will still be decryptable since they are all encrypted"},{"line_number":644,"context_line":"    with the only secondary key in the repository."},{"line_number":645,"context_line":""},{"line_number":646,"context_line":"    The `keystone-manage credential_rotate` and `keystone-manage"},{"line_number":647,"context_line":"    credential_migrate` commands are intended to be done in sequence. After"}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_2072ad7b","line":644,"in_reply_to":"fa7ab95a_b515244a","updated":"2016-08-29 16:47:36.000000000","message":"Done","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"a6b54e54fae3b5f43c6ea172cdeacadefb4cea7b","unresolved":false,"context_lines":[{"line_number":659,"context_line":""},{"line_number":660,"context_line":"    def validate_primary_key(self):"},{"line_number":661,"context_line":"        primary_key_hash \u003d self.credential_provider_api.primary_key_hash"},{"line_number":662,"context_line":"        credentials \u003d self.credential_api.driver.list_credentials("},{"line_number":663,"context_line":"            driver_hints.Hints()"},{"line_number":664,"context_line":"        )"},{"line_number":665,"context_line":"        for credential in credentials:"}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_07230357","line":662,"updated":"2016-08-29 16:04:42.000000000","message":"Can we make the faster by selecting a count using the primary_key_hash in the \u0027where\u0027 clause? This way we don\u0027t have to down load all (or any) of the credentials to do the check.","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0ac6d620655d55d1c3dde90839b0d1f91fc45859","unresolved":false,"context_lines":[{"line_number":659,"context_line":""},{"line_number":660,"context_line":"    def validate_primary_key(self):"},{"line_number":661,"context_line":"        primary_key_hash \u003d self.credential_provider_api.primary_key_hash"},{"line_number":662,"context_line":"        credentials \u003d self.credential_api.driver.list_credentials("},{"line_number":663,"context_line":"            driver_hints.Hints()"},{"line_number":664,"context_line":"        )"},{"line_number":665,"context_line":"        for credential in credentials:"}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_e0852507","line":662,"in_reply_to":"fa7ab95a_07230357","updated":"2016-08-29 16:47:36.000000000","message":"Yep - we essentially want to ask the credential backend for every credential  that has key_hash !\u003d to primary_key_hash. I originally tried to do this with driver_hints but we need to fix driver hints to be able to support NotEqual checks I think. Otherwise we have to ask for everything that matches the primary key (using hints) then ask for all the credentials anyway to figure out the delta ourselves.","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"a6b54e54fae3b5f43c6ea172cdeacadefb4cea7b","unresolved":false,"context_lines":[{"line_number":676,"context_line":"        from keystone.common import fernet_utils as utils"},{"line_number":677,"context_line":"        fernet_utils \u003d utils.FernetUtils("},{"line_number":678,"context_line":"            CONF.credential.key_repository,"},{"line_number":679,"context_line":"            credential_provider.MAX_ACTIVE_KEYS"},{"line_number":680,"context_line":"        )"},{"line_number":681,"context_line":""},{"line_number":682,"context_line":"        keystone_user_id, keystone_group_id \u003d cls.get_user_group()"}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_4ac35238","line":679,"updated":"2016-08-29 16:04:42.000000000","message":"Where does this come from?","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0ac6d620655d55d1c3dde90839b0d1f91fc45859","unresolved":false,"context_lines":[{"line_number":676,"context_line":"        from keystone.common import fernet_utils as utils"},{"line_number":677,"context_line":"        fernet_utils \u003d utils.FernetUtils("},{"line_number":678,"context_line":"            CONF.credential.key_repository,"},{"line_number":679,"context_line":"            credential_provider.MAX_ACTIVE_KEYS"},{"line_number":680,"context_line":"        )"},{"line_number":681,"context_line":""},{"line_number":682,"context_line":"        keystone_user_id, keystone_group_id \u003d cls.get_user_group()"}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_3bc74265","line":679,"in_reply_to":"fa7ab95a_4ac35238","updated":"2016-08-29 16:47:36.000000000","message":"Bad refactor - fixed.","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"a6b54e54fae3b5f43c6ea172cdeacadefb4cea7b","unresolved":false,"context_lines":[{"line_number":722,"context_line":"        )"},{"line_number":723,"context_line":"        for credential in credentials:"},{"line_number":724,"context_line":"            if credential[\u0027key_hash\u0027] !\u003d primary_key_hash:"},{"line_number":725,"context_line":"                # If the key_hash isn\u0027t None but doens\u0027t match the"},{"line_number":726,"context_line":"                # primary_key_hash, then we know the credential was encrypted"},{"line_number":727,"context_line":"                # with a secondary key. Let\u0027s decrypt it, and send it through"},{"line_number":728,"context_line":"                # the update path to re-encrypt it with the new primary key."}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_a73ed789","line":725,"updated":"2016-08-29 16:04:42.000000000","message":"You\u0027re not checking for None here. Do you need to be? If it is None you wouldn\u0027t need to do the decrypt step right?\n\n\"doesn\u0027t\"","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0ac6d620655d55d1c3dde90839b0d1f91fc45859","unresolved":false,"context_lines":[{"line_number":722,"context_line":"        )"},{"line_number":723,"context_line":"        for credential in credentials:"},{"line_number":724,"context_line":"            if credential[\u0027key_hash\u0027] !\u003d primary_key_hash:"},{"line_number":725,"context_line":"                # If the key_hash isn\u0027t None but doens\u0027t match the"},{"line_number":726,"context_line":"                # primary_key_hash, then we know the credential was encrypted"},{"line_number":727,"context_line":"                # with a secondary key. Let\u0027s decrypt it, and send it through"},{"line_number":728,"context_line":"                # the update path to re-encrypt it with the new primary key."}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_7bbc6adc","line":725,"in_reply_to":"fa7ab95a_a73ed789","updated":"2016-08-29 16:47:36.000000000","message":"Nope - we shouldn\u0027t have to check for None here because the data_migration will take every credential and encrypt it, which will populate the key_hash column of each row in the credential table.","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"a6b54e54fae3b5f43c6ea172cdeacadefb4cea7b","unresolved":false,"context_lines":[{"line_number":740,"context_line":"    def main(cls):"},{"line_number":741,"context_line":"        # Check to make sure we have a repository that works..."},{"line_number":742,"context_line":"        from keystone.common import fernet_utils as utils"},{"line_number":743,"context_line":"        keystone_user_id, keystone_group_id \u003d cls.get_user_group()"},{"line_number":744,"context_line":"        fernet_utils \u003d utils.FernetUtils("},{"line_number":745,"context_line":"            CONF.credential.key_repository,"},{"line_number":746,"context_line":"            credential_provider.MAX_ACTIVE_KEYS"}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_47c34b38","line":743,"updated":"2016-08-29 16:04:42.000000000","message":"Is this actually needed here?","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0ac6d620655d55d1c3dde90839b0d1f91fc45859","unresolved":false,"context_lines":[{"line_number":740,"context_line":"    def main(cls):"},{"line_number":741,"context_line":"        # Check to make sure we have a repository that works..."},{"line_number":742,"context_line":"        from keystone.common import fernet_utils as utils"},{"line_number":743,"context_line":"        keystone_user_id, keystone_group_id \u003d cls.get_user_group()"},{"line_number":744,"context_line":"        fernet_utils \u003d utils.FernetUtils("},{"line_number":745,"context_line":"            CONF.credential.key_repository,"},{"line_number":746,"context_line":"            credential_provider.MAX_ACTIVE_KEYS"}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_00de89dc","line":743,"in_reply_to":"fa7ab95a_47c34b38","updated":"2016-08-29 16:47:36.000000000","message":"Nope - not needed. Good catch.","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"a6b54e54fae3b5f43c6ea172cdeacadefb4cea7b","unresolved":false,"context_lines":[{"line_number":743,"context_line":"        keystone_user_id, keystone_group_id \u003d cls.get_user_group()"},{"line_number":744,"context_line":"        fernet_utils \u003d utils.FernetUtils("},{"line_number":745,"context_line":"            CONF.credential.key_repository,"},{"line_number":746,"context_line":"            credential_provider.MAX_ACTIVE_KEYS"},{"line_number":747,"context_line":"        )"},{"line_number":748,"context_line":"        fernet_utils.validate_key_repository(requires_write\u003dTrue)"},{"line_number":749,"context_line":"        klass \u003d cls()"}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_aa0bfe8c","line":746,"updated":"2016-08-29 16:04:42.000000000","message":"Where does this come from?","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0ac6d620655d55d1c3dde90839b0d1f91fc45859","unresolved":false,"context_lines":[{"line_number":743,"context_line":"        keystone_user_id, keystone_group_id \u003d cls.get_user_group()"},{"line_number":744,"context_line":"        fernet_utils \u003d utils.FernetUtils("},{"line_number":745,"context_line":"            CONF.credential.key_repository,"},{"line_number":746,"context_line":"            credential_provider.MAX_ACTIVE_KEYS"},{"line_number":747,"context_line":"        )"},{"line_number":748,"context_line":"        fernet_utils.validate_key_repository(requires_write\u003dTrue)"},{"line_number":749,"context_line":"        klass \u003d cls()"}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_00cda965","line":746,"in_reply_to":"fa7ab95a_aa0bfe8c","updated":"2016-08-29 16:47:36.000000000","message":"Yep this was a bad refactor - fixed.","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"bcb341d25d94a5b7c2c09c17dba7695b89d134d1","unresolved":false,"context_lines":[{"line_number":635,"context_line":"    encrypted with the current primary key. If any credentials are encrypted"},{"line_number":636,"context_line":"    with a secondary key the rotation will abort. This protects against"},{"line_number":637,"context_line":"    removing a key that is still required to decrypt credentials. Once a key is"},{"line_number":638,"context_line":"    removed from the repository, it is impossible to recover the original data."},{"line_number":639,"context_line":"    To make sure all credentials are encrypted with the latest primary key,"},{"line_number":640,"context_line":"    please see the `keystone-manage credential_migrate` command. Since the"},{"line_number":641,"context_line":"    maximum number of keys in the credential repository is 3, once all"}],"source_content_type":"text/x-python","patch_set":40,"id":"fa7ab95a_80f4b2cf","line":638,"updated":"2016-08-31 19:39:55.000000000","message":"Seems to be missing something like (I suck at words):\n\n....without resorting to restoring keys from backup (more on backup below).","commit_id":"980997e81bd3b24a25acdc4a9ef186db20e056fa"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0f5606d264829e81dc15d79f54bfae29761e93dd","unresolved":false,"context_lines":[{"line_number":635,"context_line":"    encrypted with the current primary key. If any credentials are encrypted"},{"line_number":636,"context_line":"    with a secondary key the rotation will abort. This protects against"},{"line_number":637,"context_line":"    removing a key that is still required to decrypt credentials. Once a key is"},{"line_number":638,"context_line":"    removed from the repository, it is impossible to recover the original data."},{"line_number":639,"context_line":"    To make sure all credentials are encrypted with the latest primary key,"},{"line_number":640,"context_line":"    please see the `keystone-manage credential_migrate` command. Since the"},{"line_number":641,"context_line":"    maximum number of keys in the credential repository is 3, once all"}],"source_content_type":"text/x-python","patch_set":40,"id":"fa7ab95a_38376fb1","line":638,"in_reply_to":"fa7ab95a_80f4b2cf","updated":"2016-09-01 13:38:12.000000000","message":"Done","commit_id":"980997e81bd3b24a25acdc4a9ef186db20e056fa"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"bcb341d25d94a5b7c2c09c17dba7695b89d134d1","unresolved":false,"context_lines":[{"line_number":647,"context_line":"    encrypt credentials. In the event keys are overrotated, applying a key"},{"line_number":648,"context_line":"    repository from backup can help recover otherwise useless credentials."},{"line_number":649,"context_line":"    Persisting snapshots of the key repository in secure and encrypted source"},{"line_number":650,"context_line":"    control, or a dedicated key management system would be good examples of"},{"line_number":651,"context_line":"    encryption key backups."},{"line_number":652,"context_line":""},{"line_number":653,"context_line":"    The `keystone-manage credential_rotate` and `keystone-manage"}],"source_content_type":"text/x-python","patch_set":40,"id":"fa7ab95a_60c40e92","line":650,"updated":"2016-08-31 19:39:55.000000000","message":"s/would be/are/","commit_id":"980997e81bd3b24a25acdc4a9ef186db20e056fa"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0f5606d264829e81dc15d79f54bfae29761e93dd","unresolved":false,"context_lines":[{"line_number":647,"context_line":"    encrypt credentials. In the event keys are overrotated, applying a key"},{"line_number":648,"context_line":"    repository from backup can help recover otherwise useless credentials."},{"line_number":649,"context_line":"    Persisting snapshots of the key repository in secure and encrypted source"},{"line_number":650,"context_line":"    control, or a dedicated key management system would be good examples of"},{"line_number":651,"context_line":"    encryption key backups."},{"line_number":652,"context_line":""},{"line_number":653,"context_line":"    The `keystone-manage credential_rotate` and `keystone-manage"}],"source_content_type":"text/x-python","patch_set":40,"id":"fa7ab95a_b84b7f2a","line":650,"in_reply_to":"fa7ab95a_60c40e92","updated":"2016-09-01 13:38:12.000000000","message":"Done","commit_id":"980997e81bd3b24a25acdc4a9ef186db20e056fa"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"bcb341d25d94a5b7c2c09c17dba7695b89d134d1","unresolved":false,"context_lines":[{"line_number":694,"context_line":""},{"line_number":695,"context_line":""},{"line_number":696,"context_line":"class CredentialMigrate(BasePermissionsSetup):"},{"line_number":697,"context_line":"    \"\"\"Provides ability to encrypt credentials using a new primary key."},{"line_number":698,"context_line":""},{"line_number":699,"context_line":"    This assumes that there is already a credential key repository in place and"},{"line_number":700,"context_line":"    that the database backend has been upgraded to the Newton schema. If the"}],"source_content_type":"text/x-python","patch_set":40,"id":"fa7ab95a_c015dae5","line":697,"updated":"2016-08-31 19:39:55.000000000","message":"the ability","commit_id":"980997e81bd3b24a25acdc4a9ef186db20e056fa"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0f5606d264829e81dc15d79f54bfae29761e93dd","unresolved":false,"context_lines":[{"line_number":694,"context_line":""},{"line_number":695,"context_line":""},{"line_number":696,"context_line":"class CredentialMigrate(BasePermissionsSetup):"},{"line_number":697,"context_line":"    \"\"\"Provides ability to encrypt credentials using a new primary key."},{"line_number":698,"context_line":""},{"line_number":699,"context_line":"    This assumes that there is already a credential key repository in place and"},{"line_number":700,"context_line":"    that the database backend has been upgraded to the Newton schema. If the"}],"source_content_type":"text/x-python","patch_set":40,"id":"fa7ab95a_1868eb86","line":697,"in_reply_to":"fa7ab95a_c015dae5","updated":"2016-09-01 13:38:12.000000000","message":"Done","commit_id":"980997e81bd3b24a25acdc4a9ef186db20e056fa"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"bcb341d25d94a5b7c2c09c17dba7695b89d134d1","unresolved":false,"context_lines":[{"line_number":697,"context_line":"    \"\"\"Provides ability to encrypt credentials using a new primary key."},{"line_number":698,"context_line":""},{"line_number":699,"context_line":"    This assumes that there is already a credential key repository in place and"},{"line_number":700,"context_line":"    that the database backend has been upgraded to the Newton schema. If the"},{"line_number":701,"context_line":"    credential repository doesn\u0027t exist yet, you can use ``keystone-manage"},{"line_number":702,"context_line":"    credential_setup`` to create one."},{"line_number":703,"context_line":""}],"source_content_type":"text/x-python","patch_set":40,"id":"fa7ab95a_60cece84","line":700,"updated":"2016-08-31 19:39:55.000000000","message":"s/to/to at least/ ?","commit_id":"980997e81bd3b24a25acdc4a9ef186db20e056fa"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0f5606d264829e81dc15d79f54bfae29761e93dd","unresolved":false,"context_lines":[{"line_number":697,"context_line":"    \"\"\"Provides ability to encrypt credentials using a new primary key."},{"line_number":698,"context_line":""},{"line_number":699,"context_line":"    This assumes that there is already a credential key repository in place and"},{"line_number":700,"context_line":"    that the database backend has been upgraded to the Newton schema. If the"},{"line_number":701,"context_line":"    credential repository doesn\u0027t exist yet, you can use ``keystone-manage"},{"line_number":702,"context_line":"    credential_setup`` to create one."},{"line_number":703,"context_line":""}],"source_content_type":"text/x-python","patch_set":40,"id":"fa7ab95a_b8f9bfca","line":700,"in_reply_to":"fa7ab95a_60cece84","updated":"2016-09-01 13:38:12.000000000","message":"Done","commit_id":"980997e81bd3b24a25acdc4a9ef186db20e056fa"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"c7794e5c49ed3a4aa778bd9936320dc47fc177a5","unresolved":false,"context_lines":[{"line_number":666,"context_line":"        self.credential_api \u003d drivers[\u0027credential_api\u0027]"},{"line_number":667,"context_line":""},{"line_number":668,"context_line":"    def validate_primary_key(self):"},{"line_number":669,"context_line":"        crypto, keys \u003d credential_fernet.get_multi_fernet_keys()"},{"line_number":670,"context_line":"        primary_key_hash \u003d credential_fernet.primary_key_hash(keys)"},{"line_number":671,"context_line":""},{"line_number":672,"context_line":"        credentials \u003d self.credential_api.driver.list_credentials("}],"source_content_type":"text/x-python","patch_set":49,"id":"fa7ab95a_ba2273a1","line":669,"range":{"start_line":669,"start_character":8,"end_line":669,"end_character":14},"updated":"2016-09-02 01:54:48.000000000","message":"unused variable, prefix with _","commit_id":"2262fa61f6411a2f9f4be25a9ec70d3d444f5423"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"c7794e5c49ed3a4aa778bd9936320dc47fc177a5","unresolved":false,"context_lines":[{"line_number":714,"context_line":"        self.credential_api \u003d drivers[\u0027credential_api\u0027]"},{"line_number":715,"context_line":""},{"line_number":716,"context_line":"    def migrate_credentials(self):"},{"line_number":717,"context_line":"        crypto, keys \u003d credential_fernet.get_multi_fernet_keys()"},{"line_number":718,"context_line":"        primary_key_hash \u003d credential_fernet.primary_key_hash(keys)"},{"line_number":719,"context_line":""},{"line_number":720,"context_line":"        # FIXME(lbragstad): We *should* be able to use Hints() to ask only for"}],"source_content_type":"text/x-python","patch_set":49,"id":"fa7ab95a_3a46634f","line":717,"range":{"start_line":717,"start_character":8,"end_line":717,"end_character":14},"updated":"2016-09-02 01:54:48.000000000","message":"same comment as above","commit_id":"2262fa61f6411a2f9f4be25a9ec70d3d444f5423"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"c7794e5c49ed3a4aa778bd9936320dc47fc177a5","unresolved":false,"context_lines":[{"line_number":726,"context_line":"            driver_hints.Hints()"},{"line_number":727,"context_line":"        )"},{"line_number":728,"context_line":"        for credential in credentials:"},{"line_number":729,"context_line":"            if credential[\u0027key_hash\u0027] !\u003d primary_key_hash:"},{"line_number":730,"context_line":"                # If the key_hash isn\u0027t None but doesn\u0027t match the"},{"line_number":731,"context_line":"                # primary_key_hash, then we know the credential was encrypted"},{"line_number":732,"context_line":"                # with a secondary key. Let\u0027s decrypt it, and send it through"}],"source_content_type":"text/x-python","patch_set":49,"id":"fa7ab95a_dab01746","line":729,"range":{"start_line":729,"start_character":15,"end_line":729,"end_character":37},"updated":"2016-09-02 01:54:48.000000000","message":"so this can be None... okay","commit_id":"2262fa61f6411a2f9f4be25a9ec70d3d444f5423"}],"keystone/common/sql/contract_repo/versions/002_remove_unencrypted_blob_column_from_credential.py":[{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"fe5c767fae4662a09a7d2993d189576b603cff72","unresolved":false,"context_lines":[{"line_number":18,"context_line":"    meta.bind \u003d migrate_engine"},{"line_number":19,"context_line":""},{"line_number":20,"context_line":"    credential_table \u003d sql.Table(\u0027credential\u0027, meta, autoload\u003dTrue)"},{"line_number":21,"context_line":"    credential_table.c.blob.drop()"}],"source_content_type":"text/x-python","patch_set":8,"id":"1ac06dbe_0a073f1d","line":21,"updated":"2016-08-19 19:01:22.000000000","message":"This will need to be updated to remove the read-only credential trigger.","commit_id":"b739ad127d5e1059f27700e860ecabdd18aa7935"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"a4dfed824b0b937ba2eed57803939ca03b8b3871","unresolved":false,"context_lines":[{"line_number":18,"context_line":"    meta.bind \u003d migrate_engine"},{"line_number":19,"context_line":""},{"line_number":20,"context_line":"    credential_table \u003d sql.Table(\u0027credential\u0027, meta, autoload\u003dTrue)"},{"line_number":21,"context_line":"    credential_table.c.blob.drop()"}],"source_content_type":"text/x-python","patch_set":9,"id":"1ac06dbe_dcd16dfd","line":21,"updated":"2016-08-19 20:30:25.000000000","message":"Need to figure out how to remove triggers here.","commit_id":"d0bc6d04ac330f5a8a200c90e3c1c89e0f3d5653"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"b0bff8b868e1aea0fc4d1218c354e1be5844515f","unresolved":false,"context_lines":[{"line_number":21,"context_line":"    credential_table.c.blob.drop()"},{"line_number":22,"context_line":""},{"line_number":23,"context_line":"    if migrate_engine \u003d\u003d \u0027postgresql\u0027:"},{"line_number":24,"context_line":"        stmt \u003d \u0027DROP TRIGGER IF EXISTS credential_read_only on credential\u0027"},{"line_number":25,"context_line":"    else:"},{"line_number":26,"context_line":"        stmt \u003d \u0027DROP TRIGGER IF EXISTS credential_read_only\u0027"},{"line_number":27,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_32e4c9df","line":24,"updated":"2016-08-22 21:56:11.000000000","message":"You shouldn\u0027t need the IF EXISTS in either of these. If the trigger does not exist, something is wrong.","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"836ef615c47339a96a983a58df515efbecfc5314","unresolved":false,"context_lines":[{"line_number":21,"context_line":"    credential_table.c.blob.drop()"},{"line_number":22,"context_line":""},{"line_number":23,"context_line":"    if migrate_engine \u003d\u003d \u0027postgresql\u0027:"},{"line_number":24,"context_line":"        stmt \u003d \u0027DROP TRIGGER IF EXISTS credential_read_only on credential\u0027"},{"line_number":25,"context_line":"    else:"},{"line_number":26,"context_line":"        stmt \u003d \u0027DROP TRIGGER IF EXISTS credential_read_only\u0027"},{"line_number":27,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_751f1d08","line":24,"in_reply_to":"1ac06dbe_32e4c9df","updated":"2016-08-23 14:35:00.000000000","message":"Done","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":11816,"name":"mike bayer","display_name":"Mike Bayer","email":"mike_mp@zzzcomputing.com","username":"zzzeek","status":"Red Hat"},"change_message_id":"997aca598be208e7a1b4fc517725ff1795aff869","unresolved":false,"context_lines":[{"line_number":39,"context_line":"        # weird when using triggers, which is why we use the `IF EXISTS`"},{"line_number":40,"context_line":"        # conditional here.  I think what is happening is that the"},{"line_number":41,"context_line":"        # credential_table.c.blob.drop() causes sqlalchemy to create a new"},{"line_number":42,"context_line":"        # credential table - but it doesn\u0027t copy the triggers over, which"},{"line_number":43,"context_line":"        # causes the DROP TRIGGER statement to fail without `IF EXISTS` because"},{"line_number":44,"context_line":"        # the trigger doesn\u0027t exist in the new table(?!)."},{"line_number":45,"context_line":"        drop_credential_update_trigger \u003d ("}],"source_content_type":"text/x-python","patch_set":26,"id":"fa7ab95a_788dac30","line":42,"updated":"2016-08-26 17:12:43.000000000","message":"that\u0027s sqlalchemy-migrate doing that.   Long term Keystone should move onto alembic for migrations and also not use migrations on SQLite at all, IMO.","commit_id":"eda242aaaeb13dcfc51ab85fdca5a0a61d18766c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"c4b0b79d8da0f8f17aec5f54836717018f9c8d16","unresolved":false,"context_lines":[{"line_number":39,"context_line":"        # weird when using triggers, which is why we use the `IF EXISTS`"},{"line_number":40,"context_line":"        # conditional here.  I think what is happening is that the"},{"line_number":41,"context_line":"        # credential_table.c.blob.drop() causes sqlalchemy to create a new"},{"line_number":42,"context_line":"        # credential table - but it doesn\u0027t copy the triggers over, which"},{"line_number":43,"context_line":"        # causes the DROP TRIGGER statement to fail without `IF EXISTS` because"},{"line_number":44,"context_line":"        # the trigger doesn\u0027t exist in the new table(?!)."},{"line_number":45,"context_line":"        drop_credential_update_trigger \u003d ("}],"source_content_type":"text/x-python","patch_set":26,"id":"fa7ab95a_cfba650a","line":42,"in_reply_to":"fa7ab95a_788dac30","updated":"2016-08-26 19:30:48.000000000","message":"Awesome - thanks for the confirmation Mike!","commit_id":"eda242aaaeb13dcfc51ab85fdca5a0a61d18766c"}],"keystone/common/sql/contract_repo/versions/003_remove_unencrypted_blob_column_from_credential.py":[{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"c7794e5c49ed3a4aa778bd9936320dc47fc177a5","unresolved":false,"context_lines":[{"line_number":21,"context_line":""},{"line_number":22,"context_line":"    credential_table \u003d sql.Table(\u0027credential\u0027, meta, autoload\u003dTrue)"},{"line_number":23,"context_line":"    credential_table.c.blob.drop()"},{"line_number":24,"context_line":""},{"line_number":25,"context_line":"    if migration_helpers.USE_TRIGGERS:"},{"line_number":26,"context_line":"        if migrate_engine.name \u003d\u003d \u0027postgresql\u0027:"},{"line_number":27,"context_line":"            drop_credential_update_trigger \u003d ("}],"source_content_type":"text/x-python","patch_set":49,"id":"fa7ab95a_3a9ae36e","line":24,"updated":"2016-09-02 01:54:48.000000000","message":"the inverse would be easier to read, exit earlier, and save on indenting:\n\n      if not migration_helpers.USE_TRIGGERS:\n        # Skip managing triggers if we\u0027re doing an offline upgrade.\n        return","commit_id":"2262fa61f6411a2f9f4be25a9ec70d3d444f5423"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"c7794e5c49ed3a4aa778bd9936320dc47fc177a5","unresolved":false,"context_lines":[{"line_number":54,"context_line":"        migrate_engine.execute(drop_credential_update_trigger)"},{"line_number":55,"context_line":"        migrate_engine.execute(drop_credential_insert_trigger)"},{"line_number":56,"context_line":""},{"line_number":57,"context_line":"    credential_table.c.encrypted_blob.alter(nullable\u003dFalse)"},{"line_number":58,"context_line":"    credential_table.c.key_hash.alter(nullable\u003dFalse)"}],"source_content_type":"text/x-python","patch_set":49,"id":"fa7ab95a_1aab5f76","line":57,"range":{"start_line":57,"start_character":44,"end_line":57,"end_character":58},"updated":"2016-09-02 01:54:48.000000000","message":"nullable is false only after migrating, but true before?\n\nedit: looking at the sql model, shouldn\u0027t this be true?","commit_id":"2262fa61f6411a2f9f4be25a9ec70d3d444f5423"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"c7794e5c49ed3a4aa778bd9936320dc47fc177a5","unresolved":false,"context_lines":[{"line_number":55,"context_line":"        migrate_engine.execute(drop_credential_insert_trigger)"},{"line_number":56,"context_line":""},{"line_number":57,"context_line":"    credential_table.c.encrypted_blob.alter(nullable\u003dFalse)"},{"line_number":58,"context_line":"    credential_table.c.key_hash.alter(nullable\u003dFalse)"}],"source_content_type":"text/x-python","patch_set":49,"id":"fa7ab95a_dacbb751","line":58,"range":{"start_line":58,"start_character":38,"end_line":58,"end_character":52},"updated":"2016-09-02 01:54:48.000000000","message":"same comment as above","commit_id":"2262fa61f6411a2f9f4be25a9ec70d3d444f5423"}],"keystone/common/sql/data_migration_repo/versions/002_migrate_unencrypted_credentials.py":[{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"b0bff8b868e1aea0fc4d1218c354e1be5844515f","unresolved":false,"context_lines":[{"line_number":19,"context_line":"class CredentialEncryptor(object):"},{"line_number":20,"context_line":""},{"line_number":21,"context_line":"    def encrypt(self, unencrypted_blob):"},{"line_number":22,"context_line":"        dependency.resolve_future_dependencies()"},{"line_number":23,"context_line":"        encrypted_blob \u003d self.credential_provider_api.encrypt(unencrypted_blob)"},{"line_number":24,"context_line":"        primary_key_hash \u003d self.credential_provider_api.primary_key_hash"},{"line_number":25,"context_line":"        return (encrypted_blob, primary_key_hash)"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_552deffc","line":22,"updated":"2016-08-22 21:56:11.000000000","message":"*magic wand*","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"3d79280eef1c89f11846fd61cc17e10512e8bd54","unresolved":false,"context_lines":[{"line_number":19,"context_line":"class CredentialEncryptor(object):"},{"line_number":20,"context_line":""},{"line_number":21,"context_line":"    def encrypt(self, unencrypted_blob):"},{"line_number":22,"context_line":"        dependency.resolve_future_dependencies()"},{"line_number":23,"context_line":"        encrypted_blob \u003d self.credential_provider_api.encrypt(unencrypted_blob)"},{"line_number":24,"context_line":"        primary_key_hash \u003d self.credential_provider_api.primary_key_hash"},{"line_number":25,"context_line":"        return (encrypted_blob, primary_key_hash)"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_ab1e845c","line":22,"in_reply_to":"1ac06dbe_552deffc","updated":"2016-08-22 22:05:38.000000000","message":"Very much so - this was a Hail Mary as I was falling out of the fail tree. I\u0027ll add a comment","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"836ef615c47339a96a983a58df515efbecfc5314","unresolved":false,"context_lines":[{"line_number":19,"context_line":"class CredentialEncryptor(object):"},{"line_number":20,"context_line":""},{"line_number":21,"context_line":"    def encrypt(self, unencrypted_blob):"},{"line_number":22,"context_line":"        dependency.resolve_future_dependencies()"},{"line_number":23,"context_line":"        encrypted_blob \u003d self.credential_provider_api.encrypt(unencrypted_blob)"},{"line_number":24,"context_line":"        primary_key_hash \u003d self.credential_provider_api.primary_key_hash"},{"line_number":25,"context_line":"        return (encrypted_blob, primary_key_hash)"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_95cc596b","line":22,"in_reply_to":"1ac06dbe_ab1e845c","updated":"2016-08-23 14:35:00.000000000","message":"Done","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"b0bff8b868e1aea0fc4d1218c354e1be5844515f","unresolved":false,"context_lines":[{"line_number":31,"context_line":"    session \u003d sql.orm.sessionmaker(bind\u003dmigrate_engine)()"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":"    credential_table \u003d sql.Table(\u0027credential\u0027, meta, autoload\u003dTrue)"},{"line_number":34,"context_line":"    credentials \u003d list(credential_table.select().execute())"},{"line_number":35,"context_line":""},{"line_number":36,"context_line":"    credential_encryptor \u003d CredentialEncryptor()"},{"line_number":37,"context_line":"    for credential in credentials:"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_159257f6","line":34,"updated":"2016-08-22 21:56:11.000000000","message":"For deployments with LOTS of credentials, it\u0027d be more efficient to select a row (or a small batch) that needs updating, and update them a few at a time until there are none left to update (instead of selecting them all at once).","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"3d79280eef1c89f11846fd61cc17e10512e8bd54","unresolved":false,"context_lines":[{"line_number":31,"context_line":"    session \u003d sql.orm.sessionmaker(bind\u003dmigrate_engine)()"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":"    credential_table \u003d sql.Table(\u0027credential\u0027, meta, autoload\u003dTrue)"},{"line_number":34,"context_line":"    credentials \u003d list(credential_table.select().execute())"},{"line_number":35,"context_line":""},{"line_number":36,"context_line":"    credential_encryptor \u003d CredentialEncryptor()"},{"line_number":37,"context_line":"    for credential in credentials:"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_eb3cfcc9","line":34,"in_reply_to":"1ac06dbe_159257f6","updated":"2016-08-22 22:05:38.000000000","message":"++","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"836ef615c47339a96a983a58df515efbecfc5314","unresolved":false,"context_lines":[{"line_number":31,"context_line":"    session \u003d sql.orm.sessionmaker(bind\u003dmigrate_engine)()"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":"    credential_table \u003d sql.Table(\u0027credential\u0027, meta, autoload\u003dTrue)"},{"line_number":34,"context_line":"    credentials \u003d list(credential_table.select().execute())"},{"line_number":35,"context_line":""},{"line_number":36,"context_line":"    credential_encryptor \u003d CredentialEncryptor()"},{"line_number":37,"context_line":"    for credential in credentials:"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_b5a51548","line":34,"in_reply_to":"1ac06dbe_eb3cfcc9","updated":"2016-08-23 14:35:00.000000000","message":"That\u0027s an interesting point since we are migrating in two places. The first is the initial database migration (which lives here), and the second is the `keystone-manage credential_migrate` migration, which will forcibly update all credentials to be encrypted with the current primary key.\n\nAdding batch support to `keystone-manage credential_migrate` seems reasonable, but I\u0027m not sure how you\u0027d do that effectively in a database upgrade?","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"192b2230b78e4ba7d7eae9c568dbf078ada18761","unresolved":false,"context_lines":[{"line_number":36,"context_line":"    session \u003d sql.orm.sessionmaker(bind\u003dmigrate_engine)()"},{"line_number":37,"context_line":""},{"line_number":38,"context_line":"    credential_table \u003d sql.Table(\u0027credential\u0027, meta, autoload\u003dTrue)"},{"line_number":39,"context_line":"    credentials \u003d list(credential_table.select().execute())"},{"line_number":40,"context_line":""},{"line_number":41,"context_line":"    credential_encryptor \u003d CredentialEncryptor()"},{"line_number":42,"context_line":"    for credential in credentials:"}],"source_content_type":"text/x-python","patch_set":26,"id":"fa7ab95a_9ac6da47","line":39,"updated":"2016-08-26 21:08:49.000000000","message":"As mentioned in a previous review, it\u0027d still be worth selecting things in batches in a loop until the select comes back empty. Can do that in a follow up, though.","commit_id":"eda242aaaeb13dcfc51ab85fdca5a0a61d18766c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"37d20f806509fe265c13a9121786dfaeca7aa8a8","unresolved":false,"context_lines":[{"line_number":36,"context_line":"    session \u003d sql.orm.sessionmaker(bind\u003dmigrate_engine)()"},{"line_number":37,"context_line":""},{"line_number":38,"context_line":"    credential_table \u003d sql.Table(\u0027credential\u0027, meta, autoload\u003dTrue)"},{"line_number":39,"context_line":"    credentials \u003d list(credential_table.select().execute())"},{"line_number":40,"context_line":""},{"line_number":41,"context_line":"    credential_encryptor \u003d CredentialEncryptor()"},{"line_number":42,"context_line":"    for credential in credentials:"}],"source_content_type":"text/x-python","patch_set":26,"id":"fa7ab95a_86de7f6b","line":39,"in_reply_to":"fa7ab95a_5510a301","updated":"2016-08-26 22:13:01.000000000","message":"Ah - I see what you mean.. Piece it all together in small increments versus grabbing the whole set at once..","commit_id":"eda242aaaeb13dcfc51ab85fdca5a0a61d18766c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"9aba300d325df23b880826f8ede819e0bab6095b","unresolved":false,"context_lines":[{"line_number":36,"context_line":"    session \u003d sql.orm.sessionmaker(bind\u003dmigrate_engine)()"},{"line_number":37,"context_line":""},{"line_number":38,"context_line":"    credential_table \u003d sql.Table(\u0027credential\u0027, meta, autoload\u003dTrue)"},{"line_number":39,"context_line":"    credentials \u003d list(credential_table.select().execute())"},{"line_number":40,"context_line":""},{"line_number":41,"context_line":"    credential_encryptor \u003d CredentialEncryptor()"},{"line_number":42,"context_line":"    for credential in credentials:"}],"source_content_type":"text/x-python","patch_set":26,"id":"fa7ab95a_5510a301","line":39,"in_reply_to":"fa7ab95a_9ac6da47","updated":"2016-08-26 21:18:35.000000000","message":"This would be tough to do because that would mean running the migration in pieces, right? This piece is executed in the `keystone-manage db_sync --migrate` command.","commit_id":"eda242aaaeb13dcfc51ab85fdca5a0a61d18766c"}],"keystone/common/sql/data_migration_repo/versions/003_migrate_unencrypted_credentials.py":[{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"52569ac52b1d732e351a2b9161e4ff0e24f118c4","unresolved":false,"context_lines":[{"line_number":39,"context_line":"    credentials \u003d list(credential_table.select().execute())"},{"line_number":40,"context_line":""},{"line_number":41,"context_line":"    credential_encryptor \u003d CredentialEncryptor()"},{"line_number":42,"context_line":"    for credential in credentials:"},{"line_number":43,"context_line":"        encrypted_blob, primary_key_hash \u003d credential_encryptor.encrypt("},{"line_number":44,"context_line":"            credential[\u0027blob\u0027]"},{"line_number":45,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":47,"id":"fa7ab95a_325f75e6","line":42,"updated":"2016-09-01 19:45:22.000000000","message":"This came up in IRC earlier. Should we make this reentrant so that if it fails it could be run again? I think all that would need to change is selecting only unencrypted credentials and maybe even in chunks of 10 or 20.","commit_id":"3e603bca591868812a27a18aa0f2047de68a363e"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"c7794e5c49ed3a4aa778bd9936320dc47fc177a5","unresolved":false,"context_lines":[{"line_number":23,"context_line":"    credential_table \u003d sql.Table(\u0027credential\u0027, meta, autoload\u003dTrue)"},{"line_number":24,"context_line":"    credentials \u003d list(credential_table.select().execute())"},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"    crypto, keys \u003d credential_fernet.get_multi_fernet_keys()"},{"line_number":27,"context_line":"    primary_key_hash \u003d credential_fernet.primary_key_hash(keys)"},{"line_number":28,"context_line":"    for credential in credentials:"},{"line_number":29,"context_line":"        encrypted_blob \u003d crypto.encrypt(credential[\u0027blob\u0027].encode(\u0027utf-8\u0027))"},{"line_number":30,"context_line":"        values \u003d {"}],"source_content_type":"text/x-python","patch_set":49,"id":"fa7ab95a_bab6f3f6","line":27,"range":{"start_line":26,"start_character":4,"end_line":27,"end_character":63},"updated":"2016-09-02 01:54:48.000000000","message":"in an offline migration, what will happen if i have not yet setup the fernet keys for encryption?","commit_id":"2262fa61f6411a2f9f4be25a9ec70d3d444f5423"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"b178df08bfe282b0a56ed8b4286b2478ec43b027","unresolved":false,"context_lines":[{"line_number":24,"context_line":"    credentials \u003d list(credential_table.select().execute())"},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"    for credential in credentials:"},{"line_number":27,"context_line":"        crypto, keys \u003d credential_fernet.get_multi_fernet_keys()"},{"line_number":28,"context_line":"        primary_key_hash \u003d credential_fernet.primary_key_hash(keys)"},{"line_number":29,"context_line":"        encrypted_blob \u003d crypto.encrypt(credential[\u0027blob\u0027].encode(\u0027utf-8\u0027))"},{"line_number":30,"context_line":"        values \u003d {"}],"source_content_type":"text/x-python","patch_set":50,"id":"fa7ab95a_47498b5f","line":27,"range":{"start_line":27,"start_character":8,"end_line":27,"end_character":64},"updated":"2016-09-02 02:57:23.000000000","message":"this now gets instantiated every loop :(\n\n  if not credentials:\n    return\n\n  for credential in credentials:\n    ...","commit_id":"9e4e9cfbcb4c2a47251e69b675525bf56003e803"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"5881a47756e3a17a146a04e49427cdd79c045f7f","unresolved":false,"context_lines":[{"line_number":24,"context_line":"    credentials \u003d list(credential_table.select().execute())"},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"    for credential in credentials:"},{"line_number":27,"context_line":"        crypto, keys \u003d credential_fernet.get_multi_fernet_keys()"},{"line_number":28,"context_line":"        primary_key_hash \u003d credential_fernet.primary_key_hash(keys)"},{"line_number":29,"context_line":"        encrypted_blob \u003d crypto.encrypt(credential[\u0027blob\u0027].encode(\u0027utf-8\u0027))"},{"line_number":30,"context_line":"        values \u003d {"}],"source_content_type":"text/x-python","patch_set":50,"id":"fa7ab95a_a7a177fa","line":27,"in_reply_to":"fa7ab95a_47498b5f","updated":"2016-09-02 02:59:34.000000000","message":"Wont\u0027t that evaluate to the same thing with no values in the list? \n\n  \u003e\u003e\u003e l \u003d [1, 2, 3]\n  \u003e\u003e\u003e for v in l:\n  ...     print v\n  ... \n  1 \n  2\n  3\n  \u003e\u003e\u003e l \u003d []\n  \u003e\u003e\u003e for v in l:\n  ...     print v\n  ... \n  \u003e\u003e\u003e","commit_id":"9e4e9cfbcb4c2a47251e69b675525bf56003e803"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"34b6dfbba1522c8dd2c305df571ed98590fca79e","unresolved":false,"context_lines":[{"line_number":24,"context_line":"    credentials \u003d list(credential_table.select().execute())"},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"    for credential in credentials:"},{"line_number":27,"context_line":"        crypto, keys \u003d credential_fernet.get_multi_fernet_keys()"},{"line_number":28,"context_line":"        primary_key_hash \u003d credential_fernet.primary_key_hash(keys)"},{"line_number":29,"context_line":"        encrypted_blob \u003d crypto.encrypt(credential[\u0027blob\u0027].encode(\u0027utf-8\u0027))"},{"line_number":30,"context_line":"        values \u003d {"}],"source_content_type":"text/x-python","patch_set":50,"id":"fa7ab95a_8790f344","line":27,"in_reply_to":"fa7ab95a_47534be2","updated":"2016-09-02 03:12:08.000000000","message":"The repository could change under the crypto object, resulting in the credential being encrypted with a new primary key. If we bring the primary_key_hash assignment outside of the for loop, we will assume every credential will be encrypted with the same key regardless.\n\nBy keeping it inside the loop we ensure the primary_key_hash matches the encryption key the was used to encrypt the credential.","commit_id":"9e4e9cfbcb4c2a47251e69b675525bf56003e803"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"5731f38be7771f9a0f2c2fb9012d1ebc965993c1","unresolved":false,"context_lines":[{"line_number":24,"context_line":"    credentials \u003d list(credential_table.select().execute())"},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"    for credential in credentials:"},{"line_number":27,"context_line":"        crypto, keys \u003d credential_fernet.get_multi_fernet_keys()"},{"line_number":28,"context_line":"        primary_key_hash \u003d credential_fernet.primary_key_hash(keys)"},{"line_number":29,"context_line":"        encrypted_blob \u003d crypto.encrypt(credential[\u0027blob\u0027].encode(\u0027utf-8\u0027))"},{"line_number":30,"context_line":"        values \u003d {"}],"source_content_type":"text/x-python","patch_set":50,"id":"fa7ab95a_47534be2","line":27,"in_reply_to":"fa7ab95a_a7889772","updated":"2016-09-02 03:05:07.000000000","message":"yes, but now you\u0027ll be setting crypto, keys and key_hash at the start of every loop, when they are always the same value ;)","commit_id":"9e4e9cfbcb4c2a47251e69b675525bf56003e803"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"664fd444d9ddae3f4af0d85652b09c3c89c28cde","unresolved":false,"context_lines":[{"line_number":24,"context_line":"    credentials \u003d list(credential_table.select().execute())"},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"    for credential in credentials:"},{"line_number":27,"context_line":"        crypto, keys \u003d credential_fernet.get_multi_fernet_keys()"},{"line_number":28,"context_line":"        primary_key_hash \u003d credential_fernet.primary_key_hash(keys)"},{"line_number":29,"context_line":"        encrypted_blob \u003d crypto.encrypt(credential[\u0027blob\u0027].encode(\u0027utf-8\u0027))"},{"line_number":30,"context_line":"        values \u003d {"}],"source_content_type":"text/x-python","patch_set":50,"id":"fa7ab95a_a7889772","line":27,"in_reply_to":"fa7ab95a_a7a177fa","updated":"2016-09-02 03:02:08.000000000","message":"I think we want to instantiate the primary_key_hash every time we execute the loop because it will be per credential. IN the super random event of the primary_key changing during the migration, keystone will make sure the hash matches the actual key that was used to encrypt it.\n\n\nIf we don\u0027t expect that to be a super high probability, I\u0027ll be happy to refactor.","commit_id":"9e4e9cfbcb4c2a47251e69b675525bf56003e803"}],"keystone/common/sql/expand_repo/versions/002_add_key_hash_and_encrypted_blob_to_credential.py":[{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"fe5c767fae4662a09a7d2993d189576b603cff72","unresolved":false,"context_lines":[{"line_number":27,"context_line":"    )"},{"line_number":28,"context_line":"    credential_table \u003d sql.Table(\u0027credential\u0027, meta, autoload\u003dTrue)"},{"line_number":29,"context_line":"    credential_table.create_column(key_hash)"},{"line_number":30,"context_line":"    credential_table.create_column(encrypted_blob)"}],"source_content_type":"text/x-python","patch_set":8,"id":"1ac06dbe_4a181785","line":30,"updated":"2016-08-19 19:01:22.000000000","message":"Need to figure out how to add triggers here.","commit_id":"b739ad127d5e1059f27700e860ecabdd18aa7935"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"a4dfed824b0b937ba2eed57803939ca03b8b3871","unresolved":false,"context_lines":[{"line_number":27,"context_line":"    )"},{"line_number":28,"context_line":"    credential_table \u003d sql.Table(\u0027credential\u0027, meta, autoload\u003dTrue)"},{"line_number":29,"context_line":"    credential_table.create_column(key_hash)"},{"line_number":30,"context_line":"    credential_table.create_column(encrypted_blob)"}],"source_content_type":"text/x-python","patch_set":9,"id":"1ac06dbe_1cdca511","line":30,"updated":"2016-08-19 20:30:25.000000000","message":"Need to create a read-only trigger here.","commit_id":"d0bc6d04ac330f5a8a200c90e3c1c89e0f3d5653"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"b0bff8b868e1aea0fc4d1218c354e1be5844515f","unresolved":false,"context_lines":[{"line_number":16,"context_line":""},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"MYSQL_TRIGGER \u003d \"\"\""},{"line_number":19,"context_line":"CREATE TRIGGER credential_read_only BEFORE INSERT ON credential"},{"line_number":20,"context_line":"FOR EACH ROW SIGNAL SQLSTATE \u0027%d\u0027"},{"line_number":21,"context_line":"SET MESSAGE_TEXT \u003d \u0027%s\u0027, MYSQL_ERRNO \u003d %d;"},{"line_number":22,"context_line":")"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_953927f5","line":19,"updated":"2016-08-22 21:56:11.000000000","message":"Hrm, you need to block updates as well! So, it\u0027d be the same 3 triggers, but with BEFORE UPDATE ON.\n\nBut then we won\u0027t be able to migrate stuff! Perhaps the triggers should actually check, for example, if BEFORE UPDATE both NEW.blob and NEW.encrypted_blob are set, and if not, reject the write? (because the data migration step could set both and bypass the trigger, unlike either version of the app?)","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"d3b3ab5fe8b9b2eec8044a6d19ee1f669f47f47f","unresolved":false,"context_lines":[{"line_number":17,"context_line":""},{"line_number":18,"context_line":"MYSQL_TRIGGER \u003d \"\"\""},{"line_number":19,"context_line":"DELIMITER $$"},{"line_number":20,"context_line":"CREATE TRIGGER credential_read_only BEFORE INSERT ON credential FOR EACH ROW"},{"line_number":21,"context_line":"BEGIN"},{"line_number":22,"context_line":"  IF NEW.key_hash IS NULL THEN"},{"line_number":23,"context_line":"    SIGNAL SQLSTATE \u0027%d\u0027 SET MESSAGE_TEXT \u003d \u0027%s\u0027, MYSQL_ERRNO \u003d \u0027%d\u0027;"}],"source_content_type":"text/x-python","patch_set":13,"id":"1ac06dbe_a7f15d6d","line":20,"updated":"2016-08-23 16:37:30.000000000","message":"This trigger should work for the BEFORE UPDATE case","commit_id":"8ef2243ecec0a2834d4daf8553d21ca85e6919a7"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"fcf79d4a3cabe8ab7969d3f71aa1715440df670b","unresolved":false,"context_lines":[{"line_number":17,"context_line":""},{"line_number":18,"context_line":"MYSQL_TRIGGER \u003d \"\"\""},{"line_number":19,"context_line":"DELIMITER $$"},{"line_number":20,"context_line":"CREATE TRIGGER credential_read_only BEFORE INSERT ON credential FOR EACH ROW"},{"line_number":21,"context_line":"BEGIN"},{"line_number":22,"context_line":"  IF NEW.key_hash IS NULL THEN"},{"line_number":23,"context_line":"    SIGNAL SQLSTATE \u0027%d\u0027 SET MESSAGE_TEXT \u003d \u0027%s\u0027, MYSQL_ERRNO \u003d \u0027%d\u0027;"}],"source_content_type":"text/x-python","patch_set":13,"id":"1ac06dbe_5de7963f","line":20,"in_reply_to":"1ac06dbe_a7f15d6d","updated":"2016-08-23 16:55:01.000000000","message":"Done","commit_id":"8ef2243ecec0a2834d4daf8553d21ca85e6919a7"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"d3b3ab5fe8b9b2eec8044a6d19ee1f669f47f47f","unresolved":false,"context_lines":[{"line_number":19,"context_line":"DELIMITER $$"},{"line_number":20,"context_line":"CREATE TRIGGER credential_read_only BEFORE INSERT ON credential FOR EACH ROW"},{"line_number":21,"context_line":"BEGIN"},{"line_number":22,"context_line":"  IF NEW.key_hash IS NULL THEN"},{"line_number":23,"context_line":"    SIGNAL SQLSTATE \u0027%d\u0027 SET MESSAGE_TEXT \u003d \u0027%s\u0027, MYSQL_ERRNO \u003d \u0027%d\u0027;"},{"line_number":24,"context_line":"  END IF;"},{"line_number":25,"context_line":"  IF NEW.encrypted_blob IS NULL THEN"}],"source_content_type":"text/x-python","patch_set":13,"id":"1ac06dbe_da56ec04","line":22,"updated":"2016-08-23 16:37:30.000000000","message":"I don\u0027t think you need to check both key_hash and encrypted_blob, as they should always be set together, right?","commit_id":"8ef2243ecec0a2834d4daf8553d21ca85e6919a7"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"fcf79d4a3cabe8ab7969d3f71aa1715440df670b","unresolved":false,"context_lines":[{"line_number":19,"context_line":"DELIMITER $$"},{"line_number":20,"context_line":"CREATE TRIGGER credential_read_only BEFORE INSERT ON credential FOR EACH ROW"},{"line_number":21,"context_line":"BEGIN"},{"line_number":22,"context_line":"  IF NEW.key_hash IS NULL THEN"},{"line_number":23,"context_line":"    SIGNAL SQLSTATE \u0027%d\u0027 SET MESSAGE_TEXT \u003d \u0027%s\u0027, MYSQL_ERRNO \u003d \u0027%d\u0027;"},{"line_number":24,"context_line":"  END IF;"},{"line_number":25,"context_line":"  IF NEW.encrypted_blob IS NULL THEN"}],"source_content_type":"text/x-python","patch_set":13,"id":"1ac06dbe_da6c2c0a","line":22,"in_reply_to":"1ac06dbe_da56ec04","updated":"2016-08-23 16:55:01.000000000","message":"Yes - I wasn\u0027t sure how detailed to make this check. I\u0027ll omit the key_hash check.","commit_id":"8ef2243ecec0a2834d4daf8553d21ca85e6919a7"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"d3b3ab5fe8b9b2eec8044a6d19ee1f669f47f47f","unresolved":false,"context_lines":[{"line_number":22,"context_line":"  IF NEW.key_hash IS NULL THEN"},{"line_number":23,"context_line":"    SIGNAL SQLSTATE \u0027%d\u0027 SET MESSAGE_TEXT \u003d \u0027%s\u0027, MYSQL_ERRNO \u003d \u0027%d\u0027;"},{"line_number":24,"context_line":"  END IF;"},{"line_number":25,"context_line":"  IF NEW.encrypted_blob IS NULL THEN"},{"line_number":26,"context_line":"    SIGNAL SQLSTATE \u0027%d\u0027 SET MESSAGE_TEXT \u003d \u0027%s\u0027, MYSQL_ERRNO \u003d \u0027%d\u0027;"},{"line_number":27,"context_line":"  END IF;"},{"line_number":28,"context_line":"END; $$"}],"source_content_type":"text/x-python","patch_set":13,"id":"1ac06dbe_1a3ec4c0","line":25,"updated":"2016-08-23 16:37:30.000000000","message":"If either the old column and the new column are null in either the OLD or NEW rows, then you should raise for the BEFORE UPDATE case; something like:\n\n  ... BEFORE UPDATE ...\n  IF (NEW.encrypted_blob is NULL AND OLD.encrypted_blob IS NULL) OR (NEW.blob is NULL AND OLD.blob IS NULL) THEN\n    SIGNAL ...","commit_id":"8ef2243ecec0a2834d4daf8553d21ca85e6919a7"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"fcf79d4a3cabe8ab7969d3f71aa1715440df670b","unresolved":false,"context_lines":[{"line_number":22,"context_line":"  IF NEW.key_hash IS NULL THEN"},{"line_number":23,"context_line":"    SIGNAL SQLSTATE \u0027%d\u0027 SET MESSAGE_TEXT \u003d \u0027%s\u0027, MYSQL_ERRNO \u003d \u0027%d\u0027;"},{"line_number":24,"context_line":"  END IF;"},{"line_number":25,"context_line":"  IF NEW.encrypted_blob IS NULL THEN"},{"line_number":26,"context_line":"    SIGNAL SQLSTATE \u0027%d\u0027 SET MESSAGE_TEXT \u003d \u0027%s\u0027, MYSQL_ERRNO \u003d \u0027%d\u0027;"},{"line_number":27,"context_line":"  END IF;"},{"line_number":28,"context_line":"END; $$"}],"source_content_type":"text/x-python","patch_set":13,"id":"1ac06dbe_fddbe279","line":25,"in_reply_to":"1ac06dbe_1a3ec4c0","updated":"2016-08-23 16:55:01.000000000","message":"Done","commit_id":"8ef2243ecec0a2834d4daf8553d21ca85e6919a7"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"9a28c7a8fe3b26af3d3ed8fd9a0a265e7c1223b2","unresolved":false,"context_lines":[{"line_number":87,"context_line":"    elif migrate_engine.name \u003d\u003d \u0027mysql\u0027:"},{"line_number":88,"context_line":"        error_state \u003d 45000"},{"line_number":89,"context_line":"        mysql_errno \u003d 1001"},{"line_number":90,"context_line":"        stmt \u003d MYSQL_INSERT_TRIGGER % (error_state, error_message, mysql_errno)"},{"line_number":91,"context_line":"        migrate_engine.execute(stmt)"},{"line_number":92,"context_line":"        stmt \u003d MYSQL_UPDATE_TRIGGER % (error_state, error_message, mysql_errno)"},{"line_number":93,"context_line":"        migrate_engine.execute(stmt)"}],"source_content_type":"text/x-python","patch_set":14,"id":"1ac06dbe_49e07896","line":90,"updated":"2016-08-23 17:59:30.000000000","message":"This is an anti-pattern that leads to SQL injection; in this case, we\u0027re not at risk because all the parameters are statically defined in this module. But anyway, use the parameter options of execute() to safely pass in arguments and let sqlalchemy do the hard part.","commit_id":"29b920a885bea179781f132376ff653a22e0e1e6"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"192b2230b78e4ba7d7eae9c568dbf078ada18761","unresolved":false,"context_lines":[{"line_number":28,"context_line":"CREATE TRIGGER credential_update_read_only BEFORE UPDATE ON credential"},{"line_number":29,"context_line":"FOR EACH ROW"},{"line_number":30,"context_line":"BEGIN"},{"line_number":31,"context_line":"  IF NEW.encrypted_blob IS NULL THEN"},{"line_number":32,"context_line":"    SIGNAL SQLSTATE \u0027%d\u0027 SET MESSAGE_TEXT \u003d \u0027%s\u0027, MYSQL_ERRNO \u003d \u0027%d\u0027;"},{"line_number":33,"context_line":"  END IF;"},{"line_number":34,"context_line":"END;"}],"source_content_type":"text/x-python","patch_set":26,"id":"fa7ab95a_9a697a28","line":31,"updated":"2016-08-26 21:08:49.000000000","message":"-1 So, this is effectively blocking the old release from writing updates, and allows the migration process to write updates, but it\u0027s also *allowing* the new release to write updates that will not be reflected in the old column, leading to a brief inconsistency between the API responses from the two releases.","commit_id":"eda242aaaeb13dcfc51ab85fdca5a0a61d18766c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"a151bd4e0078e9494e93512477ab799287310a8f","unresolved":false,"context_lines":[{"line_number":28,"context_line":"CREATE TRIGGER credential_update_read_only BEFORE UPDATE ON credential"},{"line_number":29,"context_line":"FOR EACH ROW"},{"line_number":30,"context_line":"BEGIN"},{"line_number":31,"context_line":"  IF NEW.encrypted_blob IS NULL THEN"},{"line_number":32,"context_line":"    SIGNAL SQLSTATE \u0027%d\u0027 SET MESSAGE_TEXT \u003d \u0027%s\u0027, MYSQL_ERRNO \u003d \u0027%d\u0027;"},{"line_number":33,"context_line":"  END IF;"},{"line_number":34,"context_line":"END;"}],"source_content_type":"text/x-python","patch_set":26,"id":"fa7ab95a_ebc47660","line":31,"in_reply_to":"fa7ab95a_9a697a28","updated":"2016-08-26 22:11:47.000000000","message":"Done","commit_id":"eda242aaaeb13dcfc51ab85fdca5a0a61d18766c"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"192b2230b78e4ba7d7eae9c568dbf078ada18761","unresolved":false,"context_lines":[{"line_number":34,"context_line":"END;"},{"line_number":35,"context_line":"\"\"\""},{"line_number":36,"context_line":""},{"line_number":37,"context_line":"SQLITE_INSERT_TRIGGER \u003d \"\"\""},{"line_number":38,"context_line":"CREATE TRIGGER credential_insert_read_only BEFORE INSERT ON credential"},{"line_number":39,"context_line":"BEGIN"},{"line_number":40,"context_line":"  SELECT RAISE (ABORT, \u0027%s\u0027);"}],"source_content_type":"text/x-python","patch_set":26,"id":"fa7ab95a_3ad22ed3","line":37,"updated":"2016-08-26 21:08:49.000000000","message":"Mike suggested we drop SQLite triggers entirely - I\u0027d be fine to do that here.","commit_id":"eda242aaaeb13dcfc51ab85fdca5a0a61d18766c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"f3a09831928cc0ed5160455973e445d12a4891b1","unresolved":false,"context_lines":[{"line_number":34,"context_line":"END;"},{"line_number":35,"context_line":"\"\"\""},{"line_number":36,"context_line":""},{"line_number":37,"context_line":"SQLITE_INSERT_TRIGGER \u003d \"\"\""},{"line_number":38,"context_line":"CREATE TRIGGER credential_insert_read_only BEFORE INSERT ON credential"},{"line_number":39,"context_line":"BEGIN"},{"line_number":40,"context_line":"  SELECT RAISE (ABORT, \u0027%s\u0027);"}],"source_content_type":"text/x-python","patch_set":26,"id":"fa7ab95a_30becdd4","line":37,"in_reply_to":"fa7ab95a_3ad22ed3","updated":"2016-08-26 21:43:32.000000000","message":"Since SQLite is the default when running the tests, how to we make it pass the FullMigration tests when the triggers are need to make it read only?","commit_id":"eda242aaaeb13dcfc51ab85fdca5a0a61d18766c"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"192b2230b78e4ba7d7eae9c568dbf078ada18761","unresolved":false,"context_lines":[{"line_number":64,"context_line":"\"\"\""},{"line_number":65,"context_line":""},{"line_number":66,"context_line":"POSTGRESQL_UPDATE_TRIGGER \u003d \"\"\""},{"line_number":67,"context_line":"CREATE OR REPLACE FUNCTION read_only_update()"},{"line_number":68,"context_line":"  RETURNS trigger AS"},{"line_number":69,"context_line":"$BODY$"},{"line_number":70,"context_line":"BEGIN"}],"source_content_type":"text/x-python","patch_set":26,"id":"fa7ab95a_1a99ea1f","line":67,"updated":"2016-08-26 21:08:49.000000000","message":"This should have a more specific function name, because read_only_update is quite generic (and could potentially collide with another, equally generic name).","commit_id":"eda242aaaeb13dcfc51ab85fdca5a0a61d18766c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"a151bd4e0078e9494e93512477ab799287310a8f","unresolved":false,"context_lines":[{"line_number":64,"context_line":"\"\"\""},{"line_number":65,"context_line":""},{"line_number":66,"context_line":"POSTGRESQL_UPDATE_TRIGGER \u003d \"\"\""},{"line_number":67,"context_line":"CREATE OR REPLACE FUNCTION read_only_update()"},{"line_number":68,"context_line":"  RETURNS trigger AS"},{"line_number":69,"context_line":"$BODY$"},{"line_number":70,"context_line":"BEGIN"}],"source_content_type":"text/x-python","patch_set":26,"id":"fa7ab95a_4b83a204","line":67,"in_reply_to":"fa7ab95a_1a99ea1f","updated":"2016-08-26 22:11:47.000000000","message":"I added keystone to the name, maybe that will help?","commit_id":"eda242aaaeb13dcfc51ab85fdca5a0a61d18766c"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"192b2230b78e4ba7d7eae9c568dbf078ada18761","unresolved":false,"context_lines":[{"line_number":68,"context_line":"  RETURNS trigger AS"},{"line_number":69,"context_line":"$BODY$"},{"line_number":70,"context_line":"BEGIN"},{"line_number":71,"context_line":"  IF NEW.encrypted_blob IS NULL THEN"},{"line_number":72,"context_line":"    RAISE EXCEPTION \u0027%s\u0027;"},{"line_number":73,"context_line":"  END IF;"},{"line_number":74,"context_line":"  RETURN NEW;"}],"source_content_type":"text/x-python","patch_set":26,"id":"fa7ab95a_9aac1a37","line":71,"updated":"2016-08-26 21:08:49.000000000","message":"Same as L31","commit_id":"eda242aaaeb13dcfc51ab85fdca5a0a61d18766c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"a151bd4e0078e9494e93512477ab799287310a8f","unresolved":false,"context_lines":[{"line_number":68,"context_line":"  RETURNS trigger AS"},{"line_number":69,"context_line":"$BODY$"},{"line_number":70,"context_line":"BEGIN"},{"line_number":71,"context_line":"  IF NEW.encrypted_blob IS NULL THEN"},{"line_number":72,"context_line":"    RAISE EXCEPTION \u0027%s\u0027;"},{"line_number":73,"context_line":"  END IF;"},{"line_number":74,"context_line":"  RETURN NEW;"}],"source_content_type":"text/x-python","patch_set":26,"id":"fa7ab95a_0b90ba42","line":71,"in_reply_to":"fa7ab95a_9aac1a37","updated":"2016-08-26 22:11:47.000000000","message":"Done","commit_id":"eda242aaaeb13dcfc51ab85fdca5a0a61d18766c"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"192b2230b78e4ba7d7eae9c568dbf078ada18761","unresolved":false,"context_lines":[{"line_number":105,"context_line":"        credential_update_trigger \u003d SQLITE_UPDATE_TRIGGER % error_message"},{"line_number":106,"context_line":"    else:"},{"line_number":107,"context_line":"        error_state \u003d 45000"},{"line_number":108,"context_line":"        mysql_errno \u003d 1001"},{"line_number":109,"context_line":"        credential_insert_trigger \u003d MYSQL_INSERT_TRIGGER % ("},{"line_number":110,"context_line":"            error_state, error_message, mysql_errno"},{"line_number":111,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":26,"id":"fa7ab95a_3a3ceee1","line":108,"updated":"2016-08-26 21:08:49.000000000","message":"It\u0027d be nice to have a couple inline comments to document these values.","commit_id":"eda242aaaeb13dcfc51ab85fdca5a0a61d18766c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"a151bd4e0078e9494e93512477ab799287310a8f","unresolved":false,"context_lines":[{"line_number":105,"context_line":"        credential_update_trigger \u003d SQLITE_UPDATE_TRIGGER % error_message"},{"line_number":106,"context_line":"    else:"},{"line_number":107,"context_line":"        error_state \u003d 45000"},{"line_number":108,"context_line":"        mysql_errno \u003d 1001"},{"line_number":109,"context_line":"        credential_insert_trigger \u003d MYSQL_INSERT_TRIGGER % ("},{"line_number":110,"context_line":"            error_state, error_message, mysql_errno"},{"line_number":111,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":26,"id":"fa7ab95a_ab9fce4e","line":108,"in_reply_to":"fa7ab95a_3a3ceee1","updated":"2016-08-26 22:11:47.000000000","message":"I can add those to the triggers above.","commit_id":"eda242aaaeb13dcfc51ab85fdca5a0a61d18766c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"ba1662723213d4c8a4aa51f799cb7e24c7020fdd","unresolved":false,"context_lines":[{"line_number":96,"context_line":"    key_hash \u003d sql.Column(\u0027key_hash\u0027, sql.String(64), nullable\u003dTrue)"},{"line_number":97,"context_line":"    encrypted_blob \u003d sql.Column("},{"line_number":98,"context_line":"        \u0027encrypted_blob\u0027,"},{"line_number":99,"context_line":"        ks_sql.JsonBlob,"},{"line_number":100,"context_line":"        nullable\u003dTrue"},{"line_number":101,"context_line":"    )"},{"line_number":102,"context_line":"    credential_table \u003d sql.Table(\u0027credential\u0027, meta, autoload\u003dTrue)"}],"source_content_type":"text/x-python","patch_set":36,"id":"fa7ab95a_bac30537","line":99,"updated":"2016-08-30 15:16:50.000000000","message":"This should be\n\n  ks_sql.Text()\n\nIf I try to list credentials that have been migrated from Mitaka I get the following error: \n\nhttp://cdn.pasteraw.com/ia1013mmgkoon0uo6llxp0yq4b8o7cm","commit_id":"7db1e67ad62eff6a65c2c196175532bc79c93ffc"}],"keystone/credential/backends/sql.py":[{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"d7ad1f6e478e26bfe7ee2b92c7b4dc01d2ef179b","unresolved":false,"context_lines":[{"line_number":27,"context_line":"    project_id \u003d sql.Column(sql.String(64))"},{"line_number":28,"context_line":"    blob \u003d sql.Column(sql.JsonBlob(), nullable\u003dFalse)"},{"line_number":29,"context_line":"    type \u003d sql.Column(sql.String(255), nullable\u003dFalse)"},{"line_number":30,"context_line":"    key_hash \u003d sql.Column(sql.String(64), nullable\u003dTrue)"},{"line_number":31,"context_line":"    extra \u003d sql.Column(sql.JsonBlob())"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"3ac371cc_68be1000","line":30,"range":{"start_line":30,"start_character":37,"end_line":30,"end_character":39},"updated":"2016-08-18 04:59:21.000000000","message":"whats the reasoning behind 64? i\u0027m not challenging you, just looking for source material or a reason","commit_id":"ed38d1418701877ae477d189f51a8ae8bd4633c4"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"2ad05a4b4b1b72e460db07079696e122ccd25450","unresolved":false,"context_lines":[{"line_number":27,"context_line":"    project_id \u003d sql.Column(sql.String(64))"},{"line_number":28,"context_line":"    blob \u003d sql.Column(sql.JsonBlob(), nullable\u003dFalse)"},{"line_number":29,"context_line":"    type \u003d sql.Column(sql.String(255), nullable\u003dFalse)"},{"line_number":30,"context_line":"    key_hash \u003d sql.Column(sql.String(64), nullable\u003dTrue)"},{"line_number":31,"context_line":"    extra \u003d sql.Column(sql.JsonBlob())"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"3ac371cc_8857de24","line":30,"in_reply_to":"3ac371cc_68be1000","updated":"2016-08-18 16:23:49.000000000","message":"I figured we\u0027d keep it consistent with what we use for the ID column. Even though IDs are 32 bytes long, we allocate 64 characters for them.\n\nFor the key_hash - I believe the implementation only cares about the first 7 characters of the hash, so we could shorten it to 10 (even though other pluggable implementations might use the full hash). \n\nHappy to shorten it either way. Thoughts?","commit_id":"ed38d1418701877ae477d189f51a8ae8bd4633c4"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"86001dfdb93fe3a7c2e6ef01442741104271824f","unresolved":false,"context_lines":[{"line_number":27,"context_line":"    project_id \u003d sql.Column(sql.String(64))"},{"line_number":28,"context_line":"    blob \u003d sql.Column(sql.JsonBlob(), nullable\u003dFalse)"},{"line_number":29,"context_line":"    type \u003d sql.Column(sql.String(255), nullable\u003dFalse)"},{"line_number":30,"context_line":"    key_hash \u003d sql.Column(sql.String(64), nullable\u003dTrue)"},{"line_number":31,"context_line":"    extra \u003d sql.Column(sql.JsonBlob())"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"1ac06dbe_2ab045dd","line":30,"in_reply_to":"3ac371cc_79486bcd","updated":"2016-08-19 15:22:29.000000000","message":"Good point - and you are correct. I can trim this to be 7 characters.","commit_id":"ed38d1418701877ae477d189f51a8ae8bd4633c4"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"189c131e99b1e1ee0add285be9a70197bb39f47c","unresolved":false,"context_lines":[{"line_number":27,"context_line":"    project_id \u003d sql.Column(sql.String(64))"},{"line_number":28,"context_line":"    blob \u003d sql.Column(sql.JsonBlob(), nullable\u003dFalse)"},{"line_number":29,"context_line":"    type \u003d sql.Column(sql.String(255), nullable\u003dFalse)"},{"line_number":30,"context_line":"    key_hash \u003d sql.Column(sql.String(64), nullable\u003dTrue)"},{"line_number":31,"context_line":"    extra \u003d sql.Column(sql.JsonBlob())"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"3ac371cc_79486bcd","line":30,"in_reply_to":"3ac371cc_8857de24","updated":"2016-08-18 18:57:55.000000000","message":"I meant to ask about this as well. This one isn\u0027t exposed externally, and never will be, so we don\u0027t have any reason to support keys longer than our specific implementation requires (40 chars for a SHA1 hex digest, or 32 for an MD5 hex digest).","commit_id":"ed38d1418701877ae477d189f51a8ae8bd4633c4"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"a6b54e54fae3b5f43c6ea172cdeacadefb4cea7b","unresolved":false,"context_lines":[{"line_number":29,"context_line":"    project_id \u003d sql.Column(sql.String(64))"},{"line_number":30,"context_line":"    encrypted_blob \u003d sql.Column(sql.JsonBlob(), nullable\u003dFalse)"},{"line_number":31,"context_line":"    type \u003d sql.Column(sql.String(255), nullable\u003dFalse)"},{"line_number":32,"context_line":"    key_hash \u003d sql.Column(sql.String(7), nullable\u003dTrue)"},{"line_number":33,"context_line":"    extra \u003d sql.Column(sql.JsonBlob())"},{"line_number":34,"context_line":""},{"line_number":35,"context_line":""}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_c733db60","line":32,"updated":"2016-08-29 16:04:42.000000000","message":"Should this be nullable? I thought we wanted to force encryption.","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"7ff8a7792840ac3d2fcbfc23d59e6a6c42bbf41c","unresolved":false,"context_lines":[{"line_number":29,"context_line":"    project_id \u003d sql.Column(sql.String(64))"},{"line_number":30,"context_line":"    encrypted_blob \u003d sql.Column(sql.JsonBlob(), nullable\u003dFalse)"},{"line_number":31,"context_line":"    type \u003d sql.Column(sql.String(255), nullable\u003dFalse)"},{"line_number":32,"context_line":"    key_hash \u003d sql.Column(sql.String(7), nullable\u003dTrue)"},{"line_number":33,"context_line":"    extra \u003d sql.Column(sql.JsonBlob())"},{"line_number":34,"context_line":""},{"line_number":35,"context_line":""}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_435b94f6","line":32,"in_reply_to":"fa7ab95a_31e68144","updated":"2016-08-31 19:38:20.000000000","message":"It doesn\u0027t seem like a good trade off to have the schema open to bad data for a test. Maybe we can mock it or something? Or maybe this should happen in the migration tests?","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"cce7ddb42c9ba26e4b24bc44dcd6d89d9a0af9b4","unresolved":false,"context_lines":[{"line_number":29,"context_line":"    project_id \u003d sql.Column(sql.String(64))"},{"line_number":30,"context_line":"    encrypted_blob \u003d sql.Column(sql.JsonBlob(), nullable\u003dFalse)"},{"line_number":31,"context_line":"    type \u003d sql.Column(sql.String(255), nullable\u003dFalse)"},{"line_number":32,"context_line":"    key_hash \u003d sql.Column(sql.String(7), nullable\u003dTrue)"},{"line_number":33,"context_line":"    extra \u003d sql.Column(sql.JsonBlob())"},{"line_number":34,"context_line":""},{"line_number":35,"context_line":""}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_31e68144","line":32,"in_reply_to":"fa7ab95a_bb10b25a","updated":"2016-08-29 17:00:25.000000000","message":"Actually - we don\u0027t have this set to False because we rely on the ability to stuff unencrypted credentials in to the backend in keystone.tests.unit.test_backend_sql.SqlCredential.test_update_credential_is_encrypted_when_stored","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0ac6d620655d55d1c3dde90839b0d1f91fc45859","unresolved":false,"context_lines":[{"line_number":29,"context_line":"    project_id \u003d sql.Column(sql.String(64))"},{"line_number":30,"context_line":"    encrypted_blob \u003d sql.Column(sql.JsonBlob(), nullable\u003dFalse)"},{"line_number":31,"context_line":"    type \u003d sql.Column(sql.String(255), nullable\u003dFalse)"},{"line_number":32,"context_line":"    key_hash \u003d sql.Column(sql.String(7), nullable\u003dTrue)"},{"line_number":33,"context_line":"    extra \u003d sql.Column(sql.JsonBlob())"},{"line_number":34,"context_line":""},{"line_number":35,"context_line":""}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_bb10b25a","line":32,"in_reply_to":"fa7ab95a_c733db60","updated":"2016-08-29 16:47:36.000000000","message":"Done. This should also force people to to a data_migration *before* ever running this code.","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":2903,"name":"Morgan Fainberg","email":"morgan.fainberg@gmail.com","username":"mdrnstm"},"change_message_id":"557b055b28aba5d3911c9aa03614e36fc67f091b","unresolved":false,"context_lines":[{"line_number":27,"context_line":"    user_id \u003d sql.Column(sql.String(64),"},{"line_number":28,"context_line":"                         nullable\u003dFalse)"},{"line_number":29,"context_line":"    project_id \u003d sql.Column(sql.String(64))"},{"line_number":30,"context_line":"    encrypted_blob \u003d sql.Column(sql.JsonBlob(), nullable\u003dFalse)"},{"line_number":31,"context_line":"    type \u003d sql.Column(sql.String(255), nullable\u003dFalse)"},{"line_number":32,"context_line":"    key_hash \u003d sql.Column(sql.String(7), nullable\u003dTrue)"},{"line_number":33,"context_line":"    extra \u003d sql.Column(sql.JsonBlob())"}],"source_content_type":"text/x-python","patch_set":34,"id":"fa7ab95a_ffccd4bc","line":30,"range":{"start_line":30,"start_character":4,"end_line":30,"end_character":18},"updated":"2016-08-29 21:31:51.000000000","message":"Nit picking here. Why are you changing the name of the column? you actually didn\u0027t need to do so. Just adding key_hash would have been sufficient and you can know if it\u0027s encrypted by looking at keyhash.\nSmaller changes to the schema are better, also not all blobs are encrypted as evidenced by your comment below.","commit_id":"40603754b07ee14b6c7f9b33c5d698609845363d"},{"author":{"_account_id":2903,"name":"Morgan Fainberg","email":"morgan.fainberg@gmail.com","username":"mdrnstm"},"change_message_id":"0166d7fbe32bde97ec629db4aaa38dd95da56d8f","unresolved":false,"context_lines":[{"line_number":27,"context_line":"    user_id \u003d sql.Column(sql.String(64),"},{"line_number":28,"context_line":"                         nullable\u003dFalse)"},{"line_number":29,"context_line":"    project_id \u003d sql.Column(sql.String(64))"},{"line_number":30,"context_line":"    encrypted_blob \u003d sql.Column(sql.JsonBlob(), nullable\u003dFalse)"},{"line_number":31,"context_line":"    type \u003d sql.Column(sql.String(255), nullable\u003dFalse)"},{"line_number":32,"context_line":"    key_hash \u003d sql.Column(sql.String(7), nullable\u003dTrue)"},{"line_number":33,"context_line":"    extra \u003d sql.Column(sql.JsonBlob())"}],"source_content_type":"text/x-python","patch_set":34,"id":"fa7ab95a_ba7a5a86","line":30,"in_reply_to":"fa7ab95a_ff14d44d","updated":"2016-08-29 21:38:53.000000000","message":"WFM.","commit_id":"40603754b07ee14b6c7f9b33c5d698609845363d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"5ba01cef357ceac95406bed518293cc77e99279d","unresolved":false,"context_lines":[{"line_number":27,"context_line":"    user_id \u003d sql.Column(sql.String(64),"},{"line_number":28,"context_line":"                         nullable\u003dFalse)"},{"line_number":29,"context_line":"    project_id \u003d sql.Column(sql.String(64))"},{"line_number":30,"context_line":"    encrypted_blob \u003d sql.Column(sql.JsonBlob(), nullable\u003dFalse)"},{"line_number":31,"context_line":"    type \u003d sql.Column(sql.String(255), nullable\u003dFalse)"},{"line_number":32,"context_line":"    key_hash \u003d sql.Column(sql.String(7), nullable\u003dTrue)"},{"line_number":33,"context_line":"    extra \u003d sql.Column(sql.JsonBlob())"}],"source_content_type":"text/x-python","patch_set":34,"id":"fa7ab95a_ff14d44d","line":30,"in_reply_to":"fa7ab95a_ffccd4bc","updated":"2016-08-29 21:36:53.000000000","message":"We need a second column in order to upgrade and allow both version (Mitaka and Newton) to be able to read versions of the blob they understand.","commit_id":"40603754b07ee14b6c7f9b33c5d698609845363d"},{"author":{"_account_id":2903,"name":"Morgan Fainberg","email":"morgan.fainberg@gmail.com","username":"mdrnstm"},"change_message_id":"557b055b28aba5d3911c9aa03614e36fc67f091b","unresolved":false,"context_lines":[{"line_number":29,"context_line":"    project_id \u003d sql.Column(sql.String(64))"},{"line_number":30,"context_line":"    encrypted_blob \u003d sql.Column(sql.JsonBlob(), nullable\u003dFalse)"},{"line_number":31,"context_line":"    type \u003d sql.Column(sql.String(255), nullable\u003dFalse)"},{"line_number":32,"context_line":"    key_hash \u003d sql.Column(sql.String(7), nullable\u003dTrue)"},{"line_number":33,"context_line":"    extra \u003d sql.Column(sql.JsonBlob())"},{"line_number":34,"context_line":""},{"line_number":35,"context_line":""}],"source_content_type":"text/x-python","patch_set":34,"id":"fa7ab95a_3f539c6c","line":32,"range":{"start_line":32,"start_character":4,"end_line":32,"end_character":55},"updated":"2016-08-29 21:31:51.000000000","message":"see earlier comment. recommend using the full has and adding a specific 7 character index here (you should probably index this column anyway).\n\nartificially truncating a cryptographic representation of the key is a terrible idea.\n\nIt is ok to utilize the smaller prefix (aka git or GPG) if there are no collisions. Without a clear comment on why 7 bits are sufficient I am a strong -1 on this.","commit_id":"40603754b07ee14b6c7f9b33c5d698609845363d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"2b3cad05ce2c3b2eaa1319c1b67e1e51c812470c","unresolved":false,"context_lines":[{"line_number":29,"context_line":"    project_id \u003d sql.Column(sql.String(64))"},{"line_number":30,"context_line":"    encrypted_blob \u003d sql.Column(sql.JsonBlob(), nullable\u003dFalse)"},{"line_number":31,"context_line":"    type \u003d sql.Column(sql.String(255), nullable\u003dFalse)"},{"line_number":32,"context_line":"    key_hash \u003d sql.Column(sql.String(7), nullable\u003dTrue)"},{"line_number":33,"context_line":"    extra \u003d sql.Column(sql.JsonBlob())"},{"line_number":34,"context_line":""},{"line_number":35,"context_line":""}],"source_content_type":"text/x-python","patch_set":34,"id":"fa7ab95a_9ce67313","line":32,"in_reply_to":"fa7ab95a_3f539c6c","updated":"2016-08-29 22:08:02.000000000","message":"Done","commit_id":"40603754b07ee14b6c7f9b33c5d698609845363d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"ba1662723213d4c8a4aa51f799cb7e24c7020fdd","unresolved":false,"context_lines":[{"line_number":27,"context_line":"    user_id \u003d sql.Column(sql.String(64),"},{"line_number":28,"context_line":"                         nullable\u003dFalse)"},{"line_number":29,"context_line":"    project_id \u003d sql.Column(sql.String(64))"},{"line_number":30,"context_line":"    encrypted_blob \u003d sql.Column(sql.JsonBlob(), nullable\u003dFalse)"},{"line_number":31,"context_line":"    type \u003d sql.Column(sql.String(255), nullable\u003dFalse)"},{"line_number":32,"context_line":"    key_hash \u003d sql.Column(sql.String(64), nullable\u003dTrue)"},{"line_number":33,"context_line":"    extra \u003d sql.Column(sql.JsonBlob())"}],"source_content_type":"text/x-python","patch_set":36,"id":"fa7ab95a_3ae615e8","line":30,"updated":"2016-08-30 15:16:50.000000000","message":"This should be\n\n  sql.Text()\n\nSee comment in the migrations.","commit_id":"7db1e67ad62eff6a65c2c196175532bc79c93ffc"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"bcb341d25d94a5b7c2c09c17dba7695b89d134d1","unresolved":false,"context_lines":[{"line_number":29,"context_line":"    project_id \u003d sql.Column(sql.String(64))"},{"line_number":30,"context_line":"    encrypted_blob \u003d sql.Column(sql.Text(), nullable\u003dTrue)"},{"line_number":31,"context_line":"    type \u003d sql.Column(sql.String(255), nullable\u003dFalse)"},{"line_number":32,"context_line":"    key_hash \u003d sql.Column(sql.String(64), nullable\u003dTrue)"},{"line_number":33,"context_line":"    extra \u003d sql.Column(sql.JsonBlob())"},{"line_number":34,"context_line":""},{"line_number":35,"context_line":""}],"source_content_type":"text/x-python","patch_set":40,"id":"fa7ab95a_60db2eee","line":32,"updated":"2016-08-31 19:39:55.000000000","message":"I have a followup in Rev 31 about this.","commit_id":"980997e81bd3b24a25acdc4a9ef186db20e056fa"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"993e6a6885dc07f18d866fbd830757d107a99d25","unresolved":false,"context_lines":[{"line_number":27,"context_line":"    user_id \u003d sql.Column(sql.String(64),"},{"line_number":28,"context_line":"                         nullable\u003dFalse)"},{"line_number":29,"context_line":"    project_id \u003d sql.Column(sql.String(64))"},{"line_number":30,"context_line":"    encrypted_blob \u003d sql.Column(sql.Text(), nullable\u003dTrue)"},{"line_number":31,"context_line":"    type \u003d sql.Column(sql.String(255), nullable\u003dFalse)"},{"line_number":32,"context_line":"    key_hash \u003d sql.Column(sql.String(64), nullable\u003dTrue)"},{"line_number":33,"context_line":"    extra \u003d sql.Column(sql.JsonBlob())"}],"source_content_type":"text/x-python","patch_set":41,"id":"fa7ab95a_de9e6b53","line":30,"updated":"2016-09-01 14:06:10.000000000","message":"Still working this piece out with David.","commit_id":"0fbe45b6b3157eff50afb32a87e24f9d5040c052"}],"keystone/credential/core.py":[{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"a68ac7352512428a2986dc40394a2d429e5ee983","unresolved":false,"context_lines":[{"line_number":54,"context_line":""},{"line_number":55,"context_line":"    def update_credential(self, credential_id, credential):"},{"line_number":56,"context_line":"        \"\"\"Update an existing credential.\"\"\""},{"line_number":57,"context_line":"        return self.driver.update_credential(credential_id, credential)"},{"line_number":58,"context_line":""},{"line_number":59,"context_line":""},{"line_number":60,"context_line":"@versionutils.deprecated("}],"source_content_type":"text/x-python","patch_set":6,"id":"3ac371cc_d447f3b9","side":"PARENT","line":57,"updated":"2016-08-17 19:16:55.000000000","message":"Won\u0027t key_hash come back in an update call as well? Can you not add the exclusion to the model\u0027s to_dict() instead, or do you need it at the manager layer for some reason?","commit_id":"ee8b639f185904a513bd032428f67068b0870b93"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0ec465c63d3669713b1137347d15f8982d13943b","unresolved":false,"context_lines":[{"line_number":54,"context_line":""},{"line_number":55,"context_line":"    def update_credential(self, credential_id, credential):"},{"line_number":56,"context_line":"        \"\"\"Update an existing credential.\"\"\""},{"line_number":57,"context_line":"        return self.driver.update_credential(credential_id, credential)"},{"line_number":58,"context_line":""},{"line_number":59,"context_line":""},{"line_number":60,"context_line":"@versionutils.deprecated("}],"source_content_type":"text/x-python","patch_set":6,"id":"3ac371cc_05bcb7da","side":"PARENT","line":57,"in_reply_to":"3ac371cc_d447f3b9","updated":"2016-08-17 20:29:57.000000000","message":"I could - but I use the self.credential_api.driver.list_credentials() call and I rely on the fact that the credential will have the key_hash in it. If I add it to the model or driver, I don\u0027t think I\u0027ll get that information unless I\u0027m in the driver, right?","commit_id":"ee8b639f185904a513bd032428f67068b0870b93"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"b0bff8b868e1aea0fc4d1218c354e1be5844515f","unresolved":false,"context_lines":[{"line_number":80,"context_line":"                )"},{"line_number":81,"context_line":"            credential[\u0027blob\u0027] \u003d decrypted_blob"},{"line_number":82,"context_line":"            credential.pop(\u0027key_hash\u0027, None)"},{"line_number":83,"context_line":"            credential.pop(\u0027encrypted_blob\u0027, None)"},{"line_number":84,"context_line":"        return credentials"},{"line_number":85,"context_line":""},{"line_number":86,"context_line":"    def get_credential(self, credential_id):"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_b0db914e","line":83,"updated":"2016-08-22 21:56:11.000000000","message":"This work could/should be refactored into a helper method that decrypts a single credential correctly.","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"836ef615c47339a96a983a58df515efbecfc5314","unresolved":false,"context_lines":[{"line_number":80,"context_line":"                )"},{"line_number":81,"context_line":"            credential[\u0027blob\u0027] \u003d decrypted_blob"},{"line_number":82,"context_line":"            credential.pop(\u0027key_hash\u0027, None)"},{"line_number":83,"context_line":"            credential.pop(\u0027encrypted_blob\u0027, None)"},{"line_number":84,"context_line":"        return credentials"},{"line_number":85,"context_line":""},{"line_number":86,"context_line":"    def get_credential(self, credential_id):"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_4bfc0272","line":83,"in_reply_to":"1ac06dbe_b0db914e","updated":"2016-08-23 14:35:00.000000000","message":"Done","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"cae70c72509c9d054cb45788dc5dbf36d5e39c59","unresolved":false,"context_lines":[{"line_number":44,"context_line":"    def __init__(self):"},{"line_number":45,"context_line":"        super(Manager, self).__init__(CONF.credential.driver)"},{"line_number":46,"context_line":""},{"line_number":47,"context_line":"    def _decrypt_credential(self, credential):"},{"line_number":48,"context_line":"        \"\"\"Return a decrypted credential reference.\"\"\""},{"line_number":49,"context_line":"        if credential[\u0027type\u0027] \u003d\u003d \u0027ec2\u0027:"},{"line_number":50,"context_line":"            decrypted_blob \u003d json.loads("}],"source_content_type":"text/x-python","patch_set":27,"id":"fa7ab95a_7524c7ae","line":47,"updated":"2016-08-26 21:24:44.000000000","message":"This method returns an object intended to be rendered by the API.","commit_id":"1059deb3d2e5bbf66a3a84def65e80acdf42ea11"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"aee4a0cd266d34e2d7e5adbd379d53124e756fa8","unresolved":false,"context_lines":[{"line_number":44,"context_line":"    def __init__(self):"},{"line_number":45,"context_line":"        super(Manager, self).__init__(CONF.credential.driver)"},{"line_number":46,"context_line":""},{"line_number":47,"context_line":"    def _decrypt_credential(self, credential):"},{"line_number":48,"context_line":"        \"\"\"Return a decrypted credential reference.\"\"\""},{"line_number":49,"context_line":"        if credential[\u0027type\u0027] \u003d\u003d \u0027ec2\u0027:"},{"line_number":50,"context_line":"            decrypted_blob \u003d json.loads("}],"source_content_type":"text/x-python","patch_set":27,"id":"fa7ab95a_c61b271a","line":47,"in_reply_to":"fa7ab95a_7524c7ae","updated":"2016-08-26 22:29:19.000000000","message":"This was an attempt to consolidate logic into one place for the credential API. Should we just have these methods return decrypted strings or encrypted strings?","commit_id":"1059deb3d2e5bbf66a3a84def65e80acdf42ea11"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"cae70c72509c9d054cb45788dc5dbf36d5e39c59","unresolved":false,"context_lines":[{"line_number":61,"context_line":"        credential.pop(\u0027encrypted_blob\u0027, None)"},{"line_number":62,"context_line":"        return credential"},{"line_number":63,"context_line":""},{"line_number":64,"context_line":"    def _encrypt_credential(self, credential):"},{"line_number":65,"context_line":"        \"\"\"Return an encrypted credential reference.\"\"\""},{"line_number":66,"context_line":"        credential_copy \u003d credential.copy()"},{"line_number":67,"context_line":"        if credential.get(\u0027type\u0027, None) \u003d\u003d \u0027ec2\u0027:"}],"source_content_type":"text/x-python","patch_set":27,"id":"fa7ab95a_f530d7ea","line":64,"updated":"2016-08-26 21:24:44.000000000","message":"This method returns an object intended to be passed into the database.\n\nThat\u0027s super confusing.","commit_id":"1059deb3d2e5bbf66a3a84def65e80acdf42ea11"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"aee4a0cd266d34e2d7e5adbd379d53124e756fa8","unresolved":false,"context_lines":[{"line_number":61,"context_line":"        credential.pop(\u0027encrypted_blob\u0027, None)"},{"line_number":62,"context_line":"        return credential"},{"line_number":63,"context_line":""},{"line_number":64,"context_line":"    def _encrypt_credential(self, credential):"},{"line_number":65,"context_line":"        \"\"\"Return an encrypted credential reference.\"\"\""},{"line_number":66,"context_line":"        credential_copy \u003d credential.copy()"},{"line_number":67,"context_line":"        if credential.get(\u0027type\u0027, None) \u003d\u003d \u0027ec2\u0027:"}],"source_content_type":"text/x-python","patch_set":27,"id":"fa7ab95a_66e17bfe","line":64,"in_reply_to":"fa7ab95a_f530d7ea","updated":"2016-08-26 22:29:19.000000000","message":"Same comment as above.","commit_id":"1059deb3d2e5bbf66a3a84def65e80acdf42ea11"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"cae70c72509c9d054cb45788dc5dbf36d5e39c59","unresolved":false,"context_lines":[{"line_number":137,"context_line":"        ref.pop(\u0027key_hash\u0027, None)"},{"line_number":138,"context_line":"        ref.pop(\u0027encrypted_blob\u0027, None)"},{"line_number":139,"context_line":"        # If the update request contains a `blob` attribute - we should return"},{"line_number":140,"context_line":"        # taht in the update response. If not, then we should return the"},{"line_number":141,"context_line":"        # existing `blob` attribute since it wasn\u0027t updated."},{"line_number":142,"context_line":"        if credential.get(\u0027blob\u0027):"},{"line_number":143,"context_line":"            ref[\u0027blob\u0027] \u003d credential[\u0027blob\u0027]"}],"source_content_type":"text/x-python","patch_set":27,"id":"fa7ab95a_95422b3d","line":140,"updated":"2016-08-26 21:24:44.000000000","message":"taht","commit_id":"1059deb3d2e5bbf66a3a84def65e80acdf42ea11"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"aee4a0cd266d34e2d7e5adbd379d53124e756fa8","unresolved":false,"context_lines":[{"line_number":137,"context_line":"        ref.pop(\u0027key_hash\u0027, None)"},{"line_number":138,"context_line":"        ref.pop(\u0027encrypted_blob\u0027, None)"},{"line_number":139,"context_line":"        # If the update request contains a `blob` attribute - we should return"},{"line_number":140,"context_line":"        # taht in the update response. If not, then we should return the"},{"line_number":141,"context_line":"        # existing `blob` attribute since it wasn\u0027t updated."},{"line_number":142,"context_line":"        if credential.get(\u0027blob\u0027):"},{"line_number":143,"context_line":"            ref[\u0027blob\u0027] \u003d credential[\u0027blob\u0027]"}],"source_content_type":"text/x-python","patch_set":27,"id":"fa7ab95a_267db3e7","line":140,"in_reply_to":"fa7ab95a_95422b3d","updated":"2016-08-26 22:29:19.000000000","message":"Done","commit_id":"1059deb3d2e5bbf66a3a84def65e80acdf42ea11"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"a6b54e54fae3b5f43c6ea172cdeacadefb4cea7b","unresolved":false,"context_lines":[{"line_number":129,"context_line":"        self._validate_credential_update(credential_id, credential)"},{"line_number":130,"context_line":"        if credential.get(\u0027blob\u0027):"},{"line_number":131,"context_line":"            credential_copy \u003d self._encrypt_credential(credential)"},{"line_number":132,"context_line":"        else:"},{"line_number":133,"context_line":"            credential_copy \u003d credential.copy()"},{"line_number":134,"context_line":"            existing_credential \u003d self.get_credential(credential_id)"},{"line_number":135,"context_line":"            existing_blob \u003d existing_credential[\u0027blob\u0027]"}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_677dafb0","line":132,"updated":"2016-08-29 16:04:42.000000000","message":"Can someone actually update without including the blob?","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0ac6d620655d55d1c3dde90839b0d1f91fc45859","unresolved":false,"context_lines":[{"line_number":129,"context_line":"        self._validate_credential_update(credential_id, credential)"},{"line_number":130,"context_line":"        if credential.get(\u0027blob\u0027):"},{"line_number":131,"context_line":"            credential_copy \u003d self._encrypt_credential(credential)"},{"line_number":132,"context_line":"        else:"},{"line_number":133,"context_line":"            credential_copy \u003d credential.copy()"},{"line_number":134,"context_line":"            existing_credential \u003d self.get_credential(credential_id)"},{"line_number":135,"context_line":"            existing_blob \u003d existing_credential[\u0027blob\u0027]"}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_565e9b12","line":132,"in_reply_to":"fa7ab95a_677dafb0","updated":"2016-08-29 16:47:36.000000000","message":"I suppose you could. Our jsonschema validation doesn\u0027t require the blob on update.","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"bcb341d25d94a5b7c2c09c17dba7695b89d134d1","unresolved":false,"context_lines":[{"line_number":127,"context_line":"    def update_credential(self, credential_id, credential):"},{"line_number":128,"context_line":"        \"\"\"Update an existing credential.\"\"\""},{"line_number":129,"context_line":"        self._validate_credential_update(credential_id, credential)"},{"line_number":130,"context_line":"        if credential.get(\u0027blob\u0027):"},{"line_number":131,"context_line":"            credential_copy \u003d self._encrypt_credential(credential)"},{"line_number":132,"context_line":"        else:"},{"line_number":133,"context_line":"            credential_copy \u003d credential.copy()"}],"source_content_type":"text/x-python","patch_set":40,"id":"fa7ab95a_c3188478","line":130,"updated":"2016-08-31 19:39:55.000000000","message":"(nit) if \u0027blob\u0027 in credential - is more idiomatic Python","commit_id":"980997e81bd3b24a25acdc4a9ef186db20e056fa"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0f5606d264829e81dc15d79f54bfae29761e93dd","unresolved":false,"context_lines":[{"line_number":127,"context_line":"    def update_credential(self, credential_id, credential):"},{"line_number":128,"context_line":"        \"\"\"Update an existing credential.\"\"\""},{"line_number":129,"context_line":"        self._validate_credential_update(credential_id, credential)"},{"line_number":130,"context_line":"        if credential.get(\u0027blob\u0027):"},{"line_number":131,"context_line":"            credential_copy \u003d self._encrypt_credential(credential)"},{"line_number":132,"context_line":"        else:"},{"line_number":133,"context_line":"            credential_copy \u003d credential.copy()"}],"source_content_type":"text/x-python","patch_set":40,"id":"fa7ab95a_b8e7ff10","line":130,"in_reply_to":"fa7ab95a_c3188478","updated":"2016-09-01 13:38:12.000000000","message":"Done","commit_id":"980997e81bd3b24a25acdc4a9ef186db20e056fa"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"1af0571234ccb3889843780ac2edeff3b83f24f9","unresolved":false,"context_lines":[{"line_number":91,"context_line":"            credential \u003d self._decrypt_credential(credential)"},{"line_number":92,"context_line":"        return credentials"},{"line_number":93,"context_line":""},{"line_number":94,"context_line":"    def list_credentials_for_user(self, user_id, type\u003dNone):"},{"line_number":95,"context_line":"        \"\"\"List credentials for a specific user.\"\"\""},{"line_number":96,"context_line":"        credentials \u003d self.driver.list_credentials_for_user(user_id, type\u003dtype)"},{"line_number":97,"context_line":"        for credential in credentials:"},{"line_number":98,"context_line":"            credential \u003d self._decrypt_credential(credential)"},{"line_number":99,"context_line":"        return credentials"},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"    def get_credential(self, credential_id):"},{"line_number":102,"context_line":"        \"\"\"Return a credential reference.\"\"\""}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_a3009567","line":99,"range":{"start_line":94,"start_character":0,"end_line":99,"end_character":26},"updated":"2016-09-01 21:26:11.000000000","message":"Why are we adding a new method to the credential API? Seems unrelated to this change ?","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"3f693b14f546346a6b615e4dbdc6bff76d685dc1","unresolved":false,"context_lines":[{"line_number":96,"context_line":"        credentials \u003d self.driver.list_credentials_for_user(user_id, type\u003dtype)"},{"line_number":97,"context_line":"        for credential in credentials:"},{"line_number":98,"context_line":"            credential \u003d self._decrypt_credential(credential)"},{"line_number":99,"context_line":"        return credentials"},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"    def get_credential(self, credential_id):"},{"line_number":102,"context_line":"        \"\"\"Return a credential reference.\"\"\""}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_0f8ebf78","line":99,"in_reply_to":"fa7ab95a_a3009567","updated":"2016-09-02 01:12:49.000000000","message":"This is something the credential API has always supported - we are just adding it to the manager so that we can control the encryption part.","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"1af0571234ccb3889843780ac2edeff3b83f24f9","unresolved":false,"context_lines":[{"line_number":107,"context_line":"        \"\"\"Create a credential.\"\"\""},{"line_number":108,"context_line":"        credential_copy \u003d self._encrypt_credential(credential)"},{"line_number":109,"context_line":"        ref \u003d self.driver.create_credential(credential_id, credential_copy)"},{"line_number":110,"context_line":"        ref.pop(\u0027key_hash\u0027, None)"},{"line_number":111,"context_line":"        ref.pop(\u0027encrypted_blob\u0027, None)"},{"line_number":112,"context_line":"        ref[\u0027blob\u0027] \u003d credential[\u0027blob\u0027]"},{"line_number":113,"context_line":"        return ref"},{"line_number":114,"context_line":""},{"line_number":115,"context_line":"    def _validate_credential_update(self, credential_id, credential):"}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_234485b5","line":112,"range":{"start_line":110,"start_character":0,"end_line":112,"end_character":40},"updated":"2016-09-01 21:26:11.000000000","message":"shouldn\u0027t we decrypt ref and return it ?","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"3f693b14f546346a6b615e4dbdc6bff76d685dc1","unresolved":false,"context_lines":[{"line_number":109,"context_line":"        ref \u003d self.driver.create_credential(credential_id, credential_copy)"},{"line_number":110,"context_line":"        ref.pop(\u0027key_hash\u0027, None)"},{"line_number":111,"context_line":"        ref.pop(\u0027encrypted_blob\u0027, None)"},{"line_number":112,"context_line":"        ref[\u0027blob\u0027] \u003d credential[\u0027blob\u0027]"},{"line_number":113,"context_line":"        return ref"},{"line_number":114,"context_line":""},{"line_number":115,"context_line":"    def _validate_credential_update(self, credential_id, credential):"}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_0fe5df36","line":112,"in_reply_to":"fa7ab95a_234485b5","updated":"2016-09-02 01:12:49.000000000","message":"Either way works, I can refactor this in another patch if that\u0027s the way we want to go.","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"1af0571234ccb3889843780ac2edeff3b83f24f9","unresolved":false,"context_lines":[{"line_number":136,"context_line":"        ref \u003d self.driver.update_credential(credential_id, credential_copy)"},{"line_number":137,"context_line":"        ref.pop(\u0027key_hash\u0027, None)"},{"line_number":138,"context_line":"        ref.pop(\u0027encrypted_blob\u0027, None)"},{"line_number":139,"context_line":"        # If the update request contains a `blob` attribute - we should return"},{"line_number":140,"context_line":"        # that in the update response. If not, then we should return the"},{"line_number":141,"context_line":"        # existing `blob` attribute since it wasn\u0027t updated."},{"line_number":142,"context_line":"        if credential.get(\u0027blob\u0027):"},{"line_number":143,"context_line":"            ref[\u0027blob\u0027] \u003d credential[\u0027blob\u0027]"},{"line_number":144,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_83571159","line":141,"range":{"start_line":139,"start_character":0,"end_line":141,"end_character":60},"updated":"2016-09-01 21:26:11.000000000","message":"Isn\u0027t the pattern that we return the whole entity after updating it?","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"3f693b14f546346a6b615e4dbdc6bff76d685dc1","unresolved":false,"context_lines":[{"line_number":138,"context_line":"        ref.pop(\u0027encrypted_blob\u0027, None)"},{"line_number":139,"context_line":"        # If the update request contains a `blob` attribute - we should return"},{"line_number":140,"context_line":"        # that in the update response. If not, then we should return the"},{"line_number":141,"context_line":"        # existing `blob` attribute since it wasn\u0027t updated."},{"line_number":142,"context_line":"        if credential.get(\u0027blob\u0027):"},{"line_number":143,"context_line":"            ref[\u0027blob\u0027] \u003d credential[\u0027blob\u0027]"},{"line_number":144,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_0fbcff04","line":141,"in_reply_to":"fa7ab95a_83571159","updated":"2016-09-02 01:12:49.000000000","message":"Yes - blob will still be returned in this case. This is just making sure we return the updated blob in if was in fact updated.","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"c7794e5c49ed3a4aa778bd9936320dc47fc177a5","unresolved":false,"context_lines":[{"line_number":53,"context_line":"                )"},{"line_number":54,"context_line":"            )"},{"line_number":55,"context_line":"        else:"},{"line_number":56,"context_line":"            decrypted_blob \u003d self.credential_provider_api.decrypt("},{"line_number":57,"context_line":"                credential[\u0027encrypted_blob\u0027]"},{"line_number":58,"context_line":"            )"},{"line_number":59,"context_line":"        credential[\u0027blob\u0027] \u003d decrypted_blob"},{"line_number":60,"context_line":"        credential.pop(\u0027key_hash\u0027, None)"},{"line_number":61,"context_line":"        credential.pop(\u0027encrypted_blob\u0027, None)"}],"source_content_type":"text/x-python","patch_set":49,"id":"fa7ab95a_dac5778e","line":58,"range":{"start_line":56,"start_character":12,"end_line":58,"end_character":13},"updated":"2016-09-02 01:54:48.000000000","message":"this should be outside the conditional, and use the conditional to determine if you should load json or not, it\u0027ll result in fewer lines of code","commit_id":"2262fa61f6411a2f9f4be25a9ec70d3d444f5423"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"c7794e5c49ed3a4aa778bd9936320dc47fc177a5","unresolved":false,"context_lines":[{"line_number":64,"context_line":"    def _encrypt_credential(self, credential):"},{"line_number":65,"context_line":"        \"\"\"Return an encrypted credential reference.\"\"\""},{"line_number":66,"context_line":"        credential_copy \u003d credential.copy()"},{"line_number":67,"context_line":"        if credential.get(\u0027type\u0027, None) \u003d\u003d \u0027ec2\u0027:"},{"line_number":68,"context_line":"            # NOTE(lbragstad): When dealing with ec2 credentials, it\u0027s possible"},{"line_number":69,"context_line":"            # for the `blob` to be a dictionary. Let\u0027s make sure we are"},{"line_number":70,"context_line":"            # encrypting a string otherwise encryption will fail."}],"source_content_type":"text/x-python","patch_set":49,"id":"fa7ab95a_3ab90316","line":67,"range":{"start_line":67,"start_character":8,"end_line":67,"end_character":49},"updated":"2016-09-02 01:54:48.000000000","message":"same comment as above, if type \u003d\u003d ec2, then dump credential blob, then do the encrypting","commit_id":"2262fa61f6411a2f9f4be25a9ec70d3d444f5423"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"c7794e5c49ed3a4aa778bd9936320dc47fc177a5","unresolved":false,"context_lines":[{"line_number":89,"context_line":"            credential \u003d self._decrypt_credential(credential)"},{"line_number":90,"context_line":"        return credentials"},{"line_number":91,"context_line":""},{"line_number":92,"context_line":"    def list_credentials_for_user(self, user_id, type\u003dNone):"},{"line_number":93,"context_line":"        \"\"\"List credentials for a specific user.\"\"\""},{"line_number":94,"context_line":"        credentials \u003d self.driver.list_credentials_for_user(user_id, type\u003dtype)"},{"line_number":95,"context_line":"        for credential in credentials:"}],"source_content_type":"text/x-python","patch_set":49,"id":"fa7ab95a_1ae51f37","line":92,"range":{"start_line":92,"start_character":8,"end_line":92,"end_character":33},"updated":"2016-09-02 01:54:48.000000000","message":"this could have gone into a different patch","commit_id":"2262fa61f6411a2f9f4be25a9ec70d3d444f5423"}],"keystone/credential/providers/fernet/core.py":[{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"5c8d8fdaae762004d50ace8e272f1ce1d0b9b998","unresolved":false,"context_lines":[{"line_number":49,"context_line":"        key_digest \u003d hashlib.sha1("},{"line_number":50,"context_line":"            primary_key._signing_key + primary_key._encryption_key"},{"line_number":51,"context_line":"        ).hexdigest()"},{"line_number":52,"context_line":"        return key_digest[:7]"},{"line_number":53,"context_line":""},{"line_number":54,"context_line":"    @property"},{"line_number":55,"context_line":"    def crypto(self):"}],"source_content_type":"text/x-python","patch_set":8,"id":"1ac06dbe_3630a256","line":52,"updated":"2016-08-19 19:58:34.000000000","message":"One concern that I have with the so far is that I\u0027m unsure how this will work across multiple threads. In some of my testing locally I noticed that a new created credential will not have a different key_hash from the other credentials immediately after a key rotation.\n\nIs there a way to make this calculate the absolute newest key hash from disk every time we ask for it?","commit_id":"b739ad127d5e1059f27700e860ecabdd18aa7935"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"a4dfed824b0b937ba2eed57803939ca03b8b3871","unresolved":false,"context_lines":[{"line_number":49,"context_line":"        key_digest \u003d hashlib.sha1("},{"line_number":50,"context_line":"            primary_key._signing_key + primary_key._encryption_key"},{"line_number":51,"context_line":"        ).hexdigest()"},{"line_number":52,"context_line":"        return key_digest[:7]"},{"line_number":53,"context_line":""},{"line_number":54,"context_line":"    @property"},{"line_number":55,"context_line":"    def crypto(self):"}],"source_content_type":"text/x-python","patch_set":9,"id":"1ac06dbe_9cfed569","line":52,"updated":"2016-08-19 20:30:25.000000000","message":"One concern that I have with the so far is that I\u0027m unsure how this will work across multiple threads. In some of my testing locally I noticed that a new created credential will not have a different key_hash from the other credentials immediately after a key rotation.\n\nIs there a way to make this calculate the absolute newest key hash from disk every time we ask for it?","commit_id":"d0bc6d04ac330f5a8a200c90e3c1c89e0f3d5653"},{"author":{"_account_id":2903,"name":"Morgan Fainberg","email":"morgan.fainberg@gmail.com","username":"mdrnstm"},"change_message_id":"796c43cccf15e30d534466fa36a891cd716d9603","unresolved":false,"context_lines":[{"line_number":84,"context_line":"        :returns: a decrypted credential"},{"line_number":85,"context_line":"        \"\"\""},{"line_number":86,"context_line":"        try:"},{"line_number":87,"context_line":"            if isinstance(credential, six.text_type):"},{"line_number":88,"context_line":"                return self.crypto.decrypt("},{"line_number":89,"context_line":"                    credential.encode(\u0027utf-8\u0027)"},{"line_number":90,"context_line":"                ).decode(\u0027utf-8\u0027)"},{"line_number":91,"context_line":"            return self.crypto.decrypt(credential).decode(\u0027utf-8\u0027)"},{"line_number":92,"context_line":"        except (fernet.InvalidToken, TypeError, ValueError):"},{"line_number":93,"context_line":"            msg \u003d _(\u0027Credential could not be decrypted. Please contact the\u0027"},{"line_number":94,"context_line":"                    \u0027 administrator\u0027)"}],"source_content_type":"text/x-python","patch_set":28,"id":"fa7ab95a_48f48586","line":91,"range":{"start_line":87,"start_character":11,"end_line":91,"end_character":66},"updated":"2016-08-26 22:53:20.000000000","message":"Totally nit picking here but I would go with:\n\n  if isinstance(credential, six.text_type):\n    credential \u003d credential.encode(\u0027utf-8\u0027)\n  return self.crypto.decrypt(credential).decode(\u0027utf-8\u0027)","commit_id":"22b7e9cfd82a0cfa28852b75f45208c9be028273"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"ebec7eb1cd86b82ae2f08e9b9a7fd698f64be72b","unresolved":false,"context_lines":[{"line_number":88,"context_line":"                return self.crypto.decrypt("},{"line_number":89,"context_line":"                    credential.encode(\u0027utf-8\u0027)"},{"line_number":90,"context_line":"                ).decode(\u0027utf-8\u0027)"},{"line_number":91,"context_line":"            return self.crypto.decrypt(credential).decode(\u0027utf-8\u0027)"},{"line_number":92,"context_line":"        except (fernet.InvalidToken, TypeError, ValueError):"},{"line_number":93,"context_line":"            msg \u003d _(\u0027Credential could not be decrypted. Please contact the\u0027"},{"line_number":94,"context_line":"                    \u0027 administrator\u0027)"}],"source_content_type":"text/x-python","patch_set":28,"id":"fa7ab95a_2be81be2","line":91,"in_reply_to":"fa7ab95a_48f48586","updated":"2016-08-26 23:15:39.000000000","message":"Done","commit_id":"22b7e9cfd82a0cfa28852b75f45208c9be028273"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"a6b54e54fae3b5f43c6ea172cdeacadefb4cea7b","unresolved":false,"context_lines":[{"line_number":47,"context_line":"    def primary_key_hash(self):"},{"line_number":48,"context_line":"        \"\"\"The first 7 bits of the primary key\u0027s hash.\"\"\""},{"line_number":49,"context_line":"        primary_key \u003d self.crypto._fernets[0]"},{"line_number":50,"context_line":"        key_digest \u003d hashlib.sha1("},{"line_number":51,"context_line":"            primary_key._signing_key + primary_key._encryption_key"},{"line_number":52,"context_line":"        ).hexdigest()"},{"line_number":53,"context_line":"        return key_digest[:7]"}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_4a8fd274","line":50,"updated":"2016-08-29 16:04:42.000000000","message":"To be safe maybe we should use a stronger hash after all.","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0ac6d620655d55d1c3dde90839b0d1f91fc45859","unresolved":false,"context_lines":[{"line_number":47,"context_line":"    def primary_key_hash(self):"},{"line_number":48,"context_line":"        \"\"\"The first 7 bits of the primary key\u0027s hash.\"\"\""},{"line_number":49,"context_line":"        primary_key \u003d self.crypto._fernets[0]"},{"line_number":50,"context_line":"        key_digest \u003d hashlib.sha1("},{"line_number":51,"context_line":"            primary_key._signing_key + primary_key._encryption_key"},{"line_number":52,"context_line":"        ).hexdigest()"},{"line_number":53,"context_line":"        return key_digest[:7]"}],"source_content_type":"text/x-python","patch_set":31,"id":"fa7ab95a_56b5bb2e","line":50,"in_reply_to":"fa7ab95a_4a8fd274","updated":"2016-08-29 16:47:36.000000000","message":"Recommendations?","commit_id":"076fcf73bc0156e58657914093188269455b0ae4"},{"author":{"_account_id":2903,"name":"Morgan Fainberg","email":"morgan.fainberg@gmail.com","username":"mdrnstm"},"change_message_id":"557b055b28aba5d3911c9aa03614e36fc67f091b","unresolved":false,"context_lines":[{"line_number":44,"context_line":"class Provider(core.Provider):"},{"line_number":45,"context_line":""},{"line_number":46,"context_line":"    @property"},{"line_number":47,"context_line":"    def primary_key_hash(self):"},{"line_number":48,"context_line":"        \"\"\"The first 7 bits of the primary key\u0027s hash.\"\"\""},{"line_number":49,"context_line":"        primary_key \u003d self.crypto._fernets[0]"},{"line_number":50,"context_line":"        key_digest \u003d hashlib.sha1("},{"line_number":51,"context_line":"            primary_key._signing_key + primary_key._encryption_key"},{"line_number":52,"context_line":"        ).hexdigest()"},{"line_number":53,"context_line":"        return key_digest[:7]"},{"line_number":54,"context_line":""},{"line_number":55,"context_line":"    @property"},{"line_number":56,"context_line":"    def crypto(self):"}],"source_content_type":"text/x-python","patch_set":34,"id":"fa7ab95a_3f3e3c28","line":53,"range":{"start_line":47,"start_character":0,"end_line":53,"end_character":29},"updated":"2016-08-29 21:31:51.000000000","message":"what is the point of truncating this to 7 bits? Remember SQL is really good at indexing specific numbers of preceeding bits. You\u0027re much much much more likely to get collisions with lower bit values here. Please add a comment why 7 bits is considered sufficient. I highly recommend using the full hash and accepting smaller counts if there are no conflicts (aka git). Artificially truncating the hash is a bad bad bad bad idea.","commit_id":"40603754b07ee14b6c7f9b33c5d698609845363d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"2b3cad05ce2c3b2eaa1319c1b67e1e51c812470c","unresolved":false,"context_lines":[{"line_number":50,"context_line":"        key_digest \u003d hashlib.sha1("},{"line_number":51,"context_line":"            primary_key._signing_key + primary_key._encryption_key"},{"line_number":52,"context_line":"        ).hexdigest()"},{"line_number":53,"context_line":"        return key_digest[:7]"},{"line_number":54,"context_line":""},{"line_number":55,"context_line":"    @property"},{"line_number":56,"context_line":"    def crypto(self):"}],"source_content_type":"text/x-python","patch_set":34,"id":"fa7ab95a_bcefb7e7","line":53,"in_reply_to":"fa7ab95a_3f3e3c28","updated":"2016-08-29 22:08:02.000000000","message":"Done","commit_id":"40603754b07ee14b6c7f9b33c5d698609845363d"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"1af0571234ccb3889843780ac2edeff3b83f24f9","unresolved":false,"context_lines":[{"line_number":81,"context_line":"        :returns: a decrypted credential"},{"line_number":82,"context_line":"        \"\"\""},{"line_number":83,"context_line":"        try:"},{"line_number":84,"context_line":"            if isinstance(credential, six.text_type):"},{"line_number":85,"context_line":"                credential \u003d credential.encode(\u0027utf-8\u0027)"},{"line_number":86,"context_line":"            return self.crypto.decrypt(credential).decode(\u0027utf-8\u0027)"},{"line_number":87,"context_line":"        except (fernet.InvalidToken, TypeError, ValueError):"},{"line_number":88,"context_line":"            msg \u003d _(\u0027Credential could not be decrypted. Please contact the\u0027"}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_23a80577","line":85,"range":{"start_line":84,"start_character":0,"end_line":85,"end_character":55},"updated":"2016-09-01 21:26:11.000000000","message":"why is this needed ?","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"3f693b14f546346a6b615e4dbdc6bff76d685dc1","unresolved":false,"context_lines":[{"line_number":82,"context_line":"        \"\"\""},{"line_number":83,"context_line":"        try:"},{"line_number":84,"context_line":"            if isinstance(credential, six.text_type):"},{"line_number":85,"context_line":"                credential \u003d credential.encode(\u0027utf-8\u0027)"},{"line_number":86,"context_line":"            return self.crypto.decrypt(credential).decode(\u0027utf-8\u0027)"},{"line_number":87,"context_line":"        except (fernet.InvalidToken, TypeError, ValueError):"},{"line_number":88,"context_line":"            msg \u003d _(\u0027Credential could not be decrypted. Please contact the\u0027"}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_4f5fe7e4","line":85,"in_reply_to":"fa7ab95a_23a80577","updated":"2016-09-02 01:12:49.000000000","message":"This is a compatibility fix with Fernet. plaintext must be in bytes before passing it to Fernet or else cryptography will raise a ValueError. This also protects us in the python3 case I believe.","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"08aeb46e396b7ba3643eb2eb2276e438dd67bc78","unresolved":false,"context_lines":[{"line_number":53,"context_line":""},{"line_number":54,"context_line":"def primary_key_hash(keys):"},{"line_number":55,"context_line":"    \"\"\"Calculate a hash of the primary key used for encryption.\"\"\""},{"line_number":56,"context_line":"    return hashlib.sha1(keys[0]).hexdigest()"},{"line_number":57,"context_line":""},{"line_number":58,"context_line":""},{"line_number":59,"context_line":"class Provider(core.Provider):"}],"source_content_type":"text/x-python","patch_set":50,"id":"fa7ab95a_2a40ce33","line":56,"range":{"start_line":56,"start_character":24,"end_line":56,"end_character":31},"updated":"2016-09-02 03:31:38.000000000","message":"i think you need do an encode here","commit_id":"9e4e9cfbcb4c2a47251e69b675525bf56003e803"}],"keystone/server/backends.py":[{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"1af0571234ccb3889843780ac2edeff3b83f24f9","unresolved":false,"context_lines":[{"line_number":51,"context_line":"        assignment_api\u003d_ASSIGNMENT_API,"},{"line_number":52,"context_line":"        catalog_api\u003dcatalog.Manager(),"},{"line_number":53,"context_line":"        credential_api\u003dcredential.Manager(),"},{"line_number":54,"context_line":"        credential_provider_api\u003dcredential.provider.Manager(),"},{"line_number":55,"context_line":"        domain_config_api\u003dresource.DomainConfigManager(),"},{"line_number":56,"context_line":"        endpoint_policy_api\u003dendpoint_policy.Manager(),"},{"line_number":57,"context_line":"        federation_api\u003dfederation.Manager(),"}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_e35c5d4d","line":54,"range":{"start_line":54,"start_character":8,"end_line":54,"end_character":31},"updated":"2016-09-01 21:26:11.000000000","message":"how is this different from the credential API?","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"3f693b14f546346a6b615e4dbdc6bff76d685dc1","unresolved":false,"context_lines":[{"line_number":51,"context_line":"        assignment_api\u003d_ASSIGNMENT_API,"},{"line_number":52,"context_line":"        catalog_api\u003dcatalog.Manager(),"},{"line_number":53,"context_line":"        credential_api\u003dcredential.Manager(),"},{"line_number":54,"context_line":"        credential_provider_api\u003dcredential.provider.Manager(),"},{"line_number":55,"context_line":"        domain_config_api\u003dresource.DomainConfigManager(),"},{"line_number":56,"context_line":"        endpoint_policy_api\u003dendpoint_policy.Manager(),"},{"line_number":57,"context_line":"        federation_api\u003dfederation.Manager(),"}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_cffa17bd","line":54,"in_reply_to":"fa7ab95a_e35c5d4d","updated":"2016-09-02 01:12:49.000000000","message":"This was modeled after the token and token provider API. This could give us the flexibility to swap out different encryption providers in the future (deployers could do this, too).","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"}],"keystone/tests/unit/test_backend_sql.py":[{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"1af0571234ccb3889843780ac2edeff3b83f24f9","unresolved":false,"context_lines":[{"line_number":1101,"context_line":"            credential"},{"line_number":1102,"context_line":"        )"},{"line_number":1103,"context_line":""},{"line_number":1104,"context_line":"        # The process of updating an existing credential should encrypt the"},{"line_number":1105,"context_line":"        # credential if it isn\u0027t encrypted already and store the encrypted"},{"line_number":1106,"context_line":"        # value, which is a user-initiated/lazy migration of unencrypted"},{"line_number":1107,"context_line":"        # credentials."},{"line_number":1108,"context_line":"        new_blob \u003d uuid.uuid4().hex"},{"line_number":1109,"context_line":"        updated_credential \u003d self.credential_api.update_credential("},{"line_number":1110,"context_line":"            credential_id,"}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_c373d97e","line":1107,"range":{"start_line":1104,"start_character":10,"end_line":1107,"end_character":22},"updated":"2016-09-01 21:26:11.000000000","message":"Given --migrate would migrate all the credentials in the db and no credentials can be added during the upgrade process, is this really needed?","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"3f693b14f546346a6b615e4dbdc6bff76d685dc1","unresolved":false,"context_lines":[{"line_number":1104,"context_line":"        # The process of updating an existing credential should encrypt the"},{"line_number":1105,"context_line":"        # credential if it isn\u0027t encrypted already and store the encrypted"},{"line_number":1106,"context_line":"        # value, which is a user-initiated/lazy migration of unencrypted"},{"line_number":1107,"context_line":"        # credentials."},{"line_number":1108,"context_line":"        new_blob \u003d uuid.uuid4().hex"},{"line_number":1109,"context_line":"        updated_credential \u003d self.credential_api.update_credential("},{"line_number":1110,"context_line":"            credential_id,"}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_6f8e6b2f","line":1107,"in_reply_to":"fa7ab95a_c373d97e","updated":"2016-09-02 01:12:49.000000000","message":"Done","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"1af0571234ccb3889843780ac2edeff3b83f24f9","unresolved":false,"context_lines":[{"line_number":1140,"context_line":"            credential"},{"line_number":1141,"context_line":"        )"},{"line_number":1142,"context_line":""},{"line_number":1143,"context_line":"        # Pull the credential directly from the backend, the `blob` should be"},{"line_number":1144,"context_line":"        # encrypted."},{"line_number":1145,"context_line":"        credential_from_backend \u003d self.credential_api.driver.get_credential("},{"line_number":1146,"context_line":"            credential_id"},{"line_number":1147,"context_line":"        )"},{"line_number":1148,"context_line":"        self.assertNotEqual("},{"line_number":1149,"context_line":"            credential_from_backend[\u0027encrypted_blob\u0027],"},{"line_number":1150,"context_line":"            credential[\u0027blob\u0027]"},{"line_number":1151,"context_line":"        )"},{"line_number":1152,"context_line":""},{"line_number":1153,"context_line":"        # Make sure the `blob` values listed from the API are not encrypted."},{"line_number":1154,"context_line":"        listed_credentials \u003d self.credential_api.list_credentials()"}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_0374e133","line":1151,"range":{"start_line":1143,"start_character":0,"end_line":1151,"end_character":9},"updated":"2016-09-01 21:26:11.000000000","message":"this is already tested in previous test cases ^","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"3f693b14f546346a6b615e4dbdc6bff76d685dc1","unresolved":false,"context_lines":[{"line_number":1148,"context_line":"        self.assertNotEqual("},{"line_number":1149,"context_line":"            credential_from_backend[\u0027encrypted_blob\u0027],"},{"line_number":1150,"context_line":"            credential[\u0027blob\u0027]"},{"line_number":1151,"context_line":"        )"},{"line_number":1152,"context_line":""},{"line_number":1153,"context_line":"        # Make sure the `blob` values listed from the API are not encrypted."},{"line_number":1154,"context_line":"        listed_credentials \u003d self.credential_api.list_credentials()"}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_0fd95f22","line":1151,"in_reply_to":"fa7ab95a_0374e133","updated":"2016-09-02 01:12:49.000000000","message":"Where?","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"}],"keystone/tests/unit/test_sql_banned_operations.py":[{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"1af0571234ccb3889843780ac2edeff3b83f24f9","unresolved":false,"context_lines":[{"line_number":233,"context_line":"        # tests, otherwise fails."},{"line_number":234,"context_line":"        2,"},{"line_number":235,"context_line":"        # NOTE(lbragstad): The expand 003 migration alters the credential table"},{"line_number":236,"context_line":"        # to make `blob` nullable. This allows the triggers added in 003 to"},{"line_number":237,"context_line":"        # catch writes when the `blob` attribute isn\u0027t populated."},{"line_number":238,"context_line":"        3"},{"line_number":239,"context_line":"    ]"},{"line_number":240,"context_line":""}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_e3db7d7a","line":237,"range":{"start_line":236,"start_character":35,"end_line":237,"end_character":65},"updated":"2016-09-01 21:26:11.000000000","message":"which is forbidden because ?","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"3f693b14f546346a6b615e4dbdc6bff76d685dc1","unresolved":false,"context_lines":[{"line_number":234,"context_line":"        2,"},{"line_number":235,"context_line":"        # NOTE(lbragstad): The expand 003 migration alters the credential table"},{"line_number":236,"context_line":"        # to make `blob` nullable. This allows the triggers added in 003 to"},{"line_number":237,"context_line":"        # catch writes when the `blob` attribute isn\u0027t populated."},{"line_number":238,"context_line":"        3"},{"line_number":239,"context_line":"    ]"},{"line_number":240,"context_line":""}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_6fd90b57","line":237,"in_reply_to":"fa7ab95a_e3db7d7a","updated":"2016-09-02 01:12:49.000000000","message":"Done","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"}],"keystone/tests/unit/test_sql_upgrade.py":[{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"6af95961663340e7af3f38ff56c20045d1b6b649","unresolved":false,"context_lines":[{"line_number":1575,"context_line":"                      \u0027blob\u0027: unencrypted_blob,"},{"line_number":1576,"context_line":"                      \u0027user_id\u0027: uuid.uuid4().hex,"},{"line_number":1577,"context_line":"                      \u0027type\u0027: \u0027cert\u0027}"},{"line_number":1578,"context_line":"        self.insert_dict(session, \u0027credential\u0027, credential)"},{"line_number":1579,"context_line":""},{"line_number":1580,"context_line":"        self.upgrade(self.max_version)"},{"line_number":1581,"context_line":"        filter \u003d credential_table.c.id \u003d\u003d credential_id"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_3567934e","line":1578,"updated":"2016-08-22 21:35:29.000000000","message":"This is going to fail because the read-only trigger is already in place.","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"5e06c447632df075f3aa37ae720fabc7c69d5a2f","unresolved":false,"context_lines":[{"line_number":1575,"context_line":"                      \u0027blob\u0027: unencrypted_blob,"},{"line_number":1576,"context_line":"                      \u0027user_id\u0027: uuid.uuid4().hex,"},{"line_number":1577,"context_line":"                      \u0027type\u0027: \u0027cert\u0027}"},{"line_number":1578,"context_line":"        self.insert_dict(session, \u0027credential\u0027, credential)"},{"line_number":1579,"context_line":""},{"line_number":1580,"context_line":"        self.upgrade(self.max_version)"},{"line_number":1581,"context_line":"        filter \u003d credential_table.c.id \u003d\u003d credential_id"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_f5f7bb56","line":1578,"in_reply_to":"1ac06dbe_3567934e","updated":"2016-08-22 21:37:28.000000000","message":"Done","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"84db056c91561d89a79f537730ec3175ff43d63d","unresolved":false,"context_lines":[{"line_number":1683,"context_line":"        self.upgrade(self.max_version)"},{"line_number":1684,"context_line":"        # Make sure the expand repo is also upgraded"},{"line_number":1685,"context_line":"        # self.initialize_repo(repo_name\u003dEXPAND_REPO)"},{"line_number":1686,"context_line":"        # self.upgrade(self.max_version)"},{"line_number":1687,"context_line":""},{"line_number":1688,"context_line":"    def test_credential_migration(self):"},{"line_number":1689,"context_line":"        session \u003d self.sessionmaker()"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_df60de03","line":1686,"updated":"2016-08-22 21:14:24.000000000","message":"ah these could be removed","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"5e06c447632df075f3aa37ae720fabc7c69d5a2f","unresolved":false,"context_lines":[{"line_number":1683,"context_line":"        self.upgrade(self.max_version)"},{"line_number":1684,"context_line":"        # Make sure the expand repo is also upgraded"},{"line_number":1685,"context_line":"        # self.initialize_repo(repo_name\u003dEXPAND_REPO)"},{"line_number":1686,"context_line":"        # self.upgrade(self.max_version)"},{"line_number":1687,"context_line":""},{"line_number":1688,"context_line":"    def test_credential_migration(self):"},{"line_number":1689,"context_line":"        session \u003d self.sessionmaker()"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_b501c33a","line":1686,"in_reply_to":"1ac06dbe_df60de03","updated":"2016-08-22 21:37:28.000000000","message":"Done","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"b0bff8b868e1aea0fc4d1218c354e1be5844515f","unresolved":false,"context_lines":[{"line_number":1685,"context_line":"        # self.initialize_repo(repo_name\u003dEXPAND_REPO)"},{"line_number":1686,"context_line":"        # self.upgrade(self.max_version)"},{"line_number":1687,"context_line":""},{"line_number":1688,"context_line":"    def test_credential_migration(self):"},{"line_number":1689,"context_line":"        session \u003d self.sessionmaker()"},{"line_number":1690,"context_line":"        credential_table_name \u003d \u0027credential\u0027"},{"line_number":1691,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_10d705ee","line":1688,"updated":"2016-08-22 21:56:11.000000000","message":"Use a version number here (002) in case we have multiple migrations that could be described as \"credential migrations\"","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"836ef615c47339a96a983a58df515efbecfc5314","unresolved":false,"context_lines":[{"line_number":1685,"context_line":"        # self.initialize_repo(repo_name\u003dEXPAND_REPO)"},{"line_number":1686,"context_line":"        # self.upgrade(self.max_version)"},{"line_number":1687,"context_line":""},{"line_number":1688,"context_line":"    def test_credential_migration(self):"},{"line_number":1689,"context_line":"        session \u003d self.sessionmaker()"},{"line_number":1690,"context_line":"        credential_table_name \u003d \u0027credential\u0027"},{"line_number":1691,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_cb4752ba","line":1688,"in_reply_to":"1ac06dbe_10d705ee","updated":"2016-08-23 14:35:00.000000000","message":"Done","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"84db056c91561d89a79f537730ec3175ff43d63d","unresolved":false,"context_lines":[{"line_number":1689,"context_line":"        session \u003d self.sessionmaker()"},{"line_number":1690,"context_line":"        credential_table_name \u003d \u0027credential\u0027"},{"line_number":1691,"context_line":""},{"line_number":1692,"context_line":"        # upgrade to latest legacy migration so that we have a credential table"},{"line_number":1693,"context_line":"        # populate the credential table with some sample credentials"},{"line_number":1694,"context_line":"        credentials \u003d list()"},{"line_number":1695,"context_line":"        for i in range(5):"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_ff531a37","line":1692,"updated":"2016-08-22 21:14:24.000000000","message":"this step is already done in the setUp","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"5e06c447632df075f3aa37ae720fabc7c69d5a2f","unresolved":false,"context_lines":[{"line_number":1689,"context_line":"        session \u003d self.sessionmaker()"},{"line_number":1690,"context_line":"        credential_table_name \u003d \u0027credential\u0027"},{"line_number":1691,"context_line":""},{"line_number":1692,"context_line":"        # upgrade to latest legacy migration so that we have a credential table"},{"line_number":1693,"context_line":"        # populate the credential table with some sample credentials"},{"line_number":1694,"context_line":"        credentials \u003d list()"},{"line_number":1695,"context_line":"        for i in range(5):"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_15fb7748","line":1692,"in_reply_to":"1ac06dbe_ff531a37","updated":"2016-08-22 21:37:28.000000000","message":"Done","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"b0bff8b868e1aea0fc4d1218c354e1be5844515f","unresolved":false,"context_lines":[{"line_number":1731,"context_line":""},{"line_number":1732,"context_line":"        # upgrade migrate repo to 002 to migrate existing credentials"},{"line_number":1733,"context_line":"        self.initialize_repo(repo_name\u003dDATA_MIGRATION_REPO)"},{"line_number":1734,"context_line":"        self.upgrade(2)"},{"line_number":1735,"context_line":""},{"line_number":1736,"context_line":"        # make sure we\u0027ve actually updated the credential with the"},{"line_number":1737,"context_line":"        # encrypted blob and the corresponding key hash"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_50321d60","line":1734,"updated":"2016-08-22 21:56:11.000000000","message":"Not a bad hack, but ideally you\u0027re testing this one step in isolation. In reality, you\u0027re applying both data migration 001 and 002 here. That will only become a problem when we\u0027ve got a lot of migrations in each repo, and we\u0027re still trying to use this pattern.","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"836ef615c47339a96a983a58df515efbecfc5314","unresolved":false,"context_lines":[{"line_number":1731,"context_line":""},{"line_number":1732,"context_line":"        # upgrade migrate repo to 002 to migrate existing credentials"},{"line_number":1733,"context_line":"        self.initialize_repo(repo_name\u003dDATA_MIGRATION_REPO)"},{"line_number":1734,"context_line":"        self.upgrade(2)"},{"line_number":1735,"context_line":""},{"line_number":1736,"context_line":"        # make sure we\u0027ve actually updated the credential with the"},{"line_number":1737,"context_line":"        # encrypted blob and the corresponding key hash"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_2ba2664b","line":1734,"in_reply_to":"1ac06dbe_50321d60","updated":"2016-08-23 14:35:00.000000000","message":"Good catch - I added a couple lines to the beginning of the test to make sure all repositories are at 001.","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"cae70c72509c9d054cb45788dc5dbf36d5e39c59","unresolved":false,"context_lines":[{"line_number":1688,"context_line":"            filter \u003d credential_table.c.id \u003d\u003d credential[\u0027id\u0027]"},{"line_number":1689,"context_line":"            cols \u003d [credential_table.c.key_hash, credential_table.c.blob,"},{"line_number":1690,"context_line":"                    credential_table.c.encrypted_blob]"},{"line_number":1691,"context_line":"            statement \u003d sqlalchemy.select(cols).where(filter)"},{"line_number":1692,"context_line":"            result \u003d session.execute(statement).fetchone()"},{"line_number":1693,"context_line":""},{"line_number":1694,"context_line":"            self.assertIsNotNone(result.encrypted_blob)"}],"source_content_type":"text/x-python","patch_set":27,"id":"fa7ab95a_357ddfe7","line":1691,"updated":"2016-08-26 21:24:44.000000000","message":"we usually call this variable \u0027q\u0027 in our code","commit_id":"1059deb3d2e5bbf66a3a84def65e80acdf42ea11"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"aee4a0cd266d34e2d7e5adbd379d53124e756fa8","unresolved":false,"context_lines":[{"line_number":1688,"context_line":"            filter \u003d credential_table.c.id \u003d\u003d credential[\u0027id\u0027]"},{"line_number":1689,"context_line":"            cols \u003d [credential_table.c.key_hash, credential_table.c.blob,"},{"line_number":1690,"context_line":"                    credential_table.c.encrypted_blob]"},{"line_number":1691,"context_line":"            statement \u003d sqlalchemy.select(cols).where(filter)"},{"line_number":1692,"context_line":"            result \u003d session.execute(statement).fetchone()"},{"line_number":1693,"context_line":""},{"line_number":1694,"context_line":"            self.assertIsNotNone(result.encrypted_blob)"}],"source_content_type":"text/x-python","patch_set":27,"id":"fa7ab95a_06efcf87","line":1691,"in_reply_to":"fa7ab95a_357ddfe7","updated":"2016-08-26 22:29:19.000000000","message":"Done","commit_id":"1059deb3d2e5bbf66a3a84def65e80acdf42ea11"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"cae70c72509c9d054cb45788dc5dbf36d5e39c59","unresolved":false,"context_lines":[{"line_number":1711,"context_line":"                      \u0027encrypted_blob\u0027: uuid.uuid4().hex,"},{"line_number":1712,"context_line":"                      \u0027user_id\u0027: uuid.uuid4().hex,"},{"line_number":1713,"context_line":"                      \u0027type\u0027: \u0027cert\u0027}"},{"line_number":1714,"context_line":"        self.insert_dict(session, credential_table_name, credential)"},{"line_number":1715,"context_line":""},{"line_number":1716,"context_line":""},{"line_number":1717,"context_line":"class MySQLOpportunisticFullMigration(FullMigration):"}],"source_content_type":"text/x-python","patch_set":27,"id":"fa7ab95a_555b4387","line":1714,"updated":"2016-08-26 21:24:44.000000000","message":"Love this test now! It\u0027ll be great for other devs to use as an example.","commit_id":"1059deb3d2e5bbf66a3a84def65e80acdf42ea11"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"aee4a0cd266d34e2d7e5adbd379d53124e756fa8","unresolved":false,"context_lines":[{"line_number":1711,"context_line":"                      \u0027encrypted_blob\u0027: uuid.uuid4().hex,"},{"line_number":1712,"context_line":"                      \u0027user_id\u0027: uuid.uuid4().hex,"},{"line_number":1713,"context_line":"                      \u0027type\u0027: \u0027cert\u0027}"},{"line_number":1714,"context_line":"        self.insert_dict(session, credential_table_name, credential)"},{"line_number":1715,"context_line":""},{"line_number":1716,"context_line":""},{"line_number":1717,"context_line":"class MySQLOpportunisticFullMigration(FullMigration):"}],"source_content_type":"text/x-python","patch_set":27,"id":"fa7ab95a_66ec1b90","line":1714,"in_reply_to":"fa7ab95a_555b4387","updated":"2016-08-26 22:29:19.000000000","message":"++ thanks!","commit_id":"1059deb3d2e5bbf66a3a84def65e80acdf42ea11"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"1af0571234ccb3889843780ac2edeff3b83f24f9","unresolved":false,"context_lines":[{"line_number":1729,"context_line":"            self.assertIsNotNone(result.key_hash)"},{"line_number":1730,"context_line":"            # verify the original blob column is still populated"},{"line_number":1731,"context_line":"            self.assertEqual(result.blob, credential[\u0027blob\u0027])"},{"line_number":1732,"context_line":""},{"line_number":1733,"context_line":"        # upgrade contract repo to 003 to remove triggers and blob column"},{"line_number":1734,"context_line":"        self.contract(3)"},{"line_number":1735,"context_line":""}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_637a0d8a","line":1732,"updated":"2016-09-01 21:26:11.000000000","message":"verify triggers don\u0027t allow for writting yet","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"3f693b14f546346a6b615e4dbdc6bff76d685dc1","unresolved":false,"context_lines":[{"line_number":1729,"context_line":"            self.assertIsNotNone(result.key_hash)"},{"line_number":1730,"context_line":"            # verify the original blob column is still populated"},{"line_number":1731,"context_line":"            self.assertEqual(result.blob, credential[\u0027blob\u0027])"},{"line_number":1732,"context_line":""},{"line_number":1733,"context_line":"        # upgrade contract repo to 003 to remove triggers and blob column"},{"line_number":1734,"context_line":"        self.contract(3)"},{"line_number":1735,"context_line":""}],"source_content_type":"text/x-python","patch_set":48,"id":"fa7ab95a_cfcf777d","line":1732,"in_reply_to":"fa7ab95a_637a0d8a","updated":"2016-09-02 01:12:49.000000000","message":"Done","commit_id":"dee0dec0db571cad0272d0a2f79ac7f900280a4d"}],"keystone/tests/unit/test_v3.py":[{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"b0bff8b868e1aea0fc4d1218c354e1be5844515f","unresolved":false,"context_lines":[{"line_number":1043,"context_line":"        self.assertIsNotNone(entity.get(\u0027blob\u0027))"},{"line_number":1044,"context_line":"        self.assertIsNotNone(entity.get(\u0027type\u0027))"},{"line_number":1045,"context_line":"        self.assertNotIn(\u0027key_hash\u0027, entity)"},{"line_number":1046,"context_line":"        self.assertNotIn(\u0027encrypted_blob\u0027, entity)"},{"line_number":1047,"context_line":"        if ref:"},{"line_number":1048,"context_line":"            self.assertEqual(ref[\u0027user_id\u0027], entity[\u0027user_id\u0027])"},{"line_number":1049,"context_line":"            self.assertEqual(ref[\u0027blob\u0027], entity[\u0027blob\u0027])"}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_707bd9f5","line":1046,"updated":"2016-08-22 21:56:11.000000000","message":"++","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"}],"keystone/tests/unit/test_v3_credential.py":[{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"4b6572ba1f957d88d8974f3925c1dc7ef8058729","unresolved":false,"context_lines":[{"line_number":482,"context_line":""},{"line_number":483,"context_line":""},{"line_number":484,"context_line":"# NOTE(lbragstad): Do we really need a separate class for this now that"},{"line_number":485,"context_line":"# encryption of credentials is always enabled?"},{"line_number":486,"context_line":"class CredentialEncryptionTests(CredentialBaseTestCase):"},{"line_number":487,"context_line":"    \"\"\"Test v3 credential encryption.\"\"\""},{"line_number":488,"context_line":""}],"source_content_type":"text/x-python","patch_set":8,"id":"1ac06dbe_2a5a0584","line":485,"updated":"2016-08-19 15:25:14.000000000","message":"Not even enabled - it\u0027s always on period...\n\nShould we just collapse this into another class?","commit_id":"b739ad127d5e1059f27700e860ecabdd18aa7935"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"b0bff8b868e1aea0fc4d1218c354e1be5844515f","unresolved":false,"context_lines":[{"line_number":482,"context_line":""},{"line_number":483,"context_line":""},{"line_number":484,"context_line":"# NOTE(lbragstad): Do we really need a separate class for this now that"},{"line_number":485,"context_line":"# encryption of credentials is always enabled?"},{"line_number":486,"context_line":"class CredentialEncryptionTests(CredentialBaseTestCase):"},{"line_number":487,"context_line":"    \"\"\"Test v3 credential encryption.\"\"\""},{"line_number":488,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_b058b18b","line":485,"updated":"2016-08-22 21:56:11.000000000","message":"Does it share a setUp with another class?","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"836ef615c47339a96a983a58df515efbecfc5314","unresolved":false,"context_lines":[{"line_number":482,"context_line":""},{"line_number":483,"context_line":""},{"line_number":484,"context_line":"# NOTE(lbragstad): Do we really need a separate class for this now that"},{"line_number":485,"context_line":"# encryption of credentials is always enabled?"},{"line_number":486,"context_line":"class CredentialEncryptionTests(CredentialBaseTestCase):"},{"line_number":487,"context_line":"    \"\"\"Test v3 credential encryption.\"\"\""},{"line_number":488,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"1ac06dbe_2b70a6db","line":485,"in_reply_to":"1ac06dbe_b058b18b","updated":"2016-08-23 14:35:00.000000000","message":"I don\u0027t think there is anything specific to the setUp here that we won\u0027t have to do in other places that test credentials.","commit_id":"de165adc642f2682e4f114bff1574c75cced950b"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"42e0a8b3060bdc1e2d988813f97e4eb9a6447628","unresolved":false,"context_lines":[{"line_number":536,"context_line":"                          cred_from_credential_api[0][\u0027id\u0027])"},{"line_number":537,"context_line":""},{"line_number":538,"context_line":""},{"line_number":539,"context_line":"# NOTE(lbragstad): Do we really need a separate class for this now that"},{"line_number":540,"context_line":"# encryption of credentials is always enabled?"},{"line_number":541,"context_line":"class CredentialEncryptionTests(CredentialBaseTestCase):"},{"line_number":542,"context_line":"    \"\"\"Test v3 credential encryption.\"\"\""},{"line_number":543,"context_line":""}],"source_content_type":"text/x-python","patch_set":17,"id":"1ac06dbe_0d6d8732","line":540,"range":{"start_line":539,"start_character":0,"end_line":540,"end_character":46},"updated":"2016-08-25 03:01:44.000000000","message":"I am okay with this, as this separate class is testing specifically the credentials are encrypted in the backend, VS testing credential API behavior.\n\nHowever, these tests should go into test backend, since they are not Restful tests: https://github.com/openstack/keystone/blob/b4ff783/keystone/tests/unit/test_backend_sql.py#L1010","commit_id":"9c9c23f3703ff0ded89a819e59b7da4b2b844c1b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"7ab3e4e33352499cc1eef0930b43066268f6d1cc","unresolved":false,"context_lines":[{"line_number":537,"context_line":""},{"line_number":538,"context_line":""},{"line_number":539,"context_line":"# NOTE(lbragstad): Do we really need a separate class for this now that"},{"line_number":540,"context_line":"# encryption of credentials is always enabled?"},{"line_number":541,"context_line":"class CredentialEncryptionTests(CredentialBaseTestCase):"},{"line_number":542,"context_line":"    \"\"\"Test v3 credential encryption.\"\"\""},{"line_number":543,"context_line":""}],"source_content_type":"text/x-python","patch_set":17,"id":"1ac06dbe_d503cd74","line":540,"in_reply_to":"1ac06dbe_0d6d8732","updated":"2016-08-25 14:27:33.000000000","message":"Done","commit_id":"9c9c23f3703ff0ded89a819e59b7da4b2b844c1b"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"42e0a8b3060bdc1e2d988813f97e4eb9a6447628","unresolved":false,"context_lines":[{"line_number":640,"context_line":"        listed_credentials \u003d self.credential_api.list_credentials()"},{"line_number":641,"context_line":"        self.assertEqual("},{"line_number":642,"context_line":"            created_credential,"},{"line_number":643,"context_line":"            listed_credentials[0]"},{"line_number":644,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":17,"id":"1ac06dbe_93feb603","line":643,"range":{"start_line":643,"start_character":12,"end_line":643,"end_character":33},"updated":"2016-08-25 03:01:44.000000000","message":"this will fail if another credential is created somehow (eg parallel test run). Just do assertIn","commit_id":"9c9c23f3703ff0ded89a819e59b7da4b2b844c1b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"7ab3e4e33352499cc1eef0930b43066268f6d1cc","unresolved":false,"context_lines":[{"line_number":640,"context_line":"        listed_credentials \u003d self.credential_api.list_credentials()"},{"line_number":641,"context_line":"        self.assertEqual("},{"line_number":642,"context_line":"            created_credential,"},{"line_number":643,"context_line":"            listed_credentials[0]"},{"line_number":644,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":17,"id":"1ac06dbe_b5061183","line":643,"in_reply_to":"1ac06dbe_93feb603","updated":"2016-08-25 14:27:33.000000000","message":"Done","commit_id":"9c9c23f3703ff0ded89a819e59b7da4b2b844c1b"}],"keystone/tests/unit/test_v3_identity.py":[{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"c7794e5c49ed3a4aa778bd9936320dc47fc177a5","unresolved":false,"context_lines":[{"line_number":72,"context_line":""},{"line_number":73,"context_line":"    def setUp(self):"},{"line_number":74,"context_line":"        super(IdentityTestCase, self).setUp()"},{"line_number":75,"context_line":"        self.useFixture("},{"line_number":76,"context_line":"            ksfixtures.KeyRepository("},{"line_number":77,"context_line":"                self.config_fixture,"},{"line_number":78,"context_line":"                \u0027credential\u0027,"},{"line_number":79,"context_line":"                credential_fernet.MAX_ACTIVE_KEYS"},{"line_number":80,"context_line":"            )"},{"line_number":81,"context_line":"        )"},{"line_number":82,"context_line":""},{"line_number":83,"context_line":"        self.group \u003d unit.new_group_ref(domain_id\u003dself.domain_id)"},{"line_number":84,"context_line":"        self.group \u003d self.identity_api.create_group(self.group)"}],"source_content_type":"text/x-python","patch_set":49,"id":"fa7ab95a_d440eea7","line":81,"range":{"start_line":75,"start_character":8,"end_line":81,"end_character":9},"updated":"2016-09-02 01:54:48.000000000","message":"probably easier to add this to the RestfulTestCase","commit_id":"2262fa61f6411a2f9f4be25a9ec70d3d444f5423"}]}
